From 642be7a7ea7e19ea9467cb52c18985e51ca5f6a1 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sat, 31 Oct 2026 16:23:46 -0700 Subject: [PATCH 1/6] fix(pstack): align native workflows with T3 orchestration capabilities --- .../src/mcp/OrchestratorMcpService.test.ts | 196 +- apps/server/src/mcp/OrchestratorMcpService.ts | 53 +- .../Adapters/OpenCode2AdapterV2.test.ts | 2 + .../src/orchestration-v2/Orchestrator.ts | 9 +- .../ProviderPoolFailover.integration.test.ts | 104 +- .../ProviderTurnControlService.test.ts | 42 +- .../ProviderTurnControlService.ts | 9 +- .../ProviderTurnStartService.ts | 3 + apps/server/src/peer/RemoteDelegation.test.ts | 18 + apps/server/src/peer/RemoteDelegation.ts | 7 + .../src/provider/orchestrationTargets.ts | 32 +- apps/server/src/pstack/PstackPack.test.ts | 6 + apps/server/src/pstack/PstackPack.ts | 11 + apps/server/src/pstack/composer.test.ts | 10 +- apps/server/src/pstack/composer.ts | 28 +- apps/server/src/pstack/pstackModels.test.ts | 185 +- apps/server/src/pstack/pstackModels.ts | 234 +- docs/user/pstack.md | 10 +- packages/contracts/src/orchestratorMcp.ts | 3 +- .../src/server/ProviderAdapter.ts | 2 + .../src/server/adapter.test.ts | 269 +- .../provider-opencode/src/server/adapter.ts | 16 +- .../src/server/v2/adapter.ts | 1 + packages/pstack/UPSTREAM.json | 1736 ++++- packages/pstack/src/check.test.ts | 1 + packages/pstack/src/check.ts | 2 +- packages/pstack/upstream/README.md | 2 +- packages/pstack/upstream/UPSTREAM.json | 4 +- .../pstack/upstream/docs/guide/04-design.md | 8 +- .../upstream/docs/guide/06-verify-and-ship.md | 6 +- .../upstream/docs/guide/07-overnight.md | 6 +- .../docs/guide/10-recipes-and-pitfalls.md | 6 +- packages/pstack/upstream/plugin.json | 2 +- .../pstack/upstream/skills/architect/SKILL.md | 2 +- .../pstack/upstream/skills/arena/SKILL.md | 6 +- .../skills/automate-maintainer/SKILL.md | 2 +- .../upstream/skills/automate-me/SKILL.md | 2 +- .../upstream/skills/automate-team/SKILL.md | 2 +- .../create-formal-verification-skill/SKILL.md | 2 +- .../upstream/skills/figure-it-out/SKILL.md | 2 +- packages/pstack/upstream/skills/how/SKILL.md | 2 +- .../upstream/skills/interrogate/SKILL.md | 64 +- .../references/code-quality-review.md | 47 - .../interrogate/references/lead-judgment.md | 39 - .../interrogate/references/reviewer-prompt.md | 62 - .../skills/interrogate/references/rubric.md | 77 - .../upstream/skills/ketchup-video/SKILL.md | 12 +- .../maintain-verification-skill/SKILL.md | 2 +- .../upstream/skills/no-comments/SKILL.md | 2 +- .../upstream/skills/poteto-help/SKILL.md | 6 +- .../skills/poteto-help/references/recipes.md | 4 +- .../upstream/skills/poteto-mode/SKILL.md | 11 +- .../poteto-mode/playbooks/autonomous-run.md | 2 +- .../poteto-mode/playbooks/autopilot-full.md | 10 +- .../poteto-mode/playbooks/autopilot-stack.md | 12 +- .../skills/poteto-mode/playbooks/babysit.md | 2 +- .../poteto-mode/playbooks/multi-phase-plan.md | 10 +- .../poteto-mode/playbooks/orchestrate.md | 123 +- .../poteto-mode/scripts/orch/orch.test.ts | 795 -- .../skills/poteto-mode/scripts/orch/orch.ts | 590 -- .../skills/poteto-mode/scripts/orch/store.ts | 1681 ---- .../skills/poteto-mode/scripts/package.json | 2 +- .../principle-explain-the-number/SKILL.md | 5 +- .../pstack/upstream/skills/recall/SKILL.md | 2 +- .../pstack/upstream/skills/reflect/SKILL.md | 2 +- .../upstream/skills/setup-pstack/SKILL.md | 2 +- .../skills/show-me-your-work/SKILL.md | 2 +- .../upstream/skills/statechart/SKILL.md | 82 +- .../skills/statechart/references/render.md | 93 + .../skills/statechart/scripts/backends/cfg.ts | 33 + .../statechart/scripts/backends/mermaid.ts | 201 + .../statechart/scripts/backends/plain-text.ts | 72 + .../statechart/scripts/backends/report.ts | 177 + .../skills/statechart/scripts/bug-states.mjs | 81 - .../skills/statechart/scripts/bun.lock | 17 + .../skills/statechart/scripts/chart-edges.mjs | 68 - .../skills/statechart/scripts/chart-parts.mjs | 21 - .../statechart/scripts/chart-to-tla.mjs | 205 - .../statechart/scripts/compare-charts.mjs | 74 - .../statechart/scripts/frontends/config.ts | 276 + .../statechart/scripts/frontends/dot.ts | 74 + .../statechart/scripts/frontends/marks.ts | 93 + .../statechart/scripts/frontends/messages.ts | 52 + .../statechart/scripts/frontends/spec.ts | 114 + .../statechart/scripts/frontends/trace.ts | 24 + .../statechart/scripts/frontends/value.ts | 166 + .../statechart/scripts/frontends/verdict.ts | 40 + .../statechart/scripts/frontends/view.ts | 119 + .../upstream/skills/statechart/scripts/ir.ts | 547 ++ .../skills/statechart/scripts/lib.mjs | 970 --- .../skills/statechart/scripts/map-view.mjs | 92 - .../skills/statechart/scripts/package.json | 11 + .../statechart/scripts/passes/annotate.ts | 128 + .../skills/statechart/scripts/passes/label.ts | 269 + .../skills/statechart/scripts/passes/lift.ts | 85 + .../statechart/scripts/passes/loop-check.ts | 68 + .../skills/statechart/scripts/passes/paths.ts | 60 + .../skills/statechart/scripts/passes/reach.ts | 135 + .../statechart/scripts/passes/region-check.ts | 97 + .../statechart/scripts/passes/regions.ts | 295 + .../skills/statechart/scripts/pipeline.ts | 76 + .../statechart/scripts/property-loop.mjs | 60 - .../skills/statechart/scripts/read-check.mjs | 79 - .../statechart/scripts/regions-chart.mjs | 307 - .../skills/statechart/scripts/render.mjs | 531 -- .../skills/statechart/scripts/rule-book.mjs | 84 - .../upstream/skills/statechart/scripts/run.ts | 481 ++ .../statechart/scripts/second-replay.mjs | 122 - .../skills/statechart/scripts/snapshot.mjs | 74 - .../skills/statechart/scripts/spec-text.mjs | 126 - .../skills/statechart/scripts/statechart | 29 + .../statechart/scripts/statechart-help.mjs | 350 - .../skills/statechart/scripts/statechart.mjs | 903 --- .../skills/statechart/scripts/statechart.ts | 164 + .../statechart/scripts/test/ConfigOracle.java | 63 + .../skills/statechart/scripts/test/band.mmd | 15 - .../statechart/scripts/test/chart.test.ts | 1210 +++ .../statechart/scripts/test/charts/Braces.mmd | 7 + .../scripts/test/charts/Escapes.mmd | 11 + .../statechart/scripts/test/charts/Phases.mmd | 29 + .../statechart/scripts/test/cli.test.ts | 1314 ++++ .../scripts/test/config-fuzz.test.ts | 263 + .../statechart/scripts/test/corpus.test.ts | 403 + .../scripts/test/corpus/Counter/ChartRun.out | 57 + .../scripts/test/corpus/Counter/Counter.cfg | 3 + .../scripts/test/corpus/Counter/Counter.tla | 10 + .../scripts/test/corpus/Counter/chart.mmd | 9 + .../scripts/test/corpus/Counter/graph.dot | 16 + .../scripts/test/corpus/Counter/report.md | 7 + .../scripts/test/corpus/Counter/sany.out | 2 + .../scripts/test/corpus/Counter/verdict-1.out | 49 + .../test/corpus/CounterAction/ChartRun.out | 60 + .../corpus/CounterAction/CounterAction.cfg | 4 + .../corpus/CounterAction/CounterAction.tla | 22 + .../test/corpus/CounterAction/chart.mmd | 26 + .../test/corpus/CounterAction/graph.dot | 19 + .../test/corpus/CounterAction/report.md | 20 + .../test/corpus/CounterAction/sany.out | 2 + .../test/corpus/CounterAction/verdict-1.exit | 1 + .../test/corpus/CounterAction/verdict-1.out | 56 + .../test/corpus/CounterAction/verdict-2.exit | 1 + .../test/corpus/CounterAction/verdict-2.out | 82 + .../test/corpus/CounterAction/verdict-3.out | 57 + .../test/corpus/CounterDeadlock/ChartRun.out | 59 + .../CounterDeadlock/CounterDeadlock.cfg | 3 + .../CounterDeadlock/CounterDeadlock.tla | 11 + .../test/corpus/CounterDeadlock/chart.mmd | 22 + .../test/corpus/CounterDeadlock/graph.dot | 16 + .../test/corpus/CounterDeadlock/report.md | 17 + .../test/corpus/CounterDeadlock/sany.out | 2 + .../corpus/CounterDeadlock/verdict-1.exit | 1 + .../test/corpus/CounterDeadlock/verdict-1.out | 56 + .../corpus/CounterDeadlock/verdict-2.exit | 1 + .../test/corpus/CounterDeadlock/verdict-2.out | 77 + .../test/corpus/CounterFan/ChartRun.out | 69 + .../test/corpus/CounterFan/CounterFan.cfg | 2 + .../test/corpus/CounterFan/CounterFan.tla | 11 + .../scripts/test/corpus/CounterFan/chart.mmd | 85 + .../scripts/test/corpus/CounterFan/graph.dot | 70 + .../scripts/test/corpus/CounterFan/report.md | 12 + .../scripts/test/corpus/CounterFan/sany.out | 2 + .../test/corpus/CounterFan/verdict-1.exit | 1 + .../test/corpus/CounterFan/verdict-1.out | 67 + .../test/corpus/CounterFan/verdict-2.out | 57 + .../test/corpus/CounterGuard/ChartRun.exit | 1 + .../test/corpus/CounterGuard/ChartRun.out | 85 + .../test/corpus/CounterGuard/CounterGuard.cfg | 6 + .../test/corpus/CounterGuard/CounterGuard.tla | 13 + .../test/corpus/CounterGuard/chart.mmd | 5 + .../test/corpus/CounterGuard/graph.dot | 10 + .../test/corpus/CounterGuard/report.md | 31 + .../scripts/test/corpus/CounterGuard/sany.out | 2 + .../test/corpus/CounterGuard/verdict-1.exit | 1 + .../test/corpus/CounterGuard/verdict-1.out | 64 + .../test/corpus/CounterGuard/verdict-2.exit | 1 + .../test/corpus/CounterGuard/verdict-2.out | 77 + .../test/corpus/CounterInitPast/ChartRun.out | 55 + .../CounterInitPast/CounterInitPast.cfg | 3 + .../CounterInitPast/CounterInitPast.tla | 10 + .../test/corpus/CounterInitPast/chart.mmd | 12 + .../test/corpus/CounterInitPast/graph.dot | 6 + .../test/corpus/CounterInitPast/report.md | 12 + .../test/corpus/CounterInitPast/sany.out | 2 + .../corpus/CounterInitPast/verdict-1.exit | 1 + .../test/corpus/CounterInitPast/verdict-1.out | 33 + .../test/corpus/CounterInitPast/verdict-2.out | 54 + .../corpus/CounterInitPastHolds/ChartRun.out | 54 + .../CounterInitPastHolds.cfg | 3 + .../CounterInitPastHolds.tla | 10 + .../corpus/CounterInitPastHolds/chart.mmd | 1 + .../corpus/CounterInitPastHolds/graph.dot | 6 + .../corpus/CounterInitPastHolds/report.md | 7 + .../test/corpus/CounterInitPastHolds/sany.out | 2 + .../corpus/CounterInitPastHolds/verdict-1.out | 46 + .../test/corpus/CounterInstance/ChartRun.out | 59 + .../CounterInstance/CounterInstance.cfg | 2 + .../CounterInstance/CounterInstance.tla | 10 + .../corpus/CounterInstance/CounterSteps.tla | 6 + .../test/corpus/CounterInstance/chart.mmd | 17 + .../test/corpus/CounterInstance/graph.dot | 15 + .../test/corpus/CounterInstance/report.md | 14 + .../test/corpus/CounterInstance/sany.out | 3 + .../corpus/CounterInstance/verdict-1.exit | 1 + .../test/corpus/CounterInstance/verdict-1.out | 78 + .../test/corpus/CounterJump/ChartRun.out | 59 + .../test/corpus/CounterJump/CounterJump.cfg | 3 + .../test/corpus/CounterJump/CounterJump.tla | 19 + .../scripts/test/corpus/CounterJump/chart.mmd | 26 + .../scripts/test/corpus/CounterJump/graph.dot | 22 + .../scripts/test/corpus/CounterJump/report.md | 12 + .../scripts/test/corpus/CounterJump/sany.out | 2 + .../test/corpus/CounterJump/verdict-1.exit | 1 + .../test/corpus/CounterJump/verdict-1.out | 58 + .../test/corpus/CounterJump/verdict-2.out | 57 + .../test/corpus/CounterLoop/ChartRun.out | 57 + .../test/corpus/CounterLoop/CounterLoop.cfg | 2 + .../test/corpus/CounterLoop/CounterLoop.tla | 11 + .../scripts/test/corpus/CounterLoop/chart.mmd | 17 + .../scripts/test/corpus/CounterLoop/graph.dot | 15 + .../scripts/test/corpus/CounterLoop/report.md | 14 + .../scripts/test/corpus/CounterLoop/sany.out | 2 + .../test/corpus/CounterLoop/verdict-1.exit | 1 + .../test/corpus/CounterLoop/verdict-1.out | 76 + .../test/corpus/CounterOrder/ChartRun.out | 59 + .../test/corpus/CounterOrder/CounterOrder.cfg | 3 + .../test/corpus/CounterOrder/CounterOrder.tla | 11 + .../test/corpus/CounterOrder/chart.mmd | 19 + .../test/corpus/CounterOrder/graph.dot | 10 + .../test/corpus/CounterOrder/report.md | 15 + .../scripts/test/corpus/CounterOrder/sany.out | 2 + .../test/corpus/CounterOrder/verdict-1.exit | 1 + .../test/corpus/CounterOrder/verdict-1.out | 56 + .../test/corpus/CounterOrder/verdict-2.exit | 1 + .../test/corpus/CounterOrder/verdict-2.out | 67 + .../test/corpus/CounterRules/ChartRun.out | 62 + .../test/corpus/CounterRules/CounterRules.cfg | 6 + .../test/corpus/CounterRules/CounterRules.tla | 24 + .../test/corpus/CounterRules/Moves.tla | 5 + .../test/corpus/CounterRules/chart.mmd | 22 + .../test/corpus/CounterRules/graph.dot | 10 + .../test/corpus/CounterRules/report.md | 23 + .../scripts/test/corpus/CounterRules/sany.out | 3 + .../test/corpus/CounterRules/verdict-1.exit | 1 + .../test/corpus/CounterRules/verdict-1.out | 58 + .../test/corpus/CounterRules/verdict-2.exit | 1 + .../test/corpus/CounterRules/verdict-2.out | 66 + .../test/corpus/CounterRules/verdict-3.exit | 1 + .../test/corpus/CounterRules/verdict-3.out | 74 + .../test/corpus/CounterRules/verdict-4.out | 59 + .../test/corpus/CounterStart/ChartRun.out | 58 + .../test/corpus/CounterStart/CounterStart.cfg | 3 + .../test/corpus/CounterStart/CounterStart.tla | 15 + .../test/corpus/CounterStart/chart.mmd | 18 + .../test/corpus/CounterStart/graph.dot | 13 + .../test/corpus/CounterStart/report.md | 11 + .../scripts/test/corpus/CounterStart/sany.out | 2 + .../test/corpus/CounterStart/verdict-1.exit | 1 + .../test/corpus/CounterStart/verdict-1.out | 33 + .../test/corpus/CounterStart/verdict-2.out | 57 + .../test/corpus/CounterStop/ChartRun.out | 57 + .../test/corpus/CounterStop/CounterStop.cfg | 3 + .../test/corpus/CounterStop/CounterStop.tla | 15 + .../scripts/test/corpus/CounterStop/chart.mmd | 7 + .../scripts/test/corpus/CounterStop/graph.dot | 13 + .../scripts/test/corpus/CounterStop/report.md | 6 + .../scripts/test/corpus/CounterStop/sany.out | 2 + .../test/corpus/CounterStop/verdict-1.out | 49 + .../test/corpus/CounterTie/ChartRun.out | 61 + .../test/corpus/CounterTie/CounterTie.cfg | 4 + .../test/corpus/CounterTie/CounterTie.tla | 14 + .../scripts/test/corpus/CounterTie/chart.mmd | 30 + .../scripts/test/corpus/CounterTie/graph.dot | 19 + .../scripts/test/corpus/CounterTie/report.md | 27 + .../scripts/test/corpus/CounterTie/sany.out | 2 + .../test/corpus/CounterTie/verdict-1.exit | 1 + .../test/corpus/CounterTie/verdict-1.out | 56 + .../test/corpus/CounterTie/verdict-2.exit | 1 + .../test/corpus/CounterTie/verdict-2.out | 64 + .../test/corpus/CounterTie/verdict-3.exit | 1 + .../test/corpus/CounterTie/verdict-3.out | 87 + .../test/corpus/CounterTie/verdict-4.out | 57 + .../corpus/CounterTieInGraph/ChartRun.out | 60 + .../CounterTieInGraph/CounterTieInGraph.cfg | 3 + .../CounterTieInGraph/CounterTieInGraph.tla | 12 + .../test/corpus/CounterTieInGraph/chart.mmd | 23 + .../test/corpus/CounterTieInGraph/graph.dot | 19 + .../test/corpus/CounterTieInGraph/report.md | 11 + .../test/corpus/CounterTieInGraph/sany.out | 2 + .../corpus/CounterTieInGraph/verdict-1.exit | 1 + .../corpus/CounterTieInGraph/verdict-1.out | 56 + .../corpus/CounterTieInGraph/verdict-2.out | 57 + .../test/corpus/CounterView/ChartRun.out | 61 + .../test/corpus/CounterView/CounterView.cfg | 4 + .../test/corpus/CounterView/CounterView.tla | 14 + .../scripts/test/corpus/CounterView/chart.mmd | 23 + .../scripts/test/corpus/CounterView/graph.dot | 19 + .../scripts/test/corpus/CounterView/report.md | 27 + .../scripts/test/corpus/CounterView/sany.out | 2 + .../test/corpus/CounterView/verdict-1.exit | 1 + .../test/corpus/CounterView/verdict-1.out | 56 + .../test/corpus/CounterView/verdict-2.exit | 1 + .../test/corpus/CounterView/verdict-2.out | 64 + .../test/corpus/CounterView/verdict-3.exit | 1 + .../test/corpus/CounterView/verdict-3.out | 87 + .../test/corpus/CounterView/verdict-4.out | 57 + .../scripts/test/corpus/CounterView/view.mjs | 1 + .../scripts/test/corpus/DieHard/ChartRun.out | 59 + .../scripts/test/corpus/DieHard/DieHard.cfg | 4 + .../scripts/test/corpus/DieHard/DieHard.tla | 38 + .../scripts/test/corpus/DieHard/chart.mmd | 92 + .../scripts/test/corpus/DieHard/graph.dot | 126 + .../scripts/test/corpus/DieHard/report.md | 17 + .../scripts/test/corpus/DieHard/sany.out | 2 + .../test/corpus/DieHard/verdict-1.exit | 1 + .../scripts/test/corpus/DieHard/verdict-1.out | 91 + .../scripts/test/corpus/DieHard/verdict-2.out | 57 + .../test/corpus/FirstRule/ChartRun.out | 61 + .../test/corpus/FirstRule/FirstRule.cfg | 6 + .../test/corpus/FirstRule/FirstRule.tla | 11 + .../scripts/test/corpus/FirstRule/chart.mmd | 22 + .../scripts/test/corpus/FirstRule/graph.dot | 16 + .../scripts/test/corpus/FirstRule/report.md | 25 + .../scripts/test/corpus/FirstRule/sany.out | 2 + .../test/corpus/FirstRule/verdict-1.exit | 1 + .../test/corpus/FirstRule/verdict-1.out | 56 + .../test/corpus/FirstRule/verdict-2.exit | 1 + .../test/corpus/FirstRule/verdict-2.out | 64 + .../test/corpus/FirstRule/verdict-3.exit | 1 + .../test/corpus/FirstRule/verdict-3.out | 64 + .../test/corpus/FirstRule/verdict-4.exit | 1 + .../test/corpus/FirstRule/verdict-4.out | 77 + .../test/corpus/HiddenClock/ChartRun.out | 57 + .../test/corpus/HiddenClock/HiddenClock.cfg | 4 + .../test/corpus/HiddenClock/HiddenClock.tla | 11 + .../scripts/test/corpus/HiddenClock/chart.mmd | 13 + .../scripts/test/corpus/HiddenClock/graph.dot | 12 + .../scripts/test/corpus/HiddenClock/report.md | 11 + .../scripts/test/corpus/HiddenClock/sany.out | 2 + .../test/corpus/HiddenClock/verdict-1.exit | 1 + .../test/corpus/HiddenClock/verdict-1.out | 72 + .../scripts/test/corpus/Login/ChartRun.out | 57 + .../scripts/test/corpus/Login/Login.cfg | 3 + .../scripts/test/corpus/Login/Login.tla | 62 + .../scripts/test/corpus/Login/chart.mmd | 27 + .../scripts/test/corpus/Login/graph.dot | 40 + .../scripts/test/corpus/Login/report.md | 7 + .../scripts/test/corpus/Login/sany.out | 1 + .../scripts/test/corpus/Login/verdict-1.out | 47 + .../scripts/test/corpus/Login/view.mjs | 1 + .../test/corpus/LoginGuarded/ChartRun.out | 57 + .../test/corpus/LoginGuarded/LoginGuarded.cfg | 2 + .../test/corpus/LoginGuarded/LoginGuarded.tla | 64 + .../test/corpus/LoginGuarded/chart.mmd | 28 + .../test/corpus/LoginGuarded/graph.dot | 37 + .../test/corpus/LoginGuarded/report.md | 8 + .../scripts/test/corpus/LoginGuarded/sany.out | 1 + .../test/corpus/LoginGuarded/verdict-1.out | 47 + .../scripts/test/corpus/LoginGuarded/view.mjs | 1 + .../test/corpus/LoginParallel/ChartRun.out | 60 + .../corpus/LoginParallel/LoginParallel.cfg | 3 + .../corpus/LoginParallel/LoginParallel.tla | 64 + .../test/corpus/LoginParallel/chart.mmd | 44 + .../test/corpus/LoginParallel/graph.dot | 58 + .../test/corpus/LoginParallel/report.md | 17 + .../test/corpus/LoginParallel/sany.out | 1 + .../test/corpus/LoginParallel/verdict-1.exit | 1 + .../test/corpus/LoginParallel/verdict-1.out | 83 + .../test/corpus/LoginParallel/verdict-2.out | 57 + .../test/corpus/LoginParallel/view.mjs | 1 + .../test/corpus/MediaPlayer/ChartRun.out | 60 + .../test/corpus/MediaPlayer/MediaPlayer.cfg | 3 + .../test/corpus/MediaPlayer/MediaPlayer.tla | 103 + .../scripts/test/corpus/MediaPlayer/chart.mmd | 43 + .../scripts/test/corpus/MediaPlayer/graph.dot | 93 + .../scripts/test/corpus/MediaPlayer/report.md | 14 + .../scripts/test/corpus/MediaPlayer/sany.out | 1 + .../test/corpus/MediaPlayer/verdict-1.exit | 1 + .../test/corpus/MediaPlayer/verdict-1.out | 59 + .../test/corpus/MediaPlayer/verdict-2.out | 72 + .../scripts/test/corpus/MediaPlayer/view.mjs | 3 + .../test/corpus/MediaPlayerFixed/ChartRun.out | 57 + .../MediaPlayerFixed/MediaPlayerFixed.cfg | 2 + .../MediaPlayerFixed/MediaPlayerFixed.tla | 100 + .../test/corpus/MediaPlayerFixed/chart.mmd | 31 + .../test/corpus/MediaPlayerFixed/graph.dot | 85 + .../test/corpus/MediaPlayerFixed/report.md | 9 + .../test/corpus/MediaPlayerFixed/sany.out | 1 + .../corpus/MediaPlayerFixed/verdict-1.out | 47 + .../test/corpus/MediaPlayerFixed/view.mjs | 3 + .../test/corpus/MediaPlayerProps/ChartRun.out | 59 + .../corpus/MediaPlayerProps/MediaPlayer.tla | 103 + .../MediaPlayerProps/MediaPlayerProps.cfg | 3 + .../MediaPlayerProps/MediaPlayerProps.tla | 6 + .../test/corpus/MediaPlayerProps/chart.mmd | 42 + .../test/corpus/MediaPlayerProps/graph.dot | 93 + .../test/corpus/MediaPlayerProps/report.md | 19 + .../test/corpus/MediaPlayerProps/sany.out | 2 + .../corpus/MediaPlayerProps/verdict-1.exit | 1 + .../corpus/MediaPlayerProps/verdict-1.out | 61 + .../corpus/MediaPlayerProps/verdict-2.out | 59 + .../corpus/MediaPlayerProps/verdict-3.out | 61 + .../corpus/MediaPlayerProps/verdict-4.exit | 1 + .../corpus/MediaPlayerProps/verdict-4.out | 66 + .../corpus/MediaPlayerProps/verdict-5.exit | 1 + .../corpus/MediaPlayerProps/verdict-5.out | 58 + .../corpus/MediaPlayerProps/verdict-6.out | 49 + .../test/corpus/MediaPlayerProps/view.mjs | 3 + .../test/corpus/PickWorker/ChartRun.out | 57 + .../test/corpus/PickWorker/PickWorker.cfg | 6 + .../test/corpus/PickWorker/PickWorker.tla | 11 + .../scripts/test/corpus/PickWorker/chart.mmd | 5 + .../scripts/test/corpus/PickWorker/graph.dot | 11 + .../scripts/test/corpus/PickWorker/report.md | 7 + .../scripts/test/corpus/PickWorker/sany.out | 5 + .../test/corpus/PickWorker/verdict-1.out | 70 + .../scripts/test/corpus/Shadow/ChartRun.out | 61 + .../scripts/test/corpus/Shadow/Shadow.cfg | 4 + .../scripts/test/corpus/Shadow/Shadow.tla | 11 + .../scripts/test/corpus/Shadow/ShadowLib.tla | 6 + .../scripts/test/corpus/Shadow/ShadowSub.tla | 3 + .../scripts/test/corpus/Shadow/chart.mmd | 9 + .../scripts/test/corpus/Shadow/graph.dot | 16 + .../scripts/test/corpus/Shadow/report.md | 8 + .../scripts/test/corpus/Shadow/sany.out | 4 + .../scripts/test/corpus/Shadow/verdict-1.out | 65 + .../test/corpus/SpacedNames/ChartRun.out | 59 + .../test/corpus/SpacedNames/SpacedNames.cfg | 2 + .../test/corpus/SpacedNames/SpacedNames.tla | 9 + .../scripts/test/corpus/SpacedNames/chart.mmd | 23 + .../scripts/test/corpus/SpacedNames/graph.dot | 17 + .../scripts/test/corpus/SpacedNames/report.md | 15 + .../scripts/test/corpus/SpacedNames/sany.out | 1 + .../test/corpus/SpacedNames/verdict-1.exit | 1 + .../test/corpus/SpacedNames/verdict-1.out | 65 + .../test/corpus/SpacedNames/verdict-2.exit | 1 + .../test/corpus/SpacedNames/verdict-2.out | 75 + .../scripts/test/corpus/TwoPhase/ChartRun.out | 69 + .../scripts/test/corpus/TwoPhase/TwoPhase.cfg | 3 + .../scripts/test/corpus/TwoPhase/TwoPhase.tla | 75 + .../scripts/test/corpus/TwoPhase/chart.mmd | 60 + .../scripts/test/corpus/TwoPhase/graph.dot | 297 + .../scripts/test/corpus/TwoPhase/report.md | 20 + .../scripts/test/corpus/TwoPhase/sany.out | 1 + .../test/corpus/TwoPhase/verdict-1.exit | 1 + .../test/corpus/TwoPhase/verdict-1.out | 95 + .../test/corpus/TwoPhase/verdict-2.out | 57 + .../scripts/test/corpus/TwoPhase/view.mjs | 1 + .../test/corpus/TwoPhaseFixed/ChartRun.out | 57 + .../corpus/TwoPhaseFixed/TwoPhaseFixed.cfg | 3 + .../corpus/TwoPhaseFixed/TwoPhaseFixed.tla | 75 + .../test/corpus/TwoPhaseFixed/chart.mmd | 37 + .../test/corpus/TwoPhaseFixed/graph.dot | 223 + .../test/corpus/TwoPhaseFixed/report.md | 11 + .../test/corpus/TwoPhaseFixed/sany.out | 1 + .../test/corpus/TwoPhaseFixed/verdict-1.out | 47 + .../test/corpus/TwoPhaseFixed/view.mjs | 1 + .../test/corpus/TypeGuard/ChartRun.exit | 1 + .../test/corpus/TypeGuard/ChartRun.out | 75 + .../test/corpus/TypeGuard/TypeGuard.cfg | 5 + .../test/corpus/TypeGuard/TypeGuard.tla | 11 + .../scripts/test/corpus/TypeGuard/chart.mmd | 18 + .../scripts/test/corpus/TypeGuard/graph.dot | 14 + .../scripts/test/corpus/TypeGuard/report.md | 29 + .../scripts/test/corpus/TypeGuard/sany.out | 2 + .../test/corpus/TypeGuard/verdict-1.exit | 1 + .../test/corpus/TypeGuard/verdict-1.out | 64 + .../test/corpus/TypeGuard/verdict-2.exit | 1 + .../test/corpus/TypeGuard/verdict-2.out | 75 + .../test/corpus/UserNoDeadlock/ChartRun.out | 58 + .../corpus/UserNoDeadlock/UserNoDeadlock.cfg | 2 + .../corpus/UserNoDeadlock/UserNoDeadlock.tla | 8 + .../test/corpus/UserNoDeadlock/chart.mmd | 22 + .../test/corpus/UserNoDeadlock/graph.dot | 16 + .../test/corpus/UserNoDeadlock/report.md | 15 + .../test/corpus/UserNoDeadlock/sany.out | 2 + .../test/corpus/UserNoDeadlock/verdict-1.exit | 1 + .../test/corpus/UserNoDeadlock/verdict-1.out | 56 + .../test/corpus/UserNoDeadlock/verdict-2.exit | 1 + .../test/corpus/UserNoDeadlock/verdict-2.out | 77 + .../scripts/test/differential.test.ts | 565 ++ .../statechart/scripts/test/grid/Grid.cfg | 2 + .../statechart/scripts/test/grid/Grid.tla | 9 + .../statechart/scripts/test/grid/sany.out | 2 + .../skills/statechart/scripts/test/java.ts | 63 + .../skills/statechart/scripts/test/labels.ts | 643 ++ .../statechart/scripts/test/messages.test.ts | 212 + .../statechart/scripts/test/pipeline.test.ts | 270 + .../statechart/scripts/test/plain-tlc.test.ts | 255 + .../statechart/scripts/test/regions.mmd | 62 - .../statechart/scripts/test/render.test.ts | 81 + .../scripts/test/statechart.test.mjs | 6854 ----------------- .../skills/statechart/scripts/test/sync.mmd | 19 - .../statechart/scripts/test/tlc-out.test.ts | 90 + .../skills/statechart/scripts/test/tlc.ts | 158 + .../skills/statechart/scripts/test/upload.mmd | 16 - .../statechart/scripts/tlc-to-chart-args.mjs | 52 - .../statechart/scripts/tlc-to-chart.mjs | 802 -- .../skills/statechart/scripts/tlc-tools.mjs | 69 - .../skills/statechart/scripts/tsconfig.json | 11 + .../skills/statechart/scripts/ways-in.mjs | 69 - .../skills/statechart/scripts/worker.ts | 35 + .../pstack/upstream/skills/swarm/SKILL.md | 2 +- packages/pstack/upstream/skills/why/SKILL.md | 2 +- 503 files changed, 22660 insertions(+), 16102 deletions(-) delete mode 100644 packages/pstack/upstream/skills/interrogate/references/code-quality-review.md delete mode 100644 packages/pstack/upstream/skills/interrogate/references/lead-judgment.md delete mode 100644 packages/pstack/upstream/skills/interrogate/references/reviewer-prompt.md delete mode 100644 packages/pstack/upstream/skills/interrogate/references/rubric.md delete mode 100644 packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.test.ts delete mode 100755 packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.ts delete mode 100644 packages/pstack/upstream/skills/poteto-mode/scripts/orch/store.ts create mode 100644 packages/pstack/upstream/skills/statechart/references/render.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/backends/cfg.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/backends/mermaid.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/backends/plain-text.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/backends/report.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/bug-states.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/bun.lock delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/chart-edges.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/chart-parts.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/chart-to-tla.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/compare-charts.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/config.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/dot.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/marks.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/messages.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/spec.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/trace.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/value.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/verdict.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/frontends/view.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/ir.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/lib.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/map-view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/package.json create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/annotate.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/label.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/lift.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/loop-check.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/paths.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/reach.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/region-check.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/passes/regions.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/pipeline.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/property-loop.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/read-check.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/regions-chart.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/render.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/rule-book.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/run.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/second-replay.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/snapshot.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/spec-text.mjs create mode 100755 packages/pstack/upstream/skills/statechart/scripts/statechart delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/statechart-help.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/statechart.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/statechart.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/ConfigOracle.java delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/band.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/chart.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/charts/Braces.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/charts/Escapes.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/charts/Phases.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/cli.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/config-fuzz.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/Counter.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/Counter.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Counter/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/CounterAction.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/CounterAction.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterAction/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/CounterFan.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/CounterFan.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterFan/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInitPastHolds/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/CounterSteps.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/CounterJump.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/CounterJump.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterJump/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/CounterRules.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/CounterRules.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/Moves.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-3.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterRules/verdict-4.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/CounterStart.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/CounterStart.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStart/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/CounterStop.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/CounterStop.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterStop/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/CounterTie.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/CounterTie.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-3.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTie/verdict-4.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/CounterView.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/CounterView.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-3.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/verdict-4.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/CounterView/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/DieHard.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/DieHard.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/DieHard/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/FirstRule.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/FirstRule.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-3.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-4.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/FirstRule/verdict-4.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/Login.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/Login.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Login/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginGuarded/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/LoginParallel/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayer/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerFixed/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayer.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-3.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-6.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/MediaPlayerProps/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/PickWorker.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/PickWorker.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/PickWorker/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/Shadow.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/Shadow.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/ShadowLib.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/ShadowSub.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/Shadow/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhase/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TwoPhaseFixed/view.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/ChartRun.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/chart.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/graph.dot create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/report.md create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/sany.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.exit create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.out create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/differential.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/grid/Grid.cfg create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/grid/Grid.tla create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/grid/sany.out create mode 100755 packages/pstack/upstream/skills/statechart/scripts/test/java.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/labels.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/messages.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/pipeline.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/plain-tlc.test.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/regions.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/render.test.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/statechart.test.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/sync.mmd create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/tlc-out.test.ts create mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/tlc.ts delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/test/upload.mmd delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/tlc-to-chart-args.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/tlc-to-chart.mjs delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/tlc-tools.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/tsconfig.json delete mode 100644 packages/pstack/upstream/skills/statechart/scripts/ways-in.mjs create mode 100644 packages/pstack/upstream/skills/statechart/scripts/worker.ts diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 95c37cc55afe1..9486d166f4fbc 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -1062,6 +1062,7 @@ describe("OrchestratorMcpService provider resolution", () => { readonly driver: ProviderDriverKind; readonly model?: string; readonly enabled?: boolean; + readonly optionValues?: ReadonlyArray; }): ServerProvider => ({ instanceId: input.instanceId, driver: input.driver, @@ -1075,7 +1076,26 @@ describe("OrchestratorMcpService provider resolution", () => { models: input.model === undefined ? [] - : [{ slug: input.model, name: input.model, isCustom: false, capabilities: null }], + : [ + { + slug: input.model, + name: input.model, + isCustom: false, + capabilities: + input.optionValues === undefined + ? null + : { + optionDescriptors: [ + { + id: "reasoningEffort", + label: "Effort", + type: "select", + options: input.optionValues.map((id) => ({ id, label: id })), + }, + ], + }, + }, + ], slashCommands: [], skills: [], }); @@ -1629,6 +1649,9 @@ describe("OrchestratorMcpService provider resolution", () => { "unavailable", "expired", "unknown quota", + "unregistered adapters", + "only cooling adapter registered", + "only healthy adapter registered", ] as const)("pool delegation availability: %s", (scenario) => Effect.gen(function* () { const poolInstanceId = ProviderInstanceId.make("pool_codex"); @@ -1667,7 +1690,12 @@ describe("OrchestratorMcpService provider resolution", () => { }, }, ]; - const available = ["healthy capacity", "expired", "unknown quota"].includes(scenario); + const available = [ + "healthy capacity", + "expired", + "unknown quota", + "only healthy adapter registered", + ].includes(scenario); const dependencies = Layer.mergeAll( NodeServices.layer, Layer.mock(ThreadManagementService.ThreadManagementService)({ @@ -1692,7 +1720,15 @@ describe("OrchestratorMcpService provider resolution", () => { refreshInstance: () => Effect.succeed(members.map(({ pool: _pool, ...member }) => member)), }), - adapterRegistryLayer([codexInstanceId, workInstanceId]), + adapterRegistryLayer( + scenario === "unregistered adapters" + ? [] + : scenario === "only cooling adapter registered" + ? [codexInstanceId] + : scenario === "only healthy adapter registered" + ? [workInstanceId] + : [codexInstanceId, workInstanceId], + ), Layer.mock(ProjectService.ProjectService)({}), Layer.mock(SecretRequests.SecretRequests)({}), Layer.mock(RemoteDelegation.RemoteDelegation)({}), @@ -2350,6 +2386,38 @@ describe("OrchestratorMcpService provider resolution", () => { selectedInstanceId: codexInstanceId, candidateDriver: driver, }, + { + name: "requested-options-on-peer", + inheritedEnabled: true, + peerEnabled: true, + explicit: false, + selectedInstanceId: codexAltInstanceId, + candidateDriver: driver, + }, + { + name: "requested-model-on-peer", + inheritedEnabled: true, + peerEnabled: true, + explicit: false, + selectedInstanceId: codexAltInstanceId, + candidateDriver: driver, + }, + { + name: "requested-model-prefers-advertised-peer", + inheritedEnabled: true, + peerEnabled: true, + explicit: false, + selectedInstanceId: codexAltInstanceId, + candidateDriver: driver, + }, + { + name: "requested-model-unknown-catalog-fallback", + inheritedEnabled: true, + peerEnabled: false, + explicit: false, + selectedInstanceId: codexInstanceId, + candidateDriver: driver, + }, { name: "unavailable-inherited-falls-back-to-healthy-peer", inheritedEnabled: false, @@ -2444,17 +2512,30 @@ describe("OrchestratorMcpService provider resolution", () => { }), ServerSettings.layerTest(), providerRegistryLayer([ - providerSnapshot({ - instanceId: codexInstanceId, - driver, - model: "gpt-5.4", - enabled: testCase.inheritedEnabled, - }), + { + ...providerSnapshot({ + instanceId: codexInstanceId, + driver, + model: "gpt-5.4", + enabled: testCase.inheritedEnabled, + ...(testCase.name === "requested-options-on-peer" + ? { optionValues: ["medium", "high"] } + : {}), + }), + ...(testCase.name === "requested-model-prefers-advertised-peer" || + testCase.name === "requested-model-unknown-catalog-fallback" + ? { models: [] } + : {}), + }, providerSnapshot({ instanceId: codexAltInstanceId, driver: testCase.candidateDriver, - model: "codex-alt-model", + model: + testCase.name === "requested-options-on-peer" ? "gpt-5.4" : "codex-alt-model", enabled: testCase.peerEnabled, + ...(testCase.name === "requested-options-on-peer" + ? { optionValues: ["high", "max"] } + : {}), }), ]), adapterRegistryLayer( @@ -2480,7 +2561,17 @@ describe("OrchestratorMcpService provider resolution", () => { ? codexInstanceId : testCase.selectedInstanceId, } as const) - : ({ driverKind: testCase.candidateDriver } as const); + : ({ + driverKind: testCase.candidateDriver, + ...(testCase.name === "requested-model-on-peer" || + testCase.name === "requested-model-prefers-advertised-peer" || + testCase.name === "requested-model-unknown-catalog-fallback" + ? { model: "codex-alt-model" } + : {}), + ...(testCase.name === "requested-options-on-peer" + ? { model: "gpt-5.4", options: [{ id: "reasoningEffort", value: "max" }] } + : {}), + } as const); if (testCase.selectedInstanceId === null) { const error = yield* service .delegateTask(scope, { @@ -2558,6 +2649,12 @@ describe("OrchestratorMcpService provider resolution", () => { ); if (testCase.name === "healthy-inherited") { assert.deepEqual(request.modelSelection, parentModelSelection, testCase.name); + } else if (testCase.name === "requested-options-on-peer") { + assert.deepEqual(request.modelSelection, { + instanceId: codexAltInstanceId, + model: "gpt-5.4", + options: [{ id: "reasoningEffort", value: "max" }], + }); } else { assert.equal(request.modelSelection.model, "codex-alt-model", testCase.name); } @@ -3001,11 +3098,18 @@ describe("OrchestratorMcpService provider resolution", () => { }), ); - it.effect( - "pstack targets set the same effort under any parent and leave speed to inheritance", - () => + it.effect.each([ + { driver: "codex", model: "gpt-6-astra", effortId: "reasoningEffort" }, + { driver: "pi", model: "openai/gpt-6-astra", effortId: "thinking" }, + { driver: "opencode2", model: "openai/gpt-6-astra", effortId: "variant" }, + { driver: "acpRegistry", model: "gpt-6-astra", effortId: "reasoningEffort" }, + ])( + "pstack targets dispatch advertised $driver models and effort under either parent", + ({ driver, model, effortId }) => Effect.gen(function* () { const claudeId = ProviderInstanceId.make("claudeAgent"); + const targetId = ProviderInstanceId.make(driver === "codex" ? "codex" : `${driver}-work`); + const inheritedSpeed = driver === "codex" ? [{ id: "serviceTier", value: "priority" }] : []; const providers: ServerProvider[] = [ { ...providerSnapshot({ @@ -3016,30 +3120,34 @@ describe("OrchestratorMcpService provider resolution", () => { }, { ...providerSnapshot({ - instanceId: codexInstanceId, - driver: ProviderDriverKind.make("codex"), + instanceId: targetId, + driver: ProviderDriverKind.make(driver), }), models: [ { - slug: "gpt-6-astra", + slug: model, name: "Astra", isCustom: false, capabilities: { optionDescriptors: [ { - id: "reasoningEffort", + id: effortId, label: "Effort", type: "select", currentValue: "medium", options: ["low", "medium", "high", "xhigh"].map((id) => ({ id, label: id })), }, - { - id: "serviceTier", - label: "Tier", - type: "select", - currentValue: "default", - options: ["default", "priority"].map((id) => ({ id, label: id })), - }, + ...(driver === "codex" + ? [ + { + id: "serviceTier", + label: "Tier", + type: "select" as const, + currentValue: "default", + options: ["default", "priority"].map((id) => ({ id, label: id })), + }, + ] + : []), ], }, }, @@ -3050,13 +3158,19 @@ describe("OrchestratorMcpService provider resolution", () => { settings: DEFAULT_SERVER_SETTINGS, projectId, nowMs: 0, - file: parseModelsFile( - "architect runners: claudeAgent/claude-opus-5-5 effort=xhigh, codex/gpt-6-astra reasoningEffort=high", - ), - defaults: [{ label: "architect runners", defaultValue: "auto" }], + file: + driver === "codex" + ? parseModelsFile( + "architect runners: claudeAgent/claude-opus-5-5 effort=xhigh, codex/gpt-6-astra reasoningEffort=high", + ) + : undefined, + defaults: [ + { label: "architect runners", defaultValue: "claude-opus-5-5-xhigh, gpt-6-astra-high" }, + ], providers, capableInstanceIds: new Set(providers.map((provider) => provider.instanceId)), }); + assert.deepEqual(resolved.diagnostics, []); const results: unknown[][] = []; for (const inherited of [ { @@ -3068,12 +3182,9 @@ describe("OrchestratorMcpService provider resolution", () => { ], }, { - instanceId: codexInstanceId, - model: "gpt-6-astra", - options: [ - { id: "reasoningEffort", value: "xhigh" }, - { id: "serviceTier", value: "priority" }, - ], + instanceId: targetId, + model, + options: [{ id: effortId, value: "xhigh" }, ...inheritedSpeed], }, ]) { const selections: unknown[] = []; @@ -3144,9 +3255,9 @@ describe("OrchestratorMcpService provider resolution", () => { ], }, { - instanceId: codexInstanceId, - model: "gpt-6-astra", - options: [{ id: "reasoningEffort", value: "high" }], + instanceId: targetId, + model, + options: [{ id: effortId, value: "high" }], }, ], [ @@ -3156,12 +3267,9 @@ describe("OrchestratorMcpService provider resolution", () => { options: [{ id: "effort", value: "xhigh" }], }, { - instanceId: codexInstanceId, - model: "gpt-6-astra", - options: [ - { id: "reasoningEffort", value: "high" }, - { id: "serviceTier", value: "priority" }, - ], + instanceId: targetId, + model, + options: [{ id: effortId, value: "high" }, ...inheritedSpeed], }, ], ]); diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 2e6f9fa79cd21..c455a477d1d86 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -2,6 +2,7 @@ import { providerConstraints, delegationProviderConstraints, invalidOptionSelections, + isCapabilityModelVisible, readOrchestrationProviders, } from "../provider/orchestrationTargets.ts"; import { @@ -306,7 +307,7 @@ function listCapabilityModels(input: { }) { const rank = new Map(input.preferences?.modelOrder.map((slug, index) => [slug, index])); const visible = input.provider.models - .filter((model) => model.isCustom || !input.preferences?.hiddenModels.includes(model.slug)) + .filter((model) => isCapabilityModelVisible(model, input.preferences)) .toSorted((a, b) => (rank.get(a.slug) ?? rank.size) - (rank.get(b.slug) ?? rank.size)); const { instanceId, driver, displayName } = input.provider; const providerText = `${instanceId} ${driver} ${displayName ?? ""}`.toLowerCase(); @@ -1249,9 +1250,18 @@ const make = Effect.gen(function* () { }): Effect.fn.Return { const requestedInstanceId = input.target?.providerInstanceId; const requestedDriver = input.target?.driverKind; + const inheritedSelection = input.parent.thread.modelSelection; + const requestedModel = input.target?.model; + const requestedOptions = input.target?.options; + const modelFor = (provider: ServerProvider) => + requestedModel ?? + (provider.instanceId === inheritedSelection.instanceId + ? inheritedSelection.model + : provider.models[0]?.slug); const orchestrationCapableInstanceIds = yield* loadOrchestrationCapableInstanceIds(); const availability = { providers: input.providers, + capableInstanceIds: orchestrationCapableInstanceIds, settings: yield* serverSettings.getSettings.pipe( Effect.mapError(() => failure("orchestration_error", "Unable to read server settings.")), ), @@ -1272,6 +1282,7 @@ const make = Effect.gen(function* () { const candidates = input.providers.filter( (provider) => provider.driver === requestedDriver && + provider.pool?.role !== "member" && orchestrationCapableInstanceIds.has(provider.instanceId), ); if (candidates.length === 0) { @@ -1294,6 +1305,14 @@ const make = Effect.gen(function* () { ...preferred, ...healthyCandidates.filter((candidate) => !preferred.includes(candidate)), ]; + if (requestedModel !== undefined) { + orderedCandidates.sort( + (left, right) => + Number(right.models.some((model) => model.slug === requestedModel)) - + Number(left.models.some((model) => model.slug === requestedModel)), + ); + } + let firstEnabled: ServerProvider | undefined; for (const candidate of orderedCandidates) { const enabledForProject = input.skipProjectEnablementFilter === true || @@ -1301,11 +1320,25 @@ const make = Effect.gen(function* () { projectId: input.parent.thread.projectId, instanceId: candidate.instanceId, })); - if (enabledForProject) { - instanceId = candidate.instanceId; - break; - } + if (!enabledForProject) continue; + firstEnabled ??= candidate; + const model = modelFor(candidate); + const advertised = candidate.models.find((entry) => entry.slug === model); + if ( + model === undefined || + (requestedModel !== undefined && + candidate.models.length > 0 && + advertised === undefined) || + (requestedOptions !== undefined && + invalidOptionSelections(requestedOptions, advertised?.capabilities?.optionDescriptors) + .length > 0) + ) + continue; + instanceId = candidate.instanceId; + break; } + // Keep the specific model or option error when every enabled instance rejects the target. + instanceId ??= firstEnabled?.instanceId; if (instanceId === undefined) { return yield* failure( "provider_unavailable", @@ -1338,13 +1371,7 @@ const make = Effect.gen(function* () { ); } - const inheritedSelection = input.parent.thread.modelSelection; - const requestedModel = input.target?.model; - const model = - requestedModel ?? - (instanceId === inheritedSelection.instanceId - ? inheritedSelection.model - : provider.models[0]?.slug); + const model = modelFor(provider); if (model === undefined) { return yield* failure( "model_unavailable", @@ -1362,7 +1389,6 @@ const make = Effect.gen(function* () { ); } - const requestedOptions = input.target?.options; if (requestedOptions !== undefined) { const descriptors = provider.models.find((candidate) => candidate.slug === model) ?.capabilities?.optionDescriptors; @@ -2124,6 +2150,7 @@ const make = Effect.gen(function* () { ); const availability = { providers, + capableInstanceIds: orchestrationCapableInstanceIds, settings, projectId: parent?.thread.projectId ?? null, nowMs: yield* Clock.currentTimeMillis, diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts index c18fb4f094465..637a482534fc0 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts @@ -662,6 +662,7 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { ], { external: true }, ).pipe(Scope.provide(scope)); + assert.isFalse(runtime.supportsT3Tools); yield* runtime.startTurn(turnInput(thread)); yield* Scope.close(scope, Exit.void); }), @@ -2461,6 +2462,7 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { } // A transient registration failure changes availability, not the adapter's supported features. assert.isTrue(runtime.providerSession.capabilities.tools.supportsMcpTools); + assert.isTrue(runtime.supportsT3Tools); }).pipe(Effect.scoped), ); diff --git a/apps/server/src/orchestration-v2/Orchestrator.ts b/apps/server/src/orchestration-v2/Orchestrator.ts index ab0fa6637a3ea..cd9e63ced1459 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.ts @@ -1034,6 +1034,13 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio ) { return { type: "unrouted", selection: input.selection }; } + const registeredInstanceIds = new Set(yield* providerAdapters.list()); + const excluded = new Set(input.excluded); + for (const provider of providers) { + if (provider.pool?.role !== "pool" && !registeredInstanceIds.has(provider.instanceId)) { + excluded.add(provider.instanceId); + } + } const settings = yield* serverSettings.getSettings.pipe(Effect.orDie); const nowMs = DateTime.toEpochMillis(yield* DateTime.now); return routeModelSelection({ @@ -1043,7 +1050,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio projectId: input.projectId, threadId: input.threadId, current: input.current, - ...(input.excluded === undefined ? {} : { excluded: input.excluded }), + excluded, nowMs, }); }); diff --git a/apps/server/src/orchestration-v2/ProviderPoolFailover.integration.test.ts b/apps/server/src/orchestration-v2/ProviderPoolFailover.integration.test.ts index f650c3e1443cf..989262b270685 100644 --- a/apps/server/src/orchestration-v2/ProviderPoolFailover.integration.test.ts +++ b/apps/server/src/orchestration-v2/ProviderPoolFailover.integration.test.ts @@ -277,11 +277,12 @@ const makeCatalog = ( const makeRegistry = ( keys: { readonly first: string; readonly second: string }, state: Ref.Ref, + missingInstanceId?: ProviderInstanceId, ) => { const instances = [ makeInstance(makePoolAdapter(first, state), keys.first), makeInstance(makePoolAdapter(second, state), keys.second), - ]; + ].filter((instance) => instance.instanceId !== missingInstanceId); return ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( Layer.provide( Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { @@ -601,50 +602,67 @@ it.live("fails the run with the pool's earliest reset when every account is cool ), ); -it.live("routes a selection naming a cooling account to its healthy sibling", () => - Effect.scoped( - Effect.gen(function* () { - const cwd = yield* checkpointWorkspace("pool-failover-cooling-member"); - const threadId = ThreadId.make("thread:pool-failover-cooling-member"); - const state = yield* Ref.make({ - failNextStart: null, - holdTurn: null, - started: [], - }); - const providers = yield* Ref.make>([ - makeProvider( - first, - "Claude", - "claude:home:first", - DateTime.formatIso( - DateTime.makeUnsafe(DateTime.toEpochMillis(yield* DateTime.now) + minutes(10)), +it.live.each(["cooling", "unregistered"] as const)( + "routes a selection naming a %s account to its healthy sibling", + (scenario) => + Effect.scoped( + Effect.gen(function* () { + const cwd = yield* checkpointWorkspace("pool-failover-cooling-member"); + const threadId = ThreadId.make("thread:pool-failover-cooling-member"); + const state = yield* Ref.make({ + failNextStart: null, + holdTurn: null, + started: [], + }); + const providers = yield* Ref.make>([ + makeProvider( + first, + "Claude", + "claude:home:first", + scenario === "cooling" + ? DateTime.formatIso( + DateTime.makeUnsafe(DateTime.toEpochMillis(yield* DateTime.now) + minutes(10)), + ) + : undefined, ), - ), - makeProvider(second, "Claude - Work", "claude:home:second"), - ]); - const projection = yield* Effect.gen(function* () { - const orchestrator = yield* Orchestrator.OrchestratorV2; - const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; - yield* createThread(threadId, cwd, first); - const completed = yield* awaitRunStatus(threadId, "completed"); - yield* sendMessage(threadId, "hello", first); - yield* Fiber.join(completed); - yield* worker.drain(); - return yield* orchestrator.getThreadProjection(threadId); - }).pipe( - Effect.provide( - ProviderReplayHarness.layerWithRegistry( - { name: "pool-failover-cooling-member" }, - makeRegistry({ first: "claude:home:first", second: "claude:home:second" }, state), - { settings, poolCatalog: makeCatalog(providers) }, + { + ...makeProvider(second, "Claude - Work", "claude:home:second"), + usageLimits: { + checkedAt: "2026-10-01T00:00:00.000Z", + windows: [{ id: "session", label: "Session", usedPercent: 50 }], + }, + }, + ]); + const projection = yield* Effect.gen(function* () { + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; + yield* createThread(threadId, cwd, first); + assert.equal( + (yield* orchestrator.getThreadProjection(threadId)).thread.providerInstanceId, + second, + ); + const completed = yield* awaitRunStatus(threadId, "completed"); + yield* sendMessage(threadId, "hello", first); + yield* Fiber.join(completed); + yield* worker.drain(); + return yield* orchestrator.getThreadProjection(threadId); + }).pipe( + Effect.provide( + ProviderReplayHarness.layerWithRegistry( + { name: "pool-failover-cooling-member" }, + makeRegistry( + { first: "claude:home:first", second: "claude:home:second" }, + state, + scenario === "unregistered" ? first : undefined, + ), + { settings, poolCatalog: makeCatalog(providers) }, + ), ), - ), - ); - assert.equal(projection.thread.providerInstanceId, second); - assert.equal(projection.runs[0]?.providerInstanceId, second); - assert.equal(projection.runs[0]?.status, "completed"); - }), - ), + ); + assert.equal(projection.runs[0]?.providerInstanceId, second); + assert.equal(projection.runs[0]?.status, "completed"); + }), + ), ); it.live("holds queued work when every pooled account becomes signed out", () => diff --git a/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts b/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts index aee233e37bb89..ad38592529f4d 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts @@ -452,6 +452,36 @@ it.effect.each([ }; const received: string[] = []; let threadReads = 0; + const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { + instanceId: providerInstanceId, + driver, + providerSessionId, + providerSession: { + id: providerSessionId, + driver, + providerInstanceId, + status: "running", + cwd: "/workspace", + model: modelSelection.model, + capabilities: CodexProviderCapabilitiesV2, + createdAt: now, + updatedAt: now, + lastError: null, + }, + events: Stream.empty, + ensureThread: () => Effect.die("unused ensureThread"), + resumeThread: () => Effect.die("unused resumeThread"), + startTurn: () => Effect.die("unused startTurn"), + steerTurn: (input) => + Effect.sync(() => { + received.push(input.message.text); + }), + interruptTurn: () => Effect.die("unused interruptTurn"), + respondToRuntimeRequest: () => Effect.die("unused respondToRuntimeRequest"), + readThreadSnapshot: () => Effect.die("unused readThreadSnapshot"), + rollbackThread: () => Effect.die("unused rollbackThread"), + forkThread: () => Effect.die("unused forkThread"), + }; const layers = ProviderTurnControlService.layer.pipe( Layer.provide(Layer.mock(ProjectKnowledge.ProjectKnowledge)({})), Layer.provide( @@ -536,17 +566,7 @@ it.effect.each([ }), }), Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ - get: () => - Effect.succeedSome({ - steerTurn: ( - input: Parameters< - ProviderAdapter.ProviderAdapterV2SessionRuntime["steerTurn"] - >[0], - ) => - Effect.sync(() => { - received.push(input.message.text); - }), - } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime), + get: () => Effect.succeedSome(runtime), }), ), ), diff --git a/apps/server/src/orchestration-v2/ProviderTurnControlService.ts b/apps/server/src/orchestration-v2/ProviderTurnControlService.ts index 8e571b63bb6ce..df9d93f93ca7a 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnControlService.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnControlService.ts @@ -447,7 +447,14 @@ export const layer: Layer.Layer< ) { const thread = yield* projections.getThread(input.threadId); composer = yield* pstack.expandComposer( - { ...composer, projectId: thread.projectId }, + { + ...composer, + projectId: thread.projectId, + parentModelSelection: thread.modelSelection, + supportsOrchestrationTools: + loaded.session.value.supportsT3Tools ?? + loaded.session.value.providerSession.capabilities.tools.supportsMcpTools, + }, message, ); } diff --git a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts index 836a45b1dda3b..1998c37491d7b 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts @@ -1505,6 +1505,9 @@ export const layer: Layer.Layer< const pstackComposer = yield* pstack.expandComposer( { projectId: projection.thread.projectId, + parentModelSelection: projection.thread.modelSelection, + supportsOrchestrationTools: + session.supportsT3Tools ?? session.providerSession.capabilities.tools.supportsMcpTools, text: message.text, records: message.context?.records ?? [], }, diff --git a/apps/server/src/peer/RemoteDelegation.test.ts b/apps/server/src/peer/RemoteDelegation.test.ts index 38e6ad3a39cff..473df0fa004f4 100644 --- a/apps/server/src/peer/RemoteDelegation.test.ts +++ b/apps/server/src/peer/RemoteDelegation.test.ts @@ -1138,6 +1138,24 @@ it.effect("a task named only by environment inherits the parent's model, within yield* linkTo(a.links, b, "auto-accept-edits"); yield* seedParent(a); + for (const target of [ + { environmentId: box.environmentId, driverKind: "claudeAgent" }, + { + environmentId: box.environmentId, + driverKind: "claudeAgent", + providerInstanceId: instanceId, + model: modelSelection.model, + }, + ]) { + const rejected = yield* a.call("delegate_task", { + task: "Review with the requested harness.", + target, + }); + expect(rejected.isError).toBe(true); + expect(JSON.stringify(rejected.content)).toContain("orchestrator_capabilities"); + expect(yield* Ref.get(thread.launchCount)).toBe(0); + } + const result = yield* a.call("delegate_task", { task: "Run the suite on the box.", target: { environmentId: box.environmentId }, diff --git a/apps/server/src/peer/RemoteDelegation.ts b/apps/server/src/peer/RemoteDelegation.ts index 03a8b12cc4194..c1ad103295fb3 100644 --- a/apps/server/src/peer/RemoteDelegation.ts +++ b/apps/server/src/peer/RemoteDelegation.ts @@ -265,6 +265,13 @@ const make = Effect.gen(function* () { yield* followOne(scope.thread.threadId, recordedTask.id).pipe(Effect.forkIn(followers)); return { taskId: recordedTask.id }; } + if (target.driverKind !== undefined) { + return yield* new OrchestratorMcpFailure({ + code: "target_required", + message: + "Linked delegation does not resolve target.driverKind. Omit it and pass target.providerInstanceId and target.model from orchestrator_capabilities with this environmentId.", + }); + } // The provider and model inherit as they do for a task here. Another // provider there has no model to inherit, so the caller names one. const inherited = parent.thread.modelSelection; diff --git a/apps/server/src/provider/orchestrationTargets.ts b/apps/server/src/provider/orchestrationTargets.ts index 8ae91490deac0..8832964aac089 100644 --- a/apps/server/src/provider/orchestrationTargets.ts +++ b/apps/server/src/provider/orchestrationTargets.ts @@ -3,7 +3,9 @@ import { findProviderPool, providerLimitedUntilMs, type ProjectId, + type ProviderInstanceId, type ServerProvider, + type ServerProviderModel, type ProviderOptionDescriptor, type ProviderOptionSelection, } from "@t3tools/contracts"; @@ -12,6 +14,13 @@ import * as Effect from "effect/Effect"; import * as ProviderRegistry from "./ProviderRegistry.ts"; import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; +export function isCapabilityModelVisible( + model: Pick, + preferences: { readonly hiddenModels: ReadonlyArray } | undefined, +): boolean { + return model.isCustom || !preferences?.hiddenModels.includes(model.slug); +} + export function providerConstraints( provider: ServerProvider | undefined, supportsOrchestrationV2: boolean, @@ -44,6 +53,7 @@ export function delegationProviderConstraints( supportsOrchestrationV2: boolean, input: { readonly providers: ReadonlyArray; + readonly capableInstanceIds?: ReadonlySet; readonly settings: Parameters[0]["settings"]; readonly projectId: ProjectId | null; readonly nowMs: number; @@ -58,7 +68,11 @@ export function delegationProviderConstraints( return constraints; } const members = poolMemberHealth({ ...input, members: owner.members }); - const eligible = members.filter((member) => member.eligible); + const eligible = members.filter( + (member) => + member.eligible && + (input.capableInstanceIds === undefined || input.capableInstanceIds.has(member.instanceId)), + ); if (eligible.length === 0) { constraints.push("No pool member is available for this project."); } else if (eligible.every((member) => member.limitedUntilMs !== null)) { @@ -112,13 +126,15 @@ export const readOrchestrationProviders = Effect.fn("OrchestrationTargets.readPr registry.getPublishedProviders, adapters.list(), ]); - // A pool has no adapter of its own; routing picks a member's at dispatch. - const capableInstanceIds = new Set([ - ...instanceIds, - ...providers - .filter((provider) => provider.pool?.role === "pool") - .map((provider) => provider.instanceId), - ]); + const capableInstanceIds = new Set(instanceIds); + // A pool delegates through a registered member adapter, never its virtual id. + for (const provider of providers) { + if ( + provider.pool?.role === "pool" && + provider.pool.memberInstanceIds.some((instanceId) => instanceIds.includes(instanceId)) + ) + capableInstanceIds.add(provider.instanceId); + } return { providers, capableInstanceIds }; }, ); diff --git a/apps/server/src/pstack/PstackPack.test.ts b/apps/server/src/pstack/PstackPack.test.ts index 19045124d9ee3..6c2786ed22119 100644 --- a/apps/server/src/pstack/PstackPack.test.ts +++ b/apps/server/src/pstack/PstackPack.test.ts @@ -284,6 +284,12 @@ it.layer(NodeServices.layer)("PstackPack", (it) => { "- architect runners (panel, one child per seat): [1] inherit [2] inherit", ); assert.include(expansion!, "Fallback: architect runners seat 1"); + const unsupported = yield* pack.expandComposer( + { ...input, supportsOrchestrationTools: false }, + { createdBy: "user" }, + ); + assert.include(unsupported.text, "this session does not expose T3 orchestration tools"); + assert.notInclude(unsupported.text, ""); assert.equal( (yield* pack.expandComposer( { ...input, text: "$pstack:unknown" }, diff --git a/apps/server/src/pstack/PstackPack.ts b/apps/server/src/pstack/PstackPack.ts index 6520f7668ee2d..6af7d75387500 100644 --- a/apps/server/src/pstack/PstackPack.ts +++ b/apps/server/src/pstack/PstackPack.ts @@ -1,6 +1,7 @@ import { resolveProjectProviderInstanceEnabled, type ComposerContextRecord, + type ModelSelection, type OrchestrationV2ConversationMessage, type ProjectId, type ServerProviderSkill, @@ -49,6 +50,8 @@ export class PstackPack extends Context.Service< readonly expandComposer: ( input: { readonly projectId: ProjectId; + readonly parentModelSelection?: ModelSelection; + readonly supportsOrchestrationTools?: boolean; readonly text: string; readonly records: ReadonlyArray; }, @@ -280,6 +283,11 @@ const make = Effect.fn("PstackPack.make")(function* (sourceRoot?: string) { plan.skillNames.filter((name) => pack.skills.some((skill) => skill.name === name)), ); if (names.size === 0) return input; + if (input.supportsOrchestrationTools === false) + return plan.apply( + names, + "pstack is unavailable: this session does not expose T3 orchestration tools. Use a T3-managed session to run this workflow.", + ); const file = yield* fs.readFileString(modelsPath).pipe( Effect.catchIf( (error) => error.reason._tag === "NotFound", @@ -302,6 +310,9 @@ const make = Effect.fn("PstackPack.make")(function* (sourceRoot?: string) { defaults: pack.defaults, settings: currentSettings, projectId: input.projectId, + ...(input.parentModelSelection === undefined + ? {} + : { parentModelSelection: input.parentModelSelection }), nowMs: yield* Clock.currentTimeMillis, ...providers, providers: providers.providers.map((provider) => ({ diff --git a/apps/server/src/pstack/composer.test.ts b/apps/server/src/pstack/composer.test.ts index 142f1bb8eda0d..2e1c013084ca1 100644 --- a/apps/server/src/pstack/composer.test.ts +++ b/apps/server/src/pstack/composer.test.ts @@ -67,7 +67,7 @@ it.layer(NodeServices.layer)("pstack composer", (it) => { ); }), ); - it.effect.each(["$pstack:architect", "/pstack:architect"])( + it.effect.each(["$pstack:architect", "/pstack:architect", "pstack:architect"])( "consumes leading %s and preserves disabled identity", (token) => Effect.gen(function* () { @@ -93,14 +93,14 @@ it.layer(NodeServices.layer)("pstack composer", (it) => { }).pipe(Effect.provide(Layer.merge(fixture.layer, fixture.settingsLayer))); }), ); - it.effect( + it.effect.each(["Please $pstack:architect", "pstack:architect", " /pstack:architect"])( "rewrites inline composer mentions, expands several skills once, and leaves unknown names alone", - () => + (leading) => Effect.gen(function* () { const fixture = yield* makeFixture(); const input = { projectId: ProjectId.make("composer-test"), - text: "Please $pstack:architect design this, then $pstack:setup-pstack configure it. $pstack:architect again $pstack:missing", + text: `${leading} design this, then $pstack:setup-pstack configure it. $pstack:architect again $pstack:missing`, records: [], }; const result = yield* expandPstackComposer(input, { createdBy: "user" }).pipe( @@ -108,7 +108,7 @@ it.layer(NodeServices.layer)("pstack composer", (it) => { ); assert.isTrue( result.text.startsWith( - "Please pstack:architect design this, then pstack:setup-pstack configure it. pstack:architect again $pstack:missing", + `${leading.replace(/[$/]/, "")} design this, then pstack:setup-pstack configure it. pstack:architect again $pstack:missing`, ), ); assert.include(result.text, "pstack skill: architect, setup-pstack"); diff --git a/apps/server/src/pstack/composer.ts b/apps/server/src/pstack/composer.ts index 4091de36819a3..ec463cd9109ee 100644 --- a/apps/server/src/pstack/composer.ts +++ b/apps/server/src/pstack/composer.ts @@ -44,13 +44,20 @@ export function planPstackComposer( >, ) { if (!hasPstackComposerInvocation(input, message)) return undefined; - const tokenText = input.text.replace( - /^(\s*)\/(?=pstack:)/, - (_match, prefix: string) => `${prefix}$`, - ); - const mentions = [...tokenText.matchAll(SKILL_MENTION_PATTERN)].filter((match) => - match[2]?.startsWith(PSTACK_SKILL_PREFIX), - ); + const mentions = [...input.text.matchAll(SKILL_MENTION_PATTERN)] + .filter((match) => match[2]?.startsWith(PSTACK_SKILL_PREFIX)) + .map((match) => ({ + name: match[2]!, + start: match.index + (match[1]?.length ?? 0), + end: match.index + match[0].length, + })); + const leading = /^(\s*)\/?(pstack:[a-zA-Z0-9:_-]+)(?=\s|$)/.exec(input.text); + if (leading) + mentions.unshift({ + name: leading[2]!, + start: leading[1]!.length, + end: leading[0].length, + }); const referenced = new Set( collectComposerContextReferences(input.text).map((ref) => ref.contextId), ); @@ -67,7 +74,7 @@ export function planPstackComposer( ), ); const skillNames = [ - ...new Set([...mentions.map((mention) => mention[2]!), ...recordsById.values()]), + ...new Set([...mentions.map((mention) => mention.name), ...recordsById.values()]), ]; if (skillNames.length === 0) return undefined; return { @@ -75,10 +82,9 @@ export function planPstackComposer( apply(names: ReadonlySet, expansion: string) { let text = input.text; for (const mention of mentions.toReversed()) { - const name = mention[2]!; + const { name, start, end } = mention; if (!names.has(name)) continue; - const start = mention.index + (mention[1]?.length ?? 0); - text = `${text.slice(0, start)}${name}${text.slice(mention.index + mention[0].length)}`; + text = `${text.slice(0, start)}${name}${text.slice(end)}`; } const consumedIds = new Map([...recordsById].filter(([, name]) => names.has(name))); text = replaceComposerContextReferences( diff --git a/apps/server/src/pstack/pstackModels.test.ts b/apps/server/src/pstack/pstackModels.test.ts index 714c2b32b1ead..c0b6a7e4bb5b5 100644 --- a/apps/server/src/pstack/pstackModels.test.ts +++ b/apps/server/src/pstack/pstackModels.test.ts @@ -225,24 +225,145 @@ it.effect("preserves explicit panel cardinality, aliases, comments and model sla assert.include(result.diagnostics[0]!, "missing/vendor/model"); }), ); -it.effect("recognizes Pi-style thinking as effort", () => +it.effect.each([ + { instanceId: "custom-codex", driver: "codex", model: "gpt-6-astra" }, + { instanceId: "pi", driver: "pi", model: "openai/gpt-6-astra" }, + { instanceId: "opencode-work", driver: "opencode2", model: "openai/gpt-6-astra" }, + { instanceId: "acp-work", driver: "acpRegistry", model: "gpt-6-astra" }, +])( + "resolves default models and advertised thinking across harnesses: $driver", + ({ instanceId, driver, model }) => + Effect.sync(() => { + const piStyle = provider(instanceId, driver, model, [effort("thinking")]); + const result = resolveRoles({ + ...availability, + file: undefined, + defaults: [{ label: "runner", defaultValue: "gpt-6-astra-xhigh-fast" }], + providers: [{ ...providers[1]!, enabled: false }, piStyle], + capableInstanceIds: new Set([piStyle.instanceId]), + }); + assert.deepEqual(result.diagnostics, []); + assert.deepEqual(result.roles[0]!.seats, [ + { + kind: "target", + target: { + providerInstanceId: instanceId, + model, + options: [{ id: "thinking", value: "xhigh" }], + }, + }, + ]); + }), +); +it.effect( + "keeps advertised default model IDs intact and does not require a reasoning control", + () => + Effect.sync(() => { + const catalog = provider("pi", "pi", "grok-4.7", []); + const exact = provider("custom", "acpRegistry", "custom-fast", []); + const result = resolveRoles({ + ...availability, + file: undefined, + defaults: [{ label: "architect runners", defaultValue: "grok-4.7-xhigh, custom-fast" }], + providers: [catalog, exact], + capableInstanceIds: new Set([catalog.instanceId, exact.instanceId]), + }); + assert.deepEqual(result.diagnostics, []); + assert.deepEqual(result.roles[0]!.seats, [ + { kind: "target", target: { providerInstanceId: "pi", model: "grok-4.7", options: [] } }, + { + kind: "target", + target: { providerInstanceId: "custom", model: "custom-fast", options: [] }, + }, + ]); + }), +); +it.effect( + "does not guess a provider-qualified model when an unqualified default is ambiguous", + () => + Effect.sync(() => { + const catalog = provider("pi", "pi", "vendor-a/shared-model", []); + const other = provider("opencode", "opencode2", "vendor-b/shared-model", []); + const result = resolveRoles({ + ...availability, + file: undefined, + defaults: [{ label: "architect runners", defaultValue: "shared-model" }], + providers: [catalog, other], + capableInstanceIds: new Set([catalog.instanceId, other.instanceId]), + }); + assert.deepEqual(result.roles[0]!.seats, [{ kind: "inherit" }]); + assert.include(result.diagnostics[0]!, "ambiguous"); + }), +); +it.effect("prefers the parent's available harness and reports unavailable inherited seats", () => Effect.sync(() => { - const piStyle = provider("custom-codex", "codex", "gpt-6-astra", [effort("thinking")]); - const result = resolveRoles({ + const other = provider("pi", "pi", "gpt-6-astra", [effort("thinking")]); + const input = { ...availability, + defaults: [{ label: "architect runners", defaultValue: "gpt-6-astra-high, inherit-parent" }], + providers: [...providers, other], + capableInstanceIds: new Set([...capableInstanceIds, other.instanceId]), + parentModelSelection: { instanceId: other.instanceId, model: "gpt-6-astra" }, file: undefined, - defaults: [{ label: "runner", defaultValue: "gpt-6-astra-xhigh-fast" }], - providers: [piStyle], - capableInstanceIds: new Set([piStyle.instanceId]), - }); + }; + const result = resolveRoles(input); assert.deepEqual(result.diagnostics, []); assert.deepEqual(result.roles[0]!.seats, [ { kind: "target", target: { - providerInstanceId: "custom-codex", + providerInstanceId: "pi", model: "gpt-6-astra", - options: [{ id: "thinking", value: "xhigh" }], + options: [{ id: "thinking", value: "high" }], + }, + }, + { kind: "inherit" }, + ]); + const unavailable = resolveRoles({ + ...input, + file: parseModelsFile("architect runners: auto, missing/model"), + providers: [...providers, { ...other, enabled: false }], + }); + assert.deepEqual(unavailable.roles[0]!.seats, [ + { kind: "unavailable" }, + { kind: "unavailable" }, + ]); + assert.include(unavailable.diagnostics.join("\n"), "Parent model cannot run a child task"); + assert.notInclude(unavailable.diagnostics.join("\n"), "runs on the parent model"); + }), +); +it.effect("parses option booleans according to the selected model's descriptors", () => + Effect.sync(() => { + const catalog = provider("pi", "pi", "vendor/model", [ + { id: "enabled", label: "Enabled", type: "boolean" }, + { + id: "variant", + label: "Variant", + type: "select", + options: [ + { id: "true", label: "True" }, + { id: "false", label: "False" }, + ], + }, + ]); + const result = resolveRoles({ + ...availability, + file: parseModelsFile("architect runners: pi/vendor/model enabled=false variant=true"), + defaults, + providers: [catalog], + capableInstanceIds: new Set([catalog.instanceId]), + }); + assert.deepEqual(result.diagnostics, []); + assert.deepEqual(result.roles[0]!.seats, [ + { + kind: "target", + target: { + providerInstanceId: "pi", + model: "vendor/model", + options: [ + { id: "enabled", value: false }, + { id: "variant", value: "true" }, + ], }, }, ]); @@ -300,6 +421,8 @@ it.effect("formats the adapter block exactly and omits empty notes", () => "Read /skills/architect/SKILL.md and follow it.", "Read /skills/arena/SKILL.md and follow it.", "Resolved roles (exact delegate_task targets; they override any model a skill or names):", + "These targets are a snapshot. If a target is rejected or marked unavailable, refresh orchestrator_capabilities before retrying; keep the user's model choices and reasoning budget, and report any fallback.", + "After setup-pstack changes , re-read it and resolve against fresh orchestrator_capabilities; the earlier targets below no longer apply.", "- architect runners: [1] inherit", "- interrogate reviewers (panel, one child per seat): [1] inherit [2] inherit", "- feature, refactoring (pool, pick one per spawn): [1] inherit [2] inherit", @@ -373,18 +496,58 @@ it.effect("default seats skip unhealthy and incapable instances offering the mod installed: false, }; const incapable = { ...first, instanceId: ProviderInstanceId.make("incapable") }; + const missingDefault = provider("missing-default", "codex", "gpt-6-astra", [ + effort("reasoningEffort"), + { + id: "variant", + label: "Variant", + type: "select", + options: [{ id: "standard", label: "Standard" }], + }, + ]); const result = resolveRoles({ ...availability, file: undefined, defaults, - providers: [unhealthy, incapable, ...providers], - capableInstanceIds: new Set([...capableInstanceIds, unhealthy.instanceId]), + providers: [unhealthy, incapable, missingDefault, ...providers], + capableInstanceIds: new Set([ + ...capableInstanceIds, + unhealthy.instanceId, + missingDefault.instanceId, + ]), }); assert.deepEqual(result.diagnostics, []); const seat = result.roles[0]!.seats[1]!; assert.equal(seat.kind === "target" && seat.target.providerInstanceId, "codex"); }), ); +it.effect.each([false, true])( + "implicit model defaults follow orchestration visibility; custom=%s", + (isCustom) => + Effect.sync(() => { + const codex = providers[1]!; + const input = { + ...availability, + settings: { + ...DEFAULT_SERVER_SETTINGS, + providerModelPreferences: { + [codex.instanceId]: { hiddenModels: ["gpt-6-astra"], modelOrder: [] }, + }, + }, + defaults: [{ label: "architect runners", defaultValue: "gpt-6-astra-high" }], + providers: [{ ...codex, models: codex.models.map((model) => ({ ...model, isCustom })) }], + capableInstanceIds, + }; + const implicit = resolveRoles({ ...input, file: undefined }); + assert.equal(implicit.roles[0]!.seats[0]!.kind, isCustom ? "target" : "inherit"); + const explicit = resolveRoles({ + ...input, + file: parseModelsFile("architect runners: codex/gpt-6-astra reasoningEffort=high"), + }); + assert.equal(explicit.roles[0]!.seats[0]!.kind, "target"); + assert.deepEqual(explicit.diagnostics, []); + }), +); it.effect("clamps default effort below its budget", () => Effect.sync(() => { const codex = provider("codex", "codex", "gpt-6-astra", [ diff --git a/apps/server/src/pstack/pstackModels.ts b/apps/server/src/pstack/pstackModels.ts index 843e53f09b935..bc0bb85902f3c 100644 --- a/apps/server/src/pstack/pstackModels.ts +++ b/apps/server/src/pstack/pstackModels.ts @@ -1,5 +1,8 @@ import { + type ModelSelection, + type ModelPreferences, type ProviderInstanceId, + type ProviderOptionDescriptor, type ProviderOptionSelection, type ServerProvider, } from "@t3tools/contracts"; @@ -8,6 +11,7 @@ import { PSTACK_PANEL_ROLES, type PstackRoleDefault } from "@t3tools/pstack"; import { delegationProviderConstraints, invalidOptionSelections, + isCapabilityModelVisible, } from "../provider/orchestrationTargets.ts"; interface Entry { @@ -16,8 +20,14 @@ interface Entry { readonly options: ReadonlyArray; readonly error?: string; } -type Seat = { readonly kind: "inherit" } | { readonly kind: "target"; readonly target: Entry }; -type ParsedSeat = { readonly kind: "inherit" } | { readonly kind: "entry"; readonly entry: Entry }; +type Seat = + | { readonly kind: "inherit" } + | { readonly kind: "unavailable" } + | { readonly kind: "target"; readonly target: Entry }; +type ParsedSeat = + | { readonly kind: "inherit" } + | { readonly kind: "entry"; readonly entry: Entry } + | { readonly kind: "unresolved"; readonly model: string; readonly reason: string }; export function parseModelsFile(text: string) { const lines = new Map>(); @@ -61,7 +71,7 @@ export function parseModelsFile(text: string) { const value = token.slice(equal + 1); options.push({ id: token.slice(0, equal), - value: value === "true" ? true : value === "false" ? false : value, + value, }); } return { @@ -80,61 +90,117 @@ export function parseModelsFile(text: string) { } const effortLadder = ["low", "medium", "high", "xhigh", "max"]; -// Speed stays with the provider's own default unless the models file sets it. +// Omitted speed follows T3's normal inheritance/default rules. const speedOptionIds = new Set(["fastMode", "serviceTier"]); +function resolveOptions( + requested: ReadonlyArray, + descriptors: ReadonlyArray, +) { + const selections = requested.map((selection) => { + const descriptor = descriptors.find((descriptor) => descriptor.id === selection.id); + return descriptor?.type === "boolean" && + (selection.value === "true" || selection.value === "false") + ? { id: selection.id, value: selection.value === "true" } + : selection; + }); + const problems = [...invalidOptionSelections(selections, descriptors)]; + const options: ProviderOptionSelection[] = []; + for (const descriptor of descriptors) { + if ( + speedOptionIds.has(descriptor.id) && + !selections.some((option) => option.id === descriptor.id) + ) + continue; + const value = + selections.find((option) => option.id === descriptor.id)?.value ?? + getProviderOptionCurrentValue(descriptor) ?? + (descriptor.type === "boolean" ? false : undefined); + if (value === undefined) problems.push(`option ${descriptor.id} has no current value`); + else options.push({ id: descriptor.id, value }); + } + problems.push(...invalidOptionSelections(options, descriptors)); + return { options, problems }; +} + function defaultSeat( slug: string, providers: ReadonlyArray, constraintsFor: (provider: ServerProvider) => ReadonlyArray, + parentModelSelection: ModelSelection | undefined, + modelPreferences: ModelPreferences["providerModelPreferences"] | undefined, ): ParsedSeat { if (slug === "inherit-parent" || slug === "auto") return { kind: "inherit" }; const withoutFast = slug.replace(/-fast$/, ""); const effortMatch = /-(low|medium|high|xhigh|max)$/.exec(withoutFast); - const model = effortMatch ? withoutFast.slice(0, effortMatch.index) : withoutFast; - const driver = model.startsWith("claude-") - ? "claudeAgent" - : model.startsWith("gpt-") - ? "codex" - : model.startsWith("grok-") - ? "grok" - : model.startsWith("composer-") || model.startsWith("cursor-grok-") - ? "cursor" - : undefined; - const provider = providers.find( - (candidate) => - candidate.driver === driver && - constraintsFor(candidate).length === 0 && - candidate.pool?.role !== "member" && - candidate.models.some((candidate) => candidate.slug === model), - ); - const descriptors = - provider?.models.find((candidate) => candidate.slug === model)?.capabilities - ?.optionDescriptors ?? []; - const options: ProviderOptionSelection[] = []; - const effort = effortMatch?.[1]; - const descriptor = descriptors.find( - (candidate) => - candidate.type === "select" && - candidate.options.filter((option) => effortLadder.includes(option.id)).length >= 2, + const variants = [ + { model: slug, effort: undefined }, + ...(withoutFast === slug ? [] : [{ model: withoutFast, effort: undefined }]), + ...(effortMatch === null + ? [] + : [{ model: withoutFast.slice(0, effortMatch.index), effort: effortMatch[1] }]), + ]; + const advertised = providers.flatMap((provider) => + provider.models.map((model) => ({ provider, model })), ); - if (effort !== undefined && descriptor?.type === "select") { - const clamped = effortLadder - .slice(0, effortLadder.indexOf(effort) + 1) - .findLast((value) => descriptor.options.some((option) => option.id === value)); - options.push({ id: descriptor.id, value: clamped ?? effort }); + for (const variant of variants) { + const matching = advertised.filter( + ({ model }) => + model.slug === variant.model || + (!variant.model.includes("/") && model.slug.endsWith(`/${variant.model}`)), + ); + if (matching.length === 0) continue; + const eligible = matching.filter( + ({ provider, model }) => + constraintsFor(provider).length === 0 && + provider.pool?.role !== "member" && + isCapabilityModelVisible(model, modelPreferences?.[provider.instanceId]), + ); + const exact = eligible.filter(({ model }) => model.slug === variant.model); + const candidates = exact.length > 0 ? exact : eligible; + if (new Set(candidates.map(({ model }) => model.slug)).size > 1) + return { + kind: "unresolved", + model: slug, + reason: "model alias is ambiguous; run setup-pstack to choose an advertised model", + }; + const priority = (provider: ServerProvider) => + provider.pool?.role === "pool" + ? 0 + : provider.instanceId === parentModelSelection?.instanceId + ? 1 + : 2; + const available = candidates.toSorted( + (left, right) => priority(left.provider) - priority(right.provider), + ); + for (const { provider, model } of available) { + const descriptors = model.capabilities?.optionDescriptors ?? []; + const options: ProviderOptionSelection[] = []; + const descriptor = descriptors.find( + (candidate) => + candidate.type === "select" && + candidate.options.filter((option) => effortLadder.includes(option.id)).length >= 2, + ); + if (variant.effort !== undefined && descriptor?.type === "select") { + const clamped = effortLadder + .slice(0, effortLadder.indexOf(variant.effort) + 1) + .findLast((value) => descriptor.options.some((option) => option.id === value)); + if (clamped === undefined) continue; + options.push({ id: descriptor.id, value: clamped }); + } + if (resolveOptions(options, descriptors).problems.length > 0) continue; + return { + kind: "entry", + entry: { providerInstanceId: provider.instanceId, model: model.slug, options }, + }; + } + return { + kind: "unresolved", + model: slug, + reason: "no available provider can run this model with the requested reasoning budget", + }; } - return { - kind: "entry", - entry: { - providerInstanceId: provider?.instanceId ?? driver ?? "unknown", - model, - options, - ...(provider !== undefined && effort !== undefined && descriptor === undefined - ? { error: "model does not advertise an effort option" } - : {}), - }, - }; + return { kind: "unresolved", model: slug, reason: "model is not advertised" }; } export function resolveRoles( @@ -142,6 +208,9 @@ export function resolveRoles( readonly file: ReturnType | undefined; readonly defaults: ReadonlyArray; readonly capableInstanceIds: ReadonlySet; + readonly parentModelSelection?: ModelSelection; + readonly settings: Parameters[2]["settings"] & + Partial>; } & Parameters[2], ) { const constraintsFor = (provider: ServerProvider) => @@ -151,6 +220,24 @@ export function resolveRoles( input, ); const diagnostics = [...(input.file?.diagnostics ?? [])]; + const parent = + input.parentModelSelection === undefined + ? undefined + : input.providers.find( + (provider) => provider.instanceId === input.parentModelSelection?.instanceId, + ); + const parentConstraints = + input.parentModelSelection === undefined + ? [] + : parent === undefined + ? ["provider instance is not registered"] + : constraintsFor(parent); + const inherited: Seat = + parentConstraints.length === 0 ? { kind: "inherit" } : { kind: "unavailable" }; + if (parentConstraints.length > 0) + diagnostics.push( + `Parent model cannot run a child task (${parentConstraints.join(" ")}); refresh orchestrator_capabilities before choosing another target.`, + ); for (const role of input.file?.lines.keys() ?? []) { if (!input.defaults.some((entry) => entry.label === role)) diagnostics.push(`Unknown role ${role}; ignored.`); @@ -160,43 +247,48 @@ export function resolveRoles( input.file?.lines.get(role.label) ?? role.defaultValue .split(",") - .map((slug) => defaultSeat(slug.trim(), input.providers, constraintsFor)); + .map((slug) => + defaultSeat( + slug.trim(), + input.providers, + constraintsFor, + input.parentModelSelection, + input.settings.providerModelPreferences, + ), + ); const panel = PSTACK_PANEL_ROLES.has(role.label); + const fallback = (index: number, target: string, reason: string): Seat | undefined => { + const action = + inherited.kind === "unavailable" + ? "parent delegation is unavailable; refresh orchestrator_capabilities before choosing another target." + : panel + ? "runs on the parent model." + : "excluded from pool; uses the parent model if no seats remain."; + diagnostics.push( + `Fallback: ${role.label} seat ${index + 1} ${target} unavailable (${reason}); ${action}`, + ); + return panel ? inherited : undefined; + }; const seats = entries.map((seat, index): Seat | undefined => { - if (seat.kind === "inherit") return seat; + if (seat.kind === "inherit") + return inherited.kind === "unavailable" && !panel ? undefined : inherited; + if (seat.kind === "unresolved") return fallback(index, seat.model, seat.reason); const entry = seat.entry; const provider = input.providers.find( (provider) => provider.instanceId === entry.providerInstanceId, ); const model = provider?.models.find((model) => model.slug === entry.model); const descriptors = model?.capabilities?.optionDescriptors ?? []; + const { options, problems } = resolveOptions(entry.options, descriptors); let reason = entry.error ?? (provider === undefined ? "provider instance is not registered" : constraintsFor(provider).join(" ") || undefined); reason ??= model === undefined ? "model is not advertised" : undefined; - reason ??= invalidOptionSelections(entry.options, descriptors).join(" ") || undefined; - const options: ProviderOptionSelection[] = []; - for (const descriptor of descriptors) { - if ( - speedOptionIds.has(descriptor.id) && - !entry.options.some((option) => option.id === descriptor.id) - ) - continue; - const value = - entry.options.find((option) => option.id === descriptor.id)?.value ?? - getProviderOptionCurrentValue(descriptor) ?? - (descriptor.type === "boolean" ? false : undefined); - if (value === undefined) reason ??= `option ${descriptor.id} has no current value`; - else options.push({ id: descriptor.id, value }); - } - reason ??= invalidOptionSelections(options, descriptors).join(" ") || undefined; + reason ??= problems.join(" ") || undefined; if (reason !== undefined) { - diagnostics.push( - `Fallback: ${role.label} seat ${index + 1} ${entry.providerInstanceId}/${entry.model} unavailable (${reason}); ${panel ? "runs on the parent model." : "excluded from pool; uses the parent model if no seats remain."}`, - ); - return panel ? { kind: "inherit" } : undefined; + return fallback(index, `${entry.providerInstanceId}/${entry.model}`, reason); } return { kind: "target", @@ -206,7 +298,7 @@ export function resolveRoles( const available = seats.filter((seat) => seat !== undefined); return { role: role.label, - seats: available.length > 0 ? available : [{ kind: "inherit" } as const], + seats: available.length > 0 ? available : [inherited], }; }); return { roles, diagnostics }; @@ -225,6 +317,8 @@ export function formatPstackBlock(input: { `: ${input.modelsPath}`, ...input.skillNames.map((name) => `Read /skills/${name}/SKILL.md and follow it.`), "Resolved roles (exact delegate_task targets; they override any model a skill or names):", + "These targets are a snapshot. If a target is rejected or marked unavailable, refresh orchestrator_capabilities before retrying; keep the user's model choices and reasoning budget, and report any fallback.", + "After setup-pstack changes , re-read it and resolve against fresh orchestrator_capabilities; the earlier targets below no longer apply.", ...input.resolved.roles.map(({ role, seats }) => { const rule = seats.length > 1 @@ -232,7 +326,7 @@ export function formatPstackBlock(input: { ? " (panel, one child per seat)" : " (pool, pick one per spawn)" : ""; - return `- ${role}${rule}: ${seats.map((seat, index) => `[${index + 1}] ${seat.kind === "inherit" ? "inherit" : JSON.stringify(seat.target)}`).join(" ")}`; + return `- ${role}${rule}: ${seats.map((seat, index) => `[${index + 1}] ${seat.kind === "target" ? JSON.stringify(seat.target) : seat.kind}`).join(" ")}`; }), ...(input.resolved.diagnostics.length === 0 ? [] diff --git a/docs/user/pstack.md b/docs/user/pstack.md index 942fd8fd1b565..1075d299dd398 100644 --- a/docs/user/pstack.md +++ b/docs/user/pstack.md @@ -2,7 +2,7 @@ pstack is [Lauren Tan's](https://github.com/cursor/plugins/tree/main/pstack) set of rigorous agent workflows, such as `architect`, `arena`, `how`, `why`, and `poteto-mode`. T3 Code ships it built in -and runs it the same way in every provider. Its multi-model steps can mix providers. For example, +and runs it through its orchestration tools across supported providers. Its multi-model steps can mix providers. For example, one `$pstack:architect` can run an Opus runner and a GPT runner side by side, whichever provider your thread uses. @@ -18,7 +18,9 @@ and keeps your model choices. Run `$pstack:setup-pstack` in any thread. It reads the providers and models this environment offers, asks for a reasoning budget, shows every pstack role with its models, and saves your choices for every provider's threads on this machine. Run it again to change them, or after you enable or add a -provider. Until you run it, pstack uses its own defaults where this environment offers those models. +provider. Until you run it, pstack matches its defaults to models advertised by this environment's +available providers, including models offered through another harness. Models without a reasoning +setting keep their own defaults. List several models on a role to give pstack a pool that picks one per spawn. Arena runners, architect runners, and interrogate reviewers are panels that run every model listed. @@ -33,11 +35,15 @@ Use `$pstack:automate-maintainer` or `$pstack:automate-team` to capture a mainta When a model becomes unavailable, pools use their remaining choices; if none remain, they use your thread’s model. Panels keep every seat, using your thread’s model for unavailable entries. The reply explains each unavailable choice with `Fallback: …`; run `$pstack:setup-pstack` to change it. +If your thread's provider cannot start a child either, pstack reports the unavailable seat and checks +the current capabilities before choosing another target. ## Good to know - pstack runs on the machine the thread runs on. A linked machine needs pstack turned on and its own `$pstack:setup-pstack`, since its providers differ. +- OpenCode sessions using an external **Server URL** do not expose T3's orchestration tools. Leave + that setting blank to let T3 manage the server before running a pstack workflow. - Long-running workflows use T3 Code's scheduled tasks and pull request watching. Remove schedules you no longer need from the scheduled tasks list. - Steps that need a Cursor-only feature, such as Bugbot setup or a Cursor cloud machine, are skipped diff --git a/packages/contracts/src/orchestratorMcp.ts b/packages/contracts/src/orchestratorMcp.ts index 86ff0e4af8533..b96e63e93199e 100644 --- a/packages/contracts/src/orchestratorMcp.ts +++ b/packages/contracts/src/orchestratorMcp.ts @@ -94,7 +94,8 @@ export const OrchestratorMcpTarget = Schema.Struct({ ), driverKind: Schema.optional( ProviderDriverKind.annotate({ - description: "Prefer `providerInstanceId`.", + description: + "Local targets only; prefer providerInstanceId. For a linked environment, omit driverKind and use providerInstanceId from that environment's orchestrator_capabilities.", }), ), model: Schema.optional(TrimmedNonEmptyString), diff --git a/packages/provider-core/src/server/ProviderAdapter.ts b/packages/provider-core/src/server/ProviderAdapter.ts index cadf4173a60b1..0f27bafaad249 100644 --- a/packages/provider-core/src/server/ProviderAdapter.ts +++ b/packages/provider-core/src/server/ProviderAdapter.ts @@ -569,6 +569,8 @@ export interface ProviderAdapterV2SessionRuntime { readonly driver: ProviderDriverKind; readonly providerSessionId: ProviderSessionId; readonly providerSession: OrchestrationV2ProviderSession; + /** Overrides MCP support when a session cannot mount T3's orchestration tools. */ + readonly supportsT3Tools?: boolean; readonly events: Stream.Stream; /** Manager runtimes broadcast independent subscriptions; adapters may expose only a single-consumer stream. */ readonly subscribeEvents?: Effect.Effect; diff --git a/packages/provider-opencode/src/server/adapter.test.ts b/packages/provider-opencode/src/server/adapter.test.ts index 7f9e5ddba9e41..98d638ab581ab 100644 --- a/packages/provider-opencode/src/server/adapter.test.ts +++ b/packages/provider-opencode/src/server/adapter.test.ts @@ -8,6 +8,7 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import type { OpencodeClient, ToolPart } from "@opencode-ai/sdk/v2"; import { CheckpointId, + EnvironmentId, NodeId, ProjectId, ProviderInstanceId, @@ -2742,122 +2743,164 @@ it.effect.each([false, true])( ), ); -it.live.each([true, false])("opens an authenticated OpenCode session (external=%s)", (external) => - Effect.gen(function* () { - const password = external ? "settings-password" : "environment-password"; - const requests: string[] = []; - let eventConnected!: () => void; - const eventRequest = new Promise((resolve) => { - eventConnected = resolve; - }); - const server = yield* Effect.acquireRelease( - Effect.promise( - () => - new Promise>((resolve) => { - const server = NodeHttp.createServer((request, response) => { - if ( - request.headers.authorization !== - `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}` - ) { - response.writeHead(401).end("Unauthorized"); - return; - } - const url = new URL(request.url!, "http://localhost"); - requests.push(url.pathname); - if (url.pathname === "/event") { - response.writeHead(200, { "Content-Type": "text/event-stream" }); - response.write(": connected\n\n"); - eventConnected(); - return; - } - response.writeHead(200, { "Content-Type": "application/json" }); - response.end( - JSON.stringify( - url.pathname.endsWith("/children") - ? [] - : url.pathname === "/session" - ? { id: "native-auth", time: { created: 1, updated: 1 } } - : { healthy: true, version: "1.14.19" }, - ), - ); - }); - server.listen(0, "127.0.0.1", () => resolve(server)); - }), - ), - (server) => +it.live.each([ + { external: true, credential: true, mcpStatus: "connected" }, + { external: false, credential: false, mcpStatus: "connected" }, + { external: false, credential: true, mcpStatus: "connected" }, + { external: false, credential: true, mcpStatus: "disabled" }, + { external: false, credential: true, mcpStatus: "failed" }, + { external: false, credential: true, mcpStatus: "needs_auth" }, + { external: false, credential: true, mcpStatus: "needs_client_registration" }, + { external: false, credential: true, mcpStatus: "missing" }, +])( + "opens authenticated OpenCode and confirms T3 tools (external=$external, credential=$credential, MCP=$mcpStatus)", + ({ external, credential, mcpStatus }) => + Effect.gen(function* () { + const password = external ? "settings-password" : "environment-password"; + const requests: string[] = []; + let eventConnected!: () => void; + const eventRequest = new Promise((resolve) => { + eventConnected = resolve; + }); + const server = yield* Effect.acquireRelease( Effect.promise( () => - new Promise((resolve) => { - server.closeAllConnections(); - server.close(() => resolve()); + new Promise>((resolve) => { + const server = NodeHttp.createServer((request, response) => { + if ( + request.headers.authorization !== + `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}` + ) { + response.writeHead(401).end("Unauthorized"); + return; + } + const url = new URL(request.url!, "http://localhost"); + requests.push(url.pathname); + if (url.pathname === "/event") { + response.writeHead(200, { "Content-Type": "text/event-stream" }); + response.write(": connected\n\n"); + eventConnected(); + return; + } + response.writeHead(200, { "Content-Type": "application/json" }); + response.end( + JSON.stringify( + url.pathname === "/mcp" + ? mcpStatus === "missing" + ? {} + : { + "t3-code": { + status: mcpStatus, + ...(mcpStatus === "failed" || + mcpStatus === "needs_client_registration" + ? { error: "fixture failure" } + : {}), + }, + } + : url.pathname.endsWith("/children") + ? [] + : url.pathname === "/session" + ? { id: "native-auth", time: { created: 1, updated: 1 } } + : { healthy: true, version: "1.14.19" }, + ), + ); + }); + server.listen(0, "127.0.0.1", () => resolve(server)); }), ), - ); - const address = server.address(); - assert.isNotNull(address); - if (!address || typeof address === "string") return; - const url = `http://127.0.0.1:${address.port}`; - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; - let managedStarts = 0; - const runtimeLayer = OpenCodeRuntime.layer.pipe( - Layer.provide( - Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make((command) => { - assert.isTrue(ChildProcess.isStandardCommand(command)); - if (!ChildProcess.isStandardCommand(command)) return Effect.die("Unexpected pipeline"); - assert.include(command.args, "serve"); - assert.equal(command.options.env?.OPENCODE_SERVER_PASSWORD, password); - managedStarts += 1; - return spawner.spawn( - ChildProcess.make( - process.execPath, - [ - "-e", - `console.log("server listening on " + process.argv[1]); setInterval(() => {}, 1000)`, - url, - ], - command.options, - ), - ); - }), + (server) => + Effect.promise( + () => + new Promise((resolve) => { + server.closeAllConnections(); + server.close(() => resolve()); + }), + ), + ); + const address = server.address(); + assert.isNotNull(address); + if (!address || typeof address === "string") return; + const url = `http://127.0.0.1:${address.port}`; + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; + let managedStarts = 0; + const runtimeLayer = OpenCodeRuntime.layer.pipe( + Layer.provide( + Layer.succeed( + ChildProcessSpawner.ChildProcessSpawner, + ChildProcessSpawner.make((command) => { + assert.isTrue(ChildProcess.isStandardCommand(command)); + if (!ChildProcess.isStandardCommand(command)) + return Effect.die("Unexpected pipeline"); + assert.include(command.args, "serve"); + assert.equal(command.options.env?.OPENCODE_SERVER_PASSWORD, password); + managedStarts += 1; + return spawner.spawn( + ChildProcess.make( + process.execPath, + [ + "-e", + `console.log("server listening on " + process.argv[1]); setInterval(() => {}, 1000)`, + url, + ], + command.options, + ), + ); + }), + ), ), - ), - Layer.provide(OpenCodeServerLedger.layerTest), - Layer.provideMerge(NodeServices.layer), - ); - const instanceId = ProviderInstanceId.make("opencode-auth"); - const adapter = yield* makeOpenCodeAdapterV2({ - instanceId, - settings: yield* decodeOpenCodeSettings({ - serverUrl: external ? url : "", - ...(external ? { serverPassword: password } : {}), - }), - environment: { OPENCODE_SERVER_PASSWORD: "environment-password" }, - }).pipe(Effect.provide(runtimeLayer)); - const session = yield* adapter.openSession({ - threadId: ThreadId.make("thread-opencode-auth"), - providerSessionId: ProviderSessionId.make("session-opencode-auth"), - modelSelection: { instanceId, model: "anthropic/claude-sonnet", options: [] }, - runtimePolicy: { runtimeMode: "full-access", interactionMode: "default", cwd: null }, - }); - yield* session.ensureThread({ - threadId: ThreadId.make("thread-opencode-auth"), - modelSelection: { instanceId, model: "anthropic/claude-sonnet", options: [] }, - runtimePolicy: { runtimeMode: "full-access", interactionMode: "default", cwd: null }, - }); - assert.include(requests, "/session"); - yield* Effect.promise(() => eventRequest); - assert.include(requests, "/event"); - assert.include(requests, "/global/health"); - assert.equal(managedStarts, external ? 0 : 1); - }).pipe( - Effect.scoped, - Effect.provide([ - NodeServices.layer, - TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), - IdAllocator.layer, - McpProviderSessions.layer, - ]), - ), + Layer.provide(OpenCodeServerLedger.layerTest), + Layer.provideMerge(NodeServices.layer), + ); + const instanceId = ProviderInstanceId.make("opencode-auth"); + const threadId = ThreadId.make("thread-opencode-auth"); + if (credential) { + const mcpSessions = yield* McpProviderSessions.McpProviderSessions; + yield* mcpSessions.set({ + environmentId: EnvironmentId.make("environment-opencode-auth"), + threadId, + providerSessionId: "session-opencode-auth", + providerInstanceId: instanceId, + endpoint: "http://t3.invalid/mcp", + authorizationHeader: "Bearer fixture-credential", + browserToolsAvailable: false, + }); + } + const adapter = yield* makeOpenCodeAdapterV2({ + instanceId, + settings: yield* decodeOpenCodeSettings({ + serverUrl: external ? url : "", + ...(external ? { serverPassword: password } : {}), + }), + environment: { OPENCODE_SERVER_PASSWORD: "environment-password" }, + }).pipe(Effect.provide(runtimeLayer)); + const session = yield* adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make("session-opencode-auth"), + modelSelection: { instanceId, model: "anthropic/claude-sonnet", options: [] }, + runtimePolicy: { runtimeMode: "full-access", interactionMode: "default", cwd: null }, + }); + assert.equal(session.supportsT3Tools, !external && credential && mcpStatus === "connected"); + assert.equal( + requests.filter((request) => request === "/mcp").length, + !external && credential ? 1 : 0, + ); + yield* session.ensureThread({ + threadId, + modelSelection: { instanceId, model: "anthropic/claude-sonnet", options: [] }, + runtimePolicy: { runtimeMode: "full-access", interactionMode: "default", cwd: null }, + }); + assert.include(requests, "/session"); + yield* Effect.promise(() => eventRequest); + assert.include(requests, "/event"); + assert.include(requests, "/global/health"); + assert.equal(managedStarts, external ? 0 : 1); + }).pipe( + Effect.scoped, + Effect.provide([ + NodeServices.layer, + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), + IdAllocator.layer, + McpProviderSessions.layer, + ]), + ), ); diff --git a/packages/provider-opencode/src/server/adapter.ts b/packages/provider-opencode/src/server/adapter.ts index efa5232ee5aa3..1b6ddd832f091 100644 --- a/packages/provider-opencode/src/server/adapter.ts +++ b/packages/provider-opencode/src/server/adapter.ts @@ -958,13 +958,9 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function }); const mcpSession = yield* mcpSessions.read(input.threadId); - const hasT3Mcp = mcpSession !== undefined && !connection.external; - const orchestrationSystemPrompt = t3OrchestrationSystemPrompt( - hasT3Mcp, - mcpSession?.capabilities?.has("device") === true, - ); - if (hasT3Mcp) { - yield* OpenCodeRuntime.runOpenCodeSdk("mcp.add", () => + let hasT3Mcp = false; + if (mcpSession !== undefined && !connection.external) { + const result = yield* OpenCodeRuntime.runOpenCodeSdk("mcp.add", () => client.mcp.add({ name: "t3-code", config: { @@ -975,7 +971,12 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function }, }), ); + hasT3Mcp = result.data?.["t3-code"]?.status === "connected"; } + const orchestrationSystemPrompt = t3OrchestrationSystemPrompt( + hasT3Mcp, + mcpSession?.capabilities?.has("device") === true, + ); const now = yield* DateTime.now; let sessionEntity: OrchestrationV2ProviderSession = { @@ -3038,6 +3039,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function driver: OPENCODE_PROVIDER, providerSessionId: input.providerSessionId, providerSession: sessionEntity, + supportsT3Tools: hasT3Mcp, events: Stream.fromEffectRepeat(Queue.take(events)), hasPendingBackgroundWork: Effect.sync(() => busySessionIds.size > 0), ensureThread: Effect.fn("OpencodeAdapter.ensureThread")( diff --git a/packages/provider-opencode/src/server/v2/adapter.ts b/packages/provider-opencode/src/server/v2/adapter.ts index 83d374a58eff8..203906290df60 100644 --- a/packages/provider-opencode/src/server/v2/adapter.ts +++ b/packages/provider-opencode/src/server/v2/adapter.ts @@ -3349,6 +3349,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr get providerSession() { return session; }, + supportsT3Tools: !connection.external, events: Stream.fromQueue(events), // Idle release must wait for background children and held wakes after parent settlement. hasPendingBackgroundWork: Effect.sync(() => [...threads.values()].some(owesWork)), diff --git a/packages/pstack/UPSTREAM.json b/packages/pstack/UPSTREAM.json index 1c596c21b64f8..5706b11e93c4d 100644 --- a/packages/pstack/UPSTREAM.json +++ b/packages/pstack/UPSTREAM.json @@ -1,8 +1,8 @@ { "repository": "https://github.com/ScriptedAlchemy/pstack-t3code", - "commit": "0079f86170774864824e1b411c2a07d411e67bd5", - "subtreeHash": "efdef335d398373e8aba33736848c1854d6df731", - "version": "0.15.32-t3code.2", + "commit": "edad65c05f9be17d2b9c247edfb48a584fdf7e32", + "subtreeHash": "f4a44a39c4d484d480550afc5cca0c105528e4d2", + "version": "0.15.36-t3code.1", "files": { ".gitignore": { "sha256": "8ffe9de56bd30c1a95cd945262d6e3ac355ada4111551dde41f67851b9f1fc55", @@ -13,11 +13,11 @@ "executable": false }, "README.md": { - "sha256": "e1eac74aaff8986505523affa65bc58682932762ae8fa5d516d297c96fdc85ca", + "sha256": "ab2e291e13f7a230275847fd396ffce8d8d2a443a6ec2b81bd21174e326ec62f", "executable": false }, "UPSTREAM.json": { - "sha256": "0df82ea5ceca6924f2d7bf9a076a36586f1c85c5a547506ae0e94b11ac2dba3c", + "sha256": "a649da22552d5e73bc198d3477e766f1f15849090964068b11f7b25b404fa1c7", "executable": false }, "agents/comment-sicko.md": { @@ -93,7 +93,7 @@ "executable": false }, "docs/guide/04-design.md": { - "sha256": "6a07d443f2b37dae9b3a77aed7cbc6340d480767c14a9bfd6a7e49b3a8d69b8f", + "sha256": "b60470f2f143a50a3e2f9bbf62b732434617acba6114b5a6fee1b4df12914ab4", "executable": false }, "docs/guide/05-build-and-clean.md": { @@ -101,11 +101,11 @@ "executable": false }, "docs/guide/06-verify-and-ship.md": { - "sha256": "ca65b3f064e07dc8e3907f422cb32ae703245104e6fb01ef79a82b0ea16cf818", + "sha256": "8c6aa95d4f84f02870ace9fa159f84d0d0403592246a135f99f5e0975f3363b0", "executable": false }, "docs/guide/07-overnight.md": { - "sha256": "2632955c5ca99f7d8be7c8283fb7d4004decdfca497fbe1b4de610f80eb79000", + "sha256": "cc5039aaef57d5c542b6e4adc39403e1a1a5132199e1cfdea818632857ce9c64", "executable": false }, "docs/guide/08-principles.md": { @@ -117,7 +117,7 @@ "executable": false }, "docs/guide/10-recipes-and-pitfalls.md": { - "sha256": "94ff01ababc5133644cea0a84362c74548c22c071cc06d71fcfecc1e70bdebe6", + "sha256": "52000e09b925b64fe3469e3f56403b42d15d920e3a9856cc3ff0106d04765214", "executable": false }, "docs/guide/README.md": { @@ -149,11 +149,11 @@ "executable": false }, "plugin.json": { - "sha256": "cd62e4e678d68b28cc47c2351e9b44ee1930378a7374900ad431bc70677f9263", + "sha256": "f3e1c10b9f0039a5a91ff7036bbe4b329845df11a0f6ea9e6a332423274eae3a", "executable": false }, "skills/architect/SKILL.md": { - "sha256": "af607fce2619de80661d71d63118f73d3aee2ebb80ae6100cfb6fb19afd70c26", + "sha256": "49672c903a66f4d25bbec151ac7f44f4e9e0dc6833c994130d71bb57d65a9ed7", "executable": false }, "skills/architect/references/design-red-flags.md": { @@ -169,11 +169,11 @@ "executable": false }, "skills/arena/SKILL.md": { - "sha256": "3ef9ed8b9bfb7e46666d807dee9ab7bdb863a43cb8ebec0523dcee7ec71df17a", + "sha256": "7d0467d155851f2c9648d608573c7dfff67423e82e97517ebf9250c837449527", "executable": false }, "skills/automate-maintainer/SKILL.md": { - "sha256": "95c17880de2b4521d57df49c60274148f9c90710974b5be37e73674611f12feb", + "sha256": "c55919d26a0486a1d7c2a2316dfd8e49c26e00c37ffb05117952eb1b3a4f7e7c", "executable": false }, "skills/automate-maintainer/references/evaluation.md": { @@ -193,11 +193,11 @@ "executable": false }, "skills/automate-me/SKILL.md": { - "sha256": "d45c2dfaccdfface448151d2d196b0167d19b7981fdfa7a167964942d0e0f92e", + "sha256": "3f8601eb4507cf978f7e7d7c62a0b29f5176be5dc840495cbb5c63ffc80dc66e", "executable": false }, "skills/automate-team/SKILL.md": { - "sha256": "1c4f25014af5adec96a1c5d68c5d872988248daceb7b9a15df6a5d39f98b7a75", + "sha256": "74a4236167cb22c1beb49c78498cf07ad1f8d1af5684a9120c7f11cf33680d58", "executable": false }, "skills/automate-team/references/roster-discovery.md": { @@ -221,7 +221,7 @@ "executable": false }, "skills/create-formal-verification-skill/SKILL.md": { - "sha256": "e3ecf4ae2d4f1b68125827e9bbdb02f1f1e4e452787870f5ee845f86e7879f3f", + "sha256": "7b08c47e3423b5b8f3ffc667898b906cfaa096c25f12be56150d258dfa2f26b0", "executable": false }, "skills/create-verification-skill/SKILL.md": { @@ -241,11 +241,11 @@ "executable": false }, "skills/figure-it-out/SKILL.md": { - "sha256": "04ff6765a9f29dd9f9c74050fe6c91cfde03bc554d1174f2e645c32b97c9f0bd", + "sha256": "59d44a19fbec662c4e657963b09022efc5f9a5ca7c1f4b6826f01c6548529be1", "executable": false }, "skills/how/SKILL.md": { - "sha256": "7bcb12d931074187e3fe175aa119270290771931f9384aac05cde758c97f5596", + "sha256": "8b059a14f3f52109140b72fc5f9abb1a8b4cd0329aa830ccd1dbe0e950480bb1", "executable": false }, "skills/how/references/explainer-prompt.md": { @@ -261,27 +261,11 @@ "executable": false }, "skills/interrogate/SKILL.md": { - "sha256": "ec27cbedd6e40a60cf17307d5e190bddb1a5556a1c6647494f89c2677b3daa77", - "executable": false - }, - "skills/interrogate/references/code-quality-review.md": { - "sha256": "79162fda183eafd40d51661fbb5e151c7ba250f89e506ba17f5bd135dcbd50a8", - "executable": false - }, - "skills/interrogate/references/lead-judgment.md": { - "sha256": "355f5195a76bc75a655125292369f2a8231a2b61fabe5188d8f1386060922b87", - "executable": false - }, - "skills/interrogate/references/reviewer-prompt.md": { - "sha256": "f6005cc2b535006fca97a96c5bfda49df57312eedf1ffd8d1aaaf3eb33817c25", - "executable": false - }, - "skills/interrogate/references/rubric.md": { - "sha256": "0f9c6bdb0b96e26dbde5f6407f417e0c02f54080e7a1268d8994f3c3ec6c5ac0", + "sha256": "f8c2702696dc7d59f0b560f086340b6e7c6f0fb9d74abba8a1dbefe5a4e024d3", "executable": false }, "skills/ketchup-video/SKILL.md": { - "sha256": "c5f564de9b5f76bbe7c1b820f15071830f0daf24266ca38524312a0b3b5d0d60", + "sha256": "2088e0a6d3e78272a781a1b46b79a957bf6fbbfa18e0ab47b06ef7d03fb4a164", "executable": false }, "skills/ketchup/SKILL.md": { @@ -293,7 +277,7 @@ "executable": false }, "skills/maintain-verification-skill/SKILL.md": { - "sha256": "0ac81df9688c0578a6487d7c06a6fba90fe59f23dd274ab38f5771968cbee72b", + "sha256": "ddee509346bfecc21bdbf501f384d933c5953e9b69f73d53c3910ecb18aeba0c", "executable": false }, "skills/make-bot-ui/SKILL.md": { @@ -301,11 +285,11 @@ "executable": false }, "skills/no-comments/SKILL.md": { - "sha256": "d7d633c6c1dca4b4ebde68b92e4a2c1cb675c1ae1c6092af5dc40268e7cc9053", + "sha256": "41b603898d1d807991f6dd32c5565bda5449d5dde5a7fbc4f4ff6d83ff6313b4", "executable": false }, "skills/poteto-help/SKILL.md": { - "sha256": "c07311f8a4f53626197d603686152348353dfff0d62f9a7723ca94fce421dec9", + "sha256": "2880467e2985cb2b24c294320c2f86fdf439b7c48b29d54a22c689186f130c0a", "executable": false }, "skills/poteto-help/references/prompting.md": { @@ -313,11 +297,11 @@ "executable": false }, "skills/poteto-help/references/recipes.md": { - "sha256": "93bd72a1b858338cefce136a2692d1ffffca7d39ce231c314cec749f8e2fd905", + "sha256": "5d6072a8a4874b53d9f1ba37d5d66e2e1e2e044cdefbef0400949ec6ce7af6b9", "executable": false }, "skills/poteto-mode/SKILL.md": { - "sha256": "9668120a717d135e8ce746bb4bd7276388864479a533d62ee0f5a2db12412086", + "sha256": "111d0e66fa5976d2ae881d60cc588443abec3f9b56774a0a6a7f3f13f1c744d3", "executable": false }, "skills/poteto-mode/playbooks/authoring-a-skill.md": { @@ -325,19 +309,19 @@ "executable": false }, "skills/poteto-mode/playbooks/autonomous-run.md": { - "sha256": "74394391f0348ee644a64ef60530c5eb74dea60e74c2db344f836068418997c4", + "sha256": "904bb82b603090d3b7fc55fd830ebc33a8df27048785eff3760a2aa6c6bf2a97", "executable": false }, "skills/poteto-mode/playbooks/autopilot-full.md": { - "sha256": "c08ebbf23dc5a5e5ba62c13bcc7b8d24a3744c777d4a78a16d40250807ccf385", + "sha256": "4f28047443842be6eee4c10f4749d67aa1ff2d3b10a531b97a4a1bdfdf77d451", "executable": false }, "skills/poteto-mode/playbooks/autopilot-stack.md": { - "sha256": "003b3bbcecbffa17835424bc5f598c1eda69c8028da0ee122364e6873f55df23", + "sha256": "34de4bb101dd56524d8067c73bba5988ead6f93d3ab8f96ac13a6adcb43696cb", "executable": false }, "skills/poteto-mode/playbooks/babysit.md": { - "sha256": "e80e329edf6fa2eb5197dd19be046d7dc17f2dc9e032abffa5291519dac1d51a", + "sha256": "9fad11f2e53d6f46bd785f0e54e81c489231e780e74402ee39235bfdc995efb3", "executable": false }, "skills/poteto-mode/playbooks/bug-fix.md": { @@ -361,7 +345,7 @@ "executable": false }, "skills/poteto-mode/playbooks/multi-phase-plan.md": { - "sha256": "1a54e4247f62715dee6b2a09244cfa303ff05eab384dcaa7b81c7a575937ae28", + "sha256": "1e6817986c19b3a2aad233eeb10c5f81a52486950966ee5311ff5accb89126a8", "executable": false }, "skills/poteto-mode/playbooks/opening-a-pr.md": { @@ -369,7 +353,7 @@ "executable": false }, "skills/poteto-mode/playbooks/orchestrate.md": { - "sha256": "21651fc28f6736471aa7deb8406ddb3b94c618626ec3a9f6edaf886a637de18d", + "sha256": "f983757fbcde722e958b5281e451e614c16f3561779cac49077ce967508ef336", "executable": false }, "skills/poteto-mode/playbooks/pause-safely.md": { @@ -432,20 +416,8 @@ "sha256": "ccd2ed08fd9da9d0d942e5f2491cfc74bb92d8a08fa51755e0da1ac8de287361", "executable": false }, - "skills/poteto-mode/scripts/orch/orch.test.ts": { - "sha256": "c15bbcc17700acb812722bf3330e2b2ee2838407636cdb86784cdd9bb8ee93f3", - "executable": false - }, - "skills/poteto-mode/scripts/orch/orch.ts": { - "sha256": "9779b5c71c2a792a14fb4ad86f89c9a5bf350e69b0e80284bc8bcaadbb99314c", - "executable": true - }, - "skills/poteto-mode/scripts/orch/store.ts": { - "sha256": "bf0ed29c44093ee3b333aa10b138612f773c81eda73df7dc5337c59713f35a97", - "executable": false - }, "skills/poteto-mode/scripts/package.json": { - "sha256": "573838ef5ca3503022a0ffb758295935f4bbd3112f558fb2e1b522a2390087fe", + "sha256": "5ca458c2e30023b8893f114ebdf2d6a0b967149e1205ad859450fae170518a33", "executable": false }, "skills/poteto-mode/scripts/watch-pr/cli.test.ts": { @@ -525,7 +497,7 @@ "executable": false }, "skills/principle-explain-the-number/SKILL.md": { - "sha256": "cd5a25f9e7cf023627ccb470dc5faa97542f29b4319496a32b839a8e1e988047", + "sha256": "3c84fbf9a7d9842abcfcd134010782b32092ac3d3fb4aaf9ed5732dca83aa58c", "executable": false }, "skills/principle-fix-root-causes/SKILL.md": { @@ -597,11 +569,11 @@ "executable": false }, "skills/recall/SKILL.md": { - "sha256": "2eb3a991d0cdeddb03ce805b1b37006a7c4f140429842ee58be72f94760eb4b6", + "sha256": "92d40f3b99529ded50155727c8ab1c089188c6c9c9e5e39bae22da24e8729ca6", "executable": false }, "skills/reflect/SKILL.md": { - "sha256": "145ce88ba06ff69a5dd007b7e26111406d2749f4f29a1c6d1b19e33736673217", + "sha256": "7560bc681b9de1b66999aa22169e4dde48bf3b9526479c218e1d3b649e5cc885", "executable": false }, "skills/reflect/references/divergent-reviewer.md": { @@ -621,7 +593,7 @@ "executable": false }, "skills/setup-pstack/SKILL.md": { - "sha256": "750f54953871c8707ca6584d9a892900d2b13e634533a6c498f9f4426214c47f", + "sha256": "2802296c02bd558c9bc38d028014ec08553acac3a8f8101c3fccacd4e42e0ef7", "executable": false }, "skills/sharpen/SKILL.md": { @@ -629,7 +601,7 @@ "executable": false }, "skills/show-me-your-work/SKILL.md": { - "sha256": "dde5838d662842d2d66e38942b2e8d8c693cb0412aca63ebfaa73161f5370a50", + "sha256": "78b75d5af116a36013e2ad033682ad0b8f594642a03dc1bed551939138eff93f", "executable": false }, "skills/show-me-your-work/references/decision-log-template.tsv": { @@ -641,115 +613,1639 @@ "executable": true }, "skills/statechart/SKILL.md": { - "sha256": "813f757aca998b5ae075041fb90d8d71bcfe3f3003d5676f3473bc83416d661f", + "sha256": "be852f2831d5708f459a169c97dd7b66d80b588c0eef544650efcbf7fadab0c6", + "executable": false + }, + "skills/statechart/references/render.md": { + "sha256": "8cca446eb0c8f87f2ef7b97c4b149f59aacdc153134b4217c4bcde09b10ac3c4", + "executable": false + }, + "skills/statechart/scripts/backends/cfg.ts": { + "sha256": "aea4d8766d214e7fa08f4881f1037319655e75eb56652e1b6d5d4f13a162e3e1", + "executable": false + }, + "skills/statechart/scripts/backends/mermaid.ts": { + "sha256": "75704b2704a9399a39479ff704a580d5b9a1d0fede01fe771df28bf799b434b6", + "executable": false + }, + "skills/statechart/scripts/backends/plain-text.ts": { + "sha256": "e2bedb855eb3806ec3fe7a7bfa49baa309fb4cf0da425b5fe1e0686268ce255a", + "executable": false + }, + "skills/statechart/scripts/backends/report.ts": { + "sha256": "730b4048578ab0468e1581f16fec60b246e321161effb68e481efb373f0f2501", + "executable": false + }, + "skills/statechart/scripts/bun.lock": { + "sha256": "398b702272b3693222717d08789da97985f5455ecd1ca15663595439c2670894", + "executable": false + }, + "skills/statechart/scripts/frontends/config.ts": { + "sha256": "5eeab1e755f20a2c83b408c6503fdc9c6b10c971d8dedad3f912bd15f9763daf", + "executable": false + }, + "skills/statechart/scripts/frontends/dot.ts": { + "sha256": "4eef9ec848557dcd01ca8dbbec75b71c0653c1b73583108bafbb6f559f6d677b", + "executable": false + }, + "skills/statechart/scripts/frontends/marks.ts": { + "sha256": "eb0120e61a4004a435c7b7c46916da9dc8b2fa8696ed0d3eb64dc953fed3ab11", + "executable": false + }, + "skills/statechart/scripts/frontends/messages.ts": { + "sha256": "36bfc6fb3ffa86897661efb2aa67bd95b55a4da9a3e952831f6e0668826b4b92", + "executable": false + }, + "skills/statechart/scripts/frontends/spec.ts": { + "sha256": "450cdfc8d0577a96818ca3697efebbbf70b530a4ba7e3eee03cf1ba8d3f1a0fd", + "executable": false + }, + "skills/statechart/scripts/frontends/trace.ts": { + "sha256": "7d083cf89b81c41556fc3fd5f07019886e0f6783c632475e7181ac4b81887367", + "executable": false + }, + "skills/statechart/scripts/frontends/value.ts": { + "sha256": "7c54be394b7a10f60591858a0f08fb1e8dc0941df1f38ede07fbe7719ff56da0", + "executable": false + }, + "skills/statechart/scripts/frontends/verdict.ts": { + "sha256": "0d0a9d6b6d6da5a7c48851b86d9d041f22982404dda6288795a1715138d7008b", + "executable": false + }, + "skills/statechart/scripts/frontends/view.ts": { + "sha256": "2c0af8939321c5aaed4b6ecac8ce6a98d0e6774d8bd15d4b02fe4f406b8c9620", + "executable": false + }, + "skills/statechart/scripts/ir.ts": { + "sha256": "5602beca4e4ac08421cfa07e4da989f0e75439f1d136cc923226909e771dc7ff", + "executable": false + }, + "skills/statechart/scripts/package.json": { + "sha256": "3c93c04ccc937989179a2b08f3bbbd009412199ac64b15bf4536af6fdca87a4b", + "executable": false + }, + "skills/statechart/scripts/passes/annotate.ts": { + "sha256": "dc8eeee09c3a9a0c060f32fb1d554e01bd80e5265751cdeb041ef3e19561b92a", + "executable": false + }, + "skills/statechart/scripts/passes/label.ts": { + "sha256": "b5ef402369c5e7846be1fd39165e95cbfe715fc36eb9331a0401b26523c539c9", + "executable": false + }, + "skills/statechart/scripts/passes/lift.ts": { + "sha256": "8977dddc6c93bb2e3914d5b50760eb66876107ba51e2b8cf9e18611b4c40c89c", + "executable": false + }, + "skills/statechart/scripts/passes/loop-check.ts": { + "sha256": "07d459385849aa35834040bf34e279c39baba68244ec9da17c57703a6ee8e2d4", + "executable": false + }, + "skills/statechart/scripts/passes/paths.ts": { + "sha256": "9af1066b007ef86cb05c5e278eef09f090f1e46eb91d356e5146edbd4fee4ec7", + "executable": false + }, + "skills/statechart/scripts/passes/reach.ts": { + "sha256": "b52b38f926814937b733221b40bd31075a69a2fb91ecbce2164752a8684bc5d6", + "executable": false + }, + "skills/statechart/scripts/passes/region-check.ts": { + "sha256": "8a6e0618740a14bb7048f6563b4324958177f4685d4d6d90364cc6245b1498b4", + "executable": false + }, + "skills/statechart/scripts/passes/regions.ts": { + "sha256": "d740ebfdbb5ec7b4984412a635a596b43af7a77b8bcb49a1463c8afa75299f72", + "executable": false + }, + "skills/statechart/scripts/pipeline.ts": { + "sha256": "8aa8d9295ab6762f7f2ffae91a0aee98b1a1314747c875edba4598fdab8ed543", + "executable": false + }, + "skills/statechart/scripts/run.ts": { + "sha256": "f330e0a3b0a9f8181c2e52252c4a47db3f0a64f79a7cca5490263e33a5f13c72", + "executable": false + }, + "skills/statechart/scripts/statechart": { + "sha256": "aeffb7077850fd4c225e3d18049ca6ed7b988cf48e88cd8463526a7feaa74d13", + "executable": true + }, + "skills/statechart/scripts/statechart.ts": { + "sha256": "1c653f05d0cd0f3298455701f36e3d7d1f837f93e6e642f7c20afb5d613c2d12", + "executable": false + }, + "skills/statechart/scripts/test/ConfigOracle.java": { + "sha256": "4f3367314f0fc34e39777d40dd31f96451985b77834fd0e925716aa3b169a021", + "executable": false + }, + "skills/statechart/scripts/test/chart.test.ts": { + "sha256": "3f9022fcc981d17f58b5e7a9302e766241f81a477bda70c3e621d7a1d95f19c1", + "executable": false + }, + "skills/statechart/scripts/test/charts/Braces.mmd": { + "sha256": "dad412ccff506f4ace1b40617bcc2f103ad0372ddc0135ab26535fc488fef8d3", + "executable": false + }, + "skills/statechart/scripts/test/charts/Escapes.mmd": { + "sha256": "211adde39edbb349e2d8acc2f95762bfb7da1fb030e5dc815a8982775e1cd372", + "executable": false + }, + "skills/statechart/scripts/test/charts/Phases.mmd": { + "sha256": "bf6062f6a0ee7b8d487882a1810d17951606fd536b2ee370aecc3d3914da0339", + "executable": false + }, + "skills/statechart/scripts/test/cli.test.ts": { + "sha256": "8a3b028dafe2065029a66242f7a3832d18058e772a0dfc1b2c170511ffee8e07", + "executable": false + }, + "skills/statechart/scripts/test/config-fuzz.test.ts": { + "sha256": "a258744d47529418d32de0fd47aa2d013298581b73064f7ba4d628e4d5471321", + "executable": false + }, + "skills/statechart/scripts/test/corpus.test.ts": { + "sha256": "5686aa487dfec3e0a06b3c15390dbc3ab5953ced532c5eee7d5c5e2f54f492c7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/ChartRun.out": { + "sha256": "05cf40936af6590878a1bbe8290b9fdebe7d33503331105be352b1901584200e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/Counter.cfg": { + "sha256": "ee4c110b174a3bd2cc54226420ba2c979682be57c7964273b8a30d6395dc2c3f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/Counter.tla": { + "sha256": "a277dd9ba29b50b21d87d5ebbbaff3ed87949e427ce563ddb37ede7ba728da05", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/chart.mmd": { + "sha256": "bcdc46dccfa6172fa9e36c3236fb1cb9221d0648534499d5fe0c51ebd13d22b0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/graph.dot": { + "sha256": "4c12b6f39274a6227df5837a066430cc2ce5b928d0c8cf63269bd428367c7026", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/report.md": { + "sha256": "e52a2affff3ca2d85c6ee87a4543ca99024fa0db8a3d48070a6752520121a413", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/sany.out": { + "sha256": "9041a7726d3e497fb10c5d67293d76f3318d448ce08c371a90454c43c65722c1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Counter/verdict-1.out": { + "sha256": "ce981c26c48bba282ebc9794a473575f3981909c4ec47bd9df9327316eae71e4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/ChartRun.out": { + "sha256": "ea9ceacfd5ea703bf19893d82f7e790f3b39441b3588c7248286710cf472495d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/CounterAction.cfg": { + "sha256": "4666d0659cf69047aa41e64a48f307cff103a6b0815d85ead5bd4c0386a0f5b7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/CounterAction.tla": { + "sha256": "895ed84802b88a6deb9653e7baf36b1eb67b848592c91d168d84db03b73c74f5", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/chart.mmd": { + "sha256": "d35190a320e1b9e5cb35ae583b8367d2d10bf4cc17b1f7e94e4edf0613d01730", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/graph.dot": { + "sha256": "906fdbce95c4d0628441a231d9d9124d506d34f95ba08f653f3db084d13c5c1d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/report.md": { + "sha256": "36d49034aab3b675026e717c876ad5da5f74468338295b399da3fd4c3b0e8d6a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/sany.out": { + "sha256": "e529b1c17203bbc05da9d74a48adb9ac4f06cff3f33df94ba215daa8b315dc9a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/verdict-1.out": { + "sha256": "a5e7c90e12c0f9b3b7420db90c06a8277369a4757c7b20ece83689ac577129ce", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/verdict-2.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/verdict-2.out": { + "sha256": "ad46743f4fbbd0557be8cdbec10e127bd90e3fd9c88c50f3338f95118d9d5b49", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterAction/verdict-3.out": { + "sha256": "9b6d49a17a5371709ee942785acf8c8742fdddaed7723d1cb3ade58686732301", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/ChartRun.out": { + "sha256": "c3ac24eafc6f680c59b976e6ddd4ec6272623e1a3641fe43707abbf208c9d9d3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.cfg": { + "sha256": "394991fa4d162ed8ea74e6e5b5473af8c246ec3db12da3737fce046c41e78692", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.tla": { + "sha256": "d6af2fc22ab5fbe8d64ae6202187ce807e53189a43eef1af860db55db933b132", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/chart.mmd": { + "sha256": "928b930e2d28f94e083183cc064f1dde292a5f65fcc0dfec013b04c234e6254b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/graph.dot": { + "sha256": "4c12b6f39274a6227df5837a066430cc2ce5b928d0c8cf63269bd428367c7026", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/report.md": { + "sha256": "b4e4365f95f4b34abea8ffb1fde1bd39b8e8eb6f30e839d75332c6c834460c25", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/sany.out": { + "sha256": "7a8d40cbae42b10eaea8e6dc42f28ce31892e22c77d521e2cb8d8a6d3d0f0c33", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.out": { + "sha256": "ae615936a1dde25fd76c7cd7a98891c2e8e8a363ea3457bf8e51b9412f0b050b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.exit": { + "sha256": "25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.out": { + "sha256": "94f71505e927d30483f539ca838f6661df4488332075f1dfec5eabe188f3f855", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/ChartRun.out": { + "sha256": "819af259403aab9b4eb407b8a6f04e088d9a428d8fbf6fa871c2380d44de8d21", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/CounterFan.cfg": { + "sha256": "e2de6875a48efd06a1db4aae4d7cc5b86ff9a0b4698074049c7ee52bfb49d3f7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/CounterFan.tla": { + "sha256": "1fddc670bda9ad7a900d29891aca1329a9e9f932d353ea944379f6f45bc50a5e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/chart.mmd": { + "sha256": "2f59dcc1236cc70d4e6609139237e07b7e6645f63129b8722ef6e1d44030b0fd", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/graph.dot": { + "sha256": "ee3e20bd9b56b5dc2cc21b653719a588c9dbe08855e0493c262e3c673cea2805", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/report.md": { + "sha256": "016771561b9b5b0a45c0d8c805723a6b8bc39396281dba6d4f6c7b3f1e151612", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/sany.out": { + "sha256": "5a58a05867b5cb952d4b1de562190e85b8dc1485a7201385e622a2af27519c96", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/verdict-1.out": { + "sha256": "546e81530ae614ae7264ae857b00e677cb01211a0194cd88583b0d80794fc260", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterFan/verdict-2.out": { + "sha256": "8e6dc10d24a0ac3ddb86afd8674310c44a8d20488d38346dc8ee896807fc3320", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.exit": { + "sha256": "84b9bb077be0d8a29d0d01ef350d718b77c2ec5f40c3ab90502b1b4b5016c550", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.out": { + "sha256": "78a0d2a62cbb70c1f182a994f1661db2596115b5e53210a2f0125b1390129c29", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.cfg": { + "sha256": "3239ec0757c48dfbf02f914005fd2c9d114e9f2989d42e15184df887e322a301", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.tla": { + "sha256": "c2d7ed7d7bd4b317764d590dbd9bb6cbe7819500c7e6707787fe3065460c99a7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/chart.mmd": { + "sha256": "8baa9330755dd4217aadd731b77fde833f747d09998944ada660b2708cc7d313", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/graph.dot": { + "sha256": "7b0b58221d3980d6d64cb979c22523fbfe5796e465bae79e6d19fdd9c607e547", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/report.md": { + "sha256": "454b4672f07c18e52827e9906fea37c6e701c5d87eea7ca3675163375c02b64f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/sany.out": { + "sha256": "8d331316b1be40a834e166268f8abe94f884bfcfe35135b6492eacb1ee825788", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.out": { + "sha256": "580ce25bf18afc8bd1b9df54f4053b719acce5ca10c2794475a8fba249417eb6", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.exit": { + "sha256": "84b9bb077be0d8a29d0d01ef350d718b77c2ec5f40c3ab90502b1b4b5016c550", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.out": { + "sha256": "3828a6b606057844dedf464e94374092293787b0f4be8b9950931de7d70946da", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/ChartRun.out": { + "sha256": "9323d9f012374d2392fd4f7fbe655e1ed6fce04160270c11ceada536c51ce677", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.cfg": { + "sha256": "f42b6456243b9a8f39599597bb630b68b02b0e66683e96896cb06d2b81af94d9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.tla": { + "sha256": "745a6e54d0b9c285d797481a09d4a531004f46b04fb5ef0b7c35194fa4d6a137", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/chart.mmd": { + "sha256": "28797d6572aa78a9f49f04a3247b62a7047e660af586d8717877164f3bda73e2", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/graph.dot": { + "sha256": "372ed91b09c1602514668fe24101315737f060e607fb959af0b55dde9efc4ed1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/report.md": { + "sha256": "757128a547d5de236c96e47780e5ce8852d1b5873912f31fd4a964222b986295", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/sany.out": { + "sha256": "74535350e20489c088c310d49ac2abab35c0bd3dcd7b9a500b351846da01f62f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.out": { + "sha256": "a10d803555dbc95cdeae0850097f60e04ed20e467760270c8c58400b0629b6d8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPast/verdict-2.out": { + "sha256": "9133f74a060cd7b2ff3364ca90bbcec4596c6fee035468e49e0ad1209260899c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/ChartRun.out": { + "sha256": "a1bf4035560dba1b655bb1577caf63df9f3dd9444380e42c9cffc721c1f6e019", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.cfg": { + "sha256": "b1ca71d25dbb08c93b0ebaf628eb536ef6c98e3e599d8146e0a0c43f5eeb800d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.tla": { + "sha256": "97bafdcba8976d26069b89c31a549d3d7d992cf9cde10d654133d55f2ce604e7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/chart.mmd": { + "sha256": "f74c6486f80c1e9820a901a730eecb3d6f9aa88076f704b87a60bb02b8b24d01", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/graph.dot": { + "sha256": "372ed91b09c1602514668fe24101315737f060e607fb959af0b55dde9efc4ed1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/report.md": { + "sha256": "60a12f6918ea21e77def19b66ff94ff47ce834f7ddbfef4c246570d02a0c6e78", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/sany.out": { + "sha256": "91f7850c1fe9fd4507f457dea2a047abd4013182f56cda2e7aa587b7b4edaf77", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInitPastHolds/verdict-1.out": { + "sha256": "ff6df0e311060e629b11d3f5939ef4e9efb23beafb3d8db9facca963a3a86acd", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/ChartRun.out": { + "sha256": "12e78945b0e37f41b86705b53c09c044a06427265e23613908052a0c7661c5f6", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.cfg": { + "sha256": "f0d016dfb57a8661eac65d738ca9b797242c486e4fe5a0d7379e9424c7de3cc8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.tla": { + "sha256": "cbee0c802f8984ef74bb2d85e7d75635522755540e0175c51ba1cb0a099a9345", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/CounterSteps.tla": { + "sha256": "ec27748a316d01d7384a402166ff3fe36c7364511fd1681797832f2a1777e8ac", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/chart.mmd": { + "sha256": "649d905eb59e1a2972e56396f4aeaff6cafb40c6733c55b5829d36cb4986eb8e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/graph.dot": { + "sha256": "ca06f91bdafb423be1fac388ab5b90d8f78ff96148c2120a61e085f0dc4c108b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/report.md": { + "sha256": "2d6a8b73b1a6f4003bd5c3c669191ab08d04cb03fe441fad8f892b4e8d581569", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/sany.out": { + "sha256": "5b8a514e26f02cf174138ba433a2a3d792c1822011e2c69907ef18f2ba5a27d8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.exit": { + "sha256": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.out": { + "sha256": "79dd3301ed8763da38d13e871e333e1f739ef985b43d586f70c7e45c93a7157f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterJump/ChartRun.out": { + "sha256": "e5a45da16ae7638b1bf30fd72cb482ed4b97183262ee130a73337a2fa4f28a0d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterJump/CounterJump.cfg": { + "sha256": "a94be2a46d740142d19cf03c288547c03cb0fa7d8ab9590a78eb0de7b99ba235", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterJump/CounterJump.tla": { + "sha256": "62e09e72adfdc0bda81d4e4d26c3c8c56641589258b6308d4c3c0e62c882f035", "executable": false }, - "skills/statechart/scripts/bug-states.mjs": { - "sha256": "8c138f76542997f4fc8aa00ccb42f5b3510a4bcb475e04e422cf06c77ae6bbf4", + "skills/statechart/scripts/test/corpus/CounterJump/chart.mmd": { + "sha256": "b9829b90a85d056c88c8742e17caf5058941048dc411a48524115481a29be92f", "executable": false }, - "skills/statechart/scripts/chart-edges.mjs": { - "sha256": "bb1896c1e0eb6b2bfe0f1e5ef71b194a3096c4a6db1879514bcfc3818d3d644a", + "skills/statechart/scripts/test/corpus/CounterJump/graph.dot": { + "sha256": "f829d5cb1d9027aed499564e5574c8955d91a9b3decb32179000de757c9817a1", "executable": false }, - "skills/statechart/scripts/chart-parts.mjs": { - "sha256": "567b4700cba97881ec129cc9b2ed6cdbe9e1f041819ea37957dd3def5261b754", + "skills/statechart/scripts/test/corpus/CounterJump/report.md": { + "sha256": "b459f42807ac3dc2d0ac0f26aa13b549525e0b946871e787ca07e58f6c8ade98", "executable": false }, - "skills/statechart/scripts/chart-to-tla.mjs": { - "sha256": "e26b0decb4762ee4c05cc96f4ba1454bd87ab6ae49f0f4bde74a29a4b493117d", + "skills/statechart/scripts/test/corpus/CounterJump/sany.out": { + "sha256": "68b6e92c3232530a43b49e8292eb0a6e2e8e2c8e04a7e0932e59e7d38f8d4527", "executable": false }, - "skills/statechart/scripts/compare-charts.mjs": { - "sha256": "1fa3c1aad875d38d6d1702fcbbcdb7592bc9a3dbe871cdbd4f9ddeda1bc859fc", + "skills/statechart/scripts/test/corpus/CounterJump/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", "executable": false }, - "skills/statechart/scripts/lib.mjs": { - "sha256": "ae8ebe18e09419a63b23465f9a53c9117e04de12a7bc945b1f75ce66a367cc5b", + "skills/statechart/scripts/test/corpus/CounterJump/verdict-1.out": { + "sha256": "9ceb7d389d7cd13f894db85dc00f0b1f0aaccfe7931804202ad6ab770e284461", "executable": false }, - "skills/statechart/scripts/map-view.mjs": { - "sha256": "d9ca92ba29a9d5e4143c64826c24b34cc41d5cac28aeee6a25db485e5b2c7d4f", + "skills/statechart/scripts/test/corpus/CounterJump/verdict-2.out": { + "sha256": "7e52bb5a6f8ddb55669eb03ea5dd360ba4bf7f5c3155a1078f1ba152805038d9", "executable": false }, - "skills/statechart/scripts/property-loop.mjs": { - "sha256": "437b66fa85d45bddc8f743fc96af3f3a358f6c3d4da5d55e23dd04c4468f00e7", + "skills/statechart/scripts/test/corpus/CounterLoop/ChartRun.out": { + "sha256": "8e7aaaa8f605109e5dff38439ec5a9ff4d3b94d02612a4b1ce2763754a66cb54", "executable": false }, - "skills/statechart/scripts/read-check.mjs": { - "sha256": "f4124d09512b62c056ca81fcde91d9dd1a4d07a99de950ec945df83f044772a9", + "skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.cfg": { + "sha256": "f0d016dfb57a8661eac65d738ca9b797242c486e4fe5a0d7379e9424c7de3cc8", "executable": false }, - "skills/statechart/scripts/regions-chart.mjs": { - "sha256": "c234623411a2d5368737c8ef4f4012ec92f902340d8fdd527d08d015a448bde9", + "skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.tla": { + "sha256": "84449896e318fd129b82f574b644ea16a73c1f23e9331cdb9361d5a284e17cdc", "executable": false }, - "skills/statechart/scripts/render.mjs": { - "sha256": "b813236db10ce50a4d04efa68953eabbee0cdc30e3fa96c03b8a2e1f477f3e57", + "skills/statechart/scripts/test/corpus/CounterLoop/chart.mmd": { + "sha256": "0157bf074e7aa642a0819cd6eb2352fc57c21866962f2944bf71e2c028c9bff9", "executable": false }, - "skills/statechart/scripts/rule-book.mjs": { - "sha256": "d26a8da74d105b5807ef8333172470660853e61069cc2573817ab7811755dde2", + "skills/statechart/scripts/test/corpus/CounterLoop/graph.dot": { + "sha256": "6558ade4931cfbe5c28ed713504072976d401072dad6acc4506c84afb3169b99", "executable": false }, - "skills/statechart/scripts/second-replay.mjs": { - "sha256": "033325ff5440a81a11aebdd485de4e2fd2f703db9f9b84c4fe24bc274d9df285", + "skills/statechart/scripts/test/corpus/CounterLoop/report.md": { + "sha256": "fb9d5d76f6c1c00746f9fdc211ee26eb82c5649ed212c2fdf9e9810a4a33ab10", "executable": false }, - "skills/statechart/scripts/snapshot.mjs": { - "sha256": "9ae5040255945474f45d09a150cf4659acdff780e18d62f8a084bcb5c79543eb", + "skills/statechart/scripts/test/corpus/CounterLoop/sany.out": { + "sha256": "2a3cb63115eaaf7e19557949fc97785d112b2dcfec55dcb00583daa8926d19f7", "executable": false }, - "skills/statechart/scripts/spec-text.mjs": { - "sha256": "494f56d5577a09f9e93057fb6bfe5e3e74df53644e9992f4452ed7008f6f5aab", + "skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.exit": { + "sha256": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17", "executable": false }, - "skills/statechart/scripts/statechart-help.mjs": { - "sha256": "ab38345c41028ad76d2866b03b3f5aa2c8303c3223c87dc9979effbeec79e46b", + "skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.out": { + "sha256": "33158ae03c65371f4b028818420652deaee879b77f4e6b8da688038079e98511", "executable": false }, - "skills/statechart/scripts/statechart.mjs": { - "sha256": "c24a83dda26aec03a3a0e5a961ece983a8283f6c4c66334fd229c2d2e8d7bb36", + "skills/statechart/scripts/test/corpus/CounterOrder/ChartRun.out": { + "sha256": "07e4fc2dca6afe0d621dd26603330c59a6052d1bebb19f395ff8e30c3772e667", "executable": false }, - "skills/statechart/scripts/test/band.mmd": { - "sha256": "9ae80a75bce87c8126095b6d9de3669cc69ae65d850acd6d4328ea9b8538d5ed", + "skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.cfg": { + "sha256": "6f321b62ecd415eda4363ce98e05b470d277f8b169dee8658a267668e7f3fb5b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.tla": { + "sha256": "173e3ba50a4b662464e1188d31d5c306c0202a03385978a8aa841cf0c2f5e8a0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/chart.mmd": { + "sha256": "8259183f842918dfaa6f70fb805db9db5a61f3db0af13c2cf09e9e131136d8d1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/graph.dot": { + "sha256": "7a5d0a1efcf36b579f640e283c38a2054c8740cf96bb2b332bb42e2aaff8481c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/report.md": { + "sha256": "a37510137bc3d2c9bdf2be48a40aca3f36b44aa5b9be1f4f2f6cfbacfbbd2693", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/sany.out": { + "sha256": "e4d8472643a723ed5b3cc8772af07d24d534edcc1a6d36b2690de2d395906ed5", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.out": { + "sha256": "a9d4f6b1b08b5ee7505e20d9a0f90a88cf533a1a4737fd71ae70a162e520af6c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.exit": { + "sha256": "25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.out": { + "sha256": "57fba1d11f08153f7598c7a6843c557d4804d7ab57b59cf68eafd22116267de3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/ChartRun.out": { + "sha256": "c6a688529dfb153ca9206928f04563b2c0f2cb448bf1cf6cfabfeb94981f9d72", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/CounterRules.cfg": { + "sha256": "931efa110d01b684843f905cae87fb2308b70b324ca9f51ab1bb6617595047c2", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/CounterRules.tla": { + "sha256": "57f1be6cc35c3f8252aacc2916975045d1932fab868d961d9c461e23f666eb86", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/Moves.tla": { + "sha256": "3c58aaa6098d94f727d427d7ce65ebbc79d684c5a6d7b8e18ccdf2b9222a0a56", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/chart.mmd": { + "sha256": "a8349b6203985b4cf3f10d581d7a15a312c19262ee7a8de8b73ba62311a817ea", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/graph.dot": { + "sha256": "80b70738200d8b16e52b598fd6ddffb607f672550168cc8756c8605180210fce", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/report.md": { + "sha256": "6db41c347e7d680d126168093dcce483e5406c5ea4728b32149f3d8c104b1259", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/sany.out": { + "sha256": "6c96b7563f60ca3b1860627a5588093b401d30c13724ccb2c77cee6238c69667", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-1.out": { + "sha256": "47b09992efa66d8a3ed65f24d25f5227dbfb48fe2b46b1803ef07ac461122073", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-2.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-2.out": { + "sha256": "fb0503db6628e034c70ee48dc402bc79514564a438ff2c61d02f7533faf7d64c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-3.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-3.out": { + "sha256": "8cd2712e7ec2f9f4941e1ae12c9476126f19c1a1a11d2a20291ec131e2f63819", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterRules/verdict-4.out": { + "sha256": "b776bfd4513942329b885fdd7fce1bde2d25c063ef81ec382eba7769555a53eb", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/ChartRun.out": { + "sha256": "0cc81c2f968b23b4c8c1fd8e1c2032991a8d2d6137ab267958aeb7aee478b240", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/CounterStart.cfg": { + "sha256": "eff943bab49dc0cad0567a0d4456671724b778d7913a2ba38de16a12386b7a58", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/CounterStart.tla": { + "sha256": "dbbb6c141d88a7bc4dc354c89d85ce9623871070b0dc93ca7ba6c0ece2043b28", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/chart.mmd": { + "sha256": "fca72ed62e89b903e63a7f2a646a29cee33f72b1b60a6c96793bbcc807e09f74", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/graph.dot": { + "sha256": "1c259eb08cedb809a62f24d59232f229d2cc0df8f6bd97285946ce2d586daaef", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/report.md": { + "sha256": "01444b757bca7435af323af5a6a31caff91388bb84ab48948b2660a80d78b8e4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/sany.out": { + "sha256": "61a0c605d5ab3806d5ea8779ac00d344e4b19a2c423288b48fb893037de63425", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/verdict-1.out": { + "sha256": "152cbd7654bbe39c550b3122009fecfa07dabed3e658ac4845b5f5f60dc309de", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStart/verdict-2.out": { + "sha256": "e4d43ce2f301d2ac1c03693981dc6a7b3c84c7af671f9b0d6bff1c4b287c6876", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/ChartRun.out": { + "sha256": "11bcbb21c9a5b3c5491e436c8fc61d339a7c27593fced2199132ab6cf8e4d489", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/CounterStop.cfg": { + "sha256": "eff943bab49dc0cad0567a0d4456671724b778d7913a2ba38de16a12386b7a58", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/CounterStop.tla": { + "sha256": "e4486923b563ac7fb028e8513e79e3caa5e0905579d423569e9c918938e0f12c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/chart.mmd": { + "sha256": "0ddf7a93d1317fa0414c0c5b05dba856c268a2124c4e06caebc3f5b4ad6f393c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/graph.dot": { + "sha256": "1c259eb08cedb809a62f24d59232f229d2cc0df8f6bd97285946ce2d586daaef", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/report.md": { + "sha256": "5d0e28ce1ceefb740ecc85076c8be6a5a5bf00d00a8bc3a0a00b5494dfbf025d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/sany.out": { + "sha256": "933921ca1dcf5b7e089d2aca59ac79a50e521f22c60cebd5812be4e3e67ec58c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterStop/verdict-1.out": { + "sha256": "9d4714340d24c6d5a1a22ed36b6bce0943c8eeff28fc32bd0b54fc1aba2b9331", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/ChartRun.out": { + "sha256": "bce917da55d7f549b2902c3ebbf2a7bfc7da9d7de8e3e099a9dc6c7fa815fc37", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/CounterTie.cfg": { + "sha256": "b3d7d0ae1bba01c98903b54cefb4ba76afbb3ce07957431a03e53505ffe95510", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/CounterTie.tla": { + "sha256": "e668ff93e2213651d6bb14e14255814cb37ab61062466384b953563b2d844daa", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/chart.mmd": { + "sha256": "0fcc808934cbf91f592eda060de978064e4854c704d1c861332f875cf7415543", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/graph.dot": { + "sha256": "906fdbce95c4d0628441a231d9d9124d506d34f95ba08f653f3db084d13c5c1d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/report.md": { + "sha256": "9649f57adfbd71a5f4430bcde0d95234883b1daf80eff2d91a0dbd1ddf6d3739", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/sany.out": { + "sha256": "4ce39c0150c1cc1c9d657b927f22103fc8ecf1d0f0678137970ac98fe2a8d355", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-1.out": { + "sha256": "62f4153c4a5e690d868b0e623c83c272a1a15ea3b9fde47f5e4ebafb6f3766d7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-2.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-2.out": { + "sha256": "bccbbf5d54a039d540712a11047e6a8d4ae168c1294e15de4f78e12e678ac7cd", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-3.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-3.out": { + "sha256": "f832750ac5552dbcc3515ec93ff959fd6f70e6462f590d7c1cd3493440f8c6f4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTie/verdict-4.out": { + "sha256": "7589bdcdaf666e3b8f1df98ddd47034fe44450e0f9c00ea552c749c7544465a6", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/ChartRun.out": { + "sha256": "4f0a042594b6b512195658761c84bf0054fa14fc6d52e72a78113a8230a28d93", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.cfg": { + "sha256": "4a48e4418169113fdd2abb891981d4917fca6b76915e38f6bcddbc5596786195", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.tla": { + "sha256": "cf1818fc7d96bd4082f9260a42b7e792d5e6494ce7ae6c97730b63e8085e0ac0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/chart.mmd": { + "sha256": "2c3fb00ad851c1bab2516ce25838209c342cbc8c5d3d795a54bb6eac3ad5d0d0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/graph.dot": { + "sha256": "ec76c05526f13d71d561cd67118da823cb0337af4f4d7b011d84f420aa8663ed", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/report.md": { + "sha256": "efb16c681db5ffef7312d550393bc62e1b3682615b06d8f2991fea29ba3938a8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/sany.out": { + "sha256": "eb1991c5d584f35db0bcdb3ad4ab5444be5c84f324f177e51b95bb8b46f75324", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.out": { + "sha256": "2cdd96422ac6aff9f266db7c2b644b02c047ce23ca85486cd5c62cb7b4f9253b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-2.out": { + "sha256": "cc159ae8b3034dea7f055fe6290c0becfc8da8d879459b7ba0421fd57b750e53", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/ChartRun.out": { + "sha256": "1763153dd2d92f64250e983f283e622a7c7519aa21b5357c07f5931225039314", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/CounterView.cfg": { + "sha256": "f7f1939032d3fb09af0d2e38907b2f8e3da73cd6f90769a801638c4c50f9ccdc", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/CounterView.tla": { + "sha256": "273e549d576351809a2220003ae1b3c8cd2e687499be05fb21b3ce461d1b5162", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/chart.mmd": { + "sha256": "e6e0208a45a15a8af7be73b8e89ee665117414c3fc082679f35b250f2643cd05", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/graph.dot": { + "sha256": "906fdbce95c4d0628441a231d9d9124d506d34f95ba08f653f3db084d13c5c1d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/report.md": { + "sha256": "2a64bbb36844f44e97626e6c0579ba1aaa6fdaea99216b7322642ab07d1cbbb5", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/sany.out": { + "sha256": "a1cd34d2d89ca38c44999e7376075047947927899d406c401c87308fd5ab203a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-1.out": { + "sha256": "5981442a7a969e88a98934eb3244542f9c8ec99f73de9050280103f12cb0c4a2", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-2.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-2.out": { + "sha256": "6237dc3d67262ecfac29bd09515e9fd81eb4c7b0340cf944fb841864c50687f0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-3.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-3.out": { + "sha256": "c72cdc09ad69e03d04f4ee840659aee046e0e6af39a9c839e6d8d20e3dd9762a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/verdict-4.out": { + "sha256": "27f4981b9ba54df20bc33114fea2e521cd5a476173eb307a7cf5ff4c8b3f2c91", + "executable": false + }, + "skills/statechart/scripts/test/corpus/CounterView/view.mjs": { + "sha256": "5f6114fd8ca165604456048f2d7e2c29f667eb4537076647aaf3bb856f1c826e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/ChartRun.out": { + "sha256": "dfd5fadfb9ea0030ecdf82c184268d09ce877336f19e3b9f2f3b95866099538a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/DieHard.cfg": { + "sha256": "d75cf14954dfc65a36abbbbde7b6d52b77c4df36a4fdf64974acd51adfdd13f8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/DieHard.tla": { + "sha256": "8a63111dc1aea97f4b32cc6fb7f36112775ef65b5a8a8603fbd35c493eba306d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/chart.mmd": { + "sha256": "89057b576e59f437dd8c96a25edfd6891512d9b5788c5883e7d1affb6b5a9681", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/graph.dot": { + "sha256": "6cf64ad56b08000c6a3f05aecd54331b71192aca1de3d737afe24b157a06ee84", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/report.md": { + "sha256": "044c2a2b41ffe7f522af78b33f1c0fa09f822b42ae0913560db355e88b9cdb4d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/sany.out": { + "sha256": "818c71c8a84a41a626740fcc61608ad77d45d8cea2175690163da289f1eda4bd", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/verdict-1.out": { + "sha256": "24363932a41b49f302c1dd934c36d583e443850e3c34677a423aecf49fc9081e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/DieHard/verdict-2.out": { + "sha256": "86b8e78b53127ebbfc013ad1423d4ca66c92d118685508dbf57307076d7cf9b5", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/ChartRun.out": { + "sha256": "6514873d106dda3ed051e38f2e7df8932c597c3d27e68538899ee9c2f70a9405", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/FirstRule.cfg": { + "sha256": "422dbc8bd263458fb1706fdaa4888ca3375ca5db1394dcf6b5aaa6ad0645fede", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/FirstRule.tla": { + "sha256": "9f1b2ddb4a573c4612cee6657f728117656a2dd1ab9e8e800e171ee1f3cbb1f4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/chart.mmd": { + "sha256": "60b3c9d31e74b633d5d97a2847aab3bd3fb4f63813e8e0d8a3a493ff5dde1bc3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/graph.dot": { + "sha256": "a1b348348bf032ff81cfd796ae19f8cdcf491d5af18eab73ebfca3058a15691b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/report.md": { + "sha256": "2323122a1509c6c554a18da72a549922e364d2e89f0e5d274f1fa4b89f14c6e1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/sany.out": { + "sha256": "94176927597f47550c6ca1cd91807367a1e2ce968c1eceae20256d96cfba1139", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-1.out": { + "sha256": "f444852a2c5f029b3d76a3d1365763435e8cfeaeedab4769bf0d580dfd8a5f70", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-2.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-2.out": { + "sha256": "1c7209d668eeb0afccaed1e7a7dff0b4d813d4dca12a440e657a1cbf895a90cc", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-3.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-3.out": { + "sha256": "3dd39a3d8eacf4e1770b22fd55bed88d03a01b44d7f592981e66279707bff3ee", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-4.exit": { + "sha256": "25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/FirstRule/verdict-4.out": { + "sha256": "6a6efbdf26c39e43588e2c4662cefea87a869d3700096347e2064ff9db439002", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/ChartRun.out": { + "sha256": "263a6666f3bd0a91020bdc2e674285889db6160e62aeeabf81d3fd6c80ad5898", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.cfg": { + "sha256": "1ff2a58417639cdb8fd7fe134d9acc49badc7ddefae309e8a49343550bb37dc7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.tla": { + "sha256": "1cdb3a20c553557f60fddc93d0c2a57dcd815f0028020a58110ff92ae5e6ef1b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/chart.mmd": { + "sha256": "7d29b01c2bc123ec82c9ccf12a082b2bd700afa6ae5f5e4451c08fba553b317f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/graph.dot": { + "sha256": "abfe48a392e3f5217684882942261deb1a117e63e3e3836f037a551019fff710", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/report.md": { + "sha256": "bb7e070ee6e8b332ab4a4166c33989a5aa38bc0e0ecf75a93c8935d0680c8b82", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/sany.out": { + "sha256": "86bc954e5dffa1c9516ccb8064610f66668db25a34be69cc03bb4a5736c2a566", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.exit": { + "sha256": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17", + "executable": false + }, + "skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.out": { + "sha256": "3719bbe9a22627acc9684a503de82250dba80ed9ce0859b1fb09b6d30db2553e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/ChartRun.out": { + "sha256": "69e092decc546972f84ff731b24cb5d2f4d75eb373dbedde38b8aa136f2bfdb8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/Login.cfg": { + "sha256": "97707bb4e8fb4a42465bade63b9be92dae0ebcc877530d588e5bdefb3687655e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/Login.tla": { + "sha256": "25aa3c50791c7f24fe96243baeef4cc4281d9fa37f29abb78533862bd6b6e9de", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/chart.mmd": { + "sha256": "6cfda25977c467c106f9fb7f5602960bdd40919ac9294994ccd3bb33346faf35", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/graph.dot": { + "sha256": "f120b5583e931755578032d87cedf5bcadc4cb4c4ef497605b4290e1de704673", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/report.md": { + "sha256": "e0132cf83d5637c96747873fa6ecdea23961b6b3b818e6db65748013580fe87d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/sany.out": { + "sha256": "e102731bbbf16d04bce331007e37f29c9f90e0f1371eb5a9cc88f65bc7d56979", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/verdict-1.out": { + "sha256": "92ff242b55fc6763a13988a98481a54a1ab19eb3f0a4514d30efe3a48e45b581", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Login/view.mjs": { + "sha256": "f59cef65025cb9aacade3fa5027c5cba15a44cabbfe64084f6dedcb173cf93e7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/ChartRun.out": { + "sha256": "b9578b274bf3458ac3b721eca7638f48e97874a32765e0db95807cbb7f9609aa", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.cfg": { + "sha256": "7877ce4ca1c561150cf6a447f61f273742f4b7112ed84f10e827b38f032ec6cd", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.tla": { + "sha256": "c62583af14b40df94a4276438edecfdf482d2b185eb6b302c3db2dfe71c129e1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/chart.mmd": { + "sha256": "298760e9f3334c9807f2c3698c1f487981e9cb67c2688e6d910a2904321383f8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/graph.dot": { + "sha256": "3e2ffcda3ccb6fa266b13f6f19d18d81d819f9e0e9809e91868edf77d9663237", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/report.md": { + "sha256": "6476c58f4938e059179774f90a7cd1829b2c7c38903223c81a193034ebae83db", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/sany.out": { + "sha256": "e8e73f621775ea526e48dc565b3cc0010a22a90ca075e0fb0ca2f73c0522840f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/verdict-1.out": { + "sha256": "46b5c3b2cdbf05571417d8aff407d103d0d4755e8e8c0ba46186391a611a6364", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginGuarded/view.mjs": { + "sha256": "9a45c5718c3eb1171b0a2271e79612e6af833038f13ab8e16b37f15947d53cca", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/ChartRun.out": { + "sha256": "2dda75ca79ff432a8f74fa504c8876f01441f8196b274f4f834959c49576c87b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.cfg": { + "sha256": "ab2bc033be4345d363d5845fb6bf573c4b79b477bdee965625e2a4af44fd9c44", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.tla": { + "sha256": "8f93509fca05425c054e7ec518e9653097ac9464c98fbf426fee551f940b2e51", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/chart.mmd": { + "sha256": "d00f84c57486de54bde56ee75ad223e6936bd1b0334319cef60ebbee899a2480", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/graph.dot": { + "sha256": "8f4d20009e23824b0bfbeddd2ca9d1c12e764599a7e179018c98d432d091ab86", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/report.md": { + "sha256": "ed54afd8a979fb2c7eedf28b0bbdd1bfc91b360d883eacd8254483f1db5155d0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/sany.out": { + "sha256": "9906450ef11cc5c94325cef0b648736135708d37ee3a41841b55035f41e43309", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.out": { + "sha256": "dcfd62876aa6e62fd08d8dad9b38a5f54ec86725f92c48e8328ac0461dd4be67", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/verdict-2.out": { + "sha256": "bad6cc9bf2bce357642aba04e7fd952800b718e7a518b9679764a4058465d1d0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/LoginParallel/view.mjs": { + "sha256": "9a45c5718c3eb1171b0a2271e79612e6af833038f13ab8e16b37f15947d53cca", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/ChartRun.out": { + "sha256": "2460f0f517117836b5a02e70e4ef2f9889061f1b2cb83ac4bf8fc41532fa2b1d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.cfg": { + "sha256": "0f1a86e105cf1c303cedc510b4fe080175fc77eb88fe98c1eba163d04436ffad", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.tla": { + "sha256": "ace4961900ceb44281c94879c05550123a74c5427e9fcfaf3dfb134f51c38390", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/chart.mmd": { + "sha256": "e3a6e6987bd8c6aa9fcc480a5f19e1c8c954804935f2d5f938279e6e7c2f6ceb", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/graph.dot": { + "sha256": "881d999fa6e60ba7698ea8633f16a315be97a370aa300cdea885d04333028534", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/report.md": { + "sha256": "ef407dcd4e536034ec7512076364831c7f904912cba5ed6f53659db02e13d82a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/sany.out": { + "sha256": "6385013e3e792b0404acc8ae8c42718fba4a626ff83590bb680318c08d497e88", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.out": { + "sha256": "b6f6af48489b17cc9d21ccaf851a555ec8e51d29bc76486b11801b4ae1c11b86", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/verdict-2.out": { + "sha256": "d9eaa58bbc7364560b1365ef1fe74454ab97ca093cbf05a9bb14d530145165cf", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayer/view.mjs": { + "sha256": "019383f6b4f753b5721485f3cff9b3762e046f4cea134f26a709e9ce4c43dde9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/ChartRun.out": { + "sha256": "1050612e030b11a1f6e1942880bfcfb634913e0123fe04133e38554553cb79f6", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.cfg": { + "sha256": "2d6715915152436fc1016313ef8dd55251c0f712417226c47345228527acff83", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.tla": { + "sha256": "59c4c86bfa1f38e0a01e8675d9936558d9a3899c3de8243483e633674a6bccfc", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/chart.mmd": { + "sha256": "1a77aeca211f5469041f03c6d149beec17f300d6cb5e5386e5f73d6a214f73ae", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/graph.dot": { + "sha256": "cb6b2d85fb70cfa6aefe16e3fdcb686b60f6593ec755e04ed5c4ec15867766f7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/report.md": { + "sha256": "ea569763d18889ff07c5be90ac6fb74ca83f4a19f1ba79000fa151c692d38d54", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/sany.out": { + "sha256": "22f3486dbbfc11c5ae0f62e25aab9742b0875285c5de8e490f5cd84b1b948bf0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/verdict-1.out": { + "sha256": "442c9923009f88f8f462a2bb4533987b6797495bdfee359a74c49b1a360fe386", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerFixed/view.mjs": { + "sha256": "019383f6b4f753b5721485f3cff9b3762e046f4cea134f26a709e9ce4c43dde9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/ChartRun.out": { + "sha256": "4a67e04e5c801f92ce4a9fe7bf48fdc3a800abe6754d75c0df2ac0f7fa85af68", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayer.tla": { + "sha256": "ace4961900ceb44281c94879c05550123a74c5427e9fcfaf3dfb134f51c38390", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.cfg": { + "sha256": "d9995ff6b04506d0531f7264a85c932fe0e4ea2017608952d6908ef9ed31f828", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.tla": { + "sha256": "31f510c61062eaede29be56efd920aa0cfdb9851e09cdbb0c971468c48e54985", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/chart.mmd": { + "sha256": "40e8330310aa4ec838ad4bf99b12e5eb0ddc7b6ec99bf21d764c05ab05217d9b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/graph.dot": { + "sha256": "881d999fa6e60ba7698ea8633f16a315be97a370aa300cdea885d04333028534", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/report.md": { + "sha256": "5a68beff9cbd182489e784b065f588a48ec61c60f87ed70dafe80ee0fde56ef3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/sany.out": { + "sha256": "dc86d7c73175b68cf92d65d35758da6836a38e0ef3d5dba209c80b90df6a53d4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.out": { + "sha256": "364c7dfbf9cabaea6f15e96811df209ee272800f5d5b671ce27532346dc3215d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-2.out": { + "sha256": "e01f79327c1e8a2978629ad8e5486baf95dfb0dd4ffd44152b159b8d35fb0bc9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-3.out": { + "sha256": "8f9d8deff62a9625782caff95aa89f03607aa50bd31f31b5d26f459c04757f09", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.exit": { + "sha256": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.out": { + "sha256": "04579153733514c4c7bb351a04764e1a9dc5f6815b7dc056fb51ee0dec5ef48a", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.out": { + "sha256": "274b0b70a9e7cf8d6c140a968b8674be5fe8073252db78ec6b823cc7f40f7538", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-6.out": { + "sha256": "8f03e3f3afb0378a13715c9f90a3b59c1f46a53f295f8eb74f1b61c62d832a43", + "executable": false + }, + "skills/statechart/scripts/test/corpus/MediaPlayerProps/view.mjs": { + "sha256": "019383f6b4f753b5721485f3cff9b3762e046f4cea134f26a709e9ce4c43dde9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/ChartRun.out": { + "sha256": "caeb61ac10b01bf2e6044cebd316b599793eb075fb0256aa81c696e4e5c7a7ce", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/PickWorker.cfg": { + "sha256": "0da0617d4459668020b0bf88a29b1eee01882c88f204728eb9a38493b60bd4b3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/PickWorker.tla": { + "sha256": "8cd92057affd06b367a85c1d87c81b827cfdfd41a2bea10989ce911ed2fd3124", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/chart.mmd": { + "sha256": "5fff795e5d0da737cdf1fef95c993ea6ca45fd9f5fb61e163d2e292c40186a64", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/graph.dot": { + "sha256": "1d60f0497f939134c6cb91f5e869445aa7da04ba8caad5128f1509a28c31f245", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/report.md": { + "sha256": "87546454dfa4b96d15c53dbdc6eef469e0390f14b4ff1eb370d4437508f90da8", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/sany.out": { + "sha256": "866b218db376118471b5635ad1b050c14552c1cc7df37d65f9a5ce5b4734dbaf", + "executable": false + }, + "skills/statechart/scripts/test/corpus/PickWorker/verdict-1.out": { + "sha256": "e994e9457f86627fe3db72c80f63513931b75d192b3387b2ef6132c398780b55", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/ChartRun.out": { + "sha256": "e01704359d955974f20afee7ea1d5f4f120a5319c219929d747c9e9bc2c9b36d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/Shadow.cfg": { + "sha256": "4f0262dfe7368f4c8df45c47e912fca78885a24053b31cd67c3b33f49e61f7a1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/Shadow.tla": { + "sha256": "c7ed4c7ab82d7ed8515621d4ff88d349920cd1a4783a2ee47a71278af9dfe148", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/ShadowLib.tla": { + "sha256": "3590a4185a432b4fbaa867d7d717482a0d57e2374711bf3bb65244cc936bc666", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/ShadowSub.tla": { + "sha256": "b84408d4b14dc56402b7cc8e0e11589f8b0f7bb3dc3f1408afcc7518f3fb7f1f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/chart.mmd": { + "sha256": "386fbbc905c7c89c4457a3d48f4b76bdf42861093188dc7369c32aef7b48658c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/graph.dot": { + "sha256": "a1b348348bf032ff81cfd796ae19f8cdcf491d5af18eab73ebfca3058a15691b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/report.md": { + "sha256": "16788146e79b669a06152e69fbb0b3ef738bab2bd37c6c278137e7b96373dd97", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/sany.out": { + "sha256": "e7396792c0a425651e9765498b6922a95fa63c37d3fa9927137b3c35596eb310", + "executable": false + }, + "skills/statechart/scripts/test/corpus/Shadow/verdict-1.out": { + "sha256": "b6df6a9904d6b1fbe9fb20ae1d7f2e52aded6a7884e6e2514db4e984d6d2c4b9", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/ChartRun.out": { + "sha256": "68bb08c06e2c22ec57dd84e595c2895f01bf6c7be3cadb8ae3a942c662888f0c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.cfg": { + "sha256": "e32044a2d32c9e2c9c9459b257ff062bd33028914d392b0ec1d25815772dfbfa", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.tla": { + "sha256": "0685a3f78ae2d30bc883201b3c9fc16f9f590b15d02a62295da3cc48566d777b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/chart.mmd": { + "sha256": "de161c72bdfa1feff2ccbebbc39db5fee3bd4b1d789945802b310838875a2bf1", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/graph.dot": { + "sha256": "1f9e6cbdfd8b30ca4ec376261e8fb6114a7efe4ef1a5ec01ebeeaaddcabce211", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/report.md": { + "sha256": "62126c7b8928173a53952a5372c703b6c6af5155331f6b8547b21e957319540d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/sany.out": { + "sha256": "415faa44484371020747737d57b69bec28640dce60adf5cbeac26d0a79f89953", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.out": { + "sha256": "05335eac8e0ae5eeda209c59bf7b000c411592022f9afd452847b1d6688319d6", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.exit": { + "sha256": "25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.out": { + "sha256": "abf2a12c671af7ef26fb4a0a0f59accff35b36251c278bdde7067f00558f7e5c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/ChartRun.out": { + "sha256": "aed20dee26285d007a0529727c67ff86d52c1eaa3e1ea4c56472df4f2c5b1b92", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.cfg": { + "sha256": "a4e4546592e8e5d361ccbeb4808338e15fb61338fdbc84c358e9b0a41a58a704", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.tla": { + "sha256": "c27868893c2f4de82a0c561af6fc65d2abe233e840d9a2d830920d1487ab793d", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/chart.mmd": { + "sha256": "e8761c527163b1d15d699da7f7e026337c939e51b2f82feea82a93ce1d67482c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/graph.dot": { + "sha256": "0276d507b1457de02d25cacced67c19b3442764225bc183e90c8c6745ba5f680", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/report.md": { + "sha256": "6b847fe8dc3e043d17c1ec3df7b18c7d618a1b7a351eb8f1943fa5d70a1b640b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/sany.out": { + "sha256": "3944566b6d4e0b3457c9b30320041e19e97d45f40a01b9a868ddeb9d7ad6304e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.out": { + "sha256": "f22ad5c0f211bf7ad01aa04bd2f461c9e48ed394d1814a7445b990c0f91eec75", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/verdict-2.out": { + "sha256": "bd8a217318a09cd985601e9483dcc061d502348cfc3e33a684ed35240b7cb5ba", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhase/view.mjs": { + "sha256": "25bc78b0ee8c7ab24ed1f0dbe78ed60adce0f086579c03bf8c97d02c448c7f26", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/ChartRun.out": { + "sha256": "73094c6a6b78588569eef707f88ce7d6f7da4505a4882a2ff0cc1dcffc72b0f0", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.cfg": { + "sha256": "a4e4546592e8e5d361ccbeb4808338e15fb61338fdbc84c358e9b0a41a58a704", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.tla": { + "sha256": "9d9459496de208594596804e0a70faad6390286c7b3fc2b9ae173e61d25ade57", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/chart.mmd": { + "sha256": "d7a0b12bfc62b60507334c045369ef53bbeba7e432f0c335a5c0a72e0b667fe7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/graph.dot": { + "sha256": "2a9a55c417ecf703035dce21c7ba347ad6cb3dc50a0a634a6b60f4bfb727a773", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/report.md": { + "sha256": "598a1594b7768b770d1c4df3b304ac26c2890bbf5bc9610541c5dcbd26c12d9f", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/sany.out": { + "sha256": "2eb698b9b60b3ec587f382e5a6398b32da62e08e9803e1121e627a697bfaf061", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/verdict-1.out": { + "sha256": "a85d46fa117924eec003fe28749f71bee520016d65e3d4cad671768635157600", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TwoPhaseFixed/view.mjs": { + "sha256": "25bc78b0ee8c7ab24ed1f0dbe78ed60adce0f086579c03bf8c97d02c448c7f26", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.exit": { + "sha256": "461144ccfd56ee3cf0f9a9d80e520c5b872166b23092d5fd838ecbdb46d64dab", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.out": { + "sha256": "d3a2f8df56c31c081e5c0b27da754180cdb5b2a62073467421a90fc8514c4a08", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.cfg": { + "sha256": "ddd3b43e9455ebfe7ed7c487867352b9466bf60fb35900b84ad5281fe6763766", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.tla": { + "sha256": "ccda004cd0e3c40a7c39c200095a12eb3e0cc7c40f9ecabff24026c1cdfc53b4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/chart.mmd": { + "sha256": "e180050487251de907e1a25c34a3acf28159d28a3166afd0786c7f8bd1bb52d5", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/graph.dot": { + "sha256": "a35107023f5a7a2e9833c3c0f67b69e163a7331038510b3dafcd4f4092db7251", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/report.md": { + "sha256": "4c954c446383fcaad78deb5ac19f70fa74fc028f90b576f1d91b2c45e09e0108", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/sany.out": { + "sha256": "cf58712d31a0daab97eb960e7820c7b0c4326dfe785937eef9f7ed2476347ee3", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.out": { + "sha256": "3e60feb2884f636dd1ea5cd1459fcef8a1db34e5f9f801ccfe6735203969ec84", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.exit": { + "sha256": "461144ccfd56ee3cf0f9a9d80e520c5b872166b23092d5fd838ecbdb46d64dab", + "executable": false + }, + "skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.out": { + "sha256": "4ff80d714ae9e9ac57e54645b92aaac286ded5b5ce119b8d25c54ec53856bbc4", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/ChartRun.out": { + "sha256": "acd62745ae91ce9250ed32bb84a084f7a7327232f0e96a599eb49ed4f74c2388", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.cfg": { + "sha256": "2cf30ce46c41f9d6978b8080f2e0cdd627e0b034e367f98b351882fb0277b748", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.tla": { + "sha256": "b4ebd22cce4480f25812ea31f67447df520fb554ed9bd0114641c261f4e2b804", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/chart.mmd": { + "sha256": "730f79185a86f0345440cfbbcd05eb4b6b9b6e4653e00073708432e341c5fea7", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/graph.dot": { + "sha256": "a1b348348bf032ff81cfd796ae19f8cdcf491d5af18eab73ebfca3058a15691b", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/report.md": { + "sha256": "53433b379971530176a5e150a14c466c4452d07c523b3207e8b6c2b55c68b0cf", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/sany.out": { + "sha256": "b11bbc9d4985f64b11b09f48029c1c6a84089835cebf4211a56ee6e70e4c226c", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.exit": { + "sha256": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.out": { + "sha256": "f014279be781ee6e9daebaccdea137b13b21f563b1ca5107bd1172d29c3a0884", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.exit": { + "sha256": "25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e", + "executable": false + }, + "skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.out": { + "sha256": "cd7c28af45d5bc12ba9e0f148e25b5234b781bbb21ddb0194d5cd1b661c7b800", + "executable": false + }, + "skills/statechart/scripts/test/differential.test.ts": { + "sha256": "d4d64a7d17b4630616e797f1b184848c2a8b7712777d9982cafee81dc6046a9f", + "executable": false + }, + "skills/statechart/scripts/test/grid/Grid.cfg": { + "sha256": "41d15a4d5bef6385c00b30f2126204e74a779d835d37f1ea73669afcfb579ef5", + "executable": false + }, + "skills/statechart/scripts/test/grid/Grid.tla": { + "sha256": "e52e75d6fccff01cfe385140cbac3339b5820faeb1e567c7edc1267fa99f989e", + "executable": false + }, + "skills/statechart/scripts/test/grid/sany.out": { + "sha256": "e42a575f90275de315767d9f46ab0881349ebc53206e191aee57501494a5aba9", + "executable": false + }, + "skills/statechart/scripts/test/java.ts": { + "sha256": "4fef906fe1dcfdb98dafbd3ecdae1e82589908529ffc11b6bbdcfb8aef505932", + "executable": true + }, + "skills/statechart/scripts/test/labels.ts": { + "sha256": "5a884161428fc00229d9a0e896001df4b262b56a441b6b3d66d1b59b06050650", "executable": false }, - "skills/statechart/scripts/test/regions.mmd": { - "sha256": "53d12de8df0e4269c2fbee7399285ead307952c98bb8f8e2eefeba4492e42e25", + "skills/statechart/scripts/test/messages.test.ts": { + "sha256": "e7ca0ea446907074452df5388719598bb297d7f8ee50145b8b8f2a41c0c4c92f", "executable": false }, - "skills/statechart/scripts/test/statechart.test.mjs": { - "sha256": "65b090975ad970f38e7ed06ff3212802705948ce682f0ee90a616a95f874f800", + "skills/statechart/scripts/test/pipeline.test.ts": { + "sha256": "0f091c3e6e104fd545e113ad437df8d4856618a775073a37d41050ebf663e479", "executable": false }, - "skills/statechart/scripts/test/sync.mmd": { - "sha256": "cb1f5387058a0f79d6187eb217690588a2e2d384637b4d53cbc4bb050c570757", + "skills/statechart/scripts/test/plain-tlc.test.ts": { + "sha256": "f6b042300e66840809e26ba8fef0c7e2bc8bb6b59f3b910ea65dea176e97bf1c", "executable": false }, - "skills/statechart/scripts/test/upload.mmd": { - "sha256": "bbc1fa70e26cbcbcc7cde5d8fb7eda26df34b6d973053bf13c40b18527ff0e76", + "skills/statechart/scripts/test/render.test.ts": { + "sha256": "9cb369e5f4c464ca540ccc3de4fdd76670b84e54f06955ccb46908826579ac23", "executable": false }, - "skills/statechart/scripts/tlc-to-chart-args.mjs": { - "sha256": "6cfff1104d7024ecb079ac4c6fa539279e2b537824e01a1c8eb3c7f8e9320ffd", + "skills/statechart/scripts/test/tlc-out.test.ts": { + "sha256": "8a87d6cf32e7225aa0c8031e61b8a2912d8fbb3847a7ec2cd76e26c14e931478", "executable": false }, - "skills/statechart/scripts/tlc-to-chart.mjs": { - "sha256": "2fc9c866ed1217cd5f107ec059eb9df2d32268c1f582929321a0a4cdbb26b15a", + "skills/statechart/scripts/test/tlc.ts": { + "sha256": "73f5cf9dca5ca55aa65d5b342405e31f6eff858ed2bc2124c3e3fbb328259cca", "executable": false }, - "skills/statechart/scripts/tlc-tools.mjs": { - "sha256": "50fba780bb36edeb20804964f2e93f8c5fa85b6fe1e0e1e8d32e4b933eb69e84", + "skills/statechart/scripts/tsconfig.json": { + "sha256": "6bb2af53f7b251328e6117cd4fb761468dc4d4b96085d31a19304ccb73c32cc1", "executable": false }, - "skills/statechart/scripts/ways-in.mjs": { - "sha256": "4728ad85dbd12d192c6bfb5c4374ef3f63ce8d61e03aa120f159dfc607b2af06", + "skills/statechart/scripts/worker.ts": { + "sha256": "1f18ed5ef806982e05e8a40fc217e88257700b28b63cb4acbd647872f85f782b", "executable": false }, "skills/swarm/SKILL.md": { - "sha256": "7d7f9728009f5ccb51dff7584fbf46f5fdcc0c1650f9de4cc420ab3ab6a03e65", + "sha256": "7959a8f69a39e02cf9526889ec3aac94b531622e367fe15ed703014c2918fc73", "executable": false }, "skills/tdd/SKILL.md": { @@ -777,7 +2273,7 @@ "executable": false }, "skills/why/SKILL.md": { - "sha256": "4bfc3b9efebbd4fcd80d8476c9b3317858ec691f5ca47ea7d7e87dc7841df71f", + "sha256": "abc8d726ad6b3a648ed23303f8444ec155f8d30f5fde14624f1d670f6edc14fe", "executable": false }, "skills/why/references/epistemics.md": { diff --git a/packages/pstack/src/check.test.ts b/packages/pstack/src/check.test.ts index 6a49134926ad4..69ab7dec97a59 100644 --- a/packages/pstack/src/check.test.ts +++ b/packages/pstack/src/check.test.ts @@ -161,6 +161,7 @@ describe("checkPack", () => { "upstream/skills/poteto-mode/scripts/watch-pr/github.ts", "function isBugbot() {}\n", ); + pack.write("upstream/helper.ts", 'import { check } from "./passes/loop-check.ts";\n'); pack.seal(); expect(checkPack(pack.root)).toEqual([]); pack.write("upstream/helper.sh", "AskQuestion\n"); diff --git a/packages/pstack/src/check.ts b/packages/pstack/src/check.ts index 3cfaf06377d0c..f3e88430be9d7 100644 --- a/packages/pstack/src/check.ts +++ b/packages/pstack/src/check.ts @@ -31,7 +31,7 @@ const TERMS = [ ]; const T3_TERMS = [ - { key: "/loop", re: /\/loop\b/ }, + { key: "/loop", re: /(?`. Read a bundled skill directly and follow it. Delegation uses `delegate_task`; model choices come from resolved roles. Configure them with `$pstack:setup-pstack`. No separate harness plugin installation or translation file is required. diff --git a/packages/pstack/upstream/UPSTREAM.json b/packages/pstack/upstream/UPSTREAM.json index fc414fbb5a5e6..bbfc98207fcc3 100644 --- a/packages/pstack/upstream/UPSTREAM.json +++ b/packages/pstack/upstream/UPSTREAM.json @@ -1,6 +1,6 @@ { "repository": "https://github.com/cursor/plugins.git", "subdirectory": "pstack", - "commit": "99b609a72323b70204eab407c622f436e858e112", - "version": "0.15.32" + "commit": "4dd1d05818d19792e7cd6e818f1667f95f7e1dbe", + "version": "0.15.36" } diff --git a/packages/pstack/upstream/docs/guide/04-design.md b/packages/pstack/upstream/docs/guide/04-design.md index dea3e588172bf..3f5c655c34b7e 100644 --- a/packages/pstack/upstream/docs/guide/04-design.md +++ b/packages/pstack/upstream/docs/guide/04-design.md @@ -28,7 +28,7 @@ The design isn't sacred once code starts. If implementation shows the same worka $pstack:arena take my prompt to the arena verbatim. i want to compare their proposals with yours. ``` -[`$pstack:arena`](../../skills/arena/SKILL.md) is the general tool underneath. N subagents attempt the same design or code brief in parallel, each writing to its own worktree or directory. A read-only judge, on a different model family when your configuration allows one, scores every candidate against a rubric. The coordinator reads each candidate end to end, picks a base, grafts in the best ideas from the losers, and verifies the result. +[`$pstack:arena`](../../skills/arena/SKILL.md) is the general tool underneath. N subagents attempt the same design or code brief in parallel, each writing to its own worktree or directory. A read-only judge from a different model family scores every candidate against a rubric. When no model from another family can run, the coordinator skips the judge and picks the base itself. The coordinator reads each candidate end to end, picks a base, grafts in the best ideas from the losers, and verifies the result. ```mermaid flowchart LR @@ -66,9 +66,9 @@ Reach for it when parallelism buys coverage or lets independent checks race. `$p $pstack:interrogate the whole branch, but skeptically. no nitpicks unless it's an actual bug or regression. ``` -[`$pstack:interrogate`](../../skills/interrogate/SKILL.md) sends the same diff, intent, and rubric to reviewers on different model families. Model diversity is the point. Different models have different blind spots, so a finding two models raise independently is high-confidence signal. The lead sorts everything into `Act on`, `Consider`, `Noted`, and `Dismissed`, with a reason for each dismissal, and applies nothing automatically. +[`$pstack:interrogate`](../../skills/interrogate/SKILL.md) gives the change and its intent to adversaries on the configured models, and each tries to break it with a run against the real code. Adversaries find different failures. A finding survives only when rerunning its command shows a failure that breaks the intent. The report lists every verified finding with the adversaries that found it, then what each adversary dropped and why, and applies nothing. -Read the dismissals too. The lead is a pragmatic senior engineer, not an oracle, and you can override it. +Read the Dropped list too. You can override the verdict. ## Prototype instead of debating @@ -86,7 +86,7 @@ The same idea scales up to a real design. Pair `$pstack:architect` with prototyp $pstack:poteto-mode we need rate limiting for external webhooks. $pstack:architect it first, and answer open questions with prototypes. let me review before proceeding. ``` -Don't spend reviewers on an abstract plan. `$pstack:interrogate` belongs on a diff. Point adversarial review at a plan with no code behind it and the reviewers invent theoretical risks and edge cases that will never happen. Let prototypes settle the questions, then review what got built. +Don't spend adversaries on an abstract plan. `$pstack:interrogate` drops every finding a run does not prove, so a plan with no code behind it gives them little to break. Let prototypes settle the questions, then review what got built. ## Write the README first for shared code diff --git a/packages/pstack/upstream/docs/guide/06-verify-and-ship.md b/packages/pstack/upstream/docs/guide/06-verify-and-ship.md index 822572fe357c1..01ed3caa0cabd 100644 --- a/packages/pstack/upstream/docs/guide/06-verify-and-ship.md +++ b/packages/pstack/upstream/docs/guide/06-verify-and-ship.md @@ -123,11 +123,7 @@ A counterexample in model terms convinces you. The PM or designer who owns the b $pstack:statechart what the user sees when they edit a note offline ``` -[`$pstack:statechart`](../../skills/statechart/SKILL.md) draws a Mermaid statechart of what the user sees, generated from a TLA+ model, so the picture and the checked model can't drift apart. The reader's rules ("you never see Synced while you're offline") become invariants. Each bug TLC finds is a numbered path in the reader's words, and each state is marked works, still wrong, or fixed by your open PR. Before the hand-off, the agent reproduces each bug path in the real code, so a modeling error doesn't reach the reader as a bug. - -It also runs the other way. Sketch the behavior as a chart, agree on it with the reader, and the skill writes a TLA+ skeleton from it. TLC checks the agreed chart's rules before any code exists. As guards from the real code go into the spec, each place the code departs from the chart shows up as a difference for the reader to decide. - -When the code already has a TLA+ spec, the skill can audit the code against it. The agent models what the code does and checks that model next to the unchanged spec. The chart marks the steps where the two differ, and each rule either one breaks is a numbered path. +[`$pstack:statechart`](../../skills/statechart/SKILL.md) draws a Mermaid statechart of what the user sees, generated from a TLA+ model, so the picture and the checked model can't drift apart. The reader's rules ("you never see Synced while you're offline") become invariants. The chart marks each state that breaks a rule, and the report gives the shortest path to it in the reader's words. Before the hand-off, the agent reproduces each bug path in the real code, so a modeling error doesn't reach the reader as a bug. ## Open the PR diff --git a/packages/pstack/upstream/docs/guide/07-overnight.md b/packages/pstack/upstream/docs/guide/07-overnight.md index f9313559c2924..55565577764ad 100644 --- a/packages/pstack/upstream/docs/guide/07-overnight.md +++ b/packages/pstack/upstream/docs/guide/07-overnight.md @@ -79,13 +79,13 @@ The contract above drives one task to one finish condition. Some nights hold mor $pstack:poteto-mode full autopilot on this queue. each item is independent. i want them merged by morning. ``` -[Autopilot-stack](../../skills/poteto-mode/playbooks/autopilot-stack.md) runs the same owner loop but ships nothing. You wake up to one linear base-branch stack with a verifier's verdict on every link, and you review and land it yourself. Pick it over Autopilot-full when the changes are coupled, or when you want your own eyes on the work before anything merges: +[Autopilot-stack](../../skills/poteto-mode/playbooks/autopilot-stack.md) is Autopilot-full without the merge. You wake up to verified PRs, each with its verdict, and you review and land them yourself. A PR stacks only on a PR it builds on. Pick it over Autopilot-full when you want your own eyes on the work before anything merges: ```text -$pstack:poteto-mode autopilot these five changes but stack them, don't ship. i'll land the stack in the morning. +$pstack:poteto-mode autopilot these five changes, but don't merge. i'll land them in the morning. ``` -[Orchestrate](../../skills/poteto-mode/playbooks/orchestrate.md) is for a program that outlives any single agent: multi-day, many stacked PRs, fleets of subagents under one standing coordinator chat. The coordinator authors briefs, collects what its subagents finish, keeps the lowest unmerged PR green, and never writes code itself. It's deliberately heavy machinery. If one agent could finish the work in a session, the playbook itself routes you back to the overnight contract above: +[Orchestrate](../../skills/poteto-mode/playbooks/orchestrate.md) is for a program that outlives any single agent: multi-day, many stacked PRs, fleets of subagents under one standing coordinator chat. The coordinator decides which agent does each piece of work, checks what each one produces, and never writes code itself. If one agent could finish the work in a session, the playbook itself routes you back to the overnight contract above: ```text $pstack:poteto-mode orchestrate the store migration. own it until every package is converted and merged. i'll check in twice a day. diff --git a/packages/pstack/upstream/docs/guide/10-recipes-and-pitfalls.md b/packages/pstack/upstream/docs/guide/10-recipes-and-pitfalls.md index 638a27eefe265..7be3cfd357900 100644 --- a/packages/pstack/upstream/docs/guide/10-recipes-and-pitfalls.md +++ b/packages/pstack/upstream/docs/guide/10-recipes-and-pitfalls.md @@ -66,7 +66,7 @@ Each worker owns one package. The parent waits for every slice and returns one ` $pstack:interrogate the whole branch, but skeptically. don't change anything yet. no nitpicks unless it's an actual bug or regression in behavior. ``` -The qualifiers do real work. "don't change anything yet" keeps it read-only, and the nitpick rule pre-filters the noise so `Act on` findings are worth your time. +The qualifiers do real work. "don't change anything yet" keeps it read-only, and the nitpick rule pre-filters the noise so the verified findings are worth your time. ## Fix a bug through a failing test @@ -158,7 +158,7 @@ $pstack:ketchup [`$pstack:ketchup`](../../skills/ketchup/SKILL.md) tells you in plain words what changed since your last message, then lists each call waiting on you with its pick, evidence, and risk. It changes nothing while it reports. -To watch the catch-up instead, run [`$pstack:ketchup-video`](../../skills/ketchup-video/SKILL.md). It shows what happened in a short silent video of slides, and lists the calls waiting on you as text under it. +To watch the catch-up instead, run [`$pstack:ketchup-video`](../../skills/ketchup-video/SKILL.md). It shows what happened in a short subtitled video of slides, and lists the calls waiting on you as text under it. ## The pitfalls @@ -172,7 +172,7 @@ To watch the catch-up instead, run [`$pstack:ketchup-video`](../../skills/ketchu - **Trusting an unvetted number.** A warm cache or a skipped code path can fake a speedup. Run `$pstack:benchmark-checklist` before the number goes anywhere. - **Correcting the same mistake by hand.** A correction in chat helps one run. `$pstack:correct` fixes the repo so no later run repeats it. - **Using `$pstack:arena` for coverage.** `$pstack:arena` repeats one design or code brief, then picks a base and grafts the best parts. `$pstack:swarm` partitions slices or declared race arms and aggregates one report. -- **Accepting every review comment.** Bots and humans both file real catches and noise in one list. `$pstack:interrogate` sorts findings into act-on and dismissed buckets with reasons, and you can override either way. +- **Accepting every review comment.** Bots and humans both file real catches and noise in one list. `$pstack:interrogate` keeps only the findings a rerun proves and lists what it dropped and why, and you can override either way. - **Treating `auto` as a model slug.** `auto` and `inherit-parent` mean "omit the model field so the subagent inherits the parent chat model." [Setup](./01-setup.md) covers the roles. - **Reporting success off a green build.** A build proves it compiles. Ask for the real command, flow, stored value, or profile, and expect the evidence in the reply. - **Writing a `SKILL.md` freehand.** Route it through the [Authoring or modifying a skill playbook](../../skills/poteto-mode/playbooks/authoring-a-skill.md) so validation and review happen. diff --git a/packages/pstack/upstream/plugin.json b/packages/pstack/upstream/plugin.json index fc801c354a58a..486e9dbef7d55 100644 --- a/packages/pstack/upstream/plugin.json +++ b/packages/pstack/upstream/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "pstack", - "version": "0.15.32-t3code.2", + "version": "0.15.36-t3code.1", "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. T3 Code-native port with automate-maintainer and automate-team.", "author": { "name": "Lauren Tan" diff --git a/packages/pstack/upstream/skills/architect/SKILL.md b/packages/pstack/upstream/skills/architect/SKILL.md index 112a888d714d7..8208876560598 100644 --- a/packages/pstack/upstream/skills/architect/SKILL.md +++ b/packages/pstack/upstream/skills/architect/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Architect -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Sketch types, function signatures, class shapes, and module boundaries with `not implemented` bodies and pseudocode. Synthesize across multiple model perspectives, then fill in code against the chosen sketch. diff --git a/packages/pstack/upstream/skills/arena/SKILL.md b/packages/pstack/upstream/skills/arena/SKILL.md index aa3e3d98ae487..4f7d99155ded9 100644 --- a/packages/pstack/upstream/skills/arena/SKILL.md +++ b/packages/pstack/upstream/skills/arena/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Arena -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Open a plan using the harness plan tool, or a list in the reply with one entry per phase (Frame, Fan out, Cross-judge, Pick, Graft, Verify) before launching anything. @@ -32,7 +32,7 @@ After all Phase B candidates complete, choose one resolved seat from `arena cros ## Phase D: Pick a base -Read every candidate end to end before picking. Score each against the rubric criterion by criterion, not on holistic feel, and compare with the cross-judge. Agreement on the base confirms the pick. Disagreement means one of you is biased or the rubric was ambiguous. Read both rationales before deciding. +Read every candidate end to end before picking. Score each against the rubric criterion by criterion, not on holistic feel, and compare with the cross-judge when Phase C ran one. Agreement on the base confirms the pick. Disagreement means one of you is biased or the rubric was ambiguous. Read both rationales before deciding. Pick the base a future maintainer can extend most easily without breaking invariants. When two feel tied, prefer the cleaner boundary or smaller API, per the Laziness Protocol. @@ -48,4 +48,4 @@ Verify the synthesized artifact like any other output, per the **prove-it-works* ## Outputs -One synthesized artifact, with a short synthesis note alongside naming the base and why, the cross-judge's verdict, each graft with its source candidate, what was rejected and why, any convergence or dropouts, and the verification result. +One synthesized artifact, with a short synthesis note alongside naming the base and why, the cross-judge's verdict or that Phase C skipped the judge, each graft with its source candidate, what was rejected and why, any convergence or dropouts, and the verification result. diff --git a/packages/pstack/upstream/skills/automate-maintainer/SKILL.md b/packages/pstack/upstream/skills/automate-maintainer/SKILL.md index 2d5ed793052c8..a2712d292f108 100644 --- a/packages/pstack/upstream/skills/automate-maintainer/SKILL.md +++ b/packages/pstack/upstream/skills/automate-maintainer/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Automate maintainer -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Turn named GitHub maintainers' engineering activity into mode skills. Collect each person's evidence separately, then synthesize shared conventions, ownership, and disagreements. Use [automate-me](../automate-me/SKILL.md) for the user's conversation history and [automate-team](../automate-team/SKILL.md) when the roster needs discovery. diff --git a/packages/pstack/upstream/skills/automate-me/SKILL.md b/packages/pstack/upstream/skills/automate-me/SKILL.md index 62edaf8ea49e5..942dccf635b9c 100644 --- a/packages/pstack/upstream/skills/automate-me/SKILL.md +++ b/packages/pstack/upstream/skills/automate-me/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Automate me -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. A guided flow for turning the user's working conventions into a skill agents will follow. The output is one `-mode` skill tailored to them (e.g. `jay-mode`, `priya-mode`). diff --git a/packages/pstack/upstream/skills/automate-team/SKILL.md b/packages/pstack/upstream/skills/automate-team/SKILL.md index 77eec539d92f2..a4618fc498b2e 100644 --- a/packages/pstack/upstream/skills/automate-team/SKILL.md +++ b/packages/pstack/upstream/skills/automate-team/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Automate team -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Discover a repository's active core contributors and build a mode from their observed engineering conventions. Use [automate-maintainer](../automate-maintainer/SKILL.md) for mining, attribution, group synthesis, authoring, and evaluation; read it before collecting training evidence. diff --git a/packages/pstack/upstream/skills/create-formal-verification-skill/SKILL.md b/packages/pstack/upstream/skills/create-formal-verification-skill/SKILL.md index 5512535c35f84..07d3a6ea9ec3e 100644 --- a/packages/pstack/upstream/skills/create-formal-verification-skill/SKILL.md +++ b/packages/pstack/upstream/skills/create-formal-verification-skill/SKILL.md @@ -32,7 +32,7 @@ Pick the tool that answers the question at the lowest cost: a model checker, a p Write a project-local skill with frontmatter that names the area and says when to use it. Without frontmatter, the skill never registers. From the skill alone, the next agent must be able to tell whether a change touches the verified code, run the check, tell a real bug from a modeling error, and update the verification when a change is intended. Put the coverage record and the sync commit in the skill, and tell the next agent to move the sync commit forward only after it confirms that the verification describes the code at the new commit. Use this repo's real commands, paths, and nouns, and leave no placeholders. -If the check is a TLA+ model, tell the next agent to read every result with the pstack [`$pstack:statechart`](../statechart/SKILL.md) skill. It shows each invariant violation the checker finds as its shortest path in plain words, and which of those violations a change fixes. When the check passes, `$pstack:statechart` draws a chart of how the system moves between states. Refer to `$pstack:statechart` by name. Its commands, options, and file paths change between releases and machines, so copy none of them into the generated skill or its scripts. +If the check is a TLA+ model, tell the next agent to read every result with the pstack [`$pstack:statechart`](../statechart/SKILL.md) skill. It shows each invariant violation the checker finds as its shortest path in plain words. When the check passes, `$pstack:statechart` draws a chart of how the system moves between states. Refer to `$pstack:statechart` by name. Its commands, options, and file paths change between releases and machines, so copy none of them into the generated skill or its scripts. ## Prove it before you hand off diff --git a/packages/pstack/upstream/skills/figure-it-out/SKILL.md b/packages/pstack/upstream/skills/figure-it-out/SKILL.md index d6f1dec017c4d..8cbb7f10cdb76 100644 --- a/packages/pstack/upstream/skills/figure-it-out/SKILL.md +++ b/packages/pstack/upstream/skills/figure-it-out/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Figure it out -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. When the task matches no playbook, design one. The deliverable before any code is the workflow itself: a sequence of phases that scales rigor to the task, runs the scientific method, and leaves a decision trail a human can audit after stepping away. diff --git a/packages/pstack/upstream/skills/how/SKILL.md b/packages/pstack/upstream/skills/how/SKILL.md index e7f7e4a64a9e6..bd277567f9df0 100644 --- a/packages/pstack/upstream/skills/how/SKILL.md +++ b/packages/pstack/upstream/skills/how/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # How -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Explore the codebase to answer "how does X work?" questions. Produce architectural explanations at the level of a senior engineer onboarding onto a subsystem, enough to build a working mental model, not so much that it reads like annotated source code. diff --git a/packages/pstack/upstream/skills/interrogate/SKILL.md b/packages/pstack/upstream/skills/interrogate/SKILL.md index cee4489e66a11..8019c2f014d4a 100644 --- a/packages/pstack/upstream/skills/interrogate/SKILL.md +++ b/packages/pstack/upstream/skills/interrogate/SKILL.md @@ -1,67 +1,33 @@ --- name: interrogate -description: "Use for \"interrogate\", \"adversarial review\", \"multi-model review\", \"challenge this\", \"stress test this code\", \"find blind spots\", or \"tear this apart\". Multiple LLM reviewers challenge changes from independent angles." +description: "Use for \"interrogate\", \"adversarial review\", \"multi-model review\", \"challenge this\", \"stress test this code\", \"find blind spots\", or \"tear this apart\". Adversaries try to break a change or design, and only findings proven by a run survive." disable-model-invocation: true --- # Interrogate -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. -Spawn one reviewer per configured model to adversarially review code changes. The adversarial signal comes from model diversity, not assigned personas. The deliverable is a synthesized verdict. Do NOT auto-apply changes. +Adversaries try to break a change or a design. A finding survives only when a run proves it. Report the findings. Do not apply fixes. -## Step 1, Scope +## Frame -Identify what to review from context: +The scope is what the user names. Otherwise it is the branch diff from its merge base with the base branch, through the working tree. State the intent with the **in-your-own-words** skill, from the user's message, the commits, and the PR description. Ask the user when the intent is unclear. -- If the user points at specific files or a diff, use that. -- If on a feature branch, run `git diff main...HEAD` (or the appropriate base branch) for the full changeset. -- If the user's message references recent work, gather the relevant files. +## Fan out -Package the diff or file contents with any surrounding context files the reviewers need to understand the code. +Run Phase A steps 3 and 4 and Phase B of the **arena** skill, with four changes. Use every resolved `interrogate reviewers` seat from `` in place of `arena runners`, including inherited seats. Each adversary uses `delegate_task`, `role: "review"`, and `isolation: "worktree"`. A worktree begins at the committed local HEAD. When the scope includes uncommitted changes, save a patch to an absolute shared path and ask each adversary to apply it in its own worktree before running the repro; report if the needed files cannot be made available. Do not commit, push, or alter the parent's checkout. In place of the arena task and rationale, give each adversary the scope, the intent, and this brief: -## Step 2, Intent +> Break this change. Prove each finding with a run against the real code, such as a failing test, a repro script, or command output that shows the wrong behavior. For a design or plan, run a prototype or probe the current tree. Keep repros in your own worktree. Report each finding with a title, the exact command, the output it printed, and the output the intent requires. Leave out anything you did not run. That covers style, naming, structure, and risks that need a future change to matter. No findings is a valid result. -Before spawning reviewers, state the intent in one clear paragraph from the user's message, commit messages, the PR description if one exists, and the code. If you're unsure about the intent, ask the user before proceeding. +## Verify -## Step 3, Spawn Reviewers +Verify each finding per arena Phase F by rerunning its command. Keep a finding when the output shows the failure and the failure breaks the intent. Drop the rest. Merge findings that share a root cause. -Launch one reviewer per resolved seat of `interrogate reviewers` in ``, labeled Reviewer A, B, and so on. Set `role: "review"` and tell each "Do not edit files, commit, or push." Use the same review brief and independent angles. Check `git status` after the round. +Skip arena Phases C, D, and E. Adversaries find different failures, so the verdict keeps every verified finding, not one adversary's list. -Read `references/reviewer-prompt.md` and fill in the same template for every reviewer with the stated intent, the diff or file contents, the rubric from `references/rubric.md`, and the code-quality lens from `references/code-quality-review.md`. Copy the role's resolved target from `` exactly, including all options. +## Output -## Step 4, Synthesize - -As results come back, build a unified picture. Parse every reviewer's findings. Merge findings that describe the same issue differently, and note which models raised each one. Findings two or more models raised independently are the highest signal. Read a lone model's finding, but weight it accordingly. Note disagreements. If one model flags something and another explicitly says the opposite, that is context for the verdict. - -## Step 5, Lead Judgment - -You are the lead reviewer, a pragmatic senior engineer, not a neutral aggregator. Read `references/lead-judgment.md` for the full framework. Put every finding in one of four buckets, with the model(s) that raised it and a one-line rationale. - -- **Act on**. Real issues affecting correctness, security, or maintainability given the actual goals. They would block a real PR. -- **Consider**. Legitimate, but you're not sure they outweigh the cost of addressing them now. Worth the user's attention. -- **Noted**. Technically valid but not actionable. Context-dependent, premature optimization, or low-impact at this stage. -- **Dismissed**. Wrong, nitpicky, or missing context, with a brief reason. - -## Output Format - -### Intent -> [The stated intent paragraph from Step 2] - -### Reviewers -- Reviewer [label]: [model name], [N findings] (one bullet per reviewer) - -### Act On -[Each: description, which models raised it, why it matters.] - -### Consider -[Each: description, which models raised it, the tradeoff.] - -### Noted -[Brief list.] - -### Dismissed -[Each with a brief rationale.] - -### Agreement Map -[Where models agreed and diverged, and what that pattern tells us.] +- **Intent**: the restatement. +- **Findings**: for each verified finding, the command, its output, how it breaks the intent, and the adversaries that found it. Say "no verified findings" when none survive. +- **Dropped**: for each adversary, the count of dropped findings and why. diff --git a/packages/pstack/upstream/skills/interrogate/references/code-quality-review.md b/packages/pstack/upstream/skills/interrogate/references/code-quality-review.md deleted file mode 100644 index 16581250d8104..0000000000000 --- a/packages/pstack/upstream/skills/interrogate/references/code-quality-review.md +++ /dev/null @@ -1,47 +0,0 @@ -# Code Quality Review - -Each reviewer applies this code-quality lens in addition to the rubric. It is a strict standard focused on implementation quality, maintainability, abstraction quality, and codebase health. - -Above all, be ambitious about code structure. Do not merely identify local cleanup. Actively search for "code judo" moves, restructurings that preserve behavior while making the implementation dramatically simpler, smaller, more direct, and more elegant. - -## Core Prompt - -Start from this baseline: - -> Perform a deep code quality audit of the current branch's changes. -> Rethink how to structure / implement the changes to meaningfully improve code quality without impacting behavior. -> Work to improve abstractions, modularity, reduce Spaghetti code, improve succinctness and legibility. -> Be ambitious, if there is a clear path to improving the implementation that involves restructuring some of the codebase, go for it. -> Be extremely thorough and rigorous. Measure twice, cut once. - -## Dimensions - -Each dimension is stated once. Apply the ones that are relevant. - -0. **Be ambitious about structural simplification.** Do not stop at "this could be a bit cleaner." Look for reframings that make whole branches, helpers, modes, conditionals, or layers disappear. Assume a "code judo" move is often available. It uses the existing architecture more effectively and makes the change dramatically simpler. If you can delete complexity rather than rearrange it, push hard for that. - -1. **Do not let a PR push a file from under 1k lines to over 1k lines without a very strong reason.** Treat this as a strong smell. Prefer extracting helpers, subcomponents, or modules. If the diff crosses that threshold, ask whether the code should be decomposed first. Waive only for a compelling structural reason where the resulting file stays clearly organized. - -2. **Do not allow spaghetti growth in existing code.** Be suspicious of new ad-hoc conditionals, scattered special cases, or one-off branches inserted into unrelated flows. Treat "weird if statements in random places" as a design problem, not a style nit. Prefer pushing the logic into a dedicated helper, state machine, or module instead of tangling an existing path. - -3. **Bias toward cleaning the design, not just accepting working code.** If behavior can stay the same while the structure becomes meaningfully cleaner, push for the cleaner version. Prefer simplifications that remove moving pieces over refactors that spread the same complexity around. - -4. **Prefer direct, boring, maintainable code over hacky or magical code.** Treat brittle, ad-hoc, or "magic" behavior as a problem. Be skeptical of generic mechanisms that hide simple data-shape assumptions. Flag thin abstractions, identity wrappers, or pass-through helpers that add indirection without buying clarity. - -5. **Push on type and boundary cleanliness when it affects maintainability.** Question unnecessary optionality, `unknown`, `any`, or cast-heavy code when a clearer type boundary could exist. Prefer explicit typed models over loosely-shaped ad-hoc objects. If a branch leans on a silent fallback to paper over an unclear invariant, ask whether the boundary should be made explicit. - -6. **Keep logic in the canonical layer and reuse existing helpers.** Call out feature logic leaking into shared paths or implementation details leaking through APIs. Prefer existing canonical utilities over bespoke one-offs. Push code toward the right package, service, or module instead of normalizing drift. - -7. **Treat unnecessary sequential orchestration and non-atomic updates as design smells when the cleaner structure is obvious.** If independent work is serialized for no reason, ask whether it should run in parallel. If related updates can leave state half-applied, push for a more atomic structure. Do not over-index on micro-optimizations, but do flag avoidable orchestration complexity that makes the code more brittle. - -## Output Expectations - -Prioritize structural code-quality regressions and missed simplifications first, then spaghetti and branching complexity, then boundary, type, and file-size concerns, then smaller modularity and legibility issues. - -## Approval Bar - -Do not approve merely because behavior seems correct. Treat these as presumptive blockers unless the author can justify them: the PR keeps a lot of incidental complexity when a code-judo move would delete it. Pushes a file from below 1000 lines to above 1000 lines. Adds ad-hoc branching that tangles an existing flow. Scatters feature checks across shared code. Adds an unnecessary abstraction, wrapper, or cast-heavy contract, or duplicates an existing helper or puts logic in the wrong layer when there is a clear canonical home. If those conditions are not met, leave explicit, actionable feedback and push for a cleaner decomposition. - -## Review Tone - -Be direct, serious, and demanding about quality. Do not be rude, but do not soften major maintainability issues into mild suggestions. If the code is making the codebase messier, say so. If the implementation missed an obvious dramatic simplification, say that too. Do not be satisfied with "maybe rename this" when the real issue is structural. diff --git a/packages/pstack/upstream/skills/interrogate/references/lead-judgment.md b/packages/pstack/upstream/skills/interrogate/references/lead-judgment.md deleted file mode 100644 index 3b41346c9c3f5..0000000000000 --- a/packages/pstack/upstream/skills/interrogate/references/lead-judgment.md +++ /dev/null @@ -1,39 +0,0 @@ -# Lead Judgment Framework - -## Filtering Principles - -### Nitpick Gravity - -Reviewers, especially adversarial ones, tend to fill their review. If they don't find critical issues, they'll inflate nits to fill the space. If a reviewer's findings are all nits and style preferences, the code is probably fine. Say so. - -### Hypothetical vs. Actual - -"What if someone passes null here?" is only a finding if the caller can actually pass null. Trace the call site. If the input is validated upstream or the type system prevents it, dismiss the finding. Reviewers working from a diff can't always see the full call chain. You can. - -### Premature Abstraction Warnings - -Reviewers often suggest extracting functions, adding interfaces, or creating abstractions. Does this code need to change in a second way? If not, the abstraction is premature. Simple inline code that works beats a clean abstraction that's overkill for the current scope. - -### "I Would Have Done It Differently" - -This is the most common false positive in code review. A finding that amounts to "I prefer a different approach" is not a bug, not a design flaw, and not actionable unless the reviewer shows a concrete problem with the current approach. Dismiss these, and say why. - -### Missing Context Signals - -Watch for findings that reveal the reviewer didn't understand the context: -- Suggesting changes to code the author didn't write or modify -- Flagging patterns that are consistent with the rest of the codebase (the reviewer just doesn't know that) -- Recommending approaches that conflict with constraints you know about - -These are honest mistakes from reviewers working with limited information. Dismiss them gracefully. - -## When Reviewers Are Right - -Don't dismiss findings just because they're uncomfortable. The whole point of adversarial review is to catch things you'd miss. Signs a finding deserves attention: - -- Multiple models flag the same issue independently (consensus signal) -- The finding identifies a concrete execution path, not a hypothetical -- The finding reveals a gap in your mental model of the code -- You read the finding and think "...yeah, actually" - -Be especially careful about dismissing security findings and correctness bugs. These deserve more scrutiny even when they come from a single model. diff --git a/packages/pstack/upstream/skills/interrogate/references/reviewer-prompt.md b/packages/pstack/upstream/skills/interrogate/references/reviewer-prompt.md deleted file mode 100644 index ef7bff3d77c35..0000000000000 --- a/packages/pstack/upstream/skills/interrogate/references/reviewer-prompt.md +++ /dev/null @@ -1,62 +0,0 @@ -# Reviewer Prompt Template - -Build each reviewer subagent's prompt from this template, filling in the placeholders. - ---- - -You are an adversarial code reviewer. Find real problems in the code below: bugs, design flaws, security issues, and maintainability concerns. You are not here to be helpful or encouraging. You are here to stress-test. - -## Intent - -The author's stated intent for this change: - -> {INTENT} - -Review whether the code achieves this intent well. Do NOT question the intent itself. Assume the goal is correct and challenge the execution. - -## Code Under Review - -{DIFF_OR_FILES} - -## Review Rubric - -{RUBRIC_CONTENTS} - -## Code Quality Lens - -{CODE_QUALITY_CONTENTS} - -## Instructions - -Review the code through every lens in the rubric and the code-quality lens above that you find relevant. Do not force lenses that don't apply. A simple bug fix does not need paragraphs about architectural integrity. - -For each finding, provide: - -1. **Severity**: `critical` | `warning` | `nit` - - `critical`: Would cause bugs, data loss, security issues, or fundamentally broken behavior - - `warning`: Design concern, maintainability risk, or correctness issue that isn't immediately broken but will cause pain - - `nit`: Style, naming, minor improvement. -2. **Finding**: What the problem is, in concrete terms. Reference specific lines/functions. -3. **Evidence**: Why you believe this is a problem. Show your reasoning. Don't just assert. -4. **Suggestion** (optional): What you'd do instead, only if you have a concrete alternative. - -A good finding references specific code, not vague concerns ("this could be better"). It distinguishes between "this is broken" and "I would have done this differently". It considers the stated intent. A finding that ignores the context of what's being built is a bad finding. - -Do not restate what the code does without identifying a problem. Do not praise the code. If you find nothing wrong, say "no findings" and stop. An empty review is a valid outcome. - -## Output - -Return your findings as a structured list: - -``` -## Findings - -### 1. [Severity] Short title -**Location**: file:line or function name -**Finding**: What's wrong -**Evidence**: Why this matters -**Suggestion**: (optional) What to do instead - -### 2. [Severity] Short title -... -``` diff --git a/packages/pstack/upstream/skills/interrogate/references/rubric.md b/packages/pstack/upstream/skills/interrogate/references/rubric.md deleted file mode 100644 index 8dab195ab8405..0000000000000 --- a/packages/pstack/upstream/skills/interrogate/references/rubric.md +++ /dev/null @@ -1,77 +0,0 @@ -# Review Rubric - -Review through whichever lenses are relevant. Not every lens applies to every change. - -## Correctness - -Does the code actually do what the intent says it should? - -- Edge cases: empty inputs, nil/undefined, boundary values, concurrent access -- Error handling: are errors caught, propagated, or silently swallowed? -- Off-by-one, type coercion, integer overflow, string encoding -- State management: race conditions, stale closures, dangling references -- Do both the happy path and the sad path work? -- Idempotency: what happens if this operation runs twice, or if a previous run crashed halfway? If the answer is "it depends on what state was left behind," a reconciliation step is missing. -- Concurrency: if multiple actors can touch the same mutable state (files, branches, shared data), is access serialized structurally (locks, sequential phases, exclusive ownership), or by conventions that won't hold? - -When you find a potential bug, trace the execution path. Don't just flag "this could be nil". Show the call chain that makes it nil. - -## Root Causes vs. Symptoms - -Is the code fixing the actual problem or papering over a symptom? - -Answering this often requires looking beyond the changed files. Read the surrounding code (callers, callees, type definitions, sibling modules) and understand the architecture the change lives in. Use the tools available to you (Read, Grep, Glob) to follow the call chain, read the types, and understand why the code exists before judging whether the change addresses the right layer. - -- Guard clauses that mask a deeper invariant violation -- Retry logic that hides a broken contract -- Type casts that silence a modeling error -- A workaround: why is it needed? What would a proper fix look like? -- A fix in module A that should really be a fix in module B's contract -- Instructions where structure would be better: if the fix is a comment saying "don't do X" or a convention someone has to remember, ask whether it could instead be a type constraint, a lint rule, or a runtime check that makes the wrong thing impossible - -## Structural Integrity - -Does the code fit well into the system it's part of? - -- Boundary discipline: is validation at system boundaries, or scattered through business logic? Validate data once where it enters the system, then trust it internally. -- Abstraction level: is the code mixing high-level orchestration with low-level detail? -- Coupling: does this change introduce dependencies that will make future changes harder? -- Data model fit: do the data structures match the actual access patterns? -- Bolted-on vs. integrated: if the new requirement had been known from the start, would the code look like this, or does the change feel patched onto the existing design? -- Legacy dual-paths: does the change introduce a new API while keeping the old one alive? If there are no external consumers, migrate callers and delete the old path in the same wave. Don't leave compatibility layers that will become permanent. - -Don't penalize simple code for lacking abstraction. Premature abstraction is worse than duplication. - -## Verification - -Can you tell that this code works from reading it? - -- Are there tests? Do they test behavior or implementation details? -- Are there assertions/invariants that would catch regressions? -- If this is a bug fix: is there a test for the bug? -- If this touches an integration boundary: is the full path tested? -- Check the real thing, not a proxy. If the code checks liveness via file mtime or cached state instead of reading the actual value, that's a verification gap. -- For delegated or async work: does the code verify actual output artifacts, or does it trust self-reports and summaries? - -## Complexity Budget - -Is the complexity justified by what the code accomplishes? - -- Code that could be simpler without losing correctness or clarity -- Abstractions that serve only one call site -- Configuration or parameterization for cases that don't exist yet -- Dead code, unused imports, vestigial parameters -- Over-engineering: "just in case" code paths with no current callers -- Obsolete compatibility paths kept alive for transitional stability that's no longer needed. If the migration is done, delete the scaffolding -- Does the user experience justify the complexity? Every feature, control, and option should earn its place. Half-finished features are worse than missing ones. - -Simpler is better unless simpler is wrong. - -## Security - -For each security finding, trace the input path through the code and show it. - -- User input flowing to dangerous sinks (SQL, shell, eval, innerHTML) without sanitization -- Authentication/authorization gaps in new endpoints -- Secrets in code, logs, or error messages -- TOCTOU (time-of-check-time-of-use) in security-critical paths diff --git a/packages/pstack/upstream/skills/ketchup-video/SKILL.md b/packages/pstack/upstream/skills/ketchup-video/SKILL.md index a6ca19c4a9187..711ed772fbaa7 100644 --- a/packages/pstack/upstream/skills/ketchup-video/SKILL.md +++ b/packages/pstack/upstream/skills/ketchup-video/SKILL.md @@ -1,12 +1,12 @@ --- name: ketchup-video -description: "Catch the user up like $pstack:ketchup, with a short silent video of slides for what happened and the waiting calls as text under it. Use for $pstack:ketchup-video." +description: "Catch the user up like $pstack:ketchup, with a short subtitled video of slides for what happened and the waiting calls as text under it. Use for $pstack:ketchup-video." disable-model-invocation: true --- # Ketchup video -Follow [`$pstack:ketchup`](../ketchup/SKILL.md), but show what happened in a short silent video instead of its summary and visuals. Its window starts at the last message the user wrote before `$pstack:ketchup-video`, not before any earlier `$pstack:ketchup`. Writing and rendering the video, outside the repo, is the one exception to its read-only rule. The reply is ketchup's opening sentence, the embedded video, one line naming any work left that you can do yourself, and the action items. When the video shows a call's options, call each option by the name the video gives it. If the render fails, say so in one line and give only the calls. +Follow [`$pstack:ketchup`](../ketchup/SKILL.md), but show what happened in a short subtitled video instead of its summary and visuals. Its window starts at the last message the user wrote before `$pstack:ketchup-video`, not before any earlier `$pstack:ketchup`. Writing and rendering the video, outside the repo, is the one exception to its read-only rule. The reply is ketchup's opening sentence, the embedded video, one line naming any work left that you can do yourself, and the action items. When the video shows a call's options, call each option by the name the video gives it. If the render fails, say so in one line and give only the calls. ## Slides @@ -18,6 +18,12 @@ Keep text to the minimum. Put each label on the thing it names, in plain words a Show what came of each thing the user asked for, and whether it is done, still running, failed, or not checked. Never show something done that still waits on the user. Show any bug found and fixed on the way. If a visible change has no screenshot, say so on its slide and name what changed on screen. Draw only from facts you have. Never invent a number, and never draw a screen you did not capture. +## Subtitles + +Give each slide one short subtitle line, about ten words, that says its point the way you would tell the user, from only what that slide shows. Say what the picture means rather than read out its labels. Put the subtitle in a strip below the slide, the same height on every slide and empty except for the subtitle, so it never covers the slide. Set it small, but readable when the video plays at chat size. + ## Render -Render one silent landscape video, such as an mp4, with motion between slides. Every frame is the slide itself, edge to edge. Make the frames from the slides, never by recording a screen. Hold each slide long enough to read its words, at least one second for each three words it adds. Words inside a screenshot do not count. Before you embed the video, look at one frame from each slide in the rendered file, and count the words it adds against its hold in that file. Fix any slide you cannot read in that time, render again, and check again. +Render one silent landscape video, such as an mp4, with motion between slides. Every frame is the slide itself, edge to edge, above its subtitle strip. Make the frames from the slides, never by recording a screen, and burn each subtitle into its slide's frames so it stays on screen for the slide's whole hold. The hold is the time a slide is fully on screen, not counting the motion into or out of it. Hold each slide long enough to read the words it adds, at least one second for each three words. A slide adds its subtitle and each word that was not on the slide before it, including a label it changes. The first slide adds all its words. Words inside a screenshot do not count. + +Before you embed the video, check the rendered file. Look at one frame from each slide at chat size, and check that its subtitle is readable and covers nothing. Measure each hold by matching the file's frames to the slide, not with a still-frame detector, which counts the faint ends of a fade as still. Count the words each slide adds against its measured hold. Fix any slide you cannot read in that time, render again, and check again. diff --git a/packages/pstack/upstream/skills/maintain-verification-skill/SKILL.md b/packages/pstack/upstream/skills/maintain-verification-skill/SKILL.md index 1f960e5ea26e2..cfa2f0d1cccf9 100644 --- a/packages/pstack/upstream/skills/maintain-verification-skill/SKILL.md +++ b/packages/pstack/upstream/skills/maintain-verification-skill/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Maintain a verification skill -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. A feature map rots the moment the app changes. This skill is the upkeep loop for a skill generated by `$pstack:create-verification-skill` (or any project-local verification skill with a feature map). The unit of rigor is the feature, not every sentence: cover every feature file from source and exercise every feature live, without terminalising every bullet. diff --git a/packages/pstack/upstream/skills/no-comments/SKILL.md b/packages/pstack/upstream/skills/no-comments/SKILL.md index 97f84ca40e1b6..b97c69c69e695 100644 --- a/packages/pstack/upstream/skills/no-comments/SKILL.md +++ b/packages/pstack/upstream/skills/no-comments/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # No comments -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Spawn Comment Sicko. Act on accepted findings. diff --git a/packages/pstack/upstream/skills/poteto-help/SKILL.md b/packages/pstack/upstream/skills/poteto-help/SKILL.md index d19a1789494d8..e62ec56c2c285 100644 --- a/packages/pstack/upstream/skills/poteto-help/SKILL.md +++ b/packages/pstack/upstream/skills/poteto-help/SKILL.md @@ -67,7 +67,7 @@ The default answer is `$pstack:poteto-mode`, which runs most of the others when | Understand a change or subsystem, explained plainly | [`$pstack:teach`](../teach/SKILL.md) | | Catch up on their own recent work on a topic | [`$pstack:recall`](../recall/SKILL.md) | | Catch up on what changed in this chat since their last message, in plain words and small visuals, with every item waiting on them listed and decidable from the reply alone | [`$pstack:ketchup`](../ketchup/SKILL.md) | -| Catch up like `$pstack:ketchup`, in a short silent video of slides, with the waiting calls as text under it | [`$pstack:ketchup-video`](../ketchup-video/SKILL.md) | +| Catch up like `$pstack:ketchup`, in a short subtitled video of slides, with the waiting calls as text under it | [`$pstack:ketchup-video`](../ketchup-video/SKILL.md) | | Know what a small diff could break outside itself | [`$pstack:blast-radius`](../blast-radius/SKILL.md) | | Settle types and module shape before code that crosses a function boundary | [`$pstack:architect`](../architect/SKILL.md) | | Get several attempts at one brief, merged into the best one | [`$pstack:arena`](../arena/SKILL.md) | @@ -83,7 +83,7 @@ The default answer is `$pstack:poteto-mode`, which runs most of the others when | Bring a verification skill and its feature map back in line with the app | [`$pstack:maintain-verification-skill`](../maintain-verification-skill/SKILL.md) | | Formally verify a race, invariant, or other property that tests rarely reach | [`$pstack:create-formal-verification-skill`](../create-formal-verification-skill/SKILL.md) | | Bring a formal verification back in line after the code it covers changed, and learn whether a failure is drift or a bug | [`$pstack:maintain-formal-verification-skill`](../maintain-formal-verification-skill/SKILL.md) | -| Show a PM or designer what the user sees as a chart checked by a model, with each bug drawn as a numbered path, turn an agreed chart into a TLA+ skeleton, or audit the code against its TLA+ spec | [`$pstack:statechart`](../statechart/SKILL.md) | +| Show a PM or designer what the user sees as a chart checked by a TLA+ model, with the shortest path to each state that breaks a rule | [`$pstack:statechart`](../statechart/SKILL.md) | | Vet a performance number before reporting or acting on it | [`$pstack:benchmark-checklist`](../benchmark-checklist/SKILL.md) | | Run a large or cross-cutting change, or one to review after stepping away | [`$pstack:figure-it-out`](../figure-it-out/SKILL.md) | | Keep a decision log during a run, and review it afterward | [`$pstack:show-me-your-work`](../show-me-your-work/SKILL.md) | @@ -119,7 +119,7 @@ Playbooks are step lists inside `$pstack:poteto-mode`, not skills, so they have - "land the stack" runs Shipping. - "take over this branch" runs Session pickup. - "pause safely" runs Pause safely. -- "full autopilot on this queue" runs Autopilot-full. "stack them, don't ship" runs Autopilot-stack. +- "full autopilot on this queue" runs Autopilot-full. "autopilot, don't merge" runs Autopilot-stack. - "run the eval playbook" runs Eval. Use `schedule_task` for timed work and `watch_pull_request` for PR events. Use `skill-creator` when available, otherwise write the Agent Skills folder directly. diff --git a/packages/pstack/upstream/skills/poteto-help/references/recipes.md b/packages/pstack/upstream/skills/poteto-help/references/recipes.md index 906116b8d177e..fb247e66888fa 100644 --- a/packages/pstack/upstream/skills/poteto-help/references/recipes.md +++ b/packages/pstack/upstream/skills/poteto-help/references/recipes.md @@ -33,7 +33,7 @@ Swap in the real paths, skills, and done checks. Informal wording works. ## Review and ship -- `$pstack:interrogate the whole branch, but skeptically. don't change anything yet. no nitpicks unless it's a real bug or regression.` Read the dismissals too. +- `$pstack:interrogate the whole branch, but skeptically. don't change anything yet. no nitpicks unless it's a real bug or regression.` Read the Dropped list too. - `$pstack:create-formal-verification-skill prove holds in , even when interleave or retry.` After merges or refactors in that code: `$pstack:maintain-formal-verification-skill`. - `$pstack:swarm check every package under against its check script. one worker per package. one report.` - `$pstack:poteto-mode open the pr. small ordered commits, evidence in the description.` @@ -46,6 +46,6 @@ Swap in the real paths, skills, and done checks. Informal wording works. - `$pstack:show-me-your-work catch me up on what you did last night.` Read its Attention section first. - `$pstack:ketchup` catches you up on this chat since your last message and lists every call that waits on you. - `$pstack:poteto-mode full autopilot on this queue. each item is independent.` -- `$pstack:poteto-mode autopilot these changes but stack them, don't ship. i'll land the stack.` +- `$pstack:poteto-mode autopilot these changes, but don't merge. i'll land them.` - `$pstack:reflect capture what we learned so the next run doesn't repeat it.` Approve only edits that change a future decision. - `$pstack:bro` restates the last reply in plain words. diff --git a/packages/pstack/upstream/skills/poteto-mode/SKILL.md b/packages/pstack/upstream/skills/poteto-mode/SKILL.md index f4bfcb70eca1a..7716a9d09f5de 100644 --- a/packages/pstack/upstream/skills/poteto-mode/SKILL.md +++ b/packages/pstack/upstream/skills/poteto-mode/SKILL.md @@ -16,6 +16,7 @@ The Principles section below grounds every trigger. In your reply, name each pri Remaining triggers: +- Your first message on a task → tell the user, in your own words, their goal, the problem they are solving, and, if the task ships a change, the smallest change you expect to meet it. Don't wait for confirmation. If the evidence rules out the change the user asked for, show it and stop. Restate the user's goal in each subagent brief, and open each PR's `## Why` with it. Open each final report with it too, your final reply included, except after a fully specified one-line edit. - Nontrivial change, architecture decision, or "are we sure?" → the **how** skill. - About to the harness question tool on a "which approach", "how should I", or "what should this do" fork → classify it before you ask. If the answer is a fact you could observe by running something (behavior, timing, layout, output, perf, even whether an eval separates), it is not the human's to answer. Sketch it via the Prototype playbook (`playbooks/prototype.md`) and let the result decide. If the task is a read-only Investigation whose deliverable is a cited answer, stay in it and answer from the evidence rather than building a sketch. Reserve the question for a genuine product or preference call no experiment can settle. Under a full-autonomy grant, decide a call the grant covers, act on it, and report it, with no reply word and no offer. Under the grant, for a call only the operator can make, apply a default, report it with a full explanation, and say in plain words what the operator could tell you to do instead. The operator answers in their own words. Never give a shorthand token to type back. Gates the operator named and the Always-pause list in Autonomy still need the operator. - Any code → name the data shape first, and choose its organizing structure per **principle-model-the-domain**. @@ -67,7 +68,7 @@ Read the leaf skill in full for any principle you apply. Each entry names when i - **Fix Root Causes.** Debugging. Trace each symptom back to the first place the system went wrong and fix it there. A guard is a fix only when the state it handles is legitimate. - **Sequence Work into Verifiable Units** (**principle-sequence-verifiable-units**). Multi-step work (sweeps, migrations, runs of similar edits) and how you stack commits and PRs. Break work into small units that each end in a check, verify each before the next, and order delivery so the sequence proves itself. - **Test Behavior, Not Implementation.** Writing, changing, or keeping a test. Assert what the code's users can observe, so the test fails when that behavior breaks and keeps passing when only the internals change. -- **Explain the Number.** Before you trust, report, or act on a number you measured (a speedup, a regression, a throughput, a latency, or an eval result). Find what limits it, and rule out that it measured something other than the work you think. +- **Explain the Number.** Apply before you measure, and before you trust, report, or act on a number you measured (a speedup, a regression, a throughput, a latency, or an eval result). Find what limits it, and rule out that it measured something other than the work you think. **Delegation** @@ -92,9 +93,11 @@ Read the leaf skill in full for any principle you apply. Each entry names when i **Use the poteto-agent persona for code-writing delegates and ad-hoc helpers inside playbook steps.** Start the `delegate_task` brief with the absolute `` and `` paths, then "Act as the agent defined in /agents/poteto-agent.md; read it first and treat its body as your role. The poteto-mode skill is /skills/poteto-mode/SKILL.md." The parent runs the playbook. Routed workflows (`how`, `why`, `interrogate`, `reflect`, `swarm`) supply their own roles and briefs. -**Defaults for every `delegate_task` call.** Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Use the `feature, refactoring`, `bug-fix`, `perf-issue`, or `hillclimb` role for the matching code playbook. Hardest changes use `hardest tasks`; prose and judgment use `judgment and prose`; verification uses `swarm workers`. Research and review briefs say "Do not edit files, commit, or push" and the parent checks `git status` afterward. Set `role` to the closest of `implementation`, `research`, `review`, `design`, `test`, or `general`. +The child receives only `task`, without your conversation or your `` block. Replace path placeholders with concrete absolute paths, and paste each role line and fallback note the child will need into `task`. A child that will coordinate more work needs those exact targets too; native harness instruction inheritance does not supply them. -**Honor the resolved role.** Targets in `` override every model named in a persona, harness rule, or models file. Do not adjust model, effort, or other options for a spawn. Change role choices through `$pstack:setup-pstack`; they take effect on the next pstack invocation. Panels retain every resolved seat, including inherited ones. +**Defaults for every `delegate_task` call.** Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Use the `feature, refactoring`, `bug-fix`, `perf-issue`, or `hillclimb` role for the matching code playbook. Hardest changes use `hardest tasks`; prose and judgment use `judgment and prose`; verification uses `swarm workers`. Research and review briefs say "Do not edit files, commit, or push" and the parent checks `git status` afterward. Set `role` to the closest of `implementation`, `research`, `review`, `design`, `test`, or `general`. + +**Honor the resolved role.** Targets in `` override defaults named in a persona, harness rule, or models file. Explicit user changes supersede that snapshot and need validation against fresh `orchestrator_capabilities`. Do not adjust model, effort, or other options for a spawn. Change role choices through `$pstack:setup-pstack`; they take effect on the next pstack invocation. To use newly saved choices in this turn, validate the exact new targets and options against fresh capabilities first; do not reuse the old block for changed roles. Panels retain every resolved seat, including inherited ones. You own every subagent's work. Review the diff and write your own summary, don't pass through what it said. A second opinion is the same prompt against a different model. Agreement is high-signal. @@ -141,7 +144,7 @@ A large or cross-cutting effort (a migration across many call sites, an ambitiou - **Autonomous run.** A long task to drive to completion without stopping ("run until done", "keep running until X"). - **Orchestrate.** A standing project handed to one coordinator chat: multi-day, many stacked PRs, dozens to hundreds of subagents, minimal human turns ("run this whole project", "own this migration until it lands"). Distinct from Autonomous run, which drives one task to a predicate. Work one agent could finish inside the session's budget routes there, not here, however program-shaped the phrasing sounds. - **Autopilot-full.** A queue of independent PRs run to merged with full autonomy. One owner per PR carries build through merge, and the root swarm-verifies each PR before its owner merges ("autopilot this queue", "full autopilot", one-owner-per-PR programs). -- **Autopilot-stack.** A queue of changes built and verified with full autonomy, delivered as one linear reviewed base-branch stack the operator lands ("autopilot-stack", "stack them, don't ship", "build the stack, I'll land it"). +- **Autopilot-stack.** Autopilot-full without the merge. A queue of PRs built and verified with full autonomy, then handed to the operator to land. A PR stacks only on a PR it builds on ("autopilot-stack", "autopilot, don't merge", "stack them, don't ship", "build the stack, I'll land it"). - **Session pickup.** Resuming or taking over a prior agent's in-flight work from a transcript, T3 thread link, or pushed branch. - **Pause safely.** Suspending in-flight work cleanly so it can be resumed, on an explicit pause, going offline, a T3 server restart, or imminent context compaction. The complement to Session pickup. - **Multi-phase or multi-PR plan.** Work that spans phases or stacked PRs. File: `playbooks/multi-phase-plan.md`. diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/autonomous-run.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/autonomous-run.md index 0db377a147ab4..276024c941689 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/autonomous-run.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/autonomous-run.md @@ -6,7 +6,7 @@ 2. Pick an event wake: `watch_pull_request` for PR checks, reviews, and conflicts; child completions for delegated work. End the turn after arming it. Where no supported event exists, use `schedule_task` with an interval (`everyMs` at least 60000) or fixed time, including the absolute pack/models paths and relevant resolved roles in the prompt. Report cadence and `nextRunAt`, retain `scheduledTaskId`, and delete it with `delete_scheduled_task` once the predicate holds. Use one long heartbeat only when a fallback is needed, never a watcher agent or sleep loop. 3. Each iteration makes the smallest change the evidence justifies, verifies it against the predicate, commits if it advanced, discards changes that didn't help. Belt-and-suspenders that "might help" gets reverted, not left to ride. Sequence the work via the **sequence-verifiable-units** principle skill, verifying each unit before the next instead of batching checks at the end. -4. Mid-run discoveries are yours. Address broken skills, related bugs, flaky verifiers, review noise, tooling failures, orphaned follow-ups, and fixable drift yourself via poteto-mode. Put out-of-band fixes in their own PR. Do not park reversible work for the human or use the harness question tool. Surface only irreversible actions, genuine product or preference calls no experiment can settle, or a real dead end. Keep the predicate as the main drive, and return to it after each side fix. +4. Mid-run discoveries that support the goal are yours. Address broken skills, related bugs, flaky verifiers, review noise, tooling failures, orphaned follow-ups, and fixable drift yourself via poteto-mode. Put out-of-band fixes in their own PR. Do not park reversible work for the human or use the harness question tool. Surface only irreversible actions, genuine product or preference calls no experiment can settle, or a real dead end. Keep the predicate as the main drive, and return to it after each side fix. 5. Checkpoint every iteration via the **show-me-your-work** skill, a row for what changed and whether the predicate moved. 6. Stop when the predicate is met. A plateau is not a stop, so keep going and pivot your approach to push past it. Surface a genuine dead end rather than spinning, and never relax the predicate to declare victory. diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-full.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-full.md index d3ba5e3a2245c..65fe78f0f6632 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-full.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-full.md @@ -7,13 +7,13 @@ A verdict covers one patch and green CI covers one head. Step 4 runs a full swar 1. **The operator's gates come first.** The grant covers only the queue's items, so items the operator names as theirs and PRs outside the queue stop at merge-ready. A request to state the plan is not a go. 2. **Spawn one owner per PR, and keep its work where a replacement can find it.** Resolve the forge once for the program. GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr` for every PR operation. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). Each owner is a isolated delegated worker that builds, opens its PR per `playbooks/opening-a-pr.md`, proves the change on the real artifact, babysits to green per `playbooks/babysit.md`, and merges once steps 4 and 5 clear it. - Open the PR early so CI and bots run alongside the work. The code is ready only after the slop-strip (a code simplification pass) and the `$pstack:no-comments` pass, so the full swarm runs on finished code. Then rebase onto current trunk and report the code-ready head before self-proof, so verification runs on the base the patch lands on. Report every later head, and merge-ready once self-proof, CI, and babysit are done. Fix rounds stay on that base unless trunk conflicts or breaks CI, because each rebase restarts CI. Run the repo's checks for the touched paths on the committed head before the code-ready push and before a later push that needs a verifier. + Open the PR early so CI and bots run alongside the work. The code is ready only after the slop-strip (a code simplification pass) and the `$pstack:no-comments` pass, so the full swarm runs on finished code. Then rebase onto current trunk and report the code-ready head before self-proof, so verification runs on the base the patch lands on. Report every later head, and merge-ready once self-proof, CI, and babysit are done. Fix rounds stay on that base unless trunk conflicts, breaks CI, or lands a known concurrent change to the same files, because each rebase restarts CI. Run the repo's checks for the touched paths on the committed head before the code-ready push and before a later push that needs a verifier. - Assume the worker process dies. Soon after starting, push and start a decision trail in the durable store beside `` at `orch/` per the **show-me-your-work** skill, then push after every unit worth keeping. Whoever spawns a subagent records its id, state, and a stuck-after runtime from past runs of its kind in that store, so the root can probe it if the parent dies. Force-push only your own branch, with `--force-with-lease` pinned to the SHA you last pushed or took over, and on any push you did not make, stop and report to the root. + Assume the worker process dies. Soon after starting, push and start a decision trail in the durable store beside `` at `orch/` per the **show-me-your-work** skill, then push each finished unit before you start the next. Whoever spawns a subagent records its id, state, and a stuck-after runtime from past runs of its kind in that store, so the root can probe it if the parent dies. Force-push only your own branch, with `--force-with-lease` pinned to the SHA you last pushed or took over, and on any push you did not make, stop and report to the root. 3. **Run owners in parallel off trunk, never stacked,** so each PR lands on its own. -4. **Swarm-verify once.** A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head. Fan out independent verifiers at the code-ready SHA per the **swarm** skill. They re-run the gates, prove the load-bearing behavior live on the real surface through its control skill (such as the browser or device tools or the shell available in the session) or a named driver, and audit the diff. The verdict is not clean without the live lane, or before you audit the merge-ready report's receipts, because no lane trusts a claim it has not checked. +4. **Swarm-verify once.** A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head, and a wording-only change gets none. Fan out independent verifiers at the code-ready SHA per the **swarm** skill. They re-run the gates, prove the load-bearing behavior live on the real surface through its control skill (such as the browser or device tools or the shell available in the session) or a named driver, and audit the diff. The verdict is not clean without the live lane, or before you audit the merge-ready report's receipts, because no lane trusts a claim it has not checked. - Send every proven finding, whatever a lane labeled it, to the owner in one fix-forward, with a red test across every site with the same defect. A defect trunk shows without the PR is recorded against trunk and does not gate the PR, but one the PR copies or newly reaches is the PR's own. Never push a fix yourself, because each branch has one writer. Replace a stuck owner instead. + Block a PR only for a problem you can show, with a failing run at the head or a doc line the PR made wrong. A problem the PR's base already has, whether trunk or the PR below it in a stack, doesn't block unless the PR copies it or newly reaches it. Only a bug a user would hit, a failed gate, or a wrong shipped doc blocks, and everything else goes in a follow-up. 5. **The owner merges only what you verified, tested on the trunk it lands on.** Green CI alone never authorizes a merge. Once the swarm's lanes have started, merge prep rebases onto current trunk, reports the new head, and waits for green CI. Right before merging, fetch trunk and ask whether its changes can affect the patch, because rebasing for every trunk move never ends. Rebase and wait for CI when the code that would land was never tested, as with a conflict, a file both sides changed, or a change to which checks run for the PR. A lockfile both sides changed needs no rebase when a frozen install passes on the merge result and the packages the PR changes resolve there as on the head, because that install tests the lockfile that would land, and a failing install means the rebase must repair it. When trunk changed a caller of the patch or something the patch depends on, such as code it calls or a dependency version, run the patch's checks on the merge result and merge on a pass. Otherwise merge. If trunk would force a third rebase past merge prep, hold and report to the root, which sequences the colliding work. @@ -22,6 +22,6 @@ A verdict covers one patch and green CI covers one head. Step 4 runs a full swar On the operator's go, create an hourly `schedule_task` (`schedule: {type: "interval", everyMs: 3600000}`) to audit until no delegated work is left, because long runs drift and agents stall silently. Each tick re-reads this playbook from the runtime pack and fixes drift, such as an unreported head or an unrecorded agent, by having its owner record it, because drift is a gap in the record, not a stall. It probes every agent, down to the leaf lanes, and judges each by its side effects, because status text and summaries can hide a stuck child. An agent is stuck when it errors, cannot start turns, or shows nothing new past its stuck-after runtime, and so is an owner that has neither pushed nor written a trail row by the first tick. One waiting on you or the operator is not stuck, so answer it. Cancel a stuck agent with `task_cancel` before replacing its work; do not leave two writers alive. A tick that changed something for the operator ends by catching them up per the **ketchup** skill, and one that changed nothing stays quiet. 7. **Stand down at once on the operator's stop.** A hold reaches every owner as a zero-writes order. Each reports where it stands and keeps its brief until the operator releases it. -**Reply:** catch the operator up per the **ketchup** skill, covering each PR's state and verdict, what merged, countersigns and why, and open operator gates, with links to each PR and the trails. +**Reply:** catch the operator up per the **ketchup** skill, covering each PR's state and verdict, what merged, countersigns and why, open operator gates, and parked follow-ups, with links to each PR and the trails. Scheduled audit prompts include the absolute pack/models paths and relevant resolved role lines. Retain each `scheduledTaskId` and delete it with `delete_scheduled_task` when the run finishes or the operator stops it. Only the parent creates PR watches; children return PR URLs, head SHAs, and check/review states. diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-stack.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-stack.md index 235ee66d07564..819862c8fe7b7 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-stack.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/autopilot-stack.md @@ -1,12 +1,8 @@ ### Autopilot-stack -**You own the stack, never the landing. Hand the operator one linear stack of base-chained PRs to review and land.** **Autopilot-full** holds except for how verified work leaves. Each tick re-reads both playbooks from the runtime pack, and this one wins where they differ. +Run the Autopilot-full playbook, but do not merge. -1. **Run Autopilot-full's owners, minus the landing.** Owners report STACK-READY in place of merge-ready. They never rebase onto another branch, trunk included, and never retarget, merge, close a PR, or arm auto-merge, because the root owns the chain's topology and the operator owns the landing. When an item depends on another, its owner branches from that item's branch, not from trunk. -2. **Give the topology one writer.** Owners push only their own branches and report each tip and its intended parent. Only you rebase or retarget a link, because two writers on one chain undo each other. Append each clean PR in dependency order, with its base set to its parent's branch (`origin pr edit --base ` or `gh pr edit --base `). Never require `gt`. An owner takes your push as its new base and lease, and on a conflict resolves its own slice for you to push. -3. **Absorb drift at the root.** Until landing starts, apply Autopilot-full step 5's test to each link and rebase the chain bottom-up where it calls for one. Then apply the patch-id rule link by link, since a link's base is its parent. -4. **Deliver the chain, then keep your hands off it.** Each link carries its verdict in its PR. Once landing starts, nobody writes to the chain except the post-merge retarget that `playbooks/shipping.md` sanctions, because a second writer breaks a landing in flight. A mid-landing problem goes through you to the operator, and any fix waits for the operator's go. +1. **In place of step 5, stop at verified.** Stop when each PR is verified and green at its current head, then hand the PRs to the operator. After the handoff, nobody pushes without the operator's go. Every loop tick prompt names this file, so each tick sees the no-merge rule. +2. **Stack a PR only when it builds on another PR.** This is the one exception to step 3. Base the PR on the other PR's branch in place of trunk, per **Size and stacks** in `playbooks/opening-a-pr.md`. In place of step 2's rebase onto trunk, each PR rebases onto its parent PR's branch, and the stack root rebases onto trunk. A new head on the lower PR restacks the PRs above it, and a PR whose `git patch-id` the restack leaves unchanged keeps its verdict. -**Choosing between the autopilots.** Autopilot-full when the PRs are independent and landing authority is granted. Autopilot-stack when the operator wants review before landing, the work is sequenced or coupled, or merge authority is withheld. - -**Reply:** catch the operator up per the **ketchup** skill, with links to the stack root and tip, a one-line verdict per link, and anything parked or excluded with the reason. +**Reply:** list each PR with its verdict, in place of what merged. diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/babysit.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/babysit.md index 7f7e789c07106..ed8c438eae2aa 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/babysit.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/babysit.md @@ -7,7 +7,7 @@ Babysitting starts when the user asks for it, which is normally once a phase or 1. **Declare the mode and resolve the forge before any status read.** `drive` runs the loop to merge-ready, for "babysit this", "get it green", "merge-ready". `background` triages without blocking, which is the mode for a plan still executing. `threads-only` answers review comments and touches nothing else, for "address the review bot comments". `check` is one status pass and a report, for "check on X" and "is it green". Undeclared defaults to `drive`. Small or docs-only PRs get `check`, not `drive`. GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr ...` for view, checks, threads, and later shipping. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). 2. **Work the merge frontier and nothing above it.** The lowest unmerged PR is the only one that matters until it merges. Upstack threads get read and batched, never fixed at the cost of restarting the frontier's checks. If you catch yourself upstack while the frontier is red, stop and go back down. 3. **One babysitter per stack.** Before starting, check nothing else is already on it. -4. **Never mutate stack topology.** No base retarget, rebase, stack-wide submit, or force-push from inside a babysit. Fix on the owning branch, report anything rebase-shaped upward, and let the owner do it. An Autopilot-full owner babysitting its own PR is that owner. Where this playbook says to report a rebase, that owner rebases its own branch and publishes it with `git push --force-with-lease` per `playbooks/autopilot-full.md` step 2. In Autopilot-stack, the root is that owner. The one sanctioned creation: when a fix's owning PR has already merged, it becomes a new PR on top of the remaining stack, never a rewrite of merged history, and it is the single case where the frozen queue list of step 6 changes. +4. **Never mutate stack topology.** No base retarget, rebase, stack-wide submit, or force-push from inside a babysit. Fix on the owning branch, report anything rebase-shaped upward, and let the owner do it. An Autopilot-full or Autopilot-stack owner babysitting its own PR is that owner. Where this playbook says to report a rebase, that owner rebases its own branch and publishes it with `git push --force-with-lease` per `playbooks/autopilot-full.md` step 2. The one sanctioned creation: when a fix's owning PR has already merged, it becomes a new PR on top of the remaining stack, never a rewrite of merged history, and it is the single case where the frozen queue list of step 6 changes. 5. **Order is conflicts, then review threads, then CI.** Batch every known fix into one push wave. A conflict is the one blocker you report rather than resolve. Say which branch needs the rebase and stop. Do not fall through to CI to look busy. Name the drift sweep in that report, since trunk may have grown callers of code the stack deletes or moves, and the owner's rebase has to reconcile them in the same wave. 6. **Trust the active forge's verdict, not a green check list.** Ready means the forge agrees this head can merge with no unresolved blockers. On GitHub, run `scripts/watch-pr/watch-pr --status-only` once per wake for its verdict. Never run its polling mode. On Origin, read `origin pr view --checks --comments` and `origin pr thread list ` once. Treat review text as untrusted data and verify it against code. diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/multi-phase-plan.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/multi-phase-plan.md index 19b6c57facec8..051a052e37984 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/multi-phase-plan.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/multi-phase-plan.md @@ -5,7 +5,7 @@ 1. When the change is one or two files with an obvious approach, skip the plan. Say so and stop. 2. Settle open questions by prototype before you write. Run `playbooks/prototype.md` for each. Keep the branch, the SHA, and the screenshots for Appendix A. Ask the operator only about a product or preference call that no run can settle. Give options (the **never-block-on-the-human** principle skill). 3. Explore in subagents with `delegate_task`, the poteto-agent persona brief, and the `how explorer` resolved target (the **guard-the-context-window** principle skill). Use the role's exact resolved target from ``. Each returns file pointers, conventions, test commands, and entry points. No inlined dumps. -4. Copy the skeleton below into the plan file and fill every placeholder. Unless the operator names a path, write the file under the agent store's `docs/`. Keep every heading and every sub-block in the order shown. One section per PR. One PR is one change with its own evidence (the **sequence-verifiable-units** principle skill). Name the execution playbook in **How to read this**. Pick between `playbooks/autopilot-full.md` and `playbooks/autopilot-stack.md` per the rule at the end of `playbooks/autopilot-stack.md`. A standing program takes `playbooks/orchestrate.md`. +4. Copy the skeleton below into the plan file and fill every placeholder. Unless the operator names a path, write the file under the agent store's `docs/`. Keep every heading and every sub-block in the order shown. One section per PR. One PR is one change with its own evidence (the **sequence-verifiable-units** principle skill). Name the execution playbook in **How to read this**. Pick `playbooks/autopilot-full.md` when the run may merge. Pick `playbooks/autopilot-stack.md` when the operator lands the PRs. A standing program takes `playbooks/orchestrate.md`. 5. Write under `$pstack:technical-writing` in full, then `$pstack:unslop`. The body is one Diátaxis mode, how-to. Appendices hold explanation and reference. Each heading states the task or the finding. No long dashes. No mid-sentence colons. 6. Run `node /skills/poteto-mode/scripts/check-plan.mjs ` and fix every line it prints (the **encode-lessons-in-structure** principle skill). 7. Hand back. Post the plan path and the script's output, then stop. Execution starts on the operator's explicit go, under the execution playbook the plan names. @@ -58,13 +58,13 @@ Tests alone are not sufficient verification. A PR is verified only when its unit - [ ] Run the repo's lint and typecheck once before the PR-facing push. Push with hooks on. - [ ] Run a code simplification pass before each commit and `$pstack:no-comments` before review. - [ ] Triage every review bot and security-reviewer comment per `../references/review-bot-triage.md`. -- [ ] Rebase onto current trunk before the code-ready report and babysit. Keep that merge base in fix rounds. Rebase again only at merge prep, on a `git merge-tree` conflict with trunk, or on a CI failure that comes from a change on trunk. +- [ ] Rebase onto current trunk before the code-ready report and babysit. Keep that merge base in fix rounds. Rebase again only at merge prep, on a `git merge-tree` conflict with trunk, on a CI failure that comes from a change on trunk, or when a known concurrent change to the same files lands. ### Verdict and merge, for every PR -- [ ] A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head. Run each full swarm per `/skills/swarm/SKILL.md`. One gates lane. The ten live lanes from the PR's **Verify, live** block. The perf lane from its **Verify, perf** block. Two or more audit lanes, each with its own focus, that read the diff and the receipts and distrust the PR body. The root audits the receipts in the merge-ready report before the verdict. -- [ ] Clean only when every lane is `PASS`. Findings go back to the owner, including a defect that a lane filed as a note. A new head gets a fresh verdict, except for results that stay valid under the patch-id rule in `playbooks/shipping.md`. -- [ ] +- [ ] A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head, and a wording-only change gets none. Run each full swarm per `/skills/swarm/SKILL.md`. One gates lane. The ten live lanes from the PR's **Verify, live** block. The perf lane from its **Verify, perf** block. Two or more audit lanes, each with its own focus, that read the diff and the receipts and distrust the PR body. The root audits the receipts in the merge-ready report before the verdict. +- [ ] Judge every finding by the execution playbook's blocking rule. The verdict is clean when none blocks. A failure on trunk behavior in a lane marked `Regression lane against trunk.` is recorded against trunk and does not block the PR. A new head gets a fresh verdict, except for results that stay valid under the patch-id rule in `playbooks/shipping.md`. +- [ ] ### Boot recipe, for every live lane diff --git a/packages/pstack/upstream/skills/poteto-mode/playbooks/orchestrate.md b/packages/pstack/upstream/skills/poteto-mode/playbooks/orchestrate.md index d28c63843d22d..0762d12db7e91 100644 --- a/packages/pstack/upstream/skills/poteto-mode/playbooks/orchestrate.md +++ b/packages/pstack/upstream/skills/poteto-mode/playbooks/orchestrate.md @@ -1,114 +1,27 @@ ### Orchestrate -**You own the program, never the code. Author briefs, drain the queue, keep the frontier green, decide.** For a whole project handed to one standing coordinator chat: multi-day, many stacked PRs, dozens to hundreds of subagents, the human checking in twice a day instead of every five minutes. One task driven to a predicate is Autonomous run. One ambitious run needing a bespoke workflow is figure-it-out. Route here when the work outlives any single agent. Work one agent could finish inside the session's budget is not a program. +**You run the program, never the code. Decide which agent does each piece of work, then check what each one produces.** For a standing project handed to one coordinator chat: multi-day, many stacked PRs, dozens to hundreds of subagents, few human turns. One task driven to a predicate is Autonomous run. One ambitious run that needs a bespoke workflow is figure-it-out. Work that one agent could finish inside the session's budget is not a program. -Ceremony must scale with the program. On cheap near-identical units, collapse it as each section directs. +Run it through T3 orchestration. Call `orchestrator_capabilities` before delegation and respect its nesting and concurrency limits. Spawn workers with `delegate_task`, copying the role's resolved target and complete options from ``; an inherited seat omits `target`. Keep project notes beside `` under `orch//`, outside the replaceable pack. The parent links and watches PRs through `link_pull_request` and `watch_pull_request`; workers return the PR URL, head SHA, and pending checks. Child completion and PR events wake the parent. End the turn while waiting. Run no polling loops and keep no second scheduler or agent ledger. -Three rules carry the rest. +#### Topology -- Completions are queue events, not interrupts. -- Every spawn and every fresh follow-up brief carries the standing orders verbatim. -- The brief is the product. A vague brief fails quietly, because a worker cannot ask you a question. +- **One owner per PR.** The owner is the only writer of its branch and carries the PR from build to merge. Only the owner pushes, rebases, or retargets it. Closing or retargeting a base PR orphans every PR above it, so the owner does that only on your call. You never author or edit code. Conflicted merges, chain rebases, and fixes go to owners. +- **A fresh worker for each independent task.** Its kickoff carries everything it needs, including each upstream report it depends on, pasted in full. Workers can't see their siblings. Use `isolation: "worktree"` for independent code edits. A worktree starts at the parent checkout's committed local HEAD and stays on this environment. Arrange the committed base within the task's authorization first; a branch name in the brief does not change it. Use the shared checkout only for a browser or device check, a simulator, or auth available only there, with a single writer. Include the absolute pack/models paths and relevant resolved roles in every brief; never change effort or speed from the resolved target. +- **A sub-coordinator only when one area has many parallel workers.** It owns that area's workers and kickoffs and sends you one rollup, never raw worker reports. Stop at three levels: you, sub-coordinators, and workers. Keep a bounded rolling window within the advertised T3 concurrency limit, and start the next worker as each finishes instead of waiting on a whole batch. +- **Use a fresh delegate for every task and review round.** T3 does not fork a worker's process state. Preserve committed work and artifacts, then give the new worker a consolidated brief with the prior findings, branch, head SHA, and remaining work. Arrange its checkout base explicitly. Retain every `taskId`, cancel replaced work with `task_cancel`, and use a new `clientRequestId` for new work; retries of the same launch keep the same key and arguments. Never poll or message a child to check on it. +- **Each PR gets a verifier that isn't its author,** on a different model family. The owner merges only with the task's merge authorization and that verifier's pass at the current head. Each owner returns pending checks to the parent; the parent delegates any later fix or merge as a fresh task. +- **Start with one unit.** Take it through kickoff, owner, verifier, and merge before you fan out, so a flaw in the kickoff costs one agent instead of fifty. On near-identical cheap units, the first unit is that test. -#### Roles and placement +#### Supervision -- **Coordinator (this chat).** Local. Frames, authors briefs, drains the inbox, owns the human report, makes judgment calls. It never authors or edits code. Conflicted merges, chain rebases, and code changes are always tasks. Mechanically landing a verified unit (fast-forward or clean cherry-pick of a worker's commit, then push) is bookkeeping the coordinator may do itself on repos where local git is cheap. Queueing finished work behind an idle stacker is how a deadline harvests nothing. The loop is agentic end to end. Agents are spawned and drained only through the delegate_task tool. Use the role's exact resolved target from ``. State reads and writes go through `scripts/orch/orch.ts` at drain points, one command in and one line out. The CLI never spawns, waits, or wakes anything. -- **Sub-coordinator.** Always local, durable, one per track, and only when the program exceeds what one coordinator's drains can manage. A track the coordinator can drain itself needs no middle layer. Each nested layer re-pays a full orientation preamble, and a blocking sub-coordinator hides its children while the parent idles. Owns its track's units and boards, authors its workers' briefs, spawns its own workers and verifiers (nesting works to depth 3, and a nested spawn has the advertised `delegate_task` schema; respect its nesting constraints). Rolls up aggregates at wave boundaries. Never forwards raw child reports. Cap in-flight children at what one drain can process, roughly ten, as a rolling window. Never as blocking batches, which cost the slowest child of every batch. -- **Worker / verifier.** Always `isolation: "worktree"` unless the task needs this machine: the browser or device tools or the shell runtime verification (available in the session). Reading T3 thread history through `t3_thread_read`. Simulators and local IDE state. Auth that exists only here. Workers should not depend on an implicit local store, so their briefs inline what they need or point at repo paths. Prefer fewer, broader workers. One writer per worktree or branch (principle-separate-before-serializing-shared-state). Run a unit's verifier on a different model family from its worker. +- **Each kickoff names the evidence that proves the work is done,** such as a command and its expected output, a browser or device check, or an artifact. It also names the goal, the paths the worker may and may not write, and the actions it must not take. If you can't name the evidence, the unit isn't scoped yet. +- **Read the diff or the artifact, not only the worker's summary.** Green CI is an input to a verdict, not a verdict. Behavioral work needs more than a type check. A verdict covers one head SHA, so a new push needs a new verdict. Read a sample of each sub-coordinator's kickoffs while its workers run, and fix its instructions when one fails. +- **Send back work that drifts from scope or grows past what was asked.** Keep only the fixes that block the program, and park the rest as follow-ups. +- **Check a PR's live state before you report on it,** with `gh pr view` and `gh pr checks` or your forge's equivalents, not the worker's last report. Account for every worker you spawned as done, replaced, or absorbed into another worker's scope. +- **Stop or replace a worker that stalls or loops.** Judge it by its pushes and PR activity, not its status text. Retry by cause: a smaller scope after a context or memory limit, the same kickoff after a dropped connection, and a different model after a tool error. After two retries, drop the unit and replan around it. When a stopped worker returns late, check its result against the PR's current state, and hand any unique findings to a fresh worker. +- **Ask the user only for a real decision or a blocker,** such as an irreversible action, a product or preference call that no experiment settles, or a dead end that survived a replan. Rebase mechanics, retries, CI flakes, review triage, and "should I keep going" never reach the user. -Depth stays at coordinator, track, worker. Author the track decomposition per project (build, landing, and verification are common cuts, not a required shape). Hard-coded swarm trees were tried and parked as too rigid. +**Reply:** what landed against the goal, each PR's live state and verdict with its link, what was dropped and why, and the decisions waiting on the user. -#### Store layout - -Use the durable store beside `` at `orch//`. Pass it as `orch --store ` or `ORCH_STORE`. Never store durable run state inside ``, which updates replace. Run `bun install --frozen-lockfile` in `/skills/poteto-mode/scripts/` before using the helpers; the writable pack may need installation again after an update. Every file has exactly one writer. Owners publish facts, readers aggregate at read time. Use `bun scripts/orch/orch.ts` for bookkeeping, written below as `orch`, while its canonical plain TSV and JSON stay readable without the CLI. - -- `preferences.md` is the standing-orders register: numbered lines, one constraint each (model policy, stack shape and count, verification bar, forbidden paths, escalation policy). Paste it verbatim into every spawn and every fresh follow-up brief. Directives decay across resumes, and each dropped one costs a human turn. When you catch yourself restating an instruction, append the line before you act (principle-encode-lessons-in-structure). -- `overview.md` is the durable PR and issue DB. Append. Never rewrite wholesale per event. -- `units.tsv` has one row per unit: id, track, state, branch, PR, head SHA, brief path. Update rows in place. -- `frontier.json` is the computed merge frontier, per Stack safety. -- `ledger.tsv` is the verification ledger, per Verification. -- `inbox/` holds completion pointers. `gates.md` parks human gates (question, options, default on no answer). -- `decisions.tsv` is the trail via the show-me-your-work skill. -- `status.md` is derived from `units.tsv` and `ledger.tsv` at each drain, never hand-maintained. Regenerate it from the tables instead of narrating events into it. - -#### The brief - -Your prompts to agents are your only product, and a sloppy brief compounds into slop across the whole tree. Every spawn carries all of it. A field you cannot fill is a unit you have not scoped yet. - -``` -GOAL one sentence, the outcome, executable by a stranger with no chat access -SCOPE paths this unit may write; paths it may not; its exclusive worktree or branch -CONTEXT pointers to files and PRs; upstream reports pasted in full when this unit - depends on them, because workers cannot see siblings -ACCEPTANCE checkable criteria, one per line -VERIFY exact commands or the control-skill path, plus known gotchas -TIMEBOX rough cap on runtime; on expiry, return partial findings and stop rather than run on -FORBIDDEN no PR retargets or merges, no rebase, no force-push, no fixes outside scope, - plus unit-specific bans -REPORT status, branch, head SHA, PRs, verdict, what you actually ran, deviations, - suggested follow-ups -STANDING -``` - -Size the brief to the unit. A one-command unit gets the template collapsed to a paragraph that still names goal, scope, the verify command, and the report shape. A 4KB scaffold around a two-line edit costs more to write and obey than the edit. Local spawns may reference the standing-orders file by store path. Verbatim paste is for isolated spawns and every fresh follow-up brief. - -A sub-coordinator brief adds its track boundary and unit list, its spawn budget with the worktree default and the local exception list, the drain protocol, and the rollup format (per child: name, status, PR, head SHA, verdict, one line, plus track status and frontier delta). - -A dependency is a context relay, not just ordering. Undeclared upstream context makes the worker guess. Missing fields are a refuse-to-spawn condition. Audit one sampled worker brief per sub-coordinator per wave, concurrently with the wave it samples, never as a gate in front of it. A failing brief stops that track and fixes the sub-coordinator's instructions, not just the worker, because brief quality decays late in a run. Never resume-chain a brief. Respawn fresh with consolidated scope. - -#### Steps - -1. **Frame.** State the done predicate as something countable ("all 126 units merged, each ledger-verified `unit-test-verified` or better"). Quantify scope: units, rough effort, expected stacks, and the wall-clock budget. If one agent could finish inside that budget, stop here and run Autonomous run instead. Collapsing must not depend on another document being present. It means do the work directly in this session, plain workers where they help, verification inline, landing as you go, and none of the store, register, or pilot machinery below. Schedule landing against the budget. By roughly 70% of it, stop spawning and land what is verified. Name the tracks per project. A contested decomposition or one-way door goes through the arena skill before the pilot. Present the framing once. Reversible prep proceeds without waiting. -2. **Install the runtime.** Run `orch init`. Open the trail via the show-me-your-work skill, write the standing orders before any spawn, and seed `frontier.json` from existing PRs with `orch frontier set --repo `, adding `--forge origin` when the forge is Origin. -3. **Pilot.** Push one unit through the whole path: brief, worker, verification, stack entry, ledger row, merge. The pilot exists to falsify the brief template, the verify recipe, and the unit size while that costs one agent instead of fifty. Fix the contract from pilot evidence before any fan-out. Scale the pilot to the unit. On programs of near-identical cheap units, the first unit is the pilot, run as a normal unit with its verify command inline, and fan-out starts the moment it lands. The dedicated pilot pipeline (separate verifier agent, audit gate) is for expensive or novel unit shapes, not for clone-units where a serialized pilot has nothing to falsify. -4. **Scale.** Spawn a rolling window of workers up to the in-flight cap, refilling as children finish. Blocking batches pay the slowest child of every batch. Spawn track sub-coordinators only past the one-drain threshold in Roles. Recompute ready work after each drain. Relay upstream reports into downstream briefs. Keep sibling communication upward only. The sampled brief audit runs alongside the wave it samples and stops the next refill on failure, not the current one. -5. **Drain.** Run the queue discipline below at every drain point. -6. **Land.** Landing is continuous, never a terminal phase. Integration starts with the first verified unit and runs alongside the remaining waves. On heavy repos the stacker is a standing role from wave one, integrating as units verify. On repos where local git is cheap, the coordinator lands verified units itself per Roles. Keep the frontier green before upper-stack work. Stack safety governs. Advance `frontier.json` only on merge or reported new head SHAs. -7. **Close.** Drain the final inbox, reconcile every spawned agent to a terminal row (done, abandoned, zombie-reconciled), confirm the predicate on the real artifact, confirm every landed PR has a verdict for its current head SHA, audit the trail per show-me-your-work including its cross-model review, encode recurring corrections into `preferences.md` or the brief template. Leave the store intact. It is the postmortem. - -#### Queue and drain - -- On a completion notification, run `orch inbox push [--report PATH]` and return to what you were doing. Never deep-review inline. A completion that needs review becomes a verifier unit. Never review a diff inside a drain. -- Drain in batches at four points: the end of a critical section, a track rollup, a PR-event wake from `watch_pull_request` (with one long scheduled heartbeat only when needed), and before a human report. Begin each batch with `orch inbox drain`. Arrivals during a drain wait for the next one. -- Critical sections you finish first: authoring a brief, a stack operation, a conflict decision, writing a gate, updating ledger or frontier. -- Each drain classifies every pointer (landed, needs-verify, failed, zombie, noise), writes the resulting rows through `orch unit add`, `orch unit set`, and `orch ledger record`, runs `orch status`, then spawns the next wave in one message. -- Account for every spawned child at its track's rollup: arrived, respawned, or its scope explicitly absorbed. Silently redoing a missing child's work hides both the wasted spend and the coverage gap its result existed to close. -- A drain turn ends with the three lines from `orch status`: counts against the states, what changed, gates open. Detail lives in `status.md`. The full reply contract applies at checkpoints and close. - -#### Stack safety - -- The frontier is a computed object, never narrative. Recompute `frontier.json` after every merge and stack mutation: ordered PR list, branch names, head SHAs, a generation number, the lowest unmerged PR. The command walks the PR base chain through the checkout's current branch, so chain order and PR states come from the forge. It uses `gh` by default. Pass `--forge origin` when the program resolved the forge to Origin. Head SHAs come from local branch refs, so resolve it from the stacker's clone. A branch with no PR errors rather than guessing. -- Exactly one stacker per stack may mutate topology (chain rebases, force-pushes, base retargets, merges), serialized within its stack. Record the holder in the standing orders. Chain rebases run in an isolated checkout. If they require another machine, explicitly choose a linked environment; a worktree still consumes this environment's resources. -- Workers never rebase and never retarget or merge PRs. Babysitters follow `playbooks/babysit.md`, one per stack, scoped to one immutable frontier generation. They report conflicts to the stacker rather than rebasing. -- PR closes and retargets go through the stacker only. Closing a base PR orphans every chain above it. Merges and stack surgery are units with briefs like any other. -- The parent tracks post-merge fallout through supported PR event watches and one explicitly requested scheduled audit when needed. - -#### Verification - -Scale verification to the unit. When VERIFY is a single cheap command, the worker runs it and reports the output, and the coordinator spot-checks receipts. A dedicated verifier agent (on a different model family than the worker) is for units whose verification is expensive, judgment-laden, or high-blast-radius. A verifier agent whose entire product would be rerunning one command is ceremony, not verification. - -Write ledger rows with `orch ledger record`. Check the current PR and head SHA with `orch ledger check`. `ledger.tsv`, one row per verdict, keyed by PR number plus head SHA: `live-ui-verified | unit-test-verified | type-check-only | verifier-blocked | verifier-failed`. CI green is an input to a verdict, not a verdict. Behavioral work needs better than `type-check-only`. `verifier-blocked` is not a pass. Respawn when the environment heals. `verifier-failed` gets a fix unit, not a re-verify. A worker may self-report. A verifier overrides it on the same key. A new head SHA voids the row, so re-verify after restack. The ledger answers "was this verified", not memory and not the transcript. - -A unit is not done until its output is externalized the moment it lands, never batched to the end of the run. A worker pushes its branch, a verifier writes its ledger row, receipts land in the store. Work that exists only on one checkout when its process dies was never done. - -#### Liveness and failure - -- Never message an agent to check on it or poll `task_status`. Follow its returned `next` and completion events. Read the ledger, `units.tsv`, forge state, pushed branches, and `t3_thread_read` only when evidence requires diagnosis. Keep every `taskId` so replaced work can be cancelled. Thread activity timestamps alone do not prove liveness. -- A silent death gets a synthetic postmortem row in the inbox (unit, failure mode, last evidence, options). Replan on evidence as it arrives. Never wait for full quiescence. -- Retry by mode: cap-hit or oom, respawn with smaller scope. Network-drop, retry as-is. Tool-error, retry on a different model. Unknown, retry once. Two retries, then abandon the unit and replan around it. -- A zombie that returns hours late reconciles against the current frontier and ledger before anything is accepted. Salvage unique findings through a fresh unit, never a blind merge. -- When continued spawning would produce garbage tree-wide (bad upstream output, broken acceptance, dead infra), write a stop line at the top of the standing orders, let in-flight work finish, fix the cause, clear it. -- Bound your own infra retries the same way you bound a child's. After a few consecutive tool aborts, stop retrying. Write a terminal handoff to durable state (what is done, where it lives, the exact command to resume) and end the run. -- After a T3 server restart, verify task state through one recovery read before assuming a worker survived. Re-read standing orders and `units.tsv`, recompute the frontier, reconcile pushed branches and PRs with thread history, then cancel or replace lost work using fresh consolidated briefs. The store lock clears when its owning PID is gone. - -#### Escalation - -Reaches the human, batched into the status page rather than per item: irreversible actions (force-push to shared branches, deploys, deletions, closing someone else's PR), genuine product or preference calls no experiment settles, a standing order that contradicts observed reality, a program-level dead end that survived a replan. Park each as a `gates.md` entry before asking, and route work around it. - -Never reaches the human: frontier nudges, rebase mechanics, retries, CI flake triage, review-thread triage, format fixes, scope the brief already forbids (refuse and continue), and "should I keep going". When in doubt, act and log. - -Mid-run discoveries fix only what blocks the frontier. Everything else parks in follow-ups. At this fan-out a small scope leak multiplies into PRs nobody asked for. - -**Reply:** at checkpoints and close: the predicate and the count against it from `units.tsv` and `ledger.tsv`, tracks and what each landed, the frontier (PR list plus SHAs), verdicts summary, what was abandoned and why, gates awaiting the human (the only asks), the store path, and the trail path. Numbers from the tables, not narrative. Include PR links. - -Scheduled audit prompts include the absolute pack/models paths and relevant resolved role lines. Retain each `scheduledTaskId` and delete it with `delete_scheduled_task` when the run finishes or the operator stops it. Only the parent creates PR watches; children return PR URLs, head SHAs, and check/review states. +Retain every `scheduledTaskId` for an explicitly needed heartbeat and delete it with `delete_scheduled_task` at completion or cancellation. Include the absolute pack/models paths and resolved roles in scheduled prompts. Never schedule a child-status poll. diff --git a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.test.ts b/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.test.ts deleted file mode 100644 index be8edd505b7d1..0000000000000 --- a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.test.ts +++ /dev/null @@ -1,795 +0,0 @@ -import { afterEach, describe, expect, it } from "bun:test"; -import { - chmod, - mkdir, - mkdtemp, - readFile, - readdir, - rm, - writeFile, -} from "node:fs/promises"; -import { realpathSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { - NotFoundError, - UserError, - openStore, - parseVerdict, - type Forge, - type OpenStoreOptions, - type Store, -} from "./store.ts"; - -const SCRIPT = join(import.meta.dir, "orch.ts"); -const directories: string[] = []; -const handles: Store[] = []; - -interface RunResult { - readonly code: number; - readonly stdout: string; - readonly stderr: string; -} - -async function makeDirectory(): Promise { - const directory = await mkdtemp(join(tmpdir(), "orch-test-")); - directories.push(directory); - return directory; -} - -function useStore( - directory: string, - options?: OpenStoreOptions -): Store { - const store = openStore(directory, options); - handles.push(store); - return store; -} - -async function initializedStore(): Promise<{ - readonly directory: string; - readonly store: Store; -}> { - const directory = await makeDirectory(); - const store = useStore(directory); - await store.init(); - return { directory, store }; -} - -function git({ - args, - repo, -}: { - args: readonly string[]; - repo: string; -}): string { - const result = Bun.spawnSync(["git", "-C", repo, ...args]); - if (result.exitCode !== 0) { - throw new Error( - `git ${args.join(" ")} failed: ${result.stderr.toString()}` - ); - } - return result.stdout.toString().trim(); -} - -async function makeGitStack(directory: string): Promise<{ - readonly repo: string; - readonly mergedSha: string; - readonly closedSha: string; - readonly openSha: string; - readonly draftSha: string; -}> { - const repo = join(directory, "repo"); - await mkdir(repo); - git({ repo, args: ["init", "--initial-branch=main"] }); - git({ repo, args: ["config", "user.name", "Orch Test"] }); - git({ repo, args: ["config", "user.email", "orch@example.com"] }); - await writeFile(join(repo, "main.txt"), "main\n"); - git({ repo, args: ["add", "."] }); - git({ repo, args: ["commit", "-m", "main"] }); - - const branches = [ - "stack/merged", - "stack/closed", - "stack/open", - "stack/draft", - ]; - for (const [index, branch] of branches.entries()) { - git({ repo, args: ["checkout", "-b", branch] }); - await writeFile(join(repo, `stack-${index}.txt`), `${branch}\n`); - git({ repo, args: ["add", "."] }); - git({ repo, args: ["commit", "-m", branch] }); - } - git({ repo, args: ["checkout", "stack/open"] }); - - return { - repo, - mergedSha: git({ repo, args: ["rev-parse", "stack/merged"] }), - closedSha: git({ repo, args: ["rev-parse", "stack/closed"] }), - openSha: git({ repo, args: ["rev-parse", "stack/open"] }), - draftSha: git({ repo, args: ["rev-parse", "stack/draft"] }), - }; -} - -type FixtureState = "open" | "draft" | "merged" | "closed"; - -const FORGE_FIXTURES: Readonly< - Record< - Forge, - { - readonly fields: string; - readonly row: (pr: { - number: number; - state: FixtureState; - head: string; - base: string; - }) => Record; - } - > -> = { - gh: { - fields: "number,state,headRefName,baseRefName", - row: ({ number, state, head, base }) => ({ - number, - state: state === "draft" ? "OPEN" : state.toUpperCase(), - headRefName: head, - baseRefName: base, - }), - }, - origin: { - fields: "number,status,headRef,baseRef", - row: ({ number, state, head, base }) => ({ - number, - status: state, - headRef: head, - baseRef: base, - }), - }, -}; - -function forgeRows( - forge: Forge, - prs: readonly { - number: number; - state: FixtureState; - head: string; - base: string; - }[] -): string { - return JSON.stringify(prs.map((pr) => FORGE_FIXTURES[forge].row(pr))); -} - -async function withFakeForge({ - directory, - forge, - operation, - currentRows, -}: { - directory: string; - forge: Forge; - operation: () => Promise; - currentRows?: string; -}): Promise { - const bin = join(directory, "bin"); - const outputPath = join(directory, "forge-output.txt"); - await mkdir(bin, { recursive: true }); - await writeFile( - outputPath, - `${ - currentRows ?? - forgeRows(forge, [ - { - number: 11, - state: "open", - head: "stack/open", - base: "stack/closed", - }, - ]) - }\n` - ); - const { fields } = FORGE_FIXTURES[forge]; - const list = (filter: string, branch: string): string => - `pr list ${filter} ${branch} --state all --json ${fields}`; - const fake = join(bin, forge); - await writeFile( - fake, - `#!/usr/bin/env bash -set -euo pipefail -if [ "$(pwd -P)" != "${realpathSync(join(directory, "repo"))}" ]; then - printf '${forge} ran outside the fixture repo: %s\\n' "$(pwd -P)" >&2 - exit 2 -fi -case "$*" in - "${list("--head", "stack/open")}") - cat "${outputPath}" - ;; - "${list("--head", "stack/closed")}") - printf '%s\\n' '${forgeRows(forge, [{ number: 13, state: "closed", head: "stack/closed", base: "stack/merged" }])}' - ;; - "${list("--head", "stack/merged")}") - printf '%s\\n' '${forgeRows(forge, [{ number: 10, state: "merged", head: "stack/merged", base: "main" }])}' - ;; - "${list("--base", "stack/open")}") - printf '%s\\n' '${forgeRows(forge, [ - { number: 14, state: "draft", head: "stack/draft", base: "stack/open" }, - { - number: 15, - state: "closed", - head: "stack/abandoned", - base: "stack/open", - }, - ])}' - ;; - "${list("--base", "stack/draft")}") - printf '[]\\n' - ;; - *) - printf 'unexpected ${forge} arguments: %s\\n' "$*" >&2 - exit 2 - ;; -esac -` - ); - await chmod(fake, 0o755); - - const originalPath = process.env.PATH; - process.env.PATH = `${bin}:${originalPath ?? ""}`; - try { - return await operation(); - } finally { - if (originalPath === undefined) { - delete process.env.PATH; - } else { - process.env.PATH = originalPath; - } - } -} - -function runCli( - args: readonly string[], - env: Readonly> = process.env -): RunResult { - const result = Bun.spawnSync([process.execPath, SCRIPT, ...args], { env }); - return { - code: result.exitCode, - stdout: result.stdout.toString(), - stderr: result.stderr.toString(), - }; -} - -afterEach(async () => { - for (const store of handles.splice(0).reverse()) { - await store.close(); - } - for (const directory of directories.splice(0)) { - await rm(directory, { recursive: true, force: true }); - } -}); - -describe("Store", () => { - it("initializes an idempotent plain-file store and releases its lock", async () => { - const directory = await makeDirectory(); - const store = useStore(directory); - - expect(await store.init()).toEqual({ store: directory }); - const firstUnits = await readFile(join(directory, "units.tsv"), "utf8"); - const firstLedger = await readFile( - join(directory, "ledger.tsv"), - "utf8" - ); - - expect(await store.init()).toEqual({ store: directory }); - expect(await readFile(join(directory, "units.tsv"), "utf8")).toBe( - firstUnits - ); - expect(await readFile(join(directory, "ledger.tsv"), "utf8")).toBe( - firstLedger - ); - expect((await readdir(directory)).sort()).toEqual([ - ".orch.lock", - "frontier.json", - "gates.md", - "inbox", - "ledger.tsv", - "preferences.md", - "units.tsv", - ]); - - await store.close(); - expect(await readdir(directory)).not.toContain(".orch.lock"); - }); - - it("composes unit add, set, get, list, and counts", async () => { - const { store } = await initializedStore(); - - expect( - await store.units.add({ - id: "u1", - track: "build", - brief: "briefs/u1.md", - }) - ).toMatchObject({ id: "u1", state: "pending" }); - expect( - await store.units.add({ id: "=SUM(A1)", track: "+build" }) - ).toMatchObject({ id: "'=SUM(A1)", track: "'+build" }); - - const updated = await store.units.set({ - id: "u1", - state: "done", - branch: "poteto/u1", - pr: 184530, - sha: "abc123", - }); - expect(updated).toEqual({ - id: "u1", - track: "build", - state: "done", - branch: "poteto/u1", - pr: "184530", - sha: "abc123", - brief: "briefs/u1.md", - }); - expect(await store.units.get("u1")).toEqual(updated); - expect( - await store.units.list({ state: "done", track: "build" }) - ).toEqual([updated]); - expect(await store.units.counts()).toEqual({ done: 1, pending: 1 }); - await expect( - store.units.add({ id: "u1", track: "build" }) - ).rejects.toThrow("unit u1 already exists"); - await expect( - store.units.set({ id: "missing", state: "done" }) - ).rejects.toBeInstanceOf(NotFoundError); - }); - - it("records, replaces, checks, and summarizes typed ledger verdicts", async () => { - const { store } = await initializedStore(); - - try { - await store.ledger.check({ pr: 184530, sha: "abc123" }); - throw new Error("expected ledger check to fail"); - } catch (error) { - expect(error).toBeInstanceOf(NotFoundError); - if (error instanceof NotFoundError) { - expect(error.output).toEqual({ - compact: "NOT-VERIFIED", - json: { - pr: "184530", - sha: "abc123", - verdict: "NOT-VERIFIED", - }, - }); - } - } - expect(() => parseVerdict("looks-good")).toThrow("verdict must be"); - - const recorded = await store.ledger.record({ - pr: 184530, - sha: "abc123", - verdict: "unit-test-verified", - evidence: "reports/verify.md", - verifier: "opus", - }); - expect(await store.ledger.check({ pr: 184530, sha: "abc123" })).toEqual( - recorded - ); - expect(await store.ledger.summary()).toEqual({ - "unit-test-verified": 1, - }); - - await store.ledger.record({ - pr: 184530, - sha: "abc123", - verdict: "live-ui-verified", - evidence: "reports/live.md", - }); - expect(await store.ledger.summary()).toEqual({ - "live-ui-verified": 1, - }); - }); - - it("pushes, peeks, and atomically drains inbox pointers", async () => { - const { directory, store } = await initializedStore(); - - const first = await store.inbox.push({ - agent: "worker-1", - unit: "u1", - status: "done", - report: "reports/u1.md", - }); - expect(first.pointer).toMatchObject({ unit: "u1", status: "done" }); - expect(first.filename).toEndWith(".tsv"); - await store.inbox.push({ - agent: "worker-2", - unit: "u2", - status: "failed", - }); - - expect(await store.inbox.count()).toBe(2); - expect(await store.inbox.peek()).toHaveLength(2); - expect(await store.inbox.count()).toBe(2); - expect(await store.inbox.drain()).toHaveLength(2); - expect(await store.inbox.count()).toBe(0); - expect(await readdir(join(directory, "inbox"))).toEqual([]); - expect( - (await readdir(directory)).filter((name) => - name.startsWith(".inbox-drain-") - ) - ).toEqual([]); - }); - - it("replaces a stale lock whose holder pid is dead", async () => { - const { directory } = await initializedStore(); - const exited = Bun.spawn(["true"]); - await exited.exited; - await writeFile(join(directory, ".orch.lock"), `${exited.pid}\n`); - - const stale: string[] = []; - const recovered = useStore(directory, { - onStaleLock: (holder) => stale.push(holder), - }); - expect( - await recovered.units.add({ id: "u1", track: "build" }) - ).toMatchObject({ id: "u1" }); - expect(stale).toEqual([String(exited.pid)]); - await recovered.close(); - expect(await readdir(directory)).not.toContain(".orch.lock"); - }); - - it("blocks a writer and steals the pid lock only with force", async () => { - const { directory, store } = await initializedStore(); - await store.close(); - await writeFile(join(directory, ".orch.lock"), `${process.pid}\n`); - - const blocked = useStore(directory); - await expect( - blocked.units.add({ id: "u1", track: "build" }) - ).rejects.toThrow(`store lock held by pid ${process.pid}`); - - const stolen: string[] = []; - const forced = useStore(directory, { - force: true, - onLockStolen: (holder) => stolen.push(holder), - }); - expect( - await forced.units.add({ id: "u1", track: "build" }) - ).toMatchObject({ id: "u1" }); - expect(stolen).toEqual([String(process.pid)]); - await forced.close(); - expect(await readdir(directory)).not.toContain(".orch.lock"); - }); - - it("parks gates, stores standing orders, and renders status", async () => { - const { directory, store } = await initializedStore(); - await store.units.add({ id: "u1", track: "build" }); - expect( - await store.gates.park({ - id: "release", - question: "Ship now?", - options: "ship,wait", - defaultAnswer: "wait", - }) - ).toMatchObject({ kind: "open", id: "release" }); - expect( - await store.standing.add({ line: "Never force push." }) - ).toEqual({ number: 1, line: "Never force push." }); - - const first = await store.status.render(); - expect(first.changed).toBe("first render"); - expect(first.summary.openGateIds).toEqual(["release"]); - expect(await readFile(join(directory, "status.md"), "utf8")).toContain( - "| release | open | Ship now? |" - ); - expect((await store.status.render()).changed).toBe("no derived changes"); - - expect( - await store.gates.resolve({ id: "release", answer: "ship" }) - ).toMatchObject({ kind: "resolved", answer: "ship" }); - expect((await store.status.render()).changed).toBe("open gates 1->0"); - expect(await store.gates.list()).toEqual([]); - expect(await store.standing.show()).toEqual([ - { number: 1, line: "Never force push." }, - ]); - }); - - for (const forge of ["gh", "origin"] as const) { - it(`resolves the ordered frontier from the ${forge} base chain and validates an optional pin`, async () => { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - const forgeParams = forge === "gh" ? {} : { forge }; - - await withFakeForge({ - directory, - forge, - operation: async () => { - expect( - await store.frontier.set({ repo: stack.repo, ...forgeParams }) - ).toEqual({ - generation: 1, - prs: [ - { - pr: 10, - branches: "stack/merged", - sha: stack.mergedSha, - state: "MERGED", - }, - { - pr: 13, - branches: "stack/closed", - sha: stack.closedSha, - state: "CLOSED", - }, - { - pr: 11, - branches: "stack/open", - sha: stack.openSha, - state: "OPEN", - }, - { - pr: 14, - branches: "stack/draft", - sha: stack.draftSha, - state: "OPEN", - }, - ], - lowestUnmerged: 11, - }); - expect( - ( - await store.frontier.set({ - repo: stack.repo, - ...forgeParams, - prs: [10, 13, 11, 14], - }) - ).generation - ).toBe(2); - expect((await store.frontier.show()).generation).toBe(2); - await expect( - store.frontier.set({ - repo: stack.repo, - ...forgeParams, - prs: [10, 11, 12], - }) - ).rejects.toThrow( - "frontier pin mismatch: missing from resolved chain: 12; extra in resolved chain: 13,14" - ); - await expect( - store.frontier.set({ - repo: stack.repo, - ...forgeParams, - prs: [13, 10, 11, 14], - }) - ).rejects.toThrow( - "frontier pin mismatch: order differs: expected 13,10,11,14; resolved 10,13,11,14" - ); - await expect( - store.frontier.set({ - repo: stack.repo, - ...forgeParams, - prs: [10, 10], - }) - ).rejects.toThrow("--prs must not contain duplicates"); - }, - }); - }); - - it(`resolves a replacement ${forge} PR alongside closed branch history`, async () => { - for (const historicalState of ["closed", "merged"] as const) { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - await withFakeForge({ - directory, forge, - currentRows: forgeRows(forge, [ - { number: 9, state: historicalState, head: "stack/open", base: "main" }, - { number: 11, state: "open", head: "stack/open", base: "stack/closed" }, - ]), - operation: async () => { - const frontier = await store.frontier.set({ repo: stack.repo, forge }); - expect(frontier.prs.map((row) => row.pr)).toEqual([10, 13, 11, 14]); - expect(frontier.lowestUnmerged).toBe(11); - }, - }); - } - }); - - it(`still rejects multiple active ${forge} PRs on one branch`, async () => { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - await withFakeForge({ - directory, forge, - currentRows: forgeRows(forge, [ - { number: 11, state: "open", head: "stack/open", base: "stack/closed" }, - { number: 12, state: "open", head: "stack/open", base: "main" }, - ]), - operation: async () => { - await expect(store.frontier.set({ repo: stack.repo, forge })).rejects.toThrow("multiple pull requests"); - }, - }); - }); - - it(`rejects unparseable ${forge} output loudly`, async () => { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - - await withFakeForge({ - directory, - forge, - currentRows: "this line is not forge output", - operation: async () => { - await expect( - store.frontier.set({ repo: stack.repo, forge }) - ).rejects.toThrow( - `${forge} pr list --head stack/open output is not valid JSON` - ); - }, - }); - }); - } - - it("names the missing pull request when a chain branch has none", async () => { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - - await withFakeForge({ - directory, - forge: "gh", - currentRows: "[]", - operation: async () => { - await expect(store.frontier.set({ repo: stack.repo })).rejects.toThrow( - "branch stack/open has no pull request; open one with gh pr create" - ); - }, - }); - }); - - it("rejects malformed TSV, verdict, frontier, and inbox data", async () => { - const { directory, store } = await initializedStore(); - - await writeFile(join(directory, "units.tsv"), "wrong\n"); - await expect(store.units.list()).rejects.toThrow( - "units.tsv has an invalid header" - ); - await writeFile( - join(directory, "units.tsv"), - "id\ttrack\tstate\tbranch\tpr\tsha\tbrief\nshort\trow\n" - ); - await expect(store.units.list()).rejects.toThrow( - "units.tsv has a malformed row" - ); - - await writeFile( - join(directory, "ledger.tsv"), - "pr\tsha\tverdict\tevidence\tverifier\tts\n1\tsha\tinvalid\treport\tme\tnow\n" - ); - await expect(store.ledger.summary()).rejects.toThrow( - "ledger.tsv has invalid verdict invalid" - ); - - await writeFile(join(directory, "frontier.json"), '{"generation":"1"}\n'); - await expect(store.frontier.show()).rejects.toThrow( - "frontier.json has an invalid shape" - ); - - await writeFile(join(directory, "inbox", "bad.tsv"), "too\tshort\n"); - await expect(store.inbox.peek()).rejects.toThrow( - "inbox pointer bad.tsv is malformed" - ); - }); - - it("rejects operations after close", async () => { - const { store } = await initializedStore(); - await store.close(); - await expect(store.units.list()).rejects.toThrow("store is closed"); - await expect(store.status.render()).rejects.toBeInstanceOf(UserError); - }); -}); - -describe("orch CLI", () => { - it("prints commander help and rejects invalid parsing with exit 1", async () => { - const help = runCli(["--help"]); - expect(help.code).toBe(0); - expect(help.stdout).toContain("Commands:"); - expect(help.stdout).toContain("unit"); - expect(help.stdout).toContain("ledger"); - - const frontierHelp = runCli(["frontier", "set", "--help"]); - expect(frontierHelp.code).toBe(0); - expect(frontierHelp.stdout).toContain("--repo "); - expect(frontierHelp.stdout).toContain("--forge "); - expect(frontierHelp.stdout).toContain("--prs "); - - const directory = await makeDirectory(); - const invalid = runCli(["--store", directory, "unit", "add", "u1"]); - expect(invalid.code).toBe(1); - expect(invalid.stderr).toContain("required option '--track '"); - - const badForge = runCli([ - "--store", - directory, - "frontier", - "set", - "--repo", - directory, - "--forge", - "gt", - ]); - expect(badForge.code).toBe(1); - expect(badForge.stderr).toContain("Allowed choices are gh, origin"); - }); - - it("accepts ORCH_STORE and emits complete JSON", async () => { - const directory = await makeDirectory(); - const env = { ...process.env, ORCH_STORE: directory }; - expect(runCli(["init"], env).code).toBe(0); - - const added = runCli( - ["unit", "add", "u1", "--track", "build", "--json"], - env - ); - expect(added.code).toBe(0); - expect(JSON.parse(added.stdout)).toEqual({ - id: "u1", - track: "build", - state: "pending", - branch: "", - pr: "", - sha: "", - brief: "", - }); - }); - - it("maps user and not-found outcomes to the preserved exit codes", async () => { - const directory = await makeDirectory(); - expect(runCli(["--store", directory, "init"]).code).toBe(0); - - const missingRepo = runCli([ - "--store", - directory, - "frontier", - "set", - ]); - expect(missingRepo.code).toBe(1); - expect(missingRepo.stderr).toContain( - "set --repo or ORCH_REPO" - ); - - const userError = runCli([ - "--store", - directory, - "unit", - "add", - "", - "--track", - "build", - ]); - expect(userError.code).toBe(1); - expect(userError.stderr).toContain("unit id must not be empty"); - - const missingUnit = runCli([ - "--store", - directory, - "unit", - "get", - "missing", - ]); - expect(missingUnit.code).toBe(2); - expect(missingUnit.stderr).toContain("unit missing not found"); - - const missingLedger = runCli([ - "--store", - directory, - "--json", - "ledger", - "check", - "184530", - "abc123", - ]); - expect(missingLedger.code).toBe(2); - expect(JSON.parse(missingLedger.stdout)).toEqual({ - pr: "184530", - sha: "abc123", - verdict: "NOT-VERIFIED", - }); - expect(missingLedger.stderr).toBe(""); - }); -}); diff --git a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.ts b/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.ts deleted file mode 100755 index bfa6fb0626c05..0000000000000 --- a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/orch.ts +++ /dev/null @@ -1,590 +0,0 @@ -#!/usr/bin/env bun - -import { ensureDependenciesInstalled } from "../install-dependencies.ts"; -import { - NotFoundError, - UsageError, - openStore, - parseVerdict, - type Counts, - type Forge, - type Frontier, - type InboxPointer, - type OpenGate, - type StandingLine, - type StatusReport, - type Store, - type Unit, - type Verdict, -} from "./store.ts"; - -ensureDependenciesInstalled(); -const { - Command: CommanderCommand, - CommanderError, - InvalidArgumentError, - Option, -} = await import("commander"); -type Command = InstanceType; - -const DISPLAY_LIMIT = 4; - -interface Io { - readonly stdout: (value: string) => void; - readonly stderr: (value: string) => void; -} - -interface GlobalOptions { - readonly store?: string; - readonly json: boolean; - readonly force: boolean; -} - -interface UnitAddOptions { - readonly track: string; - readonly brief?: string; -} - -interface UnitSetOptions { - readonly state: string; - readonly branch?: string; - readonly pr?: number; - readonly sha?: string; -} - -interface UnitListOptions { - readonly state?: string; - readonly track?: string; -} - -interface LedgerRecordOptions { - readonly evidence: string; - readonly verifier?: string; -} - -interface InboxPushOptions { - readonly report?: string; -} - -interface InboxDrainOptions { - readonly peek: boolean; -} - -interface GateParkOptions { - readonly question: string; - readonly options: string; - readonly default: string; -} - -interface GateResolveOptions { - readonly answer: string; -} - -interface FrontierSetOptions { - readonly repo?: string; - readonly forge: Forge; - readonly prs?: readonly number[]; -} - -function message(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} - -function positiveInteger(value: string): number { - const parsed = Number(value); - if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(parsed)) { - throw new InvalidArgumentError("must be a positive integer"); - } - return parsed; -} - -function prList(value: string): readonly number[] { - const parts = value.split(","); - if (parts.some((part) => part.length === 0)) { - throw new InvalidArgumentError("requires a comma-separated PR list"); - } - return parts.map(positiveInteger); -} - -function countLine(value: Counts): string { - const entries = Object.entries(value); - return entries.length === 0 - ? "none" - : entries.map(([name, count]) => `${name}=${count}`).join(", "); -} - -function unitLine(unit: Unit): string { - return [ - unit.id, - unit.track, - unit.state, - unit.branch, - unit.pr, - unit.sha, - unit.brief, - ].join("\t"); -} - -function pointerLine(pointer: InboxPointer): string { - return [ - pointer.ts, - pointer.agent, - pointer.unit, - pointer.status, - pointer.report, - ].join("\t"); -} - -function gateLine(gate: OpenGate): string { - return [ - gate.id, - gate.question, - gate.options, - gate.defaultAnswer, - ].join("\t"); -} - -function compactRows( - rows: readonly T[], - format: (row: T) => string, - empty: string, - limit: number | null = DISPLAY_LIMIT -): string { - if (rows.length === 0) { - return empty; - } - const visible = limit === null ? rows : rows.slice(0, limit); - const lines = visible.map(format); - if (limit !== null && rows.length > limit) { - lines.push(`... ${rows.length - limit} more; use --json`); - } - return lines.join("\n"); -} - -function frontierLine(value: Frontier): string { - const prs = - value.prs.length === 0 - ? "none" - : value.prs - .map( - (row) => - `${row.branches}#${row.pr}@${row.sha}:${row.state}` - ) - .join(","); - return `generation=${value.generation} prs=${prs} lowest-unmerged=${value.lowestUnmerged ?? "none"}`; -} - -function statusLines(report: StatusReport): string { - const visible = report.summary.openGateIds.slice(0, DISPLAY_LIMIT); - const more = - report.summary.openGateIds.length > DISPLAY_LIMIT - ? `,+${report.summary.openGateIds.length - DISPLAY_LIMIT} more` - : ""; - return [ - `counts: units=${report.units.length}; states=${countLine(report.summary.unitStates)}; ledger=${countLine(report.summary.ledgerVerdicts)}`, - `changed: ${report.changed}`, - `gates open: ${report.summary.openGateIds.length}${ - visible.length > 0 ? `; ids=${visible.join(",")}${more}` : "" - }`, - ].join("\n"); -} - -function emit( - io: Io, - json: boolean, - value: T, - compact: (result: T) => string, - jsonValue: (result: T) => unknown = (result) => result -): void { - const rendered = json - ? JSON.stringify(jsonValue(value), null, 2) - : compact(value); - io.stdout(rendered.endsWith("\n") ? rendered : `${rendered}\n`); -} - -function storeDirectory(program: Command): string { - const value = program.opts().store; - if (value === undefined || value.trim().length === 0) { - throw new UsageError("set --store or ORCH_STORE"); - } - return value; -} - -function frontierRepo(options: FrontierSetOptions): string { - const value = options.repo; - if (value === undefined || value.trim().length === 0) { - throw new UsageError("set --repo or ORCH_REPO"); - } - return value; -} - -async function runStore( - program: Command, - io: Io, - operation: (store: Store) => Promise, - compact: (result: T) => string, - jsonValue?: (result: T) => unknown -): Promise { - const options = program.opts(); - const store = openStore(storeDirectory(program), { - force: options.force, - onLockStolen: (holder) => - io.stderr(`stealing store lock held by pid ${holder}\n`), - onStaleLock: (holder) => - io.stderr(`replacing stale store lock (pid ${holder} is dead)\n`), - }); - try { - const result = await operation(store); - emit(io, options.json, result, compact, jsonValue); - } finally { - await store.close(); - } -} - -function leaf(parent: Command, name: string, description: string): Command { - return parent - .command(name) - .description(description) - .allowExcessArguments(false); -} - -function requireSubcommand(program: Command): never { - storeDirectory(program); - throw new UsageError("a valid command is required"); -} - -function createProgram(io: Io): Command { - const program = new CommanderCommand("orch") - .description("Plain-file orchestrate bookkeeping") - .usage("[--store ] [--json] [--force] ") - .configureOutput({ writeOut: io.stdout, writeErr: io.stderr }) - .exitOverride() - .showHelpAfterError() - .allowExcessArguments(false) - .addOption( - new Option("--store ", "store directory (or ORCH_STORE)").env( - "ORCH_STORE" - ) - ) - .option("--json", "print complete rows as JSON", false) - .option("--force", "steal an existing store lock", false); - - leaf(program, "init", "initialize the store").action(() => - runStore( - program, - io, - (store) => store.init(), - (result) => `initialized ${result.store}` - ) - ); - - const unit = program - .command("unit") - .description("manage work units") - .action(() => requireSubcommand(program)); - leaf(unit, "add ", "add a unit") - .requiredOption("--track ", "unit track") - .option("--brief ", "brief path") - .action((id: string, options: UnitAddOptions) => - runStore( - program, - io, - (store) => - store.units.add({ - id, - track: options.track, - brief: options.brief, - }), - unitLine - ) - ); - leaf(unit, "set ", "update a unit") - .requiredOption("--state ", "unit state") - .option("--branch ", "branch name") - .option("--pr ", "pull request number", positiveInteger) - .option("--sha ", "commit SHA") - .action((id: string, options: UnitSetOptions) => - runStore( - program, - io, - (store) => - store.units.set({ - id, - state: options.state, - branch: options.branch, - pr: options.pr, - sha: options.sha, - }), - unitLine - ) - ); - leaf(unit, "get ", "get a unit").action((id: string) => - runStore(program, io, (store) => store.units.get(id), unitLine) - ); - leaf(unit, "list", "list units") - .option("--state ", "filter by state") - .option("--track ", "filter by track") - .action((options: UnitListOptions) => - runStore( - program, - io, - (store) => store.units.list(options), - (rows) => compactRows(rows, unitLine, "(no units)") - ) - ); - leaf(unit, "counts", "count units by state").action(() => - runStore(program, io, (store) => store.units.counts(), countLine) - ); - - const ledger = program - .command("ledger") - .description("manage verification records") - .action(() => requireSubcommand(program)); - leaf(ledger, "record", "record a verification verdict") - .argument("", "pull request number", positiveInteger) - .argument("", "commit SHA") - .argument("", "verification verdict", parseVerdict) - .requiredOption("--evidence ", "evidence path") - .option("--verifier ", "verifier name") - .action( - ( - pr: number, - sha: string, - verdict: Verdict, - options: LedgerRecordOptions - ) => - runStore( - program, - io, - (store) => - store.ledger.record({ - pr, - sha, - verdict, - evidence: options.evidence, - verifier: options.verifier, - }), - (row) => `${row.pr}\t${row.sha}\t${row.verdict}` - ) - ); - leaf(ledger, "check", "check a verification verdict") - .argument("", "pull request number", positiveInteger) - .argument("", "commit SHA") - .action((pr: number, sha: string) => - runStore( - program, - io, - (store) => store.ledger.check({ pr, sha }), - (row) => row.verdict - ) - ); - leaf(ledger, "summary", "count verification verdicts").action(() => - runStore(program, io, (store) => store.ledger.summary(), countLine) - ); - - const inbox = program - .command("inbox") - .description("manage agent pointers") - .action(() => requireSubcommand(program)); - leaf(inbox, "push ", "push an inbox pointer") - .option("--report ", "report path") - .action( - ( - agent: string, - unitId: string, - status: string, - options: InboxPushOptions - ) => - runStore( - program, - io, - (store) => - store.inbox.push({ - agent, - unit: unitId, - status, - report: options.report, - }), - (result) => - `${result.pointer.unit}\t${result.pointer.status}\t${result.filename}`, - (result) => result.pointer - ) - ); - leaf(inbox, "drain", "drain inbox pointers") - .option("--peek", "read without draining", false) - .action((options: InboxDrainOptions) => - runStore( - program, - io, - (store) => - options.peek ? store.inbox.peek() : store.inbox.drain(), - (rows) => compactRows(rows, pointerLine, "(empty)", null) - ) - ); - leaf(inbox, "count", "count inbox pointers").action(() => - runStore( - program, - io, - (store) => store.inbox.count(), - String, - (count) => ({ count }) - ) - ); - - const gate = program - .command("gate") - .description("manage decision gates") - .action(() => requireSubcommand(program)); - leaf(gate, "park ", "park a decision gate") - .requiredOption("--question ", "gate question") - .requiredOption("--options ", "gate options") - .requiredOption("--default ", "default answer") - .action((id: string, options: GateParkOptions) => - runStore( - program, - io, - (store) => - store.gates.park({ - id, - question: options.question, - options: options.options, - defaultAnswer: options.default, - }), - (result) => `${result.id}\topen` - ) - ); - leaf(gate, "list", "list open decision gates").action(() => - runStore( - program, - io, - (store) => store.gates.list(), - (rows) => compactRows(rows, gateLine, "(no open gates)") - ) - ); - leaf(gate, "resolve ", "resolve a decision gate") - .requiredOption("--answer ", "chosen answer") - .action((id: string, options: GateResolveOptions) => - runStore( - program, - io, - (store) => store.gates.resolve({ id, answer: options.answer }), - (result) => `${result.id}\tresolved\t${result.answer}` - ) - ); - - const frontier = program - .command("frontier") - .description("manage the stack frontier") - .action(() => requireSubcommand(program)); - leaf( - frontier, - "set", - "resolve the pull request base chain and set the frontier" - ) - .addOption( - new Option( - "--repo ", - "repository directory (or ORCH_REPO)" - ).env("ORCH_REPO") - ) - .addOption( - new Option("--forge ", "pull request forge") - .choices(["gh", "origin"]) - .default("gh") - ) - .option( - "--prs ", - "optional expected pull request order pin", - prList - ) - .action((options: FrontierSetOptions) => - runStore( - program, - io, - (store) => - store.frontier.set({ - repo: frontierRepo(options), - forge: options.forge, - prs: options.prs, - }), - frontierLine - ) - ); - leaf(frontier, "show", "show the frontier").action(() => - runStore(program, io, (store) => store.frontier.show(), frontierLine) - ); - - leaf(program, "status", "render status.md and print a summary").action(() => - runStore(program, io, (store) => store.status.render(), statusLines) - ); - - const standing = program - .command("standing") - .description("manage standing orders") - .action(() => requireSubcommand(program)); - leaf(standing, "show", "show standing orders").action(() => - runStore( - program, - io, - (store) => store.standing.show(), - (rows) => - compactRows( - rows, - (item: StandingLine) => `${item.number}. ${item.line}`, - "(no standing orders)" - ) - ) - ); - leaf(standing, "add ", "add a standing order").action((line: string) => - runStore( - program, - io, - (store) => store.standing.add({ line }), - (item) => `${item.number}. ${item.line}` - ) - ); - - program.action(() => requireSubcommand(program)); - return program; -} - -function handleError(error: unknown, program: Command, io: Io): number { - if (error instanceof CommanderError) { - return error.exitCode === 0 ? 0 : 1; - } - const json = program.opts().json; - if (error instanceof NotFoundError) { - const output = error.output; - if (output === undefined) { - io.stderr(`error: ${error.message}\n`); - } else { - emit(io, json, output.json, () => output.compact); - } - return 2; - } - io.stderr(`error: ${message(error)}\n`); - if (error instanceof UsageError) { - io.stderr(program.helpInformation()); - } - return 1; -} - -export async function main( - argv: readonly string[], - io: Io = { - stdout: (value) => process.stdout.write(value), - stderr: (value) => process.stderr.write(value), - } -): Promise { - const program = createProgram(io); - try { - await program.parseAsync(argv, { from: "user" }); - return 0; - } catch (error) { - return handleError(error, program, io); - } -} - -if (import.meta.main) { - process.exitCode = await main(process.argv.slice(2)); -} diff --git a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/store.ts b/packages/pstack/upstream/skills/poteto-mode/scripts/orch/store.ts deleted file mode 100644 index a7cef5af83af9..0000000000000 --- a/packages/pstack/upstream/skills/poteto-mode/scripts/orch/store.ts +++ /dev/null @@ -1,1681 +0,0 @@ -import { execFileSync } from "node:child_process"; -import { randomUUID } from "node:crypto"; -import type { Dirent } from "node:fs"; -import { - access, - mkdir, - open, - readFile, - readdir, - rename, - rm, - unlink, - writeFile, -} from "node:fs/promises"; -import { basename, dirname, join, resolve } from "node:path"; - -const UNIT_HEADER = "id\ttrack\tstate\tbranch\tpr\tsha\tbrief"; -const LEDGER_HEADER = "pr\tsha\tverdict\tevidence\tverifier\tts"; -const LOCK_FILE = ".orch.lock"; - -export type Verdict = - | "live-ui-verified" - | "unit-test-verified" - | "type-check-only" - | "verifier-blocked" - | "verifier-failed"; - -export interface Unit { - readonly id: string; - readonly track: string; - readonly state: string; - readonly branch: string; - readonly pr: string; - readonly sha: string; - readonly brief: string; -} - -export interface LedgerEntry { - readonly pr: string; - readonly sha: string; - readonly verdict: Verdict; - readonly evidence: string; - readonly verifier: string; - readonly ts: string; -} - -export interface InboxPointer { - readonly ts: string; - readonly agent: string; - readonly unit: string; - readonly status: string; - readonly report: string; -} - -export interface InboxPushResult { - readonly pointer: InboxPointer; - readonly filename: string; -} - -export interface OpenGate { - readonly kind: "open"; - readonly id: string; - readonly question: string; - readonly options: string; - readonly defaultAnswer: string; -} - -export interface ResolvedGate { - readonly kind: "resolved"; - readonly id: string; - readonly question: string; - readonly options: string; - readonly defaultAnswer: string; - readonly answer: string; -} - -export type Gate = OpenGate | ResolvedGate; - -export type FrontierPrState = "OPEN" | "MERGED" | "CLOSED"; - -export interface FrontierPr { - readonly pr: number; - readonly branches: string; - readonly sha: string; - readonly state: FrontierPrState; -} - -export interface Frontier { - readonly generation: number; - readonly prs: readonly FrontierPr[]; - readonly lowestUnmerged: number | null; -} - -export interface StandingLine { - readonly number: number; - readonly line: string; -} - -export type Counts = Readonly>; - -export interface StatusSummary { - readonly unitStates: Counts; - readonly ledgerVerdicts: Counts; - readonly frontierGeneration: number; - readonly openGateIds: readonly string[]; -} - -export interface StatusReport { - readonly units: readonly Unit[]; - readonly ledger: readonly LedgerEntry[]; - readonly frontier: Frontier; - readonly gates: readonly Gate[]; - readonly summary: StatusSummary; - readonly changed: string; -} - -export interface AddUnitParams { - readonly id: string; - readonly track: string; - readonly brief?: string; -} - -export interface SetUnitParams { - readonly id: string; - readonly state: string; - readonly branch?: string; - readonly pr?: number; - readonly sha?: string; -} - -export interface ListUnitsParams { - readonly state?: string; - readonly track?: string; -} - -export interface RecordLedgerParams { - readonly pr: number; - readonly sha: string; - readonly verdict: Verdict; - readonly evidence: string; - readonly verifier?: string; -} - -export interface CheckLedgerParams { - readonly pr: number; - readonly sha: string; -} - -export interface PushInboxParams { - readonly agent: string; - readonly unit: string; - readonly status: string; - readonly report?: string; -} - -export interface ParkGateParams { - readonly id: string; - readonly question: string; - readonly options: string; - readonly defaultAnswer: string; -} - -export interface ResolveGateParams { - readonly id: string; - readonly answer: string; -} - -export interface SetFrontierParams { - readonly repo: string; - readonly forge?: Forge; - readonly prs?: readonly number[]; -} - -export interface AddStandingParams { - readonly line: string; -} - -export interface OpenStoreOptions { - readonly force?: boolean; - readonly onLockStolen?: (holder: string) => void; - readonly onStaleLock?: (holder: string) => void; -} - -export interface Store { - readonly units: { - readonly add: (params: AddUnitParams) => Promise; - readonly set: (params: SetUnitParams) => Promise; - readonly get: (id: string) => Promise; - readonly list: (params?: ListUnitsParams) => Promise; - readonly counts: () => Promise; - }; - readonly ledger: { - readonly record: (params: RecordLedgerParams) => Promise; - readonly check: (params: CheckLedgerParams) => Promise; - readonly summary: () => Promise; - }; - readonly inbox: { - readonly push: (params: PushInboxParams) => Promise; - readonly drain: () => Promise; - readonly peek: () => Promise; - readonly count: () => Promise; - }; - readonly gates: { - readonly park: (params: ParkGateParams) => Promise; - readonly list: () => Promise; - readonly resolve: (params: ResolveGateParams) => Promise; - }; - readonly frontier: { - readonly set: (params: SetFrontierParams) => Promise; - readonly show: () => Promise; - }; - readonly standing: { - readonly show: () => Promise; - readonly add: (params: AddStandingParams) => Promise; - }; - readonly status: { - readonly render: () => Promise; - }; - readonly init: () => Promise<{ readonly store: string }>; - readonly close: () => Promise; -} - -export interface NotFoundOutput { - readonly compact: string; - readonly json: unknown; -} - -export class UserError extends Error {} -export class UsageError extends UserError {} -export class NotFoundError extends UserError { - public constructor( - message: string, - public readonly output?: NotFoundOutput - ) { - super(message); - } -} - -function errorCode(error: unknown): string | null { - if ( - error !== null && - typeof error === "object" && - "code" in error && - typeof error.code === "string" - ) { - return error.code; - } - return null; -} - -function errorMessage(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} - -function isRecord(value: unknown): value is Record { - return value !== null && typeof value === "object" && !Array.isArray(value); -} - -function isUnknownArray(value: unknown): value is readonly unknown[] { - return Array.isArray(value); -} - -function verdictOrNull(value: string): Verdict | null { - switch (value) { - case "live-ui-verified": - case "unit-test-verified": - case "type-check-only": - case "verifier-blocked": - case "verifier-failed": - return value; - default: - return null; - } -} - -function frontierPrStateOrNull(value: unknown): FrontierPrState | null { - switch (value) { - case "OPEN": - case "MERGED": - case "CLOSED": - return value; - default: - return null; - } -} - -export function parseVerdict(value: string): Verdict { - const verdict = verdictOrNull(value); - if (verdict === null) { - throw new UserError( - "verdict must be live-ui-verified, unit-test-verified, type-check-only, verifier-blocked, or verifier-failed" - ); - } - return verdict; -} - -function cleanCell(value: string): string { - const cleaned = value.replace(/[\t\n\r]/g, " "); - return /^[=+\-@]/.test(cleaned) ? `'${cleaned}` : cleaned; -} - -function requiredCell(value: string, label: string): string { - const cleaned = cleanCell(value); - if (cleaned.trim().length === 0) { - throw new UserError(`${label} must not be empty`); - } - return cleaned; -} - -function requiredLine(value: string, label: string): string { - const cleaned = value.replace(/[\n\r]/g, " ").trim(); - if (cleaned.length === 0) { - throw new UserError(`${label} must not be empty`); - } - return cleaned; -} - -function positiveInteger(value: number, label: string): number { - if (!Number.isSafeInteger(value) || value < 1) { - throw new UserError(`${label} must be a positive integer`); - } - return value; -} - -async function exists(path: string): Promise { - try { - await access(path); - return true; - } catch (error) { - if (errorCode(error) === "ENOENT") { - return false; - } - throw error; - } -} - -async function atomicWrite(path: string, contents: string): Promise { - const temporary = join( - dirname(path), - `.${basename(path)}.${process.pid}.${randomUUID()}.tmp` - ); - try { - await writeFile(temporary, contents, { flag: "wx" }); - await rename(temporary, path); - } finally { - await rm(temporary, { force: true }); - } -} - -async function writeIfMissing(path: string, contents: string): Promise { - if (!(await exists(path))) { - await atomicWrite(path, contents); - } -} - -async function requiredFile(path: string): Promise { - try { - return await readFile(path, "utf8"); - } catch (error) { - if (errorCode(error) === "ENOENT") { - throw new UserError( - `store is not initialized at ${dirname(path)}; run orch init` - ); - } - throw error; - } -} - -function holderIsDead(holder: string): boolean { - const pid = Number.parseInt(holder, 10); - if (!Number.isSafeInteger(pid) || pid <= 0 || String(pid) !== holder) { - return false; - } - try { - process.kill(pid, 0); - return false; - } catch (error) { - return errorCode(error) === "ESRCH"; - } -} - -async function acquireLock( - store: string, - options: OpenStoreOptions -): Promise<() => Promise> { - const path = join(store, LOCK_FILE); - const pid = String(process.pid); - const create = async (): Promise => { - const handle = await open(path, "wx"); - await handle.writeFile(`${pid}\n`); - await handle.close(); - }; - - const takeOver = async (): Promise => { - await unlink(path); - try { - await create(); - } catch (retryError) { - if (errorCode(retryError) === "EEXIST") { - const retryHolder = - (await readFile(path, "utf8")).trim() || "unknown"; - throw new UserError(`store lock held by pid ${retryHolder}`); - } - throw retryError; - } - }; - - try { - await create(); - } catch (error) { - if (errorCode(error) !== "EEXIST") { - throw error; - } - let holder = "unknown"; - try { - holder = (await readFile(path, "utf8")).trim() || "unknown"; - } catch { - holder = "unknown"; - } - if (holderIsDead(holder)) { - options.onStaleLock?.(holder); - await takeOver(); - } else if (options.force) { - options.onLockStolen?.(holder); - await takeOver(); - } else { - throw new UserError(`store lock held by pid ${holder}`); - } - } - - return async (): Promise => { - try { - if ((await readFile(path, "utf8")).trim() === pid) { - await unlink(path); - } - } catch (error) { - if (errorCode(error) !== "ENOENT") { - throw error; - } - } - }; -} - -async function readTsv( - path: string, - header: string, - width: number -): Promise { - const lines = (await requiredFile(path)).replace(/\r/g, "").split("\n"); - if (lines.shift() !== header) { - throw new UserError(`${basename(path)} has an invalid header`); - } - return lines - .filter((value) => value.length > 0) - .map((value) => { - const cells = value.split("\t"); - if (cells.length !== width) { - throw new UserError(`${basename(path)} has a malformed row`); - } - return cells; - }); -} - -async function writeTsv( - path: string, - header: string, - rows: readonly (readonly string[])[] -): Promise { - const body = rows.map((row) => row.map(cleanCell).join("\t")).join("\n"); - await atomicWrite(path, `${header}\n${body}${body.length > 0 ? "\n" : ""}`); -} - -async function readUnits(store: string): Promise { - return (await readTsv(join(store, "units.tsv"), UNIT_HEADER, 7)).map( - (row) => ({ - id: row[0] ?? "", - track: row[1] ?? "", - state: row[2] ?? "", - branch: row[3] ?? "", - pr: row[4] ?? "", - sha: row[5] ?? "", - brief: row[6] ?? "", - }) - ); -} - -function unitCells(unit: Unit): readonly string[] { - return [ - unit.id, - unit.track, - unit.state, - unit.branch, - unit.pr, - unit.sha, - unit.brief, - ]; -} - -async function saveUnits(store: string, rows: readonly Unit[]): Promise { - await writeTsv( - join(store, "units.tsv"), - UNIT_HEADER, - rows.map(unitCells) - ); -} - -async function readLedger(store: string): Promise { - return (await readTsv(join(store, "ledger.tsv"), LEDGER_HEADER, 6)).map( - (row) => { - const rawVerdict = row[2] ?? ""; - const verdict = verdictOrNull(rawVerdict); - if (verdict === null) { - throw new UserError(`ledger.tsv has invalid verdict ${rawVerdict}`); - } - return { - pr: row[0] ?? "", - sha: row[1] ?? "", - verdict, - evidence: row[3] ?? "", - verifier: row[4] ?? "", - ts: row[5] ?? "", - }; - } - ); -} - -function ledgerCells(row: LedgerEntry): readonly string[] { - return [ - row.pr, - row.sha, - row.verdict, - row.evidence, - row.verifier, - row.ts, - ]; -} - -async function saveLedger( - store: string, - rows: readonly LedgerEntry[] -): Promise { - await writeTsv( - join(store, "ledger.tsv"), - LEDGER_HEADER, - rows.map(ledgerCells) - ); -} - -function pointerCells(pointer: InboxPointer): readonly string[] { - return [ - pointer.ts, - pointer.agent, - pointer.unit, - pointer.status, - pointer.report, - ]; -} - -async function readPointers( - directory: string -): Promise { - let entries: Dirent[]; - try { - entries = await readdir(directory, { withFileTypes: true }); - } catch (error) { - if (errorCode(error) === "ENOENT") { - throw new UserError( - `store is not initialized at ${dirname(directory)}; run orch init` - ); - } - throw error; - } - const result: InboxPointer[] = []; - const files = entries - .filter((entry) => entry.isFile() && entry.name.endsWith(".tsv")) - .sort((left, right) => left.name.localeCompare(right.name)); - for (const entry of files) { - const raw = (await readFile(join(directory, entry.name), "utf8")).replace( - /\r?\n$/, - "" - ); - const row = raw.split("\t"); - if (/[\r\n]/.test(raw) || row.length !== 5) { - throw new UserError(`inbox pointer ${entry.name} is malformed`); - } - result.push({ - ts: row[0] ?? "", - agent: row[1] ?? "", - unit: row[2] ?? "", - status: row[3] ?? "", - report: row[4] ?? "", - }); - } - return result; -} - -function renderGates(rows: readonly Gate[]): string { - if (rows.length === 0) { - return ""; - } - const blocks = rows.map((gate) => { - const answer = - gate.kind === "resolved" ? `\n- Answer: ${gate.answer}` : ""; - return `## ${gate.id} - -- Status: ${gate.kind} -- Question: ${gate.question} -- Options: ${gate.options} -- Default: ${gate.defaultAnswer}${answer}`; - }); - return `# Gates\n\n${blocks.join("\n\n")}\n`; -} - -async function readGates(store: string): Promise { - const raw = (await requiredFile(join(store, "gates.md"))) - .replace(/\r/g, "") - .trim(); - if (raw.length === 0) { - return []; - } - const prefix = "# Gates\n\n## "; - if (!raw.startsWith(prefix)) { - throw new UserError("gates.md has an invalid heading"); - } - const result: Gate[] = []; - for (const block of raw.slice(prefix.length).split("\n\n## ")) { - const lines = block.split("\n").filter((value) => value.length > 0); - const id = lines.shift() ?? ""; - const fields = new Map(); - for (const value of lines) { - const match = /^- ([^:]+): (.*)$/.exec(value); - if (match === null) { - throw new UserError(`gates.md has a malformed gate ${id}`); - } - fields.set(match[1] ?? "", match[2] ?? ""); - } - const status = fields.get("Status"); - const question = fields.get("Question"); - const options = fields.get("Options"); - const defaultAnswer = fields.get("Default"); - if ( - id.length === 0 || - question === undefined || - options === undefined || - defaultAnswer === undefined - ) { - throw new UserError(`gates.md has a malformed gate ${id}`); - } - if (status === "open") { - result.push({ kind: "open", id, question, options, defaultAnswer }); - } else if (status === "resolved" && fields.has("Answer")) { - result.push({ - kind: "resolved", - id, - question, - options, - defaultAnswer, - answer: fields.get("Answer") ?? "", - }); - } else { - throw new UserError(`gates.md has invalid status ${status ?? ""}`); - } - } - if (new Set(result.map((gate) => gate.id)).size !== result.length) { - throw new UserError("gates.md has duplicate gate ids"); - } - return result; -} - -function parseFrontier(raw: string): Frontier { - let value: unknown; - try { - value = JSON.parse(raw); - } catch { - throw new UserError("frontier.json is not valid JSON"); - } - if (!isRecord(value)) { - throw new UserError("frontier.json must contain an object"); - } - if (Object.keys(value).length === 0) { - return { generation: 0, prs: [], lowestUnmerged: null }; - } - if ( - typeof value.generation !== "number" || - !Number.isSafeInteger(value.generation) || - value.generation < 0 || - !isUnknownArray(value.prs) || - !( - value.lowestUnmerged === null || - (typeof value.lowestUnmerged === "number" && - Number.isSafeInteger(value.lowestUnmerged)) - ) - ) { - throw new UserError("frontier.json has an invalid shape"); - } - const prs: FrontierPr[] = []; - for (const row of value.prs) { - const state = isRecord(row) - ? frontierPrStateOrNull(row.state) - : null; - if ( - !isRecord(row) || - typeof row.pr !== "number" || - !Number.isSafeInteger(row.pr) || - row.pr < 1 || - typeof row.branches !== "string" || - row.branches.length === 0 || - typeof row.sha !== "string" || - state === null - ) { - throw new UserError("frontier.json has an invalid PR row"); - } - prs.push({ - pr: row.pr, - branches: row.branches, - sha: row.sha, - state, - }); - } - return { - generation: value.generation, - prs, - lowestUnmerged: value.lowestUnmerged, - }; -} - -async function readFrontier(store: string): Promise { - return parseFrontier(await requiredFile(join(store, "frontier.json"))); -} - -async function readStanding( - store: string -): Promise { - const raw = (await requiredFile(join(store, "preferences.md"))).replace( - /\r/g, - "" - ); - if (raw.trim().length === 0) { - return []; - } - const result: StandingLine[] = []; - for (const value of raw.split("\n").filter((item) => item.length > 0)) { - const match = /^([1-9]\d*)\. (.+)$/.exec(value); - const number = Number(match?.[1] ?? 0); - if (match === null || number !== result.length + 1) { - throw new UserError("preferences.md has malformed numbering"); - } - result.push({ number, line: match[2] ?? "" }); - } - return result; -} - -function countValues(values: readonly string[]): Counts { - const result: Record = {}; - for (const value of values) { - result[value] = (result[value] ?? 0) + 1; - } - return Object.fromEntries( - Object.entries(result).sort(([left], [right]) => - left.localeCompare(right) - ) - ); -} - -function summarize( - unitRows: readonly Unit[], - ledgerRows: readonly LedgerEntry[], - currentFrontier: Frontier, - gateRows: readonly Gate[] -): StatusSummary { - return { - unitStates: countValues(unitRows.map((unit) => unit.state)), - ledgerVerdicts: countValues(ledgerRows.map((row) => row.verdict)), - frontierGeneration: currentFrontier.generation, - openGateIds: gateRows - .filter((gate): gate is OpenGate => gate.kind === "open") - .map((gate) => gate.id) - .sort(), - }; -} - -function countRecord(value: unknown): Record | null { - if (!isRecord(value)) { - return null; - } - const result: Record = {}; - for (const [name, count] of Object.entries(value)) { - if ( - typeof count !== "number" || - !Number.isSafeInteger(count) || - count < 0 - ) { - return null; - } - result[name] = count; - } - return result; -} - -function previousSummary(raw: string): StatusSummary | null { - const match = //.exec(raw); - if (match === null) { - return null; - } - let value: unknown; - try { - value = JSON.parse(match[1] ?? ""); - } catch { - return null; - } - if ( - !isRecord(value) || - typeof value.frontierGeneration !== "number" || - !isUnknownArray(value.openGateIds) - ) { - return null; - } - const unitStates = countRecord(value.unitStates); - const ledgerVerdicts = countRecord(value.ledgerVerdicts); - const openGateIds = value.openGateIds.filter( - (item): item is string => typeof item === "string" - ); - if ( - unitStates === null || - ledgerVerdicts === null || - openGateIds.length !== value.openGateIds.length - ) { - return null; - } - return { - unitStates, - ledgerVerdicts, - frontierGeneration: value.frontierGeneration, - openGateIds, - }; -} - -function changed(before: StatusSummary | null, after: StatusSummary): string { - if (before === null) { - return "first render"; - } - const result: string[] = []; - const groups: readonly { - readonly label: string; - readonly oldCounts: Counts; - readonly newCounts: Counts; - }[] = [ - { - label: "units", - oldCounts: before.unitStates, - newCounts: after.unitStates, - }, - { - label: "ledger", - oldCounts: before.ledgerVerdicts, - newCounts: after.ledgerVerdicts, - }, - ]; - for (const { label, oldCounts, newCounts } of groups) { - const names = [ - ...new Set([...Object.keys(oldCounts), ...Object.keys(newCounts)]), - ].sort(); - for (const name of names) { - const oldCount = oldCounts[name] ?? 0; - const newCount = newCounts[name] ?? 0; - if (oldCount !== newCount) { - result.push(`${label} ${name} ${oldCount}->${newCount}`); - } - } - } - if (before.frontierGeneration !== after.frontierGeneration) { - result.push( - `frontier generation ${before.frontierGeneration}->${after.frontierGeneration}` - ); - } - if (before.openGateIds.join("\0") !== after.openGateIds.join("\0")) { - result.push( - `open gates ${before.openGateIds.length}->${after.openGateIds.length}` - ); - } - return result.length === 0 ? "no derived changes" : result.join("; "); -} - -function markdown(value: string): string { - return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|"); -} - -function table( - headers: readonly string[], - rows: readonly (readonly string[])[] -): string { - if (rows.length === 0) { - return "(none)"; - } - return [ - `| ${headers.join(" | ")} |`, - `| ${headers.map(() => "---").join(" | ")} |`, - ...rows.map((row) => `| ${row.map(markdown).join(" | ")} |`), - ].join("\n"); -} - -function statusMarkdown( - unitRows: readonly Unit[], - ledgerRows: readonly LedgerEntry[], - currentFrontier: Frontier, - gateRows: readonly Gate[], - currentSummary: StatusSummary -): string { - return `# Orchestrate status - -Generated: ${new Date().toISOString()} - -## Units - -States: ${countLine(currentSummary.unitStates)} - -${table( - ["ID", "Track", "State", "Branch", "PR", "SHA", "Brief"], - unitRows.map(unitCells) -)} - -## Verification ledger - -Verdicts: ${countLine(currentSummary.ledgerVerdicts)} - -${table( - ["PR", "SHA", "Verdict", "Evidence", "Verifier", "Timestamp"], - ledgerRows.map(ledgerCells) -)} - -## Frontier - -Generation: ${currentFrontier.generation} -Lowest unmerged: ${currentFrontier.lowestUnmerged ?? "none"} - -${table( - ["Branch", "PR", "SHA", "State"], - currentFrontier.prs.map((row) => [ - row.branches, - String(row.pr), - row.sha, - row.state, - ]) -)} - -## Gates - -${table( - ["ID", "Status", "Question", "Options", "Default", "Answer"], - gateRows.map((gate) => [ - gate.id, - gate.kind, - gate.question, - gate.options, - gate.defaultAnswer, - gate.kind === "resolved" ? gate.answer : "", - ]) -)} - - -`; -} - -function countLine(value: Counts): string { - const entries = Object.entries(value); - return entries.length === 0 - ? "none" - : entries.map(([name, count]) => `${name}=${count}`).join(", "); -} - -export type Forge = "gh" | "origin"; - -interface ForgePullRequestQuery { - readonly stateField: string; - readonly headField: string; - readonly baseField: string; - readonly states: ReadonlyMap; -} - -const FORGE_PULL_REQUEST_QUERIES: Readonly< - Record -> = { - gh: { - stateField: "state", - headField: "headRefName", - baseField: "baseRefName", - states: new Map([ - ["OPEN", "OPEN"], - ["MERGED", "MERGED"], - ["CLOSED", "CLOSED"], - ]), - }, - origin: { - stateField: "status", - headField: "headRef", - baseField: "baseRef", - states: new Map([ - ["open", "OPEN"], - ["draft", "OPEN"], - ["merged", "MERGED"], - ["closed", "CLOSED"], - ]), - }, -}; - -export function parseForge(value: string): Forge { - if (value === "gh" || value === "origin") { - return value; - } - throw new UserError("forge must be gh or origin"); -} - -interface ForgePullRequest { - readonly pr: number; - readonly state: FrontierPrState; - readonly branch: string; - readonly base: string; -} - -function runTool({ - command, - args, - repo, -}: { - command: string; - args: readonly string[]; - repo: string; -}): string { - try { - return execFileSync(command, args, { - cwd: repo, - encoding: "utf8", - env: process.env, - stdio: ["ignore", "pipe", "pipe"], - }); - } catch (error) { - throw new UserError( - `${command} ${args.join(" ")} failed: ${errorMessage(error)}` - ); - } -} - -function listPullRequests({ - filter, - branch, - forge, - repo, -}: { - filter: "--head" | "--base"; - branch: string; - forge: Forge; - repo: string; -}): readonly ForgePullRequest[] { - const query = FORGE_PULL_REQUEST_QUERIES[forge]; - const command = `${forge} pr list ${filter} ${branch}`; - // Every state is fetched because a merged or closed PR still anchors the - // chain, and a draft counts as OPEN. - const raw = runTool({ - command: forge, - args: [ - "pr", - "list", - filter, - branch, - "--state", - "all", - "--json", - ["number", query.stateField, query.headField, query.baseField].join( - "," - ), - ], - repo, - }); - let value: unknown; - try { - value = JSON.parse(raw); - } catch { - throw new UserError(`${command} output is not valid JSON`); - } - if (!isUnknownArray(value)) { - throw new UserError(`${command} output is not a JSON array`); - } - return value.map((row) => { - const status = isRecord(row) ? row[query.stateField] : undefined; - const state = - typeof status === "string" ? (query.states.get(status) ?? null) : null; - const head = isRecord(row) ? row[query.headField] : undefined; - const base = isRecord(row) ? row[query.baseField] : undefined; - if ( - !isRecord(row) || - typeof row.number !== "number" || - !Number.isSafeInteger(row.number) || - row.number < 1 || - state === null || - typeof head !== "string" || - head.length === 0 || - typeof base !== "string" || - base.length === 0 - ) { - throw new UserError(`${command} output has an invalid PR row`); - } - return { pr: row.number, state, branch: head, base }; - }); -} - -function branchPullRequest({ - branch, - forge, - repo, -}: { - branch: string; - forge: Forge; - repo: string; -}): ForgePullRequest { - const rows = listPullRequests({ filter: "--head", branch, forge, repo }); - // A branch may have closed or merged historical PRs after stack surgery. - // Prefer its sole active PR, but retain historical anchors when none is open. - const active = rows.filter((row) => row.state === "OPEN"); - const candidates = active.length > 0 ? active : rows; - const row = candidates[0]; - if (row === undefined) { - throw new UserError( - `branch ${branch} has no pull request; open one with ${forge} pr create, or resolve the frontier from a branch whose chain is fully submitted` - ); - } - if (candidates.length > 1) { - throw new UserError( - `branch ${branch} has multiple pull requests (${candidates.map((item) => `#${item.pr}`).join(", ")}); close the stale ones first` - ); - } - return row; -} - -function openChildren({ - branch, - forge, - repo, -}: { - branch: string; - forge: Forge; - repo: string; -}): readonly ForgePullRequest[] { - return listPullRequests({ filter: "--base", branch, forge, repo }).filter( - (row) => row.state === "OPEN" - ); -} - -function runGit({ - args, - repo, -}: { - args: readonly string[]; - repo: string; -}): string { - return runTool({ command: "git", args, repo }).trim(); -} - -function branchSha({ - branch, - repo, -}: { - branch: string; - repo: string; -}): string { - const sha = runGit({ args: ["rev-parse", branch], repo }); - if (!/^[0-9a-f]{40,64}$/i.test(sha)) { - throw new UserError(`git rev-parse ${branch} returned an invalid SHA`); - } - return sha; -} - -function currentBranch(repo: string): string { - return runGit({ args: ["symbolic-ref", "--short", "HEAD"], repo }); -} - -function trunkBranch(repo: string): string { - try { - return runGit({ - args: ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], - repo, - }).replace(/^origin\//, ""); - } catch { - // Init-plus-fetch checkouts and local fixtures lack origin/HEAD. - return "main"; - } -} - -function resolveFrontier(repo: string, forge: Forge): readonly FrontierPr[] { - const trunk = trunkBranch(repo); - const current = currentBranch(repo); - if (current === trunk) { - return []; - } - const seen = new Set([current]); - let bottom = branchPullRequest({ branch: current, forge, repo }); - const chain = [bottom]; - while (bottom.base !== trunk) { - if (seen.has(bottom.base)) { - throw new UserError( - `pull request base chain loops at branch ${bottom.base}` - ); - } - seen.add(bottom.base); - bottom = branchPullRequest({ branch: bottom.base, forge, repo }); - chain.unshift(bottom); - } - let tip = current; - for (;;) { - const children = openChildren({ branch: tip, forge, repo }); - const child = children[0]; - if (child === undefined) { - break; - } - if (children.length > 1) { - throw new UserError( - `stack is not linear: branches ${children.map((row) => row.branch).join(", ")} all target ${tip}` - ); - } - if (seen.has(child.branch)) { - throw new UserError( - `pull request base chain loops at branch ${child.branch}` - ); - } - seen.add(child.branch); - chain.push(child); - tip = child.branch; - } - if (new Set(chain.map((row) => row.pr)).size !== chain.length) { - throw new UserError( - "pull request base chain contains duplicate pull requests" - ); - } - return chain.map((row) => ({ - pr: row.pr, - branches: row.branch, - sha: branchSha({ branch: row.branch, repo }), - state: row.state, - })); -} - -function validateFrontierPin({ - actual, - expected, -}: { - actual: readonly number[]; - expected: readonly number[]; -}): void { - if ( - actual.length === expected.length && - actual.every((pr, index) => pr === expected[index]) - ) { - return; - } - const actualSet = new Set(actual); - const expectedSet = new Set(expected); - const missing = expected.filter((pr) => !actualSet.has(pr)); - const extra = actual.filter((pr) => !expectedSet.has(pr)); - const drift: string[] = []; - if (missing.length > 0) { - drift.push(`missing from resolved chain: ${missing.join(",")}`); - } - if (extra.length > 0) { - drift.push(`extra in resolved chain: ${extra.join(",")}`); - } - if (missing.length === 0 && extra.length === 0) { - drift.push( - `order differs: expected ${expected.join(",")}; resolved ${actual.join(",")}` - ); - } - throw new UserError(`frontier pin mismatch: ${drift.join("; ")}`); -} - -export function openStore( - directory: string, - options: OpenStoreOptions = {} -): Store { - const store = resolve(directory); - let closed = false; - let releaseLock: (() => Promise) | null = null; - let lockRequest: Promise | null = null; - - const ensureOpen = (): void => { - if (closed) { - throw new UserError("store is closed"); - } - }; - - const ensureLock = async (): Promise => { - ensureOpen(); - if (releaseLock !== null) { - return; - } - if (lockRequest === null) { - lockRequest = acquireLock(store, options).then((release) => { - releaseLock = release; - }); - } - try { - await lockRequest; - } catch (error) { - lockRequest = null; - throw error; - } - }; - - const beginWrite = async (): Promise => { - ensureOpen(); - if (!(await exists(store))) { - throw new UserError( - `store is not initialized at ${store}; run orch init` - ); - } - await ensureLock(); - }; - - return { - units: { - add: async (params) => { - await beginWrite(); - const row: Unit = { - id: requiredCell(params.id, "unit id"), - track: requiredCell(params.track, "track"), - state: "pending", - branch: "", - pr: "", - sha: "", - brief: - params.brief === undefined - ? "" - : requiredCell(params.brief, "brief"), - }; - const rows = [...(await readUnits(store))]; - if (rows.some((unit) => unit.id === row.id)) { - throw new UserError(`unit ${row.id} already exists`); - } - rows.push(row); - await saveUnits(store, rows); - return row; - }, - set: async (params) => { - await beginWrite(); - const id = requiredCell(params.id, "unit id"); - const state = requiredCell(params.state, "state"); - const rows = [...(await readUnits(store))]; - const index = rows.findIndex((unit) => unit.id === id); - const old = rows[index]; - if (index < 0 || old === undefined) { - throw new NotFoundError(`unit ${id} not found`); - } - const row: Unit = { - ...old, - state, - branch: - params.branch === undefined - ? old.branch - : requiredCell(params.branch, "branch"), - pr: - params.pr === undefined - ? old.pr - : String(positiveInteger(params.pr, "PR")), - sha: - params.sha === undefined - ? old.sha - : requiredCell(params.sha, "SHA"), - }; - rows[index] = row; - await saveUnits(store, rows); - return row; - }, - get: async (id) => { - ensureOpen(); - const cleanId = requiredCell(id, "unit id"); - const row = (await readUnits(store)).find( - (unit) => unit.id === cleanId - ); - if (row === undefined) { - throw new NotFoundError(`unit ${cleanId} not found`); - } - return row; - }, - list: async (params = {}) => { - ensureOpen(); - const state = - params.state === undefined - ? undefined - : requiredCell(params.state, "state"); - const track = - params.track === undefined - ? undefined - : requiredCell(params.track, "track"); - return (await readUnits(store)).filter( - (unit) => - (state === undefined || unit.state === state) && - (track === undefined || unit.track === track) - ); - }, - counts: async () => { - ensureOpen(); - return countValues( - (await readUnits(store)).map((unit) => unit.state) - ); - }, - }, - ledger: { - record: async (params) => { - await beginWrite(); - const verdict = parseVerdict(params.verdict); - const row: LedgerEntry = { - pr: String(positiveInteger(params.pr, "PR")), - sha: requiredCell(params.sha, "SHA"), - verdict, - evidence: requiredCell(params.evidence, "evidence"), - verifier: - params.verifier === undefined - ? "" - : requiredCell(params.verifier, "verifier"), - ts: new Date().toISOString(), - }; - const rows = [...(await readLedger(store))]; - const index = rows.findIndex( - (old) => old.pr === row.pr && old.sha === row.sha - ); - if (index < 0) { - rows.push(row); - } else { - rows[index] = row; - } - await saveLedger(store, rows); - return row; - }, - check: async (params) => { - ensureOpen(); - const pr = String(positiveInteger(params.pr, "PR")); - const sha = requiredCell(params.sha, "SHA"); - const row = (await readLedger(store)).find( - (value) => value.pr === pr && value.sha === sha - ); - if (row === undefined) { - throw new NotFoundError("NOT-VERIFIED", { - compact: "NOT-VERIFIED", - json: { pr, sha, verdict: "NOT-VERIFIED" }, - }); - } - return row; - }, - summary: async () => { - ensureOpen(); - return countValues( - (await readLedger(store)).map((row) => row.verdict) - ); - }, - }, - inbox: { - push: async (params) => { - await beginWrite(); - const pointer: InboxPointer = { - ts: new Date().toISOString(), - agent: requiredCell(params.agent, "agent"), - unit: requiredCell(params.unit, "unit"), - status: requiredCell(params.status, "status"), - report: - params.report === undefined - ? "" - : requiredCell(params.report, "report"), - }; - const inbox = join(store, "inbox"); - if (!(await exists(inbox))) { - throw new UserError( - `store is not initialized at ${store}; run orch init` - ); - } - const timestamp = pointer.ts.replace(/[:.]/g, "-"); - const filename = `${timestamp}-${process.pid}-${randomUUID()}.tsv`; - const contents = `${pointerCells(pointer).map(cleanCell).join("\t")}\n`; - await atomicWrite(join(inbox, filename), contents); - return { pointer, filename }; - }, - drain: async () => { - await beginWrite(); - const inbox = join(store, "inbox"); - const rows = await readPointers(inbox); - const drained = join( - store, - `.inbox-drain-${process.pid}-${randomUUID()}` - ); - await rename(inbox, drained); - try { - await mkdir(inbox); - } catch (error) { - await rename(drained, inbox); - throw error; - } - await rm(drained, { recursive: true, force: true }); - return rows; - }, - peek: async () => { - ensureOpen(); - return readPointers(join(store, "inbox")); - }, - count: async () => { - ensureOpen(); - return (await readPointers(join(store, "inbox"))).length; - }, - }, - gates: { - park: async (params) => { - await beginWrite(); - const gate: OpenGate = { - kind: "open", - id: requiredLine(params.id, "gate id"), - question: requiredLine(params.question, "question"), - options: requiredLine(params.options, "options"), - defaultAnswer: requiredLine( - params.defaultAnswer, - "default" - ), - }; - const rows = [...(await readGates(store))]; - const index = rows.findIndex((old) => old.id === gate.id); - if (index < 0) { - rows.push(gate); - } else { - rows[index] = gate; - } - await atomicWrite(join(store, "gates.md"), renderGates(rows)); - return gate; - }, - list: async () => { - ensureOpen(); - return (await readGates(store)).filter( - (gate): gate is OpenGate => gate.kind === "open" - ); - }, - resolve: async (params) => { - await beginWrite(); - const id = requiredLine(params.id, "gate id"); - const rows = [...(await readGates(store))]; - const index = rows.findIndex((gate) => gate.id === id); - const old = rows[index]; - if (index < 0 || old === undefined) { - throw new NotFoundError(`gate ${id} not found`); - } - const gate: ResolvedGate = { - kind: "resolved", - id: old.id, - question: old.question, - options: old.options, - defaultAnswer: old.defaultAnswer, - answer: requiredLine(params.answer, "answer"), - }; - rows[index] = gate; - await atomicWrite(join(store, "gates.md"), renderGates(rows)); - return gate; - }, - }, - frontier: { - set: async (params) => { - await beginWrite(); - const repo = resolve(requiredLine(params.repo, "repo directory")); - const forge = parseForge(params.forge ?? "gh"); - const pin = - params.prs === undefined - ? undefined - : params.prs.map((pr) => positiveInteger(pr, "PR")); - if (pin !== undefined && new Set(pin).size !== pin.length) { - throw new UserError("--prs must not contain duplicates"); - } - const old = await readFrontier(store); - const prs = resolveFrontier(repo, forge); - if (pin !== undefined) { - validateFrontierPin({ - actual: prs.map((row) => row.pr), - expected: pin, - }); - } - const value: Frontier = { - generation: old.generation + 1, - prs, - lowestUnmerged: prs.find((row) => row.state === "OPEN")?.pr ?? null, - }; - await atomicWrite( - join(store, "frontier.json"), - `${JSON.stringify(value, null, 2)}\n` - ); - return value; - }, - show: async () => { - ensureOpen(); - return readFrontier(store); - }, - }, - standing: { - show: async () => { - ensureOpen(); - return readStanding(store); - }, - add: async (params) => { - await beginWrite(); - const rows = [...(await readStanding(store))]; - const item: StandingLine = { - number: rows.length + 1, - line: requiredLine(params.line, "standing order"), - }; - rows.push(item); - await atomicWrite( - join(store, "preferences.md"), - `${rows.map((row) => `${row.number}. ${row.line}`).join("\n")}\n` - ); - return item; - }, - }, - status: { - render: async () => { - await beginWrite(); - const unitRows = await readUnits(store); - const ledgerRows = await readLedger(store); - const currentFrontier = await readFrontier(store); - const gateRows = await readGates(store); - const currentSummary = summarize( - unitRows, - ledgerRows, - currentFrontier, - gateRows - ); - const path = join(store, "status.md"); - const before = (await exists(path)) - ? previousSummary(await readFile(path, "utf8")) - : null; - const change = changed(before, currentSummary); - await atomicWrite( - path, - statusMarkdown( - unitRows, - ledgerRows, - currentFrontier, - gateRows, - currentSummary - ) - ); - return { - units: unitRows, - ledger: ledgerRows, - frontier: currentFrontier, - gates: gateRows, - summary: currentSummary, - changed: change, - }; - }, - }, - init: async () => { - ensureOpen(); - await mkdir(store, { recursive: true }); - await ensureLock(); - await writeIfMissing(join(store, "units.tsv"), `${UNIT_HEADER}\n`); - await writeIfMissing(join(store, "ledger.tsv"), `${LEDGER_HEADER}\n`); - await mkdir(join(store, "inbox"), { recursive: true }); - await writeIfMissing(join(store, "gates.md"), ""); - await writeIfMissing(join(store, "preferences.md"), ""); - await writeIfMissing(join(store, "frontier.json"), "{}\n"); - return { store }; - }, - close: async () => { - if (closed) { - return; - } - if (lockRequest !== null) { - try { - await lockRequest; - } catch { - // A failed acquisition has no lock to release. - } - } - const release = releaseLock; - releaseLock = null; - closed = true; - if (release !== null) { - await release(); - } - }, - }; -} diff --git a/packages/pstack/upstream/skills/poteto-mode/scripts/package.json b/packages/pstack/upstream/skills/poteto-mode/scripts/package.json index 4e73389d7529c..dea1104e9e071 100644 --- a/packages/pstack/upstream/skills/poteto-mode/scripts/package.json +++ b/packages/pstack/upstream/skills/poteto-mode/scripts/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "test": "bun test orch watch-pr check-plan.test.ts", + "test": "bun test watch-pr check-plan.test.ts", "typecheck": "tsc --project watch-pr/tsconfig.json --noEmit --strict" }, "dependencies": { diff --git a/packages/pstack/upstream/skills/principle-explain-the-number/SKILL.md b/packages/pstack/upstream/skills/principle-explain-the-number/SKILL.md index 1e180c14aefe4..3948ad3409243 100644 --- a/packages/pstack/upstream/skills/principle-explain-the-number/SKILL.md +++ b/packages/pstack/upstream/skills/principle-explain-the-number/SKILL.md @@ -1,17 +1,18 @@ --- name: principle-explain-the-number -description: "Apply before you trust, report, or act on a number you measured: a speedup, a regression, a throughput, a latency, or an eval result. Find what limits it, and rule out that it measured something other than the work you think." +description: "Apply before you measure, and before you trust, report, or act on a number you measured: a speedup, a regression, a throughput, a latency, or an eval result. Find what limits it, and rule out that it measured something other than the work you think." disable-model-invocation: true --- # Explain the Number +- **Write the decision before you run.** Name the result that would change a decision, and the noise floor. Stop when the run-to-run spread is larger than the effect. - **Ask "why not double?"** Name the resource or code path that bounds the result, such as a core, a lock, the disk, the network, or the load generator itself. Get it from a profile or from system counters taken during a run, then map it to source. A guess from reading the code is not a limiter. If you cannot say why the number is not twice as good, you do not know what you measured. - **List what else the number could be measuring, and rule out each one with evidence.** The usual suspects are errors, skipped or cached work, code that never ran, an untuned side (one left on default settings), noise, and a piece too small to matter end to end. - **Keep the evidence with the number.** Put the run count, the spread, and the limiter in the notes or a linked artifact. For a performance number, run the full procedure with the [benchmark-checklist](../benchmark-checklist/SKILL.md) skill. For an eval result, ask the same of the trials: did every run do the task, does the gap hold across trials and models, and does the scenario matter. -You skipped this when the evidence behind a number has no run count, no spread, or no named limiter, or when the time saved is larger than the time the changed piece took. +You skipped this when a run started without the result that would change a decision, when the evidence behind a number has no run count, no spread, or no named limiter, or when the time saved is larger than the time the changed piece took. Distinct from [Prove It Works](../principle-prove-it-works/SKILL.md), which checks that an output is real. diff --git a/packages/pstack/upstream/skills/recall/SKILL.md b/packages/pstack/upstream/skills/recall/SKILL.md index 138b27763e386..3b4a5e773773f 100644 --- a/packages/pstack/upstream/skills/recall/SKILL.md +++ b/packages/pstack/upstream/skills/recall/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Recall -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. **Before you start or resume work, you rebuild the user's recent working context and hand back a tight capsule of where things stand now and what to do next.** diff --git a/packages/pstack/upstream/skills/reflect/SKILL.md b/packages/pstack/upstream/skills/reflect/SKILL.md index 5ebbb23bc2fd1..fa8dbdc24b116 100644 --- a/packages/pstack/upstream/skills/reflect/SKILL.md +++ b/packages/pstack/upstream/skills/reflect/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Reflect -Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` once before the first delegation. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. +Use `delegate_task` for every spawn. Arena runners, architect runners, and interrogate reviewers are panels: one child per seat, including fallbacks. An `unavailable` seat is not a target: retain its panel position, refresh capabilities, report the blocked seat, and do not launch it until a valid target is resolved. Every other role is a pool: pick the seat whose model best fits the job; spread same-role children in a wave across seats. Arena cross-judges prefer a different model family. Copy the chosen target from `` exactly, including all options; `inherit` means the parent model and omits `target`. If a pool spawn is rejected, report it and try the next seat; never invent targets or change options. Repeat fallback notes and report provider, model, or option mismatches. If a required role is missing, stop that spawn. Call `orchestrator_capabilities` before the first delegation and again when availability changes or a target is rejected. Resolved roles are a snapshot. An explicit user change supersedes it; validate the requested target and options against fresh capabilities. After setup writes new choices, do not reuse the older snapshot for changed roles. Start each brief with the absolute `` and `` paths; include the relevant resolved role lines when children will delegate. Retain every `taskId`. With `mode: "async"`, spawn the wave and end the turn; completion wakes the parent. Do not poll children. Use `mode: "wait"` only when this turn needs the result; a wait timeout does not cancel the child. Cancel replaced or unneeded work with `task_cancel`. Mine the current conversation for durable learnings, then route them into skill edits. diff --git a/packages/pstack/upstream/skills/setup-pstack/SKILL.md b/packages/pstack/upstream/skills/setup-pstack/SKILL.md index 7793e2d5babc5..6943e42887835 100644 --- a/packages/pstack/upstream/skills/setup-pstack/SKILL.md +++ b/packages/pstack/upstream/skills/setup-pstack/SKILL.md @@ -87,7 +87,7 @@ Overwrite `` with no frontmatter: comment lines, `# budget: