devrites 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +20 -8
  3. package/docs/adr/0012-semantic-workspace-upgrades.md +77 -0
  4. package/docs/adr/README.md +1 -0
  5. package/docs/architecture.md +31 -10
  6. package/docs/cli.md +10 -4
  7. package/docs/command-map.md +15 -6
  8. package/docs/engine/commands.md +13 -5
  9. package/docs/engine/workspace-schema.md +10 -1
  10. package/docs/flow.md +12 -7
  11. package/docs/orchestration.md +2 -2
  12. package/docs/quick-reference.md +4 -0
  13. package/docs/skills.md +8 -5
  14. package/docs/usage.md +31 -5
  15. package/engine/commands.go +2 -2
  16. package/engine/internal/doctor/doctor.go +21 -23
  17. package/engine/internal/doctor/doctor_test.go +31 -3
  18. package/engine/internal/lib/buildreadiness.go +9 -1
  19. package/engine/internal/lib/readiness_contract.json +8 -1
  20. package/engine/internal/lib/readinessartifact.go +28 -5
  21. package/engine/internal/lib/readinessartifact_test.go +105 -4
  22. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +1 -0
  23. package/engine/tests/doctor_cli_test.go +15 -0
  24. package/engine/tests/parity_buildreadiness_test.go +27 -1
  25. package/pack/.claude/agents/devrites-plan-drafter.md +3 -1
  26. package/pack/.claude/agents/devrites-upgrade-planner.md +100 -0
  27. package/pack/.claude/skills/devrites-lib/SKILL.md +2 -1
  28. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +1 -0
  29. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +3 -1
  30. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +17 -0
  31. package/pack/.claude/skills/rite/SKILL.md +2 -0
  32. package/pack/.claude/skills/rite/reference/menu.md +5 -1
  33. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +3 -1
  34. package/pack/.claude/skills/rite-build/reference/phase-contract.md +3 -3
  35. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +10 -4
  36. package/pack/.claude/skills/rite-clarify/SKILL.md +2 -1
  37. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +3 -1
  38. package/pack/.claude/skills/rite-define/SKILL.md +2 -1
  39. package/pack/.claude/skills/rite-define/reference/plan-template.md +3 -1
  40. package/pack/.claude/skills/rite-doctor/SKILL.md +2 -2
  41. package/pack/.claude/skills/rite-plan/SKILL.md +4 -1
  42. package/pack/.claude/skills/rite-upgrade/SKILL.md +125 -0
  43. package/pack/.claude/skills/rite-vet/SKILL.md +6 -2
  44. package/pack/.claude/skills/rite-vet/reference/artifacts.md +9 -1
  45. package/pack/generated/claude/agents/devrites-plan-drafter.md +3 -1
  46. package/pack/generated/claude/agents/devrites-upgrade-planner.md +100 -0
  47. package/pack/generated/claude/skills/devrites-lib/SKILL.md +2 -1
  48. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +1 -0
  49. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +3 -1
  50. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +17 -0
  51. package/pack/generated/claude/skills/rite/SKILL.md +2 -0
  52. package/pack/generated/claude/skills/rite/reference/menu.md +5 -1
  53. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +3 -1
  54. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +3 -3
  55. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +10 -4
  56. package/pack/generated/claude/skills/rite-clarify/SKILL.md +2 -1
  57. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +3 -1
  58. package/pack/generated/claude/skills/rite-define/SKILL.md +2 -1
  59. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +3 -1
  60. package/pack/generated/claude/skills/rite-doctor/SKILL.md +2 -2
  61. package/pack/generated/claude/skills/rite-plan/SKILL.md +4 -1
  62. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +125 -0
  63. package/pack/generated/claude/skills/rite-vet/SKILL.md +6 -2
  64. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +9 -1
  65. package/pack/generated/codex/agents/devrites-plan-drafter.toml +3 -1
  66. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +106 -0
  67. package/pack/generated/codex/skills/devrites-lib/SKILL.md +2 -1
  68. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +1 -0
  69. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +3 -1
  70. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +17 -0
  71. package/pack/generated/codex/skills/rite/SKILL.md +2 -0
  72. package/pack/generated/codex/skills/rite/reference/menu.md +5 -1
  73. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +3 -1
  74. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +3 -3
  75. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +10 -4
  76. package/pack/generated/codex/skills/rite-clarify/SKILL.md +2 -1
  77. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +3 -1
  78. package/pack/generated/codex/skills/rite-define/SKILL.md +2 -1
  79. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +3 -1
  80. package/pack/generated/codex/skills/rite-doctor/SKILL.md +2 -2
  81. package/pack/generated/codex/skills/rite-plan/SKILL.md +4 -1
  82. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +141 -0
  83. package/pack/generated/codex/skills/rite-upgrade/agents/openai.yaml +2 -0
  84. package/pack/generated/codex/skills/rite-vet/SKILL.md +6 -2
  85. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +9 -1
  86. package/package.json +2 -2
  87. package/scripts/check-npm-audit.mjs +110 -0
  88. package/scripts/npm-audit-exceptions.json +14 -0
  89. package/scripts/run-outcome-evals.sh +33 -2
