@terreno/mcp 57.6.1 → 57.8.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 (143) hide show
  1. package/dist/docs/resources/ui.md +5 -4
  2. package/dist/docs/upgrades/57.7.0.md +64 -0
  3. package/dist/docs/upgrades/57.8.0.md +61 -0
  4. package/dist/docs/upgrades/58.0.0.md +4 -0
  5. package/dist/docs/versioned/next/explanation/example-coverage.md +1 -0
  6. package/dist/docs/versioned/next/explanation/loop-engineering.md +32 -3
  7. package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
  8. package/dist/docs/versioned/next/explanation/repository-settings.md +12 -5
  9. package/dist/docs/versioned/next/explanation/roadmap-process.md +41 -19
  10. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +285 -22
  11. package/dist/docs/versioned/next/explanation/static-analysis.md +3 -2
  12. package/dist/docs/versioned/next/explanation/test-isolation.md +42 -0
  13. package/dist/docs/versioned/next/how-to/circleci.md +121 -64
  14. package/dist/docs/versioned/next/how-to/github-actions-ci.md +4 -3
  15. package/dist/docs/versioned/next/how-to/preview-demo.md +19 -0
  16. package/dist/docs/versioned/next/how-to/product-announcements.md +3 -0
  17. package/dist/docs/versioned/next/how-to/run-tests-locally.md +23 -2
  18. package/dist/docs/versioned/next/implementationPlans/IP_TEMPLATE.md +3 -1
  19. package/dist/docs/versioned/next/implementationPlans/admin-improvements.md +1 -1
  20. package/dist/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +2 -1
  21. package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +1 -0
  22. package/dist/docs/versioned/next/implementationPlans/ai-agents-and-failover.md +1 -0
  23. package/dist/docs/versioned/next/implementationPlans/ai-observability.md +1 -0
  24. package/dist/docs/versioned/next/implementationPlans/announcements-surfaces.md +1 -1
  25. package/dist/docs/versioned/next/implementationPlans/announcements.md +2 -1
  26. package/dist/docs/versioned/next/implementationPlans/app-mcp-server.md +1 -0
  27. package/dist/docs/versioned/next/implementationPlans/better-auth-strict-oauth-provider.md +2 -1
  28. package/dist/docs/versioned/next/implementationPlans/can-as-permission-seam.md +1 -1
  29. package/dist/docs/versioned/next/implementationPlans/collection-registry.md +1 -1
  30. package/dist/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +1 -1
  31. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +1 -1
  32. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
  33. package/dist/docs/versioned/next/implementationPlans/compile-pipeline-dedup.md +2 -1
  34. package/dist/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +1 -1
  35. package/dist/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
  36. package/dist/docs/versioned/next/implementationPlans/describe-model-schema.md +1 -1
  37. package/dist/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -1
  38. package/dist/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -1
  39. package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -1
  40. package/dist/docs/versioned/next/implementationPlans/framework-audit-log.md +1 -1
  41. package/dist/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +1 -0
  42. package/dist/docs/versioned/next/implementationPlans/inbound-webhooks.md +1 -1
  43. package/dist/docs/versioned/next/implementationPlans/knip-cleanup.md +1 -0
  44. package/dist/docs/versioned/next/implementationPlans/mcp-doc-sync-off-compile-path.md +2 -1
  45. package/dist/docs/versioned/next/implementationPlans/mcp-service-tokens.md +2 -1
  46. package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +2 -1
  47. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +1 -1
  48. package/dist/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +1 -1
  49. package/dist/docs/versioned/next/implementationPlans/notification-center.md +1 -1
  50. package/dist/docs/versioned/next/implementationPlans/org-management-ui.md +1 -1
  51. package/dist/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +1 -1
  52. package/dist/docs/versioned/next/implementationPlans/rate-limiting.md +1 -1
  53. package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -0
  54. package/dist/docs/versioned/next/implementationPlans/support-agent.md +1 -0
  55. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +1 -1
  56. package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  57. package/dist/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -1
  58. package/dist/docs/versioned/next/reference/ai.md +3 -1
  59. package/dist/docs/versioned/next/reference/announcement-release-packs.md +146 -0
  60. package/dist/docs/versioned/next/reference/announcements.md +7 -3
  61. package/dist/docs/versioned/next/reference/api.md +1 -0
  62. package/dist/docs/versioned/next/reference/environment-variables.md +1 -0
  63. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +2 -2
  64. package/dist/docs/versioned/next/reference/test.md +1 -1
  65. package/dist/docs/versioned/next/reference/ui.md +79 -8
  66. package/dist/docs/versioned/next/tasks/can-as-permission-seam.md +4 -4
  67. package/dist/docs/versioned/next/tasks/collection-registry.md +6 -6
  68. package/dist/docs/versioned/next/tasks/demo-workshop.md +150 -0
  69. package/dist/docs/versioned/next/tasks/describe-model-schema.md +5 -5
  70. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +6 -3
  71. package/package.json +2 -2
  72. package/src/docs/resources/ui.md +5 -4
  73. package/src/docs/upgrades/57.7.0.md +64 -0
  74. package/src/docs/upgrades/57.8.0.md +61 -0
  75. package/src/docs/upgrades/58.0.0.md +4 -0
  76. package/src/docs/versioned/next/explanation/example-coverage.md +1 -0
  77. package/src/docs/versioned/next/explanation/loop-engineering.md +32 -3
  78. package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
  79. package/src/docs/versioned/next/explanation/repository-settings.md +12 -5
  80. package/src/docs/versioned/next/explanation/roadmap-process.md +41 -19
  81. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +285 -22
  82. package/src/docs/versioned/next/explanation/static-analysis.md +3 -2
  83. package/src/docs/versioned/next/explanation/test-isolation.md +42 -0
  84. package/src/docs/versioned/next/how-to/circleci.md +121 -64
  85. package/src/docs/versioned/next/how-to/github-actions-ci.md +4 -3
  86. package/src/docs/versioned/next/how-to/preview-demo.md +19 -0
  87. package/src/docs/versioned/next/how-to/product-announcements.md +3 -0
  88. package/src/docs/versioned/next/how-to/run-tests-locally.md +23 -2
  89. package/src/docs/versioned/next/implementationPlans/IP_TEMPLATE.md +3 -1
  90. package/src/docs/versioned/next/implementationPlans/admin-improvements.md +1 -1
  91. package/src/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +2 -1
  92. package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +1 -0
  93. package/src/docs/versioned/next/implementationPlans/ai-agents-and-failover.md +1 -0
  94. package/src/docs/versioned/next/implementationPlans/ai-observability.md +1 -0
  95. package/src/docs/versioned/next/implementationPlans/announcements-surfaces.md +1 -1
  96. package/src/docs/versioned/next/implementationPlans/announcements.md +2 -1
  97. package/src/docs/versioned/next/implementationPlans/app-mcp-server.md +1 -0
  98. package/src/docs/versioned/next/implementationPlans/better-auth-strict-oauth-provider.md +2 -1
  99. package/src/docs/versioned/next/implementationPlans/can-as-permission-seam.md +1 -1
  100. package/src/docs/versioned/next/implementationPlans/collection-registry.md +1 -1
  101. package/src/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +1 -1
  102. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +1 -1
  103. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
  104. package/src/docs/versioned/next/implementationPlans/compile-pipeline-dedup.md +2 -1
  105. package/src/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +1 -1
  106. package/src/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
  107. package/src/docs/versioned/next/implementationPlans/describe-model-schema.md +1 -1
  108. package/src/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -1
  109. package/src/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -1
  110. package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -1
  111. package/src/docs/versioned/next/implementationPlans/framework-audit-log.md +1 -1
  112. package/src/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +1 -0
  113. package/src/docs/versioned/next/implementationPlans/inbound-webhooks.md +1 -1
  114. package/src/docs/versioned/next/implementationPlans/knip-cleanup.md +1 -0
  115. package/src/docs/versioned/next/implementationPlans/mcp-doc-sync-off-compile-path.md +2 -1
  116. package/src/docs/versioned/next/implementationPlans/mcp-service-tokens.md +2 -1
  117. package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +2 -1
  118. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +1 -1
  119. package/src/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +1 -1
  120. package/src/docs/versioned/next/implementationPlans/notification-center.md +1 -1
  121. package/src/docs/versioned/next/implementationPlans/org-management-ui.md +1 -1
  122. package/src/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +1 -1
  123. package/src/docs/versioned/next/implementationPlans/rate-limiting.md +1 -1
  124. package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -0
  125. package/src/docs/versioned/next/implementationPlans/support-agent.md +1 -0
  126. package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +1 -1
  127. package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  128. package/src/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -1
  129. package/src/docs/versioned/next/reference/ai.md +3 -1
  130. package/src/docs/versioned/next/reference/announcement-release-packs.md +146 -0
  131. package/src/docs/versioned/next/reference/announcements.md +7 -3
  132. package/src/docs/versioned/next/reference/api.md +1 -0
  133. package/src/docs/versioned/next/reference/environment-variables.md +1 -0
  134. package/src/docs/versioned/next/reference/lifecycle-plugin.md +2 -2
  135. package/src/docs/versioned/next/reference/test.md +1 -1
  136. package/src/docs/versioned/next/reference/ui.md +79 -8
  137. package/src/docs/versioned/next/tasks/can-as-permission-seam.md +4 -4
  138. package/src/docs/versioned/next/tasks/collection-registry.md +6 -6
  139. package/src/docs/versioned/next/tasks/demo-workshop.md +150 -0
  140. package/src/docs/versioned/next/tasks/describe-model-schema.md +5 -5
  141. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +6 -3
  142. package/dist/docs/ui-types-documentation.json +0 -33
  143. package/src/docs/ui-types-documentation.json +0 -33
