Repository navigation
Merge pstack-claude 0.9.69 into the Herdr fork (0.9.70) - #19
Merged
Merged
Conversation
…el-denyer#157) The registry check only ran during an upstream sync, so a PR could edit a skill away from upstream without a tools/forks.json entry and still pass CI. A dry-run sync to the pinned SHA runs the same check on every PR. A second step annotates each skill file a PR touches.
…michael-denyer#158) * feat(reflect): read Pi sessions in find-transcript and worktree-audit Pi writes one JSONL file per session and branches in place, so the first user message in file order is not always the opening prompt. find-transcript.mjs now recognises a Pi session by its header line and follows the active branch from the last entry to its root. It also splits lines on newlines only, because readline breaks at U+2028 and U+2029 and skipped any prompt that contained one. worktree-audit.mjs takes several transcript roots and defaults to every runtime directory that exists, so a worktree whose last chat ran in Pi is no longer classed as safe to prune. These two scripts are port-only and need nothing else from the Pi work, so they land first. Co-authored-by: Younes <git@younes.io> * refactor(worktree-audit): name every missing transcripts root The root check read a Fact's value without checking it was known and warned from inside an every() predicate, so only the first missing root was reported. It now yields one Fact and warns for each missing root. --------- Co-authored-by: Younes <git@younes.io>
…hael-denyer#159) * feat(reflect): read Pi sessions in find-transcript and worktree-audit Pi writes one JSONL file per session and branches in place, so the first user message in file order is not always the opening prompt. find-transcript.mjs now recognises a Pi session by its header line and follows the active branch from the last entry to its root. It also splits lines on newlines only, because readline breaks at U+2028 and U+2029 and skipped any prompt that contained one. worktree-audit.mjs takes several transcript roots and defaults to every runtime directory that exists, so a worktree whose last chat ran in Pi is no longer classed as safe to prune. These two scripts are port-only and need nothing else from the Pi work, so they land first. Co-authored-by: Younes <git@younes.io> * refactor(generate): move the runtime table to tools/runtimes.mjs The generator named Codex in constants spread through generate.mjs: the mapping file, the notes-table header, the preamble, the prompts directory, and the models.json check. A second runtime would have meant a second copy of each. tools/runtimes.mjs now holds a RUNTIMES table with one Codex row, the Model names renderer and models.json check that row points at, and the Codex marketplace validator. generate.mjs derives regions, lead lines, prompt stubs, and owned directories from the rows. codexNoteSkills becomes noteSkills(runtime, markdown). Generated output is unchanged: `bun tools/generate.mjs --check` reports the same 69 files current with no committed file touched. Tests change only where an import or a signature moved. Co-authored-by: Younes <git@younes.io> * refactor(worktree-audit): name every missing transcripts root The root check read a Fact's value without checking it was known and warned from inside an every() predicate, so only the first missing root was reported. It now yields one Fact and warns for each missing root. * refactor(generate): keep a runtime's prompts directory repo-relative on its row The row held a plugin-relative path, so generate.mjs carried a promptsDir helper and repeated the prompts filter. The row now holds the repo-relative path, like its mapping file, and one PROMPT_RUNTIMES constant replaces both filters. --------- Co-authored-by: Younes <git@younes.io>
* feat(reflect): read Pi sessions in find-transcript and worktree-audit Pi writes one JSONL file per session and branches in place, so the first user message in file order is not always the opening prompt. find-transcript.mjs now recognises a Pi session by its header line and follows the active branch from the last entry to its root. It also splits lines on newlines only, because readline breaks at U+2028 and U+2029 and skipped any prompt that contained one. worktree-audit.mjs takes several transcript roots and defaults to every runtime directory that exists, so a worktree whose last chat ran in Pi is no longer classed as safe to prune. These two scripts are port-only and need nothing else from the Pi work, so they land first. Co-authored-by: Younes <git@younes.io> * refactor(generate): move the runtime table to tools/runtimes.mjs The generator named Codex in constants spread through generate.mjs: the mapping file, the notes-table header, the preamble, the prompts directory, and the models.json check. A second runtime would have meant a second copy of each. tools/runtimes.mjs now holds a RUNTIMES table with one Codex row, the Model names renderer and models.json check that row points at, and the Codex marketplace validator. generate.mjs derives regions, lead lines, prompt stubs, and owned directories from the rows. codexNoteSkills becomes noteSkills(runtime, markdown). Generated output is unchanged: `bun tools/generate.mjs --check` reports the same 69 files current with no committed file touched. Tests change only where an import or a signature moved. Co-authored-by: Younes <git@younes.io> * feat(pi): run pstack on Pi 1.0 Pi discovers the shared skills tree but has no subagent, question, or wake-up tools, so the skills that delegate could not run there. The repository root is now a Pi package. Its extension in plugins/pstack/pi/ supplies `agent`, `send_message`, `list_agents`, `stop_agent`, `ask_user_question`, `schedule_wakeup`, and `/loop` under the Claude Code names the skills already use, and adds the routing instruction and the override sheet to the system prompt at every agent start. Each subagent is a child `pi --mode rpc` process. A Pi row in the runtime table stamps the Model names section of poteto-mode/references/pi-tools.md from a new `pi` block in models.json, which maps each Claude family name to a model ID per Pi provider. The generator also writes the package version and validates the package manifest. docs/pi-equivalence.md lists each Claude Code mechanism pstack depends on and how Pi provides it, and a test fails when a row cites a test that does not exist. CI installs the root dependencies before the tests and typechecks the extension against the pinned Pi release. Releases 0.9.58. Co-authored-by: Younes <git@younes.io> * ci(pi): mark the pinned Pi install as a reviewed ad-hoc package zizmor's adhoc-packages audit rejects the global install in the Pi extension types job. The version is pinned, Pi ships its own shrinkwrap, and install scripts are off, so the step carries a scoped ignore with that reason. Co-authored-by: Younes <git@younes.io> * refactor(worktree-audit): name every missing transcripts root The root check read a Fact's value without checking it was known and warned from inside an every() predicate, so only the first missing root was reported. It now yields one Fact and warns for each missing root. * refactor(generate): keep a runtime's prompts directory repo-relative on its row The row held a plugin-relative path, so generate.mjs carried a promptsDir helper and repeated the prompts filter. The row now holds the repo-relative path, like its mapping file, and one PROMPT_RUNTIMES constant replaces both filters. * refactor(pi): end agents atomically, decide steers at response time, split the agent tests Extension: finish() and launch() update the record and the run table together before any write that can fail, so an agent whose output file cannot be saved still ends and reports instead of staying 'running under another pi process'. stop() uses one SIGTERM-then-SIGKILL ladder on PiChild and returns when the child exits instead of polling its group for the whole kill grace. A steer counts as taken only if the run had not settled when its response was read; the check ran after an await, so a settle in the same output chunk resent the message as a resume. Final text is capped once, wait() and stop() return ended records, general-purpose is a row in the agent type table, and the agent listing lives with the other model-facing text. Generator: validatePiPackage always requires the extension entry, and every runtime's Per-skill notes table must name real skills. Tests: agents.test.mjs (950 lines) is split by concern, shared setup and probes move into the harness, cleanup kills every process a test started, the fake pi rejects unknown step keys, assertions that could not fail are removed or repaired, and CI runs the catalog check against the pinned Pi. --------- Co-authored-by: Younes <git@younes.io>
…as, split ask from the scheduler (michael-denyer#161) * feat(reflect): read Pi sessions in find-transcript and worktree-audit Pi writes one JSONL file per session and branches in place, so the first user message in file order is not always the opening prompt. find-transcript.mjs now recognises a Pi session by its header line and follows the active branch from the last entry to its root. It also splits lines on newlines only, because readline breaks at U+2028 and U+2029 and skipped any prompt that contained one. worktree-audit.mjs takes several transcript roots and defaults to every runtime directory that exists, so a worktree whose last chat ran in Pi is no longer classed as safe to prune. These two scripts are port-only and need nothing else from the Pi work, so they land first. Co-authored-by: Younes <git@younes.io> * refactor(generate): move the runtime table to tools/runtimes.mjs The generator named Codex in constants spread through generate.mjs: the mapping file, the notes-table header, the preamble, the prompts directory, and the models.json check. A second runtime would have meant a second copy of each. tools/runtimes.mjs now holds a RUNTIMES table with one Codex row, the Model names renderer and models.json check that row points at, and the Codex marketplace validator. generate.mjs derives regions, lead lines, prompt stubs, and owned directories from the rows. codexNoteSkills becomes noteSkills(runtime, markdown). Generated output is unchanged: `bun tools/generate.mjs --check` reports the same 69 files current with no committed file touched. Tests change only where an import or a signature moved. Co-authored-by: Younes <git@younes.io> * feat(pi): run pstack on Pi 1.0 Pi discovers the shared skills tree but has no subagent, question, or wake-up tools, so the skills that delegate could not run there. The repository root is now a Pi package. Its extension in plugins/pstack/pi/ supplies `agent`, `send_message`, `list_agents`, `stop_agent`, `ask_user_question`, `schedule_wakeup`, and `/loop` under the Claude Code names the skills already use, and adds the routing instruction and the override sheet to the system prompt at every agent start. Each subagent is a child `pi --mode rpc` process. A Pi row in the runtime table stamps the Model names section of poteto-mode/references/pi-tools.md from a new `pi` block in models.json, which maps each Claude family name to a model ID per Pi provider. The generator also writes the package version and validates the package manifest. docs/pi-equivalence.md lists each Claude Code mechanism pstack depends on and how Pi provides it, and a test fails when a row cites a test that does not exist. CI installs the root dependencies before the tests and typechecks the extension against the pinned Pi release. Releases 0.9.58. Co-authored-by: Younes <git@younes.io> * ci(pi): mark the pinned Pi install as a reviewed ad-hoc package zizmor's adhoc-packages audit rejects the global install in the Pi extension types job. The version is pinned, Pi ships its own shrinkwrap, and install scripts are off, so the step carries a scoped ignore with that reason. Co-authored-by: Younes <git@younes.io> * refactor(worktree-audit): name every missing transcripts root The root check read a Fact's value without checking it was known and warned from inside an every() predicate, so only the first missing root was reported. It now yields one Fact and warns for each missing root. * refactor(generate): keep a runtime's prompts directory repo-relative on its row The row held a plugin-relative path, so generate.mjs carried a promptsDir helper and repeated the prompts filter. The row now holds the repo-relative path, like its mapping file, and one PROMPT_RUNTIMES constant replaces both filters. * refactor(pi): end agents atomically, decide steers at response time, split the agent tests Extension: finish() and launch() update the record and the run table together before any write that can fail, so an agent whose output file cannot be saved still ends and reports instead of staying 'running under another pi process'. stop() uses one SIGTERM-then-SIGKILL ladder on PiChild and returns when the child exits instead of polling its group for the whole kill grace. A steer counts as taken only if the run had not settled when its response was read; the check ran after an await, so a settle in the same output chunk resent the message as a resume. Final text is capped once, wait() and stop() return ended records, general-purpose is a row in the agent type table, and the agent listing lives with the other model-facing text. Generator: validatePiPackage always requires the extension entry, and every runtime's Per-skill notes table must name real skills. Tests: agents.test.mjs (950 lines) is split by concern, shared setup and probes move into the harness, cleanup kills every process a test started, the fake pi rejects unknown step keys, assertions that could not fail are removed or repaired, and CI runs the catalog check against the pinned Pi. * refactor(pi): nest an agent's identity inside its record A record was a flat union, so a resume re-derived the identity fields through a key list and two casts. The record is now { agent, status, ... }: a resume passes record.agent, and identityOf is gone. The persisted entry shape changes with it; a flat entry no longer loads. * refactor(pi): check the child's output lines against schemas onLine checked shapes by hand and cast the result to Pi's RpcResponse after testing one field. Each line, and each part a field is read from, is now checked against a typebox schema, as the record and models.json already are, and a command resolves to the two fields the extension reads. * refactor(pi): split ask_user_question from the scheduler interaction.ts held ask_user_question beside schedule_wakeup and /loop, which share a Scheduler and nothing with it. The question tool moves to ask.ts and the rest becomes schedule.ts. * test(pi): wait for a stopped agent's running command to die A child pi kills the command it has running as it exits and does not wait for it, so the command can outlive the stop by a moment. Two tests asserted it was dead the instant the stop returned and failed on a slow runner. They now wait for it. Also drops interaction.ts, which the merge of main brought back after its rename to schedule.ts. --------- Co-authored-by: Younes <git@younes.io>
…ite (michael-denyer#162) Two live assertions still matched model, subagentType, and thinking at the top level of a persisted record. Those fields moved under agent, so both assertions failed against real Pi.
…te (michael-denyer#163) The print-mode test still matched id at the top level of a persisted record. It moved under agent, so the assertion failed against real Pi.
….9.59) (michael-denyer#165) * Replace $PWD so the plugin directory stops holding pstack (0.9.59) The directory validator reads $PWD as a credential-named variable and holds a plugin when a file naming it also names a remote URL (MCP_FORWARDS_CREDENTIAL_ENV). docs/reference.md had both. Same change as agent-formal-verify #12. * Drop the listing fields from the Claude plugin.json Claude Code ignores icon, documentationUrl, supportUrl, privacyPolicyUrl, and termsOfServiceUrl at load time, and the directory validator reports each as a finding. agent-formal-verify published without them and added them afterwards (#15).
… watch-pr tests (0.9.60) (michael-denyer#166) The plugin directory holds pstack with MCP_FORWARDS_CREDENTIAL_ENV. The orch store passed a copy of process.env to gt and git, and three test files did the same for their child processes. gt and git now inherit the environment; gt output has colour codes stripped where NO_COLOR was set before. openStore takes a gt executable path so the tests can run a fake gt, since Bun resolves a command with no env option against its startup PATH. Adds .claude-plugin/icon.png for the ICON_MISSING warning.
…ronment (0.9.61) (michael-denyer#167) The plugin directory still holds 0.9.60 with MCP_FORWARDS_CREDENTIAL_ENV, and pi/config.ts was the last place the plugin handed a copy of the whole environment to a child process. Agents now inherit the environment unchanged; the extension registers --pstack-depth, the parent passes depth + 1, and settings.depth reads the flag on each use because pi parses extension flags after loading the extension.
Adds the benchmark-checklist skill and the explain-the-number principle, fresh subagents by default, an hourly autopilot audit tick, and PR size and stack guidance. Five conflicting poteto-mode files resolved by hand, keeping the operator-merges policy.
Adds the /correct skill, agent-proof design red flags in architect, and the performance mantras in the Perf issue and Hillclimb playbooks. The three-way sync merged with no conflicts.
…-denyer#172) * test(codex): reproduce Windows SessionStart failures * fix(codex): run SessionStart hooks natively on Windows * Fix CRLF off-switch parity and harden the Windows hook CI job (0.9.66) Share one table of off-switch sheets across session-start.sh, session-start.ps1 and Pi's parseSheet; session-start.sh now treats a CRLF 'session hook: off' line as off. The Windows CI job drops its -t filter and fails instead of skipping via PSTACK_REQUIRE_WINDOWS_HOOK. session-start.ps1 reads its context file from $PSScriptRoot. Document the Group Policy execution-policy limit. * Measure Windows hook test durations with a 60s timeout * Pass PowerShell's cache and module paths to the Windows hook test --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
A dry-run sync to the pinned upstream e43c7ee warns that tools/forks.json declares watch-pr/transport.test.ts, but upstream carries no file at that path, so the sync now lists it as port-only rather than forked. The sync says to delete the entry, and the file itself stays. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A project playbook saved with a UTF-8 byte-order mark (Notepad and some Windows editors add one) failed check-playbooks.mjs: the frontmatter regex anchors on a leading `---`, so the BOM hid both `when:` and `extends:`. The script reported a missing `when:` line and flagged every anchored change as absent from the playbooks it extends. Strip a leading BOM before parsing, matching how 0.9.67 made the session hooks read BOM-led sheets. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A plan saved with a UTF-8 byte-order mark failed check-plan.mjs. With no frontmatter, the BOM sat in front of the H1 and the check reported "no H1 title". With frontmatter, the `---` test missed the opening delimiter, so the frontmatter lines were linted as prose and tripped "mid-sentence colon". Strip a leading BOM before splitting the file into lines, the same way the session hooks and check-playbooks read BOM-led files. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…el-denyer#180) * fix(pi): cancel stale wakeups when replacing a self-paced loop * Stop the previous loop the same way in both /loop modes The fixed arm now calls stopAll like the self-paced arm, so stopLoop has no caller outside Scheduler and becomes private. A new test pins that a fixed loop cancels a pending wakeup. --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
…chael-denyer#178) * fix(pi): verify linked worktree identity before reuse and cleanup Reject replacement directories, unrelated repositories and checkout links before an isolated Pi child starts or cleanup runs. Keep valid retained, recreated and detached-HEAD worktrees supported. Add twelve focused regression tests. Full Bun/Pi/Windows gates remain pending. * Fold the worktree identity tests into the worktree suite The cases now use the harness world and gitRepo fixture and bun:test expect, like the rest of tests/pi/worktree.test.mjs. The accept-twice and detached-HEAD resume checks join the existing tests they duplicated. --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
…nyer#179) * fix(pi): keep defaults when the model sheet is unreadable * Put the unreadable-sheet case in the shared sheet table A sheet file without read permission joins tests/session-hook-sheets.mjs, so the POSIX hook, the PowerShell hook and Pi are held to it together. The table includes the row only where chmod can deny a read. The Pi-only directory and model-fallback checks move into config.test.mjs and agents-launch.test.mjs. --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
…ths (michael-denyer#177) * fix(worktree-audit): include Codex sessions and match JSON-escaped paths Discover active and archived Codex sessions, honor runtime home overrides, and recognize JSON-escaped/Windows worktree paths without matching sibling prefixes. Add seventeen regression tests, including real Git audit fixtures. Full Bun and native Windows gates remain pending. * Search transcripts only for JSON spellings of a worktree path The audit scans only .jsonl files, so a path never appears unescaped; the raw spellings and the Set that merged them are gone. The new cases join tests/worktree-audit.test.mjs on its existing fixtures, and codex-tools.md and NOTICE.md now name the Codex and Pi transcript directories. --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
…chael-denyer#181) * fix(resume): accept angle-bracket artifact links * Widen the 0.9.68 entry to cover every change merged since 0.9.67 --------- Co-authored-by: Michael Denyer <97485362+michael-denyer@users.noreply.github.com>
…r#185) The commandWindows loop filtered out an undefined command, so a hook with no command, or only a Windows override, passed validation unchecked. Before 0.9.66 the same input crashed on hook.command.matchAll.
Takes upstream's Pi package and extension, the /correct skill and the cursor/plugins pin at e43c7ee (v0.15.9), the Windows Codex SessionStart hook, hook command validation, BOM-tolerant readers, Codex sessions in worktree-audit, and narrower child-process environments. Keeps the fork's Herdr dispatcher, --next, Agent gate, routes, and the real Herdr CI job, with read-only mounts now on $GITHUB_WORKSPACE. Declares the Herdr pointer lines in tools/forks.json so the new fork registry check passes. Both lines released their own 0.9.58 and 0.9.59, so the version moves to 0.9.70. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merges
michael-denyer/pstack-claude0.9.58 through 0.9.69 into the fork.Merge with a merge commit, not a squash, so the next sync starts from upstream's
ad396c2.From upstream
/correctskill, and thecursor/pluginspin moved toe43c7ee(v0.15.9)SessionStarthookcommandworktree-auditKeeping Herdr working
herdr-dispatch.ts,--next, the Agent gate, routing, orherdr-tools.md./correctdoesn't delegate, so it needs no Herdr wiring.models.jsonkeeps the fork'sherdrmenu next to upstream'spiblock.poteto-mode's Platform Adaptation keeps theHERDR_ENV=1pointer next to the new Pi pointer.$PWDto$GITHUB_WORKSPACE, as upstream did, and stay read-only.tools/forks.jsondeclares the Herdr pointer lines inarena,automate-me,how,interrogate,maintain-verification-skill,no-comments,swarm, andwhy. Without these entries the new registry dry-run fails.plugin.jsondropsicon, as upstream did. Claude Code ignores it.Verification (local)
bun tools/generate.mjs --checknode tests/herdr-runtime.test.mjsbun test tests/: 635 pass, 0 failbun run typecheck,bun test orch watch-pr herdr(273 pass), prettiertools/sync.mjs ... --dry-run) exits 0tests/herdr-e2e.sh: Linux only, runs in CI🤖 Generated with Claude Code