Merge pull request #3542 from open-gsd/fix/3534-effective-effort

This commit is contained in:
Tom Boucher
2026-08-15 07:00:23 -04:00
committed by GitHub
4 changed files with 140 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
type: Added
pr: 3542
---
**`resolve-execution` now tells the truth about what the agent will run at** — the query reported only the config-cascade effort, which is not what an installed agent uses when its `effort:` frontmatter was hand-stripped or drifted. `--json` adds `effort_effective` (read from the installed agent frontmatter for the claude runtime; `"inherit"` when the key is absent) and `effort_effective_source` (`frontmatter` | `frontmatter-absent` | `resolved`). All existing fields, including `--pick effort`, are unchanged. (#3534)

View File

@@ -1416,6 +1416,14 @@ The model-catalog's `reasoning_effort` per-tier hint is a legacy field kept for
Valid effort values: `minimal`, `low`, `medium`, `high`, `xhigh`, `max`.
`query resolve-execution --json` reports two effort views ([#3534](https://github.com/open-gsd/gsd-core/issues/3534)):
`effort` is the **resolved** config-cascade value; `effort_effective` is what the installed
agent will actually run at — read from the installed agent's `effort:` frontmatter for the
claude runtime (`effort_effective_source: "frontmatter"`), reported as `"inherit"` when the
key is absent (`"frontmatter-absent"` — the agent follows the session effort), and equal to
the resolved value with source `"resolved"` when there is no install-time channel or no
agent file to read. `--pick effort` still returns the resolved value.
#### Where effort actually reaches — added in v1.8.0
Effort resolved from the cascade above reaches a runtime through one of two channels.

View File

@@ -620,6 +620,45 @@ function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: bo
const fastModeSupported = RUNTIMES_WITH_FAST_MODE.has(runtime);
// #3534 (10a): the effective effort — what the installed agent will actually
// run at. `effort` above is the config cascade; for the claude runtime the
// per-agent frontmatter key is the source of truth (Claude Code's Agent tool
// has no per-spawn effort parameter), so the query reads the installed file.
// An ABSENT key is a real state — the agent follows the session effort
// ('inherit'), not drift. No file / no frontmatter / any read failure means
// no evidence: the resolved value is reported, flagged 'resolved' so a
// consumer can tell evidence from echo. Additive only — every existing key
// is unchanged.
let effortEffectiveSource: 'frontmatter' | 'frontmatter-absent' | 'resolved' = 'resolved';
let effortEffective: string = effort;
if (runtime === 'claude') {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/unbound-method
const { getGlobalConfigDir } = require('./runtime-homes.cjs') as { getGlobalConfigDir(runtime: string, explicitDir?: string | null): string };
const agentsDirEff = path.join(getGlobalConfigDir(runtime), 'agents');
const agentPath = path.join(agentsDirEff, `${agentType}.md`);
// agentType is an unvalidated CLI positional: keep the read inside the
// agents dir so `../../x` cannot point it elsewhere (defense in depth —
// the reflected surface is only a frontmatter effort line).
if (!path.resolve(agentPath).startsWith(path.resolve(agentsDirEff) + path.sep)) {
throw new Error('agent path escapes the agents directory');
}
const agentContent = fs.readFileSync(agentPath, 'utf8');
// eslint-disable-next-line local/no-unbounded-quantifier -- same lazy `*?` bounded by the `^---$/m` closing anchor as the sibling frontmatter regexes in this file
const fmMatchEff = /^---\r?\n([\s\S]*?)^---\r?$/m.exec(agentContent);
if (fmMatchEff) {
const effortLine = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmMatchEff[1]);
if (effortLine) {
effortEffective = effortLine[1];
effortEffectiveSource = 'frontmatter';
} else {
effortEffective = 'inherit';
effortEffectiveSource = 'frontmatter-absent';
}
}
} catch { /* no frontmatter evidence — stay on the resolved value */ }
}
// Own-property guard: agentType is an unvalidated CLI positional, so a
// prototype-chain value ("toString", "constructor") would otherwise return
// an inherited truthy member from this plain object and misreport a
@@ -633,6 +672,8 @@ function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: bo
effort_rendered: rendered.value,
effort_param: rendered.param,
effort_propagation: rendered.channel,
effort_effective: effortEffective,
effort_effective_source: effortEffectiveSource,
fast_mode: fastMode,
fast_mode_supported: fastModeSupported,
};

View File

@@ -91,6 +91,92 @@ function projectWithEffort(effort) {
return dir;
}
describe('#3534 resolve-execution reports resolved AND effective effort', () => {
function agentHome(t, agentFileBody) {
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3534-home-'));
t.after(() => cleanup(home));
if (agentFileBody !== null) {
fs.mkdirSync(path.join(home, 'agents'), { recursive: true });
fs.writeFileSync(path.join(home, 'agents', 'gsd-executor.md'), agentFileBody);
}
return home;
}
function resolveExecution(dir, agent = 'gsd-executor', extra = [], env = {}) {
return JSON.parse(
runGsdTools(`query resolve-execution ${agent} ${extra.join(' ')}`, dir, env).output,
);
}
test('10a: effective effort reads the installed frontmatter (claude)', (t) => {
const dir = projectWithEffort('high');
t.after(() => cleanup(dir));
const home = agentHome(t, '---\nname: gsd-executor\neffort: low\ndescription: x\n---\nBody.\n');
// #3534: pass the fixture home as the CHILD env argument — testEnvBase()
// blanks CLAUDE_CONFIG_DIR after the process.env spread, so a process.env
// mutation never reaches the child (and a dev's real ~/.claude would).
const out = resolveExecution(dir, 'gsd-executor', [], { CLAUDE_CONFIG_DIR: home });
assert.equal(out.effort, 'high', 'resolved cascade value unchanged');
assert.equal(out.effort_effective, 'low', 'the installed frontmatter value');
assert.equal(out.effort_effective_source, 'frontmatter');
// Existing keys all still present, unchanged shape.
for (const k of ['model', 'profile', 'effort', 'effort_rendered', 'effort_param', 'effort_propagation', 'fast_mode', 'fast_mode_supported']) {
assert.ok(k in out, `existing key ${k} must remain`);
}
});
test('10a: absent frontmatter reports inherit as the effective state (the 10a repro)', (t) => {
const dir = projectWithEffort('high');
t.after(() => cleanup(dir));
const home = agentHome(t, '---\nname: gsd-executor\ndescription: x\n---\nBody.\n');
const out = resolveExecution(dir, 'gsd-executor', [], { CLAUDE_CONFIG_DIR: home });
assert.equal(out.effort, 'high');
assert.equal(out.effort_effective, 'inherit', 'absent key = follows the session');
assert.equal(out.effort_effective_source, 'frontmatter-absent');
});
test('10a: missing agent file falls back to resolved with the flag', (t) => {
const dir = projectWithEffort('high');
t.after(() => cleanup(dir));
const home = agentHome(t, null);
const out = resolveExecution(dir, 'gsd-executor', [], { CLAUDE_CONFIG_DIR: home });
assert.equal(out.effort_effective, out.effort);
assert.equal(out.effort_effective_source, 'resolved');
});
test('10a: runtimes without an install-time channel report resolved', (t) => {
const dir = createTempProject();
t.after(() => cleanup(dir));
fs.writeFileSync(
path.join(dir, '.planning', 'config.json'),
JSON.stringify({ runtime: 'codex', effort: { default: 'medium' } }, null, 2),
);
const out = resolveExecution(dir);
assert.equal(out.effort, 'medium');
assert.equal(out.effort_effective, 'medium');
assert.equal(out.effort_effective_source, 'resolved');
});
test('10a: CRLF frontmatter is read', (t) => {
const dir = projectWithEffort('high');
t.after(() => cleanup(dir));
const home = agentHome(t, ['---', 'name: gsd-executor', 'effort: xhigh', 'description: x', '---', 'Body.', ''].join('\r\n'));
const out = resolveExecution(dir, 'gsd-executor', [], { CLAUDE_CONFIG_DIR: home });
assert.equal(out.effort_effective, 'xhigh');
assert.equal(out.effort_effective_source, 'frontmatter');
});
test('10a: frontmatter-less agent file degrades to resolved', (t) => {
const dir = projectWithEffort('high');
t.after(() => cleanup(dir));
const home = agentHome(t, 'No frontmatter here at all.\n');
const out = resolveExecution(dir, 'gsd-executor', [], { CLAUDE_CONFIG_DIR: home });
assert.equal(out.effort_effective, out.effort);
assert.equal(out.effort_effective_source, 'resolved');
});
});
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