@@ -0,0 +1,219 @@
1
+ # Implementation Plan: Demo workshop
2
+
3
+ **Status:** Approved
4
+ **Branch:** `cursor/demo-workshop-cc39`
5
+ **Owner:** —
6
+ **Created:** 2026-09-26
7
+ **Task list:** [demo-workshop.md](../tasks/demo-workshop.md)
8
+ **Depends on:** —
9
+ **RTK deprecation flag:** None
10
+ **Supersedes:** the visual-regression non-goal in [examples-demo-coverage.md](examples-demo-coverage.md). Story coverage from that plan stays as it is.
11
+
12
+ ## Goal
13
+
14
+ Turn the Expo demo into the workshop for `@terreno/ui`: a person can find a component, copy a usage snippet, change theme and viewport from the URL, and see whether every stable story passes interaction, accessibility, and visual checks on web and Android.
15
+
16
+ Released demo majors stay online. The site opens on the newest released major (57 today). A dropdown lists older majors, such as 56, and the next major when `preview/<major>` exists, such as 58 preview.
17
+
18
+ ## Non-Goals
19
+
20
+ - An addon or plugin system.
21
+ - iOS simulator runs, iOS screenshot baselines, and iOS accessibility checks.
22
+ - A translation catalog for `@terreno/ui`.
23
+ - The commented combinatorial prop matrix in `demo/app/demo/[component].tsx`.
24
+ - A pull-request comment bot.
25
+ - Rebuilding frozen majors on later master commits.
26
+ - Android baselines for historical majors. Android CI proves the branch under test.
27
+ - Feature-branch Netlify aliases (`pr-*`) appearing in the version dropdown.
28
+
29
+ ## Decisions
30
+
31
+ | Question | Decision |
32
+ |----------|----------|
33
+ | Platforms | Web and Android in this plan. iOS is a later plan. |
34
+ | Visual coverage | Every stable named story. A story may opt out only with `stability: "exclude"` and a non-empty reason. |
35
+ | Usage snippets | One curated `usageExample` string on each story config, shown with a copy button. |
36
+ | Preview controls | Theme, viewport, background, locale, right-to-left, and reduced motion on demo and dev routes. State lives in the URL. Embeds apply the query and hide the bar. |
37
+ | Theme values | Light, plus the existing canonical dark role remap (`demo/components/palette/darkTheme.ts`). Custom palettes stay on `/palette`. |
38
+ | Viewports | The Box breakpoints already documented for web: 320, 375, 1024, and 1280. |
39
+ | Locale | The toolbar passes the selected locale only to components that already read one (consent content, date and calendar formatting). Right-to-left is a separate control. |
40
+ | Collaboration | CI artifacts and a check summary. No pull-request comment. |
41
+ | Agent access | Public `@terreno/mcp` tools read a generated demo catalog and the version manifest URL. |
42
+ | Visual comparison | Separate cropped web and Android PNG baselines. Reuse the chart pixel matcher. An Android-specific tolerance requires a note in the how-to. |
43
+ | Android CI | The full Android suite blocks pull requests that change `ui/` or `demo/`. |
44
+ | Preview branch | `preview/<major>` (for example `preview/58`) is the only branch labeled as the next-major preview. |
45
+ | Frozen majors | When master moves to a new major, keep the previous major at its last master commit. The current major redeploys on every master merge. Backfill 56 from its last master commit. |
46
+ | Version dropdown | Persistent top bar on the home, demo, and dev pages. Default is the highest released major, which is the production deploy. |
47
+ | Docs embeds | A docs version embeds the demo major it documents. The next docs version embeds `preview/<major>` when that deploy exists. |
48
+ | Accessibility | Web uses axe and fails a story on serious or critical violations. Android asserts the accessibility tree from the same step list. Moderate axe findings are warnings in the health report. |
49
+ | Interaction source | One declarative step list per story. Playwright runs it on web. A generated Maestro flow runs it on Android. |
50
+
51
+ ## Architecture
52
+
53
+ ```text
54
+ demo/demoConfig.tsx
55
+ usageExample, related routes, story stability, interaction steps
56
+ │
57
+ ├─ home/dev search + category filters + linked related components
58
+ ├─ preview bar (URL state) and version bar (versions.json)
59
+ ├─ web Playwright: interactions, axe, cropped PNG
60
+ ├─ Android Maestro: same steps, accessibility tree, cropped PNG
61
+ └─ demo-catalog.json ── @terreno/mcp list/get tools
62
+
63
+ master deploy ── production site = current major (57)
64
+ last 56 master commit ── Netlify alias v56 (frozen)
65
+ preview/58 ── Netlify alias v58 (preview: true)
66
+ production /versions.json ── dropdown on every major
67
+ ```
68
+
69
+ ### Catalog contract
70
+
71
+ `DemoConfiguration` gains:
72
+
73
+ - `usageExample: string` — copyable usage, including the import when it is a normal `@terreno/ui` import.
74
+ - `related` entries resolve through `findDemoConfig`. Unknown names fail the catalog test.
75
+ - `stories[name].stability`: `"stable"` by default, or `"exclude"` with `excludeReason`.
76
+ - `stories[name].showInDemo` is honored. `false` hides that story from demo mode and keeps it on the dev route.
77
+ - `stories[name].interactions`: ordered steps `{action: "press" | "type" | "expectText" | "expectRole", targetTestID?, value?, role?, name?}`.
78
+ - Control defaults come from the control type: `false` for boolean, `0` for number, `""` for string. The `""` fallback for every type goes away.
79
+
80
+ `demo/collectRegisteredStoryRenders.ts` remains the inventory. Story ids stay `<Component> / <story>`.
81
+
82
+ ### Preview and version chrome
83
+
84
+ `DemoChrome` wraps home, demo, and dev. The version dropdown is on every non-embed page. Preview controls are on demo and dev component routes. `?embed=1` hides both and still applies preview query params.
85
+
86
+ Preview query keys: `theme`, `viewport`, `background`, `locale`, `rtl`, `reducedMotion`.
87
+
88
+ The version dropdown does not switch code inside one bundle. It navigates to that major's deploy. Every bundle loads `versions.json` from the production demo origin so a frozen major learns about a new preview without a rebuild. That file is served with CORS for the demo aliases.
89
+
90
+ ```typescript
91
+ interface DemoVersionManifest {
92
+ currentMajor: number;
93
+ versions: {
94
+ major: number;
95
+ label: string;
96
+ url: string;
97
+ preview: boolean;
98
+ released: boolean;
99
+ }[];
100
+ }
101
+ ```
102
+
103
+ `currentMajor` is the highest `released && !preview` entry. Production is that major. `preview/58` adds `{major: 58, label: "58 preview", preview: true, released: false}` while the branch exists. Merging 58 to master freezes 57 onto alias `v57`, points production at 58, and removes the preview flag.
104
+
105
+ Feature-branch aliases stay out of the manifest.
106
+
107
+ ### Proof
108
+
109
+ | Check | Web | Android |
110
+ | --- | --- | --- |
111
+ | Render | Existing `demo/storiesSmoke.test.tsx` | Maestro opens the story |
112
+ | Interaction | Playwright runs `interactions` | Generated Maestro flow |
113
+ | Accessibility | axe, serious and critical fail | Accessibility-tree assertions from `expectRole` / labels |
114
+ | Visual | Cropped PNG in `demo/rendered-snapshots/web/` | Cropped PNG in `demo/rendered-snapshots/android/` |
115
+
116
+ Chart goldens stay in `demo/rendered-snapshots/` until the chart task moves them. The chart compare script keeps working for chart fixtures.
117
+
118
+ A stable story without a web golden, an Android golden, or an exclusion fails CI. The same rule applies to interaction steps.
119
+
120
+ `demo/story-health.json` is produced by those jobs and copied into the demo build. The component page shows pass, fail, or not-run for render, interaction, accessibility, and visual, per platform. Missing file means not-run.
121
+
122
+ CI uploads the JSON, PNGs, and diffs as job artifacts and writes a check summary. It does not comment on the pull request.
123
+
124
+ Android runs on a CircleCI Android image with one pinned emulator: Pixel 6, API 34. The job is path-filtered like today's Maestro demo job (`ui/**`, `demo/**`) and is required on those pull requests.
125
+
126
+ ### MCP
127
+
128
+ `bun run demo:catalog` writes `demo/demo-catalog.json` with component name, category, route, snippet, story ids, stability, and the production `versions.json` URL. `@terreno/mcp` exposes:
129
+
130
+ - `terreno_list_demo_stories`
131
+ - `terreno_get_demo_story`
132
+
133
+ Tools return catalog data. They do not drive a browser and they do not run Android.
134
+
135
+ ### Docs embeds
136
+
137
+ `website` `DEMO_URL` becomes per docs version. Version 57.x points at the 57 demo alias (production while 57 is current). The next docs version points at the `preview/<major>` URL when the manifest lists that preview, and otherwise at production. `ComponentDemo` keeps `?embed=1`.
138
+
139
+ ## Models
140
+
141
+ None.
142
+
143
+ ## APIs
144
+
145
+ `GET /versions.json` on the production demo host, shape above.
146
+
147
+ MCP tools `terreno_list_demo_stories` and `terreno_get_demo_story`. No new HTTP API on `@terreno/api`.
148
+
149
+ ## Notifications
150
+
151
+ None.
152
+
153
+ ## UI
154
+
155
+ - Home and dev: search box and category filters over `DemoConfig`.
156
+ - Component page: copyable snippet, linked related components, preview controls, per-story health.
157
+ - Persistent version dropdown on home, demo, and dev.
158
+ - Embed mode unchanged except that preview query params apply.
159
+
160
+ Frontend verification uses the running web demo. Android verification uses the emulator flow for Button, then the full stable catalog.
161
+
162
+ ## Phases
163
+
164
+ 1. Catalog contract, search, snippets, and the preview bar. Button is the tracer.
165
+ 2. Web interaction, axe, and visual baselines, then the health report.
166
+ 3. Android emulator CI for Button, then every stable story.
167
+ 4. Version manifest, frozen majors, preview branch, docs embeds.
168
+ 5. MCP catalog tools.
169
+
170
+ ## Feature Flags & Migrations
171
+
172
+ None. New demo query params are optional. Existing `/demo/<component>` URLs keep working. Production keeps serving the current major at the existing demo host.
173
+
174
+ ## Not Included / Future Work
175
+
176
+ - iOS simulator coverage and iOS goldens.
177
+ - Library-wide translations.
178
+ - Demo addon API.
179
+ - Pixel baselines for every historical major.
180
+
181
+ ## Files to Create / Modify
182
+
183
+ Create
184
+
185
+ - `docs/how-to/preview-demo.md`
186
+ - `docs/how-to/demo-versions.md`
187
+ - `docs/how-to/compare-demo-snapshots.md`
188
+ - `demo/components/DemoChrome.tsx`
189
+ - `scripts/demo/` runners and manifest publisher
190
+ - `demo/demo-catalog.json` (generated)
191
+
192
+ Modify
193
+
194
+ - `demo/demoConfig.tsx`, `demo/story-config/*.config.tsx`, `demo/app/demo/[component].tsx`
195
+ - `demo/components/DemoHomePage.tsx`, `demo/components/DevHomePage.tsx`
196
+ - `demo/README.md`
197
+ - `docs/reference/test.md`, `docs/reference/ui.md`, `docs/reference/mcp-server.md`
198
+ - `docs/how-to/run-tests-locally.md`, `docs/how-to/circleci.md`
199
+ - `.circleci/config.yml`, `.circleci/continue-config.yml`
200
+ - `scripts/ci/netlify-deploy.sh`
201
+ - `website/src/components/ComponentDemo/index.tsx`, `website/docusaurus.config.ts`
202
+ - `mcp-server/src/tools.ts`
203
+
204
+ ## Task List
205
+
206
+ [docs/tasks/demo-workshop.md](../tasks/demo-workshop.md)
207
+
208
+ ## Acceptance Criteria
209
+
210
+ - [ ] Search on the home grid finds Button by name and by category, and Button's related components are links.
211
+ - [ ] Every registered component has a `usageExample`, and the Button page copies it.
212
+ - [ ] A URL on the Button demo sets theme, viewport, background, locale, right-to-left, and reduced motion. Embed hides the bar and keeps the query.
213
+ - [ ] Every stable story has web interaction steps or an exclusion reason, an axe result, and a web PNG baseline.
214
+ - [ ] The same stable stories have Android interaction, accessibility-tree, and PNG results. The Android job fails the pull request when `ui/` or `demo/` changes.
215
+ - [ ] The Button page shows health from `story-health.json`. CI publishes a check summary and artifacts and does not comment.
216
+ - [ ] Production opens on major 57. Alias `v56` serves the last major-56 master commit. `preview/58` appears as "58 preview" and is not the default.
217
+ - [ ] A 57 docs page embeds the 57 demo. The next docs version embeds the 58 preview when that alias exists.
218
+ - [ ] `terreno_get_demo_story` returns Button's snippet, route, and the version manifest URL.
219
+ - [ ] `demo/README.md`, the how-to pages named above, and the reference pages named above match this behavior.
@@ -1,6 +1,6 @@
1
1
  # `describeModel()` schema metadata
