shapeup-sdlc 3.1.2 → 3.3.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-plugin/plugin.json +1 -1
- package/AGENTS.md +7 -6
- package/README.md +1 -1
- package/SECURITY.md +4 -1
- package/bin/init.mjs +3 -0
- package/commands/retro.md +19 -2
- package/commands/ship.md +5 -0
- package/hooks/dispatch-receipt.mjs +6 -3
- package/hooks/gate-intake.mjs +1 -1
- package/hooks/gate-zerowork.mjs +5 -2
- package/hooks/lib/decision.mjs +49 -6
- package/hooks/safety-spine.mjs +8 -5
- package/hooks/sandbox-guard.mjs +13 -6
- package/kernel/compile.mjs +112 -6
- package/kernel/harness.mjs +11 -5
- package/kernel/init/run.mjs +122 -6
- package/kernel/lib/breadboard.mjs +165 -0
- package/kernel/lib/paths.mjs +15 -3
- package/kernel/probe/owner.mjs +139 -0
- package/kernel/probe/resume.mjs +7 -1
- package/kernel/probe/stats.mjs +49 -2
- package/kernel/reduce/hill.mjs +12 -2
- package/kernel/verify/build.mjs +319 -0
- package/kernel/verify/spec.mjs +190 -2
- package/package.json +1 -1
- package/skills/ba-pitch-analyzer/SKILL.md +11 -5
- package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +2 -1
- package/skills/ba-pitch-analyzer/assets/templates/ux-behavior.tmpl.md +12 -2
- package/skills/ba-pitch-analyzer/references/doc-schemas.md +3 -0
- package/skills/ba-pitch-analyzer/references/ux-behavior-patterns.md +9 -0
- package/skills/coach/SKILL.md +232 -43
- package/skills/orient/SKILL.md +15 -5
- package/skills/qa-edge-hunter/SKILL.md +3 -2
- package/skills/scope-architect/SKILL.md +7 -0
- package/skills/scope-hammer/SKILL.md +13 -0
- package/skills/solution-architect/SKILL.md +7 -2
- package/skills/tech-lead/SKILL.md +7 -7
- package/skills/tech-lead/references/gates.md +69 -17
- package/skills/tech-lead/references/protocol.md +33 -8
- package/skills/tech-lead/schemas/domain.schema.json +180 -9
- package/skills/tech-lead/workflows/shapeup-run.js +54 -12
|
@@ -296,6 +296,27 @@
|
|
|
296
296
|
"cardinality": "1:N",
|
|
297
297
|
"via": "wiring-map entries[].wiring_seam",
|
|
298
298
|
"note": "projected by reduce graph as UseCase -DEPENDS_ON-> Seam. NOTE: the projection reads `row.seam`/`row.entry_point` while WiringEntry declares `wiring_seam`/`entry_call_site` — see harness-defects"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"from": "RoundBuildVerdict",
|
|
302
|
+
"to": "ProjectProfile",
|
|
303
|
+
"cardinality": "N:1",
|
|
304
|
+
"via": "build_probe + launch_probe",
|
|
305
|
+
"note": "the gate runs the probes the profile declares; run_cmd comes from the run ledger"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"from": "RoundBuildVerdict",
|
|
309
|
+
"to": "AegisTriple",
|
|
310
|
+
"cardinality": "1:N",
|
|
311
|
+
"via": "discovered_tasks[]",
|
|
312
|
+
"note": "a red gate's output, digested — surfaces as payload.bugs on the next round's orders"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"from": "RoundBuildVerdict",
|
|
316
|
+
"to": "HillShard",
|
|
317
|
+
"cardinality": "1:N",
|
|
318
|
+
"via": "round",
|
|
319
|
+
"note": "a red gate withholds DOWNHILL_EXECUTION from every T0-green verdict of that round"
|
|
299
320
|
}
|
|
300
321
|
]
|
|
301
322
|
},
|
|
@@ -314,6 +335,7 @@
|
|
|
314
335
|
],
|
|
315
336
|
"ba-pitch-analyzer": [
|
|
316
337
|
"pitch",
|
|
338
|
+
"breadboard",
|
|
317
339
|
"lens",
|
|
318
340
|
"orient_dir",
|
|
319
341
|
"spec_folder",
|
|
@@ -324,12 +346,16 @@
|
|
|
324
346
|
"scope-architect": [
|
|
325
347
|
"feature",
|
|
326
348
|
"spec_folder",
|
|
327
|
-
"tasks"
|
|
349
|
+
"tasks",
|
|
350
|
+
"breadboard",
|
|
351
|
+
"kb_rules_path"
|
|
328
352
|
],
|
|
329
353
|
"solution-architect": [
|
|
330
354
|
"feature",
|
|
331
355
|
"spec_folder",
|
|
332
|
-
"project_profile"
|
|
356
|
+
"project_profile",
|
|
357
|
+
"breadboard",
|
|
358
|
+
"kb_rules_path"
|
|
333
359
|
],
|
|
334
360
|
"spec-evaluator": [
|
|
335
361
|
"spec_folder",
|
|
@@ -342,9 +368,11 @@
|
|
|
342
368
|
],
|
|
343
369
|
"orient": [
|
|
344
370
|
"pitch",
|
|
371
|
+
"breadboard",
|
|
345
372
|
"stack",
|
|
346
373
|
"spec_folder",
|
|
347
|
-
"feature"
|
|
374
|
+
"feature",
|
|
375
|
+
"kb_rules_path"
|
|
348
376
|
],
|
|
349
377
|
"qa-edge-hunter": [
|
|
350
378
|
"feature",
|
|
@@ -365,7 +393,8 @@
|
|
|
365
393
|
"scope_id"
|
|
366
394
|
],
|
|
367
395
|
"coach": [
|
|
368
|
-
"feedback"
|
|
396
|
+
"feedback",
|
|
397
|
+
"stack"
|
|
369
398
|
]
|
|
370
399
|
},
|
|
371
400
|
"x-result-by-worker": {
|
|
@@ -454,7 +483,7 @@
|
|
|
454
483
|
]
|
|
455
484
|
},
|
|
456
485
|
"Operation": {
|
|
457
|
-
"description": "The full operation vocabulary. Replaces lifecycle flags (--tasks-only, --from-discovered, --map-scopes …): the caller knows the pipeline position; the worker never re-derives it. Ownership: execute/fix/spike = task-executor · analyze/reconcile/retrofit-surface/coverage = ba-pitch-analyzer · map-scopes = scope-architect · wire = solution-architect · evaluate = spec-evaluator · orient = orient · hunt = qa-edge-hunter · translate = translator · hammer = scope-hammer · coach = coach.",
|
|
486
|
+
"description": "The full operation vocabulary. Replaces lifecycle flags (--tasks-only, --from-discovered, --map-scopes …): the caller knows the pipeline position; the worker never re-derives it. Ownership: execute/fix/spike = task-executor · analyze/reconcile/retrofit-surface/coverage = ba-pitch-analyzer · map-scopes = scope-architect · wire = solution-architect · evaluate = spec-evaluator · orient = orient · hunt = qa-edge-hunter · translate = translator · hammer = scope-hammer · coach/scan/research = coach (scan seeds the knowledge base from the project on disk instead of from L4 feedback; research seeds it from the platform's official documentation, aimed by a stack hint, and cross-checks the scan's rules where a scan exists; same write surface, same categorization gate).",
|
|
458
487
|
"type": "string",
|
|
459
488
|
"enum": [
|
|
460
489
|
"execute",
|
|
@@ -471,7 +500,9 @@
|
|
|
471
500
|
"hunt",
|
|
472
501
|
"translate",
|
|
473
502
|
"hammer",
|
|
474
|
-
"coach"
|
|
503
|
+
"coach",
|
|
504
|
+
"scan",
|
|
505
|
+
"research"
|
|
475
506
|
]
|
|
476
507
|
},
|
|
477
508
|
"Substrate": {
|
|
@@ -709,6 +740,10 @@
|
|
|
709
740
|
"type": "string"
|
|
710
741
|
},
|
|
711
742
|
"description": "Subset of [idle, loading, success, error, empty] the element must express via data-state."
|
|
743
|
+
},
|
|
744
|
+
"source": {
|
|
745
|
+
"type": "string",
|
|
746
|
+
"description": "The breadboard UI affordance (U#) this element implements; absent without a breadboard."
|
|
712
747
|
}
|
|
713
748
|
}
|
|
714
749
|
},
|
|
@@ -1434,6 +1469,116 @@
|
|
|
1434
1469
|
"status"
|
|
1435
1470
|
]
|
|
1436
1471
|
},
|
|
1472
|
+
"RoundBuildVerdict": {
|
|
1473
|
+
"description": "The round build gate's verdict — did the FEATURE build and launch this round, measured before the judge is asked. A T0Artifact is one scope's fixtures inside its own substrate; this is the whole feature's run_cmd (from the run ledger), then the profile's build_probe and launch_probe, run in that order and stopping at the first failure. Zero LLM tokens. Two readers act on it: reduce hill withholds DOWNHILL_EXECUTION from every T0-green verdict of a round whose gate is red (a green fixture in a round the app did not compile is evidence about the fixture, not the scope), and harness compile turns each failing step into a `bug` for the next round's fix orders, addressed by the files the tool's output names. Immutable per gate run (`wx`, next ordinal); readers take the latest artifact for a round.",
|
|
1474
|
+
"x-tier": "LOCAL",
|
|
1475
|
+
"x-location": ".shapeup/<slug>/build/r<N>-t<T>.json",
|
|
1476
|
+
"x-writer": "harness verify build",
|
|
1477
|
+
"x-readers": "harness reduce hill (red rounds move no dot), harness compile (next round's payload.bugs), tech-lead (GATE L2 block: build gate state)",
|
|
1478
|
+
"type": "object",
|
|
1479
|
+
"required": [
|
|
1480
|
+
"schema_version",
|
|
1481
|
+
"round",
|
|
1482
|
+
"trial",
|
|
1483
|
+
"at",
|
|
1484
|
+
"overall",
|
|
1485
|
+
"steps"
|
|
1486
|
+
],
|
|
1487
|
+
"properties": {
|
|
1488
|
+
"schema_version": {
|
|
1489
|
+
"type": "integer",
|
|
1490
|
+
"enum": [
|
|
1491
|
+
1
|
|
1492
|
+
]
|
|
1493
|
+
},
|
|
1494
|
+
"round": {
|
|
1495
|
+
"type": "integer"
|
|
1496
|
+
},
|
|
1497
|
+
"trial": {
|
|
1498
|
+
"type": "integer",
|
|
1499
|
+
"description": "Ordinal of this gate run within the round — a re-run after a hand fix lands beside its predecessor, never over it."
|
|
1500
|
+
},
|
|
1501
|
+
"run_id": {
|
|
1502
|
+
"type": "string",
|
|
1503
|
+
"pattern": "^[a-z0-9][a-z0-9-]*-[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8}$"
|
|
1504
|
+
},
|
|
1505
|
+
"at": {
|
|
1506
|
+
"type": "string",
|
|
1507
|
+
"description": "ISO timestamp."
|
|
1508
|
+
},
|
|
1509
|
+
"archetype": {
|
|
1510
|
+
"type": [
|
|
1511
|
+
"string",
|
|
1512
|
+
"null"
|
|
1513
|
+
],
|
|
1514
|
+
"description": "The profile's archetype, for the reader deciding whether a missing launch_probe matters."
|
|
1515
|
+
},
|
|
1516
|
+
"overall": {
|
|
1517
|
+
"type": "string",
|
|
1518
|
+
"enum": [
|
|
1519
|
+
"green",
|
|
1520
|
+
"red"
|
|
1521
|
+
]
|
|
1522
|
+
},
|
|
1523
|
+
"steps": {
|
|
1524
|
+
"type": "array",
|
|
1525
|
+
"items": {
|
|
1526
|
+
"type": "object",
|
|
1527
|
+
"properties": {
|
|
1528
|
+
"kind": {
|
|
1529
|
+
"type": "string",
|
|
1530
|
+
"enum": [
|
|
1531
|
+
"run_cmd",
|
|
1532
|
+
"build_probe",
|
|
1533
|
+
"launch_probe"
|
|
1534
|
+
]
|
|
1535
|
+
},
|
|
1536
|
+
"cmd": {
|
|
1537
|
+
"type": "string"
|
|
1538
|
+
},
|
|
1539
|
+
"exit": {
|
|
1540
|
+
"type": "integer"
|
|
1541
|
+
},
|
|
1542
|
+
"pass": {
|
|
1543
|
+
"type": "boolean"
|
|
1544
|
+
},
|
|
1545
|
+
"skipped": {
|
|
1546
|
+
"type": "boolean",
|
|
1547
|
+
"description": "true when an earlier step failed and this one was not run."
|
|
1548
|
+
},
|
|
1549
|
+
"stdout_tail": {
|
|
1550
|
+
"type": "string"
|
|
1551
|
+
},
|
|
1552
|
+
"stderr_tail": {
|
|
1553
|
+
"type": "string"
|
|
1554
|
+
},
|
|
1555
|
+
"error": {
|
|
1556
|
+
"type": "string",
|
|
1557
|
+
"description": "Spawn failure or timeout — the command did not run to completion, which is not the same fact as a non-zero exit."
|
|
1558
|
+
}
|
|
1559
|
+
},
|
|
1560
|
+
"required": [
|
|
1561
|
+
"kind",
|
|
1562
|
+
"cmd"
|
|
1563
|
+
]
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
"warnings": {
|
|
1567
|
+
"type": "array",
|
|
1568
|
+
"items": {
|
|
1569
|
+
"type": "string"
|
|
1570
|
+
},
|
|
1571
|
+
"description": "What the gate could not check and why — a launch-required archetype with no launch_probe, a ledger with no run_cmd."
|
|
1572
|
+
},
|
|
1573
|
+
"discovered_tasks": {
|
|
1574
|
+
"type": "array",
|
|
1575
|
+
"items": {
|
|
1576
|
+
"$ref": "#/$defs/AegisTriple"
|
|
1577
|
+
},
|
|
1578
|
+
"description": "The failing steps' output digested; populated only on red."
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1437
1582
|
"SeesawRegistry": {
|
|
1438
1583
|
"description": "The fixture registry of every FINISHED scope — what seesawCheck re-runs on each later attempt so a new scope cannot silently break a shipped one — a regression mistaken for progress is the pathology the seesaw exists for.",
|
|
1439
1584
|
"x-tier": "LOCAL",
|
|
@@ -2147,7 +2292,7 @@
|
|
|
2147
2292
|
"x-tier": "SHARED",
|
|
2148
2293
|
"x-location": "shapeup/<slug>/project-profile.md",
|
|
2149
2294
|
"x-writer": "tech-lead (GATE L0 — not harness compile, which stays pipeline-blind)",
|
|
2150
|
-
"x-readers": "harness verify trace (reachability entry_point), solution-architect (wire), tech-lead",
|
|
2295
|
+
"x-readers": "harness verify trace (reachability entry_point), harness verify build (build_probe + launch_probe, once per round before EVAL), solution-architect (wire), tech-lead",
|
|
2151
2296
|
"type": "object",
|
|
2152
2297
|
"required": [
|
|
2153
2298
|
"schema_version",
|
|
@@ -2179,6 +2324,14 @@
|
|
|
2179
2324
|
"note": {
|
|
2180
2325
|
"type": "string",
|
|
2181
2326
|
"description": "Optional context on the archetype/entry-point choice."
|
|
2327
|
+
},
|
|
2328
|
+
"build_probe": {
|
|
2329
|
+
"type": "string",
|
|
2330
|
+
"description": "OPTIONAL — a command that asserts the BUILT ARTIFACT, not the build's exit code, and exits 0 only when it holds. Exists because a green build is not proof the feature compiled: some toolchains compile only the files reachable from an entry point, so a scope's new files can sit outside the compiled set while the build stays green (measured: an app package holding 3 compiled files, 58 errors once the rest became reachable). Archetype-specific by construction — e.g. 'the compiled source map lists every file under each scope's substrate'. Run by harness verify build after run_cmd, once per round before EVAL; absent = no such step, never a failure."
|
|
2331
|
+
},
|
|
2332
|
+
"launch_probe": {
|
|
2333
|
+
"type": "string",
|
|
2334
|
+
"description": "OPTIONAL for most archetypes, EXPECTED for `mobile` — a command that installs the built artifact, starts it, asserts the first screen (ids, text, a screenshot diff) and fails on fatal runtime log patterns. Exists because nothing else in the loop launches the app: a blank first screen survived three EVAL rounds on a run whose every T0 fixture was green. Run by harness verify build after run_cmd and build_probe; a mobile profile without one is warned about on every round so the 'on-device install unverified' risk has a named owner. Absent = no such step."
|
|
2182
2335
|
}
|
|
2183
2336
|
}
|
|
2184
2337
|
},
|
|
@@ -2222,7 +2375,7 @@
|
|
|
2222
2375
|
},
|
|
2223
2376
|
"kb_rules_path": {
|
|
2224
2377
|
"type": "string",
|
|
2225
|
-
"description": "Coachable workers (task-executor, ba-pitch-analyzer, qa-edge-hunter): shapeup/knowledge-base/<skill>.md — steering, never spec; conflict → the AC wins, noted in deviations."
|
|
2378
|
+
"description": "Coachable workers (task-executor, ba-pitch-analyzer, qa-edge-hunter, orient, scope-architect, solution-architect): shapeup/knowledge-base/<skill>.md — steering, never spec and never a gate; conflict → the AC, the contract or the gate wins, noted in deviations. The judge and the census are not coachable."
|
|
2226
2379
|
},
|
|
2227
2380
|
"verify": {
|
|
2228
2381
|
"$ref": "#/$defs/VerifySpec",
|
|
@@ -2248,6 +2401,10 @@
|
|
|
2248
2401
|
"type": "string",
|
|
2249
2402
|
"description": "ba-pitch-analyzer (analyze) / orient: the kicked-off pitch path (shaped + bet; frontmatter appetite/status/bet)."
|
|
2250
2403
|
},
|
|
2404
|
+
"breadboard": {
|
|
2405
|
+
"type": "string",
|
|
2406
|
+
"description": "orient / ba-pitch-analyzer (analyze) / solution-architect / scope-architect: the run's staged breadboard — Places (P#), UI and code affordances (U#, N#), stores (S#), slices (V#). Absent = the pitch has no separate breadboard (it may carry one inline); never inferred from the pitch's folder."
|
|
2407
|
+
},
|
|
2251
2408
|
"lens": {
|
|
2252
2409
|
"type": "string",
|
|
2253
2410
|
"enum": [
|
|
@@ -2263,7 +2420,7 @@
|
|
|
2263
2420
|
},
|
|
2264
2421
|
"stack": {
|
|
2265
2422
|
"type": "string",
|
|
2266
|
-
"description": "orient: stack hint aiming the code-surface sweeps (e.g. \"pnpm, Next 16 web :3000\")."
|
|
2423
|
+
"description": "orient: stack hint aiming the code-surface sweeps (e.g. \"pnpm, Next 16 web :3000\"). coach (research): the platform and toolchain the official-documentation research is aimed at — required, since a project with nothing on disk names no stack by itself."
|
|
2267
2424
|
},
|
|
2268
2425
|
"discovered_ledger": {
|
|
2269
2426
|
"type": "string",
|
|
@@ -2379,6 +2536,20 @@
|
|
|
2379
2536
|
"type": "string",
|
|
2380
2537
|
"description": "Resolved path to the run's intake — the pitch a fresh ORIENT dispatch is compiled against."
|
|
2381
2538
|
},
|
|
2539
|
+
"breadboard_path": {
|
|
2540
|
+
"type": [
|
|
2541
|
+
"string",
|
|
2542
|
+
"null"
|
|
2543
|
+
],
|
|
2544
|
+
"description": "Resolved path to the run's staged breadboard (`.shapeup/<slug>/breadboard.md`), the pitch's second half, handed to the four planning dispatches as payload.breadboard. Null when the pitch had no separate breadboard file."
|
|
2545
|
+
},
|
|
2546
|
+
"breadboard_source": {
|
|
2547
|
+
"type": [
|
|
2548
|
+
"string",
|
|
2549
|
+
"null"
|
|
2550
|
+
],
|
|
2551
|
+
"description": "How `init run` found the breadboard — flag | sibling | shaping-dir | shared-root | embedded — read from the receipt. Null when there was none or the receipt is unreadable."
|
|
2552
|
+
},
|
|
2382
2553
|
"spec_folder": {
|
|
2383
2554
|
"type": [
|
|
2384
2555
|
"string",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// WHAT THIS FILE OWNS.
|
|
4
4
|
// ORIENT → GATE L1a → ANALYZE → WIRE → GATE L1a.5 → MAP SCOPES → GATE L1b →
|
|
5
|
-
// rounds of (BUILD → GATE L2 → EVAL → GATE L3) bounded by budgets.maxRounds →
|
|
5
|
+
// rounds of (BUILD → build gate → GATE L2 → EVAL → GATE L3) bounded by budgets.maxRounds →
|
|
6
6
|
// QA → GATE H → ship report → RunReturn.
|
|
7
7
|
//
|
|
8
8
|
// THE THREE PLANES THIS FILE RESPECTS, because collapsing them is what the previous version cost:
|
|
@@ -400,6 +400,7 @@ const RESUME = {
|
|
|
400
400
|
type: "object",
|
|
401
401
|
properties: {
|
|
402
402
|
intake_path: nullable("string"), spec_folder: nullable("string"), orient_dir: nullable("string"),
|
|
403
|
+
breadboard_path: nullable("string"), breadboard_source: nullable("string"),
|
|
403
404
|
project_profile_path: nullable("string"), status: nullable("string"),
|
|
404
405
|
lens: nullable("string"), stack: nullable("string"),
|
|
405
406
|
run_cmd: nullable("string"), app_url: nullable("string"),
|
|
@@ -972,7 +973,7 @@ if (!rs.has_orient_artifacts) {
|
|
|
972
973
|
await setRunStatus("orienting", "Orient");
|
|
973
974
|
const o = await worker({
|
|
974
975
|
skill: "orient", operation: "orient", schema: ORIENT, phase: "Orient", label: "orient",
|
|
975
|
-
payload: { pitch: rs.intake_path, spec_folder: specFolder, feature: slug, stack: rs.stack },
|
|
976
|
+
payload: { pitch: rs.intake_path, breadboard: rs.breadboard_path, spec_folder: specFolder, feature: slug, stack: rs.stack },
|
|
976
977
|
// NAME THE FILES. "write the orient/ artifacts" was the whole instruction, while completion is
|
|
977
978
|
// decided by four exact filenames — so a leg that did the work and called its output
|
|
978
979
|
// `code-surface-map.md` and `discovered-tasks.md` aborted the run at the post-condition, having
|
|
@@ -998,8 +999,10 @@ if (!rs.has_orient_artifacts) {
|
|
|
998
999
|
}
|
|
999
1000
|
|
|
1000
1001
|
{
|
|
1002
|
+
// `breadboard` travels in the block because a missing one is invisible anywhere later: every
|
|
1003
|
+
// downstream artifact reads the same whether or not the pitch's second half reached the run.
|
|
1001
1004
|
const g = await crossGate("L1a", "Orient", ["proceed", "ask", "abort"],
|
|
1002
|
-
{ spiked_area: spikedArea, spike_result: spikeResult, riskiest_unknowns: riskiest });
|
|
1005
|
+
{ breadboard: rs.breadboard_source ?? "none", spiked_area: spikedArea, spike_result: spikeResult, riskiest_unknowns: riskiest });
|
|
1003
1006
|
if (g.stop) return withWarnings(g.stop);
|
|
1004
1007
|
}
|
|
1005
1008
|
|
|
@@ -1015,7 +1018,7 @@ if (!rs.has_spec_tree) {
|
|
|
1015
1018
|
await setRunStatus("mapping", "Analyze");
|
|
1016
1019
|
const a = await worker({
|
|
1017
1020
|
skill: "ba-pitch-analyzer", operation: "analyze", schema: PHASE_OK, phase: "Analyze", label: "analyze",
|
|
1018
|
-
payload: { pitch: rs.intake_path, spec_folder: specFolder, feature: slug, lens: rs.lens, orient_dir: rs.orient_dir },
|
|
1021
|
+
payload: { pitch: rs.intake_path, breadboard: rs.breadboard_path, spec_folder: specFolder, feature: slug, lens: rs.lens, orient_dir: rs.orient_dir },
|
|
1019
1022
|
extra: "Write the spec tree and the board from the orient artifacts — do not re-scan the code.",
|
|
1020
1023
|
});
|
|
1021
1024
|
if (a.__failed) return diedAt("ANALYZE", a);
|
|
@@ -1047,7 +1050,7 @@ if (!rs.has_wiring_map) {
|
|
|
1047
1050
|
log(`WIRE — dispatching (slug ${slug})`);
|
|
1048
1051
|
const w = await worker({
|
|
1049
1052
|
skill: "solution-architect", operation: "wire", schema: PHASE_OK, phase: "Wire", label: "wire",
|
|
1050
|
-
payload: { feature: slug, spec_folder: specFolder, project_profile: rs.project_profile_path },
|
|
1053
|
+
payload: { feature: slug, spec_folder: specFolder, project_profile: rs.project_profile_path, breadboard: rs.breadboard_path },
|
|
1051
1054
|
extra: "Write the wiring map: per use case, engine → seam → entry-point call site → affordance.",
|
|
1052
1055
|
});
|
|
1053
1056
|
if (w.__failed) return diedAt("WIRE", w);
|
|
@@ -1078,7 +1081,7 @@ if (scopes.length === 0) {
|
|
|
1078
1081
|
log(`MAP SCOPES — dispatching (slug ${slug})`);
|
|
1079
1082
|
const m = await worker({
|
|
1080
1083
|
skill: "scope-architect", operation: "map-scopes", schema: MAPSCOPES, phase: "MapScopes", label: "map-scopes",
|
|
1081
|
-
payload: { feature: slug },
|
|
1084
|
+
payload: { feature: slug, breadboard: rs.breadboard_path },
|
|
1082
1085
|
// SAY THE PASS RULE, for the same reason ORIENT's filenames are named above: the rule lives in
|
|
1083
1086
|
// `verify t0` (a fixture passes iff it exits 0) and the architect never saw it. Given a contract
|
|
1084
1087
|
// that said only "commands that drive this scope end-to-end", it wrote the scope's error paths
|
|
@@ -1163,11 +1166,13 @@ if (waves.length > 1 || excluded.added || ceiling < maxParallelScopes) {
|
|
|
1163
1166
|
`${ceiling < maxParallelScopes ? ` (the window is ${maxParallelScopes}; the substrate the contracts declared is what caps it, not the dial)` : ""}`);
|
|
1164
1167
|
}
|
|
1165
1168
|
|
|
1166
|
-
// Advisory lints at L1b. spec-lint is hard — a substrate overlap makes parallel builds unsafe
|
|
1167
|
-
//
|
|
1169
|
+
// Advisory lints at L1b. spec-lint is hard — a substrate overlap makes parallel builds unsafe, and
|
|
1170
|
+
// a breadboard Place with no screen builds the wrong thing; trace-lint stays advisory until
|
|
1171
|
+
// `covers:` is populated; hill-derive is a projection. The abort names no cause of its own: spec-lint
|
|
1172
|
+
// has more than one kind of red, and the detail says which.
|
|
1168
1173
|
const specLint = await cmd(`verify spec --slug ${slug}`, "MapScopes", "spec-lint");
|
|
1169
1174
|
if (!specLint.ok) {
|
|
1170
|
-
return aborted("L1b", `spec-lint reported
|
|
1175
|
+
return aborted("L1b", `spec-lint reported red findings before BUILD: ${specLint.detail || `exit ${specLint.exit_code}`}`);
|
|
1171
1176
|
}
|
|
1172
1177
|
await advisory(`verify trace --slug ${slug} --quiet`, "MapScopes", "trace-lint");
|
|
1173
1178
|
await advisory(`reduce hill --slug ${slug}`, "MapScopes", "hill-derive");
|
|
@@ -1345,17 +1350,54 @@ while (verdict !== "pass" && round <= maxRounds) {
|
|
|
1345
1350
|
return withWarnings({ status: "gate_h", breaker: "inner", hammer_proposals: allHammer, green_scopes: allGreen });
|
|
1346
1351
|
}
|
|
1347
1352
|
|
|
1353
|
+
// ---- ROUND BUILD GATE — the feature builds and launches, measured before anyone is asked --------
|
|
1354
|
+
//
|
|
1355
|
+
// T0 is per scope and inside the scope's substrate; nothing in it proves the FEATURE compiles or
|
|
1356
|
+
// starts. Measured on a live mobile run: 30/30 T0 trials green on the first try (TypeScript
|
|
1357
|
+
// stand-ins, structural greps, a suite wrapper that never compiled its sources) while the
|
|
1358
|
+
// ledger's own `run_cmd` failed, and three rounds of EVAL then graded a blank screen because
|
|
1359
|
+
// nothing in the loop had ever installed or launched the app. `verify build` runs the ledger's
|
|
1360
|
+
// `run_cmd`, then the profile's `build_probe` and `launch_probe`, and writes one artifact
|
|
1361
|
+
// `reduce hill` and `harness compile` both read — so a red gate moves no dot downhill and becomes
|
|
1362
|
+
// the next round's bug list without this script carrying anything across the round boundary.
|
|
1363
|
+
//
|
|
1364
|
+
// Exit 3 is "nothing declared" — the honest state of a run whose L0 pinned no run command and
|
|
1365
|
+
// whose profile names no probe — and it is logged, not treated as green. Any other non-0/1 exit
|
|
1366
|
+
// means the gate itself did not run, and the run says so rather than inferring a verdict.
|
|
1367
|
+
const gate = await cmd(`verify build --slug ${slug} --round ${round}`, "Build", `build-gate:r${round}`);
|
|
1368
|
+
let buildGate = "green";
|
|
1369
|
+
if (gate.exit_code === 1) {
|
|
1370
|
+
buildGate = "red";
|
|
1371
|
+
log(`BUILD GATE r${round} — RED${gate.detail ? `: ${gate.detail}` : ""}. EVAL will not run over a feature ` +
|
|
1372
|
+
`that does not build or launch; the failing step is compiled into round ${round + 1}'s orders as bugs.`);
|
|
1373
|
+
} else if (gate.exit_code === 3) {
|
|
1374
|
+
buildGate = "undeclared";
|
|
1375
|
+
log(`BUILD GATE r${round} — nothing declared: no run_cmd in the run ledger and no build_probe or ` +
|
|
1376
|
+
`launch_probe in project-profile.md. EVAL runs over an unproven build; pin them at GATE L0.`);
|
|
1377
|
+
} else if (gate.exit_code !== 0) {
|
|
1378
|
+
buildGate = "unknown";
|
|
1379
|
+
log(`BUILD GATE r${round} — the gate itself did not run (exit ${gate.exit_code}` +
|
|
1380
|
+
`${gate.detail ? `: ${gate.detail}` : ""}). Treated as undeclared, not as green.`);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1348
1383
|
await advisory(`reduce hill --slug ${slug}`, "Build", "hill-derive");
|
|
1349
1384
|
{
|
|
1350
1385
|
const g = await crossGate("L2", "Build", ["proceed", "ask", "abort"],
|
|
1351
|
-
{ round, green_scopes: roundGreen, hammer_proposals: roundHammer });
|
|
1386
|
+
{ round, green_scopes: roundGreen, hammer_proposals: roundHammer, build_gate: buildGate });
|
|
1352
1387
|
if (g.stop) return withWarnings(g.stop);
|
|
1353
1388
|
}
|
|
1354
1389
|
|
|
1355
1390
|
// ---- EVAL — exactly one feature-level pass per round (the single-judge invariant) ------------
|
|
1356
1391
|
phase("Eval");
|
|
1357
1392
|
await setRunStatus("evaluating", "Eval");
|
|
1358
|
-
if (
|
|
1393
|
+
if (buildGate === "red") {
|
|
1394
|
+
// A red gate outranks --no-eval: skipping the judge is the operator's call, but the build failing
|
|
1395
|
+
// is a measured fact, and a round that does not compile has nothing for anyone to pass.
|
|
1396
|
+
verdict = "fail";
|
|
1397
|
+
findings = [];
|
|
1398
|
+
log(`EVAL r${round} — not dispatched: the round build gate is red. The judge grades a running ` +
|
|
1399
|
+
`feature; this one does not build or launch. Round ${round + 1} fixes the gate's failing step.`);
|
|
1400
|
+
} else if (args.noEval) {
|
|
1359
1401
|
log("EVAL — skipped (--no-eval)");
|
|
1360
1402
|
verdict = "pass";
|
|
1361
1403
|
} else {
|
|
@@ -1405,7 +1447,7 @@ while (verdict !== "pass" && round <= maxRounds) {
|
|
|
1405
1447
|
|
|
1406
1448
|
await advisory(`reduce graph --slug ${slug}`, "Eval", `graph:eval-r${round}`);
|
|
1407
1449
|
await advisory(`reduce hill --slug ${slug}`, "Eval", "hill-derive");
|
|
1408
|
-
const g3 = await crossGate("L3", "Eval", ["loop", "stop", "ask"], { round, verdict });
|
|
1450
|
+
const g3 = await crossGate("L3", "Eval", ["loop", "stop", "ask"], { round, verdict, build_gate: buildGate });
|
|
1409
1451
|
if (g3.stop) return withWarnings(g3.stop);
|
|
1410
1452
|
|
|
1411
1453
|
if (verdict === "pass") break; // → QA → GATE H → ship
|