I1402 — A finding says which of six states it found, and the state that could hide a person's edit is no longer one of them
Derive a typed state for every projected record from two independent questions — has a person rewritten the generated region since it was posted, and has the canonical record moved since — and report the answer instead of collapsing both into one word.
DONE wave 1 · p1 · implementation profile · runs alone
Objective
Derive a typed state for every projected record from two independent questions — has a person rewritten the generated region since it was posted, and has the canonical record moved since — and report the answer instead of collapsing both into one word.
Why
The check reported `missing` and `body` and nothing else, and the second word hid a defect. The adapter advertises that it will not overwrite what a person wrote inside the generated region, and it decided that by comparing the remote's marker hash to the canonical hash: if they differed it returned early. So the protection was blind in exactly the case it is for. A person edits the region, the plan then moves, and the next --apply splices canonical text over their words without --force and without a word of warning. The marker records what the body was posted as, so the question was always answerable against the marker rather than against the canonical record.
Current state
region_edited() returned false whenever the canonical record had moved, so `edited` was unreachable in the common case. Findings about a remote record claiming an unknown canonical id were printed from inside a pipeline, therefore from a subshell, so the drift count never saw them and --check could not fail on one.
Desired state
insync, behind, edited, conflict, adopt and missing for a canonical record, and unmanaged for a remote one. --apply rewrites behind and adopt, and refuses edited and conflict without --force. Every finding counts toward the exit code.
Scope
- scripts/github-sync
- test/cases/45_github_projection.sh
Out of scope
- Distinguishing a record never created from one deleted on GitHub, which is not derivable from either side without a second store of what was created; `missing` states what is observable, which is that there is no counterpart now
- Resolving a conflict automatically
Dependencies
What waits on this
- I1403READY Backfill the projection once, deliberately, and record what it created
- I1404DONE The drift check is a gate, so a projection that stops being applied cannot stay silent
Acceptance criteria
- A person's edit is detected whether or not the canonical record has moved since, proved by a case for each
- A record whose region was edited and whose canonical record also moved is reported as conflict, and --apply refuses it without --force
- An issue claiming a canonical id this repository does not have is reported and counted, so --check fails on it
- Every state is proved by a case that builds the remote as a fixture, offline
Validation
- bash test/run.sh 45_github_projection
- scripts/github-sync --check
Evidence required
- states_distinguished
- edit_protection_holds
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| states_distinguished | test | bash test/run.sh 45_github_projection | six states proved against fixture remotes: insync, behind, edited, conflict, adopt, missing, plus unmanaged for a remote record claiming an unknown id; the unmanaged finding now counts toward the exit code | 3db47f7 |
| edit_protection_holds | test | bash test/run.sh 45_github_projection | an edited region is detected both when the canonical record is unchanged (edited) and when it has moved since (conflict); the second was unreachable before, so --apply would have overwritten a person's text without --force | 3db47f7 |
Risk
Low. Every distinction is derived from bytes both sides already carry.
Timeline
- started
- 2026-09-09T17:20:34Z
- verified
- 2026-09-09T17:20:41Z
- completed
- 2026-09-09T17:20:44Z
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I1402.yaml. Read it back with majordomus plan show I1402.