scrumrun 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  All notable changes follow Semantic Versioning.
4
4
 
5
+ ## Unreleased
6
+
7
+ ## 2.1.0 - 2026-07-22
8
+
9
+ ### Added
10
+
11
+ - Canonical Run ledger schema with stable event ids, RFC3339 timestamps, actors, reasons, and typed evidence.
12
+ - Explicit early-v2 Run-ledger preflight/apply/rollback through `migrate --to 2` and `update --migrate`.
13
+ - Conformance checks that reconstruct Run state and detect event-order, evidence, timestamp, and frontmatter drift.
14
+ - Durable multi-file mutation journal with byte-exact rollback, interruption recovery, hash-only receipts, and explicit `doctor --recover`.
15
+ - Executable Policy Engine with stable Guardrail ids, explicit passed/blocked/deferred results, and deterministic conformance checks.
16
+ - Shared canonical projection fingerprint plus metadata-watch/content-hash freshness checks for `state.md`, `map.md`, and semantic SQLite.
17
+ - Evidence-backed release scorecard separating local readiness from external registry/tag gates.
18
+
19
+ ### Changed
20
+
21
+ - Run is now the sole operational-history authority; Task status is synchronized without duplicated transition prose.
22
+ - Run completion requires evidenced validation, learning, and completion transitions.
23
+ - Intake classification and risk detection now recognize multilingual runtime, schema, migration, transaction, release, and cross-cutting signals.
24
+ - Semantic cache schema 3 removes commit-only invalidation and performs a one-time disposable rebuild from older cache schemas.
25
+ - `map --show` now refuses missing or stale fingerprints instead of presenting an unverified projection.
26
+
5
27
  ## 2.0.0 - 2026-07-21
6
28
 
7
29
  ### Breaking
package/CORE.md CHANGED
@@ -84,6 +84,8 @@ AGENTS.md
84
84
 
85
85
  Canonical truth is Markdown. SQLite/cache data stores only rebuildable indexes, symbol projections, relations, and bounded context packages. Deleting `.cache/` must never delete authored truth.
86
86
 
87
+ `state.md` and the semantic index use two-tier freshness checks. Matching path/stat watch fingerprints avoid rereading unchanged sources; any metadata drift falls back to complete content hashing. A cache schema mismatch rebuilds the disposable index once. Watch metadata is only an optimization and never authority.
88
+
87
89
  `vault.local.md`, migration backups, and caches are local-only. Their values never appear in logs, history, reviews, memory, reports, commits, or normal responses.
88
90
 
89
91
  ## Authority and read policy
@@ -146,6 +148,8 @@ Intake must:
146
148
  5. recommend one route and, in guided mode, at most two useful alternatives;
147
149
  6. ask one explicit approval question before execution; config may change presentation, never remove the gate.
148
150
 
151
+ Policy evaluation is structured per active `GR-NNN`: `passed`, `blocked`, or `deferred`. A block must cite the exact Guardrail id and reason code. Deferred checks must be visible in the plan and re-evaluated at their named execution boundary; they are never silently counted as passed. Invalid/duplicate Guardrails or configuration that weakens approval block conformance and intake.
152
+
149
153
  Before approval, do not create canonical files, update status, edit application code, or retain request content outside ignored disposable context cache. Ambiguous acknowledgement is not approval.
150
154
 
151
155
  ## Execution lifecycle
@@ -165,7 +169,9 @@ Rules:
165
169
 
166
170
  - one Run belongs to one Task;
167
171
  - one Task may have multiple immutable attempts;
168
- - every state transition writes exactly one append-only history event;
172
+ - every state transition writes exactly one append-only `RUN-NNN-EVT-NNN` JSON event with RFC3339 time, actor, reason, and typed evidence;
173
+ - the Run ledger is operational history; Task synchronizes current status without copying the Run event;
174
+ - validation, learning, completion, failure, block, and resume require a reason or evidence;
169
175
  - validation must match the risk and acceptance criteria;
170
176
  - configured reviews run before completion;
171
177
  - learning proposes memory candidates after validation and never auto-confirms AI inference;
@@ -174,6 +180,8 @@ Rules:
174
180
 
175
181
  Canonical mutations are schema-validated, lossless, and atomic. Preserve unknown fields, prose, and unrelated owner edits. A failed mutation must leave canonical state unchanged or recoverable.
176
182
 
183
+ Task/Run pair mutations use an ignored durable journal under `.scrumrun/.backup/transactions/`. A captured failure rolls back immediately; an interrupted `prepared` transaction is rolled back before the next approved mutation, while an interrupted `committed` transaction is verified and finalized. Audit only reports `TRANSACTION_PENDING`. Recovery writes occur only when the approved operation is retried or `doctor --recover` is explicitly invoked, and recovery refuses to overwrite bytes that match neither side of the journal.
184
+
177
185
  ## Semantic memory
178
186
 
179
187
  ### Knowledge
@@ -220,6 +228,7 @@ scrumrun migrate --to 2 --rollback
220
228
  ```
221
229
 
222
230
  - ordinary install/update never applies a migration; update performs a read-only v1 preflight, and only explicit `update --migrate` applies its verified plan;
231
+ - early v2 Run prose is also preflighted read-only and upgraded explicitly to ledger schema 1 with byte-exact backup and safe rollback;
223
232
  - dry-run writes no project data;
224
233
  - apply inventories source hashes, creates a byte-exact local backup, transforms in staging, validates, and activates by atomic directory swap;
225
234
  - incomplete hybrid v1/v2 trees reuse existing evidenced canonical relations instead of creating duplicate Tasks/Runs;
@@ -283,6 +292,7 @@ Review is read-only unless fixes are separately authorized. Report findings by s
283
292
  - Intake is read-only.
284
293
  - No execution without explicit valid approval.
285
294
  - Guardrails cannot be bypassed; they may only be superseded/retired with history.
295
+ - Active Guardrails produce explicit passed/blocked/deferred evaluations; blocks cite stable ids and deferred checks remain visible until their execution gate.
286
296
  - Task is atomic; Sprint is grouping; Run is an attempt.
287
297
  - Retries preserve prior Runs.
288
298
  - History is append-only.
package/DECISIONS.md CHANGED
@@ -294,3 +294,59 @@ Date: 2026-07-21
294
294
  **Consequences** — Ongoing projects cannot silently miss the required migration, while automation and cautious users retain a zero-write default. Cost: update output is longer inside a v1 project and the update command must remain migration-aware.
295
295
 
296
296
  **Alternatives considered** — Automatically applying during every update: rejected because updating client integrations is not implicit consent to rewrite project state. Keeping update unaware: safer in isolation but makes partial upgrades easy and confusing.
297
+
298
+ ---
299
+
300
+ ## ADR-021 — Run owns a structured append-only event ledger
301
+ Status: accepted
302
+ Date: 2026-07-22
303
+
304
+ **Context** — A prose `history.md` made operational truth expensive to parse and allowed two incompatible event formats, ambiguous same-day entries, duplicated Task/Run history, and status derived from punctuation-sensitive text.
305
+
306
+ **Decision** — Run is the sole operational-history authority. Every native Run stores schema-versioned `RUN-NNN-EVT-NNN` JSON events with RFC3339 time, actor, ordered `from`/`to`, reason, and typed evidence. Task synchronizes current status without copying event prose. Early-v2 Runs migrate explicitly: deterministic chains are recovered; ambiguity becomes an evidenced snapshot rather than an invented transition.
307
+
308
+ **Consequences** — Status reconstruction and audit are deterministic, retries preserve attempts, and agents can retrieve a bounded Run instead of rereading a global log. Cost: event/schema migrations and stricter evidence gates become release obligations.
309
+
310
+ **Alternatives considered** — Keep one append-only Markdown history with a better template: simpler storage, but still global, token-heavy, and vulnerable to parser drift. Duplicate transitions into Task: convenient locally but creates two authorities.
311
+
312
+ ---
313
+
314
+ ## ADR-022 — Canonical multi-file mutations use durable local transactions
315
+ Status: accepted
316
+ Date: 2026-07-22
317
+
318
+ **Context** — Approval, Run transitions, and retries update linked artifacts. Per-file atomic rename prevents partial bytes but cannot prevent a crash after only one file has committed.
319
+
320
+ **Decision** — Linked canonical mutations use an ignored durable transaction journal with prepared/committed states, byte-exact before images, content hashes, fsync-backed writes, deterministic recovery, and hash-only receipts. Prepared work rolls back; committed work is verified and finalized. Recovery refuses to overwrite owner changes made after interruption. Ordinary audit reports pending recovery but remains read-only.
321
+
322
+ **Consequences** — Task/Run pairs cannot silently diverge after an interrupted mutation. Failure injection proves rollback and recovery. Cost: more local I/O and a transaction schema that must be validated as hostile input.
323
+
324
+ **Alternatives considered** — Best-effort compensating writes: cannot distinguish interruption from later owner edits. SQLite as canonical transaction store: violates Markdown portability and creates another authority.
325
+
326
+ ---
327
+
328
+ ## ADR-023 — Guardrails are evaluated by an executable Policy Engine
329
+ Status: accepted
330
+ Date: 2026-07-22
331
+
332
+ **Context** — Canonical Guardrails without deterministic evaluation could be present in context yet silently ignored or described as passed. Migrated prose also makes aggressive inference unsafe.
333
+
334
+ **Decision** — Active stable-id Guardrails produce structured `passed`, `blocked`, or `deferred` evaluations. Blocks cite the exact `GR-NNN` id and reason code. Deferred checks remain visible for their mutation, migration, review, or owner boundary. Fresh rules declare enforcement explicitly; migrated prose uses conservative inference and ambiguous rules stay manual. Configuration cannot disable approval or weaken active policy.
335
+
336
+ **Consequences** — Policy advice is traceable to project authority instead of model opinion, while uncertainty stays explicit. Cost: not every project rule can be automated; manual/deferred enforcement remains a first-class result.
337
+
338
+ **Alternatives considered** — Treat every loaded rule as passed unless a keyword matches: false assurance. Block all manual rules at intake: safe but makes legitimate project-specific policy unusable.
339
+
340
+ ---
341
+
342
+ ## ADR-024 — Derived freshness uses metadata fast path and content-hash fallback
343
+ Status: accepted
344
+ Date: 2026-07-22
345
+
346
+ **Context** — Recomputing every artifact hash and reparsing the entire code graph before each status or semantic query makes a disposable index defeat its purpose. Trusting timestamps alone would make cache metadata an accidental authority.
347
+
348
+ **Decision** — `state.md` and semantic SQLite store a source fingerprint plus a watch fingerprint over path/stat identity. An unchanged watch avoids source reads. Any metadata drift triggers a complete canonical/source content fingerprint before staleness is asserted. Cache schemas are explicit and mismatches force one rebuild. Commit-only changes do not invalidate semantically unchanged code.
349
+
350
+ **Consequences** — Normal queries stay fast while content remains the final verifier. State and intake share one canonical fingerprint implementation. Cost: metadata-only touches may trigger one full verification, and scanner changes require a cache-schema bump.
351
+
352
+ **Alternatives considered** — Hash every source on every query: maximally simple but O(project) on the hot path. Timestamp-only cache validity: faster but allows derived metadata to masquerade as truth.
@@ -32,6 +32,17 @@ npx scrumrun@latest update --migrate
32
32
 
33
33
  `--migrate` is explicit consent. Ordinary update never changes canonical project data.
34
34
 
35
+ ### Early v2 Run upgrade
36
+
37
+ Projects created by the first 2.0.0 build may contain prose-based Run transitions. The same ongoing-project commands detect this layout without writes and, after explicit `--migrate`, upgrade it to ledger schema 1:
38
+
39
+ ```text
40
+ .scrumrun/.migration/run-ledger-v1/manifest.json
41
+ .scrumrun/.migration/run-ledger-v1/backup/runs/RUN-NNN.md
42
+ ```
43
+
44
+ Every changed Run and `method.json` is hashed and backed up byte-exactly. Proven transition chains become ordered `RUN-NNN-EVT-NNN` events. When only the recorded status is provable, migration emits one evidenced `snapshot`; it does not invent the missing path. Replay is idempotent, an interrupted apply restores prepared sources, and rollback refuses when a Run changed after migration.
45
+
35
46
  ## Standalone flow
36
47
 
37
48
  ```bash
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ScrumRun gives an agent a small command surface and a precise project memory: what should be done, how each attempt happened, which decisions constrain the code, and why the architecture exists in its current form.
6
6
 
7
- **Method target:** `2.0.0` · **Runtime:** Node.js `>=22.13.0` · **License:** MIT
7
+ **Package:** `2.1.0` · **Method target:** `2.0.0` · **Runtime:** Node.js `>=22.13.0` · **License:** MIT
8
8
 
9
9
  ## The model
10
10
 
@@ -71,6 +71,10 @@ EXECUTING → VALIDATING → LEARNING → COMPLETED | FAILED | BLOCKED
71
71
 
72
72
  Nothing canonical is persisted before approval. Failed retries remain available as separate Runs.
73
73
 
74
+ Each Run contains a machine-validated event ledger. Events have stable ids such as `RUN-044-EVT-003`, RFC3339 timestamps, actors, reasons, and typed evidence for commands, tests, files, reviews, decisions, insights, and risks. Run is the only operational history; Task keeps its approved scope and synchronized current status without duplicating those events. Completion is rejected when validation or learning evidence is missing.
75
+
76
+ Linked Task/Run writes use a durable ignored transaction journal. Captured failures roll back immediately; interrupted operations are recovered byte-exactly on retry or through explicit `doctor --recover`. Read-only audit reports pending recovery and never repairs state silently.
77
+
74
78
  ## Semantic project memory
75
79
 
76
80
  Canonical memory is human-readable Markdown:
@@ -90,7 +94,9 @@ The agent can answer questions such as:
90
94
 
91
95
  AI extraction creates candidates only. Confirmation requires resolvable evidence. Stale and invalidated memory is labeled; rejected/deprecated/invalidated records are excluded from active truth by default.
92
96
 
93
- The fast graph/search layer is `.scrumrun/.cache/semantic-index.sqlite`. It is ignored and disposable: deleting it never deletes knowledge. The current JavaScript/TypeScript adapter derives qualified symbols plus `defined_in`, `depends_on`, `used_by`, and `protected_by` relations.
97
+ The fast graph/search layer is `.scrumrun/.cache/semantic-index.sqlite`. It is ignored and disposable: deleting it never deletes knowledge. Unchanged queries use a metadata-only freshness check; metadata drift falls back to complete content fingerprints before rebuilding. Cache-schema upgrades force one safe disposable rebuild. The current JavaScript/TypeScript adapter derives qualified symbols plus `defined_in`, `depends_on`, `used_by`, and `protected_by` relations.
98
+
99
+ `map.md` is shown only when its source fingerprint matches the current semantic index. A fresh placeholder or stale map is rejected with an explicit rebuild instruction instead of being presented as project truth.
94
100
 
95
101
  ## Migrating an ongoing v1 project
96
102
 
@@ -114,7 +120,7 @@ npx scrumrun@latest migrate --to 2 --apply
114
120
  npx scrumrun@latest migrate --to 2 --rollback
115
121
  ```
116
122
 
117
- Migration uses content hashes, a byte-exact ignored backup, staging validation, an atomic directory switch, a source-to-destination report, idempotent replay, and rollback protection. It also recognizes incomplete hybrid v1/v2 trees, reuses already-linked canonical work, and normalizes only deterministic schema aliases. Legacy-only files leave the active tree but remain byte-exact in the ignored backup; vault contents remain local. Ambiguous history remains an explicit warning, and the migrator never invents a Sprint or Run.
123
+ Migration uses content hashes, a byte-exact ignored backup, staging validation, an atomic directory switch, a source-to-destination report, idempotent replay, and rollback protection. It also recognizes incomplete hybrid v1/v2 trees, reuses already-linked canonical work, and normalizes only deterministic schema aliases. Early v2 Run prose is preflighted and upgraded to the structured ledger by the same explicit `update --migrate` gate; ambiguous paths become evidenced snapshots instead of invented transitions. Legacy-only files leave the active tree but remain byte-exact in the ignored backup; vault contents remain local. Ambiguous history remains an explicit warning, and the migrator never invents a Sprint or Run.
118
124
 
119
125
  After migration or an integration update, verify both installed assets and project state:
120
126
 
@@ -149,7 +155,7 @@ npx scrumrun@latest doctor codex --strict
149
155
  vault.local.md # optional, ignored, never indexed
150
156
  ```
151
157
 
152
- `guardrails.md` is canonical project policy. `config.md` contains preferences and cannot weaken it. `state.md`, `map.md`, context packages, and SQLite are generated navigation aids, never authority.
158
+ `guardrails.md` is canonical project policy. Active `GR-NNN` rules are evaluated into explicit `passed`, `blocked`, or `deferred` results; blocks identify the exact Guardrail and deferred checks stay visible for their execution-time gate. `config.md` contains preferences and cannot weaken policy. Duplicate/unknown Guardrails, disabled approval, and unsafe read-only paths fail conformance. `state.md`, `map.md`, context packages, and SQLite are generated navigation aids, never authority. `state.md` carries the same source fingerprint used by intake, an RFC3339 generation time, and a watch fingerprint for fast verified staleness checks.
153
159
 
154
160
  ## Useful commands
155
161
 
@@ -161,6 +167,9 @@ npx scrumrun@latest commands
161
167
  npx scrumrun@latest sc plan intake "Fix pricing rounding"
162
168
  npx scrumrun@latest sc plan intake --approve <token>
163
169
 
170
+ # verify canonical policy and all twenty executable invariants
171
+ npx scrumrun@latest sc review artifact --run
172
+
164
173
  # memory lifecycle
165
174
  npx scrumrun@latest sc knowledge insight --propose "Pricing stays in backend" --evidence src/pricing.ts
166
175
  npx scrumrun@latest sc knowledge insight --confirm INS-001
@@ -181,6 +190,7 @@ The command manifest in `lib/commands/manifest.js` generates help and compatibil
181
190
  | [`SPEC.md`](./SPEC.md) | Normative 2.0 state machines, invariants, and conformance rules. |
182
191
  | [`DECISIONS.md`](./DECISIONS.md) | Architectural decisions and trade-offs. |
183
192
  | `MIGRATION-1-to-2.md` | Upgrade, verification, rollback, and recovery guide. |
193
+ | [`docs/RELEASE-SCORECARD.md`](docs/RELEASE-SCORECARD.md) | Evidence-backed local readiness scores and residual release risks. |
184
194
 
185
195
  ScrumRun remains client-independent: any agent that reads Markdown can follow `CORE.md`; Codex, Claude Code, and OpenCode integrations are accelerators.
186
196
 
package/SPEC.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ScrumRun Method Specification
2
2
 
3
- Version: `2.0.0` · Status: release candidate
3
+ Version: `2.0.0` · Status: stable
4
4
 
5
5
  `SPEC.md` defines normative meanings and invariants. `lib/v2/schema.js` defines the machine-enforced ids, paths, statuses, transitions, structural relations, and truth ownership metadata; `lib/commands/manifest.js` defines command grammar. `CORE.md` is the operational runtime guide. Generated `docs/SCHEMA.md` must match the executable schema byte-for-byte. A conflict is a conformance failure: semantics defer to SPEC, mechanically enforced values defer to the schema, grammar defers to the command manifest, and the conflicting representation must be corrected.
6
6
 
@@ -108,6 +108,14 @@ FEAT-003
108
108
  └── generated → INS-041
109
109
  ```
110
110
 
111
+ ### 3.3 Run event ledger
112
+
113
+ A newly authored Run declares `ledger: 1` and owns exactly one append-only operational event stream under `## Events`. Event ids are stable and scoped to the Run (`RUN-044-EVT-001`, `RUN-044-EVT-002`, ...). Each JSON event records a contiguous sequence, RFC3339 timestamp, timestamp precision, actor, source and destination states, reason, and typed evidence.
114
+
115
+ Native Runs begin with `created → executing`. A migration may instead create one evidenced `snapshot` as the historical baseline when the source proves a recorded status but not its full transition path. A snapshot exposes that uncertainty and never fabricates intermediate states.
116
+
117
+ The ledger is the execution authority. Task retains approved scope and synchronized current status, but does not copy Run events. Conformance reconstructs the Run state from its ledger and rejects missing or duplicate ids, invalid ordering, time reversal, illegal transitions, frontmatter drift, missing evidence, and unevidenced completion.
118
+
111
119
  ## 4. State machines
112
120
 
113
121
  Only the following transitions are valid.
@@ -192,7 +200,7 @@ Everything through `AWAITING_APPROVAL` is read-only. It may exist in process mem
192
200
 
193
201
  Approval atomically creates one Task and its first Run. If either write fails, neither may remain. Project changes after planning invalidate the token. Reusing a successfully consumed token is idempotent.
194
202
 
