feat(pipeline): report when a re-ingest disagrees with the page on disk (#104) #110
Closed
mathias
wants to merge 1 commits from
agent/104-surface-merge-conflicts into agent/105-source-drift
pull from: agent/104-surface-merge-conflicts
merge into: :agent/105-source-drift
:main
:fix/pipeline-writes-158
:fix/edit-path-io-192
:fix/create-regular-188
:fix/guard-special-179
:fix/linkcheck-keeps-write-178
:fix/write-dir-redirect-173
:fix/tunnel-detect-174
:fix/pipeline-root-157
:w3/154-mcp-auth-wiring
:fix/tunnel-traversal
:test/in-brain-targets-171
:fix/brain-scan-root-157
:fix/graph-wiki-root-157
:fix/vectorstore-root-157
:fix/notes-root-157
:fix/search-root-157
:fix/brainwide-root-157
:fix/raw-symlinks-153
:feat/symlink-tripwire-157
:fix/rest-auth-149
:fix/123-brain-edit-envelope-check
:feat/106-page-aware-extraction
:fix/105-source-drift-on-main
:fix/104-surface-merge-conflicts-on-main
:agent/b1-compile-on-ingest
:agent/104-surface-merge-conflicts
:agent/105-source-drift
:agent/107-loud-unconfigured-alerting
:feat/brain-read-edit
:fix/capture-i1-refusal-names-target
:renovate/configure
:feat/consolidate-single-harness
:icebox/cmd-routing
:feat/brain-pending-promote
:fix/close-session-classification-refresh
:fix/capture-summarywriter
No Reviewers
Labels
Clear labels
agent/ready
blocked/client
blocked/supplier
blocked/upstream
risk/high
risk/low
risk/medium
size/l
size/m
size/s
size/xs
status:dispatch
status:dispatched
status:review
status:reviewing
type/chore
type/decision
type/docs
type/feat
type/fix
type/ops
type/research
type/test
Meets the agent-ready contract; authorizes unattended execution
Waiting on the client
Waiting on a supplier
Waiting on an upstream project or vendor
Irreversible, security-relevant, or control plane
Reversible, local, test-covered
Touches shared state, deploys or data
Too big - split it
Under two days
Under half a day
Under an hour
Maintenance, no behaviour change
Human decision required; never agent/ready
Documentation only
New capability
Corrects broken behaviour
Infrastructure, deploy, runtime
Investigate; output is a finding, not code
Test coverage only
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mathias/hyperguild#110
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #104 by a much smaller route than the issue proposed. Stacked on #109 (which is stacked on #103) — review order is #103 → #109 → this.
The simplification
#104 proposed calling brain-gardener's Judge from the ingest path. That needs either a shared Go module across two repos that don't share one, or an HTTP service that doesn't exist (brain-enrich is a CronJob) — and it puts an LLM call on the write path, where a timeout must neither fail the ingest nor silently mark nothing as contested.
None of that is needed to get the signal.
#103's asymmetry means that when a fresh extraction produces a different version of a section already on disk, the on-disk version wins and the model's version is discarded. That discard is the detection. The merge was already computing "the source now says something different about this" and throwing it away.
So: return it instead.
No model on the write path, no cue-word matcher, no cross-repo dependency, deterministic, and it lands in
log.mdfor free becauseAppendLogalready writes run warnings there.What this is not
Textual disagreement, not semantic contradiction. Two sections differing is not proof that one contradicts the other — it might be a rewording, an expansion, or the model being sloppy.
Deciding that is the Judge-gated
brain-enrichpass's job. It already runs hourly, already derivesOldClaimSpan, and is approval-gated by design (brain-gardener ADR-0001: audit autonomous, enrich gated). Detection here, adjudication there — and the detection is now free rather than a second LLM pipeline that would drift from the first.This also means I have not populated brain#21's
contestedfield. That pointer is for a note contradicting another note, established by a Judge. Setting it from a textual diff would put an unjudged claim in a field whose whole purpose is to mark judged ones.Why the signal matters on its own
Without it, "the source is unchanged" and "the source changed its mind and we ignored it" are indistinguishable from outside — and #109 makes the first case silent by design (skip, no work). Those two outcomes must not look alike.
Tests
Red-first, both levels.
MergeReportnames only the section that actually disagreed and reports nothing for identical content; the pipeline test asserts the on-disk version still wins and that the run names both the section and the page.Mergeis kept as a thin wrapper overMergeReport, so no existing caller changed.task checkexit 0, full suite green.Risk: LOW — additive reporting. The merge result is byte-identical to #103's; only the return signature gained a second value.
Superseded by #127: same commit, rebased onto #126 (which replaces #109) instead of force-pushing over this stale branch.
Created via git-mcp on behalf of @mathias
Pull request closed