Skip to content

An UNTRACKED, non-ignored board or ledger is committed whole by the post-merge carry, operator content included — deliberately left unprotected by #618 #682

Description

@pbean

Recorded from #618 phase 4 so the tradeoff is written down rather than inferred from a filter.

The gap

WorktreeFlow._carried_artifact_rels builds clean_incoming_collisions' protected operand from paths.sprint_status and paths.deferred_work, restricted to paths git already tracks. An untracked, non-ignored artifact is therefore not protected, and the post-merge carry commits it whole — including anything the operator put in it.

Measured, sandbox story run, board written but never committed, operator marker appended mid-run:

dirty at merge : {'.../sprint-status.yaml': '??'}   (a stray: not in the incoming set)
summary        : done=1 paused=False escalated=0
git log        : f8279f01 chore(sprint-status): carry 1-1-a to done
marker in that commit = True

Same shape reproduced on an untracked ledger via _carry_harvested_deferrals (chore(deferred-work): carry harvested findings from ...).

Why it was left out

Protecting untracked artifacts was measured and rejected: with the board protected unconditionally, an untracked non-ignored board with no operator dirt anywhere ends every isolated run done=0 paused=True escalated=1. Every run, of every project that has yet to commit its board, halted at its first story — the unattended-halt class #460 and #618 exist to remove.

It also buys less than the tracked case does. The #618 hazard is committing a divergence from a baseline somebody else authored; an untracked artifact has no baseline, the orchestrator has been reading that exact file as its own all along, and committing it whole is how a non-ignored board first reaches git at all (#350's carry). The bytes are committed, not overwritten — nothing is lost, unlike the tracked case.

For completeness, the third shape: a gitignored artifact never reaches the question. dirty_paths does not report ignored files and git add refuses an ignored pathspec, so the carry degrades (board-advance-carry-uncommitted / harvest-carry-uncommitted) instead of committing.

What a fix would need

A discriminator between "the run's own artifact, first time into git" and "content the operator authored in that file" — which the pre-flight cannot see from dirty_paths alone, since both read ??. Options worth weighing:

  • Have the carry commit only when the run's own write actually changed the file, comparing against the pre-carry bytes rather than staging by pathspec.
  • Refuse only when the untracked artifact is dirty and the run has no pending write of its own for it.
  • Accept the current behavior and document it (the file is the orchestrator's; committing it is the point).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Robustness, enhancement, tests, or docs worth schedulingarea:engineOrchestrator engine and run lifecyclebugSomething isn't workingneeds-designAwaiting a maintainer design decision before code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions