shapeup-sdlc 1.6.2
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 +20 -0
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.env.shapeup.example +14 -0
- package/AGENTS.md +133 -0
- package/LICENSE +21 -0
- package/README.md +362 -0
- package/SECURITY.md +72 -0
- package/bin/init.mjs +329 -0
- package/commands/build.md +14 -0
- package/commands/eval.md +15 -0
- package/commands/hammer.md +12 -0
- package/commands/orient.md +11 -0
- package/commands/qa.md +12 -0
- package/commands/retro.md +13 -0
- package/commands/scopes.md +14 -0
- package/commands/shape.md +12 -0
- package/commands/ship.md +53 -0
- package/commands/wire.md +11 -0
- package/hooks/anti-rationalization.mjs +244 -0
- package/hooks/compact-snapshot.mjs +47 -0
- package/hooks/gate-deadline.mjs +151 -0
- package/hooks/gate-intake.mjs +110 -0
- package/hooks/gate-l2.mjs +161 -0
- package/hooks/gate-zerowork.mjs +264 -0
- package/hooks/hooks.json +118 -0
- package/hooks/lib/decision.mjs +183 -0
- package/hooks/safety-spine.mjs +296 -0
- package/hooks/sandbox-guard.mjs +172 -0
- package/hooks/session-rehydrate.mjs +109 -0
- package/hooks/slop-cleaner.mjs +176 -0
- package/oracles/_shared.mjs +46 -0
- package/oracles/http-oracle.mjs +155 -0
- package/oracles/index.mjs +36 -0
- package/oracles/process-oracle.mjs +146 -0
- package/oracles/snapshot-oracle.mjs +119 -0
- package/oracles/test-oracle.mjs +138 -0
- package/package.json +49 -0
- package/skills/advisor-protocol/SKILL.md +171 -0
- package/skills/ba-pitch-analyzer/SKILL.md +175 -0
- package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +84 -0
- package/skills/ba-pitch-analyzer/assets/templates/api-feasibility.tmpl.md +65 -0
- package/skills/ba-pitch-analyzer/assets/templates/assess-report.tmpl.md +127 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/be-service.contract.tmpl.md +62 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/offline-storage.contract.tmpl.md +92 -0
- package/skills/ba-pitch-analyzer/assets/templates/contracts/third-party-api.contract.tmpl.md +66 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/context-map.tmpl.md +64 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/event-choreography.tmpl.md +77 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/migration-plan.tmpl.md +104 -0
- package/skills/ba-pitch-analyzer/assets/templates/cross-context/team-handoff.tmpl.md +79 -0
- package/skills/ba-pitch-analyzer/assets/templates/domain-model.tmpl.md +79 -0
- package/skills/ba-pitch-analyzer/assets/templates/feedback.tmpl.md +87 -0
- package/skills/ba-pitch-analyzer/assets/templates/integration.tmpl.md +64 -0
- package/skills/ba-pitch-analyzer/assets/templates/run-state.tmpl.md +77 -0
- package/skills/ba-pitch-analyzer/assets/templates/scope-summary.tmpl.md +105 -0
- package/skills/ba-pitch-analyzer/assets/templates/synthesis.tmpl.md +215 -0
- package/skills/ba-pitch-analyzer/assets/templates/task-board.tmpl.md +35 -0
- package/skills/ba-pitch-analyzer/assets/templates/task-spike.tmpl.md +86 -0
- package/skills/ba-pitch-analyzer/assets/templates/task.tmpl.md +84 -0
- package/skills/ba-pitch-analyzer/assets/templates/usecase.tmpl.md +105 -0
- package/skills/ba-pitch-analyzer/assets/templates/ux-behavior.tmpl.md +65 -0
- package/skills/ba-pitch-analyzer/references/contract-patterns.md +152 -0
- package/skills/ba-pitch-analyzer/references/ddd-patterns.md +173 -0
- package/skills/ba-pitch-analyzer/references/doc-schemas.md +275 -0
- package/skills/ba-pitch-analyzer/references/integration-analysis.md +152 -0
- package/skills/ba-pitch-analyzer/references/task-generation.md +623 -0
- package/skills/ba-pitch-analyzer/references/test-surface.md +102 -0
- package/skills/ba-pitch-analyzer/references/ux-behavior-patterns.md +269 -0
- package/skills/ba-pitch-analyzer/scripts/board-derive.mjs +241 -0
- package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +239 -0
- package/skills/coach/SKILL.md +202 -0
- package/skills/orient/SKILL.md +262 -0
- package/skills/qa-edge-hunter/SKILL.md +369 -0
- package/skills/scope-architect/SKILL.md +111 -0
- package/skills/scope-hammer/SKILL.md +186 -0
- package/skills/shapeup/SKILL.md +403 -0
- package/skills/shapeup/resources/breadboard-reflection.md +88 -0
- package/skills/shapeup/resources/breadboarding.md +334 -0
- package/skills/shapeup/resources/context-compaction.md +240 -0
- package/skills/shapeup/resources/framing-doc.md +92 -0
- package/skills/shapeup/resources/kickoff-doc.md +95 -0
- package/skills/shapeup/resources/shaping.md +194 -0
- package/skills/shapeup/resources/spike.md +86 -0
- package/skills/solution-architect/SKILL.md +129 -0
- package/skills/spec-evaluator/README.md +93 -0
- package/skills/spec-evaluator/SKILL.md +213 -0
- package/skills/spec-evaluator/references/anti-leniency.md +50 -0
- package/skills/spec-evaluator/references/dimension-contract.md +126 -0
- package/skills/spec-evaluator/references/dimensions/_registry.md +40 -0
- package/skills/spec-evaluator/references/dimensions/completeness.md +102 -0
- package/skills/spec-evaluator/references/dimensions/integration.md +129 -0
- package/skills/spec-evaluator/references/dimensions/performance.md +48 -0
- package/skills/spec-evaluator/references/dimensions/security.md +60 -0
- package/skills/spec-evaluator/references/dimensions/spec-conformance.md +134 -0
- package/skills/spec-evaluator/references/dimensions/tdd-surface.md +110 -0
- package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +100 -0
- package/skills/spec-evaluator/references/probing.md +177 -0
- package/skills/spec-evaluator/references/report-schema.md +101 -0
- package/skills/spec-evaluator/references/verdict-ledger.md +92 -0
- package/skills/spec-evaluator/scripts/verdict-ledger.mjs +166 -0
- package/skills/task-executor/SKILL.md +194 -0
- package/skills/tech-lead/README.md +71 -0
- package/skills/tech-lead/SKILL.md +459 -0
- package/skills/tech-lead/references/delegation.md +254 -0
- package/skills/tech-lead/references/gates.md +379 -0
- package/skills/tech-lead/references/invocation.md +45 -0
- package/skills/tech-lead/references/ledger-schema.md +214 -0
- package/skills/tech-lead/references/round-protocol.md +184 -0
- package/skills/tech-lead/references/state-model.md +66 -0
- package/skills/tech-lead/references/tiny-lane.md +52 -0
- package/skills/tech-lead/schemas/domain.schema.json +2294 -0
- package/skills/tech-lead/schemas/gate-answers.schema.json +92 -0
- package/skills/tech-lead/schemas/work-order.schema.json +21 -0
- package/skills/tech-lead/schemas/work-result.schema.json +40 -0
- package/skills/tech-lead/scripts/aegis-digest.mjs +124 -0
- package/skills/tech-lead/scripts/budget-check.mjs +156 -0
- package/skills/tech-lead/scripts/compile-order.mjs +518 -0
- package/skills/tech-lead/scripts/fit-check.mjs +196 -0
- package/skills/tech-lead/scripts/gate-answers.mjs +338 -0
- package/skills/tech-lead/scripts/ingest-result.mjs +270 -0
- package/skills/tech-lead/scripts/init-run.mjs +326 -0
- package/skills/tech-lead/scripts/lib/argv.mjs +224 -0
- package/skills/tech-lead/scripts/lib/contract-md.mjs +481 -0
- package/skills/tech-lead/scripts/lib/is-main.mjs +82 -0
- package/skills/tech-lead/scripts/lib/paths.mjs +278 -0
- package/skills/tech-lead/scripts/lib/ratchet-tree.mjs +112 -0
- package/skills/tech-lead/scripts/run-snapshot.mjs +269 -0
- package/skills/tech-lead/scripts/ship-report.mjs +275 -0
- package/skills/tech-lead/scripts/stats.mjs +423 -0
- package/skills/tech-lead/scripts/t0-verify.mjs +470 -0
- package/skills/tech-lead/scripts/trace-lint.mjs +367 -0
- package/skills/tech-lead/scripts/validate-envelope.mjs +283 -0
- package/skills/translator/README.md +66 -0
- package/skills/translator/SKILL.md +258 -0
- package/skills/translator/references/preservation-rules.md +102 -0
package/SECURITY.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
This plugin installs **eight hook entries (seven Node scripts + one `echo`)**, four of them in
|
|
4
|
+
a `PreToolUse` position where they can deny tool calls. That is the product — and it is also
|
|
5
|
+
exactly the kind of surface a careful reviewer should want spelled out before installing.
|
|
6
|
+
This page is that spelling-out.
|
|
7
|
+
|
|
8
|
+
## Reporting a vulnerability
|
|
9
|
+
|
|
10
|
+
Use [GitHub private vulnerability reporting](https://github.com/nguyenvanphituoc/shapeup-sdlc-plugin/security/advisories/new)
|
|
11
|
+
for anything exploitable — especially anything that would let a run **escape a deny** (talk
|
|
12
|
+
past GATE L2, write outside a substrate, widen its own safety overrides) or exfiltrate data.
|
|
13
|
+
For non-sensitive hardening ideas, an ordinary issue is fine.
|
|
14
|
+
|
|
15
|
+
In scope: the hooks, the pipeline scripts (`compile-order` / `ingest-result` /
|
|
16
|
+
`validate-envelope` / `t0-verify`), the installer/migration shell scripts, and any prompt-
|
|
17
|
+
injection path through skill files. Please do not test against machines you don't own.
|
|
18
|
+
|
|
19
|
+
## The claims, stated so they can be falsified
|
|
20
|
+
|
|
21
|
+
1. **No hook or pipeline script makes a network request.** There is no `fetch`, no
|
|
22
|
+
`node:http(s)`, no `node:net`, no shelling out to `curl`/`wget` anywhere in `hooks/` or
|
|
23
|
+
`skills/*/scripts/`. Verify: `grep -rnE "fetch|node:http|node:net|curl|wget" hooks/ skills/*/scripts/`.
|
|
24
|
+
2. **No hook has dependencies.** Plain `.mjs`, Node standard library only, no `node_modules`,
|
|
25
|
+
no install-time scripts. What you read is what runs.
|
|
26
|
+
3. **Every hook is fail-open by design.** Unparseable input, missing state files, or an
|
|
27
|
+
unrecognized invocation shape → the hook defers and the normal permission flow proceeds. A
|
|
28
|
+
hook denies only when it can positively prove its condition (a non-green board, a matched
|
|
29
|
+
destructive command, a path outside a declared substrate, an invalid order file).
|
|
30
|
+
4. **The model cannot widen its own safety envelope.** The escape hatch
|
|
31
|
+
(`.shapeup/safety-overrides.json`) is human-authored; `safety-spine` itself denies any
|
|
32
|
+
write/move/delete touching that file, a malformed overrides file is treated as absent
|
|
33
|
+
(override channel fails closed), and every exercised override is logged.
|
|
34
|
+
5. **Stop hooks never block.** The two Stop-position hooks are advisory: they emit at most a
|
|
35
|
+
`systemMessage` and always exit 0.
|
|
36
|
+
|
|
37
|
+
If you find any of these to be false, that is a vulnerability — report it as claim #ⁿ.
|
|
38
|
+
|
|
39
|
+
## What each hook does
|
|
40
|
+
|
|
41
|
+
Wired in [`hooks/hooks.json`](hooks/hooks.json); every script is short enough to read in one
|
|
42
|
+
sitting, and reading them is the recommended review.
|
|
43
|
+
|
|
44
|
+
| Hook | Event (matcher) | Reads | Can deny | Never does |
|
|
45
|
+
|---|---|---|---|---|
|
|
46
|
+
| [`safety-spine.mjs`](hooks/safety-spine.mjs) | PreToolUse (`Bash\|Read\|Write\|Edit\|MultiEdit`) | The proposed command/path; `.shapeup/safety-overrides.json` | Yes — provably destructive ops only: `rm -rf` on unrecoverable targets, `git push --force` / push to main, `git reset --hard`, `git clean -fdx`, `DROP TABLE`/`TRUNCATE`, reads of `.env`/keys/cloud credentials, and any write to its own overrides file | Never blocks an unmatched command; `--force-with-lease` stays allowed |
|
|
47
|
+
| [`gate-l2.mjs`](hooks/gate-l2.mjs) | PreToolUse (`Skill`) | The round's task board (`.shapeup/<slug>/tasks/`) | Yes — the once-per-round EVAL dispatch while any task is unfinished | Never gates a single-task eval (`--task`); no board → defers |
|
|
48
|
+
| [`validate-envelope.mjs`](skills/tech-lead/scripts/validate-envelope.mjs) | PreToolUse (`Skill\|Agent`) | The `--order` file named in the dispatch; the JSON schemas | Yes — a worker dispatch whose order file is missing or schema-invalid | Never gates a dispatch that carries no `--order` (standalone skill use stays free) |
|
|
49
|
+
| [`sandbox-guard.mjs`](hooks/sandbox-guard.mjs) | PreToolUse (`Edit\|Write\|MultiEdit`) | The target path; the active scope contract | Yes — writes outside the active scope's substrate whitelist | No-op unless a scope is active; the active feature's own `.shapeup/<slug>/` run-trace is always writable. Appends denials to the local pathology log |
|
|
50
|
+
| [`anti-rationalization.mjs`](hooks/anti-rationalization.mjs) | Stop | Board/T0 facts vs. the reply's completion claims | **No** — advisory `systemMessage` only | Never `decision:"block"`, never exit 2 |
|
|
51
|
+
| [`slop-cleaner.mjs`](hooks/slop-cleaner.mjs) | Stop | The session's git diff (local `git diff`, via `spawnSync`) | **No** — advisory `systemMessage` flagging TODO / `console.log` / commented-out leftovers | Same — never blocks |
|
|
52
|
+
| [`compact-snapshot.mjs`](hooks/compact-snapshot.mjs) | PreCompact | Run state | No — writes `.shapeup/<slug>/run-snapshot.json` before compaction | Touches nothing outside `.shapeup/` |
|
|
53
|
+
| [`session-rehydrate.mjs`](hooks/session-rehydrate.mjs) | SessionStart (`compact\|resume`) | The saved run snapshot | No — injects the "trust the files, not the summary" hint when a run is in flight | Silent when no run is in flight |
|
|
54
|
+
|
|
55
|
+
(The eighth `hooks.json` entry is a plain `echo` on SessionStart confirming the plugin loaded.)
|
|
56
|
+
|
|
57
|
+
## Data handling
|
|
58
|
+
|
|
59
|
+
- **Nothing leaves the machine.** Run state lives in the gitignored `.shapeup/`; telemetry
|
|
60
|
+
is a per-machine JSONL shard under `shapeup/metrics/` that travels only if you
|
|
61
|
+
commit it. There is no phone-home of any kind.
|
|
62
|
+
- **The safety-spine actively blocks secret reads** (`.env`, `*.pem`, `*.key`, ssh/cloud
|
|
63
|
+
credentials) rather than merely not making them.
|
|
64
|
+
- The installer (`scripts/install-harness.sh`) writes only into the target project
|
|
65
|
+
(`.claude/`, `.agents/`, `.codex/`, `shapeup/`, `.gitignore`) and tells you what it
|
|
66
|
+
is going to do first; the `curl | bash` form requires an explicit `--yes` for exactly that
|
|
67
|
+
reason.
|
|
68
|
+
|
|
69
|
+
## Supported versions
|
|
70
|
+
|
|
71
|
+
Pre-1.0-ecosystem project, solo-maintained: fixes land on `main` and ship in the next tag.
|
|
72
|
+
Report against the latest release.
|
package/bin/init.mjs
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// npx shapeup-sdlc init — cross-platform scaffolding installer (P1-1).
|
|
3
|
+
//
|
|
4
|
+
// A faithful Node port of scripts/install-harness.sh + lib-harness.sh, with the two
|
|
5
|
+
// differences that were the point of porting it:
|
|
6
|
+
// • no bash, no jq, no python3 — Node parses JSON natively, so the settings.json merge
|
|
7
|
+
// that needed a jq→python3→give-up fallback chain is just JSON.parse. Works on Windows.
|
|
8
|
+
// • no Playwright prerequisite — the browser is a lazy dependency, checked by the eval
|
|
9
|
+
// skill at the moment a [ui] criterion is actually probed (see
|
|
10
|
+
// skills/spec-evaluator/references/probing.md), never at install time.
|
|
11
|
+
//
|
|
12
|
+
// The bash installers remain the stable curl-able entrypoints for existing bookmarks;
|
|
13
|
+
// this is the `npx` front door. Both produce the same layout, and drift between them is
|
|
14
|
+
// a bug (tests/structural keeps the shared bits honest).
|
|
15
|
+
//
|
|
16
|
+
// Usage:
|
|
17
|
+
// npx shapeup-sdlc init [-d <dir>] [-y] [-o] [--cli claude,antigravity,codex|all]
|
|
18
|
+
//
|
|
19
|
+
// What it configures (identical to install-harness.sh):
|
|
20
|
+
// AGENTS.md harness block · Claude Code plugin (CLI or settings.json merge) ·
|
|
21
|
+
// Antigravity .agents/skills + subagents · Codex .codex/skills · CLAUDE.md @AGENTS.md
|
|
22
|
+
// import · .gitignore rules · shapeup/metrics/ · Tier C templates
|
|
23
|
+
|
|
24
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync, cpSync, readdirSync, appendFileSync } from "node:fs";
|
|
25
|
+
import { resolve, join, dirname, basename } from "node:path";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
import { spawnSync } from "node:child_process";
|
|
28
|
+
import { createInterface } from "node:readline";
|
|
29
|
+
import { LOCAL, LEGACY, metricsDir } from "../skills/tech-lead/scripts/lib/paths.mjs";
|
|
30
|
+
|
|
31
|
+
/** The root a project migrating off the pre-ADR-0001 layout may still be carrying. */
|
|
32
|
+
const LEGACY_LOCAL = LEGACY.local;
|
|
33
|
+
|
|
34
|
+
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
35
|
+
const REPO = "nguyenvanphituoc/shapeup-sdlc-plugin";
|
|
36
|
+
const MARKETPLACE_KEY = "nvptuoc-marketplace";
|
|
37
|
+
const PLUGIN_KEY = "shapeup-sdlc-plugin@nvptuoc-marketplace";
|
|
38
|
+
const ALL_CLIS = ["claude", "antigravity", "codex"];
|
|
39
|
+
|
|
40
|
+
// ---- args -------------------------------------------------------------------
|
|
41
|
+
const argv = process.argv.slice(2);
|
|
42
|
+
const usage = `Usage: npx shapeup-sdlc init [options]
|
|
43
|
+
Options:
|
|
44
|
+
-d, --directory <path> Target project directory (default: current directory)
|
|
45
|
+
--cli <list> Comma-separated: claude,antigravity,codex or "all" (default: all)
|
|
46
|
+
-o, --override Overwrite existing files in target
|
|
47
|
+
-y, --yes Run unattended (answer yes to all prompts)
|
|
48
|
+
-h, --help Print this help`;
|
|
49
|
+
|
|
50
|
+
let targetDir = ".", yes = false, override = false, clis = [...ALL_CLIS];
|
|
51
|
+
const positional = [];
|
|
52
|
+
for (let i = 0; i < argv.length; i++) {
|
|
53
|
+
const a = argv[i];
|
|
54
|
+
if (a === "-d" || a === "--directory") targetDir = argv[++i];
|
|
55
|
+
else if (a === "-y" || a === "--yes") yes = true;
|
|
56
|
+
else if (a === "-o" || a === "--override") override = true;
|
|
57
|
+
else if (a === "--cli") {
|
|
58
|
+
const v = argv[++i] || "";
|
|
59
|
+
clis = v === "all" ? [...ALL_CLIS] : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
60
|
+
const bad = clis.filter((c) => !ALL_CLIS.includes(c));
|
|
61
|
+
if (bad.length) { console.error(`Unknown CLI(s): ${bad.join(", ")}. Valid: ${ALL_CLIS.join(", ")}, all`); process.exit(1); }
|
|
62
|
+
} else if (a === "-h" || a === "--help") { console.log(usage); process.exit(0); }
|
|
63
|
+
else if (a.startsWith("-")) { console.error(`Unknown option: ${a}\n${usage}`); process.exit(1); }
|
|
64
|
+
else positional.push(a);
|
|
65
|
+
}
|
|
66
|
+
if (positional.length && positional[0] !== "init") {
|
|
67
|
+
console.error(`Unknown command: ${positional[0]} (only "init" is supported)\n${usage}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const target = resolve(targetDir);
|
|
72
|
+
if (!existsSync(target)) { console.error(`Target directory does not exist: ${target}`); process.exit(1); }
|
|
73
|
+
console.log(`Installing Shape Up SDLC Harness into: ${target}`);
|
|
74
|
+
console.log(`CLIs: ${clis.join(", ")}`);
|
|
75
|
+
|
|
76
|
+
// ---- confirmation -----------------------------------------------------------
|
|
77
|
+
if (!yes) {
|
|
78
|
+
if (!process.stdin.isTTY) {
|
|
79
|
+
console.error("Non-interactive environment and no --yes given. Re-run with -y.");
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
83
|
+
const answer = await new Promise((res) => rl.question(`Proceed with installation in ${target}? [y/N] `, res));
|
|
84
|
+
rl.close();
|
|
85
|
+
if (!/^y$/i.test(answer.trim())) { console.log("Installation cancelled."); process.exit(1); }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---- 0. AGENTS.md harness block ---------------------------------------------
|
|
89
|
+
const agentsSrc = join(PKG_ROOT, "AGENTS.md");
|
|
90
|
+
const agentsDst = join(target, "AGENTS.md");
|
|
91
|
+
if (!existsSync(agentsSrc)) {
|
|
92
|
+
console.warn("Warning: AGENTS.md not found in package — skipping root AGENTS.md setup.");
|
|
93
|
+
} else {
|
|
94
|
+
const block = readFileSync(agentsSrc, "utf8");
|
|
95
|
+
if (!existsSync(agentsDst)) {
|
|
96
|
+
writeFileSync(agentsDst, block);
|
|
97
|
+
console.log(`Created ${rel(agentsDst)} from harness template`);
|
|
98
|
+
} else {
|
|
99
|
+
const existing = readFileSync(agentsDst, "utf8");
|
|
100
|
+
if (existing.includes("<!-- HARNESS_START -->")) {
|
|
101
|
+
// Replace the old block in place (block goes at the top, mirroring the bash awk).
|
|
102
|
+
const stripped = existing
|
|
103
|
+
.split(/\r?\n/)
|
|
104
|
+
.reduce((acc, line) => {
|
|
105
|
+
if (line.includes("<!-- HARNESS_START -->")) acc.skip = true;
|
|
106
|
+
else if (line.includes("<!-- HARNESS_END -->")) acc.skip = false;
|
|
107
|
+
else if (!acc.skip) acc.out.push(line);
|
|
108
|
+
return acc;
|
|
109
|
+
}, { out: [], skip: false }).out.join("\n");
|
|
110
|
+
writeFileSync(agentsDst, block + stripped);
|
|
111
|
+
console.log(`Updated harness block in ${rel(agentsDst)}`);
|
|
112
|
+
} else {
|
|
113
|
+
appendFileSync(agentsDst, "\n" + block);
|
|
114
|
+
console.log(`Appended harness block to existing ${rel(agentsDst)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---- 1. per-CLI install -----------------------------------------------------
|
|
120
|
+
for (const cli of clis) {
|
|
121
|
+
if (cli === "claude") installClaude();
|
|
122
|
+
else replaceSkills(cli);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---- 2. wire each CLI to the root AGENTS.md ---------------------------------
|
|
126
|
+
if (clis.includes("claude")) ensureAgentImport(join(target, "CLAUDE.md"), "CLAUDE.md", "claude");
|
|
127
|
+
if (clis.includes("antigravity")) ensureAgentImport(join(target, ".agents", "AGENTS.md"), ".agents/AGENTS.md", "auto");
|
|
128
|
+
if (clis.includes("codex")) ensureAgentImport(join(target, ".codex", "AGENTS.md"), ".codex/AGENTS.md", "auto");
|
|
129
|
+
|
|
130
|
+
// ---- 3. .gitignore ----------------------------------------------------------
|
|
131
|
+
// Both roots are listed, deliberately. A project may be mid-migration (0006 moves `.shapeup/`
|
|
132
|
+
// to `.shapeup/`), and a run trace committed by accident during that window is exactly the mistake
|
|
133
|
+
// the tier split exists to prevent. Ignoring a directory that does not exist costs nothing.
|
|
134
|
+
const GITIGNORE_RULE = `# Shape Up SDLC run workspace
|
|
135
|
+
${LOCAL}/
|
|
136
|
+
${LEGACY_LOCAL}/
|
|
137
|
+
|
|
138
|
+
# Shape Up SDLC Tier C — per-member local config (templates *.example stay committed).
|
|
139
|
+
# The env file is SHAPEUP_-namespaced (filename + keys) so it never collides with, or gets
|
|
140
|
+
# confused with, this project's own .env / .env.local.
|
|
141
|
+
.claude/settings.local.json
|
|
142
|
+
.env.shapeup.local
|
|
143
|
+
!.env.shapeup.example
|
|
144
|
+
!.claude/settings.local.example.json`;
|
|
145
|
+
const gitignore = join(target, ".gitignore");
|
|
146
|
+
if (existsSync(gitignore)) {
|
|
147
|
+
if (!readFileSync(gitignore, "utf8").includes(`${LOCAL}/`)) {
|
|
148
|
+
appendFileSync(gitignore, "\n" + GITIGNORE_RULE + "\n");
|
|
149
|
+
console.log("Added Shape Up SDLC ignore rules to .gitignore");
|
|
150
|
+
} else console.log(`${LOCAL}/ already ignored in .gitignore`);
|
|
151
|
+
} else {
|
|
152
|
+
writeFileSync(gitignore, GITIGNORE_RULE + "\n");
|
|
153
|
+
console.log("Created .gitignore and added ignore rules");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---- 4. telemetry shard dir + Tier C templates ------------------------------
|
|
157
|
+
mkdirSync(metricsDir(target), { recursive: true });
|
|
158
|
+
for (const [srcRel, note] of [
|
|
159
|
+
[".claude/settings.local.example.json", "copy to settings.local.json and edit"],
|
|
160
|
+
[".env.shapeup.example", "copy to .env.shapeup.local and edit"],
|
|
161
|
+
]) {
|
|
162
|
+
const src = join(PKG_ROOT, srcRel);
|
|
163
|
+
if (existsSync(src)) {
|
|
164
|
+
const dst = join(target, srcRel);
|
|
165
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
166
|
+
cpSync(src, dst);
|
|
167
|
+
console.log(`Installed ${srcRel} (${note})`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log("\n✅ Harness installation and scaffolding completed.");
|
|
172
|
+
console.log(" Next: open a Claude Code session in this directory and run /ship \"<your idea>\".");
|
|
173
|
+
console.log(" ([ui] evaluation needs a browser — `npx playwright install chromium` — but only");
|
|
174
|
+
console.log(" when a run actually reaches a [ui] criterion; nothing else requires it.)");
|
|
175
|
+
|
|
176
|
+
// ---- helpers ----------------------------------------------------------------
|
|
177
|
+
function rel(p) { return p.startsWith(target) ? p.slice(target.length + 1) || "." : p; }
|
|
178
|
+
|
|
179
|
+
function installClaude() {
|
|
180
|
+
const settingsFile = join(target, ".claude", "settings.json");
|
|
181
|
+
mkdirSync(join(target, ".claude"), { recursive: true });
|
|
182
|
+
|
|
183
|
+
// Primary path: the claude CLI registers the marketplace in the live session AND writes
|
|
184
|
+
// project-scoped settings. Single command strings + shell so Windows resolves claude.cmd;
|
|
185
|
+
// every argument is a static constant, nothing user-controlled is interpolated.
|
|
186
|
+
const sh = (cmd, opts = {}) => spawnSync(cmd, { shell: true, ...opts });
|
|
187
|
+
const have = sh("claude --version", { stdio: "ignore" });
|
|
188
|
+
if (have.status === 0) {
|
|
189
|
+
console.log(" [claude] registering marketplace + installing plugin via claude CLI…");
|
|
190
|
+
const add = sh(`claude plugin marketplace add --scope project ${REPO}`, { cwd: target, stdio: "inherit" });
|
|
191
|
+
const ins = add.status === 0
|
|
192
|
+
? sh(`claude plugin install --scope project ${PLUGIN_KEY}`, { cwd: target, stdio: "inherit" })
|
|
193
|
+
: add;
|
|
194
|
+
if (ins.status === 0) {
|
|
195
|
+
// The CLI registers the marketplace and enables the plugin. It does NOT know about the
|
|
196
|
+
// pipeline permission grant, so this path has to add it — and until v1.6.1 it did not,
|
|
197
|
+
// while the comment below claimed both paths merged it. Measured on a fresh `npx
|
|
198
|
+
// shapeup-sdlc init`: `permissions.allow` came out EMPTY on every machine with the claude
|
|
199
|
+
// CLI installed, which is the common case and the one that prints success. That is FC-02
|
|
200
|
+
// exactly — an enforcement point inert on the path people actually take — and the grant it
|
|
201
|
+
// skipped is the one that exists because a headless run without it was denied approval 26
|
|
202
|
+
// times in a single session.
|
|
203
|
+
//
|
|
204
|
+
// Merged, never overwritten: re-read what the CLI just wrote and add only the allow list.
|
|
205
|
+
let written = {};
|
|
206
|
+
if (existsSync(settingsFile)) {
|
|
207
|
+
try { written = JSON.parse(readFileSync(settingsFile, "utf8")); }
|
|
208
|
+
catch (e) {
|
|
209
|
+
console.error(` [claude] plugin installed, but ${rel(settingsFile)} is not valid JSON (${e.message}) —`);
|
|
210
|
+
console.error(" the pipeline permission grant was NOT added. Copy it from .claude/settings.local.example.json.");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
mergePipelinePermissions(written);
|
|
215
|
+
writeFileSync(settingsFile, JSON.stringify(written, null, 2) + "\n");
|
|
216
|
+
console.log(" [claude] plugin installed at project scope + pipeline permissions granted — run /reload-plugins to activate in a live session");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
console.log(" [claude] Warning: claude CLI failed — falling back to writing settings.json directly");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Fallback: merge settings.json natively (the whole reason this file is Node).
|
|
223
|
+
//
|
|
224
|
+
// (See mergePipelinePermissions below — it runs on both paths.)
|
|
225
|
+
let settings = {};
|
|
226
|
+
if (existsSync(settingsFile)) {
|
|
227
|
+
try { settings = JSON.parse(readFileSync(settingsFile, "utf8")); }
|
|
228
|
+
catch (e) {
|
|
229
|
+
console.error(` [claude] ${rel(settingsFile)} is not valid JSON (${e.message}) — refusing to overwrite it.`);
|
|
230
|
+
console.error(` Fix the file, or add manually: extraKnownMarketplaces.${MARKETPLACE_KEY} + enabledPlugins["${PLUGIN_KEY}"]`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
settings.extraKnownMarketplaces = settings.extraKnownMarketplaces || {};
|
|
235
|
+
settings.extraKnownMarketplaces[MARKETPLACE_KEY] = { source: { source: "github", repo: REPO } };
|
|
236
|
+
settings.enabledPlugins = settings.enabledPlugins || {};
|
|
237
|
+
settings.enabledPlugins[PLUGIN_KEY] = true;
|
|
238
|
+
mergePipelinePermissions(settings);
|
|
239
|
+
writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
240
|
+
console.log(` [claude] merged marketplace + plugin + pipeline permissions into ${rel(settingsFile)}`);
|
|
241
|
+
console.log(" [claude] the plugin auto-enables on the next session opened in this directory");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Pre-approve the harness's OWN pipeline scripts, and nothing else.
|
|
246
|
+
*
|
|
247
|
+
* WHY THIS EXISTS (measured, on this project's own benchmark).
|
|
248
|
+
*
|
|
249
|
+
* Every load-bearing step of a run is a Node script that ships with the plugin and therefore
|
|
250
|
+
* lives OUTSIDE the project — `${CLAUDE_PLUGIN_ROOT}/skills/**\/scripts/*.mjs`. Under any
|
|
251
|
+
* permission mode short of `bypassPermissions`, executing a script from outside the working
|
|
252
|
+
* directory needs approval. In an interactive session you click once and forget it. In a headless
|
|
253
|
+
* one there is nobody to click, and the run cannot take its first step.
|
|
254
|
+
*
|
|
255
|
+
* That is not hypothetical. On `sdd-harness-bench`, the run receipt step (`init-run.mjs`) was
|
|
256
|
+
* attempted six different ways in a single session — direct, via a heredoc, via two hand-written
|
|
257
|
+
* wrapper scripts, via a sub-agent — and every one came back "This command requires approval".
|
|
258
|
+
* The agent eventually gave up on the harness and built the feature by hand. It is the failure the
|
|
259
|
+
* receipt was designed to make visible, arriving through the door the receipt itself opened.
|
|
260
|
+
*
|
|
261
|
+
* Scope is deliberately narrow: `node <plugin>/skills/.../scripts/*.mjs`, by prefix. This grants
|
|
262
|
+
* the harness the right to run its own deterministic, dependency-free, network-free scripts. It
|
|
263
|
+
* grants no general `Bash(node:*)`, which would be a much larger ask for a much smaller reason.
|
|
264
|
+
*
|
|
265
|
+
* BOTH SPELLINGS ARE GRANTED, and that is the point of v1.5's leg-2 fix. The skills now write
|
|
266
|
+
* every invocation in the QUOTED literal form — `node "${CLAUDE_PLUGIN_ROOT}/skills/…"` — because
|
|
267
|
+
* the unquoted form breaks the moment the plugin is installed under a path with a space in it
|
|
268
|
+
* (`~/Library/Application Support/…`), which `lib/is-main.mjs` documents as a measured case, not a
|
|
269
|
+
* hypothetical. A prefix rule is a literal string match, so the quote character would otherwise
|
|
270
|
+
* put every call site back outside the grant — the exact mismatch this fix exists to remove. The
|
|
271
|
+
* unquoted prefix stays for older prose and for anything a user has already typed.
|
|
272
|
+
*
|
|
273
|
+
* `tests/structural/14-invocation-paths.mjs` asserts that every documented call site is in a form
|
|
274
|
+
* one of these prefixes actually matches, so the two can never drift apart again.
|
|
275
|
+
*
|
|
276
|
+
* @param {object} settings - Parsed settings.json, mutated in place.
|
|
277
|
+
* @returns {void}
|
|
278
|
+
*/
|
|
279
|
+
function mergePipelinePermissions(settings) {
|
|
280
|
+
const OWNERS = ["tech-lead", "ba-pitch-analyzer", "spec-evaluator"];
|
|
281
|
+
const PREFIXES = OWNERS.flatMap((o) => [
|
|
282
|
+
`node \${CLAUDE_PLUGIN_ROOT}/skills/${o}/scripts/`,
|
|
283
|
+
`node "\${CLAUDE_PLUGIN_ROOT}/skills/${o}/scripts/`,
|
|
284
|
+
]);
|
|
285
|
+
settings.permissions = settings.permissions || {};
|
|
286
|
+
const allow = new Set(settings.permissions.allow || []);
|
|
287
|
+
for (const p of PREFIXES) allow.add(`Bash(${p}:*)`);
|
|
288
|
+
settings.permissions.allow = [...allow];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function replaceSkills(cli) {
|
|
292
|
+
const src = join(PKG_ROOT, "skills");
|
|
293
|
+
const dest = join(target, cli === "antigravity" ? ".agents" : ".codex", "skills");
|
|
294
|
+
mkdirSync(dest, { recursive: true });
|
|
295
|
+
let n = 0;
|
|
296
|
+
for (const name of readdirSync(src)) {
|
|
297
|
+
const skillPath = join(src, name);
|
|
298
|
+
if (!existsSync(join(skillPath, "SKILL.md"))) continue; // skip empty stubs
|
|
299
|
+
rmSync(join(dest, name), { recursive: true, force: true });
|
|
300
|
+
cpSync(skillPath, join(dest, name), { recursive: true });
|
|
301
|
+
n++;
|
|
302
|
+
}
|
|
303
|
+
console.log(` [${cli}] ${n} skills replaced in ${rel(dest)}`);
|
|
304
|
+
|
|
305
|
+
if (cli === "antigravity") {
|
|
306
|
+
const distSub = join(PKG_ROOT, "dist", "antigravity", "subagents");
|
|
307
|
+
if (existsSync(distSub)) {
|
|
308
|
+
const subDest = join(target, ".agents", "subagents");
|
|
309
|
+
mkdirSync(subDest, { recursive: true });
|
|
310
|
+
cpSync(distSub, subDest, { recursive: true });
|
|
311
|
+
const idx = join(PKG_ROOT, "dist", "antigravity", "subagents.json");
|
|
312
|
+
if (existsSync(idx)) cpSync(idx, join(target, ".agents", "subagents.json"));
|
|
313
|
+
console.log(` [antigravity] subagent configs replaced in ${rel(subDest)}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function ensureAgentImport(file, label, mode) {
|
|
319
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
320
|
+
if (!existsSync(file)) writeFileSync(file, "");
|
|
321
|
+
if (mode === "claude") {
|
|
322
|
+
if (!readFileSync(file, "utf8").includes("@AGENTS.md")) {
|
|
323
|
+
appendFileSync(file, "\n@AGENTS.md\n");
|
|
324
|
+
console.log(`Appended @AGENTS.md import tag to ${label}`);
|
|
325
|
+
} else console.log(`@AGENTS.md import tag already present in ${label}`);
|
|
326
|
+
} else {
|
|
327
|
+
console.log(`${label} ready (root AGENTS.md auto-discovered)`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Implement a task's acceptance criteria exactly (minimum code, surgical diffs)
|
|
3
|
+
---
|
|
4
|
+
Use the **task-executor** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
Standalone build: implements one task (`TASK-NNN`) from a spec folder — assumption scan first,
|
|
7
|
+
minimum code, surgical diffs, verified observable outcomes. Inside an orchestrated run the
|
|
8
|
+
tech-lead dispatches this skill through the envelope port instead; this command is for picking
|
|
9
|
+
up a single task directly.
|
|
10
|
+
|
|
11
|
+
If no task ID was given, read the board (`.shapeup/<slug>/tasks/_index.md`) and take the
|
|
12
|
+
next `ready` task, stating which one you picked. Respect the substrate: if scope contracts
|
|
13
|
+
exist, writes outside the active scope's whitelist will be denied by the sandbox hook — that is
|
|
14
|
+
the harness working, not an error to route around.
|
package/commands/eval.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Judge the build against the committed spec (skeptical; absence of evidence = FAIL)
|
|
3
|
+
---
|
|
4
|
+
Use the **spec-evaluator** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
The single judge. Two modes, chosen by the arguments:
|
|
7
|
+
|
|
8
|
+
- `--task TASK-NNN` — grade one task against its acceptance criteria.
|
|
9
|
+
- `--spec <folder> --feature <slug> --single-pass` — the once-per-round verdict on the whole
|
|
10
|
+
board.
|
|
11
|
+
|
|
12
|
+
Round mode is gated: a PreToolUse hook (GATE L2) will **deny** the dispatch while any task on
|
|
13
|
+
the board is unfinished, naming the offenders. If that happens, the correct response is to
|
|
14
|
+
route back to `/build` and finish them — do not retry the eval, do not argue with the hook, and
|
|
15
|
+
do not use `--task` as a loophole to simulate a round verdict piecemeal.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: GATE H — must-have census, baseline comparison, cut list + ship verdict
|
|
3
|
+
---
|
|
4
|
+
Use the **scope-hammer** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
The stop decision. Censuses the open items (QA findings, discovered-ledger rows, attempt-budget
|
|
7
|
+
hammer proposals), compares the product to the **pitch's baseline — what customers live with
|
|
8
|
+
today, never the ideal** — and returns a cut list plus a ship verdict for the TL/PO to confirm.
|
|
9
|
+
|
|
10
|
+
Handles all three arrival paths: the normal stop (all scopes finished), an exhausted round
|
|
11
|
+
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 skill's.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Builder-led recon — read the code, spike the riskiest area, surface unknowns
|
|
3
|
+
---
|
|
4
|
+
Use the **orient** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
This is step 7 — after kick-off, before any task board exists. It reads the code, spikes the
|
|
7
|
+
single riskiest area, and emits a code-surface map, spike findings, a discovered-task seed, and
|
|
8
|
+
a hill signal. It writes **no production code**.
|
|
9
|
+
|
|
10
|
+
If the user has not named a pitch or spec folder, ask which feature to orient on rather than
|
|
11
|
+
guessing from the working tree.
|
package/commands/qa.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exploratory edge hunt on the running app (post-PASS; findings never block ship)
|
|
3
|
+
---
|
|
4
|
+
Use the **qa-edge-hunter** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
Runs after the evaluator's PASS, outside the build↔eval loop. It charters edges *outside* what
|
|
7
|
+
the evaluator probed and hunts them through six fixed lenses against the running app. Findings
|
|
8
|
+
are filed to the discovery ledger as `~` — QA has **no verdict and no score**, and a finding
|
|
9
|
+
never blocks ship.
|
|
10
|
+
|
|
11
|
+
It needs a spec folder, a PASS eval report, and a running app. If there is no PASS verdict yet,
|
|
12
|
+
say so and stop — QA before PASS is the evaluator's job done out of order.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: File ship-gate feedback into the team-shared, per-skill knowledge base
|
|
3
|
+
---
|
|
4
|
+
Use the **coach** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
Turns raw PO/TL feedback (usually from the L4 Ship gate) into per-skill guideline files under
|
|
7
|
+
committed `shapeup/knowledge-base/<skill>.md`, which the coachable skills read back at
|
|
8
|
+
the top of their next run.
|
|
9
|
+
|
|
10
|
+
Two rules the skill enforces and this command must not soften: GATE COACH-1 **asks** the PO
|
|
11
|
+
which skill owns each rule — it never assumes; and feedback whose root cause is the mechanism
|
|
12
|
+
itself (a gate, hook, or contract defect) is categorized `harness-defect` and filed to the
|
|
13
|
+
defect register as a raw idea for the Betting Table, never as worker steering.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Decompose the pitch into a spec tree + board, then map the scope contracts
|
|
3
|
+
---
|
|
4
|
+
This is step 8 (Map Scopes), which is two skills in sequence:
|
|
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`, `generate-board`, `reconcile`,
|
|
9
|
+
`retrofit-surface`, or `coverage` (which writes the shared `requirements.md` registry).
|
|
10
|
+
2. Then use the **scope-architect** skill to write the committed scope contracts
|
|
11
|
+
(`scopes/*.md`) — import-graph slicing by flow, write-whitelist substrates, fixtures.
|
|
12
|
+
Operations: `map-scopes` (default), `remap`, `split-scope`.
|
|
13
|
+
|
|
14
|
+
If the user asked for only one half ("just analyze", "just the contracts"), run only that half.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Shape a raw idea into a pitch (boundaries → breadboard → spike → pitch)
|
|
3
|
+
---
|
|
4
|
+
Use the **shapeup** skill on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
This is Phase 1 of the pipeline — it runs *before* any code and produces the `pitch.md` the
|
|
7
|
+
Betting Table decides on. Default to the full sequence (`full`); when the user names a single
|
|
8
|
+
step, pass it through as the sub-command: `shaping`, `breadboarding`, `spike`, `framing-doc`,
|
|
9
|
+
`kickoff-doc`, or `breadboard-reflection`.
|
|
10
|
+
|
|
11
|
+
Do not start building from here — a pitch that has not been bet on goes to the PO, not to
|
|
12
|
+
`/build`.
|
package/commands/ship.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run the full Shape Up harness on a pitch, with human sign-off at gates
|
|
3
|
+
---
|
|
4
|
+
Use the **tech-lead** skill to run the full harness on $ARGUMENTS.
|
|
5
|
+
|
|
6
|
+
Default to **interactive** (`auto_level = interactive`): pause at every ⏸ gate and require PO
|
|
7
|
+
sign-off — especially the Ship gate (L4). The harness's safety depends on the human being in the
|
|
8
|
+
loop; do not skip gates by default.
|
|
9
|
+
|
|
10
|
+
**Before anything else, dispatch `tech-lead` and let it open the run** — its first action is
|
|
11
|
+
`scripts/init-run.mjs`, which writes the run receipt. Do not summarise what the harness will do;
|
|
12
|
+
a session that dispatches the orchestrator and leaves no receipt is blocked at `Stop` by
|
|
13
|
+
`hooks/gate-zerowork.mjs`.
|
|
14
|
+
|
|
15
|
+
Only run headless/auto if the user explicitly asks for it in their message:
|
|
16
|
+
- `--auto` → advance low-risk gates automatically, still pause at L4 (Ship sign-off).
|
|
17
|
+
Implies `--gate-answers guarded` unless a set is named.
|
|
18
|
+
- `--unattended` → fully headless, `max_rounds 3`. Intended for CI, not day-to-day local runs.
|
|
19
|
+
Implies `--gate-answers ci` unless a set is named.
|
|
20
|
+
**Typing the flag IS the confirmation — do not stop to ask for another one.** Emit the warning
|
|
21
|
+
that no human will review the verdict before ship as the run's first line, then proceed straight
|
|
22
|
+
into GATE L0 in the same turn.
|
|
23
|
+
|
|
24
|
+
> Why this is spelled out: asking for confirmation here made `--unattended` unusable for the
|
|
25
|
+
> only job it has. In a non-interactive invocation (`claude -p …`, a CI step, a benchmark probe)
|
|
26
|
+
> there is no second turn in which to answer, so the run spent its turn requesting permission and
|
|
27
|
+
> exited having written nothing. A headless flag that cannot complete a headless run is a defect,
|
|
28
|
+
> not a safety feature — and the warning, which is the part that carries the safety value, is
|
|
29
|
+
> still printed. `--auto` remains the middle setting that pauses at L4.
|
|
30
|
+
|
|
31
|
+
- `--tiny` → the small-change lane: orient (light) → single-task board → build → T0 → done.
|
|
32
|
+
Skips wiring, scope contracts, EVAL, and QA; only gates L0 and L4 pause. The tech-lead's L0
|
|
33
|
+
fit-check applies (≤ ~2 files, no new domain concept/dependency/flow) — if the change isn't
|
|
34
|
+
tiny, it will say so and recommend the full lane.
|
|
35
|
+
|
|
36
|
+
Additional flags, pass through to `tech-lead` only when the user names them:
|
|
37
|
+
- `--gate-answers <ci|guarded|interactive|path.json>` → the pre-recorded PO decisions this run
|
|
38
|
+
crosses its gates with. Gates still emit their blocks and still record a decision; the
|
|
39
|
+
decision's **source** becomes the answer set instead of a live human, and the ledger says so.
|
|
40
|
+
Generate one with `gate-answers.mjs --init --preset ci --by "<name>"`. This is what makes a
|
|
41
|
+
headless lane finish: without it an unattended run waits at the first ⏸ until the wall-clock
|
|
42
|
+
budget expires (measured: a benchmark DNF at 1800s on a feature the control finished in 51s).
|
|
43
|
+
- `--wall-clock-budget <seconds>` → arm the deadline breaker. Off by default. Set it in any lane
|
|
44
|
+
with a hard clock (CI, a benchmark, an overnight run) and set it *below* the external kill, so
|
|
45
|
+
the harness trips its own breaker first: past the deadline `hooks/gate-deadline.mjs` denies new
|
|
46
|
+
`task-executor` work and routes to GATE H, where scope-hammer ships whatever is green. A run
|
|
47
|
+
killed from outside ships nothing — including the scopes that already passed T0.
|
|
48
|
+
- `--rounds N` → override the outer circuit breaker (build+eval cycles, default 3).
|
|
49
|
+
- `--attempts N` → override the inner circuit breaker (per-scope T0 attempts, default 5;
|
|
50
|
+
no-op on specs without scope contracts).
|
|
51
|
+
- `--orch-model / --exec-model / --eval-model / --qa-model <name>` → override GATE L0.8's
|
|
52
|
+
resolved model matrix for this run only (highest precedence over `.claude/settings.local.json`
|
|
53
|
+
/ `.claude/settings.json` / skill defaults).
|
package/commands/wire.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Write the wiring map — engine → seam → entry-point call site, per use case
|
|
3
|
+
---
|
|
4
|
+
Use the **solution-architect** skill (operation `wire`) on $ARGUMENTS.
|
|
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`; `trace-lint.mjs` 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.
|