scrumrun 2.0.0 → 2.1.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.
- package/CHANGELOG.md +45 -0
- package/CORE.md +17 -3
- package/DECISIONS.md +56 -0
- package/MIGRATION-1-to-2.md +11 -0
- package/README.md +23 -5
- package/SPEC.md +30 -10
- package/bin/scrumrun.js +175 -11
- package/docs/COMMANDS.md +10 -4
- package/docs/ENTITY-MODEL.md +1 -1
- package/docs/RELEASE-SCORECARD.md +43 -0
- package/docs/RELEASE.md +19 -12
- package/docs/SCHEMA.md +11 -0
- package/docs/SEMANTIC-MEMORY.md +1 -1
- package/docs/TROUBLESHOOTING.md +13 -1
- package/lib/commands/manifest.js +15 -3
- package/lib/commands/render.js +4 -0
- package/lib/memory/index.js +201 -41
- package/lib/memory/service.js +3 -0
- package/lib/runtime/budgets.js +4 -0
- package/lib/runtime/canonical-snapshot.js +110 -0
- package/lib/runtime/context.js +5 -45
- package/lib/runtime/mutation-gateway.js +434 -0
- package/lib/runtime/orchestrator.js +130 -65
- package/lib/runtime/policy-engine.js +267 -0
- package/lib/runtime/request-engine.js +32 -24
- package/lib/runtime/review-service.js +92 -0
- package/lib/runtime/run-ledger.js +546 -0
- package/lib/runtime/workspace-state.js +146 -0
- package/lib/security/secrets.js +15 -1
- package/lib/v2/artifacts.js +24 -1
- package/lib/v2/conformance.js +78 -12
- package/lib/v2/migration.js +74 -10
- package/lib/v2/run-ledger-migration.js +268 -0
- package/lib/v2/schema.js +28 -1
- package/lib/v2/transaction.js +254 -0
- package/package.json +1 -1
- package/scripts/generate-contract-docs.js +11 -0
- package/templates/project/.scrumrun/guardrails.md +8 -0
- package/templates/project/.scrumrun/map.md +4 -3
- package/templates/project/.scrumrun/method.json +7 -1
- package/templates/project/.scrumrun/state.md +7 -14
- package/templates/project/AGENTS.md +2 -1
- package/templates/project-lean/AGENTS.md +3 -1
- package/templates/shared/skills/scrumrun/SKILL.md +19 -5
package/bin/scrumrun.js
CHANGED
|
@@ -9,14 +9,23 @@ const root = path.resolve(__dirname, "..");
|
|
|
9
9
|
const templates = path.join(root, "templates");
|
|
10
10
|
const { version } = require(path.join(root, "package.json"));
|
|
11
11
|
const { applyMigration, dryRunMigration, rollbackMigration } = require(path.join(root, "lib", "v2", "migration"));
|
|
12
|
+
const {
|
|
13
|
+
applyRunLedgerMigration,
|
|
14
|
+
planRunLedgerMigration,
|
|
15
|
+
reportRunLedgerMigration,
|
|
16
|
+
rollbackRunLedgerMigration
|
|
17
|
+
} = require(path.join(root, "lib", "v2", "run-ledger-migration"));
|
|
12
18
|
const { ARTIFACT_TYPES, ArtifactRepository } = require(path.join(root, "lib", "v2", "artifacts"));
|
|
13
19
|
const { aliases: COMMAND_ALIASES, resolveAlias, resolveRoute } = require(path.join(root, "lib", "commands", "manifest"));
|
|
14
20
|
const { renderCommandHelp, renderCompatibilityPrompt, renderRootPrompt } = require(path.join(root, "lib", "commands", "render"));
|
|
15
21
|
const { planRequest } = require(path.join(root, "lib", "runtime", "request-engine"));
|
|
16
22
|
const { approveRequest, refreshState, retryTask, transitionRun } = require(path.join(root, "lib", "runtime", "orchestrator"));
|
|
23
|
+
const { authorizeMutation, recordMutation, satisfyGuardrail } = require(path.join(root, "lib", "runtime", "mutation-gateway"));
|
|
24
|
+
const { recordArtifactReview } = require(path.join(root, "lib", "runtime", "review-service"));
|
|
17
25
|
const { createMemory, listMemory, showMemory, transitionMemory } = require(path.join(root, "lib", "memory", "service"));
|
|
18
|
-
const { indexPath, indexStatus, queryIndex, rebuildIndex, writeMap } = require(path.join(root, "lib", "memory", "index"));
|
|
26
|
+
const { indexPath, indexStatus, mapStatus, queryIndex, rebuildIndex, writeMap } = require(path.join(root, "lib", "memory", "index"));
|
|
19
27
|
const { auditProject } = require(path.join(root, "lib", "v2", "conformance"));
|
|
28
|
+
const { recoverPendingTransactions } = require(path.join(root, "lib", "v2", "transaction"));
|
|
20
29
|
const { containsSecret } = require(path.join(root, "lib", "security", "secrets"));
|
|
21
30
|
|
|
22
31
|
const COMMANDS = ["sc"];
|
|
@@ -39,7 +48,7 @@ Usage:
|
|
|
39
48
|
scrumrun migrate --to 2 --dry-run
|
|
40
49
|
scrumrun migrate --to 2 --apply
|
|
41
50
|
scrumrun migrate --to 2 --rollback
|
|
42
|
-
scrumrun doctor [all|codex|opencode|claude] [--strict]
|
|
51
|
+
scrumrun doctor [all|codex|opencode|claude] [--strict] [--recover]
|
|
43
52
|
scrumrun uninstall [--force]
|
|
44
53
|
|
|
45
54
|
Examples:
|
|
@@ -267,7 +276,27 @@ function migrationPreflightOnUpdate({ apply = false } = {}) {
|
|
|
267
276
|
const marker = path.join(scrumDir, "method.json");
|
|
268
277
|
if (fs.existsSync(marker)) {
|
|
269
278
|
try {
|
|
270
|
-
if (JSON.parse(fs.readFileSync(marker, "utf8")).method === "2.0.0")
|
|
279
|
+
if (JSON.parse(fs.readFileSync(marker, "utf8")).method === "2.0.0") {
|
|
280
|
+
const preview = planRunLedgerMigration(process.cwd());
|
|
281
|
+
if (preview.status === "current") return { status: "already-v2" };
|
|
282
|
+
console.log("\n## Project schema migration preflight\n");
|
|
283
|
+
console.log(reportRunLedgerMigration(preview).trimEnd());
|
|
284
|
+
if (preview.status === "blocked") {
|
|
285
|
+
for (const error of preview.errors) console.error(`BLOCKED: ${error}`);
|
|
286
|
+
process.exitCode = 1;
|
|
287
|
+
return { status: "blocked" };
|
|
288
|
+
}
|
|
289
|
+
if (!apply) {
|
|
290
|
+
console.log("\nThe project remains unchanged. Apply the verified plan with: npx scrumrun@latest update --migrate");
|
|
291
|
+
return { status: "ready" };
|
|
292
|
+
}
|
|
293
|
+
const result = applyRunLedgerMigration(process.cwd());
|
|
294
|
+
refreshState(path.join(process.cwd(), ".scrumrun"));
|
|
295
|
+
console.log("\nApplied the verified ScrumRun Run ledger schema migration.");
|
|
296
|
+
console.log(`Source fingerprint: ${result.plan.fingerprint}`);
|
|
297
|
+
console.log("Rollback remains available with: npx scrumrun@latest migrate --to 2 --rollback");
|
|
298
|
+
return { status: result.status, result };
|
|
299
|
+
}
|
|
271
300
|
} catch {
|
|
272
301
|
console.error("Project migration preflight failed: .scrumrun/method.json is malformed.");
|
|
273
302
|
process.exitCode = 1;
|
|
@@ -1167,6 +1196,61 @@ function memoryOptions(args) {
|
|
|
1167
1196
|
};
|
|
1168
1197
|
}
|
|
1169
1198
|
|
|
1199
|
+
function runTransitionOptions(args) {
|
|
1200
|
+
const evidenceFlags = new Map([
|
|
1201
|
+
["--command", "command"],
|
|
1202
|
+
["--test", "test"],
|
|
1203
|
+
["--file", "file"],
|
|
1204
|
+
["--review", "review"],
|
|
1205
|
+
["--decision", "decision"],
|
|
1206
|
+
["--insight", "insight"],
|
|
1207
|
+
["--risk", "risk"]
|
|
1208
|
+
]);
|
|
1209
|
+
const referenceKinds = new Set(["file", "review", "decision", "insight"]);
|
|
1210
|
+
const evidence = [];
|
|
1211
|
+
const noteParts = [];
|
|
1212
|
+
let note = null;
|
|
1213
|
+
let actor = "agent";
|
|
1214
|
+
let occurredAt = null;
|
|
1215
|
+
for (let index = 2; index < args.length; index++) {
|
|
1216
|
+
const token = args[index];
|
|
1217
|
+
const value = args[index + 1] && !args[index + 1].startsWith("--") ? args[index + 1] : null;
|
|
1218
|
+
if (token === "--note" || token === "--actor" || token === "--at" || token === "--evidence" || evidenceFlags.has(token)) {
|
|
1219
|
+
if (!value) throw new Error(`${token} requires a value.`);
|
|
1220
|
+
index++;
|
|
1221
|
+
if (token === "--note") note = value;
|
|
1222
|
+
else if (token === "--actor") actor = value;
|
|
1223
|
+
else if (token === "--at") occurredAt = value;
|
|
1224
|
+
else if (token === "--evidence") {
|
|
1225
|
+
const separator = value.indexOf(":");
|
|
1226
|
+
const kind = separator > 0 ? value.slice(0, separator) : "note";
|
|
1227
|
+
const content = separator > 0 ? value.slice(separator + 1) : value;
|
|
1228
|
+
evidence.push(referenceKinds.has(kind) ? { kind, ref: content } : { kind, summary: content });
|
|
1229
|
+
} else {
|
|
1230
|
+
const kind = evidenceFlags.get(token);
|
|
1231
|
+
evidence.push(referenceKinds.has(kind) ? { kind, ref: value } : { kind, summary: value });
|
|
1232
|
+
}
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
if (token.startsWith("--")) throw new Error(`Unknown Run evidence option: ${token}`);
|
|
1236
|
+
noteParts.push(token);
|
|
1237
|
+
}
|
|
1238
|
+
return { note: note || noteParts.join(" ").trim() || null, evidence, actor, occurredAt };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function removeOptionPairs(args, names) {
|
|
1242
|
+
const hidden = new Set(names);
|
|
1243
|
+
const next = [];
|
|
1244
|
+
for (let index = 0; index < args.length; index++) {
|
|
1245
|
+
if (hidden.has(args[index])) {
|
|
1246
|
+
index++;
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
next.push(args[index]);
|
|
1250
|
+
}
|
|
1251
|
+
return next;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1170
1254
|
function printMemoryArtifact(artifact) {
|
|
1171
1255
|
if (!artifact) return false;
|
|
1172
1256
|
console.log(readIfExists(artifact.file));
|
|
@@ -1233,9 +1317,8 @@ function runSemanticContext(subject, args) {
|
|
|
1233
1317
|
return;
|
|
1234
1318
|
}
|
|
1235
1319
|
const mapFile = path.join(process.cwd(), ".scrumrun", "map.md");
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
if (status.stale) console.warn("WARNING: map.md is stale; rebuild it before relying on the view.");
|
|
1320
|
+
const status = mapStatus(process.cwd());
|
|
1321
|
+
if (status.stale) throw new Error(`Generated map.md is stale (${status.reason || status.error || "unknown reason"}); run /sc knowledge map --build.`);
|
|
1239
1322
|
console.log(fs.readFileSync(mapFile, "utf8"));
|
|
1240
1323
|
return;
|
|
1241
1324
|
}
|
|
@@ -1272,8 +1355,11 @@ function executeRootRoute(route) {
|
|
|
1272
1355
|
console.log(`State: ${plan.state}`);
|
|
1273
1356
|
console.log(`Classification: ${plan.classification.type} (${plan.classification.reason})`);
|
|
1274
1357
|
console.log(`Risk: ${plan.risk.level} — ${plan.risk.reasons.join("; ")}`);
|
|
1275
|
-
console.log(`Policy: ${plan.policy.status}`);
|
|
1358
|
+
console.log(`Policy: ${plan.policy.status} (${plan.policy.checked.length} checked; ${plan.policy.deferred.length} deferred)`);
|
|
1276
1359
|
for (const violation of plan.policy.violations) console.log(`BLOCKED: ${violation}`);
|
|
1360
|
+
for (const result of plan.policy.evaluations.filter((item) => item.status === "deferred")) {
|
|
1361
|
+
console.log(`DEFERRED: ${result.guardrail} ${result.code}: ${result.message}`);
|
|
1362
|
+
}
|
|
1277
1363
|
for (const warning of plan.context.warnings) console.log(`WARNING: ${warning}`);
|
|
1278
1364
|
if (plan.approvalToken) {
|
|
1279
1365
|
console.log(`Approval: scrumrun sc plan intake --approve ${plan.approvalToken}`);
|
|
@@ -1302,6 +1388,33 @@ function executeRootRoute(route) {
|
|
|
1302
1388
|
return;
|
|
1303
1389
|
}
|
|
1304
1390
|
if (noun === "plan" && subject === "run") {
|
|
1391
|
+
if (routeArgs[0] === "--authorize-mutation") {
|
|
1392
|
+
const paths = optionValues(routeArgs.slice(2), "--path");
|
|
1393
|
+
const result = authorizeMutation(process.cwd(), routeArgs[1], paths);
|
|
1394
|
+
console.log(`Authorized ${result.permit} for ${result.run} until ${result.expiresAt}.`);
|
|
1395
|
+
console.log(`Paths: ${result.paths.join(", ")}`);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (routeArgs[0] === "--record-mutation") {
|
|
1399
|
+
const permit = optionValue(routeArgs.slice(2), "--permit");
|
|
1400
|
+
if (!permit) throw new Error("--record-mutation requires --permit MUT-id.");
|
|
1401
|
+
const parsed = runTransitionOptions(removeOptionPairs(routeArgs, ["--permit"]));
|
|
1402
|
+
const result = recordMutation(process.cwd(), routeArgs[1], permit, parsed);
|
|
1403
|
+
refreshState(projectFile());
|
|
1404
|
+
console.log(`Recorded ${result.mutation} for ${result.run.id}: ${result.changes.length} verified change(s).`);
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
if (routeArgs[0] === "--satisfy-guardrail") {
|
|
1408
|
+
const guardrail = optionValue(routeArgs.slice(2), "--guardrail");
|
|
1409
|
+
if (!guardrail) throw new Error("--satisfy-guardrail requires --guardrail GR-NNN.");
|
|
1410
|
+
const migration = optionValues(routeArgs.slice(2), "--migration").map((ref) => ({ kind: "migration", ref }));
|
|
1411
|
+
const parsed = runTransitionOptions(removeOptionPairs(routeArgs, ["--guardrail", "--migration"]));
|
|
1412
|
+
parsed.evidence.push(...migration);
|
|
1413
|
+
const result = satisfyGuardrail(process.cwd(), routeArgs[1], guardrail, parsed);
|
|
1414
|
+
refreshState(projectFile());
|
|
1415
|
+
console.log(`${result.run.id}: ${result.guardrail} ${result.status}.`);
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1305
1418
|
const transitions = {
|
|
1306
1419
|
"--validate": "validating",
|
|
1307
1420
|
"--learn": "learning",
|
|
@@ -1311,7 +1424,7 @@ function executeRootRoute(route) {
|
|
|
1311
1424
|
"--block": "blocked"
|
|
1312
1425
|
};
|
|
1313
1426
|
if (transitions[routeArgs[0]]) {
|
|
1314
|
-
const result = transitionRun(process.cwd(), routeArgs[1], transitions[routeArgs[0]],
|
|
1427
|
+
const result = transitionRun(process.cwd(), routeArgs[1], transitions[routeArgs[0]], runTransitionOptions(routeArgs));
|
|
1315
1428
|
console.log(`${result.run.id}: ${result.run.status}; ${result.task.id}: ${result.task.status}.`);
|
|
1316
1429
|
if (result.learning) {
|
|
1317
1430
|
if (result.learning.created.length) console.log(`Learning candidates: ${result.learning.created.join(", ")}.`);
|
|
@@ -1332,10 +1445,24 @@ function executeRootRoute(route) {
|
|
|
1332
1445
|
if (!audit.passed) process.exitCode = 1;
|
|
1333
1446
|
return;
|
|
1334
1447
|
}
|
|
1448
|
+
if (noun === "review" && subject === "artifact" && routeArgs[0] === "--record") {
|
|
1449
|
+
const task = optionValue(routeArgs.slice(1), "--task");
|
|
1450
|
+
if (!task) throw new Error("--record requires --task TASK-NNN.");
|
|
1451
|
+
const result = recordArtifactReview(process.cwd(), {
|
|
1452
|
+
task,
|
|
1453
|
+
run: optionValue(routeArgs.slice(1), "--run"),
|
|
1454
|
+
title: optionValue(routeArgs.slice(1), "--title"),
|
|
1455
|
+
evidence: optionValues(routeArgs.slice(1), "--evidence")
|
|
1456
|
+
});
|
|
1457
|
+
refreshState(projectFile());
|
|
1458
|
+
console.log(`${result.review.record.id}: ${result.review.record.status}; ${result.audit.invariants} invariants; ${result.audit.findings.length} finding(s).`);
|
|
1459
|
+
if (!result.audit.passed) process.exitCode = 1;
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1335
1462
|
if (noun === "config" && subject === "migrate") return runMigration(routeArgs);
|
|
1336
1463
|
if (noun === "config" && subject === "doctor") {
|
|
1337
1464
|
const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
|
|
1338
|
-
return doctor(target);
|
|
1465
|
+
return doctor(target, { strict: routeArgs.includes("--strict"), recover: routeArgs.includes("--recover") });
|
|
1339
1466
|
}
|
|
1340
1467
|
if (noun === "config" && subject === "update") {
|
|
1341
1468
|
const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
|
|
@@ -2097,6 +2224,33 @@ function runMigration(parts) {
|
|
|
2097
2224
|
return;
|
|
2098
2225
|
}
|
|
2099
2226
|
try {
|
|
2227
|
+
const ledgerManifest = path.join(process.cwd(), ".scrumrun", ".migration", "run-ledger-v1", "manifest.json");
|
|
2228
|
+
if (v2Project() && actions[0] !== "--rollback") {
|
|
2229
|
+
if (actions[0] === "--dry-run") {
|
|
2230
|
+
const plan = planRunLedgerMigration(process.cwd());
|
|
2231
|
+
console.log(reportRunLedgerMigration(plan).trimEnd());
|
|
2232
|
+
if (plan.status === "blocked") process.exitCode = 1;
|
|
2233
|
+
} else {
|
|
2234
|
+
const result = applyRunLedgerMigration(process.cwd());
|
|
2235
|
+
if (result.status === "current") {
|
|
2236
|
+
console.log("ScrumRun project is already migrated to method 2.0.0 and the current Run ledger; no files changed.");
|
|
2237
|
+
} else {
|
|
2238
|
+
refreshState(path.join(process.cwd(), ".scrumrun"));
|
|
2239
|
+
console.log(result.output.trimEnd());
|
|
2240
|
+
console.log("Rollback remains available with: scrumrun migrate --to 2 --rollback");
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
if (actions[0] === "--rollback" && fs.existsSync(ledgerManifest)) {
|
|
2246
|
+
const manifest = JSON.parse(fs.readFileSync(ledgerManifest, "utf8"));
|
|
2247
|
+
if (["applied", "prepared"].includes(manifest.status)) {
|
|
2248
|
+
const result = rollbackRunLedgerMigration(process.cwd());
|
|
2249
|
+
refreshState(path.join(process.cwd(), ".scrumrun"));
|
|
2250
|
+
console.log(`Rolled back ScrumRun Run ledger migration: ${result.status}.`);
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2100
2254
|
if (actions[0] === "--dry-run") {
|
|
2101
2255
|
const result = dryRunMigration(process.cwd());
|
|
2102
2256
|
console.log(result.output.trimEnd());
|
|
@@ -2125,12 +2279,22 @@ function runMigration(parts) {
|
|
|
2125
2279
|
}
|
|
2126
2280
|
}
|
|
2127
2281
|
|
|
2128
|
-
function doctor(target = "all", { compatibility = false, strict = false } = {}) {
|
|
2282
|
+
function doctor(target = "all", { compatibility = false, strict = false, recover = false } = {}) {
|
|
2129
2283
|
const home = os.homedir();
|
|
2130
2284
|
const checks = [];
|
|
2131
2285
|
const commands = compatibility ? [...COMMANDS, ...COMPATIBILITY_COMMANDS] : COMMANDS;
|
|
2132
2286
|
const skillContent = fs.readFileSync(path.join(templates, "shared", "skills", "scrumrun", "SKILL.md"), "utf8");
|
|
2133
2287
|
|
|
2288
|
+
if (recover) {
|
|
2289
|
+
const scrumDir = path.join(process.cwd(), ".scrumrun");
|
|
2290
|
+
if (!fs.existsSync(scrumDir)) throw new Error("Cannot recover transactions outside a ScrumRun project.");
|
|
2291
|
+
const recovered = recoverPendingTransactions(scrumDir);
|
|
2292
|
+
console.log(recovered.length
|
|
2293
|
+
? `recovered ${recovered.map((item) => `${item.id}:${item.action}`).join(", ")}`
|
|
2294
|
+
: "recovered no pending kernel transactions");
|
|
2295
|
+
refreshState(scrumDir);
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2134
2298
|
function commandContent(command) {
|
|
2135
2299
|
return command === "sc" ? renderRootPrompt() : renderCompatibilityPrompt(command);
|
|
2136
2300
|
}
|
|
@@ -2245,7 +2409,7 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
2245
2409
|
runMigration(args.slice(1));
|
|
2246
2410
|
} else if (command === "doctor") {
|
|
2247
2411
|
const target = ["all", "codex", "opencode", "claude"].includes(args[1]) ? args[1] : "all";
|
|
2248
|
-
doctor(target, { compatibility: args.includes("--compat"), strict: args.includes("--strict") });
|
|
2412
|
+
doctor(target, { compatibility: args.includes("--compat"), strict: args.includes("--strict"), recover: args.includes("--recover") });
|
|
2249
2413
|
} else if (command === "claude") {
|
|
2250
2414
|
const sub = args[1];
|
|
2251
2415
|
if (sub === "install" || sub === "update") {
|
package/docs/COMMANDS.md
CHANGED
|
@@ -16,11 +16,14 @@ Use `/sc` inside a supported AI client. The equivalent CLI form is `npx scrumrun
|
|
|
16
16
|
/sc plan task --add|--list|--show|--run|--audit|--cancel|--retry
|
|
17
17
|
/sc plan sprint --add|--list|--show|--start|--complete|--block
|
|
18
18
|
/sc plan feature --add|--list|--show|--activate|--complete
|
|
19
|
-
/sc plan run --list|--show|--validate|--learn|--complete|--resume|--fail|--block
|
|
19
|
+
/sc plan run --list|--show|--validate|--learn|--complete|--resume|--fail|--block [--note] [typed evidence flags]
|
|
20
|
+
/sc plan run --authorize-mutation RUN-NNN --path <relative-path> [--path ...]
|
|
21
|
+
/sc plan run --record-mutation RUN-NNN --permit MUT-id [--note] [--actor]
|
|
22
|
+
/sc plan run --satisfy-guardrail RUN-NNN --guardrail GR-NNN [typed evidence flags]
|
|
20
23
|
/sc plan challenge <question>
|
|
21
24
|
```
|
|
22
25
|
|
|
23
|
-
CLI-native: intake/approval, Task/Run list/show, Task retry, and Run transitions. A retry requires a failed, blocked, or partial Task and creates a new Run.
|
|
26
|
+
CLI-native: intake/approval, Task/Run list/show, Task retry, Mutation Gateway actions, Guardrail satisfaction, and Run transitions. A retry requires a failed, blocked, or partial Task and creates a new Run. Mutation permits expire after 15 minutes, authorize explicit relative paths only, and must be recorded immediately after the edit.
|
|
24
27
|
|
|
25
28
|
## Knowledge
|
|
26
29
|
|
|
@@ -46,11 +49,12 @@ Creation options include `--title`, `--content`, repeated `--evidence`, repeated
|
|
|
46
49
|
/sc rules reviewer --add|--list|--show|--run
|
|
47
50
|
/sc review code --run
|
|
48
51
|
/sc review artifact --run
|
|
52
|
+
/sc review artifact --record --task TASK-NNN [--run RUN-NNN] [--title "..."] [--evidence "..."]
|
|
49
53
|
/sc review migration --run
|
|
50
54
|
/sc review release --run
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
`review artifact --run` is
|
|
57
|
+
`review artifact --run` is read-only and returns a machine-readable 21-invariant project audit. `--record` reruns that audit and persists its exact pass/fail result as a canonical `REV-NNN`; supplied evidence is additive and cannot turn a failed audit into a pass. Other review routes require repository reasoning and remain read-only unless fixes receive separate approval.
|
|
54
58
|
|
|
55
59
|
## Config and lifecycle
|
|
56
60
|
|
|
@@ -59,11 +63,13 @@ Creation options include `--title`, `--content`, repeated `--evidence`, repeated
|
|
|
59
63
|
/sc config init --local|--shared|--lean|--no-agent-hint|--force
|
|
60
64
|
/sc config update [all|codex|opencode|claude] [--migrate]
|
|
61
65
|
/sc config migrate --to 2 --dry-run|--apply|--rollback
|
|
62
|
-
/sc config doctor [all|codex|opencode|claude] [--strict]
|
|
66
|
+
/sc config doctor [all|codex|opencode|claude] [--strict] [--recover]
|
|
63
67
|
/sc config uninstall --force
|
|
64
68
|
/sc config help <topic>
|
|
65
69
|
```
|
|
66
70
|
|
|
67
71
|
Top-level CLI aliases (`init`, `update`, `migrate`, `doctor`, `uninstall`, `status`) remain available for shell automation. Ordinary update runs only a read-only migration preflight; `--migrate` is explicit application consent.
|
|
68
72
|
|
|
73
|
+
Run transitions accept typed evidence through `--command`, `--test`, `--file`, `--review`, `--decision`, `--insight`, `--risk`, or generic `--evidence kind:value`. `doctor --recover` is an explicit write that resolves only safe pending kernel transactions; doctor without it remains read-only.
|
|
74
|
+
|
|
69
75
|
Run `npx scrumrun@latest commands` for grammar rendered directly from the current manifest.
|
package/docs/ENTITY-MODEL.md
CHANGED
|
@@ -35,4 +35,4 @@ Feature and Sprint provide context/grouping and do not own execution history. Re
|
|
|
35
35
|
|
|
36
36
|
## Generated projections
|
|
37
37
|
|
|
38
|
-
`state.md` summarizes active work/memory. `map.md` summarizes bounded nodes/edges. SQLite stores the complete derived graph/search index. All are fingerprinted or explicitly stale and can be rebuilt from Markdown plus source code.
|
|
38
|
+
`state.md` summarizes active work/memory. `map.md` summarizes bounded nodes/edges. SQLite stores the complete derived graph/search index. All are fingerprinted or explicitly stale and can be rebuilt from Markdown plus source code. Freshness uses a metadata watch fast path and content-hash fallback; the watch is disposable evidence, never truth.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ScrumRun 2.1.1 Local Release Scorecard
|
|
2
|
+
|
|
3
|
+
- Date: 2026-07-22
|
|
4
|
+
- Package: `2.1.1`
|
|
5
|
+
- Method contract: `2.0.0`
|
|
6
|
+
- Scope: local implementation and package readiness; external registry/tag/release verification remains owner-gated.
|
|
7
|
+
|
|
8
|
+
## Scoring rule
|
|
9
|
+
|
|
10
|
+
A score of 9.5 or higher requires a single documented contract, machine enforcement on the critical path, adversarial/failure tests, deterministic recovery where mutation is involved, bounded performance, and an explicit residual-risk statement. Documentation alone cannot earn 9.5.
|
|
11
|
+
|
|
12
|
+
| Area | Score | Executable evidence |
|
|
13
|
+
|---|---:|---|
|
|
14
|
+
| Conceptual model | 9.7 | Frozen Feature → Task → Sprint → Run → Memory schema; Task/Sprint and retry invariants; ADR-015 and ADR-018. |
|
|
15
|
+
| Documentation architecture | 9.7 | SPEC is normative, CORE is operational, ADRs explain trade-offs, generated SCHEMA is drift-checked, README/skill/templates are conformance-tested. |
|
|
16
|
+
| Artifact kernel | 9.7 | One executable schema, safe-path checks, lossless frontmatter transitions, conflict refusal, fsync atomic writes, and durable multi-file transactions with failure injection. |
|
|
17
|
+
| Run history and audit | 9.8 | Stable ordered event ids, RFC3339 timestamps, typed evidence, state reconstruction, completion gates, retry preservation, explicit early-v2 migration, and byte-exact rollback. |
|
|
18
|
+
| Real conformance | 9.7 | Twenty normative invariants point to executable tests; clean-project audit, malformed artifacts, secrets, symlinks, migration interruption, transaction interruption, and cache corruption are exercised. |
|
|
19
|
+
| Local state and retrieval | 9.7 | Intake/state share one canonical fingerprint; state/map/SQLite expose staleness; metadata fast path falls back to full content hashes; cache schema mismatch rebuilds once; FTS5 capability is detected and a deterministic parameterized fallback preserves Node 22.13 retrieval. |
|
|
20
|
+
| Overall operation | 9.7 | Read-only intake, explicit approval, atomic Task/Run creation, Policy Engine ids, migration preflight/apply/rollback, package E2E, installed-asset doctor, exact Node 22.13 regression coverage, Node 22/24/26 CI definition, and release budgets. |
|
|
21
|
+
|
|
22
|
+
Minimum local score: **9.7/10**.
|
|
23
|
+
|
|
24
|
+
## Release evidence
|
|
25
|
+
|
|
26
|
+
- Full suite: `npm test`.
|
|
27
|
+
- Performance suite: `npm run benchmark`.
|
|
28
|
+
- Contract drift: `scripts/generate-contract-docs.js --check` runs before tests.
|
|
29
|
+
- Project conformance: `/sc review artifact --run`, twenty-one invariants, zero findings at the release checkpoint.
|
|
30
|
+
- Installed integration: `doctor codex --strict`, exact prompt/skill hashes and zero project findings.
|
|
31
|
+
- Package boundary: `npm pack --dry-run --json`, explicit file inventory, no repository-local `.scrumrun/`, tests, vault, backup, migration state, or cache.
|
|
32
|
+
- Tarball E2E: install, v2 memory, ongoing v1 migration, rollback, doctor, and uninstall run from the packed package in the test suite.
|
|
33
|
+
|
|
34
|
+
The exact final tarball checksum belongs in the owner-gated release Review/Run after all included files are frozen; embedding a tarball's own checksum inside an included document would change that checksum.
|
|
35
|
+
|
|
36
|
+
## Residual risks and gates
|
|
37
|
+
|
|
38
|
+
- Registry smoke, npm dist-tags, `v2.1.1` tag, push, and GitHub release are not proven by local tests and require explicit owner authorization.
|
|
39
|
+
- The built-in code-intelligence adapter currently covers JavaScript/TypeScript; other languages require replaceable adapters.
|
|
40
|
+
- Remote Node 22/24/26 CI must pass on the release commit; the exact Node 22.13 container passes locally but does not replace the external gate.
|
|
41
|
+
- Semantic retrieval is intentionally lexical/structural rather than a probabilistic embedding system; confirmed Markdown evidence remains the authority.
|
|
42
|
+
|
|
43
|
+
These are bounded release or extension risks, not hidden correctness claims. A failed external gate stops promotion and results in a new immutable SemVer; it never rewrites an existing npm version.
|
package/docs/RELEASE.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# ScrumRun
|
|
1
|
+
# ScrumRun Package Release Procedure
|
|
2
2
|
|
|
3
3
|
Publication is owner-gated. Tests, packaging, local commits, and release metadata preparation do not authorize npm publication, git push, tags, GitHub releases, or dist-tag changes.
|
|
4
4
|
|
|
5
|
+
The npm package/CLI follows SemVer independently from the ScrumRun method contract. For this release, package `2.1.1` implements method `2.0.0`. Published packages `2.0.0` and `2.1.0` are immutable and must never be overwritten or reused.
|
|
6
|
+
|
|
5
7
|
## Local release gate
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -12,11 +14,13 @@ npm pack --dry-run
|
|
|
12
14
|
npx scrumrun@latest sc review artifact --run # in a clean v2 fixture/current package equivalent
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
Confirm package contents exclude repository-local `.scrumrun/` state, caches, migration records, backups, vaults, tests, and secrets. The `.scrumrun/` directory inside project templates is expected. Confirm
|
|
17
|
+
Confirm package contents exclude repository-local `.scrumrun/` state, caches, migration records, backups, vaults, tests, and secrets. The `.scrumrun/` directory inside project templates is expected. Confirm package metadata, README, changelog, tarball filename, checksum, and Git tag agree on `2.1.1`; SPEC, CORE, artifact frontmatter, migration, and installed skill continue to declare method `2.0.0`.
|
|
18
|
+
|
|
19
|
+
Record the local evidence and residual risks against [`RELEASE-SCORECARD.md`](./RELEASE-SCORECARD.md). Scores describe local readiness only and never replace registry smoke or owner approval.
|
|
16
20
|
|
|
17
|
-
##
|
|
21
|
+
## Optional registry candidate (separate owner approval required)
|
|
18
22
|
|
|
19
|
-
1. Set `2.
|
|
23
|
+
1. Set a new unpublished prerelease such as `2.1.1-rc.1` and create a reviewed commit.
|
|
20
24
|
2. `npm pack`; record tarball SHA-256/integrity.
|
|
21
25
|
3. Publish to `next`, never `latest`:
|
|
22
26
|
|
|
@@ -28,17 +32,20 @@ Confirm package contents exclude repository-local `.scrumrun/` state, caches, mi
|
|
|
28
32
|
5. Run init, intake/approval, memory confirmation/query, v1 update preflight/apply/rollback, doctor, and uninstall.
|
|
29
33
|
6. Fix findings in a new RC; never replace an already-published version.
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
An RC is immutable. Fixes create `rc.2`, `rc.3`, and so on. Skipping an RC does not skip any local, tarball, owner, registry-smoke, or final-promotion gate.
|
|
36
|
+
|
|
37
|
+
## Final release (new owner approval required)
|
|
32
38
|
|
|
33
|
-
1. Set `2.
|
|
34
|
-
2.
|
|
35
|
-
3.
|
|
36
|
-
4.
|
|
37
|
-
5.
|
|
39
|
+
1. Set `2.1.1`, rerun every local gate, inspect the exact tarball, and create the final local metadata commit.
|
|
40
|
+
2. Stop and request explicit owner authorization for each external boundary.
|
|
41
|
+
3. Publish npm `2.1.1` to `next` using the reviewed tarball/source commit (`npm publish --tag next`); never publish from a changed worktree and do not move `latest` yet.
|
|
42
|
+
4. Install the registry artifact in a clean fixture and run init, intake/approval, Run lifecycle, memory query, v1 migration apply/rollback, doctor, and uninstall.
|
|
43
|
+
5. Only after registry smoke passes, create/push tag `v2.1.1`, promote/verify `latest` with an explicit dist-tag command, and create the GitHub release from `CHANGELOG.md` with the migration guide and checksum.
|
|
44
|
+
6. Verify npm metadata, dist-tags, Git tag/commit, GitHub release, README, and checksums all agree.
|
|
38
45
|
|
|
39
46
|
## Recovery
|
|
40
47
|
|
|
41
48
|
- RC defect: publish another RC; keep `latest` unchanged.
|
|
42
|
-
- Final package defect before `latest`: do not promote; publish
|
|
43
|
-
- Defect after `latest`: assess deprecation vs immediate
|
|
49
|
+
- Final package defect before `latest`: do not promote; publish a new patch after correction because `2.1.1` cannot be replaced.
|
|
50
|
+
- Defect after `latest`: assess deprecation vs immediate patch; never overwrite/unpublish without explicit owner decision and current npm-policy review.
|
|
44
51
|
- Migration issue: stop promotion, preserve registry artifact/checksum, use the documented rollback fixture, and publish a corrected version.
|
package/docs/SCHEMA.md
CHANGED
|
@@ -46,9 +46,20 @@ Task is the atomic unit. A Task may have zero or one Sprint. A Task may have man
|
|
|
46
46
|
| Field | Kinds | Presence | Type | Meaning |
|
|
47
47
|
|---|---|---|---|---|
|
|
48
48
|
| `attempt` | run | required | positive integer | monotonic execution-attempt number within one Task |
|
|
49
|
+
| `ledger` | run | optional | integer 1 | canonical Run event-ledger schema; required for newly authored Runs |
|
|
50
|
+
| `guardrails` | run | optional | integer 1 | append-only Guardrail obligation schema |
|
|
51
|
+
| `workspace` | run | optional | integer 1 | workspace mutation-gateway schema |
|
|
49
52
|
|
|
50
53
|
Native creation uses the declared initial statuses. Migration may restore a historical non-initial status only with provenance and validation.
|
|
51
54
|
|
|
55
|
+
## Run event ledger
|
|
56
|
+
|
|
57
|
+
Newly authored Runs use `ledger: 1`. Their `## Events` section contains append-only JSON event blocks with stable ids in the form `RUN-NNN-EVT-NNN`.
|
|
58
|
+
|
|
59
|
+
Every event requires `schema`, `id`, contiguous `sequence`, RFC3339 `occurred_at`, `timestamp_precision`, `actor`, `from`, `to`, `reason`, and structured `evidence`. Event types are `transition`, `snapshot`, `guardrail`, `mutation`. Evidence kinds are `approval`, `command`, `test`, `file`, `review`, `decision`, `insight`, `risk`, `note`, `migration`, `legacy`, `guardrail`, `mutation`.
|
|
60
|
+
|
|
61
|
+
A native ledger begins with `created → executing`; an evidenced migration `snapshot` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated. Run owns the event history; Task stores its intended scope and synchronized current status without copying Run events.
|
|
62
|
+
|
|
52
63
|
## Truth questions
|
|
53
64
|
|
|
54
65
|
- **feature:** Why does this initiative exist?
|
package/docs/SEMANTIC-MEMORY.md
CHANGED
|
@@ -65,4 +65,4 @@ npx scrumrun@latest sc knowledge study calculateFinalPrice
|
|
|
65
65
|
npx scrumrun@latest sc knowledge context --clear
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
SQLite is ignored and disposable. Queries default to 10 records/40 relations and hard-cap at 100/100. Match type, truth state, warnings, relation counts, and evidence are returned so recommendations remain explainable.
|
|
68
|
+
SQLite is ignored and disposable. The derived index records its search backend: FTS5/BM25 is selected when the current Node.js SQLite build supports it; otherwise ScrumRun uses deterministic parameterized token matching over the same artifact, code, and relation tables. Queries default to 10 records/40 relations and hard-cap at 100/100. Match type, truth state, warnings, relation counts, and evidence are returned so recommendations remain explainable.
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -25,8 +25,20 @@ Rollback detected post-migration work that it would erase. Copy/export wanted ch
|
|
|
25
25
|
npx scrumrun@latest sc knowledge map --build
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Fresh projections include a source fingerprint and a watch fingerprint. A matching watch avoids a full read. If file metadata changed, ScrumRun hashes canonical/source content before deciding whether the projection is actually stale. `check: "schema"` means the disposable SQLite format changed and one rebuild is required; `check: "hash"` means the safe fallback was used.
|
|
29
|
+
|
|
28
30
|
Stale generated views are warnings, not canonical corruption.
|
|
29
31
|
|
|
32
|
+
## Doctor reports `TRANSACTION_PENDING`
|
|
33
|
+
|
|
34
|
+
An approved multi-file mutation was interrupted after its durable journal was prepared. Ordinary doctor/audit is read-only and leaves the evidence untouched. Either retry the same approved operation, which recovers before writing, or explicitly run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx scrumrun@latest doctor codex --recover --strict
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Prepared transactions restore their original bytes; committed transactions verify the applied bytes and finalize. Recovery refuses if a target changed to content matching neither journal side, because that would overwrite later owner work. Receipts contain hashes and outcomes, not file contents.
|
|
41
|
+
|
|
30
42
|
## SQLite is missing/corrupt
|
|
31
43
|
|
|
32
44
|
Delete or clear only the disposable cache:
|
|
@@ -59,7 +71,7 @@ Use `doctor --compat` only while validating one-cycle v1 adapters.
|
|
|
59
71
|
|
|
60
72
|
## Node.js is unsupported
|
|
61
73
|
|
|
62
|
-
ScrumRun 2.0 requires Node.js `>=22.13.0` because semantic indexing uses native `node:sqlite`. Upgrade Node, then rerun doctor.
|
|
74
|
+
ScrumRun 2.0 requires Node.js `>=22.13.0` because semantic indexing uses native `node:sqlite`. FTS5/BM25 is used when the runtime provides it; Node builds without FTS5 use the deterministic lexical fallback. Upgrade Node, then rerun doctor.
|
|
63
75
|
|
|
64
76
|
## Safe uninstall
|
|
65
77
|
|
package/lib/commands/manifest.js
CHANGED
|
@@ -9,7 +9,19 @@ const nouns = Object.freeze({
|
|
|
9
9
|
task: ["--add [--type fix] [--status backlog]", "--list", "--show", "--run", "--audit", "--cancel", "--retry"],
|
|
10
10
|
sprint: ["--add", "--list", "--show", "--start", "--complete", "--block"],
|
|
11
11
|
feature: ["--add", "--list", "--show", "--activate", "--complete"],
|
|
12
|
-
run: [
|
|
12
|
+
run: [
|
|
13
|
+
"--list",
|
|
14
|
+
"--show",
|
|
15
|
+
"--authorize-mutation <RUN-NNN> --path <relative-path>",
|
|
16
|
+
"--record-mutation <RUN-NNN> --permit <MUT-id> [--note] [--actor]",
|
|
17
|
+
"--satisfy-guardrail <RUN-NNN> --guardrail <GR-NNN> [--note] [--evidence] [--review] [--migration] [--actor]",
|
|
18
|
+
"--validate [--note] [--evidence] [--command] [--test] [--file] [--review] [--actor] [--at]",
|
|
19
|
+
"--learn [--note] [--evidence] [--decision] [--insight] [--file] [--actor] [--at]",
|
|
20
|
+
"--complete [--note] [--evidence] [--review] [--test] [--file] [--actor] [--at]",
|
|
21
|
+
"--resume [--note] [--evidence] [--risk] [--actor] [--at]",
|
|
22
|
+
"--fail [--note] [--evidence] [--risk] [--test] [--actor] [--at]",
|
|
23
|
+
"--block [--note] [--evidence] [--risk] [--actor] [--at]"
|
|
24
|
+
],
|
|
13
25
|
intake: ["<request>", "--request", "--approve"],
|
|
14
26
|
challenge: ["<question>"]
|
|
15
27
|
}
|
|
@@ -38,7 +50,7 @@ const nouns = Object.freeze({
|
|
|
38
50
|
description: "run scoped evidence-based quality gates",
|
|
39
51
|
subjects: {
|
|
40
52
|
code: ["--run"],
|
|
41
|
-
artifact: ["--run"],
|
|
53
|
+
artifact: ["--run", "--record --task <TASK-NNN> [--run <RUN-NNN>] [--title] [--evidence]"],
|
|
42
54
|
migration: ["--run"],
|
|
43
55
|
release: ["--run"]
|
|
44
56
|
}
|
|
@@ -50,7 +62,7 @@ const nouns = Object.freeze({
|
|
|
50
62
|
init: ["--local", "--shared", "--lean", "--no-agent-hint", "--force"],
|
|
51
63
|
update: ["all [--migrate]", "codex [--migrate]", "opencode [--migrate]", "claude [--migrate]"],
|
|
52
64
|
migrate: ["--to 2 --dry-run", "--to 2 --apply", "--to 2 --rollback"],
|
|
53
|
-
doctor: ["all [--strict]", "codex [--strict]", "opencode [--strict]", "claude [--strict]"],
|
|
65
|
+
doctor: ["all [--strict] [--recover]", "codex [--strict] [--recover]", "opencode [--strict] [--recover]", "claude [--strict] [--recover]"],
|
|
54
66
|
uninstall: ["--force"],
|
|
55
67
|
help: ["<topic>"]
|
|
56
68
|
}
|
package/lib/commands/render.js
CHANGED
|
@@ -31,7 +31,11 @@ ${grammarLines().join("\n")}
|
|
|
31
31
|
- Intake, contextualization, policy, risk, classification, and planning are read-only until explicit approval.
|
|
32
32
|
- Approved work creates/updates a Task and creates a Run; a Sprint only groups Tasks when a real timebox/batch exists.
|
|
33
33
|
- Run execution follows \`executing → validating → learning → completed|failed|blocked\`.
|
|
34
|
+
- Run owns one structured, evidenced event ledger; Task synchronizes current status without copying Run history.
|
|
35
|
+
- Linked Task/Run writes use a durable recovery journal; audit reports pending recovery and never repairs without explicit authorization.
|
|
34
36
|
- \`guardrails.md\` is canonical project policy; \`golden-rules.md\` is v1 compatibility only.
|
|
37
|
+
- Evaluate active Guardrails as \`passed\`, \`blocked\`, or \`deferred\`; cite exact \`GR-NNN\` ids and keep deferred execution gates visible.
|
|
38
|
+
- Persist deferred checks as Run obligations. Every material source edit requires a short-lived path-scoped Mutation Gateway permit and immediate hash recording; policy/workspace drift or unresolved obligations block completion.
|
|
35
39
|
- Knowledge/Decision/Insight records require evidence; AI-proposed Insights remain \`candidate\` until confirmed.
|
|
36
40
|
- Never print vault values or write before approval.
|
|
37
41
|
- Unknown nouns, subjects, actions, ids, or ambiguous approval must produce a deterministic explanation, never a guessed mutation.
|