Merge pull request #2490 from open-gsd/docs/2481-adr-1239-effort-axis
feat(#2481): add a negotiated effortSurface axis and wire invocation-time effort
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user