@terreno/mcp 57.8.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.
Files changed (115) hide show
  1. package/dist/docs/upgrades/57.10.1.md +54 -0
  2. package/dist/docs/upgrades/57.9.0.md +28 -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/charts.md +12 -0
  10. package/dist/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
  11. package/dist/docs/versioned/next/explanation/example-coverage.md +4 -2
  12. package/dist/docs/versioned/next/explanation/loop-engineering.md +1 -1
  13. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +49 -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 -1
  17. package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
  18. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  19. package/dist/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  20. package/dist/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  21. package/dist/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  22. package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +7 -0
  23. package/dist/docs/versioned/next/how-to/circleci.md +9 -6
  24. package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
  25. package/dist/docs/versioned/next/how-to/install-agent-skills.md +1 -1
  26. package/dist/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  27. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  28. package/dist/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  29. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  30. package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  31. package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  32. package/dist/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  33. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  34. package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
  35. package/dist/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
  36. package/dist/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  37. package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  38. package/dist/docs/versioned/next/reference/README.md +3 -0
  39. package/dist/docs/versioned/next/reference/admin-config.md +2 -0
  40. package/dist/docs/versioned/next/reference/admin-frontend.md +216 -0
  41. package/dist/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  42. package/dist/docs/versioned/next/reference/ai-harness.md +1881 -0
  43. package/dist/docs/versioned/next/reference/ai.md +283 -28
  44. package/dist/docs/versioned/next/reference/api.md +46 -0
  45. package/dist/docs/versioned/next/reference/blocks.md +178 -0
  46. package/dist/docs/versioned/next/reference/environment-variables.md +2 -1
  47. package/dist/docs/versioned/next/reference/feature-flags.md +1 -1
  48. package/dist/docs/versioned/next/reference/mcp-server.md +17 -0
  49. package/dist/docs/versioned/next/reference/test.md +11 -2
  50. package/dist/docs/versioned/next/reference/ui.md +418 -11
  51. package/dist/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  52. package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  53. package/dist/docs/versioned/next/tasks/ai-observability.md +233 -104
  54. package/dist/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
  55. package/dist/docs/versioned/next/tasks/durable-agent-harness.md +183 -0
  56. package/dist/tools.d.ts +4 -0
  57. package/dist/tools.d.ts.map +1 -1
  58. package/dist/tools.js +37 -0
  59. package/dist/tools.js.map +1 -1
  60. package/package.json +3 -2
  61. package/src/docs/upgrades/57.10.1.md +54 -0
  62. package/src/docs/upgrades/57.9.0.md +28 -0
  63. package/src/docs/versioned/next/README.md +1 -0
  64. package/src/docs/versioned/next/explanation/README.md +4 -0
  65. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
  66. package/src/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
  67. package/src/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
  68. package/src/docs/versioned/next/explanation/ai-observability.md +101 -0
  69. package/src/docs/versioned/next/explanation/charts.md +12 -0
  70. package/src/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
  71. package/src/docs/versioned/next/explanation/example-coverage.md +4 -2
  72. package/src/docs/versioned/next/explanation/loop-engineering.md +1 -1
  73. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +49 -1
  74. package/src/docs/versioned/next/explanation/typescript-configuration.md +1 -1
  75. package/src/docs/versioned/next/explanation/versioning-policy.md +1 -1
  76. package/src/docs/versioned/next/how-to/README.md +7 -1
  77. package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
  78. package/src/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  79. package/src/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  80. package/src/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  81. package/src/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  82. package/src/docs/versioned/next/how-to/charts-and-dashboards.md +7 -0
  83. package/src/docs/versioned/next/how-to/circleci.md +9 -6
  84. package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
  85. package/src/docs/versioned/next/how-to/install-agent-skills.md +1 -1
  86. package/src/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  87. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  88. package/src/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  89. package/src/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  90. package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  91. package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  92. package/src/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  93. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  94. package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
  95. package/src/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
  96. package/src/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  97. package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  98. package/src/docs/versioned/next/reference/README.md +3 -0
  99. package/src/docs/versioned/next/reference/admin-config.md +2 -0
  100. package/src/docs/versioned/next/reference/admin-frontend.md +216 -0
  101. package/src/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  102. package/src/docs/versioned/next/reference/ai-harness.md +1881 -0
  103. package/src/docs/versioned/next/reference/ai.md +283 -28
  104. package/src/docs/versioned/next/reference/api.md +46 -0
  105. package/src/docs/versioned/next/reference/blocks.md +178 -0
  106. package/src/docs/versioned/next/reference/environment-variables.md +2 -1
  107. package/src/docs/versioned/next/reference/feature-flags.md +1 -1
  108. package/src/docs/versioned/next/reference/mcp-server.md +17 -0
  109. package/src/docs/versioned/next/reference/test.md +11 -2
  110. package/src/docs/versioned/next/reference/ui.md +418 -11
  111. package/src/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  112. package/src/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  113. package/src/docs/versioned/next/tasks/ai-observability.md +233 -104
  114. package/src/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
  115. package/src/docs/versioned/next/tasks/durable-agent-harness.md +183 -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,145 @@
