@exaudeus/workrail 3.36.0 → 3.37.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.
Files changed (44) hide show
  1. package/dist/config/config-file.js +2 -0
  2. package/dist/console-ui/assets/{index-n8cJrS4v.js → index-o-p__sHJ.js} +1 -1
  3. package/dist/console-ui/index.html +1 -1
  4. package/dist/daemon/workflow-runner.d.ts +1 -0
  5. package/dist/daemon/workflow-runner.js +3 -6
  6. package/dist/manifest.json +23 -15
  7. package/dist/trigger/notification-service.d.ts +42 -0
  8. package/dist/trigger/notification-service.js +164 -0
  9. package/dist/trigger/trigger-listener.js +7 -1
  10. package/dist/trigger/trigger-router.d.ts +3 -1
  11. package/dist/trigger/trigger-router.js +4 -1
  12. package/docs/design/agent-behavior-patterns-discovery.md +312 -0
  13. package/docs/design/agent-engine-communication-discovery.md +390 -0
  14. package/docs/design/agent-loop-architecture-alternatives-discovery.md +531 -0
  15. package/docs/design/agent-loop-error-handling-contract.md +238 -0
  16. package/docs/design/complete-step-approach-validation-discovery.md +344 -0
  17. package/docs/design/daemon-stuck-detection-discovery.md +174 -0
  18. package/docs/design/mcp-server-disconnect-discovery.md +245 -0
  19. package/docs/design/mcp-server-epipe-crash.md +198 -0
  20. package/docs/design/notification-design-candidates.md +131 -0
  21. package/docs/design/notification-design-review.md +84 -0
  22. package/docs/design/notification-implementation-plan.md +181 -0
  23. package/docs/design/spawn-agent-failure-modes.md +161 -0
  24. package/docs/design/spawn-agent-result-handling-implementation-plan.md +186 -0
  25. package/docs/design/stdio-simplification-design-candidates.md +341 -0
  26. package/docs/design/stdio-simplification-design-review.md +93 -0
  27. package/docs/design/stdio-simplification-implementation-plan.md +317 -0
  28. package/docs/design/structured-output-tools-coexist-findings.md +288 -0
  29. package/docs/discovery/coordinator-script-design.md +745 -0
  30. package/docs/discovery/coordinator-ux-discovery.md +471 -0
  31. package/docs/discovery/spawn-agent-failure-modes.md +309 -0
  32. package/docs/discovery/workflow-selection-for-discovery-tasks.md +336 -0
  33. package/docs/discovery/worktrain-status-briefing.md +325 -0
  34. package/docs/discovery/worktrain-status-design-candidates.md +202 -0
  35. package/docs/discovery/worktrain-status-design-review-findings.md +86 -0
  36. package/docs/ideas/backlog.md +608 -0
  37. package/docs/ideas/daemon-structured-output-vs-tool-calls.md +344 -0
  38. package/docs/ideas/design-candidates-backlog-consolidation.md +85 -0
  39. package/docs/ideas/design-review-findings-backlog-consolidation.md +39 -0
  40. package/docs/ideas/implementation_plan_backlog_consolidation.md +117 -0
  41. package/docs/plans/authoring-doc-staleness-enforcement-candidates.md +251 -0
  42. package/docs/plans/authoring-doc-staleness-enforcement-review.md +99 -0
  43. package/docs/plans/authoring-doc-staleness-enforcement.md +463 -0
  44. package/package.json +1 -1