2
2
 
3
- **Status:** Approved — 2026-08-24
3
+ **Status:** Complete — shipped in #1154
4
4
  **Branch:** `cursor/architecture-ips-a7ec`
5
5
  **Owner:** —
6
6
  **Created:** 2026-08-24
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Reference Documentation Coverage
2
2
 
3
- **Status:** In progress — remaining README, sanitization, and docs-audit work
3
+ **Status:** Complete
4
4
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1009
5
5
  **Priority:** High
6
6
  **Effort:** Big batch
@@ -1,7 +1,7 @@
1
1
  # Implementation Plan: Documentation Site, Versioned Docs, and Docs-Maintenance Skills
2
2
 
3
3
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1086
4
- **Status:** Approved
4
+ **Status:** Complete — shipped in #754
5
5
 
6
6
  *When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Examples, Demo, and Test Coverage
2
2
 
3
- **Status:** Approved — key decisions recorded (2026-07-29)
3
+ **Status:** Complete — shipped in #1299
4
4
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1016
5
5
  **Priority:** Medium
6
6
  **Effort:** Big batch
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Framework-level audit log
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete — shipped in #1301
4
4
  **Branch:** `cursor/framework-audit-log-grow-0d6c`
5
5
  **Owner:** —
6
6
  **Created:** 2026-09-10