1
+ # Tasks: Dashboard chart parity
2
+
3
+ IP: [dashboard-chart-parity.md](../implementationPlans/dashboard-chart-parity.md)
4
+ **Follows:** https://github.com/TerrenoLabs/terreno/issues/1175
5
+ **Roadmap issue:** set after IP approval
6
+
7
+ **Feature profile:** false (full IP)
8
+
9
+ ## Phase 1 — Tracer (scorecard)
10
+
11
+ - [x] **Task 1.1**: `SparklineChart` + dotted `comparisonData`
12
+ - Delivers: public plot-only sparkline; solid current series; dotted comparison aligned on `label`; no axes/grid/legend/tooltip row; empty input does not throw
13
+ - Files: `ui/src/SparklineChart.tsx`, `ui/src/SparklineChart.test.tsx`, `ui/src/charts/paths.ts` (reuse line path), `ui/src/Common.ts` (TypeDoc props), `ui/src/index.tsx` / `heavyOptionalExports.tsx`
14
+ - Blocked by: none
15
+ - Skills: `terreno-ui`, `update-docs`
16
+ - Docs: stub `SparklineChart` on `docs/reference/ui.md` Display list
17
+ - Acceptance: bun tests — N points → path present; comparison path present when `comparisonData` set; empty data shows nothing / emptyText if provided; `ui` compile green
18
+
19
+ - [x] **Task 1.2**: `Scorecard` + `ChartCard`
20
+ - Delivers: KPI tile with title, formatted value, optional sparkline; `ChartCard` with title, period badge, optional `onPeriodPress`, optional `filterSummary`
21
+ - Files: `ui/src/Scorecard.tsx`, `ui/src/Scorecard.test.tsx`, `ui/src/ChartCard.tsx`, `ui/src/ChartCard.test.tsx`, lazy exports
22
+ - Blocked by: 1.1
23
+ - Skills: `terreno-ui`, `verify-ui-changes`, `update-docs`
24
+ - Docs: stub both on `docs/reference/ui.md`
25
+ - Acceptance: bun tests — value + title render; sparkline mounts when `sparklineData` set; badge press fires; period badge has no press handler when `onPeriodPress` omitted
26
+
27
+ - [x] **Task 1.3**: Scorecard demo + first golden
28
+ - Delivers: demo story; gallery fixture `scorecard-sparkline-comparison` (five KPI tiles, static data); PNG golden
29
+ - Files: `demo/stories/Scorecard.stories.tsx`, `demo/stories/ChartCard.stories.tsx`, `demo/demoConfig.tsx`, `demo/chartVisual/fixtureCatalog.ts`, `demo/chartVisual/fixtures.tsx`, `demo/rendered-snapshots/scorecard-sparkline-comparison.png`
30
+ - Blocked by: 1.2
31
+ - Skills: `terreno-ui`, `review-chart-visuals`, `verify-ui-changes`
32
+ - Docs: `docs/how-to/compare-chart-rendered-snapshots.md` lists the new id
33
+ - Acceptance: fixture catalog test includes the id; `bun run ui:charts:compare` green for it; story registered
34
+
35
+ ## Phase 2 — Cartesian additive facades
36
+
37
+ - [x] **Task 2.1**: `series` + `comparisonData` on `LineChart` / `AreaChart`
38
+ - Delivers: multi-series lines from `series`; single `data` still works; dotted comparison overlay; per-series legend; markers stay
39
+ - Files: `ui/src/LineChart.tsx`, `ui/src/AreaChart.tsx`, matching tests, `ui/src/charts/scales.ts` (shared x domain across series)
40
+ - Blocked by: 1.2
41
+ - Skills: `terreno-ui`, `update-docs`
42
+ - Docs: `docs/reference/ui.md` Line/Area props
43
+ - Acceptance: bun tests — three `series` → three paths + three legend labels; `{label,value}[]` only still draws one series; comparison path exists when set
44
+
45
+ - [x] **Task 2.2**: `xTickPolicy` + bar comparison + header shortcuts
46
+ - Delivers: `"auto"` rotates when labels would truncate, stays horizontal for ≤7 categories; `BarChart` accepts `series` (grouped later if >1 — this slice: first series only unless a second is comparison) and `comparisonData`; `title` / `periodLabel` / `onPeriodPress` on cartesian facades
47
+ - Files: `ui/src/BarChart.tsx`, `ui/src/LineChart.tsx`, `ui/src/charts/layout.ts`, `ui/src/BarChart.test.tsx`, `ui/src/LineChart.test.tsx`
48
+ - Blocked by: 2.1
49
+ - Skills: `terreno-ui`
50
+ - Docs: tick policy in `docs/explanation/charts.md`
51
+ - Acceptance: bun tests — 14 long date labels with `auto` → rotated tick style; 7 weekday labels stay horizontal; facade title renders when set and `ChartCard` is not wrapping (documented exclusive use)
52
+
53
+ - [x] **Task 2.3**: Cartesian goldens
54
+ - Delivers: fixtures `bar-time-rotated-ticks`, `line-three-series`, `bar-day-of-week`
55
+ - Files: `demo/chartVisual/**`, `demo/rendered-snapshots/*.png`
56
+ - Blocked by: 2.2
57
+ - Skills: `review-chart-visuals`, `verify-ui-changes`
58
+ - Docs: compare-how-to fixture table
59
+ - Acceptance: `bun run ui:charts:compare` green for the three ids
60
+
61
+ ## Phase 3 — Donut center and share legend
62
+
63
+ - [x] **Task 3.1**: Donut hole + percent legend
64
+ - Delivers: `centerTitle`, `centerValue`; legend `swatch + label + formatShare` (default percent of total); 100% single-slice still draws a ring + center
65
+ - Files: `ui/src/DonutChart.tsx`, `ui/src/DonutChart.test.tsx`
66
+ - Blocked by: 1.2
67
+ - Skills: `terreno-ui`, `update-docs`
68
+ - Docs: `docs/reference/ui.md` Donut props
69
+ - Acceptance: bun tests — center copy present; legend includes `%`; one-slice data still has one path; existing slice color override still works
70
+
71
+ - [x] **Task 3.2**: Donut goldens
72
+ - Delivers: `donut-center-and-share`, `donut-single-slice`
73
+ - Files: `demo/chartVisual/**`, goldens
74
+ - Blocked by: 3.1
75
+ - Skills: `review-chart-visuals`
76
+ - Docs: compare-how-to
77
+ - Acceptance: compare green for both ids
78
+
79
+ ## Phase 4 — Spanning grid
80
+
81
+ - [x] **Task 4.1**: `DashboardGridItem` spans
82
+ - Delivers: eager `DashboardGridItem` with `span` per breakpoint; default span 1; children without a wrapper keep current equal-column behavior
83
+ - Files: `ui/src/DashboardGrid.tsx` and/or `ui/src/DashboardGridItem.tsx`, `ui/src/DashboardGrid.test.tsx`, `ui/src/index.tsx` (eager)
84
+ - Blocked by: 1.2
85
+ - Skills: `terreno-ui`, `update-docs`
86
+ - Docs: `docs/reference/ui.md` Layout
87
+ - Acceptance: bun tests — span 2 in a 4-column row yields ~2× cell width minus gap; unwrapped children still fill 1 column
88
+
89
+ - [ ] **Task 4.2**: `hows-it-going-dashboard` chart composition
90
+ - Delivers: hard fixture matching the reference layout (5 scorecards, cost/conv bar, 3-line imp share, DOW bar, two donuts). Table slot may be a `ChartCard` placeholder until 5.2
91
+ - Files: `demo/chartVisual/fixtures.tsx`, `fixtureCatalog.ts`, golden
92
+ - Blocked by: 2.3, 3.2, 4.1
93
+ - Skills: `review-chart-visuals`, `verify-ui-changes`
94
+ - Docs: how-to example rewritten to this composition
95
+ - Acceptance: compare green; fixture uses `Scorecard` / `ChartCard` / `DashboardGridItem`, not ad-hoc `Box` widths only
96
+
97
+ ## Phase 5 — Table chrome
98
+
99
+ - [ ] **Task 5.1**: Header groups, footer rows, range status, wrapLines
100
+ - Delivers: `headerGroups` second header row; `footerRows` sticky totals; range copy `start–end of total` with existing pagination; `wrapLines={2}` wraps cell text inside `rowHeight` (virtualization unchanged)
101
+ - Files: `ui/src/DataTable.tsx`, `ui/src/DataTable.test.tsx`, `ui/src/Pagination.tsx` (if range lives there), `ui/src/Common.ts`
102
+ - Blocked by: 4.1
103
+ - Skills: `terreno-ui`, `update-docs`
104
+ - Docs: `docs/reference/ui.md` DataTable; pointer from charts how-to
105
+ - Acceptance: bun tests — grouped header text visible; footer row present; range text `1–50 of 452` with page 1 size 50 total 452; pinned column scroll still syncs with groups/footer; wrapLines 2 does not change `rowHeight`
106
+
107
+ - [ ] **Task 5.2**: Table card in the hard fixture
108
+ - Delivers: one `ChartCard` + `DataTable` in `hows-it-going-dashboard` (Search Terms or Ad Group Check-in static rows, totals, period headers)
109
+ - Files: `demo/chartVisual/fixtures.tsx`, update golden
110
+ - Blocked by: 5.1, 4.2
111
+ - Skills: `review-chart-visuals`, `verify-ui-changes`
112
+ - Docs: how-to shows table in the same grid
113
+ - Acceptance: compare green after intentional golden update; wrapped first-column names visible in the PNG
114
+
115
+ ## Phase 6 — Packaging and docs
116
+
117
+ - [ ] **Task 6.1**: Lazy root + import regression
118
+ - Delivers: `Scorecard`, `SparklineChart`, `ChartCard`, chart facades stay on `heavyOptionalExports`; `DashboardGrid` / `DashboardGridItem` eager; root import does not evaluate chart files
119
+ - Files: `ui/src/lazyBoundaries/heavyOptionalExports.tsx`, `ui/src/index.tsx`, `ui/src/RootImportRegression.test.tsx`, `docs/reference/ui-performance.md`
120
+ - Blocked by: 4.1, 5.1, 2.1, 3.1
121
+ - Skills: `terreno-ui`, `update-docs`
122
+ - Docs: performance page lists new lazy exports
123
+ - Acceptance: `RootImportRegression` fails if new chart files load with the root import; subpath imports work
124
+
125
+ - [ ] **Task 6.2**: Diátaxis + changelog + rules
126
+ - Delivers: how-to rewritten for easy-build composition; explanation covers scorecard, comparison stroke, additive facades vs grammar; reference complete; changelog fragment; ui rule export list; TypeDoc
127
+ - Files: `docs/how-to/charts-and-dashboards.md`, `docs/explanation/charts.md`, `docs/reference/ui.md`, `docs/how-to/README.md`, `.rulesync/rules/ui/00-ui.mdc`, `changelog/unreleased/dashboard-chart-parity.md`; `bun run website:generate` if needed
128
+ - Blocked by: 6.1, 5.2
129
+ - Skills: `update-docs`, `docs-audit`, `terreno-ui`
130
+ - Docs: the files listed above
131
+ - Acceptance: a stranger can copy the how-to into a screen that resembles the reference; explanation states grammar is not required; changelog `category: Added`; existing single-series how-to steps still valid as a subset
132
+
133
+ ## Verification mapping
134
+
135
+ | Criterion | Method |
136
+ |-----------|--------|
137
+ | Scorecard + sparkline + comparison | bun tests + `scorecard-sparkline-comparison` PNG |
138
+ | Multi-series line | bun tests + `line-three-series` PNG |
139
+ | Rotated vs horizontal ticks | bun tests + `bar-time-rotated-ticks` / `bar-day-of-week` |
140
+ | Donut center + share | bun tests + donut goldens |
141
+ | Grid spans + reference layout | `hows-it-going-dashboard` PNG + grid unit tests |
142
+ | Table chrome | bun tests + table region of hard golden |
143
+ | Facade compatibility | existing `*Chart.test.tsx` stay green |
144
+ | Lazy charts | `RootImportRegression` |
145
+ | Frontend proof | `verify-ui-changes` on demo gallery; artifacts on the implementation PR |