From a60b3b586b08f5e8faa99d27df58c596f2e0015d Mon Sep 17 00:00:00 2001 From: CL Kao Date: Thu, 4 Jun 2026 21:01:49 -0700 Subject: [PATCH] Require fresh Pi stage dispatch context --- .../references/pi-first-officer-runtime.md | 6 +- skills/integration/skill_surface_test.go | 66 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/skills/first-officer/references/pi-first-officer-runtime.md b/skills/first-officer/references/pi-first-officer-runtime.md index 6602990b..9873191d 100644 --- a/skills/first-officer/references/pi-first-officer-runtime.md +++ b/skills/first-officer/references/pi-first-officer-runtime.md @@ -17,6 +17,8 @@ Use `spacedock dispatch build` with `host: "pi"` in the input JSON. Forward the A Pi first officer may dispatch via `subagent(...)` when that tool is available. The task must include the emitted dispatch-file prompt or the dispatch file content, the workflow directory, the entity path, and the completion checklist. The child must load the Spacedock ensign skill and Pi ensign runtime adapter before working. +For Spacedock stage dispatches through `pi-subagents`, call `subagent(...)` with explicit `context: "fresh"`. Do not rely on the worker agent's default context; Spacedock stage workers must be seeded by the task prompt/dispatch content, workflow directory, entity file, and completion checklist rather than inherited first-officer transcript context. + For Spacedock stage dispatches through `pi-subagents`, do not use the `subagent(... acceptance: ...)` contract. Put acceptance requirements in the task prompt/dispatch content instead. Spacedock owns the independent implementation-to-validation workflow: the gate is verification via entity stage reports, product/state commits, and independent validation, not same-agent acceptance finalization by the child that did the work. ## Awaiting Completion @@ -27,7 +29,9 @@ For `pi-agent-teams`, completion is observed through the adapter's task/member n ## Follow-up and Reuse -Fresh redispatch is the default safe behavior for the first Pi slice. If a Pi substrate exposes a resumable worker handle, record only the minimum metadata needed to prevent stale reuse mistakes: worker label, substrate, run/session handle, entity slug, stage, state, and completion epoch. A follow-up assignment must increment the epoch, and a previous completion must never satisfy the new epoch. +Fresh redispatch is the default safe behavior for the first Pi slice. Normal follow-up and retry dispatches are fresh assignment cycles, not context resumes. If a Pi substrate exposes a resumable worker handle, record only the minimum metadata needed to prevent stale reuse mistakes: worker label, substrate, run/session handle, entity slug, stage, state, and completion epoch. A follow-up assignment must increment the epoch, and a previous completion must never satisfy the new epoch. + +A non-fresh resume is only allowed as an explicit manual/debug exception. Mark the dispatch visibly as a manual/debug resume and tie it to durable metadata in the entity stage evidence, including worker label, substrate, run/session handle, entity slug, stage, state, and completion epoch. ## Shutdown diff --git a/skills/integration/skill_surface_test.go b/skills/integration/skill_surface_test.go index 13af4fb8..e8763455 100644 --- a/skills/integration/skill_surface_test.go +++ b/skills/integration/skill_surface_test.go @@ -104,6 +104,37 @@ func TestPiRuntimeAdaptersAreLoadable(t *testing.T) { } } +func TestPiFirstOfficerRuntimeRequiresFreshSubagentContextForStages(t *testing.T) { + root := skillsRoot(t) + path := filepath.Join(root, "first-officer", "references", "pi-first-officer-runtime.md") + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read Pi first-officer runtime: %v", err) + } + content := string(data) + dispatch := sectionAfter(content, "## Dispatch") + if dispatch == "" { + t.Fatal("Pi first-officer runtime is missing the Dispatch section") + } + + required := []string{ + "Spacedock stage dispatches", + "pi-subagents", + "subagent(...)", + "context: \"fresh\"", + "Do not rely on the worker agent's default context", + "task prompt/dispatch content", + "workflow directory", + "entity file", + "completion checklist", + } + for _, want := range required { + if !strings.Contains(dispatch, want) { + t.Errorf("Pi first-officer Dispatch section does not contain required fresh-context invariant %q", want) + } + } +} + func TestPiFirstOfficerRuntimeForbidsSubagentAcceptanceForStages(t *testing.T) { root := skillsRoot(t) path := filepath.Join(root, "first-officer", "references", "pi-first-officer-runtime.md") @@ -135,6 +166,41 @@ func TestPiFirstOfficerRuntimeForbidsSubagentAcceptanceForStages(t *testing.T) { } } +func TestPiFirstOfficerRuntimeFollowupsAreFreshByDefault(t *testing.T) { + root := skillsRoot(t) + path := filepath.Join(root, "first-officer", "references", "pi-first-officer-runtime.md") + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read Pi first-officer runtime: %v", err) + } + content := string(data) + followup := sectionAfter(content, "## Follow-up and Reuse") + if followup == "" { + t.Fatal("Pi first-officer runtime is missing the Follow-up and Reuse section") + } + + required := []string{ + "Fresh redispatch is the default safe behavior", + "Normal follow-up and retry dispatches are fresh assignment cycles", + "not context resumes", + "increment the epoch", + "previous completion must never satisfy the new epoch", + "non-fresh resume is only allowed as an explicit manual/debug exception", + "worker label", + "substrate", + "run/session handle", + "entity slug", + "stage", + "state", + "completion epoch", + } + for _, want := range required { + if !strings.Contains(followup, want) { + t.Errorf("Pi first-officer Follow-up and Reuse section does not contain required fresh-retry invariant %q", want) + } + } +} + func TestUserSkillReferenceClosureResolves(t *testing.T) { root := skillsRoot(t) for _, skill := range userSkills {