feat: enforce stable-control vs dev runtime modes for Gitea MCP (Closes #615) #770

Merged
sysadmin merged 2 commits from feat/issue-615-runtime-mode-enforcement into master 2026-07-20 12:36:59 -05:00
Owner

Closes #615

Implements the AC6–AC11 stable-control-runtime enforcement layer.

Head: 9bf3acf

Validation:

  • Focused stable-runtime suite: 47 passed
  • Full suite: 3827 passed, 6 skipped, 2 pre-existing failures
  • git diff --check: clean
  • The stable-control runtime ADR (docs/architecture/mcp-stable-control-runtime-policy-adr.md)
    established the policy but had no runtime enforcement: a daemon relaunched from a
    feature worktree still holds production credentials and will happily mutate real
    issues. This adds the enforcement layer (acceptance criteria 6-11).

stable_control_runtime.py:

  • classify_runtime_mode(): stable-control | dev-test | unknown, inferred from the
    process root and checkout branch, with an explicit GITEA_MCP_RUNTIME_MODE
    declaration for packaged layouts that have no git checkout.
  • build_runtime_report(): runtime mode, git SHA, branch, checkout path, process
    root, active workspace, repo binding, profile, identity, dirty files,
    alignment, and real_mutations_allowed.
  • assess_runtime_mutation_gate(): fails closed on dev-test targeting production,
    unknown runtime, dirty stable checkout, dev-worktree launch, and unsafe
    process-root/workspace alignment.
  • Post-transport-flap re-proving tracked per namespace, so proving the author
    namespace never implies reviewer, merger, or reconciler (#584).
  • assess_promotion_record(): promotion must record previous and promoted SHAs
    plus health, identity, profile, workspace, capability, and rollback proof.

Server wiring:

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

Docs and tooling:

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

Validation: 47 new tests in tests/test_stable_control_runtime.py. Full suite
3827 passed / 6 skipped / 2 failed; both failures are pre-existing on master
059ee77 (verified in a clean baseline worktree: identical 2 failures,
3780 passed).

Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]

Description

[Summary of changes and issue number closed.]

Closes #[Issue Number]

Checklist

  • I have verified my identity matches the required role.
  • No secrets, tokens, keychain IDs, or raw service URLs are committed.
  • All tests pass for touched code.
  • git diff --check is clean.

Documentation and Wiki

  • Does this change require a wiki update (workflows, tools, profiles, runbooks)?
  • If yes, has docs/wiki/ been updated accordingly?
  • If wiki pages changed, plan the Gitea Wiki sync after merge (scripts/sync-gitea-wiki.sh, see Runbooks).
  • Readiness gate (#224): the Gitea Wiki is populated and current for this repo — verify the repo Wiki tab, not docs/wiki/. If stale or empty, record the required sync as a follow-up before approval.
  • If this PR closes a wiki-related issue: closure requires live Gitea Wiki proof links (Wiki Home plus page listing or wiki git log). Markdown in docs/wiki/, sync-helper code, or policy docs alone are not sufficient to close a wiki issue.
Closes #615 Implements the AC6–AC11 stable-control-runtime enforcement layer. Head: 9bf3acf Validation: - Focused stable-runtime suite: 47 passed - Full suite: 3827 passed, 6 skipped, 2 pre-existing failures - git diff --check: clean - The stable-control runtime ADR (docs/architecture/mcp-stable-control-runtime-policy-adr.md) established the policy but had no runtime enforcement: a daemon relaunched from a feature worktree still holds production credentials and will happily mutate real issues. This adds the enforcement layer (acceptance criteria 6-11). stable_control_runtime.py: - classify_runtime_mode(): stable-control | dev-test | unknown, inferred from the process root and checkout branch, with an explicit GITEA_MCP_RUNTIME_MODE declaration for packaged layouts that have no git checkout. - build_runtime_report(): runtime mode, git SHA, branch, checkout path, process root, active workspace, repo binding, profile, identity, dirty files, alignment, and real_mutations_allowed. - assess_runtime_mutation_gate(): fails closed on dev-test targeting production, unknown runtime, dirty stable checkout, dev-worktree launch, and unsafe process-root/workspace alignment. - Post-transport-flap re-proving tracked per namespace, so proving the author namespace never implies reviewer, merger, or reconciler (#584). - assess_promotion_record(): promotion must record previous and promoted SHAs plus health, identity, profile, workspace, capability, and rollback proof. Server wiring: - _profile_operation_gate() consults the runtime gate alongside the #420 parity gate. gitea.read is never blocked, so an operator can still diagnose a sick runtime. - The gate reads a startup snapshot rather than shelling out per mutation, for the same reason parity uses a startup baseline: the runtime a process serves from is fixed when it loads its code. Enforcement is decided from how the process was loaded, so per-test production simulation cannot switch it on. - gitea_get_runtime_context() reports the live runtime under stable_control_runtime and points at the promotion runbook when blocked. Docs and tooling: - docs/stable-runtime-promotion-runbook.md: operator promotion procedure, required record fields, per-namespace re-proving, rollback. - scripts/promote-stable-runtime: read-only helper that emits and validates a promotion record; it never restarts anything. - Five canonical [THREAD STATE LEDGER] examples: runtime healthy, transport flap recovered, namespace not yet re-proven, promotion completed, rollback required. - ADR section 5 follow-ups marked landed; runbooks cross-link the new runbook. Validation: 47 new tests in tests/test_stable_control_runtime.py. Full suite 3827 passed / 6 skipped / 2 failed; both failures are pre-existing on master 059ee77 (verified in a clean baseline worktree: identical 2 failures, 3780 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> ## Description [Summary of changes and issue number closed.] Closes #[Issue Number] ## Checklist - [ ] I have verified my identity matches the required role. - [ ] No secrets, tokens, keychain IDs, or raw service URLs are committed. - [ ] All tests pass for touched code. - [ ] `git diff --check` is clean. ## Documentation and Wiki - [ ] Does this change require a wiki update (workflows, tools, profiles, runbooks)? - [ ] If yes, has `docs/wiki/` been updated accordingly? - [ ] If wiki pages changed, plan the Gitea Wiki sync after merge (`scripts/sync-gitea-wiki.sh`, see Runbooks). - [ ] Readiness gate (#224): the Gitea Wiki is populated and current for this repo — verify the repo **Wiki tab**, not `docs/wiki/`. If stale or empty, record the required sync as a follow-up before approval. - [ ] If this PR closes a wiki-related issue: closure requires live Gitea Wiki proof links (Wiki Home plus page listing or wiki git log). Markdown in `docs/wiki/`, sync-helper code, or policy docs alone are not sufficient to close a wiki issue.
jcwalker3 added 1 commit 2026-07-20 10:35:04 -05:00
The stable-control runtime ADR (docs/architecture/mcp-stable-control-runtime-policy-adr.md)
established the policy but had no runtime enforcement: a daemon relaunched from a
feature worktree still holds production credentials and will happily mutate real
issues. This adds the enforcement layer (acceptance criteria 6-11).

stable_control_runtime.py:
- classify_runtime_mode(): stable-control | dev-test | unknown, inferred from the
  process root and checkout branch, with an explicit GITEA_MCP_RUNTIME_MODE
  declaration for packaged layouts that have no git checkout.
- build_runtime_report(): runtime mode, git SHA, branch, checkout path, process
  root, active workspace, repo binding, profile, identity, dirty files,
  alignment, and real_mutations_allowed.
- assess_runtime_mutation_gate(): fails closed on dev-test targeting production,
  unknown runtime, dirty stable checkout, dev-worktree launch, and unsafe
  process-root/workspace alignment.
- Post-transport-flap re-proving tracked per namespace, so proving the author
  namespace never implies reviewer, merger, or reconciler (#584).
- assess_promotion_record(): promotion must record previous and promoted SHAs
  plus health, identity, profile, workspace, capability, and rollback proof.

Server wiring:
- _profile_operation_gate() consults the runtime gate alongside the #420 parity
  gate. gitea.read is never blocked, so an operator can still diagnose a sick
  runtime.
- The gate reads a startup snapshot rather than shelling out per mutation, for
  the same reason parity uses a startup baseline: the runtime a process serves
  from is fixed when it loads its code. Enforcement is decided from how the
  process was loaded, so per-test production simulation cannot switch it on.
- gitea_get_runtime_context() reports the live runtime under
  stable_control_runtime and points at the promotion runbook when blocked.

Docs and tooling:
- docs/stable-runtime-promotion-runbook.md: operator promotion procedure,
  required record fields, per-namespace re-proving, rollback.
- scripts/promote-stable-runtime: read-only helper that emits and validates a
  promotion record; it never restarts anything.
- Five canonical [THREAD STATE LEDGER] examples: runtime healthy, transport flap
  recovered, namespace not yet re-proven, promotion completed, rollback required.
- ADR section 5 follow-ups marked landed; runbooks cross-link the new runbook.

Validation: 47 new tests in tests/test_stable_control_runtime.py. Full suite
3827 passed / 6 skipped / 2 failed; both failures are pre-existing on master
059ee77 (verified in a clean baseline worktree: identical 2 failures,
3780 passed).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #770
issue: #615
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 99553-fed66ecfe767
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-770
phase: claimed
candidate_head: 9bf3acfef6
target_branch: master
target_branch_sha: 059ee77c1f
last_activity: 2026-07-20T15:47:02Z
expires_at: 2026-07-20T15:57:02Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #770 issue: #615 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 99553-fed66ecfe767 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-770 phase: claimed candidate_head: 9bf3acfef681f9766ae1d93680b03996a93f98a1 target_branch: master target_branch_sha: 059ee77c1fd483da385d6ccb4d842e808fb54630 last_activity: 2026-07-20T15:47:02Z expires_at: 2026-07-20T15:57:02Z blocker: none
sysadmin requested changes 2026-07-20 10:55:38 -05:00
Dismissed
sysadmin left a comment
Owner

Canonical PR State

STATE: open — independent reviewer verdict REQUEST_CHANGES recorded at head 9bf3acfef6
WHO_IS_NEXT: author
NEXT_ACTION: Remediate 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. Remediate three reviewer findings recorded at head 9bf3acfef681f9766ae1d93680b03996a93f98a1, then push and request a fresh independent review. Do not merge and do not self-approve.

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. This blocks the author namespace once GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE is bound, 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, its derived alignment) and mutable fields (dirty_files) are therefore 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 all later sessions. 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 that 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 inspected all 8 changed files at the pinned head, re-ran the focused suite, reproduced a blocking fail-closed defect in the server wiring directly against the PR code, and posted exactly one native REQUEST_CHANGES verdict.
WHY: The pure module is sound, but the acceptance criterion 7 alignment blocker, as derived by the server, inverts its intent and 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.
ISSUE: #615
HEAD_SHA: 9bf3acfef6
REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: code-defect blocker — F1 fail-closed alignment derivation and F2 process-lifetime snapshot of session-scoped facts, both in gitea_mcp_server.py; test-coverage 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 in worktree branches/review-pr-770 at head 9bf3acf: 47 passed. git diff --check clean against base 059ee77c1f. Diff independently confirmed as 8 files, 1714 insertions, 6 deletions, with no unrelated or generated files and no secrets. F1 reproduced by executing the PR's own build_runtime_report with a stable-control runtime 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)

NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, head pinned 9bf3acfef6, reviewer lease comment 13272.


REQUEST_CHANGES — independent native review of PR #770 (Closes #615, acceptance criteria 6-11). Reviewer identity sysadmin (prgs-reviewer) is distinct from PR author jcwalker3. Base master at 059ee77c1f, commits behind 0, no conflicts, checks not required by live branch protection, no prior review at this head.

Scope confirmed

The diff matches the author's record exactly: stable_control_runtime.py, tests/test_stable_control_runtime.py, thread_state_ledger_examples.py, scripts/promote-stable-runtime, docs/stable-runtime-promotion-runbook.md, gitea_mcp_server.py, docs/architecture/mcp-stable-control-runtime-policy-adr.md, docs/llm-workflow-runbooks.md. Acceptance criteria 1-5 are correctly out of scope.

Independently verified as correct

  • Acceptance criterion 6: classify_runtime_mode and build_runtime_report report every required field. The operator GITEA_MCP_RUNTIME_MODE declaration is validated against the mode allow-list, so a typo falls back to inference rather than being trusted.
  • A false stable-control declaration cannot mask a dev-worktree launch: dev_worktree_launched is derived from the process root independently of the declaration, and the dev-worktree blocker reads that derived fact. This is a genuinely good design decision.
  • Acceptance criterion 8: post-flap re-proving is tracked per namespace, a flap invalidates all four, and author proof never transfers to reviewer, merger, or reconciler.
  • Acceptance criterion 10: the promotion runbook and scripts/promote-stable-runtime are present, and the helper is read-only — it never fetches, merges, restarts, reloads, or kills.
  • Acceptance criterion 11: all five required runtime [THREAD STATE LEDGER] examples are present.
  • gitea.read is never blocked, so a sick runtime stays diagnosable.

F1 (blocking) — the alignment derivation blocks every correctly-bound session

gitea_mcp_server.py:11391-11397 derives the gate input as:

aligned = os.path.realpath(workspace_root or "") == (ctx.get("process_project_root") or "")

This redefines workspace_roots_aligned. The pre-existing meaning of that field, at gitea_mcp_server.py:573, is ctx["roots_aligned"], which is canonical_repo_root == process_project_root — a repository-level question: does this namespace target the repository the process is installed in. The new derivation instead requires the active task workspace to equal the process root.

Under the global-worktree rule, all task work happens in a branches/ worktree, so the task workspace is required to differ from the process root. The new derivation therefore reports aligned=False for exactly the sessions that are configured correctly, and assess_runtime_mutation_gate turns that into unsafe_process_root_workspace_alignment, refusing every non-read operation.

Reproduced against the PR's own code, with a healthy promoted runtime — clean control checkout, on master, runtime_mode=stable-control:

workspace  = /Users/jasonwalker/Development/Gitea-Tools/branches/issue-615-runtime-mode-enforcement
aligned    = False
runtime_mode = stable-control
real_mutations_allowed = False
BLOCK: process-root / active-workspace alignment is unsafe; the runtime and the
       task workspace disagree about which checkout is being mutated

Failure scenario, concretely:

  • Author namespace: _resolve_namespace_mutation_context(None) resolves the workspace from GITEA_ACTIVE_WORKTREE / GITEA_AUTHOR_WORKTREE (namespace_workspace_binding.py:77-97). Binding the author daemon to its session worktree is the documented fix for the publication gap recorded in issue #615 comment 13253. The moment that binding is set, every author mutation is refused by this gate.
  • Reviewer and merger namespaces: the same resolver falls through to the reviewer PR lease worktree (namespace_workspace_binding.py:83-84, wired at gitea_mcp_server.py:455-457). Holding a reviewer lease — the required precondition for submitting a verdict — sets the workspace to the lease worktree, so verdict submission itself is refused. This review, submitted while holding a lease bound to branches/review-pr-770, would be blocked by the code it is reviewing once that code is promoted.

The gate is currently inert only because it is not yet the running runtime and because the author daemon presently has no worktree binding at all. Promotion is what makes it live, and promotion is precisely when the workflow would deadlock, with gitea.read the only surviving operation.

Suggested direction: feed the gate ctx["roots_aligned"], preserving the established repository-level meaning, or express the check as worktree membership of the canonical repository root rather than path equality with the process root. A branches/ worktree of the same repository is the sanctioned configuration and must not be classified as unsafe.

F2 (blocking) — a per-session fact is frozen into a process-lifetime snapshot

_current_runtime_mode_report caches its first result into _STARTUP_RUNTIME_MODE and reuses it for every later gate evaluation. The docstring calls this a startup snapshot, but nothing captures it at startup: the module-level _STARTUP_RUNTIME_MODE is initialised to None, unlike _STARTUP_PARITY, which is genuinely captured at import. The read-only reporting path _current_runtime_mode_report(refresh=True) also seeds the cache when it is still unset, so whichever call happens to run first — possibly a read-only gitea_get_runtime_context from an arbitrary session — fixes the snapshot for the life of the process.

The startup-baseline argument is sound for the facts it was written about: process root, checkout branch, runtime SHA. It does not hold for the two session-scoped fields the report also captures. active_task_workspace and its derived alignment change per session and per namespace, and dirty_files changes whenever the checkout does. Consequences:

  • A dirty stable checkout that becomes dirty after the snapshot is never caught, so the acceptance criterion 7 dirty-runtime blocker silently stops applying for the remainder of the process.
  • One session's workspace binding decides the alignment verdict for every later session served by the same daemon, in whichever direction the first call happened to observe.

Suggested direction: capture the immutable process facts once at import, as the parity gate does, and evaluate the session-scoped fields per call.

F3 (major) — acceptance criterion 9's first case is not actually tested

tests/test_stable_control_runtime.py covers the pure functions thoroughly, and the intent behind the AC9 list is clearly met at that layer. But every server-wiring test that asserts a healthy runtime permits mutations patches _current_runtime_mode_report with a fixture whose workspace_roots_aligned=True (stable_report, line 40; test_stable_runtime_allows_mutations_when_guards_forced, lines 447-453). The only test that calls the real derivation, test_live_report_describes_this_checkout (lines 465-468), asserts just the mode and the process root, and nothing about real_mutations_allowed.

So the derivation in F1 is executed by no test, which is why 47 passing tests coexist with a gate that would refuse every correctly-bound session. AC9 case "stable healthy runtime allows real mutations" is proven against a fixture rather than against the code that runs in production.

Non-blocking observations, no change required for approval

  • assess_namespace_reproof compares str(proof_at) < str(flap_at) lexicographically. Correct for identical ISO-8601 UTC strings, silently wrong for any mixed format or offset. Consider parsing, or documenting the format contract at the recording functions.
  • _RUNTIME_MODE_GATE_UNDER_TEST is computed at import from "pytest" in sys.modules or "unittest" in sys.modules. Verified today that the production import chain pulls in neither, so this is not a live defect. It is fragile by construction: any future dependency importing unittest.mock at module scope would silently disable the entire gate in production. An explicit environment opt-in would fail safe instead.
  • observe_runtime parses porcelain with line[3:], so a rename is reported as old -> new. Cosmetic only; it feeds a count and a message.

Verdict rationale

The design of the pure module is careful, and the module-level logic for acceptance criteria 6, 8, 10, and 11 is correct and well tested. The blocking problem is in the wiring: acceptance criterion 7's alignment blocker, as derived by the server, inverts its intent and would fail-close the entire mutation workflow at exactly the moment the enforcement layer goes live. F1 and F2 are in the same wiring seam and F3 is the coverage gap that allowed F1 through, so they should be addressed together.

No merge authorization is implied or granted by this review.

[THREAD STATE LEDGER] PR #770 — REQUEST_CHANGES posted to Gitea

What is true now:

  • PR state: open
  • Server-side decision state: REQUEST_CHANGES posted to Gitea at head 9bf3acfef6
  • Local verdict/state: REQUEST_CHANGES prepared and submitted from reviewer worktree branches/review-pr-770 at the pinned head
  • 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:

  • independent reviewer inspection of all 8 changed files completed
  • REQUEST_CHANGES posted to Gitea with three findings, two blocking and one major

What is blocked:

  • Blocker classification: code-defect blocker

Who/what acts next:

  • Next actor: author
  • Required action: remediate F1, F2, and F3, push to the PR branch, and request a fresh review
  • Do not do: merge this head, self-approve, or treat the 47 passing tests as proof that F1 is absent
  • Resume from: this review at head 9bf3acfef6
## Canonical PR State STATE: open — independent reviewer verdict REQUEST_CHANGES recorded at head 9bf3acfef681f9766ae1d93680b03996a93f98a1 WHO_IS_NEXT: author NEXT_ACTION: Remediate 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. Remediate three reviewer findings recorded at head 9bf3acfef681f9766ae1d93680b03996a93f98a1, then push and request a fresh independent review. Do not merge and do not self-approve. 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. This blocks the author namespace once GITEA_AUTHOR_WORKTREE or GITEA_ACTIVE_WORKTREE is bound, 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, its derived alignment) and mutable fields (dirty_files) are therefore 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 all later sessions. 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 that 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 inspected all 8 changed files at the pinned head, re-ran the focused suite, reproduced a blocking fail-closed defect in the server wiring directly against the PR code, and posted exactly one native REQUEST_CHANGES verdict. WHY: The pure module is sound, but the acceptance criterion 7 alignment blocker, as derived by the server, inverts its intent and 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. ISSUE: #615 HEAD_SHA: 9bf3acfef681f9766ae1d93680b03996a93f98a1 REVIEW_STATUS: REQUEST_CHANGES MERGE_READY: no BLOCKERS: code-defect blocker — F1 fail-closed alignment derivation and F2 process-lifetime snapshot of session-scoped facts, both in gitea_mcp_server.py; test-coverage 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 in worktree branches/review-pr-770 at head 9bf3acf: 47 passed. git diff --check clean against base 059ee77c1fd483da385d6ccb4d842e808fb54630. Diff independently confirmed as 8 files, 1714 insertions, 6 deletions, with no unrelated or generated files and no secrets. F1 reproduced by executing the PR's own build_runtime_report with a stable-control runtime 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) NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, head pinned 9bf3acfef681f9766ae1d93680b03996a93f98a1, reviewer lease comment 13272. --- REQUEST_CHANGES — independent native review of PR #770 (Closes #615, acceptance criteria 6-11). Reviewer identity sysadmin (prgs-reviewer) is distinct from PR author jcwalker3. Base master at 059ee77c1fd483da385d6ccb4d842e808fb54630, commits behind 0, no conflicts, checks not required by live branch protection, no prior review at this head. ## Scope confirmed The diff matches the author's record exactly: `stable_control_runtime.py`, `tests/test_stable_control_runtime.py`, `thread_state_ledger_examples.py`, `scripts/promote-stable-runtime`, `docs/stable-runtime-promotion-runbook.md`, `gitea_mcp_server.py`, `docs/architecture/mcp-stable-control-runtime-policy-adr.md`, `docs/llm-workflow-runbooks.md`. Acceptance criteria 1-5 are correctly out of scope. ## Independently verified as correct - Acceptance criterion 6: `classify_runtime_mode` and `build_runtime_report` report every required field. The operator `GITEA_MCP_RUNTIME_MODE` declaration is validated against the mode allow-list, so a typo falls back to inference rather than being trusted. - A false `stable-control` declaration cannot mask a dev-worktree launch: `dev_worktree_launched` is derived from the process root independently of the declaration, and the dev-worktree blocker reads that derived fact. This is a genuinely good design decision. - Acceptance criterion 8: post-flap re-proving is tracked per namespace, a flap invalidates all four, and author proof never transfers to reviewer, merger, or reconciler. - Acceptance criterion 10: the promotion runbook and `scripts/promote-stable-runtime` are present, and the helper is read-only — it never fetches, merges, restarts, reloads, or kills. - Acceptance criterion 11: all five required runtime `[THREAD STATE LEDGER]` examples are present. - `gitea.read` is never blocked, so a sick runtime stays diagnosable. ## F1 (blocking) — the alignment derivation blocks every correctly-bound session `gitea_mcp_server.py:11391-11397` derives the gate input as: ``` aligned = os.path.realpath(workspace_root or "") == (ctx.get("process_project_root") or "") ``` This redefines `workspace_roots_aligned`. The pre-existing meaning of that field, at `gitea_mcp_server.py:573`, is `ctx["roots_aligned"]`, which is `canonical_repo_root == process_project_root` — a repository-level question: does this namespace target the repository the process is installed in. The new derivation instead requires the active task workspace to equal the process root. Under the global-worktree rule, all task work happens in a `branches/` worktree, so the task workspace is required to differ from the process root. The new derivation therefore reports `aligned=False` for exactly the sessions that are configured correctly, and `assess_runtime_mutation_gate` turns that into `unsafe_process_root_workspace_alignment`, refusing every non-read operation. Reproduced against the PR's own code, with a healthy promoted runtime — clean control checkout, on `master`, `runtime_mode=stable-control`: ``` workspace = /Users/jasonwalker/Development/Gitea-Tools/branches/issue-615-runtime-mode-enforcement aligned = False runtime_mode = stable-control real_mutations_allowed = False BLOCK: process-root / active-workspace alignment is unsafe; the runtime and the task workspace disagree about which checkout is being mutated ``` Failure scenario, concretely: - Author namespace: `_resolve_namespace_mutation_context(None)` resolves the workspace from `GITEA_ACTIVE_WORKTREE` / `GITEA_AUTHOR_WORKTREE` (`namespace_workspace_binding.py:77-97`). Binding the author daemon to its session worktree is the documented fix for the publication gap recorded in issue #615 comment 13253. The moment that binding is set, every author mutation is refused by this gate. - Reviewer and merger namespaces: the same resolver falls through to the reviewer PR lease worktree (`namespace_workspace_binding.py:83-84`, wired at `gitea_mcp_server.py:455-457`). Holding a reviewer lease — the required precondition for submitting a verdict — sets the workspace to the lease worktree, so verdict submission itself is refused. This review, submitted while holding a lease bound to `branches/review-pr-770`, would be blocked by the code it is reviewing once that code is promoted. The gate is currently inert only because it is not yet the running runtime and because the author daemon presently has no worktree binding at all. Promotion is what makes it live, and promotion is precisely when the workflow would deadlock, with `gitea.read` the only surviving operation. Suggested direction: feed the gate `ctx["roots_aligned"]`, preserving the established repository-level meaning, or express the check as worktree membership of the canonical repository root rather than path equality with the process root. A `branches/` worktree of the same repository is the sanctioned configuration and must not be classified as unsafe. ## F2 (blocking) — a per-session fact is frozen into a process-lifetime snapshot `_current_runtime_mode_report` caches its first result into `_STARTUP_RUNTIME_MODE` and reuses it for every later gate evaluation. The docstring calls this a startup snapshot, but nothing captures it at startup: the module-level `_STARTUP_RUNTIME_MODE` is initialised to `None`, unlike `_STARTUP_PARITY`, which is genuinely captured at import. The read-only reporting path `_current_runtime_mode_report(refresh=True)` also seeds the cache when it is still unset, so whichever call happens to run first — possibly a read-only `gitea_get_runtime_context` from an arbitrary session — fixes the snapshot for the life of the process. The startup-baseline argument is sound for the facts it was written about: process root, checkout branch, runtime SHA. It does not hold for the two session-scoped fields the report also captures. `active_task_workspace` and its derived alignment change per session and per namespace, and `dirty_files` changes whenever the checkout does. Consequences: - A dirty stable checkout that becomes dirty after the snapshot is never caught, so the acceptance criterion 7 dirty-runtime blocker silently stops applying for the remainder of the process. - One session's workspace binding decides the alignment verdict for every later session served by the same daemon, in whichever direction the first call happened to observe. Suggested direction: capture the immutable process facts once at import, as the parity gate does, and evaluate the session-scoped fields per call. ## F3 (major) — acceptance criterion 9's first case is not actually tested `tests/test_stable_control_runtime.py` covers the pure functions thoroughly, and the intent behind the AC9 list is clearly met at that layer. But every server-wiring test that asserts a healthy runtime permits mutations patches `_current_runtime_mode_report` with a fixture whose `workspace_roots_aligned=True` (`stable_report`, line 40; `test_stable_runtime_allows_mutations_when_guards_forced`, lines 447-453). The only test that calls the real derivation, `test_live_report_describes_this_checkout` (lines 465-468), asserts just the mode and the process root, and nothing about `real_mutations_allowed`. So the derivation in F1 is executed by no test, which is why 47 passing tests coexist with a gate that would refuse every correctly-bound session. AC9 case "stable healthy runtime allows real mutations" is proven against a fixture rather than against the code that runs in production. ## Non-blocking observations, no change required for approval - `assess_namespace_reproof` compares `str(proof_at) < str(flap_at)` lexicographically. Correct for identical ISO-8601 UTC strings, silently wrong for any mixed format or offset. Consider parsing, or documenting the format contract at the recording functions. - `_RUNTIME_MODE_GATE_UNDER_TEST` is computed at import from `"pytest" in sys.modules or "unittest" in sys.modules`. Verified today that the production import chain pulls in neither, so this is not a live defect. It is fragile by construction: any future dependency importing `unittest.mock` at module scope would silently disable the entire gate in production. An explicit environment opt-in would fail safe instead. - `observe_runtime` parses porcelain with `line[3:]`, so a rename is reported as `old -> new`. Cosmetic only; it feeds a count and a message. ## Verdict rationale The design of the pure module is careful, and the module-level logic for acceptance criteria 6, 8, 10, and 11 is correct and well tested. The blocking problem is in the wiring: acceptance criterion 7's alignment blocker, as derived by the server, inverts its intent and would fail-close the entire mutation workflow at exactly the moment the enforcement layer goes live. F1 and F2 are in the same wiring seam and F3 is the coverage gap that allowed F1 through, so they should be addressed together. No merge authorization is implied or granted by this review. [THREAD STATE LEDGER] PR #770 — REQUEST_CHANGES posted to Gitea What is true now: - PR state: open - Server-side decision state: REQUEST_CHANGES posted to Gitea at head 9bf3acfef681f9766ae1d93680b03996a93f98a1 - Local verdict/state: REQUEST_CHANGES prepared and submitted from reviewer worktree branches/review-pr-770 at the pinned head - 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: - independent reviewer inspection of all 8 changed files completed - REQUEST_CHANGES posted to Gitea with three findings, two blocking and one major What is blocked: - Blocker classification: code-defect blocker Who/what acts next: - Next actor: author - Required action: remediate F1, F2, and F3, push to the PR branch, and request a fresh review - Do not do: merge this head, self-approve, or treat the 47 passing tests as proof that F1 is absent - Resume from: this review at head 9bf3acfef681f9766ae1d93680b03996a93f98a1
jcwalker3 added 1 commit 2026-07-20 11:32:47 -05:00
Remediates reviewer findings F1, F2, and F3 from review 483 on PR #770.

F1 (blocking): _current_runtime_mode_report derived the runtime-gate alignment
input as realpath(workspace_root) == process_project_root, redefining
workspace_roots_aligned. Its established meaning 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 old derivation reported aligned False for exactly the sessions
that are configured correctly, raising unsafe_process_root_workspace_alignment
and refusing every non-read operation once an author, reviewer, or merger
namespace held a worktree binding. 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 read-only refresh=True path seeded it too. Session-scoped
fields (active_task_workspace and its derived alignment) and mutable fields
(dirty_files) were frozen for the process lifetime, so the acceptance
criterion 7 dirty-runtime blocker stopped applying after the snapshot and one
session's binding decided alignment for every later session.

Immutable process facts (process root, branch, head, checkout-ness) are now
captured at import as _STARTUP_RUNTIME_FACTS, matching the #420 parity
baseline. Dirty state, workspace binding, and alignment are recomputed on every
call. A new stable_control_runtime.observe_dirty_files() splits out the one
runtime fact that legitimately changes during a process lifetime;
observe_runtime() delegates to it so the parsing lives in one place. refresh is
retained for the read-only reporting path but 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:

- a clean stable control 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 mutation result;
- an unresolvable binding reports unknown alignment, never alignment proof.

Acceptance criteria 6, 8, 10, and 11 are unchanged and untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #770
issue: #615
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 5311-9cbb43114b9e
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-770
phase: claimed
candidate_head: ab34280f90
target_branch: master
target_branch_sha: 059ee77c1f
last_activity: 2026-07-20T16:51:41Z
expires_at: 2026-07-20T17:01:41Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #770 issue: #615 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 5311-9cbb43114b9e worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-770 phase: claimed candidate_head: ab34280f9025406d5c392d4e07c9d34d3ecd47fe target_branch: master target_branch_sha: 059ee77c1fd483da385d6ccb4d842e808fb54630 last_activity: 2026-07-20T16:51:41Z expires_at: 2026-07-20T17:01:41Z blocker: none
sysadmin approved these changes 2026-07-20 11:59:13 -05:00
sysadmin left a comment
Owner

Canonical PR State

STATE: open — independent reviewer verdict APPROVE recorded at head ab34280f90
CANONICAL_ITEM: this APPROVE verdict on PR #770 at head ab34280f90
SUPERSEDED_ITEM: review 483 (REQUEST_CHANGES) on PR #770 at the superseded head 9bf3acfef6, stale at the current head and undismissed
WHO_IS_NEXT: merger
NEXT_ACTION: A prgs-merger session reassesses PR #770 at head ab34280f90 and decides the merge under its own gates; this review grants no merge authorization
NEXT_PROMPT:

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

An independent prgs-reviewer (sysadmin) recorded APPROVE at that exact head, superseding the stale review 483 which sits at 9bf3acfef681f9766ae1d93680b03996a93f98a1. Confirm through gitea_get_pr_review_feedback that the approval is visible, undismissed, non-stale, and at the current head, and that has_blocking_change_requests is false, before acting on it. If the head has moved, the approval is void and a fresh 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, and do not review or amend the branch.

WHAT_HAPPENED: A fresh independent reviewer pinned the current head, reproduced findings F1 and F2 directly against the PR code with the real workspace-root derivation and the real namespace resolver rather than test fixtures, ran the same reproduction against the superseded head as a control to prove the harness detects the defect, confirmed the five new F3 tests fail against the pre-fix code and pass against this head, ran focused and affected integration validation, independently reproduced the two claimed pre-existing failures on the base, and posted exactly one native APPROVE verdict.
WHY: All three findings from review 483 are remediated at this head. F1 now feeds the gate the established repository-level roots_aligned contract, so a correctly bound branches/ session is permitted rather than refused. F2 captures only immutable process facts at import 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 executes the real derivation and genuinely fails against the pre-fix code.
ISSUE: #615
HEAD_SHA: ab34280f90
REVIEW_STATUS: APPROVE
MERGE_READY: yes
BLOCKERS: no blocker
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. F1 and F2 reproduced with an independent harness that drives the real _current_runtime_mode_report and the real _resolve_namespace_mutation_context against a real branches/ worktree. The five new F3 tests fail 5/5 against the pre-fix head and pass 5/5 against this head. The two author-claimed pre-existing failures reproduce identically on base 059ee77. git diff --check clean against base; remediation scope 3 files, 189 insertions, 30 deletions; no secrets and no unrelated or generated files.
LAST_UPDATED_BY: prgs-reviewer (sysadmin)

NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, head pinned ab34280f90, reviewer lease comment 13292.


APPROVE — fresh independent native review of PR #770 (Closes #615, acceptance criteria 6-11) at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Reviewer identity sysadmin (prgs-reviewer) is distinct from PR author jcwalker3. Base master 059ee77c1fd483da385d6ccb4d842e808fb54630, commits behind 0, no conflicts, mergeable, checks not required by live branch protection.

This verdict explicitly supersedes review 483, which recorded REQUEST_CHANGES at 9bf3acfef681f9766ae1d93680b03996a93f98a1. That review remains undismissed but is stale at this head, and it was not reused as the current verdict.

Method

The author's handoff asked that the remediation be judged against the real derivation rather than the pass count. It was. Findings F1 and F2 were re-tested with an independent harness written for this review, which patches only PROJECT_ROOT and the import-time facts — and derives those facts by calling the PR's own observe_runtime() against the live control checkout, so they are observed rather than invented. _resolve_namespace_mutation_context ran unpatched against the real branches/issue-615-runtime-mode-enforcement worktree. The same harness was then run against 9bf3acf as a control, because a reproduction that passes at both heads proves nothing.

F1 — remediated, confirmed by controlled experiment

The gate input is now ctx.get("roots_aligned") (gitea_mcp_server.py:11414), restoring the established repository-level meaning. That key is real and carries the expected semantics: namespace_workspace_binding.py:156 defines it as canonical_root == process_root, which is the same contract already consumed at gitea_mcp_server.py:573. The fix consumes an existing contract rather than inventing a parallel one.

Identical harness, identical real binding (workspace_path = the branches/ task worktree, canonical_repo_root = process_project_root = the stable root, roots_aligned = True), differing only in the code under test:

head 9bf3acf (pre-fix)
  workspace_roots_aligned = False
  real_mutations_allowed  = False
  block('gitea.pr.create') = ['process-root / active-workspace alignment is unsafe; ...']

head ab34280 (this head)
  runtime_mode            = stable-control
  workspace_roots_aligned = True
  real_mutations_allowed  = True
  block('gitea.pr.create') = []

The correctly bound session that the previous head refused is now permitted, and the deadlock-on-promotion scenario described in review 483 no longer reproduces.

Misalignment still fails closed: with roots_aligned False the gate raises the alignment blocker, so the fix narrowed the check to its intended meaning without removing it.

F2 — remediated, confirmed by dynamic state change

_STARTUP_RUNTIME_FACTS is now genuinely captured at import (gitea_mcp_server.py:2027), matching the _STARTUP_PARITY baseline it was previously only claimed to match. The result cache is gone entirely, and refresh no longer selects between cache and live state, so the read-only seeding path is structurally eliminated rather than merely discouraged.

Observed clean, then changed dirty state, then re-evaluated the gate:

head ab34280 (this head)
  step 1 clean       -> dirty=[]                        allowed=True   block=[]
  step 2 now dirty   -> dirty=['gitea_mcp_server.py']   allowed=False  block=['stable control runtime checkout is dirty (1 file(s)) ...']
  step 3 clean again -> dirty=[]                        allowed=True   block=[]

The same sequence at 9bf3acf reported dirty=[] at step 2 — the injected change was never observed at all, because the first report had been frozen. That is the F2 defect reproduced directly, and step 2 above is it not reproducing.

The read-only path was checked separately: a permissive refresh=True call followed by a mutation gate evaluation with a newly dirty checkout returns blocked, so a read-only call can no longer weaken a later mutation decision.

observe_dirty_files() is a clean seam for this — it isolates the one runtime fact that legitimately changes during a process lifetime, and observe_runtime() now delegates to it rather than duplicating the parsing.

F3 — remediated, and the coverage is real

TestServerWiringRealDerivation patches only the derivation's inputs — the import-time facts, the dirty-file read, and the resolved binding — and lets _current_runtime_mode_report and _runtime_mode_block execute for real. That is the distinction the finding asked for, and it holds.

Verified rather than assumed: the new test file was extracted and run against the pre-fix server code, where all five fail, and against this head, where all five pass. The pre-fix failure mode is the absent _STARTUP_RUNTIME_FACTS symbol rather than an alignment assertion, which is a weaker signal than a behavioural failure would be — noted, but the harness above supplies the behavioural proof independently, and the tests are a genuine forward regression guard: reverting the F1 derivation to path equality would make test_clean_stable_checkout_with_bound_task_worktree_permits_mutation fail, since its fixture workspace is deliberately a branches/ path distinct from the process root.

The fifth test is a good addition beyond what was asked: an unresolvable binding must report alignment as unknown rather than as proof, and only definite misalignment blocks. Failing an unresolved binding closed would have reintroduced F1 through a different door.

Scope, base, and pre-existing failures

Remediation commit scope is gitea_mcp_server.py, stable_control_runtime.py, and tests/test_stable_control_runtime.py — 3 files, 189 insertions, 30 deletions, matching the author's record exactly. No unrelated, generated, or credential-bearing files; secret scan across the full PR diff is clean; git diff --check clean against base. Acceptance criteria 6, 8, 10, and 11 are untouched by this commit and were verified as correct in review 483.

The two full-suite failures were independently reproduced on pristine base 059ee77, in the reviewer's own worktree rather than taken on trust:

  • 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 fail at the base with the branch absent, so neither is caused by this PR. They remain open pre-existing defects on master and are out of scope here.

Non-blocking observations, carried forward

The three observations from review 483 were deliberately left out of scope to keep the remediation diff minimal, which is the right call. They are not approval blockers and are restated only so they are not lost:

  • 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 import. Not a live defect, but fragile: a future dependency importing unittest.mock at module scope would silently disable the gate in production. An explicit environment opt-in would fail safe.
  • observe_runtime porcelain parsing renders a rename as old -> new; cosmetic, it feeds a count and a message.

One further observation from this review, also non-blocking: capturing _STARTUP_RUNTIME_FACTS at import moves several git subprocess calls into module import. That is a small, one-time startup cost and is the correct trade for a trustworthy baseline, but it is worth knowing that importing the server module now shells out. The dirty_files key inside _STARTUP_RUNTIME_FACTS is now dead — the gate always reads live dirt instead — and could be dropped from the captured dict for clarity.

Verdict rationale

All three findings are remediated at the seam where they were raised, and each remediation was confirmed against the real code path rather than against a fixture or a pass count. F1 restores an existing contract instead of inventing a new one, F2 removes the caching hazard structurally rather than patching around it, and F3 closes the coverage gap that let F1 through. The enforcement layer now permits the sanctioned configuration and still fails closed on genuine misalignment, dirty stable checkouts, dev-worktree launches, and unknown runtimes.

No merge authorization is implied or granted by this review. Merge is a separate role decision under its own gates.

[THREAD STATE LEDGER] PR #770 — APPROVE posted to Gitea at head ab34280f

What is true now:

  • PR state: open, base master, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection
  • Server-side decision state: APPROVE posted to Gitea at head ab34280f90, superseding review 483 which sits at the superseded head 9bf3acfef6
  • Local verdict/state: APPROVE prepared and submitted from reviewer worktree branches/review-pr-770, pinned and re-verified at the current head immediately before submission
  • Latest known validation: focused suite 52 passed; affected integration set 153 passed with 20 subtests; F1 and F2 reproduced against the real derivation with a pre-fix control run; five new F3 tests fail 5/5 pre-fix and pass 5/5 at this head; two pre-existing base failures reproduced on 059ee77

What changed:

  • fresh independent reviewer inspection completed at head ab34280f
  • APPROVE posted to Gitea; review 483 superseded by a new verdict at the current head rather than reused or dismissed

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 this approval as merge authorization; act on it if the head has moved; edit or manually release the author issue lock on #615; amend the branch; remove the branches/issue-615-runtime-mode-enforcement or branches/baseline-master-615 worktrees
  • Resume from: this review at head ab34280f90
## Canonical PR State STATE: open — independent reviewer verdict APPROVE recorded at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe CANONICAL_ITEM: this APPROVE verdict on PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe SUPERSEDED_ITEM: review 483 (REQUEST_CHANGES) on PR #770 at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1, stale at the current head and undismissed WHO_IS_NEXT: merger NEXT_ACTION: A prgs-merger session reassesses PR #770 at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe and decides the merge under its own gates; this review grants no merge authorization NEXT_PROMPT: ```text Assess PR #770 (Closes #615) for merge as prgs-merger at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. An independent prgs-reviewer (sysadmin) recorded APPROVE at that exact head, superseding the stale review 483 which sits at 9bf3acfef681f9766ae1d93680b03996a93f98a1. Confirm through gitea_get_pr_review_feedback that the approval is visible, undismissed, non-stale, and at the current head, and that has_blocking_change_requests is false, before acting on it. If the head has moved, the approval is void and a fresh 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, and do not review or amend the branch. ``` WHAT_HAPPENED: A fresh independent reviewer pinned the current head, reproduced findings F1 and F2 directly against the PR code with the real workspace-root derivation and the real namespace resolver rather than test fixtures, ran the same reproduction against the superseded head as a control to prove the harness detects the defect, confirmed the five new F3 tests fail against the pre-fix code and pass against this head, ran focused and affected integration validation, independently reproduced the two claimed pre-existing failures on the base, and posted exactly one native APPROVE verdict. WHY: All three findings from review 483 are remediated at this head. F1 now feeds the gate the established repository-level `roots_aligned` contract, so a correctly bound `branches/` session is permitted rather than refused. F2 captures only immutable process facts at import 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 executes the real derivation and genuinely fails against the pre-fix code. ISSUE: #615 HEAD_SHA: ab34280f9025406d5c392d4e07c9d34d3ecd47fe REVIEW_STATUS: APPROVE MERGE_READY: yes BLOCKERS: no blocker 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. F1 and F2 reproduced with an independent harness that drives the real `_current_runtime_mode_report` and the real `_resolve_namespace_mutation_context` against a real `branches/` worktree. The five new F3 tests fail 5/5 against the pre-fix head and pass 5/5 against this head. The two author-claimed pre-existing failures reproduce identically on base 059ee77. git diff --check clean against base; remediation scope 3 files, 189 insertions, 30 deletions; no secrets and no unrelated or generated files. LAST_UPDATED_BY: prgs-reviewer (sysadmin) NATIVE_REVIEW_PROOF: native gitea_submit_pr_review through the prgs-reviewer MCP namespace, workflow skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6, head pinned ab34280f9025406d5c392d4e07c9d34d3ecd47fe, reviewer lease comment 13292. --- APPROVE — fresh independent native review of PR #770 (Closes #615, acceptance criteria 6-11) at head `ab34280f9025406d5c392d4e07c9d34d3ecd47fe`. Reviewer identity sysadmin (prgs-reviewer) is distinct from PR author jcwalker3. Base master `059ee77c1fd483da385d6ccb4d842e808fb54630`, commits behind 0, no conflicts, mergeable, checks not required by live branch protection. This verdict explicitly supersedes review 483, which recorded REQUEST_CHANGES at `9bf3acfef681f9766ae1d93680b03996a93f98a1`. That review remains undismissed but is stale at this head, and it was not reused as the current verdict. ## Method The author's handoff asked that the remediation be judged against the real derivation rather than the pass count. It was. Findings F1 and F2 were re-tested with an independent harness written for this review, which patches only `PROJECT_ROOT` and the import-time facts — and derives those facts by calling the PR's own `observe_runtime()` against the live control checkout, so they are observed rather than invented. `_resolve_namespace_mutation_context` ran unpatched against the real `branches/issue-615-runtime-mode-enforcement` worktree. The same harness was then run against `9bf3acf` as a control, because a reproduction that passes at both heads proves nothing. ## F1 — remediated, confirmed by controlled experiment The gate input is now `ctx.get("roots_aligned")` (`gitea_mcp_server.py:11414`), restoring the established repository-level meaning. That key is real and carries the expected semantics: `namespace_workspace_binding.py:156` defines it as `canonical_root == process_root`, which is the same contract already consumed at `gitea_mcp_server.py:573`. The fix consumes an existing contract rather than inventing a parallel one. Identical harness, identical real binding (`workspace_path` = the `branches/` task worktree, `canonical_repo_root` = `process_project_root` = the stable root, `roots_aligned` = True), differing only in the code under test: ``` head 9bf3acf (pre-fix) workspace_roots_aligned = False real_mutations_allowed = False block('gitea.pr.create') = ['process-root / active-workspace alignment is unsafe; ...'] head ab34280 (this head) runtime_mode = stable-control workspace_roots_aligned = True real_mutations_allowed = True block('gitea.pr.create') = [] ``` The correctly bound session that the previous head refused is now permitted, and the deadlock-on-promotion scenario described in review 483 no longer reproduces. Misalignment still fails closed: with `roots_aligned` False the gate raises the alignment blocker, so the fix narrowed the check to its intended meaning without removing it. ## F2 — remediated, confirmed by dynamic state change `_STARTUP_RUNTIME_FACTS` is now genuinely captured at import (`gitea_mcp_server.py:2027`), matching the `_STARTUP_PARITY` baseline it was previously only claimed to match. The result cache is gone entirely, and `refresh` no longer selects between cache and live state, so the read-only seeding path is structurally eliminated rather than merely discouraged. Observed clean, then changed dirty state, then re-evaluated the gate: ``` head ab34280 (this head) step 1 clean -> dirty=[] allowed=True block=[] step 2 now dirty -> dirty=['gitea_mcp_server.py'] allowed=False block=['stable control runtime checkout is dirty (1 file(s)) ...'] step 3 clean again -> dirty=[] allowed=True block=[] ``` The same sequence at `9bf3acf` reported `dirty=[]` at step 2 — the injected change was never observed at all, because the first report had been frozen. That is the F2 defect reproduced directly, and step 2 above is it not reproducing. The read-only path was checked separately: a permissive `refresh=True` call followed by a mutation gate evaluation with a newly dirty checkout returns blocked, so a read-only call can no longer weaken a later mutation decision. `observe_dirty_files()` is a clean seam for this — it isolates the one runtime fact that legitimately changes during a process lifetime, and `observe_runtime()` now delegates to it rather than duplicating the parsing. ## F3 — remediated, and the coverage is real `TestServerWiringRealDerivation` patches only the derivation's inputs — the import-time facts, the dirty-file read, and the resolved binding — and lets `_current_runtime_mode_report` and `_runtime_mode_block` execute for real. That is the distinction the finding asked for, and it holds. Verified rather than assumed: the new test file was extracted and run against the pre-fix server code, where all five fail, and against this head, where all five pass. The pre-fix failure mode is the absent `_STARTUP_RUNTIME_FACTS` symbol rather than an alignment assertion, which is a weaker signal than a behavioural failure would be — noted, but the harness above supplies the behavioural proof independently, and the tests are a genuine forward regression guard: reverting the F1 derivation to path equality would make `test_clean_stable_checkout_with_bound_task_worktree_permits_mutation` fail, since its fixture workspace is deliberately a `branches/` path distinct from the process root. The fifth test is a good addition beyond what was asked: an unresolvable binding must report alignment as unknown rather than as proof, and only definite misalignment blocks. Failing an unresolved binding closed would have reintroduced F1 through a different door. ## Scope, base, and pre-existing failures Remediation commit scope is `gitea_mcp_server.py`, `stable_control_runtime.py`, and `tests/test_stable_control_runtime.py` — 3 files, 189 insertions, 30 deletions, matching the author's record exactly. No unrelated, generated, or credential-bearing files; secret scan across the full PR diff is clean; `git diff --check` clean against base. Acceptance criteria 6, 8, 10, and 11 are untouched by this commit and were verified as correct in review 483. The two full-suite failures were independently reproduced on pristine base `059ee77`, in the reviewer's own worktree rather than taken on trust: - `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 fail at the base with the branch absent, so neither is caused by this PR. They remain open pre-existing defects on master and are out of scope here. ## Non-blocking observations, carried forward The three observations from review 483 were deliberately left out of scope to keep the remediation diff minimal, which is the right call. They are not approval blockers and are restated only so they are not lost: - `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 import. Not a live defect, but fragile: a future dependency importing `unittest.mock` at module scope would silently disable the gate in production. An explicit environment opt-in would fail safe. - `observe_runtime` porcelain parsing renders a rename as `old -> new`; cosmetic, it feeds a count and a message. One further observation from this review, also non-blocking: capturing `_STARTUP_RUNTIME_FACTS` at import moves several `git` subprocess calls into module import. That is a small, one-time startup cost and is the correct trade for a trustworthy baseline, but it is worth knowing that importing the server module now shells out. The `dirty_files` key inside `_STARTUP_RUNTIME_FACTS` is now dead — the gate always reads live dirt instead — and could be dropped from the captured dict for clarity. ## Verdict rationale All three findings are remediated at the seam where they were raised, and each remediation was confirmed against the real code path rather than against a fixture or a pass count. F1 restores an existing contract instead of inventing a new one, F2 removes the caching hazard structurally rather than patching around it, and F3 closes the coverage gap that let F1 through. The enforcement layer now permits the sanctioned configuration and still fails closed on genuine misalignment, dirty stable checkouts, dev-worktree launches, and unknown runtimes. No merge authorization is implied or granted by this review. Merge is a separate role decision under its own gates. [THREAD STATE LEDGER] PR #770 — APPROVE posted to Gitea at head ab34280f What is true now: - PR state: open, base master, commits_behind 0, has_conflicts false, mergeable true, checks not required by live branch protection - Server-side decision state: APPROVE posted to Gitea at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe, superseding review 483 which sits at the superseded head 9bf3acfef681f9766ae1d93680b03996a93f98a1 - Local verdict/state: APPROVE prepared and submitted from reviewer worktree branches/review-pr-770, pinned and re-verified at the current head immediately before submission - Latest known validation: focused suite 52 passed; affected integration set 153 passed with 20 subtests; F1 and F2 reproduced against the real derivation with a pre-fix control run; five new F3 tests fail 5/5 pre-fix and pass 5/5 at this head; two pre-existing base failures reproduced on 059ee77 What changed: - fresh independent reviewer inspection completed at head ab34280f - APPROVE posted to Gitea; review 483 superseded by a new verdict at the current head rather than reused or dismissed 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 this approval as merge authorization; act on it if the head has moved; edit or manually release the author issue lock on #615; amend the branch; remove the branches/issue-615-runtime-mode-enforcement or branches/baseline-master-615 worktrees - Resume from: this review at head ab34280f9025406d5c392d4e07c9d34d3ecd47fe
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #770
issue: #615
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 51581-b2b2bfb0df07
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-770
phase: claimed
candidate_head: ab34280f90
target_branch: master
target_branch_sha: 059ee77c1f
last_activity: 2026-07-20T17:36:13Z
expires_at: 2026-07-20T17:46:13Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #770 issue: #615 reviewer_identity: sysadmin profile: prgs-merger session_id: 51581-b2b2bfb0df07 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-770 phase: claimed candidate_head: ab34280f9025406d5c392d4e07c9d34d3ecd47fe target_branch: master target_branch_sha: 059ee77c1fd483da385d6ccb4d842e808fb54630 last_activity: 2026-07-20T17:36:13Z expires_at: 2026-07-20T17:46:13Z blocker: none
sysadmin merged commit 0568f44cb2 into master 2026-07-20 12:36:59 -05:00
Owner

Stale #332 review-decision lock cleanup (#594)

Status: APPLIED

Manual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.

## Stale #332 review-decision lock cleanup (#594) Status: **APPLIED** - actor: `sysadmin` - profile: `prgs-merger` - timestamp: `2026-07-20T17:37:11.343400+00:00` - last terminal: `approve` on PR #770 - PR state: `closed` (merged=True) - merge_commit_sha: `0568f44cb2d87e78fd394a27a670e33c84f7842f` - prior live_mutations_count: `2` - prior profile_identity: `prgs-reviewer` Manual deletion of session-state files is **not** the workflow. This path only clears a lock when the referenced PR is merged/closed.
Owner

Canonical Issue State

STATE: closed — PR #770 landed on master via merge commit 0568f44cb2 at 2026-07-20T12:36:59-05:00; linked issue #615 is closed
CANONICAL_ITEM: this merger handoff for PR #770 at landed head ab34280f90
SUPERSEDED_ITEM: none — review 484 (APPROVE) was consumed as the merge precondition and is not superseded; review 483 (REQUEST_CHANGES) was already dismissed and stale at the landed head
WHO_IS_NEXT: reconciler
NEXT_ACTION: A prgs-reconciler session performs post-merge reconciliation for PR #770 and issue #615 under its own gates
NEXT_PROMPT:

Reconcile the landed PR #770 (Closes #615) as prgs-reconciler.

PR #770 is closed, having landed on master as merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f at 2026-07-20T12:36:59-05:00, from head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Issue #615 is closed. The post-merge decision-lock reconcile already cleared the prgs-reviewer decision lock (audit comment 13304).

Scope for reconciliation, each verified terminal and independently safe before any mutation:
- remote branch feat/issue-615-runtime-mode-enforcement
- local worktrees branches/issue-615-runtime-mode-enforcement, branches/review-pr-770, branches/merge-pr-770, branches/baseline-master-615, branches/baseline-pr770-prefix
- any residual moot leases or status labels on #615 / #770

Preview cleanup before applying it. Do not perform broad cleanup that sweeps in unrelated targets — the repository currently carries many unrelated worktrees from other issues. Do not manually delete lock files, branches, or worktrees. Do not advance the control checkout and do not restart any namespace.

Note: the control checkout remains on master at 059ee77c1fd483da385d6ccb4d842e808fb54630, which is now behind live master. Advancing it is a separate operator-owned or dedicated-phase action, deliberately not performed by the merger session.

WHAT_HAPPENED: A prgs-merger session pinned the candidate head, confirmed review 484 was visible, undismissed, non-stale and at that exact head, reassessed branch protection, checks, mergeability, conflicts, commits-behind, identity separation, runtime parity and namespace health, acquired a native merger lease, completed the merge through the sanctioned MCP merge tool with explicit operator authorization, and verified the result by read-after-write.
WHY: All merge gates passed at the pinned head with no blocking change requests, no conflicts, zero commits behind, and merger identity distinct from the PR author.
RELATED_PRS: #770 (this PR, closed, landed on master as merge commit 0568f44cb2, closing issue #615). No other PR is in scope for this merger phase; no PR was superseded, reopened, or otherwise affected by this merge.
ISSUE: #615
HEAD_SHA: ab34280f90
MERGE_COMMIT_SHA: 0568f44cb2
REVIEW_STATUS: APPROVE consumed (review 484, undismissed, non-stale, at the landed head)
MERGE_READY: consumed — merge completed and verified
BLOCKERS: no blocker
VALIDATION: No new code validation was run by the merger session; validation is the reviewer's recorded evidence at this head (focused suite 52 passed; affected integration set 153 passed with 20 subtests; five F3 tests fail 5/5 pre-fix and pass 5/5 at this head). Merger-side gate evidence: gitea_check_pr_eligibility eligible true with all checks passed; gitea_assess_pr_sync_status recommended_next_action merge_now, commits_behind 0, has_conflicts false, mergeable true, checks_required false, approval_valid_for_merge true, stale_approval false; gitea_get_pr_review_feedback approval_visible true, approval_at_current_head true, has_blocking_change_requests false, quarantined_approvals_at_current_head 0; runtime parity in_parity true, restart_required false, startup_head equals current_head 059ee77c1fd4; namespace gitea-merger ide_namespace_proven true with blocks_merge_workflow false; workflow proof skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6.
LAST_UPDATED_BY: prgs-merger (sysadmin)

NATIVE_MERGE_PROOF: native gitea_merge_pr through the prgs-merger MCP namespace, merge method 'merge', explicit operator authorization "MERGE PR 770", expected_head_sha pinned ab34280f90, merger lease comment 13298, lease_proof_kind sanctioned_acquire_merger, lease_proof_sanctioned true, post-merge decision-lock audit comment 13304.


MERGE COMPLETED — PR #770 (Closes #615, acceptance criteria 6-11) landed on master.

Merge gates, reassessed live at the pinned head

Merger identity sysadmin (prgs-merger) is distinct from PR author jcwalker3, so the self-merge block does not apply. Every gate below was evaluated against live state in this session, not carried over from the reviewer handoff.

  • Approval: review 484 APPROVE, dismissed=false, stale=false, reviewed_head_sha equal to the pinned candidate head. approval_visible=true, approval_at_current_head=true, latest_approved_head_sha equal to the landed head.
  • Blocking reviews: has_blocking_change_requests=false. Review 483 (REQUEST_CHANGES) sits at the superseded head 9bf3acfe and is recorded dismissed=true, stale=true, so it did not gate the merge.
  • Quarantine: quarantined_review_ids empty, quarantined_approvals_at_current_head=0.
  • Branch protection: live protection does not require status checks for master, so head check state (not_required, combined state pending with zero contexts) does not gate the merge. branch_protection_requires_current_base=false.
  • Mergeability and conflicts: mergeable=true, has_conflicts=false.
  • Commits behind: 0 against base master 059ee77c1f.
  • Runtime parity: in_parity=true, stale=false, restart_required=false, startup head equal to current head at 059ee77c1f, mutation gate enforced.
  • Namespace health: gitea-merger probed live through client_namespace proof, ide_namespace_proven=true, blocks_merge_workflow=false.
  • Lease: acquired natively through gitea_acquire_merger_pr_lease in worktree branches/merge-pr-770 pinned to the candidate head, comment 13298. No foreign or adopted lease was involved; lease_adopted_from_session_id is null.

Live state was re-read after lease acquisition and immediately before the merge call. The head had not moved.

Read-after-write verification

The merge tool returned performed=true with a null merge_commit field, so completion was not claimed on that response alone. Verified independently:

  • PR #770: state=closed, merge timestamp 2026-07-20T12:36:59-05:00, merge_commit_sha=0568f44cb2d87e78fd394a27a670e33c84f7842f, closed_at=2026-07-20T12:36:59-05:00.
  • Issue #615: state=closed. The status:in-progress label is no longer present on the issue.

Post-merge side effects recorded by the merge tool

  • The stale author allocator lease on issue #615 (session prgs-author-62865-d5cc892b, owner pid 62865, reported stale_dead_process) was released by the tool's own cleanup path: cleanup_status {"615": "released"}. It was not edited or released manually.
  • The prgs-reviewer review decision lock was cleared after the merge under #709, with audit comment 13304. Eight profiles were scanned; only prgs-reviewer held a lock. recovery_required=false.

Deliberately not performed

Per the bounded merger phase scope: no review or verdict, no author remediation, no branch amendment, no branch deletion, no worktree deletion, no control-checkout advance, no namespace restart, and no selection of a follow-on task.

Residual state later cycles must not retry

  • The control checkout remains on master at 059ee77c1f and is now behind live master. This is expected and intentional. Advancing it is a separate phase; do not treat it as a merge defect.
  • The merger worktree branches/merge-pr-770 was created for this phase at the pinned head and still exists. It is reconciler scope.
  • Non-blocking observations carried forward from reviews 483 and 484 remain open and were not addressed by this merge: lexicographic timestamp comparison in assess_namespace_reproof; _RUNTIME_MODE_GATE_UNDER_TEST inferred from sys.modules rather than an explicit environment opt-in; observe_runtime porcelain parsing rendering a rename as old -> new; and the now-dead dirty_files key inside _STARTUP_RUNTIME_FACTS.
  • Two full-suite failures pre-exist on base 059ee77 and were independently reproduced there by the reviewer. They are not caused by this PR and remain open defects on master: tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe and tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue.
  • This change promotes the runtime-mode enforcement layer into master, but the running MCP daemon still serves the pre-merge code at 059ee77c1f. The enforcement gate does not become live until an operator-owned promotion advances the stable control runtime, per docs/stable-runtime-promotion-runbook.md landed by this PR.

[THREAD STATE LEDGER] PR #770 — merge completed and verified at merge commit 0568f44c

What is true now:

  • PR state: closed, landed on master as merge commit 0568f44cb2, merge timestamp 2026-07-20T12:36:59-05:00
  • Linked issue state: #615 closed, status:in-progress label no longer present
  • Server-side decision state: APPROVE review 484 consumed as the merge precondition; prgs-reviewer decision lock cleared post-merge with audit comment 13304
  • Local verdict/state: merger phase executed from worktree branches/merge-pr-770 pinned at ab34280f9025406d5c392d4e07c9d34d3ecd47fe; control checkout clean on master at 059ee77c1f
  • Latest known validation: reviewer evidence at this head — focused suite 52 passed, affected integration set 153 passed with 20 subtests; merger session ran no new code validation

What changed:

  • PR #770 landed on master through the sanctioned native MCP merge tool with explicit operator authorization
  • merge result independently verified by read-after-write on the PR and the linked issue
  • stale author allocator lease on #615 released by the tool's own cleanup path
  • prgs-reviewer review decision lock cleared under #709

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: reconciler
  • Required action: preview and then apply scoped post-merge reconciliation for PR #770 and issue ADR: stable control runtime vs dev runtime for Gitea MCP (#615)
  • Do not do: re-run the merge, treat the null merge_commit field in the tool response as an ambiguous outcome now that read-after-write has resolved it, advance the control checkout as part of reconciliation, restart any namespace, manually delete lock files or branches or worktrees, or sweep unrelated worktrees into cleanup
  • Resume from: this merger handoff at merge commit 0568f44cb2
## Canonical Issue State STATE: closed — PR #770 landed on master via merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f at 2026-07-20T12:36:59-05:00; linked issue #615 is closed CANONICAL_ITEM: this merger handoff for PR #770 at landed head ab34280f9025406d5c392d4e07c9d34d3ecd47fe SUPERSEDED_ITEM: none — review 484 (APPROVE) was consumed as the merge precondition and is not superseded; review 483 (REQUEST_CHANGES) was already dismissed and stale at the landed head WHO_IS_NEXT: reconciler NEXT_ACTION: A prgs-reconciler session performs post-merge reconciliation for PR #770 and issue #615 under its own gates NEXT_PROMPT: ```text Reconcile the landed PR #770 (Closes #615) as prgs-reconciler. PR #770 is closed, having landed on master as merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f at 2026-07-20T12:36:59-05:00, from head ab34280f9025406d5c392d4e07c9d34d3ecd47fe. Issue #615 is closed. The post-merge decision-lock reconcile already cleared the prgs-reviewer decision lock (audit comment 13304). Scope for reconciliation, each verified terminal and independently safe before any mutation: - remote branch feat/issue-615-runtime-mode-enforcement - local worktrees branches/issue-615-runtime-mode-enforcement, branches/review-pr-770, branches/merge-pr-770, branches/baseline-master-615, branches/baseline-pr770-prefix - any residual moot leases or status labels on #615 / #770 Preview cleanup before applying it. Do not perform broad cleanup that sweeps in unrelated targets — the repository currently carries many unrelated worktrees from other issues. Do not manually delete lock files, branches, or worktrees. Do not advance the control checkout and do not restart any namespace. Note: the control checkout remains on master at 059ee77c1fd483da385d6ccb4d842e808fb54630, which is now behind live master. Advancing it is a separate operator-owned or dedicated-phase action, deliberately not performed by the merger session. ``` WHAT_HAPPENED: A prgs-merger session pinned the candidate head, confirmed review 484 was visible, undismissed, non-stale and at that exact head, reassessed branch protection, checks, mergeability, conflicts, commits-behind, identity separation, runtime parity and namespace health, acquired a native merger lease, completed the merge through the sanctioned MCP merge tool with explicit operator authorization, and verified the result by read-after-write. WHY: All merge gates passed at the pinned head with no blocking change requests, no conflicts, zero commits behind, and merger identity distinct from the PR author. RELATED_PRS: #770 (this PR, closed, landed on master as merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f, closing issue #615). No other PR is in scope for this merger phase; no PR was superseded, reopened, or otherwise affected by this merge. ISSUE: #615 HEAD_SHA: ab34280f9025406d5c392d4e07c9d34d3ecd47fe MERGE_COMMIT_SHA: 0568f44cb2d87e78fd394a27a670e33c84f7842f REVIEW_STATUS: APPROVE consumed (review 484, undismissed, non-stale, at the landed head) MERGE_READY: consumed — merge completed and verified BLOCKERS: no blocker VALIDATION: No new code validation was run by the merger session; validation is the reviewer's recorded evidence at this head (focused suite 52 passed; affected integration set 153 passed with 20 subtests; five F3 tests fail 5/5 pre-fix and pass 5/5 at this head). Merger-side gate evidence: gitea_check_pr_eligibility eligible true with all checks passed; gitea_assess_pr_sync_status recommended_next_action merge_now, commits_behind 0, has_conflicts false, mergeable true, checks_required false, approval_valid_for_merge true, stale_approval false; gitea_get_pr_review_feedback approval_visible true, approval_at_current_head true, has_blocking_change_requests false, quarantined_approvals_at_current_head 0; runtime parity in_parity true, restart_required false, startup_head equals current_head 059ee77c1fd4; namespace gitea-merger ide_namespace_proven true with blocks_merge_workflow false; workflow proof skills/llm-project-workflow/workflows/review-merge-pr.md hash 263d0a6cb8a6. LAST_UPDATED_BY: prgs-merger (sysadmin) NATIVE_MERGE_PROOF: native gitea_merge_pr through the prgs-merger MCP namespace, merge method 'merge', explicit operator authorization "MERGE PR 770", expected_head_sha pinned ab34280f9025406d5c392d4e07c9d34d3ecd47fe, merger lease comment 13298, lease_proof_kind sanctioned_acquire_merger, lease_proof_sanctioned true, post-merge decision-lock audit comment 13304. --- MERGE COMPLETED — PR #770 (Closes #615, acceptance criteria 6-11) landed on master. ## Merge gates, reassessed live at the pinned head Merger identity sysadmin (prgs-merger) is distinct from PR author jcwalker3, so the self-merge block does not apply. Every gate below was evaluated against live state in this session, not carried over from the reviewer handoff. - Approval: review 484 APPROVE, `dismissed=false`, `stale=false`, `reviewed_head_sha` equal to the pinned candidate head. `approval_visible=true`, `approval_at_current_head=true`, `latest_approved_head_sha` equal to the landed head. - Blocking reviews: `has_blocking_change_requests=false`. Review 483 (REQUEST_CHANGES) sits at the superseded head 9bf3acfe and is recorded `dismissed=true`, `stale=true`, so it did not gate the merge. - Quarantine: `quarantined_review_ids` empty, `quarantined_approvals_at_current_head=0`. - Branch protection: live protection does not require status checks for master, so head check state (`not_required`, combined state pending with zero contexts) does not gate the merge. `branch_protection_requires_current_base=false`. - Mergeability and conflicts: `mergeable=true`, `has_conflicts=false`. - Commits behind: 0 against base master 059ee77c1fd483da385d6ccb4d842e808fb54630. - Runtime parity: `in_parity=true`, `stale=false`, `restart_required=false`, startup head equal to current head at 059ee77c1fd4, mutation gate enforced. - Namespace health: gitea-merger probed live through `client_namespace` proof, `ide_namespace_proven=true`, `blocks_merge_workflow=false`. - Lease: acquired natively through `gitea_acquire_merger_pr_lease` in worktree `branches/merge-pr-770` pinned to the candidate head, comment 13298. No foreign or adopted lease was involved; `lease_adopted_from_session_id` is null. Live state was re-read after lease acquisition and immediately before the merge call. The head had not moved. ## Read-after-write verification The merge tool returned `performed=true` with a null `merge_commit` field, so completion was not claimed on that response alone. Verified independently: - PR #770: `state=closed`, merge timestamp 2026-07-20T12:36:59-05:00, `merge_commit_sha=0568f44cb2d87e78fd394a27a670e33c84f7842f`, `closed_at=2026-07-20T12:36:59-05:00`. - Issue #615: `state=closed`. The `status:in-progress` label is no longer present on the issue. ## Post-merge side effects recorded by the merge tool - The stale author allocator lease on issue #615 (session `prgs-author-62865-d5cc892b`, owner pid 62865, reported `stale_dead_process`) was released by the tool's own cleanup path: `cleanup_status {"615": "released"}`. It was not edited or released manually. - The prgs-reviewer review decision lock was cleared after the merge under #709, with audit comment 13304. Eight profiles were scanned; only prgs-reviewer held a lock. `recovery_required=false`. ## Deliberately not performed Per the bounded merger phase scope: no review or verdict, no author remediation, no branch amendment, no branch deletion, no worktree deletion, no control-checkout advance, no namespace restart, and no selection of a follow-on task. ## Residual state later cycles must not retry - The control checkout remains on master at 059ee77c1fd483da385d6ccb4d842e808fb54630 and is now behind live master. This is expected and intentional. Advancing it is a separate phase; do not treat it as a merge defect. - The merger worktree `branches/merge-pr-770` was created for this phase at the pinned head and still exists. It is reconciler scope. - Non-blocking observations carried forward from reviews 483 and 484 remain open and were not addressed by this merge: lexicographic timestamp comparison in `assess_namespace_reproof`; `_RUNTIME_MODE_GATE_UNDER_TEST` inferred from `sys.modules` rather than an explicit environment opt-in; `observe_runtime` porcelain parsing rendering a rename as `old -> new`; and the now-dead `dirty_files` key inside `_STARTUP_RUNTIME_FACTS`. - Two full-suite failures pre-exist on base 059ee77 and were independently reproduced there by the reviewer. They are not caused by this PR and remain open defects on master: `tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe` and `tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue`. - This change promotes the runtime-mode enforcement layer into master, but the running MCP daemon still serves the pre-merge code at 059ee77c1fd4. The enforcement gate does not become live until an operator-owned promotion advances the stable control runtime, per `docs/stable-runtime-promotion-runbook.md` landed by this PR. [THREAD STATE LEDGER] PR #770 — merge completed and verified at merge commit 0568f44c What is true now: - PR state: closed, landed on master as merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f, merge timestamp 2026-07-20T12:36:59-05:00 - Linked issue state: #615 closed, status:in-progress label no longer present - Server-side decision state: APPROVE review 484 consumed as the merge precondition; prgs-reviewer decision lock cleared post-merge with audit comment 13304 - Local verdict/state: merger phase executed from worktree branches/merge-pr-770 pinned at ab34280f9025406d5c392d4e07c9d34d3ecd47fe; control checkout clean on master at 059ee77c1fd483da385d6ccb4d842e808fb54630 - Latest known validation: reviewer evidence at this head — focused suite 52 passed, affected integration set 153 passed with 20 subtests; merger session ran no new code validation What changed: - PR #770 landed on master through the sanctioned native MCP merge tool with explicit operator authorization - merge result independently verified by read-after-write on the PR and the linked issue - stale author allocator lease on #615 released by the tool's own cleanup path - prgs-reviewer review decision lock cleared under #709 What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: reconciler - Required action: preview and then apply scoped post-merge reconciliation for PR #770 and issue #615 - Do not do: re-run the merge, treat the null merge_commit field in the tool response as an ambiguous outcome now that read-after-write has resolved it, advance the control checkout as part of reconciliation, restart any namespace, manually delete lock files or branches or worktrees, or sweep unrelated worktrees into cleanup - Resume from: this merger handoff at merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f
Owner

Canonical Issue State

STATE: terminal_cleanup_complete for PR #770 / Issue #615
WHO_IS_NEXT: controller
NEXT_ACTION: select next queue item unrelated to #770/#615; do not reopen this pair
NEXT_PROMPT:

#770/#615 terminal cleanup is complete (LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553).
Do not re-review, re-merge, or re-allocate work on #615.
Proceed with normal queue controller selection on other items only.

WHAT_HAPPENED: Supervised reconciler terminal cleanup for #770/#615 only. Gate Zero parity at 0568f44. Post-merge lease dry-run found no active lease. Author lease lease-13750394b96b4ce4 abandoned with dead-owner proof. Removed branches/merge-pr-770 via scripts/worktree-clean. Deleted remote/local feat/issue-615-runtime-mode-enforcement via gitea_cleanup_merged_pr_branch + safe local delete; verified_absent=true.
WHY: Operator-requested supervised terminal cleanup after merge performed; release stale author lease and residual branch/worktree artifacts without review/merge replay or general audit.
RELATED_PRS: #770 (closes #615)
BLOCKERS: none
VALIDATION: gitea_assess_master_parity restart_required=false at 0568f44; gitea_cleanup_post_merge_moot_lease dry-run active_lease=null; gitea_inspect_workflow_lease status=abandoned; gitea_cleanup_merged_pr_branch verified_absent=true; control checkout clean
LAST_UPDATED_BY: prgs-reconciler / sysadmin / LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553


[THREAD STATE LEDGER] PR #770 / Issue #615 — terminal cleanup complete; no remaining live work for this pair

What is true now:

  • PR state: closed after merge performed (merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f; head ab34280f90)
  • Issue state: closed
  • Current head SHA: 0568f44cb2 on prgs/master (Gate Zero parity)
  • Server-side decision state: server-side state changed — author lease lease-13750394b96b4ce4 abandoned; remote branch feat/issue-615-runtime-mode-enforcement deleted and verified absent; post-merge PR lease already inactive (no marker post needed)
  • Local verdict/state: supervised reconciler terminal cleanup complete for #770/#615 only (LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553)
  • Latest known validation: gitea_assess_master_parity restart_required=false; cleanup_post_merge_moot_lease dry-run active_lease=null; lease inspect status=abandoned; gitea_cleanup_merged_pr_branch verified_absent=true

What changed:

  • Dry-run gitea_cleanup_post_merge_moot_lease(pr=770, session 51581-b2b2bfb0df07, head ab34280, comment 13298): cleanup_allowed=false, nothing remaining
  • Abandoned control-plane author lease lease-13750394b96b4ce4 with dead-owner + missing-worktree + no-open-pr + no-live-mutation-risk proof
  • Removed clean worktree branches/merge-pr-770 via scripts/worktree-clean (non-force)
  • Removed clean worktree branches/issue-615-runtime-mode-enforcement; safe git branch -d feat/issue-615-runtime-mode-enforcement
  • Deleted remote feat/issue-615-runtime-mode-enforcement via gitea_cleanup_merged_pr_branch (no force-delete)

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: controller
  • Required action: treat #770/#615 as terminal; select next queue item unrelated to this pair
  • Do not do: re-review, re-merge, re-adopt abandoned lease-13750394b96b4ce4, force-delete branches, general cleanup audit, allocate new work on #615, or touch #618/#765/branches/issue-681-preserve-wip
  • Resume from: queue controller selection after this ledger; not from PR #770 review/merge path
## Canonical Issue State STATE: terminal_cleanup_complete for PR #770 / Issue #615 WHO_IS_NEXT: controller NEXT_ACTION: select next queue item unrelated to #770/#615; do not reopen this pair NEXT_PROMPT: ```text #770/#615 terminal cleanup is complete (LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553). Do not re-review, re-merge, or re-allocate work on #615. Proceed with normal queue controller selection on other items only. ``` WHAT_HAPPENED: Supervised reconciler terminal cleanup for #770/#615 only. Gate Zero parity at 0568f44. Post-merge lease dry-run found no active lease. Author lease lease-13750394b96b4ce4 abandoned with dead-owner proof. Removed branches/merge-pr-770 via scripts/worktree-clean. Deleted remote/local feat/issue-615-runtime-mode-enforcement via gitea_cleanup_merged_pr_branch + safe local delete; verified_absent=true. WHY: Operator-requested supervised terminal cleanup after merge performed; release stale author lease and residual branch/worktree artifacts without review/merge replay or general audit. RELATED_PRS: #770 (closes #615) BLOCKERS: none VALIDATION: gitea_assess_master_parity restart_required=false at 0568f44; gitea_cleanup_post_merge_moot_lease dry-run active_lease=null; gitea_inspect_workflow_lease status=abandoned; gitea_cleanup_merged_pr_branch verified_absent=true; control checkout clean LAST_UPDATED_BY: prgs-reconciler / sysadmin / LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553 --- [THREAD STATE LEDGER] PR #770 / Issue #615 — terminal cleanup complete; no remaining live work for this pair What is true now: - PR state: closed after merge performed (merge commit 0568f44cb2d87e78fd394a27a670e33c84f7842f; head ab34280f9025406d5c392d4e07c9d34d3ecd47fe) - Issue state: closed - Current head SHA: 0568f44cb2d87e78fd394a27a670e33c84f7842f on prgs/master (Gate Zero parity) - Server-side decision state: server-side state changed — author lease lease-13750394b96b4ce4 abandoned; remote branch feat/issue-615-runtime-mode-enforcement deleted and verified absent; post-merge PR lease already inactive (no marker post needed) - Local verdict/state: supervised reconciler terminal cleanup complete for #770/#615 only (LLM_LOCK_ID=reconciler-770-615-be0c8bf5ed9e-1784569553) - Latest known validation: gitea_assess_master_parity restart_required=false; cleanup_post_merge_moot_lease dry-run active_lease=null; lease inspect status=abandoned; gitea_cleanup_merged_pr_branch verified_absent=true What changed: - Dry-run gitea_cleanup_post_merge_moot_lease(pr=770, session 51581-b2b2bfb0df07, head ab34280, comment 13298): cleanup_allowed=false, nothing remaining - Abandoned control-plane author lease lease-13750394b96b4ce4 with dead-owner + missing-worktree + no-open-pr + no-live-mutation-risk proof - Removed clean worktree branches/merge-pr-770 via scripts/worktree-clean (non-force) - Removed clean worktree branches/issue-615-runtime-mode-enforcement; safe git branch -d feat/issue-615-runtime-mode-enforcement - Deleted remote feat/issue-615-runtime-mode-enforcement via gitea_cleanup_merged_pr_branch (no force-delete) What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: controller - Required action: treat #770/#615 as terminal; select next queue item unrelated to this pair - Do not do: re-review, re-merge, re-adopt abandoned lease-13750394b96b4ce4, force-delete branches, general cleanup audit, allocate new work on #615, or touch #618/#765/branches/issue-681-preserve-wip - Resume from: queue controller selection after this ledger; not from PR #770 review/merge path
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

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