scrumrun 3.1.2 → 4.0.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 +14 -0
- package/CORE.md +13 -5
- package/README.md +29 -16
- package/SPEC.md +7 -3
- package/bin/scrumrun.js +29 -6
- package/docs/COMMANDS.md +5 -5
- package/docs/ENTITY-MODEL.md +17 -14
- package/docs/QUICKSTART.md +21 -19
- package/docs/SCHEMA.md +10 -10
- package/lib/commands/manifest.js +1 -1
- package/lib/commands/render.js +3 -2
- package/lib/runtime/briefing.js +29 -0
- package/lib/runtime/orchestrator.js +4 -4
- package/lib/runtime/policy-integrity.js +83 -0
- package/lib/v2/artifacts.js +9 -0
- package/lib/v2/conformance.js +18 -16
- package/lib/v2/paths.js +2 -1
- package/lib/v2/schema.js +7 -7
- package/package.json +2 -2
- package/scripts/generate-contract-docs.js +2 -2
- package/templates/project/.scrumrun/method.json +3 -0
- package/templates/project/AGENTS.md +10 -4
- package/templates/project-lean/AGENTS.md +6 -2
- package/templates/shared/skills/scrumrun/SKILL.md +11 -7
- package/types/index.d.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes follow Semantic Versioning.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 4.0.0 - 2026-08-31
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Simple Markdown model.** Core, Guardrails, Knowledge, Feature, Sprint, Task, Run, and Backlog are one local Markdown graph connected by stable IDs and relative links. Derived cache/index data is optional and never required for normal work.
|
|
12
|
+
- **Execution-first.** An approved Task continues through discover → implement → verify → fix → verify. Intermediate inventories, progress reports, and discovered in-scope gaps are internal work, never a valid stopping point or Follow-up.
|
|
13
|
+
- **Flexible Task bodies.** Tasks preserve arbitrary owner frontmatter and sections. `## Done when` and `## Completion` are the packaged minimal convention; project Guardrails may require extra sections only for affected Tasks.
|
|
14
|
+
- **Human status vocabulary.** `in_progress` is accepted as a Task status. A Markdown-first Task neither needs a Run nor must agree with historical Run state.
|
|
15
|
+
- **Runs are optional.** A Run may record a Task or a Sprint execution, but no Run, ledger, permit, or CLI state transition can block daily delivery.
|
|
16
|
+
|
|
17
|
+
### Safety
|
|
18
|
+
|
|
19
|
+
- **Sealed Markdown policy.** Fresh and refreshed projects pin Core and Guardrail fingerprints. Agents do not edit policy during product work; owner-reviewed policy changes use `scrumrun update --project --seal-policy`. Strict doctor/release audits detect policy drift at the edge, not during every edit.
|
|
20
|
+
|
|
7
21
|
## 3.1.2 - 2026-08-31
|
|
8
22
|
|
|
9
23
|
### Fixed
|
package/CORE.md
CHANGED
|
@@ -84,13 +84,21 @@ AGENTS.md
|
|
|
84
84
|
|
|
85
85
|
**Before querying project state, read `.scrumrun/method.json`.** Its `paths` block is the authoritative index of every canonical location in this project. Navigate by that index; if a path is not declared there, it is not canonical truth. Directory listing and grep are fallbacks — never the first step. A ScrumRun-aware agent must never search for `goals/`, `backlog.md`, `sprint.md`, or any legacy layout: those are absent by design once migration completes and are surfaced only through `.scrumrun/.migration-backup/`.
|
|
86
86
|
|
|
87
|
-
**Markdown is the normal runtime.** After explicit approval, work directly in source files and relevant `.scrumrun/` Markdown. A Task is free to be created, refined, started, completed, and handed off in Markdown; a Run is optional audit context, never a state machine that can prevent daily work. Update the Task's scope,
|
|
87
|
+
**Markdown is the normal runtime.** After explicit approval, work directly in source files and relevant `.scrumrun/` Markdown. A Task is free to be created, refined, started, completed, and handed off in Markdown; a Run is optional audit context, never a state machine that can prevent daily work. Update the Task's scope, Done when, Completion, and Follow-ups directly. Do not use `npx scrumrun@latest` or normal `scrumrun plan/run` commands during execution.
|
|
88
|
+
|
|
89
|
+
**Execution is continuous.** Once the owner approves a Task, implement it through its delivery contract before replying. Keep using tools and editing files through the full loop: discover → implement → verify → fix → verify. A progress report is allowed only when the owner asks for status, and it must be followed immediately by more execution in the same workflow; it is never an endpoint. Do not stop to give an inventory, a progress report, a decomposition, or a remaining-work list: those are internal steps, not a deliverable. A discovery of missing code, literals, tests, migrations, or configuration inside the approved contract is required work to implement now — it cannot become a report, a partial checker, a “next step”, or a Follow-up. Stop only for an owner decision, external access, an explicit Guardrail, a secret/security risk, destructive work without approval, or an unmet required delivery criterion. A Task's `## Done when` is short and observable; `## Follow-ups` contains only work outside that contract. Never move unfinished contract work to Follow-ups without explicit owner approval.
|
|
90
|
+
|
|
91
|
+
**Verification must cover the claim.** Never claim that a Task condition is verified merely because a narrower check passes. If the request is “no visible hardcoded literals”, a locale-key parity checker alone is insufficient: scan the relevant views for literals, replace every result in scope, then re-run both the scan and the build. The same rule applies to every delivery claim.
|
|
88
92
|
|
|
89
93
|
**The CLI is maintenance, not a work gate.** Use it for `init`, `update --project`, `migrate`, `repair`, `doctor`, reports, and release checks. Strict per-edit permits and ledger finalization remain available only when the owner asks for that audit level. Missing/invalid Runs, old status vocabulary, stale projections, and optional tests are warnings to reconcile in Markdown — never an automatic blocker.
|
|
90
94
|
|
|
91
|
-
**Block only on real constraints.** An agent must stop for an explicit active Guardrail, secret/security risk, destructive action without approval, or an unmet required
|
|
95
|
+
**Block only on real constraints.** An agent must stop for an explicit active Guardrail, secret/security risk, destructive action without approval, or an unmet required Done when item. It must not manufacture a failed/blocked Run because optional E2E coverage, an optional reviewer, or a non-required environment is unavailable; record meaningful gaps in `## Follow-ups` or a risk note.
|
|
96
|
+
|
|
97
|
+
**Normal operation never mutates Run state through the CLI.** Do not call `plan run --fail|--block|--retry|--finalize|--complete|--validate` or `plan task --start` in daily work. Those are owner-requested strict audit tools only. When a legacy Run already has the wrong administrative outcome, preserve it as history and correct the delivery record directly in the Task's Completion and Follow-ups.
|
|
98
|
+
|
|
99
|
+
**Policy is sealed at the edge.** `core.md` is the universal execution contract and `guardrails.md` is project policy. Never edit either while delivering product work. A policy change requires an explicit owner request; after review, `scrumrun update --project --seal-policy` records fresh fingerprints. Daily work stays Markdown-only; `doctor --strict` and release audit policy fingerprints, canonical guardrails, and code checks at the boundary.
|
|
92
100
|
|
|
93
|
-
**
|
|
101
|
+
**Markdown relations are the graph.** Every artifact has a stable ID. Use small frontmatter links such as `sprint: SPRINT-012`, `feature: FEAT-003`, and `depends_on: [TASK-151, DEC-008]`, plus a human-readable `## Related` section with relative links. Preserve all unknown frontmatter and all owner-defined sections. A Guardrail may require a Task section such as `## Migration Plan`, `## Rollback`, or `## Guardrail Evidence`; add it only to the affected Task and keep it until the rule is satisfied.
|
|
94
102
|
|
|
95
103
|
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.
|
|
96
104
|
|
|
@@ -176,7 +184,7 @@ understand → approve → work → validate required criteria → hand off
|
|
|
176
184
|
|
|
177
185
|
Rules:
|
|
178
186
|
|
|
179
|
-
- a Task carries
|
|
187
|
+
- a Task carries intended scope, a short `## Done when` contract, `## Completion`, and relevant `## Follow-ups`;
|
|
180
188
|
- validation matches risk and acceptance criteria; tests, reviews, and environments are gates only when explicitly required by the owner, the Task, or an active Guardrail;
|
|
181
189
|
- direct Markdown workflow never blocks on Run linkage, status syntax, stale generated views, or missing optional coverage. If an optional check matters, record it as a follow-up/risk instead of fabricating failure;
|
|
182
190
|
- configured reviews run only when a Guardrail requires one;
|
|
@@ -184,7 +192,7 @@ Rules:
|
|
|
184
192
|
- strict permits, workspace-drift checks, and append-only Guardrail obligations apply only to that optional strict audit path;
|
|
185
193
|
- learning proposes memory candidates when work reveals reusable context and never auto-confirms AI inference;
|
|
186
194
|
- complete a Sprint only when its included Tasks meet its real exit gate;
|
|
187
|
-
- do not mark work complete merely because time or token budget ended.
|
|
195
|
+
- do not mark work complete merely because time or token budget ended, and do not stop an approved Task merely to report intermediate progress.
|
|
188
196
|
|
|
189
197
|
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.
|
|
190
198
|
|
package/README.md
CHANGED
|
@@ -4,30 +4,33 @@
|
|
|
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
|
-
**Package:** `
|
|
7
|
+
**Package:** `4.0.0` · **Method target:** `2.0.0` · **Runtime:** Node.js `>=22.13.0` · **License:** MIT
|
|
8
8
|
|
|
9
9
|
**New here?** Read the [Quickstart](docs/QUICKSTART.md) — first Run in under 10 minutes, no `SPEC.md` reading required. Full docs map in [`docs/INDEX.md`](docs/INDEX.md).
|
|
10
10
|
|
|
11
11
|
## The model
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Core = how the agent works
|
|
15
|
+
Guardrails = rules that cannot be broken
|
|
16
|
+
Knowledge = what the project knows and why
|
|
17
|
+
Backlog = provisioned Tasks not yet started
|
|
18
|
+
Feature = why a larger initiative exists
|
|
19
|
+
Sprint = a feature, fix, or maintenance delivery grouping Tasks
|
|
20
|
+
Task = concrete work, independent or in a Sprint
|
|
21
|
+
Run = optional record of what happened while executing a Task/Sprint
|
|
19
22
|
```
|
|
20
23
|
|
|
21
24
|
```text
|
|
22
|
-
|
|
25
|
+
SPRINT-012 (type: fix)
|
|
23
26
|
└── TASK-018
|
|
24
|
-
├──
|
|
25
|
-
├──
|
|
26
|
-
├──
|
|
27
|
-
└──
|
|
27
|
+
├── feature → FEAT-003
|
|
28
|
+
├── depends_on → TASK-014, DEC-018
|
|
29
|
+
├── guardrails → GR-004
|
|
30
|
+
└── run → RUN-044 (optional)
|
|
28
31
|
```
|
|
29
32
|
|
|
30
|
-
A Task does not need a Sprint. A
|
|
33
|
+
A Task does not need a Sprint. A Sprint may be `feature`, `fix`, or `maintenance`. A Run is optional and may document a Task or Sprint. Backlog is simply Tasks with `status: backlog`.
|
|
31
34
|
|
|
32
35
|
See [`docs/SCHEMA.md`](docs/SCHEMA.md) for the generated executable contract and [`docs/ENTITY-MODEL.md`](docs/ENTITY-MODEL.md) for the conceptual guide.
|
|
33
36
|
|
|
@@ -79,15 +82,15 @@ RECEIVED → CONTEXTUALIZING → POLICY → RISK → CLASSIFICATION
|
|
|
79
82
|
|
|
80
83
|
The agent may assert the classification (`--type fix|task|feature|docs|discovery`) and attach a short technical preview (`--preview "…"`), rendered with color in the terminal before any Task exists. Nothing canonical is persisted before approval.
|
|
81
84
|
|
|
82
|
-
Explicit approval authorizes work. In
|
|
85
|
+
Explicit approval authorizes work. In 4.0, the daily runtime is the `.scrumrun/` folder: the agent creates or refines the relevant Task Markdown, works in code, and leaves a short handoff. Feature, Sprint, and Run are optional context, not prerequisites. A structured CLI audit path remains available only at maintenance/release edges.
|
|
83
86
|
|
|
84
87
|
```text
|
|
85
88
|
EXECUTING → VALIDATING → LEARNING → COMPLETED | FAILED | BLOCKED
|
|
86
89
|
```
|
|
87
90
|
|
|
88
|
-
Every Task carries `##
|
|
91
|
+
Every Task carries a short `## Done when` delivery contract. After approval, the agent works directly in code and Task Markdown until that contract is delivered: it keeps the full discover → implement → verify → fix → verify loop running. A report is allowed only when you ask for it and never ends execution. The normal close is a concise `## Completion`; `## Follow-ups` may only contain work outside the agreed contract. No CLI transition is required.
|
|
89
92
|
|
|
90
|
-
Guardrails still apply. An agent stops only for an explicit active Guardrail, a secret/security risk, destructive work without approval, or an unmet required
|
|
93
|
+
Guardrails still apply. An agent stops only for an explicit active Guardrail, a secret/security risk, destructive work without approval, or an unmet required delivery criterion. Tests, reviews, and environments are gates only when the owner, `Done when`, or a Guardrail explicitly requires them. Optional missing E2E coverage is a follow-up/risk, not a failed Task.
|
|
91
94
|
|
|
92
95
|
Use the CLI at the edges, where its safety is valuable:
|
|
93
96
|
|
|
@@ -99,6 +102,10 @@ scrumrun repair --recover-orphan-tasks --apply
|
|
|
99
102
|
scrumrun review release --run
|
|
100
103
|
```
|
|
101
104
|
|
|
105
|
+
`core.md` and `guardrails.md` are sealed Markdown policy. Agents never edit them during normal product work. An owner-requested policy change is reviewed and then sealed explicitly with `scrumrun update --project --seal-policy`; `doctor --strict` and release checks detect later policy drift.
|
|
106
|
+
|
|
107
|
+
Relations are plain, offline Markdown: stable IDs in frontmatter (`sprint: SPRINT-012`, `depends_on: [TASK-014, DEC-018]`) and relative links in `## Related`. Task Markdown is intentionally extensible: a Guardrail can require `## Migration Plan`, `## Rollback`, or `## Guardrail Evidence` only where relevant, and ScrumRun preserves every unknown section.
|
|
108
|
+
|
|
102
109
|
`update --project` refreshes packaged `core.md` and recognized generated `AGENTS.md` with a local byte-exact backup before replacing them. The CLI can still generate/validate Task, Feature, Sprint, and Run records when desired, but it must never become a routine blocker.
|
|
103
110
|
|
|
104
111
|
For extra protection, CLI Task start/retry and every Run state transition require `--strict`. This prevents accidental administrative failures in the normal Markdown-first workflow.
|
|
@@ -161,12 +168,18 @@ It is progressive disclosure: the briefing is enough for most work; the agent fo
|
|
|
161
168
|
|
|
162
169
|
## Migrating an ongoing v1 project
|
|
163
170
|
|
|
164
|
-
Update the client integrations. For an existing project, refresh the
|
|
171
|
+
Update the client integrations. For an existing project, refresh the execution-first guidance explicitly:
|
|
165
172
|
|
|
166
173
|
```bash
|
|
167
174
|
scrumrun update --project
|
|
168
175
|
```
|
|
169
176
|
|
|
177
|
+
If you intentionally changed project Guardrails after owner review, seal the reviewed Markdown policy once:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
scrumrun update --project --seal-policy
|
|
181
|
+
```
|
|
182
|
+
|
|
170
183
|
This shows the source inventory, proposed mappings, and blockers without changing project data. Apply only the verified plan with:
|
|
171
184
|
|
|
172
185
|
```bash
|
package/SPEC.md
CHANGED
|
@@ -83,6 +83,8 @@ method: 2.0.0
|
|
|
83
83
|
|
|
84
84
|
IDs, filenames, kind, status, real ISO dates, and method version must agree. Unknown fields and authored prose are preserved. Duplicate fields, malformed frontmatter, unsafe paths, and symlinked canonical paths are invalid.
|
|
85
85
|
|
|
86
|
+
Relationship lists are valid local frontmatter, for example `depends_on: [TASK-014, DEC-018]` and `guardrails: [GR-004]`. Human-readable `## Related` links use relative Markdown paths. These local IDs and links are the canonical graph; generated indexes only accelerate retrieval.
|
|
87
|
+
|
|
86
88
|
A Task may carry an optional `assignee` scalar recording the agent identity (`SCRUMRUN_AGENT` or `config.md` `Agent Identity`) that owns the work. It is descriptive metadata, never a competing authority: it does not change status transitions or block conformance.
|
|
87
89
|
|
|
88
90
|
### 3.2 Stable identifiers
|
|
@@ -200,13 +202,15 @@ RECEIVED
|
|
|
200
202
|
|
|
201
203
|
Everything through `AWAITING_APPROVAL` is read-only. It may exist in process memory or ignored cache only. A valid approval token binds the normalized request, policy result, classification, risk, issuance time, canonical context fingerprint, and complete workspace fingerprint. Canonical or source drift after planning invalidates approval.
|
|
202
204
|
|
|
203
|
-
Approval authorizes work after the read-only planning pass. In Markdown-first mode, a Task and optional Run may be created or updated directly; their absence or imperfect administrative metadata never prevents approved work. The CLI's atomic Task/Run creation remains an optional strict/audit path. Tests, reviews, and environments are completion gates only when explicitly required by the owner, the Task's
|
|
205
|
+
Approval authorizes work after the read-only planning pass. In Markdown-first mode, a Task and optional Run may be created or updated directly; their absence or imperfect administrative metadata never prevents approved work. The CLI's atomic Task/Run creation remains an optional strict/audit path. Tests, reviews, and environments are completion gates only when explicitly required by the owner, the Task's `## Done when` contract, or an active Guardrail; missing optional coverage is a documented follow-up/risk, not a failure by itself.
|
|
206
|
+
|
|
207
|
+
An approved Task executes continuously to its delivery contract. An agent must not end a work turn merely to provide inventory, decomposition, progress, or a list of remaining implementation work. Those are internal execution steps. It may stop only for an owner decision, external access, an active Guardrail, secret/security risk, destructive work without approval, or an unmet required contract item. `## Follow-ups` may contain only work outside `## Done when`; moving required work there requires explicit owner approval.
|
|
204
208
|
|
|
205
209
|
When a structured Run is explicitly chosen, it binds the exact Guardrail-policy fingerprint and workspace baseline. Strict audit then verifies the complete delta: policy freshness, read-only boundaries, symlink safety, scannability, newly introduced secret-like content, and evidence for every Guardrail. Strict teams may opt into short-lived, path-scoped permits and per-edit recording. This audit path is never a prerequisite for ordinary Markdown-first execution.
|
|
206
210
|
|
|
207
|
-
The agent may assert the classification explicitly (`--type fix|task|feature|docs|discovery`), overriding keyword inference with validation and a stable reason. It may attach a short technical preview (`--preview`), rendered in the terminal, bound into the approval token, and stored as `## Preview` on the approved Task. A Task declares
|
|
211
|
+
The agent may assert the classification explicitly (`--type fix|task|feature|docs|discovery`), overriding keyword inference with validation and a stable reason. It may attach a short technical preview (`--preview`), rendered in the terminal, bound into the approval token, and stored as `## Preview` on the approved Task. A Task normally declares a concise `## Done when` contract before execution, but owner-defined Markdown sections remain valid; completion is measured against the agreed delivery contract, never against elapsed time or token budget.
|
|
208
212
|
|
|
209
|
-
When a structured Run is used, CLI transitions synchronously update the linked Task and append one event to its ledger. This is an optional audit path, not daily operational authority. Markdown-first completion records the
|
|
213
|
+
When a structured Run is used, CLI transitions synchronously update the linked Task and append one event to its ledger. This is an optional audit path, not daily operational authority. Markdown-first completion records the Done when contract, Completion, validation evidence, and Follow-ups directly on the Task. Multi-file CLI mutations use a durable local transaction journal; `doctor --recover` and `repair` remain explicit maintenance operations. Entering learning may extract candidates, but no administrative artifact failure blocks approved work.
|
|
210
214
|
|
|
211
215
|
Backlog is a queue view of intentionally parked Tasks, ordered oldest-first by id. CLI `--next`/`--start` helpers may create a structured Run when wanted, but an explicit owner approval is the only daily-work start gate.
|
|
212
216
|
|
package/bin/scrumrun.js
CHANGED
|
@@ -39,6 +39,7 @@ const { indexPath, indexStatus, mapStatus, queryIndex, rebuildIndex, writeMap }
|
|
|
39
39
|
const { auditProject } = require(path.join(root, "lib", "v2", "conformance"));
|
|
40
40
|
const { recoverPendingTransactions, previewPendingRecovery } = require(path.join(root, "lib", "v2", "transaction"));
|
|
41
41
|
const { containsSecret } = require(path.join(root, "lib", "security", "secrets"));
|
|
42
|
+
const { sealPolicyIntegrity } = require(path.join(root, "lib", "runtime", "policy-integrity"));
|
|
42
43
|
|
|
43
44
|
const COMMANDS = ["sc"];
|
|
44
45
|
const COMPATIBILITY_COMMANDS = Object.keys(COMMAND_ALIASES);
|
|
@@ -58,7 +59,7 @@ Usage:
|
|
|
58
59
|
scrumrun <noun> <subject> <action> [args]
|
|
59
60
|
scrumrun sc <noun> <subject> <action> [args] # compatibility alias
|
|
60
61
|
scrumrun install [all|codex|opencode|claude] [--force]
|
|
61
|
-
scrumrun update [all|codex|opencode|claude] [--project] [--migrate] [--verbose]
|
|
62
|
+
scrumrun update [all|codex|opencode|claude] [--project] [--seal-policy] [--migrate] [--verbose]
|
|
62
63
|
scrumrun init [--local|--shared] [--lean] [--no-agent-hint] [--force]
|
|
63
64
|
scrumrun status
|
|
64
65
|
scrumrun core [--path|--prompt]
|
|
@@ -380,10 +381,13 @@ function refreshProjectGuidance(cwd = process.cwd()) {
|
|
|
380
381
|
} else {
|
|
381
382
|
results.push({ status: "skipped", dest: `${agentsFile} (not recognized as ScrumRun-generated)` });
|
|
382
383
|
}
|
|
384
|
+
const marker = path.join(cwd, ".scrumrun", "method.json");
|
|
385
|
+
const sealed = writeFile(marker, sealPolicyIntegrity(path.join(cwd, ".scrumrun")), { backup: true });
|
|
386
|
+
results.push({ status: sealed.changed ? "updated" : "skipped", dest: marker, backup: sealed.backup });
|
|
383
387
|
return results;
|
|
384
388
|
}
|
|
385
389
|
|
|
386
|
-
function updateInstallation(target, { migrate = false, project = false, verbose = false } = {}) {
|
|
390
|
+
function updateInstallation(target, { migrate = false, project = false, sealPolicy = false, verbose = false } = {}) {
|
|
387
391
|
installVerbose = verbose;
|
|
388
392
|
installSummary.cleaned = 0;
|
|
389
393
|
installSummary.written = 0;
|
|
@@ -391,7 +395,14 @@ function updateInstallation(target, { migrate = false, project = false, verbose
|
|
|
391
395
|
installSummary.targets.length = 0;
|
|
392
396
|
const migration = migrate ? migrationPreflightOnUpdate({ apply: true }) : { status: "skipped" };
|
|
393
397
|
install(target, true, { compatibility: true });
|
|
398
|
+
if (sealPolicy && !project) throw new Error("`--seal-policy` requires `--project` so the owner-reviewed project policy is explicit.");
|
|
394
399
|
const projectResults = project ? refreshProjectGuidance() : [];
|
|
400
|
+
if (sealPolicy && project) {
|
|
401
|
+
const scrumDir = path.join(process.cwd(), ".scrumrun");
|
|
402
|
+
const marker = path.join(scrumDir, "method.json");
|
|
403
|
+
const sealed = writeFile(marker, sealPolicyIntegrity(scrumDir, { includeGuardrails: true }), { backup: true });
|
|
404
|
+
projectResults.push({ status: sealed.changed ? "updated" : "skipped", dest: marker, backup: sealed.backup });
|
|
405
|
+
}
|
|
395
406
|
if (migrate && v2Project()) {
|
|
396
407
|
try {
|
|
397
408
|
refreshState(path.join(process.cwd(), ".scrumrun"));
|
|
@@ -1706,7 +1717,12 @@ function executeRootRoute(route) {
|
|
|
1706
1717
|
}
|
|
1707
1718
|
if (noun === "config" && subject === "update") {
|
|
1708
1719
|
const target = ["all", "codex", "opencode", "claude"].includes(routeArgs[0]) ? routeArgs[0] : "all";
|
|
1709
|
-
return updateInstallation(target, {
|
|
1720
|
+
return updateInstallation(target, {
|
|
1721
|
+
migrate: routeArgs.includes("--migrate"),
|
|
1722
|
+
project: routeArgs.includes("--project"),
|
|
1723
|
+
sealPolicy: routeArgs.includes("--seal-policy"),
|
|
1724
|
+
verbose: routeArgs.includes("--verbose")
|
|
1725
|
+
});
|
|
1710
1726
|
}
|
|
1711
1727
|
if (noun === "config" && subject === "init") {
|
|
1712
1728
|
const localMode = routeArgs.includes("--local");
|
|
@@ -2322,6 +2338,8 @@ function promptCommand(parts) {
|
|
|
2322
2338
|
|
|
2323
2339
|
function initProject({ force, mode, agentHint, lean }) {
|
|
2324
2340
|
const cwd = process.cwd();
|
|
2341
|
+
const marker = path.join(cwd, ".scrumrun", "method.json");
|
|
2342
|
+
const markerExisted = fs.existsSync(marker);
|
|
2325
2343
|
const vars = {
|
|
2326
2344
|
PROJECT_NAME: path.basename(cwd),
|
|
2327
2345
|
DATE: new Date().toISOString().slice(0, 10)
|
|
@@ -2332,6 +2350,10 @@ function initProject({ force, mode, agentHint, lean }) {
|
|
|
2332
2350
|
|
|
2333
2351
|
results.push(...copyDir(path.join(projectTemplate, ".scrumrun"), path.join(cwd, ".scrumrun"), { force, vars }));
|
|
2334
2352
|
results.push(copyFile(path.join(root, "CORE.md"), path.join(cwd, ".scrumrun", "core.md"), { force, vars }));
|
|
2353
|
+
if (force || !markerExisted) {
|
|
2354
|
+
const sealed = writeFile(marker, sealPolicyIntegrity(path.join(cwd, ".scrumrun"), { includeGuardrails: true }), { backup: false });
|
|
2355
|
+
results.push({ status: sealed.changed ? "written" : "skipped", dest: marker, backup: sealed.backup });
|
|
2356
|
+
}
|
|
2335
2357
|
results.push(ensureProjectIgnore(cwd));
|
|
2336
2358
|
|
|
2337
2359
|
if (mode === "shared" || agentHint) {
|
|
@@ -2611,8 +2633,9 @@ function doctor(target = "all", { compatibility = false, strict = false, recover
|
|
|
2611
2633
|
console.log(`miss ScrumRun project audit: ${scrumDir}`);
|
|
2612
2634
|
} else {
|
|
2613
2635
|
const audit = auditProject(process.cwd());
|
|
2614
|
-
|
|
2615
|
-
|
|
2636
|
+
const blocking = audit.findings.filter((item) => ["critical", "high"].includes(item.severity));
|
|
2637
|
+
ok = ok && audit.passed && blocking.length === 0;
|
|
2638
|
+
console.log(`${audit.passed && blocking.length === 0 ? "ok " : "fail"} ScrumRun project audit: ${audit.findings.length} finding(s)`);
|
|
2616
2639
|
for (const item of audit.findings) console.log(` ${item.severity} ${item.code}: ${item.message}`);
|
|
2617
2640
|
}
|
|
2618
2641
|
}
|
|
@@ -2633,7 +2656,7 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
2633
2656
|
console.log(`ScrumRun ${version}`);
|
|
2634
2657
|
} else if (command === "install" || command === "update") {
|
|
2635
2658
|
const target = ["all", "codex", "opencode", "claude"].includes(args[1]) ? args[1] : "all";
|
|
2636
|
-
if (command === "update") updateInstallation(target, { migrate: args.includes("--migrate"), project: args.includes("--project"), verbose: args.includes("--verbose") });
|
|
2659
|
+
if (command === "update") updateInstallation(target, { migrate: args.includes("--migrate"), project: args.includes("--project"), sealPolicy: args.includes("--seal-policy"), verbose: args.includes("--verbose") });
|
|
2637
2660
|
else install(target, true, { compatibility: false });
|
|
2638
2661
|
} else if (command === "sc") {
|
|
2639
2662
|
runRoot(args.slice(1));
|
package/docs/COMMANDS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ScrumRun
|
|
1
|
+
# ScrumRun 4.0 Command Reference
|
|
2
2
|
|
|
3
3
|
The canonical grammar is:
|
|
4
4
|
|
|
@@ -27,13 +27,13 @@ scrumrun plan run --satisfy-guardrail RUN-NNN --guardrail GR-NNN [typed evidence
|
|
|
27
27
|
scrumrun plan challenge <question>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Normal execution is Markdown-first: after approval, work in code and the relevant Task Markdown, then record
|
|
30
|
+
Normal execution is Markdown-first: after approval, work in code and the relevant Task Markdown through discover → implement → verify → fix → verify, then record `## Completion` and any genuinely out-of-scope Follow-ups directly. A Run/`--finalize` checkpoint is optional strict audit, never a prerequisite. Mutation permits are available only for explicitly requested strict mode.
|
|
31
31
|
|
|
32
32
|
The CLI refuses Task start/retry and all Run state changes unless `--strict` is present. This prevents an agent from accidentally manufacturing a failed/blocked retry during normal work; `--strict` is for an owner-requested audit only.
|
|
33
33
|
|
|
34
34
|
`--amend` is an optional structured helper. The Markdown-first workflow may adjust Task/Feature/Sprint content directly, preserving a useful handoff. Use the CLI when atomic relation synchronization or machine audit is valuable; do not let status vocabulary or missing relations stop approved work.
|
|
35
35
|
|
|
36
|
-
Every new Task starts with
|
|
36
|
+
Every new Task starts with `## Done when` and `## Validation Scope`: only checks explicitly required by the owner, Done when contract, or an active Guardrail block completion. A discovered in-scope gap remains work to implement, not a status report or Follow-up. Missing optional E2E, integration, or review coverage belongs in a follow-up/risk note; it must not be used to mark the Run failed.
|
|
37
37
|
|
|
38
38
|
## What can be changed
|
|
39
39
|
|
|
@@ -82,14 +82,14 @@ scrumrun review release --run
|
|
|
82
82
|
```text
|
|
83
83
|
scrumrun config project --show|--language|--interaction|--approval|--quick-tasks
|
|
84
84
|
scrumrun config init --local|--shared|--lean|--no-agent-hint|--force
|
|
85
|
-
scrumrun config update [all|codex|opencode|claude] [--project] [--migrate]
|
|
85
|
+
scrumrun config update [all|codex|opencode|claude] [--project] [--seal-policy] [--migrate]
|
|
86
86
|
scrumrun config migrate --to 2 --dry-run|--apply|--rollback
|
|
87
87
|
scrumrun config doctor [all|codex|opencode|claude] [--strict] [--recover]
|
|
88
88
|
scrumrun config uninstall --force
|
|
89
89
|
scrumrun config help <topic>
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
Top-level CLI aliases (`init`, `update`, `migrate`, `doctor`, `uninstall`, `status`) remain available for shell automation. `update --project` refreshes the packaged
|
|
92
|
+
Top-level CLI aliases (`init`, `update`, `migrate`, `doctor`, `uninstall`, `status`) remain available for shell automation. `update --project` refreshes the packaged execution-first Core and recognized generated agent instructions with local backup. `update --project --seal-policy` is the owner-reviewed maintenance action that pins fresh Core/Guardrail fingerprints. Ordinary update does not inspect migrations, while `--migrate` explicitly does so and applies the verified plan.
|
|
93
93
|
|
|
94
94
|
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.
|
|
95
95
|
|
package/docs/ENTITY-MODEL.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ScrumRun
|
|
1
|
+
# ScrumRun 4.0 Markdown Model
|
|
2
2
|
|
|
3
3
|
This document explains the model. Exact ids, directories, initial states, transitions, structural cardinalities, truth ownership, and authority boundaries are generated from `lib/v2/schema.js` into [`SCHEMA.md`](SCHEMA.md). Do not maintain another hand-written schema table here.
|
|
4
4
|
|
|
@@ -6,32 +6,35 @@ This document explains the model. Exact ids, directories, initial states, transi
|
|
|
6
6
|
|
|
7
7
|
| Entity | Question | Lifetime |
|
|
8
8
|
|---|---|---|
|
|
9
|
+
| Core | How must the agent work? | Stable, sealed policy |
|
|
10
|
+
| Guardrails | What may never be broken here? | Stable, sealed project policy |
|
|
11
|
+
| Knowledge | What do we know, and why? | Reviewed until stale/deprecated/invalidated |
|
|
12
|
+
| Backlog | What is provisioned but not started? | Tasks with `status: backlog` |
|
|
9
13
|
| Feature | Why does this initiative matter? | Long-lived |
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
| Run |
|
|
13
|
-
| Memory | What do we know, and why? | Reviewed until stale/deprecated/invalidated |
|
|
14
|
+
| Sprint | Which Tasks ship together? | Feature, fix, or maintenance batch |
|
|
15
|
+
| Task | What concrete outcome must be delivered? | Until outcome/closure |
|
|
16
|
+
| Run | What happened while executing a Task or Sprint? | Optional audit/handoff record |
|
|
14
17
|
|
|
15
18
|
```text
|
|
16
|
-
|
|
19
|
+
SPRINT-012 (type: fix)
|
|
17
20
|
└── TASK-018
|
|
18
|
-
├──
|
|
19
|
-
├──
|
|
20
|
-
├──
|
|
21
|
-
└──
|
|
21
|
+
├── feature → FEAT-003
|
|
22
|
+
├── depends_on → TASK-014, DEC-018
|
|
23
|
+
├── guardrails → GR-004
|
|
24
|
+
└── run → RUN-044 (optional)
|
|
22
25
|
```
|
|
23
26
|
|
|
24
|
-
Task is
|
|
27
|
+
Task is the executable unit. A Task may be independent of a Sprint. A Sprint groups Tasks and may be `feature`, `fix`, or `maintenance`. Runs are optional and never substitute for a Task's direct Markdown handoff.
|
|
25
28
|
|
|
26
29
|
## Canonical artifacts
|
|
27
30
|
|
|
28
|
-
All artifacts carry `id`, `kind`, `status`, `created`, `updated`, and `method`. Relations
|
|
31
|
+
All artifacts carry `id`, `kind`, `status`, `created`, `updated`, and `method`. Relations use simple frontmatter and relative links: `sprint: SPRINT-012`, `feature: FEAT-003`, `depends_on: [TASK-014, DEC-018]`, and `## Related`. Unknown fields and sections are preserved. The generated [`SCHEMA.md`](SCHEMA.md) is the authoritative inventory.
|
|
29
32
|
|
|
30
33
|
## Operational flow
|
|
31
34
|
|
|
32
|
-
Approval creates
|
|
35
|
+
Approval creates or refines a Task. An optional Run can record strict audit or handoff history, but no Run, ledger, permit, or status synchronization blocks normal Markdown work. An approved Task continues through discover → implement → verify → fix → verify until its `## Done when` contract is delivered.
|
|
33
36
|
|
|
34
|
-
Feature and Sprint provide context/grouping and do not own execution history. Review attaches evidence.
|
|
37
|
+
Feature and Sprint provide context/grouping and do not own execution history. Review attaches evidence. Knowledge, Decisions, and Insights explain constraints across all of them. Core and Guardrails are sealed Markdown policy: only explicit owner-reviewed maintenance may change them.
|
|
35
38
|
|
|
36
39
|
## Generated projections
|
|
37
40
|
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# Quickstart
|
|
2
2
|
|
|
3
|
-
Get from "never heard of it" to your first
|
|
3
|
+
Get from "never heard of it" to your first delivered Task in under 10 minutes.
|
|
4
4
|
No `SPEC.md` reading required.
|
|
5
5
|
|
|
6
6
|
## The three-minute mental model
|
|
7
7
|
|
|
8
8
|
ScrumRun gives an AI coding agent a small vocabulary and a project memory.
|
|
9
9
|
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
10
|
+
- **Core** — how the agent works; read first and never edited during product work.
|
|
11
|
+
- **Guardrails** — rules that cannot be broken.
|
|
12
|
+
- **Knowledge** — facts, decisions, insights, and dossiers about the project.
|
|
13
|
+
- **Task** — one concrete delivery, independent or inside a Sprint.
|
|
14
|
+
- **Sprint** — a feature, fix, or maintenance batch of Tasks. Optional.
|
|
15
|
+
- **Feature** — a bigger initiative that groups delivery context.
|
|
16
|
+
- **Run** — optional audit/handoff Markdown for a Task or Sprint.
|
|
16
17
|
|
|
17
18
|
Everything lives as Markdown under `.scrumrun/`. Any Markdown-capable agent
|
|
18
19
|
can follow it.
|
|
@@ -64,24 +65,25 @@ command (`| less`, `> intake.txt`) or set `NO_COLOR=1` to get the plain
|
|
|
64
65
|
Markdown summary instead; add `--json` for a fully structured payload
|
|
65
66
|
you can feed to CI or a downstream tool.
|
|
66
67
|
|
|
67
|
-
Only then does
|
|
68
|
+
Only then does the Task become approved. The agent executes it directly in code and Markdown; a Run is optional.
|
|
68
69
|
|
|
69
|
-
##
|
|
70
|
+
## Delivering the Task
|
|
70
71
|
|
|
71
|
-
The agent executes inside the approved scope
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
The agent executes inside the approved scope until `## Done when` is true.
|
|
73
|
+
It does not stop after an inventory, partial fix, or progress report. Any
|
|
74
|
+
missing implementation discovered in scope is work to do now, not a follow-up.
|
|
74
75
|
|
|
75
76
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
TASK-001
|
|
78
|
+
→ inspect the duplicate-submit path
|
|
79
|
+
→ implement the fix
|
|
80
|
+
→ run the relevant checks
|
|
81
|
+
→ correct failures
|
|
82
|
+
→ write ## Completion and status: completed
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
A
|
|
84
|
-
|
|
85
|
+
A Run may be added when a strict audit or detailed handoff is useful. It never
|
|
86
|
+
needs to exist for normal work to start or finish.
|
|
85
87
|
|
|
86
88
|
You can render a Run's ledger as a human timeline instead of reading the
|
|
87
89
|
raw JSON:
|
package/docs/SCHEMA.md
CHANGED
|
@@ -20,9 +20,9 @@ The boundaries above are deliberately different: SPEC owns meanings, the executa
|
|
|
20
20
|
| Kind | Stable ID | Canonical directory | Allowed initial status | Truth owned by this artifact |
|
|
21
21
|
|---|---|---|---|---|
|
|
22
22
|
| feature | `FEAT-NNN` | `features/` | `backlog`, `proposed` | initiative purpose, scope, dependencies, and lifecycle |
|
|
23
|
-
| task | `TASK-NNN` | `tasks/` | `backlog`, `proposed`, `running` | scope,
|
|
24
|
-
| sprint | `SPRINT-NNN` | `sprints/` | `proposed` | timebox or delivery-batch membership;
|
|
25
|
-
| run | `RUN-NNN` | `runs/` | `executing` |
|
|
23
|
+
| task | `TASK-NNN` | `tasks/` | `backlog`, `proposed`, `in_progress`, `running` | scope, owner-defined sections, links, and status |
|
|
24
|
+
| sprint | `SPRINT-NNN` | `sprints/` | `proposed` | timebox or delivery-batch membership; may be feature, fix, or maintenance |
|
|
25
|
+
| run | `RUN-NNN` | `runs/` | `executing` | optional human-readable execution record and outcome |
|
|
26
26
|
| review | `REV-NNN` | `reviews/` | `proposed` | scoped findings, checks, evidence, and verdict |
|
|
27
27
|
| knowledge | `K-NNN` | `memory/knowledge/` | `candidate` | approved evidence-backed fact and validity |
|
|
28
28
|
| decision | `DEC-NNN` | `memory/decisions/` | `open` | decision, rationale, validity, and lifecycle |
|
|
@@ -37,9 +37,9 @@ Every artifact also requires `id`, `kind`, `status`, `created`, `updated`, and `
|
|
|
37
37
|
|---|---|---|---|
|
|
38
38
|
| `feature` | feature (`FEAT-NNN`) | 0..1 | long-lived initiative containing the artifact |
|
|
39
39
|
| `sprint` | sprint (`SPRINT-NNN`) | 0..1 | optional delivery batch containing a Task or Run |
|
|
40
|
-
| `task` | task (`TASK-NNN`) |
|
|
40
|
+
| `task` | task (`TASK-NNN`) | 0..1 | atomic work executed or reviewed by the artifact |
|
|
41
41
|
|
|
42
|
-
Task is the atomic unit. A Task may have zero or one Sprint. A
|
|
42
|
+
Task is the atomic unit. A Task may have zero or one Sprint. A Run is optional Markdown history and may reference either a Task or a Sprint; it never controls Task status in Markdown-first work. Sprint membership is authoritative on `Task.sprint`; a Sprint's `## Tasks` list is a human-readable projection that must agree with it. Additional owner-defined relations such as `depends_on: [TASK-014, DEC-018]` are preserved as local Markdown graph data.
|
|
43
43
|
|
|
44
44
|
## Scalar constraints
|
|
45
45
|
|
|
@@ -58,14 +58,14 @@ Newly authored Runs use `ledger: 1`. Their `## Events` section contains append-o
|
|
|
58
58
|
|
|
59
59
|
Every event requires `schema`, `id`, contiguous `sequence`, RFC3339 `occurred_at`, `timestamp_precision`, `actor`, `from`, `to`, `reason`, and structured `evidence`. Event types are `transition`, `snapshot`, `guardrail`, `mutation`. Evidence kinds are `approval`, `command`, `test`, `file`, `review`, `decision`, `insight`, `risk`, `note`, `migration`, `legacy`, `guardrail`, `mutation`.
|
|
60
60
|
|
|
61
|
-
A native ledger begins with `created → executing`; an evidenced migration `snapshot` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated. Run owns
|
|
61
|
+
A native ledger begins with `created → executing`; an evidenced migration `snapshot` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated when a strict audit Run is used. Run owns optional event history; Task Markdown owns the delivered scope and status in normal work.
|
|
62
62
|
|
|
63
63
|
## Truth questions
|
|
64
64
|
|
|
65
65
|
- **feature:** Why does this initiative exist?
|
|
66
|
-
- **task:** What
|
|
67
|
-
- **sprint:**
|
|
68
|
-
- **run:**
|
|
66
|
+
- **task:** What concrete outcome is intended?
|
|
67
|
+
- **sprint:** Which Tasks are grouped for this delivery?
|
|
68
|
+
- **run:** What happened while executing a Task or Sprint?
|
|
69
69
|
- **review:** What independent validation was performed?
|
|
70
70
|
- **knowledge:** What verified project fact is reusable?
|
|
71
71
|
- **decision:** What normative choice constrains future work?
|
|
@@ -77,7 +77,7 @@ A native ledger begins with `created → executing`; an evidenced migration `sna
|
|
|
77
77
|
| Kind | Declared transitions |
|
|
78
78
|
|---|---|
|
|
79
79
|
| feature | `backlog` → `proposed`, `active`, `cancelled`<br>`proposed` → `active`, `cancelled`<br>`active` → `paused`, `completed`, `cancelled`<br>`completed` → terminal<br>`paused` → `active`, `cancelled`<br>`cancelled` → terminal |
|
|
80
|
-
| task | `backlog` → `proposed`, `running`, `cancelled`<br>`proposed` → `running`, `cancelled`<br>`running` → `validating`, `failed`, `blocked`, `cancelled`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `running`, `cancelled`<br>`completed` → terminal<br>`failed` → `running`, `cancelled`<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
80
|
+
| task | `backlog` → `proposed`, `in_progress`, `running`, `cancelled`<br>`proposed` → `in_progress`, `running`, `cancelled`<br>`in_progress` → `completed`, `failed`, `blocked`, `cancelled`<br>`running` → `validating`, `completed`, `failed`, `blocked`, `cancelled`<br>`validating` → `learning`, `completed`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `in_progress`, `running`, `cancelled`<br>`completed` → terminal<br>`failed` → `in_progress`, `running`, `cancelled`<br>`blocked` → `in_progress`, `running`, `cancelled`<br>`cancelled` → terminal |
|
|
81
81
|
| sprint | `proposed` → `running`, `cancelled`<br>`running` → `partial`, `completed`, `blocked`, `cancelled`<br>`partial` → `running`, `completed`, `cancelled`<br>`completed` → terminal<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
82
82
|
| run | `executing` → `validating`, `failed`, `blocked`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `executing`, `failed`, `blocked`<br>`completed` → terminal<br>`failed` → terminal<br>`blocked` → `executing`, `failed` |
|
|
83
83
|
| review | `proposed` → `running`<br>`running` → `passed`, `failed`<br>`passed` → `archived`<br>`failed` → `running`, `archived`<br>`archived` → terminal |
|
package/lib/commands/manifest.js
CHANGED
|
@@ -65,7 +65,7 @@ const nouns = Object.freeze({
|
|
|
65
65
|
subjects: {
|
|
66
66
|
project: ["--show", "--language", "--interaction", "--approval", "--quick-tasks"],
|
|
67
67
|
init: ["--local", "--shared", "--lean", "--no-agent-hint", "--force"],
|
|
68
|
-
update: ["all [--migrate]", "codex [--migrate]", "opencode [--migrate]", "claude [--migrate]"],
|
|
68
|
+
update: ["all [--project] [--seal-policy] [--migrate]", "codex [--project] [--seal-policy] [--migrate]", "opencode [--project] [--seal-policy] [--migrate]", "claude [--project] [--seal-policy] [--migrate]"],
|
|
69
69
|
migrate: ["--to 2 --dry-run", "--to 2 --apply", "--to 2 --rollback"],
|
|
70
70
|
doctor: ["all [--strict] [--recover] [--dry-run]", "codex [--strict] [--recover] [--dry-run]", "opencode [--strict] [--recover] [--dry-run]", "claude [--strict] [--recover] [--dry-run]"],
|
|
71
71
|
uninstall: ["--force"],
|
package/lib/commands/render.js
CHANGED
|
@@ -29,15 +29,16 @@ ${grammarLines().join("\n")}
|
|
|
29
29
|
|
|
30
30
|
- Natural-language product work begins with a read-only understanding pass; do not invoke \`plan intake\` merely to satisfy procedure.
|
|
31
31
|
- Intake, contextualization, policy, risk, classification, and planning are read-only until explicit approval.
|
|
32
|
-
- After approval, work directly in source files and relevant \`.scrumrun/\` Markdown.
|
|
32
|
+
- After approval, work directly in source files and relevant \`.scrumrun/\` Markdown until the approved Task is delivered. Keep executing discover → implement → verify → fix → verify; a progress report is allowed only when the owner asks for it and never ends the workflow. Do not answer with an inventory, partial progress report, decomposition, or remaining-work list; those are internal steps. A gap discovered in scope is work to implement now, not a “next step”. Feature/Sprint/Run are useful only when they add context.
|
|
33
33
|
- A Run is optional audit/handoff context, never a prerequisite for starting, amending, or completing a Task. Do not block on a missing Run, a legacy status, or stale generated state.
|
|
34
34
|
- The CLI can inspect/repair/report structured artifacts, but it does not own the daily workflow.
|
|
35
35
|
- \`guardrails.md\` is canonical project policy; \`golden-rules.md\` is v1 compatibility only.
|
|
36
36
|
- Evaluate active Guardrails as \`passed\`, \`blocked\`, or \`deferred\`; cite exact \`GR-NNN\` ids and keep deferred execution gates visible.
|
|
37
|
-
- Block only for an explicit Guardrail, secret/security risk, destructive action without approval, or an unmet required
|
|
37
|
+
- Block only for an explicit Guardrail, secret/security risk, destructive action without approval, or an unmet required delivery criterion. \`Follow-ups\` may only contain work outside the approved \`Done when\` contract; optional unrun E2E/review coverage is a follow-up/risk, not a failed Run.
|
|
38
38
|
- Use the CLI only for \`init\`, \`update --project\`, \`migrate\`, \`repair\`, \`doctor\`, reports, or release checks. Do not invoke \`npx scrumrun@latest\` during execution.
|
|
39
39
|
- Never invoke \`plan run --fail|--block|--retry|--finalize|--complete|--validate\` or \`plan task --start\` during normal work. These optional strict-audit commands must not decide a Task outcome.
|
|
40
40
|
- Strict per-path Mutation Gateway permits and ledger finalization remain available only when the owner explicitly requests strict execution.
|
|
41
|
+
- Never edit \`core.md\` or \`guardrails.md\` during product work. They are sealed policy; owner-requested policy changes are reviewed and sealed at the maintenance edge.
|
|
41
42
|
- Knowledge/Decision/Insight records require evidence; AI-proposed Insights remain \`candidate\` until confirmed.
|
|
42
43
|
- Never print vault values or write before approval.
|
|
43
44
|
- Unknown nouns, subjects, actions, ids, or ambiguous approval must produce a deterministic explanation, never a guessed mutation.
|
package/lib/runtime/briefing.js
CHANGED
|
@@ -29,6 +29,31 @@ function generateBriefing(scrumDir, repository) {
|
|
|
29
29
|
}))
|
|
30
30
|
.slice(0, 5);
|
|
31
31
|
|
|
32
|
+
// Blocked-by-dependency: Tasks with `depends_on: [ID, ...]` in frontmatter
|
|
33
|
+
// where at least one target artifact is missing or not terminal. This is a
|
|
34
|
+
// visibility signal, not a hard gate — the agent reads it and decides.
|
|
35
|
+
const allById = new Map();
|
|
36
|
+
for (const kind of Object.keys(snapshot.records)) {
|
|
37
|
+
for (const r of snapshot.records[kind] || []) {
|
|
38
|
+
if (r.id) allById.set(r.id, r);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const blockedLines = [];
|
|
42
|
+
for (const task of snapshot.records.task || []) {
|
|
43
|
+
if (!task.id || TERMINAL.has(task.status)) continue;
|
|
44
|
+
const deps = Array.isArray(task.depends_on) ? task.depends_on : [];
|
|
45
|
+
const blocking = [];
|
|
46
|
+
for (const depId of deps) {
|
|
47
|
+
if (typeof depId !== "string") continue;
|
|
48
|
+
const dep = allById.get(depId);
|
|
49
|
+
if (!dep) blocking.push(`${depId} (missing)`);
|
|
50
|
+
else if (!TERMINAL.has(dep.status)) blocking.push(`${depId} (${dep.status})`);
|
|
51
|
+
}
|
|
52
|
+
if (blocking.length) {
|
|
53
|
+
blockedLines.push(`- ${task.id} waits on ${blocking.join(", ")} — ${task.title || task.id}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
32
57
|
const completedRuns = (snapshot.records.run || [])
|
|
33
58
|
.filter((r) => r.status === "completed")
|
|
34
59
|
.slice(-5)
|
|
@@ -76,6 +101,10 @@ Progressive disclosure: read this first; go deeper only if the briefing lacks wh
|
|
|
76
101
|
|
|
77
102
|
${activeWork.length ? activeWork.join("\n") : "- No active canonical work."}
|
|
78
103
|
|
|
104
|
+
## Blocked by dependency
|
|
105
|
+
|
|
106
|
+
${blockedLines.length ? blockedLines.join("\n") : "- None. All active Tasks have their `depends_on` targets terminal (or none declared)."}
|
|
107
|
+
|
|
79
108
|
## Recent
|
|
80
109
|
|
|
81
110
|
${recentLines.length ? recentLines.join("\n") : "- No completed Runs."}
|
|
@@ -57,7 +57,7 @@ function sprintBody(title, request, created, taskId) {
|
|
|
57
57
|
function buildTaskBody(request, classification, approvalId, fingerprint, risk, previewSection, links = {}) {
|
|
58
58
|
const featureSection = links.feature ? `\n## Feature\n\n- ${links.feature}\n` : "";
|
|
59
59
|
const sprintSection = links.sprint ? `\n## Sprint\n\n- ${links.sprint}\n` : "";
|
|
60
|
-
return `# ${titleFor(request)}\n\n## Request\n\n${request}\n\n##
|
|
60
|
+
return `# ${titleFor(request)}\n\n## Request\n\n${request}\n\n## Done when\n\n- [ ] _State the smallest observable delivery contract before execution._\n\n## Validation Scope\n\n- Required: validate the stated Done when contract and active Guardrails proportionately to risk.\n- Non-blocking: tests, reviews, or environments not explicitly required by the owner, Done when contract, or an active Guardrail. Record a material coverage gap as a follow-up only when it is outside the approved contract; do not fail the Task solely because that optional check was not run.\n\n## Completion\n\n- _Fill this once, after the delivery contract is satisfied._\n${previewSection}${featureSection}${sprintSection}\n## Classification\n\n- Type: ${classification.type}\n- Reason: ${classification.reason}\n- Risk: ${risk.level}\n\n## Approval\n\n- Explicit approval token: ${approvalId}\n- Context fingerprint: ${fingerprint}`;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function stateFingerprint(repository) {
|
|
@@ -595,9 +595,9 @@ function planArtifactBody(kind, title, created) {
|
|
|
595
595
|
"",
|
|
596
596
|
title,
|
|
597
597
|
"",
|
|
598
|
-
"##
|
|
598
|
+
"## Done when",
|
|
599
599
|
"",
|
|
600
|
-
|
|
600
|
+
"- [ ] _State the smallest observable delivery contract before execution._",
|
|
601
601
|
"",
|
|
602
602
|
"## Source",
|
|
603
603
|
"",
|
|
@@ -792,7 +792,7 @@ function amendPlanArtifact(projectRoot, kind, id, options = {}) {
|
|
|
792
792
|
let body = artifact.body;
|
|
793
793
|
if (options.title !== undefined) body = replaceTitle(body, options.title);
|
|
794
794
|
if (options.request !== undefined) body = replaceSection(body, "Request", options.request);
|
|
795
|
-
if (options.acceptance !== undefined) body =
|
|
795
|
+
if (options.acceptance !== undefined) body = replaceFirstKnownSection(body, ["Done when", "Acceptance Criteria"], options.acceptance.map((item) => `- [ ] ${normalizeAmendText(item, "Delivery criterion")}`).join("\n"));
|
|
796
796
|
if (options.purpose !== undefined) body = replaceFirstKnownSection(body, ["Purpose", "Motivation"], options.purpose);
|
|
797
797
|
if (options.exitCriteria !== undefined) body = replaceFirstKnownSection(body, ["Exit Criteria", "Exit criteria"], options.exitCriteria.map((item) => `- [ ] ${normalizeAmendText(item, "Exit criterion")}`).join("\n"));
|
|
798
798
|
if (options.timebox !== undefined) body = replaceSection(body, "Timebox", options.timebox);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Daily work is intentionally Markdown-first. This module keeps the
|
|
4
|
+
// enforcement at the boundary: project policy is pinned when ScrumRun is
|
|
5
|
+
// initialized/updated and `doctor --strict` / release audits detect drift.
|
|
6
|
+
|
|
7
|
+
const crypto = require("node:crypto");
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
|
|
11
|
+
function sha256(value) {
|
|
12
|
+
return crypto.createHash("sha256").update(value).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readRegular(file) {
|
|
16
|
+
if (!fs.existsSync(file) || !fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink()) return null;
|
|
17
|
+
return fs.readFileSync(file, "utf8");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function expectedCoreFingerprint(packageRoot = path.resolve(__dirname, "../..")) {
|
|
21
|
+
const core = readRegular(path.join(packageRoot, "CORE.md"));
|
|
22
|
+
return core === null ? null : sha256(core);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readPolicyIntegrity(scrumDir) {
|
|
26
|
+
const marker = path.join(scrumDir, "method.json");
|
|
27
|
+
const raw = readRegular(marker);
|
|
28
|
+
if (raw === null) return { marker: null, integrity: null, error: "method.json is missing or unsafe" };
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(raw);
|
|
31
|
+
return { marker: parsed, integrity: parsed.integrity || null, error: null };
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return { marker: null, integrity: null, error: error.message };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sealPolicyIntegrity(scrumDir, { includeGuardrails = false, coreFingerprint = expectedCoreFingerprint() } = {}) {
|
|
38
|
+
const state = readPolicyIntegrity(scrumDir);
|
|
39
|
+
if (state.error) throw new Error(`Cannot seal ScrumRun policy: ${state.error}`);
|
|
40
|
+
const core = readRegular(path.join(scrumDir, "core.md"));
|
|
41
|
+
if (core === null) throw new Error("Cannot seal ScrumRun policy: core.md is missing or unsafe.");
|
|
42
|
+
const guardrails = readRegular(path.join(scrumDir, "guardrails.md"));
|
|
43
|
+
if (guardrails === null) throw new Error("Cannot seal ScrumRun policy: guardrails.md is missing or unsafe.");
|
|
44
|
+
const next = {
|
|
45
|
+
...state.marker,
|
|
46
|
+
workflow: { ...(state.marker.workflow || {}), daily: "markdown-first" },
|
|
47
|
+
integrity: {
|
|
48
|
+
...(state.integrity || {}),
|
|
49
|
+
schema: 1,
|
|
50
|
+
core_sha256: sha256(core),
|
|
51
|
+
core_package_sha256: coreFingerprint || sha256(core),
|
|
52
|
+
...(includeGuardrails || !state.integrity || !state.integrity.guardrails_sha256
|
|
53
|
+
? { guardrails_sha256: sha256(guardrails) }
|
|
54
|
+
: {})
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return `${JSON.stringify(next, null, 2)}\n`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function auditPolicyIntegrity(scrumDir, { coreFingerprint = expectedCoreFingerprint() } = {}) {
|
|
61
|
+
const state = readPolicyIntegrity(scrumDir);
|
|
62
|
+
if (state.error) return [{ severity: "critical", code: "POLICY_MARKER", message: state.error }];
|
|
63
|
+
const integrity = state.integrity;
|
|
64
|
+
if (!integrity || integrity.schema !== 1) {
|
|
65
|
+
return [{ severity: "warning", code: "POLICY_INTEGRITY_UNSEALED", message: "Policy fingerprints are not sealed. Run `scrumrun update --project --seal-policy` after owner review." }];
|
|
66
|
+
}
|
|
67
|
+
const findings = [];
|
|
68
|
+
const core = readRegular(path.join(scrumDir, "core.md"));
|
|
69
|
+
const guardrails = readRegular(path.join(scrumDir, "guardrails.md"));
|
|
70
|
+
if (core === null) findings.push({ severity: "critical", code: "CORE_UNSAFE", message: "core.md is missing or unsafe." });
|
|
71
|
+
else {
|
|
72
|
+
const actual = sha256(core);
|
|
73
|
+
if (actual !== integrity.core_sha256) findings.push({ severity: "high", code: "CORE_TAMPERED", message: "core.md differs from the owner-sealed policy. Restore it with `scrumrun update --project`." });
|
|
74
|
+
if (coreFingerprint && actual !== coreFingerprint) findings.push({ severity: "high", code: "CORE_PACKAGE_DRIFT", message: "core.md differs from the installed ScrumRun Core. Review then run `scrumrun update --project`." });
|
|
75
|
+
}
|
|
76
|
+
if (guardrails === null) findings.push({ severity: "critical", code: "GUARDRAILS_UNSAFE", message: "guardrails.md is missing or unsafe." });
|
|
77
|
+
else if (sha256(guardrails) !== integrity.guardrails_sha256) {
|
|
78
|
+
findings.push({ severity: "high", code: "GUARDRAILS_TAMPERED", message: "guardrails.md changed after owner sealing. Review it, then explicitly run `scrumrun update --project --seal-policy`." });
|
|
79
|
+
}
|
|
80
|
+
return findings;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { auditPolicyIntegrity, expectedCoreFingerprint, sealPolicyIntegrity, sha256 };
|
package/lib/v2/artifacts.js
CHANGED
|
@@ -17,6 +17,7 @@ function sha256(content) {
|
|
|
17
17
|
|
|
18
18
|
function scalar(value) {
|
|
19
19
|
if (value === null || value === undefined) return "null";
|
|
20
|
+
if (Array.isArray(value)) return `[${value.map((item) => scalar(item)).join(", ")}]`;
|
|
20
21
|
if (typeof value === "boolean" || typeof value === "number") return String(value);
|
|
21
22
|
const text = String(value);
|
|
22
23
|
if (/^[A-Za-z0-9._/-]+$/.test(text)) return text;
|
|
@@ -36,6 +37,11 @@ function parseScalar(value) {
|
|
|
36
37
|
return text;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
if (text.startsWith("[") && text.endsWith("]")) {
|
|
41
|
+
const inner = text.slice(1, -1).trim();
|
|
42
|
+
if (!inner) return [];
|
|
43
|
+
return inner.split(",").map((item) => parseScalar(item));
|
|
44
|
+
}
|
|
39
45
|
return text;
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -102,6 +108,9 @@ function validateArtifact(record, expectedKind = null) {
|
|
|
102
108
|
errors.push(`${record.kind}.${field} must reference ${target.prefix}-NNN`);
|
|
103
109
|
}
|
|
104
110
|
}
|
|
111
|
+
if (record.kind === "run" && (record.task === undefined || record.task === null) && (record.sprint === undefined || record.sprint === null)) {
|
|
112
|
+
errors.push("run must reference TASK-NNN or SPRINT-NNN");
|
|
113
|
+
}
|
|
105
114
|
for (const [field, constraint] of Object.entries(SCALAR_FIELDS)) {
|
|
106
115
|
if (!constraint.kinds.includes(record.kind)) continue;
|
|
107
116
|
if (constraint.required && (record[field] === undefined || record[field] === null)) {
|
package/lib/v2/conformance.js
CHANGED
|
@@ -14,6 +14,7 @@ const { pendingTransactionStatus } = require("./transaction");
|
|
|
14
14
|
const { configWeakeningAttempts, validateGuardrailDocument } = require("../runtime/policy-engine");
|
|
15
15
|
const { auditActiveWorkspace } = require("../runtime/mutation-gateway");
|
|
16
16
|
const { canonicalPaths, PATHS_SCHEMA_VERSION } = require("./paths");
|
|
17
|
+
const { auditPolicyIntegrity } = require("../runtime/policy-integrity");
|
|
17
18
|
|
|
18
19
|
const INVARIANTS = Object.freeze([
|
|
19
20
|
{ id: "I-01", summary: "pre-approval work is read-only", tests: ["intake builds bounded context without writing"] },
|
|
@@ -79,10 +80,10 @@ function auditProject(projectRoot) {
|
|
|
79
80
|
if (methodMarker.method !== METHOD_VERSION) findings.push(finding("critical", "METHOD_VERSION", `method.json must declare ${METHOD_VERSION}.`, marker));
|
|
80
81
|
const expectedPaths = canonicalPaths();
|
|
81
82
|
if (!methodMarker.paths || typeof methodMarker.paths !== "object" || Array.isArray(methodMarker.paths)) {
|
|
82
|
-
findings.push(finding("
|
|
83
|
+
findings.push(finding("warning", "METHOD_PATHS_MISSING", `method.json is missing a canonical "paths" block. Non-blocking in Markdown-first daily work; run \`scrumrun update --migrate\` if you want the declared index.`, marker));
|
|
83
84
|
} else {
|
|
84
85
|
const drift = diffPathIndex(expectedPaths, methodMarker.paths);
|
|
85
|
-
for (const entry of drift) findings.push(finding("
|
|
86
|
+
for (const entry of drift) findings.push(finding("warning", "METHOD_PATHS_DRIFT", `method.json paths[${entry.label}] is ${entry.actual === undefined ? "missing" : `"${entry.actual}"`}; expected "${entry.expected}". Non-blocking; regenerate via \`update --migrate\` when convenient.`, marker));
|
|
86
87
|
if (methodMarker.paths_schema !== PATHS_SCHEMA_VERSION) {
|
|
87
88
|
findings.push(finding("warning", "METHOD_PATHS_SCHEMA", `method.json paths_schema is ${methodMarker.paths_schema || "missing"}; expected ${PATHS_SCHEMA_VERSION}.`, marker));
|
|
88
89
|
}
|
|
@@ -94,6 +95,7 @@ function auditProject(projectRoot) {
|
|
|
94
95
|
const file = path.join(scrumDir, relative);
|
|
95
96
|
if (!fs.existsSync(file) || !fs.lstatSync(file).isFile()) findings.push(finding("high", "CANONICAL_MISSING", `${relative} is missing or unsafe.`, file));
|
|
96
97
|
}
|
|
98
|
+
for (const item of auditPolicyIntegrity(scrumDir)) findings.push(finding(item.severity, item.code, item.message, marker));
|
|
97
99
|
const guardrailsFile = path.join(scrumDir, "guardrails.md");
|
|
98
100
|
const guardrails = fs.existsSync(guardrailsFile) && fs.lstatSync(guardrailsFile).isFile() ? fs.readFileSync(guardrailsFile, "utf8") : "";
|
|
99
101
|
const guardrailValidation = validateGuardrailDocument(guardrails);
|
|
@@ -141,9 +143,9 @@ function auditProject(projectRoot) {
|
|
|
141
143
|
task = null;
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
|
-
if (run.record && !run.record.task) {
|
|
145
|
-
findings.push(finding("high", "
|
|
146
|
-
} else if (run.record && (!task || task.errors.length)) {
|
|
146
|
+
if (run.record && !run.record.task && !run.record.sprint) {
|
|
147
|
+
findings.push(finding("high", "RUN_TARGET_MISSING", `${run.record.id} must reference a Task or Sprint.`, run.file));
|
|
148
|
+
} else if (run.record && run.record.task && (!task || task.errors.length)) {
|
|
147
149
|
findings.push(finding("high", "RUN_TASK_MISSING", `${run.record.id} references missing or invalid ${run.record.task}.`, run.file));
|
|
148
150
|
}
|
|
149
151
|
if (run.record && task && !task.errors.length && (run.record.sprint || null) !== (task.record.sprint || null)) {
|
|
@@ -170,6 +172,7 @@ function auditProject(projectRoot) {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
const byId = new Map(Object.values(records).flat().filter((artifact) => artifact.record).map((artifact) => [artifact.record.id, artifact]));
|
|
175
|
+
const markdownFirst = methodMarker && methodMarker.workflow && methodMarker.workflow.daily === "markdown-first";
|
|
173
176
|
for (const task of records.task || []) {
|
|
174
177
|
for (const [field, prefix] of [["feature", "FEAT"], ["sprint", "SPRINT"]]) {
|
|
175
178
|
const target = task.record && task.record[field];
|
|
@@ -182,7 +185,7 @@ function auditProject(projectRoot) {
|
|
|
182
185
|
}
|
|
183
186
|
const attemptsByTask = new Map();
|
|
184
187
|
for (const run of records.run || []) {
|
|
185
|
-
if (!run.record || run.errors.length) continue;
|
|
188
|
+
if (!run.record || run.errors.length || !run.record.task) continue;
|
|
186
189
|
if (!attemptsByTask.has(run.record.task)) attemptsByTask.set(run.record.task, []);
|
|
187
190
|
attemptsByTask.get(run.record.task).push(run.record.attempt);
|
|
188
191
|
}
|
|
@@ -199,11 +202,14 @@ function auditProject(projectRoot) {
|
|
|
199
202
|
.filter((run) => run.record && !run.errors.length && run.record.task === task.record.id)
|
|
200
203
|
.sort((left, right) => right.record.attempt - left.record.attempt);
|
|
201
204
|
if (!attempts.length) {
|
|
205
|
+
if (markdownFirst) continue;
|
|
202
206
|
if (["running", "validating", "learning", "partial"].includes(task.record.status)) {
|
|
203
207
|
findings.push(finding(
|
|
204
|
-
"high",
|
|
208
|
+
markdownFirst ? "warning" : "high",
|
|
205
209
|
"TASK_ORPHANED",
|
|
206
|
-
|
|
210
|
+
markdownFirst
|
|
211
|
+
? `${task.record.id}.status is ${task.record.status} without a Run. This is valid Markdown-first work; keep the Task handoff current.`
|
|
212
|
+
: `${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
213
|
task.file
|
|
208
214
|
));
|
|
209
215
|
}
|
|
@@ -219,17 +225,13 @@ function auditProject(projectRoot) {
|
|
|
219
225
|
blocked: "blocked",
|
|
220
226
|
partial: "partial"
|
|
221
227
|
};
|
|
222
|
-
if (taskStatuses[latest.status] && task.record.status !== taskStatuses[latest.status]) {
|
|
228
|
+
if (!markdownFirst && taskStatuses[latest.status] && task.record.status !== taskStatuses[latest.status]) {
|
|
223
229
|
findings.push(finding("high", "TASK_RUN_STATUS_MISMATCH", `${task.record.id}.status ${task.record.status} disagrees with latest ${latest.id}.status ${latest.status}.`, task.file));
|
|
224
230
|
}
|
|
225
231
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (!/^## Acceptance Criteria\b/m.test(task.body || "")) {
|
|
230
|
-
findings.push(finding("warning", "ACCEPTANCE_CRITERIA_MISSING", `${task.record.id} has no Acceptance Criteria section; define what "done" means before execution.`, task.file));
|
|
231
|
-
}
|
|
232
|
-
}
|
|
232
|
+
// Task bodies are intentionally extensible Markdown. Project Guardrails may
|
|
233
|
+
// require sections for selected Tasks; the kernel never requires one global
|
|
234
|
+
// heading or rejects unknown owner-defined structure.
|
|
233
235
|
for (const sprint of records.sprint || []) {
|
|
234
236
|
if (!sprint.record || sprint.errors.length) continue;
|
|
235
237
|
const heading = /^## Tasks[ \t]*$/m.exec(sprint.body);
|
package/lib/v2/paths.js
CHANGED
|
@@ -56,10 +56,11 @@ function flattenPaths(paths = CANONICAL_PATHS, prefix = "") {
|
|
|
56
56
|
return out;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function renderMethodJson({ methodVersion, layout = "v2", schemas = {}, migratedFrom, migration } = {}) {
|
|
59
|
+
function renderMethodJson({ methodVersion, layout = "v2", schemas = {}, workflow = { daily: "markdown-first" }, migratedFrom, migration } = {}) {
|
|
60
60
|
const payload = {
|
|
61
61
|
method: methodVersion,
|
|
62
62
|
layout,
|
|
63
|
+
workflow,
|
|
63
64
|
paths_schema: PATHS_SCHEMA_VERSION,
|
|
64
65
|
paths: canonicalPaths(),
|
|
65
66
|
schemas
|
package/lib/v2/schema.js
CHANGED
|
@@ -38,8 +38,8 @@ const ARTIFACT_TYPES = deepFreeze({
|
|
|
38
38
|
task: {
|
|
39
39
|
prefix: "TASK",
|
|
40
40
|
directory: "tasks",
|
|
41
|
-
initial: ["backlog", "proposed", "running"],
|
|
42
|
-
statuses: ["backlog", "proposed", "running", "validating", "learning", "partial", "completed", "failed", "blocked", "cancelled"]
|
|
41
|
+
initial: ["backlog", "proposed", "in_progress", "running"],
|
|
42
|
+
statuses: ["backlog", "proposed", "in_progress", "running", "validating", "learning", "partial", "completed", "failed", "blocked", "cancelled"]
|
|
43
43
|
},
|
|
44
44
|
sprint: {
|
|
45
45
|
prefix: "SPRINT",
|
|
@@ -90,7 +90,7 @@ const ARTIFACT_TRANSITIONS = deepFreeze({
|
|
|
90
90
|
backlog: ["proposed", "active", "cancelled"], proposed: ["active", "cancelled"], active: ["paused", "completed", "cancelled"], paused: ["active", "cancelled"]
|
|
91
91
|
},
|
|
92
92
|
task: {
|
|
93
|
-
backlog: ["proposed", "running", "cancelled"], proposed: ["running", "cancelled"], running: ["validating", "failed", "blocked", "cancelled"], validating: ["learning", "failed", "blocked"], learning: ["completed", "failed", "blocked"], partial: ["running", "cancelled"], failed: ["running", "cancelled"], blocked: ["running", "cancelled"]
|
|
93
|
+
backlog: ["proposed", "in_progress", "running", "cancelled"], proposed: ["in_progress", "running", "cancelled"], in_progress: ["completed", "failed", "blocked", "cancelled"], running: ["validating", "completed", "failed", "blocked", "cancelled"], validating: ["learning", "completed", "failed", "blocked"], learning: ["completed", "failed", "blocked"], partial: ["in_progress", "running", "cancelled"], failed: ["in_progress", "running", "cancelled"], blocked: ["in_progress", "running", "cancelled"]
|
|
94
94
|
},
|
|
95
95
|
sprint: {
|
|
96
96
|
proposed: ["running", "cancelled"], running: ["partial", "completed", "blocked", "cancelled"], partial: ["running", "completed", "cancelled"], blocked: ["running", "cancelled"]
|
|
@@ -108,7 +108,7 @@ const ARTIFACT_TRANSITIONS = deepFreeze({
|
|
|
108
108
|
const STRUCTURAL_RELATIONS = deepFreeze({
|
|
109
109
|
feature: { targetKind: "feature", cardinality: "0..1", meaning: "long-lived initiative containing the artifact" },
|
|
110
110
|
sprint: { targetKind: "sprint", cardinality: "0..1", meaning: "optional delivery batch containing a Task or Run" },
|
|
111
|
-
task: { targetKind: "task", cardinality: "
|
|
111
|
+
task: { targetKind: "task", cardinality: "0..1", meaning: "atomic work executed or reviewed by the artifact" }
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
const SCALAR_FIELDS = deepFreeze({
|
|
@@ -120,9 +120,9 @@ const SCALAR_FIELDS = deepFreeze({
|
|
|
120
120
|
|
|
121
121
|
const TRUTH_OWNERSHIP = deepFreeze({
|
|
122
122
|
feature: { question: "Why does this initiative exist?", truth: "initiative purpose, scope, dependencies, and lifecycle" },
|
|
123
|
-
task: { question: "What
|
|
124
|
-
sprint: { question: "
|
|
125
|
-
run: { question: "
|
|
123
|
+
task: { question: "What concrete outcome is intended?", truth: "scope, owner-defined sections, links, and status" },
|
|
124
|
+
sprint: { question: "Which Tasks are grouped for this delivery?", truth: "timebox or delivery-batch membership; may be feature, fix, or maintenance" },
|
|
125
|
+
run: { question: "What happened while executing a Task or Sprint?", truth: "optional human-readable execution record and outcome" },
|
|
126
126
|
review: { question: "What independent validation was performed?", truth: "scoped findings, checks, evidence, and verdict" },
|
|
127
127
|
knowledge: { question: "What verified project fact is reusable?", truth: "approved evidence-backed fact and validity" },
|
|
128
128
|
decision: { question: "What normative choice constrains future work?", truth: "decision, rationale, validity, and lifecycle" },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scrumrun",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "Markdown-first Agile memory and guardrails for AI coding agents.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scrumrun": "bin/scrumrun.js",
|
|
7
7
|
"sr-claude": "bin/claude-install.js"
|
|
@@ -69,7 +69,7 @@ Every artifact also requires \`id\`, \`kind\`, \`status\`, \`created\`, \`update
|
|
|
69
69
|
|---|---|---|---|
|
|
70
70
|
${relationRows.join("\n")}
|
|
71
71
|
|
|
72
|
-
Task is the atomic unit. A Task may have zero or one Sprint. A
|
|
72
|
+
Task is the atomic unit. A Task may have zero or one Sprint. A Run is optional Markdown history and may reference either a Task or a Sprint; it never controls Task status in Markdown-first work. Sprint membership is authoritative on \`Task.sprint\`; a Sprint's \`## Tasks\` list is a human-readable projection that must agree with it. Additional owner-defined relations such as \`depends_on: [TASK-014, DEC-018]\` are preserved as local Markdown graph data.
|
|
73
73
|
|
|
74
74
|
## Scalar constraints
|
|
75
75
|
|
|
@@ -85,7 +85,7 @@ Newly authored Runs use \`ledger: ${RUN_LEDGER_VERSION}\`. Their \`## Events\` s
|
|
|
85
85
|
|
|
86
86
|
Every event requires \`schema\`, \`id\`, contiguous \`sequence\`, RFC3339 \`occurred_at\`, \`timestamp_precision\`, \`actor\`, \`from\`, \`to\`, \`reason\`, and structured \`evidence\`. Event types are ${RUN_EVENT_TYPES.map((value) => `\`${value}\``).join(", ")}. Evidence kinds are ${RUN_EVIDENCE_KINDS.map((value) => `\`${value}\``).join(", ")}.
|
|
87
87
|
|
|
88
|
-
A native ledger begins with \`created → executing\`; an evidenced migration \`snapshot\` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated. Run owns
|
|
88
|
+
A native ledger begins with \`created → executing\`; an evidenced migration \`snapshot\` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated when a strict audit Run is used. Run owns optional event history; Task Markdown owns the delivered scope and status in normal work.
|
|
89
89
|
|
|
90
90
|
## Truth questions
|
|
91
91
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md - {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
## ScrumRun
|
|
3
|
+
## ScrumRun 4.0 — execution-first Markdown
|
|
4
4
|
|
|
5
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
|
|
|
@@ -15,18 +15,24 @@ Natural-language product requests begin with a read-only understanding pass. Bef
|
|
|
15
15
|
|
|
16
16
|
After approval:
|
|
17
17
|
|
|
18
|
-
- Task is the
|
|
18
|
+
- execute continuously until the approved Task is delivered: keep working through discover → implement → verify → fix → verify. A progress report is allowed only when the owner asks for status and must be followed immediately by more execution; it never closes the workflow. Do not stop to send an inventory, a progress report, a decomposition, or a list of work still to do. A missing implementation found during the work remains work to do now, not a follow-up, a “next step”, or a reason to reply. Stop only for an owner decision, external access, an explicit Guardrail, a security/secret risk, destructive work without approval, or an unmet required Acceptance Criterion;
|
|
19
|
+
- Task is the atomic work item; create or refine its Markdown directly, define a short `## Done when` delivery contract, and retain a short `## Completion` / `## Follow-ups` handoff;
|
|
20
|
+
- you may decompose implementation privately or add linked child Tasks when needed, but do not make the owner manage that decomposition and do not stop after planning it;
|
|
19
21
|
- validation is scoped: only a test/review/environment explicitly required by the owner, the Task's Acceptance Criteria, or an active Guardrail can block completion; a missing optional E2E suite is a documented follow-up/risk, never a reason to fail an otherwise accepted Task;
|
|
20
22
|
- Feature and Sprint remain useful organization, but are optional; create them only when they clarify real initiative or timebox context;
|
|
21
23
|
- Sprint is only a real timebox/batch of Tasks;
|
|
22
24
|
- Run is an optional audit/handoff record, never an administrative prerequisite to start, amend, or complete a Task; preserve useful prior attempts but do not let missing/invalid Run metadata stop work;
|
|
23
|
-
-
|
|
25
|
+
- only claim a condition validated when the check actually covers that condition; a narrow checker never proves a broad delivery claim. Record a `## Completion` at completion so the next agent inherits what was done; `## Follow-ups` may contain only work outside the approved `## Done when`, never unfinished acceptance work;
|
|
24
26
|
- work directly in code and Task Markdown after approval; do not call `npx scrumrun@latest` or normal `scrumrun plan/run` commands during execution;
|
|
25
27
|
- use the CLI only for `init`, `update --project`, `migrate`, `repair`, `doctor`, reports, or release checks. It audits/repairs the folder; it does not own the daily workflow;
|
|
26
28
|
- never invoke `plan run --fail`, `--block`, `--retry`, `--finalize`, `--complete`, `--validate`, or `plan task --start` in normal work. A failed legacy Run due to administrative state remains historical; write the corrected delivery outcome directly in the Task instead;
|
|
27
|
-
- learning proposes evidence-backed Knowledge, Decisions, or candidate Insights when the
|
|
29
|
+
- learning proposes evidence-backed Knowledge, Decisions, or candidate Insights after delivery, or only when it materially helps the current implementation; it never interrupts execution;
|
|
28
30
|
- guardrails remain mandatory: stop only for an explicit Guardrail, security/secret risk, destructive action without approval, or an unmet required Acceptance Criterion. Status vocabulary, missing Runs, unavailable optional tests, and stale generated state are warnings to reconcile, not blockers.
|
|
29
31
|
|
|
32
|
+
`core.md` and `guardrails.md` are sealed policy. Never edit either during a product Task. A policy change requires an explicit owner request; after review, seal it at the maintenance edge with `scrumrun update --project --seal-policy`.
|
|
33
|
+
|
|
34
|
+
Artifacts are local Markdown connected by stable IDs and relative links. Preserve any owner-defined frontmatter and sections in a Task. A Guardrail may require sections such as `## Migration Plan`, `## Rollback`, or `## Guardrail Evidence`; add them only to the affected Task.
|
|
35
|
+
|
|
30
36
|
Never bypass guardrails, overwrite owner work, treat generated state/cache as truth, auto-confirm AI knowledge, auto-migrate a v1 project, or print vault values.
|
|
31
37
|
|
|
32
38
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md - {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
## ScrumRun
|
|
3
|
+
## ScrumRun 4.0 lean read policy — execution-first Markdown
|
|
4
4
|
|
|
5
5
|
This project stores the complete ScrumRun v2 truth but uses a bounded default read path:
|
|
6
6
|
|
|
@@ -11,12 +11,16 @@ This project stores the complete ScrumRun v2 truth but uses a bounded default re
|
|
|
11
11
|
|
|
12
12
|
Do not scan every Task, Run, Sprint, Feature, or Memory file by default. Generated `state.md`, `map.md`, and `.cache/` guide retrieval but never override canonical Markdown.
|
|
13
13
|
|
|
14
|
-
Natural-language product work begins as a read-only understanding pass. Explicit approval authorizes direct work in source files and `.scrumrun/` Markdown.
|
|
14
|
+
Natural-language product work begins as a read-only understanding pass. Explicit approval authorizes direct work in source files and `.scrumrun/` Markdown. Execute continuously until the approved Task is delivered: keep working through discover → implement → verify → fix → verify. A progress report is allowed only when the owner asks for status and must be followed immediately by more execution; it never closes the workflow. Do not stop to provide an inventory, a partial progress report, or a list of remaining work. A missing implementation found during the work remains work to do now, not a follow-up or “next step”. Define a short `## Done when` delivery contract, record `## Completion` only at the end, and never move unfinished contract work to `## Follow-ups` without explicit owner approval. A Run is optional handoff/audit context, not a state machine that may prevent starting, amending, or completing work. A Sprint exists only for a real batch/timebox.
|
|
15
15
|
|
|
16
16
|
Guardrails are mandatory, but administrative state is not: block only for an explicit Guardrail, security/secret risk, destructive action without approval, or an unmet required Acceptance Criterion. Missing Runs, invalid legacy status vocabulary, stale generated views, and optional unrun tests are warnings to reconcile in Markdown. Record meaningful optional coverage gaps under `## Follow-ups`; they do not fail a delivered Task.
|
|
17
17
|
|
|
18
18
|
`.scrumrun/guardrails.md` is canonical policy. Never bypass it, overwrite owner work, auto-confirm AI knowledge, auto-migrate v1 state, or print vault values.
|
|
19
19
|
|
|
20
|
+
`core.md` and `guardrails.md` are sealed policy. Never edit them during a product Task. A policy change requires an explicit owner request and `scrumrun update --project --seal-policy` after review.
|
|
21
|
+
|
|
22
|
+
Artifacts are local Markdown connected by stable IDs and relative links. Preserve owner-defined frontmatter and sections in each Task; Guardrails may require a section only on affected Tasks.
|
|
23
|
+
|
|
20
24
|
Use the installed CLI only for `init`, `update --project`, `migrate`, `repair`, `doctor`, reports, and release checks. For daily product work, follow `.scrumrun/core.md` and edit the relevant Markdown directly.
|
|
21
25
|
|
|
22
26
|
Never invoke `plan run --fail`, `--block`, `--retry`, `--finalize`, `--complete`, `--validate`, or `plan task --start` during normal work. If an old Run says failed for an administrative reason, leave it as history and record the actual delivered outcome in the Task's Technical Summary and Follow-ups.
|
|
@@ -3,7 +3,9 @@ name: scrumrun
|
|
|
3
3
|
description: Use when initializing or migrating ScrumRun, handling product requests, planning or executing Tasks/Sprints/Features/Runs, managing guardrails or semantic memory, checking status, and running reviews.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# ScrumRun
|
|
6
|
+
# ScrumRun 4.0 — execution-first Markdown
|
|
7
|
+
|
|
8
|
+
Package 4.0 implements the stable ScrumRun method contract 2.0.0.
|
|
7
9
|
|
|
8
10
|
ScrumRun is an evidence-driven Agile runtime for AI agents. Its canonical shell command is:
|
|
9
11
|
|
|
@@ -36,9 +38,11 @@ Normal hot path:
|
|
|
36
38
|
5. follow the briefing's pointers to only the relevant canonical artifacts; go deeper only when the briefing lacks what you need (`## Where to look`, `scrumrun knowledge study "<topic>"`);
|
|
37
39
|
6. load `.scrumrun/core.md` when the method contract or an exceptional transition is needed.
|
|
38
40
|
|
|
39
|
-
**Markdown is the daily runtime.** After approval, work directly in source files and the relevant `.scrumrun/` Markdown.
|
|
41
|
+
**Markdown is the daily runtime.** After approval, work directly in source files and the relevant `.scrumrun/` Markdown until the approved Task is delivered. Continue the full loop — discover → implement → verify → fix → verify — before responding. A progress report is allowed only when the owner asks for status and must be followed immediately by further execution; it never ends the Task. Do not stop to issue a progress report, inventory, decomposition, or list of remaining work; those are internal implementation steps. A missing implementation discovered in scope remains required work now, never a follow-up or a “next step”. Stop only for an owner decision, external access, an explicit Guardrail, security/secret risk, destructive work without approval, or an unmet required delivery criterion. Do not invoke `npx scrumrun@latest` or normal `scrumrun plan/run` commands during execution. The CLI is optional maintenance for `init`, `update --project`, `migrate`, `repair`, `doctor`, reports, and release checks.
|
|
42
|
+
|
|
43
|
+
**Do not block on administrative state.** A missing/invalid Run, legacy status vocabulary, stale generated view, or optional unrun test is a warning to reconcile in Markdown, not a reason to refuse productive work. Define a concise `## Done when` delivery contract. `## Follow-ups` is only for work outside that contract: never relabel an unfinished requirement as a follow-up without explicit owner approval. Optional E2E/integration/review coverage belongs there or in a risk note, never in a fabricated failed Run.
|
|
40
44
|
|
|
41
|
-
**
|
|
45
|
+
**A check must prove its claim.** A passing narrow checker does not prove a broader Task condition. If the task says no visible hardcoded literals, scan the relevant source, replace every in-scope match, and re-run that scan plus the build. Apply the same coverage rule to all completion claims.
|
|
42
46
|
|
|
43
47
|
**Normal-operation command ban.** Do not invoke `scrumrun plan run --fail`, `--block`, `--retry`, `--finalize`, `--complete`, `--validate`, or `scrumrun plan task --start` during ordinary work. Those are optional strict-audit tools and cannot be used to decide whether a Task is delivered. If an old Run is already failed for an administrative reason, leave it as historical evidence, continue the Task directly, and write the corrected outcome in the Task's Technical Summary and Follow-ups.
|
|
44
48
|
|
|
@@ -104,11 +108,11 @@ During execution:
|
|
|
104
108
|
|
|
105
109
|
1. keep the change inside the approved Task scope;
|
|
106
110
|
2. preserve existing owner work and unrelated dirty files;
|
|
107
|
-
3. define or confirm the Task's `##
|
|
108
|
-
4. work normally
|
|
111
|
+
3. define or confirm the Task's short `## Done when` contract before execution and use it as the only delivery boundary;
|
|
112
|
+
4. work normally and continuously. Privately decompose work or create linked child Tasks when useful, but do not return after planning; edit code until the contract is satisfied;
|
|
109
113
|
5. validate in proportion to risk and against the acceptance criteria. Tests, reviews, and environments are required only when the owner, Acceptance Criteria, or an active Guardrail says so. Do not fail or block an otherwise accepted Task merely because an optional E2E/integration suite does not exist or was not run;
|
|
110
114
|
6. run a configured reviewer only when a Guardrail requires it;
|
|
111
|
-
7. complete the Task directly in Markdown after the required work is satisfied
|
|
115
|
+
7. complete the Task directly in Markdown after the required work is satisfied, adding a concise `## Completion`; use CLI release/doctor/repair commands only when their audit or recovery value is wanted;
|
|
112
116
|
8. use path-scoped Mutation Gateway commands only when the owner explicitly requests strict execution.
|
|
113
117
|
|
|
114
118
|
Never overwrite a prior attempt. Never mark work complete because time/token budget ended.
|
|
@@ -117,7 +121,7 @@ When work remains queued, the briefing may name the next backlog Task. The owner
|
|
|
117
121
|
|
|
118
122
|
Every explicit Guardrail remains mandatory. In strict mode, the CLI final checkpoint fails closed on policy drift, protected-path changes, unsafe symlinks, unscannable content, newly introduced secret-like content, or missing Guardrail Evidence. The ignored permit cache is disposable; deleting it invalidates outstanding strict-mode permits and never creates authority.
|
|
119
123
|
|
|
120
|
-
Task Markdown is the daily operational handoff authority. A structured Run is optional strict audit history only. Early v2 prose Runs may be repaired/migrated explicitly, but their state never overrides the Task's direct handoff or blocks approved work.
|
|
124
|
+
Task Markdown is the daily operational handoff authority. A structured Run is optional strict audit history only. `core.md` and `guardrails.md` are sealed policy: never edit either during product work. A policy change requires an explicit owner request and the owner seals reviewed changes with `scrumrun update --project --seal-policy`. Early v2 prose Runs may be repaired/migrated explicitly, but their state never overrides the Task's direct handoff or blocks approved work.
|
|
121
125
|
|
|
122
126
|
Linked canonical writes use the ignored durable transaction journal. An interrupted prepared mutation rolls back before the next approved mutation; a committed journal is verified and finalized. Audit remains read-only and reports pending recovery. Use `doctor --recover` only when explicitly requested, and never overwrite bytes changed after interruption.
|
|
123
127
|
|
package/types/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export type ArtifactKind =
|
|
|
21
21
|
|
|
22
22
|
export type ArtifactStatus =
|
|
23
23
|
| "backlog" | "proposed" | "active" | "completed" | "paused" | "cancelled"
|
|
24
|
-
| "running" | "validating" | "learning" | "partial" | "failed" | "blocked"
|
|
24
|
+
| "running" | "in_progress" | "validating" | "learning" | "partial" | "failed" | "blocked"
|
|
25
25
|
| "candidate" | "approved" | "rejected" | "deprecated" | "invalidated"
|
|
26
26
|
| "open" | "resolved" | "confirmed" | "stale" | "archived" | "executing" | "passed";
|
|
27
27
|
|