@@ -21,9 +21,14 @@ other phases reference it here.
21
21
  mismatch | design-system mismatch | test/evidence mismatch | external-doc mismatch
22
22
  | user-decision required.
23
23
  4. Local repair that preserves behavior/scope/architecture/data/UX/security/migration?
24
- YES → log in drift.md + decisions.md, $rite-plan repair, resume.
25
- NO → ask the user (format below). Don't continue on the old plan.
26
- 5. Run $rite-plan repair before resuming.
24
+ YES, active-slice technical/tool failure → log it and use Build's bounded debug
25
+ recovery. Do not ask for retry authorization and do not re-plan unless the
26
+ durable remaining-work instructions are wrong.
27
+ YES, durable plan is wrong → log it, run $rite-plan repair without a question,
28
+ re-vet, then resume.
29
+ NO, product/policy/irreversible-risk decision → ask the user (format below).
30
+ 5. Never continue on a known-wrong durable plan. A repaired active-slice implementation
31
+ may continue only after the normal recovery, reconciliation, and integrity gates pass.
27
32
  ```
28
33
 
29
34
  ## User question format
@@ -40,4 +45,5 @@ Which direction should DevRites take?
40
45
  4. Custom: describe the intended behavior
41
46
  ```
42
47
 
43
- Never continue coding on a known-wrong plan. Always re-plan before resuming.
48
+ Never turn an objective defect, environment repair, tool bug, or proof rerun into a
49
+ human permission question. Re-plan only when the durable plan changed.
@@ -79,7 +79,8 @@ dispatch uses
79
79
  `not-applicable`, or justified `deferred-nonblocking` with owner and validation gate.
80
80
  Partial/Missing, an unowned material assumption, or an open blocking/escalating question
81
81
  means `NEEDS CLARIFICATION`.
82
- 7. **Write the verdict.** Update `decision-coverage.md`; success requires exactly
82
+ 7. **Write the verdict.** Update `decision-coverage.md`; success requires exactly one
83
+ `DevRites contract: devrites.readiness-artifacts.v2` field and exactly
83
84
  `Decision coverage: CLEAR`. Normal state is `Phase: clarify`, `Next step: $rite-temper`.
84
85
  A contract-neutral retrofit runs `devrites-engine clarify-return restore <slug>`; a
85
86
  changed behavior/acceptance contract leaves the return cursor unconsumed, writes
@@ -26,6 +26,7 @@ environment, credentials/approvals, and evidence limits.
26
26
  ```markdown
27
27
  # Decision coverage
28
28
 
29
+ DevRites contract: devrites.readiness-artifacts.v2
29
30
  Decision coverage: <CLEAR | NEEDS CLARIFICATION>
30
31
  Coverage inputs SHA-256: <exact value from `devrites-engine readiness-digest coverage <slug>`>
31
32
 
@@ -54,6 +55,7 @@ Combine rows only when one decision and owner genuinely close them.
54
55
 
55
56
  Run the digest command only after `brief.md`, `spec.md`, `decisions.md`,
56
57
  `assumptions.md`, and `questions.md` are final for this clarification pass, then
57
- copy its complete field line verbatim. The build gate recomputes the digest and
58
+ write the current contract field exactly once and copy the digest's complete field line
59
+ verbatim. The build gate recomputes the digest and
58
60
  rejects stale coverage. Any open blocking, validating, or escalating question
59
61
  also prevents `CLEAR`.
@@ -126,7 +126,8 @@ Pull these via `Read` when shaping the plan:
126
126
  **Visual acceptance** target (state × viewport × input + target R-id/brief rule)**, so
127
127
  the design contract maps to slices as well as acceptance criteria.
128
128
  `Tests/proof` names exact command, cwd, expected signal, prerequisites, and mutable
129
- provenance inputs; `$rite-vet` preflights them.
129
+ provenance inputs; `$rite-vet` preflights them. Write the portable repository command,
130
+ never RTK/local wrappers, user-specific absolute paths, or temporary proof trees.
130
131
  4. **Map coverage and wiring:** every `AC-###` spec acceptance criterion maps to ≥1 `SLICE-###`
