@terreno/mcp 57.9.0 → 57.11.0

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.
Files changed (111) hide show
  1. package/dist/docs/upgrades/57.10.1.md +54 -0
  2. package/dist/docs/upgrades/57.11.0.md +80 -0
  3. package/dist/docs/versioned/next/README.md +1 -0
  4. package/dist/docs/versioned/next/explanation/README.md +4 -0
  5. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
  6. package/dist/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
  7. package/dist/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
  8. package/dist/docs/versioned/next/explanation/ai-observability.md +101 -0
  9. package/dist/docs/versioned/next/explanation/durable-agent-harness.md +544 -0
  10. package/dist/docs/versioned/next/explanation/example-coverage.md +3 -1
  11. package/dist/docs/versioned/next/explanation/local-first-data.md +1 -0
  12. package/dist/docs/versioned/next/explanation/loop-engineering.md +1 -1
  13. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
  14. package/dist/docs/versioned/next/explanation/typescript-configuration.md +1 -1
  15. package/dist/docs/versioned/next/explanation/versioning-policy.md +1 -1
  16. package/dist/docs/versioned/next/how-to/README.md +7 -0
  17. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  18. package/dist/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  19. package/dist/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  20. package/dist/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  21. package/dist/docs/versioned/next/how-to/call-external-apis.md +1 -1
  22. package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
  23. package/dist/docs/versioned/next/how-to/circleci.md +5 -3
  24. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +31 -1
  25. package/dist/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  26. package/dist/docs/versioned/next/how-to/preview-demo.md +7 -3
  27. package/dist/docs/versioned/next/how-to/run-harness-on-multiple-instances.md +93 -0
  28. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  29. package/dist/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  30. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  31. package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  32. package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  33. package/dist/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  34. package/dist/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  35. package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  36. package/dist/docs/versioned/next/reference/README.md +3 -0
  37. package/dist/docs/versioned/next/reference/admin-config.md +2 -0
  38. package/dist/docs/versioned/next/reference/admin-frontend.md +216 -0
  39. package/dist/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  40. package/dist/docs/versioned/next/reference/ai-harness.md +1974 -0
  41. package/dist/docs/versioned/next/reference/ai.md +251 -25
  42. package/dist/docs/versioned/next/reference/api.md +110 -2
  43. package/dist/docs/versioned/next/reference/blocks.md +178 -0
  44. package/dist/docs/versioned/next/reference/environment-variables.md +2 -1
  45. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +11 -0
  46. package/dist/docs/versioned/next/reference/mcp-server.md +17 -0
  47. package/dist/docs/versioned/next/reference/syncdb.md +156 -6
  48. package/dist/docs/versioned/next/reference/test.md +11 -2
  49. package/dist/docs/versioned/next/reference/ui.md +331 -6
  50. package/dist/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  51. package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  52. package/dist/docs/versioned/next/tasks/ai-observability.md +233 -104
  53. package/dist/docs/versioned/next/tasks/durable-agent-harness.md +185 -0
  54. package/dist/tools.d.ts +4 -0
  55. package/dist/tools.d.ts.map +1 -1
  56. package/dist/tools.js +37 -0
  57. package/dist/tools.js.map +1 -1
  58. package/package.json +3 -2
  59. package/src/docs/upgrades/57.10.1.md +54 -0
  60. package/src/docs/upgrades/57.11.0.md +80 -0
  61. package/src/docs/versioned/next/README.md +1 -0
  62. package/src/docs/versioned/next/explanation/README.md +4 -0
  63. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
  64. package/src/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
  65. package/src/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
  66. package/src/docs/versioned/next/explanation/ai-observability.md +101 -0
  67. package/src/docs/versioned/next/explanation/durable-agent-harness.md +544 -0
  68. package/src/docs/versioned/next/explanation/example-coverage.md +3 -1
  69. package/src/docs/versioned/next/explanation/local-first-data.md +1 -0
  70. package/src/docs/versioned/next/explanation/loop-engineering.md +1 -1
  71. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
  72. package/src/docs/versioned/next/explanation/typescript-configuration.md +1 -1
  73. package/src/docs/versioned/next/explanation/versioning-policy.md +1 -1
  74. package/src/docs/versioned/next/how-to/README.md +7 -0
  75. package/src/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  76. package/src/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  77. package/src/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  78. package/src/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  79. package/src/docs/versioned/next/how-to/call-external-apis.md +1 -1
  80. package/src/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
  81. package/src/docs/versioned/next/how-to/circleci.md +5 -3
  82. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +31 -1
  83. package/src/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  84. package/src/docs/versioned/next/how-to/preview-demo.md +7 -3
  85. package/src/docs/versioned/next/how-to/run-harness-on-multiple-instances.md +93 -0
  86. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  87. package/src/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  88. package/src/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  89. package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  90. package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  91. package/src/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  92. package/src/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  93. package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  94. package/src/docs/versioned/next/reference/README.md +3 -0
  95. package/src/docs/versioned/next/reference/admin-config.md +2 -0
  96. package/src/docs/versioned/next/reference/admin-frontend.md +216 -0
  97. package/src/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  98. package/src/docs/versioned/next/reference/ai-harness.md +1974 -0
  99. package/src/docs/versioned/next/reference/ai.md +251 -25
  100. package/src/docs/versioned/next/reference/api.md +110 -2
  101. package/src/docs/versioned/next/reference/blocks.md +178 -0
  102. package/src/docs/versioned/next/reference/environment-variables.md +2 -1
  103. package/src/docs/versioned/next/reference/lifecycle-plugin.md +11 -0
  104. package/src/docs/versioned/next/reference/mcp-server.md +17 -0
  105. package/src/docs/versioned/next/reference/syncdb.md +156 -6
  106. package/src/docs/versioned/next/reference/test.md +11 -2
  107. package/src/docs/versioned/next/reference/ui.md +331 -6
  108. package/src/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  109. package/src/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  110. package/src/docs/versioned/next/tasks/ai-observability.md +233 -104
  111. package/src/docs/versioned/next/tasks/durable-agent-harness.md +185 -0
@@ -1,134 +1,263 @@
1
- # Tasks: AI observability (Langfuse-light)
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
- ## Phase 1 — Contracts and tracer
8
+ ---
8
9
 
9
- - [ ] **Task 1.1**: Observability types + `ObservabilityApp` config validation
10
- - Delivers: `ObservabilityPlugin` / sinks / `ObservabilityControlConfig`; boot throws on `experiments.primary !== datasets.primary` or `reviewQueue` ≠ `local`; throws if a primary’s plugin is missing
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: stub `docs/explanation/ai-observability.md` (two planes + config errors)
14
- - Acceptance: unit tests for valid local-only, both plugins + defaults, illegal mix, langfuse reviewQueue, local-off + prompts primary local
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
- - [ ] **Task 1.2**: In-memory TraceSink/ScoreSink + `AIService` emit
17
- - Delivers: every `generate*` exports a trace unless `skipTrace`; sink failures logged not thrown; `AIRequest` still written; `userId`/`sessionId`/`promptRef`/`priceMap` on options
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`, `promptRef`, usage/cost fields
21
- - Acceptance: tests: emit; skipTrace; throwing sink; costUsd from price map; missing model → no usd; multi-step still logs AIRequest
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
- - [ ] **Task 1.3**: GPT routes pass identity
24
- - Delivers: `/gpt/prompt` sets `userId` from `req.user` and `sessionId` from body/header `x-ai-session-id` into `AIService`
25
- - Files: `ai/src/routes/gpt.ts`, `ai/src/routes/gpt.test.ts` (or existing route tests)
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 sink trace with that userId
35
+ - Acceptance: an authenticated prompt produces a memory-sink trace carrying that `userId` and `sessionId`
29
36
 
30
- ## Phase 2 — Local telemetry + prompts
37
+ ## 1B — Local store: prompts, traces, review
31
38
 
32
- - [ ] **Task 2.1**: Local Mongo models for prompts + traces + spans + scores
33
- - Delivers: schemas with descriptions, plugins, indexes from the IP; models registered only when local plugin constructs
34
- - Files: `ai/src/observability/local/models/*.ts`, `ai/src/observability/local/types.ts`, model tests
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: save/load version immutability; unique `(promptId, label)`; findExactlyOne/findOneOrNone only
44
+ - Acceptance: version immutability; unique `(promptId, label)`; `findExactlyOne` / `findOneOrNone` only; no unique index on scores
38
45
 
39
- - [ ] **Task 2.2**: Local `PromptRegistry` + prompt HTTP
40
- - Delivers: create named prompt, immutable versions, move production/latest, compile `{{vars}}`, get by label
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: 2.1, 1.1
43
- - Docs: `docs/how-to/observe-llm-calls.md` — create + pin production
44
- - Acceptance: v1 unchanged after v2; get production returns v2; playground compile without new version
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
- - [ ] **Task 2.3**: Local `TraceSink`/`ScoreSink` + trace/session/user/cost HTTP
47
- - Delivers: persist fan-in from ObservabilityApp; list/detail/filters; cost aggregates
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: 2.1, 1.2
56
+ - Blocked by: 1.4, 1.2
50
57
  - Docs: `docs/reference/ai.md` routes
51
- - Acceptance: filter user/session/prompt/minCost; nested spans round-trip; costs by model
52
-
53
- ## Phase 3 — Evaluators, live sample, review
54
-
55
- - [ ] **Task 3.1**: Evaluator model + LLM-judge + code/json-assert
56
- - Delivers: multidimensional scores; judge prompt from PromptRegistry production (or named constant in `prompts.ts` for the built-in judge wrapper); structured JSON via `generateJsonObject`
57
- - Files: `ai/src/observability/local/evaluatorStore.ts`, `ai/src/observability/evaluate.ts`, `ai/src/service/prompts.ts` (judge wrapper constant), tests
58
- - Blocked by: 2.2, 2.3
59
- - Docs: explanation — dimensions; how-to — define evaluator
60
- - Acceptance: numeric + boolean dimensions on one trace; judge parse failure records error score not throw to caller
61
-
62
- - [ ] **Task 3.2**: `sampleRate` live evals via BackgroundTask
63
- - Delivers: default 0; >0 enqueues eval after trace export; still never throws from generate
64
- - Files: `ai/src/observability/liveEval.ts`, `observabilityApp.ts`, tests
65
- - Blocked by: 3.1
66
- - Docs: how-to env `AI_OBS_SAMPLE_RATE`
67
- - Acceptance: rate 0 → no task; rate 1 → BackgroundTask created (mock/stub runner ok)
68
-
69
- - [ ] **Task 3.3**: Local review queue API
70
- - Delivers: enqueue from trace; list pending; submit scores; skip; assign
71
- - Files: `ai/src/observability/local/reviewStore.ts`, `ai/src/observability/routes/review.ts`, tests
72
- - Blocked by: 3.1
73
- - Docs: how-to — review flow
74
- - Acceptance: submit writes ScoreSink fan-out path; done item not in pending
75
-
76
- ## Phase 4 — Datasets and local experiments
77
-
78
- - [ ] **Task 4.1**: Datasets + items (including from trace)
79
- - Delivers: CRUD; add-from-trace copies input/output snapshot
80
- - Files: dataset store + routes + tests
81
- - Blocked by: 2.3
82
- - Docs: how-to — gold set from traces
83
- - Acceptance: item retains sourceTraceId; delete item does not delete trace
84
-
85
- - [ ] **Task 4.2**: Local ExperimentRunner always BackgroundTask
86
- - Delivers: POST experiment compares 2–3 prompt versions on a dataset, runs evaluators, stores per-item results, aggregates; promote → production label
87
- - Files: `ai/src/observability/local/experimentRunner.ts`, routes, tests
88
- - Blocked by: 4.1, 3.1, 2.2
89
- - Docs: how-to — run experiment + promote
90
- - Acceptance: 1-item dataset still uses BackgroundTask; completed aggregates; promote moves label; failure sets experiment failed
91
-
92
- ## Phase 5 — Langfuse adapter, OTel, admin, example
93
-
94
- - [ ] **Task 5.1**: Langfuse TraceSink + ScoreSink wrapping existing client
95
- - Delivers: no second OTel SDK; best-effort; uses `getLangfuseClient()`
96
- - Files: `ai/src/observability/langfuse/traceAdapter.ts`, tests (mock client)
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 with both sinks; langfuse mock throws → generate still ok; local row exists
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 5.2**: Langfuse PromptRegistry / DatasetStore / ExperimentRunner adapters
102
- - Delivers: primaries can be `langfuse`; Terreno HTTP facade delegates; Open in Langfuse URLs on status
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: 5.1, 2.2, 4.2
105
- - Docs: how-to — switch primaries via env
106
- - Acceptance: prompts primary langfuse list uses client; experiments primary langfuse does not start BackgroundTask
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 5.3**: `OtelTraceSink` OpenInference
109
- - Delivers: OTLP HTTP JSON or SDK exporter; `openinference.span.kind=LLM`; optional endpoint
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/collector endpoint
113
- - Acceptance: mock exporter receives span with model + token attrs
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.4**: `admin-frontend` screens
116
- - Delivers: prompts, traces, costs, evaluators, datasets, experiments, review; status chip; hide review without local; wire `customScreens`
117
- - Files: `admin-frontend/src/widgets/aiObservability/*.tsx`, `builtInWidgets.ts`, `ai/src` adminContribution
118
- - Blocked by: 2.2, 2.3, 3.3, 4.2
119
- - Docs: `docs/reference/admin-frontend.md` screen names
120
- - Acceptance: widget tests empty/loading; `verify-ui-changes` on admin (login admin, open Prompts + Traces)
121
-
122
- - [ ] **Task 5.5**: example-backend register + seed + env
123
- - Delivers: local plugin on; Langfuse sinks if keys; OTel if endpoint; seed prompt/evaluator; fail-fast config
124
- - Files: `example-backend/src/server.ts`, seed script, `.env.example`
125
- - Blocked by: 5.1, 5.3, 2.2, 3.1
126
- - Docs: how-to uses example-backend snippet
127
- - Acceptance: server boots with local-only; invalid primary mix prevents listen (test or documented script)
128
-
129
- - [ ] **Task 5.6**: Docs completeness + Langfuse IP pointer + prompt-governance skill note
130
- - Delivers: explanation, how-to, reference complete; `docs/implementationPlans/terreno-langfuse-integration.md` see-also; skill: registry production prompts allowed
131
- - Files: docs listed in IP; `.github/skills/ai-prompt-governance/SKILL.md` (source) + `bun run skills:sync` if required
132
- - Blocked by: 5.5
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 stranger can register ObservabilityApp from how-to without reading the IP
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,185 @@
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
+ - [x] **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
+ - Shipped: fake-context cases in `ai/src/harness/runners/inProcessRunner.test.ts` (default 8, gate, peak, stop waits for all, invalid concurrency) and real-Mongo cases in `ai/src/harness/concurrency.test.ts` (blocked task does not hold up a second; 7 two-phase tasks at concurrency 3 peak at exactly 3 with no task running twice). Roast: PASS (2026-10-04; 76 targeted tests, three mutation checks red)
142
+
143
+ - [x] **Task 2.1**: `JobsRunner`
144
+ - 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
145
+ - Files: `ai/src/harness/runners/jobsRunner.ts`, `ai/package.json`, tests
146
+ - Blocked by: 2.0
147
+ - Skills: `terreno-backend-api`
148
+ - Docs: reference "Runners"; how-to "Run on multiple instances"
149
+ - Acceptance: AC11 — two workers race one phase; exactly one commit; stale owner rejected; knip clean without jobs installed
150
+ - Shipped: `ai/src/harness/runners/jobsRunner.test.ts` (two-worker race, stale-lease commit rejected, re-dispatch of a dead job, root imports never reach `@terreno/jobs`). Drift: the job key is `taskId:phase:attempt:claims` — `claims` (new `HarnessTask` counter) gives a phase revisited by an event wake or crash replay its own job, since jobs idempotency keys are permanent. Built before Task 2.0; it does not depend on `InProcessRunner` concurrency. Roast: PASS (2026-10-04; 17 targeted tests, ai suite 1185/0, knip clean, two mutation checks red)
151
+
152
+ - [ ] **Task 2.2**: Runs admin screen
153
+ - Delivers: task list/tree with status filters, abort, resolve-interrupted (retry/abort/complete + reason), link to trace
154
+ - Files: `admin-frontend/src/widgets/harness/HarnessRuns*.tsx`, `ai/src/harness/routes/tasks.ts`
155
+ - Blocked by: 2.1
156
+ - Skills: `building-admin-interfaces`, `verify-ui-changes`
157
+ - Docs: admin-frontend reference
158
+ - Acceptance: Playwright resolves an interrupted EHR write; screenshots
159
+
160
+ ## Phase 3 — Long context
161
+
162
+ - [ ] **Task 3.1**: Background compaction + manual `compact(instructions)`; originals stay searchable
163
+ - [ ] **Task 3.2**: `reset` / handoff tool control
164
+ - [ ] **Task 3.3**: Conversation forks at any message (parent history by reference)
165
+ - [ ] **Task 3.4**: `defineDoc` typed documents (`scope`, `history: rewindable`, `fork: asOf`), committed atomically with transcripts
166
+
167
+ Each 3.x task: Blocked by 1.12; Docs: reference + explanation sections; Acceptance: bun tests (10k-message conversation fits budget; fork isolation; doc rewind).
168
+
169
+ ## Phase 4 — Durable chat + multiplayer
170
+
171
+ - [x] **Task 4.0**: Human gate — `GptHistory` migration strategy → **one-shot migration** script at deploy (decided 2026-10-02)
172
+ - [ ] **Task 4.1**: `/gpt/prompt` on the harness; history mapping
173
+ - [ ] **Task 4.2**: Steering, foreground/background abort, `viewState` over SSE
174
+ - [ ] **Task 4.3**: rtk `useHarnessConversation` SSE hook + `GPTChat` adapter in example-frontend
175
+
176
+ Acceptance: kill backend mid-chat, reconnect resumes; two browsers see the same view (Playwright, screenshots).
177
+
178
+ ## Phase 5 — Coding agents + malleability
179
+
180
+ - [x] **Task 5.0**: Human gate — sandbox provider → **Cloud Run Jobs container** (decided 2026-10-02)
181
+ - [ ] **Task 5.1**: `NodeExecutionEnv` + `CodingTools` (read/write/edit/bash with replay classes)
182
+ - [ ] **Task 5.2**: Remote sandbox adapter
183
+ - [ ] **Task 5.3**: Registry hot-replace (running calls finish on old code)
184
+
185
+ Acceptance: a coding agent fixes a failing test in a fixture repo after a forced crash.
package/dist/tools.d.ts CHANGED
@@ -45,6 +45,10 @@ export declare const generateFormFields: (args: {
45
45
  }>;
46
46
  }>;
47
47
  }) => string;
48
+ /** Same stdout as `terreno-blocks validate` for one document. */
49
+ export declare const validateUiBlocks: (args: {
50
+ document?: string;
51
+ }) => string;
48
52
  export declare const validateModelSchema: (args: {
49
53
  schema: string;
50
54
  }) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,8BAA8B,CAAC;AA8JvD,eAAO,MAAM,KAAK,EAAE,IAAI,EAiOvB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAuEH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,KAAG,MAuEH,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,KAAG,MA0MH,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM;IACvC,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,KAAK,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAC,CAAC,CAAC;KACjD,CAAC,CAAC;CACJ,KAAG,MA+GH,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,KAAG,MA6D5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM;IACzC,MAAM,EAAE,KAAK,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ,KAAG,MAsMH,CAAC;AAqBF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CA8IxD,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,8BAA8B,CAAC;AA+JvD,eAAO,MAAM,KAAK,EAAE,IAAI,EAgPvB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAuEH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,KAAG,MAuEH,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,KAAG,MA0MH,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM;IACvC,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,KAAK,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAC,CAAC,CAAC;KACjD,CAAC,CAAC;CACJ,KAAG,MA+GH,CAAC;AAKF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,GAAI,MAAM;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,KAAG,MAgB5D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,KAAG,MA6D5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM;IACzC,MAAM,EAAE,KAAK,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ,KAAG,MAsMH,CAAC;AAqBF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CAiJxD,CAAC"}