scrumrun 3.0.0 → 3.0.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.
@@ -65,8 +65,8 @@ SR-E-102 Run transition rejected: missing validation or learning evidence.
65
65
 
66
66
  | Code | Summary | Remediation |
67
67
  |---|---|---|
68
- | `SR-E-100` | Run not found. | Check the RUN-NNN id with `sc plan run --list`. |
69
- | `SR-E-101` | Run ledger contains an invalid event. | Inspect the reported event id; use `sc plan run --render` for a human view or restore from git history. |
68
+ | `SR-E-100` | Run not found. | Check the RUN-NNN id with `scrumrun plan run --list`. |
69
+ | `SR-E-101` | Run ledger contains an invalid event. | Inspect the reported event id; use `scrumrun plan run --render` for a human view or restore from git history. |
70
70
  | `SR-E-102` | Run transition rejected: missing validation or learning evidence. | Complete `--validate` and `--learn` with typed evidence before `--complete`. |
71
71
  | `SR-E-103` | Retry rejected because the previous Run is still active. | Complete, fail, or block the current Run before creating a retry. |
72
72
 
@@ -75,14 +75,14 @@ SR-E-102 Run transition rejected: missing validation or learning evidence.
75
75
  | Code | Summary | Remediation |
76
76
  |---|---|---|
77
77
  | `SR-E-150` | Guardrail check blocked the operation. | Read the reported GR-NNN, satisfy or retire it explicitly; guardrails never bypass silently. |
78
- | `SR-E-151` | Guardrail obligation is still pending. | Resolve each pending guardrail via `sc plan run --satisfy-guardrail` before completing the Run. |
78
+ | `SR-E-151` | Guardrail obligation is still pending. | Resolve each pending guardrail via `scrumrun plan run --satisfy-guardrail` before completing the Run. |
79
79
  | `SR-E-152` | Guardrail declaration is malformed. | Every active guardrail requires Status, Enforcement, Scope, and Rule fields; check `.scrumrun/guardrails.md`. |
80
80
 
81
81
  ### Edit permits (Mutation Gateway)
82
82
 
83
83
  | Code | Summary | Remediation |
84
84
  |---|---|---|
85
- | `SR-E-200` | No edit permit for this path. | Request one with `sc plan run --authorize-mutation RUN-NNN --path <path>` before editing canonical or source files. |
85
+ | `SR-E-200` | No edit permit for this path. | Request one with `scrumrun plan run --authorize-mutation RUN-NNN --path <path>` before editing canonical or source files. |
86
86
  | `SR-E-201` | Edit permit expired. | Permits last 15 minutes. Authorize a new one and record the change immediately. |
87
87
  | `SR-E-202` | Edit permit path scope mismatch. | The permit does not cover the modified path. Request a new permit that lists it. |
88
88
  | `SR-E-203` | File hash changed unexpectedly since the permit was issued. | Someone else modified the file. Re-plan the change and request a fresh permit. |
@@ -91,28 +91,28 @@ SR-E-102 Run transition rejected: missing validation or learning evidence.
91
91
 
92
92
  | Code | Summary | Remediation |
93
93
  |---|---|---|
94
- | `SR-E-250` | Pending kernel transaction cannot be recovered automatically. | Run `sc config doctor --recover --dry-run` to preview; if it shows "would overwrite owner changes", reconcile the file manually before applying. |
94
+ | `SR-E-250` | Pending kernel transaction cannot be recovered automatically. | Run `scrumrun config doctor --recover --dry-run` to preview; if it shows "would overwrite owner changes", reconcile the file manually before applying. |
95
95
  | `SR-E-251` | Journal fails integrity check. | Inspect `.scrumrun/.transactions/pending`. Do not delete; contact support or restore from backup. |
96
96
 
97
97
  ### Memory
98
98
 
99
99
  | Code | Summary | Remediation |
100
100
  |---|---|---|
101
- | `SR-E-300` | Memory candidate rejected: missing resolvable evidence. | Attach at least one `--evidence` path or `sc knowledge <subject> --propose` before `--confirm`. |
101
+ | `SR-E-300` | Memory candidate rejected: missing resolvable evidence. | Attach at least one `--evidence` path or `scrumrun knowledge <subject> --propose` before `--confirm`. |
102
102
  | `SR-E-301` | Attempt to write into vault via canonical channel. | `vault.local.md` is local-only and never indexed. Edit the file directly. |
103
103
 
104
104
  ### Semantic index and code intelligence
105
105
 
106
106
  | Code | Summary | Remediation |
107
107
  |---|---|---|
108
- | `SR-E-350` | Semantic index is stale. | Rebuild with `sc knowledge map --build`. Cache is disposable; canonical memory is unaffected. |
109
- | `SR-E-351` | Search backend advertised in the cache does not match this runtime. | Delete `.scrumrun/.cache/semantic-index.sqlite` and re-run any `sc knowledge` query to rebuild against the current runtime. |
108
+ | `SR-E-350` | Semantic index is stale. | Rebuild with `scrumrun knowledge map --build`. Cache is disposable; canonical memory is unaffected. |
109
+ | `SR-E-351` | Search backend advertised in the cache does not match this runtime. | Delete `.scrumrun/.cache/semantic-index.sqlite` and re-run any `scrumrun knowledge` query to rebuild against the current runtime. |
110
110
 
111
111
  ### Migration
112
112
 
113
113
  | Code | Summary | Remediation |
114
114
  |---|---|---|
115
- | `SR-E-400` | Migration preflight failed. | Run `npx scrumrun@latest update` (dry) to see blockers, resolve them, then apply with `--migrate`. |
115
+ | `SR-E-400` | Migration preflight failed. | Run `scrumrun update` (dry) to see blockers, resolve them, then apply with `--migrate`. |
116
116
  | `SR-E-401` | Migration rollback requested but no backup was found. | Rollback needs the ignored byte-exact backup created during `--migrate`. Restore from version control if the backup is gone. |
117
117
 
118
118
  ### Conformance and doctor
@@ -120,13 +120,13 @@ SR-E-102 Run transition rejected: missing validation or learning evidence.
120
120
  | Code | Summary | Remediation |
121
121
  |---|---|---|
122
122
  | `SR-E-450` | Conformance check failed. | The reported invariant identifies the exact violation; the message includes the file and expected shape. |
123
- | `SR-E-451` | Installed client asset is stale. | Re-run `npx scrumrun@latest update` for the specific client. `doctor --strict` shows which files diverge. |
123
+ | `SR-E-451` | Installed client asset is stale. | Re-run `scrumrun update` for the specific client. `doctor --strict` shows which files diverge. |
124
124
 
125
125
  ### Configuration and installation
126
126
 
127
127
  | Code | Summary | Remediation |
128
128
  |---|---|---|
129
- | `SR-E-500` | ScrumRun project not initialized. | Run `npx scrumrun@latest init` in the repository root. |
129
+ | `SR-E-500` | ScrumRun project not initialized. | Run `scrumrun init` in the repository root. |
130
130
  | `SR-E-501` | Unsupported Node.js runtime. | ScrumRun requires Node.js >=22.13.0 for native SQLite. Upgrade Node and retry. |
131
131
 
132
132
  ## Adding a new code
@@ -22,8 +22,8 @@ can follow it.
22
22
  You need Node.js 22.13 or newer.
23
23
 
24
24
  ```bash
25
- npx scrumrun@latest install # installs the agent integration for your client
26
- npx scrumrun@latest init # creates the .scrumrun/ tree in the current repo
25
+ scrumrun install # installs the agent integration for your client
26
+ scrumrun init # creates the .scrumrun/ tree in the current repo
27
27
  ```
28
28
 
29
29
  By default `.scrumrun/` is added to `.git/info/exclude` — the methodology
@@ -47,7 +47,7 @@ Classification: Task (fix)
47
47
  Risk: high — financial path
48
48
  Why: Payment behavior changed after completed work; corrective Task
49
49
  linked to the original Run history.
50
- Next: /sc plan intake "double charge on refresh"
50
+ Next: scrumrun plan intake "double charge on refresh"
51
51
  Awaiting owner approval.
52
52
  ```
53
53
 
@@ -55,7 +55,7 @@ Nothing has been written yet. The classification, risk, and plan are
55
55
  proposals. Approving is one command:
56
56
 
57
57
  ```bash
58
- npx scrumrun@latest sc plan intake --approve <token>
58
+ scrumrun plan intake --approve <token>
59
59
  ```
60
60
 
61
61
  In an interactive terminal, the output is rendered as a boxed intake
@@ -87,7 +87,7 @@ You can render a Run's ledger as a human timeline instead of reading the
87
87
  raw JSON:
88
88
 
89
89
  ```bash
90
- npx scrumrun@latest sc plan run --render RUN-001
90
+ scrumrun plan run --render RUN-001
91
91
  ```
92
92
 
93
93
  For aggregate signal across every Run in the project — status mix, p50
@@ -95,9 +95,9 @@ and p95 time in `VALIDATING`, retries per Task, guardrail check counts —
95
95
  use `--stats`:
96
96
 
97
97
  ```bash
98
- npx scrumrun@latest sc plan run --stats
99
- npx scrumrun@latest sc plan run --stats --task TASK-001
100
- npx scrumrun@latest sc plan run --stats --json
98
+ scrumrun plan run --stats
99
+ scrumrun plan run --stats --task TASK-001
100
+ scrumrun plan run --stats --json
101
101
  ```
102
102
 
103
103
  ## Reading the memory
@@ -113,8 +113,8 @@ Nothing becomes canonical without confirmation. AI extraction creates
113
113
  candidates; you promote them with evidence.
114
114
 
115
115
  ```bash
116
- npx scrumrun@latest sc knowledge insight --propose "..." --evidence src/foo.ts
117
- npx scrumrun@latest sc knowledge insight --confirm INS-001
116
+ scrumrun knowledge insight --propose "..." --evidence src/foo.ts
117
+ scrumrun knowledge insight --confirm INS-001
118
118
  ```
119
119
 
120
120
  Ask the agent things like *"why is calculateFinalPrice in checkout?"* or
@@ -134,10 +134,10 @@ the memory index, not by guessing.
134
134
 
135
135
  ## Common questions
136
136
 
137
- **Do I have to type `/sc` commands?** No. Natural language is the primary
138
- entry point. The `/sc` grammar exists for scripting and reproducibility.
137
+ **Do I have to type commands?** No. Natural language is the primary entry
138
+ point. For scripting and reproducibility, use `scrumrun <noun> <subject> <action>`; `/sc` is only an AI-client shortcut.
139
139
 
140
- **What if I already use v1?** Run `npx scrumrun@latest update` for a
140
+ **What if I already use v1?** Run `scrumrun update` for a
141
141
  read-only migration preflight, then `update --migrate` when you are
142
142
  satisfied with the plan.
143
143
 
@@ -158,5 +158,5 @@ for the exact remediation. If a canonical transaction was interrupted,
158
158
  preview the repair before running it:
159
159
 
160
160
  ```bash
161
- npx scrumrun@latest sc config doctor --recover --dry-run
161
+ scrumrun config doctor --recover --dry-run
162
162
  ```
@@ -26,7 +26,7 @@ Minimum local score: **9.7/10**.
26
26
  - Full suite: `npm test`.
27
27
  - Performance suite: `npm run benchmark`.
28
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.
29
+ - Project conformance: `scrumrun review artifact --run`, twenty-one invariants, zero findings at the release checkpoint.
30
30
  - Installed integration: `doctor codex --strict`, exact prompt/skill hashes and zero project findings.
31
31
  - Package boundary: `npm pack --dry-run --json`, explicit file inventory, no repository-local `.scrumrun/`, tests, vault, backup, migration state, or cache.
32
32
  - Tarball E2E: install, v2 memory, ongoing v1 migration, rollback, doctor, and uninstall run from the packed package in the test suite.
package/docs/RELEASE.md CHANGED
@@ -11,7 +11,7 @@ npm test
11
11
  npm run benchmark
12
12
  git diff --check
13
13
  npm pack --dry-run
14
- npx scrumrun@latest sc review artifact --run # in a clean v2 fixture/current package equivalent
14
+ scrumrun review artifact --run # in a clean v2 fixture/current package equivalent
15
15
  ```
16
16
 
17
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`.
@@ -60,9 +60,9 @@ Entering `learning` extracts `INS-NNN` candidates. Malformed extraction emits a
60
60
  ## Query and maintenance
61
61
 
62
62
  ```bash
63
- npx scrumrun@latest sc knowledge map --build
64
- npx scrumrun@latest sc knowledge study calculateFinalPrice
65
- npx scrumrun@latest sc knowledge context --clear
63
+ scrumrun knowledge map --build
64
+ scrumrun knowledge study calculateFinalPrice
65
+ scrumrun knowledge context --clear
66
66
  ```
67
67
 
68
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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## “Project must be explicitly migrated to v2”
4
4
 
5
- Run `npx scrumrun@latest update` for a read-only preflight, then review and apply with `update --migrate`. See [`MIGRATION-1-to-2.md`](../MIGRATION-1-to-2.md).
5
+ Run `scrumrun update` for a read-only preflight, then review and apply with `update --migrate`. See [`MIGRATION-1-to-2.md`](../MIGRATION-1-to-2.md).
6
6
 
7
7
  ## Migration is blocked
8
8
 
@@ -22,7 +22,7 @@ Rollback detected post-migration work that it would erase. Copy/export wanted ch
22
22
  `state.md` refreshes after runtime transitions. Rebuild the semantic map with:
23
23
 
24
24
  ```bash
25
- npx scrumrun@latest sc knowledge map --build
25
+ scrumrun knowledge map --build
26
26
  ```
27
27
 
28
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.
@@ -34,7 +34,7 @@ Stale generated views are warnings, not canonical corruption.
34
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
35
 
36
36
  ```bash
37
- npx scrumrun@latest doctor codex --recover --strict
37
+ scrumrun doctor codex --recover --strict
38
38
  ```
39
39
 
40
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.
@@ -44,8 +44,8 @@ Prepared transactions restore their original bytes; committed transactions verif
44
44
  Delete or clear only the disposable cache:
45
45
 
46
46
  ```bash
47
- npx scrumrun@latest sc knowledge context --clear
48
- npx scrumrun@latest sc knowledge map --build
47
+ scrumrun knowledge context --clear
48
+ scrumrun knowledge map --build
49
49
  ```
50
50
 
51
51
  Canonical Markdown is unchanged. Query automatically rebuilds a missing/stale index.
@@ -61,10 +61,10 @@ Inspect its invalidation warning and code/evidence relations. For a real code mo
61
61
  ## Doctor reports missing clients
62
62
 
63
63
  ```bash
