{"type":"transition_applied","payload":{"type":"transition_applied","status":"running","residency_state":"resident"}}
{"type":"assistant_message","payload":{"text":"I read this as a read-only test-design investigation - I’ll trace the runtime credential lifecycle, existing fixtures, and failure evidence, then specify the smallest executable RED test plus the coherence regression matrix with source citations."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"assistant_message","payload":{"text":"The live evidence narrows the defect to post-commit authority publication: the transaction committed and the successor ran, but manual QA rejected its credential as non-current/revoked. I’m now mapping that boundary onto the combined worktree’s existing fixtures and transaction fault hooks."}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":true}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":true}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"read","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"tool_execution","payload":{"tool":"bash","is_error":false}}
{"type":"assistant_message","payload":{"text":"I read this as a read-only test-design investigation - I traced the live failure through credential loading, external authority publication, transaction commit, and rollback.\n\n## Finding\n\nThe cheapest faithful RED is a disposable transaction test that:\n\n1. Starts with an external authority whose current candidate is the predecessor.\n2. Gives the service both real credential names.\n3. Executes the v1.5 successor transaction.\n4. Loads authority through the production credential loader from the successor credential directory.\n\nCurrent behavior rebinds `task26-candidate-digest` but merely copies the old authority pin; it never promotes the successor in the external registry/ledger. The production loader then reaches the exact live error.\n\nEvidence confirms this was post-commit, not startup/install failure:\n\n- Live manual QA error: `.omo/evidence/nutricoach-v150-combined/task-v14r20-live-outcome.json:8`\n- Ledger had already succeeded: same file `:11`\n- Correct remediation recorded as “repair candidate-current authority publication”: same file `:18`\n- Successor credential held digest `3dab...`: rehearsal credential line 1.\n- Its pin still referenced the 13-event v1.3 authority: rehearsal pin line 1.\n- The live drop-in loaded both `task26-authority-pin.json` and `task26-candidate-digest`: rehearsal `authority.conf:4-5`.\n\n## Cheapest failing-first test\n\n**File:** `tests/test_nutricoach_v150_v14_safety.py`  \n**Test:** `test_committed_successor_is_current_in_external_runtime_authority`\n\n### Fixture composition\n\nReuse:\n\n- `target_fixture(tmp_path)` for profile, service unit, and transaction files (`tests/test_nutricoach_v150_sealed_controller.py:26-36`).\n- `write_runtime_authority(tmp_path / \"external-authority\", predecessor)` for a private, coherent two-event registry/ledger/pin/candidate fixture (`scripts/dualcoach_v111_disposable_fixture.py:243-283`).\n- `ConcreteLiveHost.disposable(...)`, which already supports deterministic stage faults (`scripts/nutricoach_v150_concrete_host.py:90-107,130-157,220-223`).\n- `load_task26_production_authority(...)`, which reads the exact production credential names and performs activation authorization (`gateway/platforms/task26_runtime_authority.py:357-389`).\n\nConfigure the current drop-in with:\n\n```text\nLoadCredential=task26-authority-pin.json:<fixture pin>\nLoadCredential=task26-candidate-digest:<fixture predecessor digest>\n```\n\nSet `CREDENTIALS_DIRECTORY` to the successor’s generated `runtime-authority` directory after `execute_disposable(...)`.\n\n### Expected RED\n\n```python\nsource, candidate = load_task26_production_authority(\n    profile_root=host.paths.profile,\n    package_root=tmp_path / \"package\",\n)\n```\n\nCurrent code raises:\n\n```text\nValueError: runtime candidate is not current or was revoked\n```\n\nThis is the exact production branch at `gateway/platforms/task26_runtime_authority.py:146-148`.\n\nWhy it fails:\n\n- `switch_postimages()` rewrites the candidate credential for both supported names (`scripts/nutricoach_v150_host_operations.py:127-142`).\n- It copies every other credential, including the stale pin, unchanged.\n- External registry/ledger current-candidate state remains the predecessor.\n- Production loading validates the credential candidate against that external state.\n\n### GREEN observable\n\nThe same production load must succeed, followed by:\n\n```python\nassert candidate == host.candidate_digest\nwith source.authorize(candidate, \"activation\") as snapshot:\n    assert snapshot[\"candidate_digest\"] == host.candidate_digest\n```\n\nAlso assert all four authority surfaces agree:\n\n- successor credential digest;\n- external registry `current_qualified_candidate`;\n- external registry and ledger heads;\n- installed pin’s `event_count`, `registry_head_sha256`, and `ledger_head_sha256`.\n\nFinally:\n\n```python\nassert PhaseJournal(...).phase() == \"COMMITTED\"\nassert GlobalLedger(...).outcome() == \"SUCCEEDED\"\n```\n\nDo not accept only “credential file contains the successor”; that is already covered by `test_switch_rebinds_task26_candidate_credential_name` (`tests/test_nutricoach_v150_v14_safety.py:239-257`) and missed the live defect.\n\n## Required regression tests\n\n### 1. Stale pin\n\n**File:** `tests/test_nutricoach_v150_v14_safety.py`  \n**Test:** `test_runtime_authority_promotion_never_commits_a_stale_pin`\n\nAdvance the external registry/ledger to the successor while retaining the predecessor pin, then exercise production loading.\n\nExpected fail-closed result:\n\n```text\nValueError: runtime authority pin is stale\n```\n\nThe constructor requires pin event count and both heads to equal current chains (`gateway/platforms/task26_runtime_authority.py:102-126`).\n\nFor the fixed transaction, inject failure after registry/ledger publication but before pin publication and assert:\n\n- transaction is not `COMMITTED`;\n- predecessor service is running;\n- successor is absent;\n- a freshly loaded predecessor credential succeeds;\n- pin heads equal verified registry/ledger heads.\n\n### 2. Interrupted promotion\n\n**File:** `tests/test_nutricoach_v150_v14_safety.py`  \n**Test:** `test_interrupted_runtime_authority_promotion_recovers_coherent_authority`\n\nParameterize deterministic publication checkpoints, not sleeps:\n\n```python\n[\n    \"registry_published\",\n    \"ledger_published\",\n    \"registry_source_bound\",\n    \"ledger_source_bound\",\n    \"pin_published\",\n    \"successor_credential_published\",\n]\n```\n\nThese boundaries are necessary because current publication is multi-write:\n\n- base registry then ledger: `task26_candidate_authority.py:165-166`;\n- source/genesis rebinding rewrites each document again: `task26_runtime_authority.py:420-423`;\n- fixture pin and candidate are separate writes: `dualcoach_v111_disposable_fixture.py:276-283`.\n\nEach injected `BaseException`, followed by fresh-process `recover_pending(host)`, must produce:\n\n- phase `ROLLED_BACK`;\n- transaction ledger `FAILED`;\n- no successor;\n- predecessor active;\n- `verify_candidate_authority(root, predecessor)` succeeds;\n- production loading through the active runtime’s credential directory succeeds;\n- pin event count and heads exactly match the paired chains.\n\nThis requires a durable promotion journal or equivalent recovery data. Restoring only profile/unit/drop-in bytes is insufficient.\n\n### 3. Ordinary pre-commit rollback after completed promotion\n\n**File:** `tests/test_nutricoach_v150_sealed_controller.py`  \n**Test:** `test_post_promotion_fault_restores_predecessor_external_authority`\n\nExtend the existing stage matrix with a named authority-promotion stage before service activation. Inject the next-stage fault after promotion.\n\nAssert behavioral restoration, not byte rewind:\n\n- external current candidate is predecessor;\n- any compensating append remains chain-valid;\n- active predecessor pin references the new final heads;\n- successor credential/root is absent;\n- service is running;\n- approval ledger is consumed `FAILED`.\n\nThe existing generic fault matrix already checks every concrete stage, exact profile restoration, service restart, and successor cleanup (`tests/test_nutricoach_v150_sealed_controller.py:152-189`). It should include the promotion stage and authority coherence assertions.\n\n### 4. Post-commit/manual-QA rollback\n\n**File:** `tests/test_nutricoach_v150_v14_safety.py`  \n**Test:** `test_postcommit_manual_qa_rollback_republishes_predecessor_authority`\n\nReproduce the live sequence:\n\n1. Transaction reaches `COMMITTED`/`SUCCEEDED`.\n2. Successor production authority load succeeds.\n3. Invoke the committed rollback path with a synthetic manual-QA failure.\n4. Load production authority through the restored predecessor credentials.\n\nGREEN requires:\n\n- phase `ROLLED_BACK`;\n- one-use ledger remains `SUCCEEDED`, matching the live semantics;\n- predecessor is current externally;\n- predecessor credential and refreshed pin load successfully;\n- successor and weekly authority are absent.\n\nThe existing evidence rollback explicitly starts from `COMMITTED` (`r20-postcommit-rollback.py:50-69`) and checks predecessor restoration and successor removal (`:76-113`), but does not test external registry/ledger/pin authority.\n\n## Supporting unit coverage\n\nAdd **`tests/gateway/test_task26_runtime_authority_promotion.py`** with:\n\n- `test_promotion_publishes_candidate_registry_ledger_and_pin_coherently`\n- `test_stale_pin_is_rejected_after_external_head_advances`\n- `test_interrupted_promotion_recovers_without_mixed_heads`\n- `test_rollback_qualification_makes_predecessor_current_and_repins`\n- `test_revoked_successor_cannot_be_promoted_or_requalified`\n\nThe last case preserves existing revocation semantics: revoked candidates cannot be requalified (`task26_candidate_authority.py:114-117,237-239`).\n\n## Regression command\n\n```bash\nPYTHONPATH='dualcoach/profile:.' uv run pytest -q \\\n  tests/gateway/test_task26_runtime_authority_promotion.py \\\n  tests/test_nutricoach_v150_v14_safety.py \\\n  tests/test_nutricoach_v150_sealed_controller.py \\\n  tests/test_nutricoach_v150_v3_hardening.py \\\n  tests/gateway/test_nutrition_coaching.py::test_disposable_controller_issuance_enables_approved_transport\n```\n\nNo tests or live probes were run because the assigned scope was strictly read-only. The proposed RED uses only disposable fixtures and the real production credential loader; it covers live credential plus external registry/ledger/pin coherence without live actions."}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"resident"}}
{"type":"transition_applied","payload":{"type":"transition_applied","status":"completed","residency_state":"persisted_only"}}
{"type":"suspended","payload":{"reason":"quit"}}