@@ -1,6 +1,7 @@
1
1
  # GPTChat optional mascot
2
2
 
3
3
  **Status:** Complete
4
+ **Roadmap:** Area=`ui`, Target=`Released`, Impact=`Feature`
4
5
  **Branch:** `cursor/gpt-chat-mascot-d39f`
5
6
  **Owner:** cloud agent
6
7
  **Created:** 2026-09-15
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Inbound webhook framework
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete — shipped in #1251
4
4
  **Branch:** `cursor/inbound-webhooks-4945`
5
5
  **Owner:** —
6
6
  **Created:** 2026-09-01
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Remove the Knip baseline
2
2
 
3
3
  **Status:** Complete
4
+ **Roadmap:** Area=`dx`, Target=`Released`, Impact=`Improvement`
4
5
  **Created:** 2026-09-09
5
6
  **Owner:** unassigned
6
7
  **Primary packages:** repo-wide (`knip.jsonc`, `scripts/static-analysis/`, every workspace Knip currently fingerprints)
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Move mcp-server doc-sync off the compile path
2
2
 
3
- **Status:** Approved (2026-09-22)
3
+ **Status:** Complete
4
+ **Roadmap:** Area=`dx`, Target=`Released`, Impact=`Improvement`
4
5
  **Scan goal:** reduce-cold-compile-time ([charter](../scans/reduce-cold-compile-time/charter.md)) — cold `bun run compile` 305s → 214s
