shapeup-sdlc 3.18.0 → 3.18.1
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shapeup-sdlc-plugin",
|
|
3
3
|
"displayName": "ShapeUp SDLC Plugin",
|
|
4
|
-
"version": "3.18.
|
|
4
|
+
"version": "3.18.1",
|
|
5
5
|
"description": "Shape Up SDLC harness for Claude Code: shaping, intake, orient, scope-mapping, building (T0-verified, sandboxed, scope-contracted), evaluation and QA skills orchestrated by a tech-lead.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Liberty Nguyen",
|
package/AGENTS.md
CHANGED
|
@@ -10,7 +10,7 @@ Skills and commands are named short throughout this file; every one of them reso
|
|
|
10
10
|
- Hook-denied: dispatching a worker without a schema-valid WorkOrder, writing outside the scope's substrate, writing the result of a live order your own dispatch did not take, writing a committed file whose text names a run-trace path or a board id, stopping a run with no receipt (the run's first act writes one).
|
|
11
11
|
- **The tier-direction rule is refused at the write, not argued at L1b.** A committed file cannot carry a `.shapeup/` path or a `TASK-NNN` id — boards renumber per machine and the run trace is gitignored, so either one resolves only where it was written. Spec-lint has always red'd this at Board Review; the same rule now also refuses the write itself, naming the token, because four producers hit it and one of them hit it twice in one session — a worker carries no lesson across a dispatch, and by L1b the phase that wrote the line is over. The knowledge base is outside the rule: those files are instructions, not references anyone resolves.
|
|
12
12
|
- Attested, not assumed: a dispatch leaves a receipt naming the skill that ran, and ingest refuses an orchestrated result that has none. A dispatch that fails is answered by the sub-agent improvising the craft, which every other check accepts — so "the artifact exists" is not evidence that the shipped skill produced it. `--no-receipt-check` is the way through when the receipt channel itself fails.
|
|
13
|
-
- **The single writer is asked, never assumed.** A dispatch's result is applied by exactly one act — the ingest step — and that act leaves a leg row. Every phase post-condition and every settled build scope asks the leg ledger whether the row exists before any verdict on the phase or the scope, whatever the worker reported: a result on disk that nothing applied is ingested there if the scope is green, and otherwise named at the close as `unapplied_results=N`, never folded into "not green". A round that ends with nothing green names `breaker=none` and `stalled=no_green` unless the attested attempt census says a scope tripped — the close cannot name a breaker the census denies. The run graph carries a `Leg` node with an `INGESTED` edge from its `Result`, and the export a `leg` table, so "which results were never read" is one query.
|
|
13
|
+
- **The single writer is asked, never assumed.** A dispatch's result is applied by exactly one act — the ingest step — and that act leaves a leg row. Every phase post-condition and every settled build scope asks the leg ledger whether the row exists before any verdict on the phase or the scope, whatever the worker reported: a planning leg that left its artifacts and no result at all is dispatched again once, told its artifacts stand; a result on disk that nothing applied is ingested there if the scope is green, and otherwise named at the close as `unapplied_results=N`, never folded into "not green". A round that ends with nothing green names `breaker=none` and `stalled=no_green` unless the attested attempt census says a scope tripped — the close cannot name a breaker the census denies. The run graph carries a `Leg` node with an `INGESTED` edge from its `Result`, and the export a `leg` table, so "which results were never read" is one query.
|
|
14
14
|
- GATE L2 is advisory — warns when EVAL runs over unfinished tasks, permits the call (per-machine board, operator asked; ADR-0001) — a signal, not a bug.
|
|
15
15
|
- Sign-off is a file: each gate resolves from the answer set (`ci`/`guarded`/`interactive`) — cross, stop for the PO, or abort; the decision's source is ledgered.
|
|
16
16
|
- **Every ending a run does not come back from records a close** — its status, its cause and its timestamp — derived from the ending itself rather than from a hand-kept pair, so an ending nobody enumerated cannot silently record nothing. The same close derives the ledger's verdict, its round count and its Rounds and Decisions tables from the run's own records — the ones the export reads — so the file a teammate opens first cannot contradict its own close line. A breaker trip that routes to GATE H closes the run as `escalated`, carrying the breaker in its cause; a pause records no close **by design**, because a relaunch resumes it. A relaunch that resumes a run which *did* close reopens it on the record: the earlier close is kept under `prior_closes`, the close fields clear, the run pointer the close retired comes back (so the resumed run's hook decisions carry its key again), and the run's next ending is recorded as its own — so a run that aborted, was resumed and then shipped reads `shipped` with the abort beside it, never an abort under a `shipped` status. Opening a run over one that closed says it is closed, not open.
|
package/package.json
CHANGED
|
@@ -1017,10 +1017,22 @@ const attest = (phaseKey, phaseName, label) =>
|
|
|
1017
1017
|
* @returns {Promise<(object|null)>} An aborted RunReturn, or null when the leg closed (or the
|
|
1018
1018
|
* question could not be asked — a probe that did not run proves nothing, and is logged as such).
|
|
1019
1019
|
*/
|
|
1020
|
-
async function requireLeg(gate, phaseKey, phaseName, orderStem = phaseKey) {
|
|
1020
|
+
async function requireLeg(gate, phaseKey, phaseName, orderStem = phaseKey, again = null) {
|
|
1021
1021
|
const ask = () => query(`probe leg --slug ${slug} --order "${orderStem}"`, ORDERLEG, phaseName, `legcheck:${orderStem}`);
|
|
1022
1022
|
let leg = await ask();
|
|
1023
1023
|
if (!leg || !leg.found) { log(`${gate} — could not ask the leg ledger about "${phaseKey}" (probe returned ${leg ? "no order" : "nothing"}); proceeding on the artifact alone.`); return null; }
|
|
1024
|
+
// SENT BACK ONCE FOR THE ENVELOPE. A dispatched leg that wrote its artifacts and no WorkResult has
|
|
1025
|
+
// done the craft and skipped the last step, and it recurs on the same phase run after run. The
|
|
1026
|
+
// worker is dispatched again with the reason, told its artifacts stand; only a second miss is named
|
|
1027
|
+
// at the close.
|
|
1028
|
+
if (!leg.has_result && leg.has_receipt && again) {
|
|
1029
|
+
log(`${gate} — "${phaseKey}" was dispatched and returned no WorkResult; sending it back once for the envelope.`);
|
|
1030
|
+
await again(` Your previous dispatch of this order wrote the phase's artifacts and returned NO WorkResult. ` +
|
|
1031
|
+
`The artifacts are on disk and stand: do not redo them. Write the WorkResult the order names ` +
|
|
1032
|
+
`(its substrate's own results path), then ingest it as the steps above say.`);
|
|
1033
|
+
leg = await ask();
|
|
1034
|
+
if (!leg || !leg.found) return null;
|
|
1035
|
+
}
|
|
1024
1036
|
if (!leg.has_result) {
|
|
1025
1037
|
// The artifact is on disk and the envelope never came back. Measured live: the first dispatch of
|
|
1026
1038
|
// a run wrote its four artifacts and no WorkResult, every later phase read the artifacts, and
|
|
@@ -1055,9 +1067,9 @@ async function requireLeg(gate, phaseKey, phaseName, orderStem = phaseKey) {
|
|
|
1055
1067
|
* @param {string} phaseName - Progress group.
|
|
1056
1068
|
* @returns {Promise<(object|null)>} An aborted RunReturn, or null when the artifact is there.
|
|
1057
1069
|
*/
|
|
1058
|
-
async function requirePhase(gate, phaseKey, phaseName, orderStem = phaseKey) {
|
|
1070
|
+
async function requirePhase(gate, phaseKey, phaseName, orderStem = phaseKey, again = null) {
|
|
1059
1071
|
const r = await attest(phaseKey, phaseName, `require:${phaseKey}`);
|
|
1060
|
-
if (r.exit_code === 0) return await requireLeg(gate, phaseKey, phaseName, orderStem);
|
|
1072
|
+
if (r.exit_code === 0) return await requireLeg(gate, phaseKey, phaseName, orderStem, again);
|
|
1061
1073
|
// Exit 6 is `probe resume --require`'s OWN documented code for "the artifact really is not on
|
|
1062
1074
|
// disk" (kernel/probe/resume.mjs banner). Any other value — including -1, the courier's sentinel
|
|
1063
1075
|
// for a tool call that never ran — is not that predicate answering "no"; it is the predicate never
|
|
@@ -1381,8 +1393,8 @@ let spikedArea = "~", spikeResult = "~", riskiest = [];
|
|
|
1381
1393
|
if (!rs.has_orient_artifacts) {
|
|
1382
1394
|
log(`ORIENT — dispatching (slug ${slug})`);
|
|
1383
1395
|
await setRunStatus("orienting", "Orient");
|
|
1384
|
-
const
|
|
1385
|
-
skill: "orient", operation: "orient", schema: ORIENT, phase: "Orient", label: "orient",
|
|
1396
|
+
const dispatchOrient = (note = "") => worker({
|
|
1397
|
+
skill: "orient", operation: "orient", schema: ORIENT, phase: "Orient", label: note ? "orient:again" : "orient",
|
|
1386
1398
|
payload: { pitch: rs.intake_path, breadboard: rs.breadboard_path, spec_folder: specFolder, feature: slug, stack: rs.stack },
|
|
1387
1399
|
// NAME THE FILES. "write the orient/ artifacts" was the whole instruction, while completion is
|
|
1388
1400
|
// decided by four exact filenames — so a leg that did the work and called its output
|
|
@@ -1396,10 +1408,11 @@ if (!rs.has_orient_artifacts) {
|
|
|
1396
1408
|
"named exactly: `code-surface.md`, " +
|
|
1397
1409
|
"`discovered-seed.md`, `hill-signal.md`, and one `spike-<area>.md` (or `spike-not-needed.md` " +
|
|
1398
1410
|
"when the risk scan came back rank 0). Any other filename leaves the phase incomplete and " +
|
|
1399
|
-
"the run aborts, however good the contents are.",
|
|
1411
|
+
"the run aborts, however good the contents are." + note,
|
|
1400
1412
|
});
|
|
1413
|
+
const o = await dispatchOrient();
|
|
1401
1414
|
if (o.__failed) return await withWarnings(diedAt("ORIENT", o));
|
|
1402
|
-
const post = await requirePhase("ORIENT", "orient", "Orient");
|
|
1415
|
+
const post = await requirePhase("ORIENT", "orient", "Orient", "orient", dispatchOrient);
|
|
1403
1416
|
if (post) return await withWarnings(post);
|
|
1404
1417
|
await advisory(`reduce graph --slug ${slug}`, "Orient", "graph:orient");
|
|
1405
1418
|
spikedArea = o.spiked_area; spikeResult = o.spike_result; riskiest = o.riskiest_unknowns || [];
|
|
@@ -1521,13 +1534,14 @@ if (!rs.has_wiring_map) {
|
|
|
1521
1534
|
`resolve against. Write the profile, then relaunch.`));
|
|
1522
1535
|
}
|
|
1523
1536
|
log(`WIRE — dispatching (slug ${slug})`);
|
|
1524
|
-
const
|
|
1525
|
-
skill: "solution-architect", operation: "wire", schema: PHASE_OK, phase: "Wire", label: "wire",
|
|
1537
|
+
const dispatchWire = (note = "") => worker({
|
|
1538
|
+
skill: "solution-architect", operation: "wire", schema: PHASE_OK, phase: "Wire", label: note ? "wire:again" : "wire",
|
|
1526
1539
|
payload: { feature: slug, spec_folder: specFolder, project_profile: rs.project_profile_path, breadboard: rs.breadboard_path },
|
|
1527
|
-
extra: "Write the wiring map: per use case, engine → seam → entry-point call site → affordance.",
|
|
1540
|
+
extra: "Write the wiring map: per use case, engine → seam → entry-point call site → affordance." + note,
|
|
1528
1541
|
});
|
|
1542
|
+
const w = await dispatchWire();
|
|
1529
1543
|
if (w.__failed) return await withWarnings(diedAt("WIRE", w));
|
|
1530
|
-
const post = await requirePhase("WIRE", "wire", "Wire");
|
|
1544
|
+
const post = await requirePhase("WIRE", "wire", "Wire", "wire", dispatchWire);
|
|
1531
1545
|
if (post) return await withWarnings(post);
|
|
1532
1546
|
await advisory(`reduce graph --slug ${slug}`, "Wire", "graph:wire");
|
|
1533
1547
|
} else {
|