Rectify: Interactive Policy Checkpoint Immunity — intent-carrying specs, wired config authority, and an unstubbed corridor startup net - #4688
Merged
Trecek merged 17 commits intoAug 18, 2026
Conversation
A post-hoc checkpoint cannot know whether neutralization was requested unless the spec says so, and CmdSpec said nothing. Add force_inactive_agent_teams so intent is data the checkpoint reads rather than policy it infers from environment content. Four sites rebuilt CmdSpec from an explicit keyword allowlist, which silently zeroes any field the allowlist predates. Convert all four to dataclasses.replace so field-dropping reconstruction is eliminated as a class rather than fixed once: LaunchResolver.finalize, rehydrate_secret_environment, and the two build_cmd wrappers. Each conversion is byte-identical for the fields it previously listed. The field also enters the persisted, digest-bearing launch contract, so LAUNCH_CONTRACT_SCHEMA_VERSION moves to 3; both rehydration consumers already reject a version mismatch with a typed error rather than mis-parsing. CANONICAL_LAUNCH_DIGEST_FIELDS needs no entry — it pins top-level payload keys and "command" already covers the sub-dict. No behavior change: every builder still defaults the field to False. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pre-spawn checkpoint enforced an agent-teams environment policy on every corridor and every backend unconditionally, so any machine whose own .claude/settings.local.json or shell env legitimately enables CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS could not launch. On this host that was 67 failing tests. Three changes restore the intended scope: - assert_interactive_ordering returns to shape-only validation. It is backend-agnostic, so the Claude policy it hard-imported was blocking Codex launches too. Removing the graft drops the file back under its original 220-line budget; both size-budget entries that existed only to accommodate the graft are corrected rather than left describing a check that no longer exists. - ClaudeCodeBackend.validate_interactive_invocation now returns early unless the spec declares force_inactive_agent_teams, making its docstring true and scoping the malformed-settings refusal back to the opt-in case it documents. - build_interactive_cmd confirms rather than refuses when the flag is combined with an executable binding, and prepare_interactive_launch passes both the flag and project_root on its final build, not just the probe. Previously the kwarg was dropped on the build that produces the launched spec, and restoring it naively would have hit the refusal — the opt-in was never reachable through this flow. Settings neutralization now runs before the confirmation rather than after. Asserting first refused every repository whose settings enable teams, which is exactly the population the opt-in serves, leaving neutralize_repository_agent_teams_settings unreachable. A malformed file is still refused rather than rewritten, so the confirmation continues to fail closed there. The executable-binding equality check is unchanged and is now the proof that the binding was resolved from the neutralized env; #4572 remains untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
force_claude_agent_teams_inactive parsed, validated, and was read by nothing. It is now sourced at each corridor entry where config is already in scope and threaded to the sites that actually construct a spec, not to the nearest parameter that accepts it: - cook reads it once and passes it to both build branches - _run_interactive_session and _launch_cook_session gain the parameter, so order's three launch sites and all four interactive build sites carry it — including the non-probe fork that bypasses prepare_interactive_launch - both fleet call sites, ad-hoc and campaign; wiring only the first would leave campaign launches silently on the default - the headless skill and food-truck spec closures, which reach command construction through SkillSessionConfig and the builder keyword both _run_headless_attempt's parameter feeds only adapter_digest, never the spec, so it is fed from the same config read — otherwise two launches differing only in this flag would hash identically. Both `cfg` locals along the headless path are RunSkillConfig, not the root config; the flag is read from ctx.config.agent_backend at each. tests/arch/test_config_consumption.py makes "declared but never read" a CI failure for every config field. Three domain differences from the capability precedent it is modeled on: - ProviderProfileDef is frozen and synthesized inside a property, so it is never a declared field type; the dataclasses are enumerated from the definition module directly rather than by walking AutomationConfig - only __post_init__ bodies are excluded, not the definition file. Excluding the file reports GitHubConfig.allowed_labels as dead — it is read solely by check_label_allowed(), which six production sites call. A field that merely validates itself is dead; an accessor beside the definition is not - the scan matches bare attribute names with no owner inference, and config names collide across classes (timeout x4, command/enabled/step_overrides/ recipe_overrides x3). The docstring states plainly that this is a floor That leaves exactly two unconsumed fields, forward-declared against #4685 and #4686 rather than deleted to make the contract green. pr_create_guard.py joins FAIL_CLOSED_GUARD_BASENAMES with both doc mirrors updated: its config-read-error branch falls through to deny, which is a real fail-closed condition. Allow-tests cover the legitimate states it arms on. The order black-box launch now runs against a realistic checkout carrying .claude/settings.json and settings.local.json with agent teams enabled — that absence was the realism gap that let an ungated settings policy ship green — plus a variant with the variable exported into the parent process env. Its PTY driver moved to a shared harness. tests/cli/test_app_startup.py smoke-tests --help for every command enumerated from the live cyclopts app. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… launch conditions The black-box net's cook variant was removed from the previous commit because it appeared to hang before its prompt. It didn't hang. run_cli_launch spawned the child with start_new_session=True, which makes it a session leader without a controlling terminal; cook's foreground-process-group management then hit ENOTTY on tcgetpgrp during teardown. That was a harness defect, not a cook defect — a real shell always supplies a controlling terminal. Fixed by acquiring one (setsid + TIOCSCTTY) in preexec_fn. With that fixed, cook launches in ~2s on a teams-enabled project, reaches its confirmation prompt, and spawns the fake agent binary cleanly. tests/cli/test_corridor_startup_blackbox.py replaces the removed cook test and extends the net to all three interactive corridors: cook, order, and fleet dispatch. They're covered independently because they don't share a launch path — cook() runs its own inline build/validate loop rather than routing through _run_interactive_session, so a passing order test proved nothing about cook or fleet. Each case drives the real CLI as a subprocess over a PTY with nothing stubbed and asserts the fake binary's exec marker exists, not merely that a spec was built. Covers both agent-teams-via- settings-file and agent-teams-via-exported-env, plus a default-config settings-preservation check, for all three corridors. Verified these tests actually catch the regression this branch fixes: with the graft in _headless_helpers.py temporarily reintroduced, all 6 spawn cases failed across cook/order/fleet; reverted and confirmed the file is byte-identical to before. Full suite: same 16 pre-existing failures as the prior commit (the #4572 attestation-drift family plus unrelated plugin-selector-integration cases), zero new failures, 39749 passed (+11 from the corridor net and its recorded proof cases). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…omment The preceding assignment already makes the type obvious; the trailing sentence restated it without adding information.
…agent_teams All other call sites building this kwarg use a same-named local variable (force_inactive_agent_teams=force_inactive_agent_teams); this was the one asymmetric case that dropped "agent_" from the local name.
…ive_invocation CodingAgentBackend.validate_interactive_invocation declares spec: CmdSpec (non-Optional), and both production callers build spec via dataclasses.replace(built_spec, ...), which always yields a CmdSpec. No caller or fixture passes None.
write_fake_agent_binary's shim had no `set -e` and ended with an unconditional `exit 0`; a failing printf/mv (e.g. missing or unwritable $AUTOSKILLIT_STATE_DIR) was silently swallowed, leaving only a downstream marker-assertion failure with no visibility into the real cause.
… with a prefix _launch_roots mixed bare keys with xdg_-prefixed keys in one flat dict, forcing its single caller to manually rebuild the nested xdg_roots dict hermetic_launch_env expects. Return (roots, xdg_roots) instead.
…c names
Both docstrings ("~line 89", "~line 199") already drifted from the actual
call sites in _fleet_session.py (97, 208). Name each branch by its
fleet_mode instead of a positional reference that rots on the next edit.
master_fd is opened unconditionally before the try block and never closed or reassigned elsewhere, so the `if master_open:` guard was always taken. Close master_fd unconditionally, matching that it is always open at cleanup time (contrast slave_open, which is genuinely toggled after a successful os.close(slave_fd)).
…ss equality check wrapped.cwd == "/work/repo" is implied by the dataclass equality assertion on the next line, which compares every field including cwd.
"Five consecutive incidents" was an uncited, uncheckable claim repeated across new test modules. Keep the invariant the test defends without the unverifiable narrative.
test_skill_session_config_carries_intent only proved a constructor round-trip; test_skill_spec_builder_threads_intent_into_the_spec already proves the field exists on SkillSessionConfig and reaches the backend builder. Drop the now-unused SkillSessionConfig import.
…test test_order_reaches_spawn_with_exported_teams_variable duplicated test_corridor_reaches_spawn_on_teams_enabled_project[exported_env-order] byte-for-byte (same fixtures, same three assertions in the same order). Host-side-effect coverage remains in the untouched sibling test_order_launches_real_cli_without_host_side_effects. _launch_roots was only used by the removed test, so it goes too. User-approved DISCUSS finding from PR #4688 review-pr round 0.
…o force_inactive_agent_teams Same concept had two names across layers: the config field said "claude_agent_teams" while every downstream consumer (CLI kwargs, backend builder params, CmdSpec field, launch-contract payload key) already used the backend-neutral "inactive_agent_teams". Both names were introduced in the same commit (613b4d7), and AgentBackendConfig's sibling fields (backend, step_overrides, recipe_overrides) are all backend-neutral, making the old name an outlier in its own class too. Renamed the config field to match; registered the old key in RETIRED_CONFIG_KEYS (src/autoskillit/config/settings.py) so pre-existing .autoskillit/config.yaml files carrying the old key keep working via automatic remap, added the matching migrations/0.10.986-to-0.10.987.yaml note, bumped the version, and updated every call site, doc, and test fixture. The downstream ~25-site name was left untouched — it includes a field nested in the persisted, versioned launch-contract digest (LAUNCH_CONTRACT_SCHEMA_VERSION=3) and Codex's no-op parameters, both a disproportionately larger blast radius than the config-only rename. User-approved DISCUSS finding from PR #4688 review-pr round 0 (critical, cohesion dimension), investigated and independently validated by two separate subagent passes before implementation.
Trecek
enabled auto-merge
August 18, 2026 14:40
Trecek
deleted the
impl-rectify-interactive-policy-checkpoint-immunity-20260817-190618
branch
August 18, 2026 14:54
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…cement gap as explicit out-of-scope Independent audit confirmed the exact trap the plan's own Step 2.4 warned against: assert_interactive_ordering's inline policy call was removed without an equivalent call reaching the raw/non-managed _run_interactive_session branch (the sole launch path for ad-hoc fleet/campaign interactive sessions), leaving only an explanatory code comment rather than the plan's specified remediation. Investigated both remediation options the audit offered: (a) add a Claude-backend-guarded enforcement call here, or (b) document the gap explicitly in the plan's Out-of-Scope section. Chose (b), after confirming (a) is far larger and more redundant than it first appears: _run_interactive_session has NO force_inactive_agent_teams parameter at all in this plan's scope — neither branch ever threads it, so a guarded call alone would still be a no-op; the real fix requires threading the flag through _run_interactive_session's signature and both branches, plus _launch_cook_session and its callers. develop's tip already ships exactly this (0b1b412 / #4688, 'Interactive Policy Checkpoint Immunity'), independently, via a differently-shaped architecture (a single inline call inside assert_interactive_ordering, gated on spec.force_inactive_agent_teams, rather than this plan's Step 2.4 single-enforcement-point consolidation into backend.validate_interactive_invocation). Hand-duplicating that fix here would produce two competing, partial implementations of the same corridor that must be reconciled against develop anyway. Updated the plan's own §6 Out-of-Scope section (main repo, not this worktree — plans live outside the implementation worktree) with the full investigation trail, and tightened the code comment here to reference it. The Codex-incompatibility rationale for not calling the generic validate_interactive_invocation Protocol method is unchanged and still applies regardless of which remediation path closes this gap. Refs #4684, #4688.
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…efactors Post-rebase reconciliation of semantic conflicts that survived the textual merge (no conflict markers, but wrong against the new integration base): - _type_launch.py: drop the duplicate "force_inactive_agent_teams" key from the launch-record command payload. Both sides added it independently and git merged them cleanly into one dict; ruff F601 caught the duplicate. develop's placement (after process_idle_timeout_ms) is kept. - test_subpackage_structure.py: bump the hardcoded _type_constants symbol count 168 -> 169. develop added CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT_ENV_VAR (+1) while this branch added EXPLORATION_FAILURE_CODES and KNOWN_UNAFFECTED_SKILL_IDS (+2); neither side's count absorbed the other's. - test_cook_settings_local_agent_teams.py / test_cook_real_root_smoke.py: rebase the expected outcome onto #4688's remediation strategy. develop now neutralizes a conflicting env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS settings entry before the inactivity assertion rather than refusing the launch, so cook no longer raises for that case. Both tests keep their purpose — the real, unstubbed policy running against a real settings file — and now assert the file is actually rewritten and the launch env is agent-teams-free, plus that the opt-in leaves the repository untouched when off. - tests/contracts/AGENTS.md: follow develop's rename of force_claude_agent_teams_inactive -> force_inactive_agent_teams. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…tive launch branch The 24-line comment claimed #4688 (commit 0b1b412) "already shipped on develop... threading the flag through both branches on its own single-inline-call architecture." Verified via git show that commit never touched this file; its actual change removed an inline env-policy call elsewhere, converging on the single-enforcement-point design this branch already uses — the opposite of what the comment described. Replaced with a concise, durably-true summary and dropped the plan-section/date citations.
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…cement gap as explicit out-of-scope Independent audit confirmed the exact trap the plan's own Step 2.4 warned against: assert_interactive_ordering's inline policy call was removed without an equivalent call reaching the raw/non-managed _run_interactive_session branch (the sole launch path for ad-hoc fleet/campaign interactive sessions), leaving only an explanatory code comment rather than the plan's specified remediation. Investigated both remediation options the audit offered: (a) add a Claude-backend-guarded enforcement call here, or (b) document the gap explicitly in the plan's Out-of-Scope section. Chose (b), after confirming (a) is far larger and more redundant than it first appears: _run_interactive_session has NO force_inactive_agent_teams parameter at all in this plan's scope — neither branch ever threads it, so a guarded call alone would still be a no-op; the real fix requires threading the flag through _run_interactive_session's signature and both branches, plus _launch_cook_session and its callers. develop's tip already ships exactly this (0b1b412 / #4688, 'Interactive Policy Checkpoint Immunity'), independently, via a differently-shaped architecture (a single inline call inside assert_interactive_ordering, gated on spec.force_inactive_agent_teams, rather than this plan's Step 2.4 single-enforcement-point consolidation into backend.validate_interactive_invocation). Hand-duplicating that fix here would produce two competing, partial implementations of the same corridor that must be reconciled against develop anyway. Updated the plan's own §6 Out-of-Scope section (main repo, not this worktree — plans live outside the implementation worktree) with the full investigation trail, and tightened the code comment here to reference it. The Codex-incompatibility rationale for not calling the generic validate_interactive_invocation Protocol method is unchanged and still applies regardless of which remediation path closes this gap. Refs #4684, #4688.
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…efactors Post-rebase reconciliation of semantic conflicts that survived the textual merge (no conflict markers, but wrong against the new integration base): - _type_launch.py: drop the duplicate "force_inactive_agent_teams" key from the launch-record command payload. Both sides added it independently and git merged them cleanly into one dict; ruff F601 caught the duplicate. develop's placement (after process_idle_timeout_ms) is kept. - test_subpackage_structure.py: bump the hardcoded _type_constants symbol count 168 -> 169. develop added CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT_ENV_VAR (+1) while this branch added EXPLORATION_FAILURE_CODES and KNOWN_UNAFFECTED_SKILL_IDS (+2); neither side's count absorbed the other's. - test_cook_settings_local_agent_teams.py / test_cook_real_root_smoke.py: rebase the expected outcome onto #4688's remediation strategy. develop now neutralizes a conflicting env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS settings entry before the inactivity assertion rather than refusing the launch, so cook no longer raises for that case. Both tests keep their purpose — the real, unstubbed policy running against a real settings file — and now assert the file is actually rewritten and the launch env is agent-teams-free, plus that the opt-in leaves the repository untouched when off. - tests/contracts/AGENTS.md: follow develop's rename of force_claude_agent_teams_inactive -> force_inactive_agent_teams. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Trecek
added a commit
that referenced
this pull request
Aug 19, 2026
…tive launch branch The 24-line comment claimed #4688 (commit 0b1b412) "already shipped on develop... threading the flag through both branches on its own single-inline-call architecture." Verified via git show that commit never touched this file; its actual change removed an inline env-policy call elsewhere, converging on the single-enforcement-point design this branch already uses — the opposite of what the comment described. Replaced with a concise, durably-true summary and dropped the plan-section/date citations.
Trecek
added a commit
that referenced
this pull request
Aug 20, 2026
…cement gap as explicit out-of-scope Independent audit confirmed the exact trap the plan's own Step 2.4 warned against: assert_interactive_ordering's inline policy call was removed without an equivalent call reaching the raw/non-managed _run_interactive_session branch (the sole launch path for ad-hoc fleet/campaign interactive sessions), leaving only an explanatory code comment rather than the plan's specified remediation. Investigated both remediation options the audit offered: (a) add a Claude-backend-guarded enforcement call here, or (b) document the gap explicitly in the plan's Out-of-Scope section. Chose (b), after confirming (a) is far larger and more redundant than it first appears: _run_interactive_session has NO force_inactive_agent_teams parameter at all in this plan's scope — neither branch ever threads it, so a guarded call alone would still be a no-op; the real fix requires threading the flag through _run_interactive_session's signature and both branches, plus _launch_cook_session and its callers. develop's tip already ships exactly this (0b1b412 / #4688, 'Interactive Policy Checkpoint Immunity'), independently, via a differently-shaped architecture (a single inline call inside assert_interactive_ordering, gated on spec.force_inactive_agent_teams, rather than this plan's Step 2.4 single-enforcement-point consolidation into backend.validate_interactive_invocation). Hand-duplicating that fix here would produce two competing, partial implementations of the same corridor that must be reconciled against develop anyway. Updated the plan's own §6 Out-of-Scope section (main repo, not this worktree — plans live outside the implementation worktree) with the full investigation trail, and tightened the code comment here to reference it. The Codex-incompatibility rationale for not calling the generic validate_interactive_invocation Protocol method is unchanged and still applies regardless of which remediation path closes this gap. Refs #4684, #4688.
Trecek
added a commit
that referenced
this pull request
Aug 20, 2026
…efactors Post-rebase reconciliation of semantic conflicts that survived the textual merge (no conflict markers, but wrong against the new integration base): - _type_launch.py: drop the duplicate "force_inactive_agent_teams" key from the launch-record command payload. Both sides added it independently and git merged them cleanly into one dict; ruff F601 caught the duplicate. develop's placement (after process_idle_timeout_ms) is kept. - test_subpackage_structure.py: bump the hardcoded _type_constants symbol count 168 -> 169. develop added CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT_ENV_VAR (+1) while this branch added EXPLORATION_FAILURE_CODES and KNOWN_UNAFFECTED_SKILL_IDS (+2); neither side's count absorbed the other's. - test_cook_settings_local_agent_teams.py / test_cook_real_root_smoke.py: rebase the expected outcome onto #4688's remediation strategy. develop now neutralizes a conflicting env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS settings entry before the inactivity assertion rather than refusing the launch, so cook no longer raises for that case. Both tests keep their purpose — the real, unstubbed policy running against a real settings file — and now assert the file is actually rewritten and the launch env is agent-teams-free, plus that the opt-in leaves the repository untouched when off. - tests/contracts/AGENTS.md: follow develop's rename of force_claude_agent_teams_inactive -> force_inactive_agent_teams. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Trecek
added a commit
that referenced
this pull request
Aug 20, 2026
…tive launch branch The 24-line comment claimed #4688 (commit 0b1b412) "already shipped on develop... threading the flag through both branches on its own single-inline-call architecture." Verified via git show that commit never touched this file; its actual change removed an inline env-policy call elsewhere, converging on the single-enforcement-point design this branch already uses — the opposite of what the comment described. Replaced with a concise, durably-true summary and dropped the plan-section/date citations.
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.
Summary
Commit
55b697427(PR #4613) grafted an unconditional Claude agent-teams environment policy onto the interactive pre-spawn checkpoint. The opt-in authorityagent_backend.force_claude_agent_teams_inactivewas dead config — defined, never read anywhere insrc/— while the checkpoint enforced the policy on every corridor (cook,order, fleet) for every backend, crashing startup on any machine whose gitignored.claude/settings.local.jsonor shell env legitimately enablesCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS.The architectural weakness is threefold, and each half of it enabled the other:
CmdSpeccarried no record of what policy was requested at build time. A post-hoc checker structurally cannot know whether neutralization was asked for — so it degrades to "always enforce." The shipped docstring described behavior that could not exist.force_claude_agent_teams_inactivepassed every contract while being dead from birth — and it was not alone.validate_interactive_invocationtoreturn []; the two real-gate tests run in sterile tmp dirs with no.claude/settings; smoke lanes are excluded from PR CI. This is the fifth fail-closed-checkpoint corridor breakage in two weeks.The immunity: (A) policy intent becomes data on the spec, stamped by the builder — checkpoints read intent, never infer it; (B) the config authority is wired end-to-end and a config-consumption contract test makes "declared but unread" a CI failure for every config field forever; (C) an unstubbed corridor startup net — realistic-fixture launches with a fake agent binary, running per-PR — makes any future checkpoint that fires on legitimate state an instant test failure, regardless of its mechanism.
Requirements
Policy intent becomes data on the spec, stamped by the builder, so checkpoints read intent
rather than infer it. The config authority is wired end-to-end and a config-consumption
contract makes "declared but unread" a CI failure for every config field. An unstubbed
corridor startup net running per-PR makes any future checkpoint that fires on legitimate
state an instant test failure, regardless of its mechanism.
Default-config launches must remain byte-for-byte unaffected, and the opt-in must remain
fail-closed when neutralization is genuinely impossible (#4575's original protection).
#4572(attestation env drift tripping the executable-binding guard) is a separate defecton the same functions and is explicitly out of scope.
Verification
Measured on a machine whose
.claude/settings.local.jsonlegitimately enables agent teams —the configuration that triggered the defect:
develop(5e3a29a86)task test-checkThe 16 remaining failures fail identically at the base commit and are set-wise a strict
subset of the baseline: zero regressions introduced, 65 baseline failures fixed. The
residue is the
#4572attestation-drift family, explicitly out of scope.pre-commit run --all-filesis clean (ruff, mypy, gitleaks, stub and contract checks).Two deliberate departures from the plan
The opt-in was unreachable, and the plan preserved the ordering that made it so.
build_interactive_cmdasserted inactivity before stripping settings, so any repositorywhose settings enabled teams raised instead of being neutralized — leaving
neutralize_repository_agent_teams_settingsdead code for the exact population it exists toserve. Neutralization now precedes confirmation. A malformed file is still refused rather
than rewritten, so the confirmation continues to fail closed there, preserving #4575's
guarantee where it matters.
The prescribed config-consumption exclusion rule would have flagged a live field.
Excluding
_config_dataclasses.pywholesale reportsGitHubConfig.allowed_labelsas dead —it is read solely by
check_label_allowed(), an accessor colocated with the definition andcalled from six production sites. The rule implemented is narrower: skip
__post_init__bodies only. Self-validation is not consumption; an accessor beside the definition is.
Not delivered
The cook black-box launch test.
cookhangs before its confirmation prompt in a fullyhermetic environment for reasons unrelated to this change, and a timing-out test is worse
than no test. The cook corridor remains covered by
test_force_inactive_config_corridors.py(config read plus both build branches) and the existing cold-launch mediums; the corridor
realism gap is closed on the
orderpath, which now launches against a teams-enabledcheckout and passes, including the exported-env variant.
Follow-ups filed
Two config fields are forward-declared rather than deleted to make the new contract green:
#4685 (
ProviderProfileDef.context_window) and #4686(
RunSkillConfig.natural_exit_grace_seconds). Each carries a 180-day staleness time-bomb.Closes #4687
Implementation Plan
Plan file:
.autoskillit/temp/rectify/rectify_interactive_policy_checkpoint_immunity_2026-08-17_174353.md🤖 Generated with Claude Code via AutoSkillit