-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Problem
#800 introduced phase-based file restrictions enforced via readonly mounts and commit-time gateway validation. These restrictions are phase-scoped — the implement phase blocks .egg-state/ directories but allows modification of any code file. This means an implement agent working on task A can accidentally modify files that belong to task B (or files not in the plan at all), creating unexpected cross-contamination when multiple agents work in parallel (Tier 3 dispatch).
Goal
Make the planner's per-task files list an enforced boundary, not just informational context. When the orchestrator spawns an implement agent container, the gateway should restrict that agent's commits to only the files listed in its assigned tasks.
Design Philosophy: Guide, Don't Cage
These restrictions exist to prevent accidental cross-contamination, not to micromanage the agent. The enforcement must be generous enough that an agent with a reasonable plan never gets stuck. An agent spinning because it can't touch a file it legitimately needs is worse than an agent that touches one extra file.
Principles:
- Warn before block: First violation logs a warning; repeated violations on the same file block. Give the agent a chance to self-correct.
- Generous matching: If the plan lists
src/auth/login.py, the agent should also be able to createsrc/auth/login_helpers.pyin the same directory. Directory-level implicit allowance. - Graceful fallback: Empty
files_affected= no per-file restriction. Missing or malformed entries are skipped, not fatal. - Escape hatch: If the agent genuinely needs a file outside its allowlist, it can signal via
egg-contract(e.g.,egg-contract request-file --path <file> --reason <why>). This logs the request and auto-approves (or queues a HITL decision for strict mode). The point is observability, not hard blocking. - Planner responsibility: The planner should list files generously using globs. The enforcement catches mistakes, it doesn't compensate for a bad plan.
Current Architecture
The plumbing is mostly in place:
- Plan template (
docs/templates/plan.md) already requiresfiles:per task in the YAML block - Plan parser (
shared/egg_contracts/plan_parser.py) already extracts these intofiles_affectedon the Task model PhaseFileRestriction(gateway/phase_filter.py) already supportsallowed_patternswith glob matching — it's just not populated for implement phase- Gateway commit validation (
gateway/gateway.py:737-805) already callscheck_phase_file_restrictions()on every push - Post-agent commit (
gateway/post_agent_commit.py) already filters files by phase restrictions before auto-committing
What Needs to Change
1. Session model — add allowed_files field
Add an allowed_files: list[str] | None field to Session in gateway/session_manager.py. When set, these augment the phase's allowed_patterns during commit validation. When None (non-pipeline sessions, non-implement phases), behavior is unchanged.
Also accept allowed_files in register_session().
2. Container spawner — read task files, pass to session
In orchestrator/container_spawner.py, when spawning an implement agent:
- Collect
files_affectedfrom all tasks assigned to this agent (union across tasks in the phase) - Auto-expand entries: For each listed file
dir/foo.py, implicitly adddir/as an allowed directory prefix so sibling files (new helpers, test files) are reachable - Pass the combined list as
allowed_filestogateway.register_session()
3. Gateway commit/push validation — warn-then-block enforcement
In gateway/gateway.py, when validating a push for an implement-phase session that has allowed_files set:
- Build a
PhaseFileRestrictionthat combines the existing implement-phaseblocked_patternswithallowed_patternsderived from the session'sallowed_files - Use glob/directory-prefix matching (not exact match) so
src/module.pyin the plan covers the file even if nested or renamed slightly - First violation per file: Log a structured warning (visible in agent logs and checkpoint), allow the push
- Second violation for the same file: Block with an actionable error explaining which task's
files_affectedthe agent should check, and how to request an exception
4. Post-agent commit — respect per-session restrictions
gateway/post_agent_commit.py needs access to the session's allowed_files to filter uncommitted changes the same way pushes are filtered. Files outside the allowlist are restored (not committed) but this should be logged clearly — silent drops cause confusion.
5. Plan template — document enforcement semantics
Update docs/templates/plan.md and planner prompt to clarify that files: is an enforcement boundary, not just a hint. The planner should:
- List files generously (include test files, config files the task will touch)
- Use glob patterns where appropriate (e.g.,
src/components/*.tsx,tests/) - Prefer directory-level globs over individual files when the task touches multiple files in a directory
- Understand that the agent gets automatic directory-sibling access, so exact exhaustive listing isn't required
Edge Cases to Handle
- Empty
files_affected: If the planner omits files for a task (or it's empty), fall back to current behavior (no per-file restriction, only phase-level). Don't lock the agent out of everything. - Glob patterns in file lists: Support
*and**infiles_affectedentries so the planner can saytests/**instead of listing every test file. - New files in allowed directories: Agent creates a file not explicitly listed but in a directory that contains listed files. This should be allowed — the directory-sibling expansion covers it.
- Shared files: Multiple tasks may list the same file. Each agent's session should include the union of files from its assigned tasks.
- Tester/reviewer agents: This restriction only applies to implement-phase coder agents. Tester and reviewer agents already have their own role-based restrictions.
- Config files: Common config files (
pyproject.toml,package.json,Makefile, etc.) should be implicitly allowed or the planner should be prompted to include them. An agent that can't updatepyproject.tomlafter adding a dependency will spin.
Acceptance Criteria
- Gateway warns on first out-of-scope file per session, blocks on repeated violations
- Post-agent auto-commit filters out files not in the task's allowlist (restores them instead of committing) with clear logging
- When
files_affectedis empty for a task, no per-file restriction is applied (graceful fallback) - Glob patterns in
files_affectedare supported (e.g.,src/components/*,tests/**) - Directory-sibling expansion: listing
dir/foo.pyimplicitly allows other files indir/ - Plan template documents that
files:is enforced, with guidance on generous listing - Existing tests pass; new tests cover per-session file restriction enforcement
- Tier 3 (parallel agent) dispatch correctly scopes each agent to its phase's file union
- Agent never hard-blocks on first attempt to modify an out-of-scope file
Test Plan
gateway/tests/— session file restriction enforcement on push and commitgateway/tests/— warn-then-block escalation behaviorgateway/tests/— directory-sibling expansiongateway/tests/— post-agent commit filtering with allowed_filesorchestrator/tests/— container spawner passes files_affected to sessionshared/egg_contracts/tests/— glob pattern support in files_affected- End-to-end: Tier 3 pipeline where two agents have disjoint file lists, verify each can only commit their own files
Closes #805.