fix(grounding): commit the body hash to Markdown, and make the reads that use it agree - #161
Merged
Yashasvi2229 merged 7 commits intoSep 1, 2026
Conversation
The value that answers "has this grounded code changed?" was captured at saveCurrentBaseline and written only into `_mex_grounded_source` inside .mex/graph.db. That index is gitignored and disposable by invariant -- `mex graph rebuild` is offered as a routine repair -- so the drift baseline did not survive a rebuild and never reached a teammate who cloned. The groundings still resolved, so nothing looked broken; they simply stopped being able to detect a body change. Add an optional `bodyHash` to the shared `Grounding` type and populate it at both graph-lane write sites from the hash saveCurrentBaseline already reads. Optional and additive because every grounding written before this lacks it, and a required field would turn each of those scaffolds into a parse error. `refreshGroundingBaselines` writes it in two cases only: backfill, when Markdown carries none -- which asserts nothing new, since the graph.db row written on the same line already holds exactly that value from exactly that moment -- and re-baseline, when an authorized `updateFingerprints` has just moved the identity signal too. A hash that merely differs is left alone: that difference is the drift, and overwriting it would erase the finding. `persistMovedGroundings` re-derives it on a confirmed rebind, alongside the fingerprint, because a symbol's name is part of its body. Two reads had to follow. The drift checker took the baseline only from graph.db, so after a rebuild it found none and reported nothing at all -- without this the field would have been inert for `mex check`, which is the command that actually reports drift. It now prefers the committed hash and falls back to the cache for groundings authored before the field existed. `isGroundingArray` already tolerated the key and is documented rather than widened; the round trip through writeGroundings needed no change. The graph.db row stays. It is now a cache of a canonical value, and it still carries the body text that drift review needs for a diff.
…graph.db Four cases, run against the thing that used to destroy the baseline: delete .mex/graph.db, rebuild it from source, then ask whether an edited constant is still reported as drift. Two of them fail on the pre-fix tree and two pass, deliberately. The two that fail are the fix. The two that pass are the backward-compatibility pins: a grounding with no committed hash still parses and resolves and simply reports nothing after a rebuild -- which is the defect stated as a test -- and the same grounding against a live index still detects drift from the cache, so an existing scaffold that never rebuilds behaves exactly as it did. The fourth checks the case that would have been easy to get wrong: a capture pass run over a grounding whose body has already drifted must not adopt the new body as the baseline. The difference is the finding.
…d at `groundingsUnverified` means "there were groundings and not one produced a verdict", and that has two causes: no graph was supplied, or one was and nothing could be compared against it. The notice asserted the first unconditionally, so a reader was sent to inspect a code graph nobody had looked at -- the same shape as the OperationLogPathError mislabelling, a message stating a cause nobody checked. Carry the discriminator. It has to be carried rather than inferred from the grounding diagnostics, because those are subject to the same bound as every other diagnostic and a truncated report would lose the evidence. The wording of the no-graph branch was also wrong, and that is the half that was costing something. `runValidate` builds its options from serviceOptions, which carries no graph, so wiki validate has never loaded one and this is the branch every CLI run takes. "No code graph in this checkout" is a claim about the repository that the command never checked, and it was printed at a user whose graph was fresh and 7 MB. It now says what was true of the pass and names `mex check`, which is the command that does resolve groundings. That wiki validate cannot check groundings on the CLI at all is a separate finding and is left as a residue; this commit only stops it lying about why.
… root
Found on a real migrated scaffold, and it makes the rest of this branch inert
for the only population that matters in practice.
`mex check` read `frontmatter.grounds_to` directly. A pre-wiki scaffold keeps
the key there, but once `wiki migrate` adopts a file as an entity, section
13.4 moves it under the `mex` map -- and the direct read then finds nothing.
Measured on a migrated scaffold with a fresh graph and sixteen groundings
across nine files: the root key parsed as null, `extractGroundings` found
four in one file alone, and `mex check` returned zero GROUNDING_ codes of any
kind. Not stale, not missing, not gone. Silent.
That is worse than a false positive, because a scaffold that checks clean is
one nobody looks at.
Two reads, both now going through `extractGroundings`, which resolves the key
path the same way the writer does so the two ends cannot disagree:
- the checker's own loop, which ran zero times;
- the `hasGroundings` gate in the drift pipeline, which decided the
grounding runtime was irrelevant and never opened it, so the checker was
not even constructed.
The frontmatter value stays as the fallback for a file that cannot be re-read
at that point, which is the only case the old path still covers.
Measured end to end on a copy of that scaffold, before and after. Both report
"16 captured, 0 skipped". Before: 0 groundings carry a body hash, and an edit
inside a grounded function body after a graph rebuild yields 0 issues. After:
16 carry one, and the same edit yields 2 GROUNDING_DRIFT -- two because that
function is grounded from two files.
… gap The pattern is the part of this round worth reusing: a value that answers "has this changed?" belongs in Git, the index copy is a cache of it, every reader must resolve the same key path as the writer, and a fix that ships only the write half looks complete while doing nothing. ROUTER records what now works and adds the known issue this round measured but did not fix: the Wiki CLI's serviceOptions carries no code graph, so validate cannot resolve a grounding and migrate's backfill never runs. Both degrade silently.
MALFORMED_GROUNDING is one code over six problems -- a missing node id, a missing fingerprint, a malformed node id, a malformed fingerprint, a non-canonical `file`, a bad `verifiedAt`, and finding 39's missing body hash. Remediation is attached to the code, and the registry text is written for the first two: "a grounding needs both a code-graph node id and a fingerprint". The body-hash warning has both. Reported on a real scaffold where all sixteen warnings carried that line, telling the reader to supply fields that were already in the file and never naming the one that was absent. It is the same shape as the notice fixed two commits ago: advice about a cause nobody checked. `remediation` is already a per-call override on `diagnostic()` and the call site simply never used it. One option, no new code, no new field, no change to the registry -- the other five cases keep the text that does fit them. The replacement names what is missing, says plainly that no amount of hand editing supplies it, and names the commands that do.
Yashasvi2229
marked this pull request as draft
September 1, 2026 20:31
…he fingerprint `setup-grounding-e2e` caught this on CI and it is the same coupling this field exists to break, committed by the writer that introduces the field. The re-baseline was gated on the fingerprint having changed. Editing a constant changes the body and leaves the fingerprint identical -- by construction, since the fingerprint is an identity signal over normalized structure. So on exactly the edit a body hash is for, the gate never opened, the new baseline was never written, and drift could never clear. `mex sync` would have reported the same drift forever after an agent had already repaired the prose. Authorization is the caller's `updateFingerprints`, which is what actually carries the intent: `mex sync` passes true after an agent pass and re-baselines deliberately; `mex ground` and setup pass false and must never overwrite a hash that differs, because that difference is the finding. The new tests now pass `updateFingerprints: false` explicitly. That is what `captureGroundingBaselines` normalizes a missing value to before it reaches this function, so the tests exercise the real command path instead of the looser default a direct caller gets.
Yashasvi2229
marked this pull request as ready for review
September 1, 2026 21:16
Yashasvi2229
merged commit Sep 1, 2026
00b054a
into
integration/human-team-memory-v1
7 checks passed
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 this fixes
The value that answers "has this grounded code changed?" was captured at
saveCurrentBaselineand written only into_mex_grounded_sourceinside.mex/graph.db— a file that is gitignored, disposable by invariant, and whosedeletion the product recommends as a routine repair. So the drift baseline did
not survive
mex graph rebuildand never reached a teammate who cloned. Thegroundings still resolved, so nothing looked broken; they simply stopped being
able to detect a body change.
bodyHashis now committed to Markdown alongsidenodeandfingerprint.The field alone would have been inert. Two reads had to change with it, and
the second was found only by running against a real migrated scaffold:
graph.db. After arebuild that row is gone and the branch reported nothing at all — not a
coarse verdict, nothing.
frontmatter.grounds_to, the root key. A scaffoldwiki migratehas adopted keeps the key under themexmap, so the loop ranzero times — as did the
hasGroundingsgate, which then never opened thegrounding runtime. On the scaffold this was found on,
mex checkhad neverchecked a single grounding, on any run, and reported clean.
Two diagnostics that named a cause nobody checked are also corrected.
Measured, on a real migrated scaffold
25 files, 27 entities, 16 groundings,
graph.dbfresh. Identical throwawaycopies, before and after:
bodyHashwiki validatewarningsgraph.db, rebuild, edit a grounded bodyGROUNDING_DRIFTGROUNDING_DRIFTBefore this branch the capture printed "16 captured, 0 skipped" and wrote
nothing durable.
Backward compatibility
Optional and additive — a required field would turn every existing scaffold into
a parse error. A grounding with no
bodyHashstill parses, still validates,still warns, and against a live index still detects drift from the
graph.dbcache, which remains as a documented fallback and still carries the body text
drift review needs for a diff.
A hash that merely differs is never overwritten. That difference is the
drift; re-baselining it silently would erase the finding.
Tests
Five new cases in
test/graph-grounding-body-hash.test.ts, each deletinggraph.dband rebuilding it — the thing that used to destroy the baseline.Two fail on the pre-fix tree and three pass, verified by restoring the base
files and re-running; the three that pass are backward-compatibility pins and
are meant to be insensitive to the fix. Run five times, 5/5 green each time.
Known-red, and not from this branch
release-performanceis failing onintegration/human-team-memory-v1already —the two most recent runs on the branch, including at
a9e20df, with 8runtime.maintenanceMs.*violations andassetViolations: []. Those are graphand wiki index maintenance timings; this branch touches drift reading and
grounding capture, not indexing. Not addressed here, and deliberately not
worked around: widening a budget is the same move as raising a timeout.
Scope left out
There is no shipped command that only captures baselines — the backfill is
reachable only through
mex graph ground,mex syncormex setup, each ofwhich launches an agent or is scoped to a new scaffold. For an existing scaffold
whose groundings are correct and merely lack a hash, there is no way to ask for
the one thing needed. Recorded rather than fixed; a new command surface was out
of scope.