Section 07 — commit-push Redesign (Path-Scoped)
Cites §01 north star (invariants 7, 8, 18); grounded in Pass 1D/N2 (ScopeRestriction already exists). The engine (§06) invokes the path-scoped-commit capability defined here.
Intelligence Reconnaissance
Run 2026-05-29:
scripts/intel-query.sh dag-ascii scripts-first-restructure— §07 linear-walk position §06 → §07 → §08;depends_on: ["06"](sole direct predecessor; §06 complete + reviewed).scripts/intel-query.sh plan-status scripts-first-restructure— §06status: complete; §07in-review; §08-§12not-started.- Remaining bullets are direct file citations (wrapper Python is not reliably indexed in the intel graph, so they anchor on the source file, not a
file-symbolsquery): scripts/plan_orchestrator/gates.py:273load_plan_touches(plan_dir, repo_root) -> list[str] | None— the touches read-site §07.1 wires; returnsNoneon absenttouches:(gates.py:310-313), falling through to wrapper-root default.scripts/commit_push/triage.py:202compute_scope_restriction(repo, *, since_sha, exclude_paths, from_orchestrator_scope)— has NO touches/include_paths param;ScopeRestriction.include_paths(triage.py:97) is populated internally fromfrom_orchestrator_scopeJSONsession_touched_paths(triage.py:240-245). §07.1 bridgestouches:into the inclusion filter.scripts/commit_push/staging.py:157stage_and_commit_all(..., scope_restrictions=...)raisesHookFailure(staging.py:57, carries.repo/.halt_reason/.stderr) on gate failure; returnslist[CommitResult](staging-sideCommitResultatstaging.py:48, distinct from route-sidecommit_capability.CommitResultat:94) on success.scripts/plan_orchestrator/commit_capability.py:135CommitBackendprotocol (commit(scope: CommitScope) -> CommitResultat:144); route-sideCommitResult.__post_init__(:117) enforces ok=False ⇒ halt_reason+error_payload set, ok=True ⇒ commit_sha set. §07 wires the real backend (:168comment).lefthook.yml:77bug-tracker-render-hookrunsrender --write-back && git add— a live mutating-and-staging hook, the INV-17-banned pattern §07.2 audits.
Goal
See frontmatter. Two changes, both cutting parallel-work friction:
- 07.1 — path-scoped staging.
- 07.2 — structural-gate removal (JSON-v6 nodes only; legacy markdown retains gates until §09/§10 close).
07.1 — Path-scoped staging (wiring, not new machinery)
- Author
touches:onplans/scripts-first-restructure/00-overview.mdfrontmatter (the touches SoT this plan currently lacks): list the plan-dir +scripts/path-set the route walk owns, soload_plan_touchesreturns a non-empty list instead ofNone. Without this,scripts/plan_orchestrator/gates.py:load_plan_touches(gates.py:310-313) returnsNoneon absenttouches:and the scope restriction falls through to the wrapper-root default (whole-tree staging). The per-node touches source is the active plan’s overview frontmatter — pin it here as the touches read-site contract. DONE:touches: [plans/scripts-first-restructure/**, scripts/plan_orchestrator/**, scripts/commit_push/**]on 00-overview.md;load_plan_touchesreturns the non-empty list. - Author
scripts/commit_push/scoped_commit.py: read the active plan’stouches:viascripts.plan_orchestrator.gates.load_plan_touches(plan_dir, repo_root)(scripts/plan_orchestrator/gates.py:273; returnslist[str] | None), then BRIDGEtouches:into the restriction’s inclusion filter —compute_scope_restriction(scripts/commit_push/triage.py:202) has NO touches/include_paths parameter (include_pathsattriage.py:97is populated only fromfrom_orchestrator_scopeJSONsession_touched_pathsattriage.py:240-245), so EITHER constructfrom_orchestrator_scope={"session_touched_paths": list(touches)}and pass it tocompute_scope_restriction(...), OR buildScopeRestriction(include_paths=tuple(touches), ...)directly (the field exists attriage.py:97). Pass the resultingScopeRestrictiontostaging.stage_and_commit_all(..., scope_restrictions=...)(scripts/commit_push/staging.py:157,scope_restrictionsparam:166). Acceptance asserts a non-emptytouches:yields aScopeRestrictionwhoseinclude_pathsequals the touches set (no silent wrapper-root fall-through when touches is present). -
staging._build_staging_list(staging.py:103) already appliesapply_scope_restriction(triage.py:297) whenscope_restrictionis non-noop (staging.py:133-136) — wire it; no new staging code (N2). DONE:ScopedCommitBackend.commitfeedsscope_restrictions={str(repo): restriction}intostage_and_commit_all(the existing param atstaging.py:166); no new staging code added. - Implement the
CommitBackendprotocol (scripts/plan_orchestrator/commit_capability.py:135,commit(self, scope: CommitScope) -> CommitResultat:144) over the scoped-staging path:scoped_commit.pyprovides a concrete backend whosecommit(scope)runs thetouches:→compute_scope_restriction→stage_and_commit_allwiring and maps the outcome onto the route-sideCommitResult(commit_capability.py:94) so its__post_init__invariants (commit_capability.py:117: ok=False ⇒ halt_reason+error_payload both set; ok=True ⇒ commit_sha set) hold: wrapstage_and_commit_allintry/except HookFailure(staging.py:57) — onHookFailure hfreturnCommitResult(ok=False, halt_reason=hf.halt_reason, error_payload={"repo": hf.repo, "stderr": hf.stderr}); on the successlist[CommitResult](staging-side,staging.py:48) returnCommitResult(ok=True, commit_sha=results[0].sha). Never construct aCommitResultthat violates the post-init invariants.invoke_commit(touches, scope, *, backend)(commit_capability.py:148) then routes through this backend, fulfilling the §06.5↔§07 interface contract thecommit_capability.py:168“§07 owns the real backend” comment pins. No field additions toCommitScope(commit_capability.py:76) orCommitResult. - Dirty paths outside the active set are ignored — no whole-tree-clean halt. DONE:
test_touches_scope_stages_only_owned_pathsasserts an unrelatedcompiler_repo/...dirty path is excluded while owned paths are kept. - Author
scripts/commit_push/tests/test_scoped_commit.py: assert (a)touches:-derivedScopeRestrictionstages only owned paths and ignores an unrelated dirty path; (b) theCommitBackendreal backend returnsCommitResult(ok=True, commit_sha=...)on success andCommitResult(ok=False, halt_reason=..., error_payload=...)on failure (real-backend ok/failed parity mirroring §06.5test_commit_capability); (c) absenttouches:yields the wrapper-root fall-through that the §07.1 prerequisite task closes. DONE: 12 tests green. - Subsection close (07.1) — all
[x];status: complete.
07.2 — Remove structural gates; keep correctness gates
- Remove structural checks (non-linear, cyclic, routing-drift, budget) from the commit-push gate set ONLY for schema-validated JSON-v6 route nodes — enforcement moves to authoring time (schema makes bad structure unrepresentable, §03.1) + execution-time auto-cure (§06 cure_dispatch) per refinement / R1Q4. The removed
budgetis the STRUCTURAL section-budget; the INV-18 content-size cap re-homes to authoring + review-as-phase (§03 lint + §06.4 gate), NOT commit-push (perdecisions/02-content-as-context-unit.md). DONE:scripts/commit_push/structural_gate_selection.pySTRUCTURAL_GATES = {non_linear, cyclic, routing_drift, budget};structural_gates_apply(plan_dir)returns False for a JSON-v6 node. The markdown structural lefthook hooks (cross-section-closure-claim,budget-check) globsection-*.mdand a v6 plan dir has none, so they skip v6 by construction;test_cross_section_check_skips_v6_nodepins it. - Gate the removal on JSON-v6 coverage: legacy markdown plans not yet on JSON-v6 KEEP the legacy structural gates active, so a not-yet-migrated markdown plan never loses structural enforcement. The commit-push gate selects per node: JSON-v6 node -> structural gates off (schema + cure own them); legacy markdown node -> structural gates retained. DONE:
structural_gates_applydefaults to RETAIN (True) for legacy markdown + unresolvable plan_dir (fail-safe); the SSOT node-kind discriminator isscripts/plan_corpus/lazy_migration_lint.is_json_v6_plan(plan.json presence). The selector ships now defaulting to retain (every current plan is legacy markdown, so zero coverage hole); the corpus-wide ACTIVATION of the v6-off branch (when v6 + legacy plans coexist + skills consume them) is owned by §10 (corpus-wide migration), with §09 (skill rewrites) preceding it —test_structural_gates_retained_for_legacy_markdownpins the retain default. - Keep correctness gates unconditionally (both node kinds):
test_all, clippy, genuine lints, banned-msg. DONE:CORRECTNESS_GATESis disjoint fromSTRUCTURAL_GATES;correctness_gates_apply()is unconditional True;test_correctness_gates_apply_both_kindspins the disjoint partition. - INV-17 pure-gate hook audit (anchors success_criterion “Audit finds zero hook-body mutation”): author
scripts/commit_push/hook_mutation_audit.pythat scanslefthook.ymlcommit-time hook bodies (pre-commit / commit-msg / pre-push) for mutate-and-stage patterns (git add,--write-back,sed -i) and FAILS-on-detection with a regenerate instruction. Convert the known violatorlefthook.ymlbug-tracker-render-hookto a verify-only fail-on-stale check (render --checkrenders in memory + diffs; rejects with a regenerate instruction on drift; NEVER mutate+stage). DONE:render.pygainedcheck_by_subsystem+--check; the hook now runs... --split-per-section --check;audit_lefthookon the reallefthook.ymlreturns zero findings;test_hook_mutation_audit_*+test_bug_tracker_render_hook_no_longer_mutatespin it;--self-testgreen. - Author the structural-gate-removal test in
scripts/commit_push/tests/test_scoped_commit.py: assert (a) a JSON-v6 route node has structural gates off; (b) a legacy markdown plan retains the structural gate (no coverage hole); (c) correctness gates fire for both node kinds. DONE:test_structural_gates_off_for_json_v6_node,test_structural_gates_retained_for_legacy_markdown,test_correctness_gates_apply_both_kinds,test_cross_section_check_skips_v6_nodegreen. - Subsection close (07.2) — all
[x];status: complete.
Absorbed defects (bug-tracker triage 2026-05-26)
Subsumed by §07’s path-scoped staging (the whole-tree-clean / dirty_after_commit gate is removed; paths outside the active touches: set are ignored). Deliverable MUST resolve; tracker entry closed obe-via-absorb pointing here.
| Bug | Symptom subsumed |
|---|---|
| BUG-07-084 | parallel-session .review-checkpoints/ sidecars surface untracked at dirty_after_commit (orphan paths outside active set no longer fire the gate) |
07.R Third Party Review Findings
- None.
07.N Completion Checklist
- 07.1-07.2
[x]andstatus: complete. - All success criteria have
[x]checkboxes. -
pytest scripts/commit_push/tests/test_scoped_commit.pygreen (scope-restriction from touches:; unrelated dirty path ignored; absent-touches: fall-through; CommitBackend ok/failed parity; structural-gate removal for JSON-v6 node + retention for legacy markdown plan). DONE: 12 passed. -
python -m scripts.plan_corpus check plans/scripts-first-restructure/section-07-*.mdexit 0. -
/tpr-reviewpassed (final, full-section). DONE: plan-level/tpr-review(review-plan Step 6) converged 2 rounds clean across codex/agy/opencode + adjudicator re-verify (third_party_review.status: clean). Implementation is wrapper tooling (scripts/**) — per CLAUDE.md compiler-only carve-out the implementation/tpr-review+/impl-hygiene-reviewgate does not apply; SRP/SSOT verified inline (SSOT node-kind classifieris_json_v6_plan; no duplicatedplan.jsoncheck; render--checkreuses therender_bug_tracker_mdSSOT).
References
- Pass 1D/N2:
scripts/commit_push/staging.py(CommitResult:48,_build_staging_list:103,stage_and_commit_all:157),scripts/commit_push/triage.py(ScopeRestriction:77,compute_scope_restriction:202,apply_scope_restriction:297). - Touches read-site:
scripts/plan_orchestrator/gates.py:load_plan_touches(:273; returnsNoneon absenttouches:at:310-313) — NOT acommit_pushsymbol; §07.1 imports it cross-package. - §06.5 commit-backend contract:
scripts/plan_orchestrator/commit_capability.py(CommitScope:76,CommitResult:94,CommitBackend:135withcommit(scope)->CommitResult:144,invoke_commit:148;:168pins “§07 owns the real backend”). - R1Q4 (enforce at authoring + execution, not commit); §01 invariants 7, 8.
HISTORY
- 2026-05-29 — Autopilot auto-cure: review_plan_redispatch_loop reset_lost_dispatch (autopilot_run_id=72ece7c1a87349c08ab6dcbd6367019d); chain log recorded a phantom /review-plan dispatch but section frontmatter showed zero review evidence; chain counter reset and /review-plan re-dispatched (per state-discipline.md §4 Hard-abort terminal-state semantics + skill-control-contract.md §Autopilot Mode unified hook-failure continuation clause).