5
6
  **Slice:** mcp-doc-sync-off-compile-path (round 1) — **must land before** `compile-pipeline-dedup`
6
7
  **Findings:** mcp-doc-sync-on-compile-path (medium, S)
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: MCP service tokens
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete — shipped in #1263
4
+ **Roadmap:** Area=`mcp`, Target=`Released`, Impact=`Feature`
4
5
  **Branch:** TBD
5
6
  **Owner:** unassigned
6
7
  **Created:** 2026-09-03
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Migrate built-in admin to syncdb
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete
4
+ **Roadmap:** Area=`admin`, Target=`Released`, Impact=`Improvement`
4
5
  **Branch:** `cursor/migrate-admin-to-syncdb-de61`
5
6
  **Owner:** Josh Gachnang
6
7
  **Created:** 2026-09-01
@@ -214,7 +214,7 @@ are ported.
214
214
  | `codeql-analysis` | CircleCI job uploading SARIF to GitHub Code Scanning API **or** third-party SAST orb; preserve Security tab if possible |
215
215
  | `dependabot-auto-merge` | Keep Dependabot PRs on GitHub; auto-merge via CircleCI on Dependabot branches **or** Renovate on CircleCI |
216
216
  | `triage.yml` | CircleCI pipeline trigger on `issues` via GitHub Apps webhook → CircleCI API, or GitHub Action **stub** only if product accepts residual GHA (conflicts with CC1 — prefer webhook) |
217
- | `roadmap-generate` | Scheduled CircleCI job with `ROADMAP_PROJECT_TOKEN` |
217
+ | `roadmap-generate`, `roadmap-sync`, `roadmap-reconcile` | **Stay on GitHub Actions** (decided 2026-09-26). They only talk to GitHub (issues, Projects v2, `master` commits via `ROADMAP_PROJECT_TOKEN`), so moving them buys nothing. Exception to CC1. |
218
218
  | `architectural-pr-review` | Implemented: CircleCI job + `CURSOR_API_KEY` / `GITHUB_TOKEN`. Checks out `origin/master`, skips forks. GHA workflow is `push.branches-ignore: ["**"]`. |
219
219
  | Cursor Approval / Security / Bugbot | **Cannot move.** Cursor GitHub App automations, not repo workflows. |
220
220
  | `agentics-maintenance` + `*.lock.yml` gh-aw | Re-home to CircleCI scheduled pipelines calling the same scripts **or** retire features; do not leave gh-aw as the only runner if CC1 holds |
@@ -1,6 +1,6 @@
1
1
  # MongoDB migrations tooling
2
2
 
