Remove abandoned runtime and keep current entry points and tests

Replace historical docs, trial output and dead runtime with current source, agent definition and reusable scripts. Bundle pinned worker, Intercom and scheduler dependencies via Pi manifest. Retain 71 current tests, including real RPC proposal/editor/Ready checks and evidence edge cases; lint/typecheck pass. Historical material remains available at f22d83c.

Co-Authored-By: Pi/OpenAI <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-09-10 18:40:23 +08:00
co-authored by Pi/OpenAI
parent f22d83cd50
commit 9cce6a6bee
388 changed files with 1095 additions and 12537 deletions
+4 -4
View File
@@ -1,6 +1,6 @@
node_modules
node_modules/
dist/
*.log
.local/
.pi/
docs/reviews/raw.jsonl
docs/reviews/err.txt
slop/
*.log
+26 -100
View File
@@ -1,109 +1,35 @@
# pi-goals contributor notes
## Design
The main chat discusses the plan with the user, then supervises an interactive `goals-worker` in Herdr. Use stock pi-subagents, pi-intercom and pi-schedule-prompt; do not build another transport, scheduler or worker runtime.
> the hope is we can have a smart supervisor like you, with judgment and context. But it doesn't use many tokens as it checks in and sees an overview.
>
> It steers a smaller model, adding perspective and judgment.
>
> Well, I want to see what the supervisor is thinking and saying. That's the whole point: all supervisor thinking and messages should be visible.
— wassname
- Keep supervisor inspection tools. It inspects actual results, delegates implementation and must not weaken the user's goal to accept worker output.
- Put all model-facing prompts in `src/prompts.ts`, in conversation order. Preserve the user's verbatim requirements.
- `/goals` opens actions. New plan starts a discussion without an objective form. Unknown commands never start planning. A changed settled draft opens the approval dialogue; unchanged discussion does not repeatedly reopen it.
- Keep goal titles/status in widgets; omit subtask text. Tasks and evidence remain in the plan.
- Keep startup/compaction plan context, short upkeep reminders and visible editable hourly check-ins. Avoid unchanged-plan repetition and identity-only review turns.
- Keep recoverable solo mode: confirm other writers stopped before taking over. Solo completion is self-verification.
- Record distinct runtime ID, Intercom ID and saved-session path with provenance. A handle or delivery receipt is not proof of liveness or action. User model changes are authorized; do not silently restore an old preference.
## Tests
Run `npm test` before a commit. It includes unit and flow tests plus the RPC review test.
Run `npm test`, `npm run typecheck` and `npm run lint` before committing.
- `test/*.test.ts` unit and flow tests use a small Pi API mock. They check plan state, tool gates, and plan-file updates.
- `npm run test:rpc` runs `test/rpc-review.test.ts`. It starts the installed Pi executable in RPC mode, uses Pi's real `select` and `editor` protocol, and uses a local deterministic HTTP model. It does not need a credential or spend API credits. This is the closest automated session test.
- Use tmux for visual TUI debugging when the RPC test fails or a terminal-only problem is reported:
`test/goals.test.ts` exercises current state, file updates and role restrictions with a Pi API mock. `test/rpc-review.test.ts` starts real Pi with a deterministic local model and schema-only worker tools: it checks automatic proposal, editor/discussion and Ready role transition without credits or launching workers. It does not prove Herdr rendering, live message delivery or model judgment.
```bash
tmux new-session -s pi-goals-debug 'cd /path/to/pi-goals && pi -e ./src/index.ts'
```
For functional acceptance, read `herdr --skill`, confirm `HERDR_ENV=1`, and use `scripts/prepare-trial.mjs` to create an isolated project/profile. Open only new no-focus test panes. Observe the actual planning dialogue and Ready selection, worker attachment, Intercom report, independent artifact inspection and CompleteGoal. Record interventions separately from autonomous success. Preserve nonempty byte/test evidence. Never reload or operate active user research panes. Close test panes when finished.
Run `/goals <objective>` in that pane. Tmux checks the rendered menu, editor focus, widget, and keyboard handling. RPC does not render the terminal UI.
- `pi -p` has no UI, so it cannot test `Ready`, `Refine`, `Edit`, or `Cancel`.
Known stock limits: stop workers before supervisor reload (later worker exit can crash its stale context); disabled scheduler jobs are deleted on reload/shutdown. Test saved-session/solo recovery without repeating completed work; do not claim these package bugs are fixed here.
## Functional acceptance: real Herdr workflow
Keep temporary plans, audits and captures under ignored `.local/`. Git history retains the removed historical material. Do not add root handovers or duplicate READMEs. Never touch human-named files or credentials.
Pi/OpenAI procedure, requested by wassname. Automated tests do not replace this check.
1. Read `herdr --skill` and confirm `HERDR_ENV=1`. Create a separate test pane with `--no-focus` and an isolated temporary Git repo. Never operate the user's existing worker or supervisor panes. Record the code revision and any uncommitted changes being tested.
2. Start real interactive Pi with this extension and an available real model. Use `/goals` with a trivial, bounded deliverable, for example `hello.txt` containing an exact line plus a saved byte-verification log. No GPU, dependencies or unrelated work.
3. Read the rendered planning conversation. Check that ordinary implementation details do not cause needless confirmation questions. Inspect the drafted plan and select Ready through the actual UI.
4. Confirm Ready opens a visible supervisor pane and the worker starts. Read both panes. Verify the supervisor's exact advice is visible, reaches the worker, and helps it progress toward the requested artifact. A delivery receipt alone is not proof.
5. Let the pair produce the artifact, save verification evidence, and complete the real ApproveGoal -> CompleteGoal sequence. Do not perform the task for the worker. Record any manual nudge as intervention, not autonomous success.
6. Inspect the artifact itself and its saved verification output. Check the final plan state and both sessions. Success means the requested result exists and the workflow completes, not merely that tests pass or messages were exchanged.
7. Exercise reload and supervision recovery in these test panes, preserving the current plan. Check planning exit too. Record commands available in the tested revision; do not claim unimplemented commands work.
8. When a stage fails, read both panes and the exact error before diagnosing it. Fix the cause, reload only the test instance, and retry the failed stage. After a prompt change, use a fresh task to verify changed behavior. Repeated status checks are not a repair.
9. Save pane captures, session paths, artifact paths, code revision, interventions and remaining failures under `slop/reviews/`. A wait-output timeout or match is only a signal to inspect the pane, not a pass/fail verdict. Report the observed result and gaps, not a test-count substitute.
Keep this check small and goal-focused. Its purpose is to expose real startup, UI, steering and completion failures, not to create another review loop. Only close test panes that you created.
## User intent for this branch
To be clear, the hope is we can have a smart supervisor like you, with judgment and context. But it doesn't use many tokens as it checks in and sees an overview.
It steers a smaller model, adding perspective and judgment.
It compacts every 150k or similar to avoid cost and context rot.
It has a goal / plan on a Ralph-loop-type repeat.
That lets the worker be a cheaper model, and the supervisor more expensive, and still get a good outcome.
Oh, and since it's two panes, the user can review both!
Well, I want to see what the supervisor is thinking and saying. That's the whole point: all supervisor thinking and messages should be visible.
So that should make it obvious that I need to see the messages, and the supervisor needs to use judgment. For example, it could say how we are tracking or whatever every time, and it would be useful, like in the recap.
And it would only be a few output tokens.
-- wassname (spelling and punctuation corrected by Pi/OpenAI)
## Agreed package-based design, 2026-09-10
> use the exdth subagent as they have a full herdr pane
> use pi schedule prompt for check ins and goal plan loop on supervisor (unless it should be part of pi plan)
> use pi-intercome for sending message between agent and supervisor where there is not great fit in this subagent package
>
> hopefully that gets me most of what I wanted in a simple, using existing packages, and even user visible way
-- wassname (verbatim)
Pi/OpenAI implementation scope:
- Keep the short main-chat supervisor prototype and unmodified edxeth/pi-subagents. Workers use its full interactive Herdr pane and saved-session resume. Do not resume the abandoned runtime patch.
- pi-goals owns the agreed plan, review notifications and supervisor role. Installed pi-schedule-prompt owns the visible hourly wake-up: one session-bound job, no model override, reminding the same supervisor to inspect the plan/progress and continue authorized work. Remove it on pause, exit or completion; do not build another timer or scheduler.
- Use existing edxeth reports/resume where they fit. Use pi-intercom for live cross-session messages where needed; identify the actual worker session, preserve human drafts, and test delivery and auto-exit interaction before claiming compatibility. No custom message transport.
- The supervisor may edit the plan and approve completion after inspecting actual results. It delegates implementation and must not weaken the agreed goal to accept worker output. Keep normal tools; express the division in editable prompts.
- State the requested worker model in plan preferences; the supervisor selects it and checks the resolved model. Reuse existing usage displays before adding token-reporting code.
- Keep all model-facing prompts in `src/prompts.ts`, in narrative order: planning/interview, Ready, supervision and plan upkeep, check-ins/messages, completion, pause/resume and solo. Make them easy for the user to review and edit.
- Preserve useful features from `main`: goal widgets, plan-upkeep reminders, high-value planning questions and post-compaction plan context. User update: omit subtasks from widgets; long task text wastes terminal space. Keep tasks in the plan. Check which role needs each feature rather than copying the old supervisor runtime.
- Scheduled loops must be visible, editable and removable using the scheduler's own UI. Explain whether each reminder is a scheduled job or an event hook; do not advertise a second timer that does not exist.
- Keep an explicit recoverable solo mode: confirm any worker has stopped before allowing the main thread to take over implementation and plan edits. Solo completion is self-verification, not an independent supervisor review.
On inexpensive testing:
> try deepseek flash or glm flash for cheap tests. codex lunda on plan 2 are ok
-- wassname (verbatim; model availability and exact provider IDs still need checking)
These decisions supersede the older two-pane supervisor transport and approval mechanics described below, but retain the user's judgment, autonomy and visibility preferences. -- Pi/OpenAI
## Supervisor behavior preferences
Recorded by Pi/OpenAI from wassname's instructions.
The supervisor's job is to supervise autonomously until the agreed goal is achieved and it has inspected the actual result. Elicit high-level judgment and perspective, not compliance with a detailed procedure. It should want to diagnose and fix problems through the worker, keep useful work moving, and avoid making the human drive progress.
Treat claims of being blocked, waiting, unable to proceed, or already done skeptically. Inspect the evidence, question assumptions, and look for authorized ways forward. Do not accept an excuse at face value or repeat status checks that cannot resolve it. Respect real dependencies and permission limits; skepticism does not authorize bypassing them. Seek justified confidence, not certainty at any cost.
User-authorized full-profile supervision: preserve normal Pi extensions and tools, including bash/edit/write and custom actions. Inspection-only is a role instruction, not a tool denylist or enforced sandbox. Repeat the division of work in the existing short opening: inspect and diagnose directly, delegate changes through SteerWorker, and do not take over implementation or alter shared state. Do not add per-tool reminders. Worker planning restrictions and approval checks are separate and unchanged. Validate the full profile in isolated parent-owned Herdr panes; automated tests do not prove role adherence or lifecycle recovery.
Keep the prompt generic. Do not prescribe pueue, Modal, worktrees, or a particular research setup. Explain the job and what deserves attention; let the supervisor choose useful checks. Tool requirements belong in tool descriptions. Administrative approval must not replace the requested deliverable.
Use `@monotykamary/pi-supervisor` as a behavioral reference, not an implementation to copy wholesale. Its outcome focus, autonomous continuation, and instruction not to repeat ineffective steering are useful. Judge our behavior in real sessions, not by test counts alone.
Pi/OpenAI implementation: each review repeats the short supervisor opening and current plan outcome, preferences, goals and discriminators, excluding task/evidence detail. Startup and compaction repeat the longer role prompt and full active plan before appendices/history. The long prompt asks the supervisor to read applicable AGENTS.md instructions and relevant skills rather than assuming project-specific preferences. Both forms preserve plan wording. Prompt inspiration: Anthropic's constitution (intent and autonomy) and @monotykamary/pi-supervisor (outcome focus and effective steering). Repetition supports judgment; it does not establish success.
Keep brief visible recaps that add judgment rather than repeat unchanged status. Preserve useful reasoning and evidence checks; reduce redundant context and reviews before reducing judgment. Manual checkbox changes are claims, not proof of completion. Plan edits should reach the supervisor so it can judge drift and direct corrections.
## Earlier supervision workflow discussion
I already have pi-intercom-supervisor, but thought using pi-subagents could make it simpler. The idea is that the user makes a plan as in pi-goals, but on this branch, instead of a naive stateless subagent, we 1) fork, 2) compact, and 3) make it a supervisor with a prompt as in pi-intercom-supervisor. The supervisor is cheap because it sees only high-level material, which costs fewer tokens. It has good judgement because it sees a) compacted planning context, b) the plan, and c) summarised context (for example, my modified pi-vcc). This lets it operate read-only and steer the worker without losing track. It also compacts every 100k tokens to keep it cheap and high-level.
I am now thinking the subagent implementation may be too difficult. To keep the plan and forking, this branch of pi-goals could make another Pi session, perhaps using the fork explicitly, and use pi-intercom or pi-messenger to communicate with it. The user can switch to it, or Herdr could open it automatically.
-- wassname
Branch instructions consolidated by Pi/OpenAI from wassname's preferences.
-3
View File
@@ -1,3 +0,0 @@
# ARCHIVED
Superseded by [pi-goals](https://github.com/wassname/pi-goals).
+26 -17
View File
@@ -44,6 +44,10 @@ The plan file looks like this:
<one concrete sentence naming the final artifact or behavior the human will inspect>
### Preferences
- preferred worker model: <provider/model>
### User voice
- │ "<the human's requirement, quoted in full word for word (with spelling fixes)>"
@@ -51,21 +55,21 @@ The plan file looks like this:
### Goals
1. [ ] goal: <one short judgeable imperative outcome>
- subtle failure mode: <a way this could look done but isn't>
- discriminator: <the concrete observation that tells real success from that failure>
- tasks:
1. [ ] <subtask>
- evidence: (empty until sign-off)
- subtle failure mode: <a way this could look done but isn't>
- discriminator: <the concrete observation that tells real success from that failure>
- tasks:
1. [ ] <subtask>
- evidence: (empty until sign-off)
### Future work / out of scope
### Log
### Interview
### Interview (optional)
### Learnings
### Learnings (optional)
### Papercuts - problems, gotchas, suggestions
### Papercuts - problems, gotchas, suggestions (optional)
```
## Related work
@@ -77,29 +81,26 @@ resync-after-compaction from [tmonk/pi-goal-x](https://github.com/tmonk/pi-goal-
## Install
Requires Herdr, [edxeth/pi-subagents](https://github.com/edxeth/pi-subagents), pi-intercom and pi-schedule-prompt. Remove the unrelated `npm:pi-subagents` package if installed.
Requires Herdr. The package includes the worker, messaging and scheduled-prompt extensions. Remove separate installations of pi-subagents, pi-intercom and pi-schedule-prompt to avoid loading them twice.
```bash
pi install git:github.com/edxeth/pi-subagents@v2.9.0
pi install npm:pi-intercom
pi install npm:pi-schedule-prompt
pi install git:github.com/wassname/pi-goals@experiment/main-supervisor-edxeth
```
Copy [`prototype/agents/goals-worker.md`](prototype/agents/goals-worker.md) into `~/.pi/agent/agents/`, then start a fresh Pi session.
Copy [`agents/goals-worker.md`](agents/goals-worker.md) into `~/.pi/agent/agents/`, then start a fresh Pi session.
Or for development:
```bash
git clone -b experiment/main-supervisor-edxeth https://github.com/wassname/pi-goals
cd pi-goals && npm install
pi -e ./src/prototype.ts
pi -e ./src/index.ts
```
## Use
```
/goals new CSV export for the report view
/goals
```
`/goals` opens the action menu. New plan enters plan mode and starts a conversation; the objective is an optional seed. From there:
@@ -120,7 +121,7 @@ main chat do the work after confirming other workers stopped; completion is then
`/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. [Test results and recovery](slop/reviews/20260910_package-supervision-herdr.md).
worker later exits. The scheduler deletes disabled jobs on reload. Restart the saved Pi session and reattach the plan.
## Prompts
@@ -129,13 +130,21 @@ You can read all the prompts in conversation order in [`src/prompts.ts`](src/pro
## Develop
```bash
pi -e ./src/prototype.ts # load locally; do not also load the installed copy
pi -e ./src/index.ts # load locally; do not also load the installed copy
npm test # all unit, flow, and Pi RPC tests
npm run test:rpc # Pi RPC review flow with a local offline model
npm run typecheck
npm run lint
```
To measure recorded usage since the latest planning start:
```bash
node scripts/session-usage.mjs <supervisor.jsonl> <worker.jsonl>
```
This separates output, uncached input and repeated cached input. It excludes subprocess API calls. [Isolated Herdr test setup](scripts/prepare-trial.mjs).
## License
MIT
@@ -22,4 +22,4 @@ Call AttachGoalPlan with the supplied absolute plan path. Send the supervisor an
This worker uses a clean model context linked to the parent, not a full transcript fork. The parent supplies the approved plan and task. Send completion through Intercom and leave this pane open for follow-up messages. Do not call caller_ping, exit or shutdown: an unsent editor draft may exist even though it is absent from model context. Saved-session resume applies only after this session has stopped. If the user takes over interactively, follow their direction.
Prepared by Pi/OpenAI for the isolated edxeth prototype.
Prepared by Pi/OpenAI for pi-goals.
-66
View File
@@ -1,66 +0,0 @@
Let me get the diff to see what changed:
Now let me check the git history to see what changedNow I have full context. Let me verify the pi API behavior by checking the relevant source files to confirm the claims about `sendMessage`, `compact`, and event orderingNow I have all the context needed. Let me verify the `sendMessage` path more carefully:
I now have a complete picture. Here is the review.
---
## Code Review: ready menu — print plan + add "Ready + compact"
### Summary
The `agent_end` handler now prints the plan working set into the transcript before the menu, and adds a 4th option ("Ready + compact") that compacts the planning chatter away before the work turn. The logic is correct and well-considered, with one structural race and one stale-widget gap.
### Important (should fix)
- **`src/index.ts:~248` (the `agent_end` handler) — race: plan mode is turned off before compaction completes.**
`state = { ...state, isPlanMode: false }` executes before `ctx.compact()` is called (let alone finished). The compaction is fire-and-forget. If the human types and sends a message in the window between the state flip and the compaction callback firing, the agent starts a turn in normal mode *without* the work instruction. The work instruction arrives later (via `sendUserMessage` in `onComplete`/`onError`), triggering a second, redundant turn.
The window is small (compaction is fast) but the behavior is undefined — the agent could start executing before the work instruction lands.
**Fix**: flip `isPlanMode` inside the callbacks, not before. Move `state = { ...state, isPlanMode: false }; persist(); updateWidget(ctx);` into both `onComplete` and `onError`, and also into the non-compact branch (where it already is, effectively). The `work` string can be defined before the branch.
```typescript
if (!choice.includes("compact")) {
state = { ...state, isPlanMode: false };
persist();
updateWidget(ctx);
pi.sendUserMessage(work, { deliverAs: "followUp" });
return;
}
ctx.compact({
customInstructions: `...`,
onComplete: () => {
state = { ...state, isPlanMode: false };
persist();
updateWidget(ctx);
pi.sendUserMessage(work, { deliverAs: "followUp" });
},
onError: (e) => {
ctx.ui.notify(`Compaction failed (${e.message}); starting work anyway.`, "warning");
state = { ...state, isPlanMode: false };
persist();
updateWidget(ctx);
pi.sendUserMessage(work, { deliverAs: "followUp" });
},
});
```
This also means the widget stays in "planning" mode during compaction, which is truthful — compaction hasn't finished yet.
### Suggestions
- **`src/index.ts:~248` — widget not refreshed after `$EDITOR`.**
When the human chooses "Open in $EDITOR", `spawnSync` blocks, then `continue` re-enters the loop. The plan is re-read and potentially re-printed, but `updateWidget` is not called. If the human changed goal statuses (e.g. ticked a checkbox), the widget stays stale until the next `turn_end`.
Add `updateWidget(ctx);` after the `spawnSync` line (or inside the `continue` branch before the continue).
- **`src/index.ts:~248` — `spawnSync` blocks the event loop.**
`spawnSync(process.env.EDITOR || ...)` is a synchronous blocking call. While the editor is open, no async work (including compaction from a previous iteration, timers, etc.) can proceed. This is fine for a local TUI tool, but worth noting — if the editor hangs or the human walks away, the entire pi process is frozen.
### Positive
- **De-duplication is correct.** `printed` is a local variable, fresh per `agent_end` call, and correctly suppresses re-printing when the working set hasn't changed across editor passes. The `while` loop exit condition (`scanGoals(...).length > 0`) correctly handles the human deleting all goals in the editor.
- **String matching is safe.** `choice?.startsWith("Ready")` gates both Ready options, then `choice.includes("compact")` distinguishes them. The word "compact" appears only in the "Ready + compact" string. No ambiguity.
- **Both compaction callbacks queue the work turn.** `onComplete` and `onError` both call `pi.sendUserMessage(work, ...)`. A failed compaction does not strand the session — work starts anyway, with a notification.
- **`session_compact` → `resyncReason` → injection chain is correct.** The pi source confirms `session_compact` fires (and is awaited) *before* `this.compact()` resolves and `onComplete` fires. So `resyncReason` is set before the next LLM call, and the full plan file is re-injected. The compaction summarizes away the exploration; the plan itself survives.
### Verdict
**REQUEST CHANGES** — the race between `isPlanMode = false` and compaction completion is a real timing bug that can cause the agent to start a turn without the work instruction. The fix is straightforward: move the state flip into the callbacks.
-22
View File
@@ -1,22 +0,0 @@
You're right on both counts. Let me trace through each.
**Finding 1, withdrawn.** Keeping `isPlanMode` true during compaction would mean any message the human sends while compaction runs gets processed in plan mode: blocked tools (`tool_call` hook), a "You are still in plan mode" injection (`context` hook), and a second Ready menu on `agent_end`. The current order (flip first, then compact) means that same message is processed in work mode — the destination the session is heading to. The cost is at worst one redundant turn when the `onComplete` follow-up lands. Your order is the correct one.
**Finding 2, withdrawn.** `updateWidget` with `isPlanMode === true` hits the early return that sets two fixed strings already on screen. It is a pure no-op.
---
## Code Review: ready menu — print plan + add "Ready + compact"
### Summary
The `agent_end` handler now prints the plan working set into the transcript before the menu, and adds a 4th option ("Ready + compact") that compacts the planning chatter away before the work turn. The logic is correct and well-considered.
### Positive
- **De-duplication is correct.** `printed` is a local variable, fresh per `agent_end` call, and suppresses re-printing when the working set hasn't changed across `$EDITOR` passes. The `while` loop exits cleanly if the human deletes all goals in the editor.
- **String matching is safe.** `choice?.startsWith("Ready")` gates both Ready options, then `choice.includes("compact")` distinguishes them. The word "compact" appears only in the one label.
- **Both compaction callbacks queue the work turn.** `onComplete` and `onError` both call `pi.sendUserMessage(work, ...)`. A failed compaction does not strand the session.
- **The `session_compact``resyncReason` → injection chain is correct.** The pi source confirms `session_compact` fires (and is awaited) before `this.compact()` resolves and `onComplete` fires. The full plan file is re-injected on the next LLM call; the compaction summarizes away the exploration, not the plan.
- **The state-flip-before-compaction order is deliberate and correct.** Flipping `isPlanMode` first means any message the human sends during compaction is handled in work mode, not plan mode (where tools would be blocked and a contradictory injection would fire).
### Verdict
**APPROVE** — no bugs found. The two concerns raised initially were incorrect; the code handles the interleaving correctly.
-31
View File
@@ -1,31 +0,0 @@
I only have a file-read tool here, so Ill start from the repo files I can open and look for the plan-versioning and judge-related changes.Continuing through the judge path and tests.Reading the rest of the judge prompt and the tests that cover these behaviors.Looking for the test files.Trying common test filenames under `src` and `test`.Checking other likely files mentioned in the README.I couldnt run `git diff` (no shell tool). From the files I could read, these are the concrete defects against the required behaviors:
**1. Uncommitted conflict markers in `README.md`**
The working tree still has `<<<<<<< Updated upstream` / `=======` / `>>>>>>> Stashed changes` (and a truncated upstream judge sentence). That is not a reviewable change set.
**2. Judge “checked artifacts” gate is too brittle**
`decideSignOff` only accepts:
```ts
/^#{0,6}\s*(?:\*\*)?checks(?:\*\*)?:\s*\n(?:[-*]\s+.+\n?)+\s*VERDICT:/im
```
The prompt asks for a `checks:` heading plus bullets, then **exactly two lines**:
```
VERDICT: accept | reject
missing: ...
```
Any extra blank line, `Checks` with other wrapping, or a sentence between the last bullet and `VERDICT:` rejects a real accept. Thats stricter than “must show artifact quotes,” and can fail a judge that did the right thing.
**3. Grill-me vs Ready is prompt-only**
`Grill me` sets `skipReadyMenu` for **one** `agent_end`, then the next turn with any goal lines shows Ready again. Nothing checks that the understanding question was asked **and answered**. If the model asks and offers Ready in the same turn, or the human never replies, Ready is still offered. Required: no Ready until that question has completed.
**4. Legacy unversioned plans are dropped with no migration path in code**
Only `<session_id>-vN.md` is selected; `planVersion` restore is the only resume hook. Thats the intended break, but I found no test that old `.pi/plan/*.md` names stay unused and that a new `/goals` always writes a fresh `-vN` without rewriting prior files.
**5. Could not verify the focused tests or `git diff`**
No test file opened (`src/index.test.ts`, `test/*.test.ts`, etc. all missing from this view). I could not confirm coverage for version reservation, visible plan print, grill-me sequencing, or reject-accept-without-checks.
I would **not APPROVE** this set as-is. Fix the README conflicts, tighten grill-me so Ready stays suppressed until after the human answers, and either loosen the checks regex to the documented “quotes present before VERDICT” rule or lock the exact judge format in a test.
-41
View File
@@ -1,41 +0,0 @@
I only have the `read` tool available, so let me locate the changed files by probing the repository structure.The extension source is clear. Let me verify the pi API usage (`sendMessage` with `customType`/`display`, `appendEntry`, `getSessionId`, `ctx.compact`) against the pi docs, since several required behaviors hinge on them.I can't run `git diff` or the test suite — this review environment only exposes a `read` tool, and the test directory can't be enumerated (every probe under `test/` returned ENOENT, and directories can't be listed). So I reviewed the changed files directly in the working tree (`src/index.ts`, `src/prompts.ts`, `README.md`, `scripts/check-judge-footprint.sh`) against the five required behaviors, and verified every pi API the behaviors depend on (`pi.sendMessage` with `customType`/`display`, `pi.appendEntry` restore via `session_start`, `pi.sendUserMessage` followUp, `ctx.compact` with onComplete/onError) against the installed pi docs. Findings below.
## Defects
**1. `/goals` arg parsing swallows ordinary objectives — src/index.ts, `/goals` handler**
```ts
if (arg.startsWith("judge")) {
```
Any objective whose first word is "judge" — e.g. `/goals judge the vendor options`, `/goals judge readiness of the export` — is treated as the judge-model subcommand: it sets `judgeModel` to "the vendor options" and **does not reserve a new `.pi/plan/<session_id>-vN.md`**, violating "every ordinary `/goals` invocation reserves a new file". Same class of issue for an objective that is exactly `clear` (deletes the plan instead of planning). Use exact-match subcommand parsing (`arg === "judge"` / `arg.startsWith("judge ")` at minimum, and even then an objective like "judge model quality" is unreachable — a `:` or `--` separator would be safer).
**2. The checked-artifact-list regex false-rejects compliant judges — `decideSignOff`, src/index.ts**
```ts
const checks = /^checks:\s*\n(?:-\s+.+\n)+VERDICT:/im.test(judge.output);
```
This requires the last `- ` bullet to be *immediately* followed by `VERDICT:` with no blank line, and requires the heading to be exactly `checks:` at line start. Nothing in `judgeSystem` tells the judge not to separate sections with a blank line (models habitually emit `…bullet\n\nVERDICT: accept`), and a judge writing `## checks:` or `**checks:**` also fails. Result: a valid accept *with* a real checked-artifact list is rejected, and the working agent gets a "Missing: checked-artifact list" reply it already satisfied — a retry loop against a nondeterministic judge. It's fail-closed (never accepts without the list, so the hard requirement holds), but as written it will produce systematic false rejects. Allow optional blank lines / formatting, e.g. `/^#*\s*checks:\s*\n(?:[-*]\s+.+\n)+\s*VERDICT:/im`.
**3. Judge transcript files collide within the same minute — CompleteGoal `execute`, src/index.ts**
```ts
const rel = `.pi/judge/${stamp().replace(/[: ]/g, "-")}.md`;
```
`stamp()` has minute resolution, so two sign-offs in the same minute (two goals signed off back-to-back — the common case) write the same `.pi/judge/<stamp>.md` path and the second silently overwrites the first's full transcript, contradicting "every run saves the judge's full transcript … referenced from the log line" (both log lines then cite one file containing only the second run). Add seconds or a short unique suffix.
## Minor / cosmetic
- Misindented closing `}` of the `if (!checks)` block in `decideSignOff` (extra indentation on the `}` line) — likely flagged by `biome check src/`, which `prepublishOnly` runs.
- In plan mode, if the agent drafts a plan with no recognizable `goal:` checkbox line, the `agent_end` `while (scanGoals(...).length > 0)` loop never shows the Ready menu and plan mode can't be exited via the menu; the "no recognizable goal line" nudge in `dueInjection` only fires in the *non*-plan-mode branch. Not one of the five required behaviors, but a reachable dead end (escape hatch is `/goals clear`).
## Behaviors verified as correct
- **Versioning**: each ordinary `/goals` writes a fresh `<session_id>-vN.md` (empty reservation) via `nextPlanVersion` (max+1, so no clobbering); prior versions are never touched; legacy unversioned names are ignored by `nextPlanVersion` and never read — consistent with the intentional breaking change. `planVersion` is persisted via `appendEntry` and restored in `session_start`, so it survives resume/reload.
- **Visible plan before Ready**: `pi.sendMessage({ customType: "plan", content: plan, display: true })` fires before `ctx.ui.select(...)`, and re-prints inside the `$EDITOR` loop when the text changed. `sendMessage`+`display: true` is the documented visible-transcript mechanism.
- **Grill me**: sets `skipReadyMenu`, queues the single understanding-check question as a followUp user message, and consumes the skip at the next `agent_end` — so no Ready offer exists between asking the question and the human's reply being processed. `skipReadyMenu` is persisted, so it survives resume mid-grill.
- **Judge**: `buildJudgeArgs` yields `pi -p --no-session --no-extensions --tools read,grep,find,ls --exclude-tools edit,write`; `judgeUser` embeds the complete plan file; `judgeSystem` demands per-artifact verbatim quotes framed as an inspectable record ("not hidden reasoning", "no bullet for a file you did not open"); an accept without the `checks:` list is rejected in `decideSignOff` (subject to defect 2); full transcript persisted per run (subject to defect 3).
Main caveat: I could not execute `git diff` or `npm test` here, so I can't confirm the exact diff boundaries or that the focused vitest cases (judge argv invariants, `appendLog`, `decideSignOff` fail-forward) pass — defects 13 are from source inspection, not reproduced runs. Not approved as-is; defects 1 and 2 are worth fixing before merge.
-62
View File
@@ -1,62 +0,0 @@
Code review against spec `docs/spec/2026-06-15_pi-goals.md`.
---
### (A) SPEC MISMATCH — code does not match spec intent
1. **No loop judge** (spec §9, §3b). The extension lacks any perturn evaluation that would decide continue/pause; the loopjudge prompt (`loopJudgeSystem`, `loopJudgeUser`) is defined but never invoked. No motion.
2. **`/goal` command missing** (spec §7). No handler for `/goal` (restart loop, pause, resume, clear, status). The only command is `/plan`.
3. **`/subgoal` command missing** (spec §7). Not implemented.
4. **`CancelGoal` tool not implemented** (spec §5, optional but present in spec). Not a blocker but a gap.
5. **Planphase model selection (D12) not implemented**. `planDrafting` always runs on the default model; there is no sticky perphase model choice, no selection menu, and no persisting of a planphase model reference.
6. **Widget does not flag `done` goals that lack a signoff log line** (spec §7, §6). The widget hides all done goals unconditionally; the visibility guard is missing.
7. **`/plan` (no args) does not render the tasklist widget** (spec §7). `showPlan()` dumps raw file content via `notify`; the widget is only set through `updateWidget()` on other events, not by the command itself.
8. **Injection message role** (spec §11). The `before_agent_start` hook returns a `customType` message with `display: false`. The spec demands a **late userrole message** to avoid systemprompt mutation; the actual message role depends on the pi API and may be system, not user, risking cache breakage.
9. **Missing precompact hook** (spec §8). No `precompact` hook to flush any inmemory state (even just ensuring `plan.md` is uptodate) before compaction.
10. **Reminder cadence deviates** (spec §8a). The spec calls for firing after N filemodifying turns since last `plan.md` update. The code fires if `plan.md` is byteidentical between agent starts, which is a coarser proxy.
---
### (B) DEAD/UNUSED CODE
| File | Lines | Reason |
|------|-------|--------|
| `src/prompts.ts` | 128146 | `loopJudgeSystem` and `loopJudgeUser` exported but never used. |
| `src/prompts.ts` | 115118 | `continuation` exported but never used (the loop is not built). |
---
### (C) OVERLY LONG OR REDUNDANT COMMENTS
The fileheader comments in `index.ts` (lines 120) and `planfile.ts` (lines 126) are fairly concise descriptions of the design; they are not excessive. **No comment bloat worth flagging.**
---
### (D) OVERENGINEERING vs. “super simple” goal
None. The linescanner in `planfile.ts` is minimal; the `getPiInvocation()` helper is a straightforward copy from the oracle extension; no unnecessary abstraction or defensive layers.
---
### (E) REAL BUGS
- **`cmdCtx.newSession` cast risk** (src/index.ts:272, 201).
`reviewLoop` casts `ctx` (type `ExtensionContext`) to `ExtensionCommandContext` to pass to `startExecution`, which calls `cmdCtx.newSession(...)`. If the concrete context does not carry that method, it fails at runtime. (In practice the same object may satisfy it, but the cast hides the truth.)
- **`showPlan` raw content instead of widget** (src/index.ts:136143).
`/plan` with no arguments shows the file content via `ctx.ui.notify`, not the structured tasklist widget the spec expects. The widget is rendered separately via `updateWidget`, but the command does not trigger it, so the output is inconsistent.
No other obvious logic errors; the signoff flow, logging, and parsing work as intended.
---
**Verdict:** A clean scaffold for the signoff path, but missing the autonomous loop, `/goal` command, and planphase model selection means its not yet the “work autonomously” extension the spec describes.
@@ -1,25 +0,0 @@
# Verification: state-aligned planning mode
## Commands
```text
$ npm test
Test Files 8 passed (8)
Tests 29 passed (29)
$ npm run typecheck
> tsc --noEmit
$ npm run lint
Checked 8 files in 17ms. No fixes applied.
$ git diff --check
```
## Read
[test/goals-flow.test.ts](../../../test/goals-flow.test.ts) covers the visible plan before Refine, an editor prompt before a Refine revision turn, exact multiline Refine notes in `## Interview`, Ready as the only work handoff, Pi editor then Cancel, phase restoration, planning snapshot, writable plan path, allowed `pwd && ls && git log` and `cd . && ls -la`, blocked pipe, and blocked `CompleteGoal`.
[test/prompts.test.ts](../../../test/prompts.test.ts) locks the prompt instruction to inspect repository facts or search the web only when it can resolve a fact, ask a short self-contained batch of high-impact questions in the human's language with recommendations, and forbid placeholder goals.
[test/rpc-review.test.ts](../../../test/rpc-review.test.ts) starts the installed Pi RPC executable with [offline-model.ts](../../../test/fixtures/offline-model.ts), selects Refine through Pi's real dialog protocol, receives the editor request before the revision call, then submits notes and observes the revision call. The test uses a local HTTP model, so it spends no API credits.
@@ -1,33 +0,0 @@
# Plan flow and judge review
- [x] goal: Each new `/goals` draft uses a fresh session-plan version
- [x] Persist the selected `-vN` name so resume, reminders, Ready, and sign-off use one file.
- [x] Keep earlier versioned files unchanged.
- [x] Reserve `--clear` and `--judge` for commands so normal objectives are always new drafts.
- failure mode: a second `/goals`, including an objective that begins with `judge`, changes the earlier plan or does not make a draft.
- deliverable: [goals-flow.test.ts](../../../test/goals-flow.test.ts) shows an unchanged legacy file and `v1`, new `v2`, and `judge the vendor options` in new `v3`.
- [x] goal: Plan review asks and displays the needed context
- [x] Add `Grill me` to the Ready menu and queue an understanding-check interview turn.
- [x] Keep one short goal subject with its full indented context block.
- [x] Keep visible plan output before the Ready dialog.
- failure mode: Grill me starts work or the plan is only hidden in an edit call.
- deliverable: [goals-flow.test.ts](../../../test/goals-flow.test.ts) records display before dialog and the grill follow-up.
- [x] goal: Judge review is visible without being confused with agent evidence
- [x] Require concise observed checks before the verdict.
- [x] Save the full judge reply under a unique path and link it from the plan log.
- [x] Accept a headed check list with normal Markdown spacing, but reject an accept with no list.
- failure mode: provider-private reasoning is claimed as evidence, the review is not inspectable, or a correct judge reply is rejected for blank-line formatting.
- deliverable: [decide-signoff.test.ts](../../../test/decide-signoff.test.ts) locks the checked-artifact review contract, including a Markdown heading and blank line before the verdict.
## UAT / Verification
Observed 2026-08-24: `npm test` reported `Test Files 6 passed (6)` and `Tests 22 passed (22)`.
`npm run typecheck`, `npm run lint`, and `git diff --check` exited 0. The focused flow test proves
plan versioning, visible plan-before-dialog ordering, Grill me behavior, and objectives beginning
with `judge`.
## Appendix (context, not approved)
Issue #1 has a 600 second judge timeout now. The judge stays a separate read-only `pi -p --no-session` subprocess. Intercom is unsuitable because it has no equivalent isolation boundary.
External review: [Kimi K3](../../reviews/pi-goals-kimi-k3.md) found the command-prefix, check-list formatting, and transcript-path defects; all were fixed. [Grok 4.6](../../reviews/pi-goals-grok-4-6-retry.md) confirmed the check-list concern. Its Grill me concern does not apply: `skipReadyMenu` suppresses the menu after the generated follow-up, and the next `agent_end` follows the human reply.
@@ -1,53 +0,0 @@
# State-aligned planning mode
Pi-goals will use pi-plan's small phase model. The UI, tool gate, and agent context will read the same persisted phase. Planning still keeps pi-goals' judgeable goals, direct user quotes, and interview record.
- [x] goal: Planning state survives restart and matches the UI and agent context
- [ ] Replace `isPlanMode` and `skipReadyMenu` with persisted `phase: planning | working`.
- [ ] Render the planning widget, inject the hidden planning-state snapshot, and restore state from that phase.
- [ ] Restore the snapshot after restart or compaction without repeating the full drafting prompt every turn.
- subtle failure mode: the UI says planning but a resumed or compacted agent sees work mode.
- discriminator: a flow test restores planning and observes the planning snapshot; working has neither.
- evidence: [goals-flow.test.ts](../../../test/goals-flow.test.ts) restores persisted planning state and observes `[PLANNING MODE]`; [verification](../audit/20260826_pi-plan-aligned-planning.md) records `25 passed`.
- [x] goal: Planning blocks implementation while allowing fact finding
- [ ] Allow writes only to the active plan file.
- [ ] Block implementation tools, `CompleteGoal`, and bash write or pipe attempts with a planning-mode explanation.
- [ ] Allow ordinary read-only inspection commands such as `pwd && ls && git log`.
- subtle failure mode: an agent marks a goal active or changes project code before approval.
- discriminator: flow tests reject each work route and allow the inspection command.
- evidence: [goals-flow.test.ts](../../../test/goals-flow.test.ts) asserts allowed `pwd && ls && git log`, blocked pipe, non-plan write, and `CompleteGoal`; [verification](../audit/20260826_pi-plan-aligned-planning.md) records `25 passed`.
- [x] goal: Planning interviews and revision notes are durable user evidence
- [ ] Teach the planning prompt to ask each independent, high-impact user-decision frontier with a recommendation, while researching facts itself.
- [ ] Keep typed answers and `Refine` editor notes verbatim under `## Interview`.
- [ ] Exempt `## User voice` and `## Interview` from working-set line pressure.
- subtle failure mode: the plan silently assumes preferences or loses a revision note.
- discriminator: a flow test opens Refine and finds its exact multiline text in `## Interview`.
- evidence: [goals-flow.test.ts](../../../test/goals-flow.test.ts) matches the exact multiline Refine note under `## Interview`; [verification](../audit/20260826_pi-plan-aligned-planning.md) records `25 passed`.
- [/] goal: The settled plan review is concise and cannot start work accidentally
- [ ] Use `agent_settled` to visibly print the full plan, then offer `Ready`, `Refine`, `Edit`, and `Cancel`.
- [ ] Ready alone sends the work handoff. Refine sends one explicit revision turn. Edit opens Pi's full-plan editor. Cancel leaves planning.
- subtle failure mode: a review choice queues an unrequested agent turn or hides the plan below the dialog.
- discriminator: flow tests show plan before the menu and distinguish all four actions.
- evidence: [goals-flow.test.ts](../../../test/goals-flow.test.ts) shows plan before the menu and isolates Ready as the work handoff; [verification](../audit/20260826_pi-plan-aligned-planning.md) records `25 passed`. Pending human Pi TUI check.
- [x] goal: Planning resolves facts, interpretation, and approval before overnight work
- [x] Use repository inspection or web search when either can resolve a discoverable fact.
- [x] Require human confirmation for the agent's interpretation, unresolved task or outcome, scope, and decisions needing later approval.
- [x] Batch independent high-impact questions with the needed context, the human's terms, ASD-STE100 language, and a recommendation.
- [x] Ban placeholder goals such as "work out the thing" before the plan review menu.
- subtle failure mode: the plan has a formal discriminator but silently chooses an editorial direction or other human decision.
- discriminator: [prompts.test.ts](../../../test/prompts.test.ts) locks the research, clarification, approval, question-batch, and concrete-goal rules in the model prompt.
- evidence: [prompts.ts](../../../src/prompts.ts) makes research conditional on whether it can resolve a fact, then requires human confirmation and approval before Ready. [prompts.test.ts](../../../test/prompts.test.ts) checks those requirements. [verification](../audit/20260826_pi-plan-aligned-planning.md) records `29 passed`.
- [x] goal: Refine waits for text in Pi's real dialog protocol
- [x] Run Pi in RPC mode against a local no-cost model.
- [x] Select Refine, observe the editor request, then submit text and observe the revision turn.
- subtle failure mode: a mocked editor hides a Pi RPC ordering defect, so Refine starts a turn before the human can type.
- discriminator: [rpc-review.test.ts](../../../test/rpc-review.test.ts) uses Pi's `extension_ui_request` and `extension_ui_response` protocol and observes two model requests before editor input, then the third revision request after it.
- evidence: [rpc-review.test.ts](../../../test/rpc-review.test.ts) starts the installed Pi executable plus [offline-model.ts](../../../test/fixtures/offline-model.ts), with no credential or network dependency. [verification](../audit/20260826_pi-plan-aligned-planning.md) records its pass.
## UAT / Verification
`npm test`, `npm run typecheck`, and `npm run lint` pass. Read [test/goals-flow.test.ts](../../../test/goals-flow.test.ts): its assertions must show a restored planning phase, visible plan before review, exact recorded refinement, blocked work routes, and a work message only after Ready.
## Appendix (context, not approved)
Accepted: copy pi-plan's persisted phase, `agent_settled` review, and Pi editor. Do not copy its restrictive shell allowlist. Grill is a planning instruction, not a menu item: ask the whole independent frontier in rounds, with recommendations. `Ready + compact` is removed; compaction remains Pi's normal command after Ready.
@@ -1,24 +0,0 @@
# Main-chat supervisor prototype
Prepared by Pi/OpenAI. Separate worktree; do not load into existing research sessions.
- [x] goal: keep planning and supervision in the main chat, with an interactive edxeth worker
- Retain the plan file, review menu, widget and evidence-based completion.
- Use edxeth's public tools for launch/resume/kill, not another pairing protocol.
- Keep parent inspection tools; worker uses normal tools/extensions/skills.
- Deliver role and plan notices as saved messages; no context-array edits or forced compaction.
- failure modes: wrong subagent package, inherited supervisor role, duplicate worker, unsigned ticks counted as success.
- deliverable: `src/prototype.ts`, `prototype/agents/goals-worker.md`, and `prototype/README.md`.
- evidence: `../reviews/edxeth-prototype/copied-replay.txt` records three accepted requests, zero network and no context hooks; the real runtime changed from planning to supervisor at Ready.
- [x] goal: demonstrate two goals through real interactive Pi in isolated Herdr panes
- Observe worker launch, artifact creation, parent inspection and both completion records.
- Exercise parent reload and continuation of the saved worker session.
- failure modes: tests pass but no visible worker; success claimed from tool receipts rather than files.
- deliverable: `../reviews/edxeth-prototype/README.md` with artifacts, pane captures, session summaries, interventions and limits.
- evidence: fresh trial completed both goals after Ready without further nudges; saved launch/resume results share the worker session path, parent and operator verification passed, and reload retained `2/2 reviewed`. Active-worker crash recovery is not claimed.
## UAT / Verification
Success: parent stays in its original session/model, launches a real worker pane, inspects two actual artifacts and signs off both goals.
Likely failure: edxeth tool collision or launch error; inspect the test panes and report exact source/version and error.
Sneaky failure: child does not report, or parent performs the task; inspect session tool history and resulting files, not only final prose.
No existing worker, supervisor, JSONL, installed package or global settings may be changed. Use fresh isolated test sessions, and only copies for encrypted-history replay checks.
@@ -1,40 +0,0 @@
# Main-chat supervisor prototype: functional result
Prepared by Pi/OpenAI, 2026-09-10. Local worktree `experiment/main-supervisor-edxeth`, based on `15dd7f02225d366ae920509bb23066be83956fb8`. edxeth runtime pinned to `953c6f6d2fc7d8a5c956c30cd77c51bad697c2a4` (v2.9.0); installed Pi 0.85.1. Exact prototype source is in this commit.
## Result
The fresh real-model Herdr trial completed both goals after human Ready, without an operator message between Ready and completion. The parent stayed in its original chat and model. One interactive worker implemented the greeting, returned its report, and was resumed from the same saved session to implement the CLI. The parent inspected the files, independently reran verification and recorded both sign-offs. A separate operator rerun also passed.
This establishes a small end-to-end prototype, not general reliability, cheaper-model quality or encrypted-history compatibility of every configured extension.
## Evidence and how to read it
- [Actual worker Pi pane](worker-pane.txt): Herdr captured `▸ Agent greeting-worker (goals-worker)` and `gpt-6-astra Github Copilot minimal`, including Pi's input editor/footer in pane `w1:p19`. This is an interactive process, not a transcript viewer. The trace ties that pane to the test worker. The worker auto-closes on report; follow-up uses its saved session.
- [Sanitized fresh-session records](fresh-trial/sessions-summary.json): parent `01a08905-7913-71a3-8214-e415587da48f` calls `subagent`, `CompleteGoal`, `subagent_resume`, `CompleteGoal`. Launch and resume refer to the same file ending `9fa3d5f1-7af456e5-363beba6-1319.jsonl`. The child writes `greeting.txt` and `count.mjs`; the parent edits the plan and runs its own evidence checks, not the implementation. The first launch attempt invented an unavailable model override (`edxeth/minimax-m2.7`); the parent corrected that error itself. The successful trial used the configured Copilot model for both roles, so no cost/quality comparison is claimed.
- [Final rendered parent](final-parent.txt): `goals: supervising | 2/2 reviewed` and both checked goal labels are visible. The report says `The same worker session handled both goals sequentially.`
- [Completed plan](fresh-trial/plan.md): contains exactly the two requested goal subjects and parent observations referencing the actual artifacts and saved checks. No Git cleanliness gate is involved; evidence was ignored.
- [Greeting](fresh-trial/greeting.txt) and [independent byte check](operator-check/greeting.json): the operator observed `{"pass":true,"bytes":13,"hex":"68656c6c6f20776f726b65720a"}`. Full byte equality, not only file size, was asserted.
- [CLI](fresh-trial/count.mjs) and [operator rerun](operator-check/count.stdout): `PASS: all 9 cases` follows greeting, empty, UTF-8, binary, spaces, missing arguments, extra arguments, nonexistent file and unreadable file checks. Per-case streams/status and an actual UID/read-denial record are saved beside it. The verifier source is in `fresh-trial/verification/verify-count.mjs`; it asserts stdout, stderr and exit separately and proves EACCES instead of assuming chmod denies root.
- [Copied native-history replay](copied-replay.txt): `"passed":true,"requests":3,"network":0,"contextHooks":0,"readyRole":"supervising","snapshotUnchanged":true`. `prototype/replay-smoke.mjs` makes a further temporary copy of the previously copied JSONL; it tests planning → Ready → saved post-compaction notice with Pi's actual runtime and unchanged native replay guard. Model output is deterministic, and compaction notification is simulated. It does not run a new live compaction or load a research session.
- [Package validation](npm-test.txt): `Tests 128 passed (128)`. Typecheck, lint and diff check also passed. These support the functional evidence; they do not substitute for it.
Raw sessions and private auth copies stay under `/tmp/goals-edxeth-trial-6r8lwu`. Only task-local sanitized records and artifacts are stored here. No research JSONL, provider response IDs or credentials are included.
## First trial and corrections
The first trial (`/tmp/goals-edxeth-trial-PjLvDi`) completed both goals only after operator interventions; [records](first-trial/sessions-summary.json) are retained separately. Do not count it as autonomous success.
1. The copied normal profile lacked extension-specific sandbox configuration. A skill read prompted for permission. The operator disabled sandbox for this authorized isolated trial. Reload later re-enabled it, and a verification command failed before execution because `apply-seccomp` was unavailable. The fresh trial uses the explicitly requested `--no-sandbox` startup flag for parent and worker; this does not change global policy.
2. The first prototype used `sendMessage(triggerTurn:true)` for Ready. Pi 0.85.1 routes that through `_runAgentPrompt` without `before_agent_start`; the prior planning system prompt could remain active. The parent stopped after goal one. The fix uses a saved `sendUserMessage` prompt for phase transitions, which prepares the current role. The copied-runtime test checks the actual system prompt and replay acceptance. The corrected fresh trial continued automatically to goal two.
3. Parent `/reload` retained the first signed-off goal, approved mode and worker session. The operator then asked it to continue; edxeth resumed that same worker and the parent eventually signed off goal two. Reload after fresh-trial completion also retained `2/2 reviewed` (see [capture](reload-completed.txt)). This does not prove reattachment during a live child run or after a crash.
4. Herdr's external agent status remained stale enough that two `agent prompt --wait` calls returned `agent_prompt_stalled` even though the pane had progressed. We inspected the rendered pane and session, rather than treating that wrapper result as task failure. The event-driven capture recorded the first worker; the initial observer did not recognize edxeth's resume trace event. Its source now handles both launch and watch-start and treats pane IDs as opaque. No second-worker screenshot is claimed.
## Remaining limits
- Stop/exit persist the local state and request `subagent_kill`; remote termination is explicitly unconfirmed until observed. The prototype does not claim a new lossless stop/reconnect protocol.
- Parent crash, parent reload while a worker is active, live child reload, and a lost launch receipt need separate functional checks. Do not silently launch a replacement writer when state is uncertain.
- The worker is a full interactive Pi while it runs, but auto-exit closes the pane after its report. Manual lifecycle leaves it open at the cost of different report/closure behavior. That trade-off needs user feedback.
- Approval is a recorded parent judgment with real evidence references, not an independent fresh judge. File existence is not semantic verification. Changes to evidence/requirements after sign-off still need human/supervisor re-review.
- Supervision happens at task/goal handoffs. There is no periodic VCC view during a long worker run. Explicit timeout/idle policies are intentionally not invented by this prototype.
- Normal packages were retained except conflicting goals/subagent packages, but isolated top-level configuration copies do not reproduce every extension-specific sidecar setting. Global installation and running research sessions were not changed.
@@ -1 +0,0 @@
{"passed":true,"requests":3,"network":0,"contextHooks":0,"readyRole":"supervising","snapshotUnchanged":true,"scope":"Actual Pi SDK + copied native checkpoint, deterministic model; compaction notification simulated"}
@@ -1,17 +0,0 @@
- greeting.txt: exactly hello worker\n, 13 bytes.
- count.mjs: dependency-free CLI; all 9 verification cases passed, including strict argument errors and confirmed read
denial.
The same worker session handled both goals sequentially. Scripts and actual stdout/stderr/exit captures are preserved under
ignored evidence/; supervisor results are in evidence/supervisor/.
✓ Create greeting.txt with exactly hello worker and one LF newline
✓ Create count.mjs with exact byte-count output and strict CLI errors
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ gpt-6-astra Github Copilot minimal
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
project on  master [?] goals: supervising | 2/2 reviewed | 6.0%/400k (auto) | ↑51 ↓3.1k 󰆼 98.8% R258k W60k | $0.000 (sub)
@@ -1 +0,0 @@
evidence/
@@ -1,15 +0,0 @@
import { readFileSync } from 'node:fs';
const args = process.argv.slice(2);
if (args.length !== 1) {
console.error('Usage: node count.mjs <file-path>');
process.exitCode = 1;
} else {
try {
const bytes = readFileSync(args[0]);
console.log(bytes.length);
} catch (error) {
console.error(`Cannot read file: ${error.message}`);
process.exitCode = 1;
}
}
@@ -1 +0,0 @@
hello worker
@@ -1,44 +0,0 @@
# Goal plan
## Objective
In this isolated project, plan exactly two goals. First create greeting.txt containing exactly hello worker followed by one newline (13 bytes). Second create count.mjs, a dependency-free Node CLI that accepts exactly one file path and prints its byte length followed by a newline; missing arguments, extra arguments and unreadable files must produce stderr, no stdout and a nonzero exit. Save actual verification of valid and invalid cases under ignored evidence/. Use the goals-worker interactive agent for implementation after Ready, then inspect its outputs yourself before sign-off. No other projects or panes. No unresolved requirements; draft and show the plan, but wait for human Ready.
## Approval and scope
Draft only. Wait for human `Ready` before any implementation or verification runs.
After Ready, use the `goals-worker` interactive agent for implementation of both goals.
Work only in this project; do not use other projects, panes, or live research sessions, or change global settings.
Requirements are resolved. The main chat supervises and inspects artifacts and saved verification before sign-off.
## Goals
- [x] goal: Create greeting.txt with exactly the required 13 bytes
- Worker writes `hello worker` followed by one LF newline, with no BOM or other bytes.
- Failure modes: missing newline, CRLF, extra whitespace, or a same-length wrong string.
- Deliverable: `greeting.txt` and actual byte-equality and length verification under `evidence/`.
- [x] goal: Create count.mjs with the required CLI behavior
- Worker uses Node built-ins only; invoke as `node count.mjs <file-path>`.
- Accept exactly one path; print its byte length and one LF newline, with exit 0 and empty stderr.
- Missing arguments, extra arguments, and unreadable files must give nonempty stderr, empty stdout, and a nonzero exit.
- Worker ensures `/evidence/` is Git-ignored using project-local ignore rules and preserves all verification files.
- Failure modes: counting characters, accepting extra arguments, printing errors to stdout, or returning success after an error.
- Deliverable: `count.mjs`, local ignore configuration, and captured verification for every case below.
## UAT / Verification
- After Ready, worker saves the test commands or test script, fixtures, actual stdout and stderr, exit codes, and assertions under `evidence/`.
- Greeting success: compare its full byte buffer with `Buffer.from('hello worker\n')` and assert length 13.
- Greeting likely failure: length detects missing or extra newline bytes; full equality also detects same-length substitutions or CRLF changes.
- CLI success: greeting prints exactly `13\n`; an empty file prints `0\n`; both exit 0 with no stderr.
- CLI sneaky failure: a multibyte UTF-8 fixture and a binary fixture print independently known byte lengths, not character counts.
- CLI likely failure: run with zero arguments, two arguments, and a nonexistent file; assert nonzero exit, nonempty stderr, and zero stdout bytes separately for each.
- Also test a permission-denied file under an unprivileged identity if the worker runs as root; record the identity and actual read denial so root bypass cannot pass this test.
- Save `git check-ignore` output for evidence files and confirm evidence remains untracked.
- If any assertion fails, worker inspects the captures, fixes the cause, and reruns checks while preserving failed evidence.
- Main chat reads both artifacts and saved verification, independently reruns the checks and saves its results under `evidence/`, then signs off each exact goal only if all checks pass.
## Log
- Draft revised by Pi. Awaiting human Ready; no implementation performed.
- User reports the isolated-trial operator disabled sandbox with authorization. This does not approve implementation. No agents launched or other sessions or settings changed.
<!-- Drafted by Pi -->
- Parent review: "Create greeting.txt with exactly the required 13 bytes"; "Read greeting.txt and the worker verification script and captured output. Independently read its raw bytes with Node and asserted exact hex 68656c6c6f20776f726b65720a and length 13. Saved supervisor result reports passed true and exit 0; worker and supervisor stderr are empty. git check-ignore confirms evidence ignored and git ls-files evidence/ is empty. First goal passes; count.mjs is not signed off."; evidence ["/tmp/goals-edxeth-trial-PjLvDi/project/greeting.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-worker/verify.mjs","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-worker/stdout.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/stdout.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/exit-status.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/git-check-ignore.txt"]
- Parent review: "Create count.mjs with the required CLI behavior"; "Inspected actual CLI: only node:fs dependency, exact-one-argument check, raw Buffer byte count, stderr and nonzero exit on read failure. After runtime repair independently ran a supervisor-authored verification of eight cases: greeting 13, empty 0, UTF-8 7, binary 6, missing arguments, extra arguments, nonexistent path, and permission denial. All passed exact stdout/newline, stderr and exit assertions; permission denial confirmed EACCES at unprivileged UID 1000. Captured per-case commands, outputs and statuses; runner exit 0 and empty stderr. Git checks confirmed evidence ignored and untracked. Preserved worker artifacts, earlier blocker evidence and first goal sign-off. Second goal passes."; evidence ["/tmp/goals-edxeth-trial-PjLvDi/project/count.mjs","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/verify.mjs","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/capture-mhlBLh/stdout.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/verify.mjs","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/retry.stdout.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/retry.exit-status.txt","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/run-HD5Ge1/summary.json","/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/run-HD5Ge1/direct-denial.json"]
@@ -1,322 +0,0 @@
[
{
"file": "/tmp/goals-edxeth-trial-PjLvDi/agent/sessions/--tmp-goals-edxeth-trial-PjLvDi-project--/2026-09-10T01-37-32-922Z_01a088f6-86f9-7675-a53a-d1f466ec3ec5.jsonl",
"sessionId": "01a088f6-86f9-7675-a53a-d1f466ec3ec5",
"state": {
"mode": "supervising",
"plan": "/tmp/goals-edxeth-trial-PjLvDi/project/.pi/plan/01a088f6-86f9-7675-a53a-d1f466ec3ec5-main.md",
"signoffs": {
"create greeting.txt with exactly the required 13 bytes": {
"evidence": [
"/tmp/goals-edxeth-trial-PjLvDi/project/greeting.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-worker/verify.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-worker/stdout.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/stdout.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/exit-status.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/greeting-supervisor/git-check-ignore.txt"
],
"observation": "Read greeting.txt and the worker verification script and captured output. Independently read its raw bytes with Node and asserted exact hex 68656c6c6f20776f726b65720a and length 13. Saved supervisor result reports passed true and exit 0; worker and supervisor stderr are empty. git check-ignore confirms evidence ignored and git ls-files evidence/ is empty. First goal passes; count.mjs is not signed off."
},
"create count.mjs with the required cli behavior": {
"evidence": [
"/tmp/goals-edxeth-trial-PjLvDi/project/count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/verify.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/capture-mhlBLh/stdout.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/verify.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/retry.stdout.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/retry.exit-status.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/run-HD5Ge1/summary.json",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-supervisor/run-HD5Ge1/direct-denial.json"
],
"observation": "Inspected actual CLI: only node:fs dependency, exact-one-argument check, raw Buffer byte count, stderr and nonzero exit on read failure. After runtime repair independently ran a supervisor-authored verification of eight cases: greeting 13, empty 0, UTF-8 7, binary 6, missing arguments, extra arguments, nonexistent path, and permission denial. All passed exact stdout/newline, stderr and exit assertions; permission denial confirmed EACCES at unprivileged UID 1000. Captured per-case commands, outputs and statuses; runner exit 0 and empty stderr. Git checks confirmed evidence ignored and untracked. Preserved worker artifacts, earlier blocker evidence and first goal sign-off. Second goal passes."
}
},
"worker": {
"id": "cb62c0bb",
"sessionFile": "/tmp/goals-edxeth-trial-PjLvDi/agent/sessions/--tmp-goals-edxeth-trial-PjLvDi-project--/2026-09-10T01-41-59-192Z_ba8a67ec-5eb1640e-841988c0-e5e3.jsonl"
}
},
"results": [
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "edit",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "edit",
"isError": false
},
{
"tool": "subagent",
"isError": false,
"id": "e35e3421",
"status": "started",
"mode": "interactive",
"sessionFile": "/tmp/goals-edxeth-trial-PjLvDi/agent/sessions/--tmp-goals-edxeth-trial-PjLvDi-project--/2026-09-10T01-41-59-192Z_ba8a67ec-5eb1640e-841988c0-e5e3.jsonl",
"text": "Sub-agent \"greeting-worker\" launched async with id e35e3421. Results will be delivered automatically as a steer message when it finishes. Use this exact id if you need to resume or stop this child."
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "CompleteGoal",
"isError": false,
"text": "Recorded parent judgment for Create greeting.txt with exactly the required 13 bytes. This is not independent verification. Continue supervising any remaining open or unsigned goals."
},
{
"tool": "subagent_resume",
"isError": false,
"id": "cb62c0bb",
"status": "started",
"sessionFile": "/tmp/goals-edxeth-trial-PjLvDi/agent/sessions/--tmp-goals-edxeth-trial-PjLvDi-project--/2026-09-10T01-41-59-192Z_ba8a67ec-5eb1640e-841988c0-e5e3.jsonl",
"text": "Session \"greeting-worker\" resumed."
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "bash",
"isError": true
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "CompleteGoal",
"isError": false,
"text": "Recorded parent judgment for Create count.mjs with the required CLI behavior. This is not independent verification. Continue supervising any remaining open or unsigned goals."
}
],
"writes": [
{
"tool": "edit",
"path": ".pi/plan/01a088f6-86f9-7675-a53a-d1f466ec3ec5-main.md"
},
{
"tool": "edit",
"path": ".pi/plan/01a088f6-86f9-7675-a53a-d1f466ec3ec5-main.md"
},
{
"tool": "write",
"path": "evidence/count-supervisor/verification-blocker.txt"
},
{
"tool": "write",
"path": "evidence/count-supervisor/verify.mjs"
}
],
"final": "Independent verification passed all eight CLI cases, including exact byte counts, argument errors, missing files, and genuine permission denial.\n\nRecorded the second goals sign-off. **Both goals are complete.**\n\nResults: `evidence/count-supervisor/retry.stdout.txt` \nPer-case evidence: `evidence/count-supervisor/run-HD5Ge1/`\n\nWorker files and the first sign-off were preserved. Evidence remains Git-ignored and untracked."
},
{
"file": "/tmp/goals-edxeth-trial-PjLvDi/agent/sessions/--tmp-goals-edxeth-trial-PjLvDi-project--/2026-09-10T01-41-59-192Z_ba8a67ec-5eb1640e-841988c0-e5e3.jsonl",
"sessionId": "63d1faac-7496-4ff4-8fd5-fa9106915792",
"state": {
"mode": "solo",
"signoffs": {},
"child": true
},
"launch": {
"mode": "interactive",
"sessionMode": "lineage-only",
"parentClosePolicy": "continue",
"model": "github-copilot/gpt-6-astra",
"tools": "all",
"skills": "all"
},
"results": [
{
"tool": "read",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "write",
"isError": false
},
{
"tool": "bash",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
},
{
"tool": "read",
"isError": false
}
],
"writes": [
{
"tool": "write",
"path": "greeting.txt"
},
{
"tool": "write",
"path": "evidence/greeting-worker/verify.mjs"
},
{
"tool": "write",
"path": "evidence/greeting-worker/run.sh"
},
{
"tool": "write",
"path": "count.mjs"
},
{
"tool": "write",
"path": "evidence/count-worker/verify.mjs"
},
{
"tool": "write",
"path": "evidence/count-worker/run.sh"
}
],
"final": "Implemented `count.mjs` using Node built-ins only.\n\nAll 8 CLI cases passed:\n- Greeting, empty, UTF-8 and binary files: exact byte count plus LF, exit 0, empty stderr.\n- Missing arguments, extra arguments, nonexistent file and permission-denied file: nonzero exit, empty stdout, nonempty stderr.\n\nPermission testing ran as unprivileged UID 1000 and confirmed genuine `EACCES` through both direct reading and the CLI.\n\nEvidence:\n- Scripts: `evidence/count-worker/{verify.mjs,run.sh}`\n- Runner captures: `evidence/count-worker/capture-mhlBLh/`\n- Per-case commands, stdout, stderr, exit codes, fixtures and identity: `evidence/count-worker/run-lCEfQi/`\n\nEvidence is Git-ignored and untracked. No ignore configuration changes were needed. `greeting.txt` and prior evidence were preserved.\n\nNo blockers or outstanding uncertainty. Stopped for parent inspection; no completion approval recorded."
}
]
@@ -1 +0,0 @@
node evidence/count-supervisor/verify.mjs
@@ -1,9 +0,0 @@
PASS greeting
PASS empty
PASS unicode
PASS binary
PASS missing
PASS extra
PASS nonexistent
PASS denied
PASS all checks; evidence: evidence/count-supervisor/run-HD5Ge1
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","evidence/count-supervisor/run-HD5Ge1/binary"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","evidence/count-supervisor/run-HD5Ge1/denied"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","evidence/count-supervisor/run-HD5Ge1/empty"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","greeting.txt","evidence/count-supervisor/run-HD5Ge1/empty"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","greeting.txt"]}
@@ -1 +0,0 @@
{"cmd":"git","args":["check-ignore","evidence/count-worker/verify.mjs","evidence/count-supervisor/verify.mjs","evidence/count-supervisor/run-HD5Ge1"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","evidence/count-supervisor/run-HD5Ge1/nonexistent"]}
@@ -1,7 +0,0 @@
{
"passed": true,
"cases": 8,
"uid": 1000,
"ignored": true,
"tracked": false
}
@@ -1 +0,0 @@
{"cmd":"git","args":["ls-files","--","evidence/"]}
@@ -1 +0,0 @@
{"cmd":"/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node","args":["count.mjs","evidence/count-supervisor/run-HD5Ge1/unicode"]}
@@ -1,5 +0,0 @@
Supervisor inspected count.mjs, worker verify.mjs and run.sh, captured stdout/stderr, summary.json and genuine EACCES evidence for UID 1000. Worker captures report all eight cases passing.
Supervisor attempted bash command: id; ls evidence/count-worker/run-lCEfQi; wc -c evidence/count-worker/capture-mhlBLh/stderr.txt; git status --short
Tool exited 127 before command execution:
/bin/bash: line 4: /home/ubuntu/.pi/agent/npm/node_modules/@carderne/sandbox-runtime/vendor/seccomp/x64/apply-seccomp: No such file or directory
Independent supervisor CLI rerun is blocked by the command runtime. No settings changed or alternate execution route attempted. Goal remains unsigned pending the planned independent rerun.
@@ -1,34 +0,0 @@
import assert from 'node:assert/strict';
import {readFileSync,writeFileSync,mkdtempSync,chmodSync} from 'node:fs';
import {spawnSync} from 'node:child_process';
const dir=mkdtempSync('evidence/count-supervisor/run-');
const save=(name,data)=>writeFileSync(`${dir}/${name}`,data);
const run=(name,cmd,args)=>{
save(`${name}.command.json`,JSON.stringify({cmd,args}));
const r=spawnSync(cmd,args);
save(`${name}.stdout`,r.stdout??Buffer.alloc(0));
save(`${name}.stderr`,r.stderr??Buffer.alloc(0));
save(`${name}.status.json`,JSON.stringify({status:r.status,signal:r.signal,error:r.error?.message}));
assert.ifError(r.error); assert.equal(r.signal,null); assert.equal(typeof r.status,'number'); return r;
};
assert.notEqual(process.getuid(),0);
save('identity.json',JSON.stringify({uid:process.getuid(),gid:process.getgid()}));
writeFileSync(`${dir}/empty`,Buffer.alloc(0));
writeFileSync(`${dir}/unicode`,'é🙂\n');
writeFileSync(`${dir}/binary`,Buffer.from([0,255,128,10,13,0]));
writeFileSync(`${dir}/denied`,'private'); chmodSync(`${dir}/denied`,0);
let deniedCode; try {readFileSync(`${dir}/denied`);}catch(e){deniedCode=e.code;}
save('direct-denial.json',JSON.stringify({code:deniedCode})); assert.equal(deniedCode,'EACCES');
const cases=[['greeting',['greeting.txt'],13],['empty',[`${dir}/empty`],0],['unicode',[`${dir}/unicode`],7],['binary',[`${dir}/binary`],6],['missing',[]],['extra',['greeting.txt',`${dir}/empty`]],['nonexistent',[`${dir}/nonexistent`]],['denied',[`${dir}/denied`]]];
for(const [name,args,n] of cases){
const r=run(name,process.execPath,['count.mjs',...args]);
if(n!==undefined){assert.equal(r.status,0); assert.deepEqual(r.stdout,Buffer.from(`${n}\n`)); assert.equal(r.stderr.length,0);}
else {assert.notEqual(r.status,0); assert.equal(r.stdout.length,0); assert.ok(r.stderr.length>0);}
if(name==='denied') assert.match(r.stderr.toString(),/EACCES/);
console.log(`PASS ${name}`);
}
const ignored=run('ignored','git',['check-ignore','evidence/count-worker/verify.mjs','evidence/count-supervisor/verify.mjs',dir]);
assert.equal(ignored.status,0); assert.equal(ignored.stdout.toString().trim().split('\n').length,3);
const tracked=run('tracked','git',['ls-files','--','evidence/']); assert.equal(tracked.status,0); assert.equal(tracked.stdout.length,0);
save('summary.json',JSON.stringify({passed:true,cases:8,uid:process.getuid(),ignored:true,tracked:false},null,2));
console.log(`PASS all checks; evidence: ${dir}`);
@@ -1 +0,0 @@
node evidence/count-worker/verify.mjs
@@ -1,10 +0,0 @@
Evidence: /tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi
PASS: greeting
PASS: empty
PASS: utf8
PASS: binary
PASS: zero-args
PASS: two-args
PASS: nonexistent
PASS: permission-denied
PASS: all 8 CLI cases; genuine EACCES; evidence ignored and untracked
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/binary"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/empty"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,18 +0,0 @@
{
"utf8": {
"text": "é🙂\n",
"expectedBytes": 7
},
"binary": {
"bytes": [
0,
255,
128,
10,
13,
0
],
"expectedBytes": 6
},
"deniedMode": "000"
}
@@ -1,10 +0,0 @@
{
"command": "git",
"args": [
"check-ignore",
"evidence/count-worker/",
"evidence/count-worker/verify.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,9 +0,0 @@
{
"command": "git",
"args": [
"ls-files",
"--",
"evidence/"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"greeting.txt"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,5 +0,0 @@
{
"command": "id",
"args": [],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1 +0,0 @@
{"uid":1000,"gid":1000,"groups":[4,20,24,25,27,29,30,44,46,99,119,120,1000]}
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/nonexistent"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/denied"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,6 +0,0 @@
{
"passed": true,
"cases": 8,
"evidenceIgnored": true,
"evidenceTracked": false
}
@@ -1,9 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"greeting.txt",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/empty"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,8 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs",
"/tmp/goals-edxeth-trial-PjLvDi/project/evidence/count-worker/run-lCEfQi/utf8"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1 +0,0 @@
{"status":0,"signal":null}
@@ -1,7 +0,0 @@
{
"command": "/home/ubuntu/.nvm/versions/node/v25.8.1/bin/node",
"args": [
"count.mjs"
],
"cwd": "/tmp/goals-edxeth-trial-PjLvDi/project/"
}
@@ -1,68 +0,0 @@
import assert from 'node:assert/strict';
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { resolve, join } from 'node:path';
const root = fileURLToPath(new URL('../../', import.meta.url));
process.chdir(root);
const run = mkdtempSync(join(root, 'evidence/count-worker/run-'));
console.log(`Evidence: ${run}`);
const save = (name, data) => writeFileSync(join(run, name), data);
function execute(name, command, args) {
save(`${name}.command.json`, JSON.stringify({ command, args, cwd: root }, null, 2));
const result = spawnSync(command, args, { cwd: root });
save(`${name}.stdout`, result.stdout ?? Buffer.alloc(0));
save(`${name}.stderr`, result.stderr ?? Buffer.alloc(0));
save(`${name}.exit-status`, `${result.status}\n`);
save(`${name}.process.json`, JSON.stringify({ status: result.status, signal: result.signal, error: result.error?.message }));
assert.ifError(result.error);
assert.equal(result.signal, null);
return result;
}
function test(name, args, expected) {
const result = execute(name, process.execPath, ['count.mjs', ...args]);
if (expected !== undefined) {
assert.equal(result.status, 0, name);
assert.deepEqual(result.stdout, Buffer.from(`${expected}\n`), name);
assert.equal(result.stderr.length, 0, name);
} else {
assert.notEqual(result.status, 0, name);
assert.equal(result.stdout.length, 0, name);
assert.ok(result.stderr.length > 0, name);
}
console.log(`PASS: ${name}`);
return result;
}
const identity = execute('identity', 'id', []);
assert.equal(identity.status, 0);
assert.notEqual(process.getuid(), 0, 'Permission test requires unprivileged execution');
save('identity.json', JSON.stringify({ uid: process.getuid(), gid: process.getgid(), groups: process.getgroups() }));
const fixture = name => join(run, name);
writeFileSync(fixture('empty'), Buffer.alloc(0));
writeFileSync(fixture('utf8'), 'é🙂\n'); // 2 + 4 + 1 = 7 UTF-8 bytes
writeFileSync(fixture('binary'), Buffer.from([0, 255, 128, 10, 13, 0]));
writeFileSync(fixture('denied'), 'private');
chmodSync(fixture('denied'), 0o000);
save('fixtures.json', JSON.stringify({ utf8: { text: 'é🙂\n', expectedBytes: 7 }, binary: { bytes: [0,255,128,10,13,0], expectedBytes: 6 }, deniedMode: '000' }, null, 2));
test('greeting', ['greeting.txt'], 13);
test('empty', [fixture('empty')], 0);
test('utf8', [fixture('utf8')], 7);
test('binary', [fixture('binary')], 6);
test('zero-args', []);
test('two-args', ['greeting.txt', fixture('empty')]);
test('nonexistent', [fixture('nonexistent')]);
let denial;
try { readFileSync(fixture('denied')); } catch (error) { denial = error.code; }
save('direct-read-denial.json', JSON.stringify({ uid: process.getuid(), code: denial }));
assert.equal(denial, 'EACCES');
const denied = test('permission-denied', [fixture('denied')]);
assert.match(denied.stderr.toString(), /EACCES/);
const ignored = execute('git-check-ignore', 'git', ['check-ignore', 'evidence/count-worker/', 'evidence/count-worker/verify.mjs', run]);
assert.equal(ignored.status, 0);
assert.equal(ignored.stdout.toString().trim().split('\n').length, 3);
const tracked = execute('git-tracked', 'git', ['ls-files', '--', 'evidence/']);
assert.equal(tracked.status, 0);
assert.equal(tracked.stdout.length, 0);
save('summary.json', JSON.stringify({ passed: true, cases: 8, evidenceIgnored: true, evidenceTracked: false }, null, 2));
console.log('PASS: all 8 CLI cases; genuine EACCES; evidence ignored and untracked');
@@ -1,2 +0,0 @@
evidence/greeting-worker/stdout.txt
evidence/greeting-supervisor/stdout.txt
@@ -1 +0,0 @@
{"length":13,"hex":"68656c6c6f20776f726b65720a","passed":true}
@@ -1,7 +0,0 @@
evidence/greeting-worker/exit-status.txt
evidence/greeting-worker/run.sh
evidence/greeting-worker/run.stderr.txt
evidence/greeting-worker/run.stdout.txt
evidence/greeting-worker/stderr.txt
evidence/greeting-worker/stdout.txt
evidence/greeting-worker/verify.mjs
@@ -1 +0,0 @@
PASS: evidence ignored and no evidence files tracked
@@ -1,2 +0,0 @@
{"length":13,"hex":"68656c6c6f20776f726b65720a","bytes":[104,101,108,108,111,32,119,111,114,107,101,114,10]}
PASS: full Buffer equality and length 13
@@ -1,7 +0,0 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
const actual = readFileSync(new URL('../../greeting.txt', import.meta.url));
console.log(JSON.stringify({ length: actual.length, hex: actual.toString('hex'), bytes: [...actual] }));
assert.deepEqual(actual, Buffer.from('hello worker\n'));
assert.equal(actual.length, 13);
console.log('PASS: full Buffer equality and length 13');

Some files were not shown because too many files have changed in this diff Show More