195
- Run transitions synchronously update the linked Task and append transition evidence. A paired write failure restores both files. Entering `learning` may extract structured candidates from the Run, but extraction failure never blocks Run progress.
203
+ Run transitions synchronously update the linked Task and append exactly one structured event to the Run ledger. Validation, learning, completion, failure, block, and resume transitions require a reason or typed evidence. Task status changes without receiving a duplicate narrative history. Multi-file mutations use a durable local transaction journal: `prepared` operations roll back byte-exactly after failure/interruption, while `committed` journals are verified and finalized. Ordinary audit is read-only and reports pending recovery; `doctor --recover` or retrying the approved mutation performs recovery explicitly. Entering `learning` may extract structured candidates from the Run, but extraction failure never blocks Run progress.
196
204
 
197
205
  ## 6. Policy and precedence
198
206
 
@@ -208,6 +216,10 @@ When guidance conflicts, apply this order:
208
216
 
209
217
  Configuration controls preferences but cannot weaken higher levels. Missing or stale context must be surfaced, never rendered as certainty.
210
218
 
219
+ Every project Guardrail has a stable `GR-NNN` identity, lifecycle status, rule text, enforcement mode, and optional scope/source. Intake evaluates active Guardrails into structured `passed`, `blocked`, or `deferred` results. A block names the responsible Guardrail and machine-readable reason code; a deferred result is shown explicitly and must be enforced at the mutation, migration, review, or owner gate it names. Deferred checks do not become evidence of a pass.
220
+
221
+ The executable Policy Engine may infer enforcement for migrated prose, but fresh v2 policy declares it explicitly. Unknown enforcement, duplicate ids, inactive-only policy, configuration that disables approval, and unsafe read-only paths fail conformance. Configuration can tune presentation and workflow preferences; it cannot retire, bypass, or weaken active Guardrails.
222
+
211
223
  ## 7. Semantic memory and code intelligence
212
224
 
213
225
  Memory records include subject, source, evidence, validity window, confidence where useful, review trigger, and last-verified commit. Insight types may include placement rationale, design constraint, known trade-off, failure history, usage warning, compatibility reason, business rule, performance reason, security reason, and testing note.
@@ -234,7 +246,9 @@ The agreed v1 control-context baseline is 48,000 characters. A fresh lean v2 int
234
246
 
235
247
  ## 8. Generated views and cache
236
248
 
237
- `state.md`, `map.md`, context packages, and `.cache/semantic-index.sqlite` are non-authoritative projections. They carry source fingerprints or are treated as stale. Deleting `.cache/` must not remove authored knowledge, and rebuilding it from unchanged sources must yield equivalent query results.
249
+ `state.md`, `map.md`, context packages, and `.cache/semantic-index.sqlite` are non-authoritative projections. They carry source fingerprints or are treated as stale. `state.md` uses the exact canonical fingerprint bound into intake, plus a projection schema, RFC3339 generation time, and a disposable watch fingerprint. SQLite stores its source and watch fingerprints with an explicit cache schema.
250
+
251
+ Freshness checks use a two-tier strategy: unchanged path/stat identity proves that no source read or reparse is needed; changed metadata triggers a complete canonical/source content fingerprint before staleness is asserted. Cache metadata may optimize verification but never supplies project truth. A cache-schema mismatch forces one disposable rebuild. Deleting `.cache/` must not remove authored knowledge, and rebuilding it from unchanged sources must yield equivalent query results.
238
252
 
239
253
  The index must never scan or store `vault.local.md`. Source scanning is bounded, skips dependencies/build output and symlinks, and uses replaceable language adapters.
240
254
 
@@ -250,6 +264,8 @@ scrumrun migrate --to 2 --rollback
250
264
 
251
265
  `npx scrumrun@latest update` performs the same read-only preflight when run inside a v1 project and leaves project data untouched. `update --migrate` is an explicit request to apply the verified plan; it is not implicit migration.
252
266
 
267
+ Inside an early v2 project, the same commands preflight and explicitly upgrade legacy Run prose to ledger schema 1. Deterministic transition chains are recovered; incomplete history becomes an evidenced snapshot. Apply keeps byte-exact ignored backups, verifies hashes, is idempotent, and supports rollback that refuses to erase later Run changes.
268
+
253
269
  The migrator must:
254
270
 
255
271
  - hash every source file and block;
@@ -271,7 +287,7 @@ Legacy sprint entries become Tasks. History entries become Runs only with an evi
271
287
  - **I-02** Approval creates a linked Task/Run pair atomically or creates nothing.
272
288
  - **I-03** Task is atomic work; Sprint only groups Tasks with real batch/timebox evidence.
273
289
  - **I-04** Every retry creates a new Run and preserves earlier attempts.
274
- - **I-05** Only declared state transitions are accepted and paired transitions are recoverable.
290
+ - **I-05** Only declared, ordered, evidenced state transitions are accepted; Run event ids are unique and paired transitions are recoverable.
275
291
  - **I-06** `guardrails.md` is canonical project policy and configuration cannot weaken it.
276
292
  - **I-07** Markdown is canonical; SQLite and generated views are disposable projections.
277
293
  - **I-08** AI-created facts/insights remain candidates until explicit human confirmation.
@@ -284,7 +300,7 @@ Legacy sprint entries become Tasks. History entries become Runs only with an evi
284
300
  - **I-15** Migration is explicit; dry-run and ordinary update perform no project writes.