64
- npx scrumrun@latest install codex
65
- npx scrumrun@latest install claude
66
- npx scrumrun@latest install opencode
67
- npx scrumrun@latest doctor all
64
+ scrumrun install codex
65
+ scrumrun install claude
66
+ scrumrun install opencode
67
+ scrumrun doctor all
68
68
  ```
69
69
 
70
70
  Use `doctor --compat` only while validating one-cycle v1 adapters.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- // `sc plan run --normalize-legacy [--dry-run]`
3
+ // `scrumrun plan run --normalize-legacy [--dry-run]`
4
4
  //
5
5
  // Reads every Run under `.scrumrun/runs/` and validates its ledger
6
6
  // against the current schema. When a Run fails validation (invalid
@@ -244,7 +244,7 @@ function renderIntake(plan) {
244
244
  }
245
245
  }
246
246
  } else if (plan.approvalToken) {
247
- const command = "scrumrun sc plan intake --approve";
247
+ const command = "scrumrun plan intake --approve";
248
248
  const tokenWidth = Math.max(16, width - 8);
249
249
  const materialization = plan.proposal && plan.proposal.materialization ? plan.proposal.materialization.summary : "Task + Run";
250
250
  lines.push(boxLine(width, ` ${paint(FG.gray, `APPROVE · copy the command below to create ${materialization}`)}`));
@@ -274,7 +274,7 @@ function renderIntakePlain(plan) {
274
274
  lines.push(`DEFERRED: ${result.guardrail} ${result.code}: ${result.message}`);
275
275
  }
276
276
  for (const warning of plan.context.warnings) lines.push(`WARNING: ${warning}`);
277
- if (plan.approvalToken) lines.push(`Approval: scrumrun sc plan intake --approve ${plan.approvalToken}`);
277
+ if (plan.approvalToken) lines.push(`Approval: scrumrun plan intake --approve ${plan.approvalToken}`);
278
278
  return lines.join("\n");
279
279
  }
280
280
 
@@ -19,7 +19,7 @@ Execute ScrumRun with this request: $ARGUMENTS
19
19
 
20
20
  Use the installed \`scrumrun\` skill as the authoritative workflow. This prompt is the only canonical slash-command entry point.
21
21
 
22
- If \`$ARGUMENTS\` is empty, show the five nouns below and the current project status; do not guess an action. Otherwise parse exactly \`/sc <noun> <subject> <action> [args]\`.
22
+ If \`$ARGUMENTS\` is empty, show the five nouns below and the current project status; do not guess an action. Otherwise parse exactly \`<noun> <subject> <action> [args]\` and use the installed CLI as \`scrumrun <noun> <subject> <action> [args]\`. \`/sc\` is a client shortcut retained for compatibility, never a reason to use \`npx\`.
23
23
 
24
24
  ## Grammar
25
25
 
@@ -35,7 +35,7 @@ ${grammarLines().join("\n")}
35
35
  - Linked Task/Run writes use a durable recovery journal; audit reports pending recovery and never repairs without explicit authorization.
36
36
  - \`guardrails.md\` is canonical project policy; \`golden-rules.md\` is v1 compatibility only.
37
37
  - Evaluate active Guardrails as \`passed\`, \`blocked\`, or \`deferred\`; cite exact \`GR-NNN\` ids and keep deferred execution gates visible.
38
- - The normal path is one session checkpoint: after approval, work directly in code and the linked Task Markdown; finish once with \`sc plan run --finalize RUN-NNN\`. It verifies the complete workspace delta, policy, secrets, protected paths, and all Guardrail evidence together. Do not invoke \`npx scrumrun@latest\` during execution.
38
+ - The normal path is one session checkpoint: after approval, work directly in code and the linked Task Markdown; finish once with \`scrumrun plan run --finalize RUN-NNN\`. It verifies the complete workspace delta, policy, secrets, protected paths, and all Guardrail evidence together. Do not invoke \`npx scrumrun@latest\` during execution.
39
39
  - Strict per-path Mutation Gateway permits remain available only when the owner explicitly requests strict execution.
40
40
  - Knowledge/Decision/Insight records require evidence; AI-proposed Insights remain \`candidate\` until confirmed.
41
41
  - Never print vault values or write before approval.
@@ -48,7 +48,7 @@ Method: ${METHOD_VERSION}. Command grammar source: \`lib/commands/manifest.js\`.
48
48
  function renderCompatibilityPrompt(alias) {
49
49
  const spec = aliases[alias];
50
50
  if (!spec) throw new Error(`Unknown compatibility alias: ${alias}`);
51
- const target = `/sc ${spec.target.join(" ")}`;
51
+ const target = `scrumrun ${spec.target.join(" ")}`;
52
52
  return `---
53
53
  description: Deprecated ScrumRun v1 adapter — executes ${target}
54
54
  argument-hint: [action] [args]
@@ -63,11 +63,14 @@ Emit one concise deprecation note: \`/${alias}\` becomes \`${target}\` in ScrumR
63
63
  }
64
64
 
65
65
  function renderCommandHelp() {
66
- const nounLines = Object.entries(nouns).map(([noun, spec]) => ` /sc ${noun.padEnd(10)} ${spec.description}`);
67
- const aliasLines = Object.entries(aliases).map(([alias, spec]) => ` /${alias.padEnd(14)} -> /sc ${spec.target.join(" ")}`);
66
+ const nounLines = Object.entries(nouns).map(([noun, spec]) => ` scrumrun ${noun.padEnd(10)} ${spec.description}`);
67
+ const aliasLines = Object.entries(aliases).map(([alias, spec]) => ` /${alias.padEnd(14)} -> scrumrun ${spec.target.join(" ")}`);
68
68
  return `ScrumRun ${METHOD_VERSION} command grammar:
69
69
 
70
70
  Canonical root:
71
+ scrumrun <noun> <subject> <action> [args]
72
+
73
+ Client compatibility shortcut:
71
74
  /sc <noun> <subject> <action> [args]
72
75
 
73
76
  Nouns:
@@ -14,13 +14,16 @@
14
14
  // 4. run.attempt missing → 1 (documented default).
15
15
  // 5. Sprint task projections: add Task.sprint references absent from the
16
16
  // Sprint's `## Tasks` list. Extra entries remain for human review.
17
+ // 6. Orphan active Tasks: explicitly reset a Task with no associated Run to
18
+ // backlog. This never invents history; a later `task --start` creates
19
+ // the first real Run.
17
20
  //
18
21
  // Categories intentionally NOT touched (need human judgment):
19
22
  // - Secret-like content (SECRET_CANONICAL)
20
23
  // - Missing Acceptance Criteria sections
21
24
  // - Guardrail scope/enforcement (authored semantics)
22
25
  // - Task/Run status disagreements (requires domain knowledge)
23
- // - Empty Run ledgers (use `sc plan run --normalize-legacy`)
26
+ // - Empty Run ledgers (use `scrumrun plan run --normalize-legacy`)
24
27
  //
25
28
  // Every mutated file is backed up byte-exact under
26
29
  // `.scrumrun/.migration-backup/repair/<relative-path>` before being rewritten.
@@ -430,6 +433,37 @@ function planOrphanRuns(scrumDir, taskIds) {
430
433
  return orphans;
431
434
  }
