@opengsd/gsd-core 1.19.0 → 1.20.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.compact.md +5 -5
- package/agents/gsd-code-fixer.md +9 -4
- package/agents/gsd-code-reviewer.compact.md +49 -8
- package/agents/gsd-code-reviewer.md +53 -14
- package/agents/gsd-executor.md +8 -4
- package/commands/gsd/update.md +5 -2
- package/gsd-core/bin/gsd-tools.cjs +73 -23
- package/gsd-core/bin/lib/capability-lifecycle.cjs +1 -12
- package/gsd-core/bin/lib/capability-registry.cjs +284 -93
- package/gsd-core/bin/lib/capability-validator.cjs +15 -0
- package/gsd-core/bin/lib/commands.cjs +27 -17
- package/gsd-core/bin/lib/config-loader.cjs +3 -3
- package/gsd-core/bin/lib/core-utils.cjs +11 -0
- package/gsd-core/bin/lib/frontmatter.cjs +21 -3
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
- package/gsd-core/bin/lib/gate-context-drift.cjs +1 -1
- package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
- package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
- package/gsd-core/bin/lib/git-info-exclude.cjs +1 -6
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +13 -9
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -3
- package/gsd-core/bin/lib/init.cjs +180 -67
- package/gsd-core/bin/lib/io.cjs +6 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +34 -1
- package/gsd-core/bin/lib/markdown-table.cjs +23 -0
- package/gsd-core/bin/lib/mcp-server.cjs +8 -5
- package/gsd-core/bin/lib/milestone.cjs +18 -50
- package/gsd-core/bin/lib/phase-entry-grammar.cjs +119 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
- package/gsd-core/bin/lib/phase-id.cjs +609 -35
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -74
- package/gsd-core/bin/lib/phase-locator.cjs +328 -19
- package/gsd-core/bin/lib/phase.cjs +2990 -666
- package/gsd-core/bin/lib/planning-document.cjs +26 -5
- package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
- package/gsd-core/bin/lib/planning-snapshot.cjs +12 -7
- package/gsd-core/bin/lib/planning-workspace.cjs +3 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +23 -2
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +730 -144
- package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +8 -5
- package/gsd-core/bin/lib/roadmap.cjs +358 -106
- package/gsd-core/bin/lib/shell-command-projection.cjs +36 -9
- package/gsd-core/bin/lib/smart-entry.cjs +22 -11
- package/gsd-core/bin/lib/state-contract.cjs +11 -7
- package/gsd-core/bin/lib/state-document.cjs +332 -23
- package/gsd-core/bin/lib/state-transition.cjs +39 -10
- package/gsd-core/bin/lib/state.cjs +498 -179
- package/gsd-core/bin/lib/template.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +3 -2
- package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
- package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
- package/gsd-core/bin/lib/verify.cjs +2 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +26 -20
- package/gsd-core/bin/verify-reapply-patches.cjs +411 -10
- package/gsd-core/references/cicd-review-patterns.md +45 -0
- package/gsd-core/references/necessity-review.md +48 -0
- package/gsd-core/workflows/code-review-fix.md +46 -5
- package/gsd-core/workflows/code-review.md +3 -0
- package/gsd-core/workflows/execute-phase.md +1 -1
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
- package/gsd-core/workflows/plan-phase.md +1 -1
- package/gsd-core/workflows/pr-branch.md +2 -0
- package/gsd-core/workflows/reapply-patches.md +104 -0
- package/gsd-core/workflows/review.md +54 -141
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +57 -9
- package/gsd-core/workflows/ship.md +12 -0
- package/hooks/dist/gsd-graphify-update.sh +6 -0
- package/hooks/dist/gsd-statusline.js +3 -2
- package/hooks/dist/gsd-windsurf-pre-write.js +4 -4
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +4 -4
- package/hooks/dist/lib/git-probe.js +16 -0
- package/hooks/gsd-graphify-update.sh +6 -0
- package/hooks/gsd-statusline.js +3 -2
- package/hooks/gsd-windsurf-pre-write.js +4 -4
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +4 -4
- package/hooks/lib/git-probe.js +16 -0
- package/package.json +3 -3
- package/pi/package-agents/gsd-code-fixer.md +9 -4
- package/pi/package-agents/gsd-code-reviewer.md +53 -14
- package/pi/package-agents/gsd-executor.md +8 -4
- package/pi/package-agents/manifest.json +3 -3
- package/pi/package-skills/gsd-update/SKILL.md +4 -1
- package/scripts/ci-next-health.cjs +246 -23
- package/scripts/docs-guard-registry.cjs +2 -0
- package/scripts/gen-features.cjs +14 -2
- package/scripts/lib/drift-scan.cjs +27 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +6 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +11 -0
- package/scripts/lint-phase-enumeration-drift.cjs +8 -4
- package/scripts/lint-roadmap-progress-drift.cjs +421 -0
- package/scripts/mutation-matrix.cjs +23 -1
- package/scripts/sync-next-version.cjs +38 -6
- package/skills/gsd-update/SKILL.md +5 -2
- package/vscode/package.json +1 -1
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* third-party overlay manifests (ADR-2782 D4).
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.LANE_UNAVAILABLE = void 0;
|
|
26
|
+
exports.CONTEXT_WINDOW_HEADROOM = exports.LANE_UNAVAILABLE = void 0;
|
|
27
27
|
exports.configString = configString;
|
|
28
28
|
exports.normalizeHost = normalizeHost;
|
|
29
29
|
exports.resolveLaneEffort = resolveLaneEffort;
|
|
@@ -33,7 +33,10 @@ exports.isEmptyReview = isEmptyReview;
|
|
|
33
33
|
exports.fileRefPrompt = fileRefPrompt;
|
|
34
34
|
exports.applyPromptOverride = applyPromptOverride;
|
|
35
35
|
exports.artifactPaths = artifactPaths;
|
|
36
|
-
exports.
|
|
36
|
+
exports.resolveLaneCap = resolveLaneCap;
|
|
37
|
+
exports.checkPromptAgainstCap = checkPromptAgainstCap;
|
|
38
|
+
exports.refuseIfOverCap = refuseIfOverCap;
|
|
39
|
+
exports.renderPromptRefusalStub = renderPromptRefusalStub;
|
|
37
40
|
exports.resolveLanePlan = resolveLanePlan;
|
|
38
41
|
const review_lane_descriptor_cjs_1 = require("./review-lane-descriptor.cjs");
|
|
39
42
|
/* ------------------------------------------------------------------ *
|
|
@@ -60,8 +63,12 @@ exports.LANE_UNAVAILABLE = Object.freeze({
|
|
|
60
63
|
PROBE_TIMEOUT: 'probe_timeout',
|
|
61
64
|
HOST_UNREACHABLE: 'host_unreachable',
|
|
62
65
|
EGRESS_HOST_CHANGED: 'egress_host_changed',
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
/**
|
|
67
|
+
* #4559: the prompt a lane would receive is larger than its prompt cap (an explicit cap, or the
|
|
68
|
+
* default derived from the lane's context window). The lane is REFUSED — never trimmed, because a
|
|
69
|
+
* silently truncated plan set is a review of something other than the plans.
|
|
70
|
+
*/
|
|
71
|
+
PROMPT_EXCEEDS_CONTEXT: 'prompt_exceeds_context',
|
|
65
72
|
});
|
|
66
73
|
/** Every handler name the runner can dispatch. Mirrors `LaneHandler` (D6's closed enum). */
|
|
67
74
|
const KNOWN_HANDLERS = new Set(['antigravity', 'openai-compatible', 'opencode']);
|
|
@@ -233,7 +240,8 @@ function fileRefPrompt(promptPath, repoRoot) {
|
|
|
233
240
|
/**
|
|
234
241
|
* Re-point a resolved plan at a different prompt file (`review-lane invoke --prompt-file`, #5049).
|
|
235
242
|
*
|
|
236
|
-
* The override carries a
|
|
243
|
+
* The override carries a caller-substituted prompt (a section subset, say; nothing trims a prompt
|
|
244
|
+
* to fit a lane since #4559), so it has to land wherever THIS lane actually
|
|
237
245
|
* reads its prompt, and that differs by channel:
|
|
238
246
|
* - `stdin`: the runner streams the file `plan.stdin` names.
|
|
239
247
|
* - `argv-file-ref`: the prompt PATH is embedded in a sentence in argv (`fileRefPrompt`), already
|
|
@@ -279,25 +287,124 @@ function artifactPaths(runDir, slug) {
|
|
|
279
287
|
};
|
|
280
288
|
}
|
|
281
289
|
/**
|
|
282
|
-
*
|
|
290
|
+
* Fraction of a lane's declared context window a prompt may occupy (#4559). The remaining 35%
|
|
291
|
+
* absorbs the system prompt, the reply, and `estimateTokens` being a chars/4 estimate rather than
|
|
292
|
+
* a tokenizer (code tokenizes denser than prose).
|
|
293
|
+
*/
|
|
294
|
+
exports.CONTEXT_WINDOW_HEADROOM = 0.65;
|
|
295
|
+
/**
|
|
296
|
+
* Per-lane prompt cap (#2797 semantics, extended by #4559).
|
|
283
297
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
298
|
+
* Precedence: per-lane `promptBudgetKey` → global `review.max_prompt_tokens` → the default derived
|
|
299
|
+
* from the lane's context window (`floor(window * CONTEXT_WINDOW_HEADROOM)`). `-1` is the UNSET
|
|
300
|
+
* sentinel for the per-lane key, because `0` is a legitimate value meaning "no cap for this lane".
|
|
301
|
+
* An explicit `0` at either explicit level also turns the derived default off.
|
|
302
|
+
*
|
|
303
|
+
* The window is the operator's `contextWindowConfigKey` value when that is a positive finite number
|
|
304
|
+
* (`0` disables the derived cap), else the lane's declared `contextWindowTokens`. Both are narrowed
|
|
305
|
+
* from `unknown` — overlay lanes and config values cross a trust boundary — and anything that is not
|
|
306
|
+
* a positive finite number narrows to "unset".
|
|
287
307
|
*
|
|
288
308
|
* Single source of truth: `gsd-core/bin/gsd-tools.cjs`'s `review-lane plan`/`invoke` and
|
|
289
|
-
* `src/reviewer-step-dispatch.cts`'s `dispatchReviewerLanes` both resolve a lane's
|
|
309
|
+
* `src/reviewer-step-dispatch.cts`'s `dispatchReviewerLanes` both resolve a lane's cap through
|
|
290
310
|
* this function rather than each carrying their own copy (#4209 R3 — two verbatim copies drift).
|
|
291
311
|
*/
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
312
|
+
function resolveLaneCap(lane, configGet) {
|
|
313
|
+
// A spawn lane whose prompt channel is `none` (coderabbit) is fed no prompt, so there is nothing
|
|
314
|
+
// to cap: no key — explicit, global or derived — may ever refuse it (#4559, design row 18).
|
|
315
|
+
const laneRaw = lane;
|
|
316
|
+
if (laneRaw?.transport === 'spawn' && laneRaw.invoke?.promptChannel === 'none') {
|
|
317
|
+
return { cap: null, source: 'none', contextWindowTokens: null };
|
|
318
|
+
}
|
|
296
319
|
const isNum = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
|
|
320
|
+
const isPositive = (v) => isNum(v) && v > 0;
|
|
321
|
+
const raw = lane;
|
|
322
|
+
const budgetKey = typeof raw?.promptBudgetKey === 'string' && raw.promptBudgetKey ? raw.promptBudgetKey : null;
|
|
323
|
+
if (budgetKey) {
|
|
324
|
+
const per = configGet(budgetKey);
|
|
325
|
+
if (isNum(per) && per !== -1)
|
|
326
|
+
return { cap: per, source: 'lane', contextWindowTokens: null };
|
|
327
|
+
const global = configGet('review.max_prompt_tokens');
|
|
328
|
+
if (isNum(global))
|
|
329
|
+
return { cap: global, source: 'global', contextWindowTokens: null };
|
|
330
|
+
}
|
|
331
|
+
const windowKey = typeof raw?.contextWindowConfigKey === 'string' && raw.contextWindowConfigKey ? raw.contextWindowConfigKey : null;
|
|
332
|
+
const override = windowKey ? configGet(windowKey) : undefined;
|
|
333
|
+
if (override === 0)
|
|
334
|
+
return { cap: null, source: 'none', contextWindowTokens: null };
|
|
335
|
+
const windowTokens = isPositive(override)
|
|
336
|
+
? override
|
|
337
|
+
: isPositive(raw?.contextWindowTokens)
|
|
338
|
+
? raw.contextWindowTokens
|
|
339
|
+
: null;
|
|
340
|
+
if (windowTokens === null)
|
|
341
|
+
return { cap: null, source: 'none', contextWindowTokens: null };
|
|
342
|
+
return {
|
|
343
|
+
cap: Math.floor(windowTokens * exports.CONTEXT_WINDOW_HEADROOM),
|
|
344
|
+
source: 'context-window',
|
|
345
|
+
contextWindowTokens: windowTokens,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* The ONE refuse-or-run decision (#4559), shared by `review-lane invoke` and `dispatchReviewerLanes`.
|
|
350
|
+
* Refuses only when a positive cap is STRICTLY exceeded: a prompt exactly at the cap runs, and a
|
|
351
|
+
* cap of `0` or `null` never refuses. `detail` names the estimate, the cap, where the cap came from
|
|
352
|
+
* and the key to change, so the refusal is actionable rather than a bare "too big".
|
|
353
|
+
*/
|
|
354
|
+
function checkPromptAgainstCap(estimatedTokens, capInfo, lane) {
|
|
355
|
+
const { cap, source, contextWindowTokens } = capInfo;
|
|
356
|
+
if (cap === null || !(cap > 0) || !(estimatedTokens > cap))
|
|
357
|
+
return { ok: true };
|
|
358
|
+
const raw = lane;
|
|
359
|
+
const slug = typeof raw?.slug === 'string' ? raw.slug : '';
|
|
360
|
+
const budgetKey = typeof raw?.promptBudgetKey === 'string' && raw.promptBudgetKey ? raw.promptBudgetKey : null;
|
|
361
|
+
const windowKey = typeof raw?.contextWindowConfigKey === 'string' && raw.contextWindowConfigKey ? raw.contextWindowConfigKey : null;
|
|
362
|
+
// Remedy keys are the lane's DECLARED keys (an overlay lane's differ from the slug convention).
|
|
363
|
+
const remedy = source === 'lane'
|
|
364
|
+
? `raise ${budgetKey ?? 'the lane prompt cap'} or set it to 0 to remove the cap`
|
|
365
|
+
: source === 'global'
|
|
366
|
+
? `raise review.max_prompt_tokens${budgetKey ? ` or set ${budgetKey}` : ''}`
|
|
367
|
+
: windowKey
|
|
368
|
+
? `raise ${windowKey} if this lane's model has a larger window, or set it to 0 to remove the cap`
|
|
369
|
+
: budgetKey
|
|
370
|
+
? `this lane's context window is fixed by its declaration; set review.max_prompt_tokens or ${budgetKey} to override`
|
|
371
|
+
: `this lane's cap comes from its declared contextWindowTokens and the lane declares no key to override it`;
|
|
372
|
+
const windowNote = contextWindowTokens !== null ? `, ${contextWindowTokens}-token context window` : '';
|
|
373
|
+
return {
|
|
374
|
+
ok: false,
|
|
375
|
+
reason: exports.LANE_UNAVAILABLE.PROMPT_EXCEEDS_CONTEXT,
|
|
376
|
+
detail: `estimated ${estimatedTokens} tokens exceeds the ${cap}-token cap for lane '${slug}' ` +
|
|
377
|
+
`(source: ${source}${windowNote}); ${remedy}`,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Resolve the lane's cap and decide refuse-or-run in one pure step (#4559): `null` = run, otherwise
|
|
382
|
+
* the structured refusal. `review-lane invoke` and `dispatchReviewerLanes` both call this, so the
|
|
383
|
+
* two surfaces cannot disagree about whether, or how, a lane is refused.
|
|
384
|
+
*/
|
|
385
|
+
function refuseIfOverCap(input) {
|
|
386
|
+
const capInfo = resolveLaneCap(input.lane, input.configGet);
|
|
387
|
+
const verdict = checkPromptAgainstCap(input.estimatedTokens, capInfo, input.lane);
|
|
388
|
+
if (verdict.ok)
|
|
389
|
+
return null;
|
|
390
|
+
return {
|
|
391
|
+
ok: false,
|
|
392
|
+
reason: verdict.reason,
|
|
393
|
+
detail: verdict.detail,
|
|
394
|
+
refusal: {
|
|
395
|
+
estimatedTokens: input.estimatedTokens,
|
|
396
|
+
cap: capInfo.cap,
|
|
397
|
+
capSource: capInfo.source,
|
|
398
|
+
contextWindowTokens: capInfo.contextWindowTokens,
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* The text written to a refused lane's review file (#4559). The marker
|
|
404
|
+
* `review did not run: prompt exceeds` is stable: `review.md`'s skip gate and coverage loop key on it.
|
|
405
|
+
*/
|
|
406
|
+
function renderPromptRefusalStub(slug, detail) {
|
|
407
|
+
return `${slug} review did not run: prompt exceeds this lane's context cap (${detail})\n`;
|
|
301
408
|
}
|
|
302
409
|
/* ------------------------------------------------------------------ *
|
|
303
410
|
* Resolution
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
*
|
|
30
30
|
* The bounded source-review prompt built here is METADATA ONLY — repository root, canonical
|
|
31
31
|
* file paths, review depth, base SHA, and four fixed prohibitions. It never embeds file
|
|
32
|
-
* contents. If the assembled prompt exceeds a lane's resolved
|
|
33
|
-
*
|
|
32
|
+
* contents. If the assembled prompt exceeds a lane's resolved prompt cap (an explicit cap or the
|
|
33
|
+
* default derived from its context window, #4559), that lane's dispatch hard-fails with reason
|
|
34
|
+
* `prompt_exceeds_context` before `invoke` runs for it — no silent truncation of the file list.
|
|
34
35
|
*/
|
|
35
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -125,9 +126,8 @@ function validatePaths(repoRoot, paths) {
|
|
|
125
126
|
}
|
|
126
127
|
return { ok: true };
|
|
127
128
|
}
|
|
128
|
-
// `
|
|
129
|
-
//
|
|
130
|
-
// `!== 0`. See the call site below.
|
|
129
|
+
// `resolveLaneCap` (review-lane-invocation.cjs) resolves the cap; `null` and a resolved `0` both
|
|
130
|
+
// mean unbounded (#2797), and `checkPromptAgainstCap` owns that test. See the call site below.
|
|
131
131
|
/**
|
|
132
132
|
* One-line depth definition for an external reviewer lane, condensed from `<depth_levels>` in
|
|
133
133
|
* `agents/gsd-code-reviewer.md` (#4209 review: a bare `quick`/`standard`/`deep` label means
|
|
@@ -296,13 +296,15 @@ async function dispatchReviewerLanes(input, deps) {
|
|
|
296
296
|
anyFailed = true;
|
|
297
297
|
continue;
|
|
298
298
|
}
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
// #4559: refuse-not-trim, through the same check `review-lane invoke` uses.
|
|
300
|
+
const refused = (0, review_lane_invocation_cjs_1.refuseIfOverCap)({ lane, estimatedTokens, configGet });
|
|
301
|
+
if (refused) {
|
|
301
302
|
results.push({
|
|
302
303
|
slug,
|
|
303
304
|
ok: false,
|
|
304
|
-
reason:
|
|
305
|
-
detail:
|
|
305
|
+
reason: refused.reason,
|
|
306
|
+
detail: refused.detail,
|
|
307
|
+
refusal: refused.refusal,
|
|
306
308
|
});
|
|
307
309
|
anyFailed = true;
|
|
308
310
|
continue;
|