fence_inject — AgentTeamsModule¶
Retrofit fence markers into legacy markdown files for incremental update compatibility.
Wraps a legacy file's body with canonical AGENTTEAMS:BEGIN/END fence markers so it becomes eligible for merge-mode --update runs without requiring --overwrite. Used during cross-repository target remediation when legacy files lack fence metadata.
Source:
agentteams/fence_inject.py
Constants¶
DEFAULT_RETROFIT_FENCE_ID¶
Source:
agentteams/fence_inject.py
Default fence section ID used when retrofitting a legacy file.
Type: str
Value: "content"
Design note: Matches the fence ID that emit._normalize_generated_content uses for whole-body wraps, so a later --update --merge cleanly replaces in-place without duplication.
Classes¶
InjectResult¶
Source:
agentteams/fence_inject.py
Outcome of a fence marker injection attempt.
Attributes:
output_path(Path | None) — Path of the written file (sidecar or in-place). On a no-op (already fenced), set to the original file's path — notNone— since nothing new was written.injected(bool) —Trueif markers were added;Falseif no-op (idempotent).backup_path(Path | None) — Set only on successful in-place injection.Nonefor sidecar mode or no-ops.fence_id(str) — The fence ID used (base + numeric suffix if collision detected).
Functions¶
inject_fence_markers(path, *, mode="sidecar", fence_id=DEFAULT_RETROFIT_FENCE_ID, confirm_in_place=False)¶
Source:
agentteams/fence_inject.py
Add canonical AGENTTEAMS:BEGIN/END markers to a legacy markdown file.
Args:
path(Path | str) — Path to the legacy file to processmode(str, keyword-only) —"sidecar"(default) or"in-place". Default:"sidecar"confirm_in_place(bool, keyword-only) — Required formode="in-place"(safety gate). Default:Falsefence_id(str, keyword-only) — Custom fence ID (must match[a-z][a-z0-9_]*). Default:DEFAULT_RETROFIT_FENCE_ID
Returns: InjectResult — Result of the injection operation
Behavior:
- Idempotent: If the file already contains any AGENTTEAMS fence markers, this is a no-op;
injected=Falseandoutput_pathis set to the original file's path (notNone) - Sidecar mode: Writes to
<original-name>.fenced.mdalongside the source; never modifies the original - In-place mode: Modifies the original file; requires
confirm_in_place=Trueand creates a timestamped.agentteams-backups/backup before mutating - Fence ID collision: If the requested
fence_idalready exists in the file, appends a numeric suffix (e.g.,content_1,content_2) - YAML front matter preservation: If the file starts with YAML front matter, it is kept above the BEGIN marker
Raises:
ValueError— Iffence_iddoesn't match[a-z][a-z0-9_]*FileNotFoundError— If the target file doesn't existValueError— Ifmodeis neither"sidecar"nor"in-place"ValueError— Ifmode="in-place"butconfirm_in_place=False
Typical Usage¶
from pathlib import Path
from agentteams.fence_inject import inject_fence_markers
# Legacy file without fence markers
legacy_file = Path("target-team/.github/agents/my-agent.md")
# Option 1: Create a sidecar version first to review
result = inject_fence_markers(legacy_file, mode="sidecar")
if result.injected:
print(f"✓ Fenced version: {result.output_path}")
# Review, then rename/overwrite if satisfied
# Option 2: Retrofit in-place (requires explicit confirmation)
result = inject_fence_markers(
legacy_file,
mode="in-place",
confirm_in_place=True # Must pass this gate
)
if result.injected:
print(f"✓ Backed up to: {result.backup_path}")
print(f"✓ Retrofitted: {result.output_path}")
Integration with --update¶
Once a file is retrofitted with fence markers, it becomes eligible for merge-mode updates:
# Before retrofit: must use --overwrite (requires security clearance)
python build_team.py --update --overwrite --yes
# After retrofit: can use --update --merge (preserves user-authored content)
python build_team.py --update --merge --yes
This is particularly useful for cross-repository target updates where legacy agents lack fence markers. See the section-fencing guide for how fenced regions are defined and the update-lifecycle guide for how merge-mode updates consume them.
Design Rationale¶
- D-1 (Safety-first): Single fence region wrapping the entire body (heuristic per-section detection out of scope)
- D-2 (Naming convention): Default fence ID is
content(matchesemit._normalize_generated_contentfor clean in-place replacement) - D-3 (Sidecar default): Sidecar mode is default; in-place requires explicit confirmation +
@securityreview - D-4 (Not auto-invoked): Separate CLI surface; not automatic during
--update