@jaggerxtrm/specialists 3.17.0 → 3.18.0
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.
- package/README.md +268 -134
- package/config/mandatory-rules/json-only-final-output.md +13 -0
- package/config/mandatory-rules/service-skills-diff-scan-mandatory.md +26 -0
- package/config/mandatory-rules/service-skills-gitnexus-triage.md +5 -0
- package/config/skills/setup-specialists/SKILL.md +556 -0
- package/config/skills/specialists-creator/SKILL.md +132 -4
- package/config/skills/specialists-creator/scripts/audit-spec-uniformity.mjs +1 -1
- package/config/specialists/bare.specialist.json +3 -3
- package/config/specialists/changelog-drafter.specialist.json +5 -4
- package/config/specialists/changelog-keeper.specialist.json +7 -6
- package/config/specialists/debugger.specialist.json +2 -2
- package/config/specialists/executor.specialist.json +2 -2
- package/config/specialists/explorer.specialist.json +4 -4
- package/config/specialists/memory-processor.specialist.json +3 -3
- package/config/specialists/node-coordinator.specialist.json +3 -3
- package/config/specialists/obligations-scanner.specialist.json +67 -17
- package/config/specialists/overthinker.specialist.json +3 -3
- package/config/specialists/planner.specialist.json +4 -4
- package/config/specialists/quant-methodologist.specialist.json +145 -0
- package/config/specialists/quant-researcher.specialist.json +144 -0
- package/config/specialists/researcher.specialist.json +5 -5
- package/config/specialists/reviewer.specialist.json +1 -1
- package/config/specialists/seconder.specialist.json +2 -2
- package/config/specialists/security-auditor.specialist.json +2 -2
- package/config/specialists/service-skills-sync.specialist.json +90 -75
- package/config/specialists/specialists-creator.specialist.json +4 -4
- package/config/specialists/sync-docs.specialist.json +4 -4
- package/config/specialists/test-engineer.specialist.json +3 -3
- package/config/specialists/test-runner.specialist.json +7 -7
- package/config/specialists/transcriber.specialist.json +3 -3
- package/config/specialists/xt-merge.specialist.json +4 -4
- package/dist/asset-contract.json +21 -2
- package/dist/index.js +25704 -16376
- package/dist/lib.js +9849 -6147
- package/dist/types/cli/console/components.d.ts +83 -0
- package/dist/types/cli/console/components.d.ts.map +1 -0
- package/dist/types/cli/console/config-source.d.ts +58 -0
- package/dist/types/cli/console/config-source.d.ts.map +1 -0
- package/dist/types/cli/console/forensic.d.ts +11 -0
- package/dist/types/cli/console/forensic.d.ts.map +1 -0
- package/dist/types/cli/console/git.d.ts +28 -0
- package/dist/types/cli/console/git.d.ts.map +1 -0
- package/dist/types/cli/console/help.d.ts +2 -0
- package/dist/types/cli/console/help.d.ts.map +1 -0
- package/dist/types/cli/console/log.d.ts +13 -0
- package/dist/types/cli/console/log.d.ts.map +1 -0
- package/dist/types/cli/console/repo-config.d.ts +26 -0
- package/dist/types/cli/console/repo-config.d.ts.map +1 -0
- package/dist/types/cli/console/repo-discovery.d.ts +12 -0
- package/dist/types/cli/console/repo-discovery.d.ts.map +1 -0
- package/dist/types/cli/console/runtime.d.ts +12 -0
- package/dist/types/cli/console/runtime.d.ts.map +1 -0
- package/dist/types/cli/console/subscribe-prototype.d.ts +18 -0
- package/dist/types/cli/console/subscribe-prototype.d.ts.map +1 -0
- package/dist/types/cli/console/theme.d.ts +91 -0
- package/dist/types/cli/console/theme.d.ts.map +1 -0
- package/dist/types/cli/console/types.d.ts +231 -0
- package/dist/types/cli/console/types.d.ts.map +1 -0
- package/dist/types/cli/console/view-model.d.ts +252 -0
- package/dist/types/cli/console/view-model.d.ts.map +1 -0
- package/dist/types/cli/console.d.ts +2 -0
- package/dist/types/cli/console.d.ts.map +1 -0
- package/dist/types/cli/db.d.ts.map +1 -1
- package/dist/types/cli/doctor.d.ts.map +1 -1
- package/dist/types/cli/edit.d.ts.map +1 -1
- package/dist/types/cli/epic.d.ts.map +1 -1
- package/dist/types/cli/feed.d.ts.map +1 -1
- package/dist/types/cli/forensic.d.ts +2 -0
- package/dist/types/cli/forensic.d.ts.map +1 -0
- package/dist/types/cli/format-helpers.d.ts +4 -2
- package/dist/types/cli/format-helpers.d.ts.map +1 -1
- package/dist/types/cli/help.d.ts.map +1 -1
- package/dist/types/cli/init.d.ts +10 -0
- package/dist/types/cli/init.d.ts.map +1 -1
- package/dist/types/cli/list.d.ts.map +1 -1
- package/dist/types/cli/log.d.ts.map +1 -1
- package/dist/types/cli/metrics.d.ts +2 -0
- package/dist/types/cli/metrics.d.ts.map +1 -0
- package/dist/types/cli/ps.d.ts.map +1 -1
- package/dist/types/cli/result.d.ts.map +1 -1
- package/dist/types/cli/run.d.ts +1 -0
- package/dist/types/cli/run.d.ts.map +1 -1
- package/dist/types/cli/script.d.ts +3 -0
- package/dist/types/cli/script.d.ts.map +1 -1
- package/dist/types/cli/serve.d.ts.map +1 -1
- package/dist/types/cli/setup.d.ts +19 -1
- package/dist/types/cli/setup.d.ts.map +1 -1
- package/dist/types/cli/status.d.ts.map +1 -1
- package/dist/types/cli/version-check.d.ts +1 -0
- package/dist/types/cli/version-check.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/pi/session.d.ts +11 -1
- package/dist/types/pi/session.d.ts.map +1 -1
- package/dist/types/server.d.ts +15 -0
- package/dist/types/server.d.ts.map +1 -1
- package/dist/types/specialist/benchmarks.d.ts +37 -0
- package/dist/types/specialist/benchmarks.d.ts.map +1 -0
- package/dist/types/specialist/chain-identity.d.ts +7 -1
- package/dist/types/specialist/chain-identity.d.ts.map +1 -1
- package/dist/types/specialist/control.d.ts.map +1 -1
- package/dist/types/specialist/forensic-events.d.ts +138 -0
- package/dist/types/specialist/forensic-events.d.ts.map +1 -0
- package/dist/types/specialist/forensic-renderer.d.ts +34 -0
- package/dist/types/specialist/forensic-renderer.d.ts.map +1 -0
- package/dist/types/specialist/git-diff-evidence.d.ts +28 -0
- package/dist/types/specialist/git-diff-evidence.d.ts.map +1 -0
- package/dist/types/specialist/global-config.d.ts +389 -0
- package/dist/types/specialist/global-config.d.ts.map +1 -0
- package/dist/types/specialist/launch.d.ts +1 -0
- package/dist/types/specialist/launch.d.ts.map +1 -1
- package/dist/types/specialist/live-aggregates.d.ts +46 -0
- package/dist/types/specialist/live-aggregates.d.ts.map +1 -0
- package/dist/types/specialist/loader.d.ts +50 -1
- package/dist/types/specialist/loader.d.ts.map +1 -1
- package/dist/types/specialist/model-chain.d.ts +7 -0
- package/dist/types/specialist/model-chain.d.ts.map +1 -0
- package/dist/types/specialist/model-probes.d.ts +28 -0
- package/dist/types/specialist/model-probes.d.ts.map +1 -0
- package/dist/types/specialist/node-contract.d.ts +18 -18
- package/dist/types/specialist/node-supervisor.d.ts.map +1 -1
- package/dist/types/specialist/observability-db.d.ts +1 -1
- package/dist/types/specialist/observability-db.d.ts.map +1 -1
- package/dist/types/specialist/observability-sqlite.d.ts +25 -0
- package/dist/types/specialist/observability-sqlite.d.ts.map +1 -1
- package/dist/types/specialist/preset-resolver.d.ts +56 -0
- package/dist/types/specialist/preset-resolver.d.ts.map +1 -0
- package/dist/types/specialist/prometheus-projection.d.ts +25 -0
- package/dist/types/specialist/prometheus-projection.d.ts.map +1 -0
- package/dist/types/specialist/runner.d.ts +26 -1
- package/dist/types/specialist/runner.d.ts.map +1 -1
- package/dist/types/specialist/schema.d.ts +163 -54
- package/dist/types/specialist/schema.d.ts.map +1 -1
- package/dist/types/specialist/script-runner.d.ts +5 -1
- package/dist/types/specialist/script-runner.d.ts.map +1 -1
- package/dist/types/specialist/snapshot-diff.d.ts +8 -0
- package/dist/types/specialist/snapshot-diff.d.ts.map +1 -0
- package/dist/types/specialist/source-queue.d.ts +13 -0
- package/dist/types/specialist/source-queue.d.ts.map +1 -0
- package/dist/types/specialist/supervisor.d.ts +15 -1
- package/dist/types/specialist/supervisor.d.ts.map +1 -1
- package/dist/types/specialist/timeline-events.d.ts +68 -1
- package/dist/types/specialist/timeline-events.d.ts.map +1 -1
- package/dist/types/tools/specialist/resume_specialist.tool.d.ts +4 -4
- package/dist/types/tools/specialist/specialist_status.tool.d.ts +1 -1
- package/dist/types/tools/specialist/steer_specialist.tool.d.ts +4 -4
- package/dist/types/tools/specialist/use_specialist.tool.d.ts +16 -16
- package/docs/ARCHITECTURE.md +1176 -0
- package/docs/TODO.md +9 -0
- package/docs/architecture.md +11 -0
- package/docs/archive/2026-03-13-bd-decision-context-template-plan.md +96 -0
- package/docs/archive/AGENT-HANDOFF.md +351 -0
- package/docs/archive/PARITY-ANALYSIS.md +296 -0
- package/docs/archive/SPECIALISTS_REFACTOR.md.md +30 -0
- package/docs/archive/cc-programmatic.md +216 -0
- package/docs/archive/claude-agent-sdk.md +594 -0
- package/docs/archive/decision-specialist-directory.md +41 -0
- package/docs/archive/discoveries.md +148 -0
- package/docs/archive/executor-benchmark-protocol.md +198 -0
- package/docs/archive/future-features.md +66 -0
- package/docs/archive/gzrx-completion-critique.md +183 -0
- package/docs/archive/gzrx-research-notes.md +401 -0
- package/docs/archive/gzrx-tool-catalog.md +760 -0
- package/docs/archive/iron-review-hardening-qa-chain-substrate.md +322 -0
- package/docs/archive/iron-review-hardening.html +1004 -0
- package/docs/archive/issuetracking.md +312 -0
- package/docs/archive/qa-v3.0.2.md +220 -0
- package/docs/archive/restructure.md +231 -0
- package/docs/archive/script-specialists.md +1254 -0
- package/docs/archive/spec-v3.md +792 -0
- package/docs/archive/specialist-stats.md +127 -0
- package/docs/archive/specialists-friction-audit.md +1347 -0
- package/docs/archive/specialists-runtime-critique.md +170 -0
- package/docs/archive/specialists-service-evaluation.md +713 -0
- package/docs/archive/specialists-substrate-alignment.md +255 -0
- package/docs/archive/substrate-review.md +1288 -0
- package/docs/archive/test-writer-specialist.md +254 -0
- package/docs/archive/using-specialists-v3-improvements-2026-05-09.md +600 -0
- package/docs/archive/xtrm-specialists-analysis.md +314 -0
- package/docs/authoring.md +701 -0
- package/docs/background-jobs.md +203 -0
- package/docs/bare-specialists.md +83 -0
- package/docs/benchmarks/executor-benchmark-runner.md +66 -0
- package/docs/bootstrap.md +161 -0
- package/docs/cli-reference.md +1645 -0
- package/docs/deploying-alongside.md +155 -0
- package/docs/design/README.md +36 -0
- package/docs/design/darth-feedor-migration.md +290 -0
- package/docs/design/roadmap/README.md +32 -0
- package/docs/design/roadmap/chain-templates/README.md +146 -0
- package/docs/design/roadmap/chain-templates/code-quick.formula.json +27 -0
- package/docs/design/roadmap/chain-templates/code-standard.formula.json +126 -0
- package/docs/design/roadmap/chain-templates/code-with-advisors.formula.json +128 -0
- package/docs/design/roadmap/chain-templates/code-with-tests.formula.json +76 -0
- package/docs/design/roadmap/chain-templates/debug.formula.json +128 -0
- package/docs/design/roadmap/chain-templates/doc-sync.formula.json +28 -0
- package/docs/design/roadmap/chain-templates/memory-hygiene.formula.json +27 -0
- package/docs/design/roadmap/chain-templates/planning.formula.json +27 -0
- package/docs/design/roadmap/chain-templates/premortem.formula.json +26 -0
- package/docs/design/roadmap/chain-templates/release-prep.formula.json +36 -0
- package/docs/design/roadmap/chain-templates/research-only.formula.json +28 -0
- package/docs/design/roadmap/chain-templates/restitch.formula.json +125 -0
- package/docs/design/roadmap/chain-templates/security-deep.formula.json +161 -0
- package/docs/design/roadmap/chain-templates/test-only.formula.json +52 -0
- package/docs/design/roadmap/chain-templates/triage.formula.json +35 -0
- package/docs/design/roadmap/history/handoff-from-substrate-design.md +87 -0
- package/docs/design/roadmap/history/substrate-reconciliation.md +105 -0
- package/docs/design/roadmap/specialists-roadmap.md +1193 -0
- package/docs/design/sp-console-subscribe-via-materializer.md +231 -0
- package/docs/design/sp-console-tui-mock-v2.html +293 -0
- package/docs/design/sp-console-tui-mock.html +120 -0
- package/docs/design/sp-console-tui.md +340 -0
- package/docs/design/specialist-agentops-suite.md +323 -0
- package/docs/design/substrate/channels.md +14 -0
- package/docs/design/substrate/devops-platform-engineering-prd.md +446 -0
- package/docs/design/substrate/html-design-example.md +339 -0
- package/docs/design/xtrm-tiers-architecture.svg +132 -0
- package/docs/devops/dependency-verdict-materialization.md +46 -0
- package/docs/epic-readiness.md +75 -0
- package/docs/examples/mercury-atomic-summarizer.specialist.json +26 -0
- package/docs/examples/smoke-echo-text-expected-keys.specialist.json +25 -0
- package/docs/examples/smoke-echo.specialist.json +25 -0
- package/docs/features.md +1577 -0
- package/docs/hooks.md +81 -0
- package/docs/installation.md +142 -0
- package/docs/manifest.md +184 -0
- package/docs/mcp-servers.md +73 -0
- package/docs/mcp-tools.md +71 -0
- package/docs/nodes.md +231 -0
- package/docs/observability-metrics.md +152 -0
- package/docs/operator/sp-console-v2-walkthrough.md +410 -0
- package/docs/overrides-guide.md +306 -0
- package/docs/pi-rpc-boundary.md +118 -0
- package/docs/pi-session.md +195 -0
- package/docs/release.md +22 -0
- package/docs/skills.md +132 -0
- package/docs/specialists/handoff-schema.md +181 -0
- package/docs/specialists-catalog.md +99 -0
- package/docs/specialists-service-install.md +226 -0
- package/docs/specialists-service.md +363 -0
- package/docs/surface-ownership.md +138 -0
- package/docs/upgrade-notes/kan-90-global-user-config.md +114 -0
- package/docs/upgrade-notes/kan-91-expanded-overrides.md +261 -0
- package/docs/workflow.md +114 -0
- package/docs/worktree.md +71 -0
- package/docs/worktrees.md +309 -0
- package/package.json +6 -3
|
@@ -0,0 +1,1288 @@
|
|
|
1
|
+
> **ARCHIVED 2026-05-27** — content superseded by:
|
|
2
|
+
> - `docs/design/substrate/specialists-roadmap-revised.md` (canonical specialists roadmap; absorbed this file in §0/§3.2/§12/§13)
|
|
3
|
+
> - `docs/design/substrate/substrate.md` rev10 (canonical substrate design)
|
|
4
|
+
> - `docs/design/chain-templates/` (13 evidence-backed bd formulas)
|
|
5
|
+
>
|
|
6
|
+
> Preserved for historical context. Do not edit. Do not cite as authoritative.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Substrate Review Notes
|
|
11
|
+
|
|
12
|
+
> Status: working review notes from the substrate design review.
|
|
13
|
+
> Source context: `docs/design/substrate.md`, `docs/design/channels.md`, recent specialists session reports, and Mercury market-data custom workflow reports.
|
|
14
|
+
> Purpose: capture decisions and proposed clarifications before rewriting the main substrate design.
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
Grouped thematically; numbering follows write-order.
|
|
19
|
+
|
|
20
|
+
**Part I — Core model and primitives**
|
|
21
|
+
- §1 — Turn / tick model (Pi turns are observability, not scheduling)
|
|
22
|
+
- §2 — Workflow advancement (driven by workflow, not orchestrator)
|
|
23
|
+
- §3 — Containers, issues, channels, participants, pulses, evidence (primitives separated)
|
|
24
|
+
- §4 — Root issues and step issues
|
|
25
|
+
- §5 — Issue classes and roles
|
|
26
|
+
- §6 — Blocking and non-blocking semantics (edge vocabulary reconciled with substrate.md §6.7)
|
|
27
|
+
- §7 — Concrete nesting examples
|
|
28
|
+
- §11 — Channels and issue contracts together
|
|
29
|
+
- §12 — Concrete design patch needed in substrate.md
|
|
30
|
+
|
|
31
|
+
**Part II — Dispatch and operator surface**
|
|
32
|
+
- §8 — Dispatch commands
|
|
33
|
+
- §9 — Mid-flight ad-hoc insertion
|
|
34
|
+
- §10 — Pulse-driven insertion
|
|
35
|
+
|
|
36
|
+
**Part III — Cross-doc consistency and SDK**
|
|
37
|
+
- §13 — Cross-document consistency (substrate.md vs channels.md): cross-container channels, external connectors, pair: channels
|
|
38
|
+
- §14 — SDK surface (collapses §2.4's four items into two wires + reference client + schemas + credentials)
|
|
39
|
+
- §15 — Bound vs unbound participants
|
|
40
|
+
- §16 — Two activation modes (reactive vs workflow-step)
|
|
41
|
+
- §17 — Pulse delivery for non-coordinator consumers
|
|
42
|
+
- §18 — Reuse audit (what each new feature must reuse)
|
|
43
|
+
|
|
44
|
+
**Part IV — Runtime alignment (verified against pi/supervisor)**
|
|
45
|
+
- §19 — Pi turn alignment (decided: no separate substrate tick; event-driven on turn_end/pulse/sb)
|
|
46
|
+
- §20 — Daemon-observes model (decided: substrate consumes observability.db; no new hooks)
|
|
47
|
+
- §21 — Failure classification (decided: transient/semantic holds; preconditions are a separate §6.4 concern)
|
|
48
|
+
|
|
49
|
+
**Part V — Lifecycle and policy decisions**
|
|
50
|
+
- §22 — Per-issue close flow (replaces bd close + memory-ack + commit-gate + Stop hook)
|
|
51
|
+
- §23 — Autonomy gradient: container nesting, node nesting, dispatch_mode predicate
|
|
52
|
+
- §24 — Knowledge scope: rule conflict, session vs seed curator, memory pruning/promotion/identity
|
|
53
|
+
|
|
54
|
+
**Part VI — Workflow definition language**
|
|
55
|
+
- §25 — Workflow language + concrete workflows extracted from session reports
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 1. Turn / tick model
|
|
60
|
+
|
|
61
|
+
Substrate should not treat Pi's `turn_start` / `turn_end` as the container scheduler.
|
|
62
|
+
|
|
63
|
+
There are multiple clocks, each with different authority:
|
|
64
|
+
|
|
65
|
+
1. **Model turn** — Pi runtime events: `turn_start`, `turn_end`, `turn_summary`. These are observability and metric events.
|
|
66
|
+
2. **Job turn** — one prompt/resume cycle from input to `agent_end`. In keep-alive mode, `agent_end` usually means the job is quiescent and can enter `waiting`, not necessarily terminal `done`.
|
|
67
|
+
3. **Workflow step** — executor, code-sanity, reviewer, local-validation, quant-methodologist, etc. A step completes only when its required evidence is persisted.
|
|
68
|
+
4. **Container transition** — lifecycle movement such as `working -> converging -> ready`. This is computed from persisted step/evidence state, not from streamed text.
|
|
69
|
+
|
|
70
|
+
The design should say this directly:
|
|
71
|
+
|
|
72
|
+
> Pi turns are observability events, not scheduling authority. `agent_end` is the job-level quiescence barrier. Substrate advances containers/workflows only from persisted evidence or equivalent pulses.
|
|
73
|
+
|
|
74
|
+
A container should have an event/reducer loop. Inputs include job waiting/done/error, channel messages, pulse delivery, collision updates, timer/timeout events, and explicit operator actions. The reducer derives the next state; after-hooks perform idempotent side effects such as dispatching a step, resuming a job, or writing a `system.*` channel message.
|
|
75
|
+
|
|
76
|
+
This aligns with `channels.md`: derive first, act second; never let a message or model event bypass the single scheduler.
|
|
77
|
+
|
|
78
|
+
## 2. Workflow advancement
|
|
79
|
+
|
|
80
|
+
The substrate workflow engine should automate advancement, not judgment.
|
|
81
|
+
|
|
82
|
+
A workflow is not just a list of specialists. It is a persisted state machine of steps. Each step needs a contract:
|
|
83
|
+
|
|
84
|
+
- role/specialist to run
|
|
85
|
+
- input evidence required
|
|
86
|
+
- output evidence required
|
|
87
|
+
- completion predicate
|
|
88
|
+
- retry/remediation rule
|
|
89
|
+
- authority for dispatch/resume/skip
|
|
90
|
+
- blocking edges to other steps
|
|
91
|
+
|
|
92
|
+
The daemon may auto-advance only when the rule is deterministic and replayable: for example executor evidence exists, code-sanity step is ready, reviewer PASS is persisted, or a surface matcher requires security-auditor. Open-ended judgment still belongs to the orchestrator, seed judge, advisor, or operator.
|
|
93
|
+
|
|
94
|
+
The practical target is to remove routine manual dispatch from the orchestrator:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
executor -> local-validation -> code-sanity -> reviewer -> merge_ready
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
But every advancement must be based on persisted evidence, not text seen in a live stream. A PASS emitted through an initial run, a resume turn, or a channel verdict should all become the same durable `verdict` evidence.
|
|
101
|
+
|
|
102
|
+
## 3. Containers, issues, channels, participants, pulses, evidence
|
|
103
|
+
|
|
104
|
+
These are separate SDK primitives.
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
Container = runtime envelope: lifecycle, workflow, channel, ownership, merge/collision state.
|
|
108
|
+
Issue = durable contract. Root issues and step issues are both issue contracts.
|
|
109
|
+
Channel = live coordination stream for a container.
|
|
110
|
+
Participant = a running actor spawned from an issue contract into a channel.
|
|
111
|
+
Pulse = idempotent signal that wakes, inserts, opens, or messages.
|
|
112
|
+
Evidence = durable output: diff, verdict, finding, test result, checklist, decision.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A container is not an issue tree. A container contains issue contracts and owns the channel where their participants coordinate.
|
|
116
|
+
|
|
117
|
+
Every specialist dispatch should be issue-backed. This preserves the current useful property of beads: the prompt/contract for a code-sanity, reviewer, explorer, or methodologist run is durable and inspectable.
|
|
118
|
+
|
|
119
|
+
## 4. Root issues and step issues
|
|
120
|
+
|
|
121
|
+
A **root issue** represents the user's/project's desired change.
|
|
122
|
+
|
|
123
|
+
A **step issue** represents a workflow-generated contract for one participant or gate inside the root issue's container.
|
|
124
|
+
|
|
125
|
+
The step receives both the parent/root contract and its own precise step contract.
|
|
126
|
+
|
|
127
|
+
Example root:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
iss:tx92.2
|
|
131
|
+
class: root
|
|
132
|
+
role: implementation-root
|
|
133
|
+
title: Fix Treasury fractional rounding
|
|
134
|
+
contract:
|
|
135
|
+
problem: Treasury prices near tick boundaries format incorrectly
|
|
136
|
+
scope:
|
|
137
|
+
- analytics/outright/calculation.py
|
|
138
|
+
- tests/test_treasury_conversion.py
|
|
139
|
+
validation:
|
|
140
|
+
- python3 -m pytest tests/test_treasury_conversion.py -q
|
|
141
|
+
acceptance:
|
|
142
|
+
- Near-boundary ZN/ZT prices round to CME-valid fractional strings
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Example generated code-sanity step:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
iss:tx92.2.5
|
|
149
|
+
class: gate
|
|
150
|
+
role: code-sanity
|
|
151
|
+
parent_issue: iss:tx92.2
|
|
152
|
+
container: chain:tx92.2
|
|
153
|
+
validates: iss:tx92.2.3
|
|
154
|
+
|
|
155
|
+
contract:
|
|
156
|
+
problem: Validate the executor diff for the Treasury rounding fix.
|
|
157
|
+
input:
|
|
158
|
+
- parent contract: iss:tx92.2
|
|
159
|
+
- executor evidence: diff from iss:tx92.2.3
|
|
160
|
+
- methodology evidence: iss:tx92.2.2
|
|
161
|
+
scope:
|
|
162
|
+
- analytics/outright/calculation.py
|
|
163
|
+
- tests/test_treasury_conversion.py
|
|
164
|
+
non_goals:
|
|
165
|
+
- no edits
|
|
166
|
+
- no broad architecture review
|
|
167
|
+
output:
|
|
168
|
+
- OK or FINDINGS
|
|
169
|
+
- findings must cite file/line and severity
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Prompt composition for the code-sanity participant:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
GLOBAL RULES
|
|
176
|
+
ROLE RULES: code-sanity
|
|
177
|
+
PARENT CONTRACT: iss:tx92.2
|
|
178
|
+
STEP CONTRACT: iss:tx92.2.5
|
|
179
|
+
INPUT EVIDENCE:
|
|
180
|
+
- executor diff ref
|
|
181
|
+
- methodology finding ref
|
|
182
|
+
CHANNEL CONTEXT:
|
|
183
|
+
- recent relevant channel messages
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 5. Issue classes and roles
|
|
187
|
+
|
|
188
|
+
Use `class` for function in the work graph and `role` for who/what performs it.
|
|
189
|
+
|
|
190
|
+
Proposed issue classes:
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
root = user/project work item to reach done/merged
|
|
194
|
+
step = executable workflow unit, usually producing implementation/evidence
|
|
195
|
+
gate = validation step whose verdict affects readiness
|
|
196
|
+
advisor = context or decision-producing step; may or may not block
|
|
197
|
+
followup = newly discovered future work; normally non-blocking
|
|
198
|
+
decision = explicit choice required from orchestrator/operator
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
|
|
203
|
+
```text
|
|
204
|
+
iss:tx92.2 class=root role=implementation-root
|
|
205
|
+
iss:tx92.2.1 class=advisor role=explorer
|
|
206
|
+
iss:tx92.2.2 class=advisor role=quant-methodologist
|
|
207
|
+
iss:tx92.2.3 class=step role=executor
|
|
208
|
+
iss:tx92.2.4 class=gate role=local-validation
|
|
209
|
+
iss:tx92.2.5 class=gate role=code-sanity
|
|
210
|
+
iss:tx92.2.6 class=gate role=reviewer
|
|
211
|
+
iss:tx92.2.7 class=followup role=cleanup
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Global issue lists should hide workflow internals by default. Container views should show them.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
sb issue ls # root/followup/decision by default
|
|
218
|
+
sb issue ls --class step,gate,advisor
|
|
219
|
+
sb container ps chain:tx92.2 --tree # full runtime tree
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 6. Blocking and non-blocking semantics
|
|
223
|
+
|
|
224
|
+
Blocking should be edge-driven, not class-driven.
|
|
225
|
+
|
|
226
|
+
An advisor may block if the executor/reviewer requires its output. A gate normally blocks, but the precise behavior still comes from workflow edges and verdict rules. A followup normally does not block unless explicitly attached as required.
|
|
227
|
+
|
|
228
|
+
Proposed edge vocabulary, reconciled against substrate.md §6.1 / §6.7 (which already defines nine relationship types):
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
# Already in substrate.md §6.7 — keep, used unchanged:
|
|
232
|
+
blocks_on = hard gate; source cannot start/complete until target completes
|
|
233
|
+
parent = membership/gate; epic/wave member edge
|
|
234
|
+
until = temporary gate; drops when target reaches state X
|
|
235
|
+
validates = validation link; verdict affects target/root readiness
|
|
236
|
+
discovered_from = context; provenance edge, no gate
|
|
237
|
+
caused_by = context; failure/incident provenance
|
|
238
|
+
relates = soft context
|
|
239
|
+
tracks = soft context (long-running watch)
|
|
240
|
+
supersedes = lifecycle replacement
|
|
241
|
+
|
|
242
|
+
# Proposed additions — workflow-runtime provenance not yet in substrate.md:
|
|
243
|
+
informs = context; output enters target's context pack at seed time
|
|
244
|
+
(subsumed by `relates` today; promote when context-pack rules need it)
|
|
245
|
+
spawned_by = runtime provenance from a pulse / workflow event / channel after-hook
|
|
246
|
+
(subsumed by `discovered_from` today; split when replay/audit needs
|
|
247
|
+
to distinguish human creation from runtime materialization)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Naming convention note: substrate.md reads receiving-side (`blocks_on`, `discovered_from`). The earlier draft of this review used directional sending-side names (`requires`, `blocks X`). Use substrate.md's convention as canonical to avoid ambiguity at the DB layer; helper CLI flags (`--blocks X`) can still read directionally without changing the stored edge.
|
|
251
|
+
|
|
252
|
+
Example (receiving-side / canonical naming):
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
iss:tx92.2.3 executor
|
|
256
|
+
blocks_on iss:tx92.2.1 explorer
|
|
257
|
+
blocks_on iss:tx92.2.2 quant-methodologist
|
|
258
|
+
|
|
259
|
+
iss:tx92.2.5 code-sanity
|
|
260
|
+
blocks_on iss:tx92.2.3 executor
|
|
261
|
+
validates iss:tx92.2.3 executor
|
|
262
|
+
|
|
263
|
+
iss:tx92.2.6 reviewer
|
|
264
|
+
blocks_on iss:tx92.2.3 executor
|
|
265
|
+
blocks_on iss:tx92.2.5 code-sanity
|
|
266
|
+
validates iss:tx92.2 root
|
|
267
|
+
|
|
268
|
+
iss:tx92.2.7 cleanup followup
|
|
269
|
+
discovered_from iss:tx92.2.5 code-sanity
|
|
270
|
+
relates iss:tx92.2 root # promote to `informs` if context-pack rule needs it
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A low-severity cleanup found by code-sanity becomes a non-blocking followup. A correctness issue required before PASS becomes a blocking remediation step.
|
|
274
|
+
|
|
275
|
+
## 7. Concrete nesting examples
|
|
276
|
+
|
|
277
|
+
### LOW blast chain
|
|
278
|
+
|
|
279
|
+
```text
|
|
280
|
+
chain:abc
|
|
281
|
+
channel: chain:abc
|
|
282
|
+
iss:abc class=root role=implementation-root
|
|
283
|
+
iss:abc.1 class=step role=executor
|
|
284
|
+
iss:abc.2 class=gate role=code-sanity
|
|
285
|
+
iss:abc.3 class=gate role=reviewer
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### HIGH/CRITICAL Mercury quant chain
|
|
289
|
+
|
|
290
|
+
```text
|
|
291
|
+
chain:tx92.2
|
|
292
|
+
channel: chain:tx92.2
|
|
293
|
+
iss:tx92.2 class=root role=implementation-root
|
|
294
|
+
iss:tx92.2.1 class=advisor role=explorer
|
|
295
|
+
iss:tx92.2.2 class=advisor role=quant-methodologist
|
|
296
|
+
iss:tx92.2.3 class=step role=executor
|
|
297
|
+
iss:tx92.2.4 class=gate role=local-validation
|
|
298
|
+
iss:tx92.2.5 class=gate role=code-sanity
|
|
299
|
+
iss:tx92.2.6 class=gate role=reviewer
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Epic with root issues, each owning chain steps
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
epic:tx92
|
|
306
|
+
iss:tx92 class=root role=epic-root
|
|
307
|
+
|
|
308
|
+
chain:tx92.1
|
|
309
|
+
iss:tx92.1 class=root
|
|
310
|
+
iss:tx92.1.1 class=step role=executor
|
|
311
|
+
iss:tx92.1.2 class=gate role=reviewer
|
|
312
|
+
|
|
313
|
+
chain:tx92.2
|
|
314
|
+
iss:tx92.2 class=root
|
|
315
|
+
iss:tx92.2.1 class=advisor role=explorer
|
|
316
|
+
iss:tx92.2.2 class=advisor role=quant-methodologist
|
|
317
|
+
iss:tx92.2.3 class=step role=executor
|
|
318
|
+
iss:tx92.2.4 class=gate role=code-sanity
|
|
319
|
+
iss:tx92.2.5 class=gate role=reviewer
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The `.1`, `.2`, `.3` path is a display path. Store parent/child explicitly; do not rely on string parsing for semantics.
|
|
323
|
+
|
|
324
|
+
## 8. Dispatch commands
|
|
325
|
+
|
|
326
|
+
Create a root issue:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
sb issue create \
|
|
330
|
+
--class root \
|
|
331
|
+
--title "Fix Treasury fractional rounding" \
|
|
332
|
+
--problem "Treasury prices near tick boundaries format incorrectly" \
|
|
333
|
+
--scope analytics/outright/calculation.py \
|
|
334
|
+
--scope tests/test_treasury_conversion.py \
|
|
335
|
+
--validation "python3 -m pytest tests/test_treasury_conversion.py -q" \
|
|
336
|
+
--acceptance "Near-boundary ZN/ZT prices round to CME-valid fractional strings"
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Dispatch the root through a workflow:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
sb dispatch iss:tx92.2 --workflow mercury-quant-critical
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Internal effects:
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
opens container chain:tx92.2
|
|
349
|
+
opens channel chain:tx92.2
|
|
350
|
+
materializes workflow step issues .1 through .6
|
|
351
|
+
emits workflow.step.ready pulses for .1 and .2
|
|
352
|
+
dispatches explorer and quant-methodologist if auto-dispatch policy allows
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Dispatch a generated step explicitly:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
sb dispatch iss:tx92.2.5
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
A single `sb dispatch <issue-id>` is enough; issue class/role tells substrate how to run it.
|
|
362
|
+
|
|
363
|
+
## 9. Mid-flight ad-hoc insertion
|
|
364
|
+
|
|
365
|
+
Operator/orchestrator inserts a quant-researcher advisor while the chain is running:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
sb issue create \
|
|
369
|
+
--in-container chain:tx92.2 \
|
|
370
|
+
--parent iss:tx92.2 \
|
|
371
|
+
--class advisor \
|
|
372
|
+
--role quant-researcher \
|
|
373
|
+
--title "Research CME fractional rounding convention for Treasury futures" \
|
|
374
|
+
--problem "Reviewer needs external confirmation of rounding policy before PASS" \
|
|
375
|
+
--scope analytics/outright/calculation.py \
|
|
376
|
+
--input iss:tx92.2.3 \
|
|
377
|
+
--input msg:chain:tx92.2/142 \
|
|
378
|
+
--output "Evidence table with source URLs and recommended test vectors" \
|
|
379
|
+
--rel informs:iss:tx92.2 \
|
|
380
|
+
--rel spawned_by:pulse:reviewer-needs-methodology-abc \
|
|
381
|
+
--dispatch
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
If it must block reviewer PASS:
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
sb issue rel add iss:tx92.2.6 iss:tx92.2.8 --type requires
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Or during creation, with a directional helper:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
sb issue create ... --blocks iss:tx92.2.6 --dispatch
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
`--blocks X` means the new issue blocks X. Keep this directional convention consistent.
|
|
397
|
+
|
|
398
|
+
## 10. Pulse-driven insertion
|
|
399
|
+
|
|
400
|
+
A reviewer posts a structured channel verdict:
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"kind": "verdict",
|
|
405
|
+
"verdict": "PARTIAL",
|
|
406
|
+
"target_issue": "iss:tx92.2",
|
|
407
|
+
"tags": ["needs_external_methodology"],
|
|
408
|
+
"summary": "Rounding policy needs CME citation before PASS."
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
The channel after-hook emits an idempotent pulse:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"kind": "trigger",
|
|
417
|
+
"key": "reviewer:iss:tx92.2.6:needs_external_methodology",
|
|
418
|
+
"body": {
|
|
419
|
+
"action": "insert_step",
|
|
420
|
+
"container_id": "chain:tx92.2",
|
|
421
|
+
"class": "advisor",
|
|
422
|
+
"role": "quant-researcher",
|
|
423
|
+
"blocks": "iss:tx92.2.6",
|
|
424
|
+
"reason_ref": "msg:chain:tx92.2/142"
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Substrate deduplicates by key. Replay does not create duplicate advisors.
|
|
430
|
+
|
|
431
|
+
The workflow engine consumes the pulse, materializes the advisor step issue, wires the blocking edge, and dispatches if policy allows.
|
|
432
|
+
|
|
433
|
+
## 11. Channels and issue contracts together
|
|
434
|
+
|
|
435
|
+
Channels and issues are used at the same time.
|
|
436
|
+
|
|
437
|
+
- The issue contract is durable input: what the participant is supposed to do.
|
|
438
|
+
- The channel is live coordination: findings, verdicts, steer, proposals, system messages.
|
|
439
|
+
- Evidence is durable output: what the participant produced.
|
|
440
|
+
|
|
441
|
+
When a specialist job is spawned from a step issue, it becomes a participant in the container's channel.
|
|
442
|
+
|
|
443
|
+
```text
|
|
444
|
+
participant key: iss:tx92.2.5/code-sanity/job:abc123
|
|
445
|
+
issue_id: iss:tx92.2.5
|
|
446
|
+
channel_id: chain:tx92.2
|
|
447
|
+
subscribes: steer:me, system.*, verdict:me, finding:scope-overlap
|
|
448
|
+
emits: finding, verdict, note
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Do not copy every channel message into issue notes. Issues link to important channel messages through evidence refs:
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
verdict evidence -> msg:chain:tx92.2/142
|
|
455
|
+
finding evidence -> msg:chain:tx92.2/139
|
|
456
|
+
test evidence -> result:...
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## 12. Concrete design patch needed in `substrate.md`
|
|
460
|
+
|
|
461
|
+
Add a section near workflow resolution:
|
|
462
|
+
|
|
463
|
+
> Workflow steps are issue-backed contracts. When a workflow resolves, substrate materializes each executable step as a child issue contract inside the container. These step issues are durable prompts, not global backlog tasks. They are filtered out of global issue lists by default and shown in container views. Every specialist job is spawned from one step issue and joined to the container channel. The channel handles live coordination; the step issue stores mandate/prompt; evidence stores outputs and verdicts.
|
|
464
|
+
|
|
465
|
+
This removes the current ambiguity around code-sanity/reviewer beads while preserving the useful property that every specialist run has a durable contract.
|
|
466
|
+
|
|
467
|
+
## 13. Cross-document consistency — substrate.md vs channels.md
|
|
468
|
+
|
|
469
|
+
Three places where the two documents disagree. Each must resolve in one direction; otherwise the SDK story splits.
|
|
470
|
+
|
|
471
|
+
### 13.1 Cross-container channels
|
|
472
|
+
|
|
473
|
+
- substrate.md §4.2 / §14 open-Q #8: *"Peer node coordinators collaborate via cross-container channels."*
|
|
474
|
+
- channels.md §11 Out of scope across all versions: *"Cross-channel messaging (epic-level coordinator watching N chain channels). Deferred indefinitely."*
|
|
475
|
+
|
|
476
|
+
**Resolution: channels stay container-scoped per channels.md. Coordinator-to-coordinator collaboration is pulse-based, not channel-based.** A peer node emits a pulse on a documented key (e.g. `node:research:finding:<hash>`); the receiving node has a trigger that wakes on that pattern. This reuses the pulse primitive (substrate §2.3, §5.8) and does not extend the channel primitive. The "cross-container channels" phrase in substrate.md should be rewritten as "cross-container pulses" everywhere it appears.
|
|
477
|
+
|
|
478
|
+
### 13.2 External connectors (Discord, Gmail) and the north-star
|
|
479
|
+
|
|
480
|
+
- substrate.md §14.1: *"connectors (Discord, Gmail) that have clear `emit pulse` + SDK access."*
|
|
481
|
+
- channels.md §11 Out of scope: *"Bidirectional integration with external systems (Slack, GitHub comments). Channels are internal."*
|
|
482
|
+
|
|
483
|
+
**Resolution: external connectors are emitters and substrate-API consumers, never channel members.** The flow:
|
|
484
|
+
|
|
485
|
+
```text
|
|
486
|
+
Discord webhook → connector emits pulse to substrate (key: discord:msg:<id>)
|
|
487
|
+
→ pulse opens or wakes a container
|
|
488
|
+
container produces output → connector reads via §17 Change-tracking face (server-streaming)
|
|
489
|
+
→ connector posts back via Discord's own API
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Connectors get pipeline-building power without breaking "channels are internal." channels.md does not need amending; substrate.md §14.1 should be clarified to say connectors talk *to substrate* (pulse + Change-tracking), not *to channels*.
|
|
493
|
+
|
|
494
|
+
### 13.3 Ad-hoc `pair:` channels have no container
|
|
495
|
+
|
|
496
|
+
channels.md §5.1 lists `pair:<uuid>` as a channel kind for ad-hoc pair-talk with no node config. substrate.md's five container kinds (§4) do not include `pair`. This is fine but must be **explicit**: not every channel needs a container. An ad-hoc `pair:` channel is a channels-domain entity with no substrate-side container — channels has primitives that substrate does not (and that's the point of channels being a standalone package per substrate.md §13.4).
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## 14. The SDK surface — collapsing §2.4 into one coherent picture
|
|
501
|
+
|
|
502
|
+
substrate.md §2.4 enumerates four heterogeneous items (participant definition / pulse / channel client / command surface). They are not on the same axis. Restructure as **two wire surfaces, one reference client, N declarative schemas, one credential model**:
|
|
503
|
+
|
|
504
|
+
| Layer | What it is | Owned by |
|
|
505
|
+
|---|---|---|
|
|
506
|
+
| Wire — substrate | §17 three faces: Query, Change-tracking, Command (Unix socket) | substrate package |
|
|
507
|
+
| Wire — channels | `post / readSince / markSeen / capture` + subscribe (Unix socket) | channels package |
|
|
508
|
+
| Pulse | wire-level data type with idempotency key; submitted via substrate's Command face | substrate (table + Command) |
|
|
509
|
+
| Reference client library | TS client wrapping both wires; the typical SDK consumer imports this | core (depends on substrate + channels) |
|
|
510
|
+
| Participant definition | declarative JSON: `.specialist.json`, `.script.json`, `.service.json` | per-kind config |
|
|
511
|
+
| Emitter registration | credential + capability scope for any process allowed to call substrate's emit-pulse Command | substrate |
|
|
512
|
+
|
|
513
|
+
"Building a new actor" then means one of three things — be explicit which:
|
|
514
|
+
|
|
515
|
+
- *Ship a participant definition* (JSON) — for actors that live inside containers (bound).
|
|
516
|
+
- *Register as an emitter* (credential + capability) — for actors that only emit pulses (external webhooks, cron sources, foreign daemons).
|
|
517
|
+
- *Both* — for actors that participate AND emit (most specialists, all scripts that react and emit).
|
|
518
|
+
|
|
519
|
+
The SDK is **the client library wrapping both wires + the schemas you submit through them + the credential you register with**. It is not just the "command surface." Stating this once collapses the four-item list into one mental model.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## 15. Bound vs unbound participants
|
|
524
|
+
|
|
525
|
+
Channel subscriptions enter substrate through two distinct paths; both are needed, and the SDK should make them obviously different rather than overload one verb:
|
|
526
|
+
|
|
527
|
+
- **Bound** (in-container). The runtime writes the active-subscription row at spawn-time by resolving the spec template against the container (substrate §7.1). The participant never calls `subscribe()`. Applies to: specialists dispatched into a chain; scripts attached to a container via spec.
|
|
528
|
+
- **Unbound** (cross-container / external / tooling). The participant calls a channels-side subscribe verb explicitly, scoped by a capability declared at registration. Applies to: `sp tail` reading a channel as a human; a dashboard following multiple containers; a connector watching pulse outputs (via the substrate Change-tracking face, not channels).
|
|
529
|
+
|
|
530
|
+
channels.md `ChannelClient` today has `post / readSince / markSeen / capture` — no explicit `subscribe`. That is correct for the bound case (runtime writes the row). The unbound case needs an explicit subscribe verb on the SDK, gated by capability. Add it to channels.md when v3 lands; document the gap until then.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 16. Two activation modes for participants
|
|
535
|
+
|
|
536
|
+
substrate.md §7.1 (channel-reactive) and §6.9 (workflow step) describe two different ways a participant gets activated; today the duality is implicit. For SDK consumers building agent-driven pipelines, a participant definition should declare both:
|
|
537
|
+
|
|
538
|
+
```jsonc
|
|
539
|
+
{
|
|
540
|
+
"kind": "specialist",
|
|
541
|
+
"name": "data-validator",
|
|
542
|
+
|
|
543
|
+
// Reactive mode: wake on channel events
|
|
544
|
+
"channel": {
|
|
545
|
+
"subscribes": ["finding:scope-overlap", "system.*"],
|
|
546
|
+
"emits": ["finding", "verdict"],
|
|
547
|
+
"wakes_on": ["verdict.PARTIAL"]
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
// Workflow-step mode: declare which workflow positions accept me
|
|
551
|
+
"workflow_role": {
|
|
552
|
+
"name": "data-validator",
|
|
553
|
+
"compatible_with": ["quantitative-validation"] // or ["*"]
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
A participant may have only `channel` (purely reactive), only `workflow_role` (only ever dispatched as part of a workflow), or both. The mandatory Layer-2 gates of substrate.md §6.9.3 (code-sanity, obligations-scanner, security-auditor) declare `workflow_role` with `compatible_with: ["*"]` since they overlay every workflow. Today this is implicit in the specialist registry; making it declared is what lets an agent compose a new pipeline without editing config it doesn't understand.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## 17. Pulse delivery for non-coordinator consumers
|
|
563
|
+
|
|
564
|
+
substrate.md §5.8 specifies push to coordinators (*"the daemon delivers the queue to the coordinator"*). For unbound consumers — connectors, dashboards, tooling — delivery happens via the §17.1 Change-tracking face (server-streaming over the same Unix socket), not a separate push API. To make this implementable:
|
|
565
|
+
|
|
566
|
+
- Pulses are rows in a substrate table (`pulses` keyed by idempotency key) — already implied by §5.8 `pulse_dedup`. Make the *table* explicit so connectors know it is queryable and replayable, not just an in-memory queue.
|
|
567
|
+
- The Change-tracking stream emits pulse-arrival events the same way it emits container/issue/plan changes.
|
|
568
|
+
- A connector subscribes to `changesSince(cursor, filter={kind: "pulse", key_prefix: "discord:"})` and processes pulse rows in order. Identical mechanism for every consumer; no per-consumer push channel.
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## 18. Reuse audit — what each new feature should reuse
|
|
573
|
+
|
|
574
|
+
A small table forcing the SDK story to stay coherent. **The substrate already has the primitive; the SDK exposes it; new use cases compose existing primitives, they do not extend the SDK.**
|
|
575
|
+
|
|
576
|
+
| New need | Reuse, don't invent |
|
|
577
|
+
|---|---|
|
|
578
|
+
| Coordinator-to-coordinator collaboration (§13.1) | pulse + trigger, not a "cross-container channel" |
|
|
579
|
+
| External connector input (Discord/Gmail in) (§13.2) | pulse with idempotency key, not a "webhook channel kind" |
|
|
580
|
+
| External connector output (Discord/Gmail out) | §17 Change-tracking stream, not a new push API |
|
|
581
|
+
| Unbound human/tooling readers (`sp tail`, dashboard) | §17 Change-tracking stream + unbound subscribe verb (§15) |
|
|
582
|
+
| Workflow gate auto-insertion (substrate §6.9.5) | pulse handler under the §5.10 non-progress counter, not new daemon code |
|
|
583
|
+
| Reviewer findings → next issue's context (§11 above) | dual-write to substrate `evidence` (§6.8), not channel re-read |
|
|
584
|
+
| Identity revocation mid-channel | `system.epoch_bump` (channels.md §10.1), not a new authority message |
|
|
585
|
+
| Coordinator state across kill/respawn (substrate §5.9) | journal in substrate, not channel re-replay |
|
|
586
|
+
| Pulse replay / connector resume after disconnect | `changesSince(cursor)` against pulses table — same cursor model as everything else |
|
|
587
|
+
| Workflow step "ready" detection | persisted evidence on the previous step's issue — never live channel scrape (substrate §3) |
|
|
588
|
+
|
|
589
|
+
If a proposed feature does not fit any row here, the question is not "what new SDK surface do we add" but "which existing primitive is the right composition" — and only after that exhausts itself does the SDK gain a new verb.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## 19. Pi turn alignment — decided
|
|
594
|
+
|
|
595
|
+
Verified against `pi/pi-rpc.md` and `src/specialist/{runner.ts,supervisor.ts}`. Pi already has the structure substrate §3 / §6.9 assumed: substrate aligns to it, does not invent a parallel tick.
|
|
596
|
+
|
|
597
|
+
**Pi's native primitives (from pi-rpc.md §Events and §Commands):**
|
|
598
|
+
|
|
599
|
+
| Pi primitive | What it gives substrate |
|
|
600
|
+
|---|---|
|
|
601
|
+
| `turn_start` / `turn_end` events on stdout | The natural per-participant beat. One turn = one assistant response + its tool calls. |
|
|
602
|
+
| `steer` command | Queues a message *after current turn finishes its tool calls, before next LLM call*. Idempotent injection point — exactly where a channel after-hook side-effect belongs. |
|
|
603
|
+
| `follow_up` command | Queues a message delivered only when the agent is fully quiescent. Maps to "resume from `waiting` after `agent_end`." |
|
|
604
|
+
| `agent_end` event | The quiescence barrier. In keep-alive mode the session stays open and becomes resumable — substrate's `waiting` work_state has a 1:1 mapping. |
|
|
605
|
+
| `auto_compaction_start/end` events | The `specialist.compacted` pulse of substrate §5.8 is literally the receipt of `auto_compaction_end`. |
|
|
606
|
+
| `auto_retry_start/end` events | Transient failure (substrate §5.10) is exactly the pi auto-retry envelope. Substrate's transient class is *whatever pi auto-retried*. |
|
|
607
|
+
|
|
608
|
+
**Specialists' existing wiring (already in supervisor.ts):**
|
|
609
|
+
|
|
610
|
+
- `supervisor.ts:1671` already routes `turn_start` events through the timeline.
|
|
611
|
+
- `supervisor.ts:1658` already detects `agent_end` in keep-alive mode and flips to `waiting`.
|
|
612
|
+
- `supervisor.ts:1882` already counts `auto_compactions` per run.
|
|
613
|
+
- `runner.ts:1361` already exposes `session.steer(msg)` as a registered callback per job.
|
|
614
|
+
|
|
615
|
+
**Design decision for substrate:**
|
|
616
|
+
|
|
617
|
+
- **No separate container "tick" clock.** The container's reducer fires *event-driven* on three triggers:
|
|
618
|
+
1. A member participant's `turn_end` (live activity inside the container)
|
|
619
|
+
2. A pulse arrival (external trigger)
|
|
620
|
+
3. An explicit `sb` command (operator action)
|
|
621
|
+
- **Resume injection uses `session.steer()`.** When the channel after-hook (§7 channels.md) decides a participant should be resumed mid-flight (verdict, redirect, peer steer), it calls the steer function pi already exposes — guaranteed delivery before the next LLM call.
|
|
622
|
+
- **Substrate `waiting` = pi keep-alive after first `agent_end`.** No new state; reuse what supervisor.ts already implements.
|
|
623
|
+
- **Workflow step advancement.** When a step's participant emits `agent_end` (not just `turn_end`), substrate's daemon reads the persisted evidence on the step issue, evaluates the workflow's completeness predicate, and either fires the next step's spawn or escalates per §5.10.
|
|
624
|
+
|
|
625
|
+
**Patch to substrate.md §3 and §6.9.2:** replace "the daemon advances containers" with "the daemon advances containers on member `agent_end` events and pulse arrivals — never on a wall-clock tick." Remove §14.1's open question about the turn concept as resolved.
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## 20. Daemon-observes model — decided
|
|
630
|
+
|
|
631
|
+
substrate.md §5.8 / §5.10 / §6.9.2 assume the daemon can observe: process termination, exit code, compaction signals, non-progress counters. supervisor.ts already plumbs every one of these through callbacks. The daemon does not need new hooks; it needs to be a *consumer* of the event bus the supervisor already writes.
|
|
632
|
+
|
|
633
|
+
Mapping from substrate §5.8 lifecycle pulses to existing pi/supervisor events:
|
|
634
|
+
|
|
635
|
+
| substrate §5.8 pulse | source today |
|
|
636
|
+
|---|---|
|
|
637
|
+
| `specialist.spawned` | `session.start()` resolves (runner.ts:1356) |
|
|
638
|
+
| `specialist.turn-complete` | onEvent('turn_end') (supervisor.ts:1671) |
|
|
639
|
+
| `specialist.waiting` | onResumeReady callback (supervisor.ts:1974) |
|
|
640
|
+
| `specialist.compacted` | onEvent('auto_compaction_end') (supervisor.ts:1882) |
|
|
641
|
+
| `specialist.stopped` | session close / non-keep-alive `agent_end` |
|
|
642
|
+
| `specialist.context-threshold` | derive from cumulative compaction count + token usage |
|
|
643
|
+
|
|
644
|
+
**Design decision:** the substrate daemon subscribes to the observability event stream (the same one `sp log` already reads — runtime/control/error rows in `observability.db`). Lifecycle pulses are emitted as a side effect of writing those rows. No new instrumentation; substrate becomes a second reader of the existing telemetry, alongside `sp log`.
|
|
645
|
+
|
|
646
|
+
This also closes substrate.md §14.1 "Does the daemon-observes model fit how processes are actually supervised today?" — yes, exactly.
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## 21. Failure classification — decided against transcripts
|
|
651
|
+
|
|
652
|
+
The transient/semantic split of substrate.md §5.10 holds against observed failures in `.xtrm/reports/`. No third class emerges. The boundary is sharper than the design states:
|
|
653
|
+
|
|
654
|
+
| Observed failure | Class | Why |
|
|
655
|
+
|---|---|---|
|
|
656
|
+
| Pi `overloaded_error`, 5xx, rate-limit, OOM | transient | Pi already classifies via `auto_retry_*`. Substrate's transient class = "pi auto-retried." |
|
|
657
|
+
| Executor self-reported `tests_pass: false` when tests pass; executor `--no-verify` + stale file mix (mercury 2026-05-25) | semantic | Caught by reviewer/code-sanity FAIL with non-progress. The approach was wrong, not the execution. |
|
|
658
|
+
| Reviewer PARTIAL on same surface 3 cycles in a row | semantic | The §5.10 `semantic_after` counter detector. |
|
|
659
|
+
| Indefinite advance-and-regress oscillation (would consume budget) | semantic | The §5.10 `hard_cap` backstop. Not observed in transcripts yet, but the mechanism is needed. |
|
|
660
|
+
| Stale-base dispatch causing `--force-stale-base` (mercury 2026-05-25) | **precondition violation, not failure** | Caught by the Git State Precondition gate. This is *prevention*, not *recovery*. Belongs to a separate concern from §5.10. |
|
|
661
|
+
|
|
662
|
+
**Design decision:** keep the binary classification. Add an explicit *third concept* — **precondition violation** — that is NOT a failure class but a pre-dispatch gate. The Git State Precondition (CLAUDE.md gotchas, mercury report) is one example; a future "dependency-not-merged" check is another. The pattern: preconditions are checked at dispatch time and either pass or refuse-to-dispatch with an escalation event — they never enter the run-then-fail loop the §5.10 counters watch. This separates *we should not have started* from *we started and stumbled* cleanly.
|
|
663
|
+
|
|
664
|
+
Updates to substrate.md §5.10 and §6.4 "dispatch gate":
|
|
665
|
+
|
|
666
|
+
- §6.4 documents the dispatch-time precondition mechanism alongside the Stage-1 validator (today only validates the contract; should also validate the runtime environment).
|
|
667
|
+
- §5.10 stays binary (transient/semantic) and explicitly says preconditions are §6.4's responsibility, not §5.10's.
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## 22. Per-issue close flow — replaces `bd close` + memory-ack + commit-gate + Stop hook
|
|
672
|
+
|
|
673
|
+
substrate.md §14 open-Q #7 is the main remaining design item. Decided here.
|
|
674
|
+
|
|
675
|
+
### 22.1 Core principle: close is a derivation, not an imperative
|
|
676
|
+
|
|
677
|
+
bd treats close as an *action* on an issue. The procedural shims (memory-ack hook, commit-gate hook, Stop hook) exist because bd has no model of *what makes a close valid* — they try to enforce discipline from the outside.
|
|
678
|
+
|
|
679
|
+
Substrate has the model. An issue closes when:
|
|
680
|
+
|
|
681
|
+
1. its **evidence** satisfies its **acceptance criteria** (contract fields, §6.1),
|
|
682
|
+
2. its **container state** permits termination of that issue's role in the container, and
|
|
683
|
+
3. a **close reason** is recorded, drawn from a closed enum.
|
|
684
|
+
|
|
685
|
+
The operator-facing `sb issue close <id>` evaluates eligibility, persists the close, and emits the relevant pulse. The procedural shims disappear because the model makes them unnecessary (§22.5).
|
|
686
|
+
|
|
687
|
+
### 22.2 Close as transactional consequence of container merge — the common path
|
|
688
|
+
|
|
689
|
+
In the normal case nobody runs `sb issue close` per issue. The flow is:
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
participant emits evidence (verdict PASS, diff committed, test pass)
|
|
693
|
+
↓
|
|
694
|
+
substrate after-hook: does this evidence satisfy issue I's acceptance predicate?
|
|
695
|
+
↓
|
|
696
|
+
yes → issue I marked `close_ready` (a state, not yet closed)
|
|
697
|
+
↓
|
|
698
|
+
container reducer: are all member issues `close_ready` AND all workflow steps complete?
|
|
699
|
+
↓
|
|
700
|
+
yes → container advances to `ready`
|
|
701
|
+
↓
|
|
702
|
+
operator runs `sb container merge` (or auto-merge per policy)
|
|
703
|
+
↓
|
|
704
|
+
substrate transactionally:
|
|
705
|
+
- closes every member issue with close_reason matching member's outcome
|
|
706
|
+
- closes the container with close_reason=merged
|
|
707
|
+
- emits closed pulses for memory/dashboard consumers
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
`close_ready` is the per-issue analog of the container's `ready` state — *eligible to close but not yet closed*. The container's merge is the close event for every member at once.
|
|
711
|
+
|
|
712
|
+
This makes "close the issue" almost always a side-effect of "merge the container," and removes the per-issue close ceremony bd requires.
|
|
713
|
+
|
|
714
|
+
### 22.3 Eligibility table — when can an issue close
|
|
715
|
+
|
|
716
|
+
Read by `sb issue close` to decide allow/refuse. Same predicate used by the container reducer to derive `close_ready`.
|
|
717
|
+
|
|
718
|
+
| Issue class (§5) | Container kind | Container state | Eligible? | Allowed `close_reason` |
|
|
719
|
+
|---|---|---|---|---|
|
|
720
|
+
| `root` (single-issue chain) | `chain` | `ready` | yes — closes on container merge | `merged` |
|
|
721
|
+
| `root` (epic/wave member) | `epic`/`wave` | parent at `ready` | yes — closes on epic merge | `merged-as-part-of-epic` |
|
|
722
|
+
| `step` (workflow step) | `chain` | `working`/`converging` | yes if step's acceptance evidence present AND no downstream step requires unfinished output | `step-complete` |
|
|
723
|
+
| `gate` (code-sanity, reviewer, obligations-scanner) | `chain` | `working` | yes when verdict evidence written | `gate-passed`, `gate-failed` |
|
|
724
|
+
| `advisor` (explorer, methodologist, researcher) | `chain` | `working` | yes when advisory output evidence present | `advisory-complete` |
|
|
725
|
+
| `followup` (`discovered_from`, non-blocking) | any | any (incl. live) | yes any time — non-blocking by definition | `done`, `abandoned`, `superseded` |
|
|
726
|
+
| any | any | `escalated` | yes only with `--reason=abandoned` | `abandoned` (operator override) |
|
|
727
|
+
| any | any | `closed:failed` | auto-closed by cascade (§22.4) | `failed-with-container` |
|
|
728
|
+
| any | any | `closed:abandoned` | auto-closed by cascade | `abandoned-with-container` |
|
|
729
|
+
| any | any | any | yes with `--force` (operator override) | logs escalation event, requires reason |
|
|
730
|
+
|
|
731
|
+
If `sb issue close <id>` is called and eligibility is *blocked*, the response is a structured refusal:
|
|
732
|
+
|
|
733
|
+
```jsonc
|
|
734
|
+
{
|
|
735
|
+
"ok": false,
|
|
736
|
+
"error_code": "close_blocked",
|
|
737
|
+
"blocked_by": [
|
|
738
|
+
"container chain:7f3a is in 'working' state; step issues close on workflow completion",
|
|
739
|
+
"issue iss-7f3a-005 has no verdict evidence yet"
|
|
740
|
+
],
|
|
741
|
+
"next_safe_action": "wait_for_evidence | force_close | abandon_container"
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
Mirrors the channels.md §10.2 error envelope shape — same structured-refusal pattern across the SDK.
|
|
746
|
+
|
|
747
|
+
### 22.4 Container-failed cascade
|
|
748
|
+
|
|
749
|
+
When a container reaches `closed:failed` or `closed:abandoned` before all members are `done`:
|
|
750
|
+
|
|
751
|
+
- Non-done member issues auto-close with reason `failed-with-container` or `abandoned-with-container`.
|
|
752
|
+
- Their evidence is preserved per §5.10 ("never destroy work on failure").
|
|
753
|
+
- The §5.10 failure memory distills from the gate verdicts present at the time of failure.
|
|
754
|
+
- A re-seeded container can re-create issues that `supersedes` the old ones — the existing edge type (§6 in this review) carries the lifecycle replacement.
|
|
755
|
+
|
|
756
|
+
The cascade is itself a pulse handler (substrate §5.8 / §5.10 universal mechanism), not new code: container terminal pulse → cascade handler → batched close of members.
|
|
757
|
+
|
|
758
|
+
### 22.5 What replaces the three current shim gates
|
|
759
|
+
|
|
760
|
+
| Today's gate | What it was for | Replacement in substrate | Why the gate disappears |
|
|
761
|
+
|---|---|---|---|
|
|
762
|
+
| **memory-ack** (per-issue KV ack before close) | "did you save the lesson learned?" | Substrate emits the memory-distillation pulse automatically on `failed-semantic` close (§5.10). For success closes, the seed's memory-curator (§5.2) pulled relevant memories at start; there is no end-of-issue lesson to ack. | The pulse fires on the relevant close_reason; the operator is no longer the discipline-enforcer because the model defines when memory must be distilled. |
|
|
763
|
+
| **commit-gate** (`bd close` blocked if claim is open & no commit yet) | "did you commit before closing?" | An issue cannot reach `close_ready` until its `diff` evidence is present (§6.8 dual-write). The evidence ref binds to a commit SHA or branch ref. | The dual-write IS the commit gate — separate check unnecessary. |
|
|
764
|
+
| **Stop hook** (session-end with unclosed claim is blocked) | "you forgot to close before quitting" | Claims belong to *participants* (jobs), not to sessions. A session ending leaves the participant in `waiting` (pi keep-alive, §19), not the issue in `claimed`. The issue close state is independent of which orchestrator session is alive. | The session/issue coupling that made the hook necessary is removed; substrate has no per-session ownership of issues. |
|
|
765
|
+
|
|
766
|
+
The three procedural gates exist to compensate for missing model in bd. Substrate has the model, so the gates are not ports — they are *deleted*. This is the elegance §13.B of the review aims at: don't migrate procedural compensation into the new system; remove the procedural by making the model carry the constraint.
|
|
767
|
+
|
|
768
|
+
### 22.6 `done` vs `archived` — separation matters
|
|
769
|
+
|
|
770
|
+
substrate.md §6.1 puts both `done` and `archived` in the `work_state` enum without distinguishing. They serve different queries:
|
|
771
|
+
|
|
772
|
+
| State | Meaning | Default visibility | Memory distilled? |
|
|
773
|
+
|---|---|---|---|
|
|
774
|
+
| `done` | Work concluded with positive outcome (`merged`, `step-complete`, `gate-passed`, `advisory-complete`) | shown by `sb issue ls` | yes for relevant categories (e.g. an `advisor` output worth caching) |
|
|
775
|
+
| `archived` | Work concluded but not deserving registry presence (`abandoned`, `failed-with-container`, `superseded`, trivial `followup` skipped) | hidden by default; `sb issue ls --archived` to see | only `failed-semantic` produces lesson memory (§5.10); other archives are silent |
|
|
776
|
+
|
|
777
|
+
`close_reason` deterministically maps to `done` or `archived` — operator doesn't choose, the reason chooses:
|
|
778
|
+
|
|
779
|
+
```
|
|
780
|
+
merged / merged-as-part-of-epic / step-complete / gate-passed /
|
|
781
|
+
advisory-complete / done → done
|
|
782
|
+
failed-transient / failed-semantic / failed-with-container /
|
|
783
|
+
abandoned / abandoned-with-container / superseded → archived
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### 22.7 Schema additions to substrate.md §6.1
|
|
787
|
+
|
|
788
|
+
```jsonc
|
|
789
|
+
{
|
|
790
|
+
// existing fields...
|
|
791
|
+
"work_state": "draft|ready|claimed|running|waiting|reviewing|blocked|close_ready|done|archived",
|
|
792
|
+
// ^^^^^^^^^^^^ new — "all evidence in, awaiting container close"
|
|
793
|
+
|
|
794
|
+
"close_state": {
|
|
795
|
+
"eligibility": "blocked|close_ready|forced", // computed, denormalized for queries
|
|
796
|
+
"blocked_by": ["string", "..."], // populated when eligibility=blocked
|
|
797
|
+
"close_ready_at_ms": 0, // first time predicates were satisfied
|
|
798
|
+
"closed_at_ms": 0,
|
|
799
|
+
"close_reason": "merged|merged-as-part-of-epic|step-complete|gate-passed|
|
|
800
|
+
gate-failed|advisory-complete|done|
|
|
801
|
+
failed-transient|failed-semantic|failed-with-container|
|
|
802
|
+
abandoned|abandoned-with-container|superseded",
|
|
803
|
+
"closed_by": "container-merge|cascaded-from:<container-id>|operator|workflow|--force"
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
`failed`/`done`/`archived` work_state values become derivations from `close_reason` (per the §22.6 table). One source of truth — `close_reason` — drives both `work_state` and visibility.
|
|
809
|
+
|
|
810
|
+
### 22.8 Operator command surface
|
|
811
|
+
|
|
812
|
+
```bash
|
|
813
|
+
# Normal path — rarely needed because container merge closes members transactionally
|
|
814
|
+
sb issue close <id> # evaluates eligibility; refuses if blocked
|
|
815
|
+
sb issue close <id> --reason advisory-complete --evidence msg:chain:7f3a/142
|
|
816
|
+
|
|
817
|
+
# Operator-driven termination
|
|
818
|
+
sb issue close <id> --reason abandoned # explicit abandon; always eligible
|
|
819
|
+
sb issue close <id> --force --reason <r> # eligibility override; logs escalation event
|
|
820
|
+
|
|
821
|
+
# Archive vs close — single command, reason decides
|
|
822
|
+
sb issue close <id> --reason superseded # auto-categorized as archived (§22.6)
|
|
823
|
+
|
|
824
|
+
# Reopen — only from certain close_reasons
|
|
825
|
+
sb issue reopen <id> # allowed if close_reason in
|
|
826
|
+
# {abandoned, failed-*, superseded}
|
|
827
|
+
# refused for {merged, *-complete, done}
|
|
828
|
+
# — already-shipped work cannot be reopened;
|
|
829
|
+
# create a follow-up instead
|
|
830
|
+
|
|
831
|
+
# Container-driven (the common case — no per-issue command at all)
|
|
832
|
+
sb container merge <chain-id> # closes every member transactionally
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### 22.9 What this preserves from bd that was useful
|
|
836
|
+
|
|
837
|
+
The valuable thing in bd was that every closed issue carried a durable, queryable record of *what was decided and why* — `bd notes` showed the closure context across sessions. Substrate keeps that property, more rigorously:
|
|
838
|
+
|
|
839
|
+
- The `close_reason` is enum-validated (no free prose drift).
|
|
840
|
+
- The `evidence` array (§6.1) is the canonical "what proof closed this" — diff refs, verdict refs, test results, the release checklist — all structured and re-queryable.
|
|
841
|
+
- The container's channel still has the verbose discussion as a stream (§7), reachable via evidence message refs (`msg:chain:7f3a/142`).
|
|
842
|
+
|
|
843
|
+
The substitution is: bd-notes-as-prose → substrate-evidence-as-structured-references. The operator's "what happened on this issue?" query returns linkable evidence, not a chronological text dump.
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## 23. Autonomy gradient — three open questions, one pattern
|
|
848
|
+
|
|
849
|
+
substrate.md §14 #3 (container nesting), #11 (node nesting), #12 (dispatch_mode predicate) are all *autonomy* questions. They share one pattern; documenting it once prevents three inconsistent answers.
|
|
850
|
+
|
|
851
|
+
### 23.1 The pattern
|
|
852
|
+
|
|
853
|
+
Every autonomy decision in substrate has three tiers, not two:
|
|
854
|
+
|
|
855
|
+
```
|
|
856
|
+
allowed within policy ← automatic, no friction
|
|
857
|
+
↓ (exceeds policy)
|
|
858
|
+
escalates to operator ← operator approves per-instance
|
|
859
|
+
↓ (depth/reach beyond escalation)
|
|
860
|
+
hard-blocked / operator-only ← no automatic path reaches here; only `sb` command
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
This is the same shape as substrate.md §5.10 graded escalation (orchestrator → operator) and §5.8 emitter capability (within capability act, beyond escalate). Reuse it for nesting/dispatch decisions too, instead of inventing new tiers.
|
|
864
|
+
|
|
865
|
+
### 23.2 Container nesting (#3) — decided: warn at 2, hard-cap at 4
|
|
866
|
+
|
|
867
|
+
substrate.md §14 #3 picked "soft-cap at 2, deferred exact threshold." Decide the cap now.
|
|
868
|
+
|
|
869
|
+
| Depth | What it looks like | Status |
|
|
870
|
+
|---|---|---|
|
|
871
|
+
| 1 | `chain` (single issue) | normal |
|
|
872
|
+
| 2 | `epic` of chains, `wave` of chains | normal (the common compound case) |
|
|
873
|
+
| 3 | `epic` of `epic`s of chains (e.g. "platform migration" → "backend migration" + "frontend migration" → chains) | **soft-warn** — legitimate but unusual; reducer emits warning, asks operator to confirm or decompose |
|
|
874
|
+
| 4+ | anything deeper | **hard-block** — refused at container open; no auto path reaches here |
|
|
875
|
+
|
|
876
|
+
Why the cap is 4 (not 3 or 5): three is real (rare migration epics), four is *always* decomposition smell or automatic-spawn pathology. Hard-cap at 4 prevents a seed-that-opens-an-epic-that-opens-an-epic-that... runaway. Operator can still build depth ≥4 manually by `--force` if there is truly no decomposition path — but the `--force` logs an escalation event, so it cannot happen quietly.
|
|
877
|
+
|
|
878
|
+
Note: depth counts *live* container membership only. The `seed` that opened the root container is in provenance (`opened_by`), not membership — it transformed and closed. Seeds do not count.
|
|
879
|
+
|
|
880
|
+
### 23.3 Node nesting (#11) — decided: 1 via escalation, 2 manual-only, 3+ hard-blocked
|
|
881
|
+
|
|
882
|
+
substrate.md §4.2 / §14 #11: "A node opening a standing sub-node requires escalation; exact depth cap unsettled."
|
|
883
|
+
|
|
884
|
+
Apply the gradient:
|
|
885
|
+
|
|
886
|
+
| Node tree depth | How it gets created | Status |
|
|
887
|
+
|---|---|---|
|
|
888
|
+
| 0 | standalone node (`sb node create`) | normal, requires operator command |
|
|
889
|
+
| 1 (sub-node) | parent node coordinator escalates: "I need a specialized sub-node for X" | escalates to operator, operator approves per-instance via `sb node create --parent <id>` (no auto-spawn) |
|
|
890
|
+
| 2 (sub-sub-node) | operator manually creates with explicit `--parent <level-1-node>` | **operator-only path**; sub-node coordinator cannot escalate for grandchildren |
|
|
891
|
+
| 3+ | — | **hard-blocked** at `sb node create`; refused unconditionally |
|
|
892
|
+
|
|
893
|
+
Asymmetry on purpose: the *automatic* escalation path caps at depth 1, the *manual* path caps at depth 2. A grandchild-node never appears as a side effect of a great-grandparent's autonomy — that is the pathology to prevent. Below the cap, all node creation is *explicit operator action*, never cascading escalation.
|
|
894
|
+
|
|
895
|
+
The hard-block at 3+ is unconditional (no `--force`). If a use case truly needs depth 3, it is a sign the standing-node abstraction is being misused for what should be transient containers — return to design, not override.
|
|
896
|
+
|
|
897
|
+
### 23.4 `dispatch_mode` predicate (#12) — decided: flat default + optional matcher rules
|
|
898
|
+
|
|
899
|
+
substrate.md §5.8: `dispatch_mode: direct | via_seed` per node. Open: should it be a richer predicate?
|
|
900
|
+
|
|
901
|
+
Decision: keep `dispatch_mode` as a flat default *and* allow optional matcher rules that override per-pulse. **Reuse the existing matcher syntax** of seed `invite_when` (§5.2) and workflow `applies_when` (§6.9.3) — one matcher language across the system, no new DSL.
|
|
902
|
+
|
|
903
|
+
```yaml
|
|
904
|
+
# Node coordinator config
|
|
905
|
+
dispatch_mode:
|
|
906
|
+
default: via_seed # the safe default — discover shape first
|
|
907
|
+
|
|
908
|
+
rules:
|
|
909
|
+
# Known-shape pulses bypass the seed
|
|
910
|
+
- when:
|
|
911
|
+
pulse_kind: github_pr_opened
|
|
912
|
+
labels_any: [tiny, doc-only, dependency-bump]
|
|
913
|
+
then: direct
|
|
914
|
+
|
|
915
|
+
- when:
|
|
916
|
+
pulse_kind: alert
|
|
917
|
+
severity: low
|
|
918
|
+
then: direct
|
|
919
|
+
|
|
920
|
+
# Explicit force-via-seed for known-ambiguous shapes
|
|
921
|
+
- when:
|
|
922
|
+
pulse_kind: user_request
|
|
923
|
+
scope_lines_estimated_gte: 50
|
|
924
|
+
then: via_seed
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
Resolution order: rules in declared order, first match wins, fallback to `default`. The flat field is the common case (one node, one shape); the rule list is the escape valve for nodes that handle mixed shapes.
|
|
928
|
+
|
|
929
|
+
This is **not** new judgment going into the coordinator. The coordinator was already doing this judgment implicitly per substrate.md §5.8 ("the coordinator does semantic scheduling"). The matcher rules just *make it visible and replayable* — same purpose as the workflow-step preheat of §6.9.2: write the decision down before it happens so it can be audited.
|
|
930
|
+
|
|
931
|
+
### 23.5 What's reused vs new
|
|
932
|
+
|
|
933
|
+
| Decision | Mechanism reused | New surface |
|
|
934
|
+
|---|---|---|
|
|
935
|
+
| Container nesting cap | container open command + reducer warning (§3) | one config: `max_container_depth = 4`, soft-warn threshold = 2 |
|
|
936
|
+
| Node nesting cap | `sb node create` command + capability check | one config: `max_node_depth = 2`, escalation-spawnable depth = 1 |
|
|
937
|
+
| `dispatch_mode` rules | seed `invite_when` / workflow `applies_when` matcher (§5.2, §6.9.3) | optional `rules` array on node config; resolution order = declared |
|
|
938
|
+
|
|
939
|
+
All three additions are *config + reuse of existing primitives*. No new daemon machinery, no new SDK verbs. The autonomy gradient pattern (§23.1) is what unifies them; once stated, the three open questions become one consistent answer applied three times.
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
## 24. Knowledge scope — three open questions, one pattern
|
|
944
|
+
|
|
945
|
+
substrate.md §14 #5 (issue-local rule conflict), #6 (session vs seed curator), #10 (memory pruning/promotion/identity scope) are all *scope of knowledge* questions. The unifying principle, extending substrate.md §10's "levels are queries not fields":
|
|
946
|
+
|
|
947
|
+
> **Substrate stores facts with metadata; queries reconstruct the relevant slice. Rules, curation, and pruning are all queries with different filters — not different stores or new entities.**
|
|
948
|
+
|
|
949
|
+
Once this principle is named, the three open questions resolve without inventing new mechanisms.
|
|
950
|
+
|
|
951
|
+
### 24.1 Rule conflict (#5) — decided: rules govern *behavior*, contract governs *evaluation*, evidence governs *facts* — they don't collide
|
|
952
|
+
|
|
953
|
+
substrate.md §14 #5: *"if a reviewer is jobbed onto issue A but reads diff from issue B, whose rules apply? Probably the reviewer's own issue (A)."*
|
|
954
|
+
|
|
955
|
+
The "conflict" only exists if you collapse three different surfaces into one. They are separate:
|
|
956
|
+
|
|
957
|
+
| Surface | Source | Governs |
|
|
958
|
+
|---|---|---|
|
|
959
|
+
| `issue_local_rules` | the issue the specialist is spawned from (owning issue) | the specialist's *own behavior* (what files I may edit, what tools I may use, scope I must not exceed) |
|
|
960
|
+
| `contract` (problem/scope/acceptance/non_goals) | the issue under review/work (target issue) | what *outcome* counts as success — the rubric the specialist evaluates against |
|
|
961
|
+
| `evidence` (diff/verdict/result) | any referenced issue | factual input — what was already produced, immutable, not a rule |
|
|
962
|
+
|
|
963
|
+
A reviewer jobbed onto issue A reviewing diff from issue B:
|
|
964
|
+
|
|
965
|
+
- Follows **A's** `issue_local_rules` for *its own behavior* (e.g., "no edits; produce JSON verdict").
|
|
966
|
+
- Evaluates the diff against **B's** `contract` (B's acceptance/scope/non_goals — that's what the diff was produced to satisfy).
|
|
967
|
+
- Reads **B's** `evidence` as factual input (the diff is the artifact under review).
|
|
968
|
+
|
|
969
|
+
No conflict possible. A's scope rule does not exclude the diff's files because A's scope governs *A's edits*, not what A may *read*. A "rule conflict" only arises if specialists try to apply *another issue's* rules to their own behavior — which is exactly what the model forbids.
|
|
970
|
+
|
|
971
|
+
**Patch to substrate.md §6.1 / §6.5:** explicitly document that `issue_local_rules` apply only to the spawning specialist's own actions, never to referenced issues' artifacts. Reviewing a diff is not "subject to the reviewer-issue's rules constraining the diff"; it is "subject to the reviewer-issue's rules constraining the reviewer's behavior."
|
|
972
|
+
|
|
973
|
+
### 24.2 Session-level memory curator (#6) — decided: no new entity, the orchestrator is a participant
|
|
974
|
+
|
|
975
|
+
substrate.md §14 #6: *"do we keep a session-level curator for 'things you should know walking in,' or is seed enough?"*
|
|
976
|
+
|
|
977
|
+
There are two cold-start cases to cover:
|
|
978
|
+
|
|
979
|
+
- **Operator opens an interactive session.** They want context: recent work, ongoing decisions, project-wide invariants. This is an explicit *query*, not a curator running. Reuse: `sb context project <name>` and `sb memory query` (the same Query face of §17.1, just with filters).
|
|
980
|
+
- **An orchestrator (LLM agent) starts cold.** The orchestrator is itself a participant — running inside an "orchestrator session container" (a degenerate node, or a transient container at the operator's request). Once it is a participant, it gets context the *standard* way: the seed/curator (§5.2) for its container assembles the pack and feeds it in. No special "session-level" curator.
|
|
981
|
+
|
|
982
|
+
So:
|
|
983
|
+
- **Cold-start before any container** → CLI query, no curator running, no LLM cost.
|
|
984
|
+
- **Inside a container (including the orchestrator's own)** → the seed-stage memory-curator (§5.2) is the only curator. One entity, one entry point.
|
|
985
|
+
|
|
986
|
+
This collapses two would-be entities into one. **Reuse, not duplicate.** Same primitive (memory query) at two entry points (CLI on demand, advisor inside seed).
|
|
987
|
+
|
|
988
|
+
**Patch to substrate.md §10.2:** remove the "session-level curator" as an open question; clarify that the curator is *exclusively* a seed-time advisor and that cold-start contextualization happens via CLI query against the same store.
|
|
989
|
+
|
|
990
|
+
### 24.3 Memory pruning / promotion / identity scope (#10) — decided
|
|
991
|
+
|
|
992
|
+
substrate.md §10 already states: "herd / workgroup / identity are *queries*, not fields." Carry that through to the four sub-questions of §14 #10.
|
|
993
|
+
|
|
994
|
+
#### Pruning policy
|
|
995
|
+
|
|
996
|
+
There is no "per-tier" pruning, because tiers are queries. Pruning runs on *metadata fields actually stored*. Three rules:
|
|
997
|
+
|
|
998
|
+
| Trigger | Memories affected | Action |
|
|
999
|
+
|---|---|---|
|
|
1000
|
+
| `sb memory forget <id>` | one | hard-delete |
|
|
1001
|
+
| Container terminal (`merged` / `abandoned`) | memories with `in_container=<id>` | retention per §10.3 — kept if `type ∈ {failure-lesson, decision, advisor-output}`, else demoted (see below) |
|
|
1002
|
+
| Project retired (`sb project retire`) | memories with `in_project=<name>` | metadata-tagged `archived_at_ms`; queries default-hide unless `--archived` |
|
|
1003
|
+
|
|
1004
|
+
No memory is *deleted* by retirement of a parent (node, container, project) — they are *demoted* by metadata change so the default queries stop returning them. Hard-delete is operator-explicit.
|
|
1005
|
+
|
|
1006
|
+
#### Promotion predicate (workgroup → herd)
|
|
1007
|
+
|
|
1008
|
+
Memories are not stored at a tier; they get *queried* as workgroup or herd based on metadata. "Promotion" = changing metadata so the herd-level query starts catching the memory. Trigger:
|
|
1009
|
+
|
|
1010
|
+
- **Automatic, counter-based.** Substrate tracks `cross_workgroup_use_count` per memory: how many distinct containers' participants pulled it via curator queries. When the counter ≥ **3 distinct workgroups** in the last 90 days, the memory is auto-promoted: its `visibility` metadata flips so the herd-level query catches it. Threshold of 3 is the smallest convincing sample of cross-cutting relevance; ≥ 90-day window prevents one busy week from over-promoting.
|
|
1011
|
+
- **Manual override.** `sb memory promote <id>` and `sb memory demote <id>` for operator judgment calls.
|
|
1012
|
+
|
|
1013
|
+
Promotion is logged as a memory event (memories about memories — same store) so the audit trail survives.
|
|
1014
|
+
|
|
1015
|
+
#### Identity scope — per-role-per-project, not per-role-global
|
|
1016
|
+
|
|
1017
|
+
The §14 #10 sub-question: *"whether identity is per-role-global or per-role-per-project."*
|
|
1018
|
+
|
|
1019
|
+
Decided: **per-role-per-project.** A `quant-methodologist`'s memories about CME treasury tick grids belong to *mercury-market-data*, not to the `quant-methodologist` role in general. Cross-project identity creates:
|
|
1020
|
+
- Scope confusion (rules from a different project may not apply)
|
|
1021
|
+
- Surface leakage (a memory referencing internal terms from project X arriving at project Y is noise)
|
|
1022
|
+
- Trust collapse (a "this approach failed" memory from project X may have failed *because of X-specific reasons* not present in Y)
|
|
1023
|
+
|
|
1024
|
+
Implementation: every memory carries `created_by_role` AND `in_project` metadata. The identity query is `created_by_role=X AND in_project=Y`. Cross-project lookups are an explicit operator action (`sb memory query --role X --any-project`).
|
|
1025
|
+
|
|
1026
|
+
#### Provenance vs node retirement
|
|
1027
|
+
|
|
1028
|
+
When a node retires, memories created by its participants do not disappear — substrate §5.10's "never destroy work on failure" generalizes: never destroy memory on retirement either.
|
|
1029
|
+
|
|
1030
|
+
Mechanism:
|
|
1031
|
+
|
|
1032
|
+
- Memory's `in_container=node:X` metadata is preserved; an `in_container_retired_at_ms` timestamp is added.
|
|
1033
|
+
- The node-scoped query (`in_container=node:X`) returns it only with explicit `--include-retired`.
|
|
1034
|
+
- The herd / identity queries continue unchanged.
|
|
1035
|
+
- The retirement itself becomes a memory event: `{type: lifecycle, body: "node X retired with N decisions, K lessons", refs: [...]}` so the chronology is in the same store.
|
|
1036
|
+
|
|
1037
|
+
This means retiring a node is a *bookkeeping* action, not a destructive one. The same shape as substrate §5.10 `closed:failed` — preserve the evidence, change visibility, move on.
|
|
1038
|
+
|
|
1039
|
+
### 24.4 What this reinforces (cross-back to §18 reuse audit)
|
|
1040
|
+
|
|
1041
|
+
| New need | Reuse, don't invent |
|
|
1042
|
+
|---|---|
|
|
1043
|
+
| Rule conflict resolution (#5) | three existing surfaces (`issue_local_rules`, `contract`, `evidence`) — clarify they govern different things, no new conflict-resolution machinery |
|
|
1044
|
+
| Cold-start context (#6) | CLI Query face (§17.1) for cold-start; seed-time curator (§5.2) for in-container — one store, two entry points |
|
|
1045
|
+
| Tier-aware pruning (#10) | pruning runs on stored metadata fields, not on the queries themselves — pruning machinery already exists per-field |
|
|
1046
|
+
| Memory promotion (#10) | counter on existing memory metadata + curator query trigger — no new promotion engine |
|
|
1047
|
+
| Node-retired memories (#10) | metadata flag + visibility filter — same pattern as `closed:failed` evidence preservation (§5.10) |
|
|
1048
|
+
|
|
1049
|
+
Add the row from §24.2 to §18: "Cold-start orchestrator context → CLI memory query, never a parallel session-level curator entity."
|
|
1050
|
+
|
|
1051
|
+
---
|
|
1052
|
+
|
|
1053
|
+
## 25. Workflow definition language + concrete workflows from reports
|
|
1054
|
+
|
|
1055
|
+
substrate.md §6.9.3 sketches a two-layer YAML (workflow = Layer 1 domain steps; mandatory gates = Layer 2 overlay). The shape is right but needs three things before it can be shipped: (a) a precise schema for the language, (b) the actual workflows the system needs out of the box (extracted from real chains in `.xtrm/reports/`, not invented), (c) clear composition / override rules.
|
|
1056
|
+
|
|
1057
|
+
### 25.1 The language — schema
|
|
1058
|
+
|
|
1059
|
+
A workflow file is YAML with this shape. **No new field types beyond what substrate.md already has** (§5.2 `invite_when` matcher, §6.9 step bookends, §6.6 scrutiny). The language *composes* existing primitives, it does not extend them.
|
|
1060
|
+
|
|
1061
|
+
```yaml
|
|
1062
|
+
# Schema: workflow.v1
|
|
1063
|
+
name: string # globally unique
|
|
1064
|
+
description: string # one-line
|
|
1065
|
+
extends: workflow-name | null # optional inheritance (linear, not multiple)
|
|
1066
|
+
|
|
1067
|
+
# Layer 1 — domain-specific steps between executor/reviewer bookends.
|
|
1068
|
+
# Each step is a role name; the role resolves to a participant definition (§16).
|
|
1069
|
+
steps:
|
|
1070
|
+
- role: string # e.g. "code-sanity", "quant-methodologist"
|
|
1071
|
+
when: matcher | null # optional — step runs only if matcher fires
|
|
1072
|
+
skip_when: matcher | null # optional — step skips if matcher fires
|
|
1073
|
+
non_skippable: bool # default false; true = unauthorized-skip → escalation
|
|
1074
|
+
timeout_ms: integer | null
|
|
1075
|
+
inputs: [evidence_ref, ...] # what evidence from prior steps this step requires
|
|
1076
|
+
|
|
1077
|
+
# Auto-match: when this workflow should resolve for an issue/seed
|
|
1078
|
+
applies_when: # reuses §5.2 invite_when matcher syntax
|
|
1079
|
+
type: [task, bug, ...]
|
|
1080
|
+
scope_matches: ["glob", "..."]
|
|
1081
|
+
scrutiny_gte: low|medium|high|critical
|
|
1082
|
+
tags_any: [string, ...]
|
|
1083
|
+
|
|
1084
|
+
# Defaults this workflow imposes on issues that resolve to it
|
|
1085
|
+
defaults:
|
|
1086
|
+
scrutiny: low|medium|high|critical
|
|
1087
|
+
budget:
|
|
1088
|
+
dollars: number
|
|
1089
|
+
wall_ms: integer
|
|
1090
|
+
|
|
1091
|
+
# Strictness — see §6.9.4 of substrate.md
|
|
1092
|
+
strict: bool # default false: workflow can be extended at seed time
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
**Bookends are implicit.** Every workflow runs inside `executor → ... steps ... → reviewer` (substrate.md §6.9.3). The `steps` list does not include them — they are always present, always last/first respectively, and `executor` may be `debugger` instead if the issue type resolves to a `debug` family workflow.
|
|
1096
|
+
|
|
1097
|
+
**Layer 2 gates overlay every workflow.** `code-sanity`, `obligations-scanner`, `security-auditor` (if surface) are not declared in any workflow file — they are config-shipped (`config/gates/*.yaml`) and added at resolution time per the §6.9.3 conditions. A workflow author cannot opt out (the gates are mandatory by design). The same config-shipped overlay can declare `skip_when` for the codified exceptions (test-only, new-file-only).
|
|
1098
|
+
|
|
1099
|
+
### 25.2 Workflow inheritance — linear, not multiple
|
|
1100
|
+
|
|
1101
|
+
`extends: <other>` is allowed but only linear (one parent). Multiple inheritance creates ordering ambiguity for step lists. The child can:
|
|
1102
|
+
|
|
1103
|
+
- Prepend steps with `steps_before: [...]`
|
|
1104
|
+
- Append steps with `steps_after: [...]`
|
|
1105
|
+
- Replace a step by name with `steps_replace: { <name>: <new-def> }`
|
|
1106
|
+
- Skip a parent step with `steps_skip: [name, ...]` (allowed only if parent step is *not* `non_skippable`)
|
|
1107
|
+
|
|
1108
|
+
Resolution merges parent + child at workflow-load time and persists the result as the resolved workflow (§6.9.2). The merged form is what's stored on the container — children are not re-resolved per dispatch.
|
|
1109
|
+
|
|
1110
|
+
### 25.3 Concrete workflows — extracted from `.xtrm/reports/`
|
|
1111
|
+
|
|
1112
|
+
Six workflows recur in the real chains. These are the defaults substrate ships, named from actual usage in mercury 2026-05-25 and specialists 2026-05-26.
|
|
1113
|
+
|
|
1114
|
+
#### `code-quick` — LOW-blast trivial change
|
|
1115
|
+
|
|
1116
|
+
Observed in: mercury 2026-05-25 wave 1 (`98vy`, one-line sort fix).
|
|
1117
|
+
|
|
1118
|
+
```yaml
|
|
1119
|
+
name: code-quick
|
|
1120
|
+
description: Trivial change with LOW blast radius — lean chain
|
|
1121
|
+
steps: [] # bookends only; Layer 2 gates still apply
|
|
1122
|
+
applies_when:
|
|
1123
|
+
scrutiny_gte: low
|
|
1124
|
+
scrutiny_lte: low
|
|
1125
|
+
defaults:
|
|
1126
|
+
scrutiny: low
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
Resolves to: `executor → code-sanity → reviewer` (gates auto-attached). 3-job chain. The shortest legitimate path.
|
|
1130
|
+
|
|
1131
|
+
#### `code-standard` — production diff (the iron pipeline)
|
|
1132
|
+
|
|
1133
|
+
Observed in: specialists 2026-05-26 (entire iron epic). The default for most production work.
|
|
1134
|
+
|
|
1135
|
+
```yaml
|
|
1136
|
+
name: code-standard
|
|
1137
|
+
description: Production diff, Iron-style review pipeline
|
|
1138
|
+
steps: [] # bookends + Layer 2 gates do the work
|
|
1139
|
+
applies_when:
|
|
1140
|
+
type: [task, bug]
|
|
1141
|
+
scrutiny_gte: medium
|
|
1142
|
+
defaults:
|
|
1143
|
+
scrutiny: medium
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
Resolves (with Layer 2 + reviewer scrutiny auto-escalation): `executor → code-sanity → [security-auditor if surface] → obligations-scanner → reviewer`. Matches the iron-review-hardening v3.5 shape (specialists 2026-05-26 memory `using-specialists-v3-skill-v3-5-updates-1`).
|
|
1147
|
+
|
|
1148
|
+
#### `code-with-advisors` — HIGH/CRITICAL blast or unknown approach
|
|
1149
|
+
|
|
1150
|
+
Observed in: mercury 2026-05-25 waves 2-3 (`7egg`, treasury tick-grid fix; CRITICAL blast across 13 processes).
|
|
1151
|
+
|
|
1152
|
+
```yaml
|
|
1153
|
+
name: code-with-advisors
|
|
1154
|
+
extends: code-standard
|
|
1155
|
+
description: Advisor prep before standard pipeline — for HIGH/CRITICAL blast
|
|
1156
|
+
steps_before:
|
|
1157
|
+
- role: explorer
|
|
1158
|
+
when: { has_no_explorer_evidence_in_scope: true }
|
|
1159
|
+
- role: methodologist
|
|
1160
|
+
when: { scrutiny_gte: high }
|
|
1161
|
+
applies_when:
|
|
1162
|
+
scrutiny_gte: high
|
|
1163
|
+
defaults:
|
|
1164
|
+
scrutiny: high
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
Resolves: `executor → ... ` preceded by `explorer → methodologist`. Skips explorer if recent exploration evidence already exists (the `lb9s` case in mercury 2026-05-25 — HIGH blast but cause known, advisors skipped).
|
|
1168
|
+
|
|
1169
|
+
#### `debug` — bug fix, debugger is non-skippable
|
|
1170
|
+
|
|
1171
|
+
Observed in: implicit in mercury-market-data orphan worktrees (`f93g`, `fwhd` "open debugger" notes) and CLAUDE.md gotchas ("debugger-restitch loop").
|
|
1172
|
+
|
|
1173
|
+
```yaml
|
|
1174
|
+
name: debug
|
|
1175
|
+
description: Bug-fix workflow; replaces executor with debugger; restitch loop tolerated
|
|
1176
|
+
steps:
|
|
1177
|
+
- role: debugger
|
|
1178
|
+
non_skippable: true # forgetting debugger on a bug chain is the laziness this fixes
|
|
1179
|
+
# Bookend override: this workflow replaces the `executor` bookend with `debugger`
|
|
1180
|
+
bookend_override:
|
|
1181
|
+
opener: debugger
|
|
1182
|
+
applies_when:
|
|
1183
|
+
type: [bug]
|
|
1184
|
+
defaults:
|
|
1185
|
+
scrutiny: medium
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
Resolves: `debugger → code-sanity → obligations-scanner → reviewer`. The `non_skippable: true` is what closes the substrate.md §6.9.1 concern ("orchestrator forgets the debugger").
|
|
1189
|
+
|
|
1190
|
+
#### `quant-validation` — analytical / numerical rigor
|
|
1191
|
+
|
|
1192
|
+
Observed in: mercury 2026-05-25 (custom pack `using-specialists-quant` skill mentions; the `7egg` quant-methodologist + `iohb` methodology beads).
|
|
1193
|
+
|
|
1194
|
+
```yaml
|
|
1195
|
+
name: quant-validation
|
|
1196
|
+
extends: code-with-advisors
|
|
1197
|
+
description: Quantitative/statistical work — locks methodology before code
|
|
1198
|
+
steps_before:
|
|
1199
|
+
- role: quant-methodologist
|
|
1200
|
+
non_skippable: true # methodology must precede executor on quant work
|
|
1201
|
+
- role: quant-researcher
|
|
1202
|
+
when: { tags_any: [needs-external-evidence] }
|
|
1203
|
+
steps_replace:
|
|
1204
|
+
methodologist: { role: quant-methodologist } # specialize
|
|
1205
|
+
applies_when:
|
|
1206
|
+
scope_matches: ["**/analytics/**", "**/stats/**", "**/*.ipynb"]
|
|
1207
|
+
tags_any: [quant, statistical]
|
|
1208
|
+
defaults:
|
|
1209
|
+
scrutiny: high
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
Resolves: `quant-methodologist → [quant-researcher if external evidence needed] → executor → code-sanity → obligations-scanner → reviewer`. The mercury `7egg` chain (tick-grid quantization policy locked by methodology before executor implementation) is exactly this shape.
|
|
1213
|
+
|
|
1214
|
+
#### `security-deep` — sensitive surface or security epic
|
|
1215
|
+
|
|
1216
|
+
Observed in: implied by substrate.md §6.6 + specialists 2026-05-26 SCRUTINY auto-escalation table (auth, secrets, lockfiles, config, permissions).
|
|
1217
|
+
|
|
1218
|
+
```yaml
|
|
1219
|
+
name: security-deep
|
|
1220
|
+
extends: code-standard
|
|
1221
|
+
description: Sensitive surface — security advisor + gate, scrutiny critical
|
|
1222
|
+
steps_before:
|
|
1223
|
+
- role: security-auditor # advisory pass (recommendations before executor)
|
|
1224
|
+
non_skippable: true
|
|
1225
|
+
applies_when:
|
|
1226
|
+
scrutiny_gte: critical
|
|
1227
|
+
scope_matches:
|
|
1228
|
+
- "**/auth/**"
|
|
1229
|
+
- "**/secrets/**"
|
|
1230
|
+
- "**/crypto/**"
|
|
1231
|
+
- "**/migrations/**"
|
|
1232
|
+
- "**/.github/workflows/**"
|
|
1233
|
+
defaults:
|
|
1234
|
+
scrutiny: critical
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
Resolves: `security-auditor (advisor) → executor → code-sanity → security-auditor (gate) → obligations-scanner → reviewer`. The same role serves twice with different semantics (advisor pre-, gate post-) — the participant definition's `workflow_role` field (§16) declares which positions are valid.
|
|
1238
|
+
|
|
1239
|
+
### 25.4 Resolution example (matches mercury 2026-05-25 `7egg`)
|
|
1240
|
+
|
|
1241
|
+
Given an issue:
|
|
1242
|
+
- `type: task`
|
|
1243
|
+
- `scope: ["analytics/shared/treasury.py", "tests/test_treasury_format.py"]`
|
|
1244
|
+
- `scrutiny: critical` (auto-escalated by reviewer surface check; was `high` at create time)
|
|
1245
|
+
- `tags: [quant]`
|
|
1246
|
+
|
|
1247
|
+
Auto-match resolution order (first matching `applies_when` wins, else `code-standard` fallback):
|
|
1248
|
+
1. `security-deep`? scope doesn't match security paths → no
|
|
1249
|
+
2. `quant-validation`? scope matches `**/analytics/**` AND `tags: [quant]` → **yes**
|
|
1250
|
+
3. (would have been `code-with-advisors` / `code-standard` otherwise)
|
|
1251
|
+
|
|
1252
|
+
Resolved chain (Layer-1 from `quant-validation` + Layer-2 gates from config, with scrutiny auto-escalation):
|
|
1253
|
+
```
|
|
1254
|
+
quant-methodologist → quant-researcher (skipped — no `needs-external-evidence` tag)
|
|
1255
|
+
→ executor
|
|
1256
|
+
→ code-sanity
|
|
1257
|
+
→ security-auditor (skipped — surface doesn't trigger)
|
|
1258
|
+
→ obligations-scanner
|
|
1259
|
+
→ reviewer (scrutiny=critical → ddiff mode if PARTIAL)
|
|
1260
|
+
→ merge_ready
|
|
1261
|
+
```
|
|
1262
|
+
|
|
1263
|
+
This matches the actual `7egg` chain from mercury 2026-05-25 (`explorer 772978 + quant-methodologist a2177e + executor 455b77 + code-sanity 8734eb + reviewer 6ef30f`), modulo the explorer that ran ad-hoc instead of by workflow — the resolved workflow would have made the explorer step explicit and persisted.
|
|
1264
|
+
|
|
1265
|
+
### 25.5 What this resolves and what remains
|
|
1266
|
+
|
|
1267
|
+
**Closed by §25:**
|
|
1268
|
+
|
|
1269
|
+
- substrate.md §6.9 was a sketch; §25 makes it a concrete YAML schema with composition rules.
|
|
1270
|
+
- substrate.md §14.1 *"Which workflows are actually recurring?"* — answered with six concrete workflows mined from real chains.
|
|
1271
|
+
|
|
1272
|
+
**Open after §25 (next pass, not now):**
|
|
1273
|
+
|
|
1274
|
+
- Per-role timeout and budget defaults — calibrated against pi auto-retry behavior, needs real runs to set numbers.
|
|
1275
|
+
- A workflow editor or visualizer (operator-facing). Out of scope for the design pass; the YAML files plus `sb container ps --tree` are sufficient for v0.
|
|
1276
|
+
|
|
1277
|
+
### 25.6 What gets reused
|
|
1278
|
+
|
|
1279
|
+
| Workflow feature | Reused from |
|
|
1280
|
+
|---|---|
|
|
1281
|
+
| `applies_when` matcher | substrate.md §5.2 seed `invite_when`, §6.9.3 workflow `applies_when` — same matcher syntax everywhere |
|
|
1282
|
+
| `extends` + `steps_before/after/replace` | standard schema composition; no new mechanism |
|
|
1283
|
+
| Bookend (executor/reviewer/debugger) | substrate.md §6.9.3 |
|
|
1284
|
+
| Layer 2 gates auto-overlay | substrate.md §6.9.3 |
|
|
1285
|
+
| Non-skippable + unauthorized-skip-as-escalation | substrate.md §6.9.3 (Iron pattern) |
|
|
1286
|
+
| `workflow_role` field on participant definition | §16 of this review |
|
|
1287
|
+
|
|
1288
|
+
Zero new primitives. The workflow language *composes* substrate's existing matcher, role, and gate primitives — exactly what §18's reuse audit demands.
|