@opengsd/gsd-core 1.12.0 → 1.13.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/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +239 -67
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +195 -50
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-registry.cjs +514 -114
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +120 -4
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +85 -2
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +337 -13
- package/gsd-core/bin/lib/config-loader.cjs +3 -0
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +132 -15
- package/gsd-core/bin/lib/install-engine.cjs +184 -12
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +22 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/milestone.cjs +22 -2
- package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-id.cjs +251 -9
- package/gsd-core/bin/lib/phase.cjs +774 -35
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +147 -20
- package/gsd-core/bin/lib/planning-workspace.cjs +103 -28
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-parser.cjs +499 -26
- package/gsd-core/bin/lib/roadmap.cjs +187 -58
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +233 -33
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +286 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +215 -43
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +30 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +23 -13
- package/gsd-core/bin/lib/state-transition.cjs +333 -44
- package/gsd-core/bin/lib/state.cjs +684 -125
- package/gsd-core/bin/lib/surface.cjs +23 -8
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1419 -515
- package/gsd-core/bin/lib/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +273 -12
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +346 -16
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +8 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +2 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +14 -9
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +2 -0
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +15 -13
- package/gsd-core/workflows/code-review-fix.md +2 -0
- package/gsd-core/workflows/code-review.md +73 -31
- package/gsd-core/workflows/complete-milestone.md +13 -4
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update.md +1 -1
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +17 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +32 -14
- package/gsd-core/workflows/execute-plan.md +8 -8
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +2 -0
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +1 -1
- package/gsd-core/workflows/new-project.md +5 -3
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +19 -4
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +130 -12
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +11 -3
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +13 -3
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +41 -38
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +49 -3
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +283 -12
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-statusline.js +7 -3
- package/hooks/dist/gsd-validate-commit.sh +444 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +283 -12
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-statusline.js +7 -3
- package/hooks/gsd-validate-commit.sh +444 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +5 -5
- package/scripts/build-hooks.js +11 -4
- package/scripts/ci-test-scope.cjs +7 -0
- package/scripts/docs-guard-registry.cjs +10 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +5 -0
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +524 -0
- package/scripts/lint-test-file-count.allowlist.json +3 -1
- package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +8 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/vscode/package.json +1 -1
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
* 4. `flags: string[]` — Antigravity is selected by BOTH `--antigravity` and
|
|
46
46
|
* `--agy`, which a single-valued field cannot express. This also flattens
|
|
47
47
|
* D8's uniqueness invariant across every lane's flags.
|
|
48
|
+
* 5. `NATIVE_TIMEOUT` — a lane whose CLI takes its own native inner timeout flag (today only
|
|
49
|
+
* antigravity's `--print-timeout`) declares where the resolved value goes; `resolveLanePlan`
|
|
50
|
+
* computes what it is from the same resolved outer `timeoutMs` (#3274).
|
|
48
51
|
*
|
|
49
52
|
* Phase 2 (#2795) implements the manifest validator against the amended
|
|
50
53
|
* vocabulary, which is the point of amending rather than leaving it to be
|
|
@@ -69,7 +72,7 @@ exports.checkReviewerDocsParity = checkReviewerDocsParity;
|
|
|
69
72
|
* and vanishes when it has nothing to contribute (no model configured, no effort channel, prompt on
|
|
70
73
|
* stdin), which is what lets one template serve the configured and unconfigured cases.
|
|
71
74
|
*
|
|
72
|
-
* This is a closed
|
|
75
|
+
* This is a closed five-member vocabulary with no expressions, no nesting and no conditionals — a
|
|
73
76
|
* placeholder set, deliberately not a template language. The moment it needs a conditional, the
|
|
74
77
|
* lane wants a `handler` instead (D6).
|
|
75
78
|
*/
|
|
@@ -82,6 +85,9 @@ exports.ARGV_PLACEHOLDER = Object.freeze({
|
|
|
82
85
|
OUTPUT: '{{output}}',
|
|
83
86
|
/** The argv-borne prompt, or nothing unless `promptChannel` is `argv`/`argv-file-ref`. */
|
|
84
87
|
PROMPT: '{{prompt}}',
|
|
88
|
+
/** A lane's own CLI-native inner timeout duration, derived from the resolved outer `timeoutMs`
|
|
89
|
+
* (never independently configured) — see `resolveLanePlan`'s expansion of this token. */
|
|
90
|
+
NATIVE_TIMEOUT: '{{nativeTimeout}}',
|
|
85
91
|
});
|
|
86
92
|
const SPAWN_STDIN_STDOUT = {
|
|
87
93
|
promptChannel: 'stdin',
|
|
@@ -107,12 +113,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
107
113
|
effortChannel: 'none',
|
|
108
114
|
},
|
|
109
115
|
timeoutFloorMs: 900_000,
|
|
116
|
+
timeoutConfigKey: 'review.timeouts.gemini',
|
|
110
117
|
emptyOutput: 'stub-with-stderr',
|
|
111
118
|
reviewsSection: 'Gemini',
|
|
112
119
|
evidenceClass: 'source-grounded',
|
|
113
120
|
requiresBinaries: [],
|
|
114
121
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.gemini',
|
|
115
122
|
modelConfigKey: 'review.models.gemini',
|
|
123
|
+
effortConfigKey: null,
|
|
124
|
+
defaultEffort: null,
|
|
116
125
|
handler: null,
|
|
117
126
|
},
|
|
118
127
|
{
|
|
@@ -139,12 +148,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
139
148
|
env: { CLAUDE_CODE_DISABLE_CLAUDE_MDS: '1', CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1' },
|
|
140
149
|
},
|
|
141
150
|
timeoutFloorMs: 1_200_000,
|
|
151
|
+
timeoutConfigKey: 'review.timeouts.claude',
|
|
142
152
|
emptyOutput: 'stub-with-stderr',
|
|
143
153
|
reviewsSection: 'Claude',
|
|
144
154
|
evidenceClass: 'source-grounded',
|
|
145
155
|
requiresBinaries: [],
|
|
146
156
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.claude',
|
|
147
157
|
modelConfigKey: 'review.models.claude',
|
|
158
|
+
effortConfigKey: 'review.effort.claude',
|
|
159
|
+
defaultEffort: 'high',
|
|
148
160
|
handler: null,
|
|
149
161
|
},
|
|
150
162
|
{
|
|
@@ -167,12 +179,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
167
179
|
effortChannel: 'argv',
|
|
168
180
|
},
|
|
169
181
|
timeoutFloorMs: 1_200_000,
|
|
182
|
+
timeoutConfigKey: 'review.timeouts.codex',
|
|
170
183
|
emptyOutput: 'stub-with-stderr',
|
|
171
184
|
reviewsSection: 'Codex',
|
|
172
185
|
evidenceClass: 'source-grounded',
|
|
173
186
|
requiresBinaries: [],
|
|
174
187
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.codex',
|
|
175
188
|
modelConfigKey: 'review.models.codex',
|
|
189
|
+
effortConfigKey: 'review.effort.codex',
|
|
190
|
+
defaultEffort: 'high',
|
|
176
191
|
handler: null,
|
|
177
192
|
},
|
|
178
193
|
{
|
|
@@ -192,6 +207,7 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
192
207
|
effortChannel: 'none',
|
|
193
208
|
},
|
|
194
209
|
timeoutFloorMs: 360_000,
|
|
210
|
+
timeoutConfigKey: null,
|
|
195
211
|
emptyOutput: 'stub-with-stderr',
|
|
196
212
|
reviewsSection: 'CodeRabbit',
|
|
197
213
|
evidenceClass: 'diff-only',
|
|
@@ -199,6 +215,8 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
199
215
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.coderabbit',
|
|
200
216
|
// Accepts no model flag at all (review.md:367) — not merely "none configured".
|
|
201
217
|
modelConfigKey: null,
|
|
218
|
+
effortConfigKey: null,
|
|
219
|
+
defaultEffort: null,
|
|
202
220
|
handler: null,
|
|
203
221
|
},
|
|
204
222
|
{
|
|
@@ -217,6 +235,7 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
217
235
|
effortChannel: 'argv',
|
|
218
236
|
},
|
|
219
237
|
timeoutFloorMs: 660_000,
|
|
238
|
+
timeoutConfigKey: 'review.timeouts.opencode',
|
|
220
239
|
emptyOutput: 'stub-with-stderr',
|
|
221
240
|
reviewsSection: 'OpenCode',
|
|
222
241
|
evidenceClass: 'source-grounded',
|
|
@@ -225,6 +244,8 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
225
244
|
requiresBinaries: [],
|
|
226
245
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.opencode',
|
|
227
246
|
modelConfigKey: 'review.models.opencode',
|
|
247
|
+
effortConfigKey: 'review.effort.opencode',
|
|
248
|
+
defaultEffort: 'high',
|
|
228
249
|
// Phase 5b (#2799): was `null`. The review is REBUILT from assistant `text` parts; a plain
|
|
229
250
|
// stdout copy would write the raw JSON envelope as the review (#1936). See LaneHandler.
|
|
230
251
|
handler: 'opencode',
|
|
@@ -242,12 +263,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
242
263
|
effortChannel: 'none',
|
|
243
264
|
},
|
|
244
265
|
timeoutFloorMs: 900_000,
|
|
266
|
+
timeoutConfigKey: null,
|
|
245
267
|
emptyOutput: 'stub-with-stderr',
|
|
246
268
|
reviewsSection: 'Qwen',
|
|
247
269
|
evidenceClass: 'source-grounded',
|
|
248
270
|
requiresBinaries: [],
|
|
249
271
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.qwen',
|
|
250
272
|
modelConfigKey: null,
|
|
273
|
+
effortConfigKey: null,
|
|
274
|
+
defaultEffort: null,
|
|
251
275
|
handler: null,
|
|
252
276
|
},
|
|
253
277
|
{
|
|
@@ -260,19 +284,23 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
260
284
|
probe: { kind: 'command-exists', binary: 'cursor-agent' },
|
|
261
285
|
invoke: {
|
|
262
286
|
binary: 'cursor-agent',
|
|
263
|
-
args: ['-p', '--mode', 'ask', '--trust', '--output-format', 'text', '{{prompt}}'],
|
|
287
|
+
args: ['-p', '{{model}}', '--mode', 'ask', '--trust', '--output-format', 'text', '{{prompt}}'],
|
|
264
288
|
promptChannel: 'argv-file-ref',
|
|
265
289
|
outputChannel: 'stdout',
|
|
266
|
-
modelArg:
|
|
290
|
+
modelArg: '--model',
|
|
267
291
|
effortChannel: 'none',
|
|
268
292
|
},
|
|
269
293
|
timeoutFloorMs: 900_000,
|
|
294
|
+
timeoutConfigKey: null,
|
|
270
295
|
emptyOutput: 'stub-with-stderr',
|
|
271
296
|
reviewsSection: 'Cursor',
|
|
272
297
|
evidenceClass: 'source-grounded',
|
|
273
298
|
requiresBinaries: [],
|
|
274
299
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.cursor',
|
|
275
|
-
|
|
300
|
+
// #3653: cursor-agent exposes --model (204 selectable models); wired the same as codex.
|
|
301
|
+
modelConfigKey: 'review.models.cursor',
|
|
302
|
+
effortConfigKey: null,
|
|
303
|
+
defaultEffort: null,
|
|
276
304
|
handler: null,
|
|
277
305
|
},
|
|
278
306
|
{
|
|
@@ -286,13 +314,19 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
286
314
|
probe: { kind: 'command-exists', binary: 'agy' },
|
|
287
315
|
invoke: {
|
|
288
316
|
binary: 'agy',
|
|
289
|
-
|
|
317
|
+
// `{{nativeTimeout}}` is the fifth ARGV_PLACEHOLDER member (#3274) — `resolveLanePlan`
|
|
318
|
+
// (review-lane-invocation.cts) expands it to a value DERIVED from this same lane's resolved
|
|
319
|
+
// outer `timeoutMs`, so the native `--print-timeout` and the outer wall-clock cap can never
|
|
320
|
+
// drift apart. No other shipped lane's `args` template contains this token, so the expansion
|
|
321
|
+
// is inert everywhere else.
|
|
322
|
+
args: ['--print-timeout', '{{nativeTimeout}}', '{{model}}', '-p', '{{prompt}}'],
|
|
290
323
|
promptChannel: 'argv-file-ref',
|
|
291
324
|
outputChannel: 'stdout',
|
|
292
325
|
modelArg: '--model',
|
|
293
326
|
effortChannel: 'none',
|
|
294
327
|
},
|
|
295
328
|
timeoutFloorMs: 600_000,
|
|
329
|
+
timeoutConfigKey: 'review.timeouts.antigravity',
|
|
296
330
|
emptyOutput: 'handler-owned',
|
|
297
331
|
reviewsSection: 'Antigravity',
|
|
298
332
|
evidenceClass: 'source-grounded',
|
|
@@ -302,6 +336,8 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
302
336
|
// NOT `review.models.antigravity` — the shipped key is `review.models.agy` (review.md:291) and
|
|
303
337
|
// Phase 4 federated it under that name. This lane is why the key is declared, not derived.
|
|
304
338
|
modelConfigKey: 'review.models.agy',
|
|
339
|
+
effortConfigKey: null,
|
|
340
|
+
defaultEffort: null,
|
|
305
341
|
handler: 'antigravity',
|
|
306
342
|
},
|
|
307
343
|
{
|
|
@@ -323,6 +359,7 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
323
359
|
effortChannel: 'none',
|
|
324
360
|
},
|
|
325
361
|
timeoutFloorMs: 120_000,
|
|
362
|
+
timeoutConfigKey: 'review.timeouts.ollama',
|
|
326
363
|
emptyOutput: 'stub-with-stderr',
|
|
327
364
|
reviewsSection: 'Ollama',
|
|
328
365
|
evidenceClass: 'source-grounded',
|
|
@@ -331,6 +368,8 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
331
368
|
requiresBinaries: [],
|
|
332
369
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.ollama',
|
|
333
370
|
modelConfigKey: 'review.models.ollama',
|
|
371
|
+
effortConfigKey: null,
|
|
372
|
+
defaultEffort: null,
|
|
334
373
|
handler: 'openai-compatible',
|
|
335
374
|
},
|
|
336
375
|
{
|
|
@@ -352,12 +391,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
352
391
|
effortChannel: 'none',
|
|
353
392
|
},
|
|
354
393
|
timeoutFloorMs: 120_000,
|
|
394
|
+
timeoutConfigKey: 'review.timeouts.lm_studio',
|
|
355
395
|
emptyOutput: 'stub-with-stderr',
|
|
356
396
|
reviewsSection: 'LM Studio',
|
|
357
397
|
evidenceClass: 'source-grounded',
|
|
358
398
|
requiresBinaries: [],
|
|
359
399
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.lm_studio',
|
|
360
400
|
modelConfigKey: 'review.models.lm_studio',
|
|
401
|
+
effortConfigKey: null,
|
|
402
|
+
defaultEffort: null,
|
|
361
403
|
handler: 'openai-compatible',
|
|
362
404
|
},
|
|
363
405
|
{
|
|
@@ -379,12 +421,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
379
421
|
effortChannel: 'none',
|
|
380
422
|
},
|
|
381
423
|
timeoutFloorMs: 120_000,
|
|
424
|
+
timeoutConfigKey: 'review.timeouts.llama_cpp',
|
|
382
425
|
emptyOutput: 'stub-with-stderr',
|
|
383
426
|
reviewsSection: 'llama.cpp',
|
|
384
427
|
evidenceClass: 'source-grounded',
|
|
385
428
|
requiresBinaries: [],
|
|
386
429
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.llama_cpp',
|
|
387
430
|
modelConfigKey: 'review.models.llama_cpp',
|
|
431
|
+
effortConfigKey: null,
|
|
432
|
+
defaultEffort: null,
|
|
388
433
|
handler: 'openai-compatible',
|
|
389
434
|
},
|
|
390
435
|
{
|
|
@@ -424,12 +469,15 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
424
469
|
effortChannel: 'none',
|
|
425
470
|
},
|
|
426
471
|
timeoutFloorMs: 900_000,
|
|
472
|
+
timeoutConfigKey: 'review.timeouts.kimi-code',
|
|
427
473
|
emptyOutput: 'stub-with-stderr',
|
|
428
474
|
reviewsSection: 'Kimi Code',
|
|
429
475
|
evidenceClass: 'source-grounded',
|
|
430
476
|
requiresBinaries: [],
|
|
431
477
|
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.kimi-code',
|
|
432
478
|
modelConfigKey: 'review.models.kimi-code',
|
|
479
|
+
effortConfigKey: null,
|
|
480
|
+
defaultEffort: null,
|
|
433
481
|
handler: null,
|
|
434
482
|
},
|
|
435
483
|
].map((lane) => Object.freeze(lane)));
|
|
@@ -26,6 +26,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
exports.LANE_UNAVAILABLE = void 0;
|
|
27
27
|
exports.configString = configString;
|
|
28
28
|
exports.normalizeHost = normalizeHost;
|
|
29
|
+
exports.resolveLaneEffort = resolveLaneEffort;
|
|
30
|
+
exports.resolveTimeoutMs = resolveTimeoutMs;
|
|
31
|
+
exports.nativeTimeoutToken = nativeTimeoutToken;
|
|
29
32
|
exports.isEmptyReview = isEmptyReview;
|
|
30
33
|
exports.fileRefPrompt = fileRefPrompt;
|
|
31
34
|
exports.resolveLanePlan = resolveLanePlan;
|
|
@@ -122,6 +125,73 @@ function normalizeHost(raw) {
|
|
|
122
125
|
const pathPart = u.pathname.replace(/\/+$/, '');
|
|
123
126
|
return `${scheme}//${host}${port ? `:${port}` : ''}${pathPart}`;
|
|
124
127
|
}
|
|
128
|
+
/** Levels GSD's effort axis accepts (#3533). `inherit` selects the no-argument path. */
|
|
129
|
+
const EFFORT_LEVELS = new Set([
|
|
130
|
+
'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'inherit',
|
|
131
|
+
]);
|
|
132
|
+
/**
|
|
133
|
+
* Resolve one lane's reasoning effort from REVIEW configuration (#4255).
|
|
134
|
+
*
|
|
135
|
+
* Resolution order, highest first:
|
|
136
|
+
* 1. `lane.effortConfigKey` — the per-lane review effort the operator set
|
|
137
|
+
* 2. `lane.defaultEffort` — the lane's declared review default (`high` for prompt-fed,
|
|
138
|
+
* source-grounded lanes)
|
|
139
|
+
* 3. nothing — no effort argument is emitted and the reviewer CLI's own configuration decides
|
|
140
|
+
*
|
|
141
|
+
* A configured `'inherit'` selects (3) explicitly. An unrecognized level is REFUSED rather than
|
|
142
|
+
* passed to the host: it falls back to the lane default, because forwarding a typo would render an
|
|
143
|
+
* argument the CLI rejects and kill the lane outright.
|
|
144
|
+
*
|
|
145
|
+
* What this function deliberately does NOT do is consult any agent's execution settings. Before
|
|
146
|
+
* #4255 the level came from `gsd-plan-checker`'s installed frontmatter through a hardcoded agent
|
|
147
|
+
* id, so every lane ran at a fast structural verifier's `low` — and, because the rendered argument
|
|
148
|
+
* is a CLI config override, it silently beat the effort the operator had configured for that CLI
|
|
149
|
+
* itself. A value inherited from an unrelated agent is worse than no value at all, which is why
|
|
150
|
+
* (3) emits nothing rather than falling back to some other agent's number.
|
|
151
|
+
*
|
|
152
|
+
* `renderArgv` is injected (the host table and the ADR-2481 surface negotiation live in
|
|
153
|
+
* `model-catalog` / `commands`, above this module's layer) so this stays a pure function of its
|
|
154
|
+
* inputs and the golden lane table can assert it without a spawn.
|
|
155
|
+
*/
|
|
156
|
+
function resolveLaneEffort(lane, configGet, renderArgv) {
|
|
157
|
+
const none = { argv: [], value: null, source: 'none' };
|
|
158
|
+
if (!lane || typeof lane !== 'object')
|
|
159
|
+
return none;
|
|
160
|
+
const configured = lane.effortConfigKey ? configString(configGet(lane.effortConfigKey)) : null;
|
|
161
|
+
const valid = configured !== null && EFFORT_LEVELS.has(configured) ? configured : null;
|
|
162
|
+
const level = valid ?? configString(lane.defaultEffort);
|
|
163
|
+
if (level === null || level === 'inherit')
|
|
164
|
+
return none;
|
|
165
|
+
const rendered = renderArgv(lane.slug, level);
|
|
166
|
+
const argv = (rendered.argv ?? []).filter((a) => typeof a === 'string' && a !== '');
|
|
167
|
+
if (argv.length === 0)
|
|
168
|
+
return none;
|
|
169
|
+
return {
|
|
170
|
+
argv,
|
|
171
|
+
value: configString(rendered.value) ?? level,
|
|
172
|
+
source: valid !== null ? 'config' : 'lane-default',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function resolveTimeoutMs(timeoutConfigKey, floorMs, configGet) {
|
|
176
|
+
const configuredSeconds = typeof timeoutConfigKey === 'string' ? configGet(timeoutConfigKey) : undefined;
|
|
177
|
+
return typeof configuredSeconds === 'number' && Number.isFinite(configuredSeconds) && configuredSeconds > 0
|
|
178
|
+
? configuredSeconds * 1000
|
|
179
|
+
: floorMs;
|
|
180
|
+
}
|
|
181
|
+
/** Buffer (seconds) a lane's native inner timeout sits under its resolved outer wall-clock cap
|
|
182
|
+
* (#3274). Matches the shipped 600s outer / 540s native relationship exactly when unconfigured:
|
|
183
|
+
* floor(600000/1000) - 60 = 540. */
|
|
184
|
+
const NATIVE_TIMEOUT_BUFFER_SECONDS = 60;
|
|
185
|
+
/**
|
|
186
|
+
* Render the `{{nativeTimeout}}` argv placeholder from a lane's resolved outer timeout (#3274).
|
|
187
|
+
*
|
|
188
|
+
* Clamped to a 1-second floor so a very small configured (or, today, only-ever-default) outer
|
|
189
|
+
* timeout never produces a zero or negative duration string a CLI would reject or misinterpret.
|
|
190
|
+
*/
|
|
191
|
+
function nativeTimeoutToken(timeoutMs) {
|
|
192
|
+
const seconds = Math.max(1, Math.floor(timeoutMs / 1000) - NATIVE_TIMEOUT_BUFFER_SECONDS);
|
|
193
|
+
return `${seconds}s`;
|
|
194
|
+
}
|
|
125
195
|
/**
|
|
126
196
|
* Classify a lane's output as a review or as empty.
|
|
127
197
|
*
|
|
@@ -212,9 +282,10 @@ function resolveLanePlan(input) {
|
|
|
212
282
|
return fail(exports.LANE_UNAVAILABLE.UNKNOWN_HANDLER, `lane '${slug}' names handler '${String(handler)}', which this GSD version does not provide`);
|
|
213
283
|
}
|
|
214
284
|
const { promptPath, reviewPath, errPath } = artifactPaths(input.runDir, slug);
|
|
215
|
-
const
|
|
285
|
+
const floorMs = typeof lane.timeoutFloorMs === 'number' && Number.isFinite(lane.timeoutFloorMs) && lane.timeoutFloorMs > 0
|
|
216
286
|
? lane.timeoutFloorMs
|
|
217
287
|
: 900_000;
|
|
288
|
+
const timeoutMs = resolveTimeoutMs(lane.timeoutConfigKey, floorMs, input.configGet);
|
|
218
289
|
const emptyOutput = lane.emptyOutput === 'handler-owned' ? 'handler-owned' : 'stub-with-stderr';
|
|
219
290
|
// #3194: only an EXACT 'diff-only' declaration exempts a lane from evidence verification.
|
|
220
291
|
// Anything else — including a missing or garbage value on a third-party overlay body —
|
|
@@ -319,6 +390,7 @@ function resolveLanePlan(input) {
|
|
|
319
390
|
'{{effort}}': effortExpansion,
|
|
320
391
|
'{{output}}': outputExpansion,
|
|
321
392
|
'{{prompt}}': promptExpansion,
|
|
393
|
+
'{{nativeTimeout}}': [nativeTimeoutToken(timeoutMs)],
|
|
322
394
|
};
|
|
323
395
|
const template = Array.isArray(inv.args)
|
|
324
396
|
? inv.args.filter((a) => typeof a === 'string')
|
|
@@ -27,12 +27,13 @@
|
|
|
27
27
|
* "failed" and "ran cleanly with nothing to report" IS the defect this epic closes (#2494/#2605).
|
|
28
28
|
*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.MODEL_VALUE_MAX = exports.BANNER_SCAN_LINES = exports.UNRESOLVED_MODEL = exports.MODEL_SOURCE = void 0;
|
|
30
|
+
exports.ANTIGRAVITY_FAILURE_MODE = exports.MODEL_VALUE_MAX = exports.BANNER_SCAN_LINES = exports.UNRESOLVED_MODEL = exports.MODEL_SOURCE = void 0;
|
|
31
31
|
exports.parseModelBanner = parseModelBanner;
|
|
32
32
|
exports.parseTranscriptModel = parseTranscriptModel;
|
|
33
33
|
exports.checkEgressHost = checkEgressHost;
|
|
34
34
|
exports.probeLane = probeLane;
|
|
35
35
|
exports.writeReviewOrStub = writeReviewOrStub;
|
|
36
|
+
exports.emptyOutputDiagnosis = emptyOutputDiagnosis;
|
|
36
37
|
exports.handleOpencodeOutput = handleOpencodeOutput;
|
|
37
38
|
exports.antigravityWatermark = antigravityWatermark;
|
|
38
39
|
exports.antigravityTranscriptFallback = antigravityTranscriptFallback;
|
|
@@ -40,6 +41,7 @@ exports.antigravityModel = antigravityModel;
|
|
|
40
41
|
exports.resolveSpawnModel = resolveSpawnModel;
|
|
41
42
|
exports.antigravityPrompt = antigravityPrompt;
|
|
42
43
|
exports.antigravityArgv = antigravityArgv;
|
|
44
|
+
exports.antigravityFailureMode = antigravityFailureMode;
|
|
43
45
|
exports.antigravityDiagnostic = antigravityDiagnostic;
|
|
44
46
|
exports.stampBlindReview = stampBlindReview;
|
|
45
47
|
exports.stampUngroundedReview = stampUngroundedReview;
|
|
@@ -354,8 +356,15 @@ async function probeLane(plan, deps) {
|
|
|
354
356
|
* `extraDiagnostics` carries the raw HTTP response body for the OpenAI-compatible lanes: an error
|
|
355
357
|
* from such a server arrives with HTTP 4xx/5xx and the JSON in the BODY, so stderr alone is empty
|
|
356
358
|
* and the body is the only evidence.
|
|
359
|
+
*
|
|
360
|
+
* `outcome` (#4255) carries the spawn's exit status so the stub can say WHICH empty it is. The
|
|
361
|
+
* header alone cannot: a crash, a timeout kill and a model that ended its turn without writing a
|
|
362
|
+
* final message all reach here as the same zero bytes, and the third is what a too-low reasoning
|
|
363
|
+
* effort produces on a large source-grounded prompt. A clean exit inside the timeout with no
|
|
364
|
+
* output is a stopped-short model, and the stub now says so — with the effort it ran at, which is
|
|
365
|
+
* the value the operator would change.
|
|
357
366
|
*/
|
|
358
|
-
function writeReviewOrStub(plan, content, deps, extraDiagnostics) {
|
|
367
|
+
function writeReviewOrStub(plan, content, deps, extraDiagnostics, outcome) {
|
|
359
368
|
if (!(0, review_lane_invocation_cjs_1.isEmptyReview)(content)) {
|
|
360
369
|
deps.writeFile(plan.reviewPath, content.endsWith('\n') ? content : `${content}\n`);
|
|
361
370
|
return { stubbed: false };
|
|
@@ -364,9 +373,53 @@ function writeReviewOrStub(plan, content, deps, extraDiagnostics) {
|
|
|
364
373
|
const parts = [`${plan.slug} review failed or returned empty output. stderr:`, stderr];
|
|
365
374
|
if (extraDiagnostics)
|
|
366
375
|
parts.push('Raw response body:', extraDiagnostics);
|
|
376
|
+
parts.push(emptyOutputDiagnosis(plan, outcome));
|
|
367
377
|
deps.writeFile(plan.reviewPath, `${parts.join('\n')}\n`);
|
|
368
378
|
return { stubbed: true };
|
|
369
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* One line naming the effort the lane ran at and how the process ended (#4255).
|
|
382
|
+
*
|
|
383
|
+
* Kept out of the header so the `failed or returned empty output` string every downstream reader
|
|
384
|
+
* greps for is untouched — this is an added line, not a reworded one.
|
|
385
|
+
*/
|
|
386
|
+
function emptyOutputDiagnosis(plan, outcome) {
|
|
387
|
+
// `effort` lives on the spawn plan only. An HTTP lane reaches a server directly and has no
|
|
388
|
+
// reviewer CLI at all, so naming one there would be a lie about what ran (Codex review of
|
|
389
|
+
// #4255) — the two transports get different, accurate wording.
|
|
390
|
+
const spawned = plan.transport === 'spawn';
|
|
391
|
+
const level = spawned ? plan.effort : null;
|
|
392
|
+
const effort = level
|
|
393
|
+
? `ran at effort=${level}`
|
|
394
|
+
: spawned
|
|
395
|
+
? "ran with no effort argument, so the reviewer CLI's own configuration applied"
|
|
396
|
+
: 'is an HTTP lane and carries no reasoning-effort setting';
|
|
397
|
+
if (!outcome)
|
|
398
|
+
return `Diagnosis: ${plan.slug} ${effort}.`;
|
|
399
|
+
// Four endings, not two. `status` is null for BOTH a timeout kill and a process that never
|
|
400
|
+
// ran or died on a signal (ENOENT, SIGKILL) — reporting the latter as "status null" said
|
|
401
|
+
// nothing, and folding them together would attach the stopped-short hint to a crash.
|
|
402
|
+
const timedOut = outcome.errorCode === 'ETIMEDOUT';
|
|
403
|
+
const neverRan = !timedOut && outcome.status === null;
|
|
404
|
+
const cleanExit = outcome.status === 0;
|
|
405
|
+
const ending = timedOut
|
|
406
|
+
? 'was killed by the outer timeout'
|
|
407
|
+
: neverRan
|
|
408
|
+
? `did not exit normally (${outcome.errorCode ?? 'killed by a signal'})`
|
|
409
|
+
: cleanExit
|
|
410
|
+
? 'exited cleanly inside the timeout'
|
|
411
|
+
: `exited with status ${String(outcome.status)}`;
|
|
412
|
+
// Hedged deliberately. A clean exit with no output is CONSISTENT with a model ending its turn
|
|
413
|
+
// without a final message — which is what too low an effort produces on a large prompt — but it
|
|
414
|
+
// is equally consistent with the CLI writing its output somewhere this lane did not read. The
|
|
415
|
+
// line points at the likeliest cause without asserting it.
|
|
416
|
+
const tail = cleanExit
|
|
417
|
+
? ' — a clean exit that produced no output is most often a model ending its turn without'
|
|
418
|
+
+ ' writing a final message rather than a crash; if this lane carries a reasoning effort,'
|
|
419
|
+
+ ' raising it is the usual fix.'
|
|
420
|
+
: '.';
|
|
421
|
+
return `Diagnosis: ${plan.slug} ${effort} and ${ending}${tail}`;
|
|
422
|
+
}
|
|
370
423
|
/* ------------------------------------------------------------------ *
|
|
371
424
|
* Handlers (D6) — named first-party code, never conditionals in data
|
|
372
425
|
* ------------------------------------------------------------------ */
|
|
@@ -677,9 +730,13 @@ function antigravityPrompt(promptPath, repoRoot) {
|
|
|
677
730
|
* lane that fails to start is worse than one that runs on the prompt anchor alone.
|
|
678
731
|
* 2. The self-report prompt variant above, swapped in for the standard file-ref text.
|
|
679
732
|
*
|
|
680
|
-
* Both are argv shape, so they belong here rather than in the descriptor: expressing "add this
|
|
681
|
-
* only if the binary's --help mentions it" as data would need a conditional, which is
|
|
682
|
-
* what the named-handler seam exists to absorb (ADR-2782 D6).
|
|
733
|
+
* Both are argv shape, so they belong here rather than in the descriptor: expressing "add this
|
|
734
|
+
* flag only if the binary's --help mentions it" as data would need a conditional, which is
|
|
735
|
+
* precisely what the named-handler seam exists to absorb (ADR-2782 D6). The native
|
|
736
|
+
* `--print-timeout` VALUE (#3274) is NOT this handler's job — `resolveLanePlan`
|
|
737
|
+
* (review-lane-invocation.cts) resolves the `{{nativeTimeout}}` ARGV_PLACEHOLDER itself, exactly
|
|
738
|
+
* like `{{model}}`/`{{effort}}`/`{{output}}`/`{{prompt}}`, so `plan.argv` arrives here already fully
|
|
739
|
+
* resolved.
|
|
683
740
|
*/
|
|
684
741
|
function antigravityArgv(argv, promptPath, repoRoot, deps) {
|
|
685
742
|
const standard = (0, review_lane_invocation_cjs_1.fileRefPrompt)(promptPath, repoRoot);
|
|
@@ -710,8 +767,54 @@ function antigravityArgv(argv, promptPath, repoRoot, deps) {
|
|
|
710
767
|
*
|
|
711
768
|
* Mode 3 (a pre-session stall, which `--print-timeout` cannot bound because it cannot fire before a
|
|
712
769
|
* session exists) leaves no log line at all, so its tell is stated rather than searched for.
|
|
770
|
+
*
|
|
771
|
+
* #3996 mode 4 (a headless tool-permission denial) exits 0 with empty stdout and a POPULATED
|
|
772
|
+
* transcript, and reports the cause only on stderr — so the stub carries the run's stderr
|
|
773
|
+
* (`evidence.stderr`, the same `plan.errPath` content the generic stub reads), and the mode-3
|
|
774
|
+
* tell is stated only when `antigravityFailureMode` rules a session out (a fresh conv-id or
|
|
775
|
+
* transcript growth past the watermark means one verifiably started). Asserting mode 3 over a
|
|
776
|
+
* transcript this code just read inverts who is better placed to know.
|
|
713
777
|
*/
|
|
714
|
-
|
|
778
|
+
/**
|
|
779
|
+
* What actually failed, as a typed fact rather than a guess baked into prose. #3996.
|
|
780
|
+
*
|
|
781
|
+
* The session-started question is decidable at diagnostic time with the same staleness rule the
|
|
782
|
+
* layer-2 fallback uses: re-resolve the workspace's CURRENT conv-id and compare against the
|
|
783
|
+
* pre-spawn watermark. A conv-id that changed, or a transcript that grew past the watermark
|
|
784
|
+
* line count, means THIS invocation demonstrably reached a session — a pre-launch stall is
|
|
785
|
+
* ruled out. An unchanged conv-id with no growth means nothing new happened, which is the
|
|
786
|
+
* #2073 mode-3 shape even when a PRIOR session's transcript still exists on disk (existence
|
|
787
|
+
* alone would mis-diagnose mode 3 as mode 4 in any workspace with history).
|
|
788
|
+
*/
|
|
789
|
+
exports.ANTIGRAVITY_FAILURE_MODE = Object.freeze({
|
|
790
|
+
/** A session ran this invocation (fresh conv-id, or transcript growth) but no review was recovered. */
|
|
791
|
+
SESSION_STARTED: 'session_started',
|
|
792
|
+
/** No session this invocation — the #2073 mode-3 stall tell is the right signpost. */
|
|
793
|
+
PRE_SESSION_STALL: 'pre_session_stall',
|
|
794
|
+
});
|
|
795
|
+
function antigravityFailureMode(workspace, mark, deps) {
|
|
796
|
+
const convId = resolveWorkspaceConvId(workspace, deps);
|
|
797
|
+
if (!convId)
|
|
798
|
+
return exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
|
|
799
|
+
// A different conv-id than the watermark saw = a fresh session this run, transcript or not.
|
|
800
|
+
if (convId !== mark.convId)
|
|
801
|
+
return exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED;
|
|
802
|
+
const tx = transcriptPath(deps.homeDir, convId);
|
|
803
|
+
if (!deps.exists(tx))
|
|
804
|
+
return exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
|
|
805
|
+
try {
|
|
806
|
+
const lines = deps.readFile(tx).split(/\r?\n/).filter((l) => l.trim()).length;
|
|
807
|
+
return lines > mark.lines
|
|
808
|
+
? exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED
|
|
809
|
+
: exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
|
|
810
|
+
}
|
|
811
|
+
catch {
|
|
812
|
+
// #3118 fail-closed shape: the transcript indisputably exists but cannot be read — do not
|
|
813
|
+
// assert the stall case over a file this code cannot check.
|
|
814
|
+
return exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function antigravityDiagnostic(deps, evidence = {}) {
|
|
715
818
|
const lines = [
|
|
716
819
|
'Antigravity review failed or returned empty output.',
|
|
717
820
|
];
|
|
@@ -731,8 +834,28 @@ function antigravityDiagnostic(deps) {
|
|
|
731
834
|
/* an unreadable log is not worth failing the lane over */
|
|
732
835
|
}
|
|
733
836
|
}
|
|
734
|
-
|
|
735
|
-
|
|
837
|
+
// #3996: the stub carries agy's stderr, as the generic lane stub already does — mode 4 (a
|
|
838
|
+
// headless tool-permission denial) exits 0 with empty stdout and a populated transcript, and
|
|
839
|
+
// the stderr line is the only signal that names its cause.
|
|
840
|
+
const stderr = (evidence.stderr ?? '').trim();
|
|
841
|
+
if (stderr)
|
|
842
|
+
lines.push('stderr:', stderr);
|
|
843
|
+
// #3996: mode 3's tell is stated only when its precondition holds — the mode is computed from
|
|
844
|
+
// the watermark (#3996 mode 3 in a workspace with history is a no-growth transcript, not an
|
|
845
|
+
// absent one), never guessed from prose.
|
|
846
|
+
if (evidence.mode === exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED) {
|
|
847
|
+
lines.push('An agy session started this run but no review was recovered, so a pre-launch stall is ' +
|
|
848
|
+
'ruled out.' +
|
|
849
|
+
(stderr
|
|
850
|
+
? ' See stderr above; a headless run that was auto-denied a tool permission reports ' +
|
|
851
|
+
'the cause and its fix there.'
|
|
852
|
+
: ' agy reported nothing on its error stream; inspect the transcript under ' +
|
|
853
|
+
'~/.gemini/antigravity-cli/brain/<conv-id>/ for where the session stopped.'));
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
lines.push('If no agy run started, that is the pre-session-stall case: check whether a new ' +
|
|
857
|
+
'~/.gemini/antigravity-cli/brain/<conv-id>/ dir appeared within ~30s of launch.');
|
|
858
|
+
}
|
|
736
859
|
return lines.join('\n');
|
|
737
860
|
}
|
|
738
861
|
/**
|
|
@@ -948,7 +1071,10 @@ function runSpawnLane(plan, deps, repoRoot) {
|
|
|
948
1071
|
// pinned model that 404s server-side and exits 0 with empty output, so the model IS the
|
|
949
1072
|
// diagnosis — dropping it here would throw away the one piece of evidence the stub exists
|
|
950
1073
|
// to preserve.
|
|
951
|
-
deps.writeFile(plan.reviewPath, `${antigravityDiagnostic(deps
|
|
1074
|
+
deps.writeFile(plan.reviewPath, `${antigravityDiagnostic(deps, {
|
|
1075
|
+
stderr: errContent,
|
|
1076
|
+
mode: antigravityFailureMode(repoRoot, mark, deps),
|
|
1077
|
+
})}\n`);
|
|
952
1078
|
return { slug: plan.slug, ok: true, stubbed: true, model };
|
|
953
1079
|
}
|
|
954
1080
|
}
|
|
@@ -959,7 +1085,7 @@ function runSpawnLane(plan, deps, repoRoot) {
|
|
|
959
1085
|
// folded in as a diff observation, and the citation check must not change that surface.
|
|
960
1086
|
if (plan.evidenceClass !== 'diff-only')
|
|
961
1087
|
review = stampUngroundedReview(review);
|
|
962
|
-
const { stubbed } = writeReviewOrStub(plan, review, deps, extra);
|
|
1088
|
+
const { stubbed } = writeReviewOrStub(plan, review, deps, extra, out);
|
|
963
1089
|
return { slug: plan.slug, ok: true, stubbed, model };
|
|
964
1090
|
}
|
|
965
1091
|
async function runHttpLane(plan, deps) {
|