@terreno/mcp 57.9.0 → 57.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/upgrades/57.10.1.md +54 -0
- package/dist/docs/versioned/next/README.md +1 -0
- package/dist/docs/versioned/next/explanation/README.md +4 -0
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
- package/dist/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
- package/dist/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
- package/dist/docs/versioned/next/explanation/ai-observability.md +101 -0
- package/dist/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
- package/dist/docs/versioned/next/explanation/example-coverage.md +3 -1
- package/dist/docs/versioned/next/explanation/loop-engineering.md +1 -1
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
- package/dist/docs/versioned/next/explanation/typescript-configuration.md +1 -1
- package/dist/docs/versioned/next/explanation/versioning-policy.md +1 -1
- package/dist/docs/versioned/next/how-to/README.md +6 -0
- package/dist/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
- package/dist/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
- package/dist/docs/versioned/next/how-to/ai-feature-development.md +156 -0
- package/dist/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
- package/dist/docs/versioned/next/how-to/circleci.md +5 -3
- package/dist/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -1
- package/dist/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
- package/dist/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
- package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
- package/dist/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
- package/dist/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
- package/dist/docs/versioned/next/reference/README.md +3 -0
- package/dist/docs/versioned/next/reference/admin-config.md +2 -0
- package/dist/docs/versioned/next/reference/admin-frontend.md +216 -0
- package/dist/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
- package/dist/docs/versioned/next/reference/ai-harness.md +1881 -0
- package/dist/docs/versioned/next/reference/ai.md +251 -25
- package/dist/docs/versioned/next/reference/api.md +46 -0
- package/dist/docs/versioned/next/reference/blocks.md +178 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +1 -0
- package/dist/docs/versioned/next/reference/mcp-server.md +17 -0
- package/dist/docs/versioned/next/reference/test.md +11 -2
- package/dist/docs/versioned/next/reference/ui.md +325 -1
- package/dist/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
- package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
- package/dist/docs/versioned/next/tasks/ai-observability.md +233 -104
- package/dist/docs/versioned/next/tasks/durable-agent-harness.md +183 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +37 -0
- package/dist/tools.js.map +1 -1
- package/package.json +3 -2
- package/src/docs/upgrades/57.10.1.md +54 -0
- package/src/docs/versioned/next/README.md +1 -0
- package/src/docs/versioned/next/explanation/README.md +4 -0
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
- package/src/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
- package/src/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
- package/src/docs/versioned/next/explanation/ai-observability.md +101 -0
- package/src/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
- package/src/docs/versioned/next/explanation/example-coverage.md +3 -1
- package/src/docs/versioned/next/explanation/loop-engineering.md +1 -1
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
- package/src/docs/versioned/next/explanation/typescript-configuration.md +1 -1
- package/src/docs/versioned/next/explanation/versioning-policy.md +1 -1
- package/src/docs/versioned/next/how-to/README.md +6 -0
- package/src/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
- package/src/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
- package/src/docs/versioned/next/how-to/ai-feature-development.md +156 -0
- package/src/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
- package/src/docs/versioned/next/how-to/circleci.md +5 -3
- package/src/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -1
- package/src/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
- package/src/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
- package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
- package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
- package/src/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
- package/src/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
- package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
- package/src/docs/versioned/next/reference/README.md +3 -0
- package/src/docs/versioned/next/reference/admin-config.md +2 -0
- package/src/docs/versioned/next/reference/admin-frontend.md +216 -0
- package/src/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
- package/src/docs/versioned/next/reference/ai-harness.md +1881 -0
- package/src/docs/versioned/next/reference/ai.md +251 -25
- package/src/docs/versioned/next/reference/api.md +46 -0
- package/src/docs/versioned/next/reference/blocks.md +178 -0
- package/src/docs/versioned/next/reference/environment-variables.md +1 -0
- package/src/docs/versioned/next/reference/mcp-server.md +17 -0
- package/src/docs/versioned/next/reference/test.md +11 -2
- package/src/docs/versioned/next/reference/ui.md +325 -1
- package/src/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
- package/src/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
- package/src/docs/versioned/next/tasks/ai-observability.md +233 -104
- package/src/docs/versioned/next/tasks/durable-agent-harness.md +183 -0
|
@@ -1,134 +1,263 @@
|
|
|
1
|
-
# Tasks: AI observability (Langfuse
|
|
1
|
+
# Tasks: AI observability (Langfuse replacement)
|
|
2
2
|
|
|
3
3
|
IP: [ai-observability](../implementationPlans/ai-observability.md)
|
|
4
|
+
Design: [AI Observability Prototype](https://claude.ai/design/p/36eec001-f2f0-4161-b60d-20dace4d0a9c?file=AI+Observability+Prototype.dc.html)
|
|
4
5
|
|
|
5
6
|
Supporting skills (every task as applicable): `mongoose-schema-safety`, `terreno-backend-api`, `backend-test-env`, `ai-prompt-governance`, `update-docs`, `generate-sdk`, `verify-ui-changes`, `terreno-ui`, `terreno-data-fetching`, `building-terreno-apps`.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
---
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
# Phase 1 — MVP: prompt library, tracing, review queue
|
|
11
|
+
|
|
12
|
+
Ships design screens: Prompts, Prompt editor (+ Playground), Traces, Trace detail, Review queue, Review item.
|
|
13
|
+
|
|
14
|
+
## 1A — Contracts and tracer
|
|
15
|
+
|
|
16
|
+
- [x] **Task 1.1**: Observability types + `ObservabilityApp` config validation
|
|
17
|
+
- Delivers: `ObservabilityPlugin` / `TraceSink` / `ScoreSink` / `PromptRegistry` / `ReviewQueue` interfaces; `ObservabilityControlConfig`; boot throws on `experiments.primary !== datasets.primary`, on `reviewQueue !== "local"`, and on a primary whose plugin is missing
|
|
11
18
|
- Files: `ai/src/observability/types.ts`, `ai/src/observability/observabilityApp.ts`, `ai/src/observability/observabilityApp.test.ts`, `ai/src/index.ts`
|
|
12
19
|
- Blocked by: none
|
|
13
|
-
- Docs:
|
|
14
|
-
- Acceptance: unit tests for
|
|
20
|
+
- Docs: `docs/explanation/ai-observability.md` (two planes + config errors)
|
|
21
|
+
- Acceptance: unit tests for local-only valid config, illegal mix, langfuse review queue, missing plugin for a primary
|
|
15
22
|
|
|
16
|
-
- [
|
|
17
|
-
- Delivers: every `generate*` exports a trace unless `skipTrace`; sink failures logged not thrown; `AIRequest` still written; `userId
|
|
23
|
+
- [x] **Task 1.2**: In-memory sinks + `AIService` emit + production prompt resolve
|
|
24
|
+
- Delivers: `promptName` + `promptLabel` resolves via `PromptRegistry` **before the model call**, even when `skipTrace` is true or no trace sink is registered; missing registry/label/version returns 400 without a model call; resolved-version `sensitive` applies unless the generate option overrides it; every `generate*` exports a trace unless `skipTrace`; sink failures logged not thrown; `AIRequest` still written; `userId` / `sessionId` / `sensitive` / `promptRef` / `priceMap` on options; `costUsd` omitted for unpriced models
|
|
18
25
|
- Files: `ai/src/observability/memorySinks.ts`, `ai/src/service/aiService.ts`, `ai/src/types/index.ts`, `ai/src/service/aiService.test.ts`
|
|
19
26
|
- Blocked by: 1.1
|
|
20
|
-
- Docs: `docs/reference/ai.md` — `skipTrace`, `
|
|
21
|
-
- Acceptance: tests
|
|
27
|
+
- Docs: `docs/reference/ai.md` — `skipTrace`, `promptName`, `promptLabel`, `sensitive`, usage/cost fields
|
|
28
|
+
- Acceptance: tests for emit, `skipTrace`, throwing sink, cost from price map, **no `costUsd` when the model is unpriced**, multi-step still logs `AIRequest`, resolved prompt body reaches the model with `skipTrace`, and missing registry/production label → 400 with no model call
|
|
22
29
|
|
|
23
|
-
- [
|
|
24
|
-
- Delivers: `/gpt/prompt` sets `userId` from `req.user
|
|
25
|
-
- Files: `ai/src/routes/gpt.ts`,
|
|
30
|
+
- [x] **Task 1.3**: GPT routes pass identity and sensitivity
|
|
31
|
+
- Delivers: `/gpt/prompt` sets `userId` from `req.user`, `sessionId` from body/header `x-ai-session-id`, and may upgrade sensitivity with request `sensitive: true`; client `false` never downgrades a sensitive prompt version. Client `promptName` / `promptLabel` selection is admin-only; app routes select production prompts server-side
|
|
32
|
+
- Files: `ai/src/routes/gpt.ts`, route tests
|
|
26
33
|
- Blocked by: 1.2
|
|
27
34
|
- Docs: `docs/reference/ai.md` GPT section
|
|
28
|
-
- Acceptance: authenticated prompt produces a memory
|
|
35
|
+
- Acceptance: an authenticated prompt produces a memory-sink trace carrying that `userId` and `sessionId`
|
|
29
36
|
|
|
30
|
-
##
|
|
37
|
+
## 1B — Local store: prompts, traces, review
|
|
31
38
|
|
|
32
|
-
- [
|
|
33
|
-
- Delivers: schemas with
|
|
34
|
-
- Files: `ai/src/observability/local/models/*.ts`, `ai/src/observability
|
|
39
|
+
- [x] **Task 1.4**: Local Mongo models — prompts, versions, labels, traces, spans, scores
|
|
40
|
+
- Delivers: schemas with a description on every field, standard plugins, the indexes in the IP; models registered only when the local plugin constructs; `ObsPromptVersion` carries `variables[]` (`key`, `required`, `label?`, `reviewerNote?`), `inputSchema`, `outputSchema`, `outputFieldNotes`, **`sensitive` (default false)**, `config`; `ObsPrompt` carries `folder` + `tags[]`; `ObsTrace` carries `sensitive`, `errorSummary`, `prompts[]`; `ObsSpan` carries `status` and optional short `error`
|
|
41
|
+
- Files: `ai/src/observability/local/models/*.ts`, `ai/src/types/observability*.ts`, model tests
|
|
35
42
|
- Blocked by: 1.1
|
|
36
43
|
- Docs: `docs/reference/ai.md` model tables
|
|
37
|
-
- Acceptance:
|
|
44
|
+
- Acceptance: version immutability; unique `(promptId, label)`; `findExactlyOne` / `findOneOrNone` only; no unique index on scores
|
|
38
45
|
|
|
39
|
-
- [
|
|
40
|
-
- Delivers: create
|
|
46
|
+
- [x] **Task 1.5**: Local `PromptRegistry` + prompt HTTP + playground
|
|
47
|
+
- Delivers: create prompt in a folder; immutable `vN+1` create; move `production` / `staging` returning the outgoing version; `{{var}}` compile; `get({name, label})`; `POST /prompts/:name/playground` runs one `AIService` call and returns compiled messages, output, latency, tokens, cost without creating a version; list supports folder filter, search, and `include=usage7d`
|
|
41
48
|
- Files: `ai/src/observability/local/promptStore.ts`, `ai/src/observability/routes/prompts.ts`, tests
|
|
42
|
-
- Blocked by:
|
|
43
|
-
- Docs: `docs/how-to/observe-llm-calls.md` — create
|
|
44
|
-
- Acceptance: v1 unchanged after v2; get production returns
|
|
49
|
+
- Blocked by: 1.4, 1.1
|
|
50
|
+
- Docs: `docs/how-to/observe-llm-calls.md` — create a prompt and pin production
|
|
51
|
+
- Acceptance: v1 unchanged after v2; `get production` returns the labelled version; playground creates no version; usage rollup returns calls/cost for the last 7 days and `—` for a prompt with no production label
|
|
45
52
|
|
|
46
|
-
- [
|
|
47
|
-
- Delivers: persist fan-in
|
|
53
|
+
- [x] **Task 1.6**: Local `TraceSink` / `ScoreSink` + trace HTTP
|
|
54
|
+
- Delivers: persist the fan-in; list with time / prompt / status / user / session / `hasScore` / `sensitive` / `flaggedForDataset` filters and pagination; detail returns the span tree with kinds, offsets, durations, per-span I/O and cost; derive `errorSummary` from the first span where `status: "error"` using its `error`; `prompts[]` length drives the `N prompts` display; `POST /traces/:id/scores`
|
|
48
55
|
- Files: `ai/src/observability/local/traceStore.ts`, `ai/src/observability/routes/traces.ts`, tests
|
|
49
|
-
- Blocked by:
|
|
56
|
+
- Blocked by: 1.4, 1.2
|
|
50
57
|
- Docs: `docs/reference/ai.md` routes
|
|
51
|
-
- Acceptance:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- [
|
|
70
|
-
- Delivers:
|
|
71
|
-
- Files: `
|
|
72
|
-
- Blocked by:
|
|
73
|
-
- Docs:
|
|
74
|
-
- Acceptance:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
|
|
58
|
+
- Acceptance: nested spans round-trip; every filter tested; a trace with two prompts reports both; unpriced model returns no `costUsd`
|
|
59
|
+
|
|
60
|
+
- [x] **Task 1.7**: Human evaluator model + templates
|
|
61
|
+
- Delivers: `ObsEvaluator` with `type`, `target`, typed `dimensions[]`, `runModes`, `instructions`, `confidenceAlertBelow`; CRUD routes; `GET /evaluators/templates` returning the seeded set; `human` + `liveSampleRate > 0` rejected on save
|
|
62
|
+
- Files: `ai/src/observability/local/evaluatorStore.ts`, `ai/src/observability/evaluatorTemplates.ts`, `ai/src/observability/routes/evaluators.ts`, tests
|
|
63
|
+
- Blocked by: 1.4
|
|
64
|
+
- Docs: `docs/how-to/ai-feature-development.md` step 4 (human half)
|
|
65
|
+
- Acceptance: numeric / boolean / categorical dimensions persist; human evaluator with live sampling is a 400; a template installs by name
|
|
66
|
+
|
|
67
|
+
- [x] **Task 1.8**: Local review queue API
|
|
68
|
+
- Delivers: `POST /traces/review` enqueues one or many traces against a human evaluator (`reason: "manual"`); `GET /review` by status with counts, oldest-first; `GET /review/:id` returns the item, its evaluator dimensions, and rendered `given` / `wrote` panels built from the prompt version's `variables[]` labels/notes and `outputSchema` + `outputFieldNotes`, falling back to raw keys; `POST /review/:id` submit / skip / assign
|
|
69
|
+
- Files: `ai/src/observability/local/reviewStore.ts`, `ai/src/observability/reviewPanels.ts`, `ai/src/observability/routes/review.ts`, tests
|
|
70
|
+
- Blocked by: 1.6, 1.7
|
|
71
|
+
- Docs: `docs/how-to/observe-llm-calls.md` — the review flow
|
|
72
|
+
- Acceptance: submit writes dimension-keyed scores through the ScoreSink fan-out and leaves pending; skip and assign move status; panels fall back cleanly for a prompt with no labels; a submitted item is absent from pending
|
|
73
|
+
|
|
74
|
+
## 1C — Admin UI and example app
|
|
75
|
+
|
|
76
|
+
- [x] **Task 1.9**: Admin chrome — nav group, breadcrumbs, status chip
|
|
77
|
+
- Delivers: one `AI Observability` sidebar group; `Admin / AI Observability / <Section> / <leaf>` breadcrumbs; status chip reading `GET /status` (local state, primaries); **Review queue hidden when the local plugin is off**; existing `AI Requests` untouched
|
|
78
|
+
- Files: `admin-frontend/src/widgets/aiObservability/shell/*.tsx`, `builtInWidgets.ts`
|
|
79
|
+
- Blocked by: 1.1
|
|
80
|
+
- Docs: `docs/reference/admin-frontend.md` screen names
|
|
81
|
+
- Acceptance: widget tests for chip states and for the hidden review entry
|
|
82
|
+
|
|
83
|
+
- [x] **Task 1.10**: Prompts list + prompt editor + playground screens
|
|
84
|
+
- Delivers: folder rail with counts and search; table with type badge, latest, production, last used, calls 7d, cost 7d and the latest-vs-production tooltips; editor with full-width version history rows (`vN`, attached labels, creation time, selected highlight), Editor/Playground tabs, system + template + variables + schema summary, temperature preset and model hint, **Save as vN+1**, **Set vN as production…** confirm modal naming the outgoing version; playground variable inputs, Run once with a running state, compiled-messages disclosure, output, latency/tokens/cost, and a **Save this run to dataset** button disabled until phase 2
|
|
85
|
+
- Files: `admin-frontend/src/widgets/aiObservability/prompts/*.tsx`
|
|
86
|
+
- Blocked by: 1.5, 1.9
|
|
87
|
+
- Docs: `docs/how-to/ai-feature-development.md` steps 3 and 6
|
|
88
|
+
- Acceptance: widget tests empty/loading/loaded; no in-place save control exists; `verify-ui-changes` walks create → save v2 → set production
|
|
89
|
+
|
|
90
|
+
- [x] **Task 1.11**: Traces list + trace detail screens
|
|
91
|
+
- Delivers: filter bar; row selection with the bulk bar (**Send to review queue**, sensitive-count warning, Clear; Add to dataset disabled until phase 2); status dot, `sensitive` badge, error line, `N prompts`, spans/tokens/cost/latency/scores columns; pagination footer; detail header with actions, left span list (kind badge, indent, duration bar), right span detail with **collapsed** sensitive I/O disclosures, and the scores panel with value + source
|
|
92
|
+
- Files: `admin-frontend/src/widgets/aiObservability/traces/*.tsx`
|
|
93
|
+
- Blocked by: 1.6, 1.8, 1.9
|
|
94
|
+
- Docs: `docs/how-to/ai-feature-development.md` steps 7–8 (observe half)
|
|
95
|
+
- Acceptance: widget tests for empty, error row, sensitive badge, and bulk bar; `verify-ui-changes` selects traces and sends them to the queue
|
|
96
|
+
|
|
97
|
+
- [x] **Task 1.12**: Review queue + review item screens
|
|
98
|
+
- Delivers: tabs Pending / In progress / Done / Skipped with counts; oldest-first table (item, what the AI did, prompt, assignee, waiting, status); **Start reviewing — oldest first**; empty state naming both intake paths; item screen with "Item N of M pending" and prev/next, read-only given/wrote panels (long fields collapsed with word count, structured output as key/value rows, per-field reviewer notes), raw JSON disclosure, dimension-driven score form (slider / Pass-Fail / pills) headed by the evaluator instructions, comment, **Submit & next** / Skip / Assign to me, and a toast reporting the remaining count
|
|
99
|
+
- Files: `admin-frontend/src/widgets/aiObservability/review/*.tsx`
|
|
100
|
+
- Blocked by: 1.8, 1.9
|
|
101
|
+
- Docs: `docs/how-to/ai-feature-development.md` — the review loop
|
|
102
|
+
- Acceptance: widget tests for each dimension control type and the empty state; `verify-ui-changes` reviews an item end to end and reaches "Queue clear"
|
|
103
|
+
|
|
104
|
+
- [x] **Task 1.13**: example-backend registration + seed + phase-1 docs
|
|
105
|
+
- Delivers: `ObservabilityApp` with the local plugin always on; `AI_OBS_PRICE_MAP_JSON` wiring; seed prompt `example-summarize` v1 labelled production in folder `examples`; seed the human evaluator template so the queue is walkable on a fresh DB; `.env.example`
|
|
106
|
+
- Files: `example-backend/src/server.ts`, seed script, `.env.example`, `docs/explanation/ai-observability.md`, `docs/how-to/observe-llm-calls.md`, `docs/how-to/ai-feature-development.md`, `docs/reference/ai.md`
|
|
107
|
+
- Blocked by: 1.10, 1.11, 1.12
|
|
108
|
+
- Docs: (this task)
|
|
109
|
+
- Acceptance: server boots local-only with no Langfuse keys; a stranger can follow the how-to to create a prompt, pin production, trace a call, and review it; `bun run --filter=@terreno/ai compile test lint` green
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
# Phase 2 — Evaluation loop: evaluators, datasets, experiments
|
|
114
|
+
|
|
115
|
+
Ships design screens: Evaluators, Evaluator detail, New evaluator, Datasets, Dataset detail, Experiments, New experiment, Experiment results.
|
|
116
|
+
|
|
117
|
+
- [x] **Task 2.1**: `llm-judge` + `json-assert` evaluator types with the schema contract
|
|
118
|
+
- Delivers: judge execution via `AIService.generateJsonObject` against a registry judge prompt; `json-assert` in-process assertion (`path`, `constraint`, plus the built-in "validate against the prompt version `outputSchema`" mode); **create/save rejects a judge whose prompt output schema omits a required dimension, naming that dimension**; parse failure records an error score rather than throwing to the caller
|
|
119
|
+
- Files: `ai/src/observability/evaluate.ts`, `ai/src/observability/local/evaluatorStore.ts`, `ai/src/observability/evaluatorTemplates.ts`, tests
|
|
120
|
+
- Blocked by: 1.7, 1.5
|
|
121
|
+
- Docs: explanation — dimensions and the schema contract; how-to step 4
|
|
122
|
+
- Acceptance: mismatch rejected with the dimension key in the message; a judge writes exactly its declared keys; `confidenceAlertBelow` defaults to 0.7; templates for correctness / hallucination / helpfulness / toxicity / schema-assert install (`*-human` variants preserved for the review queue)
|
|
123
|
+
|
|
124
|
+
- [ ] **Task 2.2**: Live sampling execution
|
|
125
|
+
- Delivers: after trace export, roll each evaluator's `liveSampleRate` (capped by `AI_OBS_SAMPLE_RATE`) and enqueue the eval on the same `BackgroundTask` path experiments use; never throws from generate
|
|
126
|
+
- Files: `ai/src/observability/liveEval.ts`, `ai/src/observability/observabilityApp.ts`, tests
|
|
127
|
+
- Blocked by: 2.1, 1.2
|
|
128
|
+
- Docs: how-to — the sampling ceiling and its cost
|
|
129
|
+
- Acceptance: rate 0 → no task; rate 100 with ceiling 0 → no task; rate 100 with ceiling 100 → task created
|
|
130
|
+
|
|
131
|
+
- [x] **Task 2.3**: Datasets and items with provenance
|
|
132
|
+
- Delivers: dataset CRUD with `inputSchemaPromptName` + `expectedOutputSchema`; item CRUD with `origin`, `proofread`, `annotatedBy`, `tags`, `outcomeClass`; `POST /datasets/:id/import` (**JSON** array of bare objects or structured rows, plus **CSV** via `Content-Type: text/csv` or `{format: "csv", content}`; validates against the bound input schema); `POST /traces/add-to-dataset` single and bulk copying the I/O snapshot with `sourceTraceId`; **a `sensitive` trace always lands `proofread: false`**
|
|
133
|
+
- Files: `ai/src/observability/local/datasetStore.ts`, `ai/src/observability/routes/datasets.ts`, tests
|
|
134
|
+
- Blocked by: 1.6
|
|
135
|
+
- Docs: how-to steps 1–2 and 8
|
|
136
|
+
- Acceptance: sensitive rule enforced; item keeps `sourceTraceId`; deleting an item does not touch the trace; import rejects schema-invalid rows with the failing path
|
|
137
|
+
|
|
138
|
+
- [ ] **Task 2.4**: Synthetic dataset generation + playground save-to-dataset
|
|
139
|
+
- Delivers: `POST /datasets/:id/generate` drafting items via `AIService` and a named generator prompt, `origin: "synthetic"`, `proofread: false`; the phase-1 playground **Save this run to dataset** button wired
|
|
140
|
+
- Files: generate route, `ai/src/service/prompts.ts` generator constant, `admin-frontend/.../prompts/*`, tests
|
|
141
|
+
- Blocked by: 2.3, 1.5
|
|
142
|
+
- Docs: how-to step 1 (synthetic)
|
|
143
|
+
- Acceptance: generated items are excluded from a default experiment run; a playground run lands as an unproofread item with its input and output
|
|
144
|
+
|
|
145
|
+
- [x] **Task 2.5**: Experiment runner, gates, and gate-blocked promote
|
|
146
|
+
- Delivers: `POST /experiments/estimate` (generations, USD, wall clock); `POST /experiments` comparing 2–3 versions on a dataset with optional `modelOverride`, always via `BackgroundTask` locally; per-item results per version; aggregates vs `thresholds[]` (defaulting to `SOP_DEFAULT_THRESHOLDS`); outlier and low-confidence item ids; failed items ordered first; `POST /experiments/:id/promote` returning **409 while any gate fails**
|
|
147
|
+
- Files: `ai/src/observability/local/experimentRunner.ts`, `ai/src/observability/routes/experiments.ts`, tests
|
|
148
|
+
- Blocked by: 2.3, 2.1, 1.5
|
|
149
|
+
- Docs: how-to steps 5–6
|
|
150
|
+
- Acceptance: a 1-item dataset still uses `BackgroundTask`; gate pass/fail computed for boolean (`trueRate`) and numeric (`mean`) dimensions; promote 409s on a failing gate and moves the label when gates pass; unproofread items excluded unless `includeUnproofread`
|
|
151
|
+
|
|
152
|
+
- [ ] **Task 2.6**: Authenticated trace feedback
|
|
153
|
+
- Delivers: `POST /traces/:id/feedback` — thumbs / outcome class / flag-for-dataset; `source: "user-feedback"` score; owner (`trace.userId`) or admin; sets `flaggedForDataset`; optional review enqueue with `reason: "feedback"`
|
|
154
|
+
- Files: `ai/src/observability/routes/feedback.ts`, tests
|
|
155
|
+
- Blocked by: 1.6, 1.8
|
|
156
|
+
- Docs: how-to step 7; `docs/reference/ai.md`
|
|
157
|
+
- Acceptance: owner thumbs-down + flag succeeds; a stranger gets 403; an admin succeeds; the trace list `flaggedForDataset` filter finds it
|
|
158
|
+
|
|
159
|
+
- [ ] **Task 2.7**: Evaluator screens (list, detail, new)
|
|
160
|
+
- Delivers: list with type badge, dimension summary, target, run-mode chips and the footnote; detail with the dimensions table, type-specific panel (judge prompt link + model + schema-match check, assertion path/constraint, reviewer instructions), run modes with the live-sampling slider and cost warning, and the **Used by** list with 30-day runs and cost; new-evaluator screen with type/target pickers, dimension builder, and the **inline schema mismatch error naming the undeclared dimension**
|
|
161
|
+
- Files: `admin-frontend/src/widgets/aiObservability/evaluators/*.tsx`
|
|
162
|
+
- Blocked by: 2.1, 1.9
|
|
163
|
+
- Docs: `docs/reference/admin-frontend.md`
|
|
164
|
+
- Acceptance: widget tests for each type's panel and for the mismatch error; `verify-ui-changes` creates a judge evaluator
|
|
165
|
+
|
|
166
|
+
- [ ] **Task 2.8**: Dataset screens (list, detail)
|
|
167
|
+
- Delivers: list with the human/auto legend, provenance bar, source and updated columns; detail with human/auto counts, the input-schema binding line, Add item, Run experiment, tabs All / Human / Auto / **Needs review** (red count), and the items table with attribution lines and trace links
|
|
168
|
+
- Files: `admin-frontend/src/widgets/aiObservability/datasets/*.tsx`
|
|
169
|
+
- Blocked by: 2.3, 2.4, 1.9
|
|
170
|
+
- Docs: `docs/reference/admin-frontend.md`
|
|
171
|
+
- Acceptance: widget tests for each tab and the empty dataset; `verify-ui-changes` bulk-adds traces and sees them under Needs review
|
|
172
|
+
|
|
173
|
+
- [ ] **Task 2.9**: Experiment screens (list, wizard, results)
|
|
174
|
+
- Delivers: list with status badge, running progress bar and cost, plus the BackgroundTask/Langfuse footnote; 4-step wizard with a completed-step rail, dataset picker showing counts and schema match, version checkboxes tagged latest/production/superseded, evaluator checkboxes with per-item cost, and a Review & run step showing the estimate; results with gate tiles, "N gates failing", the **Promote blocked** state naming the failing gate, the outliers list with reason chips, the side-by-side table with failed rows floated and highlighted, and the promote confirm modal
|
|
175
|
+
- Files: `admin-frontend/src/widgets/aiObservability/experiments/*.tsx`
|
|
176
|
+
- Blocked by: 2.5, 2.8, 1.9
|
|
177
|
+
- Docs: how-to steps 5–6
|
|
178
|
+
- Acceptance: widget tests for running progress, gate-fail results, and blocked promote; `verify-ui-changes` runs an experiment and promotes a passing version
|
|
179
|
+
|
|
180
|
+
- [ ] **Task 2.10**: Phase-2 example seeds + docs
|
|
181
|
+
- Delivers: seed judge/assert evaluator templates and dataset `example-gold` with an input-schema binding; docs updated for evaluators, datasets, experiments, feedback, and `AI_OBS_SAMPLE_RATE`
|
|
182
|
+
- Files: `example-backend` seed, docs listed in the IP, `.rulesync/skills/ai-prompt-governance/SKILL.md` + `bun run skills:sync`
|
|
183
|
+
- Blocked by: 2.7, 2.8, 2.9, 2.6
|
|
184
|
+
- Docs: (this task)
|
|
185
|
+
- Acceptance: the full SOP loop is runnable from `docs/how-to/ai-feature-development.md` without Langfuse
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
# Phase 3 — Analytics and pluggable backends
|
|
190
|
+
|
|
191
|
+
Ships design screens: Sessions, Session timeline, Users, User AI profile, Costs, plus sink health and Open in Langfuse.
|
|
192
|
+
|
|
193
|
+
- [ ] **Task 3.1**: Session, user, and cost aggregates + screens
|
|
194
|
+
- Delivers: `GET /sessions`, `/sessions/:id`, `/users`, `/users/:id`, `/costs`; Sessions list with the Traces/Sessions toggle and the timeline detail; Users list subtitled "AI cost and usage per user — not the user CRUD table" and the profile with masked email, **no prompt content**, and 7d/30d/all-time rollups; Costs with the KPI row, by-model table showing **tokens only** for unpriced models plus the "no $0 lie" note, and the by-prompt table
|
|
195
|
+
- Files: `ai/src/observability/routes/analytics.ts`, `ai/src/observability/local/traceStore.ts`, `admin-frontend/src/widgets/aiObservability/analytics/*.tsx`, tests
|
|
196
|
+
- Blocked by: 1.6, 1.9
|
|
197
|
+
- Docs: `docs/reference/ai.md` routes; `docs/reference/admin-frontend.md`
|
|
198
|
+
- Acceptance: an unpriced model renders `tokens only` and never `$0`; no prompt or output text appears in any users response; widget tests for each screen
|
|
199
|
+
|
|
200
|
+
- [ ] **Task 3.2**: Langfuse `TraceSink` + `ScoreSink`
|
|
201
|
+
- Delivers: adapters wrapping the existing `getLangfuseClient()`; **no second OTel SDK**; best-effort with logged failures
|
|
202
|
+
- Files: `ai/src/observability/langfuse/traceAdapter.ts`, tests with a mocked client
|
|
97
203
|
- Blocked by: 1.2
|
|
98
|
-
- Docs: explanation — LangfuseApp vs ObservabilityApp
|
|
99
|
-
- Acceptance: generate
|
|
204
|
+
- Docs: explanation — `LangfuseApp` vs `ObservabilityApp`
|
|
205
|
+
- Acceptance: one generate dual-writes; a throwing Langfuse mock leaves the local row intact and the caller unaffected
|
|
100
206
|
|
|
101
|
-
- [ ] **Task
|
|
102
|
-
- Delivers:
|
|
207
|
+
- [ ] **Task 3.3**: Langfuse `PromptRegistry` / `DatasetStore` / `ExperimentRunner` + primaries
|
|
208
|
+
- Delivers: `AI_OBS_*_PRIMARY` env wiring; Terreno HTTP facade delegating to the existing Langfuse admin routes; **Open in Langfuse** deep-link bases on `GET /status`
|
|
103
209
|
- Files: `ai/src/observability/langfuse/*.ts`, tests
|
|
104
|
-
- Blocked by:
|
|
105
|
-
- Docs: how-to —
|
|
106
|
-
- Acceptance: prompts primary langfuse
|
|
210
|
+
- Blocked by: 3.2, 1.5, 2.5
|
|
211
|
+
- Docs: how-to — switching primaries by env
|
|
212
|
+
- Acceptance: prompts primary `langfuse` reads through the client; experiments primary `langfuse` starts no `BackgroundTask`; the illegal mixes still fail boot
|
|
107
213
|
|
|
108
|
-
- [ ] **Task
|
|
109
|
-
- Delivers: OTLP
|
|
110
|
-
- Files: `ai/src/observability/otel/otelTraceSink.ts`, tests with mock exporter
|
|
214
|
+
- [ ] **Task 3.4**: `OtelTraceSink` (OpenInference)
|
|
215
|
+
- Delivers: OTLP exporter behind `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`; `openinference.span.kind` set from the span kind; model and token attributes
|
|
216
|
+
- Files: `ai/src/observability/otel/otelTraceSink.ts`, tests with a mock exporter
|
|
111
217
|
- Blocked by: 1.2
|
|
112
|
-
- Docs: how-to — Phoenix
|
|
113
|
-
- Acceptance: mock exporter receives span with model
|
|
218
|
+
- Docs: how-to — pointing at Phoenix or a generic collector
|
|
219
|
+
- Acceptance: the mock exporter receives an `LLM` span with model and token attributes
|
|
114
220
|
|
|
115
|
-
- [ ] **Task 5
|
|
116
|
-
- Delivers:
|
|
117
|
-
- Files: `
|
|
118
|
-
- Blocked by:
|
|
119
|
-
- Docs:
|
|
120
|
-
- Acceptance: widget tests
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
-
|
|
221
|
+
- [ ] **Task 3.5**: Sink health chip, example wiring, and phase-3 docs
|
|
222
|
+
- Delivers: last-error-per-sink on `GET /status`; the chip renders Langfuse health and active primaries and shows **Open in Langfuse** only when that plugin is registered; example-backend adds the Langfuse and OTel sinks conditionally; docs and the `terreno-langfuse-integration.md` pointer finalized
|
|
223
|
+
- Files: `ai/src/observability/routes/status.ts`, `admin-frontend/.../shell/*.tsx`, `example-backend/src/server.ts`, docs
|
|
224
|
+
- Blocked by: 3.1, 3.3, 3.4
|
|
225
|
+
- Docs: (this task)
|
|
226
|
+
- Acceptance: chip states covered by widget tests; a Langfuse-less boot shows no deep link; `verify-ui-changes` evidence attached
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
# Phase 4 — Domain-neutral RBAC and prompt relationship hub
|
|
231
|
+
|
|
232
|
+
The user approved this phase on 2026-10-02. It adds no domain-specific vocabulary or
|
|
233
|
+
in-app approval workflow. Supporting skills: `building-admin-interfaces`,
|
|
234
|
+
`terreno-backend-api`, `terreno-ui`, `terreno-data-fetching`, `mongoose-schema-safety`,
|
|
235
|
+
`update-docs`, and `verify-ui-changes`.
|
|
236
|
+
|
|
237
|
+
- [x] **Task 4.1**: Observability RBAC vocabulary, compatibility grants, and route enforcement
|
|
238
|
+
- Delivers: resources `aiPrompt`, `aiTrace`, `aiReview`, `aiDataset`, `aiExperiment`, `aiEvaluator` with the actions approved in Q62/Q63; statement descriptions; full seeded Admin grants, read-only Auditor expansion, Super Admin wildcard; existing stored Admin roles gain only missing observability grants; optional `accessControl` on `ObservabilityApp`; every route maps to one resource/action while legacy admin fallback remains
|
|
239
|
+
- Files: `api/src/rbac/statements.ts`, `api/src/rbac/roleModel.ts`, `ai/src/observability/observabilityApp.ts`, `ai/src/observability/routes/*.ts`, route/RBAC tests, public exports as needed
|
|
240
|
+
- Blocked by: none
|
|
241
|
+
- Docs: `docs/reference/api.md`, `docs/reference/ai.md`, `docs/explanation/ai-observability.md`
|
|
242
|
+
- Acceptance: catalog and role tests prove exact actions; a read-only RBAC caller can GET but receives 403 for writes; denied mutations fail even when called directly; legacy admin tests remain green
|
|
243
|
+
|
|
244
|
+
- [x] **Task 4.2**: Prompt descriptions and prompt-scoped relationship APIs
|
|
245
|
+
- Delivers: optional prompt `description` in Mongo/types/create/detail/list; trace list accepts prompt version in addition to name; experiment list accepts `promptName`; prompt detail API composes bounded recent traces and experiments for the selected prompt without client-side full-list scans
|
|
246
|
+
- Files: prompt/trace/experiment models, stores, routes, types, tests
|
|
247
|
+
- Blocked by: 4.1
|
|
248
|
+
- Docs: `docs/reference/ai.md`, `docs/explanation/ai-observability.md`
|
|
249
|
+
- Acceptance: description round-trips; prompt v2 filtering excludes v1-only traces; prompt detail relationships contain only the named prompt's traces and experiments; old prompts without descriptions still load
|
|
250
|
+
|
|
251
|
+
- [x] **Task 4.3**: Caller-filtered navigation and read-only prompt hub
|
|
252
|
+
- Delivers: each custom screen declares its read/list permission; unauthorized screens disappear from `/admin/config`; prompt detail renders Overview, Versions, Traces, and Experiments with description/folder/tags; controls are hidden independently for denied `update`, `promote`, and `playground`; direct API calls remain protected by Task 4.1
|
|
253
|
+
- Files: `ai/src/observability/adminScreens.ts`, admin contribution tests, `admin-frontend/src/widgets/aiObservability/prompts/*`, permission helpers/tests
|
|
254
|
+
- Blocked by: 4.1, 4.2
|
|
255
|
+
- Docs: `docs/reference/admin-frontend.md`, `docs/how-to/ai-feature-development.md`
|
|
256
|
+
- Acceptance: admin-config tests cover mixed grants; widget tests prove a read-only prompt hub shows relationships and no write controls; an operator sees all controls; `verify-ui-changes` captures both roles
|
|
257
|
+
|
|
258
|
+
- [x] **Task 4.4**: Phase-4 integration and documentation gate
|
|
259
|
+
- Delivers: `ObservabilityApp({accessControl: access})` in example-backend; example roles demonstrating read-only and operator composition; integration tests for admin vs viewer HTTP; all architecture/reference/how-to pages aligned; generated SDK and `verify-ui-changes` walkthrough when the parent closes UI gates
|
|
260
|
+
- Files: `example-backend` access/seed files, `observabilityRbac.integration.test.ts`, docs named above, generated SDK when required
|
|
261
|
+
- Blocked by: 4.3
|
|
133
262
|
- Docs: (this task)
|
|
134
|
-
- Acceptance: a
|
|
263
|
+
- Acceptance: example boots with existing admin access intact; a consumer can configure a domain-neutral read-only role from docs; example-backend RBAC unit + integration tests, compile, and lint pass; parent runs SDK regen and mandatory UI walkthrough before checking this task complete
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Tasks: Durable agent harness
|
|
2
|
+
|
|
3
|
+
IP: [durable-agent-harness.md](../implementationPlans/durable-agent-harness.md)
|
|
4
|
+
**Feature profile:** false (full IP)
|
|
5
|
+
**Base branch:** `cursor/ai-observability-ip-64ca` (rebase as it lands)
|
|
6
|
+
**First PR scope:** Phases 1–2. Phases 3–5 ship in follow-up PRs.
|
|
7
|
+
|
|
8
|
+
## Resume here (paused 2026-10-03)
|
|
9
|
+
|
|
10
|
+
The Pick ⇄ Roast loop is paused after Phase 1. Tasks 1.1–1.12 are Roast-verified, one commit each.
|
|
11
|
+
|
|
12
|
+
1. **Sync the base.** `git fetch origin && git merge origin/cursor/ai-observability-ip-64ca`.
|
|
13
|
+
At pause it was 5+ commits ahead, including a master merge and "Preserve AI observability
|
|
14
|
+
across chat turn refactor", which touches `ai/`. Re-run `cd ai && bun run test:ci` after the merge.
|
|
15
|
+
2. **Resume the loop** with `/terreno:pick-roast-loop` (or `terreno-pick-roast-loop`) at
|
|
16
|
+
**Task 2.0**, then 2.1, then 2.2. Each task is Pick, then an independent Roast, then one commit.
|
|
17
|
+
3. **Brew when Phase 2 is done.** Run the full independent branch review that was deferred at
|
|
18
|
+
pause, take the PR out of draft, then Taste.
|
|
19
|
+
|
|
20
|
+
Known environment notes from Phase 1:
|
|
21
|
+
|
|
22
|
+
- `/tmp` in the cloud VM is a 1 GB tmpfs, and in-memory replica sets use about 200 MB each. Run Mongo
|
|
23
|
+
test suites sequentially and delete leftover `/tmp/mongo-mem-*` directories after killed runs.
|
|
24
|
+
- `example-backend` seed-heavy tests can hit Bun's 5 s default timeout under load. This also
|
|
25
|
+
happens on the base branch.
|
|
26
|
+
- In the root `bun run lint`, `check:explicit-any:lint` requires a `// noExplicitAny: <reason>` line
|
|
27
|
+
above every `biome-ignore … noExplicitAny`.
|
|
28
|
+
|
|
29
|
+
Every task: bun tests in `ai/` against the in-memory replica set from `@terreno/test`; real
|
|
30
|
+
models are replaced by the mock model pattern in `.claude/rules/ai/00-ai.md`. Docs pages are
|
|
31
|
+
created in 1.1 and extended by every later task — never deferred.
|
|
32
|
+
|
|
33
|
+
## Phase 1 — Durable clinical tracer
|
|
34
|
+
|
|
35
|
+
- [x] **Task 1.1**: Harness subpath + `defineTask` + transactional checkpoint/span (tracer)
|
|
36
|
+
- Delivers: `@terreno/ai/harness` export; `HarnessTask` model; `defineTask` with phases; `InProcessRunner` (no lease yet) runs a two-phase task to `completed`; `rt.commit` writes task + `ObsSpan` + root `ObsTrace` in one transaction; `Harness.open` throws without local observability plugin or without a replica set
|
|
37
|
+
- Files: `ai/package.json`, `ai/src/harness/harness.ts`, `defineTask.ts`, `runtime.ts`, `commit.ts`, `runners/inProcessRunner.ts`, `models/harnessTask.ts`, `ai/src/types/harness.ts`, tests
|
|
38
|
+
- Blocked by: none
|
|
39
|
+
- Skills: `terreno-backend-api`, `mongoose-schema-safety`, `backend-test-env`
|
|
40
|
+
- Docs: create `docs/explanation/durable-agent-harness.md` (concepts table), `docs/reference/ai-harness.md` (`defineTask`, `Harness.open`, `rt.commit`), link from `docs/reference/ai.md`
|
|
41
|
+
- Acceptance: AC1 — bun test asserts one CHAIN span per phase; forced throw inside the transaction leaves neither task update nor span
|
|
42
|
+
|
|
43
|
+
- [x] **Task 1.2**: Owner lease, crash resume, replay semantics
|
|
44
|
+
- Delivers: `HarnessOwner` lease + heartbeat; standby second process; task leases with fencing token; resume on `start()`; per-phase `replay`; `interrupted` status; `harness.resolveInterrupted(taskId, action)`
|
|
45
|
+
- Files: `runners/inProcessRunner.ts`, `models/harnessOwner.ts`, `runtime.ts`, `commit.ts`, tests
|
|
46
|
+
- Blocked by: 1.1
|
|
47
|
+
- Skills: `terreno-backend-api`, `backend-test-env`
|
|
48
|
+
- Docs: reference "Replay and interruption" table; explanation section on leases
|
|
49
|
+
- Acceptance: AC2 + AC3 (phase rows) — simulate death by abandoning a harness mid-phase with an expired lease; new harness resumes safe phase and parks `never` phase as `interrupted`; stale-token commit rejected
|
|
50
|
+
|
|
51
|
+
- [x] **Task 1.3**: Retries, failure, abort + ownership tree
|
|
52
|
+
- Delivers: task `retry` policy with backoff+jitter (Luxon), `failed` after `maxAttempts`; `rt.createTask` child ownership; `harness.abort` bottom-up with `abort` handlers; `rt.waitForTasks` (`all` / `failFast`); `background` flag
|
|
53
|
+
- Files: `runtime.ts`, `retryBackoff.ts`, `ownership.ts`, tests
|
|
54
|
+
- Blocked by: 1.2
|
|
55
|
+
- Skills: `terreno-backend-api`
|
|
56
|
+
- Docs: reference retry/abort/ownership; explanation ownership tree
|
|
57
|
+
- Acceptance: AC4 (phase retries) + AC5 — ordering assertion on abort handlers; `failFast` aborts siblings
|
|
58
|
+
|
|
59
|
+
- [x] **Task 1.4**: Agents, conversations, tools, model-call resilience
|
|
60
|
+
- Delivers: `defineAgent`, `defineTool` (zod params, `replay`), `HarnessConversation` + `HarnessMessage`; built-in `terreno.agent.turn` task (`request` ⇄ `tools` phases, `maxSteps`); tools run as child tasks; interrupted `never` tools reported to the model; model calls retry 429/5xx then `fallbackModels`; interrupted model requests re-requested; LLM/TOOL spans with usage/cost; `ExecutionEnv` interface (types only) on `rt.env`
|
|
61
|
+
- Files: `defineAgent.ts`, `defineTool.ts`, `agentLoop.ts`, `modelCall.ts`, `executionEnv.ts`, `models/harnessConversation.ts`, `models/harnessMessage.ts`, tests
|
|
62
|
+
- Blocked by: 1.3
|
|
63
|
+
- Skills: `terreno-backend-api`, `claude-api` (model ids), `ai-prompt-governance`
|
|
64
|
+
- Docs: reference agents/tools/model resilience; explanation agent loop
|
|
65
|
+
- Acceptance: AC3 (tool rows) + AC4 (model rows) — mock model returns 503 twice then succeeds; fallback used after exhaustion; span tree CHAIN→LLM→TOOL
|
|
66
|
+
|
|
67
|
+
- [x] **Task 1.5**: Subagents
|
|
68
|
+
- Delivers: `rt.runAgent(agent, {input, output})` creates a task-owned child conversation, idempotent on resume (finds existing by `ownerTaskId`), returns parsed structured output; AGENT span nests the child's spans
|
|
69
|
+
- Files: `runtime.ts`, `agentLoop.ts`, tests
|
|
70
|
+
- Blocked by: 1.4
|
|
71
|
+
- Skills: `terreno-backend-api`
|
|
72
|
+
- Docs: reference `runAgent`; explanation subagents
|
|
73
|
+
- Acceptance: bun test — crash between child creation and completion resumes the same child conversation (no duplicate); abort of parent aborts child
|
|
74
|
+
|
|
75
|
+
- [x] **Task 1.6**: Extensions, hooks, wraps, memos
|
|
76
|
+
- Delivers: `defineExtension({name, sections, tools, hooks, wraps})`; `section` rebuilt per request and recorded in transcript; hooks `beforeModelRequest` / `beforeTool` (rewrite or `{block}`) / `afterTool`; `wrapTool`; `rt.memo(key, value?)` via `HarnessMemo`
|
|
77
|
+
- Files: `extensions.ts`, `models/harnessMemo.ts`, `agentLoop.ts`, tests
|
|
78
|
+
- Blocked by: 1.4
|
|
79
|
+
- Skills: `terreno-backend-api`
|
|
80
|
+
- Docs: reference extensions/hooks/memos
|
|
81
|
+
- Acceptance: AC6 — blocked tool result reaches the model; memo value survives a new harness instance
|
|
82
|
+
|
|
83
|
+
- [x] **Task 1.7**: Events, waits, sleep
|
|
84
|
+
- Delivers: `rt.waitFor(event, {timeout})`, `rt.sleep(duration)`, `harness.sendEvent(taskId, event, payload)`; `waiting` status; runner wakes on event or `timeoutAt`; timeout resolves `undefined`
|
|
85
|
+
- Files: `runtime.ts`, `runners/inProcessRunner.ts`, tests
|
|
86
|
+
- Blocked by: 1.3
|
|
87
|
+
- Skills: `terreno-backend-api`
|
|
88
|
+
- Docs: reference waits/events
|
|
89
|
+
- Acceptance: bun test with frozen Luxon clock — event before timeout resumes with payload; timeout path; event sent while owner is down is delivered after restart
|
|
90
|
+
|
|
91
|
+
- [x] **Task 1.8**: Approvals backend
|
|
92
|
+
- Delivers: `HarnessApproval` model; `rt.approval(key, {title, summary, payload, timeout, notify?})` with approvers declared per key on `defineTask({approvals})`; `approvalGate({tools, approvers})` extension (memo-backed); `HarnessApp` routes `GET /harness/approvals` (filtered by `approvers`), `approve` / `reject` instanceActions with reason; decision span with `decidedBy`
|
|
93
|
+
- Files: `approvals.ts`, `models/harnessApproval.ts`, `harnessApp.ts`, `routes/approvals.ts`, tests
|
|
94
|
+
- Blocked by: 1.6, 1.7
|
|
95
|
+
- Skills: `terreno-backend-api`, `model-router-actions`
|
|
96
|
+
- Docs: reference approvals + routes; how-to section "Require human approval"
|
|
97
|
+
- Acceptance: AC7 — supertest: unpermitted user gets 403 and an empty list; permitted user approves; span `output.decidedBy` equals user id; reject without reason → 400
|
|
98
|
+
|
|
99
|
+
- [x] **Task 1.9**: SSE event stream + submit
|
|
100
|
+
- Delivers: `HarnessEvent` log (coalesced deltas with TTL, committed events permanent); `rt.output`; `GET /harness/conversations/:id/events` + `/harness/tasks/:id/events` SSE with `Last-Event-ID` replay then change-stream tail; `submit` instanceAction (`requestId` idempotent, `whenBusy: queue | steer`)
|
|
101
|
+
- Files: `events.ts`, `models/harnessEvent.ts`, `routes/events.ts`, `routes/conversations.ts`, tests
|
|
102
|
+
- Blocked by: 1.4
|
|
103
|
+
- Skills: `terreno-backend-api`, `model-router-actions`
|
|
104
|
+
- Docs: reference SSE contract (event types, ids, reconnect)
|
|
105
|
+
- Acceptance: AC9 — two `HarnessApp` instances on one replica set; client disconnects mid-stream, reconnects to the other with `Last-Event-ID`, receives each missed event exactly once
|
|
106
|
+
|
|
107
|
+
- [x] **Task 1.10**: Version pinning
|
|
108
|
+
- Delivers: runs resume only on exact `name@version`; `Harness.start()` throws listing unregistered in-flight versions
|
|
109
|
+
- Files: `harness.ts`, `registry.ts`, tests
|
|
110
|
+
- Blocked by: 1.2
|
|
111
|
+
- Skills: `terreno-backend-api`
|
|
112
|
+
- Docs: reference "Versioning"; how-to "Ship a new task version"
|
|
113
|
+
- Acceptance: AC10
|
|
114
|
+
|
|
115
|
+
- [x] **Task 1.11**: Approvals inbox (admin-frontend)
|
|
116
|
+
- Delivers: `admin-frontend/src/widgets/harness/HarnessApprovalInbox.tsx` (list, detail, approve/reject with reason, loading/empty/error states) via `adminRequest`; admin custom screen `harness-approvals` (group "AI Harness"); standalone export; example-frontend admin route; `bun run sdk`
|
|
117
|
+
- Files: `admin-frontend/src/widgets/harness/*`, `ai/src/harness/adminScreens.ts`, `example-frontend/app/admin/*`, tests
|
|
118
|
+
- Blocked by: 1.8
|
|
119
|
+
- Skills: `building-admin-interfaces`, `verify-ui-changes`
|
|
120
|
+
- Docs: `docs/reference/admin-frontend.md` inbox section
|
|
121
|
+
- Acceptance: AC8 — component tests + Playwright run on web: approve one item, reject one; screenshots in `/opt/cursor/artifacts/`
|
|
122
|
+
|
|
123
|
+
- [x] **Task 1.12**: Clinical tracer in example-backend + how-to
|
|
124
|
+
- Delivers: `clinic.intakeSummary` (fetch → `runAgent` summary → approval → EHR write with `replay: "never"`) against a fake EHR model; `HarnessApp` registered in `server.ts`; admin action to start a run; e2e script kills the backend during `summarize`, restarts, approves in the inbox, asserts `filed` and the span tree in Traces
|
|
125
|
+
- Files: `example-backend/src/harness/*`, `example-backend/src/server.ts`, `example-frontend/e2e/harness-intake.spec.ts`
|
|
126
|
+
- Blocked by: 1.5, 1.9, 1.10, 1.11
|
|
127
|
+
- Skills: `verify-ui-changes`, `backend-test-env`
|
|
128
|
+
- Docs: create `docs/how-to/build-a-durable-workflow.md` using this workflow as the one example
|
|
129
|
+
- Acceptance: AC2 (e2e) — Playwright spec passes; screenshots of the inbox and the trace waterfall attached
|
|
130
|
+
- Shipped as two proofs: the kill/restart runs in `example-backend/src/harness/clinicalIntake.crash.test.ts` (spawns the real API process, SIGKILLs it mid-`summarize`, restarts); `harness-intake.spec.ts` approves in the inbox and asserts `filed` and the span tree
|
|
131
|
+
|
|
132
|
+
## Phase 2 — Multi-instance
|
|
133
|
+
|
|
134
|
+
- [ ] **Task 2.0**: `InProcessRunner` concurrency
|
|
135
|
+
- Delivers: `new InProcessRunner({concurrency})` (default 8) runs up to N claimed tasks at once, each with its own lease and heartbeat; a slow phase no longer delays other tasks' wakes; `stop()` waits for all in-flight phases
|
|
136
|
+
- Files: `ai/src/harness/runners/inProcessRunner.ts`, tests
|
|
137
|
+
- Blocked by: 1.12
|
|
138
|
+
- Skills: `terreno-backend-api`
|
|
139
|
+
- Docs: reference "Runners" (concurrency option, ordering guarantees)
|
|
140
|
+
- Acceptance: bun test — a phase blocked on a gate does not prevent a second task from completing; never more than N concurrent; no task runs twice concurrently
|
|
141
|
+
|
|
142
|
+
- [ ] **Task 2.1**: `JobsRunner`
|
|
143
|
+
- Delivers: `@terreno/ai/harness/jobsRunner` with optional peer `@terreno/jobs`; job `terreno.harness.phase` keyed `taskId:phase:attempt`; harness task lease + heartbeat is the authority; importing the root `@terreno/ai` never loads jobs
|
|
144
|
+
- Files: `ai/src/harness/runners/jobsRunner.ts`, `ai/package.json`, tests
|
|
145
|
+
- Blocked by: 2.0
|
|
146
|
+
- Skills: `terreno-backend-api`
|
|
147
|
+
- Docs: reference "Runners"; how-to "Run on multiple instances"
|
|
148
|
+
- Acceptance: AC11 — two workers race one phase; exactly one commit; stale owner rejected; knip clean without jobs installed
|
|
149
|
+
|
|
150
|
+
- [ ] **Task 2.2**: Runs admin screen
|
|
151
|
+
- Delivers: task list/tree with status filters, abort, resolve-interrupted (retry/abort/complete + reason), link to trace
|
|
152
|
+
- Files: `admin-frontend/src/widgets/harness/HarnessRuns*.tsx`, `ai/src/harness/routes/tasks.ts`
|
|
153
|
+
- Blocked by: 2.1
|
|
154
|
+
- Skills: `building-admin-interfaces`, `verify-ui-changes`
|
|
155
|
+
- Docs: admin-frontend reference
|
|
156
|
+
- Acceptance: Playwright resolves an interrupted EHR write; screenshots
|
|
157
|
+
|
|
158
|
+
## Phase 3 — Long context
|
|
159
|
+
|
|
160
|
+
- [ ] **Task 3.1**: Background compaction + manual `compact(instructions)`; originals stay searchable
|
|
161
|
+
- [ ] **Task 3.2**: `reset` / handoff tool control
|
|
162
|
+
- [ ] **Task 3.3**: Conversation forks at any message (parent history by reference)
|
|
163
|
+
- [ ] **Task 3.4**: `defineDoc` typed documents (`scope`, `history: rewindable`, `fork: asOf`), committed atomically with transcripts
|
|
164
|
+
|
|
165
|
+
Each 3.x task: Blocked by 1.12; Docs: reference + explanation sections; Acceptance: bun tests (10k-message conversation fits budget; fork isolation; doc rewind).
|
|
166
|
+
|
|
167
|
+
## Phase 4 — Durable chat + multiplayer
|
|
168
|
+
|
|
169
|
+
- [x] **Task 4.0**: Human gate — `GptHistory` migration strategy → **one-shot migration** script at deploy (decided 2026-10-02)
|
|
170
|
+
- [ ] **Task 4.1**: `/gpt/prompt` on the harness; history mapping
|
|
171
|
+
- [ ] **Task 4.2**: Steering, foreground/background abort, `viewState` over SSE
|
|
172
|
+
- [ ] **Task 4.3**: rtk `useHarnessConversation` SSE hook + `GPTChat` adapter in example-frontend
|
|
173
|
+
|
|
174
|
+
Acceptance: kill backend mid-chat, reconnect resumes; two browsers see the same view (Playwright, screenshots).
|
|
175
|
+
|
|
176
|
+
## Phase 5 — Coding agents + malleability
|
|
177
|
+
|
|
178
|
+
- [x] **Task 5.0**: Human gate — sandbox provider → **Cloud Run Jobs container** (decided 2026-10-02)
|
|
179
|
+
- [ ] **Task 5.1**: `NodeExecutionEnv` + `CodingTools` (read/write/edit/bash with replay classes)
|
|
180
|
+
- [ ] **Task 5.2**: Remote sandbox adapter
|
|
181
|
+
- [ ] **Task 5.3**: Registry hot-replace (running calls finish on old code)
|
|
182
|
+
|
|
183
|
+
Acceptance: a coding agent fixes a failing test in a fixture repo after a forced crash.
|