Research: Owned issue-verdict authority (#6320)

This mission's discovery was performed by a grounding squad against current main before specification. Findings are recorded here; all are code-grounded (file:line verified).

Decision 1 — Reach the gate, not just placement

placement. do_issue_verdict calls resolve_for_write(root, slug, ISSUE_MATRIX) → mission_runtime.resolve_write_target_or_degrade before any write dir is computed; _mission_meta_exists reads read_primary_meta(repo_root, …) at the repository root, returns False for an owned-only mission, and with terminus_write=True raises fail-closed.

calls resolve_for_write. So this fix is strictly wider than #6023 and must make the shared write_target_degrade helper owned-aware.

the gate refuses first, before that code runs).

  • Decision: The load-bearing blocker is the fail-closed resolve_for_write gate, not
  • Rationale: acceptance-verdict (fixed by #6023) has no equivalent gate — it never
  • Alternatives considered: Only fixing the command's own _resolve_write_dir (rejected —

Decision 2 — Downstream surfaces are already owned-ready

(mission_runtime/resolution.py) and write_issue_matrix(..., owned=None) (specify_cli/tasks/issue_matrix.py) already accept the fact. The command and the gate chain simply drop it before it reaches them.

  • Decision: Reuse them rather than re-plumb. resolve_placement_only(..., owned=None)
  • Rationale: Minimizes new surface; the fix is a thread-through, not a redesign.
  • Alternatives considered: New owned-aware write path (rejected — redundant, violates C-001).

Decision 3 — Mirror the merged #6023 carrier pattern

success_false_envelope and LIFECYCLE_OWNED_TOPOLOGIES (specify_cli.core.owned_mission). Resolve ownership once in the command, derive mission_slug/feature_dir from the owned fact when present, else fall back to resolve_mission_handle.

dccf199f7a, red-first c85d75e064, docs b82b6a9649). Fixtures reuse: tests/integration/test_owned_acceptance_authority_6023.py, test_p0_repro_6023_owned_acceptance_verdict.py, the owned R/P/S checkout fixtures in test_explicit_checkout_commands.py / test_owned_pr_bound_finalization.py.

  • Decision: Reuse _owned_checkout.OwnedCheckoutOption / resolve_owned_or_refuse /
  • Rationale: #6023's acceptance_verdict.py is the proven template (commits a6f8add16f,
  • Alternatives considered: Bespoke owned resolution (rejected — C-001).

Decision 4 — Scope includes the coord-owned read path

_migrate_if_needed as well as the write path (operator scope decision include-coord-owned-read, DM-01M55KHFFMTF0N8FE1TWG3BEZ5).

None and the owned write dir is what matters), but an owned coord mission must not silently read the repository root. The single_branch fallback to feature_dir must not regress.

  • Decision: Thread owned through coord_read_dir_for / _resolve_read_dir /
  • Rationale: #6320's headline case is single_branch (where coord_read_dir_for returns
  • Alternatives considered: Write-only (mirror #6023 exactly) — declined by operator.

Decision 5 — Shared-helper backward-compat is a hard invariant

(decision_log._resolve_default_target, bookkeeping_commit._resolve_bookkeeping_commit_target, status_transition._resolve_write_target). The new owned param defaults None and those callers stay byte-identical (FR-005), with a unit test proving it.

write targeting.

  • Decision: assert_coord_write_materialized has three other callers
  • Rationale: Prevents collateral change to decision-log / bookkeeping / status-transition

Supply-chain / dependencies

No dependency added, upgraded, or removed. DIRECTIVE_051 / supply-chain-install-safety controls are not applicable to this mission (no install surface touched). Recorded here to satisfy the plan's supply-chain disclosure: silence would be a gap; this is an explicit N/A.

Adversarial evidence

No security-impacting dependency decision. The grounding squad's findings were re-verified against HEAD before specification; no contested finding was dropped.