Some checks failed
Work Records / validate (push) Has been cancelled
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>
160 lines
7.6 KiB
Markdown
160 lines
7.6 KiB
Markdown
---
|
||
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 T02–T04 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`
|
||
(T01–T07, 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` (T01–T07, 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.
|