285
301
  - **I-16** Migration preserves hashed source coverage, backup, mapping, idempotency, and safe rollback.
286
302
  - **I-17** Migration warnings preserve ambiguity; they never invent Sprints, Runs, approval, or truth.
287
- - **I-18** Partial writes, conflicting overwrites, unsafe paths, and symlink traversal fail without corrupting canonical state.
303
+ - **I-18** Partial/interrupted writes, conflicting overwrites, unsafe paths, and symlink traversal fail or recover without corrupting canonical state or overwriting later owner work.
288
304
  - **I-19** Code intelligence is derived, adapter-based, fingerprinted, and cannot silently confirm memory.
289
305
  - **I-20** Post-validation learning proposes candidates and never blocks Task/Run completion.
290
306
 
@@ -308,8 +324,9 @@ An implementation may claim ScrumRun method 2.0.0 only when it:
308
324
  2. enforces every exposed state machine and schema;
309
325
  3. proves read-only intake and dry-run migration through full-tree fingerprints;
310
326
  4. proves migration failure recovery, rollback safety, and vault exclusion;
311
- 5. proves cache deletion/rebuild equivalence and inactive-memory filtering;
312
- 6. bounds context/retrieval and records benchmark budgets;
313
- 7. declares method `2.0.0` and requires Node.js `>=22.13.0` for the native SQLite index.
327
+ 5. reconstructs every native Run status from a valid, evidenced ledger and detects tampering;
328
+ 6. proves cache deletion/rebuild equivalence and inactive-memory filtering;
329
+ 7. bounds context/retrieval and records benchmark budgets;
330
+ 8. declares method `2.0.0` and requires Node.js `>=22.13.0` for the native SQLite index.
314
331
 
315
332
  Method changes follow semantic versioning. Breaking entity, path, invariant, or state changes require a major version and a migration guide.
package/bin/scrumrun.js CHANGED
@@ -9,14 +9,21 @@ 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"));
17
23
  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"));
24
+ const { indexPath, indexStatus, mapStatus, queryIndex, rebuildIndex, writeMap } = require(path.join(root, "lib", "memory", "index"));
19
25
  const { auditProject } = require(path.join(root, "lib", "v2", "conformance"));
26
+ const { recoverPendingTransactions } = require(path.join(root, "lib", "v2", "transaction"));
20
27
  const { containsSecret } = require(path.join(root, "lib", "security", "secrets"));
21
28
 
22
29
  const COMMANDS = ["sc"];
@@ -39,7 +46,7 @@ Usage:
39
46
  scrumrun migrate --to 2 --dry-run
40
47
  scrumrun migrate --to 2 --apply
41
48
  scrumrun migrate --to 2 --rollback
42
- scrumrun doctor [all|codex|opencode|claude] [--strict]
49
+ scrumrun doctor [all|codex|opencode|claude] [--strict] [--recover]
43
50
  scrumrun uninstall [--force]
44
51
 
45
52
  Examples:
