@terreno/mcp 57.8.0 → 57.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/docs/upgrades/57.10.1.md +54 -0
  2. package/dist/docs/upgrades/57.9.0.md +28 -0
  3. package/dist/docs/versioned/next/README.md +1 -0
  4. package/dist/docs/versioned/next/explanation/README.md +4 -0
  5. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
  6. package/dist/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
  7. package/dist/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
  8. package/dist/docs/versioned/next/explanation/ai-observability.md +101 -0
  9. package/dist/docs/versioned/next/explanation/charts.md +12 -0
  10. package/dist/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
  11. package/dist/docs/versioned/next/explanation/example-coverage.md +4 -2
  12. package/dist/docs/versioned/next/explanation/loop-engineering.md +1 -1
  13. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +49 -1
  14. package/dist/docs/versioned/next/explanation/typescript-configuration.md +1 -1
  15. package/dist/docs/versioned/next/explanation/versioning-policy.md +1 -1
  16. package/dist/docs/versioned/next/how-to/README.md +7 -1
  17. package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
  18. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  19. package/dist/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  20. package/dist/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  21. package/dist/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  22. package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +7 -0
  23. package/dist/docs/versioned/next/how-to/circleci.md +9 -6
  24. package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
  25. package/dist/docs/versioned/next/how-to/install-agent-skills.md +1 -1
  26. package/dist/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  27. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  28. package/dist/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  29. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  30. package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  31. package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  32. package/dist/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  33. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  34. package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
  35. package/dist/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
  36. package/dist/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  37. package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  38. package/dist/docs/versioned/next/reference/README.md +3 -0
  39. package/dist/docs/versioned/next/reference/admin-config.md +2 -0
  40. package/dist/docs/versioned/next/reference/admin-frontend.md +216 -0
  41. package/dist/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  42. package/dist/docs/versioned/next/reference/ai-harness.md +1881 -0
  43. package/dist/docs/versioned/next/reference/ai.md +283 -28
  44. package/dist/docs/versioned/next/reference/api.md +46 -0
  45. package/dist/docs/versioned/next/reference/blocks.md +178 -0
  46. package/dist/docs/versioned/next/reference/environment-variables.md +2 -1
  47. package/dist/docs/versioned/next/reference/feature-flags.md +1 -1
  48. package/dist/docs/versioned/next/reference/mcp-server.md +17 -0
  49. package/dist/docs/versioned/next/reference/test.md +11 -2
  50. package/dist/docs/versioned/next/reference/ui.md +418 -11
  51. package/dist/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  52. package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  53. package/dist/docs/versioned/next/tasks/ai-observability.md +233 -104
  54. package/dist/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
  55. package/dist/docs/versioned/next/tasks/durable-agent-harness.md +183 -0
  56. package/dist/tools.d.ts +4 -0
  57. package/dist/tools.d.ts.map +1 -1
  58. package/dist/tools.js +37 -0
  59. package/dist/tools.js.map +1 -1
  60. package/package.json +3 -2
  61. package/src/docs/upgrades/57.10.1.md +54 -0
  62. package/src/docs/upgrades/57.9.0.md +28 -0
  63. package/src/docs/versioned/next/README.md +1 -0
  64. package/src/docs/versioned/next/explanation/README.md +4 -0
  65. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
  66. package/src/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
  67. package/src/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
  68. package/src/docs/versioned/next/explanation/ai-observability.md +101 -0
  69. package/src/docs/versioned/next/explanation/charts.md +12 -0
  70. package/src/docs/versioned/next/explanation/durable-agent-harness.md +531 -0
  71. package/src/docs/versioned/next/explanation/example-coverage.md +4 -2
  72. package/src/docs/versioned/next/explanation/loop-engineering.md +1 -1
  73. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +49 -1
  74. package/src/docs/versioned/next/explanation/typescript-configuration.md +1 -1
  75. package/src/docs/versioned/next/explanation/versioning-policy.md +1 -1
  76. package/src/docs/versioned/next/how-to/README.md +7 -1
  77. package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
  78. package/src/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
  79. package/src/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
  80. package/src/docs/versioned/next/how-to/ai-feature-development.md +156 -0
  81. package/src/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
  82. package/src/docs/versioned/next/how-to/charts-and-dashboards.md +7 -0
  83. package/src/docs/versioned/next/how-to/circleci.md +9 -6
  84. package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
  85. package/src/docs/versioned/next/how-to/install-agent-skills.md +1 -1
  86. package/src/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
  87. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -1
  88. package/src/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
  89. package/src/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
  90. package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
  91. package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
  92. package/src/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
  93. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  94. package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
  95. package/src/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
  96. package/src/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
  97. package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
  98. package/src/docs/versioned/next/reference/README.md +3 -0
  99. package/src/docs/versioned/next/reference/admin-config.md +2 -0
  100. package/src/docs/versioned/next/reference/admin-frontend.md +216 -0
  101. package/src/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
  102. package/src/docs/versioned/next/reference/ai-harness.md +1881 -0
  103. package/src/docs/versioned/next/reference/ai.md +283 -28
  104. package/src/docs/versioned/next/reference/api.md +46 -0
  105. package/src/docs/versioned/next/reference/blocks.md +178 -0
  106. package/src/docs/versioned/next/reference/environment-variables.md +2 -1
  107. package/src/docs/versioned/next/reference/feature-flags.md +1 -1
  108. package/src/docs/versioned/next/reference/mcp-server.md +17 -0
  109. package/src/docs/versioned/next/reference/test.md +11 -2
  110. package/src/docs/versioned/next/reference/ui.md +418 -11
  111. package/src/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
  112. package/src/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
  113. package/src/docs/versioned/next/tasks/ai-observability.md +233 -104
  114. package/src/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
  115. package/src/docs/versioned/next/tasks/durable-agent-harness.md +183 -0
