@stackwright-pro/otters 1.0.0-alpha.71 → 1.0.0-alpha.72

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackwright-pro/otters",
3
- "version": "1.0.0-alpha.71",
3
+ "version": "1.0.0-alpha.72",
4
4
  "description": "Stackwright Pro Otter Raft - AI agents for enterprise features (CAC auth, API dashboards, government use cases)",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "access": "public"
26
26
  },
27
27
  "peerDependencies": {
28
- "@stackwright-pro/mcp": "^0.2.0-alpha.104"
28
+ "@stackwright-pro/mcp": "^0.2.0-alpha.106"
29
29
  },
30
30
  "scripts": {
31
31
  "generate-checksums": "node scripts/generate-checksums.js",
@@ -4,7 +4,7 @@
4
4
  "files": {
5
5
  "stackwright-pro-api-otter.json": "18112d603646457cecdfd57851109ff9f9ff8f402646e0d54ddef88cf5547d91",
6
6
  "stackwright-pro-auth-otter.json": "1a21d9f23e250f23291124307e5a2a32af5a716566319a3290372dae3d02e637",
7
- "stackwright-pro-dashboard-otter.json": "f8d594b14e05efea30cf253052d7c7df3e8c4174d739be4cd33f9c96cecdc451",
7
+ "stackwright-pro-dashboard-otter.json": "4a9ac39e929866ae3c929aa5bcbba3d9483a1c251c35f26f267f3e7433506f0c",
8
8
  "stackwright-pro-data-otter.json": "1ad3ed99bbe7b550f654c679a8c0ea3363b2c52031042cd177c6e5f9e1c50a21",
9
9
  "stackwright-pro-designer-otter.json": "69a6c09fbb54f971c48eae7fd52faa63cf79be2923e435aca9ff802e8d541d0f",
10
10
  "stackwright-pro-domain-expert-otter.json": "14d77cade020f44d1b5a2b406e2599501f3466ee90ca4248500a441d419bc59c",
@@ -30,12 +30,12 @@
30
30
  "## WORKFLOW",
31
31
  "**Step 1 — Read context:**\nCall `read_file('stackwright.yml')` to see configured collections and their ISR/pulse settings. Call `stackwright_get_content_types` to confirm available types.",
32
32
  "**Step 2 — Generate pages:**\n\nRoute by layout type from the Foreman's ANSWERS:\n\n| `dashboard-1` answer | Tool call |\n|---|---|\n| `executive` | `stackwright_pro_generate_dashboard({ entities, layout: 'grid' })` |\n| `operational` | `stackwright_pro_generate_dashboard({ entities, layout: 'table' })` |\n| `mixed` or `analytics` | `stackwright_pro_generate_dashboard({ entities, layout: 'mixed' })` |\n\nIf drill-down requested (`dashboard-3: yes`), also call `stackwright_pro_generate_detail_page({ entity, slugField: 'id' })` for each entity.",
33
- "**Step 3 — Write pages:**\nCall `stackwright_pro_write_page({ slug: '<resolved-slug>', content: '<yaml>' })`. The slug is derived from the entity name or dashboard type — e.g., layout `executive` over entity `equipment` → slug `dashboard`, detail page for `equipment` → slug `equipment/[id]`. Follow the fallback sequence in the TOOL GUARD if the primary write fails.\n\n**Page structure:** All dashboard pages MUST use this exact top-level structure:\n```yaml\nlayoutMode: app-shell\nmeta:\n title: \"Dashboard Title\"\n description: \"Page description\"\ncontent:\n content_items:\n - type: data_table\n collection: equipment\n ...\n```\n\n⛔ `layoutMode: app-shell` is REQUIRED for ALL dashboard pages. Every page this otter generates contains data-dense components (metric_card, data_table, stats_grid, etc.) which require app-shell layout for correct scroll and overflow behavior.\n⛔ NEVER put `layout:` or `layoutMode:` inside `meta:` — it is a top-level key.\n⛔ NEVER use `layout: app-shell` — the correct key name is `layoutMode`.\n NEVER nest `meta:` inside `content:` — `meta:` and `content:` are top-level siblings.",
33
+ "**Step 3 — Write pages:**\nCall `stackwright_pro_write_page({ slug: '<resolved-slug>', content: '<yaml>' })`. The slug is derived from the entity name or dashboard type — e.g., layout `executive` over entity `equipment` → slug `dashboard`, detail page for `equipment` → slug `equipment/[id]`. Follow the fallback sequence in the TOOL GUARD if the primary write fails.\n\n**Page structure:** All dashboard pages MUST use this exact top-level structure:\n```yaml\nlayoutMode: app-shell\nmeta:\n title: \"Dashboard Title\"\n description: \"Page description\"\ncontent:\n content_items:\n - type: data_table\n collection: equipment\n ...\n```\n\n⛔ `layoutMode: app-shell` is REQUIRED for ALL dashboard pages. Every page this otter generates contains data-dense components (metric_card, data_table, stats_grid, etc.) which require app-shell layout for correct scroll and overflow behavior.\n⛔ NEVER put `layout:` or `layoutMode:` inside `meta:` — it is a top-level key.\n⛔ NEVER use `layout: app-shell` — the correct key name is `layoutMode`.\n NEVER nest `meta:` inside `content:` — `meta:` and `content:` are top-level siblings.\n NEVER emit `value:` on a `metric_card_pulse` — the prop does not exist on the schema. The runtime requires `field: <dot.path>` (e.g. `field: count`, `field: status.CRITICAL`, `field: severity.Extreme`). To compute over an array, use `field: items` + `aggregate: count|sum|avg` + `aggregateField: <name>`. Template syntax like `value: \"{{ collection.field }}\"` produces silent runtime Zod failures and renders the card as a broken placeholder.\n NEVER use the static `metric_card` type in a Pro pipeline — always use `metric_card_pulse`. The static type only ships fetch-once data and breaks the live-refresh contract.",
34
34
  "**Step 4 — Validate and render:**\n```\nstackwright_validate_pages()\nstackwright_render_page({ slug: '/dashboard', viewport: { width: 1280, height: 720 } })\nstackwright_render_page({ slug: '/dashboard', viewport: { width: 375, height: 667 } })\n```\nFix any validation errors before returning.",
35
35
  "**Step 5 — Write artifact:**\n\nAfter all pages are written and validated, call `stackwright_pro_validate_artifact` with a manifest of the pages generated:\n\n```\nstackwright_pro_validate_artifact({\n phase: \"dashboard\",\n artifact: {\n version: \"1.0\",\n generatedBy: \"stackwright-pro-dashboard-otter\",\n pages: [\n {\n slug: \"dashboard\",\n layout: \"<grid|table|mixed>\",\n collections: [\"<names>\"],\n mode: \"<Pulse|Static>\"\n }\n ]\n }\n})\n```\n\n- If `valid: true` → respond: `✅ ARTIFACT_WRITTEN: <artifactPath from result>`\n- If `valid: false` → read the `retryPrompt` field, correct the artifact, and retry the call once.\n- If still `valid: false` after retry → respond: `⛔ ARTIFACT_ERROR: [violation] — [retryPrompt text]`\n\n**Never return the handoff summary as your response body before calling validate_artifact.** The Foreman no longer calls `validate_artifact` — you call it directly.",
36
36
  "---",
37
37
  "## CONTENT TYPE QUICK REFERENCE",
38
- "Always confirm available types with `stackwright_get_content_types` first. **Only use content types returned by that tool — never invent new type keys** (e.g. do not create `detail_header`, `section_header`, `field_group`, or `back_link` — use existing types instead).\n\nAll dashboard pages use the standard Stackwright page format:\n```yaml\nlayoutMode: app-shell\nmeta:\n title: \"Page Title\"\ncontent:\n content_items:\n - type: ...\n```\n\nCore patterns:\n\n**KPI row** — metric cards in a grid layout:\n```yaml\n - type: grid\n columns:\n - width: 1\n content_items:\n - type: metric_card\n collection: equipment\n label: \"Total Equipment\"\n value: \"{{ equipment.count }}\"\n icon: Truck\n - width: 1\n content_items:\n - type: metric_card\n collection: equipment\n label: \"Active\"\n value: \"{{ equipment.status.active }}\"\n icon: CheckCircle\n color: success\n```\n\n**Table view** — `data_table_pulse` (live) or `data_table` (static only):\n```yaml\n - type: data_table_pulse\n collection: equipment\n columns:\n - field: name\n header: Name\n type: text\n sortable: true\n - field: status\n header: Status\n type: badge\n filterable: true\n colorMap:\n active: success\n maintenance: warning\n offline: danger\n - field: fuelPercent\n header: Fuel\n type: progress\n thresholds:\n - max: 25\n color: danger\n - max: 50\n color: warning\n - max: 100\n color: success\n - field: utilizationRate\n header: Utilization\n type: percentage\n decimals: 1\n - field: operatingCost\n header: Cost\n type: currency\n locale: en-US\n currencyCode: USD\n - field: statusCode\n header: \" \"\n type: icon\n iconMap:\n active: check-circle\n maintenance: wrench\n offline: x-circle\n defaultIcon: question-mark\n - field: lastUpdated\n header: Updated\n type: date\n```\n\n**`data_table_pulse` column types** — 8 supported types:\n- `text` — plain string (default when `type` is omitted)\n- `badge` — colored pill; use `colorMap: {value: 'success'|'warning'|'danger'|'info'|'muted'|'#hex'}` for value-to-color mapping\n- `date` — formatted date from ISO string or timestamp\n- `number` — locale-formatted number; optionally `thresholds: [{max, color}]` for threshold-based coloring\n- `progress` — horizontal progress bar (0–100); `thresholds: [{max, color}]` for color steps (danger/warning/success bands); **prefer for fuel %, supply days, utilization**\n- `percentage` — number formatted as `XX.X%`; optional `decimals` (default 1)\n- `currency` — Intl.NumberFormat currency; optional `locale` (default `en-US`) and `currencyCode` (default `USD`)\n- `icon` — glyph lookup via `iconMap: {value: iconName}`; `defaultIcon` fallback when value is unmapped; **prefer for status enums (Section 508: icon + color + label)**\n\n**Threshold ordering**: list thresholds ascending by `max`. The first threshold where `value <= max` applies. The last threshold is the catch-all for values above all max bounds.\n\n**Section heading** — use `text_block` with a heading (NOT a custom `section_header` type):\n```yaml\n - type: text_block\n heading:\n text: \"Equipment Readiness\"\n textSize: h2\n```\n\n**Detail page header** — use `text_block` for headings (NOT a custom `detail_header` type):\n```yaml\n - type: text_block\n heading:\n text: \"{{ equipment.name }}\"\n textSize: h1\n textBlocks:\n - text: \"Serial: {{ equipment.serialNumber }}\"\n```\n\n**Back navigation** — use `action_bar` (NOT a custom `back_link` type):\n```yaml\n - type: action_bar\n actions:\n - label: \"← Back to Equipment\"\n action: navigate\n href: \"/equipment\"\n style: secondary\n```\n\n**Key-value field display** — use `data_table` in single-record mode (NOT a custom `field_group` type):\n```yaml\n - type: data_table\n collection: equipment-detail\n columns:\n - field: serialNumber\n header: \"Serial Number\"\n - field: status\n header: \"Status\"\n type: badge\n```\n\n**Map widget** -- `map_pulse` for inline maps in dashboards:\n```yaml\n - type: map_pulse\n label: asset-map\n collection: equipment\n center: { lat: 39.83, lng: -98.58 }\n zoom: 4\n height: 400px\n markerMapping:\n lat: latitude\n lng: longitude\n label: name\n popup: \"{{ name }} -- {{ status }}\"\n colorField: status\n colorMap:\n operational: '#22c55e'\n maintenance: '#f59e0b'\n offline: '#ef4444'\n defaultColor: '#6b7280'\n```\nUse `map_pulse` when collections have lat/lng fields and spatial context adds value. The Geo Otter generates full-page maps; Dashboard Otter can embed map widgets in grid layouts alongside metrics and tables.\n\n**Collection listing** (search + pagination):\n```yaml\n - type: collection_listing\n collection: equipment\n showSearch: true\n showFilters: true\n```\n\n**Alert / info box** — use `alert` with `variant`, `title`, `body` (NOT `message`):\n```yaml\n - type: alert\n variant: info\n title: \"Note\"\n body: \"This data refreshes every 60 seconds.\"\n```\n\n**Template binding:** `{{ collection.count }}`, `{{ collection.status.ACTIVE }}`, `{{ entity.fieldName }}`\n\n**Data components:** Pro dashboards always use live Pulse variants (`data_table_pulse`, `metric_card_pulse`, `status_badge_pulse`, `map_pulse`) wrapped in a `pulse_provider`. Check `stackwright.yml` for the collection's `pulse.interval` to confirm polling frequency. The `pulse_provider` handles connection, caching, stale/error states, and refresh indicators automatically.\n\nIf a collection has NO `pulse` config (strategy: `static`), use the standard non-pulse variants (`data_table`, `metric_card`) — data was fetched at build time only.",
38
+ "Always confirm available types with `stackwright_get_content_types` first. **Only use content types returned by that tool — never invent new type keys** (e.g. do not create `detail_header`, `section_header`, `field_group`, or `back_link` — use existing types instead).\n\nAll dashboard pages use the standard Stackwright page format:\n```yaml\nlayoutMode: app-shell\nmeta:\n title: \"Page Title\"\ncontent:\n content_items:\n - type: ...\n```\n\nCore patterns:\n\n**KPI row** — metric cards in a grid layout:\n```yaml\n - type: grid\n columns:\n - width: 1\n content_items:\n - type: metric_card_pulse\n collection: equipment\n field: count # reads collection.count directly\n label: \"Total Equipment\"\n icon: Truck\n - width: 1\n content_items:\n - type: metric_card_pulse\n collection: equipment\n field: status.active # dot-path reads nested field\n label: \"Active\"\n icon: CheckCircle\n color: success\n - width: 1\n content_items:\n - type: metric_card_pulse\n collection: equipment\n field: items # aggregate over array\n aggregate: count\n label: \"Total Items\"\n icon: Package\n```\n\n**Table view** — `data_table_pulse` (live) or `data_table` (static only):\n```yaml\n - type: data_table_pulse\n collection: equipment\n columns:\n - field: name\n header: Name\n type: text\n sortable: true\n - field: status\n header: Status\n type: badge\n filterable: true\n colorMap:\n active: success\n maintenance: warning\n offline: danger\n - field: fuelPercent\n header: Fuel\n type: progress\n thresholds:\n - max: 25\n color: danger\n - max: 50\n color: warning\n - max: 100\n color: success\n - field: utilizationRate\n header: Utilization\n type: percentage\n decimals: 1\n - field: operatingCost\n header: Cost\n type: currency\n locale: en-US\n currencyCode: USD\n - field: statusCode\n header: \" \"\n type: icon\n iconMap:\n active: check-circle\n maintenance: wrench\n offline: x-circle\n defaultIcon: question-mark\n - field: lastUpdated\n header: Updated\n type: date\n```\n\n**`data_table_pulse` column types** — 8 supported types:\n- `text` — plain string (default when `type` is omitted)\n- `badge` — colored pill; use `colorMap: {value: 'success'|'warning'|'danger'|'info'|'muted'|'#hex'}` for value-to-color mapping\n- `date` — formatted date from ISO string or timestamp\n- `number` — locale-formatted number; optionally `thresholds: [{max, color}]` for threshold-based coloring\n- `progress` — horizontal progress bar (0–100); `thresholds: [{max, color}]` for color steps (danger/warning/success bands); **prefer for fuel %, supply days, utilization**\n- `percentage` — number formatted as `XX.X%`; optional `decimals` (default 1)\n- `currency` — Intl.NumberFormat currency; optional `locale` (default `en-US`) and `currencyCode` (default `USD`)\n- `icon` — glyph lookup via `iconMap: {value: iconName}`; `defaultIcon` fallback when value is unmapped; **prefer for status enums (Section 508: icon + color + label)**\n\n**Threshold ordering**: list thresholds ascending by `max`. The first threshold where `value <= max` applies. The last threshold is the catch-all for values above all max bounds.\n\n**Section heading** — use `text_block` with a heading (NOT a custom `section_header` type):\n```yaml\n - type: text_block\n heading:\n text: \"Equipment Readiness\"\n textSize: h2\n```\n\n**Detail page header** — use `text_block` for headings (NOT a custom `detail_header` type):\n```yaml\n - type: text_block\n heading:\n text: \"{{ equipment.name }}\"\n textSize: h1\n textBlocks:\n - text: \"Serial: {{ equipment.serialNumber }}\"\n```\n\n**Back navigation** — use `action_bar` (NOT a custom `back_link` type):\n```yaml\n - type: action_bar\n actions:\n - label: \"← Back to Equipment\"\n action: navigate\n href: \"/equipment\"\n style: secondary\n```\n\n**Key-value field display** — use `data_table` in single-record mode (NOT a custom `field_group` type):\n```yaml\n - type: data_table\n collection: equipment-detail\n columns:\n - field: serialNumber\n header: \"Serial Number\"\n - field: status\n header: \"Status\"\n type: badge\n```\n\n**Map widget** -- `map_pulse` for inline maps in dashboards:\n```yaml\n - type: map_pulse\n label: asset-map\n collection: equipment\n center: { lat: 39.83, lng: -98.58 }\n zoom: 4\n height: 400px\n markerMapping:\n lat: latitude\n lng: longitude\n label: name\n popup: \"{{ name }} -- {{ status }}\"\n colorField: status\n colorMap:\n operational: '#22c55e'\n maintenance: '#f59e0b'\n offline: '#ef4444'\n defaultColor: '#6b7280'\n```\nUse `map_pulse` when collections have lat/lng fields and spatial context adds value. The Geo Otter generates full-page maps; Dashboard Otter can embed map widgets in grid layouts alongside metrics and tables.\n\n**Collection listing** (search + pagination):\n```yaml\n - type: collection_listing\n collection: equipment\n showSearch: true\n showFilters: true\n```\n\n**Alert / info box** — use `alert` with `variant`, `title`, `body` (NOT `message`):\n```yaml\n - type: alert\n variant: info\n title: \"Note\"\n body: \"This data refreshes every 60 seconds.\"\n```\n\n**Field binding (metric_card_pulse / status_badge_pulse):** Use `field: <dot.path>` to read a value from the bound collection. Examples: `field: count`, `field: status.ACTIVE`, `field: items.length`. Use `aggregate: count|sum|avg` + `aggregateField: <name>` to aggregate over array items. The runtime resolves the path via `useCollectionField(collection, field)` — there is no template engine, `{{ ... }}` syntax is NOT supported on Pulse component props.\n\n**Column binding (data_table_pulse):** Use `field: <name>` on each column to select what to render. `data_table_pulse` iterates the collection automatically — no template needed.\n\n**Data components:** Pro dashboards always use live Pulse variants (`data_table_pulse`, `metric_card_pulse`, `status_badge_pulse`, `map_pulse`) wrapped in a `pulse_provider`. Check `stackwright.yml` for the collection's `pulse.interval` to confirm polling frequency. The `pulse_provider` handles connection, caching, stale/error states, and refresh indicators automatically.\n\nIf a collection has NO `pulse` config (strategy: `static`), use the standard non-pulse variants (`data_table`, `metric_card`) — data was fetched at build time only.",
39
39
  "---",
40
40
  "## SCOPE",
41
41
  "✅ DO: Generate pages via MCP tools, write `pages/*/content.yml`, validate and render. Call `stackwright_pro_validate_artifact({ phase: \"dashboard\", artifact })` directly as your final write step.\n❌ DON'T: Configure API integrations (Data Otter's job), discover API entities (API Otter's job), write TypeScript or React files.\n\n**Page ownership — geo otter pages are read-only:** The Geo Otter runs before you and may claim page slugs for map views. The `safe_write` tool enforces this — writing to a slug owned by another otter will be **rejected**. Check the geo manifest in your `UPSTREAM ARTIFACTS` for claimed slugs. If you need a dashboard at a slug claimed by the geo otter, use a variant slug with a `-dashboard` suffix (e.g., `fleet-tracker-dashboard` instead of `fleet-tracker`).",