ADR: stable control runtime vs dev runtime for Gitea MCP #615

Closed
opened 2026-07-09 19:15:45 -05:00 by jcwalker3 · 12 comments
Owner

Problem

Author/reviewer/merger/reconciler sessions have historically restarted, killed, or relaunched the Gitea MCP server from development worktrees, or edited the stable runtime checkout. That destabilizes the control plane used for real Gitea mutations and causes EOF, stale-runtime, and mixed-identity failures mid-workflow.

Goal

Establish a durable stable control runtime vs dev runtime policy:

  • Real workflow mutations use only the stable control runtime.
  • MCP server development and testing happens in isolated branches/ worktrees and optional dev/test MCP runtimes.
  • Promotion into the stable runtime is an explicit operator/release-manager action with recorded proof and rollback.

Policy (normative)

Stable control runtime

The Gitea MCP server used for real workflow mutations is the stable control runtime.

Forbidden for normal author / reviewer / merger / reconciler sessions

  • Kill the running MCP server process
  • Restart the MCP server process
  • Relaunch MCP from a development worktree
  • Edit files in the stable runtime checkout
  • Use an experimental/dev MCP server for real Gitea mutations

Development / testing

  • Develop and test MCP server code in isolated branches/ worktrees
  • When needed, use a separate dev/test MCP runtime (never the stable control runtime identity for production mutations)

Promotion (operator / release-manager only)

Promotion to the stable control runtime must record:

  • previous runtime SHA
  • promoted runtime SHA
  • source branch/PR
  • restart/reload method
  • health check proof
  • identity/profile proof
  • workspace/root proof
  • mutation capability proof
  • rollback instructions

Unhealthy stable runtime

If the stable MCP runtime is unhealthy, normal PR/review/merge work must stop until the runtime is restored or a controlled promotion/rollback completes.