432
435
 
436
+ function planOrphanTasks(scrumDir) {
437
+ const runTasks = new Set();
438
+ for (const file of listDir(path.join(scrumDir, "runs"), RUN_FILE)) {
439
+ const split = splitFrontmatter(readIf(file));
440
+ const task = split && extractField(split.header, "task");
441
+ if (TASK_REF.test(task || "")) runTasks.add(task);
442
+ }
443
+ const active = new Set(["running", "validating", "learning", "partial"]);
444
+ const orphans = [];
445
+ for (const file of listDir(path.join(scrumDir, "tasks"), TASK_FILE)) {
446
+ const original = readIf(file);
447
+ const split = splitFrontmatter(original);
448
+ if (!split) continue;
449
+ const id = extractField(split.header, "id") || path.basename(file, ".md");
450
+ const status = extractField(split.header, "status");
451
+ if (!TASK_REF.test(id) || !active.has(status) || runTasks.has(id)) continue;
452
+ const header = replaceField(split.header, "status", "backlog");
453
+ const updated = extractField(header, "updated");
454
+ const nextHeader = updated !== undefined ? replaceField(header, "updated", new Date().toISOString().slice(0, 10)) : header;
455
+ orphans.push({
456
+ id,
457
+ status,
458
+ file,
459
+ originalText: original,
460
+ nextText: `---\n${nextHeader}\n---${split.sep}${split.body}`,
461
+ reason: `${id} is ${status} but no Run references it.`
462
+ });
463
+ }
464
+ return orphans;
465
+ }
466
+
433
467
  function planFile(file, kind, ctx) {
434
468
  const original = readIf(file);
435
469
  if (original === null) return null;
@@ -657,12 +691,13 @@ function planGuardrails(scrumDir) {
657
691
  return { file, kind: "guardrails", changes, nextText: text, originalText: original };
658
692
  }
659
693
 
660
- function analyze(scrumDir) {
694
+ function analyze(scrumDir, { recoverOrphanTasks = false } = {}) {
661
695
  const featureIds = scanFeatureIds(scrumDir);
662
696
  const taskIds = scanTaskIds(scrumDir);
663
697
  const ctx = { featureIds };
664
698
  const entries = planMissingFrontmatter(scrumDir);
665
699
  const orphanRuns = planOrphanRuns(scrumDir, taskIds);
700
+ const orphanTasks = planOrphanTasks(scrumDir);
666
701
 
667
702
  const guardrailPlan = planGuardrails(scrumDir);
668
703
  if (guardrailPlan) entries.push(guardrailPlan);
@@ -704,7 +739,7 @@ function analyze(scrumDir) {
704
739
  totals.byField[change.field] = (totals.byField[change.field] || 0) + 1;
705
740
  }
706
741
  }
707
- return { entries, totals, orphanRuns };
742
+ return { entries, totals, orphanRuns, orphanTasks, recoverOrphanTasks };
708
743
  }
709
744
 
710
745
  function writeWithBackup(scrumDir, backupRoot, file, originalText, nextText) {
@@ -760,7 +795,18 @@ function apply(scrumDir, plan) {
760
795
  applied.push({ ...rec, changes: entry.changes });
761
796
  }
762
797
 
763
- // Pass 5: rebuild the disposable state.md briefing so it reflects the
798
+ // Pass 5: only after every ordinary deterministic repair has settled, reset
799
+ // explicitly requested orphan Tasks. This avoids one repair pass erasing
800
+ // another file's frontmatter update.
801
+ const recoveredTasks = [];
802
+ if (plan.recoverOrphanTasks) {
803
+ for (const orphan of planOrphanTasks(scrumDir)) {
804
+ const rec = writeWithBackup(scrumDir, backupRoot, orphan.file, orphan.originalText, orphan.nextText);
805
+ recoveredTasks.push({ ...rec, id: orphan.id, from: orphan.status, to: "backlog" });
806
+ }
807
+ }
808
+
809
+ // Pass 6: rebuild the disposable state.md briefing so it reflects the
764
810
  // repaired canonical tree (otherwise doctor keeps warning STATE_STALE).
765
811
  try {
766
812
  require("../runtime/orchestrator").refreshState(scrumDir);
@@ -769,7 +815,7 @@ function apply(scrumDir, plan) {
769
815
  // if it truly cannot be rebuilt.
770
816
  }
771
817
 
772
- return { applied, ledger: ledgerResult, quarantined };
818
+ return { applied, ledger: ledgerResult, quarantined, recoveredTasks };
773
819
  }
774
820
 
775
821
  function renderReport(plan, applied) {
@@ -778,6 +824,7 @@ function renderReport(plan, applied) {
778
824
  lines.push(`Files needing frontmatter repair: ${plan.entries.length}`);
779
825
  if (plan.ledgerMalformed) lines.push(`Run ledgers to normalize: ${plan.ledgerMalformed}`);
780
826
  if (plan.orphanRuns && plan.orphanRuns.length) lines.push(`Orphan Runs to quarantine: ${plan.orphanRuns.length}`);
827
+ if (plan.orphanTasks && plan.orphanTasks.length) lines.push(`Orphan active Tasks found: ${plan.orphanTasks.length}`);
781
828
  const byField = plan.totals.byField;
782
829
  if (Object.keys(byField).length) {
783
830
  lines.push("\nBy field:");
@@ -793,15 +840,21 @@ function renderReport(plan, applied) {
793
840
  }
794
841
  if (plan.entries.length > 30) lines.push(` … and ${plan.entries.length - 30} more`);
795
842
  }
843
+ if (plan.orphanTasks && plan.orphanTasks.length && !plan.recoverOrphanTasks) {
844
+ lines.push("\nOrphan active Tasks are not changed automatically. Review and run: scrumrun repair --recover-orphan-tasks --apply");
845
+ for (const orphan of plan.orphanTasks.slice(0, 30)) lines.push(` - ${orphan.id}: ${orphan.reason}`);
846
+ }
796
847
  if (applied) {
797
848
  lines.push("");
798
849
  const frontmatterCount = applied.applied ? applied.applied.length : 0;
799
850
  const ledgerCount = applied.ledger && applied.ledger.applied ? applied.ledger.applied.length : 0;
800
851
  const quarantinedCount = applied.quarantined ? applied.quarantined.length : 0;
801
- if (frontmatterCount || ledgerCount || quarantinedCount) {
852
+ const recoveredTaskCount = applied.recoveredTasks ? applied.recoveredTasks.length : 0;
853
+ if (frontmatterCount || ledgerCount || quarantinedCount || recoveredTaskCount) {
802
854
  if (frontmatterCount) lines.push(`Applied: ${frontmatterCount} frontmatter file(s) rewritten.`);
803
855
  if (ledgerCount) lines.push(`Normalized: ${ledgerCount} Run ledger(s) collapsed to snapshot events.`);
804
856
  if (quarantinedCount) lines.push(`Quarantined: ${quarantinedCount} orphan Run(s) moved to .scrumrun/.migration-backup/repair/orphan-runs/.`);
857
+ if (recoveredTaskCount) lines.push(`Recovered: ${recoveredTaskCount} orphan active Task(s) reset to backlog; start each explicitly to create a Run.`);
805
858
  lines.push("Backups under .scrumrun/.migration-backup/repair/ and .scrumrun/.migration-backup/runs/.");
806
859
  } else {
807
860
  lines.push("Applied: no changes.");
@@ -813,11 +866,11 @@ function renderReport(plan, applied) {
813
866
  return lines.join("\n");
814
867
  }
815
868
 
816
- function repair(scrumDir, { apply: doApply = false } = {}) {
869
+ function repair(scrumDir, { apply: doApply = false, recoverOrphanTasks = false } = {}) {
817
870
  if (!fs.existsSync(scrumDir) || !fs.lstatSync(scrumDir).isDirectory()) {
818
871
  throw new Error(".scrumrun/ not found; run this inside a ScrumRun project.");
819
872
  }
820
- const plan = analyze(scrumDir);
873
+ const plan = analyze(scrumDir, { recoverOrphanTasks });
821
874
  let ledgerPreview = null;
822
875
  try {
823
876
  ledgerPreview = normalizeLegacyRuns(scrumDir, { dryRun: true }).plan;
package/lib/errors.js CHANGED
@@ -31,44 +31,44 @@ const CATALOG = Object.freeze({
31
31
  "SR-E-052": { summary: "Approval token expired.", remediation: "Re-run intake to obtain a fresh token before approving." },
32
32
 
33
33
  // Runs and ledger
34
- "SR-E-100": { summary: "Run not found.", remediation: "Check the RUN-NNN id with `sc plan run --list`." },
35
- "SR-E-101": { summary: "Run ledger contains an invalid event.", remediation: "Inspect the reported event id; use `sc plan run --render` for a human view or restore from git history." },
34
+ "SR-E-100": { summary: "Run not found.", remediation: "Check the RUN-NNN id with `scrumrun plan run --list`." },
35
+ "SR-E-101": { summary: "Run ledger contains an invalid event.", remediation: "Inspect the reported event id; use `scrumrun plan run --render` for a human view or restore from git history." },
36
36
  "SR-E-102": { summary: "Run transition rejected: missing validation or learning evidence.", remediation: "Complete `--validate` and `--learn` with typed evidence before `--complete`." },
37
37
  "SR-E-103": { summary: "Retry rejected because the previous Run is still active.", remediation: "Complete, fail, or block the current Run before creating a retry." },
38
38
 
39
39
  // Guardrails
40
40
  "SR-E-150": { summary: "Guardrail check blocked the operation.", remediation: "Read the reported GR-NNN, satisfy or retire it explicitly; guardrails never bypass silently." },
41
- "SR-E-151": { summary: "Guardrail obligation is still pending.", remediation: "Resolve each `pending guardrail` via `sc plan run --satisfy-guardrail` before completing the Run." },
41
+ "SR-E-151": { summary: "Guardrail obligation is still pending.", remediation: "Resolve each `pending guardrail` via `scrumrun plan run --satisfy-guardrail` before completing the Run." },
42
42
  "SR-E-152": { summary: "Guardrail declaration is malformed.", remediation: "Every active guardrail requires Status, Enforcement, Scope, and Rule fields; check .scrumrun/guardrails.md." },
43
43
 
44
44
  // Edit permits (Mutation Gateway)
45
- "SR-E-200": { summary: "No edit permit for this path.", remediation: "Request one with `sc plan run --authorize-mutation RUN-NNN --path <path>` before editing canonical or source files." },
45
+ "SR-E-200": { summary: "No edit permit for this path.", remediation: "Request one with `scrumrun plan run --authorize-mutation RUN-NNN --path <path>` before editing canonical or source files." },
46
46
  "SR-E-201": { summary: "Edit permit expired.", remediation: "Permits last 15 minutes. Authorize a new one and record the change immediately." },
47
47
  "SR-E-202": { summary: "Edit permit path scope mismatch.", remediation: "The permit does not cover the modified path. Request a new permit that lists it." },
48
48
  "SR-E-203": { summary: "File hash changed unexpectedly since the permit was issued.", remediation: "Someone else modified the file. Re-plan the change and request a fresh permit." },
49
49
 
50
50
  // Transactions and recovery
51
- "SR-E-250": { summary: "Pending kernel transaction cannot be recovered automatically.", remediation: "Run `sc config doctor --recover --dry-run` to preview; if it shows 'would overwrite owner changes', reconcile the file manually before applying." },
51
+ "SR-E-250": { summary: "Pending kernel transaction cannot be recovered automatically.", remediation: "Run `scrumrun config doctor --recover --dry-run` to preview; if it shows 'would overwrite owner changes', reconcile the file manually before applying." },
52
52
  "SR-E-251": { summary: "Journal fails integrity check.", remediation: "Inspect .scrumrun/.transactions/pending. Do not delete; contact support or restore from backup." },
53
53
 
54
54
  // Memory
55
- "SR-E-300": { summary: "Memory candidate rejected: missing resolvable evidence.", remediation: "Attach at least one --evidence path or `sc knowledge <subject> --propose` before `--confirm`." },
55
+ "SR-E-300": { summary: "Memory candidate rejected: missing resolvable evidence.", remediation: "Attach at least one --evidence path or `scrumrun knowledge <subject> --propose` before `--confirm`." },
56
56
  "SR-E-301": { summary: "Attempt to write into vault via canonical channel.", remediation: "vault.local.md is local-only and never indexed. Edit the file directly." },
57
57
 
58
58
  // Semantic index / code intel
59
- "SR-E-350": { summary: "Semantic index is stale.", remediation: "Rebuild with `sc knowledge map --build`. Cache is disposable; canonical memory is unaffected." },
60
- "SR-E-351": { summary: "Search backend advertised in the cache does not match this runtime.", remediation: "Delete .scrumrun/.cache/semantic-index.sqlite and re-run any `sc knowledge` query to rebuild against the current runtime." },
59
+ "SR-E-350": { summary: "Semantic index is stale.", remediation: "Rebuild with `scrumrun knowledge map --build`. Cache is disposable; canonical memory is unaffected." },
60
+ "SR-E-351": { summary: "Search backend advertised in the cache does not match this runtime.", remediation: "Delete .scrumrun/.cache/semantic-index.sqlite and re-run any `scrumrun knowledge` query to rebuild against the current runtime." },
61
61
 
62
62
  // Migration
63
- "SR-E-400": { summary: "Migration preflight failed.", remediation: "Run `npx scrumrun@latest update` (dry) to see blockers, resolve them, then apply with `--migrate`." },
63
+ "SR-E-400": { summary: "Migration preflight failed.", remediation: "Run `scrumrun update` (dry) to see blockers, resolve them, then apply with `--migrate`." },
64
64
  "SR-E-401": { summary: "Migration rollback requested but no backup was found.", remediation: "Rollback needs the ignored byte-exact backup created during --migrate. Restore from version control if the backup is gone." },
65
65
 
66
66
  // Conformance / doctor
67
67
  "SR-E-450": { summary: "Conformance check failed.", remediation: "The reported invariant identifies the exact violation; the message includes the file and expected shape." },
68
- "SR-E-451": { summary: "Installed client asset is stale.", remediation: "Re-run `npx scrumrun@latest update` for the specific client. `doctor --strict` shows which files diverge." },
68
+ "SR-E-451": { summary: "Installed client asset is stale.", remediation: "Re-run `scrumrun update` for the specific client. `doctor --strict` shows which files diverge." },
69
69
 
70
70
  // Configuration / install
71
- "SR-E-500": { summary: "ScrumRun project not initialized.", remediation: "Run `npx scrumrun@latest init` in the repository root." },
71
+ "SR-E-500": { summary: "ScrumRun project not initialized.", remediation: "Run `scrumrun init` in the repository root." },
72
72
  "SR-E-501": { summary: "Unsupported Node.js runtime.", remediation: "ScrumRun requires Node.js >=22.13.0 for native SQLite. Upgrade Node and retry." }
73
73
  });
74
74
 
@@ -97,8 +97,8 @@ ${backlogTasks.length ? backlogTasks.join("\n") : "- No backlog Tasks."}
97
97
  - Guardrails: .scrumrun/guardrails.md
98
98
  - Tasks: .scrumrun/tasks/ (${counts.task || 0} total)
99
99
  - Runs: .scrumrun/runs/ (${counts.run || 0} total)
100
- - Semantic: sc knowledge study "<topic>" for deep queries
101
- - Full state: rebuild with sc config doctor --recover
100
+ - Semantic: scrumrun knowledge study "<topic>" for deep queries
101
+ - Full state: rebuild with scrumrun config doctor --recover
102
102
  `;
103
103
 
104
104
  return { content, sourceFingerprint, watchFingerprint: watch.fingerprint, sourceFiles: watch.files };
@@ -586,7 +586,7 @@ const ADD_PLAN_INITIAL_STATUS = Object.freeze({
586
586
  const TASK_TYPES = new Set(["task", "fix", "docs", "discovery"]);
587
587
 
588
588
  function planArtifactBody(kind, title, created) {
589
- const source = `- ${created}: created via CLI (\`sc plan ${kind} --add\`).`;
589
+ const source = `- ${created}: created via CLI (\`scrumrun plan ${kind} --add\`).`;
590
590
  if (kind === "task") {
591
591
  return [
592
592
  `# ${title}`,
@@ -79,7 +79,7 @@ function auditProject(projectRoot) {
79
79
  if (methodMarker.method !== METHOD_VERSION) findings.push(finding("critical", "METHOD_VERSION", `method.json must declare ${METHOD_VERSION}.`, marker));
80
80
  const expectedPaths = canonicalPaths();
81
81
  if (!methodMarker.paths || typeof methodMarker.paths !== "object" || Array.isArray(methodMarker.paths)) {
82
- findings.push(finding("high", "METHOD_PATHS_MISSING", `method.json must declare a canonical "paths" block so agents navigate by declaration, not by search. Run \`npx scrumrun@latest update --migrate\` to backfill.`, marker));
82
+ findings.push(finding("high", "METHOD_PATHS_MISSING", `method.json must declare a canonical "paths" block so agents navigate by declaration, not by search. Run \`scrumrun update --migrate\` to backfill.`, marker));
83
83
  } else {
84
84
  const drift = diffPathIndex(expectedPaths, methodMarker.paths);
85
85
  for (const entry of drift) findings.push(finding("high", "METHOD_PATHS_DRIFT", `method.json paths[${entry.label}] is ${entry.actual === undefined ? "missing" : `"${entry.actual}"`}; expected "${entry.expected}". Regenerate via \`update --migrate\`.`, marker));
@@ -152,7 +152,7 @@ function auditProject(projectRoot) {
152
152
  if (run.record && run.record.ledger === RUN_LEDGER_VERSION) {
153
153
  const ledger = validateRunLedger(run.record, run.body);
154
154
  if (ledger.errors.length) {
155
- findings.push(finding("high", "RUN_WRITE_BYPASS", `${run.record.id}: ledger has ${ledger.errors.length} validation error(s); this Run was likely written without going through the CLI. Recover with \`sc plan run --normalize-legacy --dry-run\` then re-run without --dry-run.`, run.file));
155
+ findings.push(finding("high", "RUN_WRITE_BYPASS", `${run.record.id}: ledger has ${ledger.errors.length} validation error(s); this Run was likely written without going through the CLI. Recover with \`scrumrun plan run --normalize-legacy --dry-run\` then re-run without --dry-run.`, run.file));
156
156
  for (const error of ledger.errors) findings.push(finding("high", "RUN_LEDGER_INVALID", `${run.record.id}: ${error}`, run.file));
157
157
  }
158
158
  } else if (run.record) {
@@ -198,7 +198,17 @@ function auditProject(projectRoot) {
198
198
  const attempts = (records.run || [])
199
199
  .filter((run) => run.record && !run.errors.length && run.record.task === task.record.id)
200
200
  .sort((left, right) => right.record.attempt - left.record.attempt);
201
- if (!attempts.length) continue;
201
+ if (!attempts.length) {
202
+ if (["running", "validating", "learning", "partial"].includes(task.record.status)) {
203
+ findings.push(finding(
204
+ "high",
205
+ "TASK_ORPHANED",
206
+ `${task.record.id}.status is ${task.record.status}, but no canonical Run references it. Review with \`scrumrun repair --recover-orphan-tasks\`, then apply the explicit recovery and start the Task to create its first Run.`,
207
+ task.file
208
+ ));
209
+ }
210
+ continue;
211
+ }
202
212
  const latest = attempts[0].record;
203
213
  const taskStatuses = {
204
214
  executing: "running",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrumrun",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Evidence-driven Agile runtime and semantic project memory for AI coding agents.",
5
5
  "bin": {
6
6
  "scrumrun": "bin/scrumrun.js",
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## ScrumRun 3.0
4
4
 
5
- This project uses ScrumRun. The method is mandatory; `/sc` is its single optional shortcut.
5
+ This project uses ScrumRun. The method is mandatory; the direct CLI is `scrumrun <noun> <subject> <action>`. `/sc` is only an optional client shortcut.
6
6
 
7
7
  For normal work, read:
8
8
 
@@ -21,11 +21,11 @@ After approval:
21
21
  - a retry creates a new Run and preserves the old one;
22
22
  - record a `## Technical Summary` at completion so the next agent inherits what was done;
23
23
  - work directly in code and the linked Task Markdown after approval; do not call `npx scrumrun@latest` during execution;
24
- - record one `## Guardrail Evidence` line per non-automatic guardrail in the Task, then run `scrumrun sc plan run --finalize RUN-NNN` once to validate and close the Run;
25
- - when a Run completes and work remains queued, surface it with `sc plan task --next` and start it with `sc plan task --start` — starting is explicit approval;
24
+ - record one `## Guardrail Evidence` line per non-automatic guardrail in the Task, then run `scrumrun plan run --finalize RUN-NNN` once to validate and close the Run;
25
+ - when a Run completes and work remains queued, surface it with `scrumrun plan task --next` and start it with `scrumrun plan task --start` — starting is explicit approval;
26
26
  - learning proposes evidence-backed Knowledge, Decisions, or candidate Insights;
27
27
  - the final checkpoint verifies all Guardrails, workspace changes, protected paths, and secret boundaries before completion; use the path-scoped Mutation Gateway only when the owner requests strict execution.
28
28
 
29
29
  Never bypass guardrails or edit around the Mutation Gateway, overwrite owner work, treat generated state/cache as truth, auto-confirm AI knowledge, auto-migrate a v1 project, or print vault values.
30
30
 
31
- If `/sc` is unavailable, follow the equivalent workflow in `.scrumrun/core.md` manually.
31
+ Never use `npx scrumrun@latest` in the normal work loop. If the installed CLI is unavailable, stop and report that blocker rather than substituting a network command.