Files
PaperClipAI/packages/paperclip-runner/examples/phase7-explorer/src/labels.ts
T
DottaandClaude Opus 5 fb006b75ca feat(runner): add Phase 7 browser scenario explorer
Implements the standalone, package-local Phase 7 scenario explorer against the
approved interaction map (docs/design/phase-7-scenario-explorer-ux.md).

Scenario runtime (src/phase7, no DOM, no network):
- Scenario index over all 106 eval cases and 16 groups, with actor role, task
  mode, fixture seed, and catalog claims declared in a profile table rather
  than inferred from case prose. Claims that cannot unlock an operation in a
  scenario's task mode or for its actor role are dropped, so no scenario shows
  a grant that unlocks nothing.
- Deterministic fixture seeds and fake-agent plans: twelve authored plans for
  the UX acceptance scenarios, derived plans for the rest. Derived plans only
  invoke operations the run actually exposes, so any denial in a derived
  transcript is a real policy result, not a scripting artefact.
- A run-artifact builder that executes each plan against the Phase 7C mock
  control plane through the Phase 7D authorization engine and records the
  ordered timeline (both channels), tool exposure, authorization records, an
  immutable ten-domain state diff, and a parity verdict. Descriptor redaction
  rules are applied at the artifact boundary so no raw secret can reach a
  rendered record.
- Parity is judged in the runtime, never in a component. Phase 7E's per-case
  result is carried through verbatim in its own labelled block and renders
  "Not run" when no report is bundled.

Explorer (examples/phase7-explorer):
- Read-only surface over the frozen SDK: hash routing, scenario picker with
  faceted filters, agent and control-plane transcript channels, and Context,
  Authorization, State diff, and Parity inspector tabs. Tokens only; the
  0.1.2 export surface is unchanged and the runtime is reached through the
  deliberately package-local @paperclip-runner-local/phase7 alias.
- Checked-in inputs arrive as declared virtual modules, so the example
  consumer keeps its boundary and fake mode does no network I/O.

Verification: 45 unit tests (index, plans, all-scenario runs and parity,
components, routes), 19 Playwright tests (route determinism, evidence routes,
credential boundary, keyboard paths, responsive), and the 24-image screenshot
acceptance set.

Fixes found while building: SDK badges overflowed at 390px inside disclosure
summaries; SDK badge capitalisation broke the label vocabulary; hash-change
deep links left the wrong mobile segment; screenshot capture leaked run state
between shots. All recorded as revisions in the interaction map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 13:30:32 +00:00

125 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {
Phase7AssertionClass,
Phase7DiffChangeKind,
Phase7ParityStatus,
Phase7PrimaryDisposition,
} from "@paperclip-runner-local/phase7";
/**
* The canonical label vocabulary from the interaction map §0. Every badge,
* facet value, and status string in the explorer resolves through this module,
* so the vocabulary stays systematic and no view invents its own wording.
*/
export const DISPOSITION_LABEL: Record<Phase7PrimaryDisposition, string> = {
control_plane_owned: "Control plane",
always_agent_tool: "Always tool",
optional_agent_tool: "Optional tool",
};
export const DISPOSITION_TONE: Record<Phase7PrimaryDisposition, "neutral" | "accent" | "success"> = {
control_plane_owned: "neutral",
always_agent_tool: "success",
optional_agent_tool: "accent",
};
export const ASSERTION_CLASS_LABEL: Record<Phase7AssertionClass, string> = {
control_plane_invariant: "Invariant",
agent_tool_contract: "Tool contract",
authorization_policy: "Authorization",
combined_multi_hop: "Multi-hop",
restraint_no_call: "Restraint",
};
export const PARITY_LABEL: Record<Phase7ParityStatus, string> = {
pass: "Pass",
fail: "Fail",
intentional_gap: "Intentional gap",
not_run: "Not run",
};
/** Status is never encoded by colour alone (WCAG colour-independence). */
export const PARITY_ICON: Record<Phase7ParityStatus, string> = {
pass: "✓",
fail: "✕",
intentional_gap: "◑",
not_run: "○",
};
export const PARITY_TONE: Record<Phase7ParityStatus, "success" | "danger" | "warning" | "neutral"> = {
pass: "success",
fail: "danger",
intentional_gap: "warning",
not_run: "neutral",
};
export const CHANGE_LABEL: Record<Phase7DiffChangeKind, string> = {
added: "Added",
changed: "Changed",
removed: "Removed",
};
export const CHANGE_ICON: Record<Phase7DiffChangeKind, string> = {
added: "+",
changed: "~",
removed: "−",
};
export const ROLE_LABEL: Record<string, string> = {
engineer: "Engineer",
manager: "Manager",
reviewer: "Reviewer",
board: "Board",
};
export const TASK_MODE_LABEL: Record<string, string> = {
standard: "Standard",
ask: "Ask",
planning: "Planning",
skill_test: "Skill test",
};
export const GROUP_LABEL: Record<string, string> = {
hb: "Heartbeat",
co: "Context",
st: "Status",
cm: "Comments",
se: "Search",
su: "Subtasks",
bl: "Blockers",
dp: "Documents",
ix: "Interactions",
ap: "Approvals",
ar: "Artifacts",
er: "Errors",
rf: "Reference surface",
mh: "Multi-hop",
rs: "Restraint",
wk: "Work modes",
};
export function groupLabel(group: string): string {
return GROUP_LABEL[group] ?? group;
}
export function outcomeLabel(outcome: string): string {
switch (outcome) {
case "allowed":
return "Allow";
case "denied":
return "Deny";
case "absent":
return "Absent";
case "exposed":
return "Exposed";
default:
return outcome;
}
}
export function outcomeTone(outcome: string): "success" | "danger" | "neutral" {
if (outcome === "allowed" || outcome === "exposed") return "success";
if (outcome === "denied") return "danger";
return "neutral";
}