Skip to content

shape-doctor.py answers COMPLIANT, exit 0, for a root in the middle of a merge (UU contracts/spec-pin.yaml, UU spec, a MERGE_HEAD, git commit refusing), because no row asks git for unmerged paths and recorded_gitlink answers an unmerged gitlink from HEAD, so a root an agent cannot commit is reported compliant, including after the git checkout --ours that a conflicted pin's NEXT names #424

Closed

Description

@brettheap

(lane openRepoShape-2, 2026-10-12T17:26Z)

Found by this lane's review of PR #358 (the fix for #291) at ebffb08, its finding F4. Pre-existing: the same on main, so the PR neither causes it nor makes it worse, but its new unmerged NEXT for a conflicted pin (git checkout --ours -- contracts/<role>-pin.yaml) leads straight into the state below. Reproduced on origin/main 92e29d9 (git 2.43.0, Python 3.12.3), local remotes only, a stub gh. Not claimed.

What is wrong. shape-doctor.py --root answers COMPLIANT, exit 0, for a root in the middle of a merge: unmerged index entries, a MERGE_HEAD, and a tree on which git commit refuses. Nothing in the doctor asks git whether the index has unmerged paths. The validators it runs read the working tree for files and recorded_gitlink (scripts/repo_shape.py:999) for a leg's commit, and recorded_gitlink deliberately answers an unmerged path from HEAD ("a conflicted merge holds only stages 1, 2 and 3 for the same path, and none of them is a commit anybody is about to make"). That is right for a validator and wrong for a verdict: the doctor reports on HEAD while the working tree is a merge nobody has finished, so pins and legs are both ok for a state that has no commit.

# $U: a checkout of openRepoShape at 92e29d9. $B: an assembly root cloned from a local scaffold; two new commits on its spec leg's remote, $NEW1 and $NEW2. Local; no network.
git -C "$B" checkout -q -b bump/a && python3 "$U/scripts/bump-leg.py" --root "$B" --leg spec --to "$NEW1" --local-remote-dir "$D/remotes"
git -C "$B" checkout -q main && git -C "$B" submodule update -q && git -C "$B" checkout -q -b bump/b && python3 "$U/scripts/bump-leg.py" --root "$B" --leg spec --to "$NEW2" --local-remote-dir "$D/remotes"
git -C "$B" checkout -q bump/a && git -C "$B" submodule update -q && git -C "$B" merge --no-edit bump/b
git -C "$B" status --short                                  # UU contracts/spec-pin.yaml, UU spec
python3 "$U/shape-doctor.py" --root "$B"                    # DRIFTED (1 leg(s) not at the pin), exit 1: pins and legs FINDING (the pin holds conflict markers)
git -C "$B" checkout --ours -- contracts/spec-pin.yaml      # one way to resolve a pin conflict
python3 "$U/shape-doctor.py" --root "$B"                    # COMPLIANT   (exit 0): manifest, pins, legs, placement all ok
git -C "$B" status --short                                  # still UU contracts/spec-pin.yaml, UU spec; .git/MERGE_HEAD present
git -C "$B" commit --dry-run                                # Unmerged paths:   (exit 1)
state of the root verdict git commit --dry-run
clean, on a bump branch (control) COMPLIANT, exit 0 nothing to commit
conflicted contracts/spec-pin.yaml and spec, markers in the pin DRIFTED (1 leg(s) not at the pin), exit 1 Unmerged paths:
the same after git checkout --ours -- contracts/spec-pin.yaml COMPLIANT, exit 0 Unmerged paths:
an unrelated tracked file conflicted (AA NOTES.md), with or without --ours taken COMPLIANT, exit 0 Unmerged paths:
git merge --no-commit, nothing conflicted, MERGE_HEAD present COMPLIANT, exit 0 All conflicts fixed but you are still merging

An agent that reads COMPLIANT (AGENTS.md "Checking a repository's compliance", item 1) reports a root compliant that it cannot commit.

What it should do. A root with unmerged index entries (git ls-files -u is not empty) is not COMPLIANT: a FINDING row of its own (the author names it, say merge), reason naming the unmerged paths (first few, then a count), NEXT git -C <root> status with the words "finish the merge (git add each path, then git commit) or abandon it (git merge --abort)", verdict INVALID (merge), exit 1 through verdict_for's catch-all (:4438, other = [row.id for row in rows if row.status == FINDING]), so no ladder rung needs an edit. It writes nothing and asks git read-only questions, like every row. Register it in CHECKS (:4242) for both kinds of root. A MERGE_HEAD with nothing unmerged (the last row) is the author's call: a note row (never moves the verdict) is the least, and a FINDING is defensible because the index then holds an uncommitted merge. The row should not be CANNOT ANSWER: an unfinished merge is a fact about their repository, not this checkout failing to ask.

How to test. In tests/test_shape_doctor.py, with its scaffolded-root harness (stage_unmerged already builds an unmerged index for three of its tests): the unmerged-pin state above, the unrelated-file state and a control that has committed the merge: the verdict is not COMPLIANT, the exit is not 0, the row names the path, and the committed merge gives COMPLIANT again. On head the first two fail with COMPLIANT, exit 0.

Not in this issue. What PR #358's unmerged NEXT should say for a leg pin (that is its own review's item). recorded_gitlink's fall-back to HEAD (right for a validator, see its docstring and #20).

Related: #20 (recorded_gitlink reads HEAD before the index); #291 and PR #358 (the NEXT that leads here); #205 and #211 (the neighbouring verdicts for a manifest that does not load and a family file beside an assembly root).

Lane: openreposhape-2 (openRepoShape-2)

Activity

  1. brettheap commented on Oct 12, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-3, session 43f4ac63-0116-41de-977d-afc23cdf7fdf@Eagle, 2026-10-12T19:29:14Z, for #424

    Logged in lanes/log/openRepoShape-3.md at 1896925e16edf31840d338ef0744840ea976a6f3.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#424:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#424"`, kept where the row or its body names `#424`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `424` as a whole token:
       none
    
  2. added this to the v1.0.0 milestone on Oct 12, 2026
  3. brettheap commented on Oct 12, 2026

    @brettheap
    ContributorAuthor

    (lane openRepoShape-3, 2026-10-12T19:51:50Z) A v1.0.0 scope notice. Brett Heap scoped openRepoShape's first release today: every procedure in AGENTS.md works end to end for ordinary repositories on Linux, macOS and Windows. The tag waits for every in-scope issue to close and a full test run to pass on all three OSes on the release commit. This issue is #424 (P2, parent #460): in scope, in the v1.0.0 milestone. Nothing changes in how it is worked. Known-limit issues stay open, without the milestone, under their area's parent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions