zz-meridian 0.8.0 → 0.10.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 (126) hide show
  1. package/dist/imports.js +32 -0
  2. package/dist/migrations.js +41 -0
  3. package/dist/ownership.js +2 -2
  4. package/dist/session.js +12 -1
  5. package/package.json +1 -1
  6. package/payload/CHANGELOG.md +51 -0
  7. package/payload/CONTRIBUTING.md +4 -2
  8. package/payload/app/(dashboard)/(overview)/README.md +19 -2
  9. package/payload/app/(dashboard)/(overview)/page.tsx +13 -6
  10. package/payload/app/(dashboard)/analytics/README.md +13 -1
  11. package/payload/app/(dashboard)/analytics/page.tsx +10 -4
  12. package/payload/app/(dashboard)/customers/README.md +12 -1
  13. package/payload/app/(dashboard)/health/README.md +9 -1
  14. package/payload/app/(dashboard)/health/page.tsx +6 -5
  15. package/payload/app/(dashboard)/keys/README.md +8 -1
  16. package/payload/app/(dashboard)/members/README.md +12 -1
  17. package/payload/app/(dashboard)/requests/README.md +16 -2
  18. package/payload/app/(dashboard)/requests/[id]/README.md +12 -1
  19. package/payload/app/(dashboard)/requests/[id]/page.tsx +3 -1
  20. package/payload/app/(dashboard)/requests/page.tsx +2 -2
  21. package/payload/app/(dashboard)/settings/README.md +7 -0
  22. package/payload/app/api/assistant/route.ts +23 -3
  23. package/payload/app/embed/health/README.md +6 -1
  24. package/payload/app/embed/health/page.tsx +14 -9
  25. package/payload/app/embed/health/view.tsx +5 -10
  26. package/payload/app/embed/overview/README.md +6 -1
  27. package/payload/app/embed/overview/page.tsx +13 -17
  28. package/payload/app/embed/overview/view.tsx +15 -30
  29. package/payload/app/embed/proposal/README.md +8 -1
  30. package/payload/app/embed/proposal/page.tsx +2 -1
  31. package/payload/app/embed/proposal/view.tsx +28 -16
  32. package/payload/app/embed/requests/README.md +6 -1
  33. package/payload/app/embed/requests/page.tsx +9 -7
  34. package/payload/app/embed/requests/view.tsx +9 -15
  35. package/payload/app/system/(atlas)/[section]/[id]/page.tsx +23 -1
  36. package/payload/app/system/preview/[section]/[card]/keys.ts +0 -1
  37. package/payload/app/system/preview/[section]/[card]/stage.tsx +1 -1
  38. package/payload/decisions/0011-one-context-two-agents.md +36 -0
  39. package/payload/docs/agents.md +36 -13
  40. package/payload/docs/assistant.md +8 -6
  41. package/payload/docs/benchmark.md +18 -0
  42. package/payload/docs/surfaces.md +6 -5
  43. package/payload/docs/voice.md +1 -1
  44. package/payload/package.json +1 -1
  45. package/payload/scripts/assistant.ts +50 -5
  46. package/payload/scripts/audit.ts +23 -6
  47. package/payload/scripts/check.ts +20 -0
  48. package/payload/scripts/fake-llm.ts +9 -0
  49. package/payload/scripts/interactions.ts +3 -0
  50. package/payload/scripts/keyboard.ts +5 -3
  51. package/payload/scripts/lib/chrome.ts +6 -0
  52. package/payload/scripts/verify.ts +3 -2
  53. package/payload/skills/zz-meridian/references/agents.md +36 -13
  54. package/payload/skills/zz-meridian/references/cache.md +16 -3
  55. package/payload/skills/zz-meridian/references/customize.md +17 -3
  56. package/payload/skills/zz-meridian/references/existing-project.md +21 -0
  57. package/payload/skills/zz-meridian/references/ownership.md +1 -1
  58. package/payload/skills/zz-meridian/references/update.md +16 -0
  59. package/payload/skills/zz-meridian/references/voice.md +1 -1
  60. package/payload/src/components/base/shell/index.tsx +21 -5
  61. package/payload/src/components/base/surface/README.md +2 -1
  62. package/payload/src/components/base/surface/index.tsx +40 -7
  63. package/payload/src/components/base/use-share-view.ts +21 -8
  64. package/payload/src/components/charts/meridian/README.md +1 -0
  65. package/payload/src/components/charts/meridian/index.tsx +12 -2
  66. package/payload/src/components/charts/trend-chart/README.md +2 -0
  67. package/payload/src/components/charts/trend-chart/index.tsx +12 -2
  68. package/payload/src/components/charts/trend-chart/preview.tsx +12 -0
  69. package/payload/src/components/patterns/ask-about/README.md +5 -2
  70. package/payload/src/components/patterns/ask-about/index.tsx +9 -3
  71. package/payload/src/components/patterns/assistant/index.tsx +17 -3
  72. package/payload/src/components/patterns/data-table/README.md +3 -3
  73. package/payload/src/components/patterns/data-table/index.tsx +19 -13
  74. package/payload/src/components/patterns/filter-bar/README.md +2 -1
  75. package/payload/src/components/patterns/filter-bar/index.tsx +5 -1
  76. package/payload/src/components/patterns/metric-tile/README.md +5 -3
  77. package/payload/src/components/patterns/metric-tile/index.tsx +34 -2
  78. package/payload/src/components/patterns/metric-tile/preview.tsx +12 -0
  79. package/payload/src/data/access.ts +16 -3
  80. package/payload/src/data/collections.ts +88 -3
  81. package/payload/src/data/metrics.ts +64 -0
  82. package/payload/src/data/request-methods.ts +2 -0
  83. package/payload/src/data/requests.ts +22 -4
  84. package/payload/src/data/sample.ts +3 -3
  85. package/payload/src/lib/agent-guidance.ts +11 -0
  86. package/payload/src/lib/assistant/prompt.ts +25 -5
  87. package/payload/src/lib/assistant/respond.ts +21 -3
  88. package/payload/src/lib/assistant/tools.ts +40 -6
  89. package/payload/src/lib/host.ts +1 -1
  90. package/payload/src/lib/insight.ts +81 -0
  91. package/payload/src/lib/shared-context.ts +104 -0
  92. package/payload/src/system/atlas-shell.tsx +1 -1
  93. package/payload/src/system/card-stage.tsx +2 -1
  94. package/payload/src/system/fixtures/sample.ts +4 -0
  95. package/payload/src/system/page-stage.tsx +24 -8
  96. package/payload/src/system/preview-loaders.ts +0 -1
  97. package/payload/src/system/registry.ts +0 -1
  98. package/payload/src/system/sample-cells.tsx +1 -1
  99. package/payload/src/views/analytics-context.ts +79 -0
  100. package/payload/src/views/analytics.tsx +23 -17
  101. package/payload/src/views/customers-context.ts +72 -0
  102. package/payload/src/views/customers.tsx +6 -3
  103. package/payload/src/views/health-context.ts +53 -0
  104. package/payload/src/views/health.tsx +8 -3
  105. package/payload/src/views/keys-context.ts +38 -0
  106. package/payload/src/views/keys.tsx +3 -0
  107. package/payload/src/views/members-context.ts +40 -0
  108. package/payload/src/views/members.tsx +4 -0
  109. package/payload/src/views/overview-context.ts +179 -0
  110. package/payload/src/views/overview.tsx +44 -29
  111. package/payload/src/views/request-context.ts +46 -0
  112. package/payload/src/views/request.tsx +4 -1
  113. package/payload/src/views/requests-context.ts +85 -0
  114. package/payload/src/views/requests.tsx +12 -6
  115. package/payload/src/views/sample-footer.tsx +20 -7
  116. package/payload/src/views/settings.tsx +40 -2
  117. package/payload/src/views/tools.ts +142 -0
  118. package/payload/tests/agents.test.tsx +12 -0
  119. package/payload/tests/assistant-guard.test.ts +12 -0
  120. package/payload/tests/embed-requests-provenance.test.tsx +4 -2
  121. package/payload/tests/empty-views.test.tsx +3 -3
  122. package/payload/tests/request-view-replay.test.tsx +1 -1
  123. package/payload/tests/shared-context.test.tsx +167 -0
  124. package/payload/src/components/charts/timeline/README.md +0 -74
  125. package/payload/src/components/charts/timeline/index.tsx +0 -148
  126. package/payload/src/components/charts/timeline/preview.tsx +0 -27
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Import specifiers in a common form, so two copies of a module that differ only in how they name the same modules
3
+ * (`'../../ui/badge/index'` beside `'@/components/ui/badge'`) compare equal. `update` uses it to tell a file the team
4
+ * only re-styled from one it changed (issue #16). Pure: the caller passes the aliases it read.
5
+ */
6
+ import path from 'node:path';
7
+ /** The project's `@/*`-style aliases from its tsconfig's `paths` (best effort; a file it cannot read gives none). */
8
+ export function aliasesOf(tsconfig) {
9
+ const out = new Map([['@meridian/', 'src/']]);
10
+ if (!tsconfig)
11
+ return out;
12
+ // `"@/*": ["./src/*"]` maps `@/` to `src/`; `"@/*": ["./*"]` maps it to the project root.
13
+ for (const m of tsconfig.matchAll(/"(@[\w-]*)\/\*"\s*:\s*\[\s*"([^"*]*)\*"/g))
14
+ out.set(`${m[1]}/`, m[2].replace(/^\.\//, ''));
15
+ return out;
16
+ }
17
+ /** `text` with every literal module specifier rewritten as the project path it names, extension and `/index` dropped. */
18
+ export function canonicalImports(rel, text, aliases) {
19
+ const canonical = (spec) => {
20
+ let to = null;
21
+ if (spec.startsWith('.'))
22
+ to = path.posix.normalize(path.posix.join(path.posix.dirname(rel), spec));
23
+ else
24
+ for (const [alias, dir] of aliases)
25
+ if (spec.startsWith(alias)) {
26
+ to = path.posix.normalize(dir + spec.slice(alias.length));
27
+ break;
28
+ }
29
+ return to === null ? spec : to.replace(/\.(tsx?|jsx?|mjs)$/, '').replace(/\/index$/, '');
30
+ };
31
+ return text.replace(/(\bfrom\s*|\bimport\s*\(\s*|\bimport\s+)(['"])([^'"\n]+)\2/g, (_m, lead, q, spec) => `${lead}${q}${canonical(spec)}${q}`);
32
+ }
@@ -205,7 +205,48 @@ export const RELEASE_MIGRATIONS = [
205
205
  instructions: 'Read "Writes authorize, then invalidate" and "The assistant route" in references/cache.md. Call `resolveAccess()` first in the route and in each Server Action, answer 401 or an error result when there is no session, and hand the assistant only the collections the caller may read. The route passes a guard that authorizes each approved change again when it runs, so `src/lib/assistant/tools.ts` and `src/lib/assistant/respond.ts` take that guard too: bring them over from the release with the route.',
206
206
  checks: GATE_AND_BUILD,
207
207
  },
208
+ {
209
+ id: 'share-view-context',
210
+ since: '0.9.0',
211
+ applies: (root) => matching(root, (_, src) => callArity(src, 'useShareView').some((n) => n === 2)),
212
+ summary: 'useShareView takes one shared context, not a sentence and an object.',
213
+ instructions: 'Read "Legible" in references/agents.md and "A shared context" in references/customize.md. Build the view\'s context with the fields of `SharedContext` from `src/lib/shared-context.ts` (scope, freshness, each figure with its unit, change and definition, what code finds, what the data cannot say) in a pure function beside the view, and pass it alone: `useShareView(context)`. Both agents read it: an MCP host and the console\'s assistant.',
214
+ checks: GATE_AND_BUILD,
215
+ },
216
+ {
217
+ id: 'assistant-view-tools',
218
+ since: '0.10.0',
219
+ applies: (root) => {
220
+ const route = text(root, 'app/api/assistant/route.ts');
221
+ const tools = text(root, 'src/lib/assistant/tools.ts');
222
+ const respond = text(root, 'src/lib/assistant/respond.ts');
223
+ if (route === null || tools === null || respond === null)
224
+ return null;
225
+ // The 0.9.0 assistant takes the views and tells the model its limits; one kept from before does neither.
226
+ const stale = [...(/\bViewTool\b/.test(tools) ? [] : ['src/lib/assistant/tools.ts']), ...(/\blimitsOf\b/.test(respond) ? [] : ['src/lib/assistant/respond.ts']), ...(/\bviews\s*:/.test(route) ? [] : ['app/api/assistant/route.ts'])];
227
+ return stale.length > 0 ? stale : null;
228
+ },
229
+ summary: "The assistant's route-side code predates view tools and its limits.",
230
+ instructions: 'Read "Adding an MCP server later" in example:docs/assistant.md. Bring `src/lib/assistant/tools.ts` and `src/lib/assistant/respond.ts` over from the release: `assistantTools(collections, writer, guard, views)` offers every view in `src/views/tools.ts` as a read-only `view_<name>` tool, `respond` lists what the assistant cannot do or see (`limitsOf`) and passes the views on, and the guard may take a `record`. Then pass `views` from the assistant route to `respond`: the template\'s `viewTools`, an adopted project\'s own view tools (`ViewTool` in `src/lib/shared-context.ts`), or none.',
231
+ checks: GATE_AND_BUILD,
232
+ },
233
+ {
234
+ id: 'agent-reads-section',
235
+ since: '0.9.0',
236
+ applies: pagesWithoutAgentReads,
237
+ summary: 'A page under app/(dashboard)/ or app/embed/ says what the agent reads.',
238
+ instructions: 'Read "A page spec" in references/customize.md. Under `## Agents` in the page\'s README.md, add `### What the agent reads`: what the page\'s shared context tells each agent, with a real line of it, and what the MCP App and the console\'s assistant each receive. `scripts/check.ts` requires it.',
239
+ checks: ['gate'],
240
+ },
208
241
  ];
242
+ /** The page specifications under the console and embed folders that lack the part the 0.9.0 check requires. */
243
+ function pagesWithoutAgentReads(root) {
244
+ const files = [];
245
+ for (const d of ['app/(dashboard)', 'app/embed'])
246
+ walk(root, d, files, /^README\.md$/);
247
+ const hits = files.filter((rel) => !/^## Agents\n[\s\S]*?^### What the agent reads$/m.test(text(root, rel) ?? ''));
248
+ return hits.length > 0 ? hits : null;
249
+ }
209
250
  /** The registry entries that apply to a project moving from `source` to `target`, as journal migrations. */
210
251
  export function releaseMigrations(root, source, target) {
211
252
  const out = [];
package/dist/ownership.js CHANGED
@@ -1,14 +1,14 @@
1
1
  const HASH = /^sha256-[0-9a-f]{64}$/;
2
2
  const SEMVER = /^\d+\.\d+\.\d+$/;
3
3
  /** The library modules Meridian's components and gates import; the rest of src/lib belongs to the template's pages. */
4
- export const LIB = ['cn', 'format', 'format-date', 'period', 'color', 'host', 'preferences', 'csv', 'safe-markdown', 'logo', 'collection', 'live'].map((n) => `src/lib/${n}.ts`);
4
+ export const LIB = ['cn', 'format', 'format-date', 'period', 'color', 'host', 'preferences', 'csv', 'safe-markdown', 'logo', 'collection', 'live', 'shared-context', 'agent-guidance', 'insight'].map((n) => `src/lib/${n}.ts`);
5
5
  /** The single files adopt copies besides the folders and the library: all of them must be in a payload that is whole. */
6
6
  export const FIXED = [...LIB, 'src/lib/assistant/prompt.ts', 'src/views/console-chrome.tsx', 'tests/setup.ts'];
7
7
  /** The one rule, a plain-English line per clause. `references/ownership.md` renders it, so the guidance cannot drift. */
8
8
  export const OWNERSHIP = [
9
9
  'Managed: the payload files under `tokens/`, `src/styles/` and `src/components/`, except each component\'s `README.md` and `preview.tsx`.',
10
10
  'Managed: the distributed scripts under `scripts/`, except `scripts/verify.config.ts`, the first-load baseline `optional:scripts/verify.baseline.json` and a team-local `optional:scripts/check.local.ts`.',
11
- 'Managed: the library helpers `src/lib/cn.ts`, `src/lib/format.ts`, `src/lib/format-date.ts`, `src/lib/period.ts`, `src/lib/color.ts`, `src/lib/host.ts`, `src/lib/preferences.ts`, `src/lib/csv.ts`, `src/lib/safe-markdown.ts`, `src/lib/logo.ts`, `src/lib/collection.ts` and `src/lib/live.ts`, and the assistant prompt `src/lib/assistant/prompt.ts`.',
11
+ 'Managed: the library helpers `src/lib/cn.ts`, `src/lib/format.ts`, `src/lib/format-date.ts`, `src/lib/period.ts`, `src/lib/color.ts`, `src/lib/host.ts`, `src/lib/preferences.ts`, `src/lib/csv.ts`, `src/lib/safe-markdown.ts`, `src/lib/logo.ts`, `src/lib/collection.ts`, `src/lib/live.ts`, `src/lib/shared-context.ts`, `src/lib/agent-guidance.ts` and `src/lib/insight.ts`, and the assistant prompt `src/lib/assistant/prompt.ts`.',
12
12
  'Managed: `src/views/console-chrome.tsx` and `tests/setup.ts`.',
13
13
  'Managed: `optional:scripts/package.json`, only when the adopt or create that set the project up generated it for this project shape.',
14
14
  'Managed: both installed skill trees, `optional:.agents/skills/zz-meridian/` and `optional:.claude/skills/zz-meridian/`, taken from the release\'s skill payload.',
package/dist/session.js CHANGED
@@ -8,6 +8,7 @@ import fs from 'node:fs';
8
8
  import path from 'node:path';
9
9
  import { managedBlock } from './context.js';
10
10
  import { inside, packageManager, run as spawn, sha256 } from './files.js';
11
+ import { aliasesOf, canonicalImports } from './imports.js';
11
12
  import { changedPaths, takeInventory } from './inventory.js';
12
13
  import { renderMergeReport } from './merge-report.js';
13
14
  import { releaseMigrations } from './migrations.js';
@@ -145,6 +146,7 @@ function plan(ctx, manifest, startedAt) {
145
146
  if (!isSafePath(p) || FORBIDDEN.test(p))
146
147
  throw new Refusal(`unsafe path ${JSON.stringify(p)}; nothing was written`);
147
148
  const sourcePayload = new Set(source.payload);
149
+ const aliases = aliasesOf(readText(root, 'tsconfig.json'));
148
150
  const operations = [];
149
151
  const retired = [];
150
152
  const targetFiles = {};
@@ -159,7 +161,16 @@ function plan(ctx, manifest, startedAt) {
159
161
  const disk = probe(root, p);
160
162
  if (disk.unsafe && (managed || keptReason.has(p)))
161
163
  throw new Refusal(`${p} is a symbolic link or not a regular file; nothing was written`);
162
- const { disposition, action } = classify({ recorded: recorded, target: targetHash, disk: disk.side.hash, managed, kept: keptReason.has(p) });
164
+ // A managed module the team only re-styled (its imports named another way, nothing else changed) is untouched: the
165
+ // release's copy replaces it instead of staging a merge nobody needs (issue #16).
166
+ let diskHash = disk.side.hash;
167
+ if (managed && recorded && diskHash && diskHash !== recorded && /\.(?:tsx?|jsx?|mjs)$/.test(p) && fs.existsSync(inside(source.tree, p))) {
168
+ const ours = readText(root, p);
169
+ const base = fs.readFileSync(inside(source.tree, p), 'utf8');
170
+ if (ours !== null && canonicalImports(p, ours, aliases) === canonicalImports(p, base, aliases))
171
+ diskHash = recorded;
172
+ }
173
+ const { disposition, action } = classify({ recorded: recorded, target: targetHash, disk: diskHash, managed, kept: keptReason.has(p) });
163
174
  operations.push({ path: p, disposition, action, base: sideOf(recorded), ours: disk.side, target: sideOf(targetHash), applied: false, appliedHash: null });
164
175
  if (disposition === 'retired-kept')
165
176
  retired.push({ path: p, baselineHash: recorded, reason: keptReason.get(p) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Bring ZZ Meridian, a dashboard design system, into a Next.js project, or start a new dashboard on it. Copies the files in; nothing depends on this package at runtime.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -2,6 +2,57 @@
2
2
 
3
3
  Every release of ZZ Meridian, newest first. Versions follow semver: a removed or renamed token, prop or card is major; a new card, token or variant is minor; a corrected value is a patch. Each entry says what breaks and what to do instead.
4
4
 
5
+ ## [0.10.0] · 2026-10-07
6
+
7
+ ### Added
8
+
9
+ - **`audit --atlas`.** The browser audit over the Design Atlas: every card's page and its bare preview, at 1440 and 390px in both themes, about ten minutes. Not part of `pnpm verify`, which stays fast (issue #9).
10
+ - **`ShareContext`.** `useShareView` as a component that renders nothing, for a server page with no client view (issue #17).
11
+ - **A database guide.** `references/existing-project.md` covers a pool that survives a dropped connection, a transaction pooler, schema changes on start, and a role that cannot create (issues #16, #17).
12
+ - **`assistant-view-tools` migration.** `update` reports an assistant whose route-side code predates view tools and its limits (issue #17).
13
+
14
+ ### Changed
15
+
16
+ - **Export is offered on a phone.** The Overview's and Analytics' Export were hidden below 640px; a page's actions are offered on every width (issue #13).
17
+ - **`update` sees past import style.** A managed module whose only difference from its release copy is how its imports are written (`@/…` for `../../…`) is untouched, not a merge (issue #16).
18
+ - **A specimen is not the page.** The audit keeps a card preview's own headings and scroll regions out of the page's outline and its one scroller (issue #8).
19
+ - **A control's role needs its keys.** `scripts/check.ts` fails an element that claims a control's role (`button`, `tab`, `switch` and the rest) with no `onKeyDown` (issue #11).
20
+
21
+ ### Fixed
22
+
23
+ - **A narrow table becomes cards on any screen.** `DataTable`'s card list follows the table's own width (under 640px), as `hideBelow` does, so a table in a half-width row or beside the assistant's column no longer runs past its frame. Found by the first `audit --atlas`, with the two below.
24
+ - **An active filter's label reads.** A filter that is on keeps its name in `ink-2` on the accent tint; `ink-3` fell to 4.04:1 in light.
25
+ - **A selected row's facts read on a phone.** On the accent wash, a card's facts step up to `ink-2`; `ink-3` fell to 4.43:1 in light.
26
+ - **The presses count a file chooser.** A file picker's button opens the system's chooser, which changes nothing in the DOM; the presses now intercept it and count it as an answer (issue #16).
27
+ - **verify's assistant-off run ignores `.env.local`.** It blanks every name the assistant reads, set in the shell or not (issue #16).
28
+
29
+ ### Breaking
30
+
31
+ - **`Timeline` is removed.** No page used it, and a card exists because a page needs it (`CONTRIBUTING.md`). Import nothing from `src/components/charts/timeline`.
32
+
33
+ ## [0.9.0] · 2026-10-07
34
+
35
+ ### Added
36
+
37
+ - **Every page tells both agents the same thing (decision 0011).** A view builds one shared context (`src/lib/shared-context.ts`, one producer per page in `src/views/*-context.ts`): its scope and freshness, what the person points at, every figure with its unit, its change and its definition, what code computed from them (`src/lib/insight.ts`: a day against its median, a share, a ranking, a run of days, what nothing recorded explains), and what the data cannot say. An MCP host receives it through `ui/update-model-context`, inline and in fullscreen; the console's assistant receives it in its prompt, with the page's address and query. All nine console pages and the four embed views share one.
38
+ - **Every view has a tool.** `src/views/tools.ts` holds each view's contract: its name, its address as input, its `ui://` resource, and one read that returns what it renders and what the agents are told. The console's assistant offers each as a read-only `view_<name>`; `docs/agents.md` registers the same list on an MCP server, with the apply tool of a change registered view-only. The embed routes render from their tool's read.
39
+ - **The assistant is told its limits.** Its prompt lists what it cannot do or see, from each collection's `pageOnly`, `hidden` and missing operations, so it can say why.
40
+ - **Ask hands a card to the console's assistant.** With the assistant on, Ask appears on the featured card of Overview, Requests, Health and Analytics; a press opens the panel and sends the card's question with the page's context (`ConsoleSurface`). Ask renders only after hydration.
41
+ - **An MCP view says when the host refuses.** A refused context is sent once more, and Ask then carries the view's address; a refused Ask tells the person in a toast with the question's words.
42
+ - **The person sees the findings.** `MetricTile` takes `finding` (a line, with a day it points the Meridian there) and `baseline` ("2.7× usual" beside a day being read); `TrendChart` takes `baseline`, a dashed median line. The Overview uses all three.
43
+ - **An agent's change leaves an Activity line.** The assistant's guard takes an optional `record`; the template writes to a new `activity` collection, and the Overview's Activity is live.
44
+ - **The Overview, Analytics and Health read through `read()`.** Daily totals, endpoints, responses, services, incidents and activity are read-only collections (`src/data/metrics.ts`).
45
+ - **An address can name a day.** `?day=2026-09-22` opens the Overview pointed at it (`Meridian day`).
46
+
47
+ ### Breaking
48
+
49
+ `update` reports the two that touch the team's own files as migrations, `share-view-context` and `agent-reads-section`, with what to change (`references/update.md`, "Resolving 0.9.0's migrations"); `src/lib/shared-context.ts`, `src/lib/agent-guidance.ts` and `src/lib/insight.ts` are Meridian's now and arrive with the update.
50
+
51
+ - `useShareView(text, structured)` is `useShareView(context)`. Build a context with the fields in `SharedContext`; `contextText` writes the text both agents read.
52
+ - `scripts/check.ts` requires `### What the agent reads` under `## Agents` in the README of every page under `app/(dashboard)/` and `app/embed/`. Add it to a product's own pages, saying what the page's context tells each agent.
53
+ - `src/data/sample.ts` no longer exports `ENDPOINTS`, `STATUS_MIX`, `ACTIVITY`, `INCIDENTS`, `PAST_INCIDENTS`, `SERVICES` or `demoTotals`: read them through `src/data/metrics.ts`. `RequestView` takes `routeP95`.
54
+ - `OverviewBody`, `HealthBody` and `AnalyticsBody` take the period key, `updatedAt` and, for the first and last, the incidents and activity beside the figures; `RequestsView` takes `updatedAt` and `now`.
55
+
5
56
  ## [0.8.0] · 2026-10-06
6
57
 
7
58
  ### Added
@@ -39,7 +39,7 @@ Every card's README follows the same order, so a reader always knows where to lo
39
39
  4. **States**: rest, hover, pressed, focus, selected, disabled, loading, empty, error, each with its transition (property, duration token, curve).
40
40
  5. **Behaviour**: interaction, keyboard, timing.
41
41
  6. **Surfaces**: console, mobile (under 1024px) and embed (an MCP App): what changes on each, or "same", and why.
42
- 7. **Agents**: what an agent may read here, what it may do (and through which proposal), and how its work is marked. Write "Not applicable" for purely presentational parts.
42
+ 7. **Agents**: what an agent may read here, what it may do (and through which proposal), and how its work is marked. Write "Not applicable" for purely presentational parts. A page or a view that shares a context (`useShareView`, decision 0011) adds **What the agent reads** under it: what its context says, with a real line of it, and what each consumer receives (the MCP App through `ui/update-model-context`, the console's assistant in its prompt).
43
43
  8. **Accessibility**: roles, names, keyboard, and contrast quoted from `node scripts/contrast.ts --all` (the dark theme and the indigo accent, the defaults; add light where it differs in kind).
44
44
  9. **Content**: copy rules with real strings.
45
45
  10. **Do and do not.**
@@ -75,6 +75,7 @@ Write the status on the line under the title: `Status: beta`.
75
75
  - Under reduced motion it shows its final state with nothing moving.
76
76
  - Keyboard: every control reachable, operable and visibly focused; focus order follows reading order.
77
77
  - An agent's work in it is marked (Agent mark, "via" attribution, or a Proposal); no agent write happens without one.
78
+ - A page shares a context both agents read: its scope and freshness, every figure with its unit, change and definition, what code derived from them, and what the data cannot say, within 2,400 characters. A figure an agent would otherwise recompute is computed in the context.
78
79
 
79
80
  ## Tools
80
81
 
@@ -84,9 +85,10 @@ Write the status on the line under the title: `Status: beta`.
84
85
  | `pnpm tokens` | Generates the token CSS and the Tailwind bridge from `tokens/` (`--check` only compares) | After any token change |
85
86
  | `node scripts/contrast.ts [--all]` | Every specified foreground on its background, in every theme and accent, and the chart palette's colour-vision checks | Gate |
86
87
  | `node scripts/registry.ts` | Regenerates the Atlas registry from the card folders (`--check` only compares) | After adding or renaming a card |
87
- | `node scripts/check.ts` | Every card has its README and preview, every README follows the anatomy, no literal colour in components, every token a spec names exists and is written with its own value, and every embed view a document offers is a route | Gate |
88
+ | `node scripts/check.ts` | Every card has its README and preview, every README follows the anatomy, no literal colour in components, every token a spec names exists and is written with its own value, every embed view a document offers is a route, and an element that claims a control's role has a key handler | Gate |
88
89
  | `pnpm typecheck`, `pnpm test` | TypeScript and the behaviour tests | Gate |
89
90
  | `node scripts/audit.ts` | With the app running: every page and embed view at 2560, 1440, 1024, 768 and 390px in both themes (embeds at 720 and 420 on a simulated host ground). Fails on sideways scroll, text clipped without an ellipsis, a control with no accessible name, more than one page scroller, rendered text under its WCAG minimum, a Tab stop with no visible focus ring (it presses Tab through the page), and any uncaught exception or console error. Prints the design metrics: type sizes, weights, radii and the hierarchy ratio | Before release |
91
+ | `node scripts/audit.ts --atlas` | With the app running: the same audit over the Design Atlas, every card's page and its bare preview, at 1440 and 390px in both themes, about ten minutes. Not part of `pnpm verify`, which stays fast: a card's preview is where a defect in its own box shows, so run it after changing a component | After changing a component |
90
92
  | `node scripts/interactions.ts` | With the app running: presses every control on every page (mouse at 1440px, taps at 390px) and follows every link. Fails on a control that changes nothing, one something covers, and a link that answers 4xx | Before release |
91
93
  | `pnpm verify` | The gate once, one production build, the route policy, the size checks and a smoke of up to three routes on desktop and the phone drawer; ends with a coverage line, and the report is in `out/verify.txt` | Before finishing any change |
92
94
  | `pnpm verify --full` | The navigation of every rail route, then every exhaustive suite: the audit, every control pressed and link followed, the keyboard walk, the assistant, the live data and Web Vitals | After a change to shared components, the shell or data, and before release |
@@ -17,7 +17,7 @@ Status: beta
17
17
 
18
18
  Everything in rows 1 and 2 shares one Meridian: point at a day in either chart and the featured figure, the tiles and both charts read that day.
19
19
 
20
- Below 1024px every row stacks: the featured card first, then the tiles (two across from 34rem, one below), then the cards. At 390px the masthead's actions wrap under the title and the period select stays visible; Export is not offered on a phone (`max-sm:hidden`), and the command palette carries destinations and appearance, not the page's actions.
20
+ Below 1024px every row stacks: the featured card first, then the tiles (two across from 34rem, one below), then the cards. At 390px the masthead's actions wrap under the title: the period select, then Export. A page's actions are offered on every width, as the request log's export is: a person on a phone may want the CSV too (issue #13).
21
21
 
22
22
  ## States
23
23
 
@@ -41,7 +41,24 @@ From `src/system/fixtures/sample.ts`: `demoSeries(period)` (one point per day, a
41
41
 
42
42
  ## Agents
43
43
 
44
- The embed view shares the period and its totals, or the day the Meridian points at, with the model. Ask on the trend posts "Why did requests change on <day>?". The Activity feed marks the agent's own changes ("Claude … · for Jonas Weber").
44
+ The page shares one context with both agents (decision 0011), built by `overviewContext` in `src/views/overview-context.ts` from the figures the page draws. The Activity feed marks the agent's own changes ("Claude … · for Jonas Weber").
45
+
46
+ ### What the agent reads
47
+
48
+ The period with its dates, freshness, the four figures, each with its change against the previous period and its definition (the tiles' `hint`s come from the same `OVERVIEW_METRICS`), the day the Meridian points at against the same weekday and the period's median, and what code computed:
49
+
50
+ ```text
51
+ - Error rate was 2.6× to 2.7× the period's median of 0.81% on 21 and 22 Sept (2.13%, 2.18%); no other day passed 1.2×.
52
+ - Latency p95 rose on the same days: 525ms and 505ms against a median of 296ms.
53
+ Unknown:
54
+ - Nothing is recorded between 20 Sept and 23 Sept: no incident and no activity event, so nothing on this page explains the rise on 21 and 22 Sept.
55
+ ```
56
+
57
+ It also says which endpoint contributes most errors, that requests follow the week, what the status classes mean against the error rate's definition, which incident sits beside a spike when one does (in 90 days: "Upload failures for files over 50 MB"), and that today is partial.
58
+
59
+ - **MCP App:** the text above as `ui/update-model-context`, with the same context as its structured part, from the inline view and from fullscreen alike, on load and on every change.
60
+ - **Console assistant:** the same text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
61
+ - **Handoff:** Ask on the featured card hands the period, or the day the Meridian points at, to the assistant: "What stands out in requests, errors and latency over the last 30 days, and why?"
45
62
 
46
63
  ## Embed view
47
64
 
@@ -5,15 +5,20 @@ import { Freshness } from '@/components/patterns/freshness';
5
5
  import { PeriodSelect } from '@/components/patterns/period-select';
6
6
  import { Skeleton } from '@/components/ui/skeleton';
7
7
  import { OverviewBody } from '@/views/overview';
8
- import { DEMO_NOW, DEMO_UPDATED_AT, demoSeries, demoTotals, ENDPOINTS, STATUS_MIX, ACTIVITY } from '@/data/sample';
9
- import { parsePeriod, PERIOD_LABEL } from '@/lib/period';
8
+ import { DEMO_NOW, DEMO_UPDATED_AT } from '@/data/sample';
9
+ import { overviewTool } from '@/views/tools';
10
+ import { cache } from 'react';
11
+ import { parsePeriod } from '@/lib/period';
10
12
  import { app } from '@/app.config';
11
13
  import { Busy } from '../_loading';
12
14
  import { OverviewSkeleton } from './loading';
13
15
 
14
16
  export const metadata = { title: 'Overview' };
15
17
 
16
- type SearchParams = Promise<{ period?: string }>;
18
+ type SearchParams = Promise<{ period?: string; day?: string }>;
19
+
20
+ /** The page's one read, through the Overview tool (`src/views/tools.ts`), shared by the export and the body. */
21
+ const readPeriod = cache((period: ReturnType<typeof parsePeriod>, day?: string) => overviewTool.read({ period, day: /^\d{4}-\d{2}-\d{2}$/.test(day ?? '') ? day : undefined }));
17
22
 
18
23
  /** The masthead renders at once; the period select, the export and the body read the address inside their own boundaries. */
19
24
  export default function OverviewPage({ searchParams }: { searchParams: SearchParams }) {
@@ -38,15 +43,17 @@ export default function OverviewPage({ searchParams }: { searchParams: SearchPar
38
43
 
39
44
  async function Actions({ searchParams }: { searchParams: SearchParams }) {
40
45
  const period = parsePeriod((await searchParams).period);
46
+ const { data } = await readPeriod(period);
41
47
  return (
42
48
  <>
43
49
  <Suspense><PeriodSelect value={period} /></Suspense>
44
- <ExportButton rows={demoSeries(period).current} filename={`overview-${period}.csv`} noun="days" className="max-sm:hidden" />
50
+ <ExportButton rows={data.series} filename={`overview-${period}.csv`} noun="days" />
45
51
  </>
46
52
  );
47
53
  }
48
54
 
49
55
  async function Body({ searchParams }: { searchParams: SearchParams }) {
50
- const period = parsePeriod((await searchParams).period);
51
- return <OverviewBody period={PERIOD_LABEL[period].toLowerCase()} series={demoSeries(period).current} totals={demoTotals(period)} endpoints={ENDPOINTS} mix={STATUS_MIX} activity={ACTIVITY} now={DEMO_NOW.toISOString()} />;
56
+ const { period, day } = await searchParams;
57
+ const { data } = await readPeriod(parsePeriod(period), day);
58
+ return <OverviewBody {...data} />;
52
59
  }
@@ -39,7 +39,19 @@ Status: beta
39
39
 
40
40
  ## Agents
41
41
 
42
- Not applicable: an agent answers analytical questions from the Overview and Requests views' shared context and the API, not by driving this page.
42
+ An agent drives nothing here; it reads the page through its shared context (decision 0011), built by `analyticsContext` in `src/views/analytics-context.ts`.
43
+
44
+ ### What the agent reads
45
+
46
+ The period, freshness, the busiest hour of the week and of the day, each region's share with its p50, every endpoint in the table's current order, the day the Meridian points at, and what code found: the same error spike as the Overview (by the same code), the slowest and the most failing endpoint against the median, the slowest region, and the weekend's share of a weekday. It says that the weekday-and-hour pattern, the regions and the endpoints are sums over the period, not a day.
47
+
48
+ ```text
49
+ - POST /v1/files fails most: 1.88% of its requests, 5.0× the 0.37% across endpoints. Evidence: /requests?q=%2Fv1%2Ffiles
50
+ ```
51
+
52
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
53
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
54
+ - **Handoff:** Ask on Errors per day hands the pointed day, or the period, to the assistant: "Why did errors change on 22 Sept 2026?"
43
55
 
44
56
  ## Accessibility
45
57
 
@@ -6,7 +6,9 @@ import { Freshness } from '@/components/patterns/freshness';
6
6
  import { PeriodSelect } from '@/components/patterns/period-select';
7
7
  import { Skeleton } from '@/components/ui/skeleton';
8
8
  import { AnalyticsBody } from '@/views/analytics';
9
- import { DEMO_NOW, DEMO_UPDATED_AT, ENDPOINTS, demoHeatmap, demoSeries, REGION_LATENCY, requestsByHour } from '@/data/sample';
9
+ import { DEMO_NOW, DEMO_UPDATED_AT } from '@/data/sample';
10
+ import { analyticsTool } from '@/views/tools';
11
+ import { cache } from 'react';
10
12
  import { parsePeriod } from '@/lib/period';
11
13
  import { Busy } from '../_loading';
12
14
  import { AnalyticsSkeleton } from './loading';
@@ -36,17 +38,21 @@ export default function AnalyticsPage({ searchParams }: { searchParams: SearchPa
36
38
  );
37
39
  }
38
40
 
41
+ /** The page's one read, through the Analytics tool (`src/views/tools.ts`), shared by the export and the body. */
42
+ const readPeriod = cache((period: ReturnType<typeof parsePeriod>) => analyticsTool.read({ period }));
43
+
39
44
  async function Actions({ searchParams }: { searchParams: SearchParams }) {
40
45
  const period = parsePeriod((await searchParams).period);
46
+ const { data } = await readPeriod(period);
41
47
  return (
42
48
  <>
43
49
  <Suspense><PeriodSelect value={period} /></Suspense>
44
- <ExportButton rows={demoSeries(period).current} filename={`analytics-${period}.csv`} noun="days" className="max-sm:hidden" />
50
+ <ExportButton rows={data.series} filename={`analytics-${period}.csv`} noun="days" />
45
51
  </>
46
52
  );
47
53
  }
48
54
 
49
55
  async function Body({ searchParams }: { searchParams: SearchParams }) {
50
- const period = parsePeriod((await searchParams).period);
51
- return <AnalyticsBody series={demoSeries(period).current} heat={demoHeatmap()} hours={requestsByHour()} regions={REGION_LATENCY} endpoints={ENDPOINTS} />;
56
+ const { data } = await readPeriod(parsePeriod((await searchParams).period));
57
+ return <AnalyticsBody {...data} />;
52
58
  }
@@ -39,7 +39,18 @@ None yet: a `zz_meridian_customer { name }` view would show one customer's card
39
39
 
40
40
  ## Agents
41
41
 
42
- An agent reads customers through their requests view; it never changes a plan or invites from here.
42
+ An agent reads customers through this page's shared context and their requests view; it never changes a plan or invites from here. The context is built by `customersContext` in `src/views/customers-context.ts`, and the tiles' `hint`s come from its `CUSTOMER_METRICS`.
43
+
44
+ ### What the agent reads
45
+
46
+ The three tiles with their definitions, every customer in the table's filter and order (with the columns a narrow screen drops: the error rate and the trend are still the person's to read), and what code found: how concentrated spend is, how combined requests moved over the last 7 days against the 7 before and who departs from that by 10 points or more, and which customers fail at twice the median rate. Lists stop at 25 and say how many more there are.
47
+
48
+ ```text
49
+ - Combined requests are up 15% over the last 7 days against the 7 before. Apart from that: Fernway Bank up 36%.
50
+ ```
51
+
52
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
53
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
43
54
 
44
55
  ## Content
45
56
 
@@ -42,7 +42,15 @@ Without a live incident row 1 becomes one full-width featured card.
42
42
 
43
43
  ## Agents
44
44
 
45
- The page itself takes no agent action. Its embed shares the summary and the open incident with the model; an agent can be asked about the incident from the inline banner.
45
+ The page itself takes no agent action. It shares one context with both agents (decision 0011), built by `healthContext` in `src/views/health-context.ts`.
46
+
47
+ ### What the agent reads
48
+
49
+ Every service's state, latency now and uptime; the average uptime with its definition; which services carry the days below operational ("Inference API accounts for 3 of them"); the open incident with its latest update by time, not by position; the longest incident resolved in the period. Unknowns: that a monitoring incident is not yet confirmed fixed, and that latency is one reading with no history to call it unusual.
50
+
51
+ - **MCP App:** the text above as `ui/update-model-context`, with the same context as its structured part, from the inline view and from fullscreen alike, on load and on every change.
52
+ - **Console assistant:** the same text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
53
+ - **Handoff:** Ask on the uptime card hands the open incident to the assistant: "Which service is hurting uptime, and is … over?"
46
54
 
47
55
  ## Embed view
48
56
 
@@ -2,22 +2,23 @@ import { app } from '@/app.config';
2
2
  import { PageFrame } from '@/components/base/shell';
3
3
  import { Freshness } from '@/components/patterns/freshness';
4
4
  import { PackedHealthBody, SubscribeButton } from '@/views/health';
5
+ import { healthTool } from '@/views/tools';
5
6
  import { packServices } from '@/components/patterns/status-list/summarise';
6
- import { DEMO_NOW, DEMO_UPDATED_AT, INCIDENTS, SERVICES, PAST_INCIDENTS } from '@/data/sample';
7
7
 
8
8
  export const metadata = { title: 'Health' };
9
9
 
10
- export default function HealthPage() {
11
- const current = INCIDENTS.find((i) => i.state !== 'resolved') ?? null;
10
+ /** Rendered from the Health tool's own read (`src/views/tools.ts`): the services and incidents the caller may read. */
11
+ export default async function HealthPage() {
12
+ const { data } = await healthTool.read({});
12
13
  return (
13
14
  <PageFrame
14
15
  kicker={<>{app.name} · {app.workspace}</>}
15
16
  title="Health"
16
17
  description="Every service, how it is right now, and what happened in the last 90 days."
17
- meta={<Freshness updatedAt={DEMO_UPDATED_AT} now={DEMO_NOW} />}
18
+ meta={<Freshness updatedAt={new Date(data.updatedAt)} now={new Date(data.now)} />}
18
19
  actions={<SubscribeButton />}
19
20
  >
20
- <PackedHealthBody services={packServices(SERVICES)} current={current} past={PAST_INCIDENTS} now={DEMO_NOW.toISOString()} />
21
+ <PackedHealthBody {...data} services={packServices(data.services)} />
21
22
  </PageFrame>
22
23
  );
23
24
  }
@@ -41,7 +41,14 @@ Not offered. Keys are secrets, and creating one is a page-only operation. In the
41
41
 
42
42
  ## Agents
43
43
 
44
- An agent may say which keys are unused (from the shared context of other views) and may propose revoking a key (a critical Proposal); it never creates one. The proposal waits for approval, and its approval removes the key through the same collection.
44
+ An agent may propose revoking a key (a critical Proposal); it never creates one, because a key's secret is shown once, on this page (`pageOnly`). The proposal waits for approval, and its approval removes the key through the same collection. The page shares its context through `keysContext` in `src/views/keys-context.ts`.
45
+
46
+ ### What the agent reads
47
+
48
+ Every key with its id, environment, hint (the first and last characters, never the secret), owner, scopes, when it was created and last used, and what code found: live keys unused for 30 days, live keys with every scope, and an owner holding several live keys. The secret and its hash are never in it (`secretHash` is `hidden`), and the context says so, with the rule that creating a key is the person's.
49
+
50
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
51
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
45
52
 
46
53
  ## Content
47
54
 
@@ -42,7 +42,18 @@ Not offered. Inviting and removing people are decisions for the console, not a c
42
42
 
43
43
  ## Agents
44
44
 
45
- The assistant reads and proposes changes here through Proposals: it adds, changes and removes members only after a person approves, and what it changed shows on this page after a refresh. A change made here is what the assistant reads next.
45
+ The assistant reads and proposes changes here through Proposals: it adds, changes and removes members only after a person approves, and what it changed shows on this page after a refresh. A change made here is what the assistant reads next. The page shares its context through `membersContext` in `src/views/members-context.ts`.
46
+
47
+ ### What the agent reads
48
+
49
+ Counts by status, role (with what each role may do) and team, everyone with their role, team, status and when they were last seen (25 at most; `query_members` finds the rest), and what code found: active members unseen for 60 days or more, unanswered invitations, and who can change the workspace. It says that "last active" is a console sign-in, not use of the API.
50
+
51
+ ```text
52
+ - 3 active members have not been seen for 60 days or more: Alice Moreno (Viewer, Support, 94 days), Felix Andersson (Viewer, Engineering, 75 days), Ravi Patel (Viewer, Support, 71 days).
53
+ ```
54
+
55
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
56
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
46
57
 
47
58
  ## Content
48
59
 
@@ -10,7 +10,7 @@ Status: beta
10
10
  |---|---|---|---|
11
11
  | Masthead | PageFrame: kicker "ZZ Meridian · Production", title "Requests", one sentence, Freshness, Export CSV | One band | Title, sentence, then Freshness and Export on their own row |
12
12
  | 1 | Row `tiles`: three Metric tiles for what the filters let through: requests (emphasis), errors and limits, latency p95, each with its 5-minute shape over the last hour and the last half hour against the one before | Three across | One column |
13
- | 2 | Data table with a Filter bar in its toolbar: search, Status, Method, Region, the result count | Columns: Request (grow), Status, Latency, Customer (from 768px), Region (from 1024px), Size (from 1280px), Received | Card list: route and status, then latency, customer and when |
13
+ | 2 | Data table with a Filter bar in its toolbar: search, Status, Method, Region, the result count | Columns: Request (grow), Status, Latency, Customer and Region (once the table is 896px wide), Size (1152px), Received; a table under 640px wide is the card list | Card list: route and status, then latency, customer and when |
14
14
 
15
15
  ## States
16
16
 
@@ -51,7 +51,21 @@ Export CSV is a link to `/api/export/requests` with the same filters and sort: t
51
51
 
52
52
  ## Agents
53
53
 
54
- An agent opens this page's views through `zz_meridian_requests`; it reads them through the shared context; it never exports or blocks from here. Its filters are marked until a person changes one.
54
+ An agent opens this page's views through `zz_meridian_requests`; it never exports or blocks from here. Its filters are marked until a person changes one. The page shares one context with both agents (decision 0011), built by `requestsContext` in `src/views/requests-context.ts` from the page's read: the rows on screen and the summary of the whole filtered set.
55
+
56
+ ### What the agent reads
57
+
58
+ The filters in words and the span of time the log covers; freshness; the three figures with their change against the half hour before and their definitions (the tiles' `hint`s come from `REQUEST_METRICS`); the page, the sort and the rows on screen by id; and what the matching requests and their errors have in common:
59
+
60
+ ```text
61
+ - 5 of the 9 errors and limits are in eu-west-1 (56%). Evidence: /requests?region=eu-west-1
62
+ ```
63
+
64
+ When a half hour holds fewer than 30 requests it says the change is too few to read as a trend, and when the summary reads only the newest 500 it says so.
65
+
66
+ - **MCP App:** the text above as `ui/update-model-context`, with the same context as its structured part, from the inline view and from fullscreen alike, on load and on every change.
67
+ - **Console assistant:** the same text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
68
+ - **Handoff:** Ask in the filter bar hands the filtered set to the assistant: "What do these requests with status 5xx have in common, and why?"
55
69
 
56
70
  ## Accessibility
57
71
 
@@ -41,7 +41,18 @@ None of its own. An agent shows a request through `zz_meridian_requests` (inline
41
41
 
42
42
  ## Agents
43
43
 
44
- Replay and blocking a key are a person's actions here. An agent that wants either sends a Proposal.
44
+ Replay is a person's action here: it is `pageOnly`, so no agent can run or propose it. The page shares its context through `requestContext` in `src/views/request-context.ts`.
45
+
46
+ ### What the agent reads
47
+
48
+ The request, its status, latency (with its definition), customer and region, every phase with its start and duration, the payloads' sizes (their text is in the page text), and what code found: where most of the time went, the latency against the route's p95, and, for a failed request, that it counts in the error rate. It says that one request is one sample, and that replaying it is the person's.
49
+
50
+ ```text
51
+ - Most of its time went to Rate limit (Over 1,200 requests a minute): 159ms, 93% of 171ms.
52
+ ```
53
+
54
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
55
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
45
56
 
46
57
  ## Accessibility
47
58
 
@@ -1,4 +1,5 @@
1
1
  import { read } from '@/data/read';
2
+ import { readEndpoints } from '@/data/metrics';
2
3
  import { FEATURED_REQUEST_IDS, traceOf, payloadsOf, type RequestRow } from '@/data/sample';
3
4
  import { MissingPage } from '@/views/missing-page';
4
5
  import { RequestView } from '@/views/request';
@@ -23,5 +24,6 @@ export default async function RequestPage({ params }: { params: Promise<{ id: st
23
24
  const { request: r, observedAt } = await byId(id);
24
25
  // Rendered here rather than thrown, so the screen is in the first HTML (see MissingPage).
25
26
  if (!r) return <MissingPage />;
26
- return <RequestView request={r} trace={traceOf(r)} payloads={payloadsOf(r)} now={observedAt} replay={replayRequest} />;
27
+ const route = (await readEndpoints()).find((e) => e.method === r.method && e.route === r.route);
28
+ return <RequestView request={r} trace={traceOf(r)} payloads={payloadsOf(r)} routeP95={route?.p95 ?? null} now={observedAt} replay={replayRequest} />;
27
29
  }
@@ -65,6 +65,6 @@ async function RequestsExport({ searchParams }: { searchParams: SearchParams })
65
65
  }
66
66
 
67
67
  async function Requests({ searchParams }: { searchParams: SearchParams }) {
68
- const { rows, total, summary, state } = await readPage(await searchParams);
69
- return <RequestsView rows={rows} total={total} summary={summary} state={state} pageSize={REQUEST_PAGE} />;
68
+ const { rows, total, summary, state, observedAt } = await readPage(await searchParams);
69
+ return <RequestsView rows={rows} total={total} summary={summary} state={state} pageSize={REQUEST_PAGE} updatedAt={DEMO_UPDATED_AT.toISOString()} now={observedAt} />;
70
70
  }
@@ -42,6 +42,13 @@ Appearance reads and writes `usePreferences()` (stored on the device). Time zone
42
42
 
43
43
  This is where people govern agents: whether assistants may read, which are connected, and the rule (locked on) that nothing an agent proposes runs without approval, a removal included. The assistant section, shown only where the product has an assistant, holds the person's switch for its panel.
44
44
 
45
+ ### What the agent reads
46
+
47
+ Every section's current values, which neither an input's nor a switch's text carries: the workspace's name, address and time zone (and an unsaved edit, when there is one), each notification switch, this device's theme, accent and density, whether the assistant is shown, whether assistants may read dashboards and which are connected with what they may do, and that deleting the workspace is typed, never proposed. Each section reports its own line; `SettingsBody` shares them as one context. None is offered to an agent as a change, and the context says so.
48
+
49
+ - **Console assistant:** this text in its prompt's `<view-context>` block, with the page's address and query, and the page text after it for anything the context does not cover.
50
+ - **MCP App:** no embed view yet; an MCP server can return this context as a tool's text (`docs/agents.md`).
51
+
45
52
  ## Accessibility
46
53
 
47
54
  - Each section is a form with its own submit; switches carry their label and description.