Skip to content

ci_resolve_dirty binds merge.roadmap.driver to a path no .gitattributes selects — the id-keyed roadmap driver never runs on a real merge #3343

Closed as not planned

Description

@noahgift

The id-keyed roadmap merge driver is bound but never selected, so every branch still conflicts on roadmap.yaml by line

Found while resolving #3297, which gh pr update-branch refused with "Cannot update PR branch due to conflicts".

scripts/lib/roadmap_merge.py is a 3-way merge driver that merges docs/roadmaps/roadmap.yaml by entry id. Re-running the exact merge through it produced 0 conflicts where plain git produced 2 conflict hunks / 6 markers. It works.

It just never runs. scripts/ci_resolve_dirty.sh::write_attrs_override writes only

docs/audits/*.jsonl merge=union

into its attributes override, and no .gitattributes anywhere declares docs/roadmaps/roadmap.yaml merge=roadmap — not on the PR base, not on the branch, not on origin/main. So the resolver binds merge.roadmap.driver to a path that no attribute ever selects, git falls back to the line-based merge, and the driver is dead code on every real merge.

Its self-test (ci_resolve_dirty.sh --selftest row 9) is green because the fixture writes that attribute line into a throwaway repo itself. A driver whose only exercise writes its own selector is the same anti-theater class as a gate that cannot fail — and a .gitattributes merge driver is retroactively inert anyway for branches cut before it lands, which is why the binding has to be per invocation.

Resolving #3297 required supplying the missing line by hand:

docs/roadmaps/roadmap.yaml merge=roadmap
docs/audits/*.jsonl merge=union

Ask

  1. write_attrs_override emits the roadmap.yaml merge=roadmap line too.
  2. A self-test row that does not write the attribute itself: run the resolver on a repo whose .gitattributes is exactly what the repo ships, and assert the driver engaged (e.g. by a conflict-free merge of two branches that both add an entry).
  3. A row that proves the negative: with the attribute absent, the merge conflicts — so the row can tell "driver ran" from "git got lucky".

Mechanism, for the record: two PRs that both add a roadmap entry conflict by line even though the entries are independent, which is how PRs land in DIRTY and stay there.

Refs #3297, #3308

Activity

  1. modified the milestones: 0.68.0, 0.70.0, 0.73.0 on Sep 16, 2026
  2. noahgift commented on Sep 23, 2026

    @noahgift
    MemberAuthor

    triage 2026-09-23 (aprender-dd, standing triage owner): milestoned to 0.70.0, theme Fast Train (CI, gates, pv, release tooling, hygiene); it moved out of backlog (the 2026-09-17 overflow bucket, not a release).

  3. added
    kind:triageEpic, tracker, decision or hygiene work (derived rule, #4159)
    kind:codeWork is a code change (derived rule, #4159)
    and removed
    kind:triageEpic, tracker, decision or hygiene work (derived rule, #4159)
    on Sep 24, 2026
  4. modified the milestones: 0.70.0, backlog on Sep 24, 2026
  5. noahgift commented on Sep 25, 2026

    @noahgift
    MemberAuthor

    Parked by APR-EPIC-001: over S3 budget (rank 142/142). Reopen = pull. Applied by aprender-d1: claude+agy both said close, apr shadow row recorded (handoff/close-quorum-2026-09-25.json).

  6. added
    parkedClosed over epic budget (APR-EPIC-001 rule 5). Reopen = pull.
    on Sep 25, 2026
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

    kind:codeWork is a code change (derived rule, #4159)parkedClosed over epic budget (APR-EPIC-001 rule 5). Reopen = pull.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions