Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions docs/architecture/C1-repair-job-authority.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,22 @@ mutation will advance, and the boundary must bind the two before it acts:
The boundary must bind that effective `HEAD` referent to the authorized repair
ref and refuse to commit if the worktree is detached, attached to the protected
parent, attached to any other ref, or its safe binding cannot be established.
- **`repair.push`.** The authorized repair ref identifies a source, not a
destination. The boundary must bind the push's **effective destination ref** to
the authorized repair ref and must not let a caller-selected destination refspec
redirect the push; the receiving/mutation side must fail closed if the effective
destination is the protected parent or cannot be proven to be the authorized
repair ref.
- **`repair.push`.** A push carries both a source and a destination ref, and the
authorized repair ref governs **both**. The boundary must bind the push's
**effective source ref** and its **effective destination ref** — each by its
effective ref-name referent, not by commit-object identity — to the authorized
repair ref, and must not let a caller-selected source or destination refspec
redirect either half. The source must be **present**: an absent source, the
deletion refspec `:refs/heads/…`, is not a `repair.push` at all but a
`branch.delete`, which is denied, so a destination that still names the repair
ref does not make it authorized. No alternate branch, tag, or commit-ish may
stand in for the authorized repair ref on either half. The receiving/mutation
side must fail closed if either effective half is the protected parent, is not
provably the authorized repair ref, or ceases to be between the check and the
push — the authorized source-to-destination relationship must hold through to
that consuming boundary, not only at an earlier pre-check. An ordinary
`refs/heads/repair:refs/heads/repair` push, whose effective source and
destination are both the authorized repair ref, remains authorized.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

*Operands that set direction without mutating a ref.* The obligation is not
limited to ref-mutating operations. `repair.change_request` mutates no ref, but
Expand Down
13 changes: 9 additions & 4 deletions src/domain/repair-job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,10 +524,15 @@ function endsWithDotLockSuffix(value: string, start: number, end: number): boole
* records, resolve the requested ref's effective ref-name referent against the
* actual repository — the terminal ref reached through a symbolic-ref chain, not
* commit-object identity, since a fresh repair branch may legitimately share the
* protected parent's commit OID — and bind the effective mutation target it will
* actually advance: the worktree's effective `HEAD` referent for a commit, and the
* effective destination ref for a push. It must **fail closed** — refusing the
* operation — if that effective mutation target is or dereferences to the
* protected parent's commit OID — and bind each effective identity the operation
* acts on to the authorized repair ref. A commit advances one mutation target: the
* worktree's effective `HEAD` referent, the ref it will actually advance. A push
* binds two effective identities in distinct roles — its destination ref, the
* receiving/mutation target the push advances, and its source ref, the input the
* push consumes to select what is sent — each of which must be the authorized
* repair ref, so an absent (deletion) or redirected source is refused. It must
* **fail closed** — refusing the
* operation — if any such effective identity is or dereferences to the
* protected parent, if it changes between comparison and update, or if it cannot
* be safely established; a resolve-then-act pre-check is not itself atomic, so the
* invariant is enforced at the boundary that actually consumes each identity — the
Expand Down
Loading