shapeup-sdlc 3.18.3 → 4.0.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/.claude/settings.local.example.json +2 -3
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +115 -56
- package/README.md +101 -143
- package/SECURITY.md +16 -25
- package/agents/orient.md +14 -0
- package/agents/qa-edge-hunter.md +13 -0
- package/agents/spec-evaluator.md +14 -0
- package/agents/task-executor.md +14 -0
- package/bin/init.mjs +13 -16
- package/bin/lib/grant.mjs +8 -19
- package/commands/build.md +9 -14
- package/commands/eval.md +3 -3
- package/commands/hammer.md +9 -5
- package/commands/retro.md +12 -11
- package/commands/scopes.md +1 -12
- package/commands/ship.md +45 -88
- package/commands/wire.md +1 -8
- package/hooks/dispatch-receipt.mjs +94 -8
- package/hooks/hooks.json +0 -41
- package/hooks/lib/decision.mjs +6 -9
- package/hooks/lib/tier.mjs +50 -0
- package/hooks/sandbox-guard.mjs +140 -64
- package/kernel/gate.mjs +203 -22
- package/kernel/harness.mjs +20 -67
- package/kernel/init/run.mjs +175 -523
- package/kernel/lib/contract.mjs +9 -93
- package/kernel/lib/floor.mjs +64 -0
- package/kernel/lib/paths.mjs +39 -112
- package/kernel/order.mjs +157 -0
- package/kernel/probe/digest.mjs +2 -38
- package/kernel/schemas/domain.schema.json +146 -1125
- package/kernel/schemas/work-order.schema.json +8 -4
- package/kernel/schemas/work-result.schema.json +1 -1
- package/kernel/verify/envelope.mjs +2 -2
- package/kernel/verify/skills.mjs +27 -8
- package/kernel/verify/t0.mjs +2 -2
- package/package.json +4 -2
- package/skills/building/SKILL.md +144 -0
- package/skills/building/resources/gates.md +76 -0
- package/skills/building/resources/guidance.md +23 -0
- package/skills/{translator/references/preservation-rules.md → building/resources/intake-preservation.md} +1 -1
- package/skills/building/resources/intake.md +157 -0
- package/skills/building/resources/orders.md +69 -0
- package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/doc-schemas.md +1 -1
- package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/task-generation.md +13 -13
- package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/test-surface.md +6 -7
- package/skills/building/resources/plan-scopes.md +95 -0
- package/skills/building/resources/plan-spec.md +102 -0
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/_index.tmpl.md +1 -1
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/assess-report.tmpl.md +10 -11
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/run-state.tmpl.md +4 -4
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/scope-summary.tmpl.md +2 -2
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/synthesis.tmpl.md +4 -4
- package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/usecase.tmpl.md +2 -2
- package/skills/building/resources/plan-wire.md +82 -0
- package/skills/building/resources/plan.md +98 -0
- package/skills/building/resources/retro-research.md +60 -0
- package/skills/building/resources/retro-scan.md +49 -0
- package/skills/building/resources/retro.md +225 -0
- package/skills/building/resources/rounds.md +78 -0
- package/skills/building/resources/stop.md +127 -0
- package/skills/orient/SKILL.md +37 -37
- package/skills/qa-edge-hunter/SKILL.md +14 -14
- package/skills/shapeup/SKILL.md +5 -5
- package/skills/shapeup/resources/breadboarding.md +1 -1
- package/skills/shapeup/resources/context-compaction.md +5 -5
- package/skills/shapeup/resources/shaping.md +1 -1
- package/skills/spec-evaluator/SKILL.md +17 -21
- package/skills/spec-evaluator/references/dimension-contract.md +1 -1
- package/skills/spec-evaluator/references/dimensions/completeness.md +3 -3
- package/skills/spec-evaluator/references/dimensions/spec-conformance.md +1 -1
- package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +5 -5
- package/skills/task-executor/SKILL.md +11 -15
- package/commands/hill.md +0 -10
- package/hooks/gate-intake.mjs +0 -109
- package/hooks/gate-zerowork.mjs +0 -349
- package/hooks/tier-guard.mjs +0 -162
- package/kernel/compile.mjs +0 -1419
- package/kernel/init/fit.mjs +0 -199
- package/kernel/init/run-args.mjs +0 -206
- package/kernel/lib/breadboard.mjs +0 -165
- package/kernel/probe/attempts.mjs +0 -161
- package/kernel/probe/concurrency.mjs +0 -535
- package/kernel/probe/eval.mjs +0 -405
- package/kernel/probe/hunt.mjs +0 -104
- package/kernel/probe/leg.mjs +0 -229
- package/kernel/probe/owner.mjs +0 -142
- package/kernel/probe/requirements.mjs +0 -328
- package/kernel/probe/resume.mjs +0 -1110
- package/kernel/probe/rounds.mjs +0 -115
- package/kernel/probe/staged.mjs +0 -83
- package/kernel/probe/stats.mjs +0 -477
- package/kernel/probe/t0.mjs +0 -115
- package/kernel/reduce/board.mjs +0 -350
- package/kernel/reduce/graph.mjs +0 -482
- package/kernel/reduce/hill.mjs +0 -322
- package/kernel/reduce/ingest.mjs +0 -767
- package/kernel/reduce/leftovers.mjs +0 -159
- package/kernel/reduce/ship.mjs +0 -529
- package/kernel/reduce/snapshot.mjs +0 -291
- package/kernel/reduce/verdict.mjs +0 -172
- package/kernel/report/export.mjs +0 -384
- package/kernel/report/facts.mjs +0 -234
- package/kernel/verify/budget.mjs +0 -153
- package/kernel/verify/build.mjs +0 -428
- package/kernel/verify/dispatch.mjs +0 -114
- package/kernel/verify/spec.mjs +0 -975
- package/kernel/verify/trace.mjs +0 -604
- package/skills/ba-pitch-analyzer/SKILL.md +0 -204
- package/skills/coach/SKILL.md +0 -397
- package/skills/hill-chart/SKILL.md +0 -154
- package/skills/hill-chart/assets/dashboard.template.html +0 -1432
- package/skills/scope-architect/SKILL.md +0 -174
- package/skills/scope-hammer/SKILL.md +0 -231
- package/skills/solution-architect/SKILL.md +0 -158
- package/skills/tech-lead/SKILL.md +0 -151
- package/skills/tech-lead/references/gates.md +0 -605
- package/skills/tech-lead/references/protocol.md +0 -889
- package/skills/tech-lead/references/tiny-lane.md +0 -53
- package/skills/tech-lead/workflows/shapeup-run.js +0 -2187
- package/skills/translator/SKILL.md +0 -258
- /package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/contract-patterns.md +0 -0
- /package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/ddd-patterns.md +0 -0
- /package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/integration-analysis.md +0 -0
- /package/skills/{ba-pitch-analyzer/references → building/resources/plan-references}/ux-behavior-patterns.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/api-feasibility.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/contracts/be-service.contract.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/contracts/offline-storage.contract.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/contracts/third-party-api.contract.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/cross-context/context-map.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/cross-context/event-choreography.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/cross-context/migration-plan.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/cross-context/team-handoff.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/domain-model.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/feedback.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/integration.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/task-board.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/task-spike.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/task.tmpl.md +0 -0
- /package/skills/{ba-pitch-analyzer/assets/templates → building/resources/plan-templates}/ux-behavior.tmpl.md +0 -0
package/bin/init.mjs
CHANGED
|
@@ -26,9 +26,9 @@ import { fileURLToPath } from "node:url";
|
|
|
26
26
|
import { spawnSync } from "node:child_process";
|
|
27
27
|
import { createInterface } from "node:readline";
|
|
28
28
|
import { LOCAL, LEGACY, metricsDir } from "../kernel/lib/paths.mjs";
|
|
29
|
-
import { mergePipelinePermissions as mergeGrant, isWorkspaceTrusted
|
|
29
|
+
import { mergePipelinePermissions as mergeGrant, isWorkspaceTrusted } from "./lib/grant.mjs";
|
|
30
30
|
|
|
31
|
-
/** The root a project migrating off the
|
|
31
|
+
/** The root a project migrating off the earlier layout may still be carrying. */
|
|
32
32
|
const LEGACY_LOCAL = LEGACY.local;
|
|
33
33
|
|
|
34
34
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -43,24 +43,21 @@ Options:
|
|
|
43
43
|
-d, --directory <path> Target project directory (default: current directory)
|
|
44
44
|
-o, --override Overwrite existing files in target
|
|
45
45
|
-y, --yes Run unattended (answer yes to all prompts)
|
|
46
|
-
--no-native-workflow
|
|
46
|
+
--no-native-workflow Accepted and ignored (kept so existing scripts that pass it still run)
|
|
47
47
|
-h, --help Print this help
|
|
48
48
|
|
|
49
|
-
The grant this writes is two Bash rules for the harness kernel,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
authorises every dynamic workflow script in the project, not only this plugin's.
|
|
53
|
-
Declining it leaves the harness fully functional in an interactive session; only
|
|
54
|
-
the unattended lane needs the pre-approval.`;
|
|
49
|
+
The grant this writes is two Bash rules for the harness kernel, and nothing else.
|
|
50
|
+
The plugin no longer asks for the unscoped "Workflow" permission and no longer
|
|
51
|
+
needs it; a "Workflow" token already in your settings is yours and is left alone.`;
|
|
55
52
|
|
|
56
|
-
let targetDir = ".", yes = false, override = false
|
|
53
|
+
let targetDir = ".", yes = false, override = false;
|
|
57
54
|
const positional = [];
|
|
58
55
|
for (let i = 0; i < argv.length; i++) {
|
|
59
56
|
const a = argv[i];
|
|
60
57
|
if (a === "-d" || a === "--directory") targetDir = argv[++i];
|
|
61
58
|
else if (a === "-y" || a === "--yes") yes = true;
|
|
62
59
|
else if (a === "-o" || a === "--override") override = true;
|
|
63
|
-
else if (a === "--no-native-workflow")
|
|
60
|
+
else if (a === "--no-native-workflow") { /* accepted, ignored: the installer never writes the token */ }
|
|
64
61
|
else if (a === "-h" || a === "--help") { console.log(usage); process.exit(0); }
|
|
65
62
|
else if (a.startsWith("-")) { console.error(`Unknown option: ${a}\n${usage}`); process.exit(1); }
|
|
66
63
|
else positional.push(a);
|
|
@@ -125,7 +122,7 @@ warnIfUntrusted(target);
|
|
|
125
122
|
ensureAgentImport(join(target, "CLAUDE.md"), "CLAUDE.md");
|
|
126
123
|
|
|
127
124
|
// ---- 3. .gitignore ----------------------------------------------------------
|
|
128
|
-
// Both roots are listed, deliberately. A project may still be carrying the
|
|
125
|
+
// Both roots are listed, deliberately. A project may still be carrying the earlier local root
|
|
129
126
|
// alongside the current one, and a run trace committed by accident from either is exactly the
|
|
130
127
|
// mistake the tier split exists to prevent. Ignoring a directory that does not exist costs nothing.
|
|
131
128
|
const GITIGNORE_RULE = `# Shape Up SDLC run workspace
|
|
@@ -274,10 +271,10 @@ function installClaude() {
|
|
|
274
271
|
* @returns {void}
|
|
275
272
|
*/
|
|
276
273
|
function mergePipelinePermissions(settings) {
|
|
277
|
-
mergeGrant(settings
|
|
278
|
-
if (
|
|
279
|
-
console.log(
|
|
280
|
-
console.log("
|
|
274
|
+
mergeGrant(settings);
|
|
275
|
+
if ((settings.permissions?.allow || []).includes("Workflow")) {
|
|
276
|
+
console.log(' [claude] your settings carry a "Workflow" permission. This plugin no longer needs it and');
|
|
277
|
+
console.log(" leaves it alone — remove it yourself if nothing else in the project uses it.");
|
|
281
278
|
}
|
|
282
279
|
}
|
|
283
280
|
|
package/bin/lib/grant.mjs
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
// verify by eye. Scope is unchanged in kind and narrower in fact: one dependency-free, network-free
|
|
49
49
|
// script that ships with the plugin. It grants no general `Bash(node:*)`.
|
|
50
50
|
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
51
|
+
// NO `Workflow` TOKEN. The run is driven by the loop in the session, not launched as a script, so
|
|
52
|
+
// nothing here needs the unscoped `Workflow` permission and the installer does not add it. It does not
|
|
53
|
+
// remove it either: a project may rely on that token for workflow scripts of its own, so a token already
|
|
54
|
+
// in a user's settings is the user's, and the merge below leaves it exactly where it is.
|
|
55
55
|
|
|
56
56
|
import { existsSync, readFileSync } from "node:fs";
|
|
57
57
|
import { join, resolve } from "node:path";
|
|
@@ -60,14 +60,6 @@ import { homedir } from "node:os";
|
|
|
60
60
|
/** The kernel's path inside the installed plugin — the one executable the grant has to cover. */
|
|
61
61
|
export const KERNEL_ENTRY = "kernel/harness.mjs";
|
|
62
62
|
|
|
63
|
-
/**
|
|
64
|
-
* The unscoped permission token that lets the tech-lead launch its Workflow script.
|
|
65
|
-
*
|
|
66
|
-
* Opt-in at install. Named here rather than spelled at each call site so the installer, the docs
|
|
67
|
-
* check and the structural suite all mean the same string.
|
|
68
|
-
*/
|
|
69
|
-
export const WORKFLOW_RULE = "Workflow";
|
|
70
|
-
|
|
71
63
|
/**
|
|
72
64
|
* The `permissions.allow` rules for the harness kernel.
|
|
73
65
|
*
|
|
@@ -127,19 +119,16 @@ export function isWorkspaceTrusted(projectDir) {
|
|
|
127
119
|
/**
|
|
128
120
|
* Merge the harness grant into a parsed settings object, in place.
|
|
129
121
|
*
|
|
122
|
+
* Adds the two kernel rules and drops only the rules this plugin itself once wrote and superseded;
|
|
123
|
+
* every other entry — a user's own rule, a `Workflow` token — is left alone.
|
|
124
|
+
*
|
|
130
125
|
* @param {object} settings - Parsed settings.json.
|
|
131
|
-
* @param {object} [opts] - Options.
|
|
132
|
-
* @param {boolean} [opts.nativeWorkflow=true] - Also grant the unscoped `Workflow` token, which is
|
|
133
|
-
* what lets the tech-lead launch its run script without a per-launch approval. `false` removes it
|
|
134
|
-
* if a previous install added it, so `--no-native-workflow` is a real opt-out and not a no-op.
|
|
135
126
|
* @returns {void}
|
|
136
127
|
*/
|
|
137
|
-
export function mergePipelinePermissions(settings
|
|
128
|
+
export function mergePipelinePermissions(settings) {
|
|
138
129
|
settings.permissions = settings.permissions || {};
|
|
139
130
|
const allow = new Set(settings.permissions.allow || []);
|
|
140
131
|
for (const r of [...allow]) if (isSupersededRule(r)) allow.delete(r);
|
|
141
132
|
for (const r of pipelineRules()) allow.add(r);
|
|
142
|
-
if (nativeWorkflow) allow.add(WORKFLOW_RULE);
|
|
143
|
-
else allow.delete(WORKFLOW_RULE);
|
|
144
133
|
settings.permissions.allow = [...allow].sort();
|
|
145
134
|
}
|
package/commands/build.md
CHANGED
|
@@ -4,8 +4,8 @@ description: Implement a task's acceptance criteria exactly (minimum code, surgi
|
|
|
4
4
|
Use the **task-executor** skill on $ARGUMENTS.
|
|
5
5
|
|
|
6
6
|
Standalone build: implements one task (`TASK-NNN`) from a spec folder — assumption scan first,
|
|
7
|
-
minimum code, surgical diffs, verified observable outcomes. Inside
|
|
8
|
-
|
|
7
|
+
minimum code, surgical diffs, verified observable outcomes. Inside a run the `building` loop
|
|
8
|
+
dispatches this skill through the envelope port instead; this command is for picking
|
|
9
9
|
up a single task directly.
|
|
10
10
|
|
|
11
11
|
If no task ID was given, read the board (`.shapeup/<slug>/tasks/_index.md`) and take the
|
|
@@ -16,19 +16,14 @@ the harness working, not an error to route around.
|
|
|
16
16
|
## Building a whole scoped feature is not this command
|
|
17
17
|
|
|
18
18
|
This command builds **one task**. A full BUILD round — every scope, the per-scope attempt loop,
|
|
19
|
-
T0 verification, the inner circuit breaker, then the single EVAL —
|
|
20
|
-
it belongs to the orchestrator:
|
|
19
|
+
T0 verification, the inner circuit breaker, then the single EVAL — belongs to the loop:
|
|
21
20
|
|
|
22
21
|
```
|
|
23
|
-
|
|
24
|
-
scriptPath: ".shapeup/workflows/shapeup-run.js",
|
|
25
|
-
args: <the RunArgs object>
|
|
26
|
-
})
|
|
22
|
+
/ship <pitch>
|
|
27
23
|
```
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
attempt loop, the T0 ratchet and the breakers are
|
|
32
|
-
be trusted to reproduce, and a session that
|
|
33
|
-
|
|
34
|
-
`skills/tech-lead/references/protocol.md` still applies, unchanged and by design.
|
|
25
|
+
`/ship` opens the run properly — `harness init run` first, so the receipt and the launch record
|
|
26
|
+
exist — and then follows the loop's phase table over the run's files. Do not hand-roll the
|
|
27
|
+
round by calling this command once per task: the attempt loop, the T0 ratchet and the breakers are
|
|
28
|
+
the loop's discipline, not steps a caller can be trusted to reproduce, and a session that
|
|
29
|
+
rebuilds them by hand is the prose lane the loop replaced.
|
package/commands/eval.md
CHANGED
|
@@ -9,8 +9,8 @@ The single judge. Two modes, chosen by the arguments:
|
|
|
9
9
|
- `--spec <folder> --feature <slug> --single-pass` — the once-per-round verdict on the whole
|
|
10
10
|
board.
|
|
11
11
|
|
|
12
|
-
Round mode
|
|
13
|
-
|
|
14
|
-
taken now grades a partial board and the
|
|
12
|
+
Round mode crosses the Round Close gate first: while any task on the board is unfinished the gate block
|
|
13
|
+
carries the unfinished scopes — advisory, so the call proceeds, but a verdict
|
|
14
|
+
taken now grades a partial board and the crossing is recorded as a `warn`. The correct response is to route
|
|
15
15
|
back to `/build` and finish them — do not shrug the warning off, and do not use `--task` as a
|
|
16
16
|
loophole to simulate a round verdict piecemeal.
|
package/commands/hammer.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: The Cut-List Review gate — must-have census, baseline comparison, cut list + ship verdict
|
|
3
3
|
---
|
|
4
|
-
Use the **
|
|
4
|
+
Use the **building** skill's `stop` sub-command on $ARGUMENTS.
|
|
5
5
|
|
|
6
6
|
The stop decision. Censuses the open items (QA findings, discovered-ledger rows, attempt-budget
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
proposals), compares the product to the **pitch's baseline — what customers live with today, never
|
|
8
|
+
the ideal** — and returns a cut list plus a ship verdict for the TL/PO to confirm. You do it in this
|
|
9
|
+
session, and every ownership, exhaustion and requirement claim cites the file it came from — the scope
|
|
10
|
+
contracts, the dispatch receipts and trials, the verdict and `requirements.md`.
|
|
9
11
|
|
|
10
12
|
Handles all three arrival paths: the normal stop (all scopes finished), an exhausted round
|
|
11
13
|
budget, and an exhausted per-scope attempt budget. The verdict is a proposal — promotion of cut
|
|
12
|
-
items is the human's call at the gate, not the
|
|
14
|
+
items is the human's call at the gate, not the census's — and it is written as data to
|
|
15
|
+
`reports/hammer-census.json` under the run's local root, which the Ship Sign-off gate reads before any answer set
|
|
16
|
+
may say `ship`.
|
package/commands/retro.md
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: File ship-gate feedback into the team-shared, per-
|
|
2
|
+
description: File ship-gate feedback into the team-shared, per-reader knowledge base
|
|
3
3
|
---
|
|
4
|
-
Use the **
|
|
4
|
+
Use the **building** skill's `retro` sub-command on $ARGUMENTS.
|
|
5
5
|
|
|
6
|
-
Turns raw PO/TL feedback (usually from the
|
|
7
|
-
committed `shapeup/knowledge-base/<
|
|
8
|
-
the
|
|
6
|
+
Turns raw PO/TL feedback (usually from the Ship Sign-off gate) into per-reader guideline files under
|
|
7
|
+
committed `shapeup/knowledge-base/<reader>.md` — readers are `task-executor`, `orient`,
|
|
8
|
+
`qa-edge-hunter`, the `plan` activity and the loop itself (`building`, read at the Intake & Config gate, where it may
|
|
9
|
+
also suggest Intake & Config values the loop confirms before pinning).
|
|
9
10
|
|
|
10
|
-
`/retro --scan` runs the
|
|
11
|
+
`/retro --scan` runs the retro's **scan** instead: it reads the project on disk (build
|
|
11
12
|
and toolchain files, CI config, CLAUDE.md, README) and drafts the same kind of guidelines from
|
|
12
13
|
it, before the first feature or after the toolchain changes. Optional, and never on the scan's
|
|
13
|
-
own authority — every drafted rule goes through
|
|
14
|
+
own authority — every drafted rule goes through the Retro Ownership gate like feedback.
|
|
14
15
|
|
|
15
|
-
`/retro --research <stack>` runs the **research
|
|
16
|
+
`/retro --research <stack>` runs the **research**: for a project with nothing on disk
|
|
16
17
|
yet, it drafts the same kind of guidelines from the platform's official documentation only —
|
|
17
18
|
build, launch, test, package manager, lint, in that order of leverage — each rule cited with url,
|
|
18
19
|
version and fetch date; on a project that has been scanned, it cross-checks every scan rule
|
|
19
20
|
against the documentation and reports each as confirmed, contradicted or unknown. The stack is
|
|
20
21
|
required and never guessed. Research is a source, not a verification: nothing it reads runs, and
|
|
21
|
-
the
|
|
22
|
+
the loop still pins every suggested command at the Intake & Config gate before the kernel executes it. A
|
|
22
23
|
later `--scan`, once the first feature has created a toolchain, retires the research rules it
|
|
23
24
|
confirms with disk evidence.
|
|
24
25
|
|
|
25
|
-
Two rules the
|
|
26
|
-
which
|
|
26
|
+
Two rules the retro enforces and this command must not soften: the Retro Ownership gate **asks** the PO
|
|
27
|
+
which reader owns each rule — it never assumes; and feedback whose root cause is the mechanism
|
|
27
28
|
itself (a gate, hook, or contract defect) is categorized `harness-defect` and filed to the
|
|
28
29
|
defect register as a raw idea for the Betting Table, never as worker steering. A third holds
|
|
29
30
|
for every category: guidance never decides a gate — a rule may add a question or a check to a
|
package/commands/scopes.md
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Decompose the pitch into a spec tree + board, then map the scope contracts
|
|
3
3
|
---
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
1. Use the **ba-pitch-analyzer** skill on $ARGUMENTS — pitch → linked DDD spec tree (domain
|
|
7
|
-
model → use cases → tasks) with BDD scenarios and the derived Test Surface. Pass through an
|
|
8
|
-
operation when the user names one: `analyze` (the default — spec tree + board), `reconcile`
|
|
9
|
-
(fold discovered-ledger items back into the board and UC invariants), `retrofit-surface`
|
|
10
|
-
(append a Test Surface to a spec written before there was one), or `coverage` (extract the
|
|
11
|
-
shared `requirements.md` registry that anchors covers-closure).
|
|
12
|
-
2. Then use the **scope-architect** skill (`map-scopes`) to write the committed scope contracts
|
|
13
|
-
(`scopes/*.md`) — import-graph slicing by flow, write-whitelist substrates, fixtures.
|
|
14
|
-
|
|
15
|
-
If the user asked for only one half ("just analyze", "just the contracts"), run only that half.
|
|
4
|
+
Alias of `/ship plan`: use the **building** skill's plan activity on $ARGUMENTS and stop once the scope contracts are on disk and the Board Review gate is crossed — run the planning substeps the phase table hands out.
|
package/commands/ship.md
CHANGED
|
@@ -1,106 +1,63 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Run the full Shape Up harness on a pitch, with human sign-off at gates
|
|
3
3
|
---
|
|
4
|
-
Use the **
|
|
4
|
+
Use the **building** skill to run the full harness on $ARGUMENTS.
|
|
5
5
|
|
|
6
|
-
Default to **interactive** (`auto_level = interactive`): pause at every
|
|
7
|
-
sign-off — especially the Ship gate
|
|
6
|
+
Default to **interactive** (`auto_level = interactive`): pause at every gate and require PO
|
|
7
|
+
sign-off — especially the Ship Sign-off gate. The harness's safety depends on the human being in the
|
|
8
8
|
loop; do not skip gates by default.
|
|
9
9
|
|
|
10
|
-
**Before anything else, dispatch `
|
|
11
|
-
`harness init run`, which writes the run receipt. Do not summarise what the harness will do
|
|
12
|
-
a
|
|
13
|
-
`hooks/gate-zerowork.mjs`.
|
|
10
|
+
**Before anything else, dispatch `building` and let it open the run** — its first action is
|
|
11
|
+
`harness init run`, which writes the run receipt and the launch record. Do not summarise what the harness will do:
|
|
12
|
+
a run that left no receipt did not happen.
|
|
14
13
|
|
|
15
|
-
## How the run
|
|
14
|
+
## How the run executes
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
`building` drives the run in this session. It opens the run, then reads the run's files and takes the first
|
|
17
|
+
phase whose condition holds, over and over: dispatch a worker, build the released scopes, run a kernel
|
|
18
|
+
command, cross a gate, close. Three things follow:
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
- **A gate is answered where it is reached.** The loop prints the gate block, asks the PO one
|
|
21
|
+
question with a recommendation, records the answer with the source `PO (live)`, and goes on. There
|
|
22
|
+
is no relaunch after an answer.
|
|
23
|
+
- **A killed session loses nothing.** The next phase is read off the files on disk, never from
|
|
24
|
+
the conversation, so `/ship` again over an open run resumes it: a finished phase is not re-run.
|
|
25
|
+
- **Every agent the run spawns is a worker** — `orient`, `task-executor`, `spec-evaluator` and
|
|
26
|
+
`qa-edge-hunter` — and each leaves a row in the run's records. Planning is not a worker: the loop does it in this session, under a write fence of its own. Nor are the census at the Cut-List Review gate, the ship report, the retro after a ship, or the English copy of a non-English pitch: the loop does those itself.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fast-forward re-derives position from disk and re-dispatches nothing already finished.
|
|
35
|
-
- **A killed session loses nothing.** Resume state comes off disk, never from context, so a fresh
|
|
36
|
-
session picks the run up where it died — the property the whole launch shape exists to buy.
|
|
37
|
-
- **The launch is the `Workflow` tool**, which is what gives the run resume-from-journal, worktree
|
|
38
|
-
isolation, and sub-agents that share the session's prompt cache instead of paying a cold start
|
|
39
|
-
each. `npx shapeup-sdlc init` writes the `"Workflow"` grant it needs. That grant is unscoped — it
|
|
40
|
-
authorises every dynamic workflow script in the project — so an install may decline it with
|
|
41
|
-
`--no-native-workflow`, in which case the launch asks for approval once per session and the
|
|
42
|
-
unattended lane is unavailable.
|
|
43
|
-
|
|
44
|
-
`--tiny`, and any spec with no committed `scopes/*.md` yet, take the unchanged prose lane in
|
|
45
|
-
`skills/tech-lead/references/protocol.md` instead — non-regression, by design.
|
|
28
|
+
**`--tiny` is no longer served** — a breaking change in 4.0. The separate small-change lane (the `tech-lead`
|
|
29
|
+
skill's prose loop) was retired with that skill, and `init run` refuses `--tiny` and `--lane` as unknown flags (exit 2). If
|
|
30
|
+
`--tiny` is typed, say so plainly and run the full loop instead, with the pitch's appetite small (`~1 week` gives
|
|
31
|
+
a two-round budget, and a small shape yields one scope): the gates and the verification floor are the same ones
|
|
32
|
+
a bigger pitch crosses, and nothing is waived for a one-file fix. Do not take any other route — a hand-built
|
|
33
|
+
change outside the loop is what the harness exists to prevent.
|
|
46
34
|
|
|
47
35
|
Only run headless/auto if the user explicitly asks for it in their message:
|
|
48
|
-
- `--auto` → advance low-risk gates automatically, still pause at
|
|
36
|
+
- `--auto` → advance low-risk gates automatically, still pause at Ship Sign-off.
|
|
49
37
|
Implies `--gate-answers guarded` unless a set is named.
|
|
50
38
|
- `--unattended` → fully headless, `max_rounds 3`. Intended for CI, not day-to-day local runs.
|
|
51
|
-
Implies `--gate-answers ci` unless a set is named.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
relaunch fast-forwards past whatever finished; the cost is a relaunch, not the run.
|
|
57
|
-
**Typing the flag IS the confirmation — do not stop to ask for another one.** Emit the warning
|
|
58
|
-
that no human will review the verdict before ship as the run's first line, then proceed straight
|
|
59
|
-
into GATE L0 in the same turn.
|
|
60
|
-
|
|
61
|
-
> Why this is spelled out: asking for confirmation here made `--unattended` unusable for the
|
|
62
|
-
> only job it has. In a non-interactive invocation (`claude -p …`, a CI step) there is no second
|
|
63
|
-
> turn in which to answer, so the run spent its turn requesting permission and
|
|
64
|
-
> exited having written nothing. A headless flag that cannot complete a headless run is a defect,
|
|
65
|
-
> not a safety feature — and the warning, which is the part that carries the safety value, is
|
|
66
|
-
> still printed. `--auto` remains the middle setting that pauses at L4.
|
|
67
|
-
|
|
68
|
-
- `--tiny` → the small-change lane: orient (light) → single-task board → build → T0 → done.
|
|
69
|
-
Skips wiring, scope contracts, EVAL, and QA; only gates L0 and L4 pause. The tech-lead's L0
|
|
70
|
-
fit-check applies (≤ ~2 files, no new domain concept/dependency/flow) — if the change isn't
|
|
71
|
-
tiny, it will say so and recommend the full lane.
|
|
39
|
+
Implies `--gate-answers ci` unless a set is named. **Typing the flag IS the confirmation — do not
|
|
40
|
+
stop to ask for another one.** Emit the warning that no human will review the verdict before ship as
|
|
41
|
+
the run's first line, then proceed straight into the Intake & Config gate in the same turn. (In a non-interactive
|
|
42
|
+
invocation there is no second turn in which to answer; a headless flag that waits for one cannot
|
|
43
|
+
complete a headless run.)
|
|
72
44
|
|
|
73
|
-
Additional flags, pass through to `
|
|
45
|
+
Additional flags, pass through to `building` only when the user names them:
|
|
74
46
|
- `--breadboard <path>` → the pitch's breadboard, when it is not beside the pitch. A pitch shaped
|
|
75
|
-
with `/shapeup` is `shaping.md` + `breadboard.md`; the
|
|
76
|
-
folder (or in `shaping/`) on its own, and reads one written inline in a single `pitch.md`.
|
|
77
|
-
|
|
78
|
-
the run at GATE L1b when one is missing or folded into another screen.
|
|
47
|
+
with `/shapeup` is `shaping.md` + `breadboard.md`; the loop finds a `breadboard.md` in the same
|
|
48
|
+
folder (or in `shaping/`) on its own, and reads one written inline in a single `pitch.md`. Its
|
|
49
|
+
Places become the spec's screens, and the Board Review gate is where a missing or folded one is caught.
|
|
79
50
|
- `--gate-answers <ci|guarded|interactive|path.json>` → the pre-recorded PO decisions this run
|
|
80
|
-
crosses its gates with. Gates still emit their blocks and still record a decision; the
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
the harness trips its own breaker first: past the deadline `harness verify budget` denies new
|
|
88
|
-
`task-executor` work and routes to GATE H, where scope-hammer ships whatever is green. A run
|
|
89
|
-
killed from outside ships nothing — including the scopes that already passed T0.
|
|
90
|
-
- `--parallel-scopes N` → how many scopes build at once (default 4). Concurrency is a cost
|
|
91
|
-
question before it is a speed one — every extra leg is another worker's full context — so this
|
|
92
|
-
is a dial rather than a constant. `1` builds scopes one at a time, which is the setting for a
|
|
93
|
-
project whose workers are not safe to run beside each other.
|
|
94
|
-
- `--adversarial-verify` → before a FAIL finding costs a whole fix round, spend one independent
|
|
95
|
-
skeptic on it, prompted to refute and to default to "real" when it cannot. Off by default; it
|
|
96
|
-
does not touch the single-judge invariant, because a refutation retracts a finding rather than
|
|
97
|
-
issuing a verdict.
|
|
51
|
+
crosses its gates with. Gates still emit their blocks and still record a decision; the decision's
|
|
52
|
+
**source** becomes the answer set instead of a live human, and the ledger says so. Generate one
|
|
53
|
+
with `harness gate --init --preset ci --by "<name>"`. Without one an unattended run waits at the
|
|
54
|
+
first gate, having built nothing.
|
|
55
|
+
- `--parallel-scopes N` → how many scopes build at once (default 4). Concurrency is a cost question
|
|
56
|
+
before it is a speed one — every extra leg is another worker's full context. `1` builds scopes one
|
|
57
|
+
at a time.
|
|
98
58
|
- `--no-qa` → skip the QA Edge Hunt. QA is a level-up, not a gate.
|
|
99
|
-
- `--
|
|
100
|
-
- `--
|
|
101
|
-
|
|
102
|
-
- `--exec-model / --eval-model / --qa-model <name>` → override
|
|
103
|
-
|
|
104
|
-
/ `.claude/settings.json` / skill defaults). `--orch-model` is shown in the L0 block and changes
|
|
105
|
-
nothing: the orchestrator is the session already running this command, so its model is the one
|
|
106
|
-
that session was started with.
|
|
59
|
+
- `--no-eval` → skip the evaluator; the run ships as not evaluated, and says so.
|
|
60
|
+
- `--rounds N` → override the outer circuit breaker (build+eval cycles, default 3). Passed to `init run` as `--max-rounds N`.
|
|
61
|
+
- `--attempts N` → override the inner circuit breaker (per-scope T0 attempts, default 5). Passed to `init run` as `--attempts N`.
|
|
62
|
+
- `--exec-model / --eval-model / --qa-model <name>` → override the resolved model matrix for this run
|
|
63
|
+
only (highest precedence over `.claude/settings.local.json` / `.claude/settings.json` / skill defaults).
|
package/commands/wire.md
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Write the wiring map — engine → seam → entry-point call site, per use case
|
|
3
3
|
---
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
This is gate L1a.5 — it front-loads the integration seam so no engine ships orphaned. The skill
|
|
7
|
-
is the sole writer of the committed `wiring-map.md`, resolved against `project-profile.md`'s
|
|
8
|
-
`entry_point`; `harness verify trace` later checks reachability against it.
|
|
9
|
-
|
|
10
|
-
It needs the spec folder (for the use cases) and the project profile. If either is missing, say
|
|
11
|
-
which one rather than inventing it.
|
|
4
|
+
Alias of `/ship plan`: use the **building** skill's plan activity on $ARGUMENTS and stop after the wiring map (the Wiring Review gate) — it needs the spec folder and `project-profile.md`; if either is missing, say which rather than inventing it.
|
|
@@ -33,6 +33,28 @@
|
|
|
33
33
|
// because a future host that reports failures through this event must not be able to satisfy the
|
|
34
34
|
// wall by firing at all.
|
|
35
35
|
//
|
|
36
|
+
// THE PLUGIN'S WORKERS ARE AGENT DEFINITIONS (`agents/<worker>.md`), and a dispatch of one is an
|
|
37
|
+
// `Agent` call whose result names the AGENT that ran (`tool_response.agentType`). An agent type the
|
|
38
|
+
// host cannot resolve is rejected upstream exactly as an unknown skill is, and the worker's skill is
|
|
39
|
+
// preloaded by the definition, so no `Skill` call happens inside it — the agent's name is the
|
|
40
|
+
// attestation, and the same result carries what the host measured (`totalTokens`, `totalDurationMs`,
|
|
41
|
+
// `resolvedModel`), recorded on the row so no session has to copy a figure by hand.
|
|
42
|
+
//
|
|
43
|
+
// THE MODEL THE CALL ASKED FOR IS RECORDED BESIDE THE ONE THAT RAN. The agent definitions say
|
|
44
|
+
// `model: inherit`, so a dispatch that carries no `model` runs on the launching session's model whatever
|
|
45
|
+
// the launch record asked for — measured across every archived run: the record said `sonnet` and the
|
|
46
|
+
// receipts of the runs that were compared named two different models. `model_requested` is the call's own
|
|
47
|
+
// `tool_input.model` (null when it carried none, which is "inherited", never a guess), and the ship gate
|
|
48
|
+
// reads `usage.model` against the launch record; without both columns a cost or a verdict can be read
|
|
49
|
+
// against a model that never ran.
|
|
50
|
+
//
|
|
51
|
+
// A SCOPE LAUNCHED IN THE BACKGROUND reports no agent at all: the result is "Async agent launched"
|
|
52
|
+
// plus an `agentId`, because the host has not run anything yet. Parallel scope builds are exactly that
|
|
53
|
+
// case, and without this arm the receipt hook went blind to every one of them. What the host did
|
|
54
|
+
// establish is that it accepted the call — an agent type it cannot resolve is rejected before this
|
|
55
|
+
// event — so the name the CALL carried (`subagent_type`) is the attestation, and the usage is null
|
|
56
|
+
// because there is none to record yet.
|
|
57
|
+
//
|
|
36
58
|
// WHAT IT WILL NOT ATTEST. A receipt is written only when the tool result NAMES the skill that ran
|
|
37
59
|
// (`tool_response.commandName`). An `Agent` dispatch whose prompt merely mentions `--order` gets no
|
|
38
60
|
// receipt: the prompt is a plan to dispatch, not evidence of one, and minting attestation from it
|
|
@@ -86,6 +108,60 @@ export function skillInvokedFrom(toolResponse) {
|
|
|
86
108
|
return name.slice(name.lastIndexOf(":") + 1) || null;
|
|
87
109
|
}
|
|
88
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Did the host launch this `Agent` call in the background and accept it?
|
|
113
|
+
*
|
|
114
|
+
* A background launch returns no `agentType`: its result says the agent was launched and names an
|
|
115
|
+
* `agentId`. Both must hold — a call asked to run in the background AND a result that reports a launch —
|
|
116
|
+
* so a failed or empty result never counts as one.
|
|
117
|
+
*
|
|
118
|
+
* @param {object} toolResponse - The `tool_response` block of an `Agent` call.
|
|
119
|
+
* @param {object} toolInput - The `tool_input` block of the same call.
|
|
120
|
+
* @returns {boolean} True for an accepted background launch.
|
|
121
|
+
*/
|
|
122
|
+
export function backgroundLaunch(toolResponse, toolInput) {
|
|
123
|
+
if (toolInput?.run_in_background !== true || !toolResponse) return false;
|
|
124
|
+
const text = typeof toolResponse === "string" ? toolResponse : JSON.stringify(toolResponse);
|
|
125
|
+
return /\bagentId\b/.test(text) && /launched/i.test(text);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Which worker an `Agent` dispatch ran, from the tool result — or, for a background launch, from the
|
|
130
|
+
* name the accepted call carried.
|
|
131
|
+
*
|
|
132
|
+
* The host reports the agent NAMESPACED (`shapeup-sdlc-plugin:orient`); the bare name is compared
|
|
133
|
+
* against the WorkOrder's `worker`, so a general-purpose agent that merely had `--order` in its prompt
|
|
134
|
+
* attests as `general-purpose` and the ingest gate refuses it.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} toolResponse - The `tool_response` block of an `Agent` call.
|
|
137
|
+
* @param {object} [toolInput] - The `tool_input` block of the same call.
|
|
138
|
+
* @returns {string|null} The bare agent name, or null when nothing names an agent.
|
|
139
|
+
*/
|
|
140
|
+
export function agentWorkerFrom(toolResponse, toolInput) {
|
|
141
|
+
const name = typeof toolResponse?.agentType === "string" && toolResponse.agentType
|
|
142
|
+
? toolResponse.agentType
|
|
143
|
+
: (backgroundLaunch(toolResponse, toolInput) ? toolInput?.subagent_type : null);
|
|
144
|
+
if (typeof name !== "string" || !name) return null;
|
|
145
|
+
return name.slice(name.lastIndexOf(":") + 1) || null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* What the host measured for an `Agent` dispatch, as the leg row carries it.
|
|
150
|
+
*
|
|
151
|
+
* Absent figures stay null — a figure the host did not give is never inferred and never zero.
|
|
152
|
+
*
|
|
153
|
+
* @param {object} toolResponse - The `tool_response` block of an `Agent` call.
|
|
154
|
+
* @returns {{tokens_total:(number|null), duration_ms:(number|null), model:(string|null)}} The usage.
|
|
155
|
+
*/
|
|
156
|
+
export function usageFrom(toolResponse) {
|
|
157
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null);
|
|
158
|
+
return {
|
|
159
|
+
tokens_total: num(toolResponse?.totalTokens),
|
|
160
|
+
duration_ms: num(toolResponse?.totalDurationMs),
|
|
161
|
+
model: typeof toolResponse?.resolvedModel === "string" ? toolResponse.resolvedModel : null,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
89
165
|
/**
|
|
90
166
|
* Build the receipt row for one dispatch.
|
|
91
167
|
*
|
|
@@ -106,12 +182,20 @@ export function receiptRow(order, skillInvoked, payload) {
|
|
|
106
182
|
skill_invoked: skillInvoked,
|
|
107
183
|
// Corroboration, not the wall — see the header. `success` is absent on hosts that do not
|
|
108
184
|
// report one, and an absent field must not read as a successful dispatch.
|
|
109
|
-
dispatch_ok: payload?.tool_response?.success === true
|
|
185
|
+
dispatch_ok: payload?.tool_response?.success === true || payload?.tool_response?.status === "completed"
|
|
186
|
+
|| backgroundLaunch(payload?.tool_response, payload?.tool_input),
|
|
110
187
|
tool: payload?.tool_name ?? null,
|
|
111
188
|
// Present when the dispatch came from a sub-agent, absent when the operator called it directly.
|
|
112
189
|
// That is the difference between an orchestrated leg and someone driving a skill by hand.
|
|
113
|
-
agent_id: payload?.agent_id ?? null,
|
|
114
|
-
agent_type: payload?.agent_type ??
|
|
190
|
+
agent_id: payload?.agent_id ?? payload?.tool_response?.agentId ?? null,
|
|
191
|
+
agent_type: payload?.agent_type ?? payload?.tool_response?.agentType
|
|
192
|
+
?? (backgroundLaunch(payload?.tool_response, payload?.tool_input) ? payload.tool_input.subagent_type : null),
|
|
193
|
+
// What the host measured for an Agent dispatch; null for a Skill call, which reports none.
|
|
194
|
+
usage: payload?.tool_name === "Agent" ? usageFrom(payload.tool_response) : null,
|
|
195
|
+
// The model the call asked for, as written; null when the call carried none and the agent inherited
|
|
196
|
+
// the session's. Compared with `usage.model` by whoever reads the row, never reconciled here.
|
|
197
|
+
model_requested: payload?.tool_name === "Agent" && typeof payload?.tool_input?.model === "string" && payload.tool_input.model
|
|
198
|
+
? payload.tool_input.model : null,
|
|
115
199
|
};
|
|
116
200
|
}
|
|
117
201
|
|
|
@@ -166,11 +250,13 @@ export async function main() {
|
|
|
166
250
|
const cited = orderPathFrom(p.tool_input);
|
|
167
251
|
if (!cited) defer("no --order threaded — not an orchestrated dispatch", "no-order");
|
|
168
252
|
|
|
169
|
-
const skillInvoked =
|
|
253
|
+
const skillInvoked = p.tool_name === "Agent"
|
|
254
|
+
? (agentWorkerFrom(p.tool_response, p.tool_input) ?? skillInvokedFrom(p.tool_response))
|
|
255
|
+
: skillInvokedFrom(p.tool_response);
|
|
170
256
|
if (!skillInvoked) {
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
defer("dispatch result names no resolved skill — nothing to attest", "no-skill-named");
|
|
257
|
+
// A host result that does not name what ran. Attesting here would mint the fact rather than
|
|
258
|
+
// record it.
|
|
259
|
+
defer("dispatch result names no resolved skill or agent — nothing to attest", "no-skill-named");
|
|
174
260
|
}
|
|
175
261
|
|
|
176
262
|
// The order path is relative to the shell; the receipt file is relative to the project root
|
|
@@ -190,7 +276,7 @@ export async function main() {
|
|
|
190
276
|
rule: written ? "receipt-written" : "receipt-write-failed",
|
|
191
277
|
reason: written
|
|
192
278
|
? `dispatch receipt: ${row.order_id} ran ${skillInvoked} (declared ${row.worker_declared}, ok=${row.dispatch_ok})`
|
|
193
|
-
: `dispatch receipt could not be written for ${row.order_id} —
|
|
279
|
+
: `dispatch receipt could not be written for ${row.order_id} — this attempt cannot be counted as attested`,
|
|
194
280
|
};
|
|
195
281
|
});
|
|
196
282
|
}
|