tools: correctness-focused Racket hygiene lints (pnet registration, fire-fn capture, memo-hash) + lint gate - #81
Draft
kumavis wants to merge 8 commits into
Draft
Conversation
…ation registration Static guard for pipeline.md § New AST Node step 6: every struct in syntax.rkt must appear in pnet-serialize.rkt's registration code, else cached module bodies deserialize it as a raw vector impostor that fails a distant struct match (the Numerics Q11 failure mode). Reads both modules via the module reader (comments stripped), compares struct names against pnet-serialize's code symbols, and flags NEW gaps against a baseline of the 133 pre-existing unregistered structs (latent debt, now tracked; the baseline should only shrink). --strict exits non-zero on new gaps (for hooks/CI); --save-baseline regenerates. Self-tested: injected probe struct is caught, clean tree passes strict. No tests: standalone read-only lint script, validated by direct run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
…e networks Static guard for propagator-design.md § Fire Function Network Parameter: a fire fn that reads/writes cells through a variable captured from the installation-time scope operates on a stale network, and BSP's merge of the returned network silently drops other propagators' writes (Track 2 Bug #2; BSP-LE 2B Phase 1a). Fire scopes are found two ways — lambdas inside installer-call arguments (net-add-propagator and friends, elab-add-propagator) and define/let bindings matching #px"fire" — then every net-cell-*/elab-cell-* call whose network argument is not bound anywhere inside the scope is flagged. Binder collection (lambda formals, define, let family, named let, for clauses, match patterns) deliberately over-approximates so flags are high-signal. Production tree scans clean (132 files, 0 findings; baseline empty). Self-tested against a fixture reproducing both documented bug shapes (for/fold-accumulator capture and installer-position anon lambda) — both flagged; the corrected twin is not. No tests: standalone read-only lint script, validated by fixture run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
Static guard for pipeline.md § New Memo / Cache Keyed on an AST Node (the GitHub #58 class): equal-hash-code is depth-bounded (~17 levels), so an equal-based (make-hash) memo keyed on expr trees collapses deep terms into a handful of buckets and degenerates to O(N^3) — while hiding behind a green suite. Line-based scan (comments count — that is where 'memoization' lives) for make-hash/make-weak-hash within +-2 lines of memo/cache/seen context, keyed by enclosing define name, gated against a baseline. The 7 baselined findings were each audited and are sound as-is: lsp/server.rkt state tables key on URI STRINGS (equal-based required), macros.rkt coercion-fn-cache keys on freshly-consed shallow pairs (equal-based required, depth ~2 so the depth bound is harmless), and pnet-serialize.rkt dynamic-ctor-cache keys on symbols (cold fallback path). No expr-tree-keyed equal hashes exist at HEAD; the lint gates new ones. No tests: standalone read-only lint script, validated by fixture run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
…ate 3 + CI job) Orchestrates the correctness-focused lints: the four custom lints run BLOCKING (baseline-gated — only NEW findings fail): lint-pnet- registration, lint-fire-fn-capture, lint-memo-hash, lint-parameters. raco review (if installed: raco pkg install review) runs REPORT-ONLY over the target files — unused identifiers/requires, shadowing — with require-ordering warnings filtered out (formatting, not correctness). Target selection: modified (default) / --staged / --all / explicit files. Total custom-lint wall time ~2s, pre-commit viable. Wiring: - tools/git-hooks/pre-commit gains Gate 3 (lint-hygiene.sh --staged) - .github/workflows/test.yml gains a lint job (custom lints block, review reports; no project compile needed — the lints are read-only) - check-parens.sh + pre-commit now resolve Racket portably ($RACKET env → project-standard macOS path → PATH), so hooks and CI work on Linux/web sessions too - .claude/rules/testing.md documents the gate at the ambient tier Note: the gate currently fails on main by design — lint-parameters --strict catches 2 parameters added since the last baseline save (current-check-fire-invariants?, current-residuation-enabled?); the follow-up commit resolves them. No tests: tooling/CI change; validated by direct runs in both passing and failing configurations (exit codes verified unpiped). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
…e entries out The new hygiene gate caught lint-parameters --strict failing at HEAD: current-check-fire-invariants? (propagator.rkt, Scheduler O(diff) S-b debug gate) and current-residuation-enabled? (global-env.rkt, PPN 4C Addendum 4B.5.a DQ4 residuation gate) were added after the last baseline save. Audit: both are #f-default flags that are only ever parameterize'd (test-scheduler-odiff.rkt / driver.rkt process-file path) — dynamic scope self-unwinds, no imperative mutation sites exist, so no cross-test leakage path; recorded in the baseline per the lint's resolution option 4. Regeneration also drops 6 entries that are no longer unclassified (current-schema/selection/session/strategy/process-registry + current-elaborating-name are since registered in test-support.rkt's parameterize blocks) and a hand-added dated section comment — the file is generated (its header says so); durable audit notes belong in commit messages. No tests: baseline data file only; verified by lint-parameters --strict going red -> green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
One Racket startup for the whole file set instead of one per file; --all drops from ~135 startups to 1 (full-tree gate ~2.5min, dominated by review's per-module analysis; --staged/default stay seconds since they only review changed files). No tests: tooling change, validated by timed --all run (exit 0, output identical modulo batching). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
The lint job's review sweep over all ~140 production files blew the job's 10-min timeout: raco review costs ~10-15s PER FILE on this tree's large modules (measured: zonk.rkt alone 16.7s; a full sweep is ~25 min). The blocking custom lints were green in 2.5s; only the report-only step overran. - lint-hygiene.sh: new --diff REF mode (merge-base when computable, REF itself in shallow CI clones) + LINT_REVIEW_TIMEOUT bound (default 240s, coreutils timeout when present) with an explicit TRUNCATED note. - test.yml lint job: PRs fetch the base sha and run --diff over exactly the PR's changed files (~seconds); push-to-main runs blocking lints only, review already reported per-PR. - pre-commit Gate 3: LINT_REVIEW_TIMEOUT=60 so commits stay fast; comment no longer claims review is ~1-2s. - testing.md: record the per-file cost + the capping/diff-scoping so the next session doesn't re-learn it. No tests: CI/tooling shell + docs only, no production .rkt semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
…n tests Antagonistic review of the PR's own tooling, every finding confirmed by a live probe before fixing: 1. fire-fn-capture missed the historical bug shape when the fire fn is a NAMED helper passed by reference (no "fire" in the name — the Track 2B discrimination-propagator form). New rule (c): any define whose name appears in an installer call's argument positions is a fire scope. Tree-wide findings stay 0 — pure coverage, zero noise. 2. memo-hash regex required a char after "make-hash", so '(make-hash' with its argument on the next line evaded the scan entirely. Plain bug, fixed with an EOL alternative. 3. memo-hash +-2-line context window missed the ordinary comment-block + blank + define layout. Widened to +-4; measured tree-wide cost: one extra finding, already covered by an existing baseline key. 4. lint-hygiene review filter dropped only 'should come before' — 'should come after' require-ordering noise leaked through (observed live on zonk.rkt). Filter now covers both spellings. 5. The pre-commit 60s review cap was FICTION on macOS (no coreutils timeout there — the primary dev machine ran uncapped). perl-alarm fallback (alarm survives exec; rc 142) with the same truncation note. Tests: the PR's original fixtures lived only in the session scratchpad — nothing pinned detection behavior. tests/test-hygiene-lints.rkt now runs each lint against committed fixtures (bug flagged / corrected twin clean / rule-c helper flagged / EOL memo flagged) plus a pnet-lint smoke test asserting struct discovery still sees 300+ structs. Fixtures are .rktl DELIBERATELY: precompile-modules! recursively raco-makes every tests/**/*.rkt and raco test would execute them — a .rkt fixture with unbound identifiers breaks both (verified live). Known limitation left standing (documented, by design): the over-approximate binder set means an unrelated in-scope binding of the captured name (e.g. a match pattern) suppresses a true positive — the false-negative-only direction keeps flag noise at zero. Validated: 6/6 via raco test; tree-wide strict runs green (0 NEW on all lints); batch-runner DEAD-WORKERS banner reproduces on an untouched known-good test in this container, i.e. environmental, not this change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Correctness-focused hygiene tooling for the Racket tree — the "lint rules" layer, deliberately skipping formatting and Typed Racket. Three new custom lints derived from this repo's own documented bug classes, an orchestrator that also runs the off-the-shelf
reviewlinter, and wiring into the pre-commit hook + CI.New custom lints (all baseline-gated: only NEW findings block)
tools/lint-pnet-registration.rktpipeline.md§ New AST Node step 6 (Numerics Q11)tools/lint-fire-fn-capture.rktpropagator-design.md§ Fire Function Network Parameter (Track 2 Bug #2, 2B Phase 1a)tools/lint-memo-hash.rkt(make-hash)memo on expr trees → depth-bounded hashing degenerates to O(N³)pipeline.md§ New Memo/Cache (GitHub #58)Each is a standalone read-only script (
--strict/--save-baseline), self-tested against fixtures reproducing the documented bug shapes (bugs flagged, corrected twins clean).Orchestrator + wiring
tools/lint-hygiene.sh— runs the three new lints + the existinglint-parameters --strictas blocking gates (~2s total), thenraco review(report-only, if installed) with require-ordering noise filtered out. Modes: modified (default) /--staged/--all/ explicit files.--staged);check-parens.sh+ the hook now resolve Racket portably ($RACKET→ macOS path → PATH), so they work in Linux/CI/web sessions too.lintjob (custom lints block; review reports; no project compile needed)..claude/rules/testing.md.Violation caught and fixed (separate commit, as per instructions)
The gate immediately caught
lint-parameters --strictred at HEAD:current-check-fire-invariants?andcurrent-residuation-enabled?were added after the last baseline save. Audited both —#f-default flags that are only everparameterized (dynamic scope self-unwinds; no imperative mutation sites), so no leakage path → re-baselined per the lint's own resolution flow. The regeneration also dropped 6 stale entries now registered intest-support.rkt.Discovered debt (baselined, not fixed here)
syntax.rktstructs are unregistered inpnet-serialize.rkt— each is a latent vector-impostor if it ever lands in a cached module body (theexpr-p*-if-narpattern). Now tracked intools/pnet-registration-baseline.txt; registering them is a follow-up that should be validated by the full suite.raco review --allreports ~1,800 correctness-class warnings tree-wide (mostly unused identifiers/requires) — report-only signal, worth an opportunistic sweep someday. Install once withraco pkg install review.Not included (deliberately)
Typed Racket (too heavy per request),
fmt(formatting),resyntax(rules lean stylistic; can be added later diff-scoped if wanted),raco check-requires(needs a compiled tree; review's unused-require warning covers the common case cheaply).Validation
Racket was installed in this session's container; every lint was run against the tree and against negative-test fixtures; the orchestrator was exercised in passing and failing configurations with exit codes verified;
raco make driver.rktcompiles clean post-change (lints are read-only, no production code touched). The full test suite was not run here — no production.rktsemantics changed.🤖 Generated with Claude Code
https://claude.ai/code/session_01GDBXgAcZS43bsR2p6WF1vi
Generated by Claude Code