Skip to content

Commit 037e37e

Browse files
Trecekclaude
andauthored
Implementation Plan: T5-P4-A1-WP2 Eliminate Duplicated Env-Assembly Block (#4138)
## Summary Expand `_assemble_shared_env_extras` in `BackendCmdBuilderBase` to produce the four env keys that are currently duplicated across all four call sites (claude skill, claude food_truck, codex skill, codex food_truck): `AUTOSKILLIT_HEADLESS`, `AUTOSKILLIT_SESSION_TYPE`, `AUTOSKILLIT_APPLICABLE_GUARDS`, `AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES`. Collapse `claude.py`'s `build_skill_session_cmd`/`_build_skill_session_cmd_impl` indirection into a single method using `_apply_config`. Replace `codex.py`'s inline config unpack with `_apply_config`. Update existing tests that reference `_build_skill_session_cmd_impl` and the 8-key count assertion. Add AST-based arch tests verifying the consolidation. Closes #4015 ## Implementation Plan Plan file: `/home/talon/projects/autoskillit-runs/impl-20260628-015959-473386/.autoskillit/temp/make-plan/t5_p4_a1_wp2_eliminate_env_assembly_duplication_plan_2026-06-28_020600.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) via AutoSkillit <!-- autoskillit:pipeline-signature steps=prepare_pr,run_arch_lenses,compose_pr,annotate_pr_diff,review_pr --> ## Token Usage Summary | Step | Model | count | uncached | output | cache_read | peak_ctx | turns | cache_write | time | |------|-------|-------|----------|--------|------------|----------|-------|-------------|------| | plan* | opus[1m] | 1 | 65 | 34.1k | 1.6M | 132.8k | 53 | 124.2k | 15m 21s | | verify* | sonnet | 1 | 837 | 16.7k | 466.1k | 70.9k | 27 | 52.4k | 6m 36s | | implement* | MiniMax-M3 | 1 | 108.3k | 20.6k | 3.8M | 0 | 118 | 0 | 15m 19s | | fix* | sonnet | 1 | 198 | 12.1k | 1.5M | 82.8k | 64 | 64.2k | 7m 8s | | audit_impl* | sonnet | 1 | 44 | 17.0k | 174.1k | 52.6k | 14 | 53.6k | 8m 40s | | prepare_pr* | MiniMax-M3 | 1 | 48.7k | 2.0k | 117.5k | 0 | 11 | 0 | 45s | | compose_pr* | MiniMax-M3 | 1 | 36.0k | 1.2k | 140.3k | 0 | 12 | 0 | 34s | | **Total** | | | 194.2k | 103.7k | 7.8M | 132.8k | | 294.4k | 54m 25s | \* *Step used a non-Anthropic provider; caching behavior may differ.* ## Token Efficiency | Step | LoC Changed | cache_read/LoC | cache_write/LoC | output/LoC | |------|-------------|----------------|-----------------|------------| | plan | 0 | — | — | — | | verify | 0 | — | — | — | | implement | 464 | 8222.3 | 0.0 | 44.5 | | fix | 17 | 89033.5 | 3777.5 | 713.2 | | audit_impl | 0 | — | — | — | | prepare_pr | 0 | — | — | — | | compose_pr | 0 | — | — | — | | **Total** | **481** | 16260.8 | 612.1 | 215.5 | ## Model Usage Breakdown | Model | steps | uncached | output | cache_read | cache_write | time | |-------|-------|----------|--------|------------|-------------|------| | opus[1m] | 1 | 65 | 34.1k | 1.6M | 124.2k | 15m 21s | | sonnet | 3 | 1.1k | 45.8k | 2.2M | 170.2k | 22m 25s | | MiniMax-M3 | 3 | 193.1k | 23.8k | 4.1M | 0 | 16m 38s | --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fc4aca9 commit 037e37e

10 files changed

Lines changed: 275 additions & 204 deletions

File tree

src/autoskillit/execution/backends/_backend_cmd_builder_base.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from typing import TYPE_CHECKING, Any, NamedTuple
2121

2222
from autoskillit.core import (
23+
AUTOSKILLIT_APPLICABLE_GUARDS,
24+
AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES,
2325
CAMPAIGN_ID_ENV_VAR,
2426
KITCHEN_SESSION_ID_ENV_VAR,
2527
SkillSessionConfig,
@@ -88,22 +90,34 @@ def _flag_vocabulary(self) -> FlagVocabulary:
8890
@staticmethod
8991
def _assemble_shared_env_extras(
9092
*,
93+
session_type: str = "",
94+
applicable_guards: frozenset[str] = frozenset(),
95+
write_guard_tool_names: frozenset[str] = frozenset(),
9196
write_prefix: str = "",
9297
write_prefixes: tuple[str, ...] = (),
9398
cwd: str = "",
9499
scenario_step_name: str = "",
95100
) -> dict[str, str]:
96-
"""Assemble the eight shared env keys consumed by both backends.
101+
"""Assemble the shared env keys consumed by both backends.
97102
98-
Always-on keys (two): ``MAX_MCP_OUTPUT_TOKENS``, ``MCP_CONNECTION_NONBLOCKING``.
103+
Always-on keys (three): ``MAX_MCP_OUTPUT_TOKENS``, ``MCP_CONNECTION_NONBLOCKING``,
104+
``AUTOSKILLIT_HEADLESS``.
99105
100-
Conditional keys (six): ``SCENARIO_STEP_NAME``,
101-
``CAMPAIGN_ID_ENV_VAR``, ``KITCHEN_SESSION_ID_ENV_VAR``,
106+
Conditional keys (nine): ``AUTOSKILLIT_SESSION_TYPE``,
107+
``AUTOSKILLIT_APPLICABLE_GUARDS``, ``AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES``,
108+
``SCENARIO_STEP_NAME``, ``CAMPAIGN_ID_ENV_VAR``, ``KITCHEN_SESSION_ID_ENV_VAR``,
102109
``AUTOSKILLIT_ALLOWED_WRITE_PREFIX``, ``AUTOSKILLIT_ALLOWED_WRITE_PREFIXES``,
103110
``AUTOSKILLIT_CWD``. Each is included only when its input is non-empty
104111
(campaign/kitchen IDs are also read from the ambient ``os.environ``).
105112
"""
106113
extras: dict[str, str] = dict(SHARED_BASELINE_ENV)
114+
extras["AUTOSKILLIT_HEADLESS"] = "1"
115+
if session_type:
116+
extras["AUTOSKILLIT_SESSION_TYPE"] = session_type
117+
if applicable_guards:
118+
extras[AUTOSKILLIT_APPLICABLE_GUARDS] = ",".join(sorted(applicable_guards))
119+
if write_guard_tool_names:
120+
extras[AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES] = ",".join(sorted(write_guard_tool_names))
107121
if scenario_step_name:
108122
extras["SCENARIO_STEP_NAME"] = scenario_step_name
109123
campaign_id = os.environ.get(CAMPAIGN_ID_ENV_VAR)

src/autoskillit/execution/backends/_claude_prompt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _extract_write_artifacts(tool_uses: list[dict[str, Any]]) -> list[str]:
5151
}
5252
)
5353

54-
# Variables that _build_skill_session_cmd_impl controls exclusively. They must not
54+
# Variables that build_skill_session_cmd controls exclusively. They must not
5555
# leak from the host process environment — the caller opts in via explicit
5656
# parameters (exit_after_stop_delay_ms, scenario_step_name, allowed_write_prefix, etc.).
5757
# Note: CLAUDE_CODE_EXIT_AFTER_STOP_DELAY, SCENARIO_STEP_NAME, and

src/autoskillit/execution/backends/claude.py

Lines changed: 44 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
AGENT_BACKEND_CLAUDE_CODE,
1414
AGENT_BACKEND_DYNACONF_ENV_VAR,
1515
AGENT_BACKEND_ENV_VAR,
16-
AUTOSKILLIT_APPLICABLE_GUARDS,
17-
AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES,
1816
CAMPAIGN_ID_ENV_VAR,
1917
CLAUDE_CODE_CAPABILITIES,
2018
CONTEXT_EXHAUSTION_MARKER,
19+
NON_VARIADIC_CLAUDE_FLAGS,
2120
ORCHESTRATOR_SESSION_REQUIRED_ENV,
2221
SESSION_TYPE_ORCHESTRATOR,
2322
SESSION_TYPE_SKILL,
2423
SKILL_SESSION_REQUIRED_ENV,
24+
VARIADIC_CLAUDE_FLAGS,
2525
AgentSessionResult,
2626
BackendCapabilities,
2727
BackendConventions,
@@ -285,17 +285,8 @@ def _env_policy(self) -> ClaudeEnvPolicy:
285285

286286
def _flag_vocabulary(self) -> FlagVocabulary:
287287
return FlagVocabulary(
288-
variadic_flags=frozenset(
289-
{ClaudeFlags.ADD_DIR, ClaudeFlags.PLUGIN_DIR, ClaudeFlags.TOOLS}
290-
),
291-
non_variadic_flags=frozenset(
292-
{
293-
ClaudeFlags.PRINT,
294-
ClaudeFlags.MODEL,
295-
ClaudeFlags.RESUME,
296-
ClaudeFlags.DANGEROUSLY_SKIP_PERMISSIONS,
297-
}
298-
),
288+
variadic_flags=VARIADIC_CLAUDE_FLAGS,
289+
non_variadic_flags=NON_VARIADIC_CLAUDE_FLAGS,
299290
model_flag=ClaudeFlags.MODEL,
300291
add_dir_flag=ClaudeFlags.ADD_DIR,
301292
resume_flag=ClaudeFlags.RESUME,
@@ -534,71 +525,25 @@ def build_skill_session_cmd(
534525
resume_message: str | None = None,
535526
) -> CmdSpec:
536527
if config is not None:
537-
return self._build_skill_session_cmd_impl(
538-
skill_command,
539-
cwd=cwd,
540-
completion_marker=config.completion_marker,
541-
model=config.model,
542-
plugin_source=config.plugin_source,
543-
output_format=config.output_format,
544-
add_dirs=config.add_dirs,
545-
exit_after_stop_delay_ms=config.exit_after_stop_delay_ms,
546-
stream_idle_timeout_ms=config.stream_idle_timeout_ms,
547-
scenario_step_name=config.scenario_step_name,
548-
temp_dir_relpath=config.temp_dir_relpath,
549-
allowed_write_prefix=config.allowed_write_prefix,
550-
allowed_write_prefixes=config.allowed_write_prefixes,
551-
provider_extras=config.provider_extras,
552-
profile_name=config.profile_name,
553-
resume_session_id=config.resume_session_id,
554-
resume_checkpoint=config.resume_checkpoint,
555-
resume_message=config.resume_message,
556-
sandbox_mode=config.sandbox_mode,
557-
)
558-
return self._build_skill_session_cmd_impl(
559-
skill_command,
560-
cwd=cwd,
561-
completion_marker=completion_marker,
562-
model=model,
563-
plugin_source=plugin_source,
564-
output_format=output_format,
565-
add_dirs=add_dirs,
566-
exit_after_stop_delay_ms=exit_after_stop_delay_ms,
567-
stream_idle_timeout_ms=stream_idle_timeout_ms,
568-
scenario_step_name=scenario_step_name,
569-
temp_dir_relpath=temp_dir_relpath,
570-
allowed_write_prefix=allowed_write_prefix,
571-
allowed_write_prefixes=allowed_write_prefixes,
572-
provider_extras=provider_extras,
573-
profile_name=profile_name,
574-
resume_session_id=resume_session_id,
575-
resume_checkpoint=resume_checkpoint,
576-
resume_message=resume_message,
577-
)
528+
cfg = self._apply_config(config)
529+
completion_marker = cfg["completion_marker"]
530+
model = cfg["model"]
531+
plugin_source = cfg["plugin_source"]
532+
output_format = cfg["output_format"]
533+
add_dirs = cfg["add_dirs"]
534+
exit_after_stop_delay_ms = cfg["exit_after_stop_delay_ms"]
535+
stream_idle_timeout_ms = cfg["stream_idle_timeout_ms"]
536+
scenario_step_name = cfg["scenario_step_name"]
537+
temp_dir_relpath = cfg["temp_dir_relpath"]
538+
allowed_write_prefix = cfg["allowed_write_prefix"]
539+
allowed_write_prefixes = cfg["allowed_write_prefixes"]
540+
provider_extras = cfg["provider_extras"]
541+
profile_name = cfg["profile_name"]
542+
resume_session_id = cfg["resume_session_id"]
543+
resume_checkpoint = cfg["resume_checkpoint"]
544+
resume_message = cfg["resume_message"]
545+
sandbox_mode = cfg["sandbox_mode"] # noqa: F841
578546

579-
def _build_skill_session_cmd_impl(
580-
self,
581-
skill_command: str,
582-
*,
583-
cwd: str,
584-
completion_marker: str,
585-
model: str | None,
586-
plugin_source: PluginSource | None,
587-
output_format: OutputFormat,
588-
add_dirs: Sequence[ValidatedAddDir] = (),
589-
exit_after_stop_delay_ms: int = 0,
590-
stream_idle_timeout_ms: int = 0,
591-
scenario_step_name: str = "",
592-
temp_dir_relpath: str | None = None,
593-
allowed_write_prefix: str = "",
594-
allowed_write_prefixes: tuple[str, ...] = (),
595-
provider_extras: Mapping[str, str] | None = None,
596-
profile_name: str = "",
597-
resume_session_id: str = "",
598-
resume_checkpoint: SessionCheckpoint | None = None,
599-
resume_message: str | None = None,
600-
sandbox_mode: str = "workspace-write",
601-
) -> CmdSpec:
602547
_has_prefix = (
603548
bool(profile_name)
604549
and skill_command.strip().startswith("/")
@@ -632,28 +577,21 @@ def _build_skill_session_cmd_impl(
632577
profile_name=profile_name,
633578
),
634579
)
635-
extras: dict[str, str] = {
636-
"AUTOSKILLIT_HEADLESS": "1",
637-
"AUTOSKILLIT_SESSION_TYPE": SESSION_TYPE_SKILL,
638-
AGENT_BACKEND_ENV_VAR: AGENT_BACKEND_CLAUDE_CODE,
639-
AGENT_BACKEND_DYNACONF_ENV_VAR: AGENT_BACKEND_CLAUDE_CODE,
640-
AUTOSKILLIT_APPLICABLE_GUARDS: ",".join(sorted(self.capabilities.applicable_guards)),
641-
AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES: ",".join(
642-
sorted(self.capabilities.write_guard_tool_names)
643-
),
644-
}
580+
extras = self._assemble_shared_env_extras(
581+
session_type=SESSION_TYPE_SKILL,
582+
applicable_guards=self.capabilities.applicable_guards,
583+
write_guard_tool_names=self.capabilities.write_guard_tool_names,
584+
write_prefix=allowed_write_prefix,
585+
write_prefixes=allowed_write_prefixes,
586+
cwd=cwd,
587+
scenario_step_name=scenario_step_name,
588+
)
589+
extras[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
590+
extras[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
645591
if exit_after_stop_delay_ms > 0:
646592
extras["CLAUDE_CODE_EXIT_AFTER_STOP_DELAY"] = str(exit_after_stop_delay_ms)
647593
if stream_idle_timeout_ms > 0:
648594
extras["CLAUDE_STREAM_IDLE_TIMEOUT_MS"] = str(stream_idle_timeout_ms)
649-
extras.update(
650-
self._assemble_shared_env_extras(
651-
write_prefix=allowed_write_prefix,
652-
write_prefixes=allowed_write_prefixes,
653-
cwd=cwd,
654-
scenario_step_name=scenario_step_name,
655-
)
656-
)
657595
extras["AUTOSKILLIT_SKILL_NAME"] = extract_skill_name(skill_command) or ""
658596
if provider_extras:
659597
for k, v in provider_extras.items():
@@ -729,28 +667,21 @@ def build_food_truck_cmd(
729667
),
730668
)
731669

732-
extras: dict[str, str] = {
733-
"AUTOSKILLIT_HEADLESS": "1",
734-
"AUTOSKILLIT_SESSION_TYPE": SESSION_TYPE_ORCHESTRATOR,
735-
AGENT_BACKEND_ENV_VAR: AGENT_BACKEND_CLAUDE_CODE,
736-
AGENT_BACKEND_DYNACONF_ENV_VAR: AGENT_BACKEND_CLAUDE_CODE,
737-
AUTOSKILLIT_APPLICABLE_GUARDS: ",".join(sorted(self.capabilities.applicable_guards)),
738-
AUTOSKILLIT_WRITE_GUARD_TOOL_NAMES: ",".join(
739-
sorted(self.capabilities.write_guard_tool_names)
740-
),
741-
}
670+
extras = self._assemble_shared_env_extras(
671+
session_type=SESSION_TYPE_ORCHESTRATOR,
672+
applicable_guards=self.capabilities.applicable_guards,
673+
write_guard_tool_names=self.capabilities.write_guard_tool_names,
674+
write_prefix=allowed_write_prefix,
675+
write_prefixes=allowed_write_prefixes,
676+
cwd=cwd,
677+
scenario_step_name=scenario_step_name,
678+
)
679+
extras[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
680+
extras[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
742681
if exit_after_stop_delay_ms > 0:
743682
extras["CLAUDE_CODE_EXIT_AFTER_STOP_DELAY"] = str(exit_after_stop_delay_ms)
744683
if stream_idle_timeout_ms > 0:
745684
extras["CLAUDE_STREAM_IDLE_TIMEOUT_MS"] = str(stream_idle_timeout_ms)
746-
extras.update(
747-
self._assemble_shared_env_extras(
748-
write_prefix=allowed_write_prefix,
749-
write_prefixes=allowed_write_prefixes,
750-
cwd=cwd,
751-
scenario_step_name=scenario_step_name,
752-
)
753-
)
754685
extras.pop(CAMPAIGN_ID_ENV_VAR, None) # food truck does not propagate campaign ID
755686
if env_extras:
756687
for k, v in env_extras.items():

src/autoskillit/execution/backends/codex.py

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -693,23 +693,24 @@ def build_skill_session_cmd(
693693
sandbox_mode: str = "workspace-write",
694694
) -> CmdSpec:
695695
if config is not None:
696-
completion_marker = config.completion_marker
697-
model = config.model
698-
plugin_source = config.plugin_source # noqa: F841 # no-op: Codex has no --plugin-dir equivalent
699-
output_format = config.output_format # noqa: F841 # no-op: --json is unconditional for Codex
700-
add_dirs = config.add_dirs
701-
exit_after_stop_delay_ms = config.exit_after_stop_delay_ms # noqa: F841 # no-op: Claude-only
702-
stream_idle_timeout_ms = config.stream_idle_timeout_ms
703-
scenario_step_name = config.scenario_step_name
704-
temp_dir_relpath = config.temp_dir_relpath
705-
allowed_write_prefix = config.allowed_write_prefix
706-
allowed_write_prefixes = config.allowed_write_prefixes
707-
provider_extras = config.provider_extras
708-
profile_name = config.profile_name
709-
resume_session_id = config.resume_session_id
710-
resume_checkpoint = config.resume_checkpoint
711-
resume_message = config.resume_message
712-
sandbox_mode = config.sandbox_mode
696+
cfg = self._apply_config(config)
697+
completion_marker = cfg["completion_marker"]
698+
model = cfg["model"]
699+
plugin_source = cfg["plugin_source"] # noqa: F841 # no-op: Codex has no --plugin-dir equivalent
700+
output_format = cfg["output_format"] # noqa: F841 # no-op: --json is unconditional for Codex
701+
add_dirs = cfg["add_dirs"]
702+
exit_after_stop_delay_ms = cfg["exit_after_stop_delay_ms"] # noqa: F841 # no-op: Claude-only
703+
stream_idle_timeout_ms = cfg["stream_idle_timeout_ms"]
704+
scenario_step_name = cfg["scenario_step_name"]
705+
temp_dir_relpath = cfg["temp_dir_relpath"]
706+
allowed_write_prefix = cfg["allowed_write_prefix"]
707+
allowed_write_prefixes = cfg["allowed_write_prefixes"]
708+
provider_extras = cfg["provider_extras"]
709+
profile_name = cfg["profile_name"]
710+
resume_session_id = cfg["resume_session_id"]
711+
resume_checkpoint = cfg["resume_checkpoint"]
712+
resume_message = cfg["resume_message"]
713+
sandbox_mode = cfg["sandbox_mode"]
713714
_has_prefix = (
714715
bool(profile_name)
715716
and skill_command.strip().startswith("/")
@@ -744,20 +745,20 @@ def build_skill_session_cmd(
744745
),
745746
)
746747

747-
extras = _codex_exec_extras(
748+
extras = self._assemble_shared_env_extras(
748749
session_type=SESSION_TYPE_SKILL,
749-
include_agent_backend_flat=True,
750750
applicable_guards=self.capabilities.applicable_guards,
751751
write_guard_tool_names=self.capabilities.write_guard_tool_names,
752+
write_prefix=allowed_write_prefix,
753+
write_prefixes=allowed_write_prefixes,
754+
cwd=cwd,
755+
scenario_step_name=scenario_step_name,
752756
)
753-
extras.update(
754-
self._assemble_shared_env_extras(
755-
write_prefix=allowed_write_prefix,
756-
write_prefixes=allowed_write_prefixes,
757-
cwd=cwd,
758-
scenario_step_name=scenario_step_name,
759-
)
760-
)
757+
extras["AUTOSKILLIT_HEADLESS_AUTO_GATE"] = "1"
758+
extras[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CODEX
759+
extras[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CODEX
760+
extras[MCP_CLIENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CODEX
761+
extras[FOOD_TRUCK_TOOL_TAGS_ENV_VAR] = ""
761762
extras["AUTOSKILLIT_SKILL_NAME"] = extract_skill_name(skill_command) or ""
762763
if provider_extras:
763764
for k, v in provider_extras.items():
@@ -843,20 +844,20 @@ def build_food_truck_cmd(
843844
),
844845
)
845846

846-
extras = _codex_exec_extras(
847+
extras = self._assemble_shared_env_extras(
847848
session_type=SESSION_TYPE_ORCHESTRATOR,
848-
include_agent_backend_flat=True,
849849
applicable_guards=self.capabilities.applicable_guards,
850850
write_guard_tool_names=self.capabilities.write_guard_tool_names,
851+
write_prefix=allowed_write_prefix,
852+
write_prefixes=allowed_write_prefixes,
853+
cwd=cwd,
854+
scenario_step_name=scenario_step_name,
851855
)
852-
extras.update(
853-
self._assemble_shared_env_extras(
854-
write_prefix=allowed_write_prefix,
855-
write_prefixes=allowed_write_prefixes,
856-
cwd=cwd,
857-
scenario_step_name=scenario_step_name,
858-
)
859-
)
856+
extras["AUTOSKILLIT_HEADLESS_AUTO_GATE"] = "1"
857+
extras[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CODEX
858+
extras[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CODEX
859+
extras[MCP_CLIENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CODEX
860+
extras[FOOD_TRUCK_TOOL_TAGS_ENV_VAR] = ""
860861
if completion_marker:
861862
extras["AUTOSKILLIT_COMPLETION_MARKER"] = completion_marker
862863
if env_extras:

tests/arch/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ AST enforcement, sub-package layer contracts, and architectural invariant tests.
126126
| `test_doc_fence_filter.py` | Unit and functional tests for `_strip_doc_fenced_blocks` section-aware code fence filter |
127127
| `test_capability_scanner_fence_immunity.py` | AST regression guard: capability scanners must import and call `_strip_doc_fenced_blocks` |
128128
| `test_backend_stdlib_boundaries.py` | Stdlib-boundary equality tests: write_guard fallback frozenset ↔ CLAUDE_CODE_CAPABILITIES, session type hook string literals ↔ SessionType enum |
129+
| `test_backend_builder_no_independent_copies.py` | AST and isinstance guards: shared env-key literals absent from per-backend files; BACKEND_REGISTRY entries inherit from BackendCmdBuilderBase; FlagVocabulary parity for Claude and Codex |
129130

130131
## Architecture Notes
131132

0 commit comments

Comments
 (0)