3
- **Status:** Approved — decisions 2026-09-10
3
+ **Status:** Complete
4
4
  **Branch:** `cursor/mongodb-migrations-tooling-04f9`
5
5
  **Owner:** —
6
6
  **Created:** 2026-09-10
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: In-app notification center
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete — shipped in #1307
4
4
  **Branch:** `cursor/notification-center-ip-b11f`
5
5
  **Owner:** —
6
6
  **Created:** 2026-09-10
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Organizations as a first-class primitive (admin UI + RBAC)
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete
4
4
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1024 (absorbs `orgs-and-teams`; retitle on approve)
5
5
  **Priority:** High
6
6
  **Effort:** Big batch
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Password reset and email verification
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete
4
4
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1023
5
5
  **Priority:** High
6
6
  **Effort:** Big batch
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: API rate limiting
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete
4
4
  **Branch:** `cursor/api-rate-limiting-d03a`
5
5
  **Owner:** —
6
6
  **Created:** 2026-08-30
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Remove legacy RTK realtime in Terreno 58
2
2
 
3
3
  **Status:** Approved
4
+ **Roadmap:** Area=`api`, Target=`58`, Impact=`Breaking`
4
5
  **Branch:** `cursor/deprecate-realtime-for-58`
5
6
  **Owner:** unassigned
6
7
  **Created:** 2026-08-25
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Support answering agent (`@terreno/support`)
2
2
 
3
3
  **Status:** Draft — awaiting approval (Grow ran unattended; every Decisions row marked `assumed` is a recommended default the reviewer can change before Approve)
4
+ **Roadmap:** Area=`ai`, Target=`Future`, Impact=`Feature`
4
5
  **Branch:** `cursor/support-agent-grow-fd63`
5
6
  **Owner:** unassigned
6
7
  **Created:** 2026-09-15
@@ -1,7 +1,7 @@
1
1
  # Implementation Plan: @terreno/syncdb-codegen
2
2
 
3
3
  **Status:** Complete — shipped as `syncdb/src/codegen` + `terreno-syncdb-codegen`
4
- **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1087
4
+ **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1110
5
5
  **Branch:** cursor/syncdb-codegen-in-package-746d
6
6
  **Owner:**
7
7
  **Created:** 2026-07-08
@@ -1,7 +1,7 @@
1
1
  # @terreno/langfuse
2
2
 
3
3
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1093
4
- **Status:** Approved
4
+ **Status:** Complete
5
5
 
6
6
  > **See also:** Native in-app observability (prompts, traces, evals, experiments, review queue) is specified in [ai-observability.md](./ai-observability.md). This IP remains the **Langfuse Cloud/self-host vendor plugin**. Both plugins may be registered; traces/scores fan out (see that IP).
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Upgrade Guides and the `upgrading-terreno` Skill
2
2
 
3
- **Status:** Approved
3
+ **Status:** Complete
4
4
  **Roadmap issue:** https://github.com/TerrenoLabs/terreno/issues/1013
5
5
  **Priority:** High
6
6
  **Effort:** Big batch
@@ -211,7 +211,7 @@ Conversation history with multi-modal prompts.
211
211
  | `userId` | ObjectId | Owner (required) |
212
212
  | `title` | string? | Auto-generated on first `/gpt/prompt` response when empty |
213
213
  | `projectId` | ObjectId? | Optional project association |
214
- | `prompts` | array | Messages: `text`, `type` (`user` \| `assistant` \| `system` \| `tool-call` \| `tool-result`), optional `content` parts, `model`, `rating`, tool fields |
214
+ | `prompts` | array | Messages: `text`, `type` (`user` \| `assistant` \| `system` \| `tool-call` \| `tool-result`), optional `content` parts, `model`, `rating`, tool fields. `text` is required unless `content` has parts; an image-only assistant response saves `text: ""` |
215
215
 
216
216
  **Virtual:** `ownerId` aliases `userId` for `Permissions.IsOwner`.
217
217
 
@@ -254,6 +254,8 @@ GPT project with persistent context and memories.
254
254
  | `/gpt/histories/:id/rating` | PATCH | `IsAuthenticated` | Rate a prompt; body: `{promptIndex, rating: "up" \| "down" \| null}` |
255
255
  | `/gpt/tools` | GET | `IsAuthenticated` | List builtin + MCP tools |
256
256
 
257
+ Generated images (image-output models such as `gemini-3-pro-image`) arrive as SSE `image` events: `{image: {mimeType, url}}` with a base64 data URL. Each image is sent once, even when the model reports it both as a stream file part and in the final `result.files`. The saved assistant prompt stores one `image` content part per image. On later turns, `buildMessages` sends an image-only assistant prompt to the model as the text `[Generated image]`, because providers reject empty assistant turns.
258
+
257
259
  AI resolution order: `x-ai-api-key` header + `createModelFn` → `createServerModelFn(modelId)` → configured `aiService` → demo SSE response when `demoMode` and none available.
258
260
 
259
261
  ### addGptHistoryRoutes(router, options?)