131
132
  (`rite-spec/reference/acceptance-criteria.md`); no orphaned criteria, no slice without a
132
133
  criterion. Lift covered/backstop `Edge Coverage` rows and resolved `Prohibitions (must-NOT)`
@@ -58,6 +58,8 @@ slices above the cut, and no slice above it depends on one below.
58
58
  After which slices to run tests / build / browser proof. For UI slices, name the visual
59
59
  acceptance targets from `design-brief.md`, not a generic "looks good" check.
60
60
  Each proof names exact command/cwd/signal, prerequisites, and mutable provenance inputs.
61
+ Use portable repository commands; runtime wrappers and local absolute/temp paths do not
62
+ belong in a durable plan.
61
63
 
62
64
  **Key links** — the wiring the assembled feature must exhibit, one row each:
63
65
  `<from> → <to> via <mechanism>` (route → handler via registration; producer → consumer via
@@ -93,7 +95,7 @@ decisions.md / evidence.md when used.
93
95
  - [ ] Every `Mode: HITL` slice has `Gate`, `SLA`, and `Checkpoint` populated
94
96
  - [ ] Human-owned choices are resolved; surviving checkpoints need unavailable pre-code
95
97
  evidence or action-time approval
96
- - [ ] Slice proof commands, cwd, prerequisites, and provenance inputs are preflightable
98
+ - [ ] Slice proof commands, cwd, prerequisites, and provenance inputs are portable and preflightable
97
99
  - [ ] Every UI slice names `Design brief states` and binary `Visual acceptance`
98
100
  - [ ] `Key links` rows cover every cross-slice wiring (or state `none`)
99
101
  - [ ] Cross-boundary contracts name producer, consumer, invariants/errors/order, and proof
@@ -35,7 +35,7 @@ It is **read-only**: it never edits the workspace, never advances a phase, never
35
35
  ## Workflow
36
36
  1. Run the diagnose core verbosely (resolve across install layouts):
37
37
  ```bash
38
- devrites-engine doctor --verbose; echo "doctor rc=$?"
38
+ devrites-engine doctor; echo "doctor rc=$?"
39
39
  ```
40
40
  1a. **Surface the learnings nudge**: point the user at `$rite-learn` when a pattern recurs across
41
41
  shipped features (read-only; silent when there's nothing to say):
@@ -84,7 +84,7 @@ progress`, but it follows the compact labels and one-next-action rule from
84
84
  ```
85
85
  Done: DevRites health checked; <OK | n issues>.
86
86
  Changed: workspace only
87
- Evidence: devrites-engine doctor --verbose rc=<0|1>; health <skipped|PASS|WARN|FAIL>; reindex <skipped|result>; learnings nudge <summary|none>; extensions/overrides <ok|n issues>
87
+ Evidence: devrites-engine doctor rc=<0|1>; health <skipped|PASS|WARN|FAIL>; reindex <skipped|result>; learnings nudge <summary|none>; extensions/overrides <ok|n issues>
88
88
  Open: <none | issue count and top issue>
89
89
  Next: <single command for the most urgent issue>
90
90
  Record: not applicable
@@ -80,7 +80,10 @@ Pull `development-workflow.md` via `Read` when reshaping slice cadence or DoD cr
80
80
  scope, not a new negotiation.
81
81
  - **revise:** apply a requested planning-artifact revision and reconcile existing artifacts in
82
82
  any direction; propose the file edit set first, confirm each file before writing, and **never
83
- edit source code**. **Gate first: revise or new?** Same intent? More than 50% of existing scope
83
+ edit source code**. The one confirmation exception is an explicit `$rite-upgrade`: its
84
+ validated `upgrade-assessment` authorizes one atomic, behavior-neutral normalization of
85
+ active planning artifacts, provided source, completed slices, answered questions, and
86
+ historical evidence remain unchanged. **Gate first: revise or new?** Same intent? More than 50% of existing scope
84
87
  survives? original *not* completable without this? Two "no"s → new work: recommend
85
88
  sealing/shipping the current workspace (MVP cut if named) then `$rite-spec` for the new
86
89
  intent, and stop. Revise preserves context; a new workspace separates the work.
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: rite-upgrade
3
+ description: User-invoked semantic upgrade for an active legacy DevRites workspace. Reconciles unfinished planning with the installed workflow contract while preserving completed work and history.
4
+ argument-hint: "[feature-slug]"
5
+ user-invocable: true
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ ## Codex compatibility
10
+
11
+ This is the Codex mirror of a DevRites skill. In Codex:
12
+
13
+ - Load DevRites engineering standards from `.agents/skills/devrites-lib/reference/standards/`. Read `.agents/skills/devrites-lib/reference/standards/core.md` before workflow work, then load the other `.agents/skills/devrites-lib/reference/standards/*.md` files exactly when this skill asks for them.
14
+ - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
15
+ - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
16
+ - For every DevRites specialist or writer dispatch, first call `spawn_agent` with the named `devrites-<role>` custom role. The matching project contract is `.codex/agents/devrites-<role>.toml`.
17
+ - If `spawn_agent` is callable but a named read-only role is unavailable, use generic `explorer` only when the host proves that run has a runtime-enforced read-only sandbox. Tell it to read `.codex/agents/devrites-<role>.toml`, follow its `developer_instructions`, and execute the unchanged packet. A missing read-only custom role is not evidence that spawning is unavailable.
18
+ - Never dispatch generic `worker` for `devrites-slice-wright` unless the host proves that worker run carries exact DevRites identity and the same `.wright-allowlist` enforcement as the named role. Codex reports a generic run as `agent_type=worker`, so the generated global hooks cannot prove that binding. Reject that unsafe rung and use the documented labelled inline wright path with `.reconcile-inline` plus the full reconcile gate.
19
+ - If the host cannot prove the generic explorer is runtime read-only, reject that rung too. Only when no spawn primitive exists or a higher-priority policy rejects a safe spawn may the root run the documented discipline inline. Label it `independence: fallback`, never call it independent, and apply every fallback risk gate. An unbound generic wright or unconfined generic explorer is such a safety rejection, not evidence that no agents exist.
20
+ - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
21
+ - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
22
+ - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
23
+
24
+
25
+ # $rite-upgrade: bring an active workspace onto the current contract
26
+
27
+ Use this when a workspace created under older DevRites rules cannot safely continue with
28
+ the installed pack. It reconciles the workspace to the current desired state; it does not
29
+ replay a chain of release-specific scripts. Structural `devrites-engine migrate` remains a
30
+ separate first step.
31
+
32
+ Current semantic contract: `devrites.readiness-artifacts.v2`.
33
+
34
+ ## Rules consulted
35
+
36
+ Read `devrites-lib/reference/standards/core.md`,
37
+ `devrites-lib/reference/standards/agents.md`,
38
+ `devrites-lib/reference/workspace-artifact-schema.md`, and the current Clarify, Define,
39
+ Plan, Vet, Converge, and Build phase contracts needed by the assessment.
40
+
41
+ ## Invariants
42
+
43
+ - Never edit application source, tests, dependencies, Git state, or historical evidence.
44
+ - Preserve completed-slice identity, status, acceptance, dependencies, historical proof,
45
+ answered questions, and `touched-files.md`.
46
+ - Reassess only unfinished planning. Archived or `done` workspaces are a no-op.
47
+ - Never downgrade a workspace that names a future semantic contract.
48
+ - This is a recovery command, not a lifecycle phase; never write `Phase: upgrade`.
49
+ - Durable plans contain portable repository commands, not RTK/local aliases, user-specific
50
+ absolute paths, or temporary proof trees. Runtime packets may name an execution adapter;
51
+ evidence may record both logical and executed forms.
52
+ - Ask at most one coherent question, and only for product, policy, irreversible risk, or
53
+ human-only access/action. Retry and repair authorization is agent-owned.
54
+ - The explicit `$rite-upgrade` invocation authorizes the bounded workspace reconciliation.
55
+ The root remains the only canonical writer.
56
+
57
+ ## Workflow
58
+
59
+ 0. **Orient and normalize structure.** Read the active slug or `$ARGUMENTS`. Run:
60
+ ```bash
61
+ devrites-engine doctor; echo "doctor rc=$?"
62
+ ```
63
+ A binary/pack integrity mismatch stops at `$rite-doctor`; do not run migration or
64
+ mutate semantic artifacts. Once healthy, orient without writing:
65
+ ```bash
66
+ devrites-engine preamble [feature-slug]
67
+ devrites-engine snapshot [feature-slug]
68
+ ```
69
+ A missing workspace stops at `$rite-spec`; an archived or `Status: done` workspace is
70
+ a no-op. For an active healthy workspace, run:
71
+ ```bash
72
+ devrites-engine migrate
73
+ devrites-engine preamble [feature-slug]
74
+ devrites-engine snapshot [feature-slug]
75
+ ```
76
+ If every semantic artifact names the current contract and `build-readiness` passes,
77
+ report a no-op.
78
+ 1. **Freeze the preservation baseline.** Create the standard orchestrator-controlled
79
+ `agent-packet/v1` baseline. Include exact paths for current phase/artifact contracts,
80
+ `state.md`, `spec.md`, `decision-coverage.md`, `architecture.md`, `plan.md`, `tasks.md`,
81
+ `traceability.md`, `test-plan.md`, `eng-review.md`, `questions.md`, `decisions.md`,
82
+ `assumptions.md`, `drift.md`, `evidence.md`, and `touched-files.md` when present. Record
83
+ the source diff identity plus the protected identity/status/acceptance/dependency
84
+ fields for every completed slice and each preserved artifact identity. Freeze the
85
+ workspace while it is assessed.
86
+ 2. **Classify from fresh context.** Dispatch `devrites-upgrade-planner` with objective
87
+ `reconcile this workspace with devrites.readiness-artifacts.v2`, the exact baseline,
88
+ and budgets `25 files / 2,000 loaded lines / 180 result lines`. Require one validated
89
+ `upgrade-assessment`. The agent writes nothing and asks nothing.
90
+ 3. **Reconcile ownership.** Reject stale, malformed, or preservation-breaking advice.
91
+ Dispatch `devrites-evidence-scout` only for a bounded missing fact. Drop an open
92
+ retry/tool-repair question with `devrites-engine resolve --drop <qid>
93
+ "superseded by explicit semantic upgrade; agent-owned recovery"` and continue. Keep a
94
+ genuine existing human gate intact; if a newly discovered human-owned choice is
95
+ unavoidable, persist one option packet and stop.
96
+ 4. **Apply current desired state in the root.**
97
+ - Remove obsolete old-engine reconstruction, old workflow hashes, temporary proof
98
+ recipes, and host-local wrappers from active `plan.md`, `tasks.md`, and
99
+ `test-plan.md`. Keep historical evidence unchanged.
100
+ - Re-run Clarify against unfinished intent until `Decision coverage: CLEAR`; it writes
101
+ exactly one `DevRites contract: devrites.readiness-artifacts.v2` field. Do not stamp a
102
+ marker over stale content.
103
+ - Re-run Temper only where unfinished scope still needs strategic review.
104
+ - For unstarted planning, use the current Define contract. When active planning needs
105
+ normalization, dispatch `devrites-plan-drafter` in repair mode and reconcile only
106
+ pending slices. Preserve all completed slices without changing their protected fields.
107
+ For all-built active work, normalize only gate-required planning fields. Use Converge
108
+ only when live code and recorded intent genuinely differ.
109
+ - Invalidate stale engineering readiness after any planning edit. Run the full current
110
+ Vet contract, including its fresh plan reviewer, portable proof commands, preflight,
111
+ and digest refresh. Vet writes exactly one current contract field to `test-plan.md`
112
+ and `eng-review.md`.
113
+ 5. **Verify preservation and readiness.** Recompute the frozen source diff, every protected
114
+ completed-slice field, `evidence.md`, answered-question records, and `touched-files.md`;
115
+ any mismatch is a hard stop and must be restored before proceeding. Then run:
116
+ ```bash
117
+ devrites-engine build-readiness [feature-slug]; echo "readiness rc=$?"
118
+ ```
119
+ Follow the gate's current route for `2` through `7`; `8` means the semantic
120
+ reconciliation is still incomplete. Finish only at `0`. Preserve the original active
121
+ build cursor when it still names the next pending slice; otherwise use the current
122
+ deterministic next action.
123
+ 6. **STOP.** Do not start Build. A second `$rite-upgrade` must take the current-contract
124
+ no-op path.
125
+
126
+ ## Output
127
+
128
+ Run `devrites-engine progress`, then use the shared completion reply contract in
129
+ `devrites-lib/reference/reply-contract.md`:
130
+
131
+ ```text
132
+ Done: workspace <slug> upgraded to devrites.readiness-artifacts.v2.
133
+ Changed: <active planning artifacts only | none; already current>
134
+ Evidence: build-readiness rc=0; source/completed slices/history preserved
135
+ Open: <none | one genuine human gate>
136
+ Next: <snapshot next command>
137
+ Record: .devrites/work/<slug>/state.md
138
+ ↻ Hygiene: /clear before the next lifecycle step
139
+ ```
140
+
141
+ On a no-op, say so and keep the snapshot's current next command.
@@ -0,0 +1,2 @@
1
+ policy:
2
+ allow_implicit_invocation: false
@@ -126,7 +126,9 @@ Pull the standard named by the active axis: `principles.md`, `patterns.md`,
126
126
  ```
127
127
  Completion: every scenario and acceptance criterion maps to planned proof, every slice is
128
128
  one-pass implementable, the Build-entry preflight is green or names an owned prerequisite,
129
- and developer-facing plans have a predicted `devex.md` scorecard.
129
+ and developer-facing plans have a predicted `devex.md` scorecard. Durable proof commands
130
+ are portable repository commands; host-local wrappers belong only in runtime packets and
131
+ recorded execution evidence.
130
132
  4a. **Forge gate.** `$rite-define` leaves `no` / `none` / `none`; Vet alone promotes under
131
133
  [`rite-build/reference/forge.md`](../rite-build/reference/forge.md). Require a costly
132
134
  unresolved architecture fork, 2–3 distinct complete contiguous `A`–`C` strategies, every
@@ -149,7 +151,9 @@ Pull the standard named by the active axis: `principles.md`, `patterns.md`,
149
151
  devrites-engine analyze; echo "final analyze rc=$?"
150
152
  ```
151
153
  Any non-zero result blocks the handoff. Then update `state.md`:
152
- write one typed field to `eng-review.md`: `Implementation readiness: READY`,
154
+ write exactly one `DevRites contract: devrites.readiness-artifacts.v2` field to both
155
+ `test-plan.md` and `eng-review.md`, plus one typed field to `eng-review.md`:
156
+ `Implementation readiness: READY`,
153
157
  `NEEDS CLARIFICATION`, or `NEEDS REPLAN`. Only READY sets `Phase: vet` and
154
158
  `Next step: $rite-build`, after a final sweep leaves no foreseeable human choice except a
155
159
  justified action-time checkpoint. Technical failure records its reproduction and
@@ -10,6 +10,7 @@ clobber.
10
10
  ```markdown
11
11
  # Eng review: <slug>
12
12
  Vetted: <iso> Cross-model: ran (codex) | off
13
+ DevRites contract: devrites.readiness-artifacts.v2
13
14
  Implementation readiness: <READY | NEEDS CLARIFICATION | NEEDS REPLAN>
14
15
  Readiness inputs SHA-256: <exact value from `devrites-engine readiness-digest engineering <slug>`>
15
16
 
@@ -74,6 +75,7 @@ test and where*, not implementation detail.
74
75
  ```markdown
75
76
  # Test plan: <slug>
76
77
  From $rite-vet on <iso>. Runner + conventions: <detected framework + command>.
78
+ DevRites contract: devrites.readiness-artifacts.v2
77
79
 
78
80
  ## Build-entry preflight
79
81
  | Gate | Command | Cwd | Expected | Prerequisites | Provenance to recapture |
@@ -83,6 +85,10 @@ From $rite-vet on <iso>. Runner + conventions: <detected framework + command>.
83
85
  Link any disposable parser fixture. Build evidence recomputes every digest; preflight values
84
86
  are not post-build proof.
85
87
 
88
+ Commands in this durable artifact are portable repository commands: no RTK or local shell
89
+ aliases, user-specific absolute paths, or temporary proof trees. A runtime packet may name
90
+ an execution adapter, and evidence may record the portable and executed forms.
91
+
86
92
  ## Coverage diagram
87
93
  <the ASCII code-paths + user-flows diagram from review-axes.md, with COVERAGE / GAPS / REGRESSIONS line>
88
94
 
@@ -163,6 +169,8 @@ acceptance/behavior changes route through the **Spec Drift Guard**.
163
169
  `devrites-engine readiness-digest coverage <slug>` and replace the complete
164
170
  `Coverage inputs SHA-256` line in `decision-coverage.md`. Only then run
165
171
  `devrites-engine readiness-digest engineering <slug>` and copy its complete
166
- field line into `eng-review.md`. The build gate rejects a stale digest,
172
+ field line into `eng-review.md`. Write
173
+ `DevRites contract: devrites.readiness-artifacts.v2` exactly once in both artifacts
174
+ only after their current-contract review is complete. The build gate rejects a stale digest,
167
175
  placeholders, non-passing preflight/readiness rows, empty tables, or an
168
176
  acceptance criterion with no test mapping.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devrites",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code and Codex",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
@@ -54,7 +54,7 @@
54
54
  "test:fast": "node scripts/run-tests.mjs --fast",
55
55
  "test:parallel": "node scripts/run-tests.mjs",
56
56
  "test:serial": "node scripts/run-tests.mjs --serial",
57
- "audit": "npm audit --audit-level=moderate",
57
+ "audit": "node scripts/check-npm-audit.mjs",
58
58
  "size:baseline": "node scripts/check-instruction-size-baseline.mjs --write",
59
59
  "release": "semantic-release",
60
60
  "release:dry": "semantic-release --dry-run --no-ci"
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8
+ const args = process.argv.slice(2);
9
+ const option = (name, fallback) => {
10
+ const index = args.indexOf(name);
11
+ return index >= 0 ? args[index + 1] : fallback;
12
+ };
13
+ const input = option('--input', '');
14
+ const exceptionsPath = resolve(option('--exceptions', resolve(root, 'scripts', 'npm-audit-exceptions.json')));
15
+ const blocking = new Set(['moderate', 'high', 'critical']);
16
+ const failures = [];
17
+ const fail = (message) => failures.push(message);
18
+ const load = (path) => JSON.parse(readFileSync(path, 'utf8'));
19
+
20
+ let report;
21
+ if (input) {
22
+ report = load(resolve(input));
23
+ } else {
24
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
25
+ const result = spawnSync(npm, ['audit', '--json'], { cwd: root, encoding: 'utf8' });
26
+ if (result.error || ![0, 1].includes(result.status)) {
27
+ console.error(result.stderr || result.error?.message || `npm audit exited ${result.status}`);
28
+ process.exit(1);
29
+ }
30
+ try {
31
+ report = JSON.parse(result.stdout);
32
+ } catch {
33
+ console.error('FAIL: npm audit did not return valid JSON');
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ const concrete = new Map();
39
+ let affected = 0;
40
+ for (const [packageName, vulnerability] of Object.entries(report.vulnerabilities || {})) {
41
+ if (!blocking.has(vulnerability.severity)) continue;
42
+ affected++;
43
+ for (const via of vulnerability.via || []) {
44
+ if (!via || typeof via !== 'object' || !blocking.has(via.severity)) continue;
45
+ const id = via.url?.match(/GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}/i)?.[0];
46
+ if (!id) {
47
+ fail(`${packageName}: blocking advisory has no GHSA identifier`);
48
+ continue;
49
+ }
50
+ const finding = concrete.get(id) || {
51
+ id,
52
+ package: via.name || packageName,
53
+ range: via.range || vulnerability.range,
54
+ source: via.url,
55
+ nodes: [],
56
+ };
57
+ finding.nodes = [...new Set([...finding.nodes, ...(vulnerability.nodes || [])])].sort();
58
+ concrete.set(id, finding);
59
+ }
60
+ }
61
+ if (affected > 0 && concrete.size === 0) fail('blocking audit findings have no concrete advisory records');
62
+
63
+ const exceptions = load(exceptionsPath);
64
+ if (!Array.isArray(exceptions)) fail('npm audit exceptions must be a JSON array');
65
+ const today = new Date().toISOString().slice(0, 10);
66
+ const seen = new Set();
67
+ for (const exception of Array.isArray(exceptions) ? exceptions : []) {
68
+ const required = ['id', 'package', 'range', 'source', 'owner', 'reason', 'expires'];
69
+ const missing = required.filter((field) => !exception?.[field]);
70
+ if (!Array.isArray(exception?.nodes) || exception.nodes.length === 0) missing.push('nodes');
71
+ if (missing.length) {
72
+ fail(`${exception?.id || 'unknown exception'}: missing ${missing.join(', ')}`);
73
+ continue;
74
+ }
75
+ if (!/^GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}$/i.test(exception.id) || !/^\d{4}-\d{2}-\d{2}$/.test(exception.expires)) {
76
+ fail(`${exception.id}: invalid id or expiry`);
77
+ continue;
78
+ }
79
+ if (seen.has(exception.id)) fail(`${exception.id}: duplicate exception`);
80
+ seen.add(exception.id);
81
+ if (exception.expires < today) fail(`${exception.id}: exception expired ${exception.expires}`);
82
+
83
+ const finding = concrete.get(exception.id);
84
+ if (!finding) {
85
+ fail(`${exception.id}: exception is stale; advisory is absent`);
86
+ continue;
87
+ }
88
+ for (const field of ['package', 'range', 'source']) {
89
+ if (exception[field] !== finding[field]) {
90
+ fail(`${exception.id}: ${field} mismatch (expected ${finding[field]})`);
91
+ }
92
+ }
93
+ if (JSON.stringify([...exception.nodes].sort()) !== JSON.stringify(finding.nodes)) {
94
+ fail(`${exception.id}: nodes mismatch (expected ${finding.nodes.join(', ')})`);
95
+ }
96
+ }
97
+ for (const finding of concrete.values()) {
98
+ if (!seen.has(finding.id)) fail(`${finding.id}: blocking advisory is not excepted`);
99
+ }
100
+
101
+ for (const message of failures) console.error(`FAIL: ${message}`);
102
+ if (failures.length) process.exit(1);
103
+ if (affected === 0) {
104
+ console.log('npm-audit: no moderate-or-higher advisories');
105
+ } else {
106
+ for (const exception of exceptions) {
107
+ console.log(`npm-audit: temporary ${exception.id} exception owned by ${exception.owner}, expires ${exception.expires}`);
108
+ }
109
+ console.log(`npm-audit: ${concrete.size} exact temporary exception(s), ${affected} affected package record(s)`);
110
+ }
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": "GHSA-r292-9mhp-454m",
4
+ "package": "tar",
5
+ "range": "<=7.5.20",
6
+ "nodes": [
7
+ "node_modules/npm/node_modules/tar"
8
+ ],
9
+ "source": "https://github.com/advisories/GHSA-r292-9mhp-454m",
10
+ "owner": "release-maintainers",
11
+ "reason": "npm@11.18.0 bundles tar@7.5.19 through @semantic-release/npm. The plugin invokes npm for authentication and publishing operations over trusted local source; it does not select or extract members from untrusted tar archives. Remove this exception as soon as npm bundles tar@7.5.21 or newer.",
12
+ "expires": "2026-08-07"
13
+ }
14
+ ]
@@ -83,11 +83,13 @@ stage_workspace() {
83
83
  mkdir -p "$ws"
84
84
  cp -R "$GOOD/." "$ws/"
85
85
  if [ "$mode" = "readiness" ]; then
86
- python3 - "$ws/state.md" <<'PY'
86
+ python3 - "$ws" "$READINESS" <<'PY'
87
87
  from pathlib import Path
88
+ import json
88
89
  import sys
89
90
 
90
- path = Path(sys.argv[1])
91
+ workspace, contract_path = map(Path, sys.argv[1:])
92
+ path = workspace / "state.md"
91
93
  text = path.read_text()
92
94
  for old, new in (
93
95
  ("| phase | done |", "| phase | build |"),
@@ -97,6 +99,35 @@ for old, new in (
97
99
  raise SystemExit(f"{path}: expected exactly one {old!r}")
98
100
  text = text.replace(old, new)
99
101
  path.write_text(text)
102
+
103
+ contract = json.loads(contract_path.read_text())
104
+ field = contract["contractField"]
105
+ declaration = f"{field}: {contract['schema']}"
106
+ for name in ("decision-coverage.md", "test-plan.md", "eng-review.md"):
107
+ artifact = workspace / name
108
+ lines = [
109
+ line for line in artifact.read_text().splitlines()
110
+ if not line.startswith(f"{field}:")
111
+ ]
112
+ lines[1:1] = ["", declaration]
113
+ artifact.write_text("\n".join(lines) + "\n")
114
+ PY
115
+ local engineering_digest
116
+ engineering_digest="$(
117
+ env DEVRITES_ROOT="$project" "$ENGINE" readiness-digest engineering "$slug"
118
+ )"
119
+ python3 - "$ws/eng-review.md" "$engineering_digest" <<'PY'
120
+ from pathlib import Path
121
+ import sys
122
+
123
+ path = Path(sys.argv[1])
124
+ field = sys.argv[2]
125
+ lines = path.read_text().splitlines()
126
+ matches = [i for i, line in enumerate(lines) if line.startswith("Readiness inputs SHA-256:")]
127
+ if len(matches) != 1:
128
+ raise SystemExit(f"{path}: expected exactly one engineering digest")
129
+ lines[matches[0]] = field
130
+ path.write_text("\n".join(lines) + "\n")
100
131
  PY
101
132
  fi
102
133