Related docs

  • docs/mcp-namespace-health.md (#543) — client-namespace health; no PID kill for EOF recovery
  • docs/mcp-namespace-eof-recovery.md — reconnect-only recovery
  • docs/mcp-daemon-import-guard.md (#558) — sanctioned daemon only
  • docs/bootstrap-review-path.md (#557) — narrow controller bootstrap when live runtime cannot review itself
  • ADR allocator/control-plane observability (PR #614 / #613)

Acceptance criteria

  1. ADR exists under docs/architecture/ documenting this policy.
  2. Operator guide / runbooks cross-link the ADR.
  3. Normal sessions do not kill/restart/relaunch stable MCP or edit stable checkout.
  4. Promotion checklist fields are documented and used for real promotions.
  5. Unhealthy runtime → stop mutation workflows (fail closed).

Labels

workflow-hardening, mcp-health, stale-runtime, safety, documentation, type:guardrail, status:ready

## Problem Author/reviewer/merger/reconciler sessions have historically restarted, killed, or relaunched the Gitea MCP server from development worktrees, or edited the stable runtime checkout. That destabilizes the control plane used for real Gitea mutations and causes EOF, stale-runtime, and mixed-identity failures mid-workflow. ## Goal Establish a durable **stable control runtime vs dev runtime** policy: * Real workflow mutations use only the **stable control runtime**. * MCP server development and testing happens in isolated `branches/` worktrees and optional **dev/test** MCP runtimes. * Promotion into the stable runtime is an explicit operator/release-manager action with recorded proof and rollback. ## Policy (normative) ### Stable control runtime The Gitea MCP server used for real workflow mutations is the stable control runtime. ### Forbidden for normal author / reviewer / merger / reconciler sessions * Kill the running MCP server process * Restart the MCP server process * Relaunch MCP from a development worktree * Edit files in the stable runtime checkout * Use an experimental/dev MCP server for real Gitea mutations ### Development / testing * Develop and test MCP server code in isolated `branches/` worktrees * When needed, use a **separate** dev/test MCP runtime (never the stable control runtime identity for production mutations) ### Promotion (operator / release-manager only) Promotion to the stable control runtime must record: * previous runtime SHA * promoted runtime SHA * source branch/PR * restart/reload method * health check proof * identity/profile proof * workspace/root proof * mutation capability proof * rollback instructions ### Unhealthy stable runtime If the stable MCP runtime is unhealthy, **normal PR/review/merge work must stop** until the runtime is restored or a controlled promotion/rollback completes. ## Related docs * `docs/mcp-namespace-health.md` (#543) — client-namespace health; no PID kill for EOF recovery * `docs/mcp-namespace-eof-recovery.md` — reconnect-only recovery * `docs/mcp-daemon-import-guard.md` (#558) — sanctioned daemon only * `docs/bootstrap-review-path.md` (#557) — narrow controller bootstrap when live runtime cannot review itself * ADR allocator/control-plane observability (PR #614 / #613) ## Acceptance criteria 1. ADR exists under `docs/architecture/` documenting this policy. 2. Operator guide / runbooks cross-link the ADR. 3. Normal sessions do not kill/restart/relaunch stable MCP or edit stable checkout. 4. Promotion checklist fields are documented and used for real promotions. 5. Unhealthy runtime → stop mutation workflows (fail closed). ## Labels workflow-hardening, mcp-health, stale-runtime, safety, documentation, type:guardrail, status:ready
Author
Owner

Canonical Issue State

STATE: open — ADR tracker; enforcement-layer acceptance criteria being added
WHO_IS_NEXT: author
NEXT_ACTION: Implement #615 runtime mode/SHA reporting and fail-closed mutation gates
NEXT_PROMPT:

Implement issue #615 enforcement layer: add runtime mode (stable-control | dev-test | unknown) plus git SHA reporting to runtime context/health checks; add fail-closed mutation gates for dev-test-targeting-production, unknown runtime, dirty stable checkout, dev-worktree-launched runtime, unsafe process-root/workspace alignment, and post-flap namespaces not yet re-proven; require per-namespace post-transport-flap reproving; add the tests, the promotion runbook/helper, and the canonical [THREAD STATE LEDGER] examples per acceptance criteria items 6-11.

WHAT_HAPPENED: Runtime enforcement + proof requirements surfaced by the 2026-07-09 transport flap (#584) appended to the #615 ADR tracker.
WHY: The ADR/policy alone (#615) lacks runtime enforcement and proof requirements; the flap showed all gitea-* namespaces drop together and that author proof does not prove the other namespaces.
RELATED_PRS: none
BLOCKERS: none
VALIDATION: 2026-07-09 clean-binding proof recorded on #584 comment 9115
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — enforcement-layer acceptance criteria added

What is true now:

  • Issue state: open
  • Server-side decision state: server-side state changed
  • Local verdict/state: no server-side state changed before this comment
  • Latest known validation: 2026-07-09 clean-binding proof recorded on #584 comment 9115

What changed:

  • enforcement-layer acceptance criteria appended to the #615 ADR tracker

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: author
  • Required action: implement #615 runtime mode/SHA reporting and mutation gates
  • Do not do: open a new tracker — #615 is canonical
  • Resume from: issue #615 body, acceptance criteria items 6-11

Missing acceptance criteria — enforcement layer (from live incident 2026-07-09)

#615 covers the ADR/policy. Adding the runtime enforcement + proof requirements
surfaced by the mid-session transport flap tracked on #584. In that incident,
all gitea-* tools dropped and self-recovered. Workspace binding was clean
before the drop, so the observed failure was not a stale process-root defect;
it was a transport/stale-runtime fault.

Add to runtime context / health checks

Report:

  • runtime mode: stable-control | dev-test | unknown
  • runtime git SHA
  • runtime branch
  • runtime checkout path
  • MCP process root
  • active task workspace
  • repo/org binding
  • profile
  • authenticated identity
  • dirty files
  • workspace alignment
  • whether real mutations are allowed

Add mutation gates — fail closed when

  • runtime mode is dev-test and target is the real production repo
  • runtime mode is unknown
  • stable runtime checkout is dirty
  • stable runtime process was launched from a development worktree
  • process-root/workspace alignment is unsafe
  • transport recently dropped and the namespace has not been re-proven

Add post-transport-flap proof

After any MCP transport flap or daemon restart, each namespace must be independently re-proven before mutation:

  • author
  • reviewer
  • merger
  • reconciler

Required proof per namespace:

  1. whoami
  2. runtime context
  3. capability resolve immediately before mutation
  4. mutate only if no reconnect/restart/stale-runtime gate is reported

Author proof alone must not be described as global MCP reliability proof.

Extend acceptance criteria

  1. Runtime mode and SHA reporting implemented.
  2. Mutation gates added for dev-test, unknown, dirty stable runtime, unsafe process-root/workspace alignment, and dev-worktree-launched runtimes.
  3. Per-namespace post-flap reproving required.
  4. Tests added for:
    • stable healthy runtime allows real mutations
    • dev/test runtime blocks real production mutations
    • unknown runtime blocks mutations
    • normal workflow cannot restart stable MCP
    • transport recovery requires namespace-specific reproving
    • author proof does not imply reviewer/merger/reconciler proof
    • promotion records previous and promoted SHAs
  5. Explicit promotion runbook/helper added.
  6. Canonical [THREAD STATE LEDGER] examples added for:
  • runtime healthy
  • transport flap recovered
  • namespace not yet re-proven
  • promotion completed
  • rollback required

Link

Transport-flap symptom tracker: #584.

See #584 comment 9115 for the 2026-07-09 clean-binding proof.

## Canonical Issue State STATE: open — ADR tracker; enforcement-layer acceptance criteria being added WHO_IS_NEXT: author NEXT_ACTION: Implement #615 runtime mode/SHA reporting and fail-closed mutation gates NEXT_PROMPT: ```text Implement issue #615 enforcement layer: add runtime mode (stable-control | dev-test | unknown) plus git SHA reporting to runtime context/health checks; add fail-closed mutation gates for dev-test-targeting-production, unknown runtime, dirty stable checkout, dev-worktree-launched runtime, unsafe process-root/workspace alignment, and post-flap namespaces not yet re-proven; require per-namespace post-transport-flap reproving; add the tests, the promotion runbook/helper, and the canonical [THREAD STATE LEDGER] examples per acceptance criteria items 6-11. ``` WHAT_HAPPENED: Runtime enforcement + proof requirements surfaced by the 2026-07-09 transport flap (#584) appended to the #615 ADR tracker. WHY: The ADR/policy alone (#615) lacks runtime enforcement and proof requirements; the flap showed all gitea-* namespaces drop together and that author proof does not prove the other namespaces. RELATED_PRS: none BLOCKERS: none VALIDATION: 2026-07-09 clean-binding proof recorded on #584 comment 9115 LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — enforcement-layer acceptance criteria added What is true now: - Issue state: open - Server-side decision state: server-side state changed - Local verdict/state: no server-side state changed before this comment - Latest known validation: 2026-07-09 clean-binding proof recorded on #584 comment 9115 What changed: - enforcement-layer acceptance criteria appended to the #615 ADR tracker What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: author - Required action: implement #615 runtime mode/SHA reporting and mutation gates - Do not do: open a new tracker — #615 is canonical - Resume from: issue #615 body, acceptance criteria items 6-11 --- ## Missing acceptance criteria — enforcement layer (from live incident 2026-07-09) #615 covers the ADR/policy. Adding the runtime enforcement + proof requirements surfaced by the mid-session transport flap tracked on #584. In that incident, all `gitea-*` tools dropped and self-recovered. Workspace binding was clean before the drop, so the observed failure was not a stale process-root defect; it was a transport/stale-runtime fault. ### Add to runtime context / health checks Report: - runtime mode: `stable-control` | `dev-test` | `unknown` - runtime git SHA - runtime branch - runtime checkout path - MCP process root - active task workspace - repo/org binding - profile - authenticated identity - dirty files - workspace alignment - whether real mutations are allowed ### Add mutation gates — fail closed when - runtime mode is `dev-test` and target is the real production repo - runtime mode is `unknown` - stable runtime checkout is dirty - stable runtime process was launched from a development worktree - process-root/workspace alignment is unsafe - transport recently dropped and the namespace has not been re-proven ### Add post-transport-flap proof After any MCP transport flap or daemon restart, each namespace must be independently re-proven before mutation: - author - reviewer - merger - reconciler Required proof per namespace: 1. `whoami` 2. runtime context 3. capability resolve immediately before mutation 4. mutate only if no reconnect/restart/stale-runtime gate is reported Author proof alone must not be described as global MCP reliability proof. ### Extend acceptance criteria 6. Runtime mode and SHA reporting implemented. 7. Mutation gates added for dev-test, unknown, dirty stable runtime, unsafe process-root/workspace alignment, and dev-worktree-launched runtimes. 8. Per-namespace post-flap reproving required. 9. Tests added for: - stable healthy runtime allows real mutations - dev/test runtime blocks real production mutations - unknown runtime blocks mutations - normal workflow cannot restart stable MCP - transport recovery requires namespace-specific reproving - author proof does not imply reviewer/merger/reconciler proof - promotion records previous and promoted SHAs 10. Explicit promotion runbook/helper added. 11. Canonical `[THREAD STATE LEDGER]` examples added for: - runtime healthy - transport flap recovered - namespace not yet re-proven - promotion completed - rollback required ### Link Transport-flap symptom tracker: #584. See #584 comment 9115 for the 2026-07-09 clean-binding proof.
jcwalker3 added status:in-progressworkflow-hardening and removed status:readyworkflow-hardening labels 2026-07-20 09:18:27 -05:00
Author
Owner

Issue claim heartbeat

<!-- gitea-issue-claim-heartbeat:v1 --> **Issue claim heartbeat** - kind: claim - issue: #615 - branch: feat/issue-615-runtime-mode-enforcement - phase: claimed - profile: prgs-author - pr: none - blocker: none - next_action: create worktree and begin implementation
Author
Owner

Canonical Issue State

STATE: open — enforcement layer implemented locally; native publication unavailable this session
WHO_IS_NEXT: controller
NEXT_ACTION: Restore a native author publication path, then re-run the full work-issue workflow to publish branch feat/issue-615-runtime-mode-enforcement and open its PR
NEXT_PROMPT:

Operator/controller step first: give the gitea-tools (prgs-author) daemon a session worktree binding (GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE pointing at the branches/ worktree), or add a worktree_path parameter to gitea_commit_files so it resolves the locked issue worktree instead of the control checkout. Then re-run the full work-issue workflow for issue #615 from the beginning: fresh identity/profile/runtime/capability proof, fresh allocator and duplicate-work gates, re-verify the local commit content on branch feat/issue-615-runtime-mode-enforcement, publish through sanctioned MCP, and open the PR.

WHAT_HAPPENED: The #615 enforcement layer (acceptance criteria 6-11) was implemented and locally committed in the session worktree. Native publication through gitea_commit_files fails closed on the #274 branches-only mutation guard, so no Gitea branch or PR exists yet.
WHY: gitea_commit_files exposes no worktree_path parameter. Author workspace binding resolves only from the worktree_path argument, GITEA_ACTIVE_WORKTREE, or GITEA_AUTHOR_WORKTREE, none of which a session can supply to that tool, so the workspace falls back to the stable control checkout and the branches-only guard refuses the mutation.
RELATED_PRS: none
BLOCKERS: environment/tooling blocker — native author publication path unavailable (gitea_commit_files workspace binding)
VALIDATION: 2026-07-20 full suite in the session worktree: 3827 passed, 6 skipped, 2 failed; clean baseline worktree at master 059ee77c1f: 3780 passed, 6 skipped, same 2 failures (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue)
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — enforcement layer implemented locally; native publication unavailable

What is true now:

  • Issue state: open
  • Server-side decision state: no server-side state changed beyond the issue claim label and this comment
  • Local verdict/state: local commit 9bf3acf created on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement; no Gitea branch and no PR exist
  • Latest known validation: full suite 3827 passed / 6 skipped / 2 failed, with clean-baseline proof that both failures are pre-existing on master 059ee77

What changed:

  • issue #615 claimed through gitea_lock_issue and the status:in-progress label
  • enforcement layer for acceptance criteria 6-11 written and locally committed
  • native publication attempt through gitea_commit_files refused by the #274 branches-only mutation guard

What is blocked:

  • Blocker classification: environment/tooling blocker

Who/what acts next:

  • Next actor: controller
  • Required action: restore a native author publication path, then re-run the full work-issue workflow from the beginning
  • Do not do: publish with a direct git push, hand-edit the issue lock, or relaunch the MCP daemon from this development worktree
  • Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement

Author work record (#615 acceptance criteria 6-11)

What the change contains

stable_control_runtime.py (new)

  • classify_runtime_mode()stable-control | dev-test | unknown, inferred from process root and checkout branch, with an explicit GITEA_MCP_RUNTIME_MODE operator declaration for packaged layouts that have no git checkout (AC 6).
  • build_runtime_report() → runtime mode, git SHA, branch, checkout path, MCP process root, active task workspace, repo binding, profile, identity, dirty files, workspace alignment, real_mutations_allowed (AC 6).
  • assess_runtime_mutation_gate() → fails closed on dev-test targeting production, unknown runtime, dirty stable checkout, dev-worktree-launched runtime, and unsafe process-root/workspace alignment (AC 7).
  • Post-transport-flap re-proving tracked per namespace: a flap invalidates all four, and author proof never transfers to reviewer, merger, or reconciler (AC 8, #584).
  • assess_promotion_record() → promotion must record previous and promoted SHAs plus restart method, health, identity, profile, workspace, capability, and rollback proof (AC 4).

gitea_mcp_server.py

  • _profile_operation_gate() consults _runtime_mode_block() alongside the #420 parity gate. gitea.read is never blocked, so an operator can still inspect a sick runtime.
  • The gate reads a startup snapshot rather than shelling out per mutation, matching the #420 startup-baseline pattern: the runtime a process serves from is fixed when it loads its code.
  • Enforcement is decided from how the process was loaded, so a test that simulates production for another guard cannot switch this gate on.
  • gitea_get_runtime_context() reports the live runtime under stable_control_runtime and points at the promotion runbook when mutations are refused.

Docs and tooling

  • docs/stable-runtime-promotion-runbook.md (new): operator promotion procedure, the eleven required record fields, per-namespace re-proving, rollback (AC 10).
  • scripts/promote-stable-runtime (new): read-only helper that emits and validates a promotion record; it never restarts, reloads, or kills anything (AC 10).
  • Five canonical [THREAD STATE LEDGER] examples added: runtime healthy, transport flap recovered, namespace not yet re-proven, promotion completed, rollback required (AC 11).
  • ADR §5 follow-ups marked landed; runbooks cross-link the new runbook (AC 2).

Validation

  • tests/test_stable_control_runtime.py (new): 47 tests covering every AC 9 case — stable healthy runtime allows real mutations, dev/test runtime blocks real production mutations, unknown runtime blocks mutations, normal workflow cannot restart stable MCP, transport recovery requires namespace-specific re-proving, author proof does not imply reviewer/merger/reconciler proof, promotion records previous and promoted SHAs.
  • Full suite (session worktree): 3827 passed, 6 skipped, 2 failed, 493 subtests passed.
  • Clean baseline worktree at master 059ee77: 3780 passed, 6 skipped, 2 failed — identical failure signatures, so both are pre-existing and unrelated to this change.
  • git diff --check clean.

Blocker detail

gitea_commit_files refused with the #274 branches-only mutation guard: workspace is the stable control checkout. The tool has no worktree_path parameter, and resolve_namespace_workspace() accepts an author workspace only from a worktree_path argument, GITEA_ACTIVE_WORKTREE, or GITEA_AUTHOR_WORKTREE — none reachable from a session for this tool — so it falls back to the process project root and fails closed. There is no other native author publish/push tool. No fallback was attempted: no direct git push, no raw API, no daemon restart, no lock-file edit.

This is a pre-existing tooling gap, not a defect in the #615 change. It should be tracked in its own issue by a run started in issue-creation mode; this run is work-issue mode and does not mix modes.

## Canonical Issue State STATE: open — enforcement layer implemented locally; native publication unavailable this session WHO_IS_NEXT: controller NEXT_ACTION: Restore a native author publication path, then re-run the full work-issue workflow to publish branch feat/issue-615-runtime-mode-enforcement and open its PR NEXT_PROMPT: ```text Operator/controller step first: give the gitea-tools (prgs-author) daemon a session worktree binding (GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE pointing at the branches/ worktree), or add a worktree_path parameter to gitea_commit_files so it resolves the locked issue worktree instead of the control checkout. Then re-run the full work-issue workflow for issue #615 from the beginning: fresh identity/profile/runtime/capability proof, fresh allocator and duplicate-work gates, re-verify the local commit content on branch feat/issue-615-runtime-mode-enforcement, publish through sanctioned MCP, and open the PR. ``` WHAT_HAPPENED: The #615 enforcement layer (acceptance criteria 6-11) was implemented and locally committed in the session worktree. Native publication through gitea_commit_files fails closed on the #274 branches-only mutation guard, so no Gitea branch or PR exists yet. WHY: gitea_commit_files exposes no worktree_path parameter. Author workspace binding resolves only from the worktree_path argument, GITEA_ACTIVE_WORKTREE, or GITEA_AUTHOR_WORKTREE, none of which a session can supply to that tool, so the workspace falls back to the stable control checkout and the branches-only guard refuses the mutation. RELATED_PRS: none BLOCKERS: environment/tooling blocker — native author publication path unavailable (gitea_commit_files workspace binding) VALIDATION: 2026-07-20 full suite in the session worktree: 3827 passed, 6 skipped, 2 failed; clean baseline worktree at master 059ee77c1fd483da385d6ccb4d842e808fb54630: 3780 passed, 6 skipped, same 2 failures (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue) LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — enforcement layer implemented locally; native publication unavailable What is true now: - Issue state: open - Server-side decision state: no server-side state changed beyond the issue claim label and this comment - Local verdict/state: local commit 9bf3acf created on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement; no Gitea branch and no PR exist - Latest known validation: full suite 3827 passed / 6 skipped / 2 failed, with clean-baseline proof that both failures are pre-existing on master 059ee77 What changed: - issue #615 claimed through gitea_lock_issue and the status:in-progress label - enforcement layer for acceptance criteria 6-11 written and locally committed - native publication attempt through gitea_commit_files refused by the #274 branches-only mutation guard What is blocked: - Blocker classification: environment/tooling blocker Who/what acts next: - Next actor: controller - Required action: restore a native author publication path, then re-run the full work-issue workflow from the beginning - Do not do: publish with a direct git push, hand-edit the issue lock, or relaunch the MCP daemon from this development worktree - Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement --- ## Author work record (#615 acceptance criteria 6-11) ### What the change contains `stable_control_runtime.py` (new) - `classify_runtime_mode()` → `stable-control` | `dev-test` | `unknown`, inferred from process root and checkout branch, with an explicit `GITEA_MCP_RUNTIME_MODE` operator declaration for packaged layouts that have no git checkout (AC 6). - `build_runtime_report()` → runtime mode, git SHA, branch, checkout path, MCP process root, active task workspace, repo binding, profile, identity, dirty files, workspace alignment, `real_mutations_allowed` (AC 6). - `assess_runtime_mutation_gate()` → fails closed on dev-test targeting production, unknown runtime, dirty stable checkout, dev-worktree-launched runtime, and unsafe process-root/workspace alignment (AC 7). - Post-transport-flap re-proving tracked per namespace: a flap invalidates all four, and author proof never transfers to reviewer, merger, or reconciler (AC 8, #584). - `assess_promotion_record()` → promotion must record previous and promoted SHAs plus restart method, health, identity, profile, workspace, capability, and rollback proof (AC 4). `gitea_mcp_server.py` - `_profile_operation_gate()` consults `_runtime_mode_block()` alongside the #420 parity gate. `gitea.read` is never blocked, so an operator can still inspect a sick runtime. - The gate reads a startup snapshot rather than shelling out per mutation, matching the #420 startup-baseline pattern: the runtime a process serves from is fixed when it loads its code. - Enforcement is decided from how the process was loaded, so a test that simulates production for another guard cannot switch this gate on. - `gitea_get_runtime_context()` reports the live runtime under `stable_control_runtime` and points at the promotion runbook when mutations are refused. Docs and tooling - `docs/stable-runtime-promotion-runbook.md` (new): operator promotion procedure, the eleven required record fields, per-namespace re-proving, rollback (AC 10). - `scripts/promote-stable-runtime` (new): read-only helper that emits and validates a promotion record; it never restarts, reloads, or kills anything (AC 10). - Five canonical `[THREAD STATE LEDGER]` examples added: runtime healthy, transport flap recovered, namespace not yet re-proven, promotion completed, rollback required (AC 11). - ADR §5 follow-ups marked landed; runbooks cross-link the new runbook (AC 2). ### Validation - `tests/test_stable_control_runtime.py` (new): 47 tests covering every AC 9 case — stable healthy runtime allows real mutations, dev/test runtime blocks real production mutations, unknown runtime blocks mutations, normal workflow cannot restart stable MCP, transport recovery requires namespace-specific re-proving, author proof does not imply reviewer/merger/reconciler proof, promotion records previous and promoted SHAs. - Full suite (session worktree): 3827 passed, 6 skipped, 2 failed, 493 subtests passed. - Clean baseline worktree at master 059ee77: 3780 passed, 6 skipped, 2 failed — identical failure signatures, so both are pre-existing and unrelated to this change. - `git diff --check` clean. ### Blocker detail `gitea_commit_files` refused with the #274 branches-only mutation guard: `workspace is the stable control checkout`. The tool has no `worktree_path` parameter, and `resolve_namespace_workspace()` accepts an author workspace only from a `worktree_path` argument, `GITEA_ACTIVE_WORKTREE`, or `GITEA_AUTHOR_WORKTREE` — none reachable from a session for this tool — so it falls back to the process project root and fails closed. There is no other native author publish/push tool. No fallback was attempted: no direct git push, no raw API, no daemon restart, no lock-file edit. This is a pre-existing tooling gap, not a defect in the #615 change. It should be tracked in its own issue by a run started in issue-creation mode; this run is `work-issue` mode and does not mix modes.
Author
Owner

Canonical Issue State

STATE: open — enforcement layer implemented and revalidated locally; author ownership cannot be re-established
WHO_IS_NEXT: controller
NEXT_ACTION: Restore an author ownership path for an already-implemented worktree, and an author publication path for gitea_commit_files, then re-run the full work-issue workflow for #615
NEXT_PROMPT:

Operator/controller step first. Two independent gaps must be closed before #615 can be published, and closing only the gitea_commit_files gap is not sufficient.

Gap 1 (ownership, new this run): the durable issue-lock store is absent (GITEA_ISSUE_LOCK_DIR / /tmp/gitea_issue_lock.json do not exist), so there is no dead-session lock record to recover. gitea_lock_issue therefore evaluates issue #615 as a fresh acquisition and refuses on the base-equivalence rule, because worktree branches/issue-615-runtime-mode-enforcement already carries implementation commit 9bf3acf. The #753/#768 dead-session recovery path cannot grant the lock either: build_recovery_record/assess require an owning open PR head (pr_head, pr_number), and #615 has no published branch and no PR. Provide a sanctioned way to lock an issue whose worktree already carries reviewed-but-unpublished work (for example an explicit already-implemented acquisition mode carrying the local head as proof), or an operator-issued lock for #615 pinned to branch feat/issue-615-runtime-mode-enforcement, worktree branches/issue-615-runtime-mode-enforcement, head 9bf3acf.

Gap 2 (publication, unchanged from comment 13253): gitea_commit_files still exposes no worktree_path parameter, so its workspace resolves to the stable control checkout and the #274 branches-only guard refuses. Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree via GITEA_AUTHOR_WORKTREE / GITEA_ACTIVE_WORKTREE.

After both are closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity/profile/runtime/capability proof, fresh duplicate-work gates, re-verify local commit 9bf3acf, publish through sanctioned MCP, open the PR against master, then stop for an independent reviewer.

WHAT_HAPPENED: Implementation commit 9bf3acf was re-verified and fully revalidated this run. Native ownership recovery through gitea_lock_issue failed closed, so no publication was attempted and no Gitea state changed beyond this comment.
WHY: The issue-lock store is absent, which removes the dead-session recovery path, and the fresh-acquisition path requires a base-equivalent worktree that the completed implementation necessarily violates.
RELATED_PRS: none
BLOCKERS: environment/tooling blocker — author ownership path unavailable (issue-lock store absent; base-equivalence versus completed implementation), and author publication path unavailable (gitea_commit_files workspace binding)
VALIDATION: 2026-07-20 session worktree full suite: 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); both signatures match the baseline recorded on comment 13253 at this identical master SHA 059ee77c1f and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — implementation revalidated; author ownership path unavailable

What is true now:

  • Issue state: open
  • Server-side decision state: no server-side state changed beyond this comment
  • Local verdict/state: local commit 9bf3acf on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement, clean, one commit ahead of master 059ee77; no Gitea branch and no PR exist
  • Latest known validation: full suite 3827 passed / 6 skipped / 2 failed, both failures pre-existing at the identical master SHA

What changed:

  • implementation commit 9bf3acf re-verified as a single in-scope commit across 8 files with no unrelated or generated files
  • full suite and focused suite re-run this session with results matching the prior record
  • gitea_lock_issue attempted once through the exact lock_issue capability and refused fail-closed
  • new diagnosis recorded: the durable issue-lock store is absent, so dead-session recovery is structurally unavailable rather than merely unattempted

What is blocked:

  • Blocker classification: environment/tooling blocker

Who/what acts next:

  • Next actor: controller
  • Required action: close the ownership gap and the publication gap, then re-run the full work-issue workflow from the beginning
  • Do not do: reset or clean the worktree to force base-equivalence, hand-edit the issue-lock store, publish with a direct git push, recreate the implementation, or relaunch the MCP daemon from this development worktree
  • Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement

Author run record

What was verified

Worktree branches/issue-615-runtime-mode-enforcement is clean and sits at 9bf3acf, whose parent is exactly the live master 059ee77. The commit spans 8 files, 1714 insertions, 6 deletions:

  • stable_control_runtime.py (new, 628 lines)
  • tests/test_stable_control_runtime.py (new, 472 lines)
  • thread_state_ledger_examples.py (new, 187 lines)
  • scripts/promote-stable-runtime (new, 171 lines)
  • docs/stable-runtime-promotion-runbook.md (new, 121 lines)
  • gitea_mcp_server.py (+125)
  • docs/architecture/mcp-stable-control-runtime-policy-adr.md (+15)
  • docs/llm-workflow-runbooks.md (+1)

No unrelated files, generated files, caches, credentials, or temporary artifacts. No scope drift beyond acceptance criteria 6-11.

Ownership attempt

The lock was attempted exactly once, after a fresh gitea_whoami and a gitea_resolve_task_capability(task=lock_issue) that returned allowed_in_current_session: true. It refused with the base-equivalence rule plus a dead-session recovery refusal.

Reading issue_lock_recovery.py shows why recovery cannot succeed for this issue: a granted recovery is built only when an owning open PR head is present and relates to the recorded head as equal or strict-descendant. #615 has neither a published branch nor a PR, so that branch of the logic is unreachable, independent of the lock store's state.

The lock store itself is absent from disk, so there is no dead-session record to match in the first place. The two facts compose into a closed loop: fresh acquisition requires a worktree that has not started implementation, and recovery requires an artifact that only publication can create.

Deliberately avoided

gitea_commit_files was not attempted this run. Author ownership was never proven, and the workflow forbids implementation mutation without it. Its own gap is also unchanged by construction: the tool still exposes no worktree_path parameter and master has not advanced since comment 13253, so it would refuse identically on the #274 branches-only guard.

No fallback of any kind was used: no direct git push, no raw API call, no browser or CLI path, no lock-file edit, no worktree reset or clean, no daemon restart, and no second issue selected.

## Canonical Issue State STATE: open — enforcement layer implemented and revalidated locally; author ownership cannot be re-established WHO_IS_NEXT: controller NEXT_ACTION: Restore an author ownership path for an already-implemented worktree, and an author publication path for gitea_commit_files, then re-run the full work-issue workflow for #615 NEXT_PROMPT: ```text Operator/controller step first. Two independent gaps must be closed before #615 can be published, and closing only the gitea_commit_files gap is not sufficient. Gap 1 (ownership, new this run): the durable issue-lock store is absent (GITEA_ISSUE_LOCK_DIR / /tmp/gitea_issue_lock.json do not exist), so there is no dead-session lock record to recover. gitea_lock_issue therefore evaluates issue #615 as a fresh acquisition and refuses on the base-equivalence rule, because worktree branches/issue-615-runtime-mode-enforcement already carries implementation commit 9bf3acf. The #753/#768 dead-session recovery path cannot grant the lock either: build_recovery_record/assess require an owning open PR head (pr_head, pr_number), and #615 has no published branch and no PR. Provide a sanctioned way to lock an issue whose worktree already carries reviewed-but-unpublished work (for example an explicit already-implemented acquisition mode carrying the local head as proof), or an operator-issued lock for #615 pinned to branch feat/issue-615-runtime-mode-enforcement, worktree branches/issue-615-runtime-mode-enforcement, head 9bf3acf. Gap 2 (publication, unchanged from comment 13253): gitea_commit_files still exposes no worktree_path parameter, so its workspace resolves to the stable control checkout and the #274 branches-only guard refuses. Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree via GITEA_AUTHOR_WORKTREE / GITEA_ACTIVE_WORKTREE. After both are closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity/profile/runtime/capability proof, fresh duplicate-work gates, re-verify local commit 9bf3acf, publish through sanctioned MCP, open the PR against master, then stop for an independent reviewer. ``` WHAT_HAPPENED: Implementation commit 9bf3acf was re-verified and fully revalidated this run. Native ownership recovery through gitea_lock_issue failed closed, so no publication was attempted and no Gitea state changed beyond this comment. WHY: The issue-lock store is absent, which removes the dead-session recovery path, and the fresh-acquisition path requires a base-equivalent worktree that the completed implementation necessarily violates. RELATED_PRS: none BLOCKERS: environment/tooling blocker — author ownership path unavailable (issue-lock store absent; base-equivalence versus completed implementation), and author publication path unavailable (gitea_commit_files workspace binding) VALIDATION: 2026-07-20 session worktree full suite: 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); both signatures match the baseline recorded on comment 13253 at this identical master SHA 059ee77c1fd483da385d6ccb4d842e808fb54630 and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — implementation revalidated; author ownership path unavailable What is true now: - Issue state: open - Server-side decision state: no server-side state changed beyond this comment - Local verdict/state: local commit 9bf3acf on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement, clean, one commit ahead of master 059ee77; no Gitea branch and no PR exist - Latest known validation: full suite 3827 passed / 6 skipped / 2 failed, both failures pre-existing at the identical master SHA What changed: - implementation commit 9bf3acf re-verified as a single in-scope commit across 8 files with no unrelated or generated files - full suite and focused suite re-run this session with results matching the prior record - gitea_lock_issue attempted once through the exact lock_issue capability and refused fail-closed - new diagnosis recorded: the durable issue-lock store is absent, so dead-session recovery is structurally unavailable rather than merely unattempted What is blocked: - Blocker classification: environment/tooling blocker Who/what acts next: - Next actor: controller - Required action: close the ownership gap and the publication gap, then re-run the full work-issue workflow from the beginning - Do not do: reset or clean the worktree to force base-equivalence, hand-edit the issue-lock store, publish with a direct git push, recreate the implementation, or relaunch the MCP daemon from this development worktree - Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement --- ## Author run record ### What was verified Worktree `branches/issue-615-runtime-mode-enforcement` is clean and sits at 9bf3acf, whose parent is exactly the live master 059ee77. The commit spans 8 files, 1714 insertions, 6 deletions: - `stable_control_runtime.py` (new, 628 lines) - `tests/test_stable_control_runtime.py` (new, 472 lines) - `thread_state_ledger_examples.py` (new, 187 lines) - `scripts/promote-stable-runtime` (new, 171 lines) - `docs/stable-runtime-promotion-runbook.md` (new, 121 lines) - `gitea_mcp_server.py` (+125) - `docs/architecture/mcp-stable-control-runtime-policy-adr.md` (+15) - `docs/llm-workflow-runbooks.md` (+1) No unrelated files, generated files, caches, credentials, or temporary artifacts. No scope drift beyond acceptance criteria 6-11. ### Ownership attempt The lock was attempted exactly once, after a fresh `gitea_whoami` and a `gitea_resolve_task_capability(task=lock_issue)` that returned `allowed_in_current_session: true`. It refused with the base-equivalence rule plus a dead-session recovery refusal. Reading `issue_lock_recovery.py` shows why recovery cannot succeed for this issue: a granted recovery is built only when an owning open PR head is present and relates to the recorded head as equal or strict-descendant. #615 has neither a published branch nor a PR, so that branch of the logic is unreachable, independent of the lock store's state. The lock store itself is absent from disk, so there is no dead-session record to match in the first place. The two facts compose into a closed loop: fresh acquisition requires a worktree that has not started implementation, and recovery requires an artifact that only publication can create. ### Deliberately avoided `gitea_commit_files` was not attempted this run. Author ownership was never proven, and the workflow forbids implementation mutation without it. Its own gap is also unchanged by construction: the tool still exposes no `worktree_path` parameter and master has not advanced since comment 13253, so it would refuse identically on the #274 branches-only guard. No fallback of any kind was used: no direct git push, no raw API call, no browser or CLI path, no lock-file edit, no worktree reset or clean, no daemon restart, and no second issue selected.
Author
Owner

Canonical Issue State

STATE: open — enforcement layer implemented locally at 9bf3acf; the owning-PR dead-session recovery path is structurally unreachable for this issue
WHO_IS_NEXT: controller
NEXT_ACTION: Close the author ownership gap and the author publication gap, then re-run the full work-issue workflow for #615 from the beginning
NEXT_PROMPT:

Operator/controller step first. This run was directed to recover the #615 issue lock through the owning-PR dead-session recovery path and to verify an existing PR head against local commit 9bf3acf. Both directions rest on a premise that live state does not support, so neither was carried out. Do not re-issue that instruction unchanged.

Correction 1: there is no PR to recover from. Open PR inventory for Scaled-Tech-Consulting/Gitea-Tools is empty (0 open PRs, inventory_complete true). PR #616 is the only PR ever opened for #615; its live state is closed with merged_at 2026-07-16T14:58:44-05:00 and merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, from head branch docs/mcp-stable-control-runtime-policy, and it carried acceptance criteria 1-5 (the ADR document) only. Its head is unrelated to 9bf3acf, which carries acceptance criteria 6-11.

Correction 2: the owning-PR recovery path cannot grant this lock, and this is a code-level fact rather than an inference. issue_lock_recovery.py returns no recovery exemption unless evidence carries both pr_number and pr_head, and unless pr_head equals local_head and remote_head (equal relation) or equals recorded_head with a distinct accepted head (strict-descendant relation). With no published branch and no PR, pr_head is null and the exemption returns None before any other check runs.

Correction 3 (supersedes comment 13257): a dead-session record for #615 does exist, in a different substrate than the one previously reported. The control-plane lease DB holds active author lease lease-13750394b96b4ce4 for work_item 615, session prgs-author-62865-d5cc892b, owner pid 62865, freshness stale_dead_process, expires 2026-07-20T18:14:33Z. Comment 13257 reported only that the durable issue-lock store was absent and concluded from that that no dead-session record existed anywhere. The lease substrate and the issue-lock substrate are distinct; the lease record does not satisfy gitea_lock_issue, which reads the issue-lock store.

Two independent gaps remain open, and closing either one alone is not sufficient:

Gap 1 (ownership): provide a sanctioned way to lock an issue whose worktree already carries fully tested but unpublished work, for example an explicit already-implemented acquisition mode that accepts the local head as proof, or an operator-issued lock for #615 pinned to branch feat/issue-615-runtime-mode-enforcement, worktree branches/issue-615-runtime-mode-enforcement, head 9bf3acf. Fresh acquisition refuses on base-equivalence because the implementation commit exists; recovery refuses because it requires an artifact only publication can create.

Gap 2 (publication, unchanged): gitea_commit_files exposes no worktree_path parameter, so its workspace resolves to the stable control checkout and the #274 branches-only guard refuses. Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree through GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE.

After both gaps are closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity, profile, runtime, and capability proof, fresh duplicate-work gates, re-verify local commit 9bf3acf, publish through sanctioned MCP, open the PR against master, then stop for an independent reviewer.

WHAT_HAPPENED: A directed resume run verified live state, found the instructed recovery premise unsupported, performed no ownership or publication mutation, and recorded this correction. gitea_lock_issue was deliberately not retried.
WHY: The instruction assumed an existing PR whose head equals 9bf3acf. No open PR exists in this repository, so the owning-PR recovery path has no PR to read and returns no exemption. Retrying gitea_lock_issue would repeat a mutation already refused at this identical master SHA with an identical worktree and head.
RELATED_PRS: #616 — live state closed, merged_at 2026-07-16T14:58:44-05:00, merge_commit_sha 293808b42d, head branch docs/mcp-stable-control-runtime-policy, acceptance criteria 1-5 only, unrelated to head 9bf3acf
BLOCKERS: environment/tooling blocker — author ownership path unavailable (owning-PR recovery requires a PR that only publication can create; fresh acquisition refuses on base-equivalence) and author publication path unavailable (gitea_commit_files workspace binding)
VALIDATION: Not re-run this iteration, and deliberately so: master is unchanged at 059ee77c1f, the worktree is clean, and HEAD is unchanged at 9bf3acf, so the recorded results remain the proof of record. Last full run (comment 13257, identical master SHA): session worktree 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); clean baseline worktree at the same master SHA 3780 passed, 6 skipped, same 2 failure signatures, so both are pre-existing and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — directed recovery premise corrected; ownership path still unavailable

What is true now:

  • Issue state: open
  • Server-side decision state: no server-side state changed beyond this comment
  • Local verdict/state: local commit 9bf3acf on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement, clean, exactly one commit ahead of live master 059ee77; no Gitea branch and no PR exist for this work
  • Latest known validation: full suite 3827 passed / 6 skipped / 2 failed at this identical master SHA, both failures pre-existing against a clean baseline

What changed:

  • open PR inventory re-read: 0 open PRs repository-wide, inventory complete, so no PR head could be compared against 9bf3acf
  • PR #616 re-read and classified as the acceptance-criteria-1-5 ADR PR whose live state is closed with merged_at set, not an owning PR for this branch
  • issue_lock_recovery.py read directly: the recovery exemption requires pr_number and pr_head and returns None without them, confirming the path is unreachable by construction rather than by circumstance
  • control-plane lease lease-13750394b96b4ce4 identified as a genuine dead-session record for #615 in the lease substrate, correcting the comment 13257 statement that no dead-session record existed
  • gitea_lock_issue deliberately not retried, because inputs are identical to the run that already refused it

What is blocked:

  • Blocker classification: environment/tooling blocker

Who/what acts next:

  • Next actor: controller
  • Required action: close the ownership gap and the publication gap, then re-run the full work-issue workflow from the beginning
  • Do not do: reset or clean the worktree to force base-equivalence, hand-edit the issue-lock store or the lease record, publish with a direct git push, recreate or modify the implementation, allocate another issue, or relaunch the MCP daemon from this development worktree
  • Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement

Author run record — directed resume, corrections only

What this run was asked to do

Resume #615 only; verify that an existing PR head equals local commit 9bf3acf; recover the native issue lock through the owning-PR dead-session recovery path; post a corrected durable handoff; stop for an independent reviewer. No implementation change and no new issue allocation.

What live state showed

The first two directions could not be carried out, because they presuppose a PR that does not exist.

Open PR inventory for this repository returned zero PRs with a complete-inventory marker. PR #616, the only PR ever opened against #615, reached live state closed with merged_at 2026-07-16T14:58:44-05:00 from head branch docs/mcp-stable-control-runtime-policy at merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, and covered acceptance criteria 1-5. The work at 9bf3acf covers acceptance criteria 6-11 and has never been published.

Why the recovery path cannot apply

Reading issue_lock_recovery.py rather than relying on the earlier summary: the recovery exemption builder requires pr_number, a locked branch name, and pr_head, and returns None when any is missing. When the relation is equal it further requires pr_head == local_head == remote_head; when the relation is strict-descendant it requires pr_head == recorded_head with a distinct accepted head equal to the local head. An unpublished branch supplies neither a remote head nor a PR head, so the builder exits before any comparison. No input a session can legitimately supply changes that outcome.

Correction to comment 13257

Comment 13257 stated that the durable issue-lock store was absent and inferred from that that no dead-session record existed to recover. The first half is unchanged, but the inference was too broad. The control-plane lease DB holds an active author lease for this exact issue: lease-13750394b96b4ce4, work item 615, session prgs-author-62865-d5cc892b, owner pid 62865 confirmed dead (stale_dead_process), expiry 2026-07-20T18:14:33Z. This does not by itself remove either gap, because gitea_lock_issue reads the issue-lock store rather than the lease substrate, but the record exists and any operator-issued lock or recovery-mode design should reconcile the two substrates rather than assume the issue has no durable ownership trace.

Implementation status

Unchanged and untouched. The worktree is clean at 9bf3acf, whose parent is exactly live master 059ee77. The commit spans 8 files, 1714 insertions, 6 deletions: stable_control_runtime.py (new), tests/test_stable_control_runtime.py (new), thread_state_ledger_examples.py (new), scripts/promote-stable-runtime (new), docs/stable-runtime-promotion-runbook.md (new), gitea_mcp_server.py, docs/architecture/mcp-stable-control-runtime-policy-adr.md, docs/llm-workflow-runbooks.md. No unrelated, generated, or temporary files.

Actions deliberately not attempted

gitea_lock_issue was not retried. Master, worktree, and head are identical to the run recorded in comment 13257, so a retry would repeat a proven-failing mutation rather than test anything new. gitea_commit_files was not attempted, because author ownership was never proven and its own gap is unchanged. No direct git push, no raw API call, no browser or CLI path, no lock-file or lease-record edit, no worktree reset or clean, no daemon restart, no implementation change, and no second issue selected.

## Canonical Issue State STATE: open — enforcement layer implemented locally at 9bf3acf; the owning-PR dead-session recovery path is structurally unreachable for this issue WHO_IS_NEXT: controller NEXT_ACTION: Close the author ownership gap and the author publication gap, then re-run the full work-issue workflow for #615 from the beginning NEXT_PROMPT: ```text Operator/controller step first. This run was directed to recover the #615 issue lock through the owning-PR dead-session recovery path and to verify an existing PR head against local commit 9bf3acf. Both directions rest on a premise that live state does not support, so neither was carried out. Do not re-issue that instruction unchanged. Correction 1: there is no PR to recover from. Open PR inventory for Scaled-Tech-Consulting/Gitea-Tools is empty (0 open PRs, inventory_complete true). PR #616 is the only PR ever opened for #615; its live state is closed with merged_at 2026-07-16T14:58:44-05:00 and merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, from head branch docs/mcp-stable-control-runtime-policy, and it carried acceptance criteria 1-5 (the ADR document) only. Its head is unrelated to 9bf3acf, which carries acceptance criteria 6-11. Correction 2: the owning-PR recovery path cannot grant this lock, and this is a code-level fact rather than an inference. issue_lock_recovery.py returns no recovery exemption unless evidence carries both pr_number and pr_head, and unless pr_head equals local_head and remote_head (equal relation) or equals recorded_head with a distinct accepted head (strict-descendant relation). With no published branch and no PR, pr_head is null and the exemption returns None before any other check runs. Correction 3 (supersedes comment 13257): a dead-session record for #615 does exist, in a different substrate than the one previously reported. The control-plane lease DB holds active author lease lease-13750394b96b4ce4 for work_item 615, session prgs-author-62865-d5cc892b, owner pid 62865, freshness stale_dead_process, expires 2026-07-20T18:14:33Z. Comment 13257 reported only that the durable issue-lock store was absent and concluded from that that no dead-session record existed anywhere. The lease substrate and the issue-lock substrate are distinct; the lease record does not satisfy gitea_lock_issue, which reads the issue-lock store. Two independent gaps remain open, and closing either one alone is not sufficient: Gap 1 (ownership): provide a sanctioned way to lock an issue whose worktree already carries fully tested but unpublished work, for example an explicit already-implemented acquisition mode that accepts the local head as proof, or an operator-issued lock for #615 pinned to branch feat/issue-615-runtime-mode-enforcement, worktree branches/issue-615-runtime-mode-enforcement, head 9bf3acf. Fresh acquisition refuses on base-equivalence because the implementation commit exists; recovery refuses because it requires an artifact only publication can create. Gap 2 (publication, unchanged): gitea_commit_files exposes no worktree_path parameter, so its workspace resolves to the stable control checkout and the #274 branches-only guard refuses. Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree through GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE. After both gaps are closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity, profile, runtime, and capability proof, fresh duplicate-work gates, re-verify local commit 9bf3acf, publish through sanctioned MCP, open the PR against master, then stop for an independent reviewer. ``` WHAT_HAPPENED: A directed resume run verified live state, found the instructed recovery premise unsupported, performed no ownership or publication mutation, and recorded this correction. gitea_lock_issue was deliberately not retried. WHY: The instruction assumed an existing PR whose head equals 9bf3acf. No open PR exists in this repository, so the owning-PR recovery path has no PR to read and returns no exemption. Retrying gitea_lock_issue would repeat a mutation already refused at this identical master SHA with an identical worktree and head. RELATED_PRS: #616 — live state closed, merged_at 2026-07-16T14:58:44-05:00, merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, head branch docs/mcp-stable-control-runtime-policy, acceptance criteria 1-5 only, unrelated to head 9bf3acf BLOCKERS: environment/tooling blocker — author ownership path unavailable (owning-PR recovery requires a PR that only publication can create; fresh acquisition refuses on base-equivalence) and author publication path unavailable (gitea_commit_files workspace binding) VALIDATION: Not re-run this iteration, and deliberately so: master is unchanged at 059ee77c1fd483da385d6ccb4d842e808fb54630, the worktree is clean, and HEAD is unchanged at 9bf3acf, so the recorded results remain the proof of record. Last full run (comment 13257, identical master SHA): session worktree 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); clean baseline worktree at the same master SHA 3780 passed, 6 skipped, same 2 failure signatures, so both are pre-existing and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — directed recovery premise corrected; ownership path still unavailable What is true now: - Issue state: open - Server-side decision state: no server-side state changed beyond this comment - Local verdict/state: local commit 9bf3acf on branch feat/issue-615-runtime-mode-enforcement in worktree branches/issue-615-runtime-mode-enforcement, clean, exactly one commit ahead of live master 059ee77; no Gitea branch and no PR exist for this work - Latest known validation: full suite 3827 passed / 6 skipped / 2 failed at this identical master SHA, both failures pre-existing against a clean baseline What changed: - open PR inventory re-read: 0 open PRs repository-wide, inventory complete, so no PR head could be compared against 9bf3acf - PR #616 re-read and classified as the acceptance-criteria-1-5 ADR PR whose live state is closed with merged_at set, not an owning PR for this branch - issue_lock_recovery.py read directly: the recovery exemption requires pr_number and pr_head and returns None without them, confirming the path is unreachable by construction rather than by circumstance - control-plane lease lease-13750394b96b4ce4 identified as a genuine dead-session record for #615 in the lease substrate, correcting the comment 13257 statement that no dead-session record existed - gitea_lock_issue deliberately not retried, because inputs are identical to the run that already refused it What is blocked: - Blocker classification: environment/tooling blocker Who/what acts next: - Next actor: controller - Required action: close the ownership gap and the publication gap, then re-run the full work-issue workflow from the beginning - Do not do: reset or clean the worktree to force base-equivalence, hand-edit the issue-lock store or the lease record, publish with a direct git push, recreate or modify the implementation, allocate another issue, or relaunch the MCP daemon from this development worktree - Resume from: this comment plus local commit 9bf3acf in branches/issue-615-runtime-mode-enforcement --- ## Author run record — directed resume, corrections only ### What this run was asked to do Resume #615 only; verify that an existing PR head equals local commit 9bf3acf; recover the native issue lock through the owning-PR dead-session recovery path; post a corrected durable handoff; stop for an independent reviewer. No implementation change and no new issue allocation. ### What live state showed The first two directions could not be carried out, because they presuppose a PR that does not exist. Open PR inventory for this repository returned zero PRs with a complete-inventory marker. PR #616, the only PR ever opened against #615, reached live state closed with merged_at 2026-07-16T14:58:44-05:00 from head branch `docs/mcp-stable-control-runtime-policy` at merge_commit_sha `293808b42d54c4736f8fb55f71b10d93a40ae4ea`, and covered acceptance criteria 1-5. The work at `9bf3acf` covers acceptance criteria 6-11 and has never been published. ### Why the recovery path cannot apply Reading `issue_lock_recovery.py` rather than relying on the earlier summary: the recovery exemption builder requires `pr_number`, a locked branch name, and `pr_head`, and returns `None` when any is missing. When the relation is equal it further requires `pr_head == local_head == remote_head`; when the relation is strict-descendant it requires `pr_head == recorded_head` with a distinct accepted head equal to the local head. An unpublished branch supplies neither a remote head nor a PR head, so the builder exits before any comparison. No input a session can legitimately supply changes that outcome. ### Correction to comment 13257 Comment 13257 stated that the durable issue-lock store was absent and inferred from that that no dead-session record existed to recover. The first half is unchanged, but the inference was too broad. The control-plane lease DB holds an active author lease for this exact issue: `lease-13750394b96b4ce4`, work item 615, session `prgs-author-62865-d5cc892b`, owner pid 62865 confirmed dead (`stale_dead_process`), expiry `2026-07-20T18:14:33Z`. This does not by itself remove either gap, because `gitea_lock_issue` reads the issue-lock store rather than the lease substrate, but the record exists and any operator-issued lock or recovery-mode design should reconcile the two substrates rather than assume the issue has no durable ownership trace. ### Implementation status Unchanged and untouched. The worktree is clean at `9bf3acf`, whose parent is exactly live master `059ee77`. The commit spans 8 files, 1714 insertions, 6 deletions: `stable_control_runtime.py` (new), `tests/test_stable_control_runtime.py` (new), `thread_state_ledger_examples.py` (new), `scripts/promote-stable-runtime` (new), `docs/stable-runtime-promotion-runbook.md` (new), `gitea_mcp_server.py`, `docs/architecture/mcp-stable-control-runtime-policy-adr.md`, `docs/llm-workflow-runbooks.md`. No unrelated, generated, or temporary files. ### Actions deliberately not attempted `gitea_lock_issue` was not retried. Master, worktree, and head are identical to the run recorded in comment 13257, so a retry would repeat a proven-failing mutation rather than test anything new. `gitea_commit_files` was not attempted, because author ownership was never proven and its own gap is unchanged. No direct git push, no raw API call, no browser or CLI path, no lock-file or lease-record edit, no worktree reset or clean, no daemon restart, no implementation change, and no second issue selected.
Author
Owner

Canonical Issue State

STATE: open — enforcement layer published as PR #770 at head 9bf3acf; author ownership re-established through owning-PR dead-session recovery; awaiting an independent reviewer
WHO_IS_NEXT: reviewer
NEXT_ACTION: Run an independent native reviewer against PR #770 pinned to head 9bf3acfef6
NEXT_PROMPT:

Run an independent native review of PR #770 (Closes #615) in the prgs-reviewer namespace, pinned to head 9bf3acfef681f9766ae1d93680b03996a93f98a1 against base master 059ee77c1fd483da385d6ccb4d842e808fb54630.

Live state to re-verify before deciding, not to assume: PR #770 open, base master, mergeable true, commits_behind 0, has_conflicts false, checks not required by live branch protection, approval_at_current_head false, no reviewer or merger lease held. The author lock for #615 is live and owned by prgs-author session pid 88416; a live author lock is not by itself a reviewer blocker.

Review scope is issue #615 acceptance criteria 6-11 only. Acceptance criteria 1-5 (the ADR document) landed earlier through PR #616 and are not in this diff. The diff is 8 files, 1714 insertions, 6 deletions: stable_control_runtime.py (new), tests/test_stable_control_runtime.py (new), thread_state_ledger_examples.py (new), scripts/promote-stable-runtime (new), docs/stable-runtime-promotion-runbook.md (new), gitea_mcp_server.py, docs/architecture/mcp-stable-control-runtime-policy-adr.md, docs/llm-workflow-runbooks.md.

Do not merge on the author's validation record; re-run validation yourself if your workflow requires it. Two full-suite failures are pre-existing at this identical master SHA against a clean baseline worktree and neither touches a file in this diff.

WHAT_HAPPENED: A directed resume run verified that PR #770 now exists at head 9bf3acf, matching the local worktree head exactly, and recovered the native #615 issue lock through the owning-PR dead-session recovery path. No implementation file was modified and no second issue was allocated.
WHY: Comment 13262 recorded that no PR existed and that owning-PR recovery was therefore structurally unreachable. PR #770 was created at 2026-07-20T10:35:04-05:00, after that comment was written at 10:29:19-05:00. The publication supplied the pr_head that the recovery builder requires, so the path that was unreachable then is reachable now and was exercised successfully this run.
RELATED_PRS: #770 — open, base master, head 9bf3acfef6, source branch feat/issue-615-runtime-mode-enforcement, mergeable true, commits_behind 0; #616 — closed with merged_at 2026-07-16T14:58:44-05:00, merge_commit_sha 293808b42d, acceptance criteria 1-5 only, unrelated head
BLOCKERS: no blocker
VALIDATION: Not re-run this iteration, and deliberately so: this run made no implementation change, the worktree is clean, HEAD is unchanged at 9bf3acf, and master is unchanged at 059ee77c1f, so the recorded results remain the proof of record. Prior full run at this identical master SHA (comment 13257): session worktree 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); clean baseline worktree at the same master SHA 3780 passed, 6 skipped, same 2 failure signatures, so both are pre-existing and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — PR #770 published at 9bf3acf; author lock recovered; reviewer is next

What is true now:

  • Issue state: open
  • Server-side decision state: no review decision recorded at the current PR head
  • Local verdict/state: worktree branches/issue-615-runtime-mode-enforcement clean at 9bf3acfef6, exactly one commit ahead of live master 059ee77c1fd483da385d6ccb4d842e808fb54630; that head is published and is the live head of PR #770
  • Latest known validation: full suite 3827 passed / 6 skipped / 2 failed at this identical master SHA, both failures pre-existing against a clean baseline worktree

What changed:

  • three-way head equality proven this run: local worktree HEAD, the PR #770 live head_sha, and the recovery record's remote_head are all 9bf3acfef6
  • native issue lock for #615 recovered through gitea_lock_issue: dead_session_recovery.recovered true, reason "owning MCP session exited; durable ownership evidence matched", head_relation equal, pr_head 9bf3acfef6, pr_number 770, prior session pid 62865 confirmed dead, replacement session pid 88416
  • lock freshness live, no competing live lock, lock file written at the durable issue-lock path
  • PR #770 sync state re-read: open, base master, mergeable true, commits_behind 0, has_conflicts false, approval_at_current_head false, no reviewer or merger lease, recommended_next_action fresh_review_required
  • comment 13262 superseded: its statement that no PR exists and that owning-PR recovery is structurally unreachable was accurate when written and is no longer true of live state

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: reviewer
  • Required action: independent native review of PR #770 pinned to head 9bf3acfef6
  • Do not do: author self-review, self-approval, or self-merge; further pushes, amends, rebases, or force-pushes to the PR branch; merging master into the branch again while it is zero commits behind; releasing or hand-editing the issue lock or the lease record; allocating another issue
  • Resume from: this comment plus PR #770 at head 9bf3acf

Author run record — directed resume, ownership recovery only

What this run was asked to do

Resume #615 only; verify the newly created PR head equals local and remote commit 9bf3acf; recover the native issue lock through the owning-PR dead-session recovery path; post a corrected durable author handoff; stop for an independent reviewer. No implementation change and no new issue allocation. All four directions were carried out.

Head verification

Three independent sources agree on the full 40-character head:

  • local worktree branches/issue-615-runtime-mode-enforcement HEAD: 9bf3acfef681f9766ae1d93680b03996a93f98a1, on branch feat/issue-615-runtime-mode-enforcement, clean tracked and untracked state
  • PR #770 live head_sha read through native MCP: 9bf3acfef681f9766ae1d93680b03996a93f98a1
  • the recovery record's remote_head: 9bf3acfef681f9766ae1d93680b03996a93f98a1

Base head is live master 059ee77c1fd483da385d6ccb4d842e808fb54630 with commits_behind 0, so no branch sync is required.

Ownership recovery

gitea_lock_issue was called once, after a fresh gitea_whoami and a gitea_resolve_task_capability(task=lock_issue) returning allowed_in_current_session: true, with the exact branch name and the exact worktree path. It granted the lock through the recovery path rather than fresh acquisition, so the base-equivalence rule that refused the two prior runs was not applied and did not need to be worked around.

The refusals recorded in comments 13257 and 13262 were correct for the state that existed then. The single fact that changed is publication: with a PR head available, the recovery builder's precondition is satisfied and it returns a granted exemption whose head_relation is equal.

Correction to the substrate question raised in comment 13262

Comment 13262 noted that the control-plane lease DB and the durable issue-lock store are distinct substrates, and that lease lease-13750394b96b4ce4 existed while the issue-lock store did not. That observation stands. This run adds the resolution: recovery reads durable ownership evidence keyed to branch, worktree, head, and claimant, and once the PR head existed the evidence matched without any operator-issued lock and without reconciling the two substrates by hand. The stale lease for the dead pid 62865 remains a control-plane record; the live author lock is now held by pid 88416.

Implementation status

Unchanged and untouched this run. No file under the worktree was edited, created, or deleted. The commit remains a single in-scope commit spanning 8 files, 1714 insertions, 6 deletions, covering acceptance criteria 6-11 only.

Actions deliberately not attempted

No implementation change, no commit, no push, no PR update, no amend, no rebase, no force-push. No review, approval, or merge action of any kind. No second issue selected and no allocator call. No direct git push, no raw API call, no browser or CLI path, no lock-file or lease-record hand-edit, no worktree reset or clean, no worktree or branch deletion, and no daemon restart. The control checkout was not used for task work and remains clean on master at 059ee77.

## Canonical Issue State STATE: open — enforcement layer published as PR #770 at head 9bf3acf; author ownership re-established through owning-PR dead-session recovery; awaiting an independent reviewer WHO_IS_NEXT: reviewer NEXT_ACTION: Run an independent native reviewer against PR #770 pinned to head 9bf3acfef681f9766ae1d93680b03996a93f98a1 NEXT_PROMPT: ```text Run an independent native review of PR #770 (Closes #615) in the prgs-reviewer namespace, pinned to head 9bf3acfef681f9766ae1d93680b03996a93f98a1 against base master 059ee77c1fd483da385d6ccb4d842e808fb54630. Live state to re-verify before deciding, not to assume: PR #770 open, base master, mergeable true, commits_behind 0, has_conflicts false, checks not required by live branch protection, approval_at_current_head false, no reviewer or merger lease held. The author lock for #615 is live and owned by prgs-author session pid 88416; a live author lock is not by itself a reviewer blocker. Review scope is issue #615 acceptance criteria 6-11 only. Acceptance criteria 1-5 (the ADR document) landed earlier through PR #616 and are not in this diff. The diff is 8 files, 1714 insertions, 6 deletions: stable_control_runtime.py (new), tests/test_stable_control_runtime.py (new), thread_state_ledger_examples.py (new), scripts/promote-stable-runtime (new), docs/stable-runtime-promotion-runbook.md (new), gitea_mcp_server.py, docs/architecture/mcp-stable-control-runtime-policy-adr.md, docs/llm-workflow-runbooks.md. Do not merge on the author's validation record; re-run validation yourself if your workflow requires it. Two full-suite failures are pre-existing at this identical master SHA against a clean baseline worktree and neither touches a file in this diff. ``` WHAT_HAPPENED: A directed resume run verified that PR #770 now exists at head 9bf3acf, matching the local worktree head exactly, and recovered the native #615 issue lock through the owning-PR dead-session recovery path. No implementation file was modified and no second issue was allocated. WHY: Comment 13262 recorded that no PR existed and that owning-PR recovery was therefore structurally unreachable. PR #770 was created at 2026-07-20T10:35:04-05:00, after that comment was written at 10:29:19-05:00. The publication supplied the pr_head that the recovery builder requires, so the path that was unreachable then is reachable now and was exercised successfully this run. RELATED_PRS: #770 — open, base master, head 9bf3acfef681f9766ae1d93680b03996a93f98a1, source branch feat/issue-615-runtime-mode-enforcement, mergeable true, commits_behind 0; #616 — closed with merged_at 2026-07-16T14:58:44-05:00, merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, acceptance criteria 1-5 only, unrelated head BLOCKERS: no blocker VALIDATION: Not re-run this iteration, and deliberately so: this run made no implementation change, the worktree is clean, HEAD is unchanged at 9bf3acf, and master is unchanged at 059ee77c1fd483da385d6ccb4d842e808fb54630, so the recorded results remain the proof of record. Prior full run at this identical master SHA (comment 13257): session worktree 3827 passed, 6 skipped, 493 subtests passed, 2 failed (test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue); clean baseline worktree at the same master SHA 3780 passed, 6 skipped, same 2 failure signatures, so both are pre-existing and neither touches a file in this diff; focused tests/test_stable_control_runtime.py 47 passed; git diff --check clean LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — PR #770 published at 9bf3acf; author lock recovered; reviewer is next What is true now: - Issue state: open - Server-side decision state: no review decision recorded at the current PR head - Local verdict/state: worktree branches/issue-615-runtime-mode-enforcement clean at 9bf3acfef681f9766ae1d93680b03996a93f98a1, exactly one commit ahead of live master 059ee77c1fd483da385d6ccb4d842e808fb54630; that head is published and is the live head of PR #770 - Latest known validation: full suite 3827 passed / 6 skipped / 2 failed at this identical master SHA, both failures pre-existing against a clean baseline worktree What changed: - three-way head equality proven this run: local worktree HEAD, the PR #770 live head_sha, and the recovery record's remote_head are all 9bf3acfef681f9766ae1d93680b03996a93f98a1 - native issue lock for #615 recovered through gitea_lock_issue: dead_session_recovery.recovered true, reason "owning MCP session exited; durable ownership evidence matched", head_relation equal, pr_head 9bf3acfef681f9766ae1d93680b03996a93f98a1, pr_number 770, prior session pid 62865 confirmed dead, replacement session pid 88416 - lock freshness live, no competing live lock, lock file written at the durable issue-lock path - PR #770 sync state re-read: open, base master, mergeable true, commits_behind 0, has_conflicts false, approval_at_current_head false, no reviewer or merger lease, recommended_next_action fresh_review_required - comment 13262 superseded: its statement that no PR exists and that owning-PR recovery is structurally unreachable was accurate when written and is no longer true of live state What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: reviewer - Required action: independent native review of PR #770 pinned to head 9bf3acfef681f9766ae1d93680b03996a93f98a1 - Do not do: author self-review, self-approval, or self-merge; further pushes, amends, rebases, or force-pushes to the PR branch; merging master into the branch again while it is zero commits behind; releasing or hand-editing the issue lock or the lease record; allocating another issue - Resume from: this comment plus PR #770 at head 9bf3acf --- ## Author run record — directed resume, ownership recovery only ### What this run was asked to do Resume #615 only; verify the newly created PR head equals local and remote commit 9bf3acf; recover the native issue lock through the owning-PR dead-session recovery path; post a corrected durable author handoff; stop for an independent reviewer. No implementation change and no new issue allocation. All four directions were carried out. ### Head verification Three independent sources agree on the full 40-character head: - local worktree `branches/issue-615-runtime-mode-enforcement` HEAD: `9bf3acfef681f9766ae1d93680b03996a93f98a1`, on branch `feat/issue-615-runtime-mode-enforcement`, clean tracked and untracked state - PR #770 live `head_sha` read through native MCP: `9bf3acfef681f9766ae1d93680b03996a93f98a1` - the recovery record's `remote_head`: `9bf3acfef681f9766ae1d93680b03996a93f98a1` Base head is live master `059ee77c1fd483da385d6ccb4d842e808fb54630` with `commits_behind` 0, so no branch sync is required. ### Ownership recovery `gitea_lock_issue` was called once, after a fresh `gitea_whoami` and a `gitea_resolve_task_capability(task=lock_issue)` returning `allowed_in_current_session: true`, with the exact branch name and the exact worktree path. It granted the lock through the recovery path rather than fresh acquisition, so the base-equivalence rule that refused the two prior runs was not applied and did not need to be worked around. The refusals recorded in comments 13257 and 13262 were correct for the state that existed then. The single fact that changed is publication: with a PR head available, the recovery builder's precondition is satisfied and it returns a granted exemption whose `head_relation` is `equal`. ### Correction to the substrate question raised in comment 13262 Comment 13262 noted that the control-plane lease DB and the durable issue-lock store are distinct substrates, and that lease `lease-13750394b96b4ce4` existed while the issue-lock store did not. That observation stands. This run adds the resolution: recovery reads durable ownership evidence keyed to branch, worktree, head, and claimant, and once the PR head existed the evidence matched without any operator-issued lock and without reconciling the two substrates by hand. The stale lease for the dead pid 62865 remains a control-plane record; the live author lock is now held by pid 88416. ### Implementation status Unchanged and untouched this run. No file under the worktree was edited, created, or deleted. The commit remains a single in-scope commit spanning 8 files, 1714 insertions, 6 deletions, covering acceptance criteria 6-11 only. ### Actions deliberately not attempted No implementation change, no commit, no push, no PR update, no amend, no rebase, no force-push. No review, approval, or merge action of any kind. No second issue selected and no allocator call. No direct git push, no raw API call, no browser or CLI path, no lock-file or lease-record hand-edit, no worktree reset or clean, no worktree or branch deletion, and no daemon restart. The control checkout was not used for task work and remains clean on master at 059ee77.
Owner

Canonical Issue State

STATE: open — PR #770 independently reviewed at head 9bf3acfef681f9766ae1d93680b03996a93f98a1; REQUEST_CHANGES review posted to Gitea as review_id 483
WHO_IS_NEXT: author
NEXT_ACTION: Remediate reviewer findings F1, F2, and F3 on branch feat/issue-615-runtime-mode-enforcement, push to PR #770, and request a fresh independent review
NEXT_PROMPT:

Resume issue #615 / PR #770 as prgs-author. An independent reviewer posted a REQUEST_CHANGES review to Gitea (review_id 483) at head 9bf3acfef681f9766ae1d93680b03996a93f98a1. Remediate all three findings, push to the existing PR branch, then stop for a fresh independent review. Do not merge, do not self-approve, and do not open a second PR.

F1 (blocking): gitea_mcp_server.py:11391-11397 derives the runtime-gate alignment input as realpath(workspace_root) == process_project_root. That redefines workspace_roots_aligned, whose established meaning at gitea_mcp_server.py:573 is ctx["roots_aligned"] (canonical_repo_root == process_project_root). Because all task work must live in a branches/ worktree, the new derivation reports aligned False for correctly configured sessions and raises unsafe_process_root_workspace_alignment, refusing every non-read operation. It blocks the author namespace as soon as GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE is bound — which is the documented fix for the publication gap in comment 13253 — and blocks reviewer and merger namespaces whenever a PR lease worktree is held. Feed the gate ctx["roots_aligned"], or express the check as worktree membership of the canonical repository root instead of path equality with the process root.

F2 (blocking): _current_runtime_mode_report caches its first result into _STARTUP_RUNTIME_MODE, which is initialised to None rather than captured at import as _STARTUP_PARITY is, and the read-only refresh=True path also seeds it. Session-scoped fields (active_task_workspace and its derived alignment) and mutable fields (dirty_files) are frozen for the process lifetime, so the acceptance criterion 7 dirty-runtime blocker stops applying after the snapshot and one session's workspace binding decides alignment for every later session. Capture immutable process facts once at import and evaluate session-scoped fields per call.

F3 (major): no test exercises the real derivation. Every server-wiring test asserting a healthy runtime permits mutations patches _current_runtime_mode_report with a fixture whose workspace_roots_aligned is True, and test_live_report_describes_this_checkout asserts only mode and process root. Add a test that drives _current_runtime_mode_report with a session bound to a branches/ worktree of this repository, forced production guards, and a stable checkout, asserting real_mutations_allowed is true and _runtime_mode_block("gitea.pr.create") is empty. It must fail on the current head and pass after F1 is fixed.

Non-blocking, fix only if convenient: lexicographic timestamp comparison in assess_namespace_reproof; _RUNTIME_MODE_GATE_UNDER_TEST inferred from sys.modules rather than an explicit env opt-in; observe_runtime porcelain parsing renders renames as "old -> new".

WHAT_HAPPENED: An independent reviewer in the prgs-reviewer namespace acquired the PR #770 reviewer lease, inspected all 8 changed files at the pinned head, re-ran the focused suite, reproduced a blocking fail-closed defect directly against the PR code, and posted exactly one native REQUEST_CHANGES review to Gitea.
WHY: Acceptance criteria 6, 8, 10, and 11 are correctly implemented and well tested at the module level, but the acceptance criterion 7 alignment blocker, as derived by the server wiring, inverts its intent: it would refuse every correctly bound author, reviewer, and merger session the moment the enforcement layer is promoted, leaving gitea.read as the only surviving operation.
RELATED_PRS: #770 — open, base master, head 9bf3acfef6, review_id 483 REQUEST_CHANGES at that exact head, undismissed and not stale, mergeable true, commits_behind 0, no approval at current head; #616 — closed with merged_at 2026-07-16T14:58:44-05:00 and merge_commit_sha 293808b42d, acceptance criteria 1-5 only, unrelated head
BLOCKERS: code blocker — F1 fail-closed alignment derivation and F2 process-lifetime snapshot of session-scoped facts, both in gitea_mcp_server.py; test blocker — F3, acceptance criterion 9's healthy-runtime case is proven only against a fixture
VALIDATION: Reviewer re-ran tests/test_stable_control_runtime.py independently in worktree branches/review-pr-770 at head 9bf3acf: 47 passed, confirming the author's count. git diff --check clean against base 059ee77c1f. Diff independently confirmed as 8 files, 1714 insertions, 6 deletions, no unrelated or generated files, no secrets, tokens, keychain identifiers, or raw service URLs. F1 reproduced by executing the PR's own build_runtime_report with runtime_mode stable-control, a clean checkout on master, and a branches/ worktree workspace: real_mutations_allowed False, blocker unsafe_process_root_workspace_alignment. The author's two full-suite failures were not re-run this session; they are recorded as pre-existing at this identical master SHA and neither touches a file in this diff.
LAST_UPDATED_BY: prgs-reviewer (sysadmin)

[THREAD STATE LEDGER] Issue #615 — PR #770 reviewed at 9bf3acf; REQUEST_CHANGES posted to Gitea; author is next

What is true now:

  • Issue state: open
  • Server-side decision state: REQUEST_CHANGES review posted to Gitea as review_id 483 at head 9bf3acfef6, visible, undismissed, not stale
  • Local verdict/state: REQUEST_CHANGES prepared locally in reviewer worktree branches/review-pr-770 pinned to that head; no author branch or worktree was modified
  • Latest known validation: focused suite tests/test_stable_control_runtime.py 47 passed, re-run independently this session; F1 reproduced directly against the PR code

What changed:

  • reviewer PR lease acquired for PR #770 (comment 13272) and released after the review was posted to Gitea
  • independent inspection of all 8 changed files completed at the pinned head
  • exactly one REQUEST_CHANGES review posted to Gitea, review_id 483
  • this durable reviewer handoff recorded

What is blocked:

  • Blocker classification: code blocker

Who/what acts next:

  • Next actor: author
  • Required action: remediate F1, F2, and F3, push to branch feat/issue-615-runtime-mode-enforcement, and request a fresh independent review
  • Do not do: merge PR #770 at this head, self-approve, dismiss review 483, open a second PR for this work, or treat the 47 passing tests as proof that F1 is absent
  • Resume from: review_id 483 on PR #770 at head 9bf3acfef6
## Canonical Issue State STATE: open — PR #770 independently reviewed at head 9bf3acfef681f9766ae1d93680b03996a93f98a1; REQUEST_CHANGES review posted to Gitea as review_id 483 WHO_IS_NEXT: author NEXT_ACTION: Remediate reviewer findings F1, F2, and F3 on branch feat/issue-615-runtime-mode-enforcement, push to PR #770, and request a fresh independent review NEXT_PROMPT: ```text Resume issue #615 / PR #770 as prgs-author. An independent reviewer posted a REQUEST_CHANGES review to Gitea (review_id 483) at head 9bf3acfef681f9766ae1d93680b03996a93f98a1. Remediate all three findings, push to the existing PR branch, then stop for a fresh independent review. Do not merge, do not self-approve, and do not open a second PR. F1 (blocking): gitea_mcp_server.py:11391-11397 derives the runtime-gate alignment input as realpath(workspace_root) == process_project_root. That redefines workspace_roots_aligned, whose established meaning at gitea_mcp_server.py:573 is ctx["roots_aligned"] (canonical_repo_root == process_project_root). Because all task work must live in a branches/ worktree, the new derivation reports aligned False for correctly configured sessions and raises unsafe_process_root_workspace_alignment, refusing every non-read operation. It blocks the author namespace as soon as GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE is bound — which is the documented fix for the publication gap in comment 13253 — and blocks reviewer and merger namespaces whenever a PR lease worktree is held. Feed the gate ctx["roots_aligned"], or express the check as worktree membership of the canonical repository root instead of path equality with the process root. F2 (blocking): _current_runtime_mode_report caches its first result into _STARTUP_RUNTIME_MODE, which is initialised to None rather than captured at import as _STARTUP_PARITY is, and the read-only refresh=True path also seeds it. Session-scoped fields (active_task_workspace and its derived alignment) and mutable fields (dirty_files) are frozen for the process lifetime, so the acceptance criterion 7 dirty-runtime blocker stops applying after the snapshot and one session's workspace binding decides alignment for every later session. Capture immutable process facts once at import and evaluate session-scoped fields per call. F3 (major): no test exercises the real derivation. Every server-wiring test asserting a healthy runtime permits mutations patches _current_runtime_mode_report with a fixture whose workspace_roots_aligned is True, and test_live_report_describes_this_checkout asserts only mode and process root. Add a test that drives _current_runtime_mode_report with a session bound to a branches/ worktree of this repository, forced production guards, and a stable checkout, asserting real_mutations_allowed is true and _runtime_mode_block("gitea.pr.create") is empty. It must fail on the current head and pass after F1 is fixed. Non-blocking, fix only if convenient: lexicographic timestamp comparison in assess_namespace_reproof; _RUNTIME_MODE_GATE_UNDER_TEST inferred from sys.modules rather than an explicit env opt-in; observe_runtime porcelain parsing renders renames as "old -> new". ``` WHAT_HAPPENED: An independent reviewer in the prgs-reviewer namespace acquired the PR #770 reviewer lease, inspected all 8 changed files at the pinned head, re-ran the focused suite, reproduced a blocking fail-closed defect directly against the PR code, and posted exactly one native REQUEST_CHANGES review to Gitea. WHY: Acceptance criteria 6, 8, 10, and 11 are correctly implemented and well tested at the module level, but the acceptance criterion 7 alignment blocker, as derived by the server wiring, inverts its intent: it would refuse every correctly bound author, reviewer, and merger session the moment the enforcement layer is promoted, leaving gitea.read as the only surviving operation. RELATED_PRS: #770 — open, base master, head 9bf3acfef681f9766ae1d93680b03996a93f98a1, review_id 483 REQUEST_CHANGES at that exact head, undismissed and not stale, mergeable true, commits_behind 0, no approval at current head; #616 — closed with merged_at 2026-07-16T14:58:44-05:00 and merge_commit_sha 293808b42d54c4736f8fb55f71b10d93a40ae4ea, acceptance criteria 1-5 only, unrelated head BLOCKERS: code blocker — F1 fail-closed alignment derivation and F2 process-lifetime snapshot of session-scoped facts, both in gitea_mcp_server.py; test blocker — F3, acceptance criterion 9's healthy-runtime case is proven only against a fixture VALIDATION: Reviewer re-ran tests/test_stable_control_runtime.py independently in worktree branches/review-pr-770 at head 9bf3acf: 47 passed, confirming the author's count. git diff --check clean against base 059ee77c1fd483da385d6ccb4d842e808fb54630. Diff independently confirmed as 8 files, 1714 insertions, 6 deletions, no unrelated or generated files, no secrets, tokens, keychain identifiers, or raw service URLs. F1 reproduced by executing the PR's own build_runtime_report with runtime_mode stable-control, a clean checkout on master, and a branches/ worktree workspace: real_mutations_allowed False, blocker unsafe_process_root_workspace_alignment. The author's two full-suite failures were not re-run this session; they are recorded as pre-existing at this identical master SHA and neither touches a file in this diff. LAST_UPDATED_BY: prgs-reviewer (sysadmin) [THREAD STATE LEDGER] Issue #615 — PR #770 reviewed at 9bf3acf; REQUEST_CHANGES posted to Gitea; author is next What is true now: - Issue state: open - Server-side decision state: REQUEST_CHANGES review posted to Gitea as review_id 483 at head 9bf3acfef681f9766ae1d93680b03996a93f98a1, visible, undismissed, not stale - Local verdict/state: REQUEST_CHANGES prepared locally in reviewer worktree branches/review-pr-770 pinned to that head; no author branch or worktree was modified - Latest known validation: focused suite tests/test_stable_control_runtime.py 47 passed, re-run independently this session; F1 reproduced directly against the PR code What changed: - reviewer PR lease acquired for PR #770 (comment 13272) and released after the review was posted to Gitea - independent inspection of all 8 changed files completed at the pinned head - exactly one REQUEST_CHANGES review posted to Gitea, review_id 483 - this durable reviewer handoff recorded What is blocked: - Blocker classification: code blocker Who/what acts next: - Next actor: author - Required action: remediate F1, F2, and F3, push to branch feat/issue-615-runtime-mode-enforcement, and request a fresh independent review - Do not do: merge PR #770 at this head, self-approve, dismiss review 483, open a second PR for this work, or treat the 47 passing tests as proof that F1 is absent - Resume from: review_id 483 on PR #770 at head 9bf3acfef681f9766ae1d93680b03996a93f98a1
Author
Owner

Canonical Issue State

STATE: open — review 483 findings F1, F2, and F3 remediated and fully revalidated in local commit ab34280f9025406d5c392d4e07c9d34d3ecd47fe; publication to PR #770 refused by the #274 branches-only guard, so the PR head remains the unremediated 9bf3acfef6
WHO_IS_NEXT: controller
NEXT_ACTION: Restore a native author publication path for gitea_commit_files, then re-run the full work-issue workflow for #615 to publish local commit ab34280 to PR #770 and request a fresh independent review
NEXT_PROMPT:

Operator/controller step first. Do not request a fresh review of PR #770 yet: its head is still 9bf3acf, which is the exact code review 483 rejected. The remediation exists only as a local commit.

Gap 2 (publication, unchanged from comment 13253 and reproduced again this run): gitea_commit_files exposes no worktree_path parameter. Its workspace resolves to the stable control checkout, and the #274 branches-only mutation guard refuses with "author mutation blocked: workspace is the stable control checkout". The author daemon currently has no worktree binding at all — gitea_get_runtime_context reports active_task_workspace_root as the control checkout with binding source "MCP server process root (default)". Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree through GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE.

Note for whoever closes this gap: comment 13269 recorded a successful publication of this branch at 10:35, so a working author publication path existed in that session and does not exist now. Reconciling why would prevent this from recurring.

After the gap is closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity, profile, runtime, and capability proof, fresh duplicate-work gates, re-verify local commit ab34280f9025406d5c392d4e07c9d34d3ecd47fe in worktree branches/issue-615-runtime-mode-enforcement, publish it to branch feat/issue-615-runtime-mode-enforcement through sanctioned MCP, verify the PR #770 head advanced off 9bf3acf, then stop for a fresh independent reviewer.

WHAT_HAPPENED: A directed resume run recovered the native author lock through the owning-PR dead-session recovery path, remediated all three review 483 findings, revalidated focused and full suites, proved both blocking findings against the pre-fix code independently, and committed the result locally. The single publication attempt through gitea_commit_files was refused fail-closed and was not retried.
WHY: F1 and F2 were code defects in the gitea_mcp_server.py wiring and F3 was the coverage gap that let F1 through; all three are fixed. Publication is blocked by a pre-existing environment gap unrelated to this change: the author namespace has no worktree binding, so the only native commit tool resolves its workspace to the control checkout and the #274 guard refuses.
RELATED_PRS: #770 — open, base master, head still 9bf3acfef6, review 483 REQUEST_CHANGES undismissed and not stale at that head, mergeable true, commits_behind 0, no approval at current head
BLOCKERS: environment/tooling blocker — author publication path unavailable (gitea_commit_files workspace binding, #274 branches-only guard)
VALIDATION: Focused suite in the session worktree at ab34280: tests/test_stable_control_runtime.py 52 passed (47 pre-existing plus 5 new wiring tests). Full suite at ab34280: 3832 passed, 6 skipped, 493 subtests passed, 2 failed. Both failures proved pre-existing independently this session in a fresh clean baseline worktree at pristine master 059ee77c1f (branches/baseline-master-615, clean before and after): identical two signatures, test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue; neither touches a file in this diff. git diff --check clean. F1 and F2 each reproduced against the pre-fix head 9bf3acf in a separate baseline worktree before the fix was written.
LAST_UPDATED_BY: prgs-author (jcwalker3)

[THREAD STATE LEDGER] Issue #615 — review 483 findings remediated locally at ab34280; publication blocked; PR #770 head unchanged

What is true now:

  • Issue state: open
  • Server-side decision state: review 483 REQUEST_CHANGES stands at head 9bf3acfef681f9766ae1d93680b03996a93f98a1; no review decision was mutated this run
  • Local verdict/state: worktree branches/issue-615-runtime-mode-enforcement clean at ab34280f90, two commits ahead of live master 059ee77c1fd483da385d6ccb4d842e808fb54630; that commit is NOT published, so PR #770 still serves 9bf3acf
  • Latest known validation: focused 52 passed, full suite 3832 passed / 6 skipped / 2 failed with independent same-session baseline proof at pristine master

What changed:

  • native author lock for #615 recovered through gitea_lock_issue owning-PR dead-session recovery: recovered true, head_relation equal, pr_head 9bf3acf, pr_number 770, prior session pid 88416 confirmed dead, replacement pid 34249
  • F1 fixed: the runtime gate is now fed ctx["roots_aligned"], restoring the established repository-level meaning of workspace_roots_aligned instead of path equality between the task workspace and the process root
  • F2 fixed: immutable process facts captured at import as _STARTUP_RUNTIME_FACTS; dirty files, workspace binding, and alignment recomputed every call; new stable_control_runtime.observe_dirty_files() with observe_runtime() delegating to it; refresh no longer selects a cache
  • F3 fixed: new TestServerWiringRealDerivation exercises the real derivation with only its inputs patched, covering all four required cases plus unknown-alignment handling
  • pre-fix proof recorded: with a clean master control checkout and a bound branches/ worktree, the old code returned workspace_roots_aligned False, real_mutations_allowed False, blocker unsafe_process_root_workspace_alignment; and a read-only refresh call while clean left a later gate seeing dirty_files empty and permitting mutation on a dirty checkout
  • acceptance criteria 6, 8, 10, and 11 untouched: no change to classify_runtime_mode, the reproof functions, assess_promotion_record, the ledger examples, the promotion runbook, or the promotion helper
  • one gitea_commit_files attempt made and refused fail-closed; not retried

What is blocked:

  • Blocker classification: environment/tooling blocker

Who/what acts next:

  • Next actor: controller
  • Required action: close the author publication gap, then re-run the full work-issue workflow from the beginning to publish ab34280
  • Do not do: request or run a fresh independent review of PR #770 while its head is still 9bf3acf, since that is the exact code review 483 rejected; merge; self-approve; dismiss review 483; open a second PR; publish with a direct git push; hand-edit the issue lock or lease record; reset or clean any worktree; allocate another issue
  • Resume from: this comment plus local commit ab34280f90 in branches/issue-615-runtime-mode-enforcement

Author run record — review 483 remediation

F1 (blocking) — alignment derivation

_current_runtime_mode_report derived the gate input as
realpath(workspace_root) == process_project_root. That redefined
workspace_roots_aligned, whose established meaning at gitea_mcp_server.py:573
is ctx["roots_aligned"]canonical_repo_root == process_project_root, a
repository-level question. Because the global worktree rule requires all task
work to live in a branches/ worktree, the task workspace is required to
differ from the process root, so the old derivation reported aligned=False for
exactly the correctly configured sessions and refused every non-read operation.

The gate is now fed ctx["roots_aligned"], with a comment recording why path
equality is the wrong question.

F2 (blocking) — frozen session-scoped state

_STARTUP_RUNTIME_MODE was initialised to None and seeded by whichever call
ran first, including the read-only refresh=True path. Immutable process facts
are now captured at import as _STARTUP_RUNTIME_FACTS, matching the #420 parity
baseline. dirty_files, the workspace binding, and the derived alignment are
recomputed on every call. stable_control_runtime.observe_dirty_files() splits
out the one runtime fact that legitimately changes during a process lifetime, and
observe_runtime() delegates to it so the porcelain parsing lives in one place.
refresh is retained for the read-only reporting path but no longer selects
between a cache and live state.

F3 (major) — real derivation now under test

TestServerWiringRealDerivation patches only the derivation's inputs — the
import-time facts, the dirty-file read, and the resolved binding — and lets the
real function build the report. Cases: clean stable checkout plus a correctly
bound branches/ worktree permits an otherwise authorized mutation; misaligned
process/canonical roots fail closed; newly dirty task state is detected after an
earlier clean read; a read-only refresh cannot freeze a permissive result; an
unresolvable binding reports unknown alignment rather than alignment proof.

All five fail on the pre-fix head and pass after the fix.

Non-blocking reviewer observations

Deliberately not changed, to keep this remediation bounded to the three findings:
the lexicographic timestamp comparison in assess_namespace_reproof,
_RUNTIME_MODE_GATE_UNDER_TEST inferred from sys.modules, and the
observe_runtime rename rendering. They remain accurate observations and are
suitable follow-up work.

One related design point surfaced while testing: assess_runtime_mutation_gate
blocks only on workspace_roots_aligned is False, not on None. That is correct
and is now asserted — a session with no binding resolved is the ordinary case,
and failing it closed would reintroduce the F1 breakage.

Diff

Three files, 189 insertions, 30 deletions: gitea_mcp_server.py,
stable_control_runtime.py, tests/test_stable_control_runtime.py. No unrelated
files, generated files, caches, credentials, or temporary artifacts.

Publication attempt

gitea_commit_files was called once, after a fresh gitea_whoami and a
gitea_resolve_task_capability(task=commit_files) returning
allowed_in_current_session: true, targeting branch
feat/issue-615-runtime-mode-enforcement with local_path payloads from the
session worktree. It was refused by the #274 branches-only guard: "author
mutation blocked: workspace is the stable control checkout". It was not retried,
because the inputs a session can supply cannot change that outcome — the tool has
no worktree_path parameter and the daemon carries no author worktree binding.

No fallback was used: no direct git push, no raw API call, no browser or CLI
path, no lock-file or lease-record hand-edit, no worktree reset or clean, no
daemon restart, and no second issue selected.

Leftover worktrees for operator disposition

Two baseline worktrees created this run and deliberately retained, since the
scheduled author iteration does not delete worktrees:

  • branches/baseline-pr770-prefix — detached at 9bf3acf, dirty (the new test
    file was copied in to prove the F3 tests fail pre-fix)
  • branches/baseline-master-615 — detached at pristine master 059ee77, clean

The control checkout was not used for task work and remains clean on master at
059ee77c1f.

## Canonical Issue State STATE: open — review 483 findings F1, F2, and F3 remediated and fully revalidated in local commit ab34280f9025406d5c392d4e07c9d34d3ecd47fe; publication to PR #770 refused by the #274 branches-only guard, so the PR head remains the unremediated 9bf3acfef681f9766ae1d93680b03996a93f98a1 WHO_IS_NEXT: controller NEXT_ACTION: Restore a native author publication path for gitea_commit_files, then re-run the full work-issue workflow for #615 to publish local commit ab34280 to PR #770 and request a fresh independent review NEXT_PROMPT: ```text Operator/controller step first. Do not request a fresh review of PR #770 yet: its head is still 9bf3acf, which is the exact code review 483 rejected. The remediation exists only as a local commit. Gap 2 (publication, unchanged from comment 13253 and reproduced again this run): gitea_commit_files exposes no worktree_path parameter. Its workspace resolves to the stable control checkout, and the #274 branches-only mutation guard refuses with "author mutation blocked: workspace is the stable control checkout". The author daemon currently has no worktree binding at all — gitea_get_runtime_context reports active_task_workspace_root as the control checkout with binding source "MCP server process root (default)". Add worktree_path to gitea_commit_files, or bind the gitea-tools prgs-author daemon to the session worktree through GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE. Note for whoever closes this gap: comment 13269 recorded a successful publication of this branch at 10:35, so a working author publication path existed in that session and does not exist now. Reconciling why would prevent this from recurring. After the gap is closed, re-run the full work-issue workflow for issue #615 from the beginning: fresh identity, profile, runtime, and capability proof, fresh duplicate-work gates, re-verify local commit ab34280f9025406d5c392d4e07c9d34d3ecd47fe in worktree branches/issue-615-runtime-mode-enforcement, publish it to branch feat/issue-615-runtime-mode-enforcement through sanctioned MCP, verify the PR #770 head advanced off 9bf3acf, then stop for a fresh independent reviewer. ``` WHAT_HAPPENED: A directed resume run recovered the native author lock through the owning-PR dead-session recovery path, remediated all three review 483 findings, revalidated focused and full suites, proved both blocking findings against the pre-fix code independently, and committed the result locally. The single publication attempt through gitea_commit_files was refused fail-closed and was not retried. WHY: F1 and F2 were code defects in the gitea_mcp_server.py wiring and F3 was the coverage gap that let F1 through; all three are fixed. Publication is blocked by a pre-existing environment gap unrelated to this change: the author namespace has no worktree binding, so the only native commit tool resolves its workspace to the control checkout and the #274 guard refuses. RELATED_PRS: #770 — open, base master, head still 9bf3acfef681f9766ae1d93680b03996a93f98a1, review 483 REQUEST_CHANGES undismissed and not stale at that head, mergeable true, commits_behind 0, no approval at current head BLOCKERS: environment/tooling blocker — author publication path unavailable (gitea_commit_files workspace binding, #274 branches-only guard) VALIDATION: Focused suite in the session worktree at ab34280: tests/test_stable_control_runtime.py 52 passed (47 pre-existing plus 5 new wiring tests). Full suite at ab34280: 3832 passed, 6 skipped, 493 subtests passed, 2 failed. Both failures proved pre-existing independently this session in a fresh clean baseline worktree at pristine master 059ee77c1fd483da385d6ccb4d842e808fb54630 (branches/baseline-master-615, clean before and after): identical two signatures, test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue; neither touches a file in this diff. git diff --check clean. F1 and F2 each reproduced against the pre-fix head 9bf3acf in a separate baseline worktree before the fix was written. LAST_UPDATED_BY: prgs-author (jcwalker3) [THREAD STATE LEDGER] Issue #615 — review 483 findings remediated locally at ab34280; publication blocked; PR #770 head unchanged What is true now: - Issue state: open - Server-side decision state: review 483 REQUEST_CHANGES stands at head 9bf3acfef681f9766ae1d93680b03996a93f98a1; no review decision was mutated this run - Local verdict/state: worktree branches/issue-615-runtime-mode-enforcement clean at ab34280f9025406d5c392d4e07c9d34d3ecd47fe, two commits ahead of live master 059ee77c1fd483da385d6ccb4d842e808fb54630; that commit is NOT published, so PR #770 still serves 9bf3acf - Latest known validation: focused 52 passed, full suite 3832 passed / 6 skipped / 2 failed with independent same-session baseline proof at pristine master What changed: - native author lock for #615 recovered through gitea_lock_issue owning-PR dead-session recovery: recovered true, head_relation equal, pr_head 9bf3acf, pr_number 770, prior session pid 88416 confirmed dead, replacement pid 34249 - F1 fixed: the runtime gate is now fed ctx["roots_aligned"], restoring the established repository-level meaning of workspace_roots_aligned instead of path equality between the task workspace and the process root - F2 fixed: immutable process facts captured at import as _STARTUP_RUNTIME_FACTS; dirty files, workspace binding, and alignment recomputed every call; new stable_control_runtime.observe_dirty_files() with observe_runtime() delegating to it; refresh no longer selects a cache - F3 fixed: new TestServerWiringRealDerivation exercises the real derivation with only its inputs patched, covering all four required cases plus unknown-alignment handling - pre-fix proof recorded: with a clean master control checkout and a bound branches/ worktree, the old code returned workspace_roots_aligned False, real_mutations_allowed False, blocker unsafe_process_root_workspace_alignment; and a read-only refresh call while clean left a later gate seeing dirty_files empty and permitting mutation on a dirty checkout - acceptance criteria 6, 8, 10, and 11 untouched: no change to classify_runtime_mode, the reproof functions, assess_promotion_record, the ledger examples, the promotion runbook, or the promotion helper - one gitea_commit_files attempt made and refused fail-closed; not retried What is blocked: - Blocker classification: environment/tooling blocker Who/what acts next: - Next actor: controller - Required action: close the author publication gap, then re-run the full work-issue workflow from the beginning to publish ab34280 - Do not do: request or run a fresh independent review of PR #770 while its head is still 9bf3acf, since that is the exact code review 483 rejected; merge; self-approve; dismiss review 483; open a second PR; publish with a direct git push; hand-edit the issue lock or lease record; reset or clean any worktree; allocate another issue - Resume from: this comment plus local commit ab34280f9025406d5c392d4e07c9d34d3ecd47fe in branches/issue-615-runtime-mode-enforcement --- ## Author run record — review 483 remediation ### F1 (blocking) — alignment derivation `_current_runtime_mode_report` derived the gate input as `realpath(workspace_root) == process_project_root`. That redefined `workspace_roots_aligned`, whose established meaning at `gitea_mcp_server.py:573` is `ctx["roots_aligned"]` — `canonical_repo_root == process_project_root`, a repository-level question. Because the global worktree rule requires all task work to live in a `branches/` worktree, the task workspace is *required* to differ from the process root, so the old derivation reported `aligned=False` for exactly the correctly configured sessions and refused every non-read operation. The gate is now fed `ctx["roots_aligned"]`, with a comment recording why path equality is the wrong question. ### F2 (blocking) — frozen session-scoped state `_STARTUP_RUNTIME_MODE` was initialised to `None` and seeded by whichever call ran first, including the read-only `refresh=True` path. Immutable process facts are now captured at import as `_STARTUP_RUNTIME_FACTS`, matching the #420 parity baseline. `dirty_files`, the workspace binding, and the derived alignment are recomputed on every call. `stable_control_runtime.observe_dirty_files()` splits out the one runtime fact that legitimately changes during a process lifetime, and `observe_runtime()` delegates to it so the porcelain parsing lives in one place. `refresh` is retained for the read-only reporting path but no longer selects between a cache and live state. ### F3 (major) — real derivation now under test `TestServerWiringRealDerivation` patches only the derivation's inputs — the import-time facts, the dirty-file read, and the resolved binding — and lets the real function build the report. Cases: clean stable checkout plus a correctly bound `branches/` worktree permits an otherwise authorized mutation; misaligned process/canonical roots fail closed; newly dirty task state is detected after an earlier clean read; a read-only refresh cannot freeze a permissive result; an unresolvable binding reports unknown alignment rather than alignment proof. All five fail on the pre-fix head and pass after the fix. ### Non-blocking reviewer observations Deliberately not changed, to keep this remediation bounded to the three findings: the lexicographic timestamp comparison in `assess_namespace_reproof`, `_RUNTIME_MODE_GATE_UNDER_TEST` inferred from `sys.modules`, and the `observe_runtime` rename rendering. They remain accurate observations and are suitable follow-up work. One related design point surfaced while testing: `assess_runtime_mutation_gate` blocks only on `workspace_roots_aligned is False`, not on `None`. That is correct and is now asserted — a session with no binding resolved is the ordinary case, and failing it closed would reintroduce the F1 breakage. ### Diff Three files, 189 insertions, 30 deletions: `gitea_mcp_server.py`, `stable_control_runtime.py`, `tests/test_stable_control_runtime.py`. No unrelated files, generated files, caches, credentials, or temporary artifacts. ### Publication attempt `gitea_commit_files` was called once, after a fresh `gitea_whoami` and a `gitea_resolve_task_capability(task=commit_files)` returning `allowed_in_current_session: true`, targeting branch `feat/issue-615-runtime-mode-enforcement` with `local_path` payloads from the session worktree. It was refused by the #274 branches-only guard: "author mutation blocked: workspace is the stable control checkout". It was not retried, because the inputs a session can supply cannot change that outcome — the tool has no `worktree_path` parameter and the daemon carries no author worktree binding. No fallback was used: no direct git push, no raw API call, no browser or CLI path, no lock-file or lease-record hand-edit, no worktree reset or clean, no daemon restart, and no second issue selected. ### Leftover worktrees for operator disposition Two baseline worktrees created this run and deliberately retained, since the scheduled author iteration does not delete worktrees: - `branches/baseline-pr770-prefix` — detached at 9bf3acf, dirty (the new test file was copied in to prove the F3 tests fail pre-fix) - `branches/baseline-master-615` — detached at pristine master 059ee77, clean The control checkout was not used for task work and remains clean on master at 059ee77c1fd483da385d6ccb4d842e808fb54630.
Author
Owner

[CONTROLLER HANDOFF] PR #770 / Issue #615 — F1–F3 remediation present at head ab34280f; fresh independent review required

Canonical Issue State

STATE:
PR-open

WHO_IS_NEXT:
reviewer

NEXT_ACTION:
Perform a fresh independent prgs-reviewer review of PR #770 at head ab34280f90, explicitly superseding the stale review 483.

NEXT_PROMPT:

Review PR #770 (Closes #615) as a fresh independent prgs-reviewer at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe.

Review 483 recorded REQUEST_CHANGES at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1 and is stale at the current head; it is undismissed, so your verdict must explicitly supersede it. Do not reuse it as the current verdict.

Assess whether commit ab34280f genuinely remediates F1, F2, and F3. Reproduce the F1 case directly against the PR code the way review 483 did: build the runtime report with a stable-control runtime and a branches/ worktree workspace, and confirm real_mutations_allowed is now true and _runtime_mode_block("gitea.pr.create") is empty. Confirm the new TestServerWiringRealDerivation tests fail on 9bf3acf and pass on ab34280f rather than trusting the pass count.

Base master is 059ee77c1fd483da385d6ccb4d842e808fb54630, commits_behind 0, no conflicts, checks not required by live branch protection. Two full-suite failures pre-exist on that base and are reproduced there; treat them as out of scope unless you find otherwise.

The author holds a live native issue lock on #615 for durable ownership. That lock is not a reviewer blocker and must not be edited or released manually. Do not merge and do not self-approve.

WHAT_HAPPENED:
The author session recovered the native issue lock for #615 from a dead session through the owning-PR path, verified that local HEAD, remote branch tip, and live PR head are all ab34280f90, confirmed review 483 is stale at that head, and re-ran focused, full, and pristine-base validation to evidence the F1–F3 remediation already carried in commit ab34280f. No code was changed by this session.

WHY:
The previous author handoff cited head 9bf3acf and no longer described this PR accurately, and the durable issue lock was orphaned by a dead session, so ownership and state both needed restating before a reviewer could act on the correct head.

RELATED_PRS:

BLOCKERS:
none for the author role; the next action belongs to a reviewer.

VALIDATION:
Focused suite tests/test_stable_control_runtime.py 52 passed; full suite 3832 passed / 2 failed / 6 skipped in the issue worktree; pristine base 059ee77 in branches/baseline-master-615 3780 passed / 2 failed / 6 skipped with byte-identical failing node IDs; git diff --check clean. Full detail below.

LAST_UPDATED_BY:
jcwalker3 / prgs-author / 2026-07-20


Purpose:
This comment is the operational handoff for the next controller/LLM session. It corrects the earlier author handoff, which cited the superseded head 9bf3acf and therefore misdescribed the current state of this PR.

Identity/profile:

  • Active profile: prgs-author
  • Authenticated identity: jcwalker3
  • Role: author
  • Self-review / role-conflict proof: gitea_route_task_session(task_type="work-issue") returned allowed_current_session; runtime review_merge_allowed=false; profile forbidden_operations include gitea.pr.approve, gitea.pr.merge, gitea.pr.request_changes. No review verdict was authored, dismissed, or altered by this session.

Target:

Three-way head equality proof (live, this session):

  • Local worktree HEAD: ab34280f9025406d5c392d4e07c9d34d3ecd47fe (git rev-parse HEAD)
  • Remote branch tip / live PR head: ab34280f9025406d5c392d4e07c9d34d3ecd47fe (gitea_assess_pr_sync_status.pr_head_sha)
  • All three values are equal. Base master 059ee77c1fd483da385d6ccb4d842e808fb54630, commits_behind: 0, has_conflicts: false, mergeable: true.
  • Worktree tracked and untracked state clean before and after validation.

Native issue-lock recovery:

  • gitea_lock_issue returned dead_session_recovery.recovered=true via the owning-PR path: prior session pid 34249 is not alive, replacement pid 65790.
  • head_relation: equal across local_head, remote_head, recorded_head, accepted_head, and pr_head, all ab34280f9025406d5c392d4e07c9d34d3ecd47fe, PR #770.
  • lock_freshness.status: live; no competing live lock; competing-branch check clear.

Prior reviewer verdict — stale at the current head:

  • Review 483 (sysadmin, prgs-reviewer) recorded REQUEST_CHANGES at head 9bf3acfef681f9766ae1d93680b03996a93f98a1.
  • gitea_get_pr_review_feedback reports reviewed_head_sha 9bf3acfe... against current_head_sha ab34280f..., with review_feedback_stale: true, author_pushed_after_request_changes: true, stale: true, dismissed: false, approval_at_current_head: false.
  • gitea_assess_pr_sync_status.recommended_next_action: fresh_review_required.
  • Review 483 remains undismissed. An author role cannot and did not dismiss it; a reviewer must supersede it with a fresh verdict at the current head.

Remediation content (commit ab34280f, authored in a prior session; diff and validation verified live this session):

  • F1 (blocking) — the runtime-gate alignment input was derived as realpath(workspace_root) == process_project_root, redefining workspace_roots_aligned, whose established meaning is ctx["roots_aligned"] (canonical_repo_root == process_project_root). Because the global worktree rule requires task work to live in a branches/ worktree, the old derivation reported alignment false for exactly the correctly configured sessions and raised unsafe_process_root_workspace_alignment, refusing every non-read operation. The gate is now fed ctx["roots_aligned"].
  • F2 (blocking) — _current_runtime_mode_report cached its first result into _STARTUP_RUNTIME_MODE, which was initialised to None rather than captured at import, and the refresh=True read-only path seeded it as well. Session-scoped fields (active_task_workspace and its derived alignment) and mutable dirty_files were frozen for the process lifetime. Immutable process facts are now captured at import as _STARTUP_RUNTIME_FACTS, matching the #420 parity baseline; dirty state, workspace binding, and alignment are recomputed per call. New stable_control_runtime.observe_dirty_files() isolates the one fact that legitimately changes during a process lifetime. refresh no longer selects a cache, so a read-only call can neither seed nor weaken a later mutation decision.
  • F3 (major) — no test exercised the real derivation; every server-wiring test asserting a healthy runtime permits mutations patched _current_runtime_mode_report with a fixture whose workspace_roots_aligned was True. New TestServerWiringRealDerivation patches only the derivation's inputs and lets the real function build the report, covering: clean stable checkout plus correctly bound branches/ worktree permits an authorized mutation; misaligned process/canonical roots fail closed; newly dirty task state detected after an earlier clean read; a read-only refresh cannot freeze a permissive result; an unresolvable binding reports unknown alignment rather than alignment proof.
  • The three reviewer non-blocking observations (lexicographic timestamp comparison in assess_namespace_reproof, _RUNTIME_MODE_GATE_UNDER_TEST inferred from sys.modules, observe_runtime porcelain rename rendering) were not changed. They were marked "fix only if convenient" and were deliberately left out of scope to keep the remediation diff minimal.
  • Acceptance criteria 6, 8, 10, and 11 are untouched by this commit.

Files touched by the remediation commit (git diff --stat 9bf3acf ab34280):

  • gitea_mcp_server.py — 71 lines changed; F1 alignment input and F2 import-time fact capture
  • stable_control_runtime.py — 17 lines changed; observe_dirty_files() helper for F2
  • tests/test_stable_control_runtime.py — 131 lines added; F3 real-derivation coverage
  • Total 3 files, 189 insertions, 30 deletions. No unrelated, generated, or credential-bearing files.

Validation (live, run in this session, in the issue worktree):

  • /opt/homebrew/bin/python3 -m pytest tests/test_stable_control_runtime.py -q -s in branches/issue-615-runtime-mode-enforcement → 52 passed (47 prior plus 5 new F3 tests).
  • /opt/homebrew/bin/python3 -m pytest -q -s in branches/issue-615-runtime-mode-enforcement → 3832 passed, 2 failed, 6 skipped, 493 subtests passed.
  • git diff --check 9bf3acf ab34280 → clean.
  • Runner proof: /opt/homebrew/bin/python3 is Python 3.14.5 with pytest 9.0.3. Bare python3 on PATH resolves to 3.13.13, which has no pytest module.

Baseline comparison (live, this session, not carried over from a prior run):

  • Baseline worktree branches/baseline-master-615, detached at 059ee77c1fd483da385d6ccb4d842e808fb54630, tracked and untracked state clean before and after.
  • Identical command /opt/homebrew/bin/python3 -m pytest -q -s on that pristine base → 3780 passed, 2 failed, 6 skipped, 493 subtests passed.
  • The two failing node IDs are byte-identical on base and on the branch:
    • tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe
    • tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue
  • Both failures therefore pre-exist on base 059ee77 and are not caused by this branch. Neither touches a file in this diff. The pass-count delta of +52 equals the focused suite this branch adds.

Server-side mutation ledger:

  • gitea_lock_issue on issue #615 — native issue lock recovered from a dead session and now held live by pid 65790, lease expiry 2026-07-20T20:36:33Z.
  • This handoff comment, plus the paired ledger comment that follows it.
  • No branch, commit, push, PR-body, label, review, or merge mutation was carried out by this session. Merge not performed.

Local-only changes:

  • Test execution in branches/issue-615-runtime-mode-enforcement and branches/baseline-master-615. Both worktrees verified clean after the runs. No file was created, edited, or deleted by this session in any checkout.
  • Control checkout remains on master at 059ee77c1fd483da385d6ccb4d842e808fb54630, clean, and was not used for task work.
  • No worktree was created, removed, pruned, or cleaned.

Blockers:

  • none for the author role. The remaining action belongs to a reviewer.
[CONTROLLER HANDOFF] PR #770 / Issue #615 — F1–F3 remediation present at head ab34280f; fresh independent review required ## Canonical Issue State STATE: PR-open WHO_IS_NEXT: reviewer NEXT_ACTION: Perform a fresh independent prgs-reviewer review of PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe, explicitly superseding the stale review 483. NEXT_PROMPT: ```text Review PR #770 (Closes #615) as a fresh independent prgs-reviewer at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Review 483 recorded REQUEST_CHANGES at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1 and is stale at the current head; it is undismissed, so your verdict must explicitly supersede it. Do not reuse it as the current verdict. Assess whether commit ab34280f genuinely remediates F1, F2, and F3. Reproduce the F1 case directly against the PR code the way review 483 did: build the runtime report with a stable-control runtime and a branches/ worktree workspace, and confirm real_mutations_allowed is now true and _runtime_mode_block("gitea.pr.create") is empty. Confirm the new TestServerWiringRealDerivation tests fail on 9bf3acf and pass on ab34280f rather than trusting the pass count. Base master is 059ee77c1fd483da385d6ccb4d842e808fb54630, commits_behind 0, no conflicts, checks not required by live branch protection. Two full-suite failures pre-exist on that base and are reproduced there; treat them as out of scope unless you find otherwise. The author holds a live native issue lock on #615 for durable ownership. That lock is not a reviewer blocker and must not be edited or released manually. Do not merge and do not self-approve. ``` WHAT_HAPPENED: The author session recovered the native issue lock for #615 from a dead session through the owning-PR path, verified that local HEAD, remote branch tip, and live PR head are all ab34280f9025406d5c392d4e07c9d34d3ecd47fe, confirmed review 483 is stale at that head, and re-ran focused, full, and pristine-base validation to evidence the F1–F3 remediation already carried in commit ab34280f. No code was changed by this session. WHY: The previous author handoff cited head 9bf3acf and no longer described this PR accurately, and the durable issue lock was orphaned by a dead session, so ownership and state both needed restating before a reviewer could act on the correct head. RELATED_PRS: - #770 BLOCKERS: none for the author role; the next action belongs to a reviewer. VALIDATION: Focused suite tests/test_stable_control_runtime.py 52 passed; full suite 3832 passed / 2 failed / 6 skipped in the issue worktree; pristine base 059ee77 in branches/baseline-master-615 3780 passed / 2 failed / 6 skipped with byte-identical failing node IDs; git diff --check clean. Full detail below. LAST_UPDATED_BY: jcwalker3 / prgs-author / 2026-07-20 --- Purpose: This comment is the operational handoff for the next controller/LLM session. It corrects the earlier author handoff, which cited the superseded head 9bf3acf and therefore misdescribed the current state of this PR. Identity/profile: - Active profile: prgs-author - Authenticated identity: jcwalker3 - Role: author - Self-review / role-conflict proof: `gitea_route_task_session(task_type="work-issue")` returned `allowed_current_session`; runtime `review_merge_allowed=false`; profile `forbidden_operations` include `gitea.pr.approve`, `gitea.pr.merge`, `gitea.pr.request_changes`. No review verdict was authored, dismissed, or altered by this session. Target: - Repo: Scaled-Tech-Consulting/Gitea-Tools (remote `prgs`) - Issue: #615 - PR: #770 - Branch: feat/issue-615-runtime-mode-enforcement - Pinned head SHA: ab34280f9025406d5c392d4e07c9d34d3ecd47fe - Worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/issue-615-runtime-mode-enforcement Three-way head equality proof (live, this session): - Local worktree HEAD: `ab34280f9025406d5c392d4e07c9d34d3ecd47fe` (`git rev-parse HEAD`) - Remote branch tip / live PR head: `ab34280f9025406d5c392d4e07c9d34d3ecd47fe` (`gitea_assess_pr_sync_status.pr_head_sha`) - All three values are equal. Base master `059ee77c1fd483da385d6ccb4d842e808fb54630`, `commits_behind: 0`, `has_conflicts: false`, `mergeable: true`. - Worktree tracked and untracked state clean before and after validation. Native issue-lock recovery: - `gitea_lock_issue` returned `dead_session_recovery.recovered=true` via the owning-PR path: prior session pid 34249 is not alive, replacement pid 65790. - `head_relation: equal` across local_head, remote_head, recorded_head, accepted_head, and pr_head, all `ab34280f9025406d5c392d4e07c9d34d3ecd47fe`, PR #770. - `lock_freshness.status: live`; no competing live lock; competing-branch check `clear`. Prior reviewer verdict — stale at the current head: - Review 483 (sysadmin, prgs-reviewer) recorded REQUEST_CHANGES at head `9bf3acfef681f9766ae1d93680b03996a93f98a1`. - `gitea_get_pr_review_feedback` reports `reviewed_head_sha` 9bf3acfe... against `current_head_sha` ab34280f..., with `review_feedback_stale: true`, `author_pushed_after_request_changes: true`, `stale: true`, `dismissed: false`, `approval_at_current_head: false`. - `gitea_assess_pr_sync_status.recommended_next_action: fresh_review_required`. - Review 483 remains undismissed. An author role cannot and did not dismiss it; a reviewer must supersede it with a fresh verdict at the current head. Remediation content (commit ab34280f, authored in a prior session; diff and validation verified live this session): - F1 (blocking) — the runtime-gate alignment input was derived as `realpath(workspace_root) == process_project_root`, redefining `workspace_roots_aligned`, whose established meaning is `ctx["roots_aligned"]` (`canonical_repo_root == process_project_root`). Because the global worktree rule requires task work to live in a `branches/` worktree, the old derivation reported alignment false for exactly the correctly configured sessions and raised `unsafe_process_root_workspace_alignment`, refusing every non-read operation. The gate is now fed `ctx["roots_aligned"]`. - F2 (blocking) — `_current_runtime_mode_report` cached its first result into `_STARTUP_RUNTIME_MODE`, which was initialised to `None` rather than captured at import, and the `refresh=True` read-only path seeded it as well. Session-scoped fields (`active_task_workspace` and its derived alignment) and mutable `dirty_files` were frozen for the process lifetime. Immutable process facts are now captured at import as `_STARTUP_RUNTIME_FACTS`, matching the #420 parity baseline; dirty state, workspace binding, and alignment are recomputed per call. New `stable_control_runtime.observe_dirty_files()` isolates the one fact that legitimately changes during a process lifetime. `refresh` no longer selects a cache, so a read-only call can neither seed nor weaken a later mutation decision. - F3 (major) — no test exercised the real derivation; every server-wiring test asserting a healthy runtime permits mutations patched `_current_runtime_mode_report` with a fixture whose `workspace_roots_aligned` was True. New `TestServerWiringRealDerivation` patches only the derivation's inputs and lets the real function build the report, covering: clean stable checkout plus correctly bound `branches/` worktree permits an authorized mutation; misaligned process/canonical roots fail closed; newly dirty task state detected after an earlier clean read; a read-only refresh cannot freeze a permissive result; an unresolvable binding reports unknown alignment rather than alignment proof. - The three reviewer non-blocking observations (lexicographic timestamp comparison in `assess_namespace_reproof`, `_RUNTIME_MODE_GATE_UNDER_TEST` inferred from `sys.modules`, `observe_runtime` porcelain rename rendering) were not changed. They were marked "fix only if convenient" and were deliberately left out of scope to keep the remediation diff minimal. - Acceptance criteria 6, 8, 10, and 11 are untouched by this commit. Files touched by the remediation commit (`git diff --stat 9bf3acf ab34280`): - `gitea_mcp_server.py` — 71 lines changed; F1 alignment input and F2 import-time fact capture - `stable_control_runtime.py` — 17 lines changed; `observe_dirty_files()` helper for F2 - `tests/test_stable_control_runtime.py` — 131 lines added; F3 real-derivation coverage - Total 3 files, 189 insertions, 30 deletions. No unrelated, generated, or credential-bearing files. Validation (live, run in this session, in the issue worktree): - `/opt/homebrew/bin/python3 -m pytest tests/test_stable_control_runtime.py -q -s` in `branches/issue-615-runtime-mode-enforcement` → 52 passed (47 prior plus 5 new F3 tests). - `/opt/homebrew/bin/python3 -m pytest -q -s` in `branches/issue-615-runtime-mode-enforcement` → 3832 passed, 2 failed, 6 skipped, 493 subtests passed. - `git diff --check 9bf3acf ab34280` → clean. - Runner proof: `/opt/homebrew/bin/python3` is Python 3.14.5 with pytest 9.0.3. Bare `python3` on PATH resolves to 3.13.13, which has no pytest module. Baseline comparison (live, this session, not carried over from a prior run): - Baseline worktree `branches/baseline-master-615`, detached at `059ee77c1fd483da385d6ccb4d842e808fb54630`, tracked and untracked state clean before and after. - Identical command `/opt/homebrew/bin/python3 -m pytest -q -s` on that pristine base → 3780 passed, 2 failed, 6 skipped, 493 subtests passed. - The two failing node IDs are byte-identical on base and on the branch: - `tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe` - `tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue` - Both failures therefore pre-exist on base `059ee77` and are not caused by this branch. Neither touches a file in this diff. The pass-count delta of +52 equals the focused suite this branch adds. Server-side mutation ledger: - `gitea_lock_issue` on issue #615 — native issue lock recovered from a dead session and now held live by pid 65790, lease expiry 2026-07-20T20:36:33Z. - This handoff comment, plus the paired ledger comment that follows it. - No branch, commit, push, PR-body, label, review, or merge mutation was carried out by this session. Merge not performed. Local-only changes: - Test execution in `branches/issue-615-runtime-mode-enforcement` and `branches/baseline-master-615`. Both worktrees verified clean after the runs. No file was created, edited, or deleted by this session in any checkout. - Control checkout remains on `master` at `059ee77c1fd483da385d6ccb4d842e808fb54630`, clean, and was not used for task work. - No worktree was created, removed, pruned, or cleaned. Blockers: - none for the author role. The remaining action belongs to a reviewer.
Author
Owner

[THREAD STATE LEDGER] PR #770 / Issue #615 — F1–F3 remediation sits at head ab34280f; a fresh independent reviewer verdict is the next step

Canonical Issue State

STATE:
PR-open

WHO_IS_NEXT:
reviewer

NEXT_ACTION:
Perform a fresh independent prgs-reviewer review of PR #770 at head ab34280f90, explicitly superseding the stale review 483.

NEXT_PROMPT:

Review PR #770 (Closes #615) as a fresh independent prgs-reviewer at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Review 483 recorded REQUEST_CHANGES at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1, is stale at the current head, and remains undismissed, so your verdict must explicitly supersede it. Verify F1, F2, and F3 against the real derivation rather than the pass count. Do not merge and do not self-approve.

WHAT_HAPPENED:
The author session recovered the orphaned native issue lock, proved three-way head equality at ab34280f, confirmed review 483 is stale at that head, and re-ran focused, full, and pristine-base validation. No code changed.

WHY:
The prior handoff described the superseded head 9bf3acf, so the canonical thread state had to be corrected before a reviewer acts.

RELATED_PRS:

BLOCKERS:
none for the author role; the next step belongs to a reviewer.

VALIDATION:
Focused suite 52 passed; full suite 3832 passed / 2 failed / 6 skipped; pristine base 059ee77 3780 passed / 2 failed / 6 skipped with identical failing node IDs; git diff --check clean.

LAST_UPDATED_BY:
jcwalker3 / prgs-author / 2026-07-20


What is true now:

  • PR state: open, base master, commits_behind: 0, has_conflicts: false, mergeable: true
  • Issue state: open, status:in-progress, native author issue lock held live by pid 65790 until 2026-07-20T20:36:33Z
  • Current head SHA: ab34280f9025406d5c392d4e07c9d34d3ecd47fe, equal across local worktree HEAD, remote branch tip, and live PR head
  • Server-side decision state: REQUEST_CHANGES posted to Gitea as review 483 at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1; that verdict is stale at the current head, is undismissed, and no verdict of any kind exists at ab34280f
  • Local verdict/state: author remediation for F1, F2, and F3 carried in commit ab34280f; no review verdict was prepared, held, or implied locally by this author session
  • Latest known validation: focused suite tests/test_stable_control_runtime.py 52 passed; full suite 3832 passed / 2 failed / 6 skipped in the issue worktree; pristine base 059ee77 3780 passed / 2 failed / 6 skipped in branches/baseline-master-615, with byte-identical failing node IDs proving both failures pre-exist on the base

What changed:

  • Native issue lock for #615 recovered from dead session pid 34249 through the owning-PR path; dead_session_recovery.recovered=true, head_relation: equal
  • Three-way head equality re-proved live at ab34280f; the earlier author handoff citing 9bf3acf is superseded by handoff comment 13287
  • Full-suite and pristine-base validation re-run live this session rather than carried over from the prior session
  • No source, test, or documentation file was modified; no commit, push, branch, PR-body, or label mutation occurred

What is blocked:

  • Blocker classification: no blocker
  • Nothing prevents the reviewer step. Review 483 cannot be dismissed by an author role and does not need to be; a fresh verdict at the current head supersedes it.

Who/what acts next:

  • Next actor: reviewer
  • Required action: perform a fresh independent prgs-reviewer review of PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe, reproducing the F1 case against the real derivation and confirming the new TestServerWiringRealDerivation tests fail at 9bf3acf and pass at ab34280f
  • Do not do: reuse review 483 as the current verdict; treat the 52 passing focused tests as sufficient proof that F1 is remediated; merge this head; self-approve; edit or manually release the author issue lock; remove the branches/baseline-master-615 or branches/issue-615-runtime-mode-enforcement worktrees
  • Resume from: handoff comment 13287 on this issue, pinned at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe
[THREAD STATE LEDGER] PR #770 / Issue #615 — F1–F3 remediation sits at head ab34280f; a fresh independent reviewer verdict is the next step ## Canonical Issue State STATE: PR-open WHO_IS_NEXT: reviewer NEXT_ACTION: Perform a fresh independent prgs-reviewer review of PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe, explicitly superseding the stale review 483. NEXT_PROMPT: ```text Review PR #770 (Closes #615) as a fresh independent prgs-reviewer at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Review 483 recorded REQUEST_CHANGES at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1, is stale at the current head, and remains undismissed, so your verdict must explicitly supersede it. Verify F1, F2, and F3 against the real derivation rather than the pass count. Do not merge and do not self-approve. ``` WHAT_HAPPENED: The author session recovered the orphaned native issue lock, proved three-way head equality at ab34280f, confirmed review 483 is stale at that head, and re-ran focused, full, and pristine-base validation. No code changed. WHY: The prior handoff described the superseded head 9bf3acf, so the canonical thread state had to be corrected before a reviewer acts. RELATED_PRS: - #770 BLOCKERS: none for the author role; the next step belongs to a reviewer. VALIDATION: Focused suite 52 passed; full suite 3832 passed / 2 failed / 6 skipped; pristine base 059ee77 3780 passed / 2 failed / 6 skipped with identical failing node IDs; git diff --check clean. LAST_UPDATED_BY: jcwalker3 / prgs-author / 2026-07-20 --- What is true now: - PR state: open, base master, `commits_behind: 0`, `has_conflicts: false`, `mergeable: true` - Issue state: open, `status:in-progress`, native author issue lock held live by pid 65790 until 2026-07-20T20:36:33Z - Current head SHA: `ab34280f9025406d5c392d4e07c9d34d3ecd47fe`, equal across local worktree HEAD, remote branch tip, and live PR head - Server-side decision state: REQUEST_CHANGES posted to Gitea as review 483 at the superseded head `9bf3acfef681f9766ae1d93680b03996a93f98a1`; that verdict is stale at the current head, is undismissed, and no verdict of any kind exists at `ab34280f` - Local verdict/state: author remediation for F1, F2, and F3 carried in commit `ab34280f`; no review verdict was prepared, held, or implied locally by this author session - Latest known validation: focused suite `tests/test_stable_control_runtime.py` 52 passed; full suite 3832 passed / 2 failed / 6 skipped in the issue worktree; pristine base `059ee77` 3780 passed / 2 failed / 6 skipped in `branches/baseline-master-615`, with byte-identical failing node IDs proving both failures pre-exist on the base What changed: - Native issue lock for #615 recovered from dead session pid 34249 through the owning-PR path; `dead_session_recovery.recovered=true`, `head_relation: equal` - Three-way head equality re-proved live at `ab34280f`; the earlier author handoff citing `9bf3acf` is superseded by handoff comment 13287 - Full-suite and pristine-base validation re-run live this session rather than carried over from the prior session - No source, test, or documentation file was modified; no commit, push, branch, PR-body, or label mutation occurred What is blocked: - Blocker classification: no blocker - Nothing prevents the reviewer step. Review 483 cannot be dismissed by an author role and does not need to be; a fresh verdict at the current head supersedes it. Who/what acts next: - Next actor: reviewer - Required action: perform a fresh independent prgs-reviewer review of PR #770 at head `ab34280f9025406d5c392d4e07c9d34d3ecd47fe`, reproducing the F1 case against the real derivation and confirming the new `TestServerWiringRealDerivation` tests fail at `9bf3acf` and pass at `ab34280f` - Do not do: reuse review 483 as the current verdict; treat the 52 passing focused tests as sufficient proof that F1 is remediated; merge this head; self-approve; edit or manually release the author issue lock; remove the `branches/baseline-master-615` or `branches/issue-615-runtime-mode-enforcement` worktrees - Resume from: handoff comment 13287 on this issue, pinned at head `ab34280f9025406d5c392d4e07c9d34d3ecd47fe`
Owner

[THREAD STATE LEDGER] PR #770 / Issue #615 — independent reviewer APPROVE recorded as review 484 at head ab34280f; merger acts next

Canonical Issue State

STATE:
PR-open

CANONICAL_ITEM:
review 484 (APPROVED) on PR #770 at head ab34280f90

SUPERSEDED_ITEM:
review 483 (REQUEST_CHANGES) on PR #770 at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1; it was stale at the current head and the server marked it dismissed once review 484 landed

WHO_IS_NEXT:
merger

NEXT_ACTION:
A prgs-merger session reassesses PR #770 at head ab34280f90 under its own gates and decides the merge; the reviewer phase granted no merge authorization.

NEXT_PROMPT:

Assess PR #770 (Closes #615) for merge as prgs-merger at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe.

An independent prgs-reviewer (sysadmin, distinct from PR author jcwalker3) recorded APPROVE as review 484 at that exact head. Confirm live through gitea_get_pr_review_feedback that approval_visible is true, approval_at_current_head is true, the review is undismissed and non-stale, and has_blocking_change_requests is false, before acting on it. If the head has moved, that approval is void and a fresh independent review is required.

Base master is 059ee77c1fd483da385d6ccb4d842e808fb54630, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection. Two full-suite failures pre-exist on that base and were independently reproduced there by the reviewer; they are out of scope for this PR.

The author holds a live native issue lock on #615. That lock is not a merger blocker and must not be edited or released manually. Acquire the merger lease through the native tool, reassess live state immediately before merging, and do not review or amend the branch.

WHAT_HAPPENED:
A fresh independent prgs-reviewer session pinned head ab34280f, verified handoff comment 13287 and ledger comment 13290 as accurate, treated review 483 as stale, and independently reproduced all three findings rather than trusting the author's pass counts. F1 and F2 were re-tested with a reviewer-authored harness that exercises the real _current_runtime_mode_report and the real unpatched _resolve_namespace_mutation_context against a real branches/ worktree, with the same harness run against the superseded head as a control. The five new F3 tests were run against the pre-fix code, where all five fail, and against this head, where all five pass. Exactly one native verdict was posted to Gitea: APPROVE, recorded as review 484.

WHY:
All three findings from review 483 are remediated at this head, confirmed against the real code path rather than against fixtures. F1 now feeds the gate the established repository-level roots_aligned contract, so a correctly bound branches/ session is permitted instead of refused, and the deadlock-on-promotion scenario no longer reproduces. F2 captures only immutable process facts once at module load and recomputes dirty state, workspace binding, and alignment per call, so the acceptance-criterion-7 dirty blocker keeps applying and a read-only call can no longer seed a permissive cache. F3 adds coverage that exercises the real derivation and genuinely fails against the pre-fix code.

RELATED_PRS:

BLOCKERS:
none for the reviewer role; the next action belongs to a merger.

VALIDATION:
Focused suite tests/test_stable_control_runtime.py 52 passed at the pinned head in worktree branches/review-pr-770. Affected integration set (stable_control_runtime, namespace_workspace_binding, workspace_guard_alignment, master_parity_gate, mcp_stale_runtime, role_namespace_gate, runtime_clarity, workspace_mutation_consistency, stable_control_runtime_policy_docs, create_issue_workspace_guard, issue_comment_workspace_guard, preflight_workspace_repo_forwarding) 153 passed, 20 subtests passed. Controlled F1 experiment: identical harness and identical real binding yields workspace_roots_aligned False and real_mutations_allowed False at 9bf3acf, and True/True with an empty block list at ab34280f. F2 dynamic sequence at ab34280f: clean permits, newly dirty blocks with the dirty-runtime reason, clean again permits; the same sequence at 9bf3acf never observed the injected dirt. F3: 5 fail pre-fix, 5 pass at this head. Both author-claimed pre-existing failures reproduced identically on pristine base 059ee77. git diff --check clean; remediation scope 3 files, 189 insertions, 30 deletions; secret scan clean.

LAST_UPDATED_BY:
sysadmin / prgs-reviewer / 2026-07-20

NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace as identity sysadmin, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, final-report schema a7634e7b8689, head pinned ab34280f90, reviewer lease comment 13292, review 484, native read-after-write confirming approval_visible true and approval_at_current_head true.


What is true now:

  • PR state: open, base master, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection
  • Issue state: open, status:in-progress, native author issue lock held by the author session; untouched by this reviewer session
  • Current head SHA: ab34280f90, re-verified live immediately before the verdict was posted to Gitea
  • Server-side decision state: review 484 APPROVED at ab34280f9025406d5c392d4e07c9d34d3ecd47fe; read-after-write confirms approval_visible true, approval_at_current_head true, dismissed false, stale false, has_blocking_change_requests false
  • Local verdict/state: verdict prepared in reviewer worktree branches/review-pr-770 pinned at the same head and posted to Gitea from there; that worktree is clean and remains detached at ab34280f
  • Latest known validation: as recorded under VALIDATION above

What changed:

  • Fresh independent reviewer inspection completed at head ab34280f, superseding the stale review 483
  • APPROVE posted to Gitea as review 484; the server marked review 483 dismissed as a consequence of the superseding verdict, which was not requested by this session
  • Reviewer lease acquired for PR #770 (comment 13292) and released at the end of this phase
  • No branch, commit, push, PR-body, label, issue-lock, or merge mutation was performed; no merge was performed

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: merger
  • Required action: a prgs-merger session reassesses PR #770 at head ab34280f90 under its own gates and decides the merge
  • Do not do: treat review 484 as merge authorization; act on it without re-verifying the head; edit or manually release the author issue lock on #615; amend the branch; remove the branches/issue-615-runtime-mode-enforcement, branches/baseline-master-615, or branches/review-pr-770 worktrees
  • Resume from: review 484 on PR #770 at head ab34280f90

Non-blocking follow-ups carried forward, not required for merge:

  • assess_namespace_reproof compares timestamps lexicographically; correct for identical ISO-8601 UTC strings, silently wrong for mixed formats or offsets
  • _RUNTIME_MODE_GATE_UNDER_TEST is inferred from sys.modules at module load; not a live defect today, but a future dependency pulling unittest.mock into module scope would silently disable the gate in production, where an explicit environment opt-in would fail safe
  • observe_runtime porcelain parsing renders a rename as "old -> new"; cosmetic
  • capturing _STARTUP_RUNTIME_FACTS at module load moves several git subprocess calls into module load time, and the dirty_files key inside that captured dict is now dead because the gate always reads live dirt
[THREAD STATE LEDGER] PR #770 / Issue #615 — independent reviewer APPROVE recorded as review 484 at head ab34280f; merger acts next ## Canonical Issue State STATE: PR-open CANONICAL_ITEM: review 484 (APPROVED) on PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe SUPERSEDED_ITEM: review 483 (REQUEST_CHANGES) on PR #770 at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1; it was stale at the current head and the server marked it dismissed once review 484 landed WHO_IS_NEXT: merger NEXT_ACTION: A prgs-merger session reassesses PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe under its own gates and decides the merge; the reviewer phase granted no merge authorization. NEXT_PROMPT: ```text Assess PR #770 (Closes #615) for merge as prgs-merger at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. An independent prgs-reviewer (sysadmin, distinct from PR author jcwalker3) recorded APPROVE as review 484 at that exact head. Confirm live through gitea_get_pr_review_feedback that approval_visible is true, approval_at_current_head is true, the review is undismissed and non-stale, and has_blocking_change_requests is false, before acting on it. If the head has moved, that approval is void and a fresh independent review is required. Base master is 059ee77c1fd483da385d6ccb4d842e808fb54630, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection. Two full-suite failures pre-exist on that base and were independently reproduced there by the reviewer; they are out of scope for this PR. The author holds a live native issue lock on #615. That lock is not a merger blocker and must not be edited or released manually. Acquire the merger lease through the native tool, reassess live state immediately before merging, and do not review or amend the branch. ``` WHAT_HAPPENED: A fresh independent prgs-reviewer session pinned head ab34280f, verified handoff comment 13287 and ledger comment 13290 as accurate, treated review 483 as stale, and independently reproduced all three findings rather than trusting the author's pass counts. F1 and F2 were re-tested with a reviewer-authored harness that exercises the real `_current_runtime_mode_report` and the real unpatched `_resolve_namespace_mutation_context` against a real `branches/` worktree, with the same harness run against the superseded head as a control. The five new F3 tests were run against the pre-fix code, where all five fail, and against this head, where all five pass. Exactly one native verdict was posted to Gitea: APPROVE, recorded as review 484. WHY: All three findings from review 483 are remediated at this head, confirmed against the real code path rather than against fixtures. F1 now feeds the gate the established repository-level `roots_aligned` contract, so a correctly bound `branches/` session is permitted instead of refused, and the deadlock-on-promotion scenario no longer reproduces. F2 captures only immutable process facts once at module load and recomputes dirty state, workspace binding, and alignment per call, so the acceptance-criterion-7 dirty blocker keeps applying and a read-only call can no longer seed a permissive cache. F3 adds coverage that exercises the real derivation and genuinely fails against the pre-fix code. RELATED_PRS: - #770 BLOCKERS: none for the reviewer role; the next action belongs to a merger. VALIDATION: Focused suite tests/test_stable_control_runtime.py 52 passed at the pinned head in worktree branches/review-pr-770. Affected integration set (stable_control_runtime, namespace_workspace_binding, workspace_guard_alignment, master_parity_gate, mcp_stale_runtime, role_namespace_gate, runtime_clarity, workspace_mutation_consistency, stable_control_runtime_policy_docs, create_issue_workspace_guard, issue_comment_workspace_guard, preflight_workspace_repo_forwarding) 153 passed, 20 subtests passed. Controlled F1 experiment: identical harness and identical real binding yields workspace_roots_aligned False and real_mutations_allowed False at 9bf3acf, and True/True with an empty block list at ab34280f. F2 dynamic sequence at ab34280f: clean permits, newly dirty blocks with the dirty-runtime reason, clean again permits; the same sequence at 9bf3acf never observed the injected dirt. F3: 5 fail pre-fix, 5 pass at this head. Both author-claimed pre-existing failures reproduced identically on pristine base 059ee77. git diff --check clean; remediation scope 3 files, 189 insertions, 30 deletions; secret scan clean. LAST_UPDATED_BY: sysadmin / prgs-reviewer / 2026-07-20 NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace as identity sysadmin, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, final-report schema a7634e7b8689, head pinned ab34280f9025406d5c392d4e07c9d34d3ecd47fe, reviewer lease comment 13292, review 484, native read-after-write confirming approval_visible true and approval_at_current_head true. --- What is true now: - PR state: open, base master, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection - Issue state: open, status:in-progress, native author issue lock held by the author session; untouched by this reviewer session - Current head SHA: ab34280f9025406d5c392d4e07c9d34d3ecd47fe, re-verified live immediately before the verdict was posted to Gitea - Server-side decision state: review 484 APPROVED at ab34280f9025406d5c392d4e07c9d34d3ecd47fe; read-after-write confirms approval_visible true, approval_at_current_head true, dismissed false, stale false, has_blocking_change_requests false - Local verdict/state: verdict prepared in reviewer worktree branches/review-pr-770 pinned at the same head and posted to Gitea from there; that worktree is clean and remains detached at ab34280f - Latest known validation: as recorded under VALIDATION above What changed: - Fresh independent reviewer inspection completed at head ab34280f, superseding the stale review 483 - APPROVE posted to Gitea as review 484; the server marked review 483 dismissed as a consequence of the superseding verdict, which was not requested by this session - Reviewer lease acquired for PR #770 (comment 13292) and released at the end of this phase - No branch, commit, push, PR-body, label, issue-lock, or merge mutation was performed; no merge was performed What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: merger - Required action: a prgs-merger session reassesses PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe under its own gates and decides the merge - Do not do: treat review 484 as merge authorization; act on it without re-verifying the head; edit or manually release the author issue lock on #615; amend the branch; remove the branches/issue-615-runtime-mode-enforcement, branches/baseline-master-615, or branches/review-pr-770 worktrees - Resume from: review 484 on PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe Non-blocking follow-ups carried forward, not required for merge: - assess_namespace_reproof compares timestamps lexicographically; correct for identical ISO-8601 UTC strings, silently wrong for mixed formats or offsets - _RUNTIME_MODE_GATE_UNDER_TEST is inferred from sys.modules at module load; not a live defect today, but a future dependency pulling unittest.mock into module scope would silently disable the gate in production, where an explicit environment opt-in would fail safe - observe_runtime porcelain parsing renders a rename as "old -> new"; cosmetic - capturing _STARTUP_RUNTIME_FACTS at module load moves several git subprocess calls into module load time, and the dirty_files key inside that captured dict is now dead because the gate always reads live dirt
sysadmin removed the status:in-progress label 2026-07-20 12:37:11 -05:00
Author
Owner

Stable control runtime — promotion record (AC4, runbook §3.7)

Archival of the completed stable-runtime promotion for PR #770. Emitted by the read-only scripts/promote-stable-runtime helper, which returned assess_promotion_record valid=true. No Git state was altered by this phase.

Promotion record

Field Value
previous_runtime_sha 059ee77c1fd483da385d6ccb4d842e808fb54630
promoted_runtime_sha 0568f44cb2d87e78fd394a27a670e33c84f7842f
source_branch feat/issue-615-runtime-mode-enforcement
source_pr #770
restart_method IDE client reconnect (/mcp); already in effect at session start, verified in-parity, not performed by this session
health_check_proof gitea_assess_master_parity all four namespaces: in_parity=true, stale=false, restart_required=false, startup_head == current_head == 0568f44cb2d87e78fd394a27a670e33c84f7842f
identity_proof gitea-tools=jcwalker3/prgs-author; gitea-reviewer=sysadmin/prgs-reviewer; gitea-merger=sysadmin/prgs-merger; gitea-reconciler=sysadmin/prgs-reconciler
profile_proof gitea_get_runtime_context active_profile per namespace: prgs-author, prgs-reviewer, prgs-merger, prgs-reconciler
workspace_proof mcp_process_root == canonical_repository_root == /Users/jasonwalker/Development/Gitea-Tools; workspace_roots_aligned=true; dirty_files=[]; bound branches/ worktree accepted via worktree_path binding, not rejected as root misalignment
mutation_capability_proof real_mutations_allowed=true, mutation_block_reasons=[], gate_enforced=true in all four namespaces
rollback_instructions git -C <stable-root> merge --ff-only 059ee77c1fd483da385d6ccb4d842e808fb54630; reconnect client (/mcp); re-prove all four namespaces per runbook §5

assess_promotion_record()valid=true, missing_fields=[], reasons=[].

SHA on every surface

Surface SHA
PR #770 merge_commit_sha 0568f44cb2d87e78fd394a27a670e33c84f7842f
Live prgs/master 0568f44cb2d87e78fd394a27a670e33c84f7842f
Control checkout HEAD (master, clean) 0568f44cb2d87e78fd394a27a670e33c84f7842f
gitea-tools startup_head / current_head 0568f44… / 0568f44…
gitea-reviewer startup_head / current_head 0568f44… / 0568f44…
gitea-merger startup_head / current_head 0568f44… / 0568f44…
gitea-reconciler startup_head / current_head 0568f44… / 0568f44…

Runtime classification in all four namespaces: runtime_mode=stable-control, in_parity=true, restart_required=false, stop_required=false, real_mutations_allowed=true, dev_worktree_launched=false, gate_enforced=true.

Caveat on previous_runtime_sha

previous_runtime_sha=059ee77… is derived by the helper from Git ancestry (the first-parent of the promoted merge commit), not from a live observation of a daemon serving 059ee77… before promotion. The control checkout and all four namespaces already reported 0568f44… at the start of the archiving session, so no pre-promotion daemon reading was captured. The promoted-state evidence above is direct and live; the pre-promotion SHA is inferential.

Canonical Issue State

STATE:
closed

WHO_IS_NEXT:
author

NEXT_ACTION:
Resume normal workflow phases on the promoted stable-control runtime at 0568f44cb2.

NEXT_PROMPT:
Run one bounded author phase for Scaled-Tech-Consulting/Gitea-Tools on the promoted stable-control runtime (0568f44cb2). Verify identity, runtime mode, and parity before any mutation. Do not promote again without a fresh promotion record.

WHAT_HAPPENED:
PR #770 reached state=closed with merge_commit_sha 0568f44cb2, and this issue reached state=closed. The stable control runtime was advanced to that SHA and reloaded via client reconnect. All four MCP namespaces were independently re-proven per runbook §5, and assess_promotion_record returned valid=true for the record archived in this comment.

WHY:
AC4 requires promotion checklist fields to be documented and used for real promotions. Runbook §3.7 requires the record, once assess_promotion_record returns valid=true, to be posted as a durable comment on the tracking issue so the promotion is auditable without chat history.

RELATED_DISCUSSION:
none

RELATED_PRS:

BRANCH:
master

HEAD_SHA:
0568f44cb2

VALIDATION:
assess_promotion_record valid=true, missing_fields=[]. gitea_assess_master_parity in_parity=true / restart_required=false across gitea-tools, gitea-reviewer, gitea-merger, gitea-reconciler. PR #770 state=closed with merge_commit_sha 0568f44cb2.

BLOCKERS:
none

LAST_UPDATED_BY:
jcwalker3 / prgs-author / 2026-07-20

[THREAD STATE LEDGER] Runtime — promotion completed and re-proven

What is true now:

  • Runtime mode: stable-control
  • Runtime git SHA: 0568f44cb2
  • Server-side decision state: server-side state changed
  • Local verdict/state: promotion record carries every required field
  • Latest known validation: assess_promotion_record valid=true; all namespaces re-proven

What changed:

  • stable control runtime advanced to the promoted SHA and reloaded by the operator

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: author
  • Required action: resume normal workflow phases on the promoted runtime
  • Do not do: promote again without a fresh record
  • Resume from: docs/stable-runtime-promotion-runbook.md section 4
# Stable control runtime — promotion record (AC4, runbook §3.7) Archival of the completed stable-runtime promotion for PR #770. Emitted by the read-only `scripts/promote-stable-runtime` helper, which returned `assess_promotion_record valid=true`. No Git state was altered by this phase. ## Promotion record | Field | Value | |---|---| | `previous_runtime_sha` | `059ee77c1fd483da385d6ccb4d842e808fb54630` | | `promoted_runtime_sha` | `0568f44cb2d87e78fd394a27a670e33c84f7842f` | | `source_branch` | `feat/issue-615-runtime-mode-enforcement` | | `source_pr` | #770 | | `restart_method` | IDE client reconnect (`/mcp`); already in effect at session start, verified in-parity, not performed by this session | | `health_check_proof` | `gitea_assess_master_parity` all four namespaces: `in_parity=true`, `stale=false`, `restart_required=false`, `startup_head == current_head == 0568f44cb2d87e78fd394a27a670e33c84f7842f` | | `identity_proof` | gitea-tools=jcwalker3/prgs-author; gitea-reviewer=sysadmin/prgs-reviewer; gitea-merger=sysadmin/prgs-merger; gitea-reconciler=sysadmin/prgs-reconciler | | `profile_proof` | `gitea_get_runtime_context` active_profile per namespace: prgs-author, prgs-reviewer, prgs-merger, prgs-reconciler | | `workspace_proof` | `mcp_process_root == canonical_repository_root == /Users/jasonwalker/Development/Gitea-Tools`; `workspace_roots_aligned=true`; `dirty_files=[]`; bound `branches/` worktree accepted via `worktree_path` binding, not rejected as root misalignment | | `mutation_capability_proof` | `real_mutations_allowed=true`, `mutation_block_reasons=[]`, `gate_enforced=true` in all four namespaces | | `rollback_instructions` | `git -C <stable-root> merge --ff-only 059ee77c1fd483da385d6ccb4d842e808fb54630`; reconnect client (`/mcp`); re-prove all four namespaces per runbook §5 | `assess_promotion_record()` → **`valid=true`, `missing_fields=[]`, `reasons=[]`**. ## SHA on every surface | Surface | SHA | |---|---| | PR #770 `merge_commit_sha` | `0568f44cb2d87e78fd394a27a670e33c84f7842f` | | Live `prgs/master` | `0568f44cb2d87e78fd394a27a670e33c84f7842f` | | Control checkout HEAD (`master`, clean) | `0568f44cb2d87e78fd394a27a670e33c84f7842f` | | `gitea-tools` startup_head / current_head | `0568f44…` / `0568f44…` | | `gitea-reviewer` startup_head / current_head | `0568f44…` / `0568f44…` | | `gitea-merger` startup_head / current_head | `0568f44…` / `0568f44…` | | `gitea-reconciler` startup_head / current_head | `0568f44…` / `0568f44…` | Runtime classification in all four namespaces: `runtime_mode=stable-control`, `in_parity=true`, `restart_required=false`, `stop_required=false`, `real_mutations_allowed=true`, `dev_worktree_launched=false`, `gate_enforced=true`. ## Caveat on `previous_runtime_sha` `previous_runtime_sha=059ee77…` is derived by the helper from **Git ancestry** (the first-parent of the promoted merge commit), not from a live observation of a daemon serving `059ee77…` before promotion. The control checkout and all four namespaces already reported `0568f44…` at the start of the archiving session, so no pre-promotion daemon reading was captured. The promoted-state evidence above is direct and live; the pre-promotion SHA is inferential. ## Canonical Issue State STATE: closed WHO_IS_NEXT: author NEXT_ACTION: Resume normal workflow phases on the promoted stable-control runtime at 0568f44cb2d87e78fd394a27a670e33c84f7842f. NEXT_PROMPT: Run one bounded author phase for Scaled-Tech-Consulting/Gitea-Tools on the promoted stable-control runtime (0568f44cb2d87e78fd394a27a670e33c84f7842f). Verify identity, runtime mode, and parity before any mutation. Do not promote again without a fresh promotion record. WHAT_HAPPENED: PR #770 reached state=closed with merge_commit_sha 0568f44cb2d87e78fd394a27a670e33c84f7842f, and this issue reached state=closed. The stable control runtime was advanced to that SHA and reloaded via client reconnect. All four MCP namespaces were independently re-proven per runbook §5, and `assess_promotion_record` returned valid=true for the record archived in this comment. WHY: AC4 requires promotion checklist fields to be documented and used for real promotions. Runbook §3.7 requires the record, once `assess_promotion_record` returns valid=true, to be posted as a durable comment on the tracking issue so the promotion is auditable without chat history. RELATED_DISCUSSION: none RELATED_PRS: - #770 BRANCH: master HEAD_SHA: 0568f44cb2d87e78fd394a27a670e33c84f7842f VALIDATION: assess_promotion_record valid=true, missing_fields=[]. gitea_assess_master_parity in_parity=true / restart_required=false across gitea-tools, gitea-reviewer, gitea-merger, gitea-reconciler. PR #770 state=closed with merge_commit_sha 0568f44cb2d87e78fd394a27a670e33c84f7842f. BLOCKERS: none LAST_UPDATED_BY: jcwalker3 / prgs-author / 2026-07-20 [THREAD STATE LEDGER] Runtime — promotion completed and re-proven What is true now: - Runtime mode: stable-control - Runtime git SHA: 0568f44cb2d87e78fd394a27a670e33c84f7842f - Server-side decision state: server-side state changed - Local verdict/state: promotion record carries every required field - Latest known validation: assess_promotion_record valid=true; all namespaces re-proven What changed: - stable control runtime advanced to the promoted SHA and reloaded by the operator What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: author - Required action: resume normal workflow phases on the promoted runtime - Do not do: promote again without a fresh record - Resume from: docs/stable-runtime-promotion-runbook.md section 4
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#615