Same-campaign kitchen reopen selects a retired recipe artifact namespace #5270
Description
Fix target and integration order — mandatory routing
Repository: TalonT-Org/AutoSkillit.
Fix PR base branch: develop, currently 381afb4e59cc23acf4ca9ace7a7644b75e6e7b14.
Scope: existing runtime kitchen identity/lifecycle and recipe artifact persistence. These files already exist on develop; this fix does not require the unmerged implementation E2E harness.
Open the remediation PR against develop. Once merged, the owner of parent #5255 must integrate that develop commit and rerun commissioning. Do not route this runtime fix into the parent feature branch merely because that branch exposed it. The unrelated collector fix #5269 is already integrated into #5255 at 93457f16ecc30249799c02084c20f41d9cd88455.
Defect
With a fixed campaign ID, closing then reopening a kitchen selects the same kitchen ID. Close retires its on-disk recipe-artifact namespace. Reopen activates that identity in the in-memory generation store, but artifact persistence still refuses the permanently retired namespace. A normal named reopen consequently returns recipe_artifact_unavailable and leaves no usable recipe execution.
This is distinct from #5266's repaired tracker-credit admission bug. Its occurrence was retained as unproven context in that ticket; the lifecycle/persistence boundary is now reproduced independently.
Actual failure and primary evidence
Implementation run 38431393443, attempt 1, source 93457f16ecc30249799c02084c20f41d9cd88455, failed after about three minutes with degraded_tool_response. Redacted artifact: e2e-implementation-38431393443-1; root transcript out/transcripts/-workspace-sandbox/d10072be-45cd-4678-afd3-24cf741092a9.jsonl.
The root closes the kitchen, then invokes named open_kitchen at record 212. Record 213 returns recipe_artifact_unavailable, phase=failed_ambiguous, retry_disposition=reconcile_required, with the same kitchen ID and a fresh operation. Record 225's subsequent skill dispatch returns recipe_execution_inactive.
The live receipt does not expose the swallowed persistence exception. The local boundary probe below supplies that missing causal evidence using the same production identity, retirement, activation, and persistence functions.
Deterministic no-model reproduction
Use a checkout of the pinned develop commit, set it up with task install-worktree, and run the following as a standalone script from the repo root using .venv/bin/python. Put the script in .autoskillit/temp/investigate/; it creates only project-temp data. No GitHub mutation, credentials, or model call is needed. The existing test payload factory supplies a valid artifact; no test runner is invoked.
import os, sys, threading
from pathlib import Path
from types import SimpleNamespace
from uuid import uuid4
sys.path.insert(0, str(Path.cwd()))
from autoskillit.pipeline import closed_kitchen_open_state
from autoskillit.server.lifecycle._kitchen_identity import establish_kitchen_identity
from autoskillit.server.recipe._recipe_artifact import RecipeArtifactError, retire_recipe_artifacts
from autoskillit.server.recipe._recipe_generation import retire_kitchen
from tests.server.test_tools_recipe_pull import _persist
os.environ["AUTOSKILLIT_CAMPAIGN_ID"] = str(uuid4())
root = Path(".autoskillit/temp/investigate") / ("campaign-reopen-" + uuid4().hex)
root.mkdir(parents=True)
ctx = SimpleNamespace(kitchen_transition_lock=threading.RLock(),
kitchen_open_state=closed_kitchen_open_state(context_id=uuid4().hex),
kitchen_process_identity=None)
first = establish_kitchen_identity(ctx)
_persist(root, kitchen_id=first)
assert retire_recipe_artifacts(root, kitchen_id=first)
retire_kitchen(first)
ctx.kitchen_open_state = closed_kitchen_open_state(context_id=ctx.kitchen_open_state.context_id)
second = establish_kitchen_identity(ctx)
print("same ID:", first == second)
try:
_persist(root, kitchen_id=second)
except RecipeArtifactError as exc:
print(type(exc).__name__ + ": " + str(exc))Verified result:
same ID: True
RecipeArtifactError: recipe artifact namespace is retired
This is a focused boundary reproduction rather than a full MCP invocation. The live run provides the complete close/named-open failure sequence. The parent ran this diagnostic and personally read the source below.
Mechanism and test gap
establish_kitchen_identityresolves the campaign-based ID for each CLOSED-to-ready activation.resolve_kitchen_idreuses the campaign environment ID.close_kitchenretires artifact and generation state, clears execution, and resets the transition to CLOSED.retire_recipe_artifactswrites a retirement marker; persistence rejects it.activate_kitchenreactivates the ID in memory. The disk retirement boundary remains in force.- Open finalization maps persistence failure to
recipe_artifact_unavailable.
Existing lifecycle tests expect a fresh ID without a fixed campaign, and artifact tests separately prove retired namespaces reject writes. A same-campaign close/reopen case combining both is missing.
Required behavior and validation
Support the public close/reopen lifecycle within a fixed campaign using a fresh usable activation/artifact authority. Preserve rejection of stale retired pulls and receipts; do not simply remove the tombstone or reuse stale artifacts. Campaign grouping must remain stable and previously captured campaign values must remain available. Changing the kitchen ID alone can orphan those values: campaign inheritance test and capture visibility tests establish this existing contract. Use existing identity/generation mechanisms; no new generic registry or state machine is requested.
Add a focused same-campaign open/initialize/close/open regression, asserting fresh usable recipe artifacts and attestation, while the first activation's handles and receipts remain invalid. Also assert that values captured before close remain visible after reopening under the same campaign. Preserve existing active-open replay and retirement checks. Run pre-commit run --all-files and the configured task test-local-gate.
Separate caller errors and goal
The empty initial invocation-digest map was intentional: initialization delivered only the clone frontier. The model then omitted step_name from clone/claim/publish requests, so no next-segment credential was attached; it later guessed unrelated digests. That is not evidence that initialization lost all plan credentials. This ticket concerns the independently reproduced reopen lifecycle defect, not those caller omissions.
No exact duplicate was found for the retired-namespace reopen boundary. Classification: bug, recipe:remediation. #5255 remains unmerged until this runtime fix and the separate harness lifecycle correction are integrated and two live implementation runs are accepted.
Verified commissioning coordination — 2026-10-13
Independent sibling blocker: #5271. These two fixes can be processed in parallel. This ticket's remediation PR must use develop as its base; follow the routing section above. Both fixes must be integrated into parent #5255 before the next two acceptance attempts.
Two independent adversarial reviewers examined this issue body. The parent personally checked their cited source and retained evidence, corrected overclaims, and incorporated the required regression safeguards before filing. No source implementation or successful commissioning is claimed by this ticket.
Activity