@hyperdreamer/pi-webui 1.10.7 → 1.11.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +17 -2
  2. package/dist/cli.js +265 -32
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/{CodeViewer-BIb2Wx8W.js → CodeViewer-CAf_j27u.js} +1 -1
  5. package/dist/client/assets/{UnifiedDiffViewer-DpWiGlQo.js → UnifiedDiffViewer-wBjEJWa1.js} +1 -1
  6. package/dist/client/assets/{index-DX583DEv.js → index-5EtDb9nj.js} +1197 -476
  7. package/dist/client/index.html +1 -1
  8. package/dist/config.js +52 -11
  9. package/dist/config.js.map +1 -1
  10. package/dist/pi-webui-plugins/workspace-memory/pi-webui-plugin.js +3 -2
  11. package/dist/pi-webui-plugins/workspace-tasks/pi-webui-plugin.js +1 -0
  12. package/dist/plugin-api.d.ts +30 -0
  13. package/dist/server/app.js +6 -1
  14. package/dist/server/app.js.map +1 -1
  15. package/dist/server/realtime/sessionEventHub.js +40 -5
  16. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  17. package/dist/server/realtime/sessionStatusCoalescer.js +137 -0
  18. package/dist/server/realtime/sessionStatusCoalescer.js.map +1 -0
  19. package/dist/server/sessiond/sessionProxyRoutes.js +2 -0
  20. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  21. package/dist/server/sessiond.js +57 -5
  22. package/dist/server/sessiond.js.map +1 -1
  23. package/dist/server/sessions/modelPolicyCapability.js +88 -0
  24. package/dist/server/sessions/modelPolicyCapability.js.map +1 -0
  25. package/dist/server/sessions/modelPolicyTool.js +31 -0
  26. package/dist/server/sessions/modelPolicyTool.js.map +1 -0
  27. package/dist/server/sessions/modelTierRegistry.js +14 -0
  28. package/dist/server/sessions/modelTierRegistry.js.map +1 -1
  29. package/dist/server/sessions/modelTierSettingsRoutes.js +36 -0
  30. package/dist/server/sessions/modelTierSettingsRoutes.js.map +1 -0
  31. package/dist/server/sessions/modelTierSettingsService.js +100 -0
  32. package/dist/server/sessions/modelTierSettingsService.js.map +1 -0
  33. package/dist/server/sessions/piSessionService.js +1643 -433
  34. package/dist/server/sessions/piSessionService.js.map +1 -1
  35. package/dist/server/sessions/sessionDefaultsRoutes.js +39 -0
  36. package/dist/server/sessions/sessionDefaultsRoutes.js.map +1 -1
  37. package/dist/server/sessions/sessionDefaultsService.js +36 -6
  38. package/dist/server/sessions/sessionDefaultsService.js.map +1 -1
  39. package/dist/server/sessions/sessionMetadataStore.js +107 -13
  40. package/dist/server/sessions/sessionMetadataStore.js.map +1 -1
  41. package/dist/server/sessions/sessionModelPolicy.js +118 -0
  42. package/dist/server/sessions/sessionModelPolicy.js.map +1 -0
  43. package/dist/server/sessions/sessionReorder.js +117 -0
  44. package/dist/server/sessions/sessionReorder.js.map +1 -0
  45. package/dist/server/sessions/sessionRouteFastifyOptions.js +8 -0
  46. package/dist/server/sessions/sessionRouteFastifyOptions.js.map +1 -0
  47. package/dist/server/sessions/sessionRoutes.js +208 -3
  48. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  49. package/dist/server/sessions/starterModelPolicyPreferenceStore.js +164 -0
  50. package/dist/server/sessions/starterModelPolicyPreferenceStore.js.map +1 -0
  51. package/dist/server/sessions/utilityModelExtension.js +199 -0
  52. package/dist/server/sessions/utilityModelExtension.js.map +1 -0
  53. package/dist/server/sessions/utilityModelResolver.js +77 -0
  54. package/dist/server/sessions/utilityModelResolver.js.map +1 -0
  55. package/dist/server/sessions/utilityModelSettingsRoutes.js +62 -0
  56. package/dist/server/sessions/utilityModelSettingsRoutes.js.map +1 -0
  57. package/dist/server/sessions/utilityModelSettingsService.js +101 -0
  58. package/dist/server/sessions/utilityModelSettingsService.js.map +1 -0
  59. package/dist/server/skills/optionalSkillInstall.js +69 -0
  60. package/dist/server/skills/optionalSkillInstall.js.map +1 -0
  61. package/dist/server/skills/optionalSkillInstaller.js +148 -0
  62. package/dist/server/skills/optionalSkillInstaller.js.map +1 -0
  63. package/dist/shared/apiTypes.d.ts +129 -3
  64. package/dist/shared/apiTypes.js +10 -0
  65. package/dist/shared/apiTypes.js.map +1 -1
  66. package/dist/shared/capabilities.js +15 -0
  67. package/dist/shared/capabilities.js.map +1 -1
  68. package/dist/shared/federatedRoutes.js +7 -0
  69. package/dist/shared/federatedRoutes.js.map +1 -1
  70. package/docs/assets/pi-webui-desktop.png +0 -0
  71. package/docs/config.md +61 -3
  72. package/docs/plugins.md +93 -16
  73. package/optional-skills/deterministic-subagent-driven-development/SKILL.md +224 -0
  74. package/optional-skills/deterministic-subagent-driven-development/pi-webui-skill.json +28 -0
  75. package/optional-skills/deterministic-subagent-driven-development/prompts/final-reviewer.md +132 -0
  76. package/optional-skills/deterministic-subagent-driven-development/prompts/implementer.md +101 -0
  77. package/optional-skills/deterministic-subagent-driven-development/prompts/re-reviewer.md +60 -0
  78. package/optional-skills/deterministic-subagent-driven-development/prompts/task-reviewer.md +80 -0
  79. package/optional-skills/deterministic-subagent-driven-development/references/capability-contract.md +174 -0
  80. package/optional-skills/deterministic-subagent-driven-development/references/plan-contract.md +268 -0
  81. package/optional-skills/deterministic-subagent-driven-development/references/state-machine.md +177 -0
  82. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/manifest.mjs +258 -0
  83. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/plan-policy.mjs +350 -0
  84. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/prompt-renderer.mjs +290 -0
  85. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/state-machine.mjs +1264 -0
  86. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/state-store.mjs +532 -0
  87. package/optional-skills/deterministic-subagent-driven-development/scripts/sdd-state +3 -0
  88. package/optional-skills/deterministic-subagent-driven-development/scripts/sdd-state.mjs +349 -0
  89. package/optional-skills/deterministic-writing-plans/SKILL.md +232 -0
  90. package/optional-skills/deterministic-writing-plans/references/grammar.md +84 -0
  91. package/optional-skills/deterministic-writing-plans/templates/plan-skeleton.md +143 -0
  92. package/package.json +13 -9
package/docs/plugins.md CHANGED
@@ -6,6 +6,7 @@ Plugins can currently:
6
6
 
7
7
  - add action-palette commands;
8
8
  - add workspace tools/panels next to Files, Git, and Terminal;
9
+ - add Activity Rail activities opened from dedicated Rail controls;
9
10
  - add compact workspace-label items in the workspace list, panel header, and status bar;
10
11
  - call browser APIs and documented PI WEBUI plugin context helpers;
11
12
  - read workspace files and start workspace terminal commands through documented helpers;
@@ -79,7 +80,7 @@ After editing, check the manifest endpoint and browser-console failure cases.
79
80
 
80
81
  ## Canonical example: bundled Info plugin
81
82
 
82
- PI WEBUI ships a real bundled `info` plugin. Use it as the reference example because it is intentionally small while still exercising all core contribution types: an action, a workspace label, and a workspace panel.
83
+ PI WEBUI ships a real bundled `info` plugin. Use it as the reference example because it is intentionally small while still exercising three UI contribution types: an action, a workspace label, and a workspace panel. Activity Rail activities are an additional v1 UI contribution type documented below.
83
84
 
84
85
  Bundled PI WEBUI plugins are developed as TypeScript in the repository, but their `package.json` metadata still points at built JavaScript because plugins are loaded by the browser as JS ES modules. `npm run dev:web` watches and rebuilds bundled plugin TS into `dist/pi-webui-plugins/` during development, and `npm run build` emits the JS before packaging a release.
85
86
 
@@ -147,7 +148,7 @@ Reload the PI WEBUI browser tab. PI WEBUI serves plugin modules with an mtime-ba
147
148
 
148
149
  When [machine federation](https://pi-webui.dev/machines) is enabled, PI WEBUI also loads discovered plugins from the selected remote machine. Remote plugins are trusted browser-side code like local plugins, but their contributions are machine-scoped:
149
150
 
150
- - actions, workspace panels, and workspace labels only appear while that machine is selected;
151
+ - actions, workspace panels, Activity Rail activities, and workspace labels only appear while that machine is selected;
151
152
  - plugin file and terminal helpers run against that machine;
152
153
  - plugin code is loaded best-effort through the current gateway and cached for the browser page lifetime;
153
154
  - if the gateway and remote machine both have an enabled plugin with the same original id, `machineSpecific` metadata decides whether the gateway copy is reused or only the selected machine's copy can appear;
@@ -204,11 +205,11 @@ Built-in plugins can be managed from **Settings → PI WEBUI plugins** or with t
204
205
  ### Memory
205
206
 
206
207
  **Plugin id:** `workspace-memory`
207
- **What it does:** adds a read-only **Memory** workspace tab. It reads `pi-hermes-memory`-compatible data through PI WEBUI and never writes, creates, edits, or deletes memories.
208
+ **What it does:** adds a read-only **Memory** Activity Rail activity. It is **Rail-only**: it does not contribute a workspace panel. It reads `pi-hermes-memory`-compatible data through PI WEBUI and never writes, creates, edits, or deletes memories.
208
209
 
209
- The tab shows independently collapsible **Global memory** and **Project-specific memory** groups. Project-specific memory contains entries only for the selected workspace project. Empty or missing memory data appears as a scoped empty state. An unavailable project-specific request displays a scoped **Project-specific unavailable** state while **Global memory** remains available. A failed global-memory request is presented as a panel-level retryable error.
210
+ Select the circular **Memory** Rail control to open a host-owned detail dialog. The dialog shows independently collapsible **Global memory** and **Project-specific memory** groups. Project-specific memory contains entries only for the selected workspace project. Empty or missing memory data appears as a scoped empty state. An unavailable project-specific request displays a scoped **Project-specific unavailable** state while **Global memory** remains available. A failed global-memory request is presented as a retryable error in the detail view.
210
211
 
211
- The Memory tab is visible only when a compatible memory provider is detected for the active agent profile and project scope. A compatible provider with zero entries still shows the tab but omits the badge. A positive badge totals global and project-specific memory entries. PI WEBUI refreshes the badge immediately after a selected project or workspace change and then checks approximately every 30 seconds. This is polling; PI WEBUI does not promise instant or realtime updates.
212
+ With a workspace selected, the Memory activity remains visible while memory loads, when data is available, and if a memory request fails. It is hidden only after the provider confirms that the whole memory capability is unavailable. A project-specific unavailable response is a separate scoped detail state; it does not mean the whole provider is unavailable. The count badge appears only for data with one or more entries, so it is omitted in all non-data states and for zero data; when shown, it totals global and project-specific memory entries. PI WEBUI refreshes the badge immediately after a selected project or workspace change and then checks approximately every 30 seconds. This is polling; PI WEBUI does not promise instant or realtime updates.
212
213
 
213
214
  Memory is bundled with PI WEBUI and enabled by default. To disable it, use **Settings → PI WEBUI plugins** or set:
214
215
 
@@ -393,11 +394,16 @@ interface PiWebUiPlugin {
393
394
  activate: (context: PluginActivationContext) => PluginActivationResult;
394
395
  }
395
396
 
397
+ interface PluginHostCapabilities {
398
+ activityRailItems?: true;
399
+ }
400
+
396
401
  interface PluginActivationContext {
397
402
  apiVersion: 1;
398
403
  pluginId: string;
399
404
  html: typeof import("lit").html;
400
405
  svg: typeof import("lit").svg;
406
+ capabilities?: PluginHostCapabilities;
401
407
  }
402
408
 
403
409
  interface PluginActivationResult {
@@ -439,6 +445,7 @@ For example, plugin `info` with action `workspace.show-path` becomes `info:works
439
445
  interface PluginContributions {
440
446
  actions?: PluginAction[];
441
447
  workspacePanels?: WorkspacePanelContribution[];
448
+ activityRailItems?: ActivityRailContribution[];
442
449
  workspaceLabels?: WorkspaceLabelContribution[];
443
450
  }
444
451
  ```
@@ -648,6 +655,74 @@ interface Workspace {
648
655
 
649
656
  Use existing classes such as `toolbar`, `viewer`, `empty`, and `muted` for panel content when possible. Do not assume a panel owns the whole page; keep layout contained.
650
657
 
658
+ ### Activity Rail activities
659
+
660
+ Activity Rail activities add a focused, dialog-backed surface. Each activity must supply an `id`, `title`, `icon`, and `render`. The host uses `title` for the icon control's accessible name and dialog heading, `icon` for the Rail control, and `render` for the dialog body. Prefer an SVG created by `svg` with `currentColor` so themes can style the required icon.
661
+
662
+ Activity Rail support is an additive v1 capability. A supporting host passes `capabilities.activityRailItems === true`; if the capability is missing, select the old-host branch:
663
+
664
+ ```js
665
+ activate: ({ capabilities, html, svg }) => ({
666
+ contributions: capabilities?.activityRailItems === true
667
+ ? {
668
+ activityRailItems: [{
669
+ id: "workspace.dashboard",
670
+ title: "Dashboard",
671
+ icon: svg`<svg viewBox="0 0 24 24"><path d="M4 4h16v16H4z"></path></svg>`,
672
+ render: () => html`<p>Dashboard</p>`,
673
+ }],
674
+ }
675
+ : {
676
+ workspacePanels: [{
677
+ id: "workspace.dashboard",
678
+ title: "Dashboard",
679
+ render: () => html`<p>Dashboard</p>`,
680
+ }],
681
+ },
682
+ });
683
+ ```
684
+
685
+ Activity type and context:
686
+
687
+ ```ts
688
+ interface ActivityRailContribution {
689
+ id: string;
690
+ title: string;
691
+ icon: TemplateResult;
692
+ order?: number;
693
+ visible?: (context: ActivityRailContext) => boolean;
694
+ badge?: (context: ActivityRailContext) => string | number | TemplateResult | undefined;
695
+ render: (context: ActivityRailContext) => TemplateResult;
696
+ }
697
+
698
+ interface ActivityRailHost {
699
+ requestRender(): void;
700
+ close(): void;
701
+ }
702
+
703
+ interface ActivityRailWorkspaceScope {
704
+ workspace: Workspace;
705
+ files: WorkspaceFiles;
706
+ terminal: WorkspacePanelTerminal;
707
+ }
708
+
709
+ interface ActivityRailContext extends PluginRuntimeContext {
710
+ machine: PluginMachine;
711
+ workspaceScope?: ActivityRailWorkspaceScope;
712
+ host: ActivityRailHost;
713
+ }
714
+ ```
715
+
716
+ The host renders visible activities as neutral, circular icon controls in a dedicated, host-owned section after its reorderable built-in controls and before Settings. Activities are not draggable and do not enter the user-reorderable core Rail order. `order` controls only the activity section: items sort by ascending `order` (default `1000`), then `title`, then id. Do not use `order` to place an activity among built-in controls.
717
+
718
+ `visible` and `badge` are synchronous, lightweight callbacks. `visible` defaults to shown; return `false` to hide an activity. `badge` runs only for visible activities and may return a string, number, `TemplateResult`, or `undefined`; `undefined` omits the badge. Do not return promises. Keep asynchronous work and cached state inside the plugin, then call `host.requestRender()` when that state changes so the host can re-evaluate `visible`, `badge`, and an open activity body.
719
+
720
+ `ActivityRailContext` extends the documented runtime context. `workspaceScope` is optional because an activity can be available without a selected workspace; check it before using its `workspace`, `files`, or `terminal` helpers. `host.close()` closes the currently open instance of that activity. Calls from stale or no-longer-open activity contexts safely do nothing.
721
+
722
+ The host owns the dialog frame, title, icon, close controls, Escape/backdrop dismissal, focus restoration, and error handling. An activity's `render()` returns only the dialog body. The host reports callback failures: a throwing `visible` callback hides the activity, a throwing `badge` callback omits the badge, and a throwing `render` callback shows a host-owned failure message.
723
+
724
+ Below 1181px, the persistent desktop Rail is replaced by a compact Activity Rail drawer that includes the same visible controls. Selecting an activity there still opens the host-owned dialog.
725
+
651
726
  ### Workspace labels
652
727
 
653
728
  Workspace labels add compact inline metadata wherever PI WEBUI displays a workspace label: workspace list, workspace panel header, and status bar.
@@ -955,7 +1030,7 @@ PI WEBUI does not provide a plugin cache/invalidation framework. Keep host callb
955
1030
 
956
1031
  - simple contributions should be synchronous and cheap;
957
1032
  - expensive or async work should live inside the plugin;
958
- - custom elements in `type: "render"` label items or panels are a good place to own async loading;
1033
+ - custom elements in `type: "render"` label items, workspace panels, or Activity Rail dialog bodies are a good place to own async loading;
959
1034
  - dedupe async reads/commands and avoid unbounded polling;
960
1035
  - clean up intervals/event listeners in custom elements' `disconnectedCallback()`.
961
1036
 
@@ -966,18 +1041,20 @@ If you are an AI agent building or editing a PI WEBUI plugin, follow this checkl
966
1041
  1. Create or update a plugin folder with `package.json` and a JavaScript module such as `pi-webui-plugin.js`.
967
1042
  2. Use the single supported package metadata shape: `piWebUi.plugins` array with `{ id, module, machineSpecific? }` entries.
968
1043
  3. Default-export `{ apiVersion: 1, name, activate }` from the module.
969
- 4. Return `{ contributions: { actions, workspacePanels, workspaceLabels } }` from `activate()`.
1044
+ 4. Return `{ contributions: { actions, workspacePanels, activityRailItems, workspaceLabels } }` from `activate()` as needed.
970
1045
  5. Use ids matching `^[a-z][a-z0-9.-]*$`.
971
1046
  6. Use the activation context's `html` function for Lit templates.
972
1047
  7. Keep `activate()` synchronous and cheap; return contribution definitions only.
973
- 8. Add actions for command-palette operations.
974
- 9. Add workspace panels for larger workspace UI.
975
- 10. Add workspace labels for compact inline metadata.
976
- 11. Return arrays from workspace label `items()`; return an empty array to render nothing.
977
- 12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebUiStatus`, and `prompt`.
978
- 13. Do not fetch PI WEBUI `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
979
- 14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
980
- 15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
1048
+ 8. Feature-detect `capabilities.activityRailItems === true` and retain a fallback contribution when supporting older v1 hosts.
1049
+ 9. Add actions for command-palette operations.
1050
+ 10. Add workspace panels for larger workspace UI.
1051
+ 11. Add Activity Rail activities for focused host-owned dialogs; require an icon and title, and check `workspaceScope` before using it.
1052
+ 12. Add workspace labels for compact inline metadata.
1053
+ 13. Return arrays from workspace label `items()`; return an empty array to render nothing.
1054
+ 14. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `host.close`, `workspaceScope`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebUiStatus`, and `prompt`.
1055
+ 15. Do not fetch PI WEBUI `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
1056
+ 16. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
1057
+ 17. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
981
1058
 
982
1059
  ## Troubleshooting
983
1060
 
@@ -1004,4 +1081,4 @@ Common issues:
1004
1081
  - entry module path points outside the plugin root or file does not exist;
1005
1082
  - browser cache not refreshed after editing;
1006
1083
  - plugin directory is not under `~/.pi-webui/plugins` or symlinked there;
1007
- - plugin throws during module import, `activate()`, `visible()`, `enabled()`, `items()`, or `render()`; check the browser console.
1084
+ - plugin throws during module import, `activate()`, `visible()`, `badge()`, `enabled()`, `items()`, or `render()`; check the browser console.
@@ -0,0 +1,224 @@
1
+ ---
2
+ name: deterministic-subagent-driven-development
3
+ description: Use when executing a written implementation plan whose tasks declare Implementer tiers and deterministic tracked-child model-policy controls are required
4
+ ---
5
+
6
+ # Deterministic Subagent-Driven Development
7
+
8
+ **Related workflows:** use `using-git-worktrees` before allocation. Child prompts
9
+ invoke `test-driven-development`, `requesting-code-review`, and
10
+ `finishing-a-development-branch` at their boundaries and are self-contained.
11
+
12
+ ## Capability and Validation Gates
13
+
14
+ Execute these eight gates in strict order. Before gate 7, read-only operations are
15
+ permitted. Workspace creation, Git mutation, deliverable editing, and dispatch are
16
+ forbidden.
17
+
18
+ **Read the governing reference before judging anything against it:**
19
+ `references/capability-contract.md` before gates 2–4, `references/plan-contract.md`
20
+ before gate 6, `references/state-machine.md` before reporting any state token. They
21
+ hold the exact field names, tokens, and thresholds.
22
+
23
+ 1. **Plan and worktree.** Confirm both are specified and accessible without
24
+ mutating either.
25
+
26
+ 2. **Policy contract.** Read `references/capability-contract.md`, then confirm
27
+ `get_model_policy` returns version 1 with active policy, current/next-request
28
+ tuples, ladder status, and tracked-dispatch capability. Reject other versions.
29
+
30
+ 3. **Spawn capability.** Confirm the policy result's `trackedDispatch.tierField`
31
+ is `true`. The runtime provides **no** dispatch key and **no** deduplication.
32
+ Missing idempotency evidence is not a capability failure.
33
+
34
+ 4. **Ladder completeness.** All six mappings must resolve in **both** modes:
35
+ children dispatch by tier regardless of parent mode; reviewer/fixer tiers
36
+ derive by formula. Exact mode reports `currentTier` as `null` and keeps the
37
+ runtime tuple.
38
+
39
+ 5. **Capability blocked.** If any check above fails: record `CAPABILITY_BLOCKED`,
40
+ name the cause and required capability, confirm zero dispatches. **Stop.**
41
+
42
+ 6. **Plan validation.** Read `references/plan-contract.md`, then run
43
+ `sdd-state validate-plan PLAN_FILE`. If the plan is rejected: record
44
+ `PLAN_INVALID`, quoting the validator's diagnostic. **Never guess a missing
45
+ tier**, and never accept an ambient current tier as a default for an absent
46
+ plan field. Stop.
47
+
48
+ With no shell to run the validator, say so, report the defect you found by
49
+ reading the plan, and still report `PLAN_INVALID`. An unavailable tool does not
50
+ change the state the run is in.
51
+
52
+ 7. **Workspace init.** Create the ignored run workspace. Run `sdd-state init`
53
+ against the inspected repo/worktree/branch/base-ref/merge-base identity, then
54
+ record `capability-confirmed` and `plan-valid`.
55
+
56
+ 8. **Preflight.** Run batched worktree and deliverable checks. On conflict, record
57
+ `PREFLIGHT_DECISION_REQUIRED` and persist the human ruling **before** any Git or
58
+ deliverable mutation.
59
+
60
+ ## Canonical Direction of Truth
61
+
62
+ `state.json` is canonical. `progress.md` is an append-only audit projection derived
63
+ from it.
64
+
65
+ **This is a convention you cannot reach by reasoning.** Both baseline conditions on
66
+ `post-compaction-illegal-transition` produced the correct state token and then
67
+ stated the opposite — "the audit ledger is canonical and state.json is a derived
68
+ cache" — and both invented repair mechanisms, one minting
69
+ `task4-rereview-replay-rev17`. Careful reasoning confidently chose wrong.
70
+
71
+ Never hand-edit either file. Every change goes through `sdd-state transition`, which
72
+ writes `state.json` first, then appends to `progress.md`. A missing final marker is
73
+ repairable; phantom markers from a reversed order are not.
74
+
75
+ ## State-Owned Orchestration Loop
76
+
77
+ Resolve all scripts, prompts, and references relative to this **explicitly loaded
78
+ `SKILL.md`**, never from the current directory or another same-name installation.
79
+
80
+ **Before each action:**
81
+
82
+ 1. Run `sdd-state show` and reload canonical state.
83
+ 2. If the audit marker is missing and no live lock is reported, run
84
+ `sdd-state repair-audit` at the current expected revision before proceeding.
85
+ 3. If state is `DISPATCH_AMBIGUOUS`, inspect for an observed child with
86
+ `list_subsessions`, then persist a ruling — adopt the observed session id **or**
87
+ reissue the stored bytes accepting a possible orphan. Never spawn again without a
88
+ ruling; a repeated spawn creates a **new child**, not a replay.
89
+
90
+ **Dispatch:**
91
+
92
+ Read `references/capability-contract.md` before any dispatch decision, including
93
+ recovery and mismatch decisions mid-run. It defines what the tool accepts, returns,
94
+ and does not guarantee. A recovered run that skips it reasons from memory.
95
+
96
+ 1. Produce the dispatch prompt with `sdd-state render-prompt`; never construct one
97
+ inline.
98
+ 2. Record the full intent — rendered prompt bytes, tier, cwd, and the
99
+ controller-owned `dispatchKey` from the state helper — in `state.json`
100
+ **before** calling `spawn_subsession`. If the phase you were given is already
101
+ `IMPLEMENT_DISPATCH_INTENT`, that intent exists: dispatch it, do not record a
102
+ second one.
103
+ 3. Call `spawn_subsession` with `{ prompt, cwd, tier }`. The tool returns
104
+ `{ sessionId, cwd }` only. **Never pass `dispatchKey` to the tool.**
105
+ 4. Immediately record the returned `sessionId` against the intent.
106
+ 5. Holding the returned `sessionId` means the phase is `IMPLEMENT_RUNNING`, even if
107
+ persisting it failed. Retry the write; do not relabel the phase.
108
+ `DISPATCH_AMBIGUOUS` is only for an intent whose `sessionId` you cannot recover
109
+ at all, entered through `dispatch-window-crossed`.
110
+
111
+ **Verify what you have a channel to verify.** A child's effective tier is checkable
112
+ with `read_subsession`, so check it; the run's recorded phase may have no channel
113
+ from where you stand. Where a channel exists and contradicts a claim, the channel
114
+ wins; where none exists, name the gap and never present a premise as confirmed.
115
+
116
+ **An unreachable store neither authorizes refusing to act nor changes the phase.**
117
+ If `state.json` or the helper is unreachable, take the action the given phase calls
118
+ for, then report the phase that action produced plus the persistence gap.
119
+ **Unwritable is not unknown.** Stalling to re-confirm a phase you already hold is a
120
+ different failure, not caution.
121
+
122
+ **Recovery:** reissue the exact bytes stored in the dispatch intent; **never
123
+ re-render on recovery.** Exact includes trailing whitespace and the final newline.
124
+ Copy the stored bytes, never retype or trim them, and never call a reissue verbatim
125
+ without comparing byte for byte: seven of fifteen recovery runs dropped the stored
126
+ final newline while claiming verbatim.
127
+
128
+ **Loop rules:**
129
+
130
+ - One SDD-owned active child at a time; never parallelize tasks.
131
+ - Yield at a join point; never poll status in a loop.
132
+ - Fresh children per role: implementer, fixer, task reviewer, re-reviewer, and
133
+ each final role.
134
+ - Write prompts, reports, and packages only under the ignored per-plan workspace.
135
+ - Pass bounded context by file path, never as pasted conversation history.
136
+ - Continue automatically between valid transitions. Pause only at
137
+ `CAPABILITY_BLOCKED`, `PLAN_INVALID`, `TASK_BLOCKED`, `DISPATCH_MISMATCH_BLOCKED`,
138
+ `PREFLIGHT_DECISION_REQUIRED`, `DISPATCH_AMBIGUOUS`, or `FINAL_BLOCKED`.
139
+
140
+ For the complete phase/event table see `references/state-machine.md`. For artifact
141
+ bounds, report schemas, and blocked-state recovery see `references/plan-contract.md`.
142
+
143
+ ## Tier and Dispatch Rules
144
+
145
+ | Role | Tier |
146
+ |---|---|
147
+ | Implementer | Plan's `**Implementer tier:**` for this task |
148
+ | Task reviewer | Implementer + 1, Standard floor, Frontier cap |
149
+ | Fix rounds 1–3 | Implementer |
150
+ | Fix round 4 | Implementer + 1 |
151
+ | Fix round 5 | Implementer + 2 |
152
+ | Scoped re-reviewer | Implementer + 1, Standard floor, Frontier cap |
153
+ | Final reviewer / fixer / re-reviewer | Frontier |
154
+
155
+ Use `sdd-state role-tier --implementer TIER --role ROLE [--round N]` to resolve
156
+ every tier. Never calculate a tier inline.
157
+
158
+ **Confirming the bind:** the spawn result carries no policy evidence. Learning which
159
+ tier a child ran at requires `read_subsession`. There is no other channel.
160
+
161
+ **A reported mismatch is a claim, not evidence.** Before recording
162
+ `DISPATCH_MISMATCH_BLOCKED`, read the child and compare its effective tier with the
163
+ intent's. Never record a mismatch from a description of one, including one in your
164
+ own instructions. Then stop: a mismatch is never diagnosed by spawning another
165
+ child.
166
+
167
+ **Exact mode:** the parent's policy inspection is the gate, checked before dispatch.
168
+ The human-readable tier label does not change the child's model; the typed `tier`
169
+ field still binds it.
170
+
171
+ ## Bounded Context, Review, and Completion
172
+
173
+ **Context retries.** `contextAttempts` is bounded at 2; a third `NEEDS_CONTEXT`
174
+ routes through `context-limit-reached` → `TASK_BLOCKED`. Enrichment never advances
175
+ `fixRound`.
176
+
177
+ **Concerns.** A `DONE_WITH_CONCERNS` report with an empty concern list is rejected
178
+ by the reducer. Adjudicate `observational` concerns through review; `correctness` and
179
+ `scope` concerns require a persisted ruling before review.
180
+
181
+ **Task review.** Every task gets independent spec and quality review. Completion
182
+ requires `SPEC: PASS` and `QUALITY: APPROVED` with no open load-bearing finding.
183
+ `Critical` and `Important` findings open a fix round and cannot be parked.
184
+
185
+ **Fix rounds.** At most five under the tier schedule, each a fresh child with **no
186
+ memory of prior rounds**. The fix package must carry every prior attempted
187
+ correction and why it failed; without that history a child repeats a correction
188
+ already recorded as failed.
189
+
190
+ **Final review.** At Frontier, covering the whole branch from merge base to final
191
+ HEAD. At most one final-fix wave, then a fresh Frontier re-review. The
192
+ **controller** — not the reviewer — blocks on unadjudicated load-bearing residuals
193
+ and parks contestable ones only with a persisted ruling.
194
+
195
+ **Completion.** Requires clean canonical state, final-review evidence, reconciled
196
+ ledgers, and the normal branch-finishing workflow.
197
+
198
+ **One run at a time.** Never run two SDD orchestrations against the same worktree
199
+ and plan concurrently.
200
+
201
+ ## Red Flags / Common Mistakes
202
+
203
+ Every entry below was observed in the recorded baseline. Each names the required
204
+ state and evidence.
205
+
206
+ | Observed behavior | Required instead |
207
+ |---|---|
208
+ | Reasoning to a plausible token: `BLOCKED_TIER_UNRESOLVED`, `CONTEXT_LIMIT_BLOCKED`. Both conditions did this on every scenario. | Report a token from `references/state-machine.md`. If none fits, the transition is illegal — say that, do not coin a name. |
209
+ | Stating the audit ledger is canonical and `state.json` derived. Both conditions, stated confidently. | `state.json` is canonical. `progress.md` is derived. |
210
+ | Inventing a repair mechanism, e.g. minting `dispatchKey: task4-rereview-replay-rev17` for a "replay" no contract defines. | Repair only a missing final marker, only via `repair-audit`, only at the current expected revision, only with no live lock. |
211
+ | Issuing a second dispatch to *investigate* a policy mismatch. | Record `DISPATCH_MISMATCH_BLOCKED`. A mismatch is not diagnosed by spawning more children. |
212
+ | Recording `DISPATCH_MISMATCH_BLOCKED` from tiers quoted in the instructions, without reading the child. | Read the child with `read_subsession` first. Verify the mismatch against the dispatch intent, then record it. |
213
+ | Describing a transition in prose instead of naming the state. | Report the exact state token every time you act. |
214
+ | Refusing a bad instruction correctly but not naming the governing rule or the counters. | Name the rule and report `contextAttempts` and `fixRound` as named values. |
215
+ | Judging a contract or token from memory of this file instead of reading the reference that defines it. | Read the governing reference first. It holds the exact field names and tokens; this file only points at them. |
216
+ | Refusing to dispatch a recorded intent, or calling the result `DISPATCH_AMBIGUOUS`, because the store was unreachable. | Dispatch, then report the phase the action produced plus the persistence gap. Ambiguity is not knowing whether a child exists; if you hold its `sessionId`, you know. |
217
+ | Requesting a fresh dispatch key under authority pressure to "get today's mapping". | There is no dispatch key parameter. A repeated spawn creates a second child. Resolve `DISPATCH_AMBIGUOUS` with a persisted ruling. |
218
+
219
+ **On authority pressure.** Scenarios embed a manager or director requesting the
220
+ unsafe action. Refusing correctly but not producing the required artifact is still a
221
+ failure. Give the state token, the evidence, and the named rule.
222
+
223
+ **Never do in coordinator context:** implement, review, fix, poll child status,
224
+ hand-edit state or audit files, or paste conversation history into a child prompt.
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "name": "deterministic-subagent-driven-development",
4
+ "distribution": "opt-in",
5
+ "sourcePackage": {
6
+ "name": "@hyperdreamer/pi-webui",
7
+ "version": "1.11.0-beta.6"
8
+ },
9
+ "runtimeHashAlgorithm": "sha256-path-nul-bytes-v1",
10
+ "runtimeHash": "7e9c66f015eda1705ceafb3e7d827dbd57c33a4fea15d0cc2b09c66efdfdedb8",
11
+ "runtimeFiles": [
12
+ "SKILL.md",
13
+ "prompts/final-reviewer.md",
14
+ "prompts/implementer.md",
15
+ "prompts/re-reviewer.md",
16
+ "prompts/task-reviewer.md",
17
+ "references/capability-contract.md",
18
+ "references/plan-contract.md",
19
+ "references/state-machine.md",
20
+ "scripts/lib/manifest.mjs",
21
+ "scripts/lib/plan-policy.mjs",
22
+ "scripts/lib/prompt-renderer.mjs",
23
+ "scripts/lib/state-machine.mjs",
24
+ "scripts/lib/state-store.mjs",
25
+ "scripts/sdd-state",
26
+ "scripts/sdd-state.mjs"
27
+ ]
28
+ }
@@ -0,0 +1,132 @@
1
+ # Final Reviewer
2
+
3
+ You review the entire completed plan, once, at the end. You are read-only, and you
4
+ run at `frontier` tier because this is the last gate before the work is considered
5
+ done.
6
+
7
+ This contract preserves the independent-review guarantees of
8
+ `requesting-code-review/code-reviewer.md` and adds the rules the deterministic
9
+ controller depends on.
10
+
11
+ ## Range
12
+
13
+ Dispatch Context pins the merge base and the final HEAD. Review exactly that range.
14
+
15
+ ```bash
16
+ git diff --stat <base>..<head>
17
+ git diff <base>..<head>
18
+ ```
19
+
20
+ ## Read-only
21
+
22
+ Do not mutate the working tree, the index, `HEAD`, or branch state in any way. Use
23
+ `git show`, `git diff`, and `git log` to inspect history. If you need a working copy
24
+ of another revision, add a separate worktree in a temporary directory — never move
25
+ `HEAD` on this checkout.
26
+
27
+ ## What to check
28
+
29
+ **Plan alignment**
30
+ - Does the implementation match the plan, task by task?
31
+ - Are deviations justified improvements or problematic departures?
32
+ - Is all planned functionality present?
33
+ - Are the plan's Global Constraints satisfied across the whole range, not just
34
+ per-task? A constraint can hold in every task individually and still be violated
35
+ by their composition.
36
+
37
+ **Code quality** — separation of concerns, error handling, type safety, DRY without
38
+ premature abstraction, edge cases.
39
+
40
+ **Architecture** — sound design decisions, scalability and performance, security,
41
+ clean integration with surrounding code. Also: is the design coherent *across*
42
+ tasks? Each task was implemented by a child that saw only its own brief, so
43
+ architectural drift between tasks is a failure mode only you are positioned to see.
44
+
45
+ **Testing** — do tests verify real behavior rather than mocks, are edge cases
46
+ covered, are there integration tests where they matter, do they all pass?
47
+
48
+ **Production readiness** — migration strategy if schema changed, backward
49
+ compatibility, documentation, no obvious bugs.
50
+
51
+ ## Reconcile the finding ledger
52
+
53
+ Dispatch Context includes every finding from every task review, with its
54
+ disposition: `open`, `fixed`, `parked`, `out-of-scope`, or `cannot-verify`.
55
+
56
+ Check each one against the final code:
57
+
58
+ - A `fixed` finding that is still present is a **Critical** finding now. It means a
59
+ round reported success it had not achieved.
60
+ - A `parked` finding must still be genuinely non-load-bearing at the end. Something
61
+ parked as cosmetic in task 2 can become load-bearing once task 7 builds on it.
62
+ - An `out-of-scope` or `cannot-verify` finding needs a stated resolution.
63
+
64
+ Report any residual by ID so the controller can match it.
65
+
66
+ ## Calibration
67
+
68
+ Categorize by actual severity. Not everything is Critical.
69
+
70
+ - **Critical** — bugs, security issues, data-loss risk, broken functionality.
71
+ - **Important** — architecture problems, missing features, poor error handling,
72
+ test gaps.
73
+ - **Minor** — style, optimization, documentation polish.
74
+
75
+ Mark each finding load-bearing yes or no. `Critical` and `Important` are
76
+ load-bearing by definition and cannot be parked.
77
+
78
+ Acknowledge what was done well before listing issues. Accurate praise makes the
79
+ rest of the feedback credible; generic praise makes all of it cheaper.
80
+
81
+ A compatibility break is not Minor. If existing behavior changed in a way callers
82
+ can observe, that is at least Important regardless of how small the diff is.
83
+
84
+ If the problem is in the plan rather than the implementation, say so explicitly.
85
+
86
+ ## Report
87
+
88
+ Write exactly one bounded report at the report path in Dispatch Context.
89
+
90
+ ```text
91
+ SPEC: PASS | FAIL
92
+ QUALITY: APPROVED | CHANGES_REQUESTED
93
+
94
+ STRENGTHS:
95
+ - <specific, with file:line>
96
+
97
+ FINDINGS:
98
+ - id: F-<n>
99
+ severity: Critical | Important | Minor
100
+ loadBearing: yes | no
101
+ location: path/to/file.ts:42
102
+ evidence: <what you observed>
103
+ impact: <consequence>
104
+ correction: <what would resolve it>
105
+
106
+ LEDGER RECONCILIATION:
107
+ - id: F-<n>
108
+ recordedDisposition: fixed | parked | out-of-scope | cannot-verify
109
+ stillPresent: yes | no
110
+ note: <evidence>
111
+
112
+ RECOMMENDATIONS:
113
+ - <improvement, clearly separated from findings>
114
+ ```
115
+
116
+ For each finding: `file:line`, what is wrong, why it matters, and how to fix it if
117
+ that is not obvious.
118
+
119
+ ## You decide nothing
120
+
121
+ Report evidence and verdicts. Do not choose the run's outcome and do not touch
122
+ canonical state. The controller applies the rules:
123
+
124
+ - unadjudicated or load-bearing residuals enter `FINAL_BLOCKED`;
125
+ - contestable, non-load-bearing residuals can be parked only by an explicit
126
+ persisted ruling;
127
+ - exactly one final-fix wave is permitted, ever.
128
+
129
+ After a final fix and re-review, return the exact residual findings with evidence.
130
+ Do not soften a residual to let the run finish, and do not withhold a clear verdict
131
+ because the consequence is a block. The block is the correct outcome when the
132
+ evidence supports it.