enhance(#2229): add three-way claim disposition (admit/refute/abstain) to /gsd-explore research pass (#2543)
Closes #2229. Each claim surfaced by /gsd-explore's research pass is dispositioned admit, refute, or abstain, with abstentions routed to a visible ledger instead of being smoothed into confident prose. Refute and abstain are separated by whether the disagreeing source is authoritative for that claim; a strong prior is never authoritative alone. Two guards ride with it: conflict-abstention, and a tier floor that presents a would-be admit as an abstain when the researcher's resolved tier is the budget tier or cannot be determined. To make that floor enforceable, resolve-model now emits the effective tier (--pick tier). It was already computed above the resolve_model_ids omit gate but was unreachable from a workflow, which left the floor inert on every non-Claude install - the model id is blank under omit and runtime-substituted where a tier map exists, and the profile defaults to balanced. The tier signal mirrors every resolution step that can change which tier runs, including the model_policy preset, and reports unknown rather than guessing. Output is additive; model, profile and effort are unchanged. Two residuals are disclosed in the workflow rather than papered over: a raw-model-id model_overrides pin reports unknown and is floored (fails closed), and a model_profile_overrides entry repointing a tier at another tier's model can under-report (fails open, and predates this change). Admin merge used only to satisfy the missing secondary reviewer on a single-maintainer PR. No CI failure and no conflict were bypassed: 38 checks green, remote runner 32255/32255 on both Node lanes.
This commit is contained in:
@@ -33,7 +33,7 @@ import planningScopeMod = require('./planning-scope.cjs');
|
||||
const { SCOPE } = planningScopeMod;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import modelResolverMod = require('./model-resolver.cjs');
|
||||
const { resolveModelInternal, resolveModelForTier, resolveProviderEscalation, resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, resolveGranularityInternal, assertValidGranularityOverride } = modelResolverMod;
|
||||
const { resolveModelInternal, resolveTierInternal, resolveModelForTier, resolveProviderEscalation, resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, resolveGranularityInternal, assertValidGranularityOverride } = modelResolverMod;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import agentCommandRouterMod = require('./agent-command-router.cjs');
|
||||
const { AGENT_FAILURE_CLASSES } = agentCommandRouterMod;
|
||||
@@ -516,10 +516,20 @@ function cmdResolveModel(cwd: string, agentType: string | undefined, raw: boolea
|
||||
const model = resolveModelInternal(cwd, agentType!);
|
||||
const effort = resolveEffortInternal(cwd, agentType!);
|
||||
|
||||
const agentModels = (MODEL_PROFILES as Record<string, unknown>)[agentType!];
|
||||
// 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
|
||||
// genuinely unknown agent as known (unknown_agent dropped from the result).
|
||||
const agentModelsMap = MODEL_PROFILES as Record<string, unknown>;
|
||||
const agentModels = Object.hasOwn(agentModelsMap, agentType!) ? agentModelsMap[agentType!] : undefined;
|
||||
// #2229: `tier` is additive — existing keys and their values are untouched, so
|
||||
// every `--pick model` / `--pick profile` / `--raw` consumer is unaffected. It
|
||||
// exists because the model id is deliberately blank under resolve_model_ids:"omit",
|
||||
// which leaves a tier-sensitive guard with nothing to read.
|
||||
const tier = resolveTierInternal(cwd, agentType!);
|
||||
const result = agentModels
|
||||
? { model, profile, effort }
|
||||
: { model, profile, effort, unknown_agent: true };
|
||||
? { model, profile, effort, tier }
|
||||
: { model, profile, effort, tier, unknown_agent: true };
|
||||
output(result, raw, model);
|
||||
}
|
||||
|
||||
@@ -594,7 +604,12 @@ function cmdResolveExecution(cwd: string, agentType: string | undefined, raw: bo
|
||||
|
||||
const fastModeSupported = RUNTIMES_WITH_FAST_MODE.has(runtime);
|
||||
|
||||
const agentModels = (MODEL_PROFILES as Record<string, unknown>)[agentType!];
|
||||
// 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
|
||||
// genuinely unknown agent as known (unknown_agent dropped from the result).
|
||||
const agentModelsMap = MODEL_PROFILES as Record<string, unknown>;
|
||||
const agentModels = Object.hasOwn(agentModelsMap, agentType!) ? agentModelsMap[agentType!] : undefined;
|
||||
const result: Record<string, unknown> = {
|
||||
model,
|
||||
profile,
|
||||
|
||||
Reference in New Issue
Block a user