@@ -0,0 +1,146 @@
1
+ # Announcement release packs
2
+
3
+ An announcement release pack is a reviewable directory containing one manifest and one Markdown file per announcement. Import the pack through `POST /announcements/import-release`; imports are idempotent by product, release version, channel, and announcement slug.
4
+
5
+ ## Directory format
6
+
7
+ ```text
8
+ announcements/releases/1.14.0/
9
+ pack.yaml
10
+ staff-required.md
11
+ patient-banner.md
12
+ changelog.md
13
+ ```
14
+
15
+ `pack.yaml` carries release metadata, shared defaults, and the ordered file list:
16
+
17
+ ```yaml
18
+ schema: terreno.announcement-pack/v1
19
+ release:
20
+ product: example
21
+ version: "1.14.0"
22
+ buildNumber: 1842
23
+ channel: production
24
+ defaults:
25
+ platforms: [ios, android, web]
26
+ audienceType: all
27
+ displayMode: feed
28
+ acknowledgementPolicy: dismiss-only
29
+ priority: 0
30
+ announcements:
31
+ - staff-required.md
32
+ - patient-banner.md
33
+ - changelog.md
34
+ ```
35
+
36
+ Each announcement is Markdown with YAML frontmatter:
37
+
38
+ ```markdown
39
+ ---
40
+ slug: staff-1-14-required
41
+ title: "Staff: version 1.14.0 is live"
42
+ displayMode: modal
43
+ audienceType: staff
44
+ acknowledgementPolicy: required
45
+ priority: 20
46
+ primaryAction:
47
+ label: Read the release notes
48
+ url: https://example.com/releases/1.14.0
49
+ ---
50
+
51
+ ## What changed in 1.14.0
52
+
53
+ The updated workflow is now available.
54
+ ```
55
+
56
+ Frontmatter overrides manifest defaults. Supported fields are:
57
+
58
+ | Field | Required | Description |
59
+ | --- | --- | --- |
60
+ | `slug` | Yes | Lowercase, hyphenated stable ID within the release. Re-importing the same slug updates instead of duplicating. |
61
+ | `title` | Yes | Surface title. Include the user-facing release version. |
62
+ | `displayMode` | No | `modal`, `banner`, or `feed`. |
63
+ | `audienceType` | No | `all`, `staff`, or `patient`. |
64
+ | `acknowledgementPolicy` | No | `required` or `dismiss-only`. |
65
+ | `platforms` | No | Any non-empty subset of `ios`, `android`, and `web`. |
66
+ | `priority` | No | Higher values appear first. |
67
+ | `minBuildNumber` | No | Per-item override for the release build number. |
68
+ | `publishAt`, `expiresAt` | No | ISO 8601 timestamps. |
69
+ | `audience` | No | Consumer-defined targeting object. |
70
+ | `primaryAction` | No | Object containing `label` and absolute `url`. |
71
+
72
+ ## Versioning
73
+
74
+ The format has three separate versions:
75
+
76
+ 1. `schema` versions the pack contract. The current value is `terreno.announcement-pack/v1`.
77
+ 2. `release.version` is the user-facing product version and is stored with each imported announcement.
78
+ 3. `release.buildNumber` is the client build and becomes `minBuildNumber` unless an announcement overrides it.
79
+
80
+ The announcement model's numeric `version` is server-managed content revision metadata. Do not put it in a pack. Editing the title or body of an already-published imported announcement increments it and re-shows required content where applicable.
81
+
82
+ ## Example pack
83
+
84
+ `example-backend/announcements/releases/1.14.0/` is a complete pack with a required staff modal, a patient banner, and a changelog feed item. The example backend ships a loader that parses the pack, validates it with `announcementReleaseImportSchema`, and prints or uploads the JSON body:
85
+
86
+ ```bash
87
+ cd example-backend
88
+ bun run announcements:pack announcements/releases/1.14.0 # print JSON
89
+ bun run announcements:pack announcements/releases/1.14.0 --upload http://localhost:4000
90
+ bun run announcements:pack announcements/releases/1.14.0 --publish --upload <api-url>
91
+ ```
92
+
93
+ Uploads use `ANNOUNCEMENTS_UPLOAD_TOKEN`, falling back to the example default `terreno-example-announcement-upload`. Uploads are drafts unless `--publish` is passed.
94
+
95
+ ## Import API
96
+
97
+ Enable automation with a dedicated secret:
98
+
99
+ ```typescript
100
+ new AnnouncementsApp({
101
+ uploadToken: process.env.ANNOUNCEMENTS_UPLOAD_TOKEN,
102
+ });
103
+ ```
104
+
105
+ The example Cloud Run backend seeds `ANNOUNCEMENTS_UPLOAD_TOKEN` from Terraform (`announcements_upload_token`, default `terreno-example-announcement-upload`). Use that bearer token against the example preview or production URL. Preview deploys use the same default until the secret exists.
106
+
107
+ Send the parsed manifest and Markdown files as JSON:
108
+
109
+ ```bash
110
+ curl --fail-with-body \
111
+ -H "Authorization: Bearer $ANNOUNCEMENTS_UPLOAD_TOKEN" \
112
+ -H "Content-Type: application/json" \
113
+ --data @announcement-release.json \
114
+ https://api.example.com/announcements/import-release
115
+ ```
116
+
117
+ The request shape is:
118
+
119
+ ```json
120
+ {
121
+ "release": {
122
+ "product": "example",
123
+ "version": "1.14.0",
124
+ "buildNumber": 1842,
125
+ "channel": "production"
126
+ },
127
+ "defaults": {
128
+ "platforms": ["ios", "android", "web"],
129
+ "displayMode": "feed"
130
+ },
131
+ "announcements": [
132
+ {
133
+ "slug": "changelog",
134
+ "title": "Version 1.14.0",
135
+ "body": "## What changed\n\nRelease details."
136
+ }
137
+ ],
138
+ "publish": false
139
+ }
140
+ ```
141
+
142
+ `publish` defaults to `false`, so new rows are drafts. Pass `"publish": true` only for an intentional live import. Draft re-imports do not unpublish an existing live announcement. An authenticated admin may also call the endpoint without the dedicated upload token.
143
+
144
+ The response reports `created`, `updated`, `unchanged`, and `published` counts plus the ID, slug, status, and content version of each row.
145
+
146
+ The body is validated with the exported Zod schema `announcementReleaseImportSchema` before any row is written. Unknown keys, malformed slugs, empty titles or bodies, invalid enum values, missing `release.product` / `release.version`, and duplicate slugs return **400** `Validation failed` with per-field messages in `meta.fields`. `importAnnouncementRelease` runs the same schema when called directly from server code.
@@ -27,6 +27,7 @@ new TerrenoApp({ userModel: User })
27
27
  | `help.enabled` | `boolean` | `false` | Registers help search/detail routes for MCP and in-app help |
