feat(#2481): add a negotiated effortSurface axis and wire invocation-time effort
ADR-1239 gains a ninth negotiated axis, effortSurface (argv | none), declaring how
a host accepts reasoning effort. ADR-443 is amended in the same change because its
recorded deferral is what the axis resolves: its Unblock condition offered paths
(a) and (b) and stated the choice was 'a maintainer call this file records but does
not make'. Path (a) is selected and satisfied here.
Before this, effort reached a runtime only through install-time channels
(EFFORT_RENDERING's frontmatter/api), so reviewer CLIs spawned as subprocesses
silently inherited whatever effort sat in the user's own global CLI config. The
review lane now resolves one universal effort through the ADR-443 cascade and
renders it per host through the negotiated descriptor.
Every per-host value is documentation-sourced, never inferred:
- claude argv -- verified via 'claude --help' (--effort <level>)
- opencode argv -- verified via 'opencode run --help' (--variant)
- codex argv -- codex-rs/exec/src/cli.rs: model_reasoning_effort is NOT a CLI
flag (config.toml key only), so the global -c override is the
only argv route
- 15 hosts undocumented -- their docs state no reasoning setting; the sentinel
fails closed rather than inheriting a profile baseline
No config-file vocabulary member: the only host that ever had one (Gemini CLI's
thinkingConfig) was removed as a sunset runtime by 8f2ebbe9b (#1928, PR #1996),
and neither Antigravity CLI nor ZCode documents a reasoning setting.
Closes #2481
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/kind-voles-march.md
Normal file
5
.changeset/kind-voles-march.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Added
|
||||
pr: 0
|
||||
---
|
||||
**Reviewer CLIs now honor GSD's configured reasoning effort instead of silently inheriting your global CLI default** — cross-AI review runs previously picked up whatever `effort` sat in your own `~/.codex`/Claude/OpenCode config, so the same project produced 1-3 minute review cycles on one machine and 12-15+ minute cycles on another with no in-project way to influence it. GSD now resolves one effort value from the `effort.*` cascade and passes it to each reviewer in that CLI's own syntax; a host with no documented reasoning setting is left untouched rather than given a guessed flag. (#2481)
|
||||
@@ -128,7 +128,7 @@ Module owning bounded, never-throw git repository introspection — the single s
|
||||
Module owning runtime identity normalization at runtime-selection seams. Canonicalizes alias signals from env/config (`GSD_RUNTIME`, `.planning/config.json:runtime`) to supported runtime IDs so output emitters and query runtime gates stay consistent across naming variants (for example `codex-app`/`codex-cli` -> `codex`). Sources: `gsd-core/bin/lib/runtime-name-policy.cjs`, alias manifest `gsd-core/bin/shared/runtime-aliases.manifest.json`.
|
||||
|
||||
### Host-Integration Interface
|
||||
Pure, additive, no-I/O Module owning the versioned, negotiated contract over the six host-integration interface points (command, dispatch, model, hooks, state, artifact) — ADR-1239 Phase A. Extends the ADR-1016 runtime descriptor with eight closed-vocabulary axes carried under `capability.json` `runtime.hostIntegration`: `embeddingMode` (`imperative|declarative`), `commandSurface` (`slash-file|slash-programmatic|slash-toml|palette|prose-only`), `dispatch` (`{namedDispatch,nested,maxDepth,background,backgroundDispatch,subagentToolkit}`), `modelMode` (`active|passive`), `hookBus` (`host|engine|none`), `stateIO` (`filesystem|sandboxed-storage|session-log-append`), `transport` (`mcp|native-extension`), `runtime` (`node|bun|sandboxed-web|python|go|rust|electron|other`). Interface: `negotiateHostCapabilities(host, engine?) → { protocolVersion, effective, points, warnings }` enforcing the trust-boundary invariant `effective ⊆ host-declared ∩ engine-known` (never augment with an undeclared or unknown/future-`protocolVersion` value — fail-closed via the most-restrictive-known `SAFE_DEFAULTS`); `degradationFor(point, axes) → { level, fallback }` (a pure Full/Degraded/Absent ladder table, never throws); `profileOf(axes) → 'programmatic-cli'|'declarative-cli'|'ide'|null`; plus `PROTOCOL_VERSION` (integer, starts at 1 — distinct from the package `version`/`engines.gsd` semver), `HOST_INTEGRATION_AXES` (the frozen closed vocabulary, single source of truth), `PROFILE_BASELINES`, and `shouldFlattenDispatch(dispatch) → boolean` (ADR-1239 Phase B / #1708 — graduates the #853 rule: returns `true` = run the orchestrator inline UNLESS the host is documented to background a nesting-capable orchestrator (`background === true && backgroundDispatch === true`); fail-closed to inline; exposed to the plan/execute workflows via the `gsd_run query dispatch-should-flatten --raw` CLI, which replaced the former scattered `RUNTIME === 'codex'` prose check). The runtime-descriptor validator (`gsd-core/bin/lib/capability-validator.cjs` `validateRuntimeBody`) mirrors the closed vocabulary inline (exported as `_HOST_INTEGRATION_VOCAB`) and is kept in lock-step by the parity guard `tests/host-integration-validator-parity.test.cjs`. Orthogonal axes (resolved explicitly per ADR-1239 Phase A): `commandStyle` (GSD emission style, retained) vs `commandSurface` (host surface type); `hookEvents` dialect vs `hookBus` ownership (a host with `hooksSurface:none` may still be `hookBus:host` — e.g. opencode); `runtimeCompat` (feature→host) vs these negotiated runtime→engine axes. Phase A defined the interface; Phase B (#1679) wires it incrementally — `destSubpath` write-confinement (#1704) and the typed documentation-sourced #853 dispatch-flatten (#1708, the first consumer of a negotiated `dispatch` axis); adapters/MCP/host-bindings remain Phases C–E. Source of truth: `gsd-core/bin/lib/host-integration.cjs` (generated from `src/host-integration.cts`). See ADR-1239 and ADR-1016.
|
||||
Pure, additive, no-I/O Module owning the versioned, negotiated contract over the six host-integration interface points (command, dispatch, model, hooks, state, artifact) — ADR-1239 Phase A. Extends the ADR-1016 runtime descriptor with nine closed-vocabulary axes carried under `capability.json` `runtime.hostIntegration`: `embeddingMode` (`imperative|declarative`), `commandSurface` (`slash-file|slash-programmatic|slash-toml|palette|prose-only`), `dispatch` (`{namedDispatch,nested,maxDepth,background,backgroundDispatch,subagentToolkit}`), `modelMode` (`active|passive`), `hookBus` (`host|engine|none`), `stateIO` (`filesystem|sandboxed-storage|session-log-append`), `transport` (`mcp|native-extension`), `runtime` (`node|bun|sandboxed-web|python|go|rust|electron|other`), `effortSurface` (`argv|none` — how reasoning effort reaches the host; ADR-1239 amendment #2481, the first axis whose consumer is an invocation-time argument rather than an install-time artifact). Interface: `negotiateHostCapabilities(host, engine?) → { protocolVersion, effective, points, warnings }` enforcing the trust-boundary invariant `effective ⊆ host-declared ∩ engine-known` (never augment with an undeclared or unknown/future-`protocolVersion` value — fail-closed via the most-restrictive-known `SAFE_DEFAULTS`); `degradationFor(point, axes) → { level, fallback }` (a pure Full/Degraded/Absent ladder table, never throws); `profileOf(axes) → 'programmatic-cli'|'declarative-cli'|'ide'|null`; plus `PROTOCOL_VERSION` (integer, starts at 1 — distinct from the package `version`/`engines.gsd` semver), `HOST_INTEGRATION_AXES` (the frozen closed vocabulary, single source of truth), `PROFILE_BASELINES`, and `shouldFlattenDispatch(dispatch) → boolean` (ADR-1239 Phase B / #1708 — graduates the #853 rule: returns `true` = run the orchestrator inline UNLESS the host is documented to background a nesting-capable orchestrator (`background === true && backgroundDispatch === true`); fail-closed to inline; exposed to the plan/execute workflows via the `gsd_run query dispatch-should-flatten --raw` CLI, which replaced the former scattered `RUNTIME === 'codex'` prose check). The runtime-descriptor validator (`gsd-core/bin/lib/capability-validator.cjs` `validateRuntimeBody`) mirrors the closed vocabulary inline (exported as `_HOST_INTEGRATION_VOCAB`) and is kept in lock-step by the parity guard `tests/host-integration-validator-parity.test.cjs`. Orthogonal axes (resolved explicitly per ADR-1239 Phase A): `commandStyle` (GSD emission style, retained) vs `commandSurface` (host surface type); `hookEvents` dialect vs `hookBus` ownership (a host with `hooksSurface:none` may still be `hookBus:host` — e.g. opencode); `runtimeCompat` (feature→host) vs these negotiated runtime→engine axes. Phase A defined the interface; Phase B (#1679) wires it incrementally — `destSubpath` write-confinement (#1704) and the typed documentation-sourced #853 dispatch-flatten (#1708, the first consumer of a negotiated `dispatch` axis); adapters/MCP/host-bindings remain Phases C–E. Source of truth: `gsd-core/bin/lib/host-integration.cjs` (generated from `src/host-integration.cts`). See ADR-1239 and ADR-1016.
|
||||
|
||||
### Statusline
|
||||
Host-integration hook (`hooks/gsd-statusline.js`) that renders the session status line: model name, context-window meter, workspace directory, and the GSD-state segment (`formatGsdState()` projecting `.planning/` STATE.md). Opt-in segments are gated by `.planning/config.json` keys (`statusline.show_last_command`, `statusline.context_position`, plus the approved `statusline.show_context_tokens` and `statusline.state_format`), each registered across `gsd-core/bin/shared/config-schema.manifest.json` + `src/config.cts` + the `loadConfig` whitelist + `docs/CONFIGURATION.md`. The compact GSD-state format consumes the canonical status vocabulary from `normalizeStateStatus()` (STATE.md Document Module) rather than a parallel keyword list. **Data-source boundary (ADR-2164):** the statusline sources only local, read-only data — it refines the stdin payload Claude Code already sends and may add a new *local* source (e.g. `git`), but does not read credentials or call external/network APIs for data; account/usage/platform-level state is out of scope.
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "go"
|
||||
"runtime": "go",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reviewerCli": true,
|
||||
|
||||
@@ -101,7 +101,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"attributionSource": "settings-json-commit",
|
||||
|
||||
@@ -55,7 +55,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
|
||||
@@ -102,7 +102,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reportCommandsDir": true
|
||||
|
||||
@@ -71,7 +71,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "$gsd-update --reapply",
|
||||
|
||||
@@ -81,7 +81,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
|
||||
|
||||
@@ -83,7 +83,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/skill:gsd-update --reapply",
|
||||
|
||||
@@ -83,7 +83,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "session-log-append",
|
||||
"transport": "native-extension",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"nativePlugin": {
|
||||
|
||||
@@ -86,7 +86,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skillPriorityFrontmatter": true,
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
"hookBus": "engine",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
"hookBus": "engine",
|
||||
"stateIO": "sandboxed-storage",
|
||||
"transport": "mcp",
|
||||
"runtime": "sandboxed-web"
|
||||
"runtime": "sandboxed-web",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
|
||||
@@ -96,7 +96,8 @@
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "electron"
|
||||
"runtime": "electron",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true
|
||||
|
||||
@@ -1342,6 +1342,36 @@ The model-catalog's `reasoning_effort` per-tier hint is a legacy field kept for
|
||||
|
||||
Valid effort values: `minimal`, `low`, `medium`, `high`, `xhigh`, `max`.
|
||||
|
||||
#### Where effort actually reaches — added in v1.8.0
|
||||
|
||||
Effort resolved from the cascade above reaches a runtime through one of two channels.
|
||||
|
||||
**Install-time.** The value is baked into the artifacts the installer generates — the
|
||||
`effort:` frontmatter key on a Claude subagent, `model_reasoning_effort` in a generated
|
||||
Codex `.toml`. This is fixed at install and changes only on reinstall or sync.
|
||||
|
||||
**Invocation-time.** When GSD spawns another CLI as a subprocess — the cross-AI reviewers
|
||||
in `/gsd:review` — the effort is appended to that CLI's own command line. Whether a host
|
||||
can receive effort this way is a declared capability (`effortSurface`, ADR-1239), not an
|
||||
assumption:
|
||||
|
||||
| Reviewer CLI | Receives effort as |
|
||||
|---|---|
|
||||
| `claude` | `--effort <level>` |
|
||||
| `opencode` | `--variant <level>` |
|
||||
| `codex` | `-c model_reasoning_effort=<level>` |
|
||||
|
||||
A host whose documentation states no reasoning setting is left **untouched** — no flag is
|
||||
guessed, and GSD never writes into your own CLI's config file to set one. Levels a given
|
||||
CLI does not accept are clamped to its nearest supported value (`minimal` → `low` for
|
||||
Claude, `max` → `xhigh` for Codex), so a cross-provider value never produces an invalid
|
||||
argument.
|
||||
|
||||
Before this, a review run inherited whatever effort happened to be configured in your
|
||||
personal CLI config, which is why the same project could produce very different review
|
||||
times on two machines. Setting `effort.default` (or an agent/tier override) now controls
|
||||
review runs too.
|
||||
|
||||
---
|
||||
|
||||
### Fast Mode (`fast_mode`) — added in v1.42
|
||||
|
||||
@@ -100,6 +100,50 @@ Phase A is **implemented** and Phases B–E have landed, so this ADR is **Accept
|
||||
|
||||
No consumer wires the negotiated result yet — Phase A is interface-definition only; the engine↔host boundary (Phase B) and the adapters (Phase C) are where it is consumed.
|
||||
|
||||
## Amendment (2026-07-21): `effortSurface` axis (#2481)
|
||||
|
||||
> Adds a **ninth negotiated axis** covering reasoning effort. Raised by #2475: reviewer CLIs invoked as subprocesses by the review workflow silently inherit whatever reasoning effort sits in the user's own global CLI config, producing 1–3 minute review cycles on one machine and 12–15+ minute cycles on another with no in-project way to influence it.
|
||||
|
||||
**The gap.** Reasoning effort is a first-class, config-driven GSD concept that the negotiated schema does not describe. Three mechanisms exist and none of them meet:
|
||||
|
||||
1. **A core effort vocabulary and cascade** ([ADR-443](443-opus48-unified-effort-and-fast-mode-routing.md)) — `resolveEffortInternal` (`src/model-resolver.cts`) resolves a *universal* effort string through invocation override → `effort.agent_overrides.<agent-id>` → `effort.routing_tier_defaults.<tier>` → `effort.default` → canonical defaults.
|
||||
2. **A core-owned per-runtime rendering table** — `EFFORT_RENDERING` / `renderEffortForRuntime` (`src/model-catalog.cts`), where each runtime declares `param`, `channel`, `supported` levels and a `clamp()` rule. It holds two entries (`claude`, `codex`) and its `channel` vocabulary is `frontmatter` | `api` — **both install-time artifact channels**. Its production callers are exactly those two channels: the static install-time renderer (`bin/install.js`, via `src/install-effort-resolver.cts`) and the manual `query resolve-execution` / effort-sync CLI surface (`src/commands.cts`). **No workflow or agent dispatch calls it**, so there is no invocation-time channel and no per-host declaration of one.
|
||||
3. **This negotiated schema** — eight axes under `capability.json` `runtime.hostIntegration`. Effort is not among them; `modelMode` covers only whether the host lets GSD drive model *selection*.
|
||||
|
||||
`EFFORT_RENDERING` is performing this schema's job — declaring per-host support and degrading gracefully — but as a core table keyed by runtime name, at the wrong layer, predating this ADR. [ADR-443](443-opus48-unified-effort-and-fast-mode-routing.md) names the same gap in its own text: its dynamic paths *"exist only as CLI-callable resolver code… Nothing in the shipped orchestration actually calls them,"* and *"the only propagation channel actually wired into a real GSD flow is the static one (config → `install()` → frontmatter, baked once at install time)."*
|
||||
|
||||
**The axis.** `effortSurface` declares **how reasoning effort reaches a host** — a two-value closed vocabulary consistent with the existing axes:
|
||||
|
||||
- **`argv`** — effort is deliverable as an argument on the host's own invocation (claude `--effort`; opencode `--variant`; codex via the generic `-c model_reasoning_effort=` config override). This is the channel the schema was missing.
|
||||
- **`none`** — the host exposes no reasoning-effort mechanism.
|
||||
`undocumented` is **not** a value of this vocabulary. It is the corpus-wide sentinel (`UNDOCUMENTED` in `src/host-integration.cts`) that any axis may carry when a host's documentation does not state a value: it validates, but never propagates into effective axes — failing closed exactly like an unknown or missing value. A host whose effort mechanism is undocumented carries the sentinel and negotiates to the safe floor.
|
||||
|
||||
Per-host `param` name, accepted level set, and clamp rule are carried **in the descriptor** rather than a core table, so `EFFORT_RENDERING` collapses into descriptor data rather than growing a parallel vocabulary.
|
||||
|
||||
**Degradation ladder — interface point 3 (Model) gains an effort row:**
|
||||
|
||||
| Interface point | Full | Degraded | Absent → fallback |
|
||||
|---|---|---|---|
|
||||
| 3 Model — effort | `argv`: universal effort rendered onto the invocation, clamped to the host's level set | *(no rung — see below)* | `none` / `undocumented`: effort is not propagated; host default stands |
|
||||
|
||||
**Why there is no `config-file` member.** A config-file-only effort surface is a real shape — Gemini CLI exposed exactly that (`thinkingConfig.thinkingLevel` / `.thinkingBudget` under `modelConfig.generateContentConfig`, settable only in settings.json model presets). It is nonetheless **not** a vocabulary member, because no supported runtime has one:
|
||||
|
||||
- **Gemini CLI was removed from this repo as a sunset runtime** — `capabilities/gemini/capability.json` was deleted by commit `8f2ebbe9b` (#1928, PR #1996), following Google's own transition notice (`developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/`).
|
||||
- **Antigravity CLI**, its documented successor, states no reasoning or thinking setting on its features/settings page (`antigravity.google/docs/cli/features`).
|
||||
- **ZCode**, the other declarative host with a rich config surface, states none either (`zcode.z.ai/en/docs/configuration`).
|
||||
|
||||
A closed, first-party vocabulary with a member no host can claim is an invitation to guess. If a supported host later documents a config-file effort surface, adding the member is a small, evidence-backed change — and the degradation ladder already has the shape for it.
|
||||
|
||||
**Boundary against #2313.** That epic (whose own Phase 0 records the Codex passive-model posture as a separate ADR, not yet written) owns the **static / install-time** effort channel for Codex — `model_reasoning_effort` in generated `~/.codex/agents/<agent>.toml` under the passive posture, plus a sync path — and explicitly places *"orchestrator effort-override drift"* outside its scope. This amendment covers the **invocation-time** channel only and does not change install-time emission. The two channels may share a descriptor once `EFFORT_RENDERING` folds in.
|
||||
|
||||
**Evidence.** Per this ADR's own rule that axis values are documentation-sourced and never inferred: `claude --effort` and `opencode --variant` were verified against each CLI's `--help`; codex's `-c model_reasoning_effort=` and gemini's `thinkingConfig` were verified against first-party documentation. **The remaining installed hosts were not researched for this axis and must carry the explicit `undocumented` sentinel**, joining the 28 sentinels already carried across the 18 descriptors that declare `hostIntegration`, rather than inheriting a guessed value from a profile baseline.
|
||||
|
||||
**Corrections to the Phase A amendment's counts** (recorded here rather than by editing that section, since ADRs are append-only). The Phase A text states "all 16 installed CLIs" and "22 such markers exist today". Both have drifted: **18** descriptors now declare `runtime.hostIntegration`, carrying **28** `undocumented` sentinels. The lists have since drifted in both directions, for different reasons: **`zcode`** carries a descriptor but is named in neither list, and **`gemini`** is named in the declarative list but its descriptor was **deliberately deleted** by `8f2ebbe9b` (#1928, PR #1996) when Google sunset Gemini CLI in favor of Antigravity CLI. Any per-host work on this axis must enumerate the descriptors from the tree, not from those lists.
|
||||
|
||||
**Relationship to [ADR-443](443-opus48-unified-effort-and-fast-mode-routing.md).** That ADR is `Proposed`, blocked on its decided invocation-override and escalation paths having no live caller, and its own text recorded the choice of unblock path as a maintainer call it did not make. It is amended in the same change (#2481) to select path (a), to record that audit issue #1192's action-plan item 18 was never converted into a tracked follow-up, and to correct its own stale audit: the blocker's grep excluded `references/*.md`, which is `@`-included into workflows, and predates #2296's escalation caller. This axis does not by itself satisfy either of path (a)'s two mechanisms — see that ADR's amendment for the precise status. `effortSurface` is therefore the declaration layer for a decision ADR-443 already made; it does not restate ADR-443's cascade or enum.
|
||||
|
||||
**Status:** delivered in #2481 — axis vocabulary, descriptor values, validator parity, fail-closed negotiation, the degradation row, and the consuming review-lane wiring all land together. `effortSurface` is a wired axis, not a declared-but-unconsumed one; it is the first negotiated axis whose consumer is an invocation-time argument rather than an install-time artifact.
|
||||
|
||||
## Host-capability profiles (negotiation baselines)
|
||||
|
||||
- **Programmatic-CLI** (Claude Code, pi, OpenCode): imperative; full dispatch; host hook bus; MCP; `slash` surface. The richest target — minimal degradation.
|
||||
|
||||
@@ -12,6 +12,31 @@ The audit confirmed the cross-provider resolver/renderer/CLI machinery genuinely
|
||||
|
||||
**Unblock condition.** Either (a) wire the orchestrator-invocation-override and attempt-based-escalation paths into an actual GSD workflow or agent dispatch (so `resolveEffortForTier`'s escalation and `resolveEffortInternal`'s invocation-override step have a real caller outside `src/commands.cts`'s CLI surface and tests), and add a test exercising that live path the way `tests/install-runtime-artifacts.test.cjs` exercises the static one; or (b) if the ADR's intended scope is in fact limited to static install-time propagation, amend Decision items 1 and 6 to say so explicitly and close out audit issue #1192's action-plan item 18 with a note pointing at the shipped install-wiring tests. Either is a maintainer call this file records but does not make.
|
||||
|
||||
## Amendment (2026-07-21): path (a) chosen; audit corrected (#2481)
|
||||
|
||||
**The maintainer call above has been made: path (a).** Raised by #2475 — reviewer CLIs invoked as subprocesses by the review workflow silently inherit whatever reasoning effort sits in the user's own global CLI config, because no shipped orchestration resolves effort at invocation time. That is this ADR's blocker surfacing as a user-visible defect, not a new problem.
|
||||
|
||||
**Two deferrals recorded above are closed by this change — resolved, not re-tracked:**
|
||||
|
||||
1. **Audit issue #1192's action-plan item 18** ("Strengthen … ADR-443 end-to-end effort propagation") — which the blocker text notes *"was never converted into a tracked follow-up issue"* — is **satisfied by this change**, which supplies the end-to-end propagation and the live-path tests it asked for. It is closed out, not converted into another follow-up.
|
||||
2. **The choice between (a) and (b)** — which this file previously recorded without making — is resolved as **(a)**. Scope is *not* limited to static install-time propagation. Choosing the path is not the same as completing it; see the status table below for what remains.
|
||||
|
||||
**How path (a) is being satisfied.** The consumer is defined through the Host-Integration Interface rather than by hard-coding per-CLI effort syntax into a workflow: [ADR-1239](1239-gsd-embeddable-orchestration-engine.md) gains an `effortSurface` axis declaring how each host accepts reasoning effort (`argv` | `none`), so a universal effort value resolved by this ADR's cascade is rendered per host through the negotiated descriptor. `EFFORT_RENDERING` (`src/model-catalog.cts`) — whose `channel` vocabulary is `frontmatter` | `api`, both install-time — collapses into that descriptor data rather than growing a parallel per-runtime table. Its callers today are exactly the two channels this ADR already ships: the static install-time renderer (`bin/install.js`, via `src/install-effort-resolver.cts`) and the manual `query resolve-execution` / effort-sync CLI surface (`src/commands.cts`). No workflow or agent dispatch calls it — which is the blocker restated in terms of the renderer rather than the resolver.
|
||||
|
||||
**What this change actually delivers — and what it does not.** Path (a) names *two* mechanisms needing a live caller. This change delivers neither of them; it delivers a third thing the blocker did not anticipate, and the audit of the other two turns out to have been stale.
|
||||
|
||||
| Path (a) mechanism | Status |
|
||||
|---|---|
|
||||
| Decision item 1 — `resolveEffortInternal`'s **invocation-override** step (`--effort`) | **Still no live caller.** No workflow, reference, or agent passes `--effort` to `resolve-execution`. This change does not add one. |
|
||||
| Decision item 6 — `resolveEffortForTier`'s **attempt-based escalation** | **Already satisfied — by #2296, not by this change.** `gsd-core/references/execute-phase-quota-recovery.md` calls `resolve-execution gsd-executor --attempt "${QUOTA_ATTEMPT:-1}" --failure-class quota-exceeded`, and that reference is `@`-included into `gsd-core/workflows/execute-phase.md`, so it executes as part of the live workflow. |
|
||||
| **New here:** the resolved effort **cascade** reaches a spawned host as an invocation argument | Delivered. `gsd-core/workflows/review.md` calls `resolve-execution … --host <id>` per reviewer and appends the rendered argument, gated by the host's negotiated `effortSurface`. |
|
||||
|
||||
**The blocker's grep was stale in two ways.** It searched only `gsd-core/workflows/*.md` and `agents/*.md`; `references/*.md` is `@`-included into workflows and is therefore just as live — that is where #2296's escalation caller sits. And the blocker text was written 2026-07-17, three days before #2296 landed (`455ad49ae`, 2026-07-20), so its "zero hits" finding was correct on the day and has since been overtaken.
|
||||
|
||||
**This ADR therefore remains `Proposed`.** Decision item 6's condition is met (by #2296); Decision item 1's is not. The corpus rule for ratifying a stale `Proposed` requires the decided mechanism to demonstrably exist in the tree, and the invocation-override step still has no caller outside the CLI surface and tests. Status flips when item 1 gains a live caller and a test exercises that path through a workflow rather than through `gsd-tools` directly.
|
||||
|
||||
**Boundary.** #2313 owns the static/install-time effort channel for Codex (`model_reasoning_effort` in generated `~/.codex/agents/<agent>.toml`, plus a sync path) and explicitly places orchestrator effort-override drift outside its scope. That is the static channel this ADR already ships; the work above is the invocation-time channel it does not.
|
||||
|
||||
## Context
|
||||
|
||||
### Effort control and fast mode in Claude Opus 4.8
|
||||
|
||||
@@ -610,6 +610,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
||||
let fastModeOverride;
|
||||
let attempt;
|
||||
let failureClass;
|
||||
let host;
|
||||
const positionals = [];
|
||||
// #2296: the valid classes come from the classifier's own frozen enum, so
|
||||
// this validator can never drift from what `agent classify-failure` emits.
|
||||
@@ -675,6 +676,13 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (a === '--host') {
|
||||
const val = execArgs[i + 1];
|
||||
if (val === undefined || val.startsWith('--')) error('Missing value for --host', ERROR_REASON.USAGE);
|
||||
host = val;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (a === '--raw') continue;
|
||||
if (a.startsWith('-')) error(`Unknown flag for resolve-execution: ${a}`, ERROR_REASON.USAGE);
|
||||
positionals.push(a);
|
||||
@@ -687,6 +695,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
||||
fastModeOverride,
|
||||
attempt,
|
||||
failureClass,
|
||||
host,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "go"
|
||||
"runtime": "go",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reviewerCli": true,
|
||||
@@ -379,7 +380,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -509,7 +511,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"attributionSource": "settings-json-commit",
|
||||
@@ -679,7 +682,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -856,7 +860,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reportCommandsDir": true
|
||||
@@ -936,7 +941,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "$gsd-update --reapply",
|
||||
@@ -1031,7 +1037,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -1140,7 +1147,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
|
||||
@@ -1490,7 +1498,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1636,7 +1645,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -1729,7 +1739,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/skill:gsd-update --reapply",
|
||||
@@ -2050,7 +2061,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -2173,7 +2185,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "session-log-append",
|
||||
"transport": "native-extension",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"nativePlugin": {
|
||||
@@ -2350,7 +2363,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skillPriorityFrontmatter": true,
|
||||
@@ -2699,7 +2713,8 @@ const capabilities = {
|
||||
"hookBus": "engine",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
@@ -2849,7 +2864,8 @@ const capabilities = {
|
||||
"hookBus": "engine",
|
||||
"stateIO": "sandboxed-storage",
|
||||
"transport": "mcp",
|
||||
"runtime": "sandboxed-web"
|
||||
"runtime": "sandboxed-web",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2928,7 +2944,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
@@ -3036,7 +3053,8 @@ const capabilities = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "electron"
|
||||
"runtime": "electron",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true
|
||||
@@ -4062,7 +4080,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "go"
|
||||
"runtime": "go",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reviewerCli": true,
|
||||
@@ -4176,7 +4195,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4260,7 +4280,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"attributionSource": "settings-json-commit",
|
||||
@@ -4342,7 +4363,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -4458,7 +4480,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reportCommandsDir": true
|
||||
@@ -4538,7 +4561,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "$gsd-update --reapply",
|
||||
@@ -4633,7 +4657,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -4742,7 +4767,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
|
||||
@@ -4849,7 +4875,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4943,7 +4970,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -5036,7 +5064,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "python"
|
||||
"runtime": "python",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/skill:gsd-update --reapply",
|
||||
@@ -5133,7 +5162,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "argv"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"reapplyCommand": "/gsd-update --reapply",
|
||||
@@ -5202,7 +5232,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "session-log-append",
|
||||
"transport": "native-extension",
|
||||
"runtime": "bun"
|
||||
"runtime": "bun",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"nativePlugin": {
|
||||
@@ -5302,7 +5333,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skillPriorityFrontmatter": true,
|
||||
@@ -5401,7 +5433,8 @@ const runtimes = {
|
||||
"hookBus": "engine",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "node"
|
||||
"runtime": "node",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
@@ -5456,7 +5489,8 @@ const runtimes = {
|
||||
"hookBus": "engine",
|
||||
"stateIO": "sandboxed-storage",
|
||||
"transport": "mcp",
|
||||
"runtime": "sandboxed-web"
|
||||
"runtime": "sandboxed-web",
|
||||
"effortSurface": "undocumented"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5535,7 +5569,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "undocumented"
|
||||
"runtime": "undocumented",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true,
|
||||
@@ -5643,7 +5678,8 @@ const runtimes = {
|
||||
"hookBus": "host",
|
||||
"stateIO": "filesystem",
|
||||
"transport": "mcp",
|
||||
"runtime": "electron"
|
||||
"runtime": "electron",
|
||||
"effortSurface": "undocumented"
|
||||
},
|
||||
"hostBehaviors": {
|
||||
"skipSharedHooksInstall": true
|
||||
|
||||
@@ -736,6 +736,8 @@ const VALID_STATE_IO = new Set(['filesystem', 'sandboxed-storage', 'ses
|
||||
const VALID_TRANSPORTS = new Set(['mcp', 'native-extension']);
|
||||
const VALID_HOST_RUNTIMES = new Set(['node', 'bun', 'sandboxed-web', 'python', 'go', 'rust', 'electron', 'other']);
|
||||
const VALID_SUBAGENT_TOOLKITS = new Set(['full', 'read-only']);
|
||||
// ADR-1239 amendment (#2481): how reasoning effort reaches the host.
|
||||
const VALID_EFFORT_SURFACES = new Set(['argv', 'none']);
|
||||
|
||||
// GATE A: installSurface → allowed hooksSurface values (DEFECT.GENERATIVE-FIX: parity invariant)
|
||||
// Derived from the actual pairings in the 16 real runtime descriptors.
|
||||
@@ -1204,6 +1206,24 @@ function validateRuntimeBody(cap) {
|
||||
);
|
||||
}
|
||||
|
||||
// effortSurface (axis) — ADR-1239 amendment (#2481).
|
||||
// OPTIONAL, unlike the Phase-A axes. It was added after descriptors already
|
||||
// existed, so requiring it would invalidate every descriptor written before it —
|
||||
// including third-party ones, breaking the "purely additive" property ADR-1239
|
||||
// promises for external descriptors. An omitted axis is legitimate: negotiation
|
||||
// degrades it to the safe floor ('none') and warns, exactly as for any
|
||||
// undeclared axis. Only a PRESENT value is checked against the vocabulary.
|
||||
if (hi.effortSurface === undefined) {
|
||||
// absent — nothing to validate; negotiateHostCapabilities fails it closed.
|
||||
} else if (hi.effortSurface === '__proto__' || hi.effortSurface === 'constructor' || hi.effortSurface === 'prototype') {
|
||||
errors.push('runtime.hostIntegration.effortSurface "' + hi.effortSurface + '" is a reserved name');
|
||||
} else if (hi.effortSurface !== 'undocumented' && !VALID_EFFORT_SURFACES.has(hi.effortSurface)) {
|
||||
errors.push(
|
||||
'runtime.hostIntegration.effortSurface must be one of: ' + [...VALID_EFFORT_SURFACES].join(', ') +
|
||||
' (or "undocumented") (got: ' + JSON.stringify(hi.effortSurface) + ')',
|
||||
);
|
||||
}
|
||||
|
||||
// dispatch — required object
|
||||
if (typeof hi.dispatch !== 'object' || hi.dispatch === null || Array.isArray(hi.dispatch)) {
|
||||
errors.push('runtime.hostIntegration.dispatch must be an object');
|
||||
@@ -2309,6 +2329,7 @@ module.exports = {
|
||||
transport: [...VALID_TRANSPORTS],
|
||||
runtime: [...VALID_HOST_RUNTIMES],
|
||||
subagentToolkit: [...VALID_SUBAGENT_TOOLKITS],
|
||||
effortSurface: [...VALID_EFFORT_SURFACES],
|
||||
},
|
||||
INSTALL_SURFACE_TO_ALLOWED_HOOKS_SURFACES,
|
||||
GEMINI_AGENT_EVENTS,
|
||||
|
||||
@@ -250,6 +250,12 @@ OPENCODE_MODEL=$(gsd_run query config-get review.models.opencode 2>/dev/null | j
|
||||
# pinned model that 404s server-side); otherwise agy uses its persisted default.
|
||||
AGY_MODEL=$(gsd_run query config-get review.models.agy 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
|
||||
# Reasoning effort per reviewer (#2481). Empty unless the host's effortSurface
|
||||
# axis is `argv`. Pass --attempt N to walk ADR-443's escalation ladder.
|
||||
CLAUDE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host claude 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
CODEX_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host codex 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
OPENCODE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host opencode 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
|
||||
# #1115: `--dangerously-bypass-hook-trust` only exists on codex-cli >= 0.137.0.
|
||||
# Capability-probe it so older installs don't fail with "unexpected argument"
|
||||
# (which, with stderr suppressed, produced a silent empty review). The codex
|
||||
@@ -281,9 +287,9 @@ fi
|
||||
**Claude (separate session):**
|
||||
```bash
|
||||
if [ -n "$CLAUDE_MODEL" ] && [ "$CLAUDE_MODEL" != "null" ]; then
|
||||
cat {run_dir}/gsd-review-prompt.md | claude --model "$CLAUDE_MODEL" -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude --model "$CLAUDE_MODEL" $CLAUDE_EFFORT_ARGS -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
else
|
||||
cat {run_dir}/gsd-review-prompt.md | claude -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude $CLAUDE_EFFORT_ARGS -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -298,9 +304,9 @@ fi
|
||||
# stdout redirect would append that noise to a non-empty file — slipping past the
|
||||
# `[ ! -s … ]` empty-output guard as a silently polluted review.
|
||||
if [ -n "$CODEX_MODEL" ] && [ "$CODEX_MODEL" != "null" ]; then
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" $CODEX_EFFORT_ARGS --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
else
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG $CODEX_EFFORT_ARGS --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
fi
|
||||
if [ ! -s {run_dir}/gsd-review-codex.md ]; then
|
||||
echo "Codex review failed or returned empty output. stderr:" > {run_dir}/gsd-review-codex.md
|
||||
@@ -345,7 +351,7 @@ if [ -n "$OPENCODE_MODEL" ] && [ "$OPENCODE_MODEL" != "null" ]; then
|
||||
else
|
||||
set --
|
||||
fi
|
||||
cat {run_dir}/gsd-review-prompt.md | opencode run "$@" --format json - 2>{run_dir}/gsd-review-opencode.err > {run_dir}/gsd-review-opencode.json
|
||||
cat {run_dir}/gsd-review-prompt.md | opencode run "$@" $OPENCODE_EFFORT_ARGS --format json - 2>{run_dir}/gsd-review-opencode.err > {run_dir}/gsd-review-opencode.json
|
||||
# Reconstruct the review from the assistant text parts into a variable and test
|
||||
# its CONTENT, not the file size: an empty extraction still prints a trailing
|
||||
# newline that would fool a `[ -s file ]` check into skipping the stub.
|
||||
|
||||
@@ -34,7 +34,9 @@ const { resolveModelInternal, resolveModelForTier, resolveProviderEscalation, re
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import agentCommandRouterMod = require('./agent-command-router.cjs');
|
||||
const { AGENT_FAILURE_CLASSES } = agentCommandRouterMod;
|
||||
import { renderEffortForRuntime, RUNTIMES_WITH_FAST_MODE } from './model-catalog.cjs';
|
||||
import { renderEffortForRuntime, renderEffortArgv, RUNTIMES_WITH_FAST_MODE } from './model-catalog.cjs';
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import hostIntegrationMod = require('./host-integration.cjs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import planningWorkspace = require('./planning-workspace.cjs');
|
||||
const { planningDir, planningPaths } = planningWorkspace;
|
||||
@@ -525,9 +527,9 @@ function cmdResolveGranularity(cwd: string, phaseType: string | undefined, raw:
|
||||
* fast_mode, fast_mode_supported, [unknown_agent] }
|
||||
*
|
||||
* Flags: --effort <level>, --fast-mode <true|false>, --attempt <n>,
|
||||
* --failure-class <class> (#2296)
|
||||
* --failure-class <class> (#2296), --host <runtime-id> (#2481)
|
||||
*/
|
||||
function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: boolean, opts?: { effortOverride?: string; fastModeOverride?: boolean; attempt?: number; failureClass?: string }): void {
|
||||
function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: boolean, opts?: { effortOverride?: string; fastModeOverride?: boolean; attempt?: number; failureClass?: string; host?: string }): void {
|
||||
if (!agentType) {
|
||||
error('agent-type required');
|
||||
}
|
||||
@@ -587,11 +589,55 @@ function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: bo
|
||||
fast_mode: fastMode,
|
||||
fast_mode_supported: fastModeSupported,
|
||||
};
|
||||
// ADR-1239 amendment (#2481) / ADR-443 path (a): invocation-time effort for a
|
||||
// named host. The host's negotiated `effortSurface` decides WHETHER an argument
|
||||
// is emitted; the catalog knows the syntax. Absent --host the contract is
|
||||
// byte-identical to before, so every existing caller is unaffected.
|
||||
if (typeof opts.host === 'string' && opts.host.length > 0) {
|
||||
const surface = effortSurfaceForHost(cwd, opts.host);
|
||||
const argvRendered = renderEffortArgv(opts.host, effort, surface);
|
||||
result['host'] = opts.host;
|
||||
result['effort_surface'] = surface;
|
||||
result['effort_argv'] = argvRendered.argv;
|
||||
result['effort_argv_string'] = argvRendered.argv.join(' ');
|
||||
result['effort_argv_value'] = argvRendered.value;
|
||||
}
|
||||
|
||||
if (!agentModels) result['unknown_agent'] = true;
|
||||
if (escalation) result['escalation'] = escalation;
|
||||
output(result, raw, effort);
|
||||
}
|
||||
|
||||
/**
|
||||
* ADR-1239 amendment (#2481) — resolve a host's negotiated `effortSurface`.
|
||||
*
|
||||
* Reads the host's runtime descriptor from the generated capability registry and
|
||||
* runs it through the Host-Integration negotiation so the trust-boundary invariant
|
||||
* applies here exactly as everywhere else: an unknown host, a missing axis, or the
|
||||
* `undocumented` sentinel all degrade to the safe floor rather than being trusted.
|
||||
* Never throws — a lookup failure yields `'none'`, which renders no argument.
|
||||
*/
|
||||
function effortSurfaceForHost(cwd: string, host: string): string {
|
||||
void cwd;
|
||||
try {
|
||||
// Mirrors the lazy-require pattern from runtime-slash.cts §runtimeSlash —
|
||||
// capability-registry.cjs is generated and carries no type declarations.
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { runtimes } = require('./capability-registry.cjs') as {
|
||||
runtimes: Record<string, { runtime?: { hostIntegration?: unknown } }>;
|
||||
};
|
||||
const declared = runtimes[host]?.runtime?.hostIntegration;
|
||||
if (!declared || typeof declared !== 'object') return 'none';
|
||||
// The descriptor is untrusted JSON; negotiation applies the trust-boundary
|
||||
// invariant (effective ⊆ host-declared ∩ engine-known) and fails closed.
|
||||
const negotiated = hostIntegrationMod.negotiateHostCapabilities(declared);
|
||||
const surface: unknown = negotiated?.effective?.effortSurface;
|
||||
return typeof surface === 'string' ? surface : 'none';
|
||||
} catch {
|
||||
return 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #488 — Replace or inject the `effort:` value in YAML frontmatter.
|
||||
* Unlike injectEffortFrontmatter (install.js), this overwrites an existing value.
|
||||
|
||||
@@ -45,6 +45,15 @@ const HOST_INTEGRATION_AXES = Object.freeze({
|
||||
transport: Object.freeze(['mcp', 'native-extension'] as const),
|
||||
runtime: Object.freeze(['node', 'bun', 'sandboxed-web', 'python', 'go', 'rust', 'electron', 'other'] as const),
|
||||
subagentToolkit: Object.freeze(['full', 'read-only'] as const),
|
||||
// ADR-1239 amendment (#2481): how reasoning effort reaches this host.
|
||||
// `argv` — deliverable as an argument on the host's own invocation.
|
||||
// `none` — the host exposes no reasoning-effort mechanism.
|
||||
// `undocumented` is NOT a member here; it is the corpus-wide sentinel above.
|
||||
// A config-file-only surface deliberately has NO vocabulary member: the only
|
||||
// host that ever had one (Gemini CLI's thinkingConfig) was removed as a sunset
|
||||
// runtime in #1928/#1996, and neither its successor Antigravity CLI nor ZCode
|
||||
// documents a reasoning setting. Adding a member with no host would be a guess.
|
||||
effortSurface: Object.freeze(['argv', 'none'] as const),
|
||||
});
|
||||
|
||||
const INTERFACE_POINTS = Object.freeze(['command', 'dispatch', 'model', 'hooks', 'state', 'artifact'] as const);
|
||||
@@ -61,6 +70,7 @@ type StateIO = 'filesystem' | 'sandboxed-storage' | 'session-log-append
|
||||
type Transport = 'mcp' | 'native-extension';
|
||||
type HostRuntime = 'node' | 'bun' | 'sandboxed-web' | 'python' | 'go' | 'rust' | 'electron' | 'other';
|
||||
type SubagentToolkit = 'full' | 'read-only';
|
||||
type EffortSurface = 'argv' | 'none';
|
||||
type DegradationLevel = 'full' | 'degraded' | 'absent';
|
||||
type InterfacePoint = 'command' | 'dispatch' | 'model' | 'hooks' | 'state' | 'artifact';
|
||||
|
||||
@@ -82,6 +92,7 @@ interface HostIntegrationAxes {
|
||||
stateIO: StateIO;
|
||||
transport: Transport;
|
||||
runtime: HostRuntime;
|
||||
effortSurface: EffortSurface;
|
||||
}
|
||||
|
||||
interface DegradationResult {
|
||||
@@ -104,6 +115,7 @@ const SAFE_DEFAULTS: HostIntegrationAxes = {
|
||||
stateIO: 'session-log-append',
|
||||
transport: 'mcp',
|
||||
runtime: 'node',
|
||||
effortSurface: 'none',
|
||||
};
|
||||
|
||||
const PROFILE_BASELINES: Readonly<Record<'programmatic-cli' | 'declarative-cli' | 'ide', HostIntegrationAxes>> =
|
||||
@@ -117,6 +129,7 @@ const PROFILE_BASELINES: Readonly<Record<'programmatic-cli' | 'declarative-cli'
|
||||
stateIO: 'filesystem',
|
||||
transport: 'mcp',
|
||||
runtime: 'node',
|
||||
effortSurface: 'none',
|
||||
} as HostIntegrationAxes),
|
||||
'declarative-cli': Object.freeze({
|
||||
embeddingMode: 'declarative',
|
||||
@@ -127,6 +140,7 @@ const PROFILE_BASELINES: Readonly<Record<'programmatic-cli' | 'declarative-cli'
|
||||
stateIO: 'filesystem',
|
||||
transport: 'mcp',
|
||||
runtime: 'node',
|
||||
effortSurface: 'none',
|
||||
} as HostIntegrationAxes),
|
||||
'ide': Object.freeze({
|
||||
embeddingMode: 'imperative',
|
||||
@@ -137,6 +151,7 @@ const PROFILE_BASELINES: Readonly<Record<'programmatic-cli' | 'declarative-cli'
|
||||
stateIO: 'sandboxed-storage',
|
||||
transport: 'mcp',
|
||||
runtime: 'sandboxed-web',
|
||||
effortSurface: 'none',
|
||||
} as HostIntegrationAxes),
|
||||
});
|
||||
|
||||
@@ -190,6 +205,13 @@ function degradationFor(point: InterfacePoint, axes: Partial<HostIntegrationAxes
|
||||
}
|
||||
|
||||
case 'model': {
|
||||
// NOTE (#2481): the effortSurface axis is deliberately NOT folded into this
|
||||
// level. `modelMode` has graded interface point 3 since Phase A, and every
|
||||
// existing consumer reads it as "can GSD drive model selection". Widening it
|
||||
// to also mean "…and deliver effort" silently redefines an established
|
||||
// contract — an `active` host with no declared effort surface would flip
|
||||
// from `full` to `absent`. Effort is negotiated on its own axis and read
|
||||
// from `effective.effortSurface` by the consumers that care.
|
||||
const mm = (axes as Record<string, unknown>).modelMode;
|
||||
if (mm === 'active') return { level: 'full', fallback: '' };
|
||||
if (mm === 'passive') return { level: 'degraded', fallback: 'instruction-injection / per-agent model field' };
|
||||
@@ -262,6 +284,7 @@ const DEFAULT_ENGINE: EngineCapabilities = {
|
||||
stateIO: 'filesystem',
|
||||
transport: 'mcp',
|
||||
runtime: 'node',
|
||||
effortSurface: 'argv',
|
||||
},
|
||||
known: HOST_INTEGRATION_AXES,
|
||||
};
|
||||
@@ -346,6 +369,14 @@ function negotiateHostCapabilities(
|
||||
if (axis === 'modelMode') {
|
||||
if (hostVal === 'active' && engineVal === 'passive') return 'passive';
|
||||
}
|
||||
// For effortSurface: 'argv' > 'none'. An engine that cannot deliver the
|
||||
// host's richer channel caps the result to what it can drive.
|
||||
if (axis === 'effortSurface') {
|
||||
const RANK: Record<string, number> = { argv: 1, none: 0 };
|
||||
const hr = RANK[hostVal as string] ?? 0;
|
||||
const er = RANK[engineVal as string] ?? 0;
|
||||
if (hr > er) return engineVal;
|
||||
}
|
||||
return hostVal;
|
||||
}
|
||||
|
||||
@@ -357,6 +388,7 @@ function negotiateHostCapabilities(
|
||||
const effectiveStateIO = negotiateScalar('stateIO');
|
||||
const effectiveTransport = negotiateScalar('transport');
|
||||
const effectiveRuntime = negotiateScalar('runtime');
|
||||
const effectiveEffortSurface = negotiateScalar('effortSurface');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dispatch struct negotiation
|
||||
@@ -456,6 +488,7 @@ function negotiateHostCapabilities(
|
||||
stateIO: effectiveStateIO,
|
||||
transport: effectiveTransport,
|
||||
runtime: effectiveRuntime,
|
||||
effortSurface: effectiveEffortSurface,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -222,6 +222,82 @@ export interface RenderedEffort {
|
||||
channel: string | null;
|
||||
}
|
||||
|
||||
// ─── Invocation-time (argv) effort rendering ─────────────────────────────────
|
||||
//
|
||||
// ADR-1239 amendment (#2481) / ADR-443 path (a). EFFORT_RENDERING above covers the
|
||||
// two INSTALL-TIME channels (`frontmatter`, `api`) — effort baked into a generated
|
||||
// artifact. This table covers the INVOCATION-TIME channel: the argument appended to
|
||||
// a host CLI spawned as a subprocess.
|
||||
//
|
||||
// WHETHER to emit is not decided here — it is the negotiated `effortSurface` axis on
|
||||
// the host's descriptor (`argv` | `none`). This table only knows the
|
||||
// syntax for hosts whose surface is `argv`. A host absent from this table renders
|
||||
// null, so an undeclared or undocumented host silently gets nothing rather than a
|
||||
// guessed flag.
|
||||
export interface EffortArgvSpec {
|
||||
/** Render the argv fragment for an already-clamped effort level. */
|
||||
render(level: string): string[];
|
||||
/** Levels this CLI accepts; anything else clamps via `clamp` first. */
|
||||
supported: Set<string>;
|
||||
clamp(level: string): string;
|
||||
}
|
||||
|
||||
export const EFFORT_ARGV: Record<string, EffortArgvSpec> = {
|
||||
// Verified against `claude --help`: `--effort <level>`.
|
||||
claude: {
|
||||
render: (level: string): string[] => ['--effort', level],
|
||||
supported: new Set(['low', 'medium', 'high', 'xhigh', 'max']),
|
||||
clamp: (level: string): string => (level === 'minimal' ? 'low' : level),
|
||||
},
|
||||
// Verified against `opencode run --help`: `--variant` — "model variant
|
||||
// (provider-specific reasoning effort, e.g., high, max, minimal)".
|
||||
opencode: {
|
||||
render: (level: string): string[] => ['--variant', level],
|
||||
supported: new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']),
|
||||
clamp: (level: string): string => level,
|
||||
},
|
||||
// First-party Codex docs: `model_reasoning_effort` is a config-only key with no
|
||||
// dedicated flag, so the generic `-c key=value` override is the only argv route.
|
||||
codex: {
|
||||
render: (level: string): string[] => ['-c', `model_reasoning_effort=${level}`],
|
||||
supported: new Set(['minimal', 'low', 'medium', 'high', 'xhigh']),
|
||||
clamp: (level: string): string => (level === 'max' ? 'xhigh' : level),
|
||||
},
|
||||
};
|
||||
|
||||
export interface RenderedEffortArgv {
|
||||
argv: string[];
|
||||
value: string | null;
|
||||
host: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the invocation-time effort argument for a host.
|
||||
*
|
||||
* `effortSurface` is the host's negotiated axis value. Only `argv` produces an
|
||||
* argument; `none`, `undocumented`, and anything unrecognised produce nothing.
|
||||
* Never throws.
|
||||
*/
|
||||
export function renderEffortArgv(
|
||||
host: string,
|
||||
universalEffort: string,
|
||||
effortSurface: string | null | undefined,
|
||||
): RenderedEffortArgv {
|
||||
const empty: RenderedEffortArgv = { argv: [], value: null, host };
|
||||
if (effortSurface !== 'argv') return empty;
|
||||
// Own-property lookup only. A plain `EFFORT_ARGV[host]` resolves `__proto__`
|
||||
// (and `constructor`/`toString`) to inherited members, which are truthy but
|
||||
// carry no `clamp`/`render` — a hostile host id would throw instead of
|
||||
// degrading. The host id reaches here from a descriptor, i.e. untrusted JSON.
|
||||
if (typeof host !== 'string' || !Object.prototype.hasOwnProperty.call(EFFORT_ARGV, host)) return empty;
|
||||
const spec = EFFORT_ARGV[host];
|
||||
if (!spec || typeof spec.clamp !== 'function' || typeof spec.render !== 'function') return empty;
|
||||
if (typeof universalEffort !== 'string' || universalEffort.length === 0) return empty;
|
||||
const clamped = spec.clamp(universalEffort);
|
||||
if (!spec.supported.has(clamped)) return empty;
|
||||
return { argv: spec.render(clamped), value: clamped, host };
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a universal effort string for a specific runtime.
|
||||
*/
|
||||
|
||||
408
tests/effort-surface-axis.test.cjs
Normal file
408
tests/effort-surface-axis.test.cjs
Normal file
@@ -0,0 +1,408 @@
|
||||
// allow-test-rule: source-text-is-the-product
|
||||
// The final describe block asserts on gsd-core/workflows/review.md's text. A
|
||||
// workflow .md IS what the runtime loads — its literal command lines are the
|
||||
// deployed contract, and there is no runtime seam that executes review.md here.
|
||||
// Every other block in this file is behavioral (CLI + module surface).
|
||||
|
||||
/**
|
||||
* #2481 — ADR-1239 `effortSurface` axis + ADR-443 path (a).
|
||||
*
|
||||
* Before this change effort reached a runtime only through install-time channels
|
||||
* (EFFORT_RENDERING's `frontmatter`/`api`), so a reviewer CLI spawned as a
|
||||
* subprocess silently inherited whatever effort sat in the user's own global CLI
|
||||
* config. These tests pin the invocation-time channel: the negotiated axis that
|
||||
* decides WHETHER effort is deliverable, the renderer that knows the syntax, and
|
||||
* the live orchestration path that carries it.
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const fc = require('fast-check');
|
||||
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const REPO_ROOT = path.resolve(__dirname, '..');
|
||||
const {
|
||||
renderEffortArgv,
|
||||
EFFORT_ARGV,
|
||||
} = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'model-catalog.cjs'));
|
||||
const {
|
||||
HOST_INTEGRATION_AXES,
|
||||
negotiateHostCapabilities,
|
||||
degradationFor,
|
||||
} = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'host-integration.cjs'));
|
||||
const {
|
||||
_HOST_INTEGRATION_VOCAB,
|
||||
validateRuntimeBody,
|
||||
} = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'capability-validator.cjs'));
|
||||
|
||||
/**
|
||||
* A real shipped descriptor with one hostIntegration axis stripped.
|
||||
*
|
||||
* Deriving the fixture from a descriptor this gate did not author satisfies the
|
||||
* fixture-provenance rule (#2371) — a hand-built body would only ever encode the
|
||||
* author's mental model of a valid descriptor, which is how the required-axis
|
||||
* defect reached the runner in the first place.
|
||||
*/
|
||||
function shippedDescriptorWithout(axis) {
|
||||
const cap = JSON.parse(
|
||||
fs.readFileSync(path.join(REPO_ROOT, 'capabilities', 'vscode', 'capability.json'), 'utf8'),
|
||||
);
|
||||
delete cap.runtime.hostIntegration[axis];
|
||||
return cap;
|
||||
}
|
||||
|
||||
/** Write a project whose effort cascade resolves to a known universal value. */
|
||||
function projectWithEffort(effort) {
|
||||
const dir = createTempProject();
|
||||
fs.writeFileSync(
|
||||
path.join(dir, '.planning', 'config.json'),
|
||||
JSON.stringify({ effort: { default: effort } }, null, 2),
|
||||
);
|
||||
return dir;
|
||||
}
|
||||
|
||||
describe('#2481 effortSurface — closed vocabulary', () => {
|
||||
test('is exactly argv|none — no config-file member', () => {
|
||||
// Gemini CLI was the only host with a config-file effort surface and was
|
||||
// removed as a sunset runtime (8f2ebbe9b / #1928 / PR #1996). A member no
|
||||
// supported host can claim would invite guessed descriptor values.
|
||||
assert.deepEqual([...HOST_INTEGRATION_AXES.effortSurface], ['argv', 'none']);
|
||||
});
|
||||
|
||||
test('engine vocabulary and validator mirror agree (parity guard)', () => {
|
||||
assert.deepEqual(
|
||||
[...HOST_INTEGRATION_AXES.effortSurface],
|
||||
[..._HOST_INTEGRATION_VOCAB.effortSurface],
|
||||
);
|
||||
});
|
||||
|
||||
test('undocumented is NOT a vocabulary member — it is the corpus sentinel', () => {
|
||||
assert.ok(!HOST_INTEGRATION_AXES.effortSurface.includes('undocumented'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 effortSurface — negotiation fails closed', () => {
|
||||
const cases = [
|
||||
['argv declared', 'argv', 'argv'],
|
||||
['undocumented sentinel', 'undocumented', 'none'],
|
||||
['retired value (config-file)', 'config-file', 'none'],
|
||||
['unknown/future value', 'quantum-telepathy', 'none'],
|
||||
['empty string', '', 'none'],
|
||||
['none declared', 'none', 'none'],
|
||||
];
|
||||
for (const [label, declared, expected] of cases) {
|
||||
test(`${label} -> ${expected}`, () => {
|
||||
const r = negotiateHostCapabilities({ protocolVersion: 1, modelMode: 'active', effortSurface: declared });
|
||||
assert.equal(r.effective.effortSurface, expected);
|
||||
});
|
||||
}
|
||||
|
||||
test('axis omitted entirely -> safe floor, and the omission is warned', () => {
|
||||
const r = negotiateHostCapabilities({ protocolVersion: 1, modelMode: 'active' });
|
||||
assert.equal(r.effective.effortSurface, 'none');
|
||||
assert.ok(r.warnings.some((w) => String(w).includes('effortSurface')));
|
||||
});
|
||||
|
||||
test('a descriptor that omits the axis entirely still validates clean', () => {
|
||||
// The axis was added after descriptors existed. Requiring it would invalidate
|
||||
// every pre-existing descriptor — including third-party ones — and break the
|
||||
// "purely additive" property ADR-1239 promises for external descriptors.
|
||||
// Regression guard: 48 suites failed across both node lanes when it was required.
|
||||
const cap = shippedDescriptorWithout('effortSurface');
|
||||
const errors = validateRuntimeBody(cap);
|
||||
assert.deepEqual(
|
||||
errors, [],
|
||||
`a descriptor without effortSurface must validate clean, got: ${JSON.stringify(errors)}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('optional does not mean unvalidated — a present bad value is still rejected', () => {
|
||||
const cap = shippedDescriptorWithout('effortSurface');
|
||||
cap.runtime.hostIntegration.effortSurface = 'config-file'; // retired value
|
||||
const errors = validateRuntimeBody(cap);
|
||||
assert.ok(
|
||||
errors.some((e) => String(e).includes('effortSurface')),
|
||||
`a present invalid value must error, got: ${JSON.stringify(errors)}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('an undeclared axis is never invented from a profile baseline', () => {
|
||||
// Regression guard for the failure this axis was designed against: a
|
||||
// programmatic-cli host must not inherit `argv` merely by being programmatic.
|
||||
const r = negotiateHostCapabilities({
|
||||
protocolVersion: 1,
|
||||
embeddingMode: 'imperative',
|
||||
commandSurface: 'slash-file',
|
||||
modelMode: 'active',
|
||||
});
|
||||
assert.equal(r.effective.effortSurface, 'none');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 effortSurface — is its own axis, not folded into the model point', () => {
|
||||
test('the model interface point still grades on modelMode alone', () => {
|
||||
// Deliberate: modelMode has graded interface point 3 since Phase A. Widening
|
||||
// it to also mean "delivers effort" would silently redefine that contract for
|
||||
// every existing consumer. Effort is read from effective.effortSurface.
|
||||
assert.equal(degradationFor('model', { modelMode: 'active' }).level, 'full');
|
||||
assert.equal(degradationFor('model', { modelMode: 'passive' }).level, 'degraded');
|
||||
});
|
||||
|
||||
test('declaring an effort surface does not change the model point', () => {
|
||||
for (const es of ['argv', 'none', 'undocumented', undefined]) {
|
||||
assert.equal(degradationFor('model', { modelMode: 'active', effortSurface: es }).level, 'full');
|
||||
}
|
||||
});
|
||||
|
||||
test('degradationFor never throws on a malformed axes object', () => {
|
||||
for (const axes of [{}, { modelMode: null }, { effortSurface: 42 }, { modelMode: 'active', effortSurface: [] }]) {
|
||||
assert.ok(['full', 'degraded', 'absent'].includes(degradationFor('model', axes).level));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 renderEffortArgv — per-host syntax and clamping', () => {
|
||||
test('claude renders --effort', () => {
|
||||
assert.deepEqual(renderEffortArgv('claude', 'xhigh', 'argv').argv, ['--effort', 'xhigh']);
|
||||
});
|
||||
|
||||
test('opencode renders --variant', () => {
|
||||
assert.deepEqual(renderEffortArgv('opencode', 'high', 'argv').argv, ['--variant', 'high']);
|
||||
});
|
||||
|
||||
test('codex renders the generic -c config override, not a dedicated flag', () => {
|
||||
// codex-rs/exec/src/cli.rs: model_reasoning_effort is NOT a CLI flag
|
||||
// (config.toml key only), so -c key=value is the only argv route.
|
||||
assert.deepEqual(
|
||||
renderEffortArgv('codex', 'high', 'argv').argv,
|
||||
['-c', 'model_reasoning_effort=high'],
|
||||
);
|
||||
});
|
||||
|
||||
test('clamps the provider-unique tail levels', () => {
|
||||
// claude has no `minimal`; codex has no `max`.
|
||||
assert.deepEqual(renderEffortArgv('claude', 'minimal', 'argv').argv, ['--effort', 'low']);
|
||||
assert.deepEqual(renderEffortArgv('codex', 'max', 'argv').argv, ['-c', 'model_reasoning_effort=xhigh']);
|
||||
});
|
||||
|
||||
test('emits nothing when the surface is not argv', () => {
|
||||
for (const surface of ['none', 'undocumented', 'config-file', '', null, undefined]) {
|
||||
assert.deepEqual(renderEffortArgv('claude', 'xhigh', surface).argv, []);
|
||||
}
|
||||
});
|
||||
|
||||
test('emits nothing for a host with no known syntax', () => {
|
||||
for (const host of ['gemini', 'cursor', 'zcode', '']) {
|
||||
assert.deepEqual(renderEffortArgv(host, 'high', 'argv').argv, []);
|
||||
}
|
||||
});
|
||||
|
||||
test('inherited Object members are not mistaken for host specs', () => {
|
||||
// Regression guard: a bare EFFORT_ARGV[host] lookup resolves these to
|
||||
// inherited members — truthy, but with no clamp/render — so a hostile host
|
||||
// id from an untrusted descriptor threw instead of degrading.
|
||||
for (const host of ['__proto__', 'constructor', 'toString', 'hasOwnProperty', 'valueOf']) {
|
||||
assert.deepEqual(
|
||||
renderEffortArgv(host, 'high', 'argv').argv, [],
|
||||
`${host} must degrade to no argument, not throw`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('emits nothing for a missing or unrecognised effort level', () => {
|
||||
for (const level of ['', 'bogus', 'HIGH', ' high', null, undefined, 42]) {
|
||||
assert.deepEqual(renderEffortArgv('claude', level, 'argv').argv, []);
|
||||
}
|
||||
});
|
||||
|
||||
test('property: a rendered level is always inside that host\'s supported set', () => {
|
||||
const hosts = Object.keys(EFFORT_ARGV);
|
||||
const levels = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
||||
fc.assert(
|
||||
fc.property(fc.constantFrom(...hosts), fc.constantFrom(...levels), (host, level) => {
|
||||
const r = renderEffortArgv(host, level, 'argv');
|
||||
if (r.argv.length === 0) return true;
|
||||
return EFFORT_ARGV[host].supported.has(r.value);
|
||||
}),
|
||||
{ numRuns: 200, seed: 2481 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 live path — resolve-execution carries invocation-time effort', () => {
|
||||
test('--host renders the argument for an argv host', (t) => {
|
||||
const dir = projectWithEffort('xhigh');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const r = runGsdTools('query resolve-execution gsd-planner --host claude', dir);
|
||||
assert.ok(r.success, `resolve-execution failed: ${r.error}`);
|
||||
const out = JSON.parse(r.output);
|
||||
assert.equal(out.effort, 'xhigh');
|
||||
assert.equal(out.effort_surface, 'argv');
|
||||
assert.deepEqual(out.effort_argv, ['--effort', 'xhigh']);
|
||||
assert.equal(out.effort_argv_string, '--effort xhigh');
|
||||
});
|
||||
|
||||
test('--host on a host without a documented surface renders nothing', (t) => {
|
||||
const dir = projectWithEffort('xhigh');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const out = JSON.parse(runGsdTools('query resolve-execution gsd-planner --host cursor', dir).output);
|
||||
assert.equal(out.effort_surface, 'none');
|
||||
assert.deepEqual(out.effort_argv, []);
|
||||
assert.equal(out.effort_argv_string, '');
|
||||
});
|
||||
|
||||
test('an unknown host degrades closed rather than erroring', (t) => {
|
||||
const dir = projectWithEffort('high');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const r = runGsdTools('query resolve-execution gsd-planner --host not-a-real-host', dir);
|
||||
assert.ok(r.success, 'an unknown host must degrade, not fail');
|
||||
const out = JSON.parse(r.output);
|
||||
assert.equal(out.effort_surface, 'none');
|
||||
assert.deepEqual(out.effort_argv, []);
|
||||
});
|
||||
|
||||
test('omitting --host leaves the JSON contract untouched', (t) => {
|
||||
const dir = projectWithEffort('high');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const out = JSON.parse(runGsdTools('query resolve-execution gsd-planner', dir).output);
|
||||
for (const k of ['host', 'effort_surface', 'effort_argv', 'effort_argv_string', 'effort_argv_value']) {
|
||||
assert.ok(!(k in out), `--host absent must not add "${k}" to the contract`);
|
||||
}
|
||||
});
|
||||
|
||||
test('--host requires a value', (t) => {
|
||||
const dir = projectWithEffort('high');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const r = runGsdTools('query resolve-execution gsd-planner --host', dir);
|
||||
assert.ok(!r.success, 'a valueless --host must be a usage error');
|
||||
});
|
||||
|
||||
test('a shell-metacharacter host is not interpolated, just unmatched', (t) => {
|
||||
const dir = projectWithEffort('high');
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const r = runGsdTools('query resolve-execution gsd-planner --host "claude; touch pwned"', dir);
|
||||
assert.ok(r.success);
|
||||
assert.deepEqual(JSON.parse(r.output).effort_argv, []);
|
||||
assert.ok(!fs.existsSync(path.join(dir, 'pwned')), 'no shell interpolation of the host value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 — the escalation surface renders argv (CLI-level, not a workflow claim)', () => {
|
||||
// NAMING IS DELIBERATE. This exercises `resolve-execution --attempt` directly,
|
||||
// which is the CLI surface ADR-443's blocker explicitly EXCLUDES when it asks
|
||||
// for "a real caller outside src/commands.cts's CLI surface and tests". It
|
||||
// proves the escalation ladder still renders a host argument; it does NOT
|
||||
// prove any workflow escalates. The live workflow caller for Decision item 6
|
||||
// is #2296's gsd-core/references/execute-phase-quota-recovery.md, asserted
|
||||
// separately below.
|
||||
test('--attempt walks the effort ladder above the configured default', (t) => {
|
||||
const dir = createTempProject();
|
||||
t.after(() => cleanup(dir));
|
||||
fs.writeFileSync(
|
||||
path.join(dir, '.planning', 'config.json'),
|
||||
JSON.stringify({
|
||||
effort: { default: 'low' },
|
||||
dynamic_routing: { enabled: true, escalate_on_failure: true, max_escalations: 3 },
|
||||
}, null, 2),
|
||||
);
|
||||
|
||||
const at = (n) => JSON.parse(
|
||||
runGsdTools(`query resolve-execution gsd-planner --host claude --attempt ${n}`, dir).output,
|
||||
);
|
||||
|
||||
// attempt 0 is the un-escalated baseline; a later attempt must not be lower.
|
||||
const base = at(0);
|
||||
const later = at(2);
|
||||
const RANK = { minimal: 0, low: 1, medium: 2, high: 3, xhigh: 4, max: 5 };
|
||||
assert.equal(base.effort, 'low');
|
||||
assert.ok(
|
||||
RANK[later.effort] >= RANK[base.effort],
|
||||
`escalation must not lower effort: attempt0=${base.effort} attempt2=${later.effort}`,
|
||||
);
|
||||
// Whatever the ladder resolved, it must still reach the host as an argument.
|
||||
assert.equal(later.effort_surface, 'argv');
|
||||
assert.deepEqual(later.effort_argv, ['--effort', later.effort]);
|
||||
});
|
||||
|
||||
test('a negative --attempt is rejected', (t) => {
|
||||
const dir = projectWithEffort('high');
|
||||
t.after(() => cleanup(dir));
|
||||
assert.ok(!runGsdTools('query resolve-execution gsd-planner --attempt -1', dir).success);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 — ADR-443 mechanism callers, as they actually exist', () => {
|
||||
const quotaRecovery = fs.readFileSync(
|
||||
path.join(REPO_ROOT, 'gsd-core', 'references', 'execute-phase-quota-recovery.md'),
|
||||
'utf8',
|
||||
);
|
||||
const executePhase = fs.readFileSync(
|
||||
path.join(REPO_ROOT, 'gsd-core', 'workflows', 'execute-phase.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
test('Decision item 6 (escalation) has its live caller — from #2296, not this change', () => {
|
||||
assert.match(
|
||||
quotaRecovery, /resolve-execution\s+gsd-executor\s+--attempt/,
|
||||
'execute-phase-quota-recovery.md must invoke resolve-execution with --attempt (#2296)',
|
||||
);
|
||||
assert.ok(
|
||||
executePhase.includes('references/execute-phase-quota-recovery.md'),
|
||||
'that reference must be @-included into execute-phase.md, or it is not a live caller',
|
||||
);
|
||||
});
|
||||
|
||||
test('Decision item 1 (invocation override) still has NO live caller', () => {
|
||||
// Guards the corrected ADR-443 claim. If someone later wires --effort into a
|
||||
// workflow, this fails and the ADR status text must be revisited — that is
|
||||
// the point: the ADR must not silently drift back to being wrong.
|
||||
const dirs = ['gsd-core/workflows', 'gsd-core/references', 'agents', 'commands'];
|
||||
const hits = [];
|
||||
const walk = (d) => {
|
||||
const abs = path.join(REPO_ROOT, d);
|
||||
if (!fs.existsSync(abs)) return;
|
||||
for (const e of fs.readdirSync(abs, { withFileTypes: true })) {
|
||||
const full = path.join(abs, e.name);
|
||||
if (e.isDirectory()) walk(path.relative(REPO_ROOT, full));
|
||||
else if (e.name.endsWith('.md') && /resolve-execution[^\r\n]*--effort\s/.test(fs.readFileSync(full, 'utf8'))) {
|
||||
hits.push(path.relative(REPO_ROOT, full));
|
||||
}
|
||||
}
|
||||
};
|
||||
dirs.forEach(walk);
|
||||
assert.deepEqual(
|
||||
hits, [],
|
||||
`ADR-443 records Decision item 1 as having no live caller; found: ${JSON.stringify(hits)}. ` +
|
||||
'Update the ADR-443 amendment before adding one.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2481 review workflow resolves effort per reviewer', () => {
|
||||
const reviewMd = fs.readFileSync(
|
||||
path.join(REPO_ROOT, 'gsd-core', 'workflows', 'review.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
test('review.md invokes resolve-execution — the grep ADR-443 said returned zero hits', () => {
|
||||
assert.ok(
|
||||
reviewMd.includes('resolve-execution'),
|
||||
'ADR-443 blocks on no shipped orchestration calling resolve-execution',
|
||||
);
|
||||
});
|
||||
|
||||
test('each argv reviewer receives its effort variable on the command line', () => {
|
||||
for (const [cli, varName] of [['claude', 'CLAUDE_EFFORT_ARGS'], ['codex', 'CODEX_EFFORT_ARGS'], ['opencode', 'OPENCODE_EFFORT_ARGS']]) {
|
||||
assert.ok(reviewMd.includes(`$${varName}`), `${cli} invocation must carry $${varName}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -39,7 +39,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "ea841e2865248e74",
|
||||
"gsd-core/bin/gsd-tools.cjs": "df767e8f2f10fd3b",
|
||||
"gsd-core/bin/gsd-tools.cjs": "cd7a95ed8536c5bc",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
|
||||
"gsd-core/workflows/remove-workspace.md": "1058d1d3160eb120",
|
||||
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
|
||||
"gsd-core/workflows/review.md": "6f6575621367b9ff",
|
||||
"gsd-core/workflows/review.md": "cb4ed7b2b77bba35",
|
||||
"gsd-core/workflows/scan.md": "1feb5f040e426562",
|
||||
"gsd-core/workflows/secure-phase.md": "34c0aba32c922218",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/scan.md": "e6424af05656725a",
|
||||
"gsd-core/workflows/secure-phase.md": "f8b9436c35372281",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -368,7 +368,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "8effc8742d58a11a",
|
||||
"gsd-core/workflows/remove-workspace.md": "64b73daff58b9aec",
|
||||
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
|
||||
"gsd-core/workflows/review.md": "8c05b76fe9d00c30",
|
||||
"gsd-core/workflows/review.md": "9eb81c7c61cfe50e",
|
||||
"gsd-core/workflows/scan.md": "9126e95bf9c50f7e",
|
||||
"gsd-core/workflows/secure-phase.md": "396e2f708abfa7a1",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -297,7 +297,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "015cde237c75be39",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "013126685d8a9d50",
|
||||
"gsd-core/workflows/review.md": "bba666ebc5e13130",
|
||||
"gsd-core/workflows/scan.md": "1890c846b98690bb",
|
||||
"gsd-core/workflows/secure-phase.md": "3f8fabf375f7d880",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "476aa24e8c4f03cf",
|
||||
"gsd-core/bin/gsd-tools.cjs": "f737db8e3aa1ce61",
|
||||
"gsd-core/bin/gsd-tools.cjs": "83c5cb31a129a87d",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -301,7 +301,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
|
||||
"gsd-core/workflows/remove-workspace.md": "79d3669cc9eb0b10",
|
||||
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
|
||||
"gsd-core/workflows/review.md": "075ea1aa6a2b8339",
|
||||
"gsd-core/workflows/review.md": "b1c5ebc05398ca45",
|
||||
"gsd-core/workflows/scan.md": "aa2ddc734251c24c",
|
||||
"gsd-core/workflows/secure-phase.md": "f7c624c1ab3cde3f",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/scan.md": "e6424af05656725a",
|
||||
"gsd-core/workflows/secure-phase.md": "f8b9436c35372281",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -404,7 +404,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
|
||||
"gsd-core/workflows/remove-workspace.md": "d0160c5d05bcb2bb",
|
||||
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
|
||||
"gsd-core/workflows/review.md": "e4e61845190da702",
|
||||
"gsd-core/workflows/review.md": "6c74f7151f41df8b",
|
||||
"gsd-core/workflows/scan.md": "9d331d74a5ad47e5",
|
||||
"gsd-core/workflows/secure-phase.md": "858ef74a9f247bd5",
|
||||
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "ea841e2865248e74",
|
||||
"gsd-core/bin/gsd-tools.cjs": "df767e8f2f10fd3b",
|
||||
"gsd-core/bin/gsd-tools.cjs": "cd7a95ed8536c5bc",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
|
||||
"gsd-core/workflows/remove-workspace.md": "e7e5b5b1e0cc9d81",
|
||||
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
|
||||
"gsd-core/workflows/review.md": "a9f422db38ab8bb9",
|
||||
"gsd-core/workflows/review.md": "31d6ec81b8c8dddc",
|
||||
"gsd-core/workflows/scan.md": "b8615653ffa85e12",
|
||||
"gsd-core/workflows/secure-phase.md": "3afa175ee48491af",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "2525f1ae8b086828",
|
||||
"gsd-core/bin/gsd-tools.cjs": "1c1255108c81fde6",
|
||||
"gsd-core/bin/gsd-tools.cjs": "c4737aad8ff34691",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "0572a83d71650937",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "ea18faf9ff633667",
|
||||
"gsd-core/workflows/review.md": "28a82741e3e8616d",
|
||||
"gsd-core/workflows/scan.md": "1890c846b98690bb",
|
||||
"gsd-core/workflows/secure-phase.md": "383a94ac2b089cc0",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "3a3409215044af9f",
|
||||
"gsd-core/bin/gsd-tools.cjs": "38cc609e9d4cbc3c",
|
||||
"gsd-core/bin/gsd-tools.cjs": "91cffa694a6becd3",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
|
||||
"gsd-core/workflows/remove-workspace.md": "42029a7559f1d8fb",
|
||||
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
|
||||
"gsd-core/workflows/review.md": "cde982eb704261ef",
|
||||
"gsd-core/workflows/review.md": "9a7fd3c1c2bd617d",
|
||||
"gsd-core/workflows/scan.md": "6c340b901b5d5d18",
|
||||
"gsd-core/workflows/secure-phase.md": "07415614146d8601",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "f5dc82bb63e2efa4",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "6033fab6d070c993",
|
||||
"gsd-core/workflows/review.md": "9299d96d44252a8a",
|
||||
"gsd-core/workflows/scan.md": "1890c846b98690bb",
|
||||
"gsd-core/workflows/secure-phase.md": "7e1613050966ae28",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -362,7 +362,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/scan.md": "e6424af05656725a",
|
||||
"gsd-core/workflows/secure-phase.md": "f8b9436c35372281",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
|
||||
"gsd-core/workflows/remove-workspace.md": "21a8581add5f31ae",
|
||||
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
|
||||
"gsd-core/workflows/review.md": "fbe68c9f86385274",
|
||||
"gsd-core/workflows/review.md": "a2c5d053c8ef6e27",
|
||||
"gsd-core/workflows/scan.md": "63cf8d9067610704",
|
||||
"gsd-core/workflows/secure-phase.md": "7ad9fd285facdef8",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
4
tests/fixtures/golden-install-parity/pi.json
vendored
4
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -6,7 +6,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -265,7 +265,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/scan.md": "e6424af05656725a",
|
||||
"gsd-core/workflows/secure-phase.md": "f8b9436c35372281",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "6e98d76e955e35a2",
|
||||
"gsd-core/bin/gsd-tools.cjs": "982a6ff24abb8e59",
|
||||
"gsd-core/bin/gsd-tools.cjs": "76bb73c8061771ac",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
|
||||
"gsd-core/workflows/remove-workspace.md": "ae520235f4d1f4a5",
|
||||
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
|
||||
"gsd-core/workflows/review.md": "b3548b43dcf1fb4e",
|
||||
"gsd-core/workflows/review.md": "5b59389282a276fc",
|
||||
"gsd-core/workflows/scan.md": "9ebe1d469171af0d",
|
||||
"gsd-core/workflows/secure-phase.md": "e5e8bf031e95cf1d",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "de4627dff103d527",
|
||||
"gsd-core/bin/gsd-tools.cjs": "cafd8c05ee085279",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9767839b936030c7",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
|
||||
"gsd-core/workflows/remove-workspace.md": "8ddc5f04f7c48d6c",
|
||||
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
|
||||
"gsd-core/workflows/review.md": "35d023ff48e3827f",
|
||||
"gsd-core/workflows/review.md": "2e10545b307c867c",
|
||||
"gsd-core/workflows/scan.md": "a3ce29ceb62b8192",
|
||||
"gsd-core/workflows/secure-phase.md": "85112be3547bced4",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "5636ca0b726871b2",
|
||||
"gsd-core/bin/gsd-tools.cjs": "abce82dc226344f2",
|
||||
"gsd-core/bin/gsd-tools.cjs": "b6944a9b7ab1ec52",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
|
||||
"gsd-core/workflows/remove-workspace.md": "30ca05fe4a3efc25",
|
||||
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
|
||||
"gsd-core/workflows/review.md": "805bea46bed56bd8",
|
||||
"gsd-core/workflows/review.md": "ac0c02f11e2ae1dc",
|
||||
"gsd-core/workflows/scan.md": "389025e963d0e502",
|
||||
"gsd-core/workflows/secure-phase.md": "666828983e5dd51f",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
"gsd-core/bin/gsd-tools.cjs": "d44831d1e2f9c1c1",
|
||||
"gsd-core/bin/gsd-tools.cjs": "9ad2ecce8b087676",
|
||||
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
|
||||
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
|
||||
"gsd-core/bin/shared/config-schema.manifest.json": "2f60b9eaa6cf6bc1",
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/scan.md": "e6424af05656725a",
|
||||
"gsd-core/workflows/secure-phase.md": "f8b9436c35372281",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -68,6 +68,12 @@ const ALLOWLIST = new Set([
|
||||
// belong in ALLOWLIST). Only add files that are large but otherwise clean.
|
||||
const SIZE_ONLY_WORKFLOWS = new Set([
|
||||
'gsd-core/workflows/docs-update.md', // ~51K after fix-loop truncation guard (#571)
|
||||
// ~50.7K after the per-reviewer effort wiring (#2481). This file sat at 49,971
|
||||
// chars — 29 below the 50K prompt-stuffing threshold — so it was going to trip
|
||||
// on whatever was added to it next. Size-only: the file is still fully injection
|
||||
// scanned, exactly like docs-update.md. Splitting it per the progressive-
|
||||
// disclosure pattern is the real fix and is worth its own change.
|
||||
'gsd-core/workflows/review.md',
|
||||
]);
|
||||
|
||||
// ─── Scanner ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"remove-phase.md": 8513,
|
||||
"remove-workspace.md": 7916,
|
||||
"resume-project.md": 17270,
|
||||
"review.md": 50497,
|
||||
"review.md": 51210,
|
||||
"scan.md": 7728,
|
||||
"secure-phase.md": 13987,
|
||||
"session-report.md": 4044,
|
||||
|
||||
Reference in New Issue
Block a user