shapeup-sdlc 3.0.2 → 3.1.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.
@@ -422,6 +422,23 @@ Why it aborts rather than pauses: nothing persists an answer between launches, s
422
422
  in front of a human once instead of looping silently.
423
423
  ```
424
424
 
425
+ ## 3b.1 The other reason `--require`/`--set-status` fail: the check never ran
426
+ ```
427
+ The abort message names an exit code. `probe resume --require` documents exit 0 (satisfied) or
428
+ 6 (artifact genuinely absent) as its only two real answers — anything else means the
429
+ mechanical courier's Bash call was denied before the kernel script ever ran, most often
430
+ an untrusted workspace or Claude Code's own auto-mode classifier flagging the courier's
431
+ --require/--set-status pattern as gate-manipulation. Neither layer is this plugin's own
432
+ hook, so neither leaves a row in .shapeup/<slug>/decisions.jsonl.
433
+ What you see: the same-shaped abort as 3b, but the artifact IS actually on disk — the abort
434
+ message now says so (exit_code shown, courier's own detail quoted) instead of asserting
435
+ "the worker most likely escalated".
436
+ Do: verify the phase's artifact by hand before trusting either diagnosis. If it exists, the
437
+ phase is done; either relaunch (the classifier's denial is not guaranteed to repeat) or
438
+ drive the remaining phase's kernel commands directly rather than through the Workflow —
439
+ this is what routes around a blocked relaunch without waiting on the classifier.
440
+ ```
441
+
425
442
  ## 3c. T0 verify → `harness verify t0` (skill-local; scope contracts present, every attempt)
426
443
  ```
427
444
  Invoke via Bash directly — NOT an Agent, this is deterministic tooling, not a worker:
@@ -609,7 +609,12 @@ async function cmd(verbs, phaseName, label) {
609
609
  `Report its exit code as exit_code, ok=true if and only if exit_code is 0, and one line of ` +
610
610
  `detail. If the command printed JSON carrying a top-level "decision" key, copy that value into ` +
611
611
  `decision EXACTLY as it appears — one bare token, no sentence, no quotes, no rephrasing. ` +
612
- `Otherwise omit decision. Do not interpret, summarise or act on the command's output beyond that.`,
612
+ `Otherwise omit decision. Do not interpret, summarise or act on the command's output beyond that.\n\n` +
613
+ `If the tool call itself is refused or blocked before the command ever runs — a permission or ` +
614
+ `policy denial, not the command's own exit — that is NOT an exit code, and you must never invent ` +
615
+ `one to fill the field: report exit_code as -1 and put the denial's own wording verbatim in ` +
616
+ `detail. -1 is not a real process exit and this file treats it as "the check could not run", never ` +
617
+ `as evidence about what the command would have said.`,
613
618
  { model: "sonnet", effort: "low", phase: phaseName, label, schema: CMD },
614
619
  );
615
620
  return (r && typeof r === "object") ? r : { exit_code: -1, ok: false, detail: `${label}: no result` };
@@ -813,12 +818,29 @@ const attest = (phaseKey, phaseName, label) =>
813
818
  async function requirePhase(gate, phaseKey, phaseName) {
814
819
  const r = await attest(phaseKey, phaseName, `require:${phaseKey}`);
815
820
  if (r.exit_code === 0) return null;
821
+ // Exit 6 is `probe resume --require`'s OWN documented code for "the artifact really is not on
822
+ // disk" (kernel/probe/resume.mjs banner). Any other value — including -1, the courier's sentinel
823
+ // for a tool call that never ran — is not that predicate answering "no"; it is the predicate never
824
+ // being asked. Collapsing both into one message manufactured a false "worker escalated" diagnosis
825
+ // the one time this was measured live: the artifacts existed, and the check itself had been denied
826
+ // by a layer above this plugin (workspace trust, or Claude Code's own auto-mode classifier —
827
+ // neither leaves a row in decisions.jsonl, because neither is this plugin's hook).
828
+ if (r.exit_code === 6) {
829
+ return aborted(gate,
830
+ `${gate} produced no artifact: the ${phaseKey} artifact is not on disk after the phase ran and its ` +
831
+ `result was ingested. The phase did not complete — its worker most likely escalated (a WorkResult ` +
832
+ `may report "escalated" with an empty artifacts list) — and because completion is derived from the ` +
833
+ `artifact, every relaunch would re-dispatch this phase and escalate again. Read the phase's result ` +
834
+ `to see what it could not complete, resolve it, then relaunch.`);
835
+ }
816
836
  return aborted(gate,
817
- `${gate} produced no artifact: the ${phaseKey} artifact is not on disk after the phase ran and its ` +
818
- `result was ingested. The phase did not complete its worker most likely escalated (a WorkResult ` +
819
- `may report "escalated" with an empty artifacts list) and because completion is derived from the ` +
820
- `artifact, every relaunch would re-dispatch this phase and escalate again. Read the phase's result ` +
821
- `to see what it could not complete, resolve it, then relaunch.`);
837
+ `${gate}'s completion check for "${phaseKey}" did not run to a verdict exit_code ${r.exit_code}, ` +
838
+ `not the 0 (satisfied) or 6 (artifact absent) \`probe resume --require\` documents.` +
839
+ `${r.detail ? ` Courier reported: ${r.detail}.` : ""} This is NOT evidence the phase failed it means ` +
840
+ `the check itself could not run, most often a Bash call denied above this plugin's own hooks and ` +
841
+ `permission grant (an untrusted workspace, or Claude Code's auto-mode classifier). Verify the ` +
842
+ `${phaseKey} artifact by hand before assuming it is missing, then either relaunch or drive the ` +
843
+ `remaining phase's kernel commands directly.`);
822
844
  }
823
845
 
824
846
  /**
@@ -851,12 +873,24 @@ async function fastForward(gate, phaseKey, phaseName, what) {
851
873
  log(`${gate} — ${what}, fast-forwarding past it`);
852
874
  const r = await attest(phaseKey, phaseName, `ff:${phaseKey}`);
853
875
  if (r.exit_code === 0) return null;
876
+ // Same distinction as requirePhase: only exit 6 is the predicate genuinely answering "not there".
877
+ // Anything else means this re-attestation call itself never executed, most likely denied above
878
+ // this plugin's hooks and permission grant — not a real disagreement between the two probes.
879
+ if (r.exit_code === 6) {
880
+ return aborted(gate,
881
+ `${gate} was fast-forwarded on a state probe that reported its artifact present, and re-asking ` +
882
+ `\`probe resume --require ${phaseKey}\` at the phase itself says it is not on disk. Two readings ` +
883
+ `of "is this done" disagree, and the one taken AT the phase is the later of the two — so the run ` +
884
+ `stops here rather than building on a phase nothing can attest. Check whether the ${phaseKey} ` +
885
+ `artifact was moved or removed since this run last touched it, then relaunch.`);
886
+ }
854
887
  return aborted(gate,
855
- `${gate} was fast-forwarded on a state probe that reported its artifact present, and re-asking ` +
856
- `\`probe resume --require ${phaseKey}\` at the phase itself says it is not on disk. Two readings ` +
857
- `of "is this done" disagree, and the one taken AT the phase is the later of the two so the run ` +
858
- `stops here rather than building on a phase nothing can attest. Check whether the ${phaseKey} ` +
859
- `artifact was moved or removed since this run last touched it, then relaunch.`);
888
+ `${gate}'s re-attestation of "${phaseKey}" did not run to a verdict — exit_code ${r.exit_code}, not ` +
889
+ `the 0 (satisfied) or 6 (artifact absent) \`probe resume --require\` documents.` +
890
+ `${r.detail ? ` Courier reported: ${r.detail}.` : ""} The two probes are not actually in disagreement — ` +
891
+ `this one never executed, most often a Bash call denied above this plugin's own hooks and ` +
892
+ `permission grant (an untrusted workspace, or Claude Code's auto-mode classifier). Verify the ` +
893
+ `${phaseKey} artifact by hand before assuming it regressed, then relaunch.`);
860
894
  }
861
895
 
862
896
  // The ledger's `status` field is bookkeeping, not this file's resume oracle — the fast-forward reads