You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+9-4Lines changed: 9 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -103,9 +103,13 @@ The manager gate fires on any exception **or** a clean bill over a floor ($1,000
103
103
104
104
**Streaming, typed end to end.** The `run` procedure is an **oRPC event iterator** (a typed async generator of `TraceEvent | StreamDone`); the client consumes it with `for await`, no manual reader or cast. A small adapter ([`lib/trace.ts`](lib/trace.ts)) maps Mastra's raw `run.stream()` chunks to the stable `TraceEvent` vocabulary so the UI depends on ours, not Mastra's internals, and a junk chunk is dropped rather than crashing the stream.
105
105
106
-
### Stateless by design
106
+
### Bounded persistence + a nightly reset
107
107
108
-
The seeded data is read-only. "Run pipeline" executes server-side, streams the trace, and **forgets** — so the 50th visitor sees the same pristine state as the 1st. (The `agent_runs` table is modelled as the canonical persisted shape of a run but intentionally left empty.)
108
+
Every run is persisted as an append-only **audit row** ([`agent_runs`](db/schema.ts)) — its verdict, outcome, and the full trace — and the dashboard's **Recent runs** panel lists them, each one replayable (click → the stored trace re-renders with no model call, zero tokens). That's the audit trail an AP buyer asks for first.
109
+
110
+
The persistence is **bounded**: a daily [Vercel Cron](vercel.json) hits [`/api/reset`](app/api/reset/route.ts) (guarded by `CRON_SECRET`), which truncates + reseeds Postgres — so the demo returns to a pristine queue each morning, the 1st visitor's view restored for the next. The reset touches **Postgres only**; it never calls the QuickBooks/BambooHR sandboxes (frozen fixtures the pipeline reads, never writes), so it can't fail on a rotated token or desync an external system.
111
+
112
+
Crucially, a saved run **can't change a future run's verdict**: the app writes only `agent_runs`, never the document tables or the ERP/HRIS, so the matcher always reads the pristine seed. Persisting the audit trail and keeping every run deterministic are not in tension. The human-in-the-loop pause/resume stays **replay-based** (the run recomputes the deterministic prefix from the decisions) — persistence is for the audit log, not the resume, which keeps it off the costly Mastra-snapshot path.
109
113
110
114
### Project layout
111
115
@@ -177,6 +181,7 @@ pnpm dev # http://localhost:3000
177
181
|`BAMBOO_HR_API_KEY` + `BAMBOO_HR_SUBDOMAIN`| optional | Live BambooHR. **Without them onboarding replays the committed real fixture** — the demo and CI work with no key. |
178
182
|`QBO_CLIENT_ID` + `QBO_CLIENT_SECRET` + `QBO_REFRESH_TOKEN` + `QBO_REALM_ID`| optional | Live QuickBooks (ERP pull). **Without all four the pipeline replays the committed real fixture** — demo and CI work with no key. |
179
183
|`UPSTASH_*` / `KV_REST_API_*`| optional | Per-IP rate limiting; fails open without it |
184
+
|`CRON_SECRET`| optional | Guards the nightly `/api/reset` cron (Vercel injects it as a bearer token). Unset → the reset route refuses all callers. |
180
185
181
186
> **Set a spend cap on the Anthropic key** — the deployed demo is public and the buttons call the model.
182
187
@@ -186,11 +191,11 @@ pnpm dev # http://localhost:3000
186
191
187
192
## What's next
188
193
189
-
A stateless demo; the decision logic is pure, typed, and unit-tested, and the read-side integrations (HRIS, ERP pull) are already real-or-replayed. Production is additive, not a rewrite:
194
+
The decision logic is pure, typed, and unit-tested; the read-side integrations (HRIS, ERP pull) are already real-or-replayed; and runs are persisted as an audit trail bounded by a nightly reset. Production is additive, not a rewrite:
190
195
191
196
- the ERP **pull** is real (QuickBooks); swap the **post**-side stub (`fakeErp`) and the Slack/Jira integration stubs for real adapters of the same interfaces,
192
197
- live BambooHR + QuickBooks (both adapters + captured fixtures already exist; keys unlock the live path) and a second HRIS / ERP behind the same `HrisAdapter` / `PoSourceAdapter`,
193
-
-add persistence and an audit trail (the`agent_runs` table is shaped for it) so a workflow and a paused run survive a refresh,
198
+
-the audit trail persists today (append-only`agent_runs`, replayable, reset nightly); drop the reset and add per-tenant scoping for a multi-client deployment, and persist the paused-run snapshot if you want resume to survive a server restart (today it's recomputed from the decisions),
194
199
- wire real approver identity to the per-step gates,
0 commit comments