Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions docs/qa/platform-checklist/FOLLOW-UPS.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions docs/qa/platform-checklist/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,7 +127,7 @@ copy its shape rather than inventing a second one.
"call": "POST /api/v1/packages",
"body": { "…": "…" }, // optional; omit for a non-body step
"expect": "what a correct response looks like — and the re-run/409 caveat",
"source": "framework file:line that grounds the call and its shape" }
"source": "framework file + symbol that grounds the call and its shape" }
],
"teardown": "the one call (or the cheaper discard-the-DB path) that undoes it",
"knownGaps": ["where the recipe is known to be sharp — e.g. an SDK helper that drops ?package="]
Expand DownExpand Up@@ -157,8 +157,14 @@ Why this shape:
the only cleanup is the `teardown` line. That is what makes a recipe safe to replay on
a live boot — and why `requires` must name the capability the recipe itself needs
(e.g. a session holding `manage_metadata`) rather than assuming a bare admin session.
- **Every call cites framework source at `file:line`.** Replay them literally; if one
4xxs, re-read the citation before assuming the recipe rotted.
- **Every call cites framework source as `file` plus the symbol it lands in.** Replay
them literally; if one 4xxs, re-read the citation before assuming the recipe rotted.
⛔ **Never pin a source LINE NUMBER** (`file.ts:NNN`, or a bare `:NNN` continuing an
earlier citation — both spellings are refused). A line number is the only part of a
citation that rots on an edit the citation has nothing to do with, and nothing can
tell a rotted one from a fresh one — so it keeps reading as "verified against source"
while pointing somewhere else. The file plus the symbol name is already the
load-bearing half. `check:platform-checklist` fails on any that reappear.
- **`why` is the debt marker.** A recipe exists because stock fixtures cannot demonstrate
something — the same discipline as a coverage waiver. Landing the fixture in the
showcase seeds proper retires the recipe; until then `why` says what is missing and
Expand Down
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/RUNNER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -201,15 +201,15 @@ contradicts it, and correct it here when it does.
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
the resolution but could not fix a boot that never exports the variable. **Check:**
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
instance `baseUrl` — if it says `:3000` and your server does not listen there, the
instance `baseUrl` — if it says `` and your server does not listen there, the
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
alone.

- **`ss` is not installed in these containers — read liveness with `curl`, never a socket
table.** `ss` and `netstat` are both absent (`command not found`); `lsof` and `fuser`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep :3000`
are present. The trap is that the usual spelling hides the cause: `ss -ltn | grep`
sends the error to stderr and prints nothing, so a **live** server is indistinguishable
from a dead one — empty stdout, exit 1, no clue why. **Check instead:**
`curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/api/v1/health` (substitute
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/SWEEP.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ value a matrix item was pinned against (`enumSource` freshness ratchet).

⚠️ Those ratchets are **NOT wired into per-PR CI** — by maintainer decision they run on a
periodic **manual** cadence (see this dir's [README.md](./README.md) "Operating cadence",
and the recorded note at `.github/workflows/lint.yml:426`). So they do **not** catch drift
and the recorded note at `.github/workflows/lint.yml`). So they do **not** catch drift
on the PR that causes it; they catch it at the **next manual run**. Do not read a green
ledger as "drift is already gated" — between runs, an unmapped kind or a grown enum sits
undetected on `main`, which is precisely why a sweep must re-derive from source rather
Expand Down
140 changes: 70 additions & 70 deletions docs/qa/platform-checklist/areas/access-security.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions docs/qa/platform-checklist/areas/ai.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -605,7 +605,7 @@
"sweep the gated affordances on the settled shell: floating chatbot FAB, the right-docked chat rail, the AppHeader AI/assistant entry, the Home layout's AI CTAs — screenshot each region, THEN read the DOM to confirm absence",
"press ⌘⇧I (the chat-dock toggle) and confirm nothing mounts — ConsoleLayout only arms the listener when dockEnabled",
"navigate directly to /ai (the stale-bookmark path): capture that a loading fallback holds while the catalog resolves, then the redirect to home lands with the splash preserved — record whether any frame of chat UI flashed",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx:258-265)",
"as admin open the System hub (SystemHubPage): screenshot the admin card cluster and record whether the 'AI Approvals' card renders on this agent-less boot (expected at head: it DOES — the card is built unconditionally, SystemHubPage.tsx)",
"click the card through to system/ai-approvals; capture the network for ~15s: GET /api/v1/ai/pending-actions?status=pending firing every ~5s and answering 501 each time (poll never stops on error — usePendingActions clears nothing and re-arms)",
"capture what the page renders: the destructive alert's text (must carry the Cloud/EE remedy sentence from the 501 body), AND whether the 'No actions waiting / When the AI proposes a sensitive action it will appear here for review' empty state renders beneath it as if a live queue exists",
"capture the browser console for the whole session"
Expand All@@ -620,7 +620,7 @@
{
"clause": "every gated affordance is ABSENT on the empty catalog: no FAB, no chat dock (⌘⇧I inert — the listener is not armed), no top-bar AI entry, no Home AI CTAs — hidden-during-load is the designed flash-free behavior, so a settled screenshot precedes every DOM read. The ENTITLED side (>= 1 agent ⇒ all of them appear) is blocked(fixture) on the open framework — record it blocked, never ticked from the hook's source",
"oracle": "screenshot",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx:117-127 gates the listener on dockEnabled = the same signal)",
"verify": "settled screenshots of shell chrome + Home show none of the affordances; post-screenshot DOM reads confirm; ⌘⇧I produces no mount (ConsoleLayout.tsx gates the listener on dockEnabled = the same signal)",
"evidence": "the region screenshots + the ⌘⇧I non-event"
},
{
Expand All@@ -630,7 +630,7 @@
"evidence": "the navigation capture / frame notes"
},
{
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx:258-265 builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"clause": "EXPECTED FAIL at head (defect K2, sweep 2026-08-30): the SystemHub 'AI Approvals' card follows the same gate as every other AI affordance — i.e. it is absent on an agent-less boot. At head it is NOT: SystemHubPage.tsx builds the card unconditionally (no useAiSurfaceEnabled read, unlike FAB/dock/header/Home), so it renders and advertises a dead surface. A run that sees the card must score this clause FAIL with the screenshot — do not tick it green, and do not re-file the defect (the sweep's FOLLOW-UPS row owns it)",
"oracle": "dom",
"verify": "screenshot the hub first, then read the card grid: the designed contract is no 'AI Approvals' card on an empty catalog; observed-at-head is the ungated card",
"evidence": "the hub screenshot + card-grid DOM"
Expand All@@ -642,7 +642,7 @@
"evidence": "the alert screenshot + one captured 501 response"
},
{
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx:286-297) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"clause": "EXPECTED FAIL at head (defect K2, same row): honest degradation means no fake empty queue and no unbounded dead poll. At head, beneath the error alert the inbox ALSO renders the 'No actions waiting' empty state (rows.length === 0 branch is not error-aware, AiPendingActionsInbox.tsx) — reading as a live, empty approval queue on a deployment that has none — and the 5s poll re-arms forever against the dead endpoint (usePendingActions keeps polling on error). Score against the honest contract; record the observed fake-empty-queue + endless poll as the FAIL evidence, do not tick green",
"oracle": "network",
"verify": "the ~15s network capture shows the repeated 501s with no backoff/stop; the page screenshot shows the empty-queue panel rendered alongside the error alert",
"evidence": "the poll trace + the page screenshot"
Expand All@@ -656,13 +656,13 @@
],
"traps": ["hydration-race", "stale-console-bundle"],
"source": [
"objectui packages/app-shell/src/hooks/useAiSurface.ts:9-40,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx:77-127 (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx:149-152 (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx:45-46 (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx:371-401 (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx:258-265 (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx:167 (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts:158-202,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx:255-297 (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts:36-113 (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"objectui packages/app-shell/src/hooks/useAiSurface.ts,71-88 (the ONE signal: access-filtered GET /ai/agents, per-seat rationale, isLoading latch for the route guard; do-NOT-simplify-to-discovery warning)",
"objectui packages/app-shell/src/layout/ConsoleLayout.tsx (FAB + chat dock + ⌘⇧I all gated on showChatbot/dockEnabled)",
"objectui packages/app-shell/src/layout/AppHeader.tsx (top-bar AI entry gated on the same hook) + console/home/HomeLayout.tsx (Home CTAs)",
"objectui packages/app-shell/src/console/ConsoleShell.tsx (RequireAiSurface — waits for resolve, splash-preserving redirect, objectui#6507)",
"objectui apps/console/src/pages/system/SystemHubPage.tsx (the 'AI Approvals' card built UNconditionally — the K2 gap) + AppContent.tsx (the system/ai-approvals route) + pages/system/AiPendingActionsPage.tsx (thin wrapper, 'Polled every 5 seconds')",
"objectui packages/plugin-chatbot/src/usePendingActions.ts,217-300 (cookie-only call(), error → error.message, pollInterval 5000 re-arming regardless of errors) + AiPendingActionsInbox.tsx (destructive alert + the error-blind 'No actions waiting' empty state)",
"packages/runtime/src/domains/ai.ts (#7653 anonymous-deny first; the /ai/agents empty-catalog courtesy #4058/#4053; every other /ai/* → capabilityUnavailable 501) + domains/unavailable.ts (single-sourced remedy sentence)",
"ai.open-edition-honest-degradation (the API half this item mirrors in the browser — 501 body/discovery parity is proven THERE, not re-proven here)"
],
"history": [
Expand Down
Loading
Loading