feat: end-to-end alignment — docs↔code drift gating, math over keyword heuristics, persistent memory#46
Merged
Conversation
…et Jaccard The one numeric family missing from forge's math inventory (cosine, MinHash, Beta posteriors, CUSUM, logistic regression all exist). Entropy backs the upcoming secret-detection upgrade; setJaccard backs exemplar similarity where MinHash estimation is unnecessary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
…for JS and shell
src/secrets.js now owns detection AND redaction: the historical format grammars
(unchanged semantics, SECRET_RE still exported) plus a Shannon-entropy gate that
catches unknown-vendor credentials the format list can never enumerate. Every
refusal site (ledger mint, recall/lesson persist, adjudicate both directions,
diagnose traces, ledger verify) now calls hasSecret().
global/guards/secret-redact.sh imports the same module via node instead of
keeping a narrower divergent sed regex — the shell guard and the JS refusal
sites can no longer disagree (sed remains only as a degraded fallback when the
source tree is unreachable). Parity pinned by tests.
Precision invariants preserved: bare English mentions ("implement password
hashing") still pass; hex digests/UUIDs/identifiers are exempt by construction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
The text-complexity rubric is now similarity-weighted k-NN regression over a labeled exemplar bank (50 example tasks with target complexities): overlap- coefficient similarity on stopword-filtered, plural-folded unigram+bigram sets, credibility-shrunk toward a prior, plus a bounded structural term (length, code fences, constraint/step counts). Replaces the four hand-tuned ALGO/ARCH/ MODERATE/TRIVIAL keyword regexes and their additive magic weights. Why: a keyword list needs the literal token — "two threads deadlock when the queue is full" scored zero on the old rubric because neither "race condition" nor "mutex" appears. k-NN scores it by resemblance to labeled neighbors, and every score stays attributable (the neighbors are returned with the estimate). The exemplar bank is data: add rows to improve coverage, no weights move. strongSignal (the LLM lower-bound floor) now means "confidently matched a hard exemplar" rather than "contains an algorithmic keyword". All existing routing behavior contracts hold (same tier cutoffs, same band rails, same free-raise/ bounded-lower reconcile). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
- lessons.matchScore keyword tier is graded (0.3 × token-overlap coefficient): an exact keyword keeps its historical 0.3, a same-module partial match earns partial credit instead of the old all-or-nothing string equality. - anchor.goalDrift returns driftScore in [0,1] (off-goal fraction per checkpoint), the graded magnitude cusum() was designed to accumulate but never received. - substrate.minimalityWarnings no longer keeps its own keyword copies: warnings derive from the preflight DIMENSIONS rubric (missing target_scope/constraints) and the route score already computed in the same gate. - skillgate gains an entropy-based obfuscation detector: a 200+ char base64-class blob at >=4.5 bits/char flags as a possible obfuscated payload — signatures catch known attack shapes, entropy catches the unknown one hiding as a blob. - providers: isLikelyGateway documented as a prior; providerStatus now probes /health on any custom base URL and reports behavioral gateway evidence, so a proxy the URL vocabulary missed still gets classified by how it behaves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
…mpact graph New src/docs_check.js reconciles documentation CLAIMS against the sources of truth that already exist in code, so a feature can no longer merge with its docs silently missing: - commands: README/GUIDE vs the commands.js table (extracted from cli.js so --help and the docs check render from the same data) - env vars: prose docs vs actual process.env reads in src/*.js AND $VAR reads in shell guards; both directions (undocumented reads + phantom vars) - MCP tools: doc counts and tool names vs the cortex_mcp TOOLS registry (now exported) - CHANGELOG: latest header matches package.json, no empty release sections, [Unreleased] can't stay empty while src commits accumulate Wired in three places: new `forge docs check` command (exit non-zero), a doctor check that runs only inside the forge repo itself, and a docs-drift CI job. scripts/bump.mjs now refuses to rotate an empty [Unreleased] — the exact mechanism that produced two empty release sections. Atlas now parses markdown: each doc becomes a graph node with references edges to the code it names (backticked paths/symbols, link targets), so `forge impact src/foo.js` lists the DOCS that go stale — the missing code→doc half of end-to-end propagation. First run of the checker found 56 real drift issues including a phantom env var (FORGE_BRAND) no audit had caught. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
Three fixes for cross-session amnesia and silent drift: - src/goal.js + `forge anchor set/show/clear`: the active goal now persists in .forge/goal.md. SessionStart re-injects it, and a bare `forge anchor` checks drift against the stored goal — it used to live in one command's argv and vanish with the session, so every new session re-assumed the objective. - sync.autoSyncIfDrifted(): the Stop hook now REPAIRS AGENTS.md drift instead of leaving it for doctor to report — lessons/facts learned in a session reach every AGENTS.md-reading tool immediately. Never adopts an unmanaged repo; kill switch FORGE_AUTOSYNC=0. - pre-edit hook: when the cached atlas knows docs that reference the file being edited (the new doc-reference edges), it says so before the edit — code changes carry their documentation with them instead of leaving it behind. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
Fixes every drift issue the new reconciler found (56 on first run): - README: forge config + forge docs rows in the Commands table; accurate 19-MCP-tools claim with a link to the full list; anchor set/show/clear noted. - GUIDE: new forge config section (auto-detection priority order, corporate gateway walkthrough); forge docs section; routing section rewritten for the exemplar k-NN rubric with honest sample output; persistent-goal docs; full 19-tool MCP table; complete environment-variable reference covering every var the code actually reads (kept honest by forge docs check in CI). - ARCHITECTURE: brand-token wording no longer reads as a phantom FORGE_BRAND env var; MCP layer described as the real 19-tool server. - ROADMAP: phantom LITELLM_GATEWAY_URL replaced with the real mechanism (litellm.config.yaml + ANTHROPIC_BASE_URL); OpenAI/Gemini detection moved from Shipped to Next (it is not implemented); version header current. - CHANGELOG: backfilled the empty [0.8.0] and [0.8.1] sections from git history and wrote [Unreleased] covering the gateway work and this change. - CLAUDE.md: docs check added to the pre-commit gate; benchmarks wording updated for the exemplar rubric. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
An 8-angle review of the branch surfaced verified defects; all fixed with
regression tests:
Secret detection (the critical class — false positives corrupted tool output):
- TOKEN_RE no longer includes '/', so file paths split into segments instead of
fusing into one high-entropy token (src/components/UserProfileCard2/index.js
was detected as a secret and redacted to '[REDACTED].js').
- The entropy gate additionally requires >=3 scattered digit runs: long camelCase
identifiers with a lone digit clear 3.9 bits/char (measured), random keys have
scattered digits — the discriminator entropy alone could not provide.
- Redaction of assigned values is now narrower than detection: only quoted or
opaque-token values mask, so reading 'const token = jwt.sign(payload, key)'
is never mangled. Detection stays broad for store refusals.
- PEM masking is case-insensitive and tolerates truncated headers.
- The guard prefilters with grep before spawning node, and the node reader uses
setEncoding('utf8') so multibyte chars split across chunks can't corrupt output.
Routing:
- strongConf 0.5 -> 0.35 plus short-phrasing exemplars (deadlock in a worker
pool, race condition in a queue, back pressure, schema migration): the LLM
lower-bound floor holds again on every review counterexample.
- Prose sequence markers ('and then', 'after that') count toward nSteps.
- Exemplar gram footprints precomputed at module load (hooks route per prompt).
Lessons: generic path tokens (src, js, index, ...) are excluded from keyword
matching — a lesson keyed to src/auth/login.js no longer matches every .js edit.
Preflight/substrate: dead regex alternatives fixed ('scal' never matched
'scalable'); authentication/payment/billing now imply the constraints dimension;
the minimality warning is worded to what the signal means; completeness guarded.
Structure: MCP tool registry moved to src/mcp_tools.js (pure data), breaking the
doctor -> docs_check -> cortex_mcp -> doctor import cycle; docs_check scans src/
recursively and follows BRAND.cli; generated/churning docs (AGENTS, CHANGELOG)
excluded from the atlas so auto-sync can't perpetually re-stale it; DOC_EXTS
replaces the empty-array sentinel; the pre-edit hook strips the root prefix only
at a path-separator boundary; new user-facing strings use brand tokens; dead
math exports removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
- test/math.test.js assembles its high-entropy fixture at runtime (the repo's _fixtures.js pattern) so no credential-shaped literal exists for gitleaks to flag — applied through the branch history, which stays clean end to end (the repo deliberately carries no scanner allowlist). - goal.js getGoal slices at the first provenance comment instead of regex-stripping comment blocks (CodeQL js/incomplete-multi-character- sanitization: a replace can leave a partial '<!--' behind). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019PXuKmJp92Gdo2FudNjSx2
3acf75b to
1693318
Compare
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 & why
Three systemic problems, fixed at the root and gated so they can't recur:
1. Docs and code were misaligned, and nothing noticed. A full audit found 56 real drift issues: the entire
forge configcommand family undocumented, 18 env vars read but documented nowhere, a phantom env var in ROADMAP (LITELLM_GATEWAY_URL— never existed in code), MCP tool counts disagreeing across four docs (5 vs 15 vs 3 vs actual 19), and two completely empty CHANGELOG release sections. This PR fixes all of them and addsforge docs check(new CI job + doctor check): commands, env vars, MCP tool names/counts, and CHANGELOG are now machine-reconciled against the code — the sources of truth are thecommands.jstable, actualprocess.envreads (src + shell guards), and themcp_tools.jsregistry.bump.mjsnow refuses to rotate an empty[Unreleased].2. Hardcoded regex made semantic decisions. Every keyword-list decision is replaced with math (data tables like exemplars stay data):
src/secrets.jsis the single source of truth — format grammars + a Shannon-entropy gate (newsrc/math.js) for unknown-vendor tokens. Thesecret-redactguard imports the same module, ending the JS/shell regex divergence.goalDriftreturns a gradeddriftScore— the magnitude the existing CUSUM detector was designed to accumulate but never received./healthfor behavioral gateway evidence.3. Changes didn't propagate end-to-end, and sessions forgot.
referencesedges —forge impact src/foo.jslists the docs that go stale, and the pre-edit hook says so before the edit.forge anchor set/show/clearpersists the active goal in.forge/goal.md; SessionStart re-injects it every session.FORGE_AUTOSYNC=0to disable) — drift used to be detected by doctor and repaired by nobody.Adversarial review before push: an 8-angle review (line-by-line, removed-behavior, cross-file, reuse/simplification/efficiency, altitude, conventions) surfaced 10 verified defects in the new code — most critically, entropy false positives on file paths/camelCase identifiers that would have corrupted tool output, and a lost routing floor on short concurrency phrasings. All fixed with regression tests in the final commit.
Checklist
npm testpasses (Node 18/20/22) — 543 tests (was 500), 0 failuresnpm run checkpasses (Biome lint + format)feat:/fix:/docs:…)CHANGELOG.mdupdated under## [Unreleased](and the empty 0.8.0/0.8.1 sections backfilled)forge substrate,forge impact, router/gate, or MCP substrate tools — GUIDE routing/config/anchor/MCP/env sections rewritten;forge docs checkpasses clean on this tree (dogfooded)Risk & rollback
Extra checks (tick if applicable)
npm run typecheckpassesFORGE_DEBUG=1)Generated by Claude Code