From a392618cfcc44b3edc02a284bb4fc59b502e7bed Mon Sep 17 00:00:00 2001 From: Alphalab Admin Date: Sat, 12 Sep 2026 01:26:52 +0000 Subject: [PATCH 1/2] fix(architect): require parent-authored judge briefs --- install.sh | 12 +++++++++ overlay/AGENTS.md | 2 ++ overlay/architect-phase-b.md | 3 +++ scripts/check-overlay.sh | 2 ++ scripts/fixtures/architect/SKILL.md | 16 ++++++++++++ scripts/jig_tests/test_integration.py | 35 +++++++++++++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 overlay/architect-phase-b.md create mode 100644 scripts/fixtures/architect/SKILL.md diff --git a/install.sh b/install.sh index 01d8933..6123e51 100755 --- a/install.sh +++ b/install.sh @@ -366,6 +366,18 @@ if [[ ${#conform_src[@]} -gt 0 ]]; then python3 "$here/bin/conform-skills.py" --out "$conform_out" "${conform_src[@]}" fi +python3 - "$conform_out/architect/SKILL.md" "$overlay/architect-phase-b.md" <<'PY' +import sys +from pathlib import Path + +skill, fragment = map(Path, sys.argv[1:]) +text = skill.read_text() +anchor = 'Arena returns one synthesized design package. The synthesis decision populates the rationale\'s "Synthesis decision" section.' +if text.count(anchor) != 1: + sys.exit("architect Phase B synthesis anchor changed; update overlay/architect-phase-b.md integration before installing") +skill.write_text(text.replace(anchor, fragment.read_text().strip())) +PY + export PI_AGENT_DIR="$agent" export OVERLAY="$overlay" python3 - "${#pstack_skill_names[@]}" "${pstack_skill_names[@]}" "${required_packages[@]}" <<'PY' diff --git a/overlay/AGENTS.md b/overlay/AGENTS.md index c090209..7cb7cfd 100644 --- a/overlay/AGENTS.md +++ b/overlay/AGENTS.md @@ -8,6 +8,8 @@ This file maps Cursor verbs onto Pi. It is process, not repository architecture. When delegation is needed, use only `{ agent: "poteto-agent", task }`. `install.sh` installs this child in `~/.pi/agent/agents/`, disables builtins, and retires the six old role profiles. Give the child a bounded implementation, investigation, or read-only task instead of selecting a different persona. Do not use a parallel worker+reviewer workflow as the default bug-fix loop. Tiny known-mechanism fixes stay parent-inline. +For competing-design bakeoffs, prefer a parent-inline pick when context allows, even when arena calls for a cross-judge. If a judge child is needed, the parent must author its task before launch. State the hard constraints and preference order explicitly. Reject candidates that violate hard constraints before ranking the rest. Include the instruction "Do not invent parent preference." Mark unspecified preferences as unknown rather than asking the judge to infer them. Scope all judge file access to explicit `design/*.md` paths. Pass those paths, not pasted design packages or session transcripts. The parent owns the final pick. + The child reads poteto-mode in full and decides reversible details without supervisor gates. It returns missing authorization or genuine product ambiguity in its normal result before taking the blocked action. New sessions see the installed settings and prompts. Existing children keep their old prompts until respawn. Leave `async` on. That is the default. `async:false` only when this turn cannot continue without the child. Do not sleep-poll. Use blocking `subagent_wait` only when this turn must consume the result. diff --git a/overlay/architect-phase-b.md b/overlay/architect-phase-b.md new file mode 100644 index 0000000..c24d471 --- /dev/null +++ b/overlay/architect-phase-b.md @@ -0,0 +1,3 @@ +For synthesis, prefer a parent-inline pick when context allows instead of arena's mandatory cross-judge. If a judge child is needed for the competing-design bakeoff, the parent must author its task before launch. State the hard constraints and preference order explicitly. Reject candidates that violate hard constraints before ranking the rest. Include the instruction "Do not invent parent preference." Mark unspecified preferences as unknown rather than asking the judge to infer them. Scope all judge file access to explicit `design/*.md` paths. Pass those paths, not pasted design packages or session transcripts. + +The parent owns the final pick and synthesizes one design package. Record the hard constraints, preference order, and decision in the rationale's "Synthesis decision" section. diff --git a/scripts/check-overlay.sh b/scripts/check-overlay.sh index 6505035..38489ec 100755 --- a/scripts/check-overlay.sh +++ b/scripts/check-overlay.sh @@ -108,6 +108,7 @@ description: stub for $name install test # stub EOF done < <(bash "$root/install.sh" --print-pstack-skills) +cp "$root/scripts/fixtures/architect/SKILL.md" "$tmp/pstack/skills/architect/SKILL.md" sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$tmp/pstack/skills/poteto-mode/SKILL.md" mkdir -p "$tmp/pstack/skills/poteto-mode/playbooks" printf 'playbook\n' >"$tmp/pstack/skills/poteto-mode/playbooks/investigation.md" @@ -371,6 +372,7 @@ description: stub for $name clone test # stub EOF done < <(bash "$root/install.sh" --print-pstack-skills) +cp "$root/scripts/fixtures/architect/SKILL.md" "$fake/pstack/skills/architect/SKILL.md" sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$fake/pstack/skills/poteto-mode/SKILL.md" git init -q "$fake" git -C "$fake" add pstack diff --git a/scripts/fixtures/architect/SKILL.md b/scripts/fixtures/architect/SKILL.md new file mode 100644 index 0000000..1e5fae7 --- /dev/null +++ b/scripts/fixtures/architect/SKILL.md @@ -0,0 +1,16 @@ +--- +name: architect +description: Architect installation fixture. +--- + +# Architect + +## Phase B: Sketch + +Compare candidate designs. + +Arena returns one synthesized design package. The synthesis decision populates the rationale's "Synthesis decision" section. + +## Phase C: Agree (opt-in) + +Proceed with the synthesized design. diff --git a/scripts/jig_tests/test_integration.py b/scripts/jig_tests/test_integration.py index de709f2..a6bfb5e 100644 --- a/scripts/jig_tests/test_integration.py +++ b/scripts/jig_tests/test_integration.py @@ -31,6 +31,10 @@ def setUp(self): encoding="utf-8", ) + (self.pstack / "skills/architect/SKILL.md").write_bytes( + (ROOT / "scripts/fixtures/architect/SKILL.md").read_bytes() + ) + def tearDown(self): self.temporary.cleanup() @@ -81,6 +85,8 @@ def owned_digest(self, home): home / ".pi/agent/jig", home / ".pi/agent/bin/jig", home / ".pi/agent/skills-pstack/jig/SKILL.md", + home / ".pi/agent/skills-pstack/architect/SKILL.md", + home / ".pi/agent/AGENTS.md", home / ".pi/agent/prompts/jig.md", ] result = {} @@ -90,6 +96,35 @@ def owned_digest(self, home): result[path.relative_to(home).as_posix()] = hashlib.sha256(path.read_bytes()).hexdigest() return result + def test_architect_synthesis_is_adapted_without_changing_upstream(self): + home = self.base / "home" + source = self.pstack / "skills/architect/SKILL.md" + before = source.read_text() + installed = self.install(home) + self.assertEqual(installed.returncode, 0, installed.stderr) + text = (home / ".pi/agent/skills-pstack/architect/SKILL.md").read_text() + prefix, rest = before.split("Arena returns", 1) + _, suffix = rest.split("\n\n## Phase C", 1) + fragment = (ROOT / "overlay/architect-phase-b.md").read_text().strip() + expected = prefix + fragment + "\n\n## Phase C" + suffix + self.assertEqual(text.split("# Architect\n", 1)[1], expected.split("# Architect\n", 1)[1]) + self.assertEqual(source.read_text(), before) + self.assertEqual( + (home / ".pi/agent/AGENTS.md").read_bytes(), + (ROOT / "overlay/AGENTS.md").read_bytes(), + ) + + def test_architect_synthesis_rejects_missing_or_duplicate_anchor(self): + source = self.pstack / "skills/architect/SKILL.md" + original = source.read_text() + anchor = next(line for line in original.splitlines() if line.startswith("Arena returns")) + for count in (0, 2): + with self.subTest(anchor_count=count): + source.write_text(original.replace(anchor, "\n\n".join([anchor] * count))) + installed = self.install(self.base / f"home-{count}") + self.assertNotEqual(installed.returncode, 0) + self.assertIn("architect Phase B synthesis anchor changed", installed.stderr) + def test_public_route_matrix_owns_generated_docs(self): document = json.loads(MATRIX.read_text(encoding="utf-8")) self.assertEqual(document["schemaVersion"], 2) From f988ad715bd51824024b1b9b658ee85a813243f2 Mon Sep 17 00:00:00 2001 From: Alphalab Admin Date: Sat, 12 Sep 2026 01:32:08 +0000 Subject: [PATCH 2/2] fix(overlay): keep judge brief guidance Pi-only --- install.sh | 12 --------- overlay/AGENTS.md | 2 +- overlay/architect-phase-b.md | 3 --- scripts/check-overlay.sh | 2 -- scripts/fixtures/architect/SKILL.md | 16 ------------ scripts/jig_tests/test_integration.py | 35 --------------------------- 6 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 overlay/architect-phase-b.md delete mode 100644 scripts/fixtures/architect/SKILL.md diff --git a/install.sh b/install.sh index 6123e51..01d8933 100755 --- a/install.sh +++ b/install.sh @@ -366,18 +366,6 @@ if [[ ${#conform_src[@]} -gt 0 ]]; then python3 "$here/bin/conform-skills.py" --out "$conform_out" "${conform_src[@]}" fi -python3 - "$conform_out/architect/SKILL.md" "$overlay/architect-phase-b.md" <<'PY' -import sys -from pathlib import Path - -skill, fragment = map(Path, sys.argv[1:]) -text = skill.read_text() -anchor = 'Arena returns one synthesized design package. The synthesis decision populates the rationale\'s "Synthesis decision" section.' -if text.count(anchor) != 1: - sys.exit("architect Phase B synthesis anchor changed; update overlay/architect-phase-b.md integration before installing") -skill.write_text(text.replace(anchor, fragment.read_text().strip())) -PY - export PI_AGENT_DIR="$agent" export OVERLAY="$overlay" python3 - "${#pstack_skill_names[@]}" "${pstack_skill_names[@]}" "${required_packages[@]}" <<'PY' diff --git a/overlay/AGENTS.md b/overlay/AGENTS.md index 7cb7cfd..c46e13c 100644 --- a/overlay/AGENTS.md +++ b/overlay/AGENTS.md @@ -8,7 +8,7 @@ This file maps Cursor verbs onto Pi. It is process, not repository architecture. When delegation is needed, use only `{ agent: "poteto-agent", task }`. `install.sh` installs this child in `~/.pi/agent/agents/`, disables builtins, and retires the six old role profiles. Give the child a bounded implementation, investigation, or read-only task instead of selecting a different persona. Do not use a parallel worker+reviewer workflow as the default bug-fix loop. Tiny known-mechanism fixes stay parent-inline. -For competing-design bakeoffs, prefer a parent-inline pick when context allows, even when arena calls for a cross-judge. If a judge child is needed, the parent must author its task before launch. State the hard constraints and preference order explicitly. Reject candidates that violate hard constraints before ranking the rest. Include the instruction "Do not invent parent preference." Mark unspecified preferences as unknown rather than asking the judge to infer them. Scope all judge file access to explicit `design/*.md` paths. Pass those paths, not pasted design packages or session transcripts. The parent owns the final pick. +For competing-design bakeoffs, prefer a parent-inline pick when context allows. If using a judge child, the parent must author its task with hard constraints and an explicit preference order. Include "Do not invent parent preference." Limit judge file access to listed `design/*.md` paths. Pass paths, not pasted designs or transcripts. The parent owns the final pick. The child reads poteto-mode in full and decides reversible details without supervisor gates. It returns missing authorization or genuine product ambiguity in its normal result before taking the blocked action. New sessions see the installed settings and prompts. Existing children keep their old prompts until respawn. diff --git a/overlay/architect-phase-b.md b/overlay/architect-phase-b.md deleted file mode 100644 index c24d471..0000000 --- a/overlay/architect-phase-b.md +++ /dev/null @@ -1,3 +0,0 @@ -For synthesis, prefer a parent-inline pick when context allows instead of arena's mandatory cross-judge. If a judge child is needed for the competing-design bakeoff, the parent must author its task before launch. State the hard constraints and preference order explicitly. Reject candidates that violate hard constraints before ranking the rest. Include the instruction "Do not invent parent preference." Mark unspecified preferences as unknown rather than asking the judge to infer them. Scope all judge file access to explicit `design/*.md` paths. Pass those paths, not pasted design packages or session transcripts. - -The parent owns the final pick and synthesizes one design package. Record the hard constraints, preference order, and decision in the rationale's "Synthesis decision" section. diff --git a/scripts/check-overlay.sh b/scripts/check-overlay.sh index 38489ec..6505035 100755 --- a/scripts/check-overlay.sh +++ b/scripts/check-overlay.sh @@ -108,7 +108,6 @@ description: stub for $name install test # stub EOF done < <(bash "$root/install.sh" --print-pstack-skills) -cp "$root/scripts/fixtures/architect/SKILL.md" "$tmp/pstack/skills/architect/SKILL.md" sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$tmp/pstack/skills/poteto-mode/SKILL.md" mkdir -p "$tmp/pstack/skills/poteto-mode/playbooks" printf 'playbook\n' >"$tmp/pstack/skills/poteto-mode/playbooks/investigation.md" @@ -372,7 +371,6 @@ description: stub for $name clone test # stub EOF done < <(bash "$root/install.sh" --print-pstack-skills) -cp "$root/scripts/fixtures/architect/SKILL.md" "$fake/pstack/skills/architect/SKILL.md" sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$fake/pstack/skills/poteto-mode/SKILL.md" git init -q "$fake" git -C "$fake" add pstack diff --git a/scripts/fixtures/architect/SKILL.md b/scripts/fixtures/architect/SKILL.md deleted file mode 100644 index 1e5fae7..0000000 --- a/scripts/fixtures/architect/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: architect -description: Architect installation fixture. ---- - -# Architect - -## Phase B: Sketch - -Compare candidate designs. - -Arena returns one synthesized design package. The synthesis decision populates the rationale's "Synthesis decision" section. - -## Phase C: Agree (opt-in) - -Proceed with the synthesized design. diff --git a/scripts/jig_tests/test_integration.py b/scripts/jig_tests/test_integration.py index a6bfb5e..de709f2 100644 --- a/scripts/jig_tests/test_integration.py +++ b/scripts/jig_tests/test_integration.py @@ -31,10 +31,6 @@ def setUp(self): encoding="utf-8", ) - (self.pstack / "skills/architect/SKILL.md").write_bytes( - (ROOT / "scripts/fixtures/architect/SKILL.md").read_bytes() - ) - def tearDown(self): self.temporary.cleanup() @@ -85,8 +81,6 @@ def owned_digest(self, home): home / ".pi/agent/jig", home / ".pi/agent/bin/jig", home / ".pi/agent/skills-pstack/jig/SKILL.md", - home / ".pi/agent/skills-pstack/architect/SKILL.md", - home / ".pi/agent/AGENTS.md", home / ".pi/agent/prompts/jig.md", ] result = {} @@ -96,35 +90,6 @@ def owned_digest(self, home): result[path.relative_to(home).as_posix()] = hashlib.sha256(path.read_bytes()).hexdigest() return result - def test_architect_synthesis_is_adapted_without_changing_upstream(self): - home = self.base / "home" - source = self.pstack / "skills/architect/SKILL.md" - before = source.read_text() - installed = self.install(home) - self.assertEqual(installed.returncode, 0, installed.stderr) - text = (home / ".pi/agent/skills-pstack/architect/SKILL.md").read_text() - prefix, rest = before.split("Arena returns", 1) - _, suffix = rest.split("\n\n## Phase C", 1) - fragment = (ROOT / "overlay/architect-phase-b.md").read_text().strip() - expected = prefix + fragment + "\n\n## Phase C" + suffix - self.assertEqual(text.split("# Architect\n", 1)[1], expected.split("# Architect\n", 1)[1]) - self.assertEqual(source.read_text(), before) - self.assertEqual( - (home / ".pi/agent/AGENTS.md").read_bytes(), - (ROOT / "overlay/AGENTS.md").read_bytes(), - ) - - def test_architect_synthesis_rejects_missing_or_duplicate_anchor(self): - source = self.pstack / "skills/architect/SKILL.md" - original = source.read_text() - anchor = next(line for line in original.splitlines() if line.startswith("Arena returns")) - for count in (0, 2): - with self.subTest(anchor_count=count): - source.write_text(original.replace(anchor, "\n\n".join([anchor] * count))) - installed = self.install(self.base / f"home-{count}") - self.assertNotEqual(installed.returncode, 0) - self.assertIn("architect Phase B synthesis anchor changed", installed.stderr) - def test_public_route_matrix_owns_generated_docs(self): document = json.loads(MATRIX.read_text(encoding="utf-8")) self.assertEqual(document["schemaVersion"], 2)