@@ -0,0 +1,463 @@
1
+ # Authoring Doc Staleness Enforcement
2
+
3
+ **Status:** Discovery / Design
4
+ **Date:** 2026-04-16
5
+
6
+ **Artifact strategy:** This document is human-readable reference material. It
7
+ is NOT execution truth. Durable execution state lives in WorkRail notes and
8
+ context variables. If the chat rewinds, this file may not reflect the latest
9
+ session state -- consult WorkRail session notes instead.
10
+
11
+ ---
12
+
13
+ ## Context / Ask
14
+
15
+ WorkRail has a known sync problem: when a new engine feature ships, authoring
16
+ documentation lags or never gets updated. We need CI-enforced mechanical checks,
17
+ not process rules.
18
+
19
+ **Existing assets:**
20
+ - `spec/authoring-spec.json` -- 16 topics, ~32 rules. Each rule has `sourceRefs`
21
+ pointing to runtime implementation files. Has `lastReviewed: "2026-04-04"`.
22
+ - `scripts/validate-authoring-spec.js` -- validates structural validity and
23
+ sourceRef path existence.
24
+ - `spec/workflow.schema.json` -- the workflow JSON schema.
25
+ - CI already runs `validate:authoring-spec` but only checks structure and path
26
+ existence, not coverage or freshness.
27
+
28
+ **Key engine surfaces for cross-reference:**
29
+ - `src/application/services/compiler/feature-registry.ts` -- closed-set feature
30
+ IDs: `wr.features.memory_context`, `wr.features.capabilities`,
31
+ `wr.features.subagent_guidance`
32
+ - `src/v2/durable-core/domain/decision-trace-builder.ts` -- loop/condition trace
33
+ logic (no direct authoring surface, but represents control flow primitives)
34
+ - `src/mcp/handlers/v2-reference-resolver.ts` -- workflow reference resolution
35
+ (workspace vs package)
36
+
37
+ ---
38
+
39
+ ## Path Recommendation
40
+
41
+ **landscape_first** -- the dominant need is understanding current gaps and
42
+ designing complementary CI checks. The problem is well-framed, the assets are
43
+ known, so full reframing is not needed.
44
+
45
+ ---
46
+
47
+ ## Constraints / Anti-goals
48
+
49
+ **Constraints:**
50
+ - Must be Node.js scripts (existing tooling pattern, no new build deps)
51
+ - Must be non-blocking on first introduction (warn mode, then harden to fail)
52
+ - Must not require manual maintenance per-rule (defeat the purpose)
53
+ - Must run fast enough for CI (< 5s)
54
+
55
+ **Anti-goals:**
56
+ - Do NOT parse TypeScript ASTs -- too fragile, too expensive
57
+ - Do NOT require authors to update a separate manifest file for every code change
58
+ - Do NOT fail CI on the first day (start advisory, promote to blocking on next PR)
59
+
60
+ ---
61
+
62
+ ## Landscape Packet
63
+
64
+ ### Critical finding: `validate:authoring-spec` is NOT in CI
65
+
66
+ The `validate:authoring-spec` npm script exists but is NOT wired into
67
+ `.github/workflows/ci.yml`. Only `validate:registry` and
68
+ `validate:workflow-discovery` run in CI. This means the structural validation
69
+ that already exists is not enforced on PRs. **Any new checks must also be added
70
+ to ci.yml to have enforcement value.**
71
+
72
+ ### What `validate-authoring-spec.js` already does
73
+
74
+ 1. Schema validation of `authoring-spec.json` via Ajv
75
+ 2. Rule ID uniqueness
76
+ 3. Scope catalog membership for each rule's `scope` array
77
+ 4. `sourceRefs` and `exampleRefs` path existence on disk (not JSON validity for
78
+ .json refs)
79
+ 5. Enforcement mode constraints (`planned` rules may not have `runtime`
80
+ enforcement)
81
+
82
+ ### What it does NOT do
83
+
84
+ - Check whether sourceRef files have been modified since the spec was last reviewed
85
+ - Check whether feature registry IDs have corresponding spec coverage
86
+ - Check whether new schema fields have corresponding spec rules
87
+
88
+ ### `lastReviewed` semantics
89
+
90
+ The spec has a single top-level `lastReviewed: "2026-04-04"` date. Individual
91
+ rules do NOT have per-rule review dates. This means staleness detection must
92
+ work at the whole-spec level or we need to add per-rule `lastReviewed` dates.
93
+
94
+ **Key insight:** Adding per-rule `lastReviewed` is the correct granularity for
95
+ Check 1. The spec version already increments when required rules change, but
96
+ `lastReviewed` exists specifically for "did anyone re-read this rule since the
97
+ source changed?"
98
+
99
+ ### Feature registry state
100
+
101
+ 3 features in `FEATURE_DEFINITIONS`:
102
+ - `wr.features.memory_context` -- referenced in `features-are-closed-set` rule
103
+ - `wr.features.capabilities` -- NOT explicitly named as a known feature in any
104
+ spec rule (the `features-are-closed-set` rule mentions only
105
+ `subagent_guidance` and `memory_context` in its checks text)
106
+ - `wr.features.subagent_guidance` -- referenced in `features-are-closed-set`
107
+ rule
108
+
109
+ **Gap found:** `wr.features.capabilities` is a live feature in the registry but
110
+ is not mentioned in `authoring-spec.json`. This is exactly the kind of drift
111
+ the checks should catch.
112
+
113
+ ---
114
+
115
+ ## Candidate Generation Expectations
116
+
117
+ Path: `landscape_first`. The candidate set must:
118
+ - Reflect actual landscape precedents (validate:registry stamp pattern, existing validate-authoring-spec.js, feature-registry.ts closed-set structure)
119
+ - Address the concrete gaps found (wr.features.capabilities missing, validate:authoring-spec not in CI)
120
+ - Include at least one candidate that exploits an existing pattern rather than inventing a new mechanism
121
+ - NOT drift into free invention (e.g. "add a new MCP tool for spec linting" is out of scope)
122
+ - Candidates should differ in implementation approach, not just in naming
123
+
124
+ ## Candidate Directions
125
+
126
+ ### Check 1: sourceRefs staleness (`--check-staleness` flag)
127
+
128
+ **Approach:** For each rule that has `sourceRefs` pointing to TypeScript/JS
129
+ files, run `git log -1 --format="%as" -- <path>` to get the file's last
130
+ modification date in git. Compare against the rule's `lastReviewed` date
131
+ (falling back to the spec-level `lastReviewed`). Flag rules whose sourceRef
132
+ files were modified after the review date.
133
+
134
+ **Implementation:**
135
+
136
+ Add a `lastReviewed` field to each rule in `authoring-spec.json`. Default to
137
+ the spec-level `lastReviewed` for rules that do not set it.
138
+
139
+ Extend `validate-authoring-spec.js` with a `--check-staleness` flag:
140
+
141
+ ```js
142
+ // Pseudocode for --check-staleness logic (add to validate-authoring-spec.js)
143
+ const { execSync } = require('child_process');
144
+
145
+ function getGitLastModifiedDate(filePath) {
146
+ try {
147
+ const result = execSync(
148
+ `git log -1 --format="%as" -- "${filePath}"`,
149
+ { cwd: repoRoot, encoding: 'utf8' }
150
+ ).trim();
151
+ return result || null; // null if file has no git history
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+
157
+ function checkStaleness(spec) {
158
+ const specLastReviewed = new Date(spec.lastReviewed);
159
+ const staleRules = [];
160
+
161
+ for (const topic of spec.topics) {
162
+ for (const rule of topic.rules) {
163
+ // Per-rule lastReviewed takes precedence over spec-level
164
+ const ruleReviewDate = rule.lastReviewed
165
+ ? new Date(rule.lastReviewed)
166
+ : specLastReviewed;
167
+
168
+ for (const ref of rule.sourceRefs ?? []) {
169
+ // Only check TypeScript/JavaScript source files
170
+ if (!ref.path.match(/\.(ts|js|mts|mjs)$/)) continue;
171
+
172
+ const fullPath = path.join(repoRoot, ref.path);
173
+ const lastModified = getGitLastModifiedDate(fullPath);
174
+ if (!lastModified) continue;
175
+
176
+ if (new Date(lastModified) > ruleReviewDate) {
177
+ staleRules.push({
178
+ ruleId: rule.id,
179
+ sourceRef: ref.path,
180
+ lastModified,
181
+ ruleReviewed: ruleReviewDate.toISOString().slice(0, 10),
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ return staleRules;
189
+ }
190
+
191
+ // In main(), after existing checks:
192
+ if (process.argv.includes('--check-staleness')) {
193
+ const staleRules = checkStaleness(spec);
194
+ if (staleRules.length > 0) {
195
+ console.warn('\n[STALENESS] The following rules have sourceRef files modified after their lastReviewed date:');
196
+ for (const s of staleRules) {
197
+ console.warn(` rule: ${s.ruleId}`);
198
+ console.warn(` sourceRef: ${s.sourceRef}`);
199
+ console.warn(` file last modified: ${s.lastModified}`);
200
+ console.warn(` rule last reviewed: ${s.ruleReviewed}`);
201
+ }
202
+ // On first introduction: warn only. Promote to process.exit(1) after
203
+ // all existing rules have per-rule lastReviewed dates.
204
+ if (process.env.WORKRAIL_STRICT_STALENESS === '1') {
205
+ process.exit(1);
206
+ }
207
+ } else {
208
+ console.log('[STALENESS] All sourceRef files are current relative to lastReviewed dates.');
209
+ }
210
+ }
211
+ ```
212
+
213
+ **Schema change needed:** Add optional `lastReviewed` (date string) field to the
214
+ rule schema in `spec/authoring-spec.schema.json`.
215
+
216
+ **CI integration:**
217
+ ```json
218
+ // package.json -- add to validate:authoring-spec script or as separate target
219
+ "validate:authoring-staleness": "node scripts/validate-authoring-spec.js --check-staleness"
220
+ ```
221
+
222
+ **Limitations:**
223
+ - Git-based: only works inside a git repo (fine for CI)
224
+ - Single `lastReviewed` at spec level is coarse; per-rule dates require a one-time
225
+ backfill
226
+ - Does not detect semantic drift -- only that a file was touched after review
227
+
228
+ ---
229
+
230
+ ### Check 2: feature-registry completeness
231
+
232
+ **Approach:** Read `feature-registry.ts`, extract all feature IDs from
233
+ `FEATURE_DEFINITIONS`, then check that each feature ID appears somewhere in
234
+ `authoring-spec.json` (in any rule's `sourceRefs[].path` or in the rule text
235
+ or `checks` array).
236
+
237
+ Since the file is TypeScript, we cannot `require()` it directly in a Node.js
238
+ script without compilation. Two viable approaches:
239
+
240
+ **Option A: Text extraction (simple, no TS compilation)**
241
+
242
+ Extract feature IDs with a regex match on `feature-registry.ts`:
243
+
244
+ ```js
245
+ // scripts/validate-feature-coverage.js
246
+ const fs = require('fs');
247
+ const path = require('path');
248
+
249
+ const repoRoot = path.resolve(__dirname, '..');
250
+ const registryPath = path.join(repoRoot, 'src/application/services/compiler/feature-registry.ts');
251
+ const specPath = path.join(repoRoot, 'spec/authoring-spec.json');
252
+
253
+ function extractFeatureIds(source) {
254
+ // Match: id: 'wr.features.something'
255
+ const matches = [...source.matchAll(/id:\s*['"]([^'"]+)['"]/g)];
256
+ return matches
257
+ .map(m => m[1])
258
+ .filter(id => id.startsWith('wr.features.'));
259
+ }
260
+
261
+ function collectSpecText(spec) {
262
+ // Gather all text from all rules for full-text search
263
+ const texts = [];
264
+ const visit = (rule) => {
265
+ texts.push(rule.id, rule.rule, ...(rule.checks ?? []), ...(rule.antiPatterns ?? []));
266
+ for (const ref of rule.sourceRefs ?? []) {
267
+ texts.push(ref.path, ref.note ?? '');
268
+ }
269
+ };
270
+ for (const topic of [...(spec.topics ?? []), ...(spec.plannedTopics ?? [])]) {
271
+ for (const rule of topic.rules) visit(rule);
272
+ }
273
+ for (const rule of spec.plannedRules ?? []) visit(rule);
274
+ return texts.join('\n');
275
+ }
276
+
277
+ function main() {
278
+ const registrySource = fs.readFileSync(registryPath, 'utf8');
279
+ const spec = JSON.parse(fs.readFileSync(specPath, 'utf8'));
280
+
281
+ const featureIds = extractFeatureIds(registrySource);
282
+ const specText = collectSpecText(spec);
283
+
284
+ const uncovered = featureIds.filter(id => !specText.includes(id));
285
+
286
+ if (uncovered.length > 0) {
287
+ console.error('[FEATURE-COVERAGE] The following feature IDs from feature-registry.ts have no coverage in authoring-spec.json:');
288
+ for (const id of uncovered) {
289
+ console.error(` - ${id}`);
290
+ }
291
+ console.error('\nAdd a rule to spec/authoring-spec.json that documents this feature, or add it to a sourceRef or checks entry of an existing rule.');
292
+ process.exit(1);
293
+ }
294
+
295
+ console.log(`[FEATURE-COVERAGE] All ${featureIds.length} features covered in authoring-spec.json.`);
296
+ }
297
+
298
+ main();
299
+ ```
300
+
301
+ **Option B: JSON sidecar (more robust, requires maintenance)**
302
+
303
+ Add a `knownFeatureIds` array to `authoring-spec.json` and validate it matches
304
+ the registry. Requires one more file to keep in sync -- worse than Option A.
305
+
306
+ **Recommendation:** Option A. It is self-contained, does not require TS
307
+ compilation, and the regex pattern is stable (feature IDs follow a clear
308
+ naming convention).
309
+
310
+ **CI integration:**
311
+ ```json
312
+ "validate:feature-coverage": "node scripts/validate-feature-coverage.js"
313
+ ```
314
+
315
+ Add to the CI `validate` job alongside `validate:authoring-spec`.
316
+
317
+ **Current gap this would catch:**
318
+ `wr.features.capabilities` is in the registry but not mentioned in
319
+ `authoring-spec.json`. This check would fail immediately on the current state
320
+ of the repo, proving it works.
321
+
322
+ ---
323
+
324
+ ### Check 3: AGENTS.md enforcement rule
325
+
326
+ The following rule text should be added to `AGENTS.md` under a new section
327
+ "Engine feature additions":
328
+
329
+ ```markdown
330
+ ## When adding a new engine feature
331
+
332
+ A "new engine feature" means any of:
333
+ - A new `wr.features.*` entry in `src/application/services/compiler/feature-registry.ts`
334
+ - A new schema field in `spec/workflow.schema.json`
335
+ - A new runtime behavior in `src/v2/durable-core/` or `src/mcp/` that authors
336
+ need to declare, reference, or avoid
337
+
338
+ **Checklist -- all items required before the PR can merge:**
339
+
340
+ - [ ] `spec/authoring-spec.json`: add or update a rule covering the new feature.
341
+ - The rule's `sourceRefs` must include the file that implements the feature.
342
+ - If the feature is a `wr.features.*` ID, the rule's `checks` or rule text
343
+ must mention the full feature ID string.
344
+ - [ ] `spec/authoring-spec.json` `lastReviewed`: update to today's date.
345
+ - [ ] Run `npm run validate:authoring-spec` -- must pass.
346
+ - [ ] Run `npm run validate:feature-coverage` -- must pass (new feature ID must
347
+ appear in spec).
348
+ - [ ] `docs/authoring-v2.md` and/or `docs/authoring.md`: add a section or
349
+ paragraph explaining when and how to use the feature.
350
+ - [ ] No `planned` enforcement on a rule for a feature that is already live in
351
+ runtime -- use `runtime` or `validator`.
352
+ ```
353
+
354
+ ---
355
+
356
+ ## Challenge Notes
357
+
358
+ **Against Check 1 (staleness):**
359
+ - A file touching only comments or formatting would trigger a false positive.
360
+ Mitigation: accept this as a minor cost. A touched file is a signal worth
361
+ reviewing, even if the change was cosmetic.
362
+ - The spec currently has no per-rule `lastReviewed` dates, so the first run
363
+ would compare all rules against the spec-level `2026-04-04` date. Files
364
+ modified after that date would all appear stale even if their corresponding
365
+ rules are still accurate. Mitigation: introduce in warn-only mode first,
366
+ then do a review pass to set per-rule dates.
367
+
368
+ **Against Check 2 (feature coverage):**
369
+ - Regex-based extraction is fragile if the feature definition format changes.
370
+ Mitigation: the format is extremely stable (it's a const array) and the
371
+ regex only needs to match `id: 'wr.features.*'`. If it breaks, the check
372
+ would produce zero IDs (false negative), not a false positive. Add a guard:
373
+ if `featureIds.length === 0`, fail with "extracted 0 feature IDs -- regex
374
+ may be broken."
375
+ - Full-text search in spec text is loose. A feature ID appearing only in a
376
+ comment or antiPatterns entry counts as "covered." This is acceptable --
377
+ the check verifies the ID is acknowledged somewhere, not that the coverage
378
+ is high quality.
379
+
380
+ **Against Check 3 (AGENTS.md rule):**
381
+ - AGENTS.md rules are agent-only -- they do not enforce on human contributors.
382
+ Mitigation: the mechanical checks (1 and 2) handle the human path. The
383
+ AGENTS.md rule handles the agent path.
384
+
385
+ ---
386
+
387
+ ## Resolution Notes
388
+
389
+ All three checks are complementary and non-overlapping:
390
+ - Check 1 catches drift after initial documentation exists (time-based signal)
391
+ - Check 2 catches new features that were never documented (completeness signal)
392
+ - Check 3 provides the agent with a concrete checklist to run before merging
393
+
394
+ **Recommended implementation order:**
395
+ 1. Check 2 (feature coverage) -- highest value, simplest implementation, fails
396
+ immediately on a known gap (`wr.features.capabilities`)
397
+ 2. Check 3 (AGENTS.md rule) -- no code, zero cost, immediate value
398
+ 3. Check 1 (staleness) -- requires schema change + per-rule review date backfill
399
+
400
+ ---
401
+
402
+ ## Decision Log
403
+
404
+ | Date | Decision | Rationale |
405
+ |------|----------|-----------|
406
+ | 2026-04-16 | landscape_first path | Problem is well-framed, assets are known. No need for full reframing. |
407
+ | 2026-04-16 | Option A (regex extraction) for Check 2 | Simpler, no TS compilation, stable feature ID format |
408
+ | 2026-04-16 | Warn-only first for Check 1 | Per-rule lastReviewed dates need backfill before hard failure is fair |
409
+ | 2026-04-16 | AGENTS.md checklist > process prose | Agents respond to checklists better than principles |
410
+ | 2026-04-16 | Candidates A + B selected; C is future evolution; D dismissed | A closes primary gap (feature coverage), B adds time-based drift signal. Challenge failed to find blocking weakness in A. Main risk (regex brittleness) is mitigated by zero-extraction guard. Advisory-to-blocking upgrade path for B is deferred but not abandoned. |
411
+
412
+ ---
413
+
414
+ ## Final Recommendation
415
+
416
+ **Confidence: HIGH**
417
+
418
+ **Selected direction: Hybrid A+**
419
+
420
+ ### PR 1 (primary -- implement first)
421
+ 1. Add `scripts/validate-feature-coverage.js` -- regex-extracts all `wr.features.*` IDs from `feature-registry.ts` and hard-fails if any ID is absent from `authoring-spec.json` text.
422
+ 2. Add `validate:feature-coverage` npm script to `package.json`.
423
+ 3. Wire both `validate:authoring-spec` AND `validate:feature-coverage` into the `validate-workflows` job in `.github/workflows/ci.yml`.
424
+ 4. Update `authoring-spec.json` to add coverage for `wr.features.capabilities` (confirmed gap).
425
+ 5. Add AGENTS.md enforcement checklist (see Check 3 section below).
426
+
427
+ ### PR 2 (follow-up -- create GitHub issue at PR 1 merge time)
428
+ 1. Add `--check-staleness` flag to `validate-authoring-spec.js` with `rule.lastReviewed ?? spec.lastReviewed` fallback.
429
+ 2. Add `validate:authoring-staleness` npm script.
430
+ 3. Wire into CI as an advisory-only step (warn, don't fail).
431
+ 4. Document the advisory-to-blocking upgrade condition in the issue.
432
+
433
+ ### Residual risks
434
+ 1. Coverage is a floor check (text presence), not a quality gate. The AGENTS.md checklist is the quality gate.
435
+ 2. Staleness check deferred to PR 2. If PR 2 never ships, Criterion 2 is unmet.
436
+ 3. Open question: is `wr.features.capabilities` intentionally excluded from the spec? Evidence says no (it's in user-facing bundled workflows), but confirm with project owner before PR 1 merges.
437
+
438
+ ## Final Summary
439
+
440
+ **Path:** landscape_first
441
+ **Problem frame:** Engine velocity vs documentation discipline. The feature registry is a closed-set machine-readable source of truth; `authoring-spec.json` is the documentation target. The gap between them can be measured mechanically at CI time.
442
+ **Landscape takeaways:** (1) `wr.features.capabilities` is confirmed absent from spec but used in user-facing bundled workflows. (2) `validate:authoring-spec` script exists but is NOT in CI. (3) 3+ sourceRef files were modified after the spec's `lastReviewed: 2026-04-04` date. (4) The `stamp-workflow` pattern is the direct precedent for date-based review tracking.
443
+ **Chosen direction:** Hybrid A+ (Candidate A feature coverage + Candidate B staleness advisory with per-rule fallback)
444
+ **Strongest alternative:** Candidate C (per-rule lastReviewed dates) -- lost because the 32-rule backfill cost is not yet justified; spec-level dates are YAGNI-sufficient.
445
+ **Confidence:** HIGH
446
+ **Next actions:**
447
+ 1. Confirm with project owner whether `wr.features.capabilities` is intentionally excluded from spec (evidence says no).
448
+ 2. Implement PR 1 (validate-feature-coverage.js + ci.yml wiring + spec entry for capabilities + AGENTS.md checklist).
449
+ 3. Create a GitHub issue for PR 2 (staleness check) at the time PR 1 merges.
450
+
451
+ Three complementary CI checks designed:
452
+
453
+ 1. **`--check-staleness` flag on `validate-authoring-spec.js`** -- compares git
454
+ modification dates of sourceRef TS files against per-rule `lastReviewed`
455
+ dates. Requires schema change + backfill, introduce in warn mode.
456
+
457
+ 2. **New `scripts/validate-feature-coverage.js`** -- regex-extracts all
458
+ `wr.features.*` IDs from `feature-registry.ts` and confirms each appears
459
+ in `authoring-spec.json`. Self-contained, fast, fails on existing gap
460
+ (`wr.features.capabilities`). Highest priority to implement.
461
+
462
+ 3. **AGENTS.md checklist** -- specific, actionable 6-item checklist for "when
463
+ adding a new engine feature" that references both mechanical checks by name.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.36.0",
3
+ "version": "3.37.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {