mandrel 2.14.0 → 2.15.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/.agents/audit-checklists/navigability.md +1 -1
- package/.agents/docs/workflows.md +4 -5
- package/.agents/scripts/acceptance-eval.js +18 -1
- package/.agents/scripts/agents-bootstrap-github.js +22 -1
- package/.agents/scripts/apply-quality-bootstrap.js +6 -0
- package/.agents/scripts/audit-labels-bootstrap.js +15 -1
- package/.agents/scripts/audit-to-stories.js +26 -1
- package/.agents/scripts/boot-sweep.js +4 -1
- package/.agents/scripts/bootstrap.js +1 -0
- package/.agents/scripts/check-arch-cycles.js +20 -0
- package/.agents/scripts/check-baselines.js +8 -2
- package/.agents/scripts/check-context-budget.js +40 -5
- package/.agents/scripts/check-dead-exports.js +21 -0
- package/.agents/scripts/check-doc-links.js +12 -1
- package/.agents/scripts/check-lifecycle-doc-drift.js +9 -0
- package/.agents/scripts/check-workflow-citations.js +332 -0
- package/.agents/scripts/deliver-light.js +1 -0
- package/.agents/scripts/deliver-recover.js +4 -1
- package/.agents/scripts/diagnose-friction.js +17 -1
- package/.agents/scripts/diagnose.js +20 -14
- package/.agents/scripts/drain-pending-cleanup.js +20 -1
- package/.agents/scripts/evidence-gate.js +20 -1
- package/.agents/scripts/generate-config-docs.js +14 -1
- package/.agents/scripts/generate-lifecycle-docs.js +14 -1
- package/.agents/scripts/generate-workflows-doc.js +14 -1
- package/.agents/scripts/git-cleanup.js +32 -1
- package/.agents/scripts/lib/cli-usage.js +174 -0
- package/.agents/scripts/lib/cli-utils.js +12 -0
- package/.agents/scripts/lib/doc-tiers.js +53 -10
- package/.agents/scripts/lib/orchestration/plan-context.js +16 -11
- package/.agents/scripts/lib/workflow-closure.js +431 -0
- package/.agents/scripts/mandrel-update-preflight.js +9 -0
- package/.agents/scripts/nav-registry-diff.js +13 -0
- package/.agents/scripts/plan-context.js +17 -1
- package/.agents/scripts/plan-critics.js +10 -0
- package/.agents/scripts/plan-persist.js +33 -1
- package/.agents/scripts/plan-run-epilogue.js +12 -1
- package/.agents/scripts/quality-preview.js +17 -1
- package/.agents/scripts/resolve-doc-tiers.js +13 -0
- package/.agents/scripts/resolve-stories.js +1 -0
- package/.agents/scripts/resync-status-column.js +4 -1
- package/.agents/scripts/signals-view.js +11 -0
- package/.agents/scripts/single-story-close.js +24 -0
- package/.agents/scripts/single-story-confirm-merge.js +16 -0
- package/.agents/scripts/single-story-init.js +21 -1
- package/.agents/scripts/stories-wave-tick.js +1 -0
- package/.agents/scripts/sync-agentrc.js +16 -4
- package/.agents/scripts/update-ticket-state.js +23 -2
- package/.agents/workflows/audit-navigability.md +2 -2
- package/.agents/workflows/audit-to-stories.md +1 -1
- package/.agents/workflows/deliver.md +80 -81
- package/.agents/workflows/git-cleanup.md +9 -14
- package/.agents/workflows/helpers/acceptance-self-eval.md +14 -13
- package/.agents/workflows/helpers/audit-lens-core.md +2 -2
- package/.agents/workflows/helpers/code-review.md +11 -11
- package/.agents/workflows/helpers/deliver-digest.md +7 -8
- package/.agents/workflows/{deliver-light.md → helpers/deliver-light.md} +72 -24
- package/.agents/workflows/helpers/deliver-reference.md +46 -14
- package/.agents/workflows/helpers/deliver-story-reference.md +55 -63
- package/.agents/workflows/helpers/deliver-story.md +22 -22
- package/.agents/workflows/helpers/mandrel-sync-config.md +2 -2
- package/.agents/workflows/helpers/parallel-tooling.md +1 -2
- package/.agents/workflows/helpers/plan-reference.md +72 -14
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -4
- package/.agents/workflows/mandrel-update.md +6 -6
- package/.agents/workflows/plan.md +86 -83
- package/docs/CHANGELOG.md +15 -0
- package/package.json +2 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.agents/scripts/lib/cli-usage.js` — the one implementation of `--help` for
|
|
3
|
+
* every top-level script under `.agents/scripts/`.
|
|
4
|
+
*
|
|
5
|
+
* ## Why
|
|
6
|
+
*
|
|
7
|
+
* A script's flag contract used to live in the workflow prose that invoked it
|
|
8
|
+
* (`deliver-story.md` Step 0 restating `single-story-init.js`'s `--dry-run` /
|
|
9
|
+
* `--steal`, and so on). Two homes for one contract is a drift class: the
|
|
10
|
+
* script changes, the prose does not, and the next agent runs a flag that no
|
|
11
|
+
* longer exists. Moving the enumeration into the script's own `--help` gives
|
|
12
|
+
* the contract a single home that ships with the code that implements it.
|
|
13
|
+
*
|
|
14
|
+
* ## Contract
|
|
15
|
+
*
|
|
16
|
+
* `--help` (or `-h`) is a **query**, never an error path:
|
|
17
|
+
*
|
|
18
|
+
* - writes non-empty text to **stdout** and exits **0**;
|
|
19
|
+
* - performs no GitHub write, acquires no lease, mutates no working tree.
|
|
20
|
+
*
|
|
21
|
+
* stdout — not `Logger.info` — because help output must survive
|
|
22
|
+
* `AGENT_LOG_LEVEL=silent` and carry no `[Orchestrator]` decoration; this is
|
|
23
|
+
* the same `process.stdout.write` carve-out that machine-parsable envelopes
|
|
24
|
+
* use (see `tests/enforcement/no-console.test.js`).
|
|
25
|
+
*
|
|
26
|
+
* The short-circuit itself lives in `runAsCli` (`lib/cli-utils.js`), which
|
|
27
|
+
* fires this module **before** the script's `main` runs — so the "no side
|
|
28
|
+
* effects" half of the contract holds structurally rather than by each
|
|
29
|
+
* script remembering to check first.
|
|
30
|
+
*
|
|
31
|
+
* ## Usage
|
|
32
|
+
*
|
|
33
|
+
* runAsCli(import.meta.url, main, {
|
|
34
|
+
* source: 'my-script',
|
|
35
|
+
* usage: {
|
|
36
|
+
* invocation: 'node .agents/scripts/my-script.js --story <id> [--json]',
|
|
37
|
+
* summary: 'One line on what the script does.',
|
|
38
|
+
* flags: [
|
|
39
|
+
* ['--story <id>', 'GitHub issue number of the Story (required).'],
|
|
40
|
+
* ['--json', 'Emit the envelope as JSON instead of prose.'],
|
|
41
|
+
* ],
|
|
42
|
+
* },
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* A pre-rendered string is accepted too, so a script that already shipped a
|
|
46
|
+
* hand-written `HELP` block adopts the shared gate without its observable
|
|
47
|
+
* text changing (`usage: HELP`).
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/** Tokens that request help. */
|
|
51
|
+
export const HELP_FLAGS = Object.freeze(['--help', '-h']);
|
|
52
|
+
|
|
53
|
+
/** Left column width for the flag table; longer flags wrap to their own line. */
|
|
54
|
+
const FLAG_COLUMN = 22;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Did the caller ask for help? Scans up to the `--` end-of-flags separator so
|
|
58
|
+
* a positional literally named `--help` after `--` is not mistaken for a
|
|
59
|
+
* request.
|
|
60
|
+
*
|
|
61
|
+
* @param {string[]} argv Argument vector without the node/script entries.
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
export function wantsHelp(argv = []) {
|
|
65
|
+
if (!Array.isArray(argv)) return false;
|
|
66
|
+
for (const token of argv) {
|
|
67
|
+
if (token === '--') return false;
|
|
68
|
+
if (HELP_FLAGS.includes(token)) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Normalize one flag entry into `{ flag, description }`. Accepts a
|
|
75
|
+
* `[flag, description]` pair or an object, so call sites can use whichever
|
|
76
|
+
* reads better next to their option table.
|
|
77
|
+
*
|
|
78
|
+
* @param {[string, string]|{flag: string, description?: string}} entry
|
|
79
|
+
* @returns {{ flag: string, description: string }}
|
|
80
|
+
*/
|
|
81
|
+
function normalizeFlag(entry) {
|
|
82
|
+
if (Array.isArray(entry)) {
|
|
83
|
+
return {
|
|
84
|
+
flag: String(entry[0] ?? ''),
|
|
85
|
+
description: String(entry[1] ?? ''),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
flag: String(entry?.flag ?? ''),
|
|
90
|
+
description: String(entry?.description ?? ''),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Render one ` --flag description` row, wrapping onto a second line when
|
|
96
|
+
* the flag itself is wider than the column.
|
|
97
|
+
*
|
|
98
|
+
* @param {{ flag: string, description: string }} row
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
function renderFlagRow({ flag, description }) {
|
|
102
|
+
if (!description) return ` ${flag}`;
|
|
103
|
+
if (flag.length >= FLAG_COLUMN) {
|
|
104
|
+
return ` ${flag}\n ${' '.repeat(FLAG_COLUMN)}${description}`;
|
|
105
|
+
}
|
|
106
|
+
return ` ${flag.padEnd(FLAG_COLUMN)}${description}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Append the `--help` row unless the spec already documents it, so every
|
|
111
|
+
* rendered block is self-describing without each call site repeating it.
|
|
112
|
+
*
|
|
113
|
+
* @param {Array<{ flag: string, description: string }>} rows
|
|
114
|
+
* @returns {Array<{ flag: string, description: string }>}
|
|
115
|
+
*/
|
|
116
|
+
function withHelpRow(rows) {
|
|
117
|
+
const documented = rows.some((r) => r.flag.split(/[\s,]/)[0] === '--help');
|
|
118
|
+
if (documented) return rows;
|
|
119
|
+
return [...rows, { flag: '--help', description: 'Show this message.' }];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Render a usage spec into the text `--help` prints.
|
|
124
|
+
*
|
|
125
|
+
* @param {{
|
|
126
|
+
* invocation: string,
|
|
127
|
+
* summary?: string,
|
|
128
|
+
* flags?: Array<[string, string]|{flag: string, description?: string}>,
|
|
129
|
+
* notes?: string[],
|
|
130
|
+
* }} spec
|
|
131
|
+
* @returns {string} Text block, newline-terminated.
|
|
132
|
+
*/
|
|
133
|
+
export function formatUsage(spec) {
|
|
134
|
+
const rows = withHelpRow((spec?.flags ?? []).map(normalizeFlag));
|
|
135
|
+
const blocks = [`Usage: ${spec?.invocation ?? ''}`.trim()];
|
|
136
|
+
if (spec?.summary) blocks.push(spec.summary);
|
|
137
|
+
blocks.push(['Flags:', ...rows.map(renderFlagRow)].join('\n'));
|
|
138
|
+
for (const note of spec?.notes ?? []) blocks.push(note);
|
|
139
|
+
return `${blocks.join('\n\n')}\n`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Coerce either accepted `usage` shape — a spec object or a pre-rendered
|
|
144
|
+
* string — into the text to print.
|
|
145
|
+
*
|
|
146
|
+
* @param {object|string} usage
|
|
147
|
+
* @returns {string}
|
|
148
|
+
*/
|
|
149
|
+
export function renderUsage(usage) {
|
|
150
|
+
if (typeof usage === 'string') {
|
|
151
|
+
return usage.endsWith('\n') ? usage : `${usage}\n`;
|
|
152
|
+
}
|
|
153
|
+
return formatUsage(usage);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Answer a help request. Returns `true` when help was requested (and printed),
|
|
158
|
+
* `false` when the caller should carry on with its normal path.
|
|
159
|
+
*
|
|
160
|
+
* Never throws on a missing/blank `usage`: a script whose spec renders empty
|
|
161
|
+
* still owes the contract non-empty stdout, so a minimal line is emitted
|
|
162
|
+
* rather than a silent exit that reads as a broken CLI.
|
|
163
|
+
*
|
|
164
|
+
* @param {string[]} argv
|
|
165
|
+
* @param {object|string} usage
|
|
166
|
+
* @param {{ write: (s: string) => void }} [out] Defaults to `process.stdout`.
|
|
167
|
+
* @returns {boolean}
|
|
168
|
+
*/
|
|
169
|
+
export function respondToHelp(argv, usage, out = process.stdout) {
|
|
170
|
+
if (!wantsHelp(argv)) return false;
|
|
171
|
+
const text = renderUsage(usage ?? '');
|
|
172
|
+
out.write(text.trim().length > 0 ? text : 'Usage: (no flags documented)\n');
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { respondToHelp } from './cli-usage.js';
|
|
19
20
|
import { formatCliError } from './error-redactor.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -37,12 +38,21 @@ export function isDirectInvocation(importMetaUrl) {
|
|
|
37
38
|
* `main` is funnelled through either the caller-supplied `onError` callback
|
|
38
39
|
* or the default handler (prefixed stderr line + `process.exit(exitCode)`).
|
|
39
40
|
*
|
|
41
|
+
* A `usage` option makes the script self-describing: when the argv carries
|
|
42
|
+
* `--help` / `-h`, the rendered usage block goes to stdout and `main` is
|
|
43
|
+
* **never invoked**. That ordering is the whole point — it makes "`--help`
|
|
44
|
+
* performs no GitHub write, acquires no lease, and mutates no working tree"
|
|
45
|
+
* structurally true for every adopting script, rather than something each
|
|
46
|
+
* `main` has to remember to check before its first side effect.
|
|
47
|
+
*
|
|
40
48
|
* @param {string} importMetaUrl Caller's `import.meta.url`.
|
|
41
49
|
* @param {() => Promise<unknown>} main The CLI's main function.
|
|
42
50
|
* @param {object} [options]
|
|
43
51
|
* @param {string} [options.source='CLI'] Prefix used in the default error message.
|
|
44
52
|
* @param {number} [options.exitCode=1] Exit code used by the default error handler.
|
|
45
53
|
* @param {(err: Error) => void} [options.onError] Full override of the error handler.
|
|
54
|
+
* @param {object|string} [options.usage] Usage spec (or pre-rendered
|
|
55
|
+
* text) printed for `--help`; see `lib/cli-usage.js`.
|
|
46
56
|
*/
|
|
47
57
|
export function runAsCli(importMetaUrl, main, options = {}) {
|
|
48
58
|
if (!isDirectInvocation(importMetaUrl)) return;
|
|
@@ -52,7 +62,9 @@ export function runAsCli(importMetaUrl, main, options = {}) {
|
|
|
52
62
|
onError,
|
|
53
63
|
propagateExitCode = false,
|
|
54
64
|
errorPrefix,
|
|
65
|
+
usage,
|
|
55
66
|
} = options;
|
|
67
|
+
if (usage && respondToHelp(process.argv.slice(2), usage)) return;
|
|
56
68
|
const promise = main();
|
|
57
69
|
if (propagateExitCode) {
|
|
58
70
|
promise.then((code) => process.exit(code ?? 0));
|
|
@@ -29,11 +29,19 @@
|
|
|
29
29
|
* converted spawn boots on **instead of** the always-loaded
|
|
30
30
|
* closure, so it is budgeted independently (per-file ≤8KB
|
|
31
31
|
* ceiling gated by `check-context-budget.js`).
|
|
32
|
+
* - `workflow` / — the `.agents/workflows/**` read-tier (Story #4752),
|
|
33
|
+
* `workflowOnDemand` resolved as each entry point's transitive
|
|
34
|
+
* markdown-link closure by
|
|
35
|
+
* [`workflow-closure.js`](workflow-closure.js): the files
|
|
36
|
+
* an entry point's `mandatoryReads:` frontmatter forces
|
|
37
|
+
* you to read (`workflow`, gated) versus the reachable
|
|
38
|
+
* remainder (`workflowOnDemand`, recorded only).
|
|
32
39
|
*
|
|
33
40
|
* A file that could appear in more than one tier is kept in its **highest**
|
|
34
41
|
* tier only (alwaysLoaded > mandatoryRead > digestVisible > onDemand), so the
|
|
35
42
|
* arrays partition the doc set with no double-counting. `agentBoot` is disjoint
|
|
36
|
-
* from the read-tiers (it lives under `.agents/agents/`, not the doc/rules set)
|
|
43
|
+
* from the read-tiers (it lives under `.agents/agents/`, not the doc/rules set),
|
|
44
|
+
* and so are the workflow tiers (confined to `.agents/workflows/`).
|
|
37
45
|
*
|
|
38
46
|
* The closure is discovered by parsing `@`-import references and following
|
|
39
47
|
* them recursively (cycle-safe via a visited set). A candidate `@`-token only
|
|
@@ -47,6 +55,7 @@
|
|
|
47
55
|
|
|
48
56
|
import nodeFs from 'node:fs';
|
|
49
57
|
import path from 'node:path';
|
|
58
|
+
import { resolveWorkflowClosures } from './workflow-closure.js';
|
|
50
59
|
|
|
51
60
|
/**
|
|
52
61
|
* Basename of the always-loaded entry document (the root of the closure).
|
|
@@ -212,18 +221,31 @@ export function docsContextPaths(config) {
|
|
|
212
221
|
}
|
|
213
222
|
|
|
214
223
|
/**
|
|
215
|
-
* Resolve the
|
|
224
|
+
* Resolve the documentation read-tiers, each entry `{ path, bytes }`,
|
|
216
225
|
* partitioned so no path appears in more than one tier (highest tier wins).
|
|
226
|
+
* `workflowClosure` rides alongside the tiers as the per-entry-point workflow
|
|
227
|
+
* measurement (Story #4752) — its `reachableTotalBytes` is a recorded drift
|
|
228
|
+
* signal, never a gate.
|
|
217
229
|
*
|
|
218
230
|
* @param {object} config resolved config (`resolveConfig()` output)
|
|
219
231
|
* @param {{ root?: string, fs?: FsLike }} [opts]
|
|
220
|
-
* @returns {{
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
232
|
+
* @returns {{
|
|
233
|
+
* tiers: {
|
|
234
|
+
* alwaysLoaded: Array<{ path: string, bytes: number }>,
|
|
235
|
+
* mandatoryRead: Array<{ path: string, bytes: number }>,
|
|
236
|
+
* digestVisible: Array<{ path: string, bytes: number }>,
|
|
237
|
+
* onDemand: Array<{ path: string, bytes: number }>,
|
|
238
|
+
* agentBoot: Array<{ path: string, bytes: number }>,
|
|
239
|
+
* workflow: Array<{ path: string, bytes: number }>,
|
|
240
|
+
* workflowOnDemand: Array<{ path: string, bytes: number }>,
|
|
241
|
+
* },
|
|
242
|
+
* workflowClosure: {
|
|
243
|
+
* mandatoryTotalBytes: number,
|
|
244
|
+
* reachableTotalBytes: number,
|
|
245
|
+
* entryPoints: Array<{ path: string, mandatoryBytes: number, reachableBytes: number }>,
|
|
246
|
+
* },
|
|
247
|
+
* }}
|
|
248
|
+
* @throws {Error} on an unresolvable `mandatoryReads` entry or a mandatory cycle
|
|
227
249
|
*/
|
|
228
250
|
export function resolveDocTiers(
|
|
229
251
|
config,
|
|
@@ -265,8 +287,29 @@ export function resolveDocTiers(
|
|
|
265
287
|
// are standalone system prompts, disjoint from the doc read-tiers.
|
|
266
288
|
const agentBoot = collect(listAgentDefs(root, fs));
|
|
267
289
|
|
|
290
|
+
// 6. workflow: each entry point's transitive markdown-link closure (#4752),
|
|
291
|
+
// split into the gated mandatory set and the recorded on-demand remainder.
|
|
292
|
+
// The walk is confined to `.agents/workflows/**`, and `collect` still
|
|
293
|
+
// de-dupes, so no path is counted against two tiers.
|
|
294
|
+
const closure = resolveWorkflowClosures(root, { fs });
|
|
295
|
+
const workflow = collect(closure.mandatoryFiles.map((e) => e.path));
|
|
296
|
+
const workflowOnDemand = collect(closure.onDemandFiles.map((e) => e.path));
|
|
297
|
+
|
|
268
298
|
return {
|
|
269
|
-
tiers: {
|
|
299
|
+
tiers: {
|
|
300
|
+
alwaysLoaded,
|
|
301
|
+
mandatoryRead,
|
|
302
|
+
digestVisible,
|
|
303
|
+
onDemand,
|
|
304
|
+
agentBoot,
|
|
305
|
+
workflow,
|
|
306
|
+
workflowOnDemand,
|
|
307
|
+
},
|
|
308
|
+
workflowClosure: {
|
|
309
|
+
mandatoryTotalBytes: closure.mandatoryTotalBytes,
|
|
310
|
+
reachableTotalBytes: closure.reachableTotalBytes,
|
|
311
|
+
entryPoints: closure.entryPoints,
|
|
312
|
+
},
|
|
270
313
|
};
|
|
271
314
|
}
|
|
272
315
|
|
|
@@ -356,7 +356,7 @@ function resolveRiskHeuristics(config = {}) {
|
|
|
356
356
|
|
|
357
357
|
/**
|
|
358
358
|
* Ceilings a seed's advisory complexity signals must fit for the plan
|
|
359
|
-
* workflow to **suggest**
|
|
359
|
+
* workflow to **suggest** the light path at Gate #1 (Story #4741 R3 plan-side
|
|
360
360
|
* handshake). Framework constants, not operator knobs — mirroring the
|
|
361
361
|
* conservative intent of `complexity-gate.js`'s `STORY_SHAPE_CEILINGS`
|
|
362
362
|
* (small, mostly-additive, non-sensitive) but read against the *seed-time*
|
|
@@ -364,9 +364,14 @@ function resolveRiskHeuristics(config = {}) {
|
|
|
364
364
|
*
|
|
365
365
|
* The suggestion is **advisory only and never an automatic reroute**: it
|
|
366
366
|
* surfaces at Gate #1 for the operator to decide, and under `--yes` it is
|
|
367
|
-
* recorded on the envelope while planning proceeds unchanged.
|
|
368
|
-
*
|
|
369
|
-
*
|
|
367
|
+
* recorded on the envelope while planning proceeds unchanged.
|
|
368
|
+
*
|
|
369
|
+
* These ceilings are deliberately NOT the ones the light path itself applies
|
|
370
|
+
* (Story #4760). A confirmed suggestion routes into
|
|
371
|
+
* `workflows/helpers/deliver-light.md`, whose gate re-judges the *predicted
|
|
372
|
+
* shape* against `STORY_SHAPE_CEILINGS`. Two checks at two different stages:
|
|
373
|
+
* this one screens a seed, that one decides. Collapsing them would make a
|
|
374
|
+
* confirm a bypass.
|
|
370
375
|
*
|
|
371
376
|
* - `maxArtifacts` — enumerated seed items (one artifact each).
|
|
372
377
|
* - `maxRiskHeuristicHits` — any risk-heuristic hit disqualifies: risk
|
|
@@ -381,9 +386,9 @@ const DELIVER_LIGHT_SUGGESTION_CEILINGS = Object.freeze({
|
|
|
381
386
|
});
|
|
382
387
|
|
|
383
388
|
/**
|
|
384
|
-
* Derive the advisory
|
|
385
|
-
*
|
|
386
|
-
*
|
|
389
|
+
* Derive the advisory light-path suggestion from a seed's complexity signals
|
|
390
|
+
* (Story #4741 AC-6). Pure and total: a malformed / missing signal bag fails
|
|
391
|
+
* conservative (not suggested), never throws.
|
|
387
392
|
*
|
|
388
393
|
* `automatic: false` is part of the contract — the suggestion is surfaced for
|
|
389
394
|
* the operator, never a silent reroute of a non-interactive run.
|
|
@@ -435,16 +440,16 @@ export function buildDeliverLightSuggestion(complexitySignals) {
|
|
|
435
440
|
ceilings,
|
|
436
441
|
reasons: suggested
|
|
437
442
|
? [
|
|
438
|
-
`seed fits the
|
|
443
|
+
`seed fits the light-path ceilings (≤${ceilings.maxArtifacts} artifacts, ` +
|
|
439
444
|
'no risk-heuristic hits, no sensitive-path classes) — the operator ' +
|
|
440
|
-
'may prefer /deliver
|
|
445
|
+
'may prefer /deliver for this scope',
|
|
441
446
|
]
|
|
442
447
|
: reasons,
|
|
443
448
|
};
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
/**
|
|
447
|
-
* Attach the advisory
|
|
452
|
+
* Attach the advisory light-path suggestion to a complexity-signals bag
|
|
448
453
|
* as a **nested** field (Story #4741). Nesting — rather than a new top-level
|
|
449
454
|
* envelope key — keeps every existing per-mode envelope key set byte-stable
|
|
450
455
|
* (AC-5): the suggestion is derived from the signals it rides on.
|
|
@@ -920,7 +925,7 @@ function extractPriorArtifacts(priorBody) {
|
|
|
920
925
|
/**
|
|
921
926
|
* Build the amendment (delta) envelope — `plan-context --amends #<id>`
|
|
922
927
|
* (Story #4741 AC-4, R3-A). The heavy-amendment counterpart to routing a
|
|
923
|
-
* light amendment through
|
|
928
|
+
* light amendment through the light path: instead of re-interrogating the
|
|
924
929
|
* repo from scratch (`buildAuthoringContext`'s codebase snapshot and the BDD /
|
|
925
930
|
* memory / feedback probes), the envelope composes a DELTA from what already
|
|
926
931
|
* exists — the prior Story's body, its acceptance criteria (the real
|