@@ -267,7 +274,27 @@ function migrationPreflightOnUpdate({ apply = false } = {}) {
267
274
  const marker = path.join(scrumDir, "method.json");
268
275
  if (fs.existsSync(marker)) {
269
276
  try {
270
- if (JSON.parse(fs.readFileSync(marker, "utf8")).method === "2.0.0") return { status: "already-v2" };
277
+ if (JSON.parse(fs.readFileSync(marker, "utf8")).method === "2.0.0") {
278
+ const preview = planRunLedgerMigration(process.cwd());
279
+ if (preview.status === "current") return { status: "already-v2" };
280
+ console.log("\n## Project schema migration preflight\n");
281
+ console.log(reportRunLedgerMigration(preview).trimEnd());
282
+ if (preview.status === "blocked") {
283
+ for (const error of preview.errors) console.error(`BLOCKED: ${error}`);
284
+ process.exitCode = 1;
285
+ return { status: "blocked" };
286
+ }
287
+ if (!apply) {
288
+ console.log("\nThe project remains unchanged. Apply the verified plan with: npx scrumrun@latest update --migrate");
289
+ return { status: "ready" };
290
+ }
291
+ const result = applyRunLedgerMigration(process.cwd());
292
+ refreshState(path.join(process.cwd(), ".scrumrun"));
293
+ console.log("\nApplied the verified ScrumRun Run ledger schema migration.");
294
+ console.log(`Source fingerprint: ${result.plan.fingerprint}`);
295
+ console.log("Rollback remains available with: npx scrumrun@latest migrate --to 2 --rollback");
296
+ return { status: result.status, result };
297
+ }
271
298
  } catch {
272
299
  console.error("Project migration preflight failed: .scrumrun/method.json is malformed.");
273
300
  process.exitCode = 1;
@@ -1167,6 +1194,48 @@ function memoryOptions(args) {
1167
1194
  };
1168
1195
  }
1169
1196
 
1197
+ function runTransitionOptions(args) {
1198
+ const evidenceFlags = new Map([
1199
+ ["--command", "command"],
1200
+ ["--test", "test"],
1201
+ ["--file", "file"],
1202
+ ["--review", "review"],
1203
+ ["--decision", "decision"],
1204
+ ["--insight", "insight"],
1205
+ ["--risk", "risk"]
1206
+ ]);
1207
+ const referenceKinds = new Set(["file", "review", "decision", "insight"]);
1208
+ const evidence = [];
1209
+ const noteParts = [];
1210
+ let note = null;
1211
+ let actor = "agent";
1212
+ let occurredAt = null;
1213
+ for (let index = 2; index < args.length; index++) {
1214
+ const token = args[index];
1215
+ const value = args[index + 1] && !args[index + 1].startsWith("--") ? args[index + 1] : null;
1216
+ if (token === "--note" || token === "--actor" || token === "--at" || token === "--evidence" || evidenceFlags.has(token)) {
1217
+ if (!value) throw new Error(`${token} requires a value.`);
1218
+ index++;
1219
+ if (token === "--note") note = value;
1220
+ else if (token === "--actor") actor = value;
1221
+ else if (token === "--at") occurredAt = value;
1222
+ else if (token === "--evidence") {
1223
+ const separator = value.indexOf(":");
1224
+ const kind = separator > 0 ? value.slice(0, separator) : "note";
1225
+ const content = separator > 0 ? value.slice(separator + 1) : value;
1226
+ evidence.push(referenceKinds.has(kind) ? { kind, ref: content } : { kind, summary: content });
1227
+ } else {
1228
+ const kind = evidenceFlags.get(token);
1229
+ evidence.push(referenceKinds.has(kind) ? { kind, ref: value } : { kind, summary: value });
1230
+ }
1231
+ continue;
1232
+ }
1233
+ if (token.startsWith("--")) throw new Error(`Unknown Run evidence option: ${token}`);
1234
+ noteParts.push(token);
1235
+ }
1236
+ return { note: note || noteParts.join(" ").trim() || null, evidence, actor, occurredAt };
1237
+ }
1238
+
1170
1239
  function printMemoryArtifact(artifact) {
1171
1240
  if (!artifact) return false;
1172
1241
  console.log(readIfExists(artifact.file));
@@ -1233,9 +1302,8 @@ function runSemanticContext(subject, args) {
1233
1302
  return;
1234
1303
  }
1235
1304
  const mapFile = path.join(process.cwd(), ".scrumrun", "map.md");
1236
- if (!fs.existsSync(mapFile)) throw new Error("Generated map.md is missing; run /sc knowledge map --build.");
1237
- const status = indexStatus(process.cwd());
1238
- if (status.stale) console.warn("WARNING: map.md is stale; rebuild it before relying on the view.");
1305
+ const status = mapStatus(process.cwd());
1306
+ if (status.stale) throw new Error(`Generated map.md is stale (${status.reason || status.error || "unknown reason"}); run /sc knowledge map --build.`);
1239
1307
  console.log(fs.readFileSync(mapFile, "utf8"));
1240
1308
  return;
1241
1309
  }
@@ -1272,8 +1340,11 @@ function executeRootRoute(route) {
1272
1340
  console.log(`State: ${plan.state}`);
1273
1341
  console.log(`Classification: ${plan.classification.type} (${plan.classification.reason})`);
1274
1342
  console.log(`Risk: ${plan.risk.level} — ${plan.risk.reasons.join("; ")}`);
1275
- console.log(`Policy: ${plan.policy.status}`);
1343
+ console.log(`Policy: ${plan.policy.status} (${plan.policy.checked.length} checked; ${plan.policy.deferred.length} deferred)`);
1276
1344
  for (const violation of plan.policy.violations) console.log(`BLOCKED: ${violation}`);
1345
+ for (const result of plan.policy.evaluations.filter((item) => item.status === "deferred")) {
1346
+ console.log(`DEFERRED: ${result.guardrail} ${result.code}: ${result.message}`);
1347
+ }
1277
1348
  for (const warning of plan.context.warnings) console.log(`WARNING: ${warning}`);
1278
1349
  if (plan.approvalToken) {
1279
1350
  console.log(`Approval: scrumrun sc plan intake --approve ${plan.approvalToken}`);
@@ -1311,7 +1382,7 @@ function executeRootRoute(route) {
1311
1382
  "--block": "blocked"
1312
1383
  };
1313
1384
  if (transitions[routeArgs[0]]) {
1314
- const result = transitionRun(process.cwd(), routeArgs[1], transitions[routeArgs[0]], { note: routeArgs.slice(2).join(" ") || null });
1385
+ const result = transitionRun(process.cwd(), routeArgs[1], transitions[routeArgs[0]], runTransitionOptions(routeArgs));
1315
1386
  console.log(`${result.run.id}: ${result.run.status}; ${result.task.id}: ${result.task.status}.`);
1316
1387
  if (result.learning) {
1317
1388
  if (result.learning.created.length) console.log(`Learning candidates: ${result.learning.created.join(", ")}.`);
@@ -1335,7 +1406,7 @@ function executeRootRoute(route) {
1335
1406
  if (noun === "config" && subject === "migrate") return runMigration(routeArgs);
1336
1407
  if (noun === "config" && subject === "doctor") {
1337
1408
  const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
1338
- return doctor(target);
1409
+ return doctor(target, { strict: routeArgs.includes("--strict"), recover: routeArgs.includes("--recover") });
1339
1410
  }
1340
1411
  if (noun === "config" && subject === "update") {
1341
1412
  const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
@@ -2097,6 +2168,33 @@ function runMigration(parts) {
2097
2168
  return;
2098
2169
  }
2099
2170
  try {
2171
+ const ledgerManifest = path.join(process.cwd(), ".scrumrun", ".migration", "run-ledger-v1", "manifest.json");
2172
+ if (v2Project() && actions[0] !== "--rollback") {
2173
+ if (actions[0] === "--dry-run") {
2174
+ const plan = planRunLedgerMigration(process.cwd());
2175
+ console.log(reportRunLedgerMigration(plan).trimEnd());
2176
+ if (plan.status === "blocked") process.exitCode = 1;
2177
+ } else {
2178
+ const result = applyRunLedgerMigration(process.cwd());
2179
+ if (result.status === "current") {
2180
+ console.log("ScrumRun project is already migrated to method 2.0.0 and the current Run ledger; no files changed.");
2181
+ } else {
2182
+ refreshState(path.join(process.cwd(), ".scrumrun"));
2183
+ console.log(result.output.trimEnd());
2184
+ console.log("Rollback remains available with: scrumrun migrate --to 2 --rollback");
2185
+ }
2186
+ }
2187
+ return;
2188
+ }
2189
+ if (actions[0] === "--rollback" && fs.existsSync(ledgerManifest)) {
2190
+ const manifest = JSON.parse(fs.readFileSync(ledgerManifest, "utf8"));
2191
+ if (["applied", "prepared"].includes(manifest.status)) {
2192
+ const result = rollbackRunLedgerMigration(process.cwd());
2193
+ refreshState(path.join(process.cwd(), ".scrumrun"));
2194
+ console.log(`Rolled back ScrumRun Run ledger migration: ${result.status}.`);
2195
+ return;
2196
+ }
2197
+ }
2100
2198
  if (actions[0] === "--dry-run") {
2101
2199
  const result = dryRunMigration(process.cwd());
2102
2200
  console.log(result.output.trimEnd());
@@ -2125,12 +2223,22 @@ function runMigration(parts) {
2125
2223
  }
2126
2224
  }
2127
2225
 
2128
- function doctor(target = "all", { compatibility = false, strict = false } = {}) {
2226
+ function doctor(target = "all", { compatibility = false, strict = false, recover = false } = {}) {
2129
2227
  const home = os.homedir();
2130
2228
  const checks = [];
2131
2229
  const commands = compatibility ? [...COMMANDS, ...COMPATIBILITY_COMMANDS] : COMMANDS;
2132
2230
  const skillContent = fs.readFileSync(path.join(templates, "shared", "skills", "scrumrun", "SKILL.md"), "utf8");
2133
2231
 
2232
+ if (recover) {
2233
+ const scrumDir = path.join(process.cwd(), ".scrumrun");
2234
+ if (!fs.existsSync(scrumDir)) throw new Error("Cannot recover transactions outside a ScrumRun project.");
2235
+ const recovered = recoverPendingTransactions(scrumDir);
2236
+ console.log(recovered.length
2237
+ ? `recovered ${recovered.map((item) => `${item.id}:${item.action}`).join(", ")}`
2238
+ : "recovered no pending kernel transactions");
2239
+ refreshState(scrumDir);
2240
+ }
2241
+
2134
2242
  function commandContent(command) {
2135
2243
  return command === "sc" ? renderRootPrompt() : renderCompatibilityPrompt(command);
2136
2244
  }
@@ -2245,7 +2353,7 @@ if (!command || command === "--help" || command === "-h") {
2245
2353
  runMigration(args.slice(1));
2246
2354
  } else if (command === "doctor") {
2247
2355
  const target = ["all", "codex", "opencode", "claude"].includes(args[1]) ? args[1] : "all";
2248
- doctor(target, { compatibility: args.includes("--compat"), strict: args.includes("--strict") });
2356
+ doctor(target, { compatibility: args.includes("--compat"), strict: args.includes("--strict"), recover: args.includes("--recover") });
2249
2357
  } else if (command === "claude") {
2250
2358
  const sub = args[1];
2251
2359
  if (sub === "install" || sub === "update") {
package/docs/COMMANDS.md CHANGED
@@ -16,7 +16,7 @@ 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
20
  /sc plan challenge <question>
21
21
  ```
22
22
 
@@ -59,11 +59,13 @@ Creation options include `--title`, `--content`, repeated `--evidence`, repeated
59
59
  /sc config init --local|--shared|--lean|--no-agent-hint|--force
60
60
  /sc config update [all|codex|opencode|claude] [--migrate]
61
61
  /sc config migrate --to 2 --dry-run|--apply|--rollback
62
- /sc config doctor [all|codex|opencode|claude] [--strict]
62
+ /sc config doctor [all|codex|opencode|claude] [--strict] [--recover]
63
63
  /sc config uninstall --force
64
64
  /sc config help <topic>
65
65
  ```
66
66
 
67
67
  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
68
 
69
+ 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.
70
+
69
71
  Run `npx scrumrun@latest commands` for grammar rendered directly from the current manifest.
@@ -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.0 Local Release Scorecard
2
+
3
+ Date: 2026-07-22
4
+ Package: `2.1.0`
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.6 | 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; stale map display is refused. |
20
+ | Overall operation | 9.6 | Read-only intake, explicit approval, atomic Task/Run creation, Policy Engine ids, migration preflight/apply/rollback, package E2E, installed-asset doctor, Node 22/24/26 CI definition, and release budgets. |
21
+
22
+ Minimum local score: **9.6/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 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.0` 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 even though the same matrix is declared locally in `.github/workflows/ci.yml`.
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.