cleanup(agent-team): clear 3 SAST mediums + refresh README to built state #15

Merged
amoussa1229 merged 2 commits from feature/agent-team-medium-cleanup into main 2026-06-18 18:21:44 +00:00
4 changed files with 87 additions and 47 deletions

View file

@ -1,65 +1,99 @@
# agent-team — R720 Plane-2 FOUNDATION
# agent-team — R720 Plane-2 SDLC pipeline
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.
The durable, human-gated agentic SDLC pipeline for the R720 (`sh-secrev` VM),
design: `../docs/r720-agent-team-design.md`. A task flows INTAKE → CLARIFY (the
human gate) → PLAN → REVIEW, and (opt-in, deploy-gated) → BUILD → VERIFY → draft
PR. Every stage is durable and resumable (LangGraph + a SQLite checkpointer);
the human gate suspends on `interrupt()` and resumes on a real answer.
> Status: FOUNDATION modules only. No coordinator, no transports' concrete
> adapters, no CI workflow, no provisioning. Those are later phases (§7).
```
INTAKE → CLARIFY (Claude, human gate) → PLAN (Claude) → REVIEW (GPT-4.1)
▲ │
└── loop-back ───┤
approve/escalate → END
(P3, opt-in + INERT until the CI gate clears):
approve → BUILD (DeepSeek) → VERIFY (ci_gate) → draft PR
```
> **Status (2026-06-18).** P1 (human gate) + P2 (planner + adversarial review
> loop) + the live runtime (coordinator, Slack/GitHub/Claude-Code transports,
> intake) + P3-inert (build/verify subgraph, opt-in) + P4 (more transports +
> GitHub-issue intake) are **built, reviewed, and merged to main** (~795 tests).
> **Nothing is provisioned**: not rsync'd to the box, no live tokens, no
> systemd, no live CI. Production default runs **P2** (no builders).
> **Deploy-gated / not yet built:** the P3 *live* CI apply/verify + OIDC role
> (held behind `/sh-security-review` + the mandatory GPT-4.1 cross-review), and
> all provisioning. See the project memory `project_r720_agent_team` and §7 of
> the design for the phased rollout.
## 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
run-team.py # operator CLI: init-db, list/show/answer/expire/
# force-resume (ledger), start (intake), serve (daemon),
# intake-github
agent_team/ # importable package (snake_case)
coordinator.py # the live runtime keystone: invoker→graph→ResumeWorker;
# start_task / submit_answer / drain / tick / recover / serve
graph.py # LangGraph wiring: P1 (intake→clarify→plan) + opt-in P2
# review loop + opt-in P3 build/verify subgraph
invoker.py # §3.1 real Claude path (subscription-OAuth / API / Bedrock)
billing.py # §3.1 claude_invoke billing-mode seam
ci_gate.py # §3.3.2 pure-code authenticated-Checks PASS/FAIL gate
task_model.py / state_store.py
db/{schema.py,schema.sql} # SQLite ledger DDL + BEGIN IMMEDIATE compare-and-set
ledger.py / responder.py / resume_worker.py / deadline_timer.py / recovery.py
operator_cli.py
nodes/ # pipeline stages + their model bindings
clarifier.py + clarifier_llm.py # human gate (Claude)
planner.py # plan (Claude)
review_loop.py + review_loop_llm.py # adversarial review (GPT-4.1 via orchestrator)
builders.py + builders_llm.py # candidate diff (DeepSeek) — INERT, proposes only
verifier.py + verifier_llm.py # ci_gate sole PASS authority; LLM = fix-proposer
build_verify_subgraph.py # P3 BUILD→VERIFY topology (opt-in)
transport/ # one adapter contract + a live impl per channel
base.py # Transport ABC + QuestionSet / NormalizedAnswer
slack_adapter.py + slack_live.py + slack_listener.py # Block Kit + Socket Mode
github_adapter.py + github_live.py + github_intake.py # issue-comment + issue intake
claude_code_adapter.py + claude_code_live.py # file-drop responder
ci/ # §3.3.2 split-job CI apply/verify workflow (DEPLOY-GATED)
systemd/ # agent-team-coordinator.service (not installed)
DEPLOY-R720.md # provisioning runbook (snapshot-first, rsync, tokens, demo)
tests/ # pytest, one module per source module + sim harness
```
The top directory is kebab-case (`agent-team/`); the importable package is
snake_case (`agent_team/`), per the engineering handbook.
## Modules (contracts)
## Key design points
| 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.
- **Durable human gate (§3.3.1).** The `pending_questions` ledger is the single
source of truth for the question lifecycle. Every race (duplicate answers,
transport redelivery, answer-vs-timeout) resolves via one atomic
compare-and-set against `status`, inside a `BEGIN IMMEDIATE` transaction —
first-answer-wins (`rowcount == 1`), late/duplicate ignored. The LangGraph
`SqliteSaver` checkpointer shares the same DB file.
- **Fail-safe model seams.** Every node treats model output as untrusted and
fails SAFE: garbage never clears the 98% clarifier gate, never auto-approves a
plan, never fabricates a build success, and the verifier's `ci_gate` is the
**sole** PASS authority (the LLM is structurally a fix-proposer only).
- **Inbound auth (§3.3.1).** The Slack Socket Mode listener authorizes the
**sender** against an owner allowlist (`AGENT_TEAM_SLACK_OWNER_IDS`,
fail-closed) on top of the open-status CAS anti-replay.
- **Billing seam (§3.1).** Claude runs under subscription OAuth on the box;
GPT-4.1 (review) and DeepSeek (builders) route through the local orchestrator
`run.py`. Switching Claude billing is a config flip.
## Running the tests
```
cd agent-team
python3 -m pytest tests/ -q
python3 -m pytest -q # conftest puts the package on sys.path; no install needed
```
`tests/conftest.py` puts the package on `sys.path`, so no install is required.
## Deploy
## 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.
Deploy-gated. See `DEPLOY-R720.md` for the provisioning runbook (VM snapshot
first, rsync, venv deps, `~/secrev.env` tokens, `init-db`, systemd, and the live
P1 exit-criteria demo). Secrets are never committed.

View file

@ -94,7 +94,9 @@ def _turn_cache_key(
digest_src = json.dumps(list(qa_history), sort_keys=True, default=repr)
except (TypeError, ValueError):
digest_src = repr(list(qa_history))
content_hash = hashlib.sha1(digest_src.encode("utf-8")).hexdigest()
# sha256 (not sha1): this is a non-security cache-discriminator, but using a
# modern digest keeps the SAST scanners quiet (CWE-327) with no downside.
content_hash = hashlib.sha256(digest_src.encode("utf-8")).hexdigest()
return (thread_id, len(qa_history), content_hash)

View file

@ -173,7 +173,9 @@ def _default_http_post(
for key, value in headers.items():
request.add_header(key, value)
try:
with _urlrequest.urlopen(request) as response: # noqa: S310 (trusted api host)
# url is built from a fixed https GitHub API base; the dynamic part is the
# path, never the scheme, so there is no SSRF/file:// surface.
with _urlrequest.urlopen(request) as response: # noqa: S310 (trusted api host); nosemgrep
status = response.getcode()
raw = response.read().decode("utf-8")
except _urlerror.HTTPError as exc: # pragma: no cover - network path

View file

@ -283,7 +283,9 @@ def build_default_issue_client(
request.add_header("Authorization", f"Bearer {token}")
request.add_header("Accept", "application/vnd.github+json")
request.add_header("X-GitHub-Api-Version", "2022-11-28")
with _urlrequest.urlopen(request) as response: # noqa: S310 (trusted api host)
# url is built from a fixed https GitHub API base; the dynamic part is
# the path, never the scheme, so there is no SSRF/file:// surface.
with _urlrequest.urlopen(request) as response: # noqa: S310 (trusted api host); nosemgrep
raw = response.read().decode("utf-8")
data = json.loads(raw) if raw else []
# The issues endpoint can include pull requests (they share the