28
28
  | `matchAudience` | `(user, announcement) => boolean` | always `true` | Opaque audience JSON filter composed with `audienceType` via `matchAudienceByType` |
29
29
  | `isStaff` | `(user) => boolean` | `user.admin === true` | Staff check used by `matchAudienceByType` for `audienceType` |
30
+ | `uploadToken` | `string` | unset | Dedicated bearer token for idempotent `POST /announcements/import-release` automation |
30
31
  | `permissions` | partial CRUD overrides | admin-only | Overrides default `IsAdmin` permissions on announcement CRUD |
31
32
 
32
33
  ## Acknowledgement policy
@@ -52,7 +53,7 @@ Legacy MongoDB documents that still store `requiresAcknowledgement: true` map to
52
53
 
53
54
  ## Models
54
55
 
55
- - **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`
56
+ - **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`, and optional imported `release` metadata plus `releaseSlug`
56
57
  - **AnnouncementAcknowledgement** — per-user acknowledgement at a specific `version`
57
58
  - **AnnouncementImpression** — per-view analytics row
58
59
  - **AnnouncementClickEvent** — per-click analytics row (`action: "primaryAction"`, `version` at click time, optional `platform`)
@@ -83,7 +84,7 @@ The plugin applies `matchAudienceByType` before `matchAudience`. When `isStaff`
83
84
  | GET | `/announcements/pending` | Current interrupt (`modal` or `banner` only) + `remainingCount` (`platform` query: `ios` \| `android` \| `web`; optional `version` build number) |
84
85
  | GET | `/announcements/feed` | Paginated published changelog — all display modes (`platform` and optional `version` as above) |
85
86
  | POST | `/announcements/:id/acknowledge` | Record acknowledgement (idempotent per version). Same visibility as pending/feed; **404** when not visible. |
86
- | POST | `/announcements/:id/impression` | Record a view. Same visibility as pending/feed; **404** when not visible. |
87
+ | POST | `/announcements/:id/impression` | Record a view (`{ platform? }`). Same visibility as pending/feed; **404** when not visible. **400** for unknown keys or an invalid `platform`. |
87
88
  | POST | `/announcements/:id/click` | Record a primary-action click (`{ action: "primaryAction", platform? }`; optional `?version=` for min-build visibility). Returns **404** when not visible (checked before action/CTA validation). **400** when `action` is invalid, `primaryAction` is absent, or an explicit `platform` value is invalid. |
88
89
 
89
90
  `current` and feed items include resolved `requiresAcknowledgement` (boolean) and `displayMode` derived from policy resolution and stored fields above.
@@ -96,7 +97,7 @@ Optional integer build number on `GET /pending`, `GET /feed`, and help routes. W
96
97
  - `?version=10` (or higher) shows it
97
98
  - omitting `version` does **not** hide gated items
98
99
 
99
- `POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Click checks visibility before action/CTA validation. Returns **400** when `action` is not `"primaryAction"`, the announcement has no `primaryAction`, or an explicit body `platform` is not `ios` / `android` / `web` (omit `platform` to use query/user-agent resolution). Each click inserts a new row (not idempotent). Acknowledge is idempotent per `(userId, announcementId, version)`.
100
+ `POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Impression and click bodies are validated with strict Zod schemas (`{ platform? }` and `{ action: "primaryAction", platform? }`); unknown keys or invalid values return **400** `Validation failed` with per-field messages in `meta.fields`. Click validates `platform` first, then checks visibility before validating `action` and the CTA. Returns **400** when `action` is not `"primaryAction"`, the announcement has no `primaryAction`, or an explicit body `platform` is not `ios` / `android` / `web` (omit `platform` to use query/user-agent resolution). Each click inserts a new row (not idempotent). Acknowledge is idempotent per `(userId, announcementId, version)`.
100
101
 
101
102
  ## Admin routes
102
103
 
@@ -104,9 +105,12 @@ Admin CRUD is on `/announcements` via `modelRouter`. Custom actions:
104
105
 
105
106
  - `GET /announcements/config` — returns `{ data: { defaultAcknowledgementPolicy } }` from the plugin constructor (admin only; defaults to `"dismiss-only"` when omitted). Used to pre-fill the admin editor acknowledgement policy field.
106
107
  - `GET /announcements/overview` — paginated admin dashboard with per-announcement metrics and aggregate totals. Access uses `adminOverviewPermissions` (defaults to `IsAdmin`). Query: `page` (default `1`), `limit` (default `20`, max `100`).
108
+ - `POST /announcements/import-release` — idempotently imports up to 100 announcements keyed by release product/version/channel plus slug. Admin auth or the configured `uploadToken` bearer token is required. New items default to draft; request `publish: true` to publish them. The example deployment's token is `terreno-example-announcement-upload`, seeded by Terraform.
107
109
  - `POST /announcements/:id/publish` — draft → published
108
110
  - `POST /announcements/:id/archive` — published → archived
109
111
 
112
+ See [Announcement release packs](announcement-release-packs.md) for the Markdown/frontmatter format, JSON request shape, version rules, and draft/live behavior.
113
+
110
114
  ### `GET /announcements/overview`
111
115
 
112
116
  Admin-only list for launch dashboards. Includes **all** lifecycle statuses (`draft`, `published`, `archived`). Soft-deleted announcements and event rows are excluded.