@exaudeus/workrail 3.35.1 → 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.
- package/dist/config/config-file.js +2 -0
- package/dist/console-ui/assets/{index-D7jQyCSD.js → index-o-p__sHJ.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/daemon/workflow-runner.d.ts +5 -0
- package/dist/daemon/workflow-runner.js +131 -1
- package/dist/manifest.json +39 -31
- package/dist/mcp/handlers/v2-advance-events.js +1 -1
- package/dist/mcp/handlers/v2-execution/start.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution/start.js +3 -2
- package/dist/trigger/notification-service.d.ts +42 -0
- package/dist/trigger/notification-service.js +164 -0
- package/dist/trigger/trigger-listener.js +7 -1
- package/dist/trigger/trigger-router.d.ts +3 -1
- package/dist/trigger/trigger-router.js +4 -1
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +64 -32
- package/dist/v2/durable-core/schemas/session/events.d.ts +20 -10
- package/dist/v2/durable-core/schemas/session/events.js +1 -1
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +8 -8
- package/dist/v2/durable-core/schemas/session/gaps.js +1 -1
- package/docs/design/agent-behavior-patterns-discovery.md +312 -0
- package/docs/design/agent-engine-communication-discovery.md +390 -0
- package/docs/design/agent-loop-architecture-alternatives-discovery.md +531 -0
- package/docs/design/agent-loop-error-handling-contract.md +238 -0
- package/docs/design/complete-step-approach-validation-discovery.md +344 -0
- package/docs/design/daemon-stuck-detection-discovery.md +174 -0
- package/docs/design/mcp-server-disconnect-discovery.md +245 -0
- package/docs/design/mcp-server-epipe-crash.md +198 -0
- package/docs/design/notification-design-candidates.md +131 -0
- package/docs/design/notification-design-review.md +84 -0
- package/docs/design/notification-implementation-plan.md +181 -0
- package/docs/design/spawn-agent-failure-modes.md +161 -0
- package/docs/design/spawn-agent-result-handling-implementation-plan.md +186 -0
- package/docs/design/stdio-simplification-design-candidates.md +341 -0
- package/docs/design/stdio-simplification-design-review.md +93 -0
- package/docs/design/stdio-simplification-implementation-plan.md +317 -0
- package/docs/design/structured-output-tools-coexist-findings.md +288 -0
- package/docs/discovery/coordinator-script-design.md +745 -0
- package/docs/discovery/coordinator-ux-discovery.md +471 -0
- package/docs/discovery/spawn-agent-failure-modes.md +309 -0
- package/docs/discovery/workflow-selection-for-discovery-tasks.md +336 -0
- package/docs/discovery/worktrain-status-briefing.md +325 -0
- package/docs/discovery/worktrain-status-design-candidates.md +202 -0
- package/docs/discovery/worktrain-status-design-review-findings.md +86 -0
- package/docs/ideas/backlog.md +688 -1
- package/docs/ideas/daemon-structured-output-vs-tool-calls.md +344 -0
- package/docs/ideas/design-candidates-backlog-consolidation.md +85 -0
- package/docs/ideas/design-candidates-spawn-agent-task.md +178 -0
- package/docs/ideas/design-review-findings-backlog-consolidation.md +39 -0
- package/docs/ideas/design-review-findings-spawn-agent-task.md +139 -0
- package/docs/ideas/implementation_plan_backlog_consolidation.md +117 -0
- package/docs/ideas/implementation_plan_spawn_agent.md +217 -0
- package/docs/plans/authoring-doc-staleness-enforcement-candidates.md +251 -0
- package/docs/plans/authoring-doc-staleness-enforcement-review.md +99 -0
- package/docs/plans/authoring-doc-staleness-enforcement.md +463 -0
- 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.
|