Conversation
…calls FinalStatus/AgentEndStatus/TurnEndStatus TOOL_CALLS_EXHAUSTED, the tool_calls_exhausted flag (derived by the collector from the end status; AgentEndEvent.max_turns_exhausted deleted), run_limits.expected_tool_calls and the run.json keys. DialogStopReason gains TOOL_CALL_CAP. The evalboard reads both spellings; REPORT_SCHEMA names the historical ones. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…imits.max_tool_calls replaces max_turns StopReason and end_status_for join the event protocol; communicate() loses max_turns and its should_stop returns a reason. TurnMonitor (from EarlyStopWatcher) is attached on every run and latches the armed early stop or the cumulative tool-call cap; every adapter drops its own cap and finalizes with end_status_for. sdk_options.max_turns is allowed on Claude Code and used by the simulator and the judge. SPI_VERSION 2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…easons; an unpriceable max_usd is an error _check_run_limits, its silent cost skip and cost_data_available are deleted. The monitor latches TOKEN_BUDGET / USD_BUDGET from the stream's usage and raise_if_over_budget raises at the former call sites; a max_usd no turn can price raises BudgetUnenforceableError and finalizes ERROR. RunLimits owns the one bucket rule, every harness contract declares usage_granularity, and validate_resolved_task is the one resolution-check list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ll_triggered escalates a skill that was never offered stage_plugins turns both authored layouts into <run_dir>/plugin_root, records environment_info.skills_offered, and every harness receives the root natively. A plugin path with no skill fails resolution, and a skill_triggered target outside skills_offered raises CheckerMisuseError. The per-harness scanners, utils.process_plugins and CE045 are deleted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e contract; CE070 keeps caps and skill scans out of adapters HARNESS_PARITY's hand-written run-limit table becomes a third CE069-checked block; a RunLimits field without a cell rule fails the render. CE070 flags any cap identifier or SKILL.md literal under agents/. CLAUDE.md and the notes describe the TurnMonitor and plugin staging. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ugin root, portable parity and skill fixtures A relative run dir staged a relative plugin root that the harness, running in the sandbox, could not find. The run-limit parity and skill fixtures carried harness-specific agent fields that Codex rejects; smoke_task_timeout's cap now stays out of the watchdog's way; the live tests use StopReason and sdk_options.max_turns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude reports only a message id's unreported tokens on each TurnEndEvent, so interleaved ids cannot latch a false budget stop. The TurnMonitor prices with agent.model, then the model the agent resolved at start, before any per-message model. A Codex turn total with an unpriceable part stays unpriced. A dialog turn that reaches the tool-call cap records it before any other stop decision. EXTENDING.md shows start(plugin_root=) and the delta contract for budgets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… on every cooperative harness The parity fixtures carried permission_mode and Claude-only fields that Codex rejects, so their documented --type runs failed at resolution. Also defers two harness candidates from the final review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…solution; read plugin manifests as Claude Code does
validate_plugins now fails plan when a skill_triggered skill_name is not among
the offered skills, so a misnamed skill no longer costs a full run before it
finishes ERROR (a ${row...} name is checked on its expanded row; the checker
gate stays for detached grades of recorded runs).
Plugin scanning now matches Claude Code's plugins reference and a CLI 2.1.273
spike: a manifest skills path ADDS to the default skills/ instead of replacing
it, a declared path may name one skill, a root holding SKILL.md is a
single-skill plugin, and a skill is named by its SKILL.md frontmatter name.
The deliberately invalid skill_not_offered fixture is deleted; unit tests cover
the refusal on plan and resolution.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 16, 2026
uipreliga
marked this pull request as ready for review
September 18, 2026 00:31
uipreliga
requested review from
CarlesUIPath,
akshaylive,
bai-uipath and
tmatup
as code owners
September 18, 2026 00:31
|
Claude finished @uipreliga's task in 1m 32s —— View job Code Review for PR #183: Central Enforcement
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Implements plan 2 of 3, central enforcement (
c/2026-09-17-central-enforcement.md, designc/harness-target-architecture-v2.md). Every structural cap and budget is now oneTurnMonitoranswer on theshould_stopchannel, in one unit, on every harness. Every harness receives one staged plugin root. A task that asks for something the run cannot honor is rejected or escalated instead of silently scoring.What changes
FinalStatus.TOOL_CALLS_EXHAUSTED(iconC),tool_calls_exhausted,expected_tool_calls,expected_tool_calls_overage,tool_calls_remaining_at_stop. The collector derives the flag from the end status. The evalboard reads both spellings;docs/REPORT_SCHEMA.mdlists the historical ones.StopReason+TurnMonitor.communicate(should_stop: Callable[[], StopReason | None]), withmax_turnsremoved.run_limits.max_tool_callsreplacesmax_turnsand is cumulative per task across retries and dialog turns. All five adapters dropped their own cap code and finalize withend_status_for(reason).sdk_options.max_turnsis allowed on Claude Code, and the simulator and judge use it.SPI_VERSION = 2._check_run_limitsand its silent "skipping cost check" are deleted. Amax_usdnobody can price (no reported cost, no rate for the configured, start or reported model) finishesERRORviaBudgetUnenforceableError. New requiredHarnessContract.usage_granularity.stage_pluginswrites<run_dir>/plugin_root(name-only manifest +skills/<name>symlinks) and recordsenvironment_info.skills_offered. Each harness receives the root natively. Manifests are read as Claude Code reads them (confirmed against the plugins reference and a CLI 2.1.273 spike): declaredskillsadd toskills/, a path may name one skill, and a skill is named by its frontmattername. A path with no skill, or askill_triggeredtarget the plugins do not offer, failsplanbefore the run is paid for. The per-harness scanners,utils.process_pluginsand CE045 are deleted.docs/agents/HARNESS_PARITY.mdgains a third CE069-checked block rendered fromRunLimitsand the contracts. CE070 keeps cap identifiers andSKILL.mdscans out ofagents/.Breaking changes (greenfield, no aliases)
run_limits.max_turns/expected_turnsare rejected (extra="forbid"); usemax_tool_calls/expected_tool_calls. Every in-repo task and experiment is migrated.task.jsonwithfinal_status: MAX_TURNS_EXHAUSTED, or a recorded config withrun_limits.max_turns/expected_turns, does not load in Python.evaluatere-grades from the source YAML with its fallback warning;run --resumere-executes the row.communicateandstart(plugin_root=)signatures,HarnessContract.usage_granularity,AgentEndEvent.max_turns_exhaustedremoved.docs/EXTENDING.mdis updated. The out-of-tree Delegate agent needs a follow-up PR.agents/,commands/,hooks/,.mcp.jsonand files beside the skills no longer reach any harness. Claude Code names staged skillscoder-eval-plugins:<skill>.Verification
make verify: 6092 passed, coverage 93.28%.make evalboard-verify: 805 passed. The strictmkdocs buildpasses.tasks/run_limits/max_tool_calls_cap.yaml): claude-code, codex, pi and opencode each stop at exactly 4 resolved calls withtool_calls_exhausted: true, no crash and no retry.TOKEN_BUDGET_EXCEEDEDandCOST_BUDGET_EXCEEDEDon all four. Pi/OpenCode USD came from the provider-reported cost of a model the rate card does not carry.tasks/skills/skill_offered.yaml):SUCCESSon all four, withskills_offered == ["probe-skill"]. Under--driver docker, codex returnsSUCCESS.decision_budget_exceededwithtool_calls_remaining_at_stopset. Dialog mode: finishes with a documented stop reason.tests/test_codex_agent_live.py5/5.max_usd: no local costless harness; unit and orchestrator tests only;ANTHROPIC_API_KEYor container login here;Defects the live runs caught (fixed here)
run_dirwas relative (the defaultruns/...), so Claude Code loaded no plugin.smoke_task_timeout's tool-call cap pre-empted its watchdog. The current Claude CLI also refuses a long standalonesleep, so that fixture cannot reachTIMEOUTtoday; CI still counts it as failed.Follow-ups
usage_granularity,StopReason,start(plugin_root=), SPI 2.TurnEmitterand the ports): prompt inc/plan-3-turn-emitter-prompt.md.tasks/agents/codex_skills_test.yamlneedsPLUGIN_PATHset, orcoder-eval planovertasks/reports it..claude/harness-candidates.md: CE070 blind spots, the per-turn token delta invariant across golden streams, live tests not type-checked.🤖 Generated with Claude Code