shapeup-sdlc 3.17.1 → 3.17.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shapeup-sdlc-plugin",
|
|
3
3
|
"displayName": "ShapeUp SDLC Plugin",
|
|
4
|
-
"version": "3.17.
|
|
4
|
+
"version": "3.17.2",
|
|
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
|
@@ -62,7 +62,7 @@ Everything discovered funnels into `.shapeup/<slug>/discovery/ledger.md` (Orient
|
|
|
62
62
|
- **Ledger = single source of truth** — every discovery flow writes only its own section.
|
|
63
63
|
- **QA is a level-up, not a gate** — `--no-qa` skips it; circuit breaker outranks the Hunter.
|
|
64
64
|
- **Role separation** — Evaluator grades, task-executor fixes, QA discovers.
|
|
65
|
-
- **The requirements matrix is a projection, never a verdict** — `REQ → AC → criterion → verdict` is derived from files for one named run (the registry, the board's `covers:` clauses, the run's verdict rows, the T0 citations), never narrated and never passed in. `covers:` is the authoritative join: a criterion anchored to a requirement no AC covers is printed for reconciliation and counted as nothing. L4 reads one line off it, GATE H's census takes the clauses with no evidence, `REPORT.md` freezes the table — and none of that blocks a ship. A clause with no PASS evidence is a fact the baseline comparison weighs, not a veto.
|
|
65
|
+
- **The requirements matrix is a projection, never a verdict** — `REQ → AC → criterion → verdict` is derived from files for one named run (the registry, the board's `covers:` clauses, the run's verdict rows, the T0 citations), never narrated and never passed in. `covers:` is the authoritative join: a criterion anchored to a requirement no AC covers is printed for reconciliation and counted as nothing. A verdict that anchors no criterion at all, beside a board whose ACs carry `covers:`, is sent back to the judge once. L4 reads one line off it, GATE H's census takes the clauses with no evidence, `REPORT.md` freezes the table — and none of that blocks a ship. A clause with no PASS evidence is a fact the baseline comparison weighs, not a veto.
|
|
66
66
|
- **Hill phase is mechanical ✦** — derived only from T0/T1 artifacts, never self-reported, and a T0-green from a round whose build gate ⚙ is red moves no dot (a green fixture in a round the feature did not build is evidence about the fixture); the evaluator cites a T0 artifact it re-hashes itself, from the list its order carries. A scoped verdict citing none is refused: its round stays open and is evaluated again, never advanced.
|
|
67
67
|
- **Envelope port (v1.0)** — every dispatch is WorkOrder in / WorkResult out; shared state has exactly one writer (the ingest step); malformed envelopes are hook-denied. Workers: stateless, craft-only, pipeline-blind.
|
|
68
68
|
|
package/kernel/probe/eval.mjs
CHANGED
|
@@ -36,6 +36,8 @@ import { join, resolve, resolve as resolvePath, sep } from "node:path";
|
|
|
36
36
|
import { createHash } from "node:crypto";
|
|
37
37
|
import { runArgs } from "../lib/argv.mjs";
|
|
38
38
|
import { resultsDir, scopesDir, readRunId, verdictsDir, specDir } from "../lib/paths.mjs";
|
|
39
|
+
import { readBoard } from "../compile.mjs";
|
|
40
|
+
import { coveringAcs } from "./requirements.mjs";
|
|
39
41
|
|
|
40
42
|
/** Longest `reason` reported. A deviation is prose written by a worker and can run to paragraphs. */
|
|
41
43
|
const REASON_MAX = 400;
|
|
@@ -253,6 +255,33 @@ export function coverageProblem(cwd, slug, verdict) {
|
|
|
253
255
|
`each row is its own criterion, its id in the criterion or its traces_to; ungraded: ${shown}`;
|
|
254
256
|
}
|
|
255
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Whether a verdict anchors any criterion to a requirement when the board says which ones it could.
|
|
260
|
+
*
|
|
261
|
+
* `traces_to` is copied from the `(covers: REQ-…)` clauses of the acceptance criteria a criterion
|
|
262
|
+
* grades, and the requirements matrix is joined along it. Two judges on the same spec and the same
|
|
263
|
+
* instruction differed: one anchored every row and the matrix read 5/5, the next anchored none and it
|
|
264
|
+
* read 0/5 over the same passes. Held only to the all-empty case: which requirement a criterion
|
|
265
|
+
* traces to is the judge's reading, but none at all, beside a board that covers some, is a copy
|
|
266
|
+
* step skipped.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} cwd - Project root.
|
|
269
|
+
* @param {string} slug - Feature slug.
|
|
270
|
+
* @param {object} verdict - The WorkResult's `verdict`.
|
|
271
|
+
* @returns {(string|null)} A reason phrased for the judge, or null.
|
|
272
|
+
*/
|
|
273
|
+
export function tracesProblem(cwd, slug, verdict) {
|
|
274
|
+
if (verdict?.overall !== "PASS" && verdict?.overall !== "FAIL") return null;
|
|
275
|
+
const criteria = Array.isArray(verdict.criteria) ? verdict.criteria : [];
|
|
276
|
+
if (!criteria.length) return null;
|
|
277
|
+
if (criteria.some((c) => Array.isArray(c?.traces_to) && c.traces_to.length)) return null;
|
|
278
|
+
let covered = 0;
|
|
279
|
+
try { covered = coveringAcs(readBoard(cwd, slug)).size; } catch { return null; }
|
|
280
|
+
if (!covered) return null;
|
|
281
|
+
return `the verdict anchors none of its ${criteria.length} criteria to a requirement while the board's acceptance ` +
|
|
282
|
+
`criteria cover ${covered} — copy each graded AC's (covers: REQ-…) clause into that criterion's traces_to`;
|
|
283
|
+
}
|
|
284
|
+
|
|
256
285
|
export function citationProblem(cwd, slug, verdict, { round = null } = {}) {
|
|
257
286
|
if (verdict?.overall !== "PASS" && verdict?.overall !== "FAIL") return null;
|
|
258
287
|
if (!isScoped(cwd, slug)) return null;
|
|
@@ -297,7 +326,7 @@ export function evalVerdict(cwd, slug, round) {
|
|
|
297
326
|
? `the evaluator returned ${status || "no status"}: ${first}`
|
|
298
327
|
: `status ${status || "unknown"} with no PASS/FAIL verdict`), status);
|
|
299
328
|
}
|
|
300
|
-
const problem = verdictProblem(v) || coverageProblem(cwd, slug, v) || citationProblem(cwd, slug, v, { round });
|
|
329
|
+
const problem = verdictProblem(v) || coverageProblem(cwd, slug, v) || tracesProblem(cwd, slug, v) || citationProblem(cwd, slug, v, { round });
|
|
301
330
|
if (problem) return unfit(problem, status, overall);
|
|
302
331
|
return {
|
|
303
332
|
found: true,
|
package/kernel/reduce/ingest.mjs
CHANGED
|
@@ -37,7 +37,7 @@ import { fileURLToPath } from "node:url";
|
|
|
37
37
|
import { validate } from "../verify/envelope.mjs";
|
|
38
38
|
import { runArgs } from "../lib/argv.mjs";
|
|
39
39
|
import { tasksDir, localRoot, dispatchReceipts, legLedger, readRunId, qaDir } from "../lib/paths.mjs";
|
|
40
|
-
import { citationProblem, coverageProblem, verdictProblem } from "../probe/eval.mjs";
|
|
40
|
+
import { citationProblem, coverageProblem, tracesProblem, verdictProblem } from "../probe/eval.mjs";
|
|
41
41
|
import { huntProblem } from "../probe/hunt.mjs";
|
|
42
42
|
|
|
43
43
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -702,7 +702,7 @@ export async function cli(rawArgv) {
|
|
|
702
702
|
const evalSlug = String(result.order_id).split("/")[0];
|
|
703
703
|
const evalRound = Number((String(result.order_id).match(/-r(\d+)$/) || [])[1]) || null;
|
|
704
704
|
const problem = verdictProblem(result.verdict) || coverageProblem(cwd, evalSlug, result.verdict)
|
|
705
|
-
|| citationProblem(cwd, evalSlug, result.verdict, { round: evalRound });
|
|
705
|
+
|| tracesProblem(cwd, evalSlug, result.verdict) || citationProblem(cwd, evalSlug, result.verdict, { round: evalRound });
|
|
706
706
|
if (problem) {
|
|
707
707
|
console.error(`ingest-result: result refused — ${problem}.`);
|
|
708
708
|
console.error(` The round stays open: re-dispatch the evaluator against its order, which lists`);
|
package/package.json
CHANGED
|
@@ -188,7 +188,7 @@ clause yields no anchor — leave the array empty rather than guessing, and neve
|
|
|
188
188
|
supply one. This changes nothing you grade: the anchor is a navigation path, never a grading input,
|
|
189
189
|
and a criterion passes or fails on its evidence exactly as before. It matters downstream because
|
|
190
190
|
the requirement matrix at GATE L4 and the census at GATE H are projected from these anchors; a
|
|
191
|
-
verdict that drops them grades the build and says nothing about what the pitch asked for.
|
|
191
|
+
verdict that drops them grades the build and says nothing about what the pitch asked for. Ingest refuses a verdict that anchors no criterion at all while the board's ACs carry `covers:` clauses, and you are sent back once.
|
|
192
192
|
|
|
193
193
|
**Every FAIL criterion's `evidence` MUST carry a `file:line` locator** — schema-enforced, not
|
|
194
194
|
advice: the envelope is validated against `work-result.schema.json` at ingest and a locatorless
|