diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 11dab7bf64..9861c702e2 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -26,6 +26,8 @@ See the [main README](../../README.md) for the architecture diagram. **Access Control:** - Branch ownership (agent can only push to `egg/*` branches) +- Phase-based operation restrictions (git/gh operations filtered by SDLC phase) +- Role-based contract mutations (implementer, reviewer, human roles with field-level permissions) - No merge capability (gateway has no merge endpoint) - Force push and destructive operations blocked @@ -36,6 +38,21 @@ See the [main README](../../README.md) for the architecture diagram. | **Gateway** | Credential injection, policy enforcement, HTTP proxy | [Gateway README](../../gateway/README.md) | | **Sandbox** | Agent execution environment, git/gh wrappers | [Sandbox README](../../sandbox/README.md) | | **Shared Libraries** | Config, logging, git utilities | [Shared README](../../shared/README.md) | +| **egg_contracts** | SDLC contract models, role-based mutation validation | `shared/egg_contracts/` | + +## SDLC Contracts + +Contracts are JSON documents that track issue progress through SDLC phases, tasks, decisions, and acceptance criteria. They provide structurally-verified agent checkpoints. + +**Schema**: `.egg/schemas/contract.schema.json` + +**Role-based ownership**: Each contract field is owned by a specific role: +- `implementer`: `tasks[].commit`, `tasks[].notes`, `tasks[].files_affected` +- `reviewer`: `tasks[].status`, `phases[].status`, `phases[].review_feedback`, `acceptance_criteria[].verified`, `current_phase` +- `human`: `decisions[].resolved`, `decisions[].resolution`, `decisions[].resolved_by`, `decisions[].resolved_at`, all other fields +- `system`: Structural fields (`issue`, `schemaVersion`) + +The gateway enforces role-based mutations via the `/api/v1/contract/` endpoints. Role is determined from workflow context, preventing privilege escalation. ## Key Architectural Decisions diff --git a/gateway/README.md b/gateway/README.md index 730353abdb..4027216d55 100644 --- a/gateway/README.md +++ b/gateway/README.md @@ -34,10 +34,12 @@ The gateway sidecar is the **trusted** component that holds credentials and vali ## Policy Rules +### Ownership Policies + | Operation | Policy | Check | |-----------|--------|-------| -| `git push` | Branch ownership | Branch has open PR authored by egg, OR branch starts with `egg-` or `egg/` | -| `gh pr create` | Always allowed | egg can create PRs on any branch it can push to | +| `git push` | Branch ownership + Phase filter | Branch has open PR authored by egg, OR branch starts with `egg-` or `egg/`, AND operation is allowed in current phase | +| `gh pr create` | Phase filter | Operation is allowed in current phase (typically only in 'pr' phase) | | `gh pr comment` | PR ownership | PR must be authored by egg | | `gh pr merge` | **BLOCKED** | No merge endpoint - human must merge via GitHub UI | | `gh pr edit` | PR ownership | PR must be authored by egg | @@ -49,21 +51,44 @@ The gateway sidecar is the **trusted** component that holds credentials and vali - Branch has an open PR where author is an egg variant, OR - Branch name starts with `egg-` or `egg/` (allows new branches before PR exists) +### Phase-Based Operation Restrictions + +The gateway enforces phase-specific operation restrictions based on the current SDLC pipeline phase. This prevents operations like pushing code during planning or creating PRs during implementation. + +**Phase permissions** (configured in `.egg/phase-permissions.json`): + +| Phase | Allowed Operations | Blocked Operations | Exit Requires | +|-------|-------------------|-------------------|---------------| +| **refine** | `gh issue comment/edit`, `egg-contract add-decision` | `git push`, `gh pr create` | Human approval | +| **plan** | `gh issue comment/edit`, `egg-contract add-decision` | `git push`, `gh pr create` | Human approval | +| **implement** | `git push`, `egg-contract add-commit/mark-task/update-notes/mark-phase` | `gh pr create` | Reviewer approval | +| **pr** | `gh pr create/edit`, `git push` | — | Human merge | + +> **Note:** `egg-contract show *` is allowed in all phases for contract state viewing. + +Phase transitions require specific roles (human, reviewer, implementer) as defined in the phase configuration. + ## API Endpoints +### Git Operations + ``` POST /api/v1/git/push Request: {repo_path, remote, refspec, force} - Policy: branch_ownership + Policy: branch_ownership + phase_filter POST /api/v1/git/fetch Request: {repo_path, remote, operation, args[]} Policy: none (read operations) Operations: "fetch", "ls-remote" +``` +### GitHub Operations + +``` POST /api/v1/gh/pr/create Request: {repo, title, body, base, head} - Policy: none (always allowed) + Policy: phase_filter POST /api/v1/gh/pr/comment Request: {repo, pr_number, body} @@ -80,7 +105,67 @@ POST /api/v1/gh/pr/close POST /api/v1/gh/execute Request: {args[], require_auth} Policy: filtered passthrough for read operations +``` + +### Phase Operations + +``` +POST /api/v1/phase/advance + Request: {issue_number, repo_path?, reason?, actor?} + Policy: session_auth + Description: Advance pipeline to next phase + +POST /api/v1/phase/filter + Request: {issue_number, operation_type, command, repo_path?} + Policy: session_auth + Description: Check if operation is allowed in current phase + +GET /api/v1/phase/current/ + Query: ?repo_path= + Policy: session_auth + Description: Get current phase for an issue +GET /api/v1/phase/permissions/ + Policy: session_auth + Description: Get allowed/blocked operations for a phase +``` + +### Contract Operations + +The contract API provides role-based access to SDLC contracts that track issue progress through phases, tasks, and decisions. + +``` +GET /api/v1/contract/ + Query: ?repo_path=&include_audit_log= + Policy: session_auth + Description: Get contract state for an issue + +POST /api/v1/contract/mutate + Request: {issue_number, field_path, new_value, repo_path?, actor?, reason?} + Policy: session_auth + role_based + Description: Apply a mutation to a contract (role determines allowed fields) + +POST /api/v1/contract/validate + Request: {field_path, new_value} + Policy: session_auth + Description: Validate a mutation without applying it + +GET /api/v1/contract/exists/ + Query: ?repo_path= + Policy: session_auth + Description: Check if a contract exists for an issue +``` + +**Role-based field ownership**: Mutations are validated against the caller's role: +- `implementer`: Can modify `tasks[].commit`, `tasks[].notes`, `tasks[].files_affected` +- `reviewer`: Can modify `tasks[].status`, `phases[].status`, `phases[].review_feedback`, `acceptance_criteria[].verified`, `current_phase` +- `human`: Can modify `decisions[].resolved`, `decisions[].resolution`, `decisions[].resolved_by`, `decisions[].resolved_at`, and all other fields + +Role is determined from workflow context (session metadata), not request body, preventing privilege escalation. + +### Health + +``` GET /api/v1/health Response: {status, github_token_valid} ``` @@ -95,6 +180,11 @@ gateway/ ├── policy.py # Branch ownership, push policies ├── fork_policy.py # Fork access policies ├── private_repo_policy.py # Private/public repo access control +├── phase_filter.py # Phase-based operation filtering +├── phase_transition.py # Phase transition validation +├── phase_api.py # Phase API endpoints +├── contract_api.py # Contract API endpoints +├── auth.py # Session authentication ├── token_refresher.py # GitHub App token management ├── anthropic_credentials.py # Anthropic API key injection ├── worktree_manager.py # Git worktree lifecycle @@ -124,6 +214,10 @@ gateway/ │ ├── test_session_manager.py │ ├── test_token_refresher.py │ ├── test_worktree_manager.py +│ ├── test_phase_filter.py +│ ├── test_phase_transition.py +│ ├── test_phase_api.py +│ ├── test_contract_api.py │ ├── integration_test.sh │ └── README-integration.md └── README.md # This file @@ -135,9 +229,13 @@ gateway/ 2. **Branch ownership**: Branch has an open egg-authored PR OR starts with `egg-` or `egg/`. This allows pushing to new branches before a PR exists. -3. **Token source**: In-memory token refresh via `token_refresher.py`. Tokens are refreshed automatically 15 minutes before expiry. +3. **Phase-based filtering**: Operations are filtered based on the current SDLC pipeline phase. Configuration is loaded from `.egg/phase-permissions.json` with schema validation. This prevents incidents like pushing code during planning phases. + +4. **Token source**: In-memory token refresh via `token_refresher.py`. Tokens are refreshed automatically 15 minutes before expiry. + +5. **Dual network modes**: Squid proxy controls outbound access. Private mode restricts to Anthropic API only; public mode allows all traffic. -4. **Dual network modes**: Squid proxy controls outbound access. Private mode restricts to Anthropic API only; public mode allows all traffic. +6. **Role-based contract mutations**: Contract field ownership is tied to roles (implementer, reviewer, human). Role is determined from workflow context via session metadata, not request body, preventing privilege escalation. The `egg_contracts` shared library provides Pydantic models and validation. ## Testing