Skip to content

docs: document the typecheck gate and correct CLAUDE.md - #7

Merged
svc-finitelabs[bot] merged 1 commit into
mainfrom
agent/AGENT-44-claude-md-audit
Aug 11, 2026
Merged

docs: document the typecheck gate and correct CLAUDE.md#7
svc-finitelabs[bot] merged 1 commit into
mainfrom
agent/AGENT-44-claude-md-audit

Conversation

@svc-finitelabs

Copy link
Copy Markdown
Contributor

Part of AGENT-44 (audit the CLAUDE.md files that exist). One PR per repo; this is
the repo you flagged as the drifted one, and it was.

The gap you asked about

CLAUDE.md never mentioned typecheck. The target landed in 821ae5c and was
gated into check in 32c9c3a, both after the file was last touched, so the
file documented test/bench/format/lint/build and stopped. An agent
reading it and running make all gets format lint test build, which runs
neither format-check nor typecheck, and concludes the repo has no typecheck
gate. It does, and CI enforces it.

Added the typecheck section converged with the four sibling repos, plus the one
thing specific to this repo: the check deliberately scopes to the whole tree
rather than src/, because @alias resolves workspace-wide. That is why
src/crypto/annotations.lua is never required by anything and still matters.

Other claims that did not survive checking

  1. "Each primitive module also exposes its own selftest() and benchmark()."
    random and openssl_wrapper have selftest() and no benchmark(). Both are
    listed as primitives and both are in crypto.selftest()'s module list, which
    guards with type(mod.selftest) == "function" rather than assuming the shape.

  2. "pinned as regression cases in openssl_wrapper.selftest()." That function
    drives injected stand-in bindings, by its own docstring, so it pins the
    gate's verdict for a version string rather than the binding's behaviour. Two
    rows are weaker than the sentence implies: KDF is not pinned at 0.8.5 at all
    (the stub is "0.9.2"), and random(0) raising exists only as prose. The real
    regression site is the openssl-matrix CI job, which installs the actual
    0.8.5-1, 0.9.2-2 and 0.11.1-1 rocks and asserts the feature map against
    each. That job was already in build.yml when CLAUDE.md was last written and
    the file never mentioned it.

  3. release.yml publishes both artifacts, not just crypto.lua. It has since
    963e91a, before the file's last edit. (build.yml's artifact upload keeps
    only crypto.lua, which is the likely source of the confusion.)

  4. A fifth is_accelerated() reason string exists"OpenSSL modular exponentiation is unavailable", the fallback when unavailable_reason returns
    nil. A caller matching the four documented strings misses it. The other four
    are verbatim correct.

  5. powmod is what the Control4 build spells it, but the wrapper accepts
    bn.powmod or bn.mod_exp and pins both.

  6. CI section: runs on main and master, invokes make test-all, and the
    check job includes typecheck.

The one where the numbers were right and the conclusion was not

The measured tables are excellent and I kept all of them; every figure is dated,
attributed to a binding and OS build, and internally consistent (I re-checked the
arithmetic: 2(0.459)+0.786+1.583 = 3.287, 1.602/0.786 = 2.04, the mod_exp fit
at 256 and 3072 bits, and 82.05/0.016 ≈ 5100 all hold).

But "Expand the long-term key once" does not follow from them. Cold sign is
1.602 s, pre-expanded is 0.786 s, and expand_private_key is under 0.001 s — so
the 0.816 s gap is not the expansion. ed25519.sign also derives the public key
on every call (pt_scalarbase + pt_pack) before delegating to
sign_expanded(expanded, public_key, message). A caller that holds only the
expanded form still pays the scalar multiplication and sees none of the 2.04x.
Corrected to say cache both.

Trimmed

Cut the design-defence passages: "This has its own heading because…", "the wrong
precondition twice over…", "Two design points worth not re-litigating", and the
math.random argument. Kept the measurement inside each (the 5,100x, the ~20
bits). Both of the "not re-litigating" points are already comments in
openssl_wrapper.lua, so a reader meets them on opening the file.

Verification

  • make typecheck with the CI-pinned lua-language-server 3.19.0: clean, no
    problems found. Docs-only change, so this is a regression check rather than
    evidence for the diff.
  • Every API name, string literal and file path asserted in the rewrite was
    checked against the tree.
  • Not run: make test, make build, and the openssl-matrix job. No Lua
    source changed.

Out of scope, worth knowing

src/crypto/utils/init.lua:2 reads "Common utility functions for the Noise
Protocol Framework"
— copy-paste from lua-noiseprotocol. Not touched here.

CLAUDE.md never mentioned typecheck. The target landed in 821ae5c and was gated
into check by 32c9c3a, both after the file was last touched, so an agent reading
it and running make all skipped the gate CI enforces.

- Add the typecheck section, converged with the sibling repos, and note that it
  scopes to the whole repo because @alias resolves workspace-wide, which is why
  src/crypto/annotations.lua is never required and still matters.
- Document make check as the gate; make all runs neither format-check nor
  typecheck. make help is stale on this and says so now.
- random and openssl_wrapper have selftest() but no benchmark(); crypto.selftest
  guards on the shape rather than assuming it.
- The DriverWorks feature table is regression-tested by the openssl-matrix CI
  job against real 0.8.5/0.9.2/0.11.1 rocks. openssl_wrapper.selftest() drives
  injected stand-ins, so it pins the gate's verdict, not the binding's behaviour.
- bignum.is_accelerated has a fifth reason string; matching only the four
  documented ones misses it.
- release.yml publishes both crypto.lua and crypto-portable.lua.
- ed25519.sign also derives the public key on every call, so the 2.04x needs the
  public key cached as well as the expanded form. Holding only the expanded form
  still pays the scalar multiplication.
- Add the missing tree entries, correct the CI section, and record that stylua
  covers src/ only with no .stylua.toml.

Trims the design-defence passages; the measured hardware numbers stay.

@derek-miller derek-miller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. This is the most technically load-bearing of the six CLAUDE.md PRs, because the typecheck section is not describing the repo — it is describing lua-language-server's internals to justify how the gate is hardened. Claims like that are the easiest to get subtly wrong and the hardest for a reader to check, so I checked them.

The lua-language-server claims hold, against the pinned version

Fetched script/cli/check_worker.lua at tag 3.19.0, the version build.yml pins:

 15: require 'plugin'
163: local function downgrade_checks_to_opened(uri)
166:     if status == 'Any' or status == 'Any!' then
167:         diagStatus[d] = 'Opened!'
242:  -- Downgrade file opened status to Opened for everything to avoid
244:  downgrade_checks_to_opened(rootUri)

Both claims are exactly right:

  • "force-overwrites only codes whose default status is Any, leaving everything defaulting to Opened under local control" — the guard is literally status == 'Any' or status == 'Any!'. So the conclusion follows: an undefined-global probe measures a different code class than a return-type-mismatch probe, and "probe with a type-check code" is the correct operational advice rather than a preference.
  • require 'plugin' at line 15 confirms the runtime.plugin blind spot is real, not theoretical. A worker that loads plugins before analysis genuinely can be handed an OnSetText that blanks every file and still exit clean.

And the framing around the grep is the most valuable thing here. "Treat that as a floor, not a ceiling: its file scope is the shape of its blind spot" is the sentence that stops this section from being dangerous. A grep over script/core/diagnostics/*.lua enumerates diagnostic keys and by construction cannot surface a key consumed during file loading — so runtime.plugin is invisible to the very method offered for finding keys. Documenting the method and the class of thing it cannot find is what makes it safe to hand to someone else.

The enableScheme note is the same discipline at smaller scale: {} being inert for neededFileStatus/groupFileStatus but [] silencing everything for enableScheme is precisely the kind of asymmetry that a type-based intuition gets wrong. Confirmed it is declared as ["file"] at .luarc-typecheck.json:38.

In-repo claims verified

  • src/crypto/annotations.lua exists with zero requires of it. That is what makes "workspace-wide; not require'd" worth stating — it looks like dead code and is not.
  • typecheck runs --check "$(CURDIR)", the whole repo, so "a narrower scope gives different findings, not fewer" is right and is the reason the previous point matters.
  • check: format-check lint typecheck and all: format lint test build.
  • make help really is stale, verbatim: @echo " make check - Run format-check and lint" sits directly under a target that also runs typecheck. Worth fixing in the Makefile at some point, but documenting it is the right call for now, since help is what someone reads first and it is currently lying.
  • tools/generate_srp_vectors.py, run_benchmarks_matrix.sh, and the CI jobs (check, test, openssl-matrix) all exist as described.

Nothing outstanding.

@derek-miller

Copy link
Copy Markdown
Contributor

Approved with nothing outstanding — please merge.

The lua-language-server claims were the ones I most expected to drift, so I checked them against tag 3.19.0 itself: check_worker.lua:15 is require 'plugin', and downgrade_checks_to_opened at :163 guards on status == 'Any' or status == 'Any!', which is exactly what makes "probe with a type-check code" the right advice rather than a preference. Verified.

Separately, and not blocking this: make help still describes check as "format-check and lint" while the target also runs typecheck. Documenting that is the right call for now, but the Makefile line is worth fixing at some point since help is what someone reads first.

@svc-finitelabs
svc-finitelabs Bot merged commit f886c04 into main Aug 11, 2026
11 checks passed
@svc-finitelabs
svc-finitelabs Bot deleted the agent/AGENT-44-claude-md-audit branch August 11, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant