binky-control/workplans/BINKY-WP-0007-qonto-governed-assistant.md
tegwick ea568558a8
Some checks failed
Work Records / validate (push) Has been cancelled
BINKY-WP-0007: close out against qonto-assistant's actual delivery
qonto-assistant (sibling repo) already shipped Phase 1 (QONTO-WP-0002)
and Phase 2 MCP surface (QONTO-WP-0003), both finished, 31 tests
passing, ahead of this workplan's original sequencing. Reconciled
T02-T04 against what actually landed there instead of duplicating it.

The one piece qonto-assistant explicitly left for this repo -- the
CostRunRate consumer cutover -- is implemented here:
scripts/qonto-costrunrate-refresh.py calls GET /v1/snapshot and writes
dated, redacted evidence under finance/ for a human to reconcile
against CostRunRate.md. Verified end-to-end against a local
fixture-backed qonto-assistant instance (no real credentials); a real
pull against the live account awaits qonto-assistant getting an actual
deployment, called out as a known gap rather than faked.

Workplan closed (status: finished); remaining gaps (OIDC federation,
harness tool profile registration, Phase 3/4) are carried forward in
qonto-assistant's own closure notes, not duplicated here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 14:02:11 +02:00

160 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: BINKY-WP-0007
type: workplan
title: "Qonto Governed Assistant: policy kernel + REST (Phase 1)"
domain: infotech
repo: binky-control
status: finished
owner: claude
topic_slug: the-custodian
created: "2026-07-22"
updated: "2026-07-23"
state_hub_workstream_id: "8a691acb-3500-4f17-84fe-dcfc7a979fdc"
---
Follow-on from BINKY-WP-0005 (custody lane + first read-only pull, finished).
Blueprint: `integrations/qonto-governed-assistant/ArchitectureBlueprint.md`.
**Recommendation being executed:** build a small **Qonto Governed Assistant**
service — the only component allowed to hold the bank API key — exposing a
default-deny policy-shaped MCP server and thin REST API, so every harness
(Claude Code, Codex, Cursor, Grok, agent-harness sessions, scripts) is a dumb
client of one governed endpoint instead of each wiring vendor MCP + local
allow-lists directly. **No spend / no volume-cost tools in v1** — payments,
transfers, card ops, and plan changes stay Red lane (human in the Qonto app).
This workplan tracks **Phase 1** (policy kernel + REST — minimum useful
product, per blueprint §6) plus the binky-control-side consumer cutover.
Phase 3 (flex-auth/fleet) and Phase 4 (productization) remain follow-on work.
**2026-07-23 status check:** implementation is happening in the sibling
`qonto-assistant` repo (registered in the state-hub as `qonto-assistant`,
domain `infotech`), which has its own workplans mirroring this one:
`QONTO-WP-0001` (bootstrap, finished), `QONTO-WP-0002` (Phase 1 — policy
kernel + REST, finished), and `QONTO-WP-0003` (Phase 2 — MCP surface,
finished, ahead of this workplan's original sequencing which expected Phase 2
to wait). All three ratified DEC-2026-005 decisions hold (option B, dedicated
repo, hard v1 freeze, streamable-HTTP transport, no vendor MCP backend). 31
unit tests pass (`pytest`); REST and MCP smoke scripts both pass against
fixture data with no real Qonto credentials. Tasks T02T04 below are
rescoped to reflect what actually landed there, plus the one piece that was
explicitly left for binky-control: the CostRunRate consumer cutover (T03).
## Task: Ratify architecture decisions (blueprint §9)
```task
id: BINKY-WP-0007-T01
status: done
priority: high
state_hub_task_id: "34474d61-0238-4321-a6c0-27c3ad3b8fe3"
```
**2026-07-22:** DEC-2026-005 approved as recommended (all five). See
`DecisionQueue.md` for the ratified answers.
Bring the five decision requests from the blueprint to the founder via
DecisionQueue.md:
1. Adopt the domain-assistant architecture (option B) for Qonto.
2. Repo home: new dedicated `qonto-assistant` repo (already present in the
state-hub repo registry) vs. waiting for a generic `finance-connect`.
3. v1 policy freeze: hard deny on all spend / volume-cost tools.
4. MCP transport: remote streamable-HTTP only for production clients.
5. Whether to ever run vendor `qonto-mcp-server` as an internal-only backend
(blueprint default: no).
Done when: DEC entry filed, founder decision recorded, blueprint updated with
the ratified answers.
## Task: Service skeleton — decision function + custody wiring
```task
id: BINKY-WP-0007-T02
status: done
priority: high
state_hub_task_id: "8978dbd9-c7a1-4b5b-9c0c-acf3e08c74bf"
```
**2026-07-23:** Delivered in the `qonto-assistant` repo as `QONTO-WP-0002`
(T01T07, all done), not as new work in binky-control — the decision from T01
already routed the implementation there. Shipped: protocol-neutral capability
core, declarative `policy/qonto-v1.yaml` + pure `PolicyEngine.decide()`
(default-deny; allows `org_summary`, `list_transactions`,
`cost_run_rate_hints`, `snapshot_bundle`; hard-denies `spend`,
`volume_cost`, `credential_exfil`), a Qonto REST client with
`Authorization: login:key` injection and no secret logging, plus guardrails
beyond the original ask: bounded pagination/timeouts, rate limiting, bounded
concurrency, and redaction tests (IBAN → last-4 only). OpenBao fetch
(`tenants/binky/qonto-api`) is isolated to a dedicated runtime path, never
harness env. Verified: `pytest` → 31 passed (includes Phase 2 additions),
`python3 -m compileall`.
## Task: REST surface + CostRunRate consumer cutover
```task
id: BINKY-WP-0007-T03
status: done
priority: medium
state_hub_task_id: "a35e2565-e520-491e-8587-8ec4961f898c"
```
**2026-07-23:** REST surface (`GET /v1/accounts`, `/v1/transactions`,
`/v1/snapshot`, `/v1/health`) shipped in `qonto-assistant` as
`QONTO-WP-0002-T04`, behind the same policy kernel as T02. That repo
explicitly scoped the binky-control-side write out of its own runbook
(`docs/operator-runbook.md`: "The repo does not write directly into
binky-control/finance/CostRunRate.md. That consumer-side write remains
outside this repo.") — so the consumer cutover is this repo's own
deliverable, done here: `scripts/qonto-costrunrate-refresh.py` calls
`GET /v1/snapshot`, writes a dated evidence file under `finance/` (redacted —
IBAN last-4 only, no secrets, matching the 2026-07-21 first-pull shape), and
prints recurring-debit hints for a human to reconcile against
`finance/CostRunRate.md` prose (the script does not edit that file directly —
same judgment-call boundary as the original first pull).
Verified end-to-end against a locally started `qonto-assistant` instance
using `QONTO_FIXTURE_DIR` (no real Qonto credentials): `/v1/health` reachable,
snapshot fetched, evidence file written and inspected clean (no IBAN, no key
material), then removed since it was fixture data, not a real pull.
**Known gap, not closed by this task:** `qonto-assistant` isn't deployed
anywhere yet (local dev only) — a real CostRunRate refresh against the live
dogfood account still needs that service running somewhere reachable
(railiance, per the blueprint's placement options) with the OpenBao lane
wired to it. That's deployment/ops scope, not scripting scope, and belongs to
a follow-on task once `qonto-assistant` has a runtime home.
## Task: Smoke tests and closure
```task
id: BINKY-WP-0007-T04
status: done
priority: medium
state_hub_task_id: "a16ef7df-ddd7-474b-8327-0eca5c89ede7"
```
**2026-07-23:** REST smoke (`scripts/smoke_rest_api.py`) and MCP smoke
(`scripts/smoke_mcp.py`) both live in `qonto-assistant` and both pass against
fixtures with no real credentials (`QONTO-WP-0002-T07`, `QONTO-WP-0003-T06`).
Phase 2 (MCP surface for all harnesses) was **not** held for a hand-off — it
was already built and closed as `QONTO-WP-0003` (T01T07, all done,
2026-07-23) while T02/T03 above were still open here: streamable-HTTP MCP
adapter on the same policy kernel, three capability tools
(`qonto_org_summary`, `qonto_list_transactions`, `qonto_cost_run_rate_hints`),
shared-secret bearer-token workload auth with the real OIDC gap called out
explicitly (no issuer exists in the fleet yet), REST/MCP audit-schema parity
pinned by test, and a documented (not yet registered) `finance-qonto-read`
agent-harness tool profile contract.
This workplan (Phase 1 + consumer cutover) is closed. **Known gaps carried
forward**, tracked in `qonto-assistant`'s own closure notes, not duplicated
into new binky-control tasks: real OIDC/workload-identity federation;
per-actor identity not yet cryptographically bound to the workload-auth
token; `finance-qonto-read` documented but not registered in
`agent-harness/agent_harness/profiles.py`; Phase 3 (flex-auth resource
`finance.qonto.read`, graduated quotas, redaction profiles) and Phase 4
(productization) not started. Any of these becoming binky-control-actionable
(e.g. deploying qonto-assistant, wiring the harness profile) should be a new
workplan or an `AutopilotWorkQueue.md` residual, not a reopening of this one.
Done when: `statehub fix-consistency` run clean.