@@ -40,6 +40,8 @@
40
40
  `AdminAction` has `id`, `label`, optional `confirm`, `background`, and `patchKeys`.
41
41
 
42
42
  `AdminContribution` may provide `models`, `customScreens`, `homeWidgets`, and `scripts`.
43
+ `customScreens[].group` becomes a named sidebar heading (`AI Observability`). Screens without
44
+ `group` stay under **Screens** — that is where **AI Requests** remains.
43
45
  Model contributions contain `model`, `routePath`, and `admin`. Duplicate `routePath`s
44
46
  throw for registered routers; the same Mongoose model at two paths gets unique config
45
47
  `name`s so the admin UI can route to each.
@@ -434,3 +434,219 @@ import {CommsDashboardScreenWidget, CommsMessageDetail} from "@terreno/admin-fro
434
434
  The list screen persists filters in the URL (`channel`, `provider`, `status`, `errorClass`, `q`,
435
435
  `startDate`, `endDate`, `page`) and calls `/comms/messages`, `/comms/stats`, and
436
436
  `/comms/messages/retryMany`.
437
+
438
+ ### AI Observability chrome
439
+
440
+ `ObservabilityApp` contributes grouped custom screens (`group: "AI Observability"`): `ai-prompts`,
441
+ `ai-traces`, `ai-evaluators`, `ai-datasets`, `ai-experiments`, and `ai-review` when the local
442
+ plugin is on. `AI Requests` (`ai-requests`) stays ungrouped. Widgets also register detail routes:
443
+ `ai-prompt-editor`, `ai-trace-detail`, `ai-review-item`, `ai-evaluator-detail`, `ai-evaluator-new`,
444
+ `ai-dataset-detail`, `ai-experiment-new`, and `ai-experiment-results`.
445
+
446
+ Every observability screen wraps `AiObservabilityChrome`: breadcrumbs
447
+ `Admin / AI Observability / <Section> / <leaf>` and a status chip from
448
+ `GET /ai/observability/status` (`Local on|off` plus active primaries). Review queue nav and the
449
+ review screen body hide when `localOn` is false.
450
+
451
+ Each observability list screen is contributed with `adminAccess: {resource, action: "list"}` so
452
+ `/admin/config` omits screens the caller cannot list (`aiPrompt`, `aiTrace`, `aiEvaluator`,
453
+ `aiDataset`, `aiExperiment`, `aiReview`). Detail routes stay registered on the host; route
454
+ handlers still enforce Task 4.1 RBAC.
455
+
456
+ `GET /ai/observability/status` includes a `permissions` map (per observability resource and
457
+ action). Legacy admin users and apps without `accessControl` receive all actions `true`. RBAC
458
+ callers receive effective flags for the signed-in user. Pass the same `accessControl` to
459
+ `ObservabilityApp` and `AdminApp` so status permissions match sidebar filtering. Admin widgets
460
+ read the payload through `resolvePromptActionPermissions` and `observabilityActionAllowed` (fail
461
+ closed while status is loading, errored, or missing `permissions`). List screens gate **Create
462
+ prompt** on `aiPrompt:create`, **Create evaluator** on `aiEvaluator:create`, and **New dataset** on
463
+ `aiDataset:create`; experiment lists hide **New experiment** without `aiExperiment:create`.
464
+ Dataset detail hides **Add item** without `aiDataset:update` and **Run experiment** without
465
+ `aiExperiment:create`; dataset lists hide **Import** without
466
+ `aiDataset:update`. Judge schema checks use the production prompt version, or the latest version
467
+ when production is unset. Widgets do not couple to host Redux.
468
+
469
+ `ai-prompts` lists prompts with a folder rail, search, type badge, latest vs production columns
470
+ (tooltips), 7-day usage, and **Create prompt** when `aiPrompt:create` is allowed. The create modal
471
+ includes an optional **Description** (saved on the prompt record and shown on the hub Overview tab).
472
+ `ai-prompt-editor?name=` is a prompt hub with **Overview**, **Versions**, **Traces**, and
473
+ **Experiments** tabs. Overview shows optional description, folder, tags, and latest vs production
474
+ badges. Traces and Experiments render bounded relationship rows from
475
+ `GET /ai/observability/prompts/:name?promptVersion=` (the hub pins the selected version, defaulting
476
+ to latest after the first load, so related traces and experiments are filtered server-side while the
477
+ full immutable version list stays on the detail payload). The **Versions** tab
478
+ hosts the versioned editor: full-width version history rows show `vN`, every label attached to
479
+ that version, and its creation time on one line. The selected row is highlighted. Editor /
480
+ Playground tabs appear on Versions when allowed; **Save as vN+1**, **Set vN as production…**, and
481
+ playground **Run once** hide when `aiPrompt:update`, `aiPrompt:promote`, or `aiPrompt:playground`
482
+ is denied (fields stay read-only). Playground **Run once** does not create a version; hosts may pass `apiKey` to
483
+ `AiPromptEditorScreenWidget`, which forwards it as `x-ai-api-key` without putting the key in the
484
+ request body. Pass `apiKeyLoading` while the host reads a saved key (for example from
485
+ `useStoredState`) so the playground waits instead of showing a missing-key message. Pass
486
+ `playgroundApiKeyHint` when `GET /ai/observability/status` reports
487
+ `playgroundAi.source: "request-key"`; the editor blocks **Run once** and shows that hint until a
488
+ trimmed key is available. When `playgroundAi.source` is `server`, no key is required. When it is
489
+ `unavailable`, the editor explains that the backend must configure `aiService` or
490
+ `requestAiServiceFactory`. A stale playground **503** with the missing-key title is remapped to the
491
+ same hint for `request-key` hosts so operators are not told to fix server configuration. The
492
+ example admin supplies the Gemini key saved from Profile. **Save this run to dataset** stays
493
+ disabled until phase 2.
494
+
495
+ `ai-traces` lists traces with a filter bar: from/to date fields; dropdowns for prompt, status,
496
+ score presence (**All traces / Has a score / No scores**), and data sensitivity
497
+ (**All traces / Sensitive only / Not sensitive**); plus user and session text fields.
498
+ Checkbox selection opens a bulk bar with **Send to human review**, a sensitive-count
499
+ warning, **Clear**, and **Add to dataset** (opens a dataset picker modal; sensitive traces show a
500
+ warning before bulk add). **Send to human review** opens a modal that explains how human
501
+ evaluators define the score fields and reviewer instructions, then requires a human evaluator
502
+ before creating one review-queue item per trace. When local trace storage is on,
503
+ **Run multi-stage trace test** calls the admin-only smoke endpoint and
504
+ opens the resulting detail: two schema-validated LLM stages, one deterministic tool span, and a
505
+ final schema-validated combining LLM stage under one CHAIN root. LLM span input includes
506
+ `outputSchema`. That run needs AI the same way the playground does: hosts may pass `apiKey`,
507
+ `apiKeyLoading`, and `apiKeyHint` to `AiTracesScreenWidget`, which forwards the key as
508
+ `x-ai-api-key`. When `GET /ai/observability/status` reports `playgroundAi.source: "request-key"`
509
+ and no trimmed key is available, the button is disabled and the hint explains where to save one;
510
+ `unavailable` instead reports that the backend must configure `aiService` or
511
+ `requestAiServiceFactory`. The example admin supplies the Gemini key saved from Profile. Rows show a status dot (primary for successful runs, accent for failed runs),
512
+ `sensitive` badge, error line, numeric prompt count, span count, tokens, cost, latency, score count, and
513
+ **Open**. Pagination uses `page` / `limit` / `more` / `total`.
514
+ `ai-trace-detail?id=` shows the header, left span list (kind badge, indent, duration bar),
515
+ right span detail with **collapsed** sensitive I/O, and scores (value + source). The two columns
516
+ size from the row width rather than their content, so a wide span value (multi-stage LLM spans embed
517
+ `outputSchema` JSON) wraps inside the detail column instead of pushing it below the span list.
518
+
519
+ `ai-review` shows Pending / In progress / Done / Skipped tabs with counts. Each tab is
520
+ oldest-first and lists the trace action, prompt, assignee, waiting time, and status.
521
+ **Start reviewing — oldest first** opens the first pending item; the empty state names both
522
+ Traces intake and manual **Assign to me** assignment.
523
+
524
+ `ai-review-item?id=` shows "Item N of M pending", previous/next navigation, read-only
525
+ **What the AI was given** / **What the AI wrote** panels, collapsed long fields with word
526
+ counts, reviewer notes, and a collapsed Raw JSON disclosure. Score controls come from evaluator
527
+ dimensions (numeric slider, boolean Pass / Fail, categorical pills). Actions are
528
+ **Submit & next**, **Skip**, and **Assign to me**; completion toasts report the remaining count
529
+ or **Queue clear**.
530
+
531
+ `ai-evaluators` lists evaluators with type badge, dimension summary, target, and run-mode chips.
532
+ **Create evaluator** opens `ai-evaluator-new` with the name and optional purpose first, then how
533
+ scoring happens (human / JSON assert / LLM judge). New evaluators score a **full trace** only;
534
+ generation span and dataset item targets are not offered yet. Score fields depend on data type:
535
+ boolean has no extra control, numeric asks for min and max, and categorical is a list you add to.
536
+ LLM judge picks the judge prompt from the existing prompt list. Human setup points operators to
537
+ **Send to human review** and omits live sampling because it never applies; automatic evaluators
538
+ explain experiment/manual availability and production sampling, while LLM judges call out the billed
539
+ model-call impact. The optional purpose is saved as the evaluator description and appears on its
540
+ detail screen. LLM judge schema feedback stays idle until a prompt is selected, then shows
541
+ loading/error states and only checks dimensions after its production schema loads.
542
+ `ai-evaluator-detail?id=` leads with the evaluator name, description, and type/target/run-mode
543
+ badges, then explains the saved scores, how scoring works, where it runs, and a **Used by** list
544
+ derived from recent experiments. A failed experiments request shows **Could not load experiments.**
545
+ instead of an empty 30-day list. Its dimension and usage rows use the shared `ObservabilityTable` instead of
546
+ `DataTable`, which sizes to a height-constrained parent and collapses inside a scrolling page. LLM
547
+ judge details show loading or load-failed feedback while resolving the judge prompt; schema
548
+ mismatches appear only after its production schema loads.
549
+
550
+ `ai-datasets` lists datasets with item counts, provenance bar, input-schema binding, and updated
551
+ time. **New dataset** creates a dataset; **Import** on each row accepts `.json` or `.csv` via
552
+ `FilePickerButton` (local URI read) or paste, posting `{rows}` for JSON or `{format:'csv',content}`
553
+ for CSV. `ai-dataset-detail?id=` shows counts, schema binding, tabs **All / Human / Auto / Needs
554
+ review**. Human is proofread items. Auto is unreviewed trace or synthetic items, so a proofread
555
+ trace is not listed in both tabs. The screen includes an items table (input, expected, provenance,
556
+ trace link), **Add item**, and **Run experiment**. The items table uses `ObservabilityTable`: rows grow with their content
557
+ and cells wrap to three lines before truncating, so long inputs and expected outputs no longer
558
+ overlap adjacent rows. Input and Expected receive 2.5× the flexible width of metadata columns.
559
+ Selecting a row opens a scrollable modal with the complete input, expected output, provenance,
560
+ annotation ids, tags, timestamps, metadata, and an **Open source trace** action when linked.
561
+ Opening the source trace dismisses the item modal before navigation.
562
+ Item-query loading and failures render dedicated states with Retry; they never appear as an empty
563
+ dataset.
564
+
565
+ `ObservabilityTable` (`widgets/aiObservability/shell/ObservabilityTable.tsx`) is the shared
566
+ flow-height table for these screens. Columns take a `title`, optional `minWidth`, and optional
567
+ relative `grow`; rows take a `key`, `cells`, and optional click/accessibility properties. A string
568
+ cell renders truncated text and a node cell renders as-is. Its
569
+ bordered shell shrink-wraps the rows inside flex/scroll parents so its bottom border cannot stretch
570
+ into the following section.
571
+
572
+ `ai-experiments` lists experiments with status, running progress, and cost. **New experiment**
573
+ opens a four-step wizard (dataset with counts, prompt versions tagged latest/production/superseded,
574
+ llm-judge and json-assert evaluators, review & run with estimate). A slower in-flight estimate is ignored after the selection changes, so it cannot replace a newer estimate or its error. Human evaluators are omitted. On the prompt-versions step, **Next** and the later
575
+ wizard rail buttons stay disabled until 2–3 versions are selected. `includeUnproofread` and optional model override are on
576
+ the wizard. `ai-experiment-results?id=` polls while pending/running, shows gate tiles per version,
577
+ failing gate count, outliers, a side-by-side per-item output table (failed rows first from the
578
+ API), and **Promote to production** with a confirm modal; promote is blocked when gates fail (409).
579
+
580
+ ### AI Harness approvals inbox
581
+
582
+ `HarnessApp` (`@terreno/ai/harness`) contributes custom screen `harness-approvals`
583
+ (`group: "AI Harness"`, label **Approvals**) through `adminContribution()`, so registering
584
+ `HarnessApp` before `AdminApp` is enough on the backend. `HARNESS_ADMIN_WIDGETS["harness-approvals"]`
585
+ (`HarnessApprovalsScreenWidget`) is in the built-in screen registry, so `/admin/harness-approvals`
586
+ renders through `AdminScreenRouter` with no host route file. See
587
+ [AI harness approvals](ai-harness.md#approvals).
588
+
589
+ The inbox lists the pending approvals the signed-in user may decide
590
+ (`GET /harness/approvals`, oldest first), each with title, task (`name@version` from
591
+ `definitionKey`), age, and expiry. Selecting one shows its `summary` (rendered with `MarkdownView`
592
+ when it looks like markdown) and `payload` (pretty JSON; a string payload that looks like
593
+ markdown renders as markdown), plus a **Reason** field and
594
+ **Approve** / **Reject**.
595
+
596
+ | Behavior | Detail |
597
+ | --- | --- |
598
+ | Approve | `POST /harness/approvals/:id/approve` with `{reason}` when one was typed, else `{}`. |
599
+ | Reject | Requires a non-blank reason (checked before the request); `POST .../reject` with `{reason}` (trimmed). |
600
+ | Success | Back to the list with a confirmation; the list refetches. |
601
+ | 409 | "Already decided, expired, or its task ended" notice; back to the list and refetch. |
602
+ | Other errors | The API error `title` under the buttons; the detail stays open. |
603
+ | States | Loading spinner, error with **Try again**, empty (**Nothing to approve.**), **Refresh** button. |
604
+ | Page size | The oldest 100 (`?limit=100`). When the response has `more`, a note says so; deciding items brings the rest into view. |
605
+ | In flight | Both buttons and the reason field are disabled until the decision returns, so it is sent once. |
606
+
607
+ Requests use the host-bound admin request client (`AdminProvider` `getAuthHeaders` / `apiOrigin`),
608
+ never RTK `injectEndpoints`. Test ids: `harness-approvals` (root), `harness-approvals-list`,
609
+ `harness-approvals-empty`, `harness-approvals-error`, `harness-approvals-loading`,
610
+ `harness-approvals-refresh`, `harness-approvals-notice`, `harness-approval-row-<id>-clickable`,
611
+ `harness-approval-detail`, `harness-approval-payload`, `harness-approval-reason`,
612
+ `harness-approval-approve`, `harness-approval-reject`, `harness-approval-error`,
613
+ `harness-approval-back` (standalone only; in admin the page back chevron closes the detail).
614
+
615
+ #### Mount outside admin chrome
616
+
617
+ `HarnessApprovalInbox` is exported for app screens (for example a clinician view). Inside an
618
+ `AdminProvider` it needs no props. Elsewhere, pass a request client from `bindAdminRequest`:
619
+
620
+ ```typescript
621
+ import {bindAdminRequest, HarnessApprovalInbox} from "@terreno/admin-frontend";
622
+ import {baseUrl} from "@terreno/rtk";
623
+ import {Page} from "@terreno/ui";
624
+
625
+ // getAuthHeaders: the host's `() => ({Authorization: "Bearer …"})`, as passed to AdminProvider.
626
+ const request = bindAdminRequest({getAuthHeaders, origin: baseUrl});
627
+
628
+ export const SignOffScreen: React.FC = () => (
629
+ <Page title="Sign-offs" scroll>
630
+ <HarnessApprovalInbox request={request} />
631
+ </Page>
632
+ );
633
+ ```
634
+
635
+ | Prop | Type | Default | Description |
636
+ | --- | --- | --- | --- |
637
+ | `request` | `(args: AdminRequestArgs) => Promise<unknown>` | `AdminProvider` client | Request client, usually `bindAdminRequest({getAuthHeaders, origin})`. |
638
+ | `baseUrl` | `string` | `/harness` | Where `HarnessApp` is mounted (its `basePath`). Relative paths resolve against the client's `origin`. |
639
+ | `selected` | `HarnessApprovalRow \| undefined` | internal | Controlled selection: the approval whose detail is open. |
640
+ | `onSelectedChange` | `(approval \| undefined) => void` | — | With `selected`, lets the host's back control close the detail; the inbox then hides its own **All approvals** button. The admin screen uses this so the page chevron returns to the list. |
641
+ | `showHeading` | `boolean` | `true` | Show the **Approvals** heading; the admin screen passes `false` because `AdminScreenPage` already titles it. |
642
+ | `testID` | `string` | `harness-approvals` | Root test id. |
643
+
644
+ The built-in admin screen always uses `/harness`. A host that mounts `HarnessApp` at another
645
+ `basePath` registers its own `widgets.screens["harness-approvals"]` that renders
646
+ `<HarnessApprovalInbox baseUrl="/ops/harness" />`.
647
+
648
+ The example backend registers `HarnessApp` with a demo task, `demo.approvalDemo@1`. Start
649
+ approvals with the admin script `startHarnessApprovalDemo` (args `count`, default 2, and `prefix`)
650
+ from the admin Scripts screen or `bun run script startHarnessApprovalDemo --wet`. It needs Mongo
651
+ running as a replica set; without one the backend logs `Harness disabled` and skips the routes.
652
+ `example-frontend/e2e/harness-approvals.spec.ts` approves one and rejects one.