66 lines
3.6 KiB
Markdown
66 lines
3.6 KiB
Markdown
|
|
# agent-team — R720 Plane-2 FOUNDATION
|
||
|
|
|
||
|
|
Pre-deployment scaffolding for the R720 agent-team SDLC pipeline (design:
|
||
|
|
`../docs/r720-agent-team-design.md`). This commit ships the **Plane-2
|
||
|
|
FOUNDATION** layer only — the durable, transport-agnostic **contracts** the leaf
|
||
|
|
builders import verbatim. Nothing here is provisioned, scheduled, or wired to
|
||
|
|
live infrastructure.
|
||
|
|
|
||
|
|
> Status: FOUNDATION modules only. No coordinator, no transports' concrete
|
||
|
|
> adapters, no CI workflow, no provisioning. Those are later phases (§7).
|
||
|
|
|
||
|
|
## Layout
|
||
|
|
|
||
|
|
```
|
||
|
|
agent-team/
|
||
|
|
agent_team/ # importable package (snake_case)
|
||
|
|
state_store.py # §6.7 atomic write + integrity-checked read
|
||
|
|
billing.py # §3.1 claude_invoke billing-mode seam
|
||
|
|
task_model.py # §3.3 TaskRecord / Phase / PipelineState
|
||
|
|
db/
|
||
|
|
schema.py # §3.3.1/§6.7 SQLite DDL + connect/init/migrate
|
||
|
|
schema.sql # raw DDL, mirrors schema.py verbatim
|
||
|
|
transport/
|
||
|
|
base.py # §3.3.1 Transport ABC + QuestionSet/NormalizedAnswer
|
||
|
|
tests/ # pytest unit tests, one module per source module
|
||
|
|
```
|
||
|
|
|
||
|
|
The top directory is kebab-case (`agent-team/`); the importable package is
|
||
|
|
snake_case (`agent_team/`), per the engineering handbook.
|
||
|
|
|
||
|
|
## Modules (contracts)
|
||
|
|
|
||
|
|
| Module | Design ref | What it provides |
|
||
|
|
|---|---|---|
|
||
|
|
| `state_store` | §6.7 | `atomic_write(path, data)` (write-temp → fsync → rename), `read_checked(path, *, schema_version)` (schema-version + content-hash integrity check, raises `IntegrityError`), `compute_content_hash(data)`. Pure stdlib; no other `agent_team` deps. |
|
||
|
|
| `db.schema` | §3.3.1, §6.7 | `SCHEMA_VERSION`, `PENDING_QUESTIONS_DDL`, `BUDGET_LEDGER_DDL`, `connect()` (WAL + foreign_keys + busy_timeout), `init_db()`, `migrate()`, and the `BEGIN IMMEDIATE` compare-and-set helpers (`answer_question`/`expire_question`/`supersede_question`). SQL DDL lives **only** here. |
|
||
|
|
| `billing` | §3.1 | `BillingMode{SUBSCRIPTION,API,BEDROCK}`, `claude_invoke(prompt, *, mode=None, **kw) -> ClaudeResult`, `resolve_mode(config)`. Single seam; subscription mode pops any stray `ANTHROPIC_API_KEY` so OAuth can't be overridden. |
|
||
|
|
| `transport.base` | §3.3.1 | `Transport` ABC (`post_question` → `channel_ref`; `parse_answer` → `(question_id, answer, via)`), `QuestionSet`, `NormalizedAnswer`. Transport-independent; Slack/GitHub/Claude-Code adapters subclass in the leaves. |
|
||
|
|
| `task_model` | §3.3 | `TaskRecord`, `TaskStatus`, `Phase{INTAKE…DONE}`, `new_thread_id()`, `PipelineState` TypedDict (LangGraph state schema), JSON serialization helpers. Pure model, no I/O. |
|
||
|
|
|
||
|
|
### Durable human-in-the-loop (§3.3.1)
|
||
|
|
|
||
|
|
The `pending_questions` ledger is the single durable source of truth for the
|
||
|
|
question lifecycle. Every race (duplicate answers, transport redelivery,
|
||
|
|
answer-vs-timeout) resolves via one atomic compare-and-set against the `status`
|
||
|
|
column, run inside a `BEGIN IMMEDIATE` transaction so concurrent responders are
|
||
|
|
serialized — first-answer-wins (`rowcount == 1`), late/duplicate ignored
|
||
|
|
(`rowcount == 0`). The LangGraph `SqliteSaver` checkpointer creates its own
|
||
|
|
tables against the **same** DB file.
|
||
|
|
|
||
|
|
## Running the tests
|
||
|
|
|
||
|
|
```
|
||
|
|
cd agent-team
|
||
|
|
python3 -m pytest tests/ -q
|
||
|
|
```
|
||
|
|
|
||
|
|
`tests/conftest.py` puts the package on `sys.path`, so no install is required.
|
||
|
|
|
||
|
|
## Not in this commit (later phases)
|
||
|
|
|
||
|
|
Coordinator/brain, concrete Slack/GitHub/Claude-Code transport adapters, the
|
||
|
|
CI apply/verify workflow (§3.3.2), step-ca / Roles Anywhere, scheduling, and
|
||
|
|
the operator CLI (`run-team.py`). See `../docs/r720-agent-team-design.md` §7
|
||
|
|
for the phased rollout. Secrets are never committed.
|