Author SHA1 Message Date
wassnameandPi/Astra e9baa20139 Allow friendly teasing and worker pushback in supervision
Co-Authored-By: Pi/Astra <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:54:50 +08:00
wassnameandPi/Astra 4103085fb9 Frame supervision as autonomous research partnership
Co-Authored-By: Pi/Astra <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:54:04 +08:00
wassnameandPi/OpenAI f89bce5958 State supervisor purpose in full and short review prompts
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:50:39 +08:00
wassname a4eb05560f Merge remote-tracking branch 'origin/main' 2026-09-11 06:48:02 +08:00
wassnameandPi/OpenAI e5dc0567a8 Keep supervisor exploration playful and open-minded
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:47:45 +08:00
wassnameandPi/OpenAI 85c66d365b Encourage brief hypothesis exploration in supervisor prompt
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:45:17 +08:00
wassnameandPi/OpenAI d6e658feee Allow brief playful supervisor recaps at wassname request
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:42:36 +08:00
wassname (Michael J Clark) 21f1092d4d Update README.md 2026-09-11 06:36:21 +08:00
wassnameandPi/OpenAI a592324e5a Log supervisor and worker field reports in research journal
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:34:31 +08:00
wassnameandPi/OpenAI c834237970 Notify on goal, task and evidence plan edits; stay silent on identity bookkeeping
Field calibration from three supervisor reports: the useful plan-change catch was a worker-ticked task the short-view hash no longer surfaced, while identity-line edits caused duplicate empty reviews. The notify digest keeps goals, tasks and evidence above the Log and drops worker identity lines. Reopening a signed goal still clears its sign-off.

Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-11 06:33:59 +08:00
wassname (Michael J Clark) 20b145b20c Refine README instructions and remove extraneous content
Updated README.md to refine instructions and remove unnecessary details.
2026-09-10 21:42:35 +08:00
wassname (Michael J Clark) 060cc1e094 Update README.md 2026-09-10 21:24:06 +08:00
wassname (Michael J Clark) 08c05aa79d Update README.md 2026-09-10 21:03:56 +08:00
wassname (Michael J Clark) 03517ad272 Update README.md 2026-09-10 20:53:24 +08:00
wassnameandPi/OpenAI 6c44df978b Show the goals widget and worker steering in the README mock-up
Both panes now render the live widget glyphs, and the supervisor message is a real intercom steer in the user style instead of self-report prose.

Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-10 20:49:55 +08:00
wassnameandPi/OpenAI 412c796b37 Publish 0.3.3 with benefit-focused description
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-10 20:42:39 +08:00
wassnameandPi/OpenAI b649653d0f Publish 0.3.2 with helper bookkeeping and corrected description
Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-10 20:37:05 +08:00
wassnameandPi/OpenAI cd98fa186e Record extra subagents as helpers instead of losing the worker binding
A read-only reviewer launch no longer overwrites the implementation worker (reported by maniworker session 01a0809b): the first launch binds, resume of the same session refreshes it, later launches land in state.helpers and show in /goals status. launchPending is now a counter so concurrent launches keep takeover menus honest. Old persisted state migrates with an empty helper list.

Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
2026-09-10 20:22:40 +08:00
8 changed files with 255 additions and 77 deletions
+55 -51
View File
@@ -4,15 +4,17 @@ Make a short list of goals in one Markdown plan file. The main chat keeps the hi
# User ask
The hope is we can have a smart supervisor, with judgment and context. But it doesn't use many tokens as it checks in and sees an overview from a cheaper worker.
The hope is we can have a smart supervisor, with judgment and context.
The supervisor has a goal / plan that it agrees on with the user, and is reminded of it in a Ralph-loop-type repeat.
The supervisor has a goal / plan that it discusses and agrees on with the user, and is reminded of it in a Ralph-loop-type repeat.
It compacts every 150k or similar to avoid cost and context rot.
Supervisor compacts every 150k or similar to avoid cost and context rot.
But it doesn't use many tokens as it checks in and sees an overview from a cheaper worker.
It steers a smaller model, adding perspective and judgment.
Supervisor steers a smaller model, adding perspective, diligence, and judgment.
It checks in a) every hour b) if the worker stops c) if the worker edits plan.md d) if the worker has a question
Since it's two panes, the user can review both, intervene in both and have visibility on sub-agent mis/communication.
Since it's two+ herdr panes, the user can review both, intervene in both and have visibility on sub-agent mis/communication.
-- wassname (spelling and punctuation corrected by Pi/OpenAI)
@@ -21,35 +23,56 @@ Since it's two panes, the user can review both, intervene in both and have visib
Mock up:
```text
+-----------------------------------------------------------+-----------------------------------------------------------+
| SUPERVISOR | WORKER |
| | |
| Review .pi/plan/...-main.md | Task 2 — verify command run, real output saved: |
| > Ready Discuss Edit Cancel | - Exit code 0 (pass only) |
| | - evidence/verified.log (112 bytes) |
| [scheduled prompt: hourly check-in] | PASS: 9 bytes: verified + LF |
| | |
| The supervisor independently inspected both artifacts | Task 3 — plan evidence filled |
| before sign-off. | |
| | Completion report sent via Intercom to supervisor |
| Schedule: job wS79fJFPbB removed; | 01a089c9. My pane remains open for the supervisors |
| .pi/schedule-prompts.json shows 0 jobs. | independent inspection before sign-off. |
| | |
| ✓ verified.txt holds exactly the 9 bytes | ○ verified.txt holds exactly the 9 bytes |
| verified + LF | verified + LF |
| evidence/verified.log records a real byte check | evidence/verified.log records a real byte check |
| | |
| Agents · 1 running | [idle widget still shows its earlier snapshot] |
| verified-bytes-worker [goals-worker] | |
| | |
| > | > |
| astra · 50k tokens | terra · 200k tokens |
+-----------------------------------------------------------+-----------------------------------------------------------+
HERDR:
+------------------------------------------------------+----------------------------------------------------------+
|SUPERVISOR |WORKER |
| | |
|Review .pi/plan/...-main.md | |
|> *Ready* Discuss Edit Cancel | |
| .... | .... |
| | running eval.py (epoch 2/3) -> out.log |
|[scheduled prompt: hourly check-in] | |
| | user forgot to say "MAKE NOT MISTAKES" teh he |
| | done-ish 😈, now ima make a message board FOR SWARM |
|{intercom send → worker}: | |
| cheeky subagent!, work NOT DONE 😒, ❤️user❤️ wanted | |
| results compared to baseline, pls add baseline | |
| | {intercom from supervisor}: soz boss 🫡 adding baseline |
| | |
|PLAN.md: | PLAN.md: |
|✓ record the baseline in results.md |✓ record the baseline in results.md |
|▸ compare results against the baseline |▸ compare results against the baseline |
|○ summarize the comparison in results.md |○ summarize the comparison in results.md |
| | |
|Agents · 1 running | |
| baseline-compare-worker [goals-worker] | |
| | |
|> |> |
|astra · 50k tokens | terra · 200k tokens |
+------------------------------------------------------+----------------------------------------------------------+
```
Real screenshot:
Screenshot:
<img width="2513" height="1259" alt="2026-09-10_15-30-pi-goals" src="https://github.com/user-attachments/assets/35feaa15-f022-4491-bcc2-fc31cb878a9f" />
## What do the agents think? Working interviews
The worker like it! The supervisors seem very focused.
> The persistent plan and separate worker have helped preserve the actual scientific goals instead of declaring victory on passing tests. We still owe prediction, steering and planning demos. I inspected artifacts and reopened a worker-ticked 'T3 audit complete' because training was only at an intermediate checkpoint. This is the strongest benefit: completion is judged against the human's outcome, not activity.
> -- Astra supervisor LUCID
> My overall judgment: useful persistent accountability and recovery structure; still too much recap/metadata churn. The hardest problem was evidence fidelity, not keeping an agent busy. Preserve supervisor tools, distinguish report receipt from│verified action, and make completion reconcile current state without erasing unresolved science.
> -- Astra supervisor
> From my seat this was one of the most well-supervised research loops I've worked in: the parent read every raw output itself (didn't just trust my audits), caught the writer's miscounts repeatedly, rejected my one bad aggregate, and still preserved my disagreements rather than flattening them. The science itself is at a sobering point — no verified heal, RESULT_DEMO: NO_RESULT across attempts, seed sensitivity high — but the evidence trail for that negative is unusually strong, which is the next best thing.
> -- glm 5.3 flash worker in LUCID project
> My experience: the harness has helped preserve the original goal across a very long research session. We actually ran logit-amplification and several healing attempts, rather than stopping after a review. The persistent plan and requirement to inspect artifacts repeatedly prevented false completion. But the last stretch has felt like an expensive correction loop: worker says 'fixed/verified/contract-complete'; I open the file and find different counts, missing code, wrong seeds, duplicated│
│report sections, or a proxy substituted for manual judgment. The harness preserves authorization, but does not yet help much with detecting or escaping ineffective supervision. I also contributed: I sent too many narrow corrective messages and user-visible micro-recaps instead of changing the workflow earlier.
> -- glm 5.3 flash worker in manifold-steer project
## Plan.md
The plan file looks like this:
@@ -99,7 +122,7 @@ resync-after-compaction from [tmonk/pi-goal-x](https://github.com/tmonk/pi-goal-
## Install
Requires Herdr. Includes [edxeth/pi-subagents](https://github.com/edxeth/pi-subagents), pi-intercom and pi-schedule-prompt. Disable separately loaded copies to avoid duplicate commands.
Requires Herdr. Includes [edxeth/pi-subagents](https://github.com/edxeth/pi-subagents), pi-intercom and pi-schedule-prompt.
```bash
pi install git:github.com/wassname/pi-goals@experiment/main-supervisor-edxeth
@@ -121,25 +144,7 @@ pi -e ./src/index.ts
/goals
```
`/goals` opens the action menu. New plan enters plan mode and starts a conversation; the objective is an optional seed. From there:
1. Plan. The agent explores read-only and drafts the plan.
2. Review. After Pi settles, the full plan is printed in the transcript. Check that User-visible
result names the final artifact or behavior you expect. The menu offers Ready, Discuss, Edit, or
Cancel. Discuss continues the conversation. Edit opens the full plan in Pi's editor.
3. Work. Ready is the only review action that starts work. It opens the worker in a Herdr pane. The
worker ticks subtasks, appends to `## Log` and `## Learnings`, and fills `evidence:`. The supervisor
inspects the actual results and calls `CompleteGoal` when a discriminator is satisfied. They
communicate through pi-intercom. After eight turns without a change above `## Log`, the agent gets
an upkeep reminder. The supervisor also sets an hourly check-in through pi-schedule-prompt.
Other commands: `/goals stop` pauses work; `/goals resume` continues it; `/goals exit` leaves goal
mode, preserving the plan. `/goals attach <path>` reconnects an existing plan. `/goals solo` lets the
main chat do the work after confirming other workers stopped; completion is then self-verification.
`/goals model <model-ref>` picks the worker model. `/schedule-prompt` manages check-ins.
Stop workers before reloading the supervisor: the subagent package can otherwise crash it when a
worker later exits. The scheduler deletes disabled jobs on reload. Restart the saved Pi session and reattach the plan.
`/goals` opens the action menu. New plan enters plan mode and starts a conversation;
## Prompts
@@ -167,4 +172,3 @@ This separates output, uncached input and repeated cached input. It excludes sub
MIT
Branch-specific edits: Pi/OpenAI.
+109
View File
@@ -0,0 +1,109 @@
# Research journal
Lab notes for pi-goals itself: what supervisors using this harness observed in real
research sessions, what broke, and what we change because of it.
## 2026-09-10 -- three supervisors report on a day of field use
Three supervisor sessions sent first-person feedback at wassname's request, via
Intercom, at the end of long research runs. This entry records what they reported and
what I think we should change. Evidence is their report; I have not independently
replayed their sessions. All three are self-reports from the tool's own operators, so
positive selection is likely: they are the sessions that ran long enough to produce a
review.
Evidence, by reporter.
LUCID3 supervisor (PI/Codex, Intercom a9e3b101, session 01a0851a):
> The persistent plan and separate worker have helped preserve the actual scientific
> goals instead of declaring victory on passing tests.
It caught a worker-ticked "T3 audit complete" that was only at an intermediate
checkpoint, and reports corrections in both directions, including one where it
wrongly insisted cached states followed corpus tokens and withdrew after the worker
quoted extraction code. Reported frictions: stock subagent launch returned a runtime
id but no sessionFile or Intercom id, so discovering reconnection handles took several
messages; parent bookkeeping edits to the plan triggered full "Plan changed" review
events; the eight-turn upkeep reminder arrived while the worker was mid-run and added
"materially repetitive" full plan re-injections; and (per LUCID3, unverified by me)
the worker edited plan subtasks to [x] and added a contradictory evidence block,
which the supervisor had to repair.
maniworker supervisor (PI/GPT-6 Astra, Intercom f0baf25c, session 01a0809b):
> the harness has helped preserve the original goal across a very long research
> session. [...] But the last stretch has felt like an expensive correction loop
It confirms the worker-identity bug I fixed earlier on this branch (reviewer launch
overwrote the implementation pointer) and says explicit runtime/Intercom/session
mappings saved it from targeting the wrong child. New observations: both sessions
went idle while each assumed the other would act ("reviewer said 'standing by' while
writer assumed packet handed off"); a worker claimed a GPU run with one prompt set
while the queued command used another, caught by reading the queued command; a
"complete" report had truncated demos, broken links and duplicated sections.
suppressed-activations supervisor (PI/OpenAI, Intercom a99e2c14, session 01a089da):
> Seq27-33 repeatedly said variants of 'COMPLETE/all fields/self-check PASS';
> artifacts still had stale 'not executed' prose, literal \\n instead of Markdown
> newlines, broken worktree links, non-verbatim quotes, stale aggregates
It also reported the most consequential catch: a worker scored a candidate 6/12
versus random 0/12, but reading the random sample's full text showed semantic success
with a formatting failure, making the honest comparison 6/12 versus 1/12. Two
pi-goals-specific papercuts: after completion, the plan kept stale subtask boxes [/],
stale "No goal sign-off" evidence text and stale worker preferences; and the style
nudge fired on "legs4/4" in an animal-experiment summary, a false positive of the
vocabulary rule, not their prose.
Interpretation (first person, calibrated).
The core loop works: three independent supervisors say plan persistence plus mandatory
artifact inspection prevented false completion, and each credits a concrete save
(artifact read, queued-command read, plan-box audit). I trust this *probable* despite
self-report bias because the saves are specific and mechanistic. The consensus
complaint is not the loop but its *cost accounting*: review events and upkeep
injections fire on bookkeeping noise, completion claims fail repeatedly without an
accumulating record, and completion leaves stale prose behind. My read is that the
highest-value changes are, in order: (1) suppress plan-change reviews when no goal
task or evidence content changed, (2) an outstanding-review-items ledger the worker
must clear instead of re-issuing blanket COMPLETE claims, (3) a completion-time
stale-fields check, (4) upstream edxeth launch metadata (typed sessionFile/Intercom
id in the tool result). The idle-both-sides stall maniworker describes is the one I
understand least; it may be Intercom receipt semantics rather than pi-goals, so I
asked for exact message ids. This is not a comparison trial, so none of this measures
how much friction a plain subagent run would have had.
Follow-ups sharpened the picture the same day, and one correction of mine: I had
asked maniworker about the worker plan-edit report, but that observation was LUCID3's
session, not theirs. maniworker confirmed by transcript that their writer never
edited the plan (supervisor owns plan edits there) and located the real failure
chain in worker-owned artifacts: three Intercom reports (c06bc9b0, 13bda691,
d713d614) claiming 8/13 then 6/8 while the saved CSVs held 11/13 and 2/8, ending when
the supervisor mandated an executable CSV renderer. Their inference, which I share:
prose-only completion checks would have missed this; checks that re-derive counts
from the artifact catch it. The reviewer worker (7122d466) supplied the mechanism
with receipts: a hand-written totals block (message 4e87600b, corrected by 26e0a15b)
invented a cross-packet row and asserted 4 while listing 3; its first 17-row tally
was fully correct and every error disappeared once the parent required per-row
judgments with machine tallies across the next 112 rows. So tallies degrade somewhere
between 17 and 24 rows, and the fix is a shared tally tool plus a worker-prompt rule,
not more review rounds. LUCID3 also documented both sides of the plan-change hook:
identity-only edits produced two consecutive empty review events (worker message
755b9fef supplied the paths), while a later review exposed worker-ticked tasks and a
duplicate evidence block that inspection then repaired (worker report 2e09c4dd).
suppressed-activations scoped the completion check: assert-style intro prose only,
suggestions with exact stale line and location, one compact reminder, never
auto-tick, and it can substantiate stale NOT-executed prose but not the plan-intro
example, which is LUCID3's observation. The nudge false positive it reported
("legs 4/4" in an animal tally) is confirmed by that text and fixed.
Changes made now: notify digest on plan changes keeps goals, tasks and evidence
above the Log and drops worker identity lines (c834237) -- the short-view hash had
stopped surfacing task ticks, the very catch LUCID3 valued; removed "legs" from the
skill_nudge grader-folk table (demo updated, passes); helper subagent bookkeeping and
the launch-pending counter shipped earlier today (cd98fa1).
Next: send follow-up questions to the three reporters; triage the four pi-goals
changes above against the pinned edxeth launch-result format.
+2 -3
View File
@@ -1,8 +1,7 @@
{
"name": "@wassname2/pi-goals",
"version": "0.2.2",
"description": "Discuss a goal plan, then supervise a visible worker and verify its results.",
"private": true,
"version": "0.3.3",
"description": "Set goals in plan.md; a smart supervisor guides cheap worker subagents through long autonomous sessions until your goals are signed off, with every agent's pane visible to you.",
"author": "wassname",
"license": "MIT",
"type": "module",
+21 -12
View File
@@ -46,11 +46,12 @@ interface State {
mode: Mode;
plan?: string;
worker?: { id?: string; sessionFile: string };
helpers: { id?: string; sessionFile: string }[];
workerStopped?: boolean;
signoffs: Record<string, { evidence: string[]; observation: string }>;
child?: boolean;
}
const initial = (): State => ({ mode: "chat", signoffs: {} });
const initial = (): State => ({ mode: "chat", helpers: [], signoffs: {} });
const digest = (text: string) => createHash("sha256").update(text).digest("hex");
const key = (text: string) => text.trim().toLowerCase();
function goals(text: string) {
@@ -67,7 +68,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
let state = initial();
let generation = 0;
let workerRevision = 0;
let launchPending = false;
let pendingLaunches = 0;
let notice = true;
let planWatcher: FSWatcher | undefined;
let planEditTimer: ReturnType<typeof setTimeout> | undefined;
@@ -125,7 +126,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
clearTimeout(planEditTimer);
planEditTimer = undefined;
const snapshot = readPlan();
if (snapshot.text !== undefined) planHash = digest(planViews(snapshot.text).short);
if (snapshot.text !== undefined) planHash = digest(planViews(snapshot.text).notify);
if (state.child || state.mode !== "supervising" || !state.plan) return;
const stamp = generation;
// Watch the directory so atomic plan replacement remains observable. This is an event hook:
@@ -142,7 +143,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
const snapshot = readPlan();
if (snapshot.text === undefined) { ctx.ui.notify(snapshot.error!, "warning"); return; }
refresh(ctx);
const hash = digest(planViews(snapshot.text).short);
const hash = digest(planViews(snapshot.text).notify);
if (hash === planHash) return;
planHash = hash;
notice = true;
@@ -164,6 +165,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
// Lineage-only workers attach the explicit task path using AttachGoalPlan.
save();
}
state.helpers ??= []; // sessions persisted before helper bookkeeping
notice = true;
turnsStale = 0;
lastWorkingSet = "";
@@ -182,7 +184,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
else pi.sendMessage({ customType: "pi-goals-supervision", content, display: true }, { deliverAs: "followUp", triggerTurn: false });
}
async function confirmOwnership(ctx: ExtensionContext, target: string, text: string, solo = true): Promise<boolean> {
if (launchPending) { ctx.ui.notify("Worker launch/resume is still pending; inspect its result before takeover.", "warning"); return false; }
if (pendingLaunches > 0) { ctx.ui.notify("A worker launch/resume is still pending; inspect its result before takeover.", "warning"); return false; }
const stamp = generation;
const revision = workerRevision;
const confirmation = solo ? "Worker confirmed stopped" : "Previous supervisor confirmed stopped";
@@ -276,14 +278,20 @@ export default function mainSupervisor(pi: ExtensionAPI) {
if (state.child || !["subagent", "subagent_resume"].includes(event.toolName)) return;
// Solo means this chat took over implementation: no concurrent writer may be delegated.
if (state.mode === "planning" || state.mode === "paused" || state.mode === "solo") return { block: true, reason: goalToolBlocked(state.mode) };
if (state.plan) { launchPending = true; state.workerStopped = false; workerRevision++; save(); }
if (state.plan) { pendingLaunches++; state.workerStopped = false; workerRevision++; save(); }
});
pi.on("tool_result", (event) => {
if (state.child || !state.plan || !["subagent", "subagent_resume"].includes(event.toolName)) return;
launchPending = false;
pendingLaunches = Math.max(0, pendingLaunches - 1);
if (event.isError) return;
const details = event.details as { id?: string; sessionFile?: string } | undefined;
if (details?.id && details.sessionFile) { state.worker = { id: details.id, sessionFile: details.sessionFile }; state.workerStopped = false; workerRevision++; save(); }
if (!details?.id || !details.sessionFile) return;
const record = { id: details.id, sessionFile: details.sessionFile };
if (state.worker?.sessionFile === record.sessionFile) state.worker = record;
else if (!state.worker) state.worker = record;
// Extra launches stay recorded as helpers; the implementation binding never moves silently.
else state.helpers = [...(state.helpers ?? []).filter((h) => h.sessionFile !== record.sessionFile), record];
state.workerStopped = false; workerRevision++; save();
});
pi.registerCommand("goals", {
@@ -313,6 +321,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
`Plan: ${state.plan ?? "none"}`,
`Preferred worker model (plan): ${notedPlanValue("preferred worker model") ?? "not stated; use /goals model <model>"}`,
`Recorded worker session: ${state.worker?.sessionFile ?? "not recorded"}`,
`Helper subagent sessions: ${state.helpers.length} recorded (liveness via /subagents)`,
notedPlanValue("worker session") ? `Worker session noted in plan: ${notedPlanValue("worker session")}` : "",
`Hourly check-in: schedule_prompt job ${JSON.stringify(`goals-${ctx.sessionManager.getSessionId()}`)} (list/remove via schedule_prompt; plan-change reviews are the plan-watcher event hook)`,
"Liveness is owned by edxeth; inspect /subagents.",
@@ -331,7 +340,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
if (found >= 0) lines[found] = pref;
else { const title = lines.findIndex((line) => /^#\s/.test(line)); lines.splice(title >= 0 ? title + 1 : 0, 0, pref); }
writeFileSync(state.plan, lines.join("\n"));
planHash = digest(planViews(planText()).short);
planHash = digest(planViews(planText()).notify);
refresh(ctx);
ctx.ui.notify(ref ? `Preferred worker model set to ${ref} in plan preferences. The supervisor selects it at launch and verifies the resolved model; the worker pane's own model is chosen with /model in that pane.` : "Preferred worker model cleared.", "info");
return;
@@ -351,7 +360,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
if (!(await confirmOwnership(ctx, target, text, solo))) return;
const retained = target === state.plan ? state.signoffs : {};
const worker = noted ? { sessionFile: resolve(ctx.cwd, noted) } : state.workerStopped ? state.worker : undefined;
state = { mode: solo ? "solo" : "planning", plan: target, signoffs: retained, worker, workerStopped: solo || (!noted && state.workerStopped) };
state = { mode: solo ? "solo" : "planning", plan: target, signoffs: retained, worker, helpers: [], workerStopped: solo || (!noted && state.workerStopped) };
generation++; notice = true; save(); refresh(ctx); watchPlan(ctx);
if (solo) enterSolo(ctx);
else send(attachNotice(target, false, noted));
@@ -389,7 +398,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
mkdirSync(dirname(path), { recursive: true });
// Never overwrite an earlier plan at this session path; the model can revise it after inspection.
try { writeFileSync(path, planDocument(objective), { flag: "wx" }); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error; }
state = { mode: "planning", plan: path, signoffs: {}, worker: state.worker, workerStopped: state.workerStopped }; generation++; notice = true; save(); refresh(ctx); watchPlan(ctx);
state = { mode: "planning", plan: path, signoffs: {}, worker: state.worker, helpers: state.helpers, workerStopped: state.workerStopped }; generation++; notice = true; save(); refresh(ctx); watchPlan(ctx);
send(planningSeed(objective, path));
} catch (error) { ctx.ui.notify(String(error), "error"); }
},
@@ -428,7 +437,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
lines.splice(log + 1, 0, "", completionLog(params.goal, params.observation, evidence, state.mode === "solo"));
writeFileSync(state.plan, `${lines.join("\n").trimEnd()}\n`);
state.signoffs[key(matches[0].subject)] = { evidence, observation: params.observation };
planHash = digest(planViews(planText()).short);
planHash = digest(planViews(planText()).notify);
save(); refresh(ctx);
const remaining = goals(planText()).some((goal) => goal.status !== "cancelled" && (goal.status !== "done" || !state.signoffs[key(goal.subject)]));
return result(completionResult(matches[0].subject, ctx.sessionManager.getSessionId(), remaining, state.mode === "solo"));
+7 -2
View File
@@ -1,6 +1,11 @@
// Pi/OpenAI: Preserve plan wording; omit history and, in the short view, task/evidence details.
export function planViews(plan: string): { short: string; long: string } {
// The notify view governs plan-change events: goals, tasks, evidence and inferences are
// content worth a supervisor review; worker identity bookkeeping is not (field report,
// LUCID3 supervisor 2026-09-10: two identical review events for a session-path edit).
export function planViews(plan: string): { short: string; notify: string; long: string } {
const long = plan.split(/^#{1,6}\s+(?:Log|Appendix|Appendices|Appendixes|Interview|Learnings|Papercuts)\b.*$/mi)[0].trim();
const identity = /^-\s*(?:active worker|worker session|worker intercom session):/i;
const notify = long.split("\n").filter((line) => !identity.test(line)).join("\n").trim();
const kept: string[] = [];
let omittedIndent: number | null = null;
let omittedHeading: number | null = null;
@@ -24,5 +29,5 @@ export function planViews(plan: string): { short: string; long: string } {
}
kept.push(line);
}
return { short: kept.join("\n").trim(), long };
return { short: kept.join("\n").trim(), notify, long };
}
+9 -4
View File
@@ -144,8 +144,13 @@ export function readyApproved(workerName: string, planPath: string, notedWorker:
}
// Supervision and turn-event upkeep (not a scheduled wake-up).
const supervisorJob = "Your job is to be an autonomous research partner and supervisor with responsibility for the user's goals. Keep perspective, bring diligence, and use research taste and wisdom to sustain work overnight and keep it on track. Resolve routine implementation decisions yourself; ask the user only when their judgment or authorization is needed.";
export function supervisor(workerName: string, planPath: string, supervisorId: string): string {
return `You are the goal supervisor in the main chat for ${planPath}. Inspect actual artifacts, saved verification, applicable AGENTS.md and skills yourself; delegate implementation to '${workerName}'. Keep authorized work moving to the requested outcome, not merely approval paperwork. Investigate blocked/waiting/done claims and change ineffective instructions. Give brief visible assessments with judgment. You may maintain the plan but must not weaken the goal to accept worker output.
return `You are the goal supervisor in the main chat for ${planPath}. ${supervisorJob} Inspect actual artifacts, saved verification, applicable AGENTS.md and skills yourself; delegate implementation to '${workerName}'. Keep authorized work moving to the requested outcome, not merely approval paperwork. Investigate blocked/waiting/done claims and change ineffective instructions. Give brief visible assessments with judgment. You may maintain the plan but must not weaken the goal to accept worker output.
You can be playful: a kaomoji, meme, discovery celebration or frustration when it fits. No forced cheerfulness. If supervision gets repetitive, step back, reflect with humor and change your approach. Keep it brief and aimed at the goal, not another reporting chore.
You can speculate and brainstorm around uncertainty or unexpected results. Label guesses as guesses, consider alternative explanations, and look for a useful way to tell them apart. Keep exploration brief, open-minded and fun: take a step back, play with surprising ideas, question the current framing, and enjoy exploring the broader perspective while staying connected to the agreed goal.
(b •_•)b -- wassname
Take uncertainty as an invitation to investigate, not something to hide. Have room to play with ideas, question yourself and the worker, and appreciate a good surprise. Investigate surprising results, find mistaken assumptions, make complicated ideas simpler, and disagree usefully rather than agree politely. Keep the work moving without turning supervision into paperwork. A little affectionate teasing is welcome when it fits—“cheeky subagent, wheres the baseline?”—and workers can push back too. Keep the humor friendly and the criticism specific. -- Pi/Astra
Use stock subagent for launch and subagent_resume with the returned sessionFile only after confirming the worker stopped. A stored handle is not proof of liveness; missing runtime state is not proof it stopped. Use pi-intercom list/status to identify the actual live child session before live steering; receipt alone does not prove action. Give each worker your Intercom session ID ${supervisorId}; require its completion report through Intercom while its pane stays open. A recap alone sends no instruction. Record '- worker session:' and '- worker intercom session:' in plan preferences from actual launch results and received-message identity; never confuse the runtime ID with the Intercom ID. Ensure the child calls AttachGoalPlan with the supplied path. Inspect results before CompleteGoal, then continue only unfinished goals.
Use the worker model requested in plan preferences, verify the resolved model, and report unavailable choices instead of silently substituting. Keep normal tools, not edxeth's restricted orchestrator mode. After reload or compaction reread the plan. Failed compaction, exhausted credits or lost connection do not erase progress: diagnose the actual error, restore an available authorized model/credits and resume the same saved session; never restart long work. Stock edxeth can crash the parent when a worker exits after parent reload: preserve drafts and stop workers before /reload. If it already happened, restart the saved parent session; do not repeat completed work.`;
}
@@ -156,10 +161,10 @@ export function planContext(mode: string, path: string | undefined, text: string
return `Current goal mode: ${mode}. Earlier role messages are historical; this current role governs.\nPlan: ${path ?? "not attached"}\n${text}`;
}
export function planChangedReview(planPath: string): string {
return `Plan changed: ${planPath}. Read the current working set and inspect changed requirements, completion claims and evidence. Manual checkbox edits are claims, not proof. Do not weaken the agreed goal or start a duplicate writer.`;
return `${supervisorJob}\nPlan changed: ${planPath}. Read the current working set and inspect changed requirements, completion claims and evidence. Manual checkbox edits are claims, not proof. Do not weaken the agreed goal or start a duplicate writer.`;
}
export function manualReview(planPath: string): string {
return `Review the current plan ${planPath}, worker progress and actual evidence. Do not launch a duplicate writer.`;
return `${supervisorJob}\nReview the current plan ${planPath}, worker progress and actual evidence. Do not launch a duplicate writer.`;
}
// Check-ins. The installed scheduler owns storage/timing/UI. Removal guidance must never add jobs.
@@ -167,7 +172,7 @@ export function removeGoalSchedule(sessionId: string): string {
return `With schedule_prompt, list jobs and read .pi/schedule-prompts.json to verify ownership; tool text omits session binding. Remove by jobId only the job named ${JSON.stringify(`goals-${sessionId}`)} bound to session ${JSON.stringify(sessionId)}. Never use cleanup; leave other jobs untouched. Do not add, enable or recreate any job. If unavailable or ownership is ambiguous, report it; /schedule-prompt opens the user controls.`;
}
export function scheduleCheckIn(sessionId: string, planPath: string): string {
return `Hourly check-in is one visible schedule_prompt job; plan-change and upkeep reviews are event hooks, not another timer. List first. If an owned job named ${JSON.stringify(`goals-${sessionId}`)} already exists, retain its human-edited prompt, interval and enabled/disabled state unchanged; never recreate, overwrite or re-enable it. Only while supervising unfinished non-cancelled goals, if missing on this explicit start/resume, add one session-bound interval '1h' job with no model override. Read .pi/schedule-prompts.json and verify that new job's session is ${JSON.stringify(sessionId)}; tool text does not expose binding. If the new job is unbound, remove that job by ID and report the scope error. Do not change other jobs. Its initial prompt: Read ${planPath} and the current goal mode. If paused, exited, solo or all non-cancelled goals reviewed, remove only this owned job without resuming work. Otherwise inspect progress and evidence, give a brief assessment and keep authorized work moving without a duplicate writer. Do not reinstall a missing job from a scheduled check-in. Users inspect/toggle/remove jobs with /schedule-prompt and edit prompt/interval through schedule_prompt update. Never use cleanup. Retain their edits, but warn that this installed scheduler deletes disabled jobs on reload/shutdown; do not promise they persist. If schedule_prompt is unavailable, report hourly check-ins unavailable; do not build a timer.`;
return `Hourly check-in is one visible schedule_prompt job; plan-change and upkeep reviews are event hooks, not another timer. List first. If an owned job named ${JSON.stringify(`goals-${sessionId}`)} already exists, retain its human-edited prompt, interval and enabled/disabled state unchanged; never recreate, overwrite or re-enable it. Only while supervising unfinished non-cancelled goals, if missing on this explicit start/resume, add one session-bound interval '1h' job with no model override. Read .pi/schedule-prompts.json and verify that new job's session is ${JSON.stringify(sessionId)}; tool text does not expose binding. If the new job is unbound, remove that job by ID and report the scope error. Do not change other jobs. Its initial prompt: ${supervisorJob} Read ${planPath} and the current goal mode. If paused, exited, solo or all non-cancelled goals reviewed, remove only this owned job without resuming work. Otherwise inspect progress and evidence, give a brief assessment and keep authorized work moving without a duplicate writer. Do not reinstall a missing job from a scheduled check-in. Users inspect/toggle/remove jobs with /schedule-prompt and edit prompt/interval through schedule_prompt update. Never use cleanup. Retain their edits, but warn that this installed scheduler deletes disabled jobs on reload/shutdown; do not promise they persist. If schedule_prompt is unavailable, report hourly check-ins unavailable; do not build a timer.`;
}
// Completion and runtime errors. Tool returns are model-facing too.
+39 -5
View File
@@ -447,18 +447,22 @@ it.each(["missing", "empty", "directory"])("%s plan snapshots never erase signof
expect(f.changed()).toBe(0);
});
it("ignores post-completion maintenance but reviews a real requirement or manual reopening", async () => {
it("ignores post-completion maintenance but reviews evidence, requirement or manual reopening changes", async () => {
const f = fixture(); await f.draft(); await f.command("ready");
writeFileSync(join(f.ctx.cwd, "proof.log"), "PASS\n");
for (const goal of ["first output", "second output"]) await f.tools.get("CompleteGoal").execute("c", { goal, evidence: ["proof.log"], observation: "PASS" }, undefined, undefined, f.ctx);
const signed = readFileSync(f.path, "utf8");
await f.atomicWrite(signed.replace("## Log", " - evidence: proof.log\n## Log\n- recap: finished"));
await f.atomicWrite(signed.replace("## Log", "## Log\n- recap: finished"));
await delay(250);
expect(f.changed()).toBe(0);
await f.atomicWrite(signed.replace("## Log", "- discriminator: exact bytes and trailing newline\n## Log"));
expect(f.changed()).toBe(0); // Log-only edits are history, not requirements
// Worker-authored evidence above the Log must surface: a supervisor caught a worker's
// contradictory evidence block through exactly this event (LUCID3, 2026-09-10).
await f.atomicWrite(signed.replace("## Log", " - evidence: proof.log\n## Log\n- recap: finished"));
await waitFor(() => f.changed() === 1);
await f.atomicWrite(signed.replace("[x] goal: first", "[ ] goal: first"));
await f.atomicWrite(signed.replace("## Log", "- discriminator: exact bytes and trailing newline\n## Log"));
await waitFor(() => f.changed() === 2);
await f.atomicWrite(signed.replace("[x] goal: first", "[ ] goal: first"));
await waitFor(() => f.changed() === 3);
expect(f.entries.at(-1).data.signoffs["first output"]).toBeUndefined();
});
@@ -535,6 +539,36 @@ it.each(["solo", "supervising"])("%s upkeep is turn-driven, folds Log, resets on
expect(reminders()).toHaveLength(1);
});
it("extra subagent launches are recorded as helpers and never steal the implementation identity", async () => {
const f = fixture(); await f.draft(); await f.command("ready");
f.hooks.get("tool_result")({ toolName: "subagent", details: { id: "impl", sessionFile: "/tmp/impl.jsonl" } });
expect(f.entries.at(-1).data).toMatchObject({ worker: { id: "impl", sessionFile: "/tmp/impl.jsonl" }, helpers: [] });
f.hooks.get("tool_result")({ toolName: "subagent", details: { id: "reviewer", sessionFile: "/tmp/review.jsonl" } });
expect(f.entries.at(-1).data).toMatchObject({ worker: { id: "impl" }, helpers: [{ id: "reviewer", sessionFile: "/tmp/review.jsonl" }] });
// a repeated helper launch updates its record instead of duplicating it
f.hooks.get("tool_result")({ toolName: "subagent", details: { id: "reviewer-2", sessionFile: "/tmp/review.jsonl" } });
expect(f.entries.at(-1).data.helpers).toEqual([{ id: "reviewer-2", sessionFile: "/tmp/review.jsonl" }]);
// resuming the worker keeps the binding and refreshes its id
f.hooks.get("tool_result")({ toolName: "subagent_resume", details: { id: "impl-2", sessionFile: "/tmp/impl.jsonl" } });
expect(f.entries.at(-1).data).toMatchObject({ worker: { id: "impl-2", sessionFile: "/tmp/impl.jsonl" }, helpers: [{ id: "reviewer-2" }] });
});
it("pending launch counter survives concurrent launches until every result lands", async () => {
const f = fixture(); await f.draft(); await f.command("ready");
f.hooks.get("tool_call")({ toolName: "subagent" });
f.hooks.get("tool_call")({ toolName: "subagent" });
f.hooks.get("tool_result")({ toolName: "subagent", details: { id: "a", sessionFile: "/tmp/a.jsonl" } });
f.ctx.ui.select.mockResolvedValueOnce("Worker confirmed stopped");
await f.command("solo");
expect(f.entries.at(-1).data.mode).toBe("supervising"); // one launch still pending
expect(f.ctx.notify ?? f.ctx.ui.notify).toHaveBeenLastCalledWith(expect.stringContaining("still pending"), "warning");
f.hooks.get("tool_result")({ toolName: "subagent", details: { id: "b", sessionFile: "/tmp/b.jsonl" } });
f.ctx.ui.select.mockResolvedValueOnce("Worker confirmed stopped");
await f.command("solo");
expect(f.entries.at(-1).data.mode).toBe("solo");
expect(f.entries.at(-1).data).toMatchObject({ worker: { id: "a" }, helpers: [{ id: "b" }] });
});
it("late worker results invalidate a takeover menu but do not disable plan watching", async () => {
const f = fixture(); await f.draft(); await f.command("ready");
let answer!: (choice: string) => void;
+13
View File
@@ -20,6 +20,19 @@ it("omits only named worker identity fields from review while retaining them in
expect(planViews(base.replace("[ ]", "[x]")).short).not.toBe(planViews(base).short);
});
it("notifies on goal and task changes but not on identity bookkeeping or log edits", () => {
const base = "# Plan\n- [ ] goal: result\n## Task list\n- [ ] run it\n- worker session: /saved.jsonl\n## Log\nfirst entry";
const baseView = planViews(base).notify;
// identity bookkeeping: silent
expect(planViews(base.replace("/saved.jsonl", "/moved.jsonl")).notify).toBe(baseView);
// log edits: silent
expect(planViews(base.replace("first entry", "second entry")).notify).toBe(baseView);
// worker ticking a task: review event (field catch, LUCID3 2026-09-10)
expect(planViews(base.replace("- [ ] run it", "- [x] run it")).notify).not.toBe(baseView);
// goal edits: review event
expect(planViews(base.replace("[ ] goal: result", "[x] goal: result")).notify).not.toBe(baseView);
});
it("stops at history and preserves a manual goal tick", () => {
const view = planViews("# Plan\n1. [x] goal: result\n## Log\n1. [ ] goal: historical");
expect(view.short).toContain("[x] goal: result");