diff --git a/DESIGN.md b/DESIGN.md index 95121c5..7a91b05 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -21,13 +21,15 @@ The interaction contract translates that activity into a surface: what the user ### Surface Type -The surface type classifies activity purpose before frontend implementation: marketing, workbench, operator review, form flow, dashboard monitor, content/report, setup/debug tool, or conversation. It is interaction guidance, not a visual theme. +The surface type classifies activity purpose before frontend implementation: marketing, workbench, operator review, artifact inspector, form flow, dashboard monitor, content/report, setup/debug tool, or conversation. It is interaction guidance, not a visual theme. Artifact Inspector applies only when one rendered artifact is primary, semantic locus selection is required, and supporting context is meaningful relative to that locus; layout vocabulary or a keyword alone is insufficient. ### Surface Presentation Profile A surface presentation profile applies governed design-system guidance after the surface type is grounded. It can shape density, type hierarchy, region hierarchy, action emphasis, status treatment, and responsive behavior without reclassifying the activity or replacing the interaction contract. -The JudgmentKit default design system selects `judgmentkit.workbench.operational-v1` automatically for a Workbench supplied by the caller or recommended with medium or high confidence. `surface_profile: "none"` opts out, while the exact profile id locks that supported version. The neutral low-confidence Workbench fallback selects no profile. External design systems receive no JudgmentKit profile fallback. +The JudgmentKit default design system selects `judgmentkit.workbench.operational-v1` automatically for a Workbench supplied by the caller or recommended with medium or high confidence. It selects `judgmentkit.artifact-inspector.v1` for a grounded Artifact Inspector. `surface_profile: "none"` opts out, while an exact profile id locks that version. The neutral low-confidence Workbench fallback selects no profile. External design systems receive no JudgmentKit profile fallback. + +Artifact Inspector has scoped visual authority rather than whole-surface JudgmentKit authority. JudgmentKit governs the inspector chrome and inspection overlay; the rendered artifact preserves its declared external authority. Reviews must report owned-scope, artifact-preservation, and boundary results separately and must not describe the external artifact as JudgmentKit-conformant. Profiles remain adapter-layer contracts. Runtime renderers, reusable components, product vocabulary, product geometry, and authorization truth stay with the implementing product. diff --git a/README.md b/README.md index 856d6f1..7db4cb5 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ JudgmentKit keeps the core deterministic and lets model assistance enter through 2. Deterministic review packet: turns that evidence into a reviewable activity model candidate with guardrails. 3. Model-assisted candidate review seam: accepts a model-proposed candidate through dependency injection or MCP and runs the same guardrails. 4. Provider-neutral proposer adapter: builds a serializable activity-model request for an injected model caller and returns the proposed candidate to the review seam. -5. Surface-type recommendation: classifies activity purpose as marketing, workbench, operator review, form flow, dashboard monitor, content/report, setup/debug tool, or conversation before workflow or frontend implementation guidance. +5. Surface-type recommendation: classifies activity purpose as marketing, workbench, operator review, artifact inspector, form flow, dashboard monitor, content/report, setup/debug tool, or conversation before workflow or frontend implementation guidance. 6. UI workflow candidate review seam: accepts a model- or agent-proposed workflow candidate and checks grounding, action support, handoff clarity, and disclosure containment before UI implementation. 7. UI implementation contract gate: creates or accepts the repo authority for approved primitives, control semantics, required states, static checks, and browser QA. 8. UI generation handoff gate: turns only ready workflow reviews plus an implementation contract into compact handoffs for the next UI generation pass. -9. Frontend generation context adapter: combines a ready handoff, selected surface type, frontend context, and verification expectations, then applies supported presentation guidance such as the default Workbench operational profile without making styling or component inventory part of the kernel contract. +9. Frontend generation context adapter: combines a ready handoff, selected surface type, frontend context, and verification expectations, then applies governed presentation guidance such as the supported default Workbench operational profile or the proposed Artifact Inspector profile contract without making styling or component inventory part of the kernel contract. 10. Frontend implementation skill context: compiles repo-local frontend skill guidance into a gated MCP packet for agents that cannot read local skills directly, including the active `implementation_contract.design_system_source` for tokens, typography, icons, and component contracts. 11. Optional provider adapters: provider configuration and network calls stay outside the kernel and feed proposed candidates back through the same review contract. @@ -82,7 +82,9 @@ The canonical examples live beside it: They cover setup/onboarding, an operational dashboard, and a high-stakes review/refund workflow. The renderer package is still deferred; these examples prove the contract and repair behavior before visual rendering. -By default, `implementation_contract.design_system_source.mode` is `judgmentkit_default`: tokens, font roles, icon catalog policy, component contracts, and supported surface-presentation profiles come from JudgmentKit `/design-system/` exports. A sufficiently grounded or explicitly selected Workbench receives `judgmentkit.workbench.operational-v1` in frontend-generation context by default; `surface_profile: "none"` opts out without changing the Workbench interaction contract. If a complete `design_system_adapter` is supplied to `create_ui_implementation_contract`, the mode becomes `external_design_system` and that adapter owns tokens, typography, icons, and renderer components. Incomplete external adapters fail instead of falling back to JudgmentKit defaults, and JudgmentKit surface profiles never cross that boundary implicitly. +By default, `implementation_contract.design_system_source.mode` is `judgmentkit_default`: tokens, font roles, icon catalog policy, component contracts, and supported surface-presentation profiles come from JudgmentKit `/design-system/` exports. A sufficiently grounded or explicitly selected Workbench receives `judgmentkit.workbench.operational-v1` in frontend-generation context by default. A grounded Artifact Inspector receives `judgmentkit.artifact-inspector.v1`, which keeps one rendered artifact primary while applying JudgmentKit guidance only to declared chrome and overlay scopes. `surface_profile: "none"` opts out without changing either interaction contract. If a complete `design_system_adapter` is supplied to `create_ui_implementation_contract`, the mode becomes `external_design_system` and that adapter owns tokens, typography, icons, and renderer components. Incomplete external adapters fail instead of falling back to JudgmentKit defaults, and JudgmentKit surface profiles never cross that boundary implicitly. + +Artifact Inspector uses an explicit mixed-authority contract: `inspector_chrome` and `inspection_overlay` are JudgmentKit-owned, while `primary_artifact` remains `external_not_reviewed`. A chrome pass is never reported as conformance of the external artifact. Until trusted browser evidence separately proves owned-scope behavior, artifact preservation, and the declared cross-boundary interaction, implementation review remains `review_required`. The default field-select composition contract is source-backed and documented in [`docs/select-field-composition.md`](https://github.com/mikeylong/judgmentkit/blob/main/docs/select-field-composition.md). It separates selected-value spacing, the reserved trailing indicator slot, and the indicator's placement inside that slot instead of treating a field's content padding as the chevron's physical edge inset. diff --git a/contracts/ai-ui-generation.activity-contract.json b/contracts/ai-ui-generation.activity-contract.json index 240ce1a..cb4fa0a 100644 --- a/contracts/ai-ui-generation.activity-contract.json +++ b/contracts/ai-ui-generation.activity-contract.json @@ -26,7 +26,8 @@ "staged_flow", "dashboard", "report", - "conversation" + "conversation", + "artifact_centered" ], "preferred_default": "workspace", "work_units_guidance": [ @@ -59,6 +60,426 @@ } } }, + "interaction_models": { + "artifact_inspector": { + "id": "artifact_inspector", + "version": "1.0.0", + "status": "proposed", + "canonical_identifiers": { + "surface_type": "artifact_inspector", + "workflow_profile": "artifact-inspector-ui", + "frontend_surface_profile": "judgmentkit.artifact-inspector.v1", + "topology_kind": "artifact_centered" + }, + "recommendation_output": { + "profile_id": "artifact-inspector-ui", + "primary_structure": "persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion", + "density": "artifact-first contextual", + "navigation_shape": "artifact persistent; supporting context revealed from the active locus" + }, + "activity_evidence": { + "mandatory": [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative" + ], + "positive": [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative", + "completion_is_artifact_local", + "direct_or_spatial_interaction_required", + "external_artifact_visual_authority" + ], + "exclusions": [ + "queue_or_case_is_primary", + "many_items_are_primary", + "open_ended_creation_is_primary", + "conversation_turns_are_primary", + "linear_reading_is_completion", + "monitoring_is_completion", + "configuration_is_completion" + ] + }, + "topology": { + "kind": "artifact_centered", + "primary_object_id": "artifact", + "entry_work_unit_id": "orient", + "completion_work_unit_ids": [ + "verify_result", + "orient" + ], + "transitions": [ + { + "from": "orient", + "to": "select_locus" + }, + { + "from": "select_locus", + "to": "inspect_context" + }, + { + "from": "inspect_context", + "to": "preview" + }, + { + "from": "preview", + "to": "validate" + }, + { + "from": "validate", + "to": "complete", + "when": "supported" + }, + { + "from": "validate", + "to": "recover", + "when": "invalid_or_ambiguous" + }, + { + "from": "recover", + "to": "inspect_context", + "when": "retry_or_reconcile" + }, + { + "from": "complete", + "to": "verify_result" + }, + { + "from": "select_locus", + "to": "orient", + "when": "back" + } + ] + }, + "work_units": [ + { + "id": "orient", + "participant_intent": "Understand the artifact's current state.", + "system_responsibility": "Keep the artifact dominant and expose only essential status." + }, + { + "id": "select_locus", + "participant_intent": "Identify the part that matters.", + "system_responsibility": "Provide a visible selected state and equivalent pointer, touch, and keyboard paths." + }, + { + "id": "inspect_context", + "participant_intent": "Understand what supports or constrains the locus.", + "system_responsibility": "Reveal context progressively and keep its relation to the locus visible." + }, + { + "id": "preview", + "participant_intent": "Test a relation or action before commitment.", + "system_responsibility": "Distinguish provisional state from committed state." + }, + { + "id": "validate", + "participant_intent": "Know whether the relation or action is supported.", + "system_responsibility": "Explain valid, invalid, unavailable, stale, and ambiguous conditions locally." + }, + { + "id": "complete", + "participant_intent": "Finish the bounded task or enter an explicit commitment boundary.", + "system_responsibility": "Change only the state authorized by the activity contract." + }, + { + "id": "verify_result", + "participant_intent": "Confirm what changed.", + "system_responsibility": "Attach a stable result or receipt to the artifact when required." + }, + { + "id": "recover", + "participant_intent": "Cancel, go back, retry, or reconcile.", + "system_responsibility": "Reverse only uncommitted state and preserve committed results." + } + ], + "target_model": { + "target_kinds": [ + "whole_artifact", + "element", + "text_range", + "region", + "point" + ], + "target_identity_sources": [ + "artifact_supplied", + "participant_selection" + ], + "geometry_policy": "Geometry is read-only and stable for the current artifact revision; changed geometry must re-anchor to the same semantic target or become stale.", + "label_sources": [ + "artifact_supplied", + "artifact_authorized_proxy", + "Unavailable" + ], + "artifact_interactivity": [ + "static", + "live", + "hybrid" + ], + "native_interaction_precedence": "Live and hybrid artifacts retain native interaction precedence until explicit inspection mode, a declared inspection handle, or a declared keyboard command is active." + }, + "composition_variants": [ + { + "id": "peripheral_anchors", + "use_when": "A small stable set of support objects must remain visible around the artifact." + }, + { + "id": "contextual_tray", + "use_when": "Support objects are numerous, conditional, or need a narrow-viewport home." + }, + { + "id": "inline_annotation", + "use_when": "Short context can remain beside a locus without obscuring it." + }, + { + "id": "inspection_lens", + "use_when": "Temporary magnification or focused context is necessary to understand a dense locus." + }, + { + "id": "responsive_hybrid", + "use_when": "Wide and narrow viewports need different placement while preserving identical states and actions." + } + ], + "state_groups": { + "core": [ + "artifact loading", + "artifact ready", + "artifact unavailable or failed", + "no active locus", + "locus focused or selected", + "context loading", + "context ready", + "relation or action preview", + "supported", + "incompatible", + "unavailable", + "stale", + "ambiguous", + "blocked", + "cancelled or Back", + "local error and retry" + ], + "consequential": [ + "no option preselected", + "outcome pending", + "committing", + "commit succeeded", + "known failure", + "unknown result", + "receipt reconciliation", + "immutable receipt", + "concurrent result", + "superseding action when reversal is allowed" + ], + "reusable_guidance": [ + "inactive draft after the current result exists", + "correcting", + "explicit confirmation with no default affirmative", + "accepting", + "accepted version", + "declined or Back with no active guidance", + "acceptance failure", + "conflict", + "supersession", + "revocation" + ], + "automation": [ + "match preflight", + "exact match", + "near or partial match", + "automatic resolution pending", + "automatic resolution complete", + "inline causal receipt", + "changed authority or evidence", + "failed or uncertain automation", + "exception returned to human review" + ] + }, + "design_system": { + "scopes": [ + { + "scope_id": "inspector_chrome", + "root_selector": "[data-jk-scope='inspector-chrome']", + "authority": "judgmentkit_default", + "enforcement": "required", + "style_isolation": "required" + }, + { + "scope_id": "inspection_overlay", + "root_selector": "[data-jk-scope='inspection-overlay']", + "authority": "judgmentkit_default", + "enforcement": "required", + "style_isolation": "required" + }, + { + "scope_id": "primary_artifact", + "root_selector": "[data-artifact-root]", + "authority": "external_declared", + "enforcement": "external_not_reviewed", + "style_isolation": "required" + } + ], + "boundary_contracts": [ + { + "from_scope": "inspection_overlay", + "to_scope": "primary_artifact", + "allowed_roles": [ + "locus_target", + "annotation_overlay", + "connector_endpoint" + ], + "event_contract_required": true + } + ], + "component_roles": [ + "ArtifactViewport", + "ArtifactBoundary", + "ArtifactStatus", + "ObservationMarker", + "LocusSelection", + "ContextAnchor", + "AuthorityAnchor", + "AnchorRail", + "RelationPreview", + "RelationConnector", + "InlineReason", + "ContextTray", + "CommitBoundary", + "ReceiptMarker", + "BackAction", + "ResetAction", + "ZoomAndPanControls" + ] + }, + "diagnostics": [ + { + "code": "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + "meaning": "No bounded rendered artifact is declared." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + "meaning": "The participant cannot select a semantic artifact locus." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING", + "meaning": "The artifact-centered workflow has no explicit structured topology contract." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + "meaning": "The artifact-centered workflow declares an invalid topology kind." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + "meaning": "A structured artifact work unit has no stable identifier." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + "meaning": "A required artifact work unit is duplicated or does not preserve its canonical intent and system responsibility." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID", + "meaning": "The topology entry does not reference a declared work unit." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID", + "meaning": "Artifact-local completion does not reference declared work units." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID", + "meaning": "A topology transition does not connect declared work units." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING", + "meaning": "The artifact workflow has no explicit connected recovery work unit." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + "meaning": "The artifact workflow does not declare the canonical active state groups." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_ARTIFACT_NOT_DOMINANT", + "meaning": "Persistent chrome or supporting context visually displaces the artifact." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED", + "meaning": "Evidence, authority, action, or feedback has no visible or semantic relation to the active locus." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_DRAG_ONLY", + "meaning": "A required interaction has no tap, select, and keyboard alternative." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_LOCAL_FEEDBACK_MISSING", + "meaning": "Validation or status appears away from the action that caused it." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_UNCOMMITTED_STATE_AMBIGUOUS", + "meaning": "Preview and committed state are not distinguishable." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_DEFAULT_COMMIT", + "meaning": "A consequential affirmative or outcome is preselected." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_RECEIPT_MISSING", + "meaning": "A consequential result has no inspectable local receipt." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_GUIDANCE_COUPLED", + "meaning": "Current-item commitment and reusable-guidance acceptance occur in one action." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_MOBILE_ARTIFACT_OBSCURED", + "meaning": "Narrow-layout chrome covers the active locus or essential artifact content." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + "meaning": "JudgmentKit-owned chrome has no enforceable design-system scope." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING", + "meaning": "The artifact's visual authority is undeclared." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP", + "meaning": "Two authority scopes claim the same visible node without a valid overlay exception." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_STYLE_LEAK", + "meaning": "Styles or tokens cross an authority boundary." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + "meaning": "Chrome interacts with an artifact locus without a semantic boundary contract." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_NATIVE_ACTION_INTERCEPTED", + "meaning": "Inspector behavior captures a live artifact action without explicit inspection mode." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_TARGET_DRIFT", + "meaning": "An overlay silently moved to a different artifact target." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED", + "meaning": "A chrome transition changed artifact content or presentation." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM", + "meaning": "The review describes the external artifact as JudgmentKit-conformant." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT", + "meaning": "Artifact Inspector routing conflicts with mandatory activity evidence, exclusions, the selected surface, or the workflow profile." + }, + { + "code": "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", + "meaning": "Trusted scope, preservation, and boundary runtime evidence is absent." + } + ] + } + }, "profiles": { "operator-review-ui": { "pattern_id": "operator-review", @@ -129,6 +550,58 @@ "Selected or focused list item where state must remain visible but restrained.", "False-positive case: simple dashboard or form that should not invoke operator-review." ] + }, + "artifact-inspector-ui": { + "pattern_id": "artifact-inspector", + "applies_when_most_true": [ + "One bounded rendered artifact is the primary work object.", + "The participant must select a semantic locus within the artifact.", + "Supporting context is meaningful only in relation to the active locus.", + "Completion is visible on or directly beside the artifact." + ], + "do_not_use_when": [ + "The participant is primarily creating or freely editing the artifact.", + "A queue, case list, record set, or many-item comparison is primary.", + "The work can be completed without seeing or selecting an artifact locus.", + "Conversation turns, linear reading, monitoring, or configuration are the primary activity." + ], + "guardrails": [ + { + "id": "guardrail.artifact-dominance", + "guidance": "Keep the artifact as the largest and most persistent region." + }, + { + "id": "guardrail.locus-relative-context", + "guidance": "Keep evidence, authority, actions, feedback, and receipts visibly or semantically attached to the active locus." + }, + { + "id": "guardrail.native-interaction-precedence", + "guidance": "Do not intercept a live artifact's native action without explicit inspection mode." + }, + { + "id": "guardrail.scoped-visual-authority", + "guidance": "Apply JudgmentKit authority to inspector chrome and overlay without claiming authority over the external artifact." + }, + { + "id": "guardrail.local-recovery", + "guidance": "Back and cancellation reverse only uncommitted state and preserve committed receipts." + } + ], + "review_criteria": [ + "The artifact remains primary at wide and narrow viewports.", + "Pointer, touch, and keyboard paths express the same state machine.", + "Preview, commitment, result, and recovery states are distinguishable.", + "Chrome, overlay, external artifact, and boundary results are reported separately.", + "Trusted runtime evidence proves isolation and artifact preservation before acceptance." + ], + "test_scenarios": [ + "Rendered interface with a locus-related observation and accepted authority.", + "Document passage with a source-backed correction.", + "Diagram node with rule and evidence context.", + "Externally styled artifact with isolated JudgmentKit chrome and overlay.", + "Narrow layout retaining the artifact, locus, feedback, and recovery action.", + "False-positive queue, conversation, report, form, dashboard, and creation-canvas cases." + ] } }, "surface_types": { @@ -174,6 +647,20 @@ "Raw system mechanics are the primary task rather than diagnostic context." ] }, + "artifact_inspector": { + "label": "Artifact inspector", + "purpose": "Inspect one rendered artifact in place, act on a semantic locus, and leave an explicit artifact-local result.", + "applies_when_most_true": [ + "One bounded rendered artifact remains visible and primary throughout the work.", + "The participant must select a locus within the artifact to complete the work.", + "Supporting evidence, authority, actions, and results are meaningful only in relation to that locus." + ], + "do_not_use_when": [ + "The participant is primarily creating or freely editing the artifact.", + "A queue, case, many-item comparison, conversation, report, dashboard, form, or setup flow is primary.", + "The work can be completed without seeing or selecting anything on the artifact." + ] + }, "form_flow": { "label": "Form flow", "purpose": "Collect or change structured information with validation.", @@ -1282,6 +1769,227 @@ "spinner has no status text", "success state lacks receipt or next action" ] + }, + { + "id": "ArtifactViewport", + "label": "Artifact viewport", + "purpose": "Keep one rendered artifact visible as the primary work object", + "use_when": ["artifact inspection requires persistent artifact visibility"], + "avoid_when": ["the artifact is incidental to a queue, form, report, or conversation"], + "anatomy": ["artifact root", "viewport status", "inspection-mode boundary"], + "required_states": ["loading", "ready", "unavailable", "focus-visible"], + "token_bindings": ["surface", "border", "focus", "status"], + "accessibility_checks": ["named region", "zoom and reflow", "focus order"], + "review_checks": ["artifact remains dominant", "artifact authority remains declared"], + "failure_signals": ["chrome displaces the artifact", "artifact root is undeclared"] + }, + { + "id": "ArtifactBoundary", + "label": "Artifact boundary", + "purpose": "Declare the semantic and visual authority boundary around the artifact", + "use_when": ["inspector chrome and the artifact have different owners"], + "avoid_when": ["one authority owns the entire rendered candidate"], + "anatomy": ["scope root", "authority identity", "boundary event contract"], + "required_states": ["ready", "inspection-active", "stale"], + "token_bindings": ["border", "focus", "status"], + "accessibility_checks": ["inspection mode announcement", "focus crossing", "focus return"], + "review_checks": ["scope roots do not overlap", "events use semantic locus identifiers"], + "failure_signals": ["authority scope overlap", "undeclared boundary event"] + }, + { + "id": "ArtifactStatus", + "label": "Artifact status", + "purpose": "Report artifact loading, availability, revision, and staleness", + "use_when": ["artifact state changes affect inspection"], + "avoid_when": ["status duplicates stable artifact content"], + "anatomy": ["status label", "plain-language reason", "optional recovery action"], + "required_states": ["loading", "ready", "unavailable", "stale", "error"], + "token_bindings": ["status", "risk", "text"], + "accessibility_checks": ["status announcement", "non-color cue", "focus stability"], + "review_checks": ["status is artifact-local", "stale targets are not silently reassigned"], + "failure_signals": ["status is remote from the artifact", "stale target appears current"] + }, + { + "id": "ObservationMarker", + "label": "Observation marker", + "purpose": "Mark a host-owned observation at an artifact locus", + "use_when": ["an observation must remain attached to a locus"], + "avoid_when": ["the observation has no semantic artifact target"], + "anatomy": ["marker", "locus reference", "accessible observation label"], + "required_states": ["ready", "selected", "unavailable", "stale", "focus-visible"], + "token_bindings": ["status", "focus", "text"], + "accessibility_checks": ["accessible name", "selected state", "nonvisual relation description"], + "review_checks": ["marker remains chrome-owned", "marker does not obscure essential content"], + "failure_signals": ["marker becomes artifact content", "marker is color-only"] + }, + { + "id": "LocusSelection", + "label": "Locus selection", + "purpose": "Select one semantic element, range, region, point, or whole artifact", + "use_when": ["inspection begins from a specific artifact locus"], + "avoid_when": ["the task can be completed without locus selection"], + "anatomy": ["semantic target id", "selection indicator", "selection label"], + "required_states": ["none", "focused", "selected", "unavailable", "stale", "focus-visible"], + "token_bindings": ["focus", "status", "border"], + "accessibility_checks": ["name role value", "pointer touch and keyboard parity", "selected state"], + "review_checks": ["geometry maps to the same semantic target", "drag has a select-then-target alternative"], + "failure_signals": ["drag-only selection", "target drift"] + }, + { + "id": "ContextAnchor", + "label": "Context anchor", + "purpose": "Attach supporting context to the active artifact locus", + "use_when": ["evidence or guidance only makes sense for one locus"], + "avoid_when": ["context is global or unrelated to the selected locus"], + "anatomy": ["anchor", "locus reference", "context disclosure"], + "required_states": ["loading", "ready", "unavailable", "stale", "focus-visible"], + "token_bindings": ["surface", "border", "focus", "status"], + "accessibility_checks": ["relation description", "focus activation", "focus return"], + "review_checks": ["active locus remains visible", "context relation remains legible"], + "failure_signals": ["context is detached", "context covers its locus"] + }, + { + "id": "AuthorityAnchor", + "label": "Authority anchor", + "purpose": "Attach a rule or accepted authority to the active locus", + "use_when": ["authority changes how the locus is interpreted or acted on"], + "avoid_when": ["authority is generic or unrelated to the locus"], + "anatomy": ["authority label", "locus reference", "source status"], + "required_states": ["loading", "ready", "unavailable", "stale", "conflict"], + "token_bindings": ["status", "risk", "text", "border"], + "accessibility_checks": ["plain-language source", "nonvisual relation description", "non-color status"], + "review_checks": ["authority provenance is explicit", "changed authority becomes stale or conflicting"], + "failure_signals": ["authority is inferred from appearance", "changed authority remains silently active"] + }, + { + "id": "AnchorRail", + "label": "Anchor rail", + "purpose": "Organize a small stable set of locus-relative anchors at the artifact periphery", + "use_when": ["several anchors must remain visible without displacing the artifact"], + "avoid_when": ["anchors are numerous, temporary, or better suited to a contextual tray"], + "anatomy": ["rail label", "ordered anchors", "active anchor state"], + "required_states": ["ready", "selected", "overflow", "focus-visible"], + "token_bindings": ["surface", "border", "focus", "spacing"], + "accessibility_checks": ["logical order", "keyboard navigation", "responsive no overflow"], + "review_checks": ["rail remains peripheral", "active anchor maps to the active locus"], + "failure_signals": ["rail becomes a persistent dossier", "rail displaces the artifact"] + }, + { + "id": "RelationPreview", + "label": "Relation preview", + "purpose": "Show a provisional relation or action before commitment", + "use_when": ["the participant must evaluate a relation before accepting it"], + "avoid_when": ["the action is immediate, non-consequential, and needs no preview"], + "anatomy": ["source locus", "candidate relation", "validation status"], + "required_states": ["preview", "supported", "incompatible", "ambiguous", "cancelled"], + "token_bindings": ["status", "risk", "focus", "border"], + "accessibility_checks": ["provisional state announcement", "non-color state", "keyboard cancellation"], + "review_checks": ["preview is distinct from committed state", "feedback is local"], + "failure_signals": ["preview looks committed", "validation is detached"] + }, + { + "id": "RelationConnector", + "label": "Relation connector", + "purpose": "Make the relation between a locus and support object visible and nonvisually available", + "use_when": ["adjacency alone does not preserve the relation"], + "avoid_when": ["a connector would obscure essential artifact content"], + "anatomy": ["source endpoint", "path", "target endpoint", "relation description"], + "required_states": ["preview", "supported", "invalid", "stale"], + "token_bindings": ["status", "risk", "border"], + "accessibility_checks": ["nonvisual relation description", "non-color state", "zoom and reflow"], + "review_checks": ["endpoints use semantic locus ids", "path avoids the active locus"], + "failure_signals": ["geometry is treated as authority", "connector covers essential content"] + }, + { + "id": "InlineReason", + "label": "Inline reason", + "purpose": "Explain a local validation, availability, or decision result", + "use_when": ["a local action or relation needs a concise reason"], + "avoid_when": ["the reason is unrelated global guidance"], + "anatomy": ["status", "reason", "optional next action"], + "required_states": ["supported", "invalid", "unavailable", "stale", "error"], + "token_bindings": ["status", "risk", "text"], + "accessibility_checks": ["status announcement", "plain-language reason", "focus stability"], + "review_checks": ["reason appears at its cause", "reason names a bounded next step"], + "failure_signals": ["reason appears only globally", "reason exposes raw machinery without remediation"] + }, + { + "id": "ContextTray", + "label": "Context tray", + "purpose": "Reveal numerous or conditional support objects without replacing the artifact", + "use_when": ["context needs progressive disclosure or a narrow-viewport home"], + "avoid_when": ["a small stable set of peripheral anchors is sufficient"], + "anatomy": ["tray label", "active locus summary", "support objects", "dismiss action"], + "required_states": ["closed", "loading", "ready", "error", "focus-visible"], + "token_bindings": ["surface", "border", "focus", "spacing"], + "accessibility_checks": ["focus management", "dismissal", "focus return", "focus not obscured"], + "review_checks": ["tray does not cover the active locus", "artifact remains primary"], + "failure_signals": ["tray becomes a case dossier", "tray obscures its locus"] + }, + { + "id": "CommitBoundary", + "label": "Commit boundary", + "purpose": "Separate reversible preview from consequential commitment", + "use_when": ["an action changes governed state or creates an external effect"], + "avoid_when": ["inspection is non-consequential"], + "anatomy": ["pending outcome", "evidence summary", "explicit commit action", "back action"], + "required_states": ["pending", "committing", "succeeded", "known failure", "unknown result"], + "token_bindings": ["decision", "risk", "status", "focus"], + "accessibility_checks": ["no default affirmative", "complete action name", "status announcement"], + "review_checks": ["preconditions are visible", "unknown results reconcile before retry"], + "failure_signals": ["affirmative is preselected", "commit has no receipt"] + }, + { + "id": "ReceiptMarker", + "label": "Receipt marker", + "purpose": "Attach a durable result to the artifact locus it describes", + "use_when": ["a consequential action needs an inspectable result"], + "avoid_when": ["no consequential state change occurred"], + "anatomy": ["result status", "locus reference", "receipt identity", "time or revision"], + "required_states": ["pending", "complete", "reconciling", "concurrent", "superseded"], + "token_bindings": ["receipt", "status", "text", "border"], + "accessibility_checks": ["textual receipt", "non-color status", "relationship description"], + "review_checks": ["receipt is artifact-local", "receipt distinguishes immutable and superseded state"], + "failure_signals": ["receipt is detached", "completion has no durable result"] + }, + { + "id": "BackAction", + "label": "Back action", + "purpose": "Return to the previous safe inspection state", + "use_when": ["the participant can leave a preview or contextual branch"], + "avoid_when": ["the action would reverse a committed result"], + "anatomy": ["visible label", "safe-state target", "focus-return target"], + "required_states": ["ready", "disabled", "focus-visible"], + "token_bindings": ["text", "focus", "border"], + "accessibility_checks": ["accessible name", "keyboard activation", "focus return"], + "review_checks": ["only uncommitted state reverses", "previous safe state is explicit"], + "failure_signals": ["Back removes committed results", "focus is lost"] + }, + { + "id": "ResetAction", + "label": "Reset action", + "purpose": "Clear the current uncommitted inspection state", + "use_when": ["the participant needs to abandon a local preview or selection"], + "avoid_when": ["reset would remove committed or externally owned artifact state"], + "anatomy": ["visible label", "reset scope", "resulting safe state"], + "required_states": ["ready", "disabled", "focus-visible"], + "token_bindings": ["text", "focus", "border"], + "accessibility_checks": ["accessible name", "keyboard activation", "scope explanation"], + "review_checks": ["reset scope is bounded", "artifact content is preserved"], + "failure_signals": ["reset mutates the artifact", "reset scope is ambiguous"] + }, + { + "id": "ZoomAndPanControls", + "label": "Zoom and pan controls", + "purpose": "Navigate a dense artifact while preserving locus context", + "use_when": ["the artifact cannot remain usable at one fixed scale"], + "avoid_when": ["native artifact navigation already satisfies the activity"], + "anatomy": ["zoom controls", "pan controls", "reset view", "current scale"], + "required_states": ["ready", "disabled", "focus-visible"], + "token_bindings": ["surface", "border", "focus", "text"], + "accessibility_checks": ["keyboard operation", "accessible names", "zoom and reflow"], + "review_checks": ["native interaction precedence is preserved", "active locus remains related to feedback"], + "failure_signals": ["precise pointing is required", "zoom separates locus from feedback"] } ], "pattern_contracts": [ @@ -1369,6 +2077,41 @@ "risky action has no approval boundary" ] }, + { + "id": "artifact-inspector", + "label": "Artifact inspector", + "surface_type": "artifact_inspector", + "purpose": "Help a participant orient to a rendered artifact, select a locus, inspect locus-relative context, and complete or verify artifact-local work", + "required_regions": [ + "persistent rendered artifact", + "selectable artifact loci", + "locus-relative context", + "artifact-local feedback", + "artifact-local completion or recovery" + ], + "expected_controls": [ + "locus selection", + "context disclosure", + "preview and validation", + "Back or reset", + "commit and receipt when consequential" + ], + "completion_or_handoff": "participant verifies the result on or immediately beside the artifact locus", + "disclosure_boundary": "the primary artifact remains externally owned while JudgmentKit chrome and overlays use domain language and keep implementation machinery diagnostic-only", + "accessibility_expectations": [ + "loci have stable keyboard-reachable identities", + "artifact-to-context relationships have non-spatial descriptions", + "focus returns to the active locus after contextual work", + "zoom does not require precise pointing or separate feedback from its locus" + ], + "failure_signals": [ + "artifact is secondary to generic panels", + "context is detached from its locus", + "overlay styling leaks into the artifact", + "external artifact authority is claimed without trusted runtime evidence", + "native artifact interaction is blocked by an overlay" + ] + }, { "id": "form_flow", "label": "Form flow", diff --git a/contracts/judgmentkit-kernel.schema.json b/contracts/judgmentkit-kernel.schema.json index 5bdb832..57b7c47 100644 --- a/contracts/judgmentkit-kernel.schema.json +++ b/contracts/judgmentkit-kernel.schema.json @@ -8,6 +8,7 @@ "version", "source_model", "quality_order", + "interaction_models", "surface_types", "activity_model", "interaction_contract", @@ -104,6 +105,9 @@ } } }, + "interaction_models": { + "$ref": "#/$defs/interactionModels" + }, "profiles": { "type": "object", "additionalProperties": false, @@ -138,6 +142,9 @@ "review_criteria": { "type": "array", "items": { "type": "string" } }, "test_scenarios": { "type": "array", "items": { "type": "string" } } } + }, + "artifact-inspector-ui": { + "$ref": "#/$defs/artifactInspectorWorkflowProfile" } } }, @@ -148,6 +155,7 @@ "marketing", "workbench", "operator_review", + "artifact_inspector", "form_flow", "dashboard_monitor", "content_report", @@ -158,6 +166,7 @@ "marketing": { "$ref": "#/$defs/surfaceType" }, "workbench": { "$ref": "#/$defs/surfaceType" }, "operator_review": { "$ref": "#/$defs/surfaceType" }, + "artifact_inspector": { "$ref": "#/$defs/surfaceType" }, "form_flow": { "$ref": "#/$defs/surfaceType" }, "dashboard_monitor": { "$ref": "#/$defs/surfaceType" }, "content_report": { "$ref": "#/$defs/surfaceType" }, @@ -296,6 +305,626 @@ "do_not_use_when": { "type": "array", "items": { "type": "string" } } } }, + "nonEmptyStringSet": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "workflowGuardrail": { + "type": "object", + "additionalProperties": false, + "required": ["id", "guidance"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "guidance": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorWorkflowProfile": { + "type": "object", + "additionalProperties": false, + "required": [ + "pattern_id", + "applies_when_most_true", + "do_not_use_when", + "guardrails", + "review_criteria", + "test_scenarios" + ], + "properties": { + "pattern_id": { "const": "artifact-inspector" }, + "applies_when_most_true": { "$ref": "#/$defs/nonEmptyStringSet" }, + "do_not_use_when": { "$ref": "#/$defs/nonEmptyStringSet" }, + "guardrails": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "#/$defs/workflowGuardrail" } + }, + "review_criteria": { "$ref": "#/$defs/nonEmptyStringSet" }, + "test_scenarios": { "$ref": "#/$defs/nonEmptyStringSet" } + } + }, + "artifactInspectorCanonicalIdentifiers": { + "type": "object", + "additionalProperties": false, + "required": [ + "surface_type", + "workflow_profile", + "frontend_surface_profile", + "topology_kind" + ], + "properties": { + "surface_type": { "const": "artifact_inspector" }, + "workflow_profile": { "const": "artifact-inspector-ui" }, + "frontend_surface_profile": { "const": "judgmentkit.artifact-inspector.v1" }, + "topology_kind": { "const": "artifact_centered" } + } + }, + "artifactInspectorActivityEvidence": { + "type": "object", + "additionalProperties": false, + "required": ["mandatory", "positive", "exclusions"], + "properties": { + "mandatory": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "enum": [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative" + ] + } + }, + "positive": { + "type": "array", + "minItems": 6, + "maxItems": 6, + "uniqueItems": true, + "items": { + "enum": [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative", + "completion_is_artifact_local", + "direct_or_spatial_interaction_required", + "external_artifact_visual_authority" + ] + } + }, + "exclusions": { + "type": "array", + "minItems": 7, + "maxItems": 7, + "uniqueItems": true, + "items": { + "enum": [ + "queue_or_case_is_primary", + "many_items_are_primary", + "open_ended_creation_is_primary", + "conversation_turns_are_primary", + "linear_reading_is_completion", + "monitoring_is_completion", + "configuration_is_completion" + ] + } + } + } + }, + "artifactInspectorTransition": { + "type": "object", + "additionalProperties": false, + "required": ["from", "to"], + "properties": { + "from": { + "enum": [ + "orient", + "select_locus", + "inspect_context", + "preview", + "validate", + "complete", + "verify_result", + "recover" + ] + }, + "to": { + "enum": [ + "orient", + "select_locus", + "inspect_context", + "preview", + "validate", + "complete", + "verify_result", + "recover" + ] + }, + "when": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorTopology": { + "type": "object", + "additionalProperties": false, + "required": [ + "kind", + "primary_object_id", + "entry_work_unit_id", + "completion_work_unit_ids", + "transitions" + ], + "properties": { + "kind": { "const": "artifact_centered" }, + "primary_object_id": { "const": "artifact" }, + "entry_work_unit_id": { "const": "orient" }, + "completion_work_unit_ids": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { "enum": ["verify_result", "orient"] } + }, + "transitions": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "#/$defs/artifactInspectorTransition" }, + "allOf": [ + { + "contains": { + "type": "object", + "properties": { "to": { "const": "recover" } }, + "required": ["to"] + } + }, + { + "contains": { + "type": "object", + "properties": { "from": { "const": "recover" } }, + "required": ["from"] + } + } + ] + } + } + }, + "artifactInspectorWorkUnit": { + "type": "object", + "additionalProperties": false, + "required": ["id", "participant_intent", "system_responsibility"], + "properties": { + "id": { + "enum": [ + "orient", + "select_locus", + "inspect_context", + "preview", + "validate", + "complete", + "verify_result", + "recover" + ] + }, + "participant_intent": { "type": "string", "minLength": 1 }, + "system_responsibility": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorTargetModel": { + "type": "object", + "additionalProperties": false, + "required": [ + "target_kinds", + "target_identity_sources", + "geometry_policy", + "label_sources", + "artifact_interactivity", + "native_interaction_precedence" + ], + "properties": { + "target_kinds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "uniqueItems": true, + "items": { "enum": ["whole_artifact", "element", "text_range", "region", "point"] } + }, + "target_identity_sources": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { "enum": ["artifact_supplied", "participant_selection"] } + }, + "geometry_policy": { "type": "string", "minLength": 1 }, + "label_sources": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { "enum": ["artifact_supplied", "artifact_authorized_proxy", "Unavailable"] } + }, + "artifact_interactivity": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { "enum": ["static", "live", "hybrid"] } + }, + "native_interaction_precedence": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorCompositionVariant": { + "type": "object", + "additionalProperties": false, + "required": ["id", "use_when"], + "properties": { + "id": { + "enum": [ + "peripheral_anchors", + "contextual_tray", + "inline_annotation", + "inspection_lens", + "responsive_hybrid" + ] + }, + "use_when": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorStateGroups": { + "type": "object", + "additionalProperties": false, + "required": ["core", "consequential", "reusable_guidance", "automation"], + "properties": { + "core": { "$ref": "#/$defs/nonEmptyStringSet" }, + "consequential": { "$ref": "#/$defs/nonEmptyStringSet" }, + "reusable_guidance": { "$ref": "#/$defs/nonEmptyStringSet" }, + "automation": { "$ref": "#/$defs/nonEmptyStringSet" } + } + }, + "designSystemScope": { + "type": "object", + "additionalProperties": false, + "required": ["scope_id", "root_selector", "authority", "enforcement", "style_isolation"], + "properties": { + "scope_id": { + "enum": ["inspector_chrome", "inspection_overlay", "primary_artifact"] + }, + "root_selector": { "type": "string", "minLength": 1 }, + "authority": { + "enum": ["judgmentkit_default", "external_declared"] + }, + "enforcement": { + "enum": ["required", "external_not_reviewed"] + }, + "style_isolation": { "const": "required" } + }, + "allOf": [ + { + "if": { + "properties": { "scope_id": { "const": "inspector_chrome" } }, + "required": ["scope_id"] + }, + "then": { + "properties": { + "root_selector": { "const": "[data-jk-scope='inspector-chrome']" }, + "authority": { "const": "judgmentkit_default" }, + "enforcement": { "const": "required" } + } + } + }, + { + "if": { + "properties": { "scope_id": { "const": "inspection_overlay" } }, + "required": ["scope_id"] + }, + "then": { + "properties": { + "root_selector": { "const": "[data-jk-scope='inspection-overlay']" }, + "authority": { "const": "judgmentkit_default" }, + "enforcement": { "const": "required" } + } + } + }, + { + "if": { + "properties": { "scope_id": { "const": "primary_artifact" } }, + "required": ["scope_id"] + }, + "then": { + "properties": { + "root_selector": { "const": "[data-artifact-root]" }, + "authority": { "const": "external_declared" }, + "enforcement": { "const": "external_not_reviewed" } + } + } + }, + { + "if": { + "properties": { "authority": { "const": "judgmentkit_default" } }, + "required": ["authority"] + }, + "then": { "properties": { "enforcement": { "const": "required" } } } + }, + { + "if": { + "properties": { "authority": { "const": "external_declared" } }, + "required": ["authority"] + }, + "then": { "properties": { "enforcement": { "const": "external_not_reviewed" } } } + } + ] + }, + "artifactInspectorScopeSet": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { "$ref": "#/$defs/designSystemScope" }, + "allOf": [ + { "contains": { "type": "object", "properties": { "scope_id": { "const": "inspector_chrome" } }, "required": ["scope_id"] } }, + { "contains": { "type": "object", "properties": { "scope_id": { "const": "inspection_overlay" } }, "required": ["scope_id"] } }, + { "contains": { "type": "object", "properties": { "scope_id": { "const": "primary_artifact" } }, "required": ["scope_id"] } } + ] + }, + "boundaryContract": { + "type": "object", + "additionalProperties": false, + "required": ["from_scope", "to_scope", "allowed_roles", "event_contract_required"], + "properties": { + "from_scope": { "const": "inspection_overlay" }, + "to_scope": { "const": "primary_artifact" }, + "allowed_roles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "enum": ["locus_target", "annotation_overlay", "connector_endpoint"] } + }, + "event_contract_required": { "const": true } + } + }, + "artifactInspectorDesignSystemRegistry": { + "type": "object", + "additionalProperties": false, + "required": ["scopes", "boundary_contracts", "component_roles"], + "properties": { + "scopes": { "$ref": "#/$defs/artifactInspectorScopeSet" }, + "boundary_contracts": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "#/$defs/boundaryContract" } + }, + "component_roles": { + "type": "array", + "minItems": 17, + "maxItems": 17, + "uniqueItems": true, + "items": { + "enum": [ + "ArtifactViewport", + "ArtifactBoundary", + "ArtifactStatus", + "ObservationMarker", + "LocusSelection", + "ContextAnchor", + "AuthorityAnchor", + "AnchorRail", + "RelationPreview", + "RelationConnector", + "InlineReason", + "ContextTray", + "CommitBoundary", + "ReceiptMarker", + "BackAction", + "ResetAction", + "ZoomAndPanControls" + ] + } + } + } + }, + "artifactInspectorDiagnostic": { + "type": "object", + "additionalProperties": false, + "required": ["code", "meaning"], + "properties": { + "code": { + "type": "string", + "pattern": "^JK_ARTIFACT_INSPECTOR_[A-Z0-9_]+$" + }, + "meaning": { "type": "string", "minLength": 1 } + } + }, + "artifactInspectorRecommendationOutput": { + "type": "object", + "additionalProperties": false, + "required": [ + "profile_id", + "primary_structure", + "density", + "navigation_shape" + ], + "properties": { + "profile_id": { "const": "artifact-inspector-ui" }, + "primary_structure": { + "const": "persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion" + }, + "density": { "const": "artifact-first contextual" }, + "navigation_shape": { + "const": "artifact persistent; supporting context revealed from the active locus" + } + } + }, + "artifactInspectorRegistry": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "version", + "status", + "canonical_identifiers", + "recommendation_output", + "activity_evidence", + "topology", + "work_units", + "target_model", + "composition_variants", + "state_groups", + "design_system", + "diagnostics" + ], + "properties": { + "id": { "const": "artifact_inspector" }, + "version": { "const": "1.0.0" }, + "status": { "const": "proposed" }, + "canonical_identifiers": { "$ref": "#/$defs/artifactInspectorCanonicalIdentifiers" }, + "recommendation_output": { "$ref": "#/$defs/artifactInspectorRecommendationOutput" }, + "activity_evidence": { "$ref": "#/$defs/artifactInspectorActivityEvidence" }, + "topology": { "$ref": "#/$defs/artifactInspectorTopology" }, + "work_units": { + "type": "array", + "minItems": 8, + "maxItems": 8, + "uniqueItems": true, + "items": { "$ref": "#/$defs/artifactInspectorWorkUnit" }, + "allOf": [ + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "orient" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "select_locus" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "inspect_context" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "preview" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "validate" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "complete" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "verify_result" } } + }, + "minContains": 1, + "maxContains": 1 + }, + { + "contains": { + "type": "object", + "required": ["id"], + "properties": { "id": { "const": "recover" } } + }, + "minContains": 1, + "maxContains": 1 + } + ] + }, + "target_model": { "$ref": "#/$defs/artifactInspectorTargetModel" }, + "composition_variants": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "uniqueItems": true, + "items": { "$ref": "#/$defs/artifactInspectorCompositionVariant" } + }, + "state_groups": { "$ref": "#/$defs/artifactInspectorStateGroups" }, + "design_system": { "$ref": "#/$defs/artifactInspectorDesignSystemRegistry" }, + "diagnostics": { + "type": "array", + "minItems": 29, + "uniqueItems": true, + "items": { "$ref": "#/$defs/artifactInspectorDiagnostic" } + } + } + }, + "interactionModels": { + "type": "object", + "additionalProperties": false, + "required": ["artifact_inspector"], + "properties": { + "artifact_inspector": { "$ref": "#/$defs/artifactInspectorRegistry" } + } + }, + "artifactInspectorImplementationContract": { + "type": "object", + "additionalProperties": false, + "required": [ + "registry_id", + "registry_version", + "surface_type", + "workflow_profile", + "frontend_surface_profile", + "topology_kind", + "active_state_groups", + "state_groups", + "trusted_runtime_evidence_required", + "external_artifact_review_status" + ], + "properties": { + "registry_id": { "const": "artifact_inspector" }, + "registry_version": { "const": "1.0.0" }, + "surface_type": { "const": "artifact_inspector" }, + "workflow_profile": { "const": "artifact-inspector-ui" }, + "frontend_surface_profile": { "const": "judgmentkit.artifact-inspector.v1" }, + "topology_kind": { "const": "artifact_centered" }, + "active_state_groups": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "enum": ["core", "consequential", "reusable_guidance", "automation"] }, + "contains": { "const": "core" } + }, + "state_groups": { "$ref": "#/$defs/artifactInspectorStateGroups" }, + "trusted_runtime_evidence_required": { "const": true }, + "external_artifact_review_status": { "const": "external_not_reviewed" } + } + }, "visualTokenRole": { "type": "object", "additionalProperties": false, @@ -1201,6 +1830,14 @@ } }, "design_system_source": { "$ref": "#/$defs/designSystemSource" }, + "design_system_scopes": { "$ref": "#/$defs/artifactInspectorScopeSet" }, + "boundary_contracts": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "#/$defs/boundaryContract" } + }, + "artifact_inspector": { "$ref": "#/$defs/artifactInspectorImplementationContract" }, "local_component_authority": { "type": "object", "additionalProperties": false, @@ -1507,6 +2144,30 @@ } } } + }, + { + "if": { + "anyOf": [ + { "required": ["design_system_scopes"] }, + { "required": ["boundary_contracts"] }, + { "required": ["artifact_inspector"] } + ] + }, + "then": { + "required": ["design_system_scopes", "boundary_contracts", "artifact_inspector"] + } + }, + { + "if": { "required": ["artifact_inspector"] }, + "then": { + "properties": { + "design_system_source": { + "type": "object", + "properties": { "mode": { "const": "judgmentkit_default" } }, + "required": ["mode"] + } + } + } } ] } diff --git a/docs/agent-usage-contract.md b/docs/agent-usage-contract.md index cafb4e2..37b6df1 100644 --- a/docs/agent-usage-contract.md +++ b/docs/agent-usage-contract.md @@ -23,8 +23,11 @@ Use JudgmentKit before UI generation, UI critique, implementation planning, or h - Treat `ready_for_review` as permission to proceed with UI concept work, not final product approval. - Treat `needs_source_context` as a prompt to gather source context or ask the packet's targeted questions. - Treat surface type as activity-purpose guidance, not visual styling. +- Select `artifact_inspector` only when the rendered artifact is primary, semantic locus selection is required, and support is locus-relative. If those mandatory signals conflict with a queue, creation, conversation, linear-reading, monitoring, or configuration activity, stop at `review_required`; do not break the tie from layout vocabulary. - Treat the implementation contract as the authority for allowed primitives, control semantics, states, static checks, browser QA, visual asset handling, and accessibility evidence. - Treat `implementation_contract.design_system_source` as the active authority for visual tokens, typography, icon assets, and renderer components. `judgmentkit_default` uses JudgmentKit `/design-system/` exports; `external_design_system` requires a complete adapter and has no implicit JudgmentKit fallback. `external_authority` is trace metadata unless paired with `design_system_adapter`. +- For Artifact Inspector, preserve `design_system_scopes`, `boundary_contracts`, and `artifact_inspector` as one bundle through workflow review, implementation handoff, frontend context, and implementation review. Apply JudgmentKit conformance only to owned chrome and overlay scopes; keep `primary_artifact` as `external_not_reviewed`. +- Do not accept candidate-authored claims as trusted Artifact Inspector runtime proof. Without trusted scope, artifact-preservation, and boundary evidence, retain `review_required` and the corresponding stable diagnostic. - Treat `visual_token_adapter` as the token/font/icon evidence envelope for the active design-system source. Asset guidance cannot replace activity fit, primitive coverage, state coverage, accessibility evidence, static checks, or browser QA. - Keep implementation terms out of product UI unless the activity is setup, debugging, auditing, integration, or explicit source inspection. - When a model proposes an activity model, call `review_activity_model_candidate` before trusting it. @@ -53,6 +56,7 @@ Before handing off UI work, confirm: - implementation terms contained in disclosure, evidence, or guardrails - workflow topology, work units, surface set, primary actions, decision support, and handoff are named - implementation contract names approved primitives and required states +- Artifact Inspector handoffs, when applicable, preserve the artifact identity, semantic locus model, `artifact_centered` topology, active state groups, authority scopes, boundary contract, and external artifact status - token, font, icon, and renderer component guidance comes from `implementation_contract.design_system_source`, with no implied font CDN, remote icon package, or fallback from external systems to JudgmentKit defaults - substantive visual requirements have an image-generation, premium 3D/rendering, or high-quality visualization path when present - static checks, browser QA, core accessibility evidence, and any conditional visual-background contrast, non-text contrast, forced-colors, target-size, focus-not-obscured, no-keyboard-trap, reduced-motion, pause/stop/hide, form/error/status, media alternative, or semantic fallback evidence are specified when required diff --git a/package.json b/package.json index e6d69d0..62bdd83 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "smoke:icons": "node scripts/lucide-icon-catalog-smoke.mjs", "smoke:openai-ui-workflow": "node scripts/smoke-openai-ui-workflow.mjs", "test:presentation-theme": "node tests/presentation-theme.test.mjs && npm run presentation-theme:actual:evidence && npm run presentation-theme:pack:smoke", - "test": "node tests/lucide-icon-catalog.test.mjs && node tests/lucide-icon-catalog-smoke.test.mjs && node tests/kernel-contract.test.mjs && node tests/analyze-implementation-brief.test.mjs && node tests/activity-model-review.test.mjs && node tests/surface-types.test.mjs && node tests/workbench-surface-profile.test.mjs && node tests/ui-generation-handoff.test.mjs && node tests/visual-composition-integration.test.mjs && node tests/visual-composition-browser-runtime.test.mjs && node tests/cognitive-dimensions-review.test.mjs && node tests/openai-responses-provider.test.mjs && node tests/cli.test.mjs && node tests/demo-one-shot.test.mjs && node tests/demo-modal-actions.test.mjs && node tests/demo-comparison.test.mjs && node tests/demo-music-comparison.test.mjs && node tests/demo-model-ui-matrix.test.mjs && node tests/ui-generation-evals.test.mjs && node tests/mcp-pilot-evals.test.mjs && node tests/mcp-pilot-llm-judge.test.mjs && node tests/eval-rerun-guardrails.test.mjs && node tests/mcp.test.mjs && node tests/mcp-stdio.test.mjs && node tests/mcp-http.test.mjs && node tests/install-mcp.test.mjs && node tests/codex-plugin.test.mjs && npm run test:presentation-theme && node tests/ai-native-design-system-release.test.mjs && node tests/er-flow-dashboard.test.mjs && node tests/site.test.mjs && node tests/site-local-server.test.mjs && node tests/skills.test.mjs && node evals/run-evals.mjs" + "test": "node tests/lucide-icon-catalog.test.mjs && node tests/lucide-icon-catalog-smoke.test.mjs && node tests/kernel-contract.test.mjs && node tests/analyze-implementation-brief.test.mjs && node tests/activity-model-review.test.mjs && node tests/surface-types.test.mjs && node tests/artifact-inspector-routing.test.mjs && node tests/artifact-inspector-workflow.test.mjs && node tests/workbench-surface-profile.test.mjs && node tests/artifact-inspector-surface-profile.test.mjs && node tests/artifact-inspector-authority.test.mjs && node tests/ui-generation-handoff.test.mjs && node tests/visual-composition-integration.test.mjs && node tests/visual-composition-browser-runtime.test.mjs && node tests/cognitive-dimensions-review.test.mjs && node tests/openai-responses-provider.test.mjs && node tests/cli.test.mjs && node tests/demo-one-shot.test.mjs && node tests/demo-modal-actions.test.mjs && node tests/demo-comparison.test.mjs && node tests/demo-music-comparison.test.mjs && node tests/demo-model-ui-matrix.test.mjs && node tests/ui-generation-evals.test.mjs && node tests/mcp-pilot-evals.test.mjs && node tests/mcp-pilot-llm-judge.test.mjs && node tests/eval-rerun-guardrails.test.mjs && node tests/mcp.test.mjs && node tests/mcp-stdio.test.mjs && node tests/mcp-http.test.mjs && node tests/install-mcp.test.mjs && node tests/codex-plugin.test.mjs && npm run test:presentation-theme && node tests/ai-native-design-system-release.test.mjs && node tests/er-flow-dashboard.test.mjs && node tests/site.test.mjs && node tests/site-local-server.test.mjs && node tests/skills.test.mjs && node evals/run-evals.mjs" }, "dependencies": { "@emotion/cache": "^11.14.0", diff --git a/site/build-site.mjs b/site/build-site.mjs index 0f10b44..ee6cff2 100755 --- a/site/build-site.mjs +++ b/site/build-site.mjs @@ -7053,15 +7053,20 @@ function renderDesignSystemPatternsPage(model) {

Presentation profiles

-

Presentation profiles specialize a supported surface pattern after the activity, interaction contract, and active design-system source are established. They do not reclassify the activity or select a runtime renderer.

+

Presentation profiles specialize a surface pattern after the activity, interaction contract, and active design-system source are established. Their status identifies whether the profile is supported or still proposed; they do not reclassify the activity or select a runtime renderer.

${renderDesignSystemTable({ - caption: "Supported surface presentation profiles", + caption: "Surface presentation profiles", columns: [ { key: "id", label: "Profile", render: (row) => `${escapeHtml(row.id)}
${escapeHtml(row.name)}`, }, + { + key: "status", + label: "Status", + render: (row) => escapeHtml(row.status ?? "proposed"), + }, { key: "surface_type", label: "Surface", diff --git a/specs/artifact-inspector.md b/specs/artifact-inspector.md new file mode 100644 index 0000000..842c7a2 --- /dev/null +++ b/specs/artifact-inspector.md @@ -0,0 +1,862 @@ +# JudgmentKit Artifact Inspector Interaction Model + +Status: proposed specification + +Audience: JudgmentKit product, design, and engineering + +Scope: JudgmentKit interaction-model selection, UI generation, implementation +contracts, and review. This specification does not implement the model or +change a product prototype. + +## Decision + +Add `artifact_inspector` as a first-class JudgmentKit surface type with a +versioned interaction profile and a scoped design-system authority contract. + +The model supports work in which one rendered artifact is the primary object, +the participant acts on a specific locus within it, and supporting evidence, +authority, actions, state changes, and receipts only make sense in relation to +that locus. + +The surrounding product chrome and inspection overlay use JudgmentKit. The +artifact may use an independent design system. JudgmentKit must validate its +two layers without claiming visual authority over the artifact. + +## Why JudgmentKit Needs This Model + +The current taxonomy creates three predictable errors for artifact-centered +work: + +1. Words such as `thread` can route a bounded visual relation to + `conversation`, even when no message exchange exists. +2. `workbench` can recover the operational intent but defaults toward + master-detail, queue-detail, or tool-centric structure. +3. A whole-candidate design-system gate cannot validate quiet product chrome + around an independently styled artifact without either rejecting the + artifact or forcing it into JudgmentKit styling. + +`operator_review` is also insufficient. It tends to make the case, evidence, +risk, and decision controls primary. Artifact inspection reverses that +relationship: the artifact remains primary, and the surrounding context earns +space only through its relation to the artifact. + +## Goals + +- Select `artifact_inspector` from activity evidence rather than keywords. +- Generate an artifact-dominant interaction topology rather than a dashboard, + form, case file, dossier, or conversation. +- Keep observations, evidence, authority, actions, and results spatially or + contextually attached to the artifact. +- Support direct manipulation without requiring drag, hover, or precise + pointing. +- Keep restrained product chrome deliberate, legible, responsive, accessible, + and visibly complete. +- Allow JudgmentKit design-system enforcement for the product chrome while an + embedded artifact retains declared external visual authority. +- Require explicit commitment, recovery, and receipts when the activity has + consequential state changes. +- Preserve the existing surface taxonomy for activities that are not + artifact-centered. + +## Non-Goals + +- Defining the artifact's visual design. +- Turning JudgmentKit into a general-purpose drawing or artifact-authoring + tool. +- Treating every review surface as an artifact inspector. +- Replacing `workbench`, `operator_review`, `content_report`, or + `conversation`. +- Hard-coding one product's authority taxonomy, decision labels, or automation + rules into the base model. +- Waiving accessibility, state coverage, or review evidence because part of + the candidate has external visual authority. +- Treating an externally styled artifact as JudgmentKit-conformant when + JudgmentKit did not review that artifact. + +## Canonical Identifiers + +Use one concept consistently across the current MCP fields: + +| Contract location | Value | +| --- | --- | +| `surface_type` | `artifact_inspector` | +| Workflow profile | `artifact-inspector-ui` | +| Frontend surface profile | `judgmentkit.artifact-inspector.v1` | +| Interaction topology kind | `artifact_centered` | + +The separate identifiers preserve the existing tool interfaces while pointing +to one versioned definition. + +## Terms + +- **Artifact:** The rendered object being inspected. It may be a product UI, + document, composition, diagram, generated result, or another bounded object + that can be meaningfully rendered. +- **Artifact locus:** A selectable region, object, state, or semantic feature + within the artifact. +- **Inspector chrome:** JudgmentKit-owned controls, status, annotations, + connectors, supporting context, and recovery affordances around the + artifact. +- **Inspection overlay:** JudgmentKit-owned selections, pins, connectors, + handles, and local feedback rendered over the artifact without becoming part + of the artifact's visual authority. +- **Support object:** Evidence, authority, rule, comparison, diagnostic, or + another object whose meaning is attached to an artifact locus. +- **Relation:** The visible connection between a locus and a support object or + action. A relation may be provisional or committed. +- **Consequential action:** An action that changes governed state, publishes, + approves, rejects, commits, or creates a reusable rule. +- **Receipt:** A durable, inspectable result of a consequential action. +- **External artifact authority:** A declared visual and component authority + that owns the artifact but not the surrounding inspector chrome. + +## Activity Definition + +### Purpose + +Help a participant inspect one rendered artifact in place, select the part +that matters, bring the right supporting context to that location, and leave +the artifact in an explicit, inspectable state. + +### Primary object + +One current artifact or artifact revision. Observations, evidence, authority, +actions, guidance, and receipts are subordinate objects. + +### Participant completion + +The participant can see what required attention, understand the relevant +supporting context, complete the bounded action when one exists, and verify +the resulting state on or immediately beside the artifact. + +### Applies when most are true + +- The artifact must remain visible for the participant to complete the work. +- The work begins by inspecting or selecting a locus within the artifact. +- Supporting context only becomes meaningful in relation to that locus. +- The participant must compare, relate, validate, decide, or act in place. +- The completion state is legible on or directly beside the artifact. +- Persistent global controls would compete with the object being inspected. +- The artifact may have visual authority independent from the host product. + +The first three conditions are mandatory. A keyword match is never sufficient. + +When mandatory and exclusion evidence are both materially present, +`recommend_surface_types` should return `review_required` with the conflicting +signals. It must not break the tie from layout vocabulary. + +### Do not use when + +- The participant is primarily creating or freely editing the artifact. +- A queue, case list, record set, or many-item comparison is the primary + object. +- The work can be completed without seeing or selecting anything on the + artifact. +- A linear document or report is sufficient. +- The exchange between participants is the primary object. +- The task is primarily monitoring status, trends, or operational health. +- The task is primarily setup, configuration, or debugging. +- The artifact cannot be rendered meaningfully and the supporting dossier must + become primary. + +## Surface-Type Routing + +### Positive evidence + +`recommend_surface_types` should score the following activity evidence: + +- `rendered_artifact_is_primary` +- `locus_selection_required` +- `support_context_is_locus_relative` +- `completion_is_artifact_local` +- `direct_or_spatial_interaction_required` +- `external_artifact_visual_authority` + +### Exclusion evidence + +- `queue_or_case_is_primary` +- `many_items_are_primary` +- `open_ended_creation_is_primary` +- `conversation_turns_are_primary` +- `linear_reading_is_completion` +- `monitoring_is_completion` +- `configuration_is_completion` + +### Tie-break rules + +| Competing type | Choose `artifact_inspector` when | Choose the competing type when | +| --- | --- | --- | +| `workbench` | One artifact stays primary and locus-specific inspection is required. | The participant creates, arranges, or operates across many objects or tools. | +| `operator_review` | The participant must act directly on the produced artifact and context is attached to a locus. | The case, evidence, risk, and bounded decision can be reviewed without artifact-local interaction. | +| `content_report` | Reading leads to locus-specific inspection or state change. | Linear reading, understanding, citation, or sharing is completion. | +| `conversation` | Language is subordinate to a persistent artifact. | Turns, messages, and response continuity are the primary objects. | +| `dashboard_monitor` | The participant investigates one artifact and acts locally. | Status, exceptions, or trends across items are the primary completion. | +| `form_flow` | Structured values are contextual to an artifact locus. | Collecting or changing structured information is the activity. | + +The token `thread` must not independently trigger `conversation`. A visual +connection, causal trace, annotation thread, or authority thread is not a +conversation without message turns, exchange continuity, and a response +activity. + +### Expected recommendation output + +```json +{ + "recommended_surface_type": "artifact_inspector", + "profile_id": "artifact-inspector-ui", + "confidence": "high", + "primary_structure": "persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion", + "density": "artifact-first contextual", + "navigation_shape": "artifact persistent; supporting context revealed from the active locus" +} +``` + +## Interaction Topology + +### Core loop + +```text +orient to artifact + -> select a locus + -> reveal relevant support or actions + -> preview a relation or change + -> validate locally + -> commit when consequential, or complete when non-consequential + -> show the result on or beside the artifact + -> recover to the last safe state when cancelled or invalid +``` + +### Required work units + +| Work unit | Participant intent | System responsibility | +| --- | --- | --- | +| `orient` | Understand the artifact's current state. | Keep the artifact dominant and expose only essential status. | +| `select_locus` | Identify the part that matters. | Provide a visible selected state and equivalent pointer, touch, and keyboard paths. | +| `inspect_context` | Understand what supports or constrains the locus. | Reveal context progressively and keep its relation to the locus visible. | +| `preview` | Test a relation or action before commitment. | Distinguish provisional state from committed state. | +| `validate` | Know whether the relation or action is supported. | Explain valid, invalid, unavailable, stale, and ambiguous conditions locally. | +| `complete` | Finish the bounded non-consequential task or enter an explicit commit boundary. | Change only the state authorized by the activity contract. | +| `verify_result` | Confirm what changed. | Attach a stable result or receipt to the artifact when required. | +| `recover` | Cancel, go back, retry, or reconcile. | Reverse only uncommitted state and preserve committed results. | + +### Artifact target and interactivity model + +Every workflow declares one artifact boundary and one target model: + +- target kinds: whole artifact, element, text range, region, or point; +- target identity: artifact-supplied or explicit participant selection; +- geometry: read-only and stable for the current artifact revision; +- label: artifact-supplied, artifact-authorized proxy, or `Unavailable`; +- artifact interactivity: `static`, `live`, or `hybrid`. + +Native artifact interaction has precedence for `live` and `hybrid` artifacts. +JudgmentKit may capture a gesture only after the participant enters an explicit +inspection mode, uses a declared inspection handle, or invokes a declared +keyboard command. Artifact inspection cannot silently replace an artifact's +native action. + +If artifact geometry changes, the overlay must re-anchor to the same semantic +target or mark the target stale. It cannot silently drift to another element. + +### Normative topology shape + +`review_ui_workflow_candidate` should accept and validate this explicit shape: + +```json +{ + "workflow": { + "topology": { + "kind": "artifact_centered", + "primary_object_id": "artifact", + "entry_work_unit_id": "orient", + "completion_work_unit_ids": ["verify_result", "orient"], + "transitions": [ + {"from": "orient", "to": "select_locus"}, + {"from": "select_locus", "to": "inspect_context"}, + {"from": "inspect_context", "to": "preview"}, + {"from": "preview", "to": "validate"}, + {"from": "validate", "to": "complete", "when": "supported"}, + {"from": "validate", "to": "recover", "when": "invalid_or_ambiguous"}, + {"from": "recover", "to": "inspect_context", "when": "retry_or_reconcile"}, + {"from": "complete", "to": "verify_result"}, + {"from": "select_locus", "to": "orient", "when": "back"} + ] + }, + "work_units": [] + } +} +``` + +The reviewer should return a field-specific diagnostic when topology is +malformed. It must not collapse all topology errors into `workflow.topology is +required`. + +## Generation Procedure + +JudgmentKit should choose a composition after the activity and topology are +valid. Styling and component selection come later. + +1. Confirm that one rendered artifact is the primary object. +2. Establish whether targets are whole-artifact, element, text-range, region, + or point selections. +3. Establish whether the artifact is static, live, or hybrid and protect its + native interactions. +4. Identify the support objects that must remain related to the active target. +5. Separate reversible preview, consequential commitment, and durable result. +6. Activate only the conditional state groups required by the activity. +7. Choose the least persistent chrome that keeps the target, context, action, + and result understandable together. +8. Project the same state machine to wide and narrow viewports. +9. Declare visual-authority scopes and cross-boundary behavior before frontend + generation. +10. Generate and review the chrome, overlay, artifact-preservation, and boundary + evidence separately. + +### Allowed composition variants + +- **Peripheral anchors:** Use when a small, stable set of support objects must + remain visible around the artifact. +- **Contextual tray:** Use when support objects are numerous, conditional, or + need a narrow-viewport home. +- **Inline annotation:** Use when short context can remain beside a text range, + element, region, or point without obscuring it. +- **Inspection lens:** Use when temporary magnification or focused context is + necessary to understand a dense locus. +- **Responsive hybrid:** Use a peripheral form on wide viewports and a + contextual tray on narrow viewports while preserving identical states and + actions. + +JudgmentKit selects a variant from target density, artifact interactivity, +support-object count, consequence, and viewport evidence. The variant is not a +visual theme and cannot change the participant's completion state. + +## Composition Grammar + +### Required relationships + +- The artifact is the largest and most persistent visual region. +- Inspector chrome surrounds, overlays, or attaches to the artifact without + turning it into a card within a larger case screen. +- The active locus remains visible while its supporting context is open. +- Supporting context is adjacent to the locus, connected to it, or invoked + directly from it. +- Feedback appears at the action or relation that caused it. +- Consequential controls appear only when their preconditions are met. +- Receipts remain visibly connected to the result they describe. +- The participant can return to the previous safe state without searching for + global navigation. +- Host-owned overlay marks remain distinguishable from artifact content. + +### Chrome posture + +The chrome should be reduced but finished: + +- flat or low-elevation surfaces; +- precise alignment and spacing; +- restrained type hierarchy; +- semantic color rather than decorative color; +- sparse labels and domain language; +- contextual controls rather than persistent toolbars; +- clear focus, hover, selected, unavailable, invalid, committing, and complete + states; +- no styling that competes with the artifact. + +Reduced styling is a role distinction. It is not permission for weak hierarchy, +browser-default controls, low contrast, ambiguous targets, or unfinished +states. + +### Artifact posture + +- The artifact preserves its own visual language. +- The artifact may be richer, more dimensional, or more product-like than the + chrome. +- JudgmentKit must not restyle the artifact to make the surrounding chrome + pass. +- Artifact content must not be duplicated into a stack of inspector panels + merely to fit an existing surface pattern. + +### Avoid by default + +- queue-detail and master-detail layouts; +- dashboard summaries; +- persistent evidence dossiers; +- stacked labeled panels; +- form-like step progression; +- a chat composer or message history; +- action bars detached from the selected locus; +- generic approval panels that can be completed without inspecting the + artifact; +- drag-only, hover-only, or color-only interaction. + +## Responsive Model + +### Wide viewports + +- Keep the artifact central and persistent. +- Place sparse support anchors at the periphery or reveal them contextually. +- Route connectors without covering the inspected locus or essential artifact + content. +- Keep global chrome subordinate to artifact-local controls. + +### Narrow viewports + +- Preserve the artifact as the primary viewport content. +- Move supporting context into a temporary edge affordance, anchored popover, + or lightweight selection tray. +- Keep the selected locus, active relation, feedback, and recovery action + visible together. +- Do not require drag; support select-then-target and keyboard-equivalent flows. +- Do not cover the inspected locus with the tray that explains it. +- Preserve zoom, text reflow, and target sizing requirements. + +Responsive adaptation may change placement. It must not change the state +machine or require a different conceptual task. + +## State Coverage + +### Core states + +- artifact loading; +- artifact ready; +- artifact unavailable or failed; +- no active locus; +- locus focused or selected; +- context loading; +- context ready; +- relation or action preview; +- supported; +- incompatible; +- unavailable; +- stale; +- ambiguous; +- blocked; +- cancelled or Back; +- local error and retry. + +### Conditional consequential states + +Require this group when the activity commits a decision or external effect: + +- no option preselected; +- outcome pending; +- committing; +- commit succeeded; +- known failure; +- unknown result; +- receipt reconciliation; +- immutable receipt; +- concurrent result; +- superseding action when reversal is allowed. + +### Conditional reusable-guidance states + +Require this group when one decision can become future guidance: + +- inactive draft after the current result exists; +- correcting; +- explicit confirmation with no default affirmative; +- accepting; +- accepted version; +- declined or Back with no active guidance; +- acceptance failure; +- conflict; +- supersession; +- revocation. + +Current-item commitment and reusable-guidance acceptance are separate +consequential steps. + +### Conditional automation states + +Require this group when accepted guidance can resolve later matches: + +- match preflight; +- exact match; +- near or partial match; +- automatic resolution pending; +- automatic resolution complete; +- inline causal receipt; +- changed authority or evidence; +- failed or uncertain automation; +- exception returned to human review. + +## Mixed Visual Authority + +### Required contract + +`create_ui_implementation_contract` must support more than one declared visual +authority in a single rendered candidate. + +```json +{ + "design_system_scopes": [ + { + "scope_id": "inspector_chrome", + "root_selector": "[data-jk-scope='inspector-chrome']", + "authority": "judgmentkit_default", + "enforcement": "required", + "style_isolation": "required" + }, + { + "scope_id": "inspection_overlay", + "root_selector": "[data-jk-scope='inspection-overlay']", + "authority": "judgmentkit_default", + "enforcement": "required", + "style_isolation": "required" + }, + { + "scope_id": "primary_artifact", + "root_selector": "[data-artifact-root]", + "authority": "external_declared", + "enforcement": "external_not_reviewed", + "style_isolation": "required" + } + ], + "boundary_contracts": [ + { + "from_scope": "inspection_overlay", + "to_scope": "primary_artifact", + "allowed_roles": ["locus_target", "annotation_overlay", "connector_endpoint"], + "event_contract_required": true + } + ] +} +``` + +### Ownership + +| Concern | Owner | +| --- | --- | +| Chrome tokens, components, and interaction states | JudgmentKit | +| Artifact typography, components, color, elevation, and internal layout | Declared external authority | +| Observation markers, connectors, selection handles, and local overlay feedback | JudgmentKit | +| Artifact internal semantics, focus, contrast, and controls | Declared external authority | +| Cross-boundary focus order, overlays, obstruction, labels, and announcements | Host/Inspector contract | +| Artifact target geometry exposed to chrome | Boundary integration contract | + +### Boundary rules + +- Every visible region has one declared authority owner. +- Scope roots cannot overlap unless a named overlay exception declares which + scope owns the overlay. +- JudgmentKit selectors, resets, fonts, and tokens cannot leak into the + artifact scope. +- Artifact styles cannot leak into the chrome scope. +- Chrome annotations that appear over the artifact remain chrome-owned. They + should be rendered through a declared overlay or portal rather than silently + becoming artifact descendants. +- Cross-boundary events use declared semantic locus identifiers. Geometry alone + is insufficient authority. +- A live artifact keeps native gesture precedence until explicit inspection + mode is active. +- Entering and leaving inspection mode must be visible and announced. +- Artifact anchors remain stable for one artifact revision. Changed or missing + targets become stale or unavailable rather than being inferred. +- The artifact cannot be marked external merely to hide nonconforming chrome. +- An external artifact declaration does not count as JudgmentKit artifact + conformance. + +### Review result + +The implementation review should report scope results separately: + +```json +{ + "status": "passed_with_external_artifact_authority", + "design_system_review": { + "inspector_chrome": "passed", + "inspection_overlay": "passed", + "primary_artifact": "external_not_reviewed", + "boundary_contract": "passed" + } +} +``` + +The human-facing result must say exactly what passed. It must not summarize the +whole candidate as JudgmentKit-conformant. + +### Required boundary evidence + +- exact region and authority map; +- style-isolation proof in both directions; +- artifact render fingerprint before and after every chrome state transition; +- declared pointer, touch, keyboard, and assistive-technology crossings; +- focus-order and focus-return trace; +- overlay occlusion and target-drift checks; +- desktop and narrow views for rest, selection, preview, invalid, supported, + unavailable, and recovery states; +- separate results for chrome conformance, overlay conformance, artifact + preservation, and boundary behavior. + +## JudgmentKit Chrome Components + +The default JudgmentKit design system needs semantic coverage for artifact +inspection. Exact visual treatment remains design-system-owned. + +- `ArtifactViewport` +- `ArtifactBoundary` +- `ArtifactStatus` +- `ObservationMarker` +- `LocusSelection` +- `ContextAnchor` +- `AuthorityAnchor` +- `AnchorRail` +- `RelationPreview` +- `RelationConnector` +- `InlineReason` +- `ContextTray` +- `CommitBoundary` +- `ReceiptMarker` +- `BackAction` +- `ResetAction` +- `ZoomAndPanControls` when the artifact requires them + +The implementation contract may allow semantic HTML and SVG for these roles +until dedicated components exist, but the contract must name the allowed role, +state coverage, token boundary, and accessibility requirements. It must not +fall back to form-field primitives as the only approved component family. + +## Accessibility Contract + +- Every selectable locus has an accessible name, role, and selected state. +- Every connector has a nonvisual relation description. +- Drag has a select-then-target alternative. +- Hover content is also available through focus and activation. +- Focus order follows the participant's activity, not the DOM accident of an + overlay implementation. +- Focus remains stable when contextual chrome appears or disappears. +- Entering and leaving inspection mode is announced, and exiting returns focus + to the initiating target or the nearest valid fallback. +- Invalid, unavailable, stale, committing, complete, and failed states are + announced without stealing focus. +- Color is never the only indicator of relation or state. +- Zoom and reflow do not separate the active locus from its feedback. +- Touch targets and pointer cancellation meet the active accessibility policy. +- The host review verifies cross-boundary behavior. External artifact evidence + verifies the artifact's internal accessibility. +- An artifact-authorized semantic proxy must retain artifact-authority + provenance. JudgmentKit cannot create a semantic description from visual + inference and present it as artifact truth. + +## MCP Behavior Changes + +| Tool | Required change | +| --- | --- | +| `create_activity_model_review` | Ask whether a rendered artifact is primary, whether locus selection is required, what completion looks like, and who owns artifact styling. | +| `recommend_surface_types` | Add `artifact_inspector`, positive and exclusion evidence, and tie-break rules. Remove keyword-only conversation routing. | +| `recommend_ui_workflow_profiles` | Return `artifact-inspector-ui` when the mandatory activity evidence is present. | +| `review_ui_workflow_candidate` | Accept and validate `artifact_centered` topology, work units, transitions, recovery, state groups, and local completion. Return field-specific errors. | +| `create_ui_implementation_contract` | Support `design_system_scopes`, external artifact authority, boundary contracts, and artifact-inspector components. | +| `create_ui_generation_handoff` | Carry the artifact identity, locus model, topology, state groups, authority scopes, and responsive posture without flattening them into workbench defaults. | +| `create_frontend_generation_context` | Accept `judgmentkit.artifact-inspector.v1` and emit the corresponding components, token rules, boundary rules, and browser checks. | +| `review_cognitive_dimensions_candidate` | Check locus-to-action mapping, hidden dependencies, preview versus commitment, progressive evaluation, recovery cost, and disclosure. | +| `review_ui_implementation_candidate` | Enforce scoped design-system authority, artifact dominance, cross-boundary integrity, responsive behavior, accessibility, and conditional state coverage. | + +No new public MCP tool is required if these existing tools consume the versioned +registry definition. A separate registry resource is acceptable, but the same +definition must drive recommendation, handoff, generation, and review. + +## Review Diagnostics + +Diagnostics must be stable, specific, and repairable. + +| Code | Meaning | +| --- | --- | +| `JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING` | No bounded rendered artifact is declared. | +| `JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING` | The participant cannot select a semantic artifact locus. | +| `JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING` | No explicit structured artifact-centered topology is declared. | +| `JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID` | The topology kind is not `artifact_centered`. | +| `JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING` | A structured work unit has no stable identifier. | +| `JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID` | A required work unit is duplicated or does not preserve its canonical intent and system responsibility. | +| `JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID` | The topology entry does not reference a declared work unit. | +| `JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID` | Artifact-local completion references an undeclared work unit. | +| `JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID` | A topology transition references an undeclared work unit. | +| `JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING` | The topology does not explicitly enter and leave the `recover` work unit. | +| `JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID` | The workflow does not declare the canonical active state groups. | +| `JK_ARTIFACT_INSPECTOR_ARTIFACT_NOT_DOMINANT` | Persistent chrome or supporting context visually displaces the artifact. | +| `JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED` | Evidence, authority, action, or feedback has no visible or semantic relation to the active locus. | +| `JK_ARTIFACT_INSPECTOR_DRAG_ONLY` | A required interaction has no tap/select and keyboard alternative. | +| `JK_ARTIFACT_INSPECTOR_LOCAL_FEEDBACK_MISSING` | Validation or status appears away from the action that caused it. | +| `JK_ARTIFACT_INSPECTOR_UNCOMMITTED_STATE_AMBIGUOUS` | Preview and committed state are not distinguishable. | +| `JK_ARTIFACT_INSPECTOR_DEFAULT_COMMIT` | A consequential affirmative or outcome is preselected. | +| `JK_ARTIFACT_INSPECTOR_RECEIPT_MISSING` | A consequential result has no inspectable local receipt. | +| `JK_ARTIFACT_INSPECTOR_GUIDANCE_COUPLED` | Current-item commitment and reusable-guidance acceptance occur in one action. | +| `JK_ARTIFACT_INSPECTOR_MOBILE_ARTIFACT_OBSCURED` | Narrow-layout chrome covers the active locus or essential artifact content. | +| `JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING` | JudgmentKit-owned chrome has no enforceable design-system scope. | +| `JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING` | The artifact's visual authority is undeclared. | +| `JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP` | Two authority scopes claim the same visible node without a valid overlay exception. | +| `JK_ARTIFACT_INSPECTOR_STYLE_LEAK` | Styles or tokens cross an authority boundary. | +| `JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED` | Chrome interacts with an artifact locus without a semantic boundary contract. | +| `JK_ARTIFACT_INSPECTOR_NATIVE_ACTION_INTERCEPTED` | Inspector behavior captures a live artifact action without explicit inspection mode. | +| `JK_ARTIFACT_INSPECTOR_TARGET_DRIFT` | An overlay silently moved to a different artifact target. | +| `JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED` | A chrome transition changed artifact content or presentation. | +| `JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM` | The review describes the external artifact as JudgmentKit-conformant. | +| `JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT` | Artifact Inspector routing conflicts with mandatory activity evidence, exclusions, the selected surface, or the workflow profile. | + +Every diagnostic should include the failing field or selector, the expected +contract, the observed value, and one bounded repair instruction. + +## Deterministic Fixture Matrix + +### Positive routing fixtures + +1. A rendered interface where the participant selects one visible control and + relates an observation to accepted authority. +2. A document where the participant selects a passage and attaches a + source-backed correction. +3. A diagram where the participant selects one node and inspects the rule and + evidence that produced it. +4. A generated composition where the participant marks one region, previews a + bounded change, and verifies the local result. + +Expected: `artifact_inspector` is primary. The artifact remains the primary +object in every generated workflow. + +### Negative routing fixtures + +1. A queue of generated cases with evidence and approve/reject controls. + Expected: `operator_review`. +2. A multi-object creation canvas with persistent tools. Expected: `workbench`. +3. A linear evidence memo. Expected: `content_report`. +4. A live support exchange with attachments. Expected: `conversation`. +5. A status overview across many items. Expected: `dashboard_monitor`. +6. A structured submission sequence. Expected: `form_flow`. +7. A structured record-entry task with an incidental artifact preview. + Expected: `form_flow`; the preview alone does not trigger + `artifact_inspector`. + +### Ambiguity fixtures + +1. Queue plus artifact detail: choose `operator_review` when the queue and case + decision are primary; choose `artifact_inspector` only when locus-specific + artifact action is required for completion. +2. Whole-artifact approval without locus selection: choose `operator_review`. +3. Read-only artifact with citations but no local action: choose + `content_report`. +4. A visual object called an `authority thread`: do not choose `conversation` + unless message exchange is the activity. +5. Several artifacts compared side by side: choose `workbench` unless one + artifact remains primary and every comparison is subordinate to it. + +### Authority-boundary fixtures + +1. JudgmentKit chrome plus externally styled artifact with isolated scopes. + Expected: chrome pass, artifact external, boundary pass. +2. Global JudgmentKit reset changes artifact typography. Expected: + `JK_ARTIFACT_INSPECTOR_STYLE_LEAK`. +3. Artifact CSS changes chrome buttons. Expected: + `JK_ARTIFACT_INSPECTOR_STYLE_LEAK`. +4. Annotation overlay is visually over the artifact but has no declared owner. + Expected: `JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP` or missing scope. +5. Chrome is mislabeled as an external artifact to avoid token checks. + Expected: external-authority overclaim or chrome-scope failure. +6. A chrome state transition changes artifact bytes or computed presentation. + Expected: `JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED`. +7. A live artifact's native action is intercepted without inspection mode. + Expected: `JK_ARTIFACT_INSPECTOR_NATIVE_ACTION_INTERCEPTED`. +8. An artifact revision changes target geometry and the overlay attaches to a + different target. Expected: `JK_ARTIFACT_INSPECTOR_TARGET_DRIFT`. + +### Interaction fixtures + +- pointer drag succeeds; +- select-then-target succeeds; +- keyboard-only succeeds; +- touch path succeeds without hover; +- invalid target explains itself locally; +- unavailable context cannot become accepted support; +- Back removes only the current preview; +- known failure preserves an uncommitted choice; +- unknown result reconciles before retry; +- narrow layout preserves the artifact, active locus, feedback, and recovery. + +### Compatibility and determinism fixtures + +- Existing surface inputs produce their prior canonical outputs. +- An older schema receiving `artifact_inspector` returns an explicit + unsupported-profile diagnostic with no silent fallback. +- Repeated generation from the same fixture produces byte-identical canonical + contracts, diagnostics, reports, and hashes. + +## Acceptance Criteria + +The model is ready only when all criteria pass: + +1. Positive, negative, and ambiguity routing fixtures produce the expected + surface types without keyword-only overrides. +2. The workflow review accepts the normative `artifact_centered` topology and + returns field-specific diagnostics for malformed candidates. +3. Generated concepts keep the artifact as the primary object and do not + default to master-detail, queue-detail, dashboards, forms, dossiers, or + conversations. +4. Supporting context remains visibly or semantically attached to the active + locus. +5. Pointer, touch, and keyboard paths express the same state machine; drag and + hover are optional accelerators. +6. Wide and narrow compositions preserve artifact dominance and do not obscure + the active locus. +7. JudgmentKit chrome passes the JudgmentKit design-system gate inside its + declared scope. +8. The artifact retains its declared external authority without being restyled + or falsely described as JudgmentKit-conformant. +9. Scope overlap, style leakage, and undeclared boundary events fail with + stable diagnostics. +10. Core state coverage passes. Conditional consequential, guidance, and + automation groups are required only when the activity contract activates + them. +11. A non-consequential inspection does not invent confirmation, commitment, + guidance, receipts, or automation. +12. Consequential actions have no default affirmative, expose commitment, and + leave a receipt. +13. Back and cancellation reverse only uncommitted state. +14. Accessibility evidence covers both each authority scope and the + cross-boundary interaction. +15. Existing routing fixtures for every current surface type remain unchanged + unless they meet all mandatory artifact-inspector conditions. +16. The same versioned registry definition drives recommendation, workflow + review, implementation handoff, frontend context, and implementation + review. + +## Proof Shape + +The implementation should add: + +- a versioned interaction-model registry entry; +- a schema for `artifact_centered` topology; +- a schema for mixed design-system scopes and boundary contracts; +- positive, negative, ambiguity, responsive, accessibility, and authority- + boundary fixtures; +- deterministic recommendation and workflow-review tests; +- implementation-contract and implementation-review tests; +- stable diagnostics and canonical messages; +- desktop and narrow rendered evidence for representative states; +- accessibility evidence for keyboard, focus, name/role/value, status + announcements, noncolor cues, target size, zoom, and reflow; +- a machine-readable review receipt that separates JudgmentKit chrome status, + external artifact status, and boundary status. + +Do not describe the model as implemented until the schema, fixtures, commands, +diagnostics, reports, rendered evidence, and regression suite all pass. + +## Compatibility And Rollout + +- Add the model as a versioned, additive registry entry. +- Preserve existing surface-type behavior unless all mandatory + artifact-inspector evidence is present. +- Return a structured `artifact_inspector_profile_unsupported` diagnostic when + a caller cannot consume the profile. Do not silently downgrade the model to + `workbench` or `operator_review` and claim equivalent guidance. +- Keep `judgmentkit.artifact-inspector.v1` immutable after release. Add a new + version for contract changes. +- Keep mixed-authority review results explicit during rollout. A chrome pass is + not an artifact pass. + +## Definition Of Done + +JudgmentKit can identify an artifact-centered activity, choose +`artifact_inspector`, generate and review an artifact-dominant workflow, apply +its design system only to the surrounding chrome, preserve an independent +artifact authority, and return precise proof of what passed. + +The participant should experience one coherent product surface. The review +system should retain the authority boundary that makes that coherence honest. diff --git a/src/artifact-inspector-authority.mjs b/src/artifact-inspector-authority.mjs new file mode 100644 index 0000000..cd705f4 --- /dev/null +++ b/src/artifact-inspector-authority.mjs @@ -0,0 +1,1180 @@ +const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key); + +function isPlainObject(value) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +} + +function frozenRecord(value) { + return Object.freeze( + Object.fromEntries( + Object.entries(value).map(([key, entry]) => [ + key, + Array.isArray(entry) ? Object.freeze([...entry]) : entry, + ]), + ), + ); +} + +export const ARTIFACT_INSPECTOR_SCOPE_IDS = Object.freeze([ + "inspector_chrome", + "inspection_overlay", + "primary_artifact", +]); + +export const ARTIFACT_INSPECTOR_STATE_GROUP_IDS = Object.freeze([ + "core", + "consequential", + "reusable_guidance", + "automation", +]); + +export const ARTIFACT_INSPECTOR_CANONICAL_SCOPES = Object.freeze([ + Object.freeze({ + scope_id: "inspector_chrome", + root_selector: "[data-jk-scope='inspector-chrome']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", + }), + Object.freeze({ + scope_id: "inspection_overlay", + root_selector: "[data-jk-scope='inspection-overlay']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", + }), + Object.freeze({ + scope_id: "primary_artifact", + root_selector: "[data-artifact-root]", + authority: "external_declared", + enforcement: "external_not_reviewed", + style_isolation: "required", + }), +]); + +export const ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS = Object.freeze([ + Object.freeze({ + from_scope: "inspection_overlay", + to_scope: "primary_artifact", + allowed_roles: Object.freeze([ + "locus_target", + "annotation_overlay", + "connector_endpoint", + ]), + event_contract_required: true, + }), +]); + +export const ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS = frozenRecord({ + core: [ + "artifact loading", + "artifact ready", + "artifact unavailable or failed", + "no active locus", + "locus focused or selected", + "context loading", + "context ready", + "relation or action preview", + "supported", + "incompatible", + "unavailable", + "stale", + "ambiguous", + "blocked", + "cancelled or Back", + "local error and retry", + ], + consequential: [ + "no option preselected", + "outcome pending", + "committing", + "commit succeeded", + "known failure", + "unknown result", + "receipt reconciliation", + "immutable receipt", + "concurrent result", + "superseding action when reversal is allowed", + ], + reusable_guidance: [ + "inactive draft after the current result exists", + "correcting", + "explicit confirmation with no default affirmative", + "accepting", + "accepted version", + "declined or Back with no active guidance", + "acceptance failure", + "conflict", + "supersession", + "revocation", + ], + automation: [ + "match preflight", + "exact match", + "near or partial match", + "automatic resolution pending", + "automatic resolution complete", + "inline causal receipt", + "changed authority or evidence", + "failed or uncertain automation", + "exception returned to human review", + ], +}); + +export const ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES = Object.freeze([ + "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + "JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING", + "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + "JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID", + "JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID", + "JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID", + "JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING", + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + "JK_ARTIFACT_INSPECTOR_ARTIFACT_NOT_DOMINANT", + "JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED", + "JK_ARTIFACT_INSPECTOR_DRAG_ONLY", + "JK_ARTIFACT_INSPECTOR_LOCAL_FEEDBACK_MISSING", + "JK_ARTIFACT_INSPECTOR_UNCOMMITTED_STATE_AMBIGUOUS", + "JK_ARTIFACT_INSPECTOR_DEFAULT_COMMIT", + "JK_ARTIFACT_INSPECTOR_RECEIPT_MISSING", + "JK_ARTIFACT_INSPECTOR_GUIDANCE_COUPLED", + "JK_ARTIFACT_INSPECTOR_MOBILE_ARTIFACT_OBSCURED", + "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + "JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING", + "JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP", + "JK_ARTIFACT_INSPECTOR_STYLE_LEAK", + "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + "JK_ARTIFACT_INSPECTOR_NATIVE_ACTION_INTERCEPTED", + "JK_ARTIFACT_INSPECTOR_TARGET_DRIFT", + "JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED", + "JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM", + "JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT", + "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", +]); + +const DIAGNOSTIC_DEFAULTS = Object.freeze({ + JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING: Object.freeze({ + message: "No bounded rendered artifact is declared.", + repair_instruction: + "Declare one primary artifact scope with external visual authority.", + }), + JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING: Object.freeze({ + message: "The participant cannot select a semantic artifact locus.", + repair_instruction: + "Expose stable semantic locus identifiers and equivalent pointer, touch, and keyboard paths.", + }), + JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING: Object.freeze({ + message: + "The artifact-centered workflow has no explicit structured topology contract.", + repair_instruction: + "Provide workflow.topology as the explicit artifact_centered topology object instead of a compatibility string.", + }), + JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID: Object.freeze({ + message: "The artifact-centered workflow declares an invalid topology kind.", + repair_instruction: + "Set workflow.topology.kind to artifact_centered and preserve the canonical topology fields.", + }), + JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING: Object.freeze({ + message: "A structured artifact work unit has no stable identifier.", + repair_instruction: + "Give every Artifact Inspector work unit its canonical stable id.", + }), + JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID: Object.freeze({ + message: + "A required artifact work unit is duplicated or does not preserve its canonical participant intent or system responsibility.", + repair_instruction: + "Keep exactly one work unit per canonical id and restore it from the versioned Artifact Inspector registry definition.", + }), + JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID: Object.freeze({ + message: "The topology entry does not reference a declared work unit.", + repair_instruction: + "Point entry_work_unit_id at the declared orient work unit.", + }), + JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID: Object.freeze({ + message: "Artifact-local completion does not reference declared work units.", + repair_instruction: + "Use only declared work-unit ids in completion_work_unit_ids and retain artifact-local completion.", + }), + JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID: Object.freeze({ + message: "A topology transition does not connect declared work units.", + repair_instruction: + "Make every transition from and to reference declared Artifact Inspector work-unit ids.", + }), + JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING: Object.freeze({ + message: "The artifact workflow has no explicit connected recovery work unit.", + repair_instruction: + "Declare recover and connect both an inbound recovery transition and an outbound return to a safe work unit.", + }), + JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID: Object.freeze({ + message: + "The artifact workflow does not declare the canonical active state groups.", + repair_instruction: + "Declare core and only the conditional groups activated by the activity, using the exact registry state sets.", + }), + JK_ARTIFACT_INSPECTOR_ARTIFACT_NOT_DOMINANT: Object.freeze({ + message: "Persistent chrome or supporting context visually displaces the artifact.", + repair_instruction: + "Keep the artifact as the largest persistent work region at wide and narrow viewports.", + }), + JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED: Object.freeze({ + message: "Supporting context has no visible or semantic relation to the active locus.", + repair_instruction: + "Attach evidence, authority, actions, and feedback to the active semantic locus.", + }), + JK_ARTIFACT_INSPECTOR_DRAG_ONLY: Object.freeze({ + message: "A required interaction has no tap, select, and keyboard alternative.", + repair_instruction: + "Provide equivalent pointer, touch, and keyboard interaction paths.", + }), + JK_ARTIFACT_INSPECTOR_LOCAL_FEEDBACK_MISSING: Object.freeze({ + message: "Validation or status appears away from the action that caused it.", + repair_instruction: + "Render feedback on or immediately beside the active artifact locus.", + }), + JK_ARTIFACT_INSPECTOR_UNCOMMITTED_STATE_AMBIGUOUS: Object.freeze({ + message: "Preview and committed state are not distinguishable.", + repair_instruction: + "Declare and render distinct preview, validation, commitment, result, and recovery states.", + }), + JK_ARTIFACT_INSPECTOR_DEFAULT_COMMIT: Object.freeze({ + message: "A consequential affirmative or outcome is preselected.", + repair_instruction: + "Remove the default affirmative and require an explicit commitment action.", + }), + JK_ARTIFACT_INSPECTOR_RECEIPT_MISSING: Object.freeze({ + message: "A consequential result has no inspectable local receipt.", + repair_instruction: + "Attach a durable receipt to the artifact locus after commitment.", + }), + JK_ARTIFACT_INSPECTOR_GUIDANCE_COUPLED: Object.freeze({ + message: "Current-item commitment and reusable-guidance acceptance occur in one action.", + repair_instruction: + "Separate the current result from reusable-guidance acceptance and its lifecycle.", + }), + JK_ARTIFACT_INSPECTOR_MOBILE_ARTIFACT_OBSCURED: Object.freeze({ + message: "Narrow-layout chrome covers the active locus or essential artifact content.", + repair_instruction: + "Move contextual chrome without obscuring the active locus at narrow widths.", + }), + JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING: Object.freeze({ + message: "JudgmentKit-owned chrome has no enforceable design-system scope.", + repair_instruction: + "Declare canonical inspector_chrome and inspection_overlay scopes with required isolation.", + }), + JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING: Object.freeze({ + message: "The artifact's visual authority is undeclared.", + repair_instruction: + "Declare the canonical primary_artifact external scope and keep it external_not_reviewed.", + }), + JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP: Object.freeze({ + message: "Authority scopes overlap or duplicate an ownership claim.", + repair_instruction: + "Give every visible region one authority owner and use the declared overlay boundary only.", + }), + JK_ARTIFACT_INSPECTOR_STYLE_LEAK: Object.freeze({ + message: "Styles or tokens cross an authority boundary.", + repair_instruction: + "Isolate JudgmentKit chrome and overlay styles from external artifact styles in both directions.", + }), + JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED: Object.freeze({ + message: "Chrome interacts with an artifact locus without a semantic boundary contract.", + repair_instruction: + "Declare the overlay-to-artifact event contract and use semantic locus identifiers.", + }), + JK_ARTIFACT_INSPECTOR_NATIVE_ACTION_INTERCEPTED: Object.freeze({ + message: "Inspector behavior captures a live artifact action without explicit inspection mode.", + repair_instruction: + "Restore native interaction precedence until inspection mode is explicitly active.", + }), + JK_ARTIFACT_INSPECTOR_TARGET_DRIFT: Object.freeze({ + message: "An overlay silently moved to a different artifact target.", + repair_instruction: + "Re-anchor to the same semantic target or mark the target stale or unavailable.", + }), + JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED: Object.freeze({ + message: "A chrome transition changed artifact content or presentation.", + repair_instruction: + "Preserve the artifact render fingerprint across every chrome-only transition.", + }), + JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM: Object.freeze({ + message: "The review describes the external artifact as JudgmentKit-conformant.", + repair_instruction: + "Report the external artifact as external_not_reviewed and state only which owned scopes passed.", + }), + JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT: Object.freeze({ + message: + "Artifact Inspector routing conflicts with mandatory activity evidence, exclusions, the selected surface, or the workflow profile.", + repair_instruction: + "Resolve routing from mandatory activity evidence, exclusions, and canonical surface and profile selections; do not let metadata override grounded evidence.", + }), + JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING: Object.freeze({ + message: "Trusted scope, preservation, and boundary runtime evidence is absent or incomplete.", + repair_instruction: + "Run the trusted browser review and supply separate scope, preservation, and boundary results.", + }), +}); + +const DIAGNOSTIC_CODE_SET = new Set(ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES); +const DIAGNOSTIC_ORDER = new Map( + ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES.map((code, index) => [code, index]), +); +const SCOPE_BY_ID = new Map( + ARTIFACT_INSPECTOR_CANONICAL_SCOPES.map((scope) => [scope.scope_id, scope]), +); +const CANONICAL_BOUNDARY = ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS[0]; +const CANONICAL_BOUNDARY_ROLES = new Set(CANONICAL_BOUNDARY.allowed_roles); + +function authorityError(message, details = {}) { + const error = new TypeError(message); + error.name = "ArtifactInspectorAuthorityError"; + error.code = "invalid_artifact_inspector_authority_contract"; + error.details = details; + return error; +} + +function requirePlainObject(value, field) { + if (!isPlainObject(value)) { + throw authorityError(`${field} must be an object.`, { + field, + observed: value, + }); + } +} + +function assertOnlyKeys(value, allowedKeys, field) { + const unexpected = Object.keys(value).filter((key) => !allowedKeys.includes(key)); + if (unexpected.length > 0) { + throw authorityError(`${field} contains unsupported fields: ${unexpected.join(", ")}.`, { + field, + unexpected_fields: unexpected.sort(), + }); + } +} + +function requiredString(value, field) { + if (typeof value !== "string" || value.trim().length === 0) { + throw authorityError(`${field} must be a non-empty string.`, { + field, + observed: value, + }); + } + return value.trim(); +} + +function normalizeStringSet(value, field) { + if (!Array.isArray(value) || value.length === 0) { + throw authorityError(`${field} must be a non-empty array.`, { + field, + observed: value, + }); + } + + const normalized = value.map((entry, index) => + requiredString(entry, `${field}[${index}]`), + ); + if (new Set(normalized).size !== normalized.length) { + throw authorityError(`${field} must not contain duplicates.`, { + field, + observed: normalized, + }); + } + return normalized; +} + +function assertExactSet(actual, expected, field, diagnosticCode) { + const actualSet = new Set(actual); + const missing = expected.filter((entry) => !actualSet.has(entry)); + const unexpected = actual.filter((entry) => !expected.includes(entry)); + if (missing.length > 0 || unexpected.length > 0 || actual.length !== expected.length) { + throw authorityError(`${field} does not match the canonical Artifact Inspector contract.`, { + field, + missing, + unexpected, + diagnostic_code: diagnosticCode, + }); + } +} + +function cloneStateGroups() { + return Object.fromEntries( + ARTIFACT_INSPECTOR_STATE_GROUP_IDS.map((groupId) => [ + groupId, + [...ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS[groupId]], + ]), + ); +} + +export function normalizeArtifactInspectorScopes(value, { required = false } = {}) { + if (value === undefined || value === null) { + if (required) { + throw authorityError("design_system_scopes is required for Artifact Inspector.", { + field: "design_system_scopes", + diagnostic_code: "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + }); + } + return []; + } + + if (!Array.isArray(value)) { + throw authorityError("design_system_scopes must be an array.", { + field: "design_system_scopes", + observed: value, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + }); + } + + const byId = new Map(); + for (const [index, entry] of value.entries()) { + const field = `design_system_scopes[${index}]`; + requirePlainObject(entry, field); + assertOnlyKeys( + entry, + ["scope_id", "root_selector", "authority", "enforcement", "style_isolation"], + field, + ); + + const scopeId = requiredString(entry.scope_id, `${field}.scope_id`); + const canonical = SCOPE_BY_ID.get(scopeId); + if (!canonical) { + throw authorityError(`${field}.scope_id is not a canonical Artifact Inspector scope.`, { + field: `${field}.scope_id`, + observed: scopeId, + expected: ARTIFACT_INSPECTOR_SCOPE_IDS, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP", + }); + } + if (byId.has(scopeId)) { + throw authorityError(`design_system_scopes declares ${scopeId} more than once.`, { + field: "design_system_scopes", + scope_id: scopeId, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP", + }); + } + + const normalized = { + scope_id: scopeId, + root_selector: requiredString(entry.root_selector, `${field}.root_selector`), + authority: requiredString(entry.authority, `${field}.authority`), + enforcement: requiredString(entry.enforcement, `${field}.enforcement`), + style_isolation: requiredString(entry.style_isolation, `${field}.style_isolation`), + }; + + for (const key of ["root_selector", "authority", "enforcement", "style_isolation"]) { + if (normalized[key] !== canonical[key]) { + throw authorityError(`${field}.${key} conflicts with the canonical authority boundary.`, { + field: `${field}.${key}`, + scope_id: scopeId, + expected: canonical[key], + observed: normalized[key], + diagnostic_code: + scopeId === "primary_artifact" + ? "JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING" + : "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + }); + } + } + byId.set(scopeId, normalized); + } + + if (required) { + const missing = ARTIFACT_INSPECTOR_SCOPE_IDS.filter((scopeId) => !byId.has(scopeId)); + if (missing.length > 0) { + throw authorityError("Artifact Inspector requires all canonical authority scopes.", { + field: "design_system_scopes", + missing, + diagnostic_code: missing.includes("primary_artifact") + ? "JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING" + : "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING", + }); + } + } + + return ARTIFACT_INSPECTOR_SCOPE_IDS.filter((scopeId) => byId.has(scopeId)).map( + (scopeId) => byId.get(scopeId), + ); +} + +export function normalizeArtifactInspectorBoundaryContracts( + value, + { scopes = [], required = false } = {}, +) { + if (value === undefined || value === null) { + if (required) { + throw authorityError("boundary_contracts is required for Artifact Inspector.", { + field: "boundary_contracts", + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + return []; + } + + if (!Array.isArray(value)) { + throw authorityError("boundary_contracts must be an array.", { + field: "boundary_contracts", + observed: value, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + if (required && value.length !== 1) { + throw authorityError("Artifact Inspector requires exactly one canonical boundary contract.", { + field: "boundary_contracts", + observed_count: value.length, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + + const knownScopes = new Set(scopes.map((scope) => scope.scope_id)); + const normalized = value.map((entry, index) => { + const field = `boundary_contracts[${index}]`; + requirePlainObject(entry, field); + assertOnlyKeys( + entry, + ["from_scope", "to_scope", "allowed_roles", "event_contract_required"], + field, + ); + + const fromScope = requiredString(entry.from_scope, `${field}.from_scope`); + const toScope = requiredString(entry.to_scope, `${field}.to_scope`); + if ( + fromScope !== CANONICAL_BOUNDARY.from_scope || + toScope !== CANONICAL_BOUNDARY.to_scope + ) { + throw authorityError(`${field} is not the canonical overlay-to-artifact boundary.`, { + field, + expected: { + from_scope: CANONICAL_BOUNDARY.from_scope, + to_scope: CANONICAL_BOUNDARY.to_scope, + }, + observed: { from_scope: fromScope, to_scope: toScope }, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + if ( + knownScopes.size > 0 && + (!knownScopes.has(fromScope) || !knownScopes.has(toScope)) + ) { + throw authorityError(`${field} references an undeclared authority scope.`, { + field, + observed: { from_scope: fromScope, to_scope: toScope }, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + + const roles = normalizeStringSet(entry.allowed_roles, `${field}.allowed_roles`); + const unsupportedRoles = roles.filter((role) => !CANONICAL_BOUNDARY_ROLES.has(role)); + if (unsupportedRoles.length > 0) { + throw authorityError(`${field}.allowed_roles contains unsupported boundary roles.`, { + field: `${field}.allowed_roles`, + unexpected: unsupportedRoles, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + if (entry.event_contract_required !== true) { + throw authorityError(`${field}.event_contract_required must be true.`, { + field: `${field}.event_contract_required`, + observed: entry.event_contract_required, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + }); + } + + return { + from_scope: fromScope, + to_scope: toScope, + allowed_roles: CANONICAL_BOUNDARY.allowed_roles.filter((role) => roles.includes(role)), + event_contract_required: true, + }; + }); + + const identities = normalized.map( + (entry) => `${entry.from_scope}->${entry.to_scope}`, + ); + if (new Set(identities).size !== identities.length) { + throw authorityError("boundary_contracts contains a duplicate authority boundary.", { + field: "boundary_contracts", + diagnostic_code: "JK_ARTIFACT_INSPECTOR_SCOPE_OVERLAP", + }); + } + + return normalized; +} + +function normalizeArtifactInspectorStateConfigUnchecked( + value, + { required = false } = {}, +) { + if (value === undefined || value === null) { + if (required) { + throw authorityError("Artifact Inspector state configuration is required.", { + field: "artifact_inspector.state_groups", + diagnostic_code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }); + } + return null; + } + + requirePlainObject(value, "artifact_inspector.state_config"); + assertOnlyKeys( + value, + ["active_state_groups", "state_groups"], + "artifact_inspector.state_config", + ); + + const activeStateGroups = normalizeStringSet( + value.active_state_groups, + "artifact_inspector.active_state_groups", + ); + const unexpectedActiveGroups = activeStateGroups.filter( + (groupId) => !ARTIFACT_INSPECTOR_STATE_GROUP_IDS.includes(groupId), + ); + if (unexpectedActiveGroups.length > 0 || !activeStateGroups.includes("core")) { + throw authorityError( + "active_state_groups must contain core and only canonical conditional groups.", + { + field: "artifact_inspector.active_state_groups", + unexpected: unexpectedActiveGroups, + missing: activeStateGroups.includes("core") ? [] : ["core"], + diagnostic_code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }, + ); + } + + requirePlainObject(value.state_groups, "artifact_inspector.state_groups"); + assertOnlyKeys( + value.state_groups, + ARTIFACT_INSPECTOR_STATE_GROUP_IDS, + "artifact_inspector.state_groups", + ); + for (const groupId of ARTIFACT_INSPECTOR_STATE_GROUP_IDS) { + const states = normalizeStringSet( + value.state_groups[groupId], + `artifact_inspector.state_groups.${groupId}`, + ); + assertExactSet( + states, + ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS[groupId], + `artifact_inspector.state_groups.${groupId}`, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + } + + return { + active_state_groups: ARTIFACT_INSPECTOR_STATE_GROUP_IDS.filter((groupId) => + activeStateGroups.includes(groupId), + ), + state_groups: cloneStateGroups(), + }; +} + +export function normalizeArtifactInspectorStateConfig(value, options = {}) { + try { + return normalizeArtifactInspectorStateConfigUnchecked(value, options); + } catch (error) { + if (error?.code === "invalid_artifact_inspector_authority_contract") { + error.details = { + ...(isPlainObject(error.details) ? error.details : {}), + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }; + } + throw error; + } +} + +function normalizeArtifactInspectorContract(value) { + requirePlainObject(value, "artifact_inspector"); + assertOnlyKeys( + value, + [ + "registry_id", + "registry_version", + "surface_type", + "workflow_profile", + "frontend_surface_profile", + "topology_kind", + "active_state_groups", + "state_groups", + "trusted_runtime_evidence_required", + "external_artifact_review_status", + ], + "artifact_inspector", + ); + + const expected = { + registry_id: "artifact_inspector", + registry_version: "1.0.0", + surface_type: "artifact_inspector", + workflow_profile: "artifact-inspector-ui", + frontend_surface_profile: "judgmentkit.artifact-inspector.v1", + topology_kind: "artifact_centered", + }; + for (const [field, expectedValue] of Object.entries(expected)) { + const observed = requiredString(value[field], `artifact_inspector.${field}`); + if (observed !== expectedValue) { + throw authorityError(`artifact_inspector.${field} is not canonical.`, { + field: `artifact_inspector.${field}`, + expected: expectedValue, + observed, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + }); + } + } + if (value.trusted_runtime_evidence_required !== true) { + throw authorityError( + "artifact_inspector.trusted_runtime_evidence_required must be true.", + { + field: "artifact_inspector.trusted_runtime_evidence_required", + observed: value.trusted_runtime_evidence_required, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", + }, + ); + } + if (value.external_artifact_review_status !== "external_not_reviewed") { + throw authorityError( + "artifact_inspector.external_artifact_review_status must remain external_not_reviewed.", + { + field: "artifact_inspector.external_artifact_review_status", + expected: "external_not_reviewed", + observed: value.external_artifact_review_status, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM", + }, + ); + } + + const stateConfig = normalizeArtifactInspectorStateConfig( + { + active_state_groups: value.active_state_groups, + state_groups: value.state_groups, + }, + { required: true }, + ); + + return { + ...expected, + ...stateConfig, + trusted_runtime_evidence_required: true, + external_artifact_review_status: "external_not_reviewed", + }; +} + +export function validateArtifactInspectorAuthorityContract( + implementationContract = {}, +) { + requirePlainObject(implementationContract, "implementation_contract"); + const hasScopes = hasOwn(implementationContract, "design_system_scopes"); + const hasBoundaries = hasOwn(implementationContract, "boundary_contracts"); + const hasArtifactInspector = hasOwn(implementationContract, "artifact_inspector"); + + if (!hasScopes && !hasBoundaries && !hasArtifactInspector) { + return { + active: false, + design_system_scopes: [], + boundary_contracts: [], + artifact_inspector: null, + }; + } + + if (!hasScopes || !hasBoundaries || !hasArtifactInspector) { + throw authorityError( + "Artifact Inspector authority is partial; scopes, boundaries, and artifact_inspector are all required when any one is declared.", + { + field: "implementation_contract", + missing: [ + ...(hasScopes ? [] : ["design_system_scopes"]), + ...(hasBoundaries ? [] : ["boundary_contracts"]), + ...(hasArtifactInspector ? [] : ["artifact_inspector"]), + ], + diagnostic_code: !hasScopes + ? "JK_ARTIFACT_INSPECTOR_CHROME_SCOPE_MISSING" + : !hasBoundaries + ? "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED" + : "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + }, + ); + } + + const designSystemScopes = normalizeArtifactInspectorScopes( + implementationContract.design_system_scopes, + { required: true }, + ); + const boundaryContracts = normalizeArtifactInspectorBoundaryContracts( + implementationContract.boundary_contracts, + { scopes: designSystemScopes, required: true }, + ); + const artifactInspector = normalizeArtifactInspectorContract( + implementationContract.artifact_inspector, + ); + + return { + active: true, + design_system_scopes: designSystemScopes, + boundary_contracts: boundaryContracts, + artifact_inspector: artifactInspector, + }; +} + +export function createArtifactInspectorFinding({ + code, + severity = "fail", + check = "artifact_inspector_authority", + message, + evidence = {}, + repair_instruction, +}) { + if (!DIAGNOSTIC_CODE_SET.has(code)) { + throw authorityError(`Unknown Artifact Inspector diagnostic code: ${code}.`, { + field: "finding.code", + observed: code, + }); + } + if (!["fail", "warn"].includes(severity)) { + throw authorityError("Artifact Inspector finding severity must be fail or warn.", { + field: "finding.severity", + observed: severity, + }); + } + if (!isPlainObject(evidence)) { + throw authorityError("Artifact Inspector finding evidence must be an object.", { + field: "finding.evidence", + observed: evidence, + }); + } + + const defaults = DIAGNOSTIC_DEFAULTS[code]; + return { + severity, + check: requiredString(check, "finding.check"), + code, + message: message ? requiredString(message, "finding.message") : defaults.message, + evidence: { ...evidence }, + repair_instruction: repair_instruction + ? requiredString(repair_instruction, "finding.repair_instruction") + : defaults.repair_instruction, + }; +} + +function stableFindings(findings) { + return [...findings].sort((left, right) => { + const codeOrder = + (DIAGNOSTIC_ORDER.get(left.code) ?? Number.MAX_SAFE_INTEGER) - + (DIAGNOSTIC_ORDER.get(right.code) ?? Number.MAX_SAFE_INTEGER); + if (codeOrder !== 0) return codeOrder; + const checkOrder = left.check.localeCompare(right.check); + if (checkOrder !== 0) return checkOrder; + return left.message.localeCompare(right.message); + }); +} + +function runtimeStatus(value) { + const status = typeof value === "string" ? value : value?.status; + if (status === "pass" || status === "passed") return "pass"; + if (status === "fail" || status === "failed") return "fail"; + if (status === "review_required") return "review_required"; + if (status === "external_not_reviewed") return "external_not_reviewed"; + return null; +} + +function boundaryEvidence(receipt) { + if (!Array.isArray(receipt?.boundary_results)) return null; + return ( + receipt.boundary_results.find( + (entry) => + isPlainObject(entry) && + entry.from_scope === CANONICAL_BOUNDARY.from_scope && + entry.to_scope === CANONICAL_BOUNDARY.to_scope, + ) ?? null + ); +} + +function missingRuntimeFinding(evidence = {}) { + return createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", + evidence, + }); +} + +function baseDesignSystemReview() { + return { + inspector_chrome: "review_required", + inspection_overlay: "review_required", + primary_artifact: "external_not_reviewed", + boundary_contract: "review_required", + }; +} + +// Internal evaluator: callers cannot obtain the private, candidate-bound runtime +// receipt used by the public implementation-review entrypoints. Do not re-export +// this function from the package root. +export function reviewArtifactInspectorAuthorityEvidence( + implementationContract = {}, + { trustedRuntimeEvidence = null } = {}, +) { + let authorityContract; + try { + authorityContract = validateArtifactInspectorAuthorityContract( + implementationContract, + ); + } catch (error) { + const code = DIAGNOSTIC_CODE_SET.has(error?.details?.diagnostic_code) + ? error.details.diagnostic_code + : "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING"; + return { + status: "fail", + applicable: true, + design_system_review: baseDesignSystemReview(), + trusted_runtime_evidence: { + status: "not_evaluated", + }, + findings: [ + createArtifactInspectorFinding({ + code, + message: error instanceof Error ? error.message : undefined, + evidence: isPlainObject(error?.details) ? error.details : {}, + }), + ], + }; + } + + if (!authorityContract.active) { + return { + status: "not_applicable", + applicable: false, + design_system_review: {}, + trusted_runtime_evidence: { + status: "not_required", + }, + findings: [], + }; + } + + const designSystemReview = baseDesignSystemReview(); + if (!isPlainObject(trustedRuntimeEvidence)) { + return { + status: "review_required", + applicable: true, + design_system_review: designSystemReview, + trusted_runtime_evidence: { + status: "missing", + }, + findings: stableFindings([ + missingRuntimeFinding({ + required_source: "judgmentkit_browser_runtime", + candidate_authored_evidence_accepted: false, + }), + ]), + }; + } + + const findings = []; + let failed = false; + let incomplete = false; + const issuer = + typeof trustedRuntimeEvidence.issuer === "string" + ? trustedRuntimeEvidence.issuer.trim() + : ""; + if (issuer !== "judgmentkit_browser_runtime") { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: "trusted_runtime_evidence.issuer", + expected: "judgmentkit_browser_runtime", + observed: issuer || null, + }), + ); + } + + const receiptStatus = runtimeStatus(trustedRuntimeEvidence.status); + if (receiptStatus === "fail") failed = true; + else if (receiptStatus !== "pass") incomplete = true; + + const scopeResults = isPlainObject(trustedRuntimeEvidence.scope_results) + ? trustedRuntimeEvidence.scope_results + : {}; + for (const scopeId of ["inspector_chrome", "inspection_overlay"]) { + const status = runtimeStatus(scopeResults[scopeId]); + if (status === "pass") { + designSystemReview[scopeId] = "passed"; + } else if (status === "fail") { + designSystemReview[scopeId] = "failed"; + failed = true; + findings.push( + createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_STYLE_LEAK", + evidence: { + field: `trusted_runtime_evidence.scope_results.${scopeId}`, + observed: scopeResults[scopeId], + }, + }), + ); + } else { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: `trusted_runtime_evidence.scope_results.${scopeId}`, + expected: "pass", + observed: status, + }), + ); + } + } + + const artifactResult = scopeResults.primary_artifact; + const artifactStatus = runtimeStatus(artifactResult); + if (artifactStatus === "pass") { + failed = true; + designSystemReview.primary_artifact = "failed_external_authority_overclaim"; + findings.push( + createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM", + evidence: { + field: "trusted_runtime_evidence.scope_results.primary_artifact.status", + expected: "external_not_reviewed", + observed: "pass", + }, + }), + ); + } else if (artifactStatus !== "external_not_reviewed") { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: "trusted_runtime_evidence.scope_results.primary_artifact.status", + expected: "external_not_reviewed", + observed: artifactStatus, + }), + ); + } + + const preservationStatus = runtimeStatus( + isPlainObject(artifactResult) + ? artifactResult.artifact_preservation ?? artifactResult.preservation_status + : null, + ); + if (preservationStatus === "fail") { + failed = true; + findings.push( + createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED", + evidence: { + field: + "trusted_runtime_evidence.scope_results.primary_artifact.artifact_preservation", + observed: artifactResult, + }, + }), + ); + } else if (preservationStatus !== "pass") { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: + "trusted_runtime_evidence.scope_results.primary_artifact.artifact_preservation", + expected: "pass", + observed: preservationStatus, + }), + ); + } + + const boundaryResult = boundaryEvidence(trustedRuntimeEvidence); + const boundaryStatus = runtimeStatus(boundaryResult); + if (boundaryStatus === "pass") { + designSystemReview.boundary_contract = "passed"; + } else if (boundaryStatus === "fail") { + failed = true; + designSystemReview.boundary_contract = "failed"; + findings.push( + createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_BOUNDARY_EVENT_UNDECLARED", + evidence: { + field: "trusted_runtime_evidence.boundary_results", + observed: boundaryResult, + }, + }), + ); + } else { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: "trusted_runtime_evidence.boundary_results", + expected: { + from_scope: CANONICAL_BOUNDARY.from_scope, + to_scope: CANONICAL_BOUNDARY.to_scope, + status: "pass", + }, + observed: boundaryResult, + }), + ); + } + + if (Array.isArray(trustedRuntimeEvidence.diagnostics)) { + for (const diagnostic of trustedRuntimeEvidence.diagnostics) { + const reportedCode = isPlainObject(diagnostic) ? diagnostic.code : null; + if (DIAGNOSTIC_CODE_SET.has(reportedCode)) { + failed = true; + findings.push( + createArtifactInspectorFinding({ + code: reportedCode, + message: + isPlainObject(diagnostic) && typeof diagnostic.message === "string" + ? diagnostic.message + : undefined, + evidence: { + field: "trusted_runtime_evidence.diagnostics", + diagnostic, + }, + }), + ); + } else { + incomplete = true; + findings.push( + missingRuntimeFinding({ + field: "trusted_runtime_evidence.diagnostics", + reported_code: reportedCode, + }), + ); + } + } + } + + if (failed && findings.length === 0) { + findings.push( + missingRuntimeFinding({ + field: "trusted_runtime_evidence.status", + observed: trustedRuntimeEvidence.status, + }), + ); + } + + const status = failed + ? "fail" + : incomplete + ? "review_required" + : "passed_with_external_artifact_authority"; + + return { + status, + applicable: true, + design_system_review: designSystemReview, + trusted_runtime_evidence: { + status: + status === "passed_with_external_artifact_authority" + ? "passed" + : failed + ? "failed" + : "incomplete", + issuer: issuer || null, + artifact_preservation: + preservationStatus === "pass" + ? "passed" + : preservationStatus === "fail" + ? "failed" + : "review_required", + }, + findings: stableFindings(findings), + }; +} diff --git a/src/index.mjs b/src/index.mjs index 270f042..02598f2 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -10,17 +10,39 @@ import { LUCIDE_ICON_SOURCE, } from "./lucide-icon-catalog.generated.mjs"; import { - WORKBENCH_SURFACE_PROFILE_ID, - cloneWorkbenchSurfaceProfile, + cloneSurfacePresentationProfile, listSurfacePresentationProfiles, } from "./surface-presentation-profiles.mjs"; +import { + reviewArtifactInspectorAuthorityEvidence, + validateArtifactInspectorAuthorityContract, +} from "./artifact-inspector-authority.mjs"; import { deriveFieldValueTrailingIndicatorSlotObservation } from "./visual-composition-observation.mjs"; export { + ARTIFACT_INSPECTOR_SURFACE_PROFILE, + ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, WORKBENCH_SURFACE_PROFILE, WORKBENCH_SURFACE_PROFILE_ID, + cloneArtifactInspectorSurfaceProfile, + cloneSurfacePresentationProfile, + cloneWorkbenchSurfaceProfile, + getSurfacePresentationProfile, listSurfacePresentationProfiles, } from "./surface-presentation-profiles.mjs"; +export { + ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS, + ARTIFACT_INSPECTOR_CANONICAL_SCOPES, + ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS, + ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES, + ARTIFACT_INSPECTOR_SCOPE_IDS, + ARTIFACT_INSPECTOR_STATE_GROUP_IDS, + createArtifactInspectorFinding, + normalizeArtifactInspectorBoundaryContracts, + normalizeArtifactInspectorScopes, + normalizeArtifactInspectorStateConfig, + validateArtifactInspectorAuthorityContract, +} from "./artifact-inspector-authority.mjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const requireFromModule = createRequire(import.meta.url); @@ -36,7 +58,7 @@ let validateVisualCompositionPolicySchema; const trustedVisualCompositionEvidence = new WeakMap(); const DEFAULT_WORKFLOW_ID = "workflow.ai-ui-generation"; const OPERATOR_REVIEW_PROFILE_ID = "operator-review-ui"; -const SURFACE_TYPE_IDS = [ +const LEGACY_SURFACE_TYPE_IDS = [ "marketing", "workbench", "operator_review", @@ -46,7 +68,7 @@ const SURFACE_TYPE_IDS = [ "setup_debug_tool", "conversation", ]; -const WORKFLOW_TOPOLOGY_IDS = [ +const LEGACY_WORKFLOW_TOPOLOGY_IDS = [ "workspace", "multi_surface", "staged_flow", @@ -54,6 +76,29 @@ const WORKFLOW_TOPOLOGY_IDS = [ "report", "conversation", ]; +const DEFAULT_ARTIFACT_INSPECTOR_SURFACE_TYPE_ID = "artifact_inspector"; +const DEFAULT_ARTIFACT_INSPECTOR_WORKFLOW_PROFILE_ID = "artifact-inspector-ui"; +const DEFAULT_ARTIFACT_INSPECTOR_TOPOLOGY_KIND = "artifact_centered"; +const DEFAULT_ARTIFACT_INSPECTOR_MANDATORY_EVIDENCE_IDS = [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative", +]; +const DEFAULT_ARTIFACT_INSPECTOR_POSITIVE_EVIDENCE_IDS = [ + ...DEFAULT_ARTIFACT_INSPECTOR_MANDATORY_EVIDENCE_IDS, + "completion_is_artifact_local", + "direct_or_spatial_interaction_required", + "external_artifact_visual_authority", +]; +const DEFAULT_ARTIFACT_INSPECTOR_EXCLUSION_EVIDENCE_IDS = [ + "queue_or_case_is_primary", + "many_items_are_primary", + "open_ended_creation_is_primary", + "conversation_turns_are_primary", + "linear_reading_is_completion", + "monitoring_is_completion", + "configuration_is_completion", +]; export class JudgmentKitInputError extends Error { constructor(message, options = {}) { @@ -75,18 +120,18 @@ export function loadActivityContract(contractPath = DEFAULT_CONTRACT_PATH) { } function getContractWorkflowId(contract) { - return contract.workflow?.id ?? DEFAULT_WORKFLOW_ID; + return contract?.workflow?.id ?? DEFAULT_WORKFLOW_ID; } function getWorkflowTopologyPolicy(contract) { - const policy = contract.workflow?.topology_policy; + const policy = contract?.workflow?.topology_policy; if (isPlainObject(policy)) { return policy; } return { - allowed_topologies: WORKFLOW_TOPOLOGY_IDS, + allowed_topologies: LEGACY_WORKFLOW_TOPOLOGY_IDS, preferred_default: "workspace", work_units_guidance: [ "Name domain work units without implying a numbered sequence.", @@ -103,16 +148,140 @@ function getWorkflowTopologyPolicy(contract) { }; } +function getArtifactInspectorModel(contract) { + const model = contract?.interaction_models?.artifact_inspector; + + return isPlainObject(model) ? model : null; +} + +function getArtifactInspectorIdentifiers(contract) { + const identifiers = getArtifactInspectorModel(contract)?.canonical_identifiers ?? {}; + + return { + surface_type: + optionalString(identifiers.surface_type) || + DEFAULT_ARTIFACT_INSPECTOR_SURFACE_TYPE_ID, + workflow_profile: + optionalString(identifiers.workflow_profile) || + DEFAULT_ARTIFACT_INSPECTOR_WORKFLOW_PROFILE_ID, + frontend_surface_profile: + optionalString(identifiers.frontend_surface_profile) || + "judgmentkit.artifact-inspector.v1", + topology_kind: + optionalString(identifiers.topology_kind) || + DEFAULT_ARTIFACT_INSPECTOR_TOPOLOGY_KIND, + }; +} + +function hasArtifactInspectorContract(contract) { + const identifiers = getArtifactInspectorIdentifiers(contract); + + return Boolean( + getArtifactInspectorModel(contract) || + contract?.surface_types?.[identifiers.surface_type] || + contract?.profiles?.[identifiers.workflow_profile] || + toStringArray(contract?.workflow?.topology_policy?.allowed_topologies).includes( + identifiers.topology_kind, + ), + ); +} + +function getAllowedWorkflowTopologyIds(contract) { + const configured = toStringArray( + getWorkflowTopologyPolicy(contract).allowed_topologies, + ); + const allowed = configured.length > 0 + ? configured + : [...LEGACY_WORKFLOW_TOPOLOGY_IDS]; + + if (hasArtifactInspectorContract(contract)) { + allowed.push(getArtifactInspectorIdentifiers(contract).topology_kind); + } + + return unique(allowed); +} + function getUiWorkflowProfiles(contract) { - return isPlainObject(contract.profiles) ? contract.profiles : {}; + const profiles = isPlainObject(contract?.profiles) ? contract.profiles : {}; + const model = getArtifactInspectorModel(contract); + + if (!model) { + return profiles; + } + + const identifiers = getArtifactInspectorIdentifiers(contract); + + if (profiles[identifiers.workflow_profile]) { + return profiles; + } + + return { + ...profiles, + [identifiers.workflow_profile]: { + pattern_id: "artifact-inspector", + model_id: optionalString(model.id) || identifiers.surface_type, + model_version: optionalString(model.version) || "1.0.0", + applies_when_most_true: toStringArray( + model.activity_evidence?.positive, + ), + do_not_use_when: toStringArray(model.activity_evidence?.exclusions), + guardrails: [], + review_criteria: [ + "The rendered artifact remains primary while locus-relative context and actions stay attached to it.", + ], + test_scenarios: [], + topology: isPlainObject(model.topology) + ? cloneWorkflowStructuredValue(model.topology) + : {}, + work_units: Array.isArray(model.work_units) + ? cloneWorkflowStructuredValue(model.work_units) + : [], + target_model: isPlainObject(model.target_model) + ? cloneWorkflowStructuredValue(model.target_model) + : {}, + composition_variants: Array.isArray(model.composition_variants) + ? cloneWorkflowStructuredValue(model.composition_variants) + : [], + state_groups: isPlainObject(model.state_groups) + ? cloneWorkflowStructuredValue(model.state_groups) + : {}, + }, + }; } function getSurfaceTypes(contract) { - if (isPlainObject(contract.surface_types)) { - return contract.surface_types; + const surfaceTypes = isPlainObject(contract?.surface_types) + ? contract.surface_types + : {}; + const model = getArtifactInspectorModel(contract); + + if (!model) { + return surfaceTypes; + } + + const identifiers = getArtifactInspectorIdentifiers(contract); + + if (surfaceTypes[identifiers.surface_type]) { + return surfaceTypes; } - return {}; + return { + ...surfaceTypes, + [identifiers.surface_type]: { + label: "Artifact inspector", + purpose: + "Inspect one rendered artifact in place, act on a semantic locus, and verify the local result.", + applies_when_most_true: [ + "One rendered artifact is the primary object.", + "The participant must select a semantic locus within the artifact.", + "Supporting context is meaningful only in relation to that locus.", + ], + do_not_use_when: [ + "A queue, case list, record set, or many-item comparison is primary.", + "Open-ended creation, conversation, monitoring, reading, or configuration is completion.", + ], + }, + }; } function normalizeOptionalSurfaceType(surfaceType) { @@ -141,7 +310,11 @@ function normalizeSurfaceProfileRequest(request) { } const normalized = request.trim(); - const allowed = ["auto", "none", WORKBENCH_SURFACE_PROFILE_ID]; + const allowed = [ + "auto", + "none", + ...listSurfacePresentationProfiles().map((entry) => entry.id), + ]; if (!allowed.includes(normalized)) { throw new JudgmentKitInputError( @@ -174,33 +347,40 @@ function resolveSurfacePresentationProfile({ } const normalizedSurfaceType = normalizeOptionalSurfaceType(surfaceType); - const explicitProfileRequest = - normalizedRequest === WORKBENCH_SURFACE_PROFILE_ID; + const explicitProfileRequest = !["auto", "none"].includes(normalizedRequest); + const explicitlyRequestedProfile = explicitProfileRequest + ? cloneSurfacePresentationProfile(normalizedRequest) + : null; + const eligibleProfile = explicitProfileRequest + ? explicitlyRequestedProfile + : listSurfacePresentationProfiles().find( + (entry) => entry.surface_type === normalizedSurfaceType, + ) ?? null; if ( explicitProfileRequest && - normalizedSurfaceType !== "workbench" + explicitlyRequestedProfile?.surface_type !== normalizedSurfaceType ) { throw new JudgmentKitInputError( - "The Workbench surface profile requires a selected surface_type of \"workbench\".", + `The ${explicitlyRequestedProfile?.name ?? "requested"} surface profile requires a selected surface_type of \"${explicitlyRequestedProfile?.surface_type ?? "unknown"}\".`, { details: { surface_profile: normalizedRequest, selected_surface_type: normalizedSurfaceType, - required_surface_type: "workbench", + required_surface_type: explicitlyRequestedProfile?.surface_type ?? null, }, }, ); } - if (normalizedSurfaceType !== "workbench") { + if (!eligibleProfile) { return null; } if (designSystemMode !== "judgmentkit_default") { if (explicitProfileRequest) { throw new JudgmentKitInputError( - "The JudgmentKit Workbench surface profile is unavailable when an external design system is active.", + "JudgmentKit surface profiles are unavailable when an external design system is active.", { details: { surface_profile: normalizedRequest, @@ -216,12 +396,16 @@ function resolveSurfacePresentationProfile({ const normalizedConfidence = typeof confidence === "string" ? confidence.trim().toLowerCase() : ""; - const acceptedConfidence = new Set(["medium", "high", "provided"]); + const acceptedConfidence = new Set( + Array.isArray(eligibleProfile.activation?.accepted_confidence) + ? eligibleProfile.activation.accepted_confidence + : ["medium", "high", "provided"], + ); if (!acceptedConfidence.has(normalizedConfidence)) { if (explicitProfileRequest) { throw new JudgmentKitInputError( - "The Workbench surface profile requires an explicitly selected or sufficiently grounded Workbench surface.", + `The ${eligibleProfile.name} surface profile requires an explicitly selected or sufficiently grounded ${eligibleProfile.surface_type} surface.`, { details: { surface_profile: normalizedRequest, @@ -238,7 +422,7 @@ function resolveSurfacePresentationProfile({ } return { - ...cloneWorkbenchSurfaceProfile(), + ...cloneSurfacePresentationProfile(eligibleProfile.id), selection: { request: normalizedRequest, surface_type_source: surfaceTypeSource || "unspecified", @@ -744,8 +928,7 @@ export function recommendUiWorkflowProfiles(input, options = {}) { matched_triggers: matchedTriggers.map((entry) => entry.id), matched_exclusions: matchedExclusions.map((entry) => entry.id), }; - - return { + const basePacket = { version: contract.version, contract_id: contract.id, workflow_id: getContractWorkflowId(contract), @@ -760,12 +943,305 @@ export function recommendUiWorkflowProfiles(input, options = {}) { }, }, }; + const artifactEvidence = buildArtifactInspectorEvidence(input, contract); + + if (!artifactEvidence.should_include) { + return basePacket; + } + + const identifiers = getArtifactInspectorIdentifiers(contract); + const artifactProfile = resolveUiWorkflowGuidanceProfile( + contract, + identifiers.workflow_profile, + ); + const artifactStatus = artifactEvidence.conflict + ? "review_required" + : artifactEvidence.mandatory_satisfied + ? "recommended" + : "not_recommended"; + const artifactSummary = { + profile_id: artifactProfile.profile_id, + workflow_id: artifactProfile.workflow_id, + pattern_id: artifactProfile.pattern_id, + model_id: + optionalString(artifactProfile.model_id) || artifactEvidence.model_id, + model_version: + optionalString(artifactProfile.model_version) || artifactEvidence.model_version, + status: artifactStatus, + trigger_threshold: artifactEvidence.mandatory_evidence_ids.length, + trigger_match_count: artifactEvidence.matched_mandatory_evidence.length, + matched_triggers: artifactEvidence.matched_positive_evidence, + matched_exclusions: artifactEvidence.matched_exclusion_evidence, + }; + + return { + ...basePacket, + recommended_profile_ids: + artifactStatus === "recommended" + ? [artifactProfile.profile_id] + : basePacket.recommended_profile_ids, + ...(artifactStatus === "review_required" + ? { review_required_profile_ids: [artifactProfile.profile_id] } + : {}), + recommendations: [ + ...basePacket.recommendations, + artifactSummary, + ], + evidence: { + ...basePacket.evidence, + [artifactProfile.profile_id]: { + mandatory_evidence_ids: artifactEvidence.mandatory_evidence_ids, + positives: artifactEvidence.positives, + exclusions: artifactEvidence.exclusions, + matched_mandatory_evidence: + artifactEvidence.matched_mandatory_evidence, + missing_mandatory_evidence: + artifactEvidence.missing_mandatory_evidence, + matched_exclusion_evidence: + artifactEvidence.matched_exclusion_evidence, + conflict: artifactEvidence.conflict, + }, + }, + }; } function surfaceEvidence(id, label, matched, reason) { return matchedEvidence(id, label, matched, reason); } +function hasConversationTurnActivity(input) { + const text = normalizeText(input); + const hasConversationObject = hasAffirmedAny(text, [ + /\b(?:chat|conversation|message exchange|message thread|live chat|assistant exchange)\b/, + /\bthread\b/, + ]); + const hasTurnBehavior = hasAffirmedAny(text, [ + /\b(?:message turns?|messages?|composer|send(?:s|ing)?|reply|replies|replying|respond|responds|responding|back and forth)\b/, + /\bask(?:s|ing)? (?:a |the )?(?:question|questions)\b/, + /\b(?:participant|participants|people|teammates?|agents?|customers?|users?)\s+(?:exchange|reply|respond|message)\b/, + ]); + const hasExchangeContinuity = hasAffirmedAny(text, [ + /\b(?:open-ended|open ended|continue|continues|continuing|keep(?:s|ing)? (?:the )?(?:thread|conversation|chat)|close(?:s|d|ing)? (?:the )?(?:thread|conversation|chat)|response continuity|conversation continuity|exchange continuity)\b/, + /\b(?:message exchange|back and forth)\b/, + ]); + + return hasConversationObject && hasTurnBehavior && hasExchangeContinuity; +} + +function artifactInspectorEvidenceDefinition(input) { + const text = normalizeText(input); + const explicitStructuredCollectionPrimary = hasAffirmedAny(text, [ + /\b(?:structured\s+)?(?:intake|submission|record-entry|record entry|data-entry|data entry)?\s*form\s+(?:is|remains|stays)\s+(?:the\s+)?primary\b/, + /\bprimary (?:activity|work|surface|object)\s+(?:is|remains|stays)\s+(?:required\s+)?(?:field entry|data entry|record entry|structured collection|intake|form completion|validation|submission)\b/, + /\bcompletion\s+(?:is|means)\s+(?:required\s+)?(?:field entry|data entry|record entry|structured collection|form validation|validation and submission|submitting (?:the\s+)?(?:form|record))\b/, + ]); + const explicitRenderedArtifactPrimarySignal = hasAffirmedAny(text, [ + /\b(?:artifact|interface|document|composition|diagram|result|prototype|screen)\s+(?:that\s+)?(?:is|remains|stays|must remain|must stay)\s+(?:visible\s+and\s+)?(?:the\s+)?(?:primary|central|dominant)\b/, + /\bprimary (?:object|artifact)\s+(?:is|remains|stays|must remain|must stay)\s+(?:an?\s+|the\s+)?(?:rendered\s+)?(?:artifact|interface|document|composition|diagram|result|prototype|screen)\b/, + ]); + const renderedArtifactIsPrimary = + !explicitStructuredCollectionPrimary && + explicitRenderedArtifactPrimarySignal; + const locusSelectionRequired = hasAffirmedAny(text, [ + /\b(?:select|selects|selecting|focus|focuses|focusing|mark|marks|marking|inspect|inspects|inspecting|activate|activates|activating|choose|chooses|choosing|target|targets|targeting)\s+(?:an?\s+|the\s+|one\s+|a specific\s+|specific\s+|that\s+|this\s+)?(?:artifact\s+)?(?:locus|control|element|passage|text range|range|region|node|point|object|feature|part)\b/, + /\b(?:locus|control|element|passage|text range|region|node|point|feature)\s+(?:selection|is selected|must be selected|needs? to be selected)\b/, + /\bselect(?:ion)?\s+(?:within|inside|on)\s+(?:an?\s+|the\s+)?(?:artifact|interface|document|composition|diagram|result|prototype|screen)\b/, + ]); + const supportContextIsLocusRelative = hasAffirmedAny(text, [ + /\b(?:context|evidence|authority|rule|source|observation|correction|comparison|diagnostic|action|feedback|reason|receipt|support)\b[^.]{0,120}\b(?:attach(?:es|ed|ing)?|relat(?:e|es|ed|ing)|link(?:s|ed|ing)?|anchor(?:s|ed|ing)?|connect(?:s|ed|ing)?|belong(?:s|ed|ing)?|apply|applies|applied)\b[^.]{0,80}\b(?:locus|selected (?:control|element|passage|range|region|node|point|object|feature|part)|that (?:control|element|passage|range|region|node|point|object|feature|part|location))\b/, + /\b(?:bring|brings|bringing|attach|attaches|attaching|relate|relates|relating|show|shows|showing|reveal|reveals|revealing)\b[^.]{0,100}\b(?:context|evidence|authority|rule|source|observation|correction|comparison|diagnostic|action|feedback|reason|receipt|support)\b[^.]{0,80}\b(?:to|at|beside|for|in relation to)\s+(?:the\s+|that\s+|this\s+|selected\s+|active\s+)?(?:locus|control|element|passage|range|region|node|point|object|feature|part|location)\b/, + /\b(?:context|evidence|authority|rule|source|observation|correction|comparison|diagnostic|action|feedback|reason|receipt|support)\s+(?:is|only becomes|becomes|remains)\s+(?:meaningful|relevant|available|visible)\s+(?:in relation to|for|at|beside)\s+(?:the\s+|that\s+|this\s+|selected\s+|active\s+)?(?:locus|control|element|passage|range|region|node|point|object|feature|part|location)\b/, + ]); + const completionIsArtifactLocal = hasAffirmedAny(text, [ + /\b(?:completion|result|resulting state|verified state|receipt|feedback|outcome)\b[^.]{0,100}\b(?:on|beside|next to|attached to|at|within)\s+(?:the\s+|that\s+|this\s+|selected\s+|active\s+)?(?:artifact|locus|control|element|passage|range|region|node|point|object|feature|part|location)\b/, + /\b(?:verify|verifies|verifying|confirm|confirms|confirming|show|shows|showing)\s+(?:the\s+|a\s+)?(?:local\s+)?(?:result|state|change|receipt|outcome)\s+(?:on|beside|next to|at|within)\s+(?:the\s+|that\s+|this\s+|selected\s+|active\s+)?(?:artifact|locus|control|element|passage|range|region|node|point|object|feature|part|location)\b/, + /\bartifact-local (?:completion|result|state|feedback|receipt)\b/, + ]); + const directOrSpatialInteractionRequired = hasAffirmedAny(text, [ + /\b(?:direct manipulation|directly on|in place|spatial interaction|select-then-target|drag|touch|tap|pointer|keyboard)\b/, + /\b(?:select|mark|target|pin|connect|attach|preview)\b[^.]{0,80}\b(?:locus|control|element|passage|range|region|node|point|artifact)\b/, + ]); + const externalArtifactVisualAuthority = hasAffirmedAny(text, [ + /\b(?:external|independent|separate|declared)\s+(?:artifact\s+)?(?:visual authority|design system|styling authority|style authority)\b/, + /\b(?:artifact|interface|document|composition|diagram)\s+(?:keeps|retains|uses|has|owns)\s+(?:its\s+|an?\s+)?(?:own|independent|external|separate)\s+(?:visual language|design system|styling|style)\b/, + /\b(?:who|which system)\s+owns\s+(?:the\s+)?artifact(?:'s)?\s+(?:styling|visual design|design system)\b/, + ]); + const queueOrCaseIsPrimary = hasAffirmedAny(text, [ + /\b(?:queue|case list|record set|work queue|review queue)\s+(?:is|remains|stays|as)\s+(?:the\s+)?primary\b/, + /\bprimary (?:object|work|surface)\s+(?:is|remains|stays)\s+(?:a\s+|the\s+)?(?:queue|case|case list|record set|work queue)\b/, + /\b(?:work through|review|triage|handle)\s+(?:a\s+|the\s+)?(?:queue|case list|record set)\b/, + ]); + const manyItemsArePrimary = hasAffirmedAny(text, [ + /\b(?:many|multiple|several|a set of|a list of|a queue of|side by side)\b[^.]{0,60}\b(?:artifacts?|items?|cases?|records?|objects?|documents?|interfaces?|results?)\b/, + /\b(?:compare|review|inspect)\s+(?:many|multiple|several|a set of|a list of)\b[^.]{0,60}\b(?:artifacts?|items?|cases?|records?|objects?|documents?|interfaces?|results?)\b/, + ]); + const openEndedCreationIsPrimary = hasAffirmedAny(text, [ + /\b(?:primarily|primary activity is|activity is)\s+(?:freely\s+|open-endedly\s+)?(?:creating|authoring|drawing|designing|composing|editing)\b/, + /\b(?:open-ended|open ended|freeform|free-form)\s+(?:creation|authoring|drawing|design|composition|editing)\b/, + /\b(?:create|author|draw|design|compose|edit)\s+(?:the\s+)?artifact\s+freely\b/, + ]); + const linearReadingIsCompletion = hasAffirmedAny(text, [ + /\b(?:linear reading|reading only|read-only report|only for reading)\b/, + /\bcompletion\s+(?:is|means)\s+(?:reading|understanding|citing|sharing)\b/, + /\b(?:primary activity is|activity is)\s+(?:reading|understanding|citing|sharing)\s+(?:a\s+|the\s+)?(?:document|report|memo|article|guide)\b/, + ]); + const monitoringIsCompletion = hasAffirmedAny(text, [ + /\bcompletion\s+(?:is|means)\s+(?:monitoring|knowing current status|status awareness|tracking trends)\b/, + /\b(?:primary activity is|activity is)\s+(?:monitoring|tracking|watching)\b/, + /\b(?:dashboard|monitor)\s+(?:is|remains|stays)\s+(?:the\s+)?primary\b/, + ]); + const configurationIsCompletion = hasAffirmedAny(text, [ + /\bcompletion\s+(?:is|means)\s+(?:a\s+)?(?:valid setup|configuration|configured integration|working integration)\b/, + /\b(?:primary activity is|activity is)\s+(?:setup|configuration|configuring|debugging|troubleshooting)\b/, + /\b(?:setup|configuration|debugging)\s+(?:is|remains|stays)\s+(?:the\s+)?primary\b/, + ]); + + return { + rendered_artifact_is_primary: surfaceEvidence( + "rendered_artifact_is_primary", + "One rendered artifact is the primary object.", + renderedArtifactIsPrimary, + "Looked for one bounded rendered artifact that remains the primary visible object.", + ), + locus_selection_required: surfaceEvidence( + "locus_selection_required", + "The participant must select a semantic locus within the artifact.", + locusSelectionRequired, + "Looked for required selection of a control, element, passage, range, region, node, point, or other artifact locus.", + ), + support_context_is_locus_relative: surfaceEvidence( + "support_context_is_locus_relative", + "Supporting context is meaningful in relation to the selected locus.", + supportContextIsLocusRelative, + "Looked for evidence, authority, corrections, actions, feedback, or receipts attached to the active locus.", + ), + completion_is_artifact_local: surfaceEvidence( + "completion_is_artifact_local", + "Completion is visible on or directly beside the artifact.", + completionIsArtifactLocal, + "Looked for a verified result, state, feedback, or receipt local to the artifact or locus.", + ), + direct_or_spatial_interaction_required: surfaceEvidence( + "direct_or_spatial_interaction_required", + "The activity requires direct or spatial interaction with the artifact.", + directOrSpatialInteractionRequired, + "Looked for in-place selection, targeting, preview, touch, pointer, drag, or keyboard-equivalent interaction.", + ), + external_artifact_visual_authority: surfaceEvidence( + "external_artifact_visual_authority", + "The artifact may retain declared visual authority independent from the inspector chrome.", + externalArtifactVisualAuthority, + "Looked for an external or independent artifact design system, visual language, or styling authority.", + ), + queue_or_case_is_primary: surfaceEvidence( + "queue_or_case_is_primary", + "A queue, case list, or record set is primary.", + queueOrCaseIsPrimary, + "Looked for queue, case-list, record-set, or repeated case work as the primary object.", + ), + many_items_are_primary: surfaceEvidence( + "many_items_are_primary", + "Many items or artifacts are primary.", + manyItemsArePrimary, + "Looked for multiple or side-by-side artifacts, items, cases, records, or results as peers.", + ), + open_ended_creation_is_primary: surfaceEvidence( + "open_ended_creation_is_primary", + "Open-ended creation or editing is primary.", + openEndedCreationIsPrimary, + "Looked for freeform authoring, drawing, designing, composing, or editing as completion.", + ), + conversation_turns_are_primary: surfaceEvidence( + "conversation_turns_are_primary", + "Conversation turns and response continuity are primary.", + hasConversationTurnActivity(text), + "Required a conversation object, message-turn behavior, and exchange continuity; the word thread alone is insufficient.", + ), + linear_reading_is_completion: surfaceEvidence( + "linear_reading_is_completion", + "Linear reading, understanding, citing, or sharing is completion.", + linearReadingIsCompletion, + "Looked for reading-only or linear report completion.", + ), + monitoring_is_completion: surfaceEvidence( + "monitoring_is_completion", + "Monitoring status, trends, or operational health is completion.", + monitoringIsCompletion, + "Looked for monitoring or status awareness as the primary completion.", + ), + configuration_is_completion: surfaceEvidence( + "configuration_is_completion", + "Setup, configuration, or debugging is completion.", + configurationIsCompletion, + "Looked for valid setup, configuration, debugging, or troubleshooting as completion.", + ), + }; +} + +function buildArtifactInspectorEvidence(input, contract) { + const model = getArtifactInspectorModel(contract); + const configuredEvidence = model?.activity_evidence ?? {}; + const mandatoryIds = toStringArray(configuredEvidence.mandatory); + const positiveIds = toStringArray(configuredEvidence.positive); + const exclusionIds = toStringArray(configuredEvidence.exclusions); + const resolvedMandatoryIds = mandatoryIds.length > 0 + ? mandatoryIds + : DEFAULT_ARTIFACT_INSPECTOR_MANDATORY_EVIDENCE_IDS; + const resolvedPositiveIds = positiveIds.length > 0 + ? positiveIds + : DEFAULT_ARTIFACT_INSPECTOR_POSITIVE_EVIDENCE_IDS; + const resolvedExclusionIds = exclusionIds.length > 0 + ? exclusionIds + : DEFAULT_ARTIFACT_INSPECTOR_EXCLUSION_EVIDENCE_IDS; + const definitions = artifactInspectorEvidenceDefinition(input); + const evidenceFor = (id) => definitions[id] ?? surfaceEvidence( + id, + id.replaceAll("_", " "), + false, + `No deterministic detector is registered for ${id}.`, + ); + const positives = resolvedPositiveIds.map(evidenceFor); + const exclusions = resolvedExclusionIds.map(evidenceFor); + const matchedPositiveIds = positives + .filter((entry) => entry.matched) + .map((entry) => entry.id); + const matchedExclusionIds = exclusions + .filter((entry) => entry.matched) + .map((entry) => entry.id); + const matchedMandatoryIds = resolvedMandatoryIds.filter((id) => + matchedPositiveIds.includes(id)); + const missingMandatoryIds = resolvedMandatoryIds.filter((id) => + !matchedPositiveIds.includes(id)); + const mandatorySatisfied = missingMandatoryIds.length === 0; + const explicitInspectorIntent = /\b(?:artifact inspector|artifact inspection|artifact-centered|artifact centered)\b/i.test(input); + const shouldInclude = + hasArtifactInspectorContract(contract) && + (mandatorySatisfied || matchedMandatoryIds.length >= 2 || explicitInspectorIntent); + const conflict = mandatorySatisfied && matchedExclusionIds.length > 0; + + return { + model_id: optionalString(model?.id) || DEFAULT_ARTIFACT_INSPECTOR_SURFACE_TYPE_ID, + model_version: optionalString(model?.version) || "1.0.0", + mandatory_evidence_ids: resolvedMandatoryIds, + matched_mandatory_evidence: matchedMandatoryIds, + missing_mandatory_evidence: missingMandatoryIds, + matched_positive_evidence: matchedPositiveIds, + matched_exclusion_evidence: matchedExclusionIds, + mandatory_satisfied: mandatorySatisfied, + conflict, + should_include: shouldInclude, + status: conflict + ? "review_required" + : mandatorySatisfied + ? "matched" + : shouldInclude + ? "partial" + : "not_matched", + positives, + exclusions, + }; +} + function buildSurfaceTypeInputs(input, activityReview, contract, options = {}) { const reviewCandidate = activityReview?.candidate ?? {}; const activityModel = reviewCandidate.activity_model ?? {}; @@ -819,6 +1295,10 @@ function buildSurfaceTypeInputs(input, activityReview, contract, options = {}) { implementationTermsDetected, { directActionSource }, ); + const artifactInspectorEvidence = buildArtifactInspectorEvidence( + sourceText, + contract, + ); return { source_text: sourceText, @@ -827,6 +1307,7 @@ function buildSurfaceTypeInputs(input, activityReview, contract, options = {}) { normalized: normalizeText(sourceText), implementation_terms_detected: implementationTermsDetected, purpose_evidence: purposeEvidence, + artifact_inspector_evidence: artifactInspectorEvidence, }; } @@ -1110,9 +1591,7 @@ function buildSurfaceTypeScore(surfaceType, inputContext, contract) { hasAffirmedAny(text, [ /\b(?:schema change|prompt template|api endpoint|tool call trace|raw system mechanics|mcp server|json schema)\b/, ])); - const hasConversationPrimary = hasAffirmedAny(text, [ - /\b(?:chat|conversation|thread|message composer|assistant exchange|live chat|open-ended|open ended|reply|respond|back and forth)\b/, - ]); + const hasConversationPrimary = hasConversationTurnActivity(text); const hasReadingOrReportPrimary = hasAffirmedAny(text, [ /\b(?:content page|report|memo|article|doc|docs|documentation|guide|read|reads|reading|understand|understanding|learn|cite|citing|share|sharing|reference|summary|narrative)\b/, @@ -1133,12 +1612,40 @@ function buildSurfaceTypeScore(surfaceType, inputContext, contract) { const hasSetupDebug = hasRawSetupDebugIntent || (hasSetupDebugActivityContext && hasImplementationMachineryCue); - const hasConversation = - hasAffirmedAny(text, [ - /\b(?:chat|conversation|thread|message composer|assistant exchange|live chat|open-ended|open ended)\b/, - ]); + const hasConversation = hasConversationTurnActivity(text); const definitions = getSurfaceTypes(contract); const definition = definitions[surfaceType] ?? {}; + const artifactIdentifiers = getArtifactInspectorIdentifiers(contract); + + if (surfaceType === artifactIdentifiers.surface_type) { + const artifactEvidence = inputContext.artifact_inspector_evidence ?? + buildArtifactInspectorEvidence(inputContext.source_text, contract); + const baseScore = makeSurfaceScore( + surfaceType, + artifactEvidence.positives, + artifactEvidence.exclusions, + definition, + ); + + return { + ...baseScore, + score: artifactEvidence.mandatory_satisfied && !artifactEvidence.conflict + ? Math.max(baseScore.score, artifactEvidence.mandatory_evidence_ids.length) + : 0, + profile_id: artifactIdentifiers.workflow_profile, + profile_status: artifactEvidence.conflict + ? "review_required" + : artifactEvidence.mandatory_satisfied + ? "recommended" + : "not_recommended", + mandatory_evidence_ids: artifactEvidence.mandatory_evidence_ids, + matched_mandatory_evidence: + artifactEvidence.matched_mandatory_evidence, + missing_mandatory_evidence: + artifactEvidence.missing_mandatory_evidence, + routing_conflict: artifactEvidence.conflict, + }; + } if (surfaceType === "marketing") { const triggers = [ @@ -1498,13 +2005,14 @@ function buildSurfaceTypeScore(surfaceType, inputContext, contract) { "thread_is_product_surface", "The thread or message exchange is the product surface.", hasConversation, - "Looked for chat, conversation, thread, message composer, live chat, or open-ended exchange language.", + "Required a conversation object, message-turn behavior, and exchange continuity; the word thread alone is insufficient.", ), surfaceEvidence( "open_ended_exchange", "The activity is open-ended exchange rather than a bounded handoff.", - /\b(?:open-ended|open ended|back and forth|ask questions|respond|reply)\b/.test(text), - "Looked for open-ended exchange language.", + hasConversation && + /\b(?:open-ended|open ended|back and forth|ask questions|respond|reply|continue|continuing)\b/.test(text), + "Looked for open-ended exchange language after confirming message turns and response continuity.", ), ]; const exclusions = [ @@ -1532,6 +2040,7 @@ function buildSurfaceTypeScore(surfaceType, inputContext, contract) { function chooseSurfaceType(scores) { const positivePriority = [ + DEFAULT_ARTIFACT_INSPECTOR_SURFACE_TYPE_ID, "setup_debug_tool", "operator_review", "workbench", @@ -1550,6 +2059,7 @@ function chooseSurfaceType(scores) { "conversation", "marketing", "setup_debug_tool", + DEFAULT_ARTIFACT_INSPECTOR_SURFACE_TYPE_ID, ]; return [...scores].sort((left, right) => { @@ -1598,6 +2108,66 @@ function surfaceConfidence(activityReview, bestScore) { function buildSurfaceImplications(surfaceType) { const implications = { + artifact_inspector: { + interaction_implications: { + primary_structure: + "Persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion.", + make_easy: [ + "Orient to the artifact", + "Select the locus that matters", + "Inspect locus-relative context", + "Preview and validate locally", + "Verify the result beside the artifact", + ], + make_harder: [ + "Displace the artifact with persistent chrome", + "Detach evidence or actions from the active locus", + "Intercept native artifact interaction without explicit inspection mode", + ], + completion_focus: + "The participant can verify the bounded result on or directly beside the artifact and recover to the last safe state.", + }, + disclosure_implications: { + product_ui_rule: + "Use artifact and domain terms; keep implementation machinery diagnostic while naming visual-authority boundaries only when they affect inspection or audit.", + reveal_implementation_terms: false, + diagnostic_contexts: [ + "auditing", + "debugging", + "explicit source inspection", + "boundary inspection", + ], + }, + frontend_posture: { + density: "artifact-first contextual", + navigation_shape: + "artifact persistent; supporting context revealed from the active locus", + state_coverage: [ + "artifact loading", + "artifact ready", + "no active locus", + "locus selected", + "context loading", + "preview", + "supported", + "invalid or ambiguous", + "stale or unavailable", + "local result", + "recovery", + ], + responsive_expectations: + "Wide and narrow layouts must preserve the artifact, active locus, local feedback, and recovery path without obscuring the inspected target.", + component_families: [ + "artifact viewport", + "locus selection", + "context anchor or tray", + "relation preview", + "local feedback", + "receipt marker", + "back or reset action", + ], + }, + }, marketing: { interaction_implications: { primary_structure: "Audience need, offer, proof, and primary action.", @@ -1781,29 +2351,130 @@ export function recommendSurfaceTypes(input, options = {}) { const inputContext = buildSurfaceTypeInputs(input.trim(), activityReview, contract, { hasExplicitActivityReview, }); - const scores = SURFACE_TYPE_IDS.map((surfaceType) => + const artifactIdentifiers = getArtifactInspectorIdentifiers(contract); + const artifactEvidence = inputContext.artifact_inspector_evidence; + const surfaceTypeIds = [ + ...LEGACY_SURFACE_TYPE_IDS, + ...(artifactEvidence.should_include && + getSurfaceTypes(contract)[artifactIdentifiers.surface_type] + ? [artifactIdentifiers.surface_type] + : []), + ]; + const scores = surfaceTypeIds.map((surfaceType) => buildSurfaceTypeScore(surfaceType, inputContext, contract), ); - const recommended = chooseSurfaceType(scores); - const confidence = surfaceConfidence(activityReview, recommended.score); + const scoredRecommendation = chooseSurfaceType(scores); + const artifactScore = scores.find( + (entry) => entry.surface_type === artifactIdentifiers.surface_type, + ); + const routingConflict = artifactEvidence.conflict + ? { + status: "review_required", + surface_type: artifactIdentifiers.surface_type, + matched_mandatory_evidence: + artifactEvidence.matched_mandatory_evidence, + matched_exclusion_evidence: + artifactEvidence.matched_exclusion_evidence, + conflicting_signals: [ + ...artifactEvidence.matched_mandatory_evidence.map((id) => ({ + kind: "mandatory", + id, + })), + ...artifactEvidence.matched_exclusion_evidence.map((id) => ({ + kind: "exclusion", + id, + })), + ], + competing_surface_types: scores + .filter((entry) => + entry.surface_type !== artifactIdentifiers.surface_type && + (entry.score > 0 || entry.exclusion_match_count > 0)) + .map((entry) => ({ + surface_type: entry.surface_type, + score: entry.score, + matched_triggers: entry.matched_triggers, + matched_exclusions: entry.matched_exclusions, + })), + } + : null; + const recommended = + artifactEvidence.mandatory_satisfied && !routingConflict && artifactScore + ? artifactScore + : scoredRecommendation; + const recommendedSurfaceType = routingConflict + ? null + : recommended.surface_type; + const confidence = routingConflict + ? "low" + : surfaceConfidence(activityReview, recommended.score); const blockedSurfaceTypes = scores - .filter((entry) => entry.surface_type !== recommended.surface_type) + .filter((entry) => entry.surface_type !== recommendedSurfaceType) .filter((entry) => entry.exclusion_match_count > 0 || entry.profile_status === "blocked") .map((entry) => entry.surface_type); - const implications = buildSurfaceImplications(recommended.surface_type); + const implications = buildSurfaceImplications( + recommendedSurfaceType ?? artifactIdentifiers.surface_type, + ); + const artifactRecommendationOutput = isPlainObject( + getArtifactInspectorModel(contract)?.recommendation_output, + ) + ? getArtifactInspectorModel(contract).recommendation_output + : {}; const packet = { version: contract.version, contract_id: contract.id, workflow_id: getContractWorkflowId(contract), - status: activityReview.review_status === "ready_for_review" ? "ready" : "needs_source_context", - recommended_surface_type: recommended.surface_type, + status: routingConflict + ? "review_required" + : activityReview.review_status === "ready_for_review" + ? "ready" + : "needs_source_context", + recommended_surface_type: recommendedSurfaceType, + ...(recommendedSurfaceType === artifactIdentifiers.surface_type + ? { + profile_id: + optionalString(artifactRecommendationOutput.profile_id) || + artifactIdentifiers.workflow_profile, + primary_structure: + optionalString(artifactRecommendationOutput.primary_structure) || + "persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion", + density: + optionalString(artifactRecommendationOutput.density) || + "artifact-first contextual", + navigation_shape: + optionalString(artifactRecommendationOutput.navigation_shape) || + "artifact persistent; supporting context revealed from the active locus", + } + : {}), blocked_surface_types: unique(blockedSurfaceTypes), confidence, + ...(routingConflict ? { routing_conflict: routingConflict } : {}), evidence: { activity_review_status: activityReview.review_status, input_excerpt: input.trim().slice(0, 240), implementation_terms_detected: inputContext.implementation_terms_detected, purpose_evidence: inputContext.purpose_evidence, + ...(artifactEvidence.should_include + ? { + artifact_inspector: { + model_id: artifactEvidence.model_id, + model_version: artifactEvidence.model_version, + status: artifactEvidence.status, + mandatory_evidence_ids: + artifactEvidence.mandatory_evidence_ids, + matched_mandatory_evidence: + artifactEvidence.matched_mandatory_evidence, + missing_mandatory_evidence: + artifactEvidence.missing_mandatory_evidence, + matched_positive_evidence: + artifactEvidence.matched_positive_evidence, + matched_exclusion_evidence: + artifactEvidence.matched_exclusion_evidence, + positives: artifactEvidence.positives, + exclusions: artifactEvidence.exclusions, + conflict: artifactEvidence.conflict, + }, + } + : {}), surface_type_scores: scores.map((entry) => ({ surface_type: entry.surface_type, label: entry.label, @@ -1820,7 +2491,9 @@ export function recommendSurfaceTypes(input, options = {}) { } : {}), })), - selected_definition: resolveSurfaceType(contract, recommended.surface_type), + selected_definition: recommendedSurfaceType + ? resolveSurfaceType(contract, recommendedSurfaceType) + : null, }, interaction_implications: implications.interaction_implications, disclosure_implications: implications.disclosure_implications, @@ -1845,6 +2518,16 @@ function summarizeSurfaceReview(surfaceReview, { includeFrontendPosture = false disclosure_implications: isPlainObject(surfaceReview.disclosure_implications) ? surfaceReview.disclosure_implications : {}, + ...(optionalString(surfaceReview.status) === "review_required" + ? { status: "review_required" } + : {}), + ...(isPlainObject(surfaceReview.routing_conflict) + ? { + routing_conflict: cloneWorkflowStructuredValue( + surfaceReview.routing_conflict, + ), + } + : {}), }; if (includeFrontendPosture) { @@ -2160,7 +2843,11 @@ function extractObservedBriefDetails(input, contract, implementationTermsDetecte }; } -function buildReviewQuestions(evidence, implementationTermsDetected) { +function buildReviewQuestions( + evidence, + implementationTermsDetected, + artifactInspectorEvidence = null, +) { const questions = []; if (!evidence.hasActivity) { @@ -2185,6 +2872,50 @@ function buildReviewQuestions(evidence, implementationTermsDetected) { ); } + if (artifactInspectorEvidence?.should_include) { + const missing = new Set( + artifactInspectorEvidence.missing_mandatory_evidence, + ); + + if (missing.has("rendered_artifact_is_primary")) { + questions.push( + "Is one bounded rendered artifact the primary object that must remain visible throughout the work?", + ); + } + + if (missing.has("locus_selection_required")) { + questions.push( + "Must the participant select a specific semantic locus within the artifact to complete the work?", + ); + } + + if (missing.has("support_context_is_locus_relative")) { + questions.push( + "Does the supporting context only become meaningful in relation to the active artifact locus?", + ); + } + + if ( + !artifactInspectorEvidence.matched_positive_evidence.includes( + "completion_is_artifact_local", + ) + ) { + questions.push( + "What completion state or result must remain visible on or directly beside the artifact?", + ); + } + + if ( + !artifactInspectorEvidence.matched_positive_evidence.includes( + "external_artifact_visual_authority", + ) + ) { + questions.push( + "Who owns the artifact's visual styling, and is that authority independent from the inspector chrome?", + ); + } + } + return questions; } @@ -2400,6 +3131,11 @@ function buildCandidateDisclosurePolicy(analyzerPacket) { function selectTargetedQuestions(reviewQuestions) { const priorities = [ + /bounded rendered artifact|primary object/i, + /semantic locus|artifact locus/i, + /supporting context|active artifact locus/i, + /completion state|result.*artifact/i, + /owns.*artifact.*visual styling|authority independent/i, /activity/i, /decision|next action/i, /leave the surface|knowing|done|outcome/i, @@ -2697,6 +3433,7 @@ function buildActivityModelReviewPacket(analyzerPacket, candidate, source, contr candidateGuardrails.candidate_primary_terms_detected.length === 0; const sourceQuestions = selectTargetedQuestions(analyzerPacket.review_questions); const candidateQuestions = buildCandidateQuestions(candidateGuardrails); + const artifactInspectorEvidence = analyzerPacket.artifact_inspector_evidence; const packet = { version: analyzerPacket.version, contract_id: analyzerPacket.contract_id, @@ -2716,6 +3453,9 @@ function buildActivityModelReviewPacket(analyzerPacket, candidate, source, contr decision: evidence.decision, outcome: evidence.outcome, implementation_terms_detected: analyzerPacket.implementation_terms_detected, + ...(artifactInspectorEvidence?.should_include + ? { artifact_inspector: artifactInspectorEvidence } + : {}), }, assumptions: buildReviewAssumptions(analyzerPacket, source), confidence: buildReviewConfidence( @@ -2739,6 +3479,9 @@ function buildActivityModelReviewPacket(analyzerPacket, candidate, source, contr disclosure_translation_candidates: analyzerPacket.disclosure_policy.translation_candidates, original_review_questions: analyzerPacket.review_questions, + ...(artifactInspectorEvidence?.should_include + ? { artifact_inspector: artifactInspectorEvidence } + : {}), }, }; @@ -2792,7 +3535,12 @@ export function analyzeImplementationBrief(input, options = {}) { const implementationTermsDetected = detectImplementationTerms(input, contract); const observed = extractObservedBriefDetails(input, contract, implementationTermsDetected); const evidence = inferActivityEvidence(input); - const reviewQuestions = buildReviewQuestions(evidence, implementationTermsDetected); + const artifactInspectorEvidence = buildArtifactInspectorEvidence(input, contract); + const reviewQuestions = buildReviewQuestions( + evidence, + implementationTermsDetected, + artifactInspectorEvidence, + ); const status = reviewQuestions.length === 0 ? "ready" : "needs_review"; const packet = { version: contract.version, @@ -2804,6 +3552,9 @@ export function analyzeImplementationBrief(input, options = {}) { implementation_terms_detected: implementationTermsDetected, review_questions: reviewQuestions, ui_brief: buildUiBrief(contract, evidence, status, observed, implementationTermsDetected), + ...(artifactInspectorEvidence.should_include + ? { artifact_inspector_evidence: artifactInspectorEvidence } + : {}), }; assertNoStyleFields(packet); @@ -3022,6 +3773,9 @@ const REVIEW_PACKET_LABEL_TERMS = [ "Primary user", "Review status", ]; +const REVIEW_PACKET_ALLOWED_DOMAIN_PHRASES = new Set([ + "outcome pending", +]); function collectStringValues(value, values = []) { if (typeof value === "string") { @@ -3056,6 +3810,10 @@ function uiWorkflowPrimaryFields(candidate) { function textContainsReviewPacketMetaTerm(value) { const normalized = normalizeText(value); + if (REVIEW_PACKET_ALLOWED_DOMAIN_PHRASES.has(normalized)) { + return false; + } + if ( REVIEW_PACKET_MACHINE_TERMS.some((term) => normalized.includes(normalizeText(term)), @@ -3081,6 +3839,10 @@ function buildCandidatePrimaryMetaTermsDetected(candidate) { for (const value of collectStringValues(uiWorkflowPrimaryFields(candidate))) { const normalized = normalizeText(value); + if (REVIEW_PACKET_ALLOWED_DOMAIN_PHRASES.has(normalized)) { + continue; + } + for (const term of REVIEW_PACKET_MACHINE_TERMS) { if (normalized.includes(normalizeText(term))) { counts.set(term, (counts.get(term) ?? 0) + 1); @@ -3163,54 +3925,531 @@ function toSurfaceSetArray(value) { return value.filter(isPlainObject); } -function rawWorkflowTopology(value) { - const topology = optionalString(value); - - return WORKFLOW_TOPOLOGY_IDS.includes(topology) ? topology : ""; -} - -function inferWorkflowTopology(candidate, surfaceGuidance, contract) { - const explicitTopology = rawWorkflowTopology(candidate.workflow?.topology); - - if (explicitTopology) { - return explicitTopology; +function cloneWorkflowStructuredValue(value) { + if (Array.isArray(value)) { + return value.map((entry) => cloneWorkflowStructuredValue(entry)); } - const surfaceSet = toSurfaceSetArray(candidate.surface_set); - - if (surfaceSet.length > 1) { - return "multi_surface"; + if (isPlainObject(value)) { + return Object.fromEntries( + Object.entries(value).map(([key, entry]) => [ + key, + cloneWorkflowStructuredValue(entry), + ]), + ); } - const surfaceType = optionalString(surfaceGuidance?.recommended_surface_type); + return value; +} - if (surfaceType === "dashboard_monitor") { - return "dashboard"; +function toWorkflowWorkUnitArray(value) { + if (!Array.isArray(value)) { + return []; } - if (surfaceType === "content_report") { - return "report"; - } + return value.filter((entry) => + (typeof entry === "string" && cleanClause(entry).length > 0) || + isPlainObject(entry)); +} - if (surfaceType === "conversation") { - return "conversation"; +function workflowWorkUnitId(entry) { + if (isPlainObject(entry)) { + return optionalString(entry.id); } - const policy = getWorkflowTopologyPolicy(contract); - const preferredDefault = optionalString(policy.preferred_default); + const value = optionalString(entry); - return WORKFLOW_TOPOLOGY_IDS.includes(preferredDefault) - ? preferredDefault - : "workspace"; + return value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_+|_+$/g, ""); } -function aggregateSurfaceSet(surfaceSet) { - const sections = []; - const controls = []; +function workflowWorkUnitIds(value) { + return unique(toWorkflowWorkUnitArray(value).map(workflowWorkUnitId)); +} - for (const surface of surfaceSet) { - sections.push(...toStringArray(surface.sections)); - controls.push(...toStringArray(surface.controls)); +function duplicateWorkflowWorkUnitIds(value) { + const counts = new Map(); + for (const workUnit of toWorkflowWorkUnitArray(value)) { + const workUnitId = workflowWorkUnitId(workUnit); + if (!workUnitId) continue; + counts.set(workUnitId, (counts.get(workUnitId) ?? 0) + 1); + } + + return [...counts.entries()] + .filter(([, count]) => count > 1) + .map(([workUnitId]) => workUnitId); +} + +function rawWorkflowTopology(value, contract) { + const topology = isPlainObject(value) + ? optionalString(value.kind) + : optionalString(value); + + return getAllowedWorkflowTopologyIds(contract).includes(topology) + ? topology + : ""; +} + +function assertConsistentWorkflowTopology( + workflow, + { + code = "invalid_input", + message = + "Workflow topology and topology_contract.kind must agree when both are provided.", + } = {}, +) { + const topology = isPlainObject(workflow?.topology) + ? optionalString(workflow.topology.kind) + : optionalString(workflow?.topology); + const topologyContractKind = optionalString( + workflow?.topology_contract?.kind, + ); + + if ( + topology && + topologyContractKind && + topology !== topologyContractKind + ) { + throw new JudgmentKitInputError(message, { + code, + details: { + field: "workflow.topology", + topology, + topology_contract_kind: topologyContractKind, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + }, + }); + } + + return topology || topologyContractKind; +} + +function artifactInspectorAuthorityCandidate(implementationContract) { + const source = isPlainObject(implementationContract) + ? implementationContract + : {}; + const fieldAliases = { + design_system_scopes: ["design_system_scopes", "designSystemScopes"], + boundary_contracts: ["boundary_contracts", "boundaryContracts"], + artifact_inspector: ["artifact_inspector", "artifactInspector"], + }; + const candidate = {}; + let present = false; + + for (const [canonicalField, aliases] of Object.entries(fieldAliases)) { + const suppliedAlias = aliases.find((field) => + Object.prototype.hasOwnProperty.call(source, field), + ); + if (!suppliedAlias) continue; + present = true; + candidate[canonicalField] = source[suppliedAlias]; + } + + return { candidate, present }; +} + +function artifactInspectorRequiredStructureIssue({ + workflow, + contract, + activeStateGroupIds, +}) { + const model = getArtifactInspectorModel(contract) ?? {}; + const topologyContract = isPlainObject(workflow?.topology_contract) + ? workflow.topology_contract + : {}; + const primaryObjectId = optionalString( + topologyContract.primary_object_id, + ); + const artifact = isPlainObject(workflow?.artifact) + ? workflow.artifact + : null; + const artifactId = optionalString( + artifact?.id ?? artifact?.artifact_id, + ); + + if ( + !artifact || + !artifactId || + !optionalString(artifact.boundary) || + !primaryObjectId || + artifactId !== primaryObjectId + ) { + return { + field: "workflow.artifact", + expected: { + id: primaryObjectId || "artifact", + boundary: "A non-empty rendered artifact boundary description.", + }, + observed: artifact, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + }; + } + + const targetModel = isPlainObject(workflow?.target_model) + ? workflow.target_model + : null; + const canonicalTargetModel = isPlainObject(model.target_model) + ? model.target_model + : {}; + const targetArtifactId = optionalString( + targetModel?.artifact_id ?? targetModel?.primary_object_id, + ); + const canonicalArrayField = (field) => { + const observed = toStringArray(targetModel?.[field]); + const allowed = toStringArray(canonicalTargetModel[field]); + return ( + observed.length > 0 && + observed.every((entry) => allowed.includes(entry)) + ); + }; + const targetModelValid = Boolean( + targetModel && + targetArtifactId === primaryObjectId && + canonicalArrayField("target_kinds") && + canonicalArrayField("target_identity_sources") && + canonicalArrayField("label_sources") && + canonicalArrayField("artifact_interactivity") && + optionalString(targetModel.geometry_policy) === + optionalString(canonicalTargetModel.geometry_policy) && + optionalString(targetModel.native_interaction_precedence) === + optionalString(canonicalTargetModel.native_interaction_precedence) + ); + + if (!targetModelValid) { + return { + field: "workflow.target_model", + expected: { + ...cloneWorkflowStructuredValue(canonicalTargetModel), + artifact_id: primaryObjectId, + }, + observed: targetModel, + diagnostic_code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + }; + } + + const workflowStateGroups = isPlainObject(workflow?.state_groups) + ? workflow.state_groups + : null; + const canonicalStateGroups = isPlainObject(model.state_groups) + ? model.state_groups + : {}; + const expectedActiveStateGroupIds = toStringArray(activeStateGroupIds); + const observedStateGroupIds = workflowStateGroups + ? Object.keys(workflowStateGroups) + : []; + const activeGroupsMatch = + expectedActiveStateGroupIds.length > 0 && + expectedActiveStateGroupIds.length === observedStateGroupIds.length && + expectedActiveStateGroupIds.every((groupId) => + observedStateGroupIds.includes(groupId), + ); + const activeStatesMatch = + workflowStateGroups && + expectedActiveStateGroupIds.every((groupId) => { + const expectedStates = toStringArray(canonicalStateGroups[groupId]); + const observedStates = toStringArray(workflowStateGroups[groupId]); + return ( + expectedStates.length > 0 && + observedStates.length === expectedStates.length && + expectedStates.every((state) => observedStates.includes(state)) + ); + }); + + if (!activeGroupsMatch || !activeStatesMatch) { + return { + field: "workflow.state_groups", + expected: Object.fromEntries( + expectedActiveStateGroupIds.map((groupId) => [ + groupId, + cloneWorkflowStructuredValue(canonicalStateGroups[groupId] ?? []), + ]), + ), + observed: workflowStateGroups, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }; + } + + return null; +} + +function assertArtifactInspectorBoundaryConsistency({ + workflow, + surfaceTypes = [], + profileIds = [], + implementationContract, + additionalAuthoritySignals = [], + activityModel, + interactionContract, + contract, + code = "invalid_artifact_inspector_authority_contract", + message = + "Artifact Inspector surface, topology, profile, and scoped authority must agree.", +} = {}) { + const resolvedContract = contract ?? loadActivityContract(); + const identifiers = getArtifactInspectorIdentifiers(resolvedContract); + const topology = assertConsistentWorkflowTopology(workflow, { + code, + message, + }); + const normalizedSurfaceTypes = unique( + [ + ...surfaceTypes, + implementationContract?.surface_type, + implementationContract?.surfaceType, + ] + .map((value) => optionalString(value)) + .filter(Boolean), + ); + const normalizedProfileIds = unique( + profileIds.map(optionalString).filter(Boolean), + ); + const topologyString = + typeof workflow?.topology === "string" + ? optionalString(workflow.topology) + : ""; + const topologyContractKind = optionalString( + workflow?.topology_contract?.kind, + ); + const topologyIsArtifact = topology === identifiers.topology_kind; + const canonicalArtifactTopologyShape = Boolean( + topologyString === identifiers.topology_kind && + isPlainObject(workflow?.topology_contract) && + topologyContractKind === identifiers.topology_kind, + ); + const surfaceSignalsArtifact = normalizedSurfaceTypes.includes( + identifiers.surface_type, + ); + const profileSignalsArtifact = normalizedProfileIds.includes( + identifiers.workflow_profile, + ); + const { candidate: authorityCandidate, present: authorityBundlePresent } = + artifactInspectorAuthorityCandidate(implementationContract); + const additionalAuthoritySignalPresent = additionalAuthoritySignals.some( + (value) => Boolean(value), + ); + let authorityBundleActive = false; + let normalizedAuthority = null; + let authorityError = null; + + if (authorityBundlePresent) { + try { + normalizedAuthority = + validateArtifactInspectorAuthorityContract(authorityCandidate); + authorityBundleActive = normalizedAuthority.active; + } catch (error) { + authorityError = error; + } + } + + const anyArtifactSignal = Boolean( + topologyIsArtifact || + surfaceSignalsArtifact || + profileSignalsArtifact || + authorityBundlePresent || + additionalAuthoritySignalPresent, + ); + const surfaceAligned = + normalizedSurfaceTypes.length > 0 && + normalizedSurfaceTypes.every( + (surfaceType) => surfaceType === identifiers.surface_type, + ); + const profileAligned = normalizedProfileIds.every( + (profileId) => profileId === identifiers.workflow_profile, + ); + const allArtifactSignalsAgree = Boolean( + surfaceAligned && + canonicalArtifactTopologyShape && + authorityBundleActive && + profileAligned, + ); + const reviewedActiveStateGroups = + deriveArtifactInspectorActiveStateGroups({ + workflow: isPlainObject(workflow) ? workflow : {}, + activity_model: activityModel, + interaction_contract: interactionContract, + }); + const authorityActiveStateGroups = toStringArray( + normalizedAuthority?.artifact_inspector?.active_state_groups, + ); + + if (anyArtifactSignal && !allArtifactSignalsAgree) { + throw new JudgmentKitInputError(message, { + code, + details: { + field: "artifact_inspector.boundary_consistency", + expected: { + surface_type: identifiers.surface_type, + topology: identifiers.topology_kind, + topology_contract_kind: identifiers.topology_kind, + workflow_profile: identifiers.workflow_profile, + authority_bundle_active: true, + }, + observed: { + surface_types: normalizedSurfaceTypes, + topology: isPlainObject(workflow?.topology) + ? optionalString(workflow.topology.kind) || null + : optionalString(workflow?.topology) || null, + topology_representation: isPlainObject(workflow?.topology) + ? "object" + : typeof workflow?.topology, + topology_contract_kind: topologyContractKind || null, + canonical_topology_shape: canonicalArtifactTopologyShape, + workflow_profile_ids: normalizedProfileIds, + authority_bundle_present: authorityBundlePresent, + authority_bundle_active: authorityBundleActive, + additional_authority_signal_present: + additionalAuthoritySignalPresent, + ...(authorityError + ? { + authority_error: + optionalString(authorityError.message) || + "invalid_artifact_inspector_authority_contract", + } + : {}), + }, + reason: "artifact_inspector_boundary_signal_mismatch", + }, + }); + } + + if (allArtifactSignalsAgree) { + if ( + !artifactInspectorStateGroupsMatch( + authorityActiveStateGroups, + reviewedActiveStateGroups, + ) + ) { + throw new JudgmentKitInputError( + "Artifact Inspector authority cannot activate state groups absent from the reviewed activity and workflow.", + { + code, + details: { + field: "artifact_inspector.active_state_groups", + expected: reviewedActiveStateGroups, + observed: authorityActiveStateGroups, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }, + }, + ); + } + const requiredStructureIssue = artifactInspectorRequiredStructureIssue({ + workflow, + contract: resolvedContract, + activeStateGroupIds: reviewedActiveStateGroups, + }); + if (requiredStructureIssue) { + throw new JudgmentKitInputError( + "Artifact Inspector packets must preserve the reviewed artifact, locus model, and active state structures across every boundary.", + { + code, + details: { + ...requiredStructureIssue, + reason: "artifact_inspector_required_structure_invalid", + }, + }, + ); + } + } + + return { + artifactInspectorActive: anyArtifactSignal, + artifactSurfaceSelected: surfaceSignalsArtifact, + artifactTopologySelected: topologyIsArtifact, + authorityBundleActive, + }; +} + +function normalizeArtifactTopologyContract(candidate, contract, { force = false } = {}) { + const identifiers = getArtifactInspectorIdentifiers(contract); + const workflow = candidate.workflow ?? {}; + const supplied = isPlainObject(workflow.topology) + ? workflow.topology + : isPlainObject(workflow.topology_contract) + ? workflow.topology_contract + : null; + const source = supplied; + + if (!source) { + return null; + } + + if ( + !force && + rawWorkflowTopology(workflow.topology, contract) !== + identifiers.topology_kind && + optionalString(source.kind) !== identifiers.topology_kind + ) { + return null; + } + + return { + ...cloneWorkflowStructuredValue(source), + kind: optionalString(source.kind) || identifiers.topology_kind, + primary_object_id: optionalString(source.primary_object_id), + entry_work_unit_id: optionalString(source.entry_work_unit_id), + completion_work_unit_ids: toStringArray(source.completion_work_unit_ids), + transitions: Array.isArray(source.transitions) + ? source.transitions.filter(isPlainObject).map((transition) => ({ + ...cloneWorkflowStructuredValue(transition), + from: optionalString(transition.from), + to: optionalString(transition.to), + ...(optionalString(transition.when) + ? { when: optionalString(transition.when) } + : {}), + })) + : [], + }; +} + +function inferWorkflowTopology(candidate, surfaceGuidance, contract) { + const explicitTopology = rawWorkflowTopology( + candidate.workflow?.topology, + contract, + ); + + if (explicitTopology) { + return explicitTopology; + } + + const surfaceSet = toSurfaceSetArray(candidate.surface_set); + + if (surfaceSet.length > 1) { + return "multi_surface"; + } + + const surfaceType = optionalString(surfaceGuidance?.recommended_surface_type); + + if (surfaceType === "dashboard_monitor") { + return "dashboard"; + } + + if (surfaceType === "content_report") { + return "report"; + } + + if (surfaceType === "conversation") { + return "conversation"; + } + + const policy = getWorkflowTopologyPolicy(contract); + const preferredDefault = optionalString(policy.preferred_default); + + return getAllowedWorkflowTopologyIds(contract).includes(preferredDefault) + ? preferredDefault + : "workspace"; +} + +function aggregateSurfaceSet(surfaceSet) { + const sections = []; + const controls = []; + + for (const surface of surfaceSet) { + sections.push(...toStringArray(surface.sections)); + controls.push(...toStringArray(surface.controls)); } return { @@ -3243,9 +4482,25 @@ function normalizeUiWorkflowSurfaceSet(candidate, implementationTermsDetected) { })); } -function collectStepperEligibilityText(activityReview, candidate, surfaceGuidance) { +function collectStepperEligibilityText( + activityReview, + candidate, + surfaceGuidance, + contract, +) { const activityCandidate = activityReview?.candidate ?? {}; const workflow = candidate.workflow ?? {}; + const topology = rawWorkflowTopology(workflow.topology, contract); + const workUnitText = toWorkflowWorkUnitArray(workflow.work_units).flatMap( + (workUnit) => + isPlainObject(workUnit) + ? [ + workUnit.id, + workUnit.participant_intent, + workUnit.system_responsibility, + ] + : [workUnit], + ); return [ activityReview?.source?.input_excerpt, @@ -3259,8 +4514,8 @@ function collectStepperEligibilityText(activityReview, candidate, surfaceGuidanc activityCandidate.interaction_contract?.completion, surfaceGuidance?.recommended_surface_type, workflow.surface_name, - workflow.topology, - ...(toStringArray(workflow.work_units)), + topology, + ...workUnitText, ...(toSurfaceSetArray(candidate.surface_set).flatMap((surface) => [ surface.name, surface.purpose, @@ -3272,10 +4527,15 @@ function collectStepperEligibilityText(activityReview, candidate, surfaceGuidanc } function buildStepperEligibility(activityReview, candidate, surfaceGuidance, contract) { - const topology = rawWorkflowTopology(candidate.workflow?.topology); + const topology = rawWorkflowTopology(candidate.workflow?.topology, contract); const surfaceType = optionalString(surfaceGuidance?.recommended_surface_type); const text = normalizeText( - collectStepperEligibilityText(activityReview, candidate, surfaceGuidance), + collectStepperEligibilityText( + activityReview, + candidate, + surfaceGuidance, + contract, + ), ); const explicitStagedRequest = /\b(?:wizard|stepper|stepped|step by step|step-by-step|multi-step|multistep|staged flow|guided flow|guided setup|setup wizard|onboarding sequence)\b/.test(text); @@ -3290,6 +4550,7 @@ function buildStepperEligibility(activityReview, candidate, surfaceGuidance, con "dashboard_monitor", "content_report", "conversation", + getArtifactInspectorIdentifiers(contract).surface_type, ].includes(surfaceType); const allowed = explicitStagedRequest || @@ -3316,74 +4577,787 @@ function buildStepperEligibility(activityReview, candidate, surfaceGuidance, con form_or_setup_sequence: formOrSetupSequence, blocked_surface_type: blockedSurfaceType, }, - }; -} + }; +} + +function assertUiWorkflowCandidateShape(candidate, contract) { + if (!isPlainObject(candidate)) { + throw new JudgmentKitInputError("UI workflow candidate must be an object."); + } + + if (!isPlainObject(candidate.workflow)) { + throw new JudgmentKitInputError("UI workflow candidate requires workflow."); + } + + if (Object.prototype.hasOwnProperty.call(candidate.workflow, "steps")) { + throw new JudgmentKitInputError( + "UI workflow candidate no longer accepts workflow.steps. Use workflow.work_units.", + ); + } + + if (Object.prototype.hasOwnProperty.call(candidate, "primary_ui")) { + throw new JudgmentKitInputError( + "UI workflow candidate no longer accepts primary_ui. Use surface_set.", + ); + } + + const suppliedTopology = candidate.workflow.topology; + const artifactTopologyCandidate = isArtifactTopologyCandidate(candidate, contract); + + if (!rawWorkflowTopology(suppliedTopology, contract) && !artifactTopologyCandidate) { + throw new JudgmentKitInputError( + "UI workflow candidate requires workflow.topology.", + ); + } + + if (toWorkflowWorkUnitArray(candidate.workflow.work_units).length === 0) { + throw new JudgmentKitInputError( + "UI workflow candidate requires workflow.work_units.", + ); + } + + if (toSurfaceSetArray(candidate.surface_set).length === 0) { + throw new JudgmentKitInputError("UI workflow candidate requires surface_set."); + } + + if (!isPlainObject(candidate.handoff)) { + throw new JudgmentKitInputError("UI workflow candidate requires handoff."); + } + + if (!isPlainObject(candidate.diagnostics)) { + throw new JudgmentKitInputError("UI workflow candidate requires diagnostics."); + } +} + +function buildUiWorkflowCandidateMissingFields(candidate) { + const workflow = candidate.workflow; + const handoff = candidate.handoff; + const completionState = optionalString(workflow.completion_state); + const handoffCompletion = + optionalString(handoff.next_action).length > 0 && + optionalString(handoff.reason).length > 0; + const workUnits = toWorkflowWorkUnitArray(workflow.work_units); + const surfaceSet = toSurfaceSetArray(candidate.surface_set); + const hasSurfaceStructure = + surfaceSet.some((surface) => toStringArray(surface.sections).length > 0); + + return { + workflow_structure: workUnits.length === 0 && !hasSurfaceStructure, + primary_actions: toStringArray(workflow.primary_actions).length === 0, + decision_support: toStringArray(workflow.decision_points).length === 0, + surface_sections: !hasSurfaceStructure, + completion_or_handoff: + completionState.length === 0 && !handoffCompletion, + }; +} + +function isArtifactTopologyCandidate(candidate, contract) { + if (!hasArtifactInspectorContract(contract)) { + return false; + } + + const workflow = candidate?.workflow ?? {}; + const identifiers = getArtifactInspectorIdentifiers(contract); + const suppliedTopology = isPlainObject(workflow.topology) + ? workflow.topology + : isPlainObject(workflow.topology_contract) + ? workflow.topology_contract + : null; + + return Boolean( + rawWorkflowTopology(workflow.topology, contract) === + identifiers.topology_kind || + optionalString(workflow.topology_contract?.kind) === + identifiers.topology_kind || + (suppliedTopology && + [ + "primary_object_id", + "entry_work_unit_id", + "completion_work_unit_ids", + "transitions", + ].some((key) => Object.prototype.hasOwnProperty.call(suppliedTopology, key))), + ); +} + +function isArtifactInspectorWorkflowSelected( + candidate, + contract, + guidanceProfile, + surfaceGuidance, +) { + const identifiers = getArtifactInspectorIdentifiers(contract); + + return Boolean( + hasArtifactInspectorContract(contract) && + (isArtifactTopologyCandidate(candidate, contract) || + optionalString(guidanceProfile?.profile_id) === + identifiers.workflow_profile || + optionalString(surfaceGuidance?.recommended_surface_type) === + identifiers.surface_type), + ); +} + +function getArtifactInspectorDiagnosticMeaning(contract, code, fallback) { + const diagnostics = getArtifactInspectorModel(contract)?.diagnostics; + + if (Array.isArray(diagnostics)) { + const match = diagnostics.find( + (entry) => isPlainObject(entry) && optionalString(entry.code) === code, + ); + + return optionalString(match?.meaning) || fallback; + } + + if (isPlainObject(diagnostics)) { + const entry = diagnostics[code]; + + return optionalString(isPlainObject(entry) ? entry.meaning : entry) || fallback; + } + + return fallback; +} + +function createArtifactInspectorWorkflowDiagnostic( + contract, + { code, field, meaning, expected, observed, repair_instruction: repairInstruction }, +) { + return { + code, + meaning: getArtifactInspectorDiagnosticMeaning(contract, code, meaning), + field, + expected_contract: expected, + observed: observed === undefined + ? null + : cloneWorkflowStructuredValue(observed), + repair_instruction: repairInstruction, + }; +} + +function artifactTargetModel(candidate) { + if (isPlainObject(candidate?.workflow?.target_model)) { + return candidate.workflow.target_model; + } + + return isPlainObject(candidate?.target_model) ? candidate.target_model : null; +} + +function artifactTargetKinds(targetModel) { + if (!isPlainObject(targetModel)) { + return []; + } + + return unique([ + ...toStringArray(targetModel.target_kinds), + ...toStringArray(targetModel.kinds), + optionalString(targetModel.target_kind), + optionalString(targetModel.kind), + ]); +} + +function buildArtifactInspectorWorkflowDiagnostics( + candidate, + contract, + guidanceProfile, + surfaceGuidance, + activityReview, + sourceInput, +) { + const groundedActivityArtifactEvidence = buildArtifactInspectorEvidence( + sourceInput, + contract, + ); + if ( + !groundedActivityArtifactEvidence.mandatory_satisfied && + !isArtifactInspectorWorkflowSelected( + candidate, + contract, + guidanceProfile, + surfaceGuidance, + ) + ) { + return null; + } + + const identifiers = getArtifactInspectorIdentifiers(contract); + const workflow = candidate.workflow ?? {}; + const topologySource = isPlainObject(workflow.topology) + ? workflow.topology + : isPlainObject(workflow.topology_contract) + ? workflow.topology_contract + : {}; + const observedTopologyKind = isPlainObject(workflow.topology) + ? optionalString(workflow.topology.kind) + : optionalString(workflow.topology) || optionalString(topologySource.kind); + const topologyContract = normalizeArtifactTopologyContract(candidate, contract, { + force: true, + }); + const workUnits = toWorkflowWorkUnitArray(workflow.work_units); + const workUnitIds = workflowWorkUnitIds(workUnits); + const diagnostics = []; + const push = (diagnostic) => { + diagnostics.push(createArtifactInspectorWorkflowDiagnostic(contract, diagnostic)); + }; + const suppliedActivityArtifactEvidence = + activityReview?.review?.evidence?.artifact_inspector ?? + activityReview?.guardrails?.artifact_inspector ?? + null; + const selectedSurfaceType = optionalString( + surfaceGuidance?.recommended_surface_type, + ); + const selectedGuidanceProfileId = optionalString(guidanceProfile?.profile_id); + const hasRoutingConflict = Boolean( + !groundedActivityArtifactEvidence.mandatory_satisfied || + groundedActivityArtifactEvidence.conflict || + suppliedActivityArtifactEvidence?.conflict || + optionalString(surfaceGuidance?.status) === "review_required" || + (selectedSurfaceType && selectedSurfaceType !== identifiers.surface_type) || + (selectedGuidanceProfileId && + selectedGuidanceProfileId !== identifiers.workflow_profile), + ); + + if (hasRoutingConflict) { + push({ + code: "JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT", + field: "artifact_inspector.routing", + meaning: + "Artifact Inspector selection conflicts with grounded activity, surface, or workflow-profile evidence.", + expected: { + mandatory_evidence_satisfied: true, + activity_conflict: false, + surface_type: identifiers.surface_type, + workflow_profile: identifiers.workflow_profile, + }, + observed: { + mandatory_evidence_satisfied: + groundedActivityArtifactEvidence.mandatory_satisfied, + missing_mandatory_evidence: + groundedActivityArtifactEvidence.missing_mandatory_evidence, + activity_conflict: Boolean(groundedActivityArtifactEvidence.conflict), + matched_exclusion_evidence: + groundedActivityArtifactEvidence.matched_exclusion_evidence, + surface_review_status: optionalString(surfaceGuidance?.status) || null, + surface_type: selectedSurfaceType || null, + workflow_profile: selectedGuidanceProfileId || null, + }, + repair_instruction: + "Resolve missing mandatory evidence and any activity, surface, or profile conflict before accepting an Artifact Inspector workflow; topology and profile metadata cannot override grounded routing.", + }); + } + + if (!topologyContract) { + push({ + code: "JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING", + field: "workflow.topology_contract", + meaning: "The artifact-centered workflow has no explicit structured topology contract.", + expected: + "The normative artifact_centered topology object in workflow.topology or workflow.topology_contract.", + observed: workflow.topology, + repair_instruction: + "Provide the full artifact_centered topology object; the legacy topology string is only a downstream compatibility projection.", + }); + } + + if (observedTopologyKind !== identifiers.topology_kind) { + push({ + code: "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + field: isPlainObject(workflow.topology) + ? "workflow.topology.kind" + : "workflow.topology", + meaning: "The artifact-inspector workflow does not declare the artifact-centered topology kind.", + expected: identifiers.topology_kind, + observed: observedTopologyKind || null, + repair_instruction: `Set workflow.topology.kind to "${identifiers.topology_kind}" or use that legacy topology string.`, + }); + } + + workUnits.forEach((workUnit, index) => { + if (isPlainObject(workUnit) && !optionalString(workUnit.id)) { + push({ + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + field: `workflow.work_units[${index}].id`, + meaning: "A structured artifact work unit has no stable identifier.", + expected: "A non-empty work-unit id referenced by the topology.", + observed: workUnit.id, + repair_instruction: "Add a stable id to this work unit and use it in topology references.", + }); + } + }); + + const duplicateWorkUnitIds = duplicateWorkflowWorkUnitIds(workUnits); + if (duplicateWorkUnitIds.length > 0) { + push({ + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + field: "workflow.work_units", + meaning: + "Artifact work-unit identifiers must be unique so topology references resolve unambiguously.", + expected: "Exactly one work-unit object for each canonical id.", + observed: duplicateWorkUnitIds, + repair_instruction: `Remove duplicate work-unit ids: ${duplicateWorkUnitIds.join(", ")}.`, + }); + } + + const canonicalWorkUnits = Array.isArray( + getArtifactInspectorModel(contract)?.work_units, + ) + ? getArtifactInspectorModel(contract).work_units.filter(isPlainObject) + : []; + const canonicalWorkUnitIds = canonicalWorkUnits + .map((workUnit) => optionalString(workUnit.id)) + .filter(Boolean); + const missingCanonicalWorkUnitIds = canonicalWorkUnitIds.filter( + (workUnitId) => !workUnitIds.includes(workUnitId), + ); + + if (missingCanonicalWorkUnitIds.length > 0) { + push({ + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + field: "workflow.work_units", + meaning: "The artifact workflow omits one or more required work units.", + expected: canonicalWorkUnitIds, + observed: workUnitIds, + repair_instruction: `Add the missing canonical work units: ${missingCanonicalWorkUnitIds.join(", ")}.`, + }); + } + + for (const canonicalWorkUnit of canonicalWorkUnits) { + const workUnitIndex = workUnits.findIndex( + (workUnit) => + workflowWorkUnitId(workUnit) === optionalString(canonicalWorkUnit.id), + ); + if (workUnitIndex === -1) { + continue; + } + const observedWorkUnit = workUnits[workUnitIndex]; + for (const fieldName of [ + "participant_intent", + "system_responsibility", + ]) { + const expectedValue = optionalString(canonicalWorkUnit[fieldName]); + const observedValue = optionalString(observedWorkUnit[fieldName]); + if (observedValue !== expectedValue) { + push({ + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + field: `workflow.work_units[${workUnitIndex}].${fieldName}`, + meaning: + "A required artifact work unit does not preserve its canonical participant intent or system responsibility.", + expected: expectedValue, + observed: observedValue || null, + repair_instruction: `Restore the canonical ${fieldName} for the ${canonicalWorkUnit.id} work unit.`, + }); + } + } + } + + const primaryObjectId = optionalString(topologyContract?.primary_object_id); + + if (topologyContract && !primaryObjectId) { + push({ + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.topology.primary_object_id", + meaning: "No bounded rendered artifact is declared.", + expected: "A non-empty primary artifact id.", + observed: topologyContract?.primary_object_id, + repair_instruction: "Declare the bounded rendered artifact in workflow.topology.primary_object_id.", + }); + } + + const candidateArtifact = isPlainObject(workflow.artifact) + ? workflow.artifact + : isPlainObject(candidate.artifact) + ? candidate.artifact + : null; + const declaredArtifactId = optionalString( + candidateArtifact?.id ?? candidateArtifact?.artifact_id, + ); + + if (!candidateArtifact || !declaredArtifactId) { + push({ + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.artifact.id", + meaning: "No bounded rendered artifact is declared.", + expected: primaryObjectId || "artifact", + observed: declaredArtifactId || null, + repair_instruction: + "Declare workflow.artifact with the same stable id used by workflow.topology.primary_object_id.", + }); + } + + if (!optionalString(candidateArtifact?.boundary)) { + push({ + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.artifact.boundary", + meaning: "The rendered artifact has no explicit boundary declaration.", + expected: "A non-empty rendered artifact boundary description.", + observed: candidateArtifact?.boundary, + repair_instruction: + "Declare the bounded rendered region owned by the external artifact authority.", + }); + } + + if (declaredArtifactId && primaryObjectId && declaredArtifactId !== primaryObjectId) { + push({ + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.topology.primary_object_id", + meaning: "The artifact-centered topology references a different primary object than the declared artifact.", + expected: declaredArtifactId, + observed: primaryObjectId, + repair_instruction: "Point primary_object_id at the declared artifact id.", + }); + } + + const targetModel = artifactTargetModel(candidate); + const canonicalTargetModel = getArtifactInspectorModel(contract)?.target_model ?? {}; + const targetValuesAreCanonicalSubset = (value, canonicalValue) => { + const observed = toStringArray(value); + const allowed = toStringArray(canonicalValue); + return ( + observed.length > 0 && + observed.every((entry) => allowed.includes(entry)) + ); + }; + const targetFieldRequirements = [ + { + key: "target_kinds", + valid: targetValuesAreCanonicalSubset( + targetModel?.target_kinds, + canonicalTargetModel.target_kinds, + ), + expected: + canonicalTargetModel.target_kinds ?? + ["whole_artifact", "element", "text_range", "region", "point"], + repair: "Declare the semantic locus kinds the participant can select.", + }, + { + key: "target_identity_sources", + valid: targetValuesAreCanonicalSubset( + targetModel?.target_identity_sources, + canonicalTargetModel.target_identity_sources, + ), + expected: + canonicalTargetModel.target_identity_sources ?? + ["artifact_supplied", "participant_selection"], + repair: + "Declare whether target identity comes from the artifact or explicit participant selection.", + }, + { + key: "geometry_policy", + valid: + optionalString(targetModel?.geometry_policy) === + optionalString(canonicalTargetModel.geometry_policy), + expected: + optionalString(canonicalTargetModel.geometry_policy) || + "A read-only geometry and semantic re-anchoring policy.", + repair: + "Declare how target geometry stays stable, re-anchors, or becomes stale across artifact revisions.", + }, + { + key: "label_sources", + valid: targetValuesAreCanonicalSubset( + targetModel?.label_sources, + canonicalTargetModel.label_sources, + ), + expected: + canonicalTargetModel.label_sources ?? + ["artifact_supplied", "artifact_authorized_proxy", "Unavailable"], + repair: + "Declare the artifact-authorized sources for accessible target labels.", + }, + { + key: "artifact_interactivity", + valid: targetValuesAreCanonicalSubset( + targetModel?.artifact_interactivity, + canonicalTargetModel.artifact_interactivity, + ), + expected: + canonicalTargetModel.artifact_interactivity ?? ["static", "live", "hybrid"], + repair: + "Declare the supported artifact interactivity modes so native interaction precedence can be protected.", + }, + { + key: "native_interaction_precedence", + valid: + optionalString(targetModel?.native_interaction_precedence) === + optionalString(canonicalTargetModel.native_interaction_precedence), + expected: + optionalString(canonicalTargetModel.native_interaction_precedence) || + "A policy preserving native artifact actions until explicit inspection is active.", + repair: + "Declare when inspection may capture an action and when the artifact retains native interaction precedence.", + }, + ]; -function assertUiWorkflowCandidateShape(candidate) { - if (!isPlainObject(candidate)) { - throw new JudgmentKitInputError("UI workflow candidate must be an object."); + for (const requirement of targetFieldRequirements) { + if (!requirement.valid) { + push({ + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: `workflow.target_model.${requirement.key}`, + meaning: "The participant cannot reliably select and retain a semantic artifact locus.", + expected: requirement.expected, + observed: targetModel?.[requirement.key], + repair_instruction: requirement.repair, + }); + } } - if (!isPlainObject(candidate.workflow)) { - throw new JudgmentKitInputError("UI workflow candidate requires workflow."); - } + const targetArtifactId = optionalString( + targetModel?.artifact_id ?? targetModel?.primary_object_id, + ); - if (Object.prototype.hasOwnProperty.call(candidate.workflow, "steps")) { - throw new JudgmentKitInputError( - "UI workflow candidate no longer accepts workflow.steps. Use workflow.work_units.", - ); + if (!targetArtifactId) { + push({ + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: "workflow.target_model.artifact_id", + meaning: "The target model is not attached to the declared primary artifact.", + expected: primaryObjectId || "workflow.topology.primary_object_id", + observed: targetArtifactId || null, + repair_instruction: + "Set workflow.target_model.artifact_id to the topology primary_object_id.", + }); + } else if (primaryObjectId && targetArtifactId !== primaryObjectId) { + push({ + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: "workflow.target_model.artifact_id", + meaning: "The target model is attached to a different artifact than the topology primary object.", + expected: primaryObjectId, + observed: targetArtifactId, + repair_instruction: "Attach the target model to workflow.topology.primary_object_id.", + }); } - if (Object.prototype.hasOwnProperty.call(candidate, "primary_ui")) { - throw new JudgmentKitInputError( - "UI workflow candidate no longer accepts primary_ui. Use surface_set.", - ); + const entryWorkUnitId = optionalString(topologyContract?.entry_work_unit_id); + + if ( + topologyContract && + (!entryWorkUnitId || !workUnitIds.includes(entryWorkUnitId)) + ) { + push({ + code: "JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID", + field: "workflow.topology.entry_work_unit_id", + meaning: "The topology entry does not reference a declared work unit.", + expected: workUnitIds, + observed: entryWorkUnitId || null, + repair_instruction: "Reference one declared workflow.work_units id as the topology entry.", + }); } - if (!rawWorkflowTopology(candidate.workflow.topology)) { - throw new JudgmentKitInputError( - "UI workflow candidate requires workflow.topology.", - ); + const completionWorkUnitIds = toStringArray( + topologyContract?.completion_work_unit_ids, + ); + const invalidCompletionIds = completionWorkUnitIds.filter( + (id) => !workUnitIds.includes(id), + ); + + if ( + topologyContract && + (completionWorkUnitIds.length === 0 || + invalidCompletionIds.length > 0 || + !completionWorkUnitIds.includes("verify_result")) + ) { + push({ + code: "JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID", + field: "workflow.topology.completion_work_unit_ids", + meaning: "Artifact-local completion does not reference declared work units.", + expected: { + declared_work_unit_ids: workUnitIds, + required_artifact_local_completion: "verify_result", + }, + observed: completionWorkUnitIds, + repair_instruction: "Reference one or more declared work-unit ids that visibly complete the artifact-local activity.", + }); } - if (toStringArray(candidate.workflow.work_units).length === 0) { - throw new JudgmentKitInputError( - "UI workflow candidate requires workflow.work_units.", + const transitions = Array.isArray(topologyContract?.transitions) + ? topologyContract.transitions + : []; + const invalidTransitions = transitions + .map((transition, index) => ({ + index, + from: optionalString(transition.from), + to: optionalString(transition.to), + })) + .filter( + (transition) => + !workUnitIds.includes(transition.from) || + !workUnitIds.includes(transition.to), ); - } + const canonicalTransitions = Array.isArray( + getArtifactInspectorModel(contract)?.topology?.transitions, + ) + ? getArtifactInspectorModel(contract).topology.transitions + : []; + const transitionContractMismatch = + transitions.length !== canonicalTransitions.length || + canonicalTransitions.some((expectedTransition, index) => { + const observedTransition = transitions[index]; + return ( + !isPlainObject(observedTransition) || + optionalString(observedTransition.from) !== + optionalString(expectedTransition.from) || + optionalString(observedTransition.to) !== + optionalString(expectedTransition.to) || + optionalString(observedTransition.when) !== + optionalString(expectedTransition.when) + ); + }); - if (toSurfaceSetArray(candidate.surface_set).length === 0) { - throw new JudgmentKitInputError("UI workflow candidate requires surface_set."); + if ( + topologyContract && + (transitions.length === 0 || + invalidTransitions.length > 0 || + transitionContractMismatch) + ) { + push({ + code: "JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID", + field: "workflow.topology.transitions", + meaning: "One or more topology transitions do not connect declared work units.", + expected: canonicalTransitions, + observed: + transitions.length === 0 + ? [] + : invalidTransitions.length > 0 + ? invalidTransitions + : transitions, + repair_instruction: + "Restore the ordered canonical transition contract so every required work unit is reachable from orient through artifact-local verification and recovery.", + }); } - if (!isPlainObject(candidate.handoff)) { - throw new JudgmentKitInputError("UI workflow candidate requires handoff."); - } + const recoveryWorkUnitId = "recover"; + const hasRecoveryEntry = transitions.some( + (transition) => + optionalString(transition.to) === recoveryWorkUnitId && + workUnitIds.includes(optionalString(transition.from)), + ); + const hasRecoveryExit = transitions.some( + (transition) => + optionalString(transition.from) === recoveryWorkUnitId && + workUnitIds.includes(optionalString(transition.to)), + ); - if (!isPlainObject(candidate.diagnostics)) { - throw new JudgmentKitInputError("UI workflow candidate requires diagnostics."); + if ( + topologyContract && + (!workUnitIds.includes(recoveryWorkUnitId) || + !hasRecoveryEntry || + !hasRecoveryExit) + ) { + push({ + code: "JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING", + field: "workflow.topology.transitions", + meaning: "The artifact workflow has no explicit connected recovery work unit.", + expected: + "Declared transitions enter and leave the recover work unit using valid work-unit ids.", + observed: transitions, + repair_instruction: + "Add valid inbound and outbound transitions for the recover work unit so cancellation or invalid work returns to a declared safe state.", + }); } -} -function buildUiWorkflowCandidateMissingFields(candidate) { - const workflow = candidate.workflow; - const handoff = candidate.handoff; - const completionState = optionalString(workflow.completion_state); - const handoffCompletion = - optionalString(handoff.next_action).length > 0 && - optionalString(handoff.reason).length > 0; - const workUnits = toStringArray(workflow.work_units); - const surfaceSet = toSurfaceSetArray(candidate.surface_set); - const hasSurfaceStructure = - surfaceSet.some((surface) => toStringArray(surface.sections).length > 0); + const workflowStateGroups = isPlainObject(workflow.state_groups) + ? workflow.state_groups + : null; + const canonicalStateGroups = isPlainObject( + getArtifactInspectorModel(contract)?.state_groups, + ) + ? getArtifactInspectorModel(contract).state_groups + : {}; + const canonicalStateGroupIds = Object.keys(canonicalStateGroups); + const activityActivatedStateGroups = deriveArtifactInspectorActiveStateGroups({ + source_input: sourceInput, + workflow: { + ...workflow, + state_groups: {}, + }, + activity_model: activityReview?.candidate?.activity_model, + interaction_contract: activityReview?.candidate?.interaction_contract, + }); + + if (!workflowStateGroups) { + push({ + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + meaning: "The artifact workflow does not declare its active state groups.", + expected: { core: canonicalStateGroups.core ?? [] }, + observed: workflow.state_groups, + repair_instruction: + "Declare the core state group and only the conditional state groups activated by the activity.", + }); + } else { + const observedGroupIds = Object.keys(workflowStateGroups); + const missingActivityGroups = activityActivatedStateGroups.filter( + (groupId) => !observedGroupIds.includes(groupId), + ); + const unexpectedGroupIds = observedGroupIds.filter( + (groupId) => !canonicalStateGroupIds.includes(groupId), + ); + const inventedConditionalGroupIds = observedGroupIds.filter( + (groupId) => !activityActivatedStateGroups.includes(groupId), + ); + if (!observedGroupIds.includes("core") || unexpectedGroupIds.length > 0) { + push({ + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + meaning: + "The artifact workflow must declare core and only canonical conditional state groups.", + expected: { + required: ["core"], + allowed: canonicalStateGroupIds, + }, + observed: observedGroupIds, + repair_instruction: + "Restore core and remove unknown state-group ids; add conditional groups only when the activity activates them.", + }); + } + if (missingActivityGroups.length > 0) { + push({ + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + meaning: + "The artifact workflow omits conditional state groups activated by the reviewed activity.", + expected: activityActivatedStateGroups, + observed: observedGroupIds, + repair_instruction: `Add the activity-required state groups from the registry: ${missingActivityGroups.join(", ")}.`, + }); + } + if (inventedConditionalGroupIds.length > 0) { + push({ + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + meaning: + "The artifact workflow invents conditional state groups that the reviewed activity does not activate.", + expected: activityActivatedStateGroups, + observed: observedGroupIds, + repair_instruction: `Remove inactive conditional state groups: ${inventedConditionalGroupIds.join(", ")}.`, + }); + } + for (const groupId of observedGroupIds.filter((id) => + canonicalStateGroupIds.includes(id), + )) { + const expectedStates = toStringArray(canonicalStateGroups[groupId]); + const observedStates = toStringArray(workflowStateGroups[groupId]); + if ( + observedStates.length !== expectedStates.length || + expectedStates.some((state) => !observedStates.includes(state)) + ) { + push({ + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: `workflow.state_groups.${groupId}`, + meaning: + "An active Artifact Inspector state group does not match the canonical registry definition.", + expected: expectedStates, + observed: observedStates, + repair_instruction: `Restore the canonical ${groupId} state set from the interaction-model registry.`, + }); + } + } + } return { - workflow_structure: workUnits.length === 0 && !hasSurfaceStructure, - primary_actions: toStringArray(workflow.primary_actions).length === 0, - decision_support: toStringArray(workflow.decision_points).length === 0, - surface_sections: !hasSurfaceStructure, - completion_or_handoff: - completionState.length === 0 && !handoffCompletion, + selected: true, + surface_type: identifiers.surface_type, + workflow_profile: identifiers.workflow_profile, + topology_kind: identifiers.topology_kind, + valid: diagnostics.length === 0, + diagnostics, }; } @@ -3392,7 +5366,18 @@ function buildUiWorkflowCandidateGuardrails( contract, activityReview, surfaceGuidance, + guidanceProfile, + sourceInput, ) { + const artifactInspector = buildArtifactInspectorWorkflowDiagnostics( + candidate, + contract, + guidanceProfile, + surfaceGuidance, + activityReview, + sourceInput, + ); + return { candidate_primary_terms_detected: buildUiWorkflowPrimaryTermsDetected( candidate, @@ -3407,6 +5392,7 @@ function buildUiWorkflowCandidateGuardrails( surfaceGuidance, contract, ), + ...(artifactInspector ? { artifact_inspector: artifactInspector } : {}), }; } @@ -3421,6 +5407,7 @@ function normalizeUiWorkflowCandidate( { contract, surfaceGuidance, + guidanceProfile, stepperEligibility, } = {}, ) { @@ -3449,11 +5436,42 @@ function normalizeUiWorkflowCandidate( candidate, candidatePrimaryTermsDetected, ); - const workUnits = sanitizeUiWorkflowList( - workflow.work_units, - candidatePrimaryTermsDetected, + const artifactInspectorSelected = isArtifactInspectorWorkflowSelected( + candidate, + contract, + guidanceProfile, + surfaceGuidance, ); - const topology = inferWorkflowTopology(candidate, surfaceGuidance, contract); + const workUnits = artifactInspectorSelected + ? cloneWorkflowStructuredValue(toWorkflowWorkUnitArray(workflow.work_units)) + : sanitizeUiWorkflowList( + workflow.work_units, + candidatePrimaryTermsDetected, + ); + const topology = artifactInspectorSelected + ? getArtifactInspectorIdentifiers(contract).topology_kind + : inferWorkflowTopology(candidate, surfaceGuidance, contract); + const topologyContract = artifactInspectorSelected + ? normalizeArtifactTopologyContract(candidate, contract, { force: true }) + : null; + const workflowArtifact = isPlainObject(workflow.artifact) + ? cloneWorkflowStructuredValue(workflow.artifact) + : null; + const workflowTargetModel = isPlainObject(workflow.target_model) + ? cloneWorkflowStructuredValue(workflow.target_model) + : null; + const workflowStateGroups = isPlainObject(workflow.state_groups) + ? cloneWorkflowStructuredValue(workflow.state_groups) + : null; + const topLevelArtifact = isPlainObject(candidate.artifact) + ? cloneWorkflowStructuredValue(candidate.artifact) + : null; + const topLevelTargetModel = isPlainObject(candidate.target_model) + ? cloneWorkflowStructuredValue(candidate.target_model) + : null; + const topLevelStateGroups = isPlainObject(candidate.state_groups) + ? cloneWorkflowStructuredValue(candidate.state_groups) + : null; return { workflow: { @@ -3463,7 +5481,11 @@ function normalizeUiWorkflowCandidate( "Workflow review", ), topology, + ...(topologyContract ? { topology_contract: topologyContract } : {}), work_units: workUnits, + ...(workflowArtifact ? { artifact: workflowArtifact } : {}), + ...(workflowTargetModel ? { target_model: workflowTargetModel } : {}), + ...(workflowStateGroups ? { state_groups: workflowStateGroups } : {}), stepper_eligibility: stepperEligibility, primary_actions: sanitizeUiWorkflowList( workflow.primary_actions, @@ -3479,6 +5501,9 @@ function normalizeUiWorkflowCandidate( activityCandidate?.interaction_contract?.completion ?? "", ), }, + ...(topLevelArtifact ? { artifact: topLevelArtifact } : {}), + ...(topLevelTargetModel ? { target_model: topLevelTargetModel } : {}), + ...(topLevelStateGroups ? { state_groups: topLevelStateGroups } : {}), surface_set: surfaceSet, product_terms: sanitizedUserTerms, handoff: { @@ -3550,6 +5575,11 @@ function buildUiWorkflowQuestions(activityReview, candidateGuardrails) { ); } + for (const diagnostic of + candidateGuardrails.artifact_inspector?.diagnostics ?? []) { + questions.push(diagnostic.repair_instruction); + } + return selectTargetedQuestionsFromCandidates(questions); } @@ -3596,6 +5626,7 @@ function buildUiWorkflowConfidence(activityReview, candidateGuardrails) { activityReview.review_status !== "ready_for_review" || hasUiWorkflowMissingField(candidateGuardrails.candidate_missing_fields) || candidateGuardrails.stepper_eligibility?.blocked || + candidateGuardrails.artifact_inspector?.valid === false || candidateGuardrails.candidate_primary_terms_detected.length > 0 || candidateGuardrails.candidate_primary_meta_terms_detected.length > 0 ) { @@ -3616,6 +5647,7 @@ function buildUiWorkflowReviewPacket( contract, guidanceProfile = null, surfaceReview = null, + sourceInput = "", ) { const sourceReady = activityReview.review_status === "ready_for_review"; const surfaceGuidance = summarizeSurfaceReview(surfaceReview); @@ -3624,10 +5656,28 @@ function buildUiWorkflowReviewPacket( contract, activityReview, surfaceGuidance, + guidanceProfile, + sourceInput, ); + const validatedArtifactInspector = + candidateGuardrails.artifact_inspector?.valid === true + ? candidateGuardrails.artifact_inspector + : null; + const resolvedSurfaceGuidance = + validatedArtifactInspector && + !optionalString(surfaceGuidance?.recommended_surface_type) + ? { + recommended_surface_type: validatedArtifactInspector.surface_type, + confidence: "validated_workflow", + blocked_surface_types: [], + ...buildSurfaceImplications(validatedArtifactInspector.surface_type), + } + : surfaceGuidance; const candidateReady = !hasUiWorkflowMissingField(candidateGuardrails.candidate_missing_fields) && !candidateGuardrails.stepper_eligibility?.blocked && + candidateGuardrails.artifact_inspector?.valid !== false && + resolvedSurfaceGuidance?.status !== "review_required" && candidateGuardrails.candidate_primary_terms_detected.length === 0 && candidateGuardrails.candidate_primary_meta_terms_detected.length === 0; const normalizedCandidate = normalizeUiWorkflowCandidate( @@ -3636,7 +5686,8 @@ function buildUiWorkflowReviewPacket( candidateGuardrails.candidate_primary_terms_detected, { contract, - surfaceGuidance, + surfaceGuidance: resolvedSurfaceGuidance, + guidanceProfile, stepperEligibility: candidateGuardrails.stepper_eligibility, }, ); @@ -3656,10 +5707,10 @@ function buildUiWorkflowReviewPacket( input_excerpt: activityReview.source?.input_excerpt, }, ...(guidanceProfile ? { guidance_profile: guidanceProfile } : {}), - ...(surfaceGuidance + ...(resolvedSurfaceGuidance ? { - surface_type: surfaceGuidance.recommended_surface_type, - surface_guidance: surfaceGuidance, + surface_type: resolvedSurfaceGuidance.recommended_surface_type, + surface_guidance: resolvedSurfaceGuidance, } : {}), activity_review: activityReview, @@ -3683,12 +5734,15 @@ function buildUiWorkflowReviewPacket( candidateGuardrails.candidate_primary_terms_detected, candidate_primary_meta_terms_detected: candidateGuardrails.candidate_primary_meta_terms_detected, + ...(candidateGuardrails.artifact_inspector + ? { artifact_inspector: candidateGuardrails.artifact_inspector } + : {}), }, assumptions: buildUiWorkflowReviewAssumptions( activityReview, source, guidanceProfile, - surfaceGuidance, + resolvedSurfaceGuidance, ), confidence: buildUiWorkflowConfidence(activityReview, candidateGuardrails), targeted_questions: buildUiWorkflowQuestions(activityReview, candidateGuardrails), @@ -3702,13 +5756,19 @@ function buildUiWorkflowReviewPacket( candidate_primary_meta_terms_detected: candidateGuardrails.candidate_primary_meta_terms_detected, stepper_eligibility: candidateGuardrails.stepper_eligibility, + ...(candidateGuardrails.artifact_inspector + ? { artifact_inspector: candidateGuardrails.artifact_inspector } + : {}), implementation_terms_detected: activityReview.guardrails?.implementation_terms_detected ?? [], disclosure_translation_candidates: activityReview.guardrails?.disclosure_translation_candidates ?? [], ...(guidanceProfile ? { guidance_profile_id: guidanceProfile.profile_id } : {}), - ...(surfaceGuidance - ? { recommended_surface_type: surfaceGuidance.recommended_surface_type } + ...(resolvedSurfaceGuidance + ? { + recommended_surface_type: + resolvedSurfaceGuidance.recommended_surface_type, + } : {}), }, }; @@ -3718,8 +5778,11 @@ function buildUiWorkflowReviewPacket( return packet; } -function buildUiWorkflowCandidateShapeGuide() { - return { +function buildUiWorkflowCandidateShapeGuide( + contract, + { artifactInspector = false } = {}, +) { + const guide = { workflow: { surface_name: "Short domain name for the workflow.", topology: @@ -3751,6 +5814,49 @@ function buildUiWorkflowCandidateShapeGuide() { reveal_contexts: ["Contexts where diagnostics may be shown."], }, }; + + if (!artifactInspector) { + return guide; + } + + const model = getArtifactInspectorModel(contract) ?? {}; + const identifiers = getArtifactInspectorIdentifiers(contract); + const topology = isPlainObject(model.topology) + ? cloneWorkflowStructuredValue(model.topology) + : { + kind: identifiers.topology_kind, + primary_object_id: "artifact", + entry_work_unit_id: "orient", + completion_work_unit_ids: ["verify_result"], + transitions: [], + }; + + guide.workflow.topology = topology; + guide.workflow.work_units = Array.isArray(model.work_units) + ? cloneWorkflowStructuredValue(model.work_units) + : [ + { + id: "orient", + participant_intent: "What the participant is trying to understand or do.", + system_responsibility: "What the UI must preserve or reveal.", + }, + ]; + guide.workflow.artifact = { + id: optionalString(topology.primary_object_id) || "artifact", + boundary: "The bounded rendered artifact that remains primary.", + }; + guide.workflow.target_model = isPlainObject(model.target_model) + ? cloneWorkflowStructuredValue(model.target_model) + : { + target_kinds: ["whole_artifact", "element", "text_range", "region", "point"], + }; + guide.workflow.state_groups = isPlainObject(model.state_groups) + ? { + core: cloneWorkflowStructuredValue(model.state_groups.core ?? []), + } + : {}; + + return guide; } function buildUiWorkflowReviewContext(activityReview) { @@ -3793,18 +5899,38 @@ export function buildUiWorkflowCandidateRequest({ ); } + const resolvedContract = contract ?? loadActivityContract(contractPath); const guidanceProfile = profileId === undefined || profileId === null ? null : resolveUiWorkflowGuidanceProfile( - contract ?? loadActivityContract(contractPath), + resolvedContract, profileId, ); + const surfaceGuidance = summarizeSurfaceReview(surfaceReview); + const artifactInspectorRequested = isArtifactInspectorWorkflowSelected( + { workflow: {} }, + resolvedContract, + guidanceProfile, + surfaceGuidance, + ) || Boolean( + activityReview.review?.evidence?.artifact_inspector?.mandatory_satisfied || + activityReview.guardrails?.artifact_inspector?.mandatory_satisfied, + ); const userPayload = { brief: brief.trim(), activity_review: buildUiWorkflowReviewContext(activityReview), - candidate_shape: buildUiWorkflowCandidateShapeGuide(), + candidate_shape: buildUiWorkflowCandidateShapeGuide(resolvedContract, { + artifactInspector: artifactInspectorRequested, + }), + ...(artifactInspectorRequested + ? { + artifact_inspector_state_group_catalog: + cloneWorkflowStructuredValue( + getArtifactInspectorModel(resolvedContract)?.state_groups ?? {}, + ), + } + : {}), }; - const surfaceGuidance = summarizeSurfaceReview(surfaceReview); if (guidanceProfile) { userPayload.guidance_profile = guidanceProfile; @@ -3822,6 +5948,9 @@ export function buildUiWorkflowCandidateRequest({ "You propose a reviewable JudgmentKit UI workflow candidate.", "Return only JSON whose root object matches the UI workflow candidate shape.", "Ground topology, work units, coordinated surfaces, actions, decisions, handoff, and user-facing terms in the source brief and activity review.", + artifactInspectorRequested + ? "Use the artifact_centered topology object, stable structured work-unit ids, one artifact boundary, a semantic target model, explicit recovery transitions, artifact-local completion, and only the conditional state groups the activity needs." + : "", "Do not use staged_flow or numbered wizard/stepper framing unless the source has strong staged-flow intent such as explicit wizard wording, ordered setup/onboarding, form validation sequence, or strict dependency order.", "Keep implementation terms and JudgmentKit review-packet terms out of workflow, surface_set, and handoff.", "Implementation terms may appear only in diagnostics when they are diagnostic.", @@ -3882,6 +6011,8 @@ export function createUiWorkflowProposer({ ); } + const resolvedContract = contract ?? loadActivityContract(contractPath); + return async function proposeUiWorkflowCandidate({ brief, activity_review: activityReview, @@ -3893,12 +6024,11 @@ export function createUiWorkflowProposer({ activity_review: activityReview, profile_id: requestProfileId ?? defaultProfileId, surface_review: surfaceReview, - contract, - contractPath, + contract: resolvedContract, }); const candidate = parseCandidateResponse(await callModel(request)); - assertUiWorkflowCandidateShape(candidate); + assertUiWorkflowCandidateShape(candidate, resolvedContract); return candidate; }; @@ -3914,11 +6044,11 @@ export function reviewUiWorkflowCandidate(input, candidate, options = {}) { ...analysisOptions } = options; - assertUiWorkflowCandidateShape(candidate); + const contract = analysisOptions.contract ?? loadActivityContract(analysisOptions.contractPath); + assertUiWorkflowCandidateShape(candidate, contract); const activityReview = providedActivityReview ?? createActivityModelReview(input, analysisOptions); - const contract = analysisOptions.contract ?? loadActivityContract(analysisOptions.contractPath); const guidanceProfile = resolveUiWorkflowGuidanceProfile(contract, profileId); const resolvedSurfaceType = providedSurfaceType ? resolveSurfaceType(contract, providedSurfaceType).surface_type @@ -3941,6 +6071,7 @@ export function reviewUiWorkflowCandidate(input, candidate, options = {}) { contract, guidanceProfile, surfaceReview, + input, ); } @@ -4360,6 +6491,96 @@ function buildCognitiveDimensionsReviewFindings({ optionalString(interactionContract.primary_decision), ]).filter(Boolean); const findings = []; + const artifactInspectorSelected = + surfaceType === getArtifactInspectorIdentifiers(contract).surface_type; + + if (artifactInspectorSelected) { + const hasSemanticLocus = /\b(?:semantic\s+)?(?:locus|selected (?:element|passage|range|region|node|point|control)|active (?:element|passage|range|region|node|point|control))\b/.test( + normalizedPrimary, + ); + const hasLocusRelativeContext = /\b(?:context|evidence|authority|action|feedback|reason|receipt)\b[^.]{0,140}\b(?:locus|selected|active|attached|anchored|beside|local)\b/.test( + normalizedPrimary, + ); + const hasPreviewCommitSeparation = + /\bpreview\b/.test(normalizedPrimary) && + /\b(?:commit|confirm|complete|result)\b/.test(normalizedPrimary); + const hasLocalEvaluation = /\b(?:validate|supported|incompatible|unavailable|stale|ambiguous|blocked|local (?:feedback|result|status))\b/.test( + normalizedPrimary, + ); + const hasBoundedRecovery = /\b(?:back|cancel|recover|retry|reconcile|last safe|uncommitted)\b/.test( + normalizedPrimary, + ); + + if (!hasSemanticLocus) { + findings.push(createCognitiveFinding({ + dimension: "closeness_of_mapping", + evidence: + "Artifact Inspector candidate does not name the semantic artifact locus that actions and context operate on.", + user_cost: + "The participant cannot tell which part of the artifact an action will inspect or change.", + repair_instruction: + "Name the selectable semantic locus and keep every action mapped to that active target.", + acceptance_check: + "The selected locus and the action that operates on it are legible together.", + })); + } + + if (!hasLocusRelativeContext) { + findings.push(createCognitiveFinding({ + dimension: "hidden_dependencies", + evidence: + "Artifact Inspector support is not visibly or semantically attached to the active locus.", + user_cost: + "The participant must remember which evidence, authority, or reason applies to the selected target.", + repair_instruction: + "Attach decision-changing context, authority, feedback, and receipts to the active semantic locus.", + acceptance_check: + "Every support object exposes its relation to the active locus without relying on memory.", + })); + } + + if (!hasPreviewCommitSeparation) { + findings.push(createCognitiveFinding({ + dimension: "premature_commitment", + evidence: + "Artifact Inspector candidate does not distinguish reversible preview from commitment or durable result.", + user_cost: + "A participant can mistake provisional state for a committed outcome or commit before validation.", + repair_instruction: + "Declare separate preview, local validation, explicit commitment when consequential, and durable result states.", + acceptance_check: + "Preview and committed result remain visibly and semantically distinct with no default affirmative.", + })); + } + + if (!hasLocalEvaluation) { + findings.push(createCognitiveFinding({ + dimension: "progressive_evaluation", + evidence: + "Artifact Inspector candidate lacks locus-local supported, invalid, unavailable, stale, ambiguous, or blocked evaluation state.", + user_cost: + "The participant cannot evaluate the relation or action before completing it.", + repair_instruction: + "Show validation and failure meaning on or immediately beside the active locus before completion.", + acceptance_check: + "The participant can evaluate a preview locally before commitment or bounded completion.", + })); + } + + if (!hasBoundedRecovery) { + findings.push(createCognitiveFinding({ + dimension: "viscosity", + evidence: + "Artifact Inspector candidate lacks Back, cancellation, retry, or reconciliation behavior tied to uncommitted state.", + user_cost: + "Recovering from a wrong target or invalid preview may discard committed work or require restarting the inspection.", + repair_instruction: + "Add a recovery path that reverses only uncommitted state and preserves committed results.", + acceptance_check: + "Back and cancellation return to the last safe state without erasing committed results.", + })); + } + } if (implementationTermsDetected.length > 0 && !setupDiagnosticContext) { findings.push(createCognitiveFinding({ @@ -6567,6 +8788,33 @@ function normalizeDefaultAiNativeDesignSystem(sourcePolicy, fallbackPolicy) { }; } +function canonicalJudgmentKitDesignSystemAuthority() { + const implementationContract = getContractUiImplementationContract( + loadActivityContract(), + ); + const defaultDesignSystem = normalizeDefaultAiNativeDesignSystem( + implementationContract.default_ai_native_design_system, + DEFAULT_AI_NATIVE_DESIGN_SYSTEM, + ); + const visualTokenAdapter = normalizeVisualTokenAdapter( + implementationContract.visual_token_adapter, + DEFAULT_VISUAL_TOKEN_ADAPTER, + ); + const designSystemSource = normalizeDesignSystemSource( + implementationContract.design_system_source, + { + visualTokenAdapter, + componentContracts: defaultDesignSystem.component_contracts, + }, + ); + + return { + defaultDesignSystem, + visualTokenAdapter, + designSystemSource, + }; +} + function firstDefined(...values) { return values.find((value) => value !== undefined && value !== null); } @@ -8251,13 +10499,235 @@ function normalizeLocalComponentAuthority(sourcePolicy, fallbackPolicy) { }; } -function localComponentAuthorityIsActive(authority) { - const policy = normalizeLocalComponentAuthority( - authority, - DEFAULT_LOCAL_COMPONENT_AUTHORITY, +function localComponentAuthorityIsActive(authority) { + const policy = normalizeLocalComponentAuthority( + authority, + DEFAULT_LOCAL_COMPONENT_AUTHORITY, + ); + + return policy.mode === "repo_local" || policy.enforcement === "required"; +} + +const ARTIFACT_INSPECTOR_STATE_GROUP_IDS = [ + "core", + "consequential", + "reusable_guidance", + "automation", +]; + +function deriveArtifactInspectorActiveStateGroups(source = {}) { + const explicit = toStringArray( + source.artifact_inspector?.active_state_groups ?? + source.artifactInspector?.activeStateGroups, + ); + + if (explicit.length > 0) { + return ARTIFACT_INSPECTOR_STATE_GROUP_IDS.filter((groupId) => + explicit.includes(groupId), + ); + } + + const declaredStateGroups = isPlainObject(source.workflow?.state_groups) + ? source.workflow.state_groups + : isPlainObject(source.state_groups) + ? source.state_groups + : {}; + const activityText = normalizeText( + collectStringValues({ + source_input: source.source_input, + workflow: source.workflow, + activity_model: source.activity_model, + interaction_contract: source.interaction_contract, + }).join(" "), + ); + const active = new Set(["core"]); + + for (const groupId of ARTIFACT_INSPECTOR_STATE_GROUP_IDS.slice(1)) { + if ( + Array.isArray(declaredStateGroups[groupId]) && + declaredStateGroups[groupId].length > 0 + ) { + active.add(groupId); + } + } + + if ( + /\b(?:consequential|commit|approve|reject|publish|external effect|outcome pending|committing|commit succeeded|known failure|unknown result|receipt reconciliation|immutable receipt|concurrent result|superseding action)\b/.test( + activityText, + ) + ) { + active.add("consequential"); + } + + if ( + /\b(?:reusable guidance|later repeats?|future cases?|accepted version|accepting guidance|supersession|revocation)\b/.test( + activityText, + ) + ) { + active.add("reusable_guidance"); + } + + if ( + /\b(?:automation|automatic resolution|match preflight|exact match|near or partial match|inline causal receipt|exception returned to human review)\b/.test( + activityText, + ) + ) { + active.add("automation"); + } + + return ARTIFACT_INSPECTOR_STATE_GROUP_IDS.filter((groupId) => + active.has(groupId), + ); +} + +function artifactInspectorStateGroupsMatch(left, right) { + return ( + Array.isArray(left) && + Array.isArray(right) && + left.length === right.length && + left.every((groupId, index) => groupId === right[index]) ); +} - return policy.mode === "repo_local" || policy.enforcement === "required"; +function artifactInspectorImplementationFields(source, contract) { + const identifiers = getArtifactInspectorIdentifiers(contract); + const requestedSurfaceType = normalizeOptionalSurfaceType( + source.surface_type ?? source.surfaceType, + ); + const suppliedScopes = + source.design_system_scopes ?? source.designSystemScopes; + const suppliedBoundaries = + source.boundary_contracts ?? source.boundaryContracts; + const suppliedArtifactInspector = + source.artifact_inspector ?? source.artifactInspector; + const hasAnyBundleField = + suppliedScopes !== undefined || + suppliedBoundaries !== undefined || + suppliedArtifactInspector !== undefined; + const model = getArtifactInspectorModel(contract); + + if (requestedSurfaceType === identifiers.surface_type && !model) { + throw new JudgmentKitInputError( + "The active contract does not support the requested Artifact Inspector profile.", + { + code: "artifact_inspector_profile_unsupported", + details: { + surface_type: identifiers.surface_type, + workflow_profile: identifiers.workflow_profile, + frontend_surface_profile: identifiers.frontend_surface_profile, + fallback_applied: false, + }, + }, + ); + } + + if (requestedSurfaceType) { + resolveSurfaceType(contract, requestedSurfaceType); + } + + if ( + requestedSurfaceType && + requestedSurfaceType !== identifiers.surface_type && + hasAnyBundleField + ) { + throw new JudgmentKitInputError( + "Artifact Inspector authority fields cannot be attached to a different surface type.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + selected_surface_type: requestedSurfaceType, + required_surface_type: identifiers.surface_type, + }, + }, + ); + } + + const activate = requestedSurfaceType === identifiers.surface_type; + if (!activate && !hasAnyBundleField) { + return {}; + } + + if (!model) { + throw new JudgmentKitInputError( + "The active contract does not support Artifact Inspector authority.", + { + code: "artifact_inspector_profile_unsupported", + details: { + surface_type: identifiers.surface_type, + frontend_surface_profile: identifiers.frontend_surface_profile, + }, + }, + ); + } + + const canonicalArtifactInspector = { + registry_id: optionalString(model.id) || identifiers.surface_type, + registry_version: optionalString(model.version) || "1.0.0", + surface_type: identifiers.surface_type, + workflow_profile: identifiers.workflow_profile, + frontend_surface_profile: identifiers.frontend_surface_profile, + topology_kind: identifiers.topology_kind, + active_state_groups: deriveArtifactInspectorActiveStateGroups(source), + state_groups: cloneWorkflowStructuredValue(model.state_groups ?? {}), + trusted_runtime_evidence_required: true, + external_artifact_review_status: "external_not_reviewed", + }; + const candidate = { + ...(activate + ? { + design_system_scopes: + suppliedScopes ?? + cloneWorkflowStructuredValue(model.design_system?.scopes ?? []), + boundary_contracts: + suppliedBoundaries ?? + cloneWorkflowStructuredValue( + model.design_system?.boundary_contracts ?? [], + ), + artifact_inspector: isPlainObject(suppliedArtifactInspector) + ? { + ...canonicalArtifactInspector, + ...cloneWorkflowStructuredValue(suppliedArtifactInspector), + state_groups: + suppliedArtifactInspector.state_groups ?? + canonicalArtifactInspector.state_groups, + } + : canonicalArtifactInspector, + } + : { + ...(suppliedScopes !== undefined + ? { design_system_scopes: suppliedScopes } + : {}), + ...(suppliedBoundaries !== undefined + ? { boundary_contracts: suppliedBoundaries } + : {}), + ...(suppliedArtifactInspector !== undefined + ? { artifact_inspector: suppliedArtifactInspector } + : {}), + }), + }; + + try { + const normalized = validateArtifactInspectorAuthorityContract(candidate); + return normalized.active + ? { + design_system_scopes: normalized.design_system_scopes, + boundary_contracts: normalized.boundary_contracts, + artifact_inspector: normalized.artifact_inspector, + } + : {}; + } catch (error) { + throw new JudgmentKitInputError( + error instanceof Error + ? error.message + : "Artifact Inspector authority contract is invalid.", + { + code: + optionalString(error?.code) || + "invalid_artifact_inspector_authority_contract", + details: isPlainObject(error?.details) ? error.details : {}, + }, + ); + } } function normalizeUiImplementationContract(input = {}, options = {}) { @@ -8315,6 +10785,64 @@ function normalizeUiImplementationContract(input = {}, options = {}) { visualCompositionPolicyAuthority, designSystemAdapter, } = resolveImplementationDesignSystem(source, base); + const artifactInspectorFields = artifactInspectorImplementationFields( + source, + contract, + ); + const artifactInspectorComponentRoleIds = new Set( + toStringArray( + getArtifactInspectorModel(contract)?.design_system?.component_roles, + ), + ); + const artifactInspectorPatternId = + optionalString( + contract.ui_workflow_profiles?.[ + getArtifactInspectorIdentifiers(contract).workflow_profile + ]?.pattern_id, + ) || "artifact-inspector"; + const artifactInspectorActive = Boolean( + artifactInspectorFields.artifact_inspector, + ); + const canonicalArtifactInspectorDesignSystem = artifactInspectorActive + ? canonicalJudgmentKitDesignSystemAuthority() + : null; + const effectiveDefaultDesignSystem = + canonicalArtifactInspectorDesignSystem?.defaultDesignSystem ?? + defaultDesignSystem; + const scopedDefaultDesignSystem = { + ...effectiveDefaultDesignSystem, + component_contracts: normalizeComponentContracts( + effectiveDefaultDesignSystem.component_contracts, + ).filter( + (entry) => + artifactInspectorActive || + !artifactInspectorComponentRoleIds.has(optionalString(entry.id)), + ), + pattern_contracts: normalizePatternContracts( + effectiveDefaultDesignSystem.pattern_contracts, + ).filter( + (entry) => + artifactInspectorActive || + optionalString(entry.id) !== artifactInspectorPatternId, + ), + }; + if ( + artifactInspectorFields.artifact_inspector && + designSystemSource.mode !== "judgmentkit_default" + ) { + throw new JudgmentKitInputError( + "Artifact Inspector requires JudgmentKit authority for its chrome and overlay scopes; external visual authority belongs only to the declared primary_artifact scope.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_source.mode", + expected: "judgmentkit_default", + observed: designSystemSource.mode, + external_artifact_scope: "primary_artifact", + }, + }, + ); + } return { id: optionalString(source.id) || optionalString(base.id) || "judgmentkit.ui-implementation-contract.portable", @@ -8350,12 +10878,15 @@ function normalizeUiImplementationContract(input = {}, options = {}) { : true, checks: browserQaChecks, }, - default_ai_native_design_system: defaultDesignSystem, + default_ai_native_design_system: scopedDefaultDesignSystem, iteration_policy: normalizeIterationPolicy( source.iteration_policy ?? source.iterationPolicy, base.iteration_policy, ), - design_system_source: designSystemSource, + design_system_source: + canonicalArtifactInspectorDesignSystem?.designSystemSource ?? + designSystemSource, + ...artifactInspectorFields, ...(designSystemAdapter ? { design_system_adapter: designSystemAdapter } : {}), @@ -8375,7 +10906,9 @@ function normalizeUiImplementationContract(input = {}, options = {}) { source.defaultAiNativeDesignSystem?.localComponentAuthority, base.local_component_authority, ), - visual_token_adapter: visualTokenAdapter, + visual_token_adapter: + canonicalArtifactInspectorDesignSystem?.visualTokenAdapter ?? + visualTokenAdapter, visual_asset_policy: normalizeVisualAssetPolicy( source.visual_asset_policy ?? source.visualAssetPolicy, base.visual_asset_policy, @@ -8449,6 +10982,13 @@ export function createUiImplementationContract(input = {}, options = {}) { mode: implementationContractSource(input ?? {}), repo_name: optionalString(input?.repo_name), target_stack: optionalString(input?.target_stack), + ...(normalizeOptionalSurfaceType(input?.surface_type ?? input?.surfaceType) + ? { + surface_type: normalizeOptionalSurfaceType( + input?.surface_type ?? input?.surfaceType, + ), + } + : {}), external_authority: optionalString( input?.external_authority ?? input?.external_system_name, ), @@ -8494,6 +11034,20 @@ export function createUiImplementationContract(input = {}, options = {}) { : []), ], }, + ...(normalized.artifact_inspector + ? [ + { + id: "artifact_inspector_authority_gate", + status: "trusted_runtime_evidence_required", + checks: [ + "JudgmentKit chrome scope", + "JudgmentKit inspection overlay scope", + "external artifact preservation", + "declared cross-boundary interaction", + ], + }, + ] + : []), ], }; @@ -8598,6 +11152,166 @@ function candidateImplementationSourceText(value) { return unique(collectSourceEntries(value)).join("\n"); } +const ARTIFACT_INSPECTOR_EXTERNAL_SOURCE_KEYS = new Set([ + "artifact", + "artifactMarkup", + "artifactSource", + "artifact_markup", + "artifact_source", + "externalArtifact", + "external_artifact", + "primaryArtifact", + "primary_artifact", +]); + +const HTML_VOID_ELEMENT_NAMES = new Set([ + "area", + "base", + "br", + "col", + "embed", + "hr", + "img", + "input", + "link", + "meta", + "param", + "source", + "track", + "wbr", +]); + +function omitDeclaredExternalArtifactSources(value, depth = 0) { + if (!value || depth > 5) { + return value; + } + if (Array.isArray(value)) { + return value.map((entry) => + omitDeclaredExternalArtifactSources(entry, depth + 1), + ); + } + if (!isPlainObject(value)) { + return value; + } + return Object.fromEntries( + Object.entries(value) + .filter(([key]) => !ARTIFACT_INSPECTOR_EXTERNAL_SOURCE_KEYS.has(key)) + .map(([key, entry]) => [ + key, + omitDeclaredExternalArtifactSources(entry, depth + 1), + ]), + ); +} + +function matchingElementEnd(source, tagName, openingEnd) { + const tagPattern = /<\s*(\/?)\s*([A-Za-z][\w:.-]*)\b[^>]*?>/g; + const normalizedTagName = tagName.toLowerCase(); + let depth = 1; + tagPattern.lastIndex = openingEnd; + let match; + while ((match = tagPattern.exec(source)) !== null) { + if (match[2].toLowerCase() !== normalizedTagName) { + continue; + } + const closing = match[1] === "/"; + const selfClosing = /\/\s*>$/.test(match[0]); + if (closing) { + depth -= 1; + if (depth === 0) { + return tagPattern.lastIndex; + } + } else if (!selfClosing) { + depth += 1; + } + } + return null; +} + +function stripBoundedPrimaryArtifactMarkup(value) { + const source = optionalString(value); + if (!source || !/\bdata-artifact-root\b/i.test(source)) { + return source; + } + const tagPattern = /<\s*(\/?)\s*([A-Za-z][\w:.-]*)\b[^>]*?>/g; + const kept = []; + let cursor = 0; + let match; + while ((match = tagPattern.exec(source)) !== null) { + if ( + match[1] === "/" || + !/\bdata-artifact-root\b/i.test(match[0]) + ) { + continue; + } + const tagName = match[2]; + const selfClosing = + /\/\s*>$/.test(match[0]) || + HTML_VOID_ELEMENT_NAMES.has(tagName.toLowerCase()); + const elementEnd = selfClosing + ? tagPattern.lastIndex + : matchingElementEnd(source, tagName, tagPattern.lastIndex); + if (elementEnd === null) { + continue; + } + kept.push(source.slice(cursor, match.index)); + cursor = elementEnd; + tagPattern.lastIndex = elementEnd; + } + kept.push(source.slice(cursor)); + return kept.join("\n"); +} + +function stripPrimaryArtifactScopedCss(value) { + const source = optionalString(value); + if (!source || !/\bdata-artifact-root\b/i.test(source)) { + return source; + } + return source.replace( + /(]*>)([\s\S]*?)(<\/style\s*>)/gi, + (_styleBlock, opening, cssText, closing) => { + const filteredCss = cssText.replace( + /([^{}]+)\{([^{}]*)\}/g, + (rule, selectorText) => { + const selectors = selectorText + .split(",") + .map((selector) => selector.trim()) + .filter(Boolean); + return selectors.length > 0 && + selectors.every((selector) => { + const artifactRootMatch = + /\[data-artifact-root(?:\s|\]|=)/i.exec(selector); + if (!artifactRootMatch) { + return false; + } + const prefix = selector.slice(0, artifactRootMatch.index); + const rootAppearsInsideFunctionalPseudo = + prefix.lastIndexOf("(") > prefix.lastIndexOf(")"); + return ( + !rootAppearsInsideFunctionalPseudo && + !/[+~]/.test(selector) && + !/:(?:has|is|not|where)\s*\(/i.test(selector) + ); + }) + ? "" + : rule; + }, + ); + return `${opening}${filteredCss}${closing}`; + }, + ); +} + +function artifactInspectorOwnedImplementationText(candidate) { + const candidateWithoutExternalSources = omitDeclaredExternalArtifactSources( + candidate, + ); + return stripPrimaryArtifactScopedCss( + stripBoundedPrimaryArtifactMarkup( + candidateImplementationSourceText(candidateWithoutExternalSources), + ), + ); +} + function normalizeCandidateList(candidate, keys) { if (!isPlainObject(candidate)) { return []; @@ -12557,6 +15271,11 @@ function reviewEvidenceFieldMapping( "token family, font role, icon role, and catalog icon-id boundary proof -> visual_token_evidence", "visual-token, typography, icon asset, renderer component, import, package, and source-export proof -> design_system_provenance", "repo-local component selector and computed-style proof -> local_component_authority_evidence", + ...(sourceContract.artifact_inspector + ? [ + "trusted owned-scope, artifact-preservation, and boundary proof -> checks.artifact_inspector (server runtime only; no candidate-authored acceptance field)", + ] + : []), ], primitives_used: { field: "primitives_used", @@ -12673,6 +15392,20 @@ function reviewEvidenceFieldMapping( not_for: "Do not use local_component_authority_evidence as component contract evidence, approved primitive evidence, accessibility evidence, static evidence, or browser QA evidence.", }, + ...(sourceContract.artifact_inspector + ? { + artifact_inspector_authority_evidence: { + field: null, + reviewed_by: "checks.artifact_inspector", + source_path: + "implementation_contract.design_system_scopes + implementation_contract.boundary_contracts + implementation_contract.artifact_inspector", + accepts: + "Only a JudgmentKit trusted browser-runtime receipt that separates inspector_chrome, inspection_overlay, primary_artifact preservation, and boundary results.", + candidate_authored_evidence_accepted: false, + external_artifact_review_status: "external_not_reviewed", + }, + } + : {}), ...(sourceContract.visual_composition_policy ? { visual_composition_evidence: { @@ -12777,7 +15510,10 @@ function candidateVisualCompositionEvidence(candidate) { ); } -function candidateTextWithoutVisualCompositionEvidence(candidate) { +function candidateTextWithoutVisualCompositionEvidence( + candidate, + implementationContract, +) { if (!isPlainObject(candidate)) return candidateText(candidate); const { visual_composition_manifest: _manifest, @@ -12795,10 +15531,15 @@ function candidateTextWithoutVisualCompositionEvidence(candidate) { delete browserQa.visual_composition_manifest; delete browserQa.visualCompositionManifest; } - return candidateText({ + const candidateWithoutVisualCompositionEvidence = { ...rest, ...(browserQa ? { browser_qa: browserQa } : {}), - }); + }; + return implementationContract?.artifact_inspector + ? artifactInspectorOwnedImplementationText( + candidateWithoutVisualCompositionEvidence, + ) + : candidateText(candidateWithoutVisualCompositionEvidence); } function visualCompositionManifestSamples(manifest) { @@ -13908,7 +16649,10 @@ function buildImplementationCandidateChecks( implementationContract, { selectedSurfaceType } = {}, ) { - const text = candidateTextWithoutVisualCompositionEvidence(candidate); + const text = candidateTextWithoutVisualCompositionEvidence( + candidate, + implementationContract, + ); const rawControls = detectRawControls(text); const primitivesUsed = normalizeCandidateList(candidate, [ "primitives_used", @@ -13929,7 +16673,23 @@ function buildImplementationCandidateChecks( "states_verified", "state_coverage", ]); - const requiredStates = implementationContract.state_coverage.required_states; + const artifactInspectorStateConfig = isPlainObject( + implementationContract.artifact_inspector, + ) + ? implementationContract.artifact_inspector + : null; + const activeArtifactInspectorStateGroups = artifactInspectorStateConfig + ? toStringArray(artifactInspectorStateConfig.active_state_groups) + : []; + const artifactInspectorRequiredStates = artifactInspectorStateConfig + ? activeArtifactInspectorStateGroups.flatMap((groupId) => + toStringArray(artifactInspectorStateConfig.state_groups?.[groupId]), + ) + : []; + const requiredStates = unique([ + ...implementationContract.state_coverage.required_states, + ...artifactInspectorRequiredStates, + ]); const missingStates = requiredStates.filter((state) => !statesCovered.includes(state)); const staticChecks = normalizeCandidateList(candidate, [ "static_checks", @@ -14208,12 +16968,20 @@ function findingContractArea(check) { return "visual_composition"; } + if (String(check).startsWith("artifact_inspector")) { + return "artifact_inspector_authority"; + } + return "evidence_gates"; } function repairInstructionForFinding(finding, implementationContract) { const check = optionalString(finding.check); + if (optionalString(finding.repair_instruction)) { + return optionalString(finding.repair_instruction); + } + if (check === "raw_controls") { return "Replace raw form controls with approved primitives or repo-local helpers before resubmitting."; } @@ -14346,7 +17114,11 @@ function buildRepairInstructions(findings, implementationContract) { }; } -function buildAutofixLoop(failed, iterationContext, iterationPolicy) { +function buildAutofixLoop( + { failed, reviewRequired }, + iterationContext, + iterationPolicy, +) { const currentAttempt = iterationContext.current_attempt; const maxAttempts = iterationContext.max_attempts; const remainingAttempts = Math.max(0, maxAttempts - currentAttempt); @@ -14354,10 +17126,14 @@ function buildAutofixLoop(failed, iterationContext, iterationPolicy) { return { owner: iterationPolicy.owner, - status: failed ? (stopForHuman ? "stopped" : "repairable") : "passed", + status: reviewRequired + ? "awaiting_runtime_review" + : failed + ? (stopForHuman ? "stopped" : "repairable") + : "passed", current_attempt: currentAttempt, max_attempts: maxAttempts, - remaining_attempts: failed ? remainingAttempts : maxAttempts - currentAttempt, + remaining_attempts: remainingAttempts, loop: iterationPolicy.loop, judgmentkit_role: iterationPolicy.judgmentkit_role, }; @@ -14397,31 +17173,96 @@ export function reviewUiImplementationCandidate(candidate, options = {}) { implementationContract, { selectedSurfaceType }, ); - const designSystemFailed = designSystemGateFailed(checks); - const failed = checks.findings.some((finding) => finding.severity === "fail"); + const artifactInspectorReview = reviewArtifactInspectorAuthorityEvidence( + implementationContract, + { + trustedRuntimeEvidence: + getTrustedVisualCompositionEvidence(candidate) + ?.artifact_inspector_authority ?? null, + }, + ); + if ( + selectedSurfaceType === getArtifactInspectorIdentifiers(contract).surface_type && + !artifactInspectorReview.applicable + ) { + throw new JudgmentKitInputError( + "Artifact Inspector implementation review requires the scoped authority contract returned by create_ui_implementation_contract.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + selected_surface_type: selectedSurfaceType, + required_fields: [ + "design_system_scopes", + "boundary_contracts", + "artifact_inspector", + ], + }, + }, + ); + } + const artifactInspectorApplicable = artifactInspectorReview.applicable; + const artifactInspectorFailed = artifactInspectorReview.status === "fail"; + const artifactInspectorReviewRequired = + artifactInspectorReview.status === "review_required"; + const combinedFindings = artifactInspectorApplicable + ? [...checks.findings, ...artifactInspectorReview.findings] + : checks.findings; + const designSystemFailed = + designSystemGateFailed(checks) || artifactInspectorFailed; + const failed = + checks.findings.some((finding) => finding.severity === "fail") || + artifactInspectorFailed; + const reviewRequired = !failed && artifactInspectorReviewRequired; + const unresolved = failed || reviewRequired; const iterationPolicy = implementationContract.iteration_policy; const iterationContext = normalizeIterationContext( options.iteration_context ?? options.iterationContext, iterationPolicy, ); - const autofixLoop = buildAutofixLoop(failed, iterationContext, iterationPolicy); - const nextAgentAction = failed + const autofixLoop = buildAutofixLoop( + { failed, reviewRequired }, + iterationContext, + iterationPolicy, + ); + const nextAgentAction = reviewRequired + ? "run_trusted_runtime_review" + : failed ? autofixLoop.status === "stopped" ? "stop_for_human" : "repair_and_resubmit" : "accept"; + const implementationReviewStatus = failed + ? "failed" + : reviewRequired + ? "review_required" + : artifactInspectorApplicable + ? "passed_with_external_artifact_authority" + : "passed"; + const designSystemAcceptanceStatus = designSystemFailed + ? "failed" + : reviewRequired + ? "review_required" + : artifactInspectorApplicable + ? "passed_with_external_artifact_authority" + : "passed"; const packet = { version: contract.version, contract_id: contract.id, workflow_id: getContractWorkflowId(contract), - implementation_review_status: failed ? "failed" : "passed", - candidate_artifact_status: failed ? "not_an_artifact" : "accepted_artifact", - design_system_acceptance_status: designSystemFailed ? "failed" : "passed", + implementation_review_status: implementationReviewStatus, + candidate_artifact_status: failed + ? "not_an_artifact" + : reviewRequired + ? "awaiting_runtime_review" + : artifactInspectorApplicable + ? "accepted_artifact_with_external_authority" + : "accepted_artifact", + design_system_acceptance_status: designSystemAcceptanceStatus, implementation_contract_id: implementationContract.id, next_agent_action: nextAgentAction, autofix_loop: autofixLoop, repair_instructions: buildRepairInstructions( - failed ? checks.findings : [], + failed ? combinedFindings : [], implementationContract, ), generation_gates: [ @@ -14433,16 +17274,26 @@ export function reviewUiImplementationCandidate(candidate, options = {}) { }, { id: "implementation_gate", - status: failed ? "failed" : "passed", + status: failed ? "failed" : reviewRequired ? "review_required" : "passed", requirement: "Generated UI must use approved primitives, respect local component authority, and provide static, browser QA, and applicable rendered visual composition evidence.", }, { id: "design_system_gate", - status: designSystemFailed ? "failed" : "passed", + status: designSystemAcceptanceStatus, requirement: "Generated UI must prove active design-system provenance for visual tokens, typography, icon assets, renderer components, component contracts, and pattern contracts before it can count as an artifact.", }, + ...(artifactInspectorApplicable + ? [ + { + id: "artifact_inspector_authority_gate", + status: artifactInspectorReview.status, + requirement: + "Review JudgmentKit chrome, inspection overlay, external artifact preservation, and the declared boundary separately with trusted runtime evidence.", + }, + ] + : []), ], checks: { raw_controls: checks.raw_controls, @@ -14460,8 +17311,11 @@ export function reviewUiImplementationCandidate(candidate, options = {}) { component_contracts: checks.component_contracts, pattern_contracts: checks.pattern_contracts, visual_composition: checks.visual_composition, + ...(artifactInspectorApplicable + ? { artifact_inspector: artifactInspectorReview } + : {}), }, - findings: checks.findings, + findings: combinedFindings, implementation_contract: implementationContract, }; @@ -14599,6 +17453,15 @@ function compactImplementationContractForHandoff(implementationContract) { implementationContract.default_ai_native_design_system, iteration_policy: implementationContract.iteration_policy, design_system_source: implementationContract.design_system_source, + ...(implementationContract.design_system_scopes + ? { design_system_scopes: implementationContract.design_system_scopes } + : {}), + ...(implementationContract.boundary_contracts + ? { boundary_contracts: implementationContract.boundary_contracts } + : {}), + ...(implementationContract.artifact_inspector + ? { artifact_inspector: implementationContract.artifact_inspector } + : {}), ...(implementationContract.design_system_adapter ? { design_system_adapter: implementationContract.design_system_adapter } : {}), @@ -14664,12 +17527,131 @@ export function createUiGenerationHandoff(workflowReview, options = {}) { const activityCandidate = workflowReview.activity_review.candidate; const workflowCandidate = workflowReview.candidate; const contract = options.contract ?? loadActivityContract(options.contractPath); + const identifiers = getArtifactInspectorIdentifiers(contract); + const workflowSelectsArtifactInspector = isArtifactTopologyCandidate( + workflowCandidate, + contract, + ); + const declaredSurfaceType = normalizeOptionalSurfaceType( + workflowReview.surface_type, + ); + const guidanceSurfaceType = normalizeOptionalSurfaceType( + workflowReview.surface_guidance?.recommended_surface_type, + ); + if ( + declaredSurfaceType && + guidanceSurfaceType && + declaredSurfaceType !== guidanceSurfaceType + ) { + throw new JudgmentKitInputError( + "UI generation handoff received conflicting reviewed surface fields.", + { + code: "handoff_blocked", + details: { + surface_type: declaredSurfaceType, + surface_guidance_type: guidanceSurfaceType, + }, + }, + ); + } + const reviewedSurfaceType = declaredSurfaceType ?? guidanceSurfaceType; + if ( + workflowSelectsArtifactInspector && + reviewedSurfaceType && + reviewedSurfaceType !== identifiers.surface_type + ) { + throw new JudgmentKitInputError( + "UI generation handoff cannot route an artifact-centered workflow to a conflicting surface.", + { + code: "handoff_blocked", + details: { + expected_surface_type: identifiers.surface_type, + observed_surface_type: reviewedSurfaceType, + workflow_topology: identifiers.topology_kind, + }, + }, + ); + } + const handoffSurfaceType = workflowSelectsArtifactInspector + ? identifiers.surface_type + : reviewedSurfaceType; + const duplicateWorkUnitIds = duplicateWorkflowWorkUnitIds( + workflowCandidate.workflow?.work_units, + ); + if ( + handoffSurfaceType === identifiers.surface_type && + duplicateWorkUnitIds.length > 0 + ) { + throw new JudgmentKitInputError( + "Artifact Inspector handoff requires unique work-unit identifiers.", + { + code: "handoff_blocked", + details: { + field: "workflow.work_units", + duplicate_work_unit_ids: duplicateWorkUnitIds, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + }, + }, + ); + } + const implementationContractInput = + options.implementation_contract ?? + options.ui_implementation_contract ?? + contract.implementation_contract; + if ( + workflowSelectsArtifactInspector && + !isPlainObject(implementationContractInput) + ) { + throw new JudgmentKitInputError( + "Artifact Inspector handoff requires an object implementation contract so scoped authority cannot be omitted.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "implementation_contract", + observed_type: Array.isArray(implementationContractInput) + ? "array" + : typeof implementationContractInput, + }, + }, + ); + } + const artifactInspectorActiveStateGroups = + deriveArtifactInspectorActiveStateGroups({ + workflow: workflowCandidate.workflow, + activity_model: activityCandidate.activity_model, + interaction_contract: activityCandidate.interaction_contract, + }); + const handoffActiveStateGroups = ARTIFACT_INSPECTOR_STATE_GROUP_IDS.filter( + (groupId) => artifactInspectorActiveStateGroups.includes(groupId), + ); const implementationContract = normalizeUiImplementationContract( - options.implementation_contract ?? - options.ui_implementation_contract ?? - contract.implementation_contract, + handoffSurfaceType === identifiers.surface_type + ? { + ...implementationContractInput, + surface_type: handoffSurfaceType, + artifact_inspector: { + ...(isPlainObject(implementationContractInput.artifact_inspector) + ? implementationContractInput.artifact_inspector + : {}), + active_state_groups: handoffActiveStateGroups, + }, + } + : implementationContractInput, { contract }, ); + assertArtifactInspectorBoundaryConsistency({ + workflow: workflowCandidate.workflow, + surfaceTypes: [handoffSurfaceType], + profileIds: [workflowReview.guidance_profile?.profile_id], + implementationContract, + activityModel: activityCandidate.activity_model, + interactionContract: activityCandidate.interaction_contract, + contract, + code: "handoff_blocked", + message: + "UI generation handoff cannot accept conflicting Artifact Inspector surface, topology, profile, or authority signals.", + }); const workflowSurfaceSet = toSurfaceSetArray(workflowCandidate.surface_set); const handoffSurfaceGuidance = summarizeSurfaceReview( workflowReview.surface_guidance, @@ -14694,7 +17676,7 @@ export function createUiGenerationHandoff(workflowReview, options = {}) { ...(workflowReview.guidance_profile ? { guidance_profile: workflowReview.guidance_profile } : {}), - ...(workflowReview.surface_type ? { surface_type: workflowReview.surface_type } : {}), + ...(handoffSurfaceType ? { surface_type: handoffSurfaceType } : {}), ...(handoffSurfaceGuidance ? { surface_guidance: handoffSurfaceGuidance } : {}), @@ -14718,7 +17700,42 @@ export function createUiGenerationHandoff(workflowReview, options = {}) { workflow: { surface_name: optionalString(workflowCandidate.workflow.surface_name), topology: optionalString(workflowCandidate.workflow.topology), - work_units: toStringArray(workflowCandidate.workflow.work_units), + ...(isPlainObject(workflowCandidate.workflow.topology_contract) + ? { + topology_contract: cloneWorkflowStructuredValue( + workflowCandidate.workflow.topology_contract, + ), + } + : {}), + work_units: cloneWorkflowStructuredValue( + toWorkflowWorkUnitArray(workflowCandidate.workflow.work_units), + ), + ...((isPlainObject(workflowCandidate.workflow.artifact) || + isPlainObject(workflowCandidate.artifact)) + ? { + artifact: cloneWorkflowStructuredValue( + workflowCandidate.workflow.artifact ?? workflowCandidate.artifact, + ), + } + : {}), + ...((isPlainObject(workflowCandidate.workflow.target_model) || + isPlainObject(workflowCandidate.target_model)) + ? { + target_model: cloneWorkflowStructuredValue( + workflowCandidate.workflow.target_model ?? + workflowCandidate.target_model, + ), + } + : {}), + ...((isPlainObject(workflowCandidate.workflow.state_groups) || + isPlainObject(workflowCandidate.state_groups)) + ? { + state_groups: cloneWorkflowStructuredValue( + workflowCandidate.workflow.state_groups ?? + workflowCandidate.state_groups, + ), + } + : {}), stepper_eligibility: workflowCandidate.workflow.stepper_eligibility ?? workflowReview.guardrails?.stepper_eligibility, primary_actions: toStringArray(workflowCandidate.workflow.primary_actions), @@ -14792,6 +17809,39 @@ export function createUiGenerationHandoff(workflowReview, options = {}) { ], implementation_contract: compactImplementationContractForHandoff(implementationContract), + ...(implementationContract.artifact_inspector + ? { + artifact_inspector: { + registry: cloneWorkflowStructuredValue( + implementationContract.artifact_inspector, + ), + artifact: cloneWorkflowStructuredValue( + workflowCandidate.workflow.artifact ?? + workflowCandidate.artifact ?? + {}, + ), + target_model: cloneWorkflowStructuredValue( + workflowCandidate.workflow.target_model ?? + workflowCandidate.target_model ?? + {}, + ), + state_groups: cloneWorkflowStructuredValue( + workflowCandidate.workflow.state_groups ?? + workflowCandidate.state_groups ?? + {}, + ), + design_system_scopes: cloneWorkflowStructuredValue( + implementationContract.design_system_scopes, + ), + boundary_contracts: cloneWorkflowStructuredValue( + implementationContract.boundary_contracts, + ), + responsive_posture: + handoffSurfaceGuidance?.frontend_posture?.responsive_expectations ?? + null, + }, + } + : {}), ...(isPlainObject(cognitiveDimensionsReview) ? { cognitive_dimensions_review: { @@ -14811,6 +17861,19 @@ export function createUiGenerationHandoff(workflowReview, options = {}) { } function textFromHandoff(handoff) { + const workflowWorkUnitText = toWorkflowWorkUnitArray( + handoff.workflow?.work_units, + ).flatMap((workUnit) => + typeof workUnit === "string" + ? [workUnit] + : [ + workUnit.id, + workUnit.label, + workUnit.participant_intent, + workUnit.system_responsibility, + ], + ); + return [ handoff.activity_model?.activity, handoff.activity_model?.objective, @@ -14822,7 +17885,7 @@ function textFromHandoff(handoff) { handoff.interaction_contract?.completion, handoff.workflow?.surface_name, handoff.workflow?.topology, - ...(toStringArray(handoff.workflow?.work_units)), + ...workflowWorkUnitText, ...(toStringArray(handoff.workflow?.primary_actions)), ...(toStringArray(handoff.workflow?.decision_points)), handoff.workflow?.completion_state, @@ -14939,9 +18002,21 @@ function hasCompleteExternalImplementationAuthority({ ) .map((contract) => normalizeText(contract.id)) .filter(Boolean); + const governedSurfaceComponentIds = listSurfacePresentationProfiles() + .flatMap((profile) => + Array.isArray(profile.component_roles) ? profile.component_roles : [], + ) + .map((component) => normalizeText(component?.id)) + .filter(Boolean); + const canonicalJudgmentKitComponentIds = unique([ + ...defaultComponentIds, + ...governedSurfaceComponentIds, + ]); const usesOnlyDefaultComponentContracts = - externalComponentIds.length === defaultComponentIds.length && - externalComponentIds.every((id) => defaultComponentIds.includes(id)); + externalComponentIds.length > 0 && + externalComponentIds.every((id) => + canonicalJudgmentKitComponentIds.includes(id), + ); const rendererComponentIds = toStringArray( designSystemSource.renderer_components, ) @@ -15347,6 +18422,7 @@ export function createFrontendGenerationContext({ surface_review: surfaceReview, surface_type: surfaceType, surface_profile: surfaceProfile, + supported_surface_profiles: supportedSurfaceProfiles, frontend_context: frontendContext, verification, contract, @@ -15372,6 +18448,14 @@ export function createFrontendGenerationContext({ } const resolvedContract = contract ?? loadActivityContract(contractPath); + const artifactIdentifiers = getArtifactInspectorIdentifiers(resolvedContract); + const handoffArtifactTopologySelected = isArtifactTopologyCandidate( + uiGenerationHandoff, + resolvedContract, + ); + const topologySurfaceType = handoffArtifactTopologySelected + ? artifactIdentifiers.surface_type + : null; const providedSurfaceType = normalizeOptionalSurfaceType(surfaceType); const handoffSurfaceType = normalizeOptionalSurfaceType( uiGenerationHandoff.surface_type, @@ -15387,7 +18471,11 @@ export function createFrontendGenerationContext({ ? normalizeOptionalSurfaceType(surfaceReview.recommended_surface_type) : null; const selectedSurfaceType = - providedSurfaceType ?? handoffSurfaceType ?? reviewedSurfaceType; + providedSurfaceType ?? + handoffSurfaceType ?? + handoffGuidanceSurfaceType ?? + reviewedSurfaceType ?? + topologySurfaceType; const surfaceTypeCandidates = [ ["surface_type", providedSurfaceType], ["ui_generation_handoff.surface_type", handoffSurfaceType], @@ -15396,6 +18484,7 @@ export function createFrontendGenerationContext({ handoffGuidanceSurfaceType, ], ["surface_review.recommended_surface_type", reviewedSurfaceType], + ["ui_generation_handoff.workflow.topology", topologySurfaceType], ].filter(([, value]) => value); const conflictingSurfaceTypes = surfaceTypeCandidates.filter( ([, value]) => value !== selectedSurfaceType, @@ -15413,6 +18502,24 @@ export function createFrontendGenerationContext({ ); } + if ( + selectedSurfaceType === artifactIdentifiers.surface_type && + !handoffArtifactTopologySelected + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context requires artifact-centered workflow topology.", + { + code: "frontend_context_blocked", + details: { + surface_type: selectedSurfaceType, + expected_topology: artifactIdentifiers.topology_kind, + observed_topology: + optionalString(uiGenerationHandoff.workflow?.topology) || null, + }, + }, + ); + } + const inferredSurfaceReview = isPlainObject(surfaceReview) ? surfaceReview : handoffSurfaceGuidance @@ -15443,23 +18550,88 @@ export function createFrontendGenerationContext({ const surfaceGuidance = summarizeSurfaceReview(inferredSurfaceReview, { includeFrontendPosture: true, }); + const artifactInspectorSelected = + handoffArtifactTopologySelected || + surfaceGuidance.recommended_surface_type === artifactIdentifiers.surface_type; + const serializedImplementationContract = isPlainObject( + uiGenerationHandoff.implementation_contract, + ) + ? uiGenerationHandoff.implementation_contract + : {}; + const serializedDesignSystemMode = optionalString( + serializedImplementationContract.design_system_source?.mode, + ); + if ( + artifactInspectorSelected && + serializedDesignSystemMode && + serializedDesignSystemMode !== "judgmentkit_default" + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context requires JudgmentKit authority for inspector chrome and the inspection overlay.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_source.mode", + expected: "judgmentkit_default", + observed: serializedDesignSystemMode, + external_artifact_scope: "primary_artifact", + }, + }, + ); + } + let effectiveImplementationContract = serializedImplementationContract; + if (artifactInspectorSelected) { + const canonicalDesignSystem = + canonicalJudgmentKitDesignSystemAuthority(); + const serializedWithoutDesignSystemAuthority = { + ...serializedImplementationContract, + }; + delete serializedWithoutDesignSystemAuthority.design_system_adapter; + delete serializedWithoutDesignSystemAuthority.designSystemAdapter; + delete serializedWithoutDesignSystemAuthority.default_ai_native_design_system; + delete serializedWithoutDesignSystemAuthority.defaultAiNativeDesignSystem; + delete serializedWithoutDesignSystemAuthority.visual_token_adapter; + delete serializedWithoutDesignSystemAuthority.visualTokenAdapter; + delete serializedWithoutDesignSystemAuthority.design_system_source; + delete serializedWithoutDesignSystemAuthority.designSystemSource; + effectiveImplementationContract = { + ...serializedWithoutDesignSystemAuthority, + default_ai_native_design_system: + canonicalDesignSystem.defaultDesignSystem, + visual_token_adapter: canonicalDesignSystem.visualTokenAdapter, + design_system_source: canonicalDesignSystem.designSystemSource, + }; + } + assertArtifactInspectorBoundaryConsistency({ + workflow: uiGenerationHandoff.workflow, + surfaceTypes: [surfaceGuidance.recommended_surface_type], + profileIds: [uiGenerationHandoff.guidance_profile?.profile_id], + implementationContract: effectiveImplementationContract, + additionalAuthoritySignals: [uiGenerationHandoff.artifact_inspector], + activityModel: uiGenerationHandoff.activity_model, + interactionContract: uiGenerationHandoff.interaction_contract, + contract: resolvedContract, + code: "frontend_context_blocked", + message: + "Frontend generation context cannot accept conflicting Artifact Inspector surface, topology, profile, or authority signals.", + }); const normalizedFrontendContext = normalizeFrontendContext(frontendContext); const normalizedVerification = normalizeVerificationContext(verification); const requiredSurfaces = toSurfaceSetArray(uiGenerationHandoff.surface_set); const requiredSurfaceAggregate = aggregateSurfaceSet(requiredSurfaces); const designSystemContract = normalizeDefaultAiNativeDesignSystem( - uiGenerationHandoff.implementation_contract?.default_ai_native_design_system, + effectiveImplementationContract.default_ai_native_design_system, DEFAULT_AI_NATIVE_DESIGN_SYSTEM, ); const localComponentAuthority = normalizeLocalComponentAuthority( - uiGenerationHandoff.implementation_contract?.local_component_authority, + effectiveImplementationContract.local_component_authority, DEFAULT_LOCAL_COMPONENT_AUTHORITY, ); const visualCompositionPolicy = - uiGenerationHandoff.implementation_contract?.visual_composition_policy ?? + effectiveImplementationContract.visual_composition_policy ?? null; const evidenceFieldMapping = reviewEvidenceFieldMapping( - uiGenerationHandoff.implementation_contract, + effectiveImplementationContract, designSystemContract, { selectedSurfaceType: surfaceGuidance.recommended_surface_type }, ); @@ -15480,8 +18652,132 @@ export function createFrontendGenerationContext({ ? "surface_review" : "inferred_from_handoff"; const designSystemSource = - uiGenerationHandoff.implementation_contract?.design_system_source ?? + effectiveImplementationContract.design_system_source ?? DEFAULT_DESIGN_SYSTEM_SOURCE; + const supportedProfileIds = supportedSurfaceProfiles === undefined + ? listSurfacePresentationProfiles().map((entry) => entry.id) + : toStringArray(supportedSurfaceProfiles); + if ( + supportedSurfaceProfiles !== undefined && + (!Array.isArray(supportedSurfaceProfiles) || + supportedProfileIds.length !== supportedSurfaceProfiles.length) + ) { + throw new JudgmentKitInputError( + "supported_surface_profiles must be an array of non-empty canonical profile ids when provided.", + ); + } + const frontendDuplicateWorkUnitIds = duplicateWorkflowWorkUnitIds( + uiGenerationHandoff.workflow?.work_units, + ); + if ( + artifactInspectorSelected && + frontendDuplicateWorkUnitIds.length > 0 + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context requires unique work-unit identifiers.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "workflow.work_units", + duplicate_work_unit_ids: frontendDuplicateWorkUnitIds, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + }, + }, + ); + } + if ( + artifactInspectorSelected && + !supportedProfileIds.includes(artifactIdentifiers.frontend_surface_profile) + ) { + throw new JudgmentKitInputError( + "The caller cannot consume the selected Artifact Inspector frontend profile.", + { + code: "artifact_inspector_profile_unsupported", + details: { + surface_type: artifactIdentifiers.surface_type, + required_surface_profile: + artifactIdentifiers.frontend_surface_profile, + supported_surface_profiles: supportedProfileIds, + fallback_applied: false, + }, + }, + ); + } + let artifactInspectorAuthority = null; + if (artifactInspectorSelected) { + try { + artifactInspectorAuthority = validateArtifactInspectorAuthorityContract( + effectiveImplementationContract, + ); + } catch (error) { + throw new JudgmentKitInputError( + error instanceof Error + ? error.message + : "Artifact Inspector authority contract is invalid.", + { + code: "invalid_artifact_inspector_authority_contract", + details: isPlainObject(error?.details) ? error.details : {}, + }, + ); + } + if (!artifactInspectorAuthority.active) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context requires scoped authority fields from the implementation contract.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + required_fields: [ + "design_system_scopes", + "boundary_contracts", + "artifact_inspector", + ], + }, + }, + ); + } + const reviewedActiveStateGroups = deriveArtifactInspectorActiveStateGroups({ + workflow: uiGenerationHandoff.workflow, + activity_model: uiGenerationHandoff.activity_model, + interaction_contract: uiGenerationHandoff.interaction_contract, + }); + if ( + !artifactInspectorStateGroupsMatch( + artifactInspectorAuthority.artifact_inspector.active_state_groups, + reviewedActiveStateGroups, + ) + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context cannot activate state groups absent from the reviewed workflow.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "artifact_inspector.active_state_groups", + expected: reviewedActiveStateGroups, + observed: + artifactInspectorAuthority.artifact_inspector + .active_state_groups, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }, + }, + ); + } + if (optionalString(designSystemSource.mode) !== "judgmentkit_default") { + throw new JudgmentKitInputError( + "Artifact Inspector frontend context requires JudgmentKit authority for inspector chrome and the inspection overlay.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_source.mode", + expected: "judgmentkit_default", + observed: optionalString(designSystemSource.mode) || null, + external_artifact_scope: "primary_artifact", + }, + }, + ); + } + } const normalizedSurfaceProfileRequest = normalizeSurfaceProfileRequest(surfaceProfile); const selectedSurfaceProfile = resolveSurfacePresentationProfile({ @@ -15504,6 +18800,7 @@ export function createFrontendGenerationContext({ handoff_status: uiGenerationHandoff.handoff_status, surface_type_source: surfaceTypeSource, surface_profile_request: normalizedSurfaceProfileRequest, + supported_surface_profiles: supportedProfileIds, }, surface_type: surfaceGuidance.recommended_surface_type, surface_guidance: surfaceGuidance, @@ -15516,7 +18813,7 @@ export function createFrontendGenerationContext({ surface_set: requiredSurfaces, product_terms: toStringArray(uiGenerationHandoff.product_terms), handoff: uiGenerationHandoff.handoff, - implementation_contract: uiGenerationHandoff.implementation_contract, + implementation_contract: effectiveImplementationContract, disclosure_reminders: uiGenerationHandoff.disclosure_reminders, frontend_context: { target_runtime: optionalString(normalizedFrontendContext.target_runtime), @@ -15540,7 +18837,7 @@ export function createFrontendGenerationContext({ interaction_implications: surfaceGuidance.interaction_implications, disclosure_implications: surfaceGuidance.disclosure_implications, frontend_posture: surfaceGuidance.frontend_posture, - implementation_contract: uiGenerationHandoff.implementation_contract, + implementation_contract: effectiveImplementationContract, design_system_source: designSystemSource, local_component_authority: localComponentAuthority, @@ -15548,14 +18845,47 @@ export function createFrontendGenerationContext({ ? { visual_composition_policy: visualCompositionPolicy } : {}), visual_asset_policy: - uiGenerationHandoff.implementation_contract?.visual_asset_policy ?? + effectiveImplementationContract.visual_asset_policy ?? DEFAULT_VISUAL_ASSET_POLICY, accessibility_policy: - uiGenerationHandoff.implementation_contract?.accessibility_policy ?? + effectiveImplementationContract.accessibility_policy ?? DEFAULT_ACCESSIBILITY_POLICY, evidence_field_mapping: evidenceFieldMapping, component_contracts: designSystemContract.component_contracts, pattern_contracts: designSystemContract.pattern_contracts, + ...(artifactInspectorAuthority?.active + ? { + artifact_inspector: { + registry: cloneWorkflowStructuredValue( + artifactInspectorAuthority.artifact_inspector, + ), + artifact: cloneWorkflowStructuredValue( + isPlainObject(uiGenerationHandoff.workflow?.artifact) + ? uiGenerationHandoff.workflow.artifact + : {}, + ), + target_model: cloneWorkflowStructuredValue( + isPlainObject(uiGenerationHandoff.workflow?.target_model) + ? uiGenerationHandoff.workflow.target_model + : {}, + ), + state_groups: cloneWorkflowStructuredValue( + isPlainObject(uiGenerationHandoff.workflow?.state_groups) + ? uiGenerationHandoff.workflow.state_groups + : {}, + ), + active_state_groups: + artifactInspectorAuthority.artifact_inspector + .active_state_groups, + design_system_scopes: + artifactInspectorAuthority.design_system_scopes, + boundary_contracts: + artifactInspectorAuthority.boundary_contracts, + external_artifact_review_status: + "external_not_reviewed", + }, + } + : {}), required_surfaces: requiredSurfaces, required_sections: requiredSurfaceAggregate.sections, required_controls: requiredSurfaceAggregate.controls, @@ -15568,6 +18898,14 @@ export function createFrontendGenerationContext({ "Render declared visual-composition relationships at required viewports after fonts are ready, measure DOM geometry under the active policy, and attach candidate-scoped visual_composition_evidence.", ] : []), + ...(artifactInspectorAuthority?.active + ? [ + "Verify JudgmentKit chrome and inspection overlay scopes independently from the external artifact.", + "Verify artifact render fingerprints remain stable across chrome-only transitions.", + "Verify native artifact actions retain precedence until explicit inspection mode is active.", + "Verify semantic target re-anchoring, stale-target handling, overlay occlusion, focus return, and cross-boundary status announcements.", + ] + : []), ]), states_to_verify: toStringArray(normalizedVerification.states_to_verify), }, @@ -15581,8 +18919,20 @@ export function createFrontendGenerationContext({ diagnostic_contexts: uiGenerationHandoff.disclosure_reminders?.diagnostic_contexts ?? [], approved_primitives: - uiGenerationHandoff.implementation_contract?.approved_primitives ?? [], + effectiveImplementationContract.approved_primitives ?? [], design_system_source: designSystemSource, + ...(artifactInspectorAuthority?.active + ? { + artifact_inspector_authority: { + design_system_scopes: + artifactInspectorAuthority.design_system_scopes, + boundary_contracts: + artifactInspectorAuthority.boundary_contracts, + external_artifact_review_status: "external_not_reviewed", + trusted_runtime_evidence_required: true, + }, + } + : {}), ...(visualCompositionPolicy ? { visual_composition_policy: visualCompositionPolicy } : {}), @@ -15599,8 +18949,16 @@ function buildFrontendImplementationInstructionMarkdown({ selectedSurfaceProfile, targetClient, evidenceFieldMapping, + artifactInspectorGuidance: trustedArtifactInspectorGuidance, }) { const implementationGuidance = frontendGenerationContext.implementation_guidance ?? {}; + const artifactInspectorGuidance = isPlainObject( + trustedArtifactInspectorGuidance, + ) + ? trustedArtifactInspectorGuidance + : isPlainObject(implementationGuidance.artifact_inspector) + ? implementationGuidance.artifact_inspector + : null; const reviewEvidenceMapping = evidenceFieldMapping ?? implementationGuidance.evidence_field_mapping ?? {}; const primitiveEvidenceMapping = reviewEvidenceMapping.primitives_used ?? {}; @@ -15619,6 +18977,15 @@ function buildFrontendImplementationInstructionMarkdown({ const verification = implementationGuidance.verification_expectations ?? {}; const frontendContext = frontendGenerationContext.frontend_context ?? {}; const workflow = frontendGenerationContext.workflow ?? {}; + const workflowWorkUnitLabels = toWorkflowWorkUnitArray(workflow.work_units) + .map((workUnit) => + typeof workUnit === "string" + ? workUnit + : optionalString( + workUnit.label ?? workUnit.participant_intent ?? workUnit.id, + ), + ) + .filter(Boolean); const requiredSurfaces = toSurfaceSetArray(implementationGuidance.required_surfaces); const visualAssetPolicy = implementationGuidance.visual_asset_policy ?? DEFAULT_VISUAL_ASSET_POLICY; @@ -15673,7 +19040,7 @@ function buildFrontendImplementationInstructionMarkdown({ "- Shape the interface around the selected surface type and surface set before choosing section layout.", ...(selectedSurfaceProfile ? [ - "- Apply the selected surface presentation profile after the activity and Workbench pattern contract are satisfied; it does not replace runtime truth or the active design-system source.", + "- Apply the selected surface presentation profile after the activity and matching pattern contract are satisfied; it does not replace runtime truth or the active design-system source.", ] : []), "- Use numbered wizard or stepper UI only when workflow.stepper_eligibility.allowed is true.", @@ -15696,6 +19063,13 @@ function buildFrontendImplementationInstructionMarkdown({ "- Repair fail or review outcomes, rerender, and replace the receipt before resubmitting. Keep browser_owned_indicator_unmeasured as the policy-declared non-blocking warning for native select arrows.", ] : []), + ...(artifactInspectorGuidance + ? [ + "- Keep the rendered artifact primary and preserve its external visual authority; apply JudgmentKit tokens and component conformance only inside inspector_chrome and inspection_overlay.", + "- Use declared semantic locus identifiers for every overlay-to-artifact event, preserve native live-artifact actions until explicit inspection mode, and re-anchor to the same target or mark it stale.", + "- Treat candidate-authored scope, preservation, or boundary claims as non-accepting context. Final acceptance requires a trusted browser-runtime receipt and must keep the primary artifact external_not_reviewed.", + ] + : []), "- Use JudgmentKit design-system exports by default; when external_design_system is active, do not mix in JudgmentKit default assets unless the external adapter explicitly names them.", "- Verify core accessibility evidence for semantics, landmarks/headings, name/role/value, keyboard navigation, focus order, focus-visible, responsive reflow/no-overflow, and automated checks.", "- Add conditional accessibility evidence for visuals, custom widgets, forms, status messages, overlays, motion, media, dense controls, and hover/focus content when those patterns appear.", @@ -15726,7 +19100,7 @@ function buildFrontendImplementationInstructionMarkdown({ "", "## Required Surface", `- Topology: ${workflow.topology || "workspace"}`, - `- Work units: ${toStringArray(workflow.work_units).join("; ") || "none supplied"}`, + `- Work units: ${workflowWorkUnitLabels.join("; ") || "none supplied"}`, `- Stepper eligibility: ${workflow.stepper_eligibility?.allowed ? "allowed" : "not allowed"}`, `- Surfaces: ${requiredSurfaces.map((surface) => `${surface.name}: ${surface.purpose}`).join("; ") || "none supplied"}`, `- Sections: ${toStringArray(implementationGuidance.required_sections).join("; ") || "none supplied"}`, @@ -15920,6 +19294,11 @@ export function createFrontendImplementationSkillContext({ implementationGuidance.visual_composition_policy ?? implementationContract.visual_composition_policy ?? null; + let artifactInspectorGuidance = isPlainObject( + implementationGuidance.artifact_inspector, + ) + ? cloneWorkflowStructuredValue(implementationGuidance.artifact_inspector) + : null; let visualTokenAdapter = normalizeVisualTokenAdapter( implementationContract.visual_token_adapter ?? {}, DEFAULT_VISUAL_TOKEN_ADAPTER, @@ -15939,6 +19318,193 @@ export function createFrontendImplementationSkillContext({ const hasInlineDesignSystemAdapter = hasDesignGuidanceValue( normalizedDesignSystemAdapter, ); + const frontendSurfaceTypeCandidates = [ + optionalString(frontendGenerationContext.surface_type), + optionalString(frontendGenerationContext.surface_guidance?.recommended_surface_type), + optionalString(implementationGuidance.surface_type), + ].filter(Boolean); + const skillArtifactConsistency = assertArtifactInspectorBoundaryConsistency({ + workflow, + surfaceTypes: frontendSurfaceTypeCandidates, + profileIds: [ + frontendGenerationContext.guidance_profile?.profile_id, + implementationGuidance.guidance_profile_id, + ], + implementationContract, + additionalAuthoritySignals: [artifactInspectorGuidance], + activityModel: frontendGenerationContext.activity_model, + interactionContract: frontendGenerationContext.interaction_contract, + code: "invalid_artifact_inspector_authority_contract", + message: + "Frontend implementation skill context cannot accept conflicting Artifact Inspector surface, topology, profile, or authority signals.", + }); + const artifactInspectorContextActive = + skillArtifactConsistency.artifactInspectorActive; + + if (artifactInspectorContextActive) { + const frontendDuplicateWorkUnitIds = duplicateWorkflowWorkUnitIds( + workflow.work_units, + ); + if (frontendDuplicateWorkUnitIds.length > 0) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context requires unique work-unit identifiers.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "workflow.work_units", + duplicate_work_unit_ids: frontendDuplicateWorkUnitIds, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + }, + }, + ); + } + let serializedArtifactInspectorAuthority; + try { + serializedArtifactInspectorAuthority = + validateArtifactInspectorAuthorityContract(implementationContract); + } catch (error) { + throw new JudgmentKitInputError( + error instanceof Error + ? error.message + : "Artifact Inspector frontend skill authority is invalid.", + { + code: "invalid_artifact_inspector_authority_contract", + details: isPlainObject(error?.details) ? error.details : {}, + }, + ); + } + if (!serializedArtifactInspectorAuthority.active) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context requires the scoped authority bundle.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + required_fields: [ + "design_system_scopes", + "boundary_contracts", + "artifact_inspector", + ], + }, + }, + ); + } + const reviewedActiveStateGroups = deriveArtifactInspectorActiveStateGroups({ + workflow: frontendGenerationContext.workflow, + activity_model: frontendGenerationContext.activity_model, + interaction_contract: frontendGenerationContext.interaction_contract, + }); + const declaredStateGroupSelections = [ + serializedArtifactInspectorAuthority.artifact_inspector + .active_state_groups, + ...(Array.isArray(artifactInspectorGuidance?.active_state_groups) + ? [artifactInspectorGuidance.active_state_groups] + : []), + ...(Array.isArray( + artifactInspectorGuidance?.registry?.active_state_groups, + ) + ? [artifactInspectorGuidance.registry.active_state_groups] + : []), + ]; + if ( + declaredStateGroupSelections.some( + (selection) => + !artifactInspectorStateGroupsMatch( + selection, + reviewedActiveStateGroups, + ), + ) + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context cannot activate state groups absent from the reviewed workflow.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "artifact_inspector.active_state_groups", + expected: reviewedActiveStateGroups, + observed: declaredStateGroupSelections, + diagnostic_code: + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + }, + }, + ); + } + artifactInspectorGuidance = { + registry: cloneWorkflowStructuredValue( + serializedArtifactInspectorAuthority.artifact_inspector, + ), + artifact: cloneWorkflowStructuredValue( + isPlainObject(workflow.artifact) ? workflow.artifact : {}, + ), + target_model: cloneWorkflowStructuredValue( + isPlainObject(workflow.target_model) ? workflow.target_model : {}, + ), + state_groups: cloneWorkflowStructuredValue( + isPlainObject(workflow.state_groups) ? workflow.state_groups : {}, + ), + active_state_groups: reviewedActiveStateGroups, + design_system_scopes: cloneWorkflowStructuredValue( + serializedArtifactInspectorAuthority.design_system_scopes, + ), + boundary_contracts: cloneWorkflowStructuredValue( + serializedArtifactInspectorAuthority.boundary_contracts, + ), + external_artifact_review_status: "external_not_reviewed", + }; + if (optionalString(designSystemSource.mode) !== "judgmentkit_default") { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context requires JudgmentKit authority for inspector chrome and the inspection overlay.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_source.mode", + expected: "judgmentkit_default", + observed: optionalString(designSystemSource.mode) || null, + external_artifact_scope: "primary_artifact", + }, + }, + ); + } + const canonicalDesignSystem = canonicalJudgmentKitDesignSystemAuthority(); + visualTokenAdapter = canonicalDesignSystem.visualTokenAdapter; + designSystemContract = canonicalDesignSystem.defaultDesignSystem; + designSystemSource = canonicalDesignSystem.designSystemSource; + } + + if ( + artifactInspectorContextActive && + optionalString(designSystemSource.mode) !== "judgmentkit_default" + ) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context requires JudgmentKit authority for inspector chrome and the inspection overlay.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_source.mode", + expected: "judgmentkit_default", + observed: optionalString(designSystemSource.mode) || null, + external_artifact_scope: "primary_artifact", + }, + }, + ); + } + + if (artifactInspectorContextActive && hasInlineDesignSystemAdapter) { + throw new JudgmentKitInputError( + "Artifact Inspector frontend skill context cannot replace JudgmentKit authority for inspector chrome or the inspection overlay with a whole-context design-system adapter.", + { + code: "invalid_artifact_inspector_authority_contract", + details: { + field: "design_system_adapter", + judgmentkit_owned_scopes: [ + "inspector_chrome", + "inspection_overlay", + ], + external_artifact_scope: "primary_artifact", + }, + }, + ); + } if ( designSystemSource.mode === "external_design_system" && @@ -15993,7 +19559,10 @@ export function createFrontendImplementationSkillContext({ } if (designSystemSource.mode === "judgmentkit_default") { - if (selectedSurfaceProfileCandidate.id !== WORKBENCH_SURFACE_PROFILE_ID) { + const supportedCanonicalProfile = cloneSurfacePresentationProfile( + selectedSurfaceProfileCandidate.id, + ); + if (!supportedCanonicalProfile) { throw new JudgmentKitInputError( "selected_surface_profile must name a supported canonical profile id.", { @@ -16021,7 +19590,7 @@ export function createFrontendImplementationSkillContext({ const candidateSelection = selectedSurfaceProfileCandidate.selection; if ( - !["auto", WORKBENCH_SURFACE_PROFILE_ID].includes(recordedRequest) || + !["auto", supportedCanonicalProfile.id].includes(recordedRequest) || !isPlainObject(candidateSelection) || candidateSelection.request !== recordedRequest || candidateSelection.surface_type_source !== recordedSurfaceTypeSource || @@ -16136,6 +19705,14 @@ export function createFrontendImplementationSkillContext({ "When local component authority is active, scan component-specific selectors for visual identity declarations and direct --jk-* token use.", ] : []), + ...(artifactInspectorGuidance + ? [ + "Verify inspector_chrome and inspection_overlay against JudgmentKit separately from the external artifact.", + "Verify the external artifact render fingerprint is unchanged across chrome-only transitions.", + "Verify native-action precedence, semantic target re-anchoring, overlay occlusion, focus return, and cross-boundary announcements with trusted runtime evidence.", + "Keep primary_artifact external_not_reviewed even when owned scopes pass.", + ] + : []), "Verify substantive visuals use imagegen or premium Three.js/WebGL/D3-style rendering when the spec calls for them.", "Provide browser-rendered visual-background contrast evidence for text over images, canvas, WebGL, video, gradients, or generated visuals.", "Verify non-text contrast for meaningful icons, charts, state indicators, custom controls, and authored focus indicators.", @@ -16180,13 +19757,14 @@ export function createFrontendImplementationSkillContext({ selectedSurfaceProfile, targetClient: normalizedTargetClient, evidenceFieldMapping, + artifactInspectorGuidance, }), implementation_sequence: [ "Confirm the activity, primary decision, workflow topology, work units, coordinated surfaces, and handoff from the ready frontend context.", "Map the selected surface type to the surface set, required sections, controls, density, navigation, and responsive expectations.", ...(selectedSurfaceProfile ? [ - "Apply selected_surface_profile as supported presentation guidance after satisfying the activity, Workbench pattern, and active design-system source contracts.", + "Apply selected_surface_profile as supported presentation guidance after satisfying the activity, matching pattern, and active design-system source contracts.", ] : []), "Use numbered wizard or stepper UI only when workflow.stepper_eligibility.allowed is true.", @@ -16209,6 +19787,12 @@ export function createFrontendImplementationSkillContext({ "On a visual composition fail or review outcome, repair only the declared relationship, rerender, replace the receipt, and resubmit before acceptance.", ] : []), + ...(artifactInspectorGuidance + ? [ + "Keep the artifact dominant; attach context, action, feedback, and results to the active semantic locus without restyling the artifact.", + "Apply JudgmentKit design-system conformance only to inspector_chrome and inspection_overlay, preserve primary_artifact as external_not_reviewed, and require trusted scope, preservation, and boundary proof before acceptance.", + ] + : []), "Use JudgmentKit design-system exports by default; when external_design_system is active, do not mix in JudgmentKit default assets unless the external adapter explicitly names them.", "When the spec calls for substantive visuals, use imagegen or premium Three.js/WebGL/D3-style rendering instead of rudimentary deterministic geometry.", "Verify core accessibility evidence: automated checks, semantic content, landmarks/headings, name-role-value, keyboard navigation, focus order, focus-visible, and responsive reflow/no-overflow.", @@ -16223,7 +19807,29 @@ export function createFrontendImplementationSkillContext({ ? { presentation_profile_id: selectedSurfaceProfile.id } : {}), workflow_topology: optionalString(workflow.topology) || "workspace", - work_units: toStringArray(workflow.work_units), + ...(isPlainObject(workflow.topology_contract) + ? { + topology_contract: cloneWorkflowStructuredValue( + workflow.topology_contract, + ), + } + : {}), + work_units: cloneWorkflowStructuredValue( + toWorkflowWorkUnitArray(workflow.work_units), + ), + ...(isPlainObject(workflow.artifact) + ? { artifact: cloneWorkflowStructuredValue(workflow.artifact) } + : {}), + ...(isPlainObject(workflow.target_model) + ? { + target_model: cloneWorkflowStructuredValue(workflow.target_model), + } + : {}), + ...(isPlainObject(workflow.state_groups) + ? { + state_groups: cloneWorkflowStructuredValue(workflow.state_groups), + } + : {}), stepper_eligibility: workflow.stepper_eligibility ?? {}, surface_set: requiredSurfaces, interaction_implications: @@ -16247,6 +19853,9 @@ export function createFrontendImplementationSkillContext({ ...(visualCompositionPolicy ? { visual_composition_policy: visualCompositionPolicy } : {}), + ...(artifactInspectorGuidance + ? { artifact_inspector: artifactInspectorGuidance } + : {}), ...(selectedSurfaceProfile ? { selected_surface_profile: selectedSurfaceProfile } : {}), @@ -16274,6 +19883,18 @@ export function createFrontendImplementationSkillContext({ ...(visualCompositionPolicy ? { visual_composition_policy: visualCompositionPolicy } : {}), + ...(artifactInspectorGuidance + ? { + artifact_inspector_authority: { + design_system_scopes: + artifactInspectorGuidance.design_system_scopes, + boundary_contracts: + artifactInspectorGuidance.boundary_contracts, + external_artifact_review_status: "external_not_reviewed", + trusted_runtime_evidence_required: true, + }, + } + : {}), visual_asset_policy: visualAssetPolicy, accessibility_policy: accessibilityPolicy, product_ui_rule: diff --git a/src/mcp.mjs b/src/mcp.mjs index e9f3b7d..089ff89 100644 --- a/src/mcp.mjs +++ b/src/mcp.mjs @@ -121,7 +121,7 @@ const ACTIVITY_MODEL_REVIEW_TOOL = { const RECOMMEND_SURFACE_TYPES_TOOL = { name: "recommend_surface_types", description: - "Recommend a purpose-based UI surface type from activity evidence before workflow review or frontend implementation.", + "Recommend a purpose-based UI surface type from activity evidence before workflow review or frontend implementation, including artifact_inspector only when its mandatory evidence is satisfied.", inputSchema: { type: "object", required: ["brief"], @@ -130,7 +130,7 @@ const RECOMMEND_SURFACE_TYPES_TOOL = { type: "string", minLength: 1, description: - "Source UI brief to classify by activity and purpose.", + "Source UI brief to classify by activity and purpose, including rendered-artifact, selectable-locus, and locus-relative support evidence when artifact_inspector may apply.", }, activity_review: { type: "object", @@ -150,7 +150,7 @@ const RECOMMEND_SURFACE_TYPES_TOOL = { const RECOMMEND_UI_WORKFLOW_PROFILES_TOOL = { name: "recommend_ui_workflow_profiles", description: - "Recommend optional UI workflow guidance profiles for a source brief without applying them automatically.", + "Recommend optional UI workflow guidance profiles, including artifact-inspector-ui when its artifact activity evidence applies, without applying them automatically.", inputSchema: { type: "object", required: ["brief"], @@ -205,12 +205,12 @@ const REVIEW_UI_WORKFLOW_CANDIDATE_TOOL = { candidate: { type: "object", description: - "Externally proposed UI workflow candidate with workflow, surface_set, handoff, and diagnostics.", + "Externally proposed UI workflow candidate with workflow, surface_set, handoff, and diagnostics. workflow.topology may be a legacy topology string or an artifact-centered topology object; workflow.topology_contract is also accepted for artifact-centered candidates.", }, profile_id: { type: "string", description: - "Optional guidance profile id, such as operator-review-ui.", + "Optional guidance profile id, such as operator-review-ui or artifact-inspector-ui.", }, surface_review: { type: "object", @@ -220,7 +220,7 @@ const REVIEW_UI_WORKFLOW_CANDIDATE_TOOL = { surface_type: { type: "string", description: - "Optional selected surface type, such as workbench, marketing, setup_debug_tool, or operator_review.", + "Optional selected surface type, such as workbench, marketing, setup_debug_tool, operator_review, or artifact_inspector.", }, }, additionalProperties: false, @@ -253,7 +253,7 @@ const REVIEW_COGNITIVE_DIMENSIONS_CANDIDATE_TOOL = { surface_type: { type: "string", description: - "Optional selected surface type, such as workbench, operator_review, dashboard_monitor, or setup_debug_tool.", + "Optional selected surface type, such as workbench, operator_review, dashboard_monitor, setup_debug_tool, or artifact_inspector.", }, surface_evidence: { type: "object", @@ -296,7 +296,7 @@ const UI_GENERATION_HANDOFF_TOOL = { const UI_IMPLEMENTATION_CONTRACT_TOOL = { name: "create_ui_implementation_contract", description: - "Create an implementation contract for generated UI, using JudgmentKit design-system authority by default, optional repo-local component authority, or a complete external design-system adapter when supplied.", + "Create an implementation contract for generated UI, using JudgmentKit design-system authority by default, optional repo-local component authority, or a complete external design-system adapter when supplied. surface_type artifact_inspector activates scoped JudgmentKit chrome and overlay authority while keeping the primary artifact external_not_reviewed.", inputSchema: { type: "object", properties: { @@ -308,6 +308,11 @@ const UI_IMPLEMENTATION_CONTRACT_TOOL = { type: "string", description: "Optional frontend stack, such as vanilla JS, React, or server-rendered HTML.", }, + surface_type: { + type: "string", + description: + "Optional selected surface type. artifact_inspector activates the canonical design_system_scopes, boundary_contracts, and artifact_inspector bundle.", + }, external_authority: { type: "string", description: @@ -328,6 +333,23 @@ const UI_IMPLEMENTATION_CONTRACT_TOOL = { description: "Optional repo-local component authority with mode none or repo_local, enforcement optional or required, families, selector_boundary, token_boundary, and computed_style_evidence expectations.", }, + design_system_scopes: { + type: "array", + items: { type: "object" }, + description: + "Optional complete Artifact Inspector authority scopes when normalizing an existing contract. Partial scope bundles fail closed.", + }, + boundary_contracts: { + type: "array", + items: { type: "object" }, + description: + "Optional complete Artifact Inspector cross-boundary contracts when normalizing an existing contract.", + }, + artifact_inspector: { + type: "object", + description: + "Optional canonical Artifact Inspector registry, state-group, runtime-proof, and external-artifact status contract.", + }, repo_evidence: { type: "array", items: { type: "string" }, @@ -383,7 +405,7 @@ const UI_IMPLEMENTATION_CONTRACT_TOOL = { const REVIEW_UI_IMPLEMENTATION_CANDIDATE_TOOL = { name: "review_ui_implementation_candidate", description: - "Review generated UI or code evidence against an active UI implementation contract. The active design-system gate is hard: missing or failing design_system_provenance means the candidate is not an artifact and must be repaired and resubmitted. Include local_component_authority_evidence when repo-local component authority is active. When visual_composition_policy applies, the hosted MCP renders exact self-contained HTML in an isolated browser, measures declared and discovered relationships, and attaches a server-trusted receipt; caller-authored visual_composition_evidence cannot satisfy acceptance.", + "Review generated UI or code evidence against an active UI implementation contract. The active design-system gate is hard: missing or failing design_system_provenance means the candidate is not an artifact and must be repaired and resubmitted. Include local_component_authority_evidence when repo-local component authority is active. When visual_composition_policy applies, the hosted MCP renders exact self-contained HTML in an isolated browser, measures declared and discovered relationships, and attaches a server-trusted receipt; caller-authored visual_composition_evidence cannot satisfy acceptance. Artifact Inspector remains review_required until trusted owned-scope, artifact-preservation, and boundary evidence exists; a chrome pass never makes the external artifact JudgmentKit-conformant.", inputSchema: { type: "object", required: ["candidate", "implementation_contract"], @@ -457,13 +479,24 @@ const FRONTEND_GENERATION_CONTEXT_TOOL = { surface_type: { type: "string", description: - "Optional selected surface type, such as marketing, workbench, setup_debug_tool, or operator_review.", + "Optional selected surface type, such as marketing, workbench, setup_debug_tool, operator_review, or artifact_inspector.", }, surface_profile: { type: "string", - enum: ["auto", "none", "judgmentkit.workbench.operational-v1"], + enum: [ + "auto", + "none", + "judgmentkit.workbench.operational-v1", + "judgmentkit.artifact-inspector.v1", + ], + description: + "Optional surface presentation profile request. Defaults to auto; use none to opt out. artifact_inspector may use judgmentkit.artifact-inspector.v1. It does not change surface classification or design-system authority.", + }, + supported_surface_profiles: { + type: "array", + items: { type: "string" }, description: - "Optional surface presentation profile request. Defaults to auto; use none to opt out. It does not change surface classification or design-system authority.", + "Optional caller capability list. If artifact_inspector is selected and judgmentkit.artifact-inspector.v1 is absent, return artifact_inspector_profile_unsupported without fallback.", }, frontend_context: { type: "object", @@ -496,7 +529,7 @@ const FRONTEND_IMPLEMENTATION_SKILL_CONTEXT_TOOL = { design_system_adapter: { type: "object", description: - "Deprecated compatibility path for complete external design-system authority. Prefer supplying design_system_adapter to create_ui_implementation_contract so the frontend context receives implementation_contract.design_system_source.", + "Deprecated compatibility path for complete external design-system authority. Prefer supplying design_system_adapter to create_ui_implementation_contract so the frontend context receives implementation_contract.design_system_source. Rejected for Artifact Inspector because JudgmentKit must retain authority over inspector chrome and the inspection overlay.", }, target_client: { type: "string", @@ -737,7 +770,14 @@ function toDisplayList(values, limit = 4) { } return values - .map((entry) => compactText(entry) || (isRecord(entry) ? compactText(entry.message) : "")) + .map((entry) => + compactText(entry) || + (isRecord(entry) + ? [entry.label, entry.participant_intent, entry.id, entry.message] + .map(compactText) + .find(Boolean) ?? "" + : ""), + ) .filter(Boolean) .slice(0, limit); } @@ -2560,6 +2600,17 @@ function planningStatus(result) { return "Implementation gate passed"; } + if (result.implementation_review_status === "review_required") { + return "Trusted runtime review required"; + } + + if ( + result.implementation_review_status === + "passed_with_external_artifact_authority" + ) { + return "Implementation gate passed; external artifact not reviewed"; + } + if (result.implementation_review_status === "failed") { return "Implementation gate failed"; } @@ -2576,6 +2627,10 @@ function planningStatus(result) { return "Frontend skill context ready"; } + if (result.status === "review_required") { + return "Review required"; + } + if (result.recommended_surface_type) { return result.status === "needs_source_context" ? "Needs source context" @@ -2664,6 +2719,79 @@ function diagnosticSummary(result) { return diagnostics.length > 0 ? diagnostics.join("; ") : "No blocking diagnostics in the planning card."; } +function artifactTopologyDiagnosticCodes(result) { + const artifactGuardrail = result.guardrails?.artifact_inspector; + const entries = [ + ...(Array.isArray(artifactGuardrail?.diagnostics) + ? artifactGuardrail.diagnostics + : []), + ...(Array.isArray(result.guardrails?.artifact_topology_diagnostics) + ? result.guardrails.artifact_topology_diagnostics + : []), + ]; + + return [ + ...new Set( + entries + .map((entry) => + typeof entry === "string" + ? compactText(entry) + : isRecord(entry) + ? compactText(entry.code) + : "", + ) + .filter(Boolean), + ), + ]; +} + +function routingConflictSignalList(routingConflict) { + if (!isRecord(routingConflict)) { + return []; + } + + const signals = Array.isArray(routingConflict.conflicting_signals) + ? routingConflict.conflicting_signals + : []; + + return signals + .map((signal) => { + if (typeof signal === "string") { + return compactText(signal); + } + + if (!isRecord(signal)) { + return ""; + } + + const kind = compactText(signal.kind); + const id = compactText(signal.id ?? signal.evidence_id ?? signal.message); + + return [kind, id].filter(Boolean).join(": "); + }) + .filter(Boolean); +} + +function competingSurfaceTypeList(routingConflict) { + if (!isRecord(routingConflict)) { + return []; + } + + const entries = Array.isArray(routingConflict.competing_surface_types) + ? routingConflict.competing_surface_types + : []; + + return entries + .map((entry) => + typeof entry === "string" + ? compactText(entry) + : isRecord(entry) + ? compactText(entry.surface_type ?? entry.id ?? entry.label) + : "", + ) + .filter(Boolean); +} + function formatActivityReviewCard(result) { const activity = result.candidate?.activity_model ?? {}; const interaction = result.candidate?.interaction_contract ?? {}; @@ -2726,7 +2854,13 @@ function formatWorkflowReviewCard(result) { firstLine("Handoff", handoff.next_action), ]); addSection(lines, "Targeted questions", bulletList(result.review?.targeted_questions)); - addSection(lines, "Diagnostics", [`${diagnosticSummary(result)}`]); + addSection(lines, "Diagnostics", [ + `${diagnosticSummary(result)}`, + listLine( + "Artifact topology diagnostics", + artifactTopologyDiagnosticCodes(result), + ), + ]); return lines.join("\n"); } @@ -2786,10 +2920,16 @@ function formatAnalysisCard(result) { } function formatSurfaceRecommendationCard(result) { + const recommendedSurfaceType = compactText(result.recommended_surface_type); + const nextStep = recommendedSurfaceType + ? `Use surface_type "${recommendedSurfaceType}" as purpose guidance before workflow review and frontend implementation.` + : isRecord(result.routing_conflict) + ? "Resolve the routing conflict before choosing a surface type or continuing to workflow review." + : "Resolve the missing surface evidence before workflow review and frontend implementation."; const lines = [ "## JudgmentKit Surface Recommendation", `**Status:** ${planningStatus(result)}`, - `**Next step:** Use surface_type "${compactText(result.recommended_surface_type)}" as purpose guidance before workflow review and frontend implementation.`, + `**Next step:** ${nextStep}`, ]; addSection(lines, "Plan from this", [ @@ -2808,6 +2948,14 @@ function formatSurfaceRecommendationCard(result) { "Implementation terms", termList(result.evidence?.implementation_terms_detected), ), + listLine( + "Routing conflict signals", + routingConflictSignalList(result.routing_conflict), + ), + listLine( + "Competing surface types", + competingSurfaceTypeList(result.routing_conflict), + ), ]); return lines.join("\n"); @@ -2943,12 +3091,19 @@ function formatImplementationReviewCard(result) { "pattern_contracts", ].includes(finding.check), ); + const nextStep = + result.implementation_review_status === "passed" + ? "**Next step:** The candidate passed the implementation gate; use the evidence in the final handoff." + : result.implementation_review_status === + "passed_with_external_artifact_authority" + ? "**Next step:** The JudgmentKit-owned chrome passed; preserve the external artifact as external_not_reviewed in the final handoff." + : result.implementation_review_status === "review_required" + ? "**Next step:** Run the trusted runtime review for the JudgmentKit-owned chrome and authority boundary; keep the external artifact external_not_reviewed." + : "**Next step:** This is not an artifact. Repair the failed gates and resubmit before final UI handoff."; const lines = [ "## JudgmentKit Implementation Review", `**Status:** ${planningStatus(result)}`, - result.implementation_review_status === "passed" - ? "**Next step:** The candidate passed the implementation gate; use the evidence in the final handoff." - : "**Next step:** This is not an artifact. Repair the failed gates and resubmit before final UI handoff.", + nextStep, ]; addSection(lines, "Checks", [ @@ -3354,7 +3509,10 @@ export function formatPlanningCard(result) { return formatImplementationContractCard(result); } - if (result?.recommended_surface_type) { + if ( + result?.recommended_surface_type || + (isRecord(result) && Object.hasOwn(result, "recommended_surface_type")) + ) { return formatSurfaceRecommendationCard(result); } @@ -3599,6 +3757,7 @@ export async function handleToolCall(name, args = {}) { surface_review: args.surface_review, surface_type: args.surface_type, surface_profile: args.surface_profile, + supported_surface_profiles: args.supported_surface_profiles, frontend_context: args.frontend_context, verification: args.verification, }); @@ -3828,10 +3987,14 @@ export function createJudgmentKitMcpServer() { inputSchema: { repo_name: z.string().optional(), target_stack: z.string().optional(), + surface_type: z.string().optional(), external_authority: z.string().optional(), design_system_adapter: z.record(z.any()).optional(), design_system_source: z.record(z.any()).optional(), local_component_authority: z.record(z.any()).optional(), + design_system_scopes: z.array(z.record(z.any())).optional(), + boundary_contracts: z.array(z.record(z.any())).optional(), + artifact_inspector: z.record(z.any()).optional(), repo_evidence: z.array(z.string()).optional(), approved_primitives: z .array(z.string()) @@ -3900,8 +4063,14 @@ export function createJudgmentKitMcpServer() { surface_review: z.record(z.any()).optional(), surface_type: z.string().optional(), surface_profile: z - .enum(["auto", "none", "judgmentkit.workbench.operational-v1"]) + .enum([ + "auto", + "none", + "judgmentkit.workbench.operational-v1", + "judgmentkit.artifact-inspector.v1", + ]) .optional(), + supported_surface_profiles: z.array(z.string()).optional(), frontend_context: z.record(z.any()).optional(), verification: z.record(z.any()).optional(), }, diff --git a/src/surface-presentation-profiles.mjs b/src/surface-presentation-profiles.mjs index 9f4d828..b16480f 100644 --- a/src/surface-presentation-profiles.mjs +++ b/src/surface-presentation-profiles.mjs @@ -1,5 +1,7 @@ export const WORKBENCH_SURFACE_PROFILE_ID = "judgmentkit.workbench.operational-v1"; +export const ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID = + "judgmentkit.artifact-inspector.v1"; function deepFreeze(value) { if (!value || typeof value !== "object" || Object.isFrozen(value)) { @@ -236,10 +238,287 @@ export const WORKBENCH_SURFACE_PROFILE = deepFreeze({ }, }); +export const ARTIFACT_INSPECTOR_SURFACE_PROFILE = deepFreeze({ + id: ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + version: "1.0.0", + status: "proposed", + name: "JudgmentKit Artifact Inspector", + surface_type: "artifact_inspector", + workflow_profile_id: "artifact-inspector-ui", + interaction_topology_kind: "artifact_centered", + purpose: + "Keep one rendered artifact primary while JudgmentKit-owned chrome and overlays attach context, actions, state, and receipts to a selected artifact locus.", + authority: { + mode: "scoped_mixed_visual_authority", + public_contract: true, + runtime_renderer: false, + design_system_source_mode: "judgmentkit_default", + design_system_source_id: "judgmentkit.design-system.source-v1", + visual_token_adapter_id: "judgmentkit.visual-token-adapter.boundary-v1", + pattern_contract_id: "artifact-inspector", + design_system_scopes: [ + { + scope_id: "inspector_chrome", + root_selector: "[data-jk-scope='inspector-chrome']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", + }, + { + scope_id: "inspection_overlay", + root_selector: "[data-jk-scope='inspection-overlay']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", + }, + { + scope_id: "primary_artifact", + root_selector: "[data-artifact-root]", + authority: "external_declared", + enforcement: "external_not_reviewed", + style_isolation: "required", + }, + ], + boundary_contracts: [ + { + from_scope: "inspection_overlay", + to_scope: "primary_artifact", + allowed_roles: [ + "locus_target", + "annotation_overlay", + "connector_endpoint", + ], + event_contract_required: true, + }, + ], + review_result_policy: + "Report chrome, overlay, external artifact, and boundary results separately; never describe the external artifact as JudgmentKit-conformant.", + }, + activation: { + default_request: "auto", + accepted_requests: [ + "auto", + "none", + ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + ], + accepted_confidence: ["medium", "high", "provided"], + explicit_profile_request_requires_grounded_surface: true, + low_confidence_neutral_fallback: "do_not_activate", + external_artifact_authority: "allowed_when_scoped", + whole_candidate_external_design_system_fallback: "none", + }, + appearance: { + supported_modes: ["light", "dark", "system"], + default_mode: "system", + visible_toggle_default: false, + token_source: + "implementation_contract.visual_token_adapter.appearance_token_sets", + scope_rule: + "JudgmentKit appearance tokens apply only to inspector_chrome and inspection_overlay.", + }, + typography: { + font_source: "implementation_contract.visual_token_adapter.font_roles", + font_roles: ["body", "heading", "label", "numeric"], + css_custom_properties: [ + { + name: "--jk-artifact-inspector-type-body", + value: "0.875rem", + usage: "restrained inspector context and local feedback", + }, + { + name: "--jk-artifact-inspector-type-label", + value: "0.75rem", + usage: "sparse locus, authority, state, and receipt labels", + }, + ], + }, + density: { + css_custom_properties: [ + { + name: "--jk-artifact-inspector-chrome-gap", + value: "0.75rem", + usage: "space between the artifact boundary and contextual chrome", + }, + { + name: "--jk-artifact-inspector-target-size", + value: "2.75rem", + usage: "minimum JudgmentKit-owned locus and action target size", + }, + { + name: "--jk-artifact-inspector-overlay-offset", + value: "0.5rem", + usage: "minimum offset for overlay feedback from essential artifact content", + }, + ], + }, + composition: { + density: "artifact-first contextual", + hierarchy: "artifact_dominant", + navigation_shape: + "artifact persistent; supporting context revealed from the active locus", + allowed_variants: [ + "peripheral anchors", + "contextual tray", + "inline annotation", + "inspection lens", + "responsive hybrid", + ], + rules: [ + "Keep the artifact as the largest and most persistent visual region.", + "Keep the active locus visible while its supporting context is open.", + "Attach evidence, authority, actions, feedback, and receipts visibly or semantically to the active locus.", + "Distinguish reversible preview from consequential commitment and durable result.", + "Keep JudgmentKit-owned overlay marks distinguishable from artifact content.", + "Use the least persistent chrome that preserves target, context, action, recovery, and result together.", + ], + avoid_by_default: [ + "master-detail", + "queue-detail", + "dashboard summaries", + "persistent evidence dossiers", + "stacked labeled panels", + "form-like step progression", + "chat composer or message history", + "drag-only, hover-only, or color-only interaction", + ], + }, + state_coverage: [ + "artifact loading", + "artifact ready", + "artifact unavailable or failed", + "no active locus", + "locus focused or selected", + "context loading", + "context ready", + "relation or action preview", + "supported", + "incompatible", + "unavailable", + "stale", + "ambiguous", + "blocked", + "cancelled or Back", + "local error and retry", + ], + responsive: { + wide: + "Keep the artifact central and persistent, reveal sparse support at the periphery or from the active locus, and route connectors around the inspected locus and essential content.", + narrow: + "Keep the artifact primary, move support into a temporary edge affordance or contextual tray, and preserve the active locus, feedback, recovery, zoom, reflow, and non-drag alternatives together without obstruction.", + invariant: + "Wide and narrow placements use the same state machine, actions, completion, and recovery semantics.", + required_evidence: [ + "wide viewport", + "narrow viewport", + "pointer path", + "touch path without hover", + "keyboard path", + "focus order and focus return", + "responsive no obstruction", + "zoom and reflow", + ], + }, + component_roles: [ + { id: "ArtifactViewport", scope_id: "inspector_chrome" }, + { id: "ArtifactBoundary", scope_id: "inspector_chrome" }, + { id: "ArtifactStatus", scope_id: "inspector_chrome" }, + { id: "ObservationMarker", scope_id: "inspection_overlay" }, + { id: "LocusSelection", scope_id: "inspection_overlay" }, + { id: "ContextAnchor", scope_id: "inspection_overlay" }, + { id: "AuthorityAnchor", scope_id: "inspection_overlay" }, + { id: "AnchorRail", scope_id: "inspector_chrome" }, + { id: "RelationPreview", scope_id: "inspection_overlay" }, + { id: "RelationConnector", scope_id: "inspection_overlay" }, + { id: "InlineReason", scope_id: "inspection_overlay" }, + { id: "ContextTray", scope_id: "inspector_chrome" }, + { id: "CommitBoundary", scope_id: "inspector_chrome" }, + { id: "ReceiptMarker", scope_id: "inspection_overlay" }, + { id: "BackAction", scope_id: "inspector_chrome" }, + { id: "ResetAction", scope_id: "inspector_chrome" }, + { + id: "ZoomAndPanControls", + scope_id: "inspector_chrome", + conditional: "artifact_requires_zoom_or_pan", + }, + ], + product_adapter_boundary: { + generic_profile_owns: [ + "inspector chrome density and hierarchy", + "selection and overlay state treatment", + "context, action, recovery, and receipt relationships", + "responsive artifact-inspection transitions", + "cross-boundary focus and announcement contract", + ], + external_artifact_authority_owns: [ + "artifact typography", + "artifact components", + "artifact color and elevation", + "artifact internal layout", + "artifact internal semantics and controls", + ], + boundary_contract_owns: [ + "semantic locus identifiers", + "artifact target geometry", + "cross-boundary event precedence", + "overlay obstruction and target drift", + "focus order and focus return", + ], + excluded_from_generic_profile: [ + "artifact restyling", + "visual inference presented as artifact truth", + "silent interception of native artifact actions", + "whole-candidate JudgmentKit conformance claims", + ], + }, + evidence_expectations: [ + "Name this profile id, artifact_inspector surface type, artifact-inspector-ui workflow profile, and artifact_centered topology.", + "Provide an exact region and authority map for inspector chrome, inspection overlay, primary artifact, and their boundary.", + "Prove style isolation in both directions without restyling the external artifact.", + "Provide the artifact render fingerprint before and after every chrome state transition.", + "Verify pointer, touch, keyboard, and assistive-technology crossings through declared semantic locus identifiers.", + "Verify focus order, focus stability, inspection-mode announcements, and focus return.", + "Verify overlay occlusion, target re-anchoring, stale targets, and target-drift prevention.", + "Show wide and narrow rest, selection, preview, invalid, supported, unavailable, recovery, and local-result states.", + "Report chrome conformance, overlay conformance, external artifact preservation, and boundary behavior separately.", + ], + provenance: { + definition_id: ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + promotion_status: "proposed", + runtime_evidence_policy: "required_per_consumer", + external_artifact_review_status: "external_not_reviewed", + }, +}); + +const SURFACE_PRESENTATION_PROFILES = deepFreeze({ + [WORKBENCH_SURFACE_PROFILE_ID]: WORKBENCH_SURFACE_PROFILE, + [ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID]: + ARTIFACT_INSPECTOR_SURFACE_PROFILE, +}); + +export function cloneSurfacePresentationProfile(profileId) { + const normalizedProfileId = + typeof profileId === "string" ? profileId.trim() : ""; + const profile = SURFACE_PRESENTATION_PROFILES[normalizedProfileId]; + + return profile ? clone(profile) : null; +} + +export function getSurfacePresentationProfile(profileId) { + return cloneSurfacePresentationProfile(profileId); +} + export function cloneWorkbenchSurfaceProfile() { - return clone(WORKBENCH_SURFACE_PROFILE); + return cloneSurfacePresentationProfile(WORKBENCH_SURFACE_PROFILE_ID); +} + +export function cloneArtifactInspectorSurfaceProfile() { + return cloneSurfacePresentationProfile( + ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + ); } export function listSurfacePresentationProfiles() { - return [cloneWorkbenchSurfaceProfile()]; + return Object.keys(SURFACE_PRESENTATION_PROFILES).map((profileId) => + cloneSurfacePresentationProfile(profileId), + ); } diff --git a/tests/artifact-inspector-authority.test.mjs b/tests/artifact-inspector-authority.test.mjs new file mode 100644 index 0000000..b3fb6c6 --- /dev/null +++ b/tests/artifact-inspector-authority.test.mjs @@ -0,0 +1,977 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import Ajv2020 from "ajv/dist/2020.js"; + +import * as judgmentKitPublicApi from "../src/index.mjs"; +import { + JudgmentKitInputError, + createUiImplementationContract, + reviewUiImplementationCandidate, +} from "../src/index.mjs"; + +import { + ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS, + ARTIFACT_INSPECTOR_CANONICAL_SCOPES, + ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS, + ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES, + ARTIFACT_INSPECTOR_SCOPE_IDS, + ARTIFACT_INSPECTOR_STATE_GROUP_IDS, + createArtifactInspectorFinding, + normalizeArtifactInspectorBoundaryContracts, + normalizeArtifactInspectorScopes, + normalizeArtifactInspectorStateConfig, + reviewArtifactInspectorAuthorityEvidence, + validateArtifactInspectorAuthorityContract, +} from "../src/artifact-inspector-authority.mjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, ".."); +const contract = JSON.parse( + fs.readFileSync( + path.join(root, "contracts/ai-ui-generation.activity-contract.json"), + "utf8", + ), +); +const schema = JSON.parse( + fs.readFileSync( + path.join(root, "contracts/judgmentkit-kernel.schema.json"), + "utf8", + ), +); +const model = contract.interaction_models.artifact_inspector; +const ajv = new Ajv2020({ allErrors: true }); +const validateKernelContract = ajv.compile(schema); + +assert.equal( + "reviewArtifactInspectorAuthorityEvidence" in judgmentKitPublicApi, + false, + "The raw Artifact Inspector authority evaluator must remain package-internal so callers cannot submit self-asserted trusted evidence.", +); + +const EXPECTED_COMPONENT_ROLES = [ + "ArtifactViewport", + "ArtifactBoundary", + "ArtifactStatus", + "ObservationMarker", + "LocusSelection", + "ContextAnchor", + "AuthorityAnchor", + "AnchorRail", + "RelationPreview", + "RelationConnector", + "InlineReason", + "ContextTray", + "CommitBoundary", + "ReceiptMarker", + "BackAction", + "ResetAction", + "ZoomAndPanControls", +]; + +function canonicalArtifactImplementationContract() { + return { + design_system_scopes: structuredClone(model.design_system.scopes), + boundary_contracts: structuredClone( + model.design_system.boundary_contracts, + ), + artifact_inspector: { + registry_id: model.id, + registry_version: model.version, + ...structuredClone(model.canonical_identifiers), + active_state_groups: ["core"], + state_groups: structuredClone(model.state_groups), + trusted_runtime_evidence_required: true, + external_artifact_review_status: "external_not_reviewed", + }, + }; +} + +function contractWithArtifactImplementation() { + const candidate = structuredClone(contract); + Object.assign( + candidate.implementation_contract, + canonicalArtifactImplementationContract(), + ); + return candidate; +} + +function completeExternalDesignSystemAdapter() { + return { + design_system_name: "Material UI", + design_system_package: "@mui/material", + token_guidance: { + token_families: ["color", "type", "spacing", "radius"], + token_roles: [ + { + role: "surface", + families: ["color"], + usage: "Material UI surface colors", + }, + { + role: "decision", + families: ["color"], + usage: "Material UI action states", + }, + ], + css_custom_properties: [ + { + name: "--mui-palette-background-paper", + role: "surface", + family: "color", + value: "theme.palette.background.paper", + usage: "Material UI surfaces", + }, + { + name: "--mui-font-family", + role: "text", + family: "type", + value: "theme.typography.fontFamily", + usage: "Material UI typography", + }, + ], + }, + font_guidance: { + font_roles: { + body: { + stack: "var(--mui-font-family)", + usage: "Material UI body typography", + }, + heading: { + stack: "var(--mui-font-family)", + usage: "Material UI headings", + }, + }, + }, + icon_guidance: { + icon_roles: ["status", "action"], + icon_catalog: { + source: "external_design_system", + library: "mui-icons-material", + package: "@mui/icons-material", + version: "repo-approved", + icon_count: 2000, + license: "MIT", + notice: "Repo-approved Material UI icon adapter.", + mcp_tools: [], + }, + }, + components: ["Stack", "Button", "Alert"], + }; +} + +function passingEvidence(method = "inspection") { + return { + status: "pass", + method, + notes: "Verified.", + }; +} + +function artifactImplementationCandidate(implementationContract) { + const activeArtifactStates = implementationContract.artifact_inspector + .active_state_groups.flatMap( + (groupId) => + implementationContract.artifact_inspector.state_groups[groupId], + ); + return { + code: ` +
+
Selected locus
+
+ + + `, + primary_artifact: { + code: 'import { TextField } from "@mui/material";', + markup: + '
', + css: "[data-artifact-root] { --artifact-brand: rebeccapurple; color: var(--artifact-brand); }", + }, + primitives_used: [], + states_covered: [ + ...implementationContract.state_coverage.required_states, + ...activeArtifactStates, + ], + static_checks: ["npm run check"], + browser_qa: { + desktop: "desktop viewport checked", + mobile: "mobile viewport checked", + }, + accessibility_evidence: { + automated_checks: passingEvidence(), + semantic_content: passingEvidence(), + landmarks_headings: passingEvidence(), + name_role_value: passingEvidence(), + keyboard_navigation: passingEvidence(), + focus_order: passingEvidence(), + focus_visible: passingEvidence(), + responsive_no_overflow: passingEvidence(), + semantic_fallbacks: passingEvidence(), + non_text_contrast: { + status: "pass", + method: "computed style inspection", + samples: [{ target: "selection boundary", contrast_ratio: 3.2 }], + }, + forced_colors: { + status: "pass", + method: "forced-colors emulation", + notes: + "Owned chrome, overlays, focus indicators, and state labels remain visible.", + }, + form_labels_instructions: passingEvidence(), + form_errors: passingEvidence(), + status_messages: passingEvidence(), + }, + design_system_provenance: { + source: "judgmentkit_default", + token_source: "/design-system/visual-token-adapter.json", + typography_source: "/design-system/visual-token-adapter.json", + icon_source: "JudgmentKit icon catalog via get_icon_svg", + renderer_component_source: + "implementation_contract.default_ai_native_design_system.component_contracts", + import_boundary: + "No visual, typography, icon, or component package imports outside the active design-system source.", + token_prefix_source: + "implementation_contract.design_system_source.token_prefixes", + source_exports: + "implementation_contract.design_system_source.source_exports", + }, + pattern_contract_evidence: { + pattern_id: "artifact-inspector", + surface_type: "artifact_inspector", + regions_present: [ + "persistent rendered artifact", + "selectable artifact loci", + "locus-relative context", + "artifact-local feedback", + "artifact-local completion or recovery", + ], + controls_present: [ + "locus selection", + "context disclosure", + "preview and validation", + "Back or reset", + "commit and receipt when consequential", + ], + completion_or_handoff: + "participant verifies the result on or immediately beside the artifact locus", + }, + artifact_inspector_runtime_evidence: structuredClone(trustedReceipt), + }; +} + +function assertInvalidKernelContract(value, message) { + assert.equal(validateKernelContract(value), false, message); + assert.ok( + (validateKernelContract.errors ?? []).length > 0, + `${message}: schema errors must be available.`, + ); +} + +assert.equal(validateKernelContract(contract), true, ajv.errorsText(validateKernelContract.errors)); +assert.deepEqual(model.canonical_identifiers, { + surface_type: "artifact_inspector", + workflow_profile: "artifact-inspector-ui", + frontend_surface_profile: "judgmentkit.artifact-inspector.v1", + topology_kind: "artifact_centered", +}); +assert.deepEqual(model.activity_evidence.mandatory, [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative", +]); +assert.deepEqual(model.activity_evidence.positive.slice(0, 3), model.activity_evidence.mandatory); +assert.deepEqual(model.activity_evidence.exclusions, [ + "queue_or_case_is_primary", + "many_items_are_primary", + "open_ended_creation_is_primary", + "conversation_turns_are_primary", + "linear_reading_is_completion", + "monitoring_is_completion", + "configuration_is_completion", +]); +assert.equal(model.topology.kind, "artifact_centered"); +assert.equal(model.topology.primary_object_id, "artifact"); +assert.deepEqual(model.work_units.map((entry) => entry.id), [ + "orient", + "select_locus", + "inspect_context", + "preview", + "validate", + "complete", + "verify_result", + "recover", +]); +assert.deepEqual(Object.keys(model.state_groups), ARTIFACT_INSPECTOR_STATE_GROUP_IDS); +assert.deepEqual(model.design_system.component_roles, EXPECTED_COMPONENT_ROLES); +assert.deepEqual( + model.design_system.scopes.map((entry) => entry.scope_id), + ARTIFACT_INSPECTOR_SCOPE_IDS, +); +assert.deepEqual( + model.diagnostics.map((entry) => entry.code), + ARTIFACT_INSPECTOR_DIAGNOSTIC_CODES, +); + +const artifactComponentIds = new Set( + contract.implementation_contract.default_ai_native_design_system + .component_contracts.map((entry) => entry.id), +); +for (const componentRole of EXPECTED_COMPONENT_ROLES) { + assert.ok( + artifactComponentIds.has(componentRole), + `Missing semantic component contract ${componentRole}.`, + ); +} +const artifactPattern = + contract.implementation_contract.default_ai_native_design_system + .pattern_contracts.find((entry) => entry.id === "artifact-inspector"); +assert.equal(artifactPattern.surface_type, "artifact_inspector"); +assert.ok( + artifactPattern.failure_signals.some((entry) => + entry.includes("external artifact authority"), + ), +); +assert.equal( + contract.implementation_contract.design_system_scopes, + undefined, + "Mixed authority must be opt-in for Artifact Inspector, not a new global default.", +); +assert.equal( + contract.implementation_contract.design_system_source.mode, + "judgmentkit_default", + "The established whole-candidate design-system source must remain unchanged.", +); + +{ + const augmented = contractWithArtifactImplementation(); + assert.equal( + validateKernelContract(augmented), + true, + ajv.errorsText(validateKernelContract.errors), + ); +} + +{ + const missingRegistry = structuredClone(contract); + delete missingRegistry.interaction_models.artifact_inspector; + assertInvalidKernelContract( + missingRegistry, + "The Artifact Inspector registry entry is required by this contract version.", + ); + + const registryWithUnknownField = structuredClone(contract); + registryWithUnknownField.interaction_models.artifact_inspector.untrusted_extension = true; + assertInvalidKernelContract( + registryWithUnknownField, + "The Artifact Inspector registry must remain strict.", + ); + + const registryWithDivergentDuplicateWorkUnit = structuredClone(contract); + const duplicateOrient = registryWithDivergentDuplicateWorkUnit.interaction_models + .artifact_inspector.work_units.find((entry) => entry.id === "recover"); + duplicateOrient.id = "orient"; + assertInvalidKernelContract( + registryWithDivergentDuplicateWorkUnit, + "The Artifact Inspector registry must reject duplicate work-unit ids even when the duplicate objects have different content.", + ); + + const partialAuthority = structuredClone(contract); + partialAuthority.implementation_contract.design_system_scopes = + structuredClone(model.design_system.scopes); + assertInvalidKernelContract( + partialAuthority, + "Partial authority declarations must fail closed.", + ); + + const wrongArtifactAuthority = contractWithArtifactImplementation(); + wrongArtifactAuthority.implementation_contract.design_system_scopes.find( + (entry) => entry.scope_id === "primary_artifact", + ).authority = "judgmentkit_default"; + assertInvalidKernelContract( + wrongArtifactAuthority, + "The primary artifact cannot be claimed by JudgmentKit.", + ); + + const globalExternalAuthority = contractWithArtifactImplementation(); + globalExternalAuthority.implementation_contract.design_system_source.mode = + "external_design_system"; + assertInvalidKernelContract( + globalExternalAuthority, + "Artifact Inspector chrome and overlays cannot inherit a global external design-system authority.", + ); + + const externalOverclaim = contractWithArtifactImplementation(); + externalOverclaim.implementation_contract.artifact_inspector + .external_artifact_review_status = "passed"; + assertInvalidKernelContract( + externalOverclaim, + "The schema must reject whole-artifact conformance overclaims.", + ); +} + +const canonicalAuthority = canonicalArtifactImplementationContract(); +const normalizedAuthority = validateArtifactInspectorAuthorityContract( + canonicalAuthority, +); +assert.equal(normalizedAuthority.active, true); +assert.deepEqual( + normalizedAuthority.design_system_scopes, + ARTIFACT_INSPECTOR_CANONICAL_SCOPES, +); +assert.deepEqual( + normalizedAuthority.boundary_contracts, + ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS, +); +assert.equal( + normalizedAuthority.artifact_inspector.external_artifact_review_status, + "external_not_reviewed", +); +assert.deepEqual( + validateArtifactInspectorAuthorityContract({}), + { + active: false, + design_system_scopes: [], + boundary_contracts: [], + artifact_inspector: null, + }, + "Legacy implementation contracts must remain not applicable.", +); + +assert.deepEqual( + normalizeArtifactInspectorScopes(model.design_system.scopes, { + required: true, + }), + model.design_system.scopes, +); +assert.deepEqual( + normalizeArtifactInspectorBoundaryContracts( + model.design_system.boundary_contracts, + { + scopes: model.design_system.scopes, + required: true, + }, + ), + model.design_system.boundary_contracts, +); +assert.deepEqual( + normalizeArtifactInspectorStateConfig( + { + active_state_groups: ["core"], + state_groups: model.state_groups, + }, + { required: true }, + ), + { + active_state_groups: ["core"], + state_groups: ARTIFACT_INSPECTOR_CANONICAL_STATE_GROUPS, + }, +); + +{ + const missingCanonicalStateGroup = structuredClone(model.state_groups); + delete missingCanonicalStateGroup.automation; + const malformedCanonicalStateGroup = structuredClone(model.state_groups); + malformedCanonicalStateGroup.core = ["artifact loading"]; + const invalidStateConfigs = [ + { + label: "missing state configuration", + value: undefined, + }, + { + label: "non-object state configuration", + value: [], + }, + { + label: "missing active state groups", + value: { state_groups: structuredClone(model.state_groups) }, + }, + { + label: "malformed active state groups", + value: { + active_state_groups: "core", + state_groups: structuredClone(model.state_groups), + }, + }, + { + label: "active state groups without core", + value: { + active_state_groups: ["consequential"], + state_groups: structuredClone(model.state_groups), + }, + }, + { + label: "missing canonical state group", + value: { + active_state_groups: ["core"], + state_groups: missingCanonicalStateGroup, + }, + }, + { + label: "malformed canonical state group", + value: { + active_state_groups: ["core"], + state_groups: malformedCanonicalStateGroup, + }, + }, + ]; + + for (const testCase of invalidStateConfigs) { + assert.throws( + () => + normalizeArtifactInspectorStateConfig(testCase.value, { + required: true, + }), + (error) => { + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + testCase.label, + ); + assert.equal( + error.details?.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + testCase.label, + ); + assert.equal(typeof error.details?.field, "string", testCase.label); + return true; + }, + testCase.label, + ); + } + + const authorityWithoutStateGroups = canonicalArtifactImplementationContract(); + delete authorityWithoutStateGroups.artifact_inspector.state_groups; + assert.throws( + () => validateArtifactInspectorAuthorityContract(authorityWithoutStateGroups), + (error) => { + assert.equal( + error.details?.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + assert.equal(error.details?.field, "artifact_inspector.state_groups"); + return true; + }, + "The implementation-contract validation path must preserve the state-group contract diagnostic.", + ); +} + +assert.throws( + () => + validateArtifactInspectorAuthorityContract({ + design_system_scopes: model.design_system.scopes, + }), + (error) => { + assert.equal(error.code, "invalid_artifact_inspector_authority_contract"); + assert.deepEqual(error.details.missing, [ + "boundary_contracts", + "artifact_inspector", + ]); + return true; + }, +); +assert.throws( + () => + normalizeArtifactInspectorScopes( + model.design_system.scopes.filter( + (entry) => entry.scope_id !== "primary_artifact", + ), + { required: true }, + ), + (error) => { + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_ARTIFACT_SCOPE_MISSING", + ); + assert.equal(error.details.field, "design_system_scopes"); + return true; + }, +); + +{ + const packet = createUiImplementationContract({ + surface_type: "artifact_inspector", + }); + const implementationContract = packet.implementation_contract; + + assert.equal(packet.implementation_contract_status, "ready"); + assert.equal(packet.source.surface_type, "artifact_inspector"); + assert.deepEqual( + implementationContract.design_system_scopes, + ARTIFACT_INSPECTOR_CANONICAL_SCOPES, + ); + assert.deepEqual( + implementationContract.boundary_contracts, + ARTIFACT_INSPECTOR_CANONICAL_BOUNDARY_CONTRACTS, + ); + assert.equal( + implementationContract.artifact_inspector.frontend_surface_profile, + "judgmentkit.artifact-inspector.v1", + ); + assert.equal( + implementationContract.artifact_inspector.external_artifact_review_status, + "external_not_reviewed", + ); + assert.equal( + implementationContract.design_system_source.mode, + contract.implementation_contract.design_system_source.mode, + "Scoped authority must not replace the existing whole-candidate authority model.", + ); + assert.ok( + packet.generation_gates.some( + (gate) => + gate.id === "artifact_inspector_authority_gate" && + gate.status === "trusted_runtime_evidence_required", + ), + ); + + const legacyPacket = createUiImplementationContract({ + surface_type: "workbench", + }); + assert.equal( + "design_system_scopes" in legacyPacket.implementation_contract, + false, + ); + assert.equal( + "artifact_inspector" in legacyPacket.implementation_contract, + false, + ); + + assert.throws( + () => + createUiImplementationContract({ + surface_type: "workbench", + design_system_scopes: model.design_system.scopes, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + return true; + }, + ); + + const legacyContractWithoutArtifactInspector = structuredClone(contract); + delete legacyContractWithoutArtifactInspector.interaction_models + .artifact_inspector; + assert.throws( + () => + createUiImplementationContract( + { surface_type: "artifact_inspector" }, + { contract: legacyContractWithoutArtifactInspector }, + ), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "artifact_inspector_profile_unsupported"); + assert.equal(error.details.fallback_applied, false); + return true; + }, + "Older contracts must receive an explicit unsupported-profile diagnostic before generic surface validation.", + ); +} + +{ + const review = reviewArtifactInspectorAuthorityEvidence(canonicalAuthority); + assert.equal(review.status, "review_required"); + assert.equal(review.applicable, true); + assert.equal(review.design_system_review.inspector_chrome, "review_required"); + assert.equal(review.design_system_review.inspection_overlay, "review_required"); + assert.equal(review.design_system_review.primary_artifact, "external_not_reviewed"); + assert.equal(review.trusted_runtime_evidence.status, "missing"); + assert.deepEqual(review.findings.map((entry) => entry.code), [ + "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", + ]); + assert.equal( + review.findings[0].evidence.candidate_authored_evidence_accepted, + false, + ); +} + +const trustedReceipt = { + issuer: "judgmentkit_browser_runtime", + status: "pass", + scope_results: { + inspector_chrome: { status: "pass" }, + inspection_overlay: { status: "pass" }, + primary_artifact: { + status: "external_not_reviewed", + artifact_preservation: "pass", + }, + }, + boundary_results: [ + { + from_scope: "inspection_overlay", + to_scope: "primary_artifact", + status: "pass", + }, + ], +}; + +{ + const legacyContractWithoutVisualComposition = structuredClone(contract); + delete legacyContractWithoutVisualComposition.implementation_contract + .visual_composition_policy; + delete legacyContractWithoutVisualComposition.implementation_contract + .visual_composition_policy_authority; + const implementationContract = createUiImplementationContract( + { surface_type: "artifact_inspector" }, + { contract: legacyContractWithoutVisualComposition }, + ).implementation_contract; + const candidate = artifactImplementationCandidate(implementationContract); + const review = reviewUiImplementationCandidate(candidate, { + contract: legacyContractWithoutVisualComposition, + implementation_contract: implementationContract, + surface_type: "artifact_inspector", + iteration_context: { current_attempt: 3 }, + trustedRuntimeEvidence: structuredClone(trustedReceipt), + trusted_runtime_evidence: structuredClone(trustedReceipt), + }); + + assert.equal( + review.implementation_review_status, + "review_required", + JSON.stringify(review.findings), + ); + assert.equal(review.candidate_artifact_status, "awaiting_runtime_review"); + assert.equal(review.design_system_acceptance_status, "review_required"); + assert.equal(review.next_agent_action, "run_trusted_runtime_review"); + assert.equal(review.autofix_loop.current_attempt, 3); + assert.equal( + review.autofix_loop.status, + "awaiting_runtime_review", + "Missing trusted proof must not exhaust or stop the candidate-repair loop.", + ); + assert.deepEqual(review.repair_instructions, { + status: "none", + groups: {}, + }); + + const overLimitReview = reviewUiImplementationCandidate(candidate, { + contract: legacyContractWithoutVisualComposition, + implementation_contract: implementationContract, + surface_type: "artifact_inspector", + iteration_context: { current_attempt: 4 }, + }); + assert.equal(overLimitReview.autofix_loop.status, "awaiting_runtime_review"); + assert.equal( + overLimitReview.autofix_loop.remaining_attempts, + 0, + "Awaiting runtime review must never report a negative repair-attempt count.", + ); + assert.equal(review.checks.design_system_provenance.status, "pass"); + assert.equal(review.checks.pattern_contracts.status, "pass"); + assert.equal(review.checks.accessibility_evidence.status, "pass"); + assert.equal( + review.checks.raw_controls.status, + "pass", + "Raw controls owned by the declared external artifact are outside JudgmentKit primitive enforcement.", + ); + assert.equal( + review.checks.design_system_provenance.status, + "pass", + "External artifact imports and tokens are outside the JudgmentKit-owned provenance scan.", + ); + assert.equal(review.checks.artifact_inspector.status, "review_required"); + assert.equal( + review.checks.artifact_inspector.design_system_review.primary_artifact, + "external_not_reviewed", + ); + assert.deepEqual(review.findings.map((entry) => entry.code), [ + "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING", + ]); + assert.equal( + review.checks.artifact_inspector.findings[0].evidence + .candidate_authored_evidence_accepted, + false, + "A candidate-authored trusted-looking receipt must be ignored.", + ); + assert.ok( + review.generation_gates.some( + (gate) => + gate.id === "artifact_inspector_authority_gate" && + gate.status === "review_required", + ), + ); + + const chromeRawControlCandidate = artifactImplementationCandidate( + implementationContract, + ); + chromeRawControlCandidate.code = + '
'; + const chromeRawControlReview = reviewUiImplementationCandidate( + chromeRawControlCandidate, + { + contract: legacyContractWithoutVisualComposition, + implementation_contract: implementationContract, + surface_type: "artifact_inspector", + }, + ); + assert.equal( + chromeRawControlReview.checks.raw_controls.status, + "fail", + "JudgmentKit-owned chrome must remain subject to primitive enforcement.", + ); + assert.deepEqual(chromeRawControlReview.checks.raw_controls.detected, [ + "input", + ]); + + const escapingArtifactSelectorCandidate = artifactImplementationCandidate( + implementationContract, + ); + escapingArtifactSelectorCandidate.code += ` + + `; + const escapingArtifactSelectorReview = reviewUiImplementationCandidate( + escapingArtifactSelectorCandidate, + { + contract: legacyContractWithoutVisualComposition, + implementation_contract: implementationContract, + surface_type: "artifact_inspector", + }, + ); + assert.equal( + escapingArtifactSelectorReview.checks.design_system_provenance.status, + "fail", + "A selector that escapes the artifact root through a sibling combinator must remain inside the JudgmentKit provenance scan.", + ); + assert.ok( + escapingArtifactSelectorReview.checks.design_system_provenance + .detected_visual_tokens.includes("--mui-color-primary"), + ); + + const genericStatesOnlyCandidate = artifactImplementationCandidate( + implementationContract, + ); + genericStatesOnlyCandidate.states_covered = + implementationContract.state_coverage.required_states; + const genericStatesOnlyReview = reviewUiImplementationCandidate( + genericStatesOnlyCandidate, + { + contract: legacyContractWithoutVisualComposition, + implementation_contract: implementationContract, + surface_type: "artifact_inspector", + }, + ); + assert.equal(genericStatesOnlyReview.checks.state_coverage.status, "fail"); + assert.deepEqual( + genericStatesOnlyReview.checks.state_coverage.missing, + implementationContract.artifact_inspector.state_groups.core, + "Every state in each active Artifact Inspector state group must be implementation-review evidence, not metadata only.", + ); + + assert.throws( + () => + createUiImplementationContract({ + surface_type: "artifact_inspector", + design_system_adapter: completeExternalDesignSystemAdapter(), + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "design_system_source.mode"); + assert.equal(error.details.expected, "judgmentkit_default"); + assert.equal(error.details.observed, "external_design_system"); + assert.equal(error.details.external_artifact_scope, "primary_artifact"); + return true; + }, + ); +} + +{ + const untrustedReceipt = structuredClone(trustedReceipt); + untrustedReceipt.issuer = "candidate_authored"; + const review = reviewArtifactInspectorAuthorityEvidence(canonicalAuthority, { + trustedRuntimeEvidence: untrustedReceipt, + }); + assert.equal(review.status, "review_required"); + assert.ok( + review.findings.some( + (entry) => + entry.code === + "JK_ARTIFACT_INSPECTOR_TRUSTED_RUNTIME_EVIDENCE_MISSING" && + entry.evidence.field === "trusted_runtime_evidence.issuer", + ), + ); +} + +{ + const review = reviewArtifactInspectorAuthorityEvidence(canonicalAuthority, { + trustedRuntimeEvidence: trustedReceipt, + }); + assert.equal(review.status, "passed_with_external_artifact_authority"); + assert.deepEqual(review.design_system_review, { + inspector_chrome: "passed", + inspection_overlay: "passed", + primary_artifact: "external_not_reviewed", + boundary_contract: "passed", + }); + assert.equal(review.trusted_runtime_evidence.artifact_preservation, "passed"); + assert.deepEqual(review.findings, []); +} + +{ + const overclaimReceipt = structuredClone(trustedReceipt); + overclaimReceipt.scope_results.primary_artifact.status = "pass"; + const review = reviewArtifactInspectorAuthorityEvidence(canonicalAuthority, { + trustedRuntimeEvidence: overclaimReceipt, + }); + assert.equal(review.status, "fail"); + assert.equal( + review.design_system_review.primary_artifact, + "failed_external_authority_overclaim", + ); + assert.ok( + review.findings.some( + (entry) => + entry.code === + "JK_ARTIFACT_INSPECTOR_EXTERNAL_AUTHORITY_OVERCLAIM", + ), + ); +} + +{ + const mutationReceipt = structuredClone(trustedReceipt); + mutationReceipt.scope_results.primary_artifact.artifact_preservation = "fail"; + const review = reviewArtifactInspectorAuthorityEvidence(canonicalAuthority, { + trustedRuntimeEvidence: mutationReceipt, + }); + assert.equal(review.status, "fail"); + assert.ok( + review.findings.some( + (entry) => entry.code === "JK_ARTIFACT_INSPECTOR_ARTIFACT_MUTATED", + ), + ); +} + +const finding = createArtifactInspectorFinding({ + code: "JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED", + evidence: { + field: "[data-jk-scope='inspection-overlay']", + expected: "semantic relation to active locus", + observed: "detached panel", + }, +}); +assert.equal(finding.severity, "fail"); +assert.equal(finding.check, "artifact_inspector_authority"); +assert.equal(finding.code, "JK_ARTIFACT_INSPECTOR_CONTEXT_DETACHED"); +assert.equal(typeof finding.repair_instruction, "string"); +assert.ok(finding.repair_instruction.length > 0); +assert.throws( + () => createArtifactInspectorFinding({ code: "UNKNOWN_DIAGNOSTIC" }), + /Unknown Artifact Inspector diagnostic code/, +); + +console.log("artifact inspector authority checks passed."); diff --git a/tests/artifact-inspector-routing.test.mjs b/tests/artifact-inspector-routing.test.mjs new file mode 100644 index 0000000..ea48d10 --- /dev/null +++ b/tests/artifact-inspector-routing.test.mjs @@ -0,0 +1,457 @@ +import assert from "node:assert/strict"; + +import { + recommendSurfaceTypes, + recommendUiWorkflowProfiles, +} from "../src/index.mjs"; + +const MANDATORY_EVIDENCE_IDS = [ + "rendered_artifact_is_primary", + "locus_selection_required", + "support_context_is_locus_relative", +]; + +const ARTIFACT_ACTIVITY = ` + An accessibility specialist inspects one rendered interface that remains the + primary artifact during an accessibility audit. They must select a specific + control inside the artifact. Evidence and corrective actions attach to that + selected control. They preview the correction, decide whether it is supported, + complete the correction, and verify the local result beside the artifact. The + outcome is a verified local result and a handoff receipt for the accessibility + team. The artifact retains its own independent visual authority while + implementation details remain diagnostic. +`; + +function artifactEvidence(review) { + return review.evidence.artifact_inspector; +} + +function surfaceScore(review, surfaceType) { + return review.evidence.surface_type_scores.find( + (entry) => entry.surface_type === surfaceType, + ); +} + +function workflowProfile(review, profileId) { + return review.recommendations.find( + (entry) => entry.profile_id === profileId, + ); +} + +{ + const review = recommendSurfaceTypes(ARTIFACT_ACTIVITY); + const evidence = artifactEvidence(review); + const score = surfaceScore(review, "artifact_inspector"); + + assert.equal(review.status, "ready"); + assert.equal(review.recommended_surface_type, "artifact_inspector"); + assert.equal(review.profile_id, "artifact-inspector-ui"); + assert.equal( + review.primary_structure, + "persistent artifact, selectable loci, contextual support, local feedback, and artifact-local completion", + ); + assert.equal(review.density, "artifact-first contextual"); + assert.equal( + review.navigation_shape, + "artifact persistent; supporting context revealed from the active locus", + ); + assert.equal(review.routing_conflict, undefined); + assert.deepEqual(evidence.mandatory_evidence_ids, MANDATORY_EVIDENCE_IDS); + assert.deepEqual(evidence.matched_mandatory_evidence, MANDATORY_EVIDENCE_IDS); + assert.deepEqual(evidence.missing_mandatory_evidence, []); + assert.equal(evidence.status, "matched"); + assert.equal(evidence.conflict, false); + assert.equal(score.profile_id, "artifact-inspector-ui"); + assert.equal(score.profile_status, "recommended"); + assert.ok( + review.interaction_implications.primary_structure.includes( + "Persistent artifact", + ), + ); + + const profiles = recommendUiWorkflowProfiles(ARTIFACT_ACTIVITY); + const artifactProfile = workflowProfile(profiles, "artifact-inspector-ui"); + assert.deepEqual(profiles.recommended_profile_ids, ["artifact-inspector-ui"]); + assert.equal(artifactProfile.status, "recommended"); + assert.equal(artifactProfile.pattern_id, "artifact-inspector"); + assert.equal(artifactProfile.trigger_match_count, 3); + assert.deepEqual( + artifactProfile.matched_triggers.slice(0, 3), + MANDATORY_EVIDENCE_IDS, + ); +} + +const ADDITIONAL_POSITIVE_FIXTURES = [ + ` + A fact checker inspects one rendered document that remains the primary + artifact. They must select a passage inside the document. A source-backed + correction attaches to that selected passage. They preview the correction + and verify the local result beside the passage. + `, + ` + An analyst inspects one rendered diagram that remains the primary artifact. + They must select a specific node. The rule and evidence that produced it + attach to that selected node. They preview the relation and verify the local + result beside the diagram. + `, + ` + A designer inspects one rendered composition that remains the primary + artifact. They must mark a specific region. Evidence and a bounded change + attach to that selected region. They preview the change and verify its local + result beside the composition. + `, +]; + +for (const fixture of ADDITIONAL_POSITIVE_FIXTURES) { + const first = recommendSurfaceTypes(fixture); + const second = recommendSurfaceTypes(fixture); + + assert.equal(first.recommended_surface_type, "artifact_inspector"); + assert.deepEqual( + artifactEvidence(first).matched_mandatory_evidence, + MANDATORY_EVIDENCE_IDS, + ); + assert.deepEqual( + second, + first, + "Repeated routing from the same fixture must remain deterministic.", + ); +} + +const NEGATIVE_ROUTING_FIXTURES = [ + [ + "operator_review", + `A release lead reviews a queue of AI-generated cases. The queue is the + primary work surface. They compare evidence and risk, approve, reject, + return, or hand off each case, and leave an audit receipt.`, + ], + [ + "workbench", + `A studio lead uses a creation workspace with a canvas and persistent tools + to compare multiple objects, decide their arrangement, edit the + composition, and leave a project handoff.`, + ], + [ + "content_report", + `A policy analyst reads a linear evidence memo. The activity is + understanding the narrative, citing references, and sharing the memo. + Completion means reading and sharing the report.`, + ], + [ + "conversation", + `A support agent handles an open-ended live chat with attachments. + Participants send messages, reply with context, and keep the conversation + thread active until it can be closed.`, + ], + [ + "dashboard_monitor", + `An operations lead monitors a status dashboard across many items. The + activity is tracking health, trends, and alerts. Completion means status + awareness and knowing whether follow-up is needed.`, + ], + [ + "form_flow", + `An intake coordinator completes a structured submission form. The activity + is entering required fields, resolving validation errors, submitting the + record, and seeing confirmation.`, + ], + [ + "form_flow", + `An operations admin updates a structured record-entry form while an + incidental rendered artifact preview remains visible. The activity is + entering required fields, resolving validation errors, submitting the + record, and seeing confirmation; no artifact locus is selected.`, + ], + [ + "setup_debug_tool", + `A platform engineer uses a setup and debugging tool for an integration. + The activity is configuring connection details, inspecting a JSON schema + and API endpoint, testing the connection, resolving setup errors, and + saving a validated configuration.`, + ], +]; + +for (const [expectedSurfaceType, fixture] of NEGATIVE_ROUTING_FIXTURES) { + const review = recommendSurfaceTypes(fixture); + + assert.equal(review.recommended_surface_type, expectedSurfaceType); + assert.notEqual(review.recommended_surface_type, "artifact_inspector"); + assert.equal(surfaceScore(review, "artifact_inspector"), undefined); +} + +{ + const incidentalSelectablePreview = ` + A clinic coordinator completes a structured intake form that is the primary + work surface. Completion means required field entry, validation, and + submission of the intake record. An incidental rendered-interface preview + remains visible; the coordinator may select a control in that preview and + supporting evidence attaches to the selected control, but the preview does + not determine completion. + `; + const first = recommendSurfaceTypes(incidentalSelectablePreview); + const second = recommendSurfaceTypes(incidentalSelectablePreview); + const profiles = recommendUiWorkflowProfiles(incidentalSelectablePreview); + const evidence = artifactEvidence(first); + const renderedPrimary = evidence.positives.find( + (entry) => entry.id === "rendered_artifact_is_primary", + ); + + assert.equal(first.recommended_surface_type, "form_flow"); + assert.notEqual(first.recommended_surface_type, "artifact_inspector"); + assert.equal(renderedPrimary.matched, false); + assert.ok( + evidence.missing_mandatory_evidence.includes( + "rendered_artifact_is_primary", + ), + ); + assert.ok( + !profiles.recommended_profile_ids.includes("artifact-inspector-ui"), + ); + assert.deepEqual( + second, + first, + "An incidental selectable preview must route deterministically.", + ); +} + +{ + const partialActivity = ` + Build an artifact inspector for one rendered interface that remains the + primary artifact. The specialist must select a specific control. Supporting + reference material is shown in a detached global library with no relation to + the selected control. + `; + const review = recommendSurfaceTypes(partialActivity); + const evidence = artifactEvidence(review); + + assert.ok(evidence, "Explicit inspector intent should expose partial evidence."); + assert.equal(evidence.status, "partial"); + assert.equal(evidence.mandatory_evidence_ids.length, 3); + assert.ok( + evidence.missing_mandatory_evidence.includes( + "support_context_is_locus_relative", + ), + ); + assert.notEqual(review.recommended_surface_type, "artifact_inspector"); + assert.equal( + surfaceScore(review, "artifact_inspector").profile_status, + "not_recommended", + ); + + const profiles = recommendUiWorkflowProfiles(partialActivity); + assert.equal( + workflowProfile(profiles, "artifact-inspector-ui").status, + "not_recommended", + ); + assert.ok(!profiles.recommended_profile_ids.includes("artifact-inspector-ui")); +} + +{ + const threadWordOnly = `${ARTIFACT_ACTIVITY} + The verified receipt is also named in the project thread for later follow-up. + `; + const review = recommendSurfaceTypes(threadWordOnly); + const evidence = artifactEvidence(review); + const conversationExclusion = evidence.exclusions.find( + (entry) => entry.id === "conversation_turns_are_primary", + ); + + assert.equal(review.recommended_surface_type, "artifact_inspector"); + assert.equal(conversationExclusion.matched, false); + assert.equal( + evidence.matched_exclusion_evidence.includes( + "conversation_turns_are_primary", + ), + false, + ); + assert.equal(surfaceScore(review, "conversation").trigger_match_count, 0); +} + +const EXCLUSION_CASES = [ + [ + "queue_or_case_is_primary", + "A review queue is the primary work surface and the team works through that queue.", + ], + [ + "many_items_are_primary", + "Multiple artifacts are primary peers and must be reviewed side by side.", + ], + [ + "open_ended_creation_is_primary", + "The primary activity is freely creating and editing the artifact.", + ], + [ + "conversation_turns_are_primary", + "The activity is an open-ended live chat where participants send messages, reply with context, and keep the conversation thread active.", + ], + [ + "linear_reading_is_completion", + "Completion means reading, understanding, citing, and sharing the report.", + ], + [ + "monitoring_is_completion", + "The primary activity is monitoring status and tracking trends.", + ], + [ + "configuration_is_completion", + "The primary activity is configuration and debugging; completion means a valid setup.", + ], +]; + +for (const [exclusionId, conflictingStatement] of EXCLUSION_CASES) { + const conflictingBrief = `${ARTIFACT_ACTIVITY}\n${conflictingStatement}`; + const review = recommendSurfaceTypes(conflictingBrief); + const evidence = artifactEvidence(review); + + assert.equal( + review.status, + "review_required", + `${exclusionId} must force explicit routing review.`, + ); + assert.equal( + review.recommended_surface_type, + null, + `${exclusionId} must not be resolved by a keyword tie-break.`, + ); + assert.equal(review.routing_conflict.status, "review_required"); + assert.deepEqual( + review.routing_conflict.matched_mandatory_evidence, + MANDATORY_EVIDENCE_IDS, + ); + assert.ok( + review.routing_conflict.matched_exclusion_evidence.includes(exclusionId), + `${exclusionId} must appear in the conflict packet.`, + ); + assert.equal(evidence.status, "review_required"); + assert.equal(evidence.conflict, true); + + const profiles = recommendUiWorkflowProfiles(conflictingBrief); + const artifactProfile = workflowProfile(profiles, "artifact-inspector-ui"); + assert.equal(artifactProfile.status, "review_required"); + assert.ok(artifactProfile.matched_exclusions.includes(exclusionId)); + assert.deepEqual(profiles.review_required_profile_ids, [ + "artifact-inspector-ui", + ]); + assert.ok(!profiles.recommended_profile_ids.includes("artifact-inspector-ui")); +} + +const SECONDARY_ARTIFACT_MARKETING_BRIEFS = [ + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer, proof, and benefits, then helping visitors + request a demo. A secondary rendered interface remains visible. Visitors may + select a specific control, and supporting evidence attaches to that selected + control, but the interface only supports the offer. Completion is a qualified + demo request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface remains visible as supporting context. Visitors may select + a control and evidence attaches there, but completion is the demo request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface remains visible but is secondary to the offer. Visitors + may select a control and evidence attaches there. Completion is the request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface serves only as supporting context. Visitors may select a + control and evidence attaches there. Completion is the request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface remains visible only as supporting context. Visitors may + select a control and evidence attaches there. Completion is the request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface is merely supporting context. Visitors may select a + control and evidence attaches there. Completion is the request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface remains visible; it only supports the offer. Visitors may + select a control and evidence attaches there. Completion is the request. + `, + ` + A growth marketer creates a public landing page for prospects. The primary + activity is explaining the offer and helping visitors request a demo. A + rendered interface supports the offer rather than serving as the primary + object. Visitors may select a control and evidence attaches there. + `, +]; + +for (const secondaryArtifactMarketingBrief of SECONDARY_ARTIFACT_MARKETING_BRIEFS) { + const review = recommendSurfaceTypes(secondaryArtifactMarketingBrief); + const evidence = artifactEvidence(review); + + assert.equal(review.recommended_surface_type, "marketing"); + if (evidence) { + const renderedPrimary = evidence.positives.find( + (entry) => entry.id === "rendered_artifact_is_primary", + ); + assert.equal(renderedPrimary.matched, false); + assert.equal( + evidence.matched_mandatory_evidence.includes( + "rendered_artifact_is_primary", + ), + false, + ); + assert.ok( + evidence.missing_mandatory_evidence.includes( + "rendered_artifact_is_primary", + ), + ); + assert.equal(surfaceScore(review, "artifact_inspector").score, 0); + } else { + assert.equal(surfaceScore(review, "artifact_inspector"), undefined); + } +} + +{ + const review = recommendSurfaceTypes(`${ARTIFACT_ACTIVITY} + A separate secondary rendered diagram remains visible as supporting context. + `); + + assert.equal(review.recommended_surface_type, "artifact_inspector"); + assert.deepEqual( + artifactEvidence(review).matched_mandatory_evidence, + MANDATORY_EVIDENCE_IDS, + "A distinct secondary artifact must not erase an explicitly primary rendered artifact.", + ); +} + +{ + const legacyMarketingBrief = ` + A growth marketer is creating a public landing page for prospects. The + activity is explaining the offer and proof, then helping visitors request a + demo. The outcome is a qualified demo request. + `; + const review = recommendSurfaceTypes(legacyMarketingBrief); + + assert.equal(review.recommended_surface_type, "marketing"); + assert.equal(review.evidence.artifact_inspector, undefined); + assert.deepEqual( + review.evidence.surface_type_scores.map((entry) => entry.surface_type), + [ + "marketing", + "workbench", + "operator_review", + "form_flow", + "dashboard_monitor", + "content_report", + "setup_debug_tool", + "conversation", + ], + "Unrelated legacy routing must not gain an Artifact Inspector score.", + ); +} + +console.log("artifact inspector routing checks passed."); diff --git a/tests/artifact-inspector-surface-profile.test.mjs b/tests/artifact-inspector-surface-profile.test.mjs new file mode 100644 index 0000000..e644b13 --- /dev/null +++ b/tests/artifact-inspector-surface-profile.test.mjs @@ -0,0 +1,265 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; + +import { + ARTIFACT_INSPECTOR_SURFACE_PROFILE, + ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + WORKBENCH_SURFACE_PROFILE, + cloneArtifactInspectorSurfaceProfile, + cloneSurfacePresentationProfile, + cloneWorkbenchSurfaceProfile, + getSurfacePresentationProfile, + listSurfacePresentationProfiles, +} from "../src/surface-presentation-profiles.mjs"; + +const EXPECTED_PROFILE_ID = "judgmentkit.artifact-inspector.v1"; +const EXPECTED_COMPONENT_ROLES = [ + "ArtifactViewport", + "ArtifactBoundary", + "ArtifactStatus", + "ObservationMarker", + "LocusSelection", + "ContextAnchor", + "AuthorityAnchor", + "AnchorRail", + "RelationPreview", + "RelationConnector", + "InlineReason", + "ContextTray", + "CommitBoundary", + "ReceiptMarker", + "BackAction", + "ResetAction", + "ZoomAndPanControls", +]; +const EXPECTED_CORE_STATES = [ + "artifact loading", + "artifact ready", + "artifact unavailable or failed", + "no active locus", + "locus focused or selected", + "context loading", + "context ready", + "relation or action preview", + "supported", + "incompatible", + "unavailable", + "stale", + "ambiguous", + "blocked", + "cancelled or Back", + "local error and retry", +]; + +function profileHash(profile) { + return createHash("sha256") + .update(JSON.stringify(profile)) + .digest("hex"); +} + +function scope(profile, scopeId) { + return profile.authority.design_system_scopes.find( + (entry) => entry.scope_id === scopeId, + ); +} + +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE_ID, + EXPECTED_PROFILE_ID, +); +assert.equal(ARTIFACT_INSPECTOR_SURFACE_PROFILE.id, EXPECTED_PROFILE_ID); +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.surface_type, + "artifact_inspector", +); +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.workflow_profile_id, + "artifact-inspector-ui", +); +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.interaction_topology_kind, + "artifact_centered", +); +assert.equal(ARTIFACT_INSPECTOR_SURFACE_PROFILE.status, "proposed"); +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.provenance.promotion_status, + "proposed", +); +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.composition.hierarchy, + "artifact_dominant", +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.composition.rules.some((rule) => + rule.includes("largest and most persistent visual region"), + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.composition.avoid_by_default.includes( + "master-detail", + ), +); +assert.deepEqual( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.state_coverage, + EXPECTED_CORE_STATES, +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.responsive.wide.includes( + "artifact central and persistent", + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.responsive.narrow.includes( + "artifact primary", + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.responsive.invariant.includes( + "same state machine", + ), +); +assert.deepEqual( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.component_roles.map((role) => role.id), + EXPECTED_COMPONENT_ROLES, +); + +const chromeScope = scope( + ARTIFACT_INSPECTOR_SURFACE_PROFILE, + "inspector_chrome", +); +const overlayScope = scope( + ARTIFACT_INSPECTOR_SURFACE_PROFILE, + "inspection_overlay", +); +const artifactScope = scope( + ARTIFACT_INSPECTOR_SURFACE_PROFILE, + "primary_artifact", +); +const boundary = + ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority.boundary_contracts[0]; + +assert.deepEqual(chromeScope, { + scope_id: "inspector_chrome", + root_selector: "[data-jk-scope='inspector-chrome']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", +}); +assert.deepEqual(overlayScope, { + scope_id: "inspection_overlay", + root_selector: "[data-jk-scope='inspection-overlay']", + authority: "judgmentkit_default", + enforcement: "required", + style_isolation: "required", +}); +assert.deepEqual(artifactScope, { + scope_id: "primary_artifact", + root_selector: "[data-artifact-root]", + authority: "external_declared", + enforcement: "external_not_reviewed", + style_isolation: "required", +}); +assert.deepEqual(boundary, { + from_scope: "inspection_overlay", + to_scope: "primary_artifact", + allowed_roles: [ + "locus_target", + "annotation_overlay", + "connector_endpoint", + ], + event_contract_required: true, +}); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority.review_result_policy.includes( + "never describe the external artifact as JudgmentKit-conformant", + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.evidence_expectations.some((entry) => + entry.includes("style isolation in both directions"), + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.evidence_expectations.some((entry) => + entry.includes("artifact render fingerprint"), + ), +); +assert.ok( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.evidence_expectations.some((entry) => + entry.includes("Report chrome conformance") && + entry.includes("external artifact preservation"), + ), +); + +assert.ok(Object.isFrozen(ARTIFACT_INSPECTOR_SURFACE_PROFILE)); +assert.ok(Object.isFrozen(ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority)); +assert.ok( + Object.isFrozen( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority.design_system_scopes, + ), +); +assert.ok(Object.isFrozen(artifactScope)); +assert.ok( + Object.isFrozen(ARTIFACT_INSPECTOR_SURFACE_PROFILE.component_roles[0]), +); + +const clonedArtifactProfile = cloneArtifactInspectorSurfaceProfile(); +const clonedById = cloneSurfacePresentationProfile(EXPECTED_PROFILE_ID); +const lookedUpProfile = getSurfacePresentationProfile( + ` ${EXPECTED_PROFILE_ID} `, +); + +for (const candidate of [clonedArtifactProfile, clonedById, lookedUpProfile]) { + assert.deepEqual(candidate, ARTIFACT_INSPECTOR_SURFACE_PROFILE); + assert.notStrictEqual(candidate, ARTIFACT_INSPECTOR_SURFACE_PROFILE); + assert.notStrictEqual( + candidate.authority, + ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority, + ); + assert.notStrictEqual( + candidate.authority.design_system_scopes, + ARTIFACT_INSPECTOR_SURFACE_PROFILE.authority.design_system_scopes, + ); +} + +clonedArtifactProfile.name = "Mutated clone"; +clonedArtifactProfile.authority.design_system_scopes[0].authority = + "mutated-authority"; +assert.equal( + ARTIFACT_INSPECTOR_SURFACE_PROFILE.name, + "JudgmentKit Artifact Inspector", +); +assert.equal( + scope(ARTIFACT_INSPECTOR_SURFACE_PROFILE, "inspector_chrome").authority, + "judgmentkit_default", +); +assert.equal(cloneSurfacePresentationProfile("unknown.profile"), null); +assert.equal(getSurfacePresentationProfile(null), null); + +const listedProfiles = listSurfacePresentationProfiles(); +assert.deepEqual( + listedProfiles.map((profile) => profile.id), + [WORKBENCH_SURFACE_PROFILE.id, EXPECTED_PROFILE_ID], +); +assert.notStrictEqual(listedProfiles[0], WORKBENCH_SURFACE_PROFILE); +assert.notStrictEqual( + listedProfiles[1], + ARTIFACT_INSPECTOR_SURFACE_PROFILE, +); +listedProfiles[1].component_roles[0].id = "MutatedRole"; +assert.equal( + listSurfacePresentationProfiles()[1].component_roles[0].id, + "ArtifactViewport", +); + +const clonedWorkbench = cloneWorkbenchSurfaceProfile(); +assert.deepEqual(clonedWorkbench, WORKBENCH_SURFACE_PROFILE); +assert.notStrictEqual(clonedWorkbench, WORKBENCH_SURFACE_PROFILE); +assert.equal(JSON.stringify(WORKBENCH_SURFACE_PROFILE).length, 5463); +assert.equal( + profileHash(WORKBENCH_SURFACE_PROFILE), + "e602ac05657defdfd247221b3d97a9e9454460c28202b1e47475f3d58cf56dbb", + "The existing Workbench profile must remain byte-compatible.", +); + +console.log("artifact inspector surface profile checks passed."); diff --git a/tests/artifact-inspector-workflow.test.mjs b/tests/artifact-inspector-workflow.test.mjs new file mode 100644 index 0000000..d8da6be --- /dev/null +++ b/tests/artifact-inspector-workflow.test.mjs @@ -0,0 +1,1954 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { + JudgmentKitInputError, + buildUiWorkflowCandidateRequest, + createActivityModelReview, + createFrontendGenerationContext, + createFrontendImplementationSkillContext, + createUiGenerationHandoff, + createUiImplementationContract, + recommendSurfaceTypes, + reviewUiWorkflowCandidate, +} from "../src/index.mjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, ".."); +const contract = JSON.parse( + fs.readFileSync( + path.join(root, "contracts/ai-ui-generation.activity-contract.json"), + "utf8", + ), +); +const model = contract.interaction_models.artifact_inspector; + +const ARTIFACT_ACTIVITY = ` + An accessibility specialist inspects one rendered interface that remains the + primary artifact during an accessibility audit. They must select a specific + control inside the artifact. Evidence and corrective actions attach to that + selected control. They preview the correction, decide whether it is supported, + complete the correction, and verify the local result beside the artifact. The + outcome is a verified local result and a handoff receipt for the accessibility + team. The artifact retains its own independent visual authority while + implementation details remain diagnostic. +`; + +const WORKBENCH_ACTIVITY = ` + A dispatch lead repeatedly reviews service exceptions in a work queue, + compares route and customer evidence, decides whether to reassign, hold, or + escalate each visit, and leaves a handoff receipt for the next owner. +`; + +const activityReview = createActivityModelReview(ARTIFACT_ACTIVITY); +const surfaceReview = recommendSurfaceTypes(ARTIFACT_ACTIVITY, { + activity_review: activityReview, +}); + +assert.equal(activityReview.review_status, "ready_for_review"); +assert.equal(surfaceReview.status, "ready"); +assert.equal(surfaceReview.recommended_surface_type, "artifact_inspector"); + +function createCandidate() { + return { + workflow: { + surface_name: "Accessibility review", + topology: structuredClone(model.topology), + work_units: structuredClone(model.work_units), + artifact: { + id: "artifact", + boundary: "Rendered interface under audit", + }, + target_model: { + ...structuredClone(model.target_model), + artifact_id: "artifact", + }, + state_groups: { + core: structuredClone(model.state_groups.core), + }, + primary_actions: [ + "Select control", + "Preview correction", + "Apply correction", + "Verify result", + "Back", + ], + decision_points: ["Is the correction supported?"], + completion_state: + "Verified result is visible beside the selected control.", + }, + surface_set: [ + { + name: "Accessibility review", + purpose: "Inspect the rendered interface in place.", + sections: [ + "Rendered interface", + "Control context", + "Local result", + ], + controls: [ + "Select control", + "Preview correction", + "Apply correction", + "Back", + ], + relationship_to_workflow: + "Keeps context and the local result attached to the interface.", + }, + ], + handoff: { + next_owner: "Accessibility team", + reason: "Correction was verified locally.", + next_action: "Review the receipt.", + }, + diagnostics: { + implementation_terms: [], + reveal_contexts: ["auditing"], + }, + }; +} + +function createWorkbenchWorkflowReview() { + const workbenchActivityReview = createActivityModelReview( + WORKBENCH_ACTIVITY, + ); + const workbenchSurfaceReview = recommendSurfaceTypes(WORKBENCH_ACTIVITY, { + activity_review: workbenchActivityReview, + }); + const review = reviewUiWorkflowCandidate( + WORKBENCH_ACTIVITY, + { + workflow: { + surface_name: "Service exception workspace", + topology: "workspace", + work_units: [ + "Inspect evidence", + "Choose action", + "Leave handoff", + ], + primary_actions: ["Reassign", "Hold", "Escalate"], + decision_points: ["Choose the next operational action."], + completion_state: "The next owner receives a reasoned handoff.", + }, + surface_set: [ + { + name: "Service exception workspace", + purpose: + "Keep the selected exception, evidence, decision, and handoff together.", + sections: [ + "Work queue", + "Detail workspace", + "Evidence", + "Handoff", + ], + controls: [ + "Select exception", + "Choose action", + "Complete handoff", + ], + relationship_to_workflow: + "Supports repeated exception review.", + }, + ], + product_terms: ["Exception", "Evidence", "Handoff"], + handoff: { + next_owner: "dispatch coordinator", + reason: "The exception needs the selected operational action.", + next_action: "Complete the handoff.", + }, + diagnostics: { + implementation_terms: [], + reveal_contexts: ["debugging", "auditing"], + }, + }, + { + activity_review: workbenchActivityReview, + surface_review: workbenchSurfaceReview, + }, + ); + + assert.equal(review.review_status, "ready_for_review"); + assert.equal(review.surface_type, "workbench"); + return review; +} + +function completeExternalDesignSystemAdapter() { + return { + design_system_name: "External UI", + design_system_package: "@example/ui", + token_guidance: { + token_families: ["color"], + css_custom_properties: [ + { + name: "--external-surface", + role: "surface", + family: "color", + value: "#ffffff", + usage: "External UI surfaces", + }, + ], + }, + font_guidance: { + font_roles: { + body: { + stack: "system-ui", + usage: "External UI body typography", + }, + }, + }, + icon_guidance: { + icon_roles: ["status"], + icon_catalog: { + source: "external_design_system", + library: "example-icons", + package: "@example/icons", + version: "1.0.0", + icon_count: 1, + license: "MIT", + notice: "Repo-approved external icon adapter.", + mcp_tools: [], + }, + }, + components: ["Button"], + }; +} + +function disguiseExternalDesignAuthority(implementationContract) { + implementationContract.design_system_source = { + ...implementationContract.design_system_source, + mode: "judgmentkit_default", + id: "external-ui.source-v1", + name: "External UI", + package: "@example/ui", + definition_point: "caller_serialized_contract", + token_prefixes: ["--external-"], + renderer_components: ["ExternalButton"], + }; + implementationContract.visual_token_adapter = { + ...implementationContract.visual_token_adapter, + css_custom_properties: [ + { + name: "--external-surface", + role: "surface", + family: "color", + value: "#ffffff", + usage: "External UI surfaces", + }, + ], + }; + implementationContract.default_ai_native_design_system = { + ...implementationContract.default_ai_native_design_system, + component_contracts: [ + { + ...implementationContract.default_ai_native_design_system + .component_contracts[0], + id: "ExternalButton", + label: "External button", + purpose: "Supply external chrome authority", + }, + ], + }; +} + +function reviewCandidate(candidate) { + return reviewUiWorkflowCandidate(ARTIFACT_ACTIVITY, candidate, { + activity_review: activityReview, + profile_id: "artifact-inspector-ui", + surface_review: surfaceReview, + }); +} + +const validWorkflowReview = reviewCandidate(createCandidate()); + +{ + const contradictoryReview = structuredClone(validWorkflowReview); + contradictoryReview.candidate.workflow.topology = "workspace"; + + assert.throws( + () => createUiGenerationHandoff(contradictoryReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal(error.details.field, "workflow.topology"); + assert.equal(error.details.topology, "workspace"); + assert.equal( + error.details.topology_contract_kind, + "artifact_centered", + ); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + ); + return true; + }, + "Handoff must reject contradictory serialized topology representations.", + ); + + const objectOnlyTopologyReview = structuredClone(validWorkflowReview); + objectOnlyTopologyReview.candidate.workflow.topology = structuredClone( + objectOnlyTopologyReview.candidate.workflow.topology_contract, + ); + delete objectOnlyTopologyReview.candidate.workflow.topology_contract; + assert.throws( + () => createUiGenerationHandoff(objectOnlyTopologyReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal( + error.details.field, + "artifact_inspector.boundary_consistency", + ); + assert.equal(error.details.observed.topology, "artifact_centered"); + assert.equal(error.details.observed.topology_representation, "object"); + assert.equal(error.details.observed.topology_contract_kind, null); + assert.equal(error.details.observed.canonical_topology_shape, false); + return true; + }, + "Handoff must reject an object-only Artifact Inspector topology that its serializer would drop.", + ); + + const missingArtifactReview = structuredClone(validWorkflowReview); + delete missingArtifactReview.candidate.workflow.artifact; + assert.throws( + () => createUiGenerationHandoff(missingArtifactReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal(error.details.field, "workflow.artifact"); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + ); + assert.equal( + error.details.reason, + "artifact_inspector_required_structure_invalid", + ); + return true; + }, + "Handoff must reject a ready packet whose reviewed artifact identity was removed after review.", + ); + + const contradictoryProfileReview = structuredClone(validWorkflowReview); + contradictoryProfileReview.guidance_profile.profile_id = + "operator-review-ui"; + assert.throws( + () => createUiGenerationHandoff(contradictoryProfileReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal( + error.details.field, + "artifact_inspector.boundary_consistency", + ); + assert.deepEqual(error.details.observed.workflow_profile_ids, [ + "operator-review-ui", + ]); + assert.equal( + error.details.expected.workflow_profile, + "artifact-inspector-ui", + ); + return true; + }, + "Handoff must reject a conflicting profile id on an otherwise canonical Artifact Inspector review.", + ); +} + +{ + const workbenchReview = createWorkbenchWorkflowReview(); + const artifactContract = createUiImplementationContract({ + surface_type: "artifact_inspector", + }).implementation_contract; + + assert.throws( + () => + createUiGenerationHandoff(workbenchReview, { + implementation_contract: artifactContract, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal( + error.details.field, + "artifact_inspector.boundary_consistency", + ); + assert.deepEqual(error.details.observed.surface_types, ["workbench"]); + assert.equal(error.details.observed.topology, "workspace"); + assert.equal(error.details.observed.authority_bundle_active, true); + return true; + }, + "Handoff must reject Artifact Inspector authority attached to a Workbench workflow.", + ); + + const workbenchHandoff = createUiGenerationHandoff(workbenchReview); + const authorityLeakingHandoff = structuredClone(workbenchHandoff); + authorityLeakingHandoff.implementation_contract = structuredClone( + artifactContract, + ); + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: authorityLeakingHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "frontend_context_blocked"); + assert.deepEqual(error.details.observed.surface_types, ["workbench"]); + assert.equal(error.details.observed.topology, "workspace"); + assert.equal(error.details.observed.authority_bundle_active, true); + return true; + }, + "Frontend generation must reject Artifact Inspector authority attached to serialized Workbench handoff.", + ); + + const workbenchFrontendContext = createFrontendGenerationContext({ + ui_generation_handoff: workbenchHandoff, + }); + const authorityLeakingFrontendContext = structuredClone( + workbenchFrontendContext, + ); + authorityLeakingFrontendContext.implementation_contract = structuredClone( + artifactContract, + ); + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: authorityLeakingFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.deepEqual(error.details.observed.surface_types, ["workbench"]); + assert.equal(error.details.observed.topology, "workspace"); + assert.equal(error.details.observed.authority_bundle_active, true); + return true; + }, + "Skill compilation must reject Artifact Inspector authority attached to serialized Workbench context.", + ); +} + +{ + const review = validWorkflowReview; + const artifactGuardrail = review.guardrails.artifact_inspector; + + assert.equal(review.review_status, "ready_for_review"); + assert.equal(artifactGuardrail.selected, true); + assert.equal(artifactGuardrail.valid, true); + assert.deepEqual(artifactGuardrail.diagnostics, []); + assert.equal(artifactGuardrail.surface_type, "artifact_inspector"); + assert.equal(artifactGuardrail.workflow_profile, "artifact-inspector-ui"); + assert.equal(artifactGuardrail.topology_kind, "artifact_centered"); + + assert.equal(review.candidate.workflow.topology, "artifact_centered"); + assert.deepEqual( + review.candidate.workflow.topology_contract, + model.topology, + "The structured candidate topology must remain available beside the compatibility string.", + ); + assert.deepEqual( + review.candidate.workflow.work_units.map((entry) => entry.id), + [ + "orient", + "select_locus", + "inspect_context", + "preview", + "validate", + "complete", + "verify_result", + "recover", + ], + ); + assert.equal(review.candidate.workflow.artifact.id, "artifact"); + assert.equal( + review.candidate.workflow.target_model.artifact_id, + "artifact", + ); + assert.deepEqual( + review.candidate.workflow.target_model.target_kinds, + model.target_model.target_kinds, + ); + assert.deepEqual( + Object.keys(review.candidate.workflow.state_groups), + ["core"], + "Conditional state groups must remain omitted when the activity does not use them.", + ); +} + +{ + const topologyDerivedReview = reviewUiWorkflowCandidate( + ARTIFACT_ACTIVITY, + createCandidate(), + { activity_review: activityReview }, + ); + const handoff = createUiGenerationHandoff(topologyDerivedReview); + + assert.equal(topologyDerivedReview.review_status, "ready_for_review"); + assert.equal(topologyDerivedReview.surface_type, "artifact_inspector"); + assert.equal( + topologyDerivedReview.surface_guidance.recommended_surface_type, + "artifact_inspector", + ); + assert.equal( + topologyDerivedReview.surface_guidance.confidence, + "validated_workflow", + ); + assert.equal(handoff.surface_type, "artifact_inspector"); + assert.deepEqual( + handoff.implementation_contract.design_system_scopes, + model.design_system.scopes, + "Validated artifact topology must carry scoped authority even without caller-supplied surface evidence.", + ); + + const emptySurfaceReview = reviewUiWorkflowCandidate( + ARTIFACT_ACTIVITY, + createCandidate(), + { + activity_review: activityReview, + surface_review: {}, + }, + ); + assert.equal(emptySurfaceReview.review_status, "ready_for_review"); + assert.equal(emptySurfaceReview.surface_type, "artifact_inspector"); + assert.equal( + createUiGenerationHandoff(emptySurfaceReview).surface_type, + "artifact_inspector", + "An empty optional surface review must not suppress canonical topology-derived routing.", + ); + + const readyReviewWithoutSurfaceMetadata = structuredClone( + topologyDerivedReview, + ); + delete readyReviewWithoutSurfaceMetadata.surface_type; + delete readyReviewWithoutSurfaceMetadata.surface_guidance; + const defensiveHandoff = createUiGenerationHandoff( + readyReviewWithoutSurfaceMetadata, + ); + assert.equal(defensiveHandoff.surface_type, "artifact_inspector"); + assert.deepEqual( + defensiveHandoff.implementation_contract.design_system_scopes, + model.design_system.scopes, + "Handoff must derive scoped authority from validated artifact topology even if surface metadata is omitted.", + ); + + const internallyConflictingReview = structuredClone(topologyDerivedReview); + internallyConflictingReview.surface_guidance = { + ...internallyConflictingReview.surface_guidance, + recommended_surface_type: "workbench", + }; + assert.throws( + () => createUiGenerationHandoff(internallyConflictingReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal(error.details.surface_type, "artifact_inspector"); + assert.equal(error.details.surface_guidance_type, "workbench"); + return true; + }, + "Handoff must reconcile both reviewed surface fields before deriving authority.", + ); + + const conflictingHandoffReview = structuredClone(topologyDerivedReview); + conflictingHandoffReview.surface_type = "workbench"; + conflictingHandoffReview.surface_guidance = { + ...conflictingHandoffReview.surface_guidance, + recommended_surface_type: "workbench", + }; + + assert.throws( + () => createUiGenerationHandoff(conflictingHandoffReview), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.equal(error.details.expected_surface_type, "artifact_inspector"); + assert.equal(error.details.observed_surface_type, "workbench"); + assert.equal(error.details.workflow_topology, "artifact_centered"); + return true; + }, + "Handoff must defensively reject a conflicting surface on an artifact-centered reviewed workflow.", + ); + + assert.throws( + () => + createUiGenerationHandoff(topologyDerivedReview, { + implementation_contract: "not-an-object", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "implementation_contract"); + return true; + }, + "Malformed caller contract input must not remove the scoped authority bundle.", + ); + + const readyReviewWithDuplicateWorkUnit = structuredClone( + topologyDerivedReview, + ); + readyReviewWithDuplicateWorkUnit.candidate.workflow.work_units.push({ + ...structuredClone( + readyReviewWithDuplicateWorkUnit.candidate.workflow.work_units[0], + ), + participant_intent: "Orient to an unrelated secondary artifact.", + }); + assert.throws( + () => createUiGenerationHandoff(readyReviewWithDuplicateWorkUnit), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + assert.deepEqual(error.details.duplicate_work_unit_ids, ["orient"]); + return true; + }, + "A serialized ready review cannot add a divergent duplicate work-unit id before handoff.", + ); +} + +{ + const conflictingActivity = `${ARTIFACT_ACTIVITY} + The primary activity is freely creating and editing the artifact. + `; + const strippedActivityReview = structuredClone(activityReview); + delete strippedActivityReview.review.evidence.artifact_inspector; + delete strippedActivityReview.guardrails.artifact_inspector; + const review = reviewUiWorkflowCandidate( + conflictingActivity, + createCandidate(), + { + activity_review: strippedActivityReview, + profile_id: "artifact-inspector-ui", + }, + ); + const diagnostic = review.guardrails.artifact_inspector.diagnostics.find( + (entry) => + entry.code === "JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT", + ); + + assert.equal(review.review_status, "needs_source_context"); + assert.equal(review.guardrails.artifact_inspector.valid, false); + assert.ok( + diagnostic, + "Grounded source evidence must restore a routing conflict stripped from supplied review metadata.", + ); + assert.equal(diagnostic.field, "artifact_inspector.routing"); + assert.equal(diagnostic.observed.activity_conflict, true); + assert.ok( + diagnostic.observed.matched_exclusion_evidence.includes( + "open_ended_creation_is_primary", + ), + ); +} + +{ + const legacyQueueActivity = ` + A support lead reviews refund cases in a shared queue during daily triage. + They decide whether each case should be approved, escalated to policy, or + returned for missing evidence. Completion is a clear case handoff with a + reason and next owner. + `; + const legacyActivityReview = createActivityModelReview(legacyQueueActivity); + const review = reviewUiWorkflowCandidate( + legacyQueueActivity, + createCandidate(), + { activity_review: legacyActivityReview }, + ); + const diagnostic = review.guardrails.artifact_inspector.diagnostics.find( + (entry) => + entry.code === "JK_ARTIFACT_INSPECTOR_KEYWORD_ROUTING_CONFLICT", + ); + + assert.equal(legacyActivityReview.review_status, "ready_for_review"); + assert.equal(review.review_status, "needs_source_context"); + assert.equal(review.guardrails.artifact_inspector.valid, false); + assert.equal(diagnostic.observed.mandatory_evidence_satisfied, false); + assert.ok( + diagnostic.observed.missing_mandatory_evidence.includes( + "rendered_artifact_is_primary", + ), + ); +} + +{ + const genericCandidate = createCandidate(); + genericCandidate.workflow.topology = "workspace"; + const review = reviewUiWorkflowCandidate( + ARTIFACT_ACTIVITY, + genericCandidate, + { activity_review: activityReview }, + ); + + assert.equal(review.review_status, "needs_source_context"); + assert.equal(review.guardrails.artifact_inspector.selected, true); + assert.equal(review.guardrails.artifact_inspector.valid, false); + assert.ok( + review.guardrails.artifact_inspector.diagnostics.some( + (entry) => + entry.code === "JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING" || + entry.code === "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + ), + "Grounded Artifact Inspector activity must require artifact topology and scoped authority.", + ); + assert.throws( + () => createUiGenerationHandoff(review), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "handoff_blocked"); + return true; + }, + ); +} + +{ + const handoff = createUiGenerationHandoff(validWorkflowReview); + const frontendContext = createFrontendGenerationContext({ + ui_generation_handoff: handoff, + frontend_context: { + target_runtime: "browser", + ui_library: "none", + }, + verification: { + commands: ["npm test"], + browser_checks: ["desktop artifact inspection", "narrow artifact inspection"], + states_to_verify: ["artifact ready", "locus focused or selected"], + }, + }); + const skillContext = createFrontendImplementationSkillContext({ + frontend_generation_context: frontendContext, + target_client: "codex", + }); + const contradictoryHandoff = structuredClone(handoff); + contradictoryHandoff.workflow.topology = "workspace"; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: contradictoryHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "frontend_context_blocked"); + assert.equal(error.details.field, "workflow.topology"); + assert.equal(error.details.topology, "workspace"); + assert.equal( + error.details.topology_contract_kind, + "artifact_centered", + ); + return true; + }, + "Frontend generation must reject contradictory serialized topology representations.", + ); + + const objectOnlyTopologyHandoff = structuredClone(handoff); + objectOnlyTopologyHandoff.workflow.topology = structuredClone( + objectOnlyTopologyHandoff.workflow.topology_contract, + ); + delete objectOnlyTopologyHandoff.workflow.topology_contract; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: objectOnlyTopologyHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "frontend_context_blocked"); + assert.equal(error.details.observed.topology, "artifact_centered"); + assert.equal(error.details.observed.topology_representation, "object"); + assert.equal(error.details.observed.topology_contract_kind, null); + assert.equal(error.details.observed.canonical_topology_shape, false); + return true; + }, + "Frontend generation must reject object-only Artifact Inspector topology packets.", + ); + + const missingTargetModelHandoff = structuredClone(handoff); + delete missingTargetModelHandoff.workflow.target_model; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: missingTargetModelHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "frontend_context_blocked"); + assert.equal(error.details.field, "workflow.target_model"); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + ); + return true; + }, + "Frontend generation must reject an Artifact Inspector handoff without its locus model.", + ); + + const contradictoryFrontendContext = structuredClone(frontendContext); + contradictoryFrontendContext.workflow.topology = "workspace"; + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: contradictoryFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "workflow.topology"); + assert.equal(error.details.topology, "workspace"); + assert.equal( + error.details.topology_contract_kind, + "artifact_centered", + ); + return true; + }, + "Skill compilation must reject contradictory serialized topology representations.", + ); + + const objectOnlyTopologyFrontendContext = structuredClone(frontendContext); + objectOnlyTopologyFrontendContext.workflow.topology = structuredClone( + objectOnlyTopologyFrontendContext.workflow.topology_contract, + ); + delete objectOnlyTopologyFrontendContext.workflow.topology_contract; + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: objectOnlyTopologyFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.observed.topology, "artifact_centered"); + assert.equal(error.details.observed.topology_representation, "object"); + assert.equal(error.details.observed.topology_contract_kind, null); + assert.equal(error.details.observed.canonical_topology_shape, false); + return true; + }, + "Skill compilation must reject object-only Artifact Inspector topology packets.", + ); + + const missingStateGroupsFrontendContext = structuredClone(frontendContext); + delete missingStateGroupsFrontendContext.workflow.state_groups; + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: missingStateGroupsFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "workflow.state_groups"); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + return true; + }, + "Skill compilation must reject an Artifact Inspector context without its active states.", + ); + const tamperedHandoffGuidance = structuredClone(handoff); + tamperedHandoffGuidance.artifact_inspector.registry + .external_artifact_review_status = "pass"; + tamperedHandoffGuidance.artifact_inspector.design_system_scopes = []; + tamperedHandoffGuidance.artifact_inspector.boundary_contracts = []; + const canonicalizedFrontendContext = createFrontendGenerationContext({ + ui_generation_handoff: tamperedHandoffGuidance, + }); + + assert.deepEqual( + canonicalizedFrontendContext.implementation_guidance.artifact_inspector + .registry, + handoff.implementation_contract.artifact_inspector, + "Frontend generation must emit the validated implementation contract registry, not serialized handoff guidance.", + ); + assert.deepEqual( + canonicalizedFrontendContext.implementation_guidance.artifact_inspector + .design_system_scopes, + model.design_system.scopes, + ); + assert.deepEqual( + canonicalizedFrontendContext.implementation_guidance.artifact_inspector + .boundary_contracts, + model.design_system.boundary_contracts, + ); + + assert.equal(handoff.handoff_status, "ready_for_generation"); + assert.equal(handoff.surface_type, "artifact_inspector"); + assert.equal(handoff.workflow.topology, "artifact_centered"); + assert.deepEqual(handoff.workflow.topology_contract, model.topology); + assert.deepEqual( + handoff.workflow.work_units.map((entry) => entry.id), + model.work_units.map((entry) => entry.id), + ); + assert.deepEqual(handoff.workflow.artifact, { + id: "artifact", + boundary: "Rendered interface under audit", + }); + assert.equal(handoff.workflow.target_model.artifact_id, "artifact"); + assert.deepEqual(Object.keys(handoff.workflow.state_groups), ["core"]); + assert.deepEqual( + handoff.implementation_contract.artifact_inspector.active_state_groups, + ["core"], + "Conditional state groups must not activate from the registry alone.", + ); + assert.deepEqual( + handoff.implementation_contract.design_system_scopes, + model.design_system.scopes, + "A default implementation contract must auto-activate scoped authority for the selected surface.", + ); + assert.deepEqual( + handoff.implementation_contract.boundary_contracts, + model.design_system.boundary_contracts, + ); + assert.equal( + handoff.implementation_contract.artifact_inspector + .external_artifact_review_status, + "external_not_reviewed", + ); + assert.deepEqual( + handoff.artifact_inspector.design_system_scopes, + model.design_system.scopes, + ); + assert.deepEqual( + handoff.artifact_inspector.boundary_contracts, + model.design_system.boundary_contracts, + ); + + assert.equal( + frontendContext.frontend_context_status, + "ready_for_frontend_implementation", + ); + assert.equal(frontendContext.surface_type, "artifact_inspector"); + assert.equal( + frontendContext.selected_surface_profile.id, + "judgmentkit.artifact-inspector.v1", + ); + assert.ok( + frontendContext.source.supported_surface_profiles.includes( + "judgmentkit.artifact-inspector.v1", + ), + "Omitting caller capabilities must use the built-in supported profile registry.", + ); + assert.deepEqual(frontendContext.workflow.topology_contract, model.topology); + assert.deepEqual( + frontendContext.workflow.work_units, + handoff.workflow.work_units, + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector.artifact, + handoff.workflow.artifact, + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector.target_model, + handoff.workflow.target_model, + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector.state_groups, + handoff.workflow.state_groups, + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector + .active_state_groups, + ["core"], + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector + .design_system_scopes, + model.design_system.scopes, + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector + .boundary_contracts, + model.design_system.boundary_contracts, + ); + assert.equal( + frontendContext.guardrails.artifact_inspector_authority + .external_artifact_review_status, + "external_not_reviewed", + ); + assert.equal( + frontendContext.guardrails.artifact_inspector_authority + .trusted_runtime_evidence_required, + true, + ); + + assert.equal(skillContext.skill_context_status, "ready"); + assert.equal( + skillContext.selected_surface_profile.id, + "judgmentkit.artifact-inspector.v1", + ); + assert.equal( + skillContext.surface_type_guidance.workflow_topology, + "artifact_centered", + ); + assert.deepEqual( + skillContext.surface_type_guidance.topology_contract, + model.topology, + ); + assert.deepEqual( + skillContext.surface_type_guidance.work_units, + handoff.workflow.work_units, + ); + assert.deepEqual( + skillContext.surface_type_guidance.artifact, + handoff.workflow.artifact, + ); + assert.deepEqual( + skillContext.surface_type_guidance.target_model, + handoff.workflow.target_model, + ); + assert.deepEqual( + skillContext.surface_type_guidance.state_groups, + handoff.workflow.state_groups, + ); + assert.deepEqual( + skillContext.artifact_inspector.artifact, + handoff.workflow.artifact, + ); + assert.deepEqual( + skillContext.artifact_inspector.target_model, + handoff.workflow.target_model, + ); + assert.deepEqual( + skillContext.artifact_inspector.state_groups, + handoff.workflow.state_groups, + ); + assert.deepEqual( + skillContext.artifact_inspector.active_state_groups, + ["core"], + ); + assert.deepEqual( + skillContext.artifact_inspector.design_system_scopes, + model.design_system.scopes, + ); + assert.deepEqual( + skillContext.artifact_inspector.boundary_contracts, + model.design_system.boundary_contracts, + ); + assert.equal( + skillContext.artifact_inspector.external_artifact_review_status, + "external_not_reviewed", + ); + assert.ok( + skillContext.instruction_markdown.includes("external_not_reviewed"), + ); + assert.ok( + skillContext.instruction_markdown.includes( + "trusted browser-runtime receipt", + ), + ); + assert.ok( + skillContext.verification_checklist.some((entry) => + entry.includes("trusted runtime evidence"), + ), + ); + + const omittedArtifactGuidanceContext = structuredClone(frontendContext); + delete omittedArtifactGuidanceContext.implementation_guidance + .artifact_inspector; + const reconstructedOmittedGuidance = + createFrontendImplementationSkillContext({ + frontend_generation_context: omittedArtifactGuidanceContext, + target_client: "codex", + }); + assert.deepEqual( + reconstructedOmittedGuidance.artifact_inspector.design_system_scopes, + model.design_system.scopes, + ); + assert.equal( + reconstructedOmittedGuidance.artifact_inspector + .external_artifact_review_status, + "external_not_reviewed", + ); + assert.ok( + reconstructedOmittedGuidance.instruction_markdown.includes( + "trusted browser-runtime receipt", + ), + ); + + const tamperedArtifactGuidanceContext = structuredClone(frontendContext); + tamperedArtifactGuidanceContext.implementation_guidance.artifact_inspector + .design_system_scopes = [ + structuredClone( + model.design_system.scopes.find( + (entry) => entry.scope_id === "primary_artifact", + ), + ), + ]; + tamperedArtifactGuidanceContext.implementation_guidance.artifact_inspector + .boundary_contracts = []; + tamperedArtifactGuidanceContext.implementation_guidance.artifact_inspector + .external_artifact_review_status = "pass"; + tamperedArtifactGuidanceContext.guardrails.artifact_inspector_authority + .external_artifact_review_status = "pass"; + const reconstructedTamperedGuidance = + createFrontendImplementationSkillContext({ + frontend_generation_context: tamperedArtifactGuidanceContext, + target_client: "codex", + }); + assert.deepEqual( + reconstructedTamperedGuidance.artifact_inspector.design_system_scopes, + model.design_system.scopes, + ); + assert.deepEqual( + reconstructedTamperedGuidance.artifact_inspector.boundary_contracts, + model.design_system.boundary_contracts, + ); + assert.equal( + reconstructedTamperedGuidance.artifact_inspector + .external_artifact_review_status, + "external_not_reviewed", + ); + assert.equal( + reconstructedTamperedGuidance.guardrails.artifact_inspector_authority + .external_artifact_review_status, + "external_not_reviewed", + ); + + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: frontendContext, + design_system_adapter: completeExternalDesignSystemAdapter(), + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "design_system_adapter"); + assert.deepEqual(error.details.judgmentkit_owned_scopes, [ + "inspector_chrome", + "inspection_overlay", + ]); + assert.equal(error.details.external_artifact_scope, "primary_artifact"); + return true; + }, + "A whole-context frontend adapter must not replace JudgmentKit authority inside Artifact Inspector.", + ); + + const serializedExternalHandoff = structuredClone(handoff); + serializedExternalHandoff.implementation_contract.design_system_source = { + ...serializedExternalHandoff.implementation_contract.design_system_source, + mode: "external_design_system", + name: "External UI", + package: "@example/ui", + }; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: serializedExternalHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "design_system_source.mode"); + assert.equal(error.details.observed, "external_design_system"); + return true; + }, + "Serialized external authority must not replace Artifact Inspector chrome authority at the frontend-context boundary.", + ); + + const disguisedExternalHandoff = structuredClone(handoff); + disguiseExternalDesignAuthority( + disguisedExternalHandoff.implementation_contract, + ); + const canonicalizedDisguisedHandoff = createFrontendGenerationContext({ + ui_generation_handoff: disguisedExternalHandoff, + }); + assert.deepEqual( + canonicalizedDisguisedHandoff.implementation_contract + .design_system_source, + handoff.implementation_contract.design_system_source, + ); + assert.deepEqual( + canonicalizedDisguisedHandoff.implementation_contract + .visual_token_adapter, + handoff.implementation_contract.visual_token_adapter, + ); + assert.deepEqual( + canonicalizedDisguisedHandoff.implementation_contract + .default_ai_native_design_system, + handoff.implementation_contract.default_ai_native_design_system, + ); + assert.equal( + canonicalizedDisguisedHandoff.implementation_guidance + .design_system_source.name, + "JudgmentKit", + ); + assert.equal( + canonicalizedDisguisedHandoff.implementation_guidance + .component_contracts.some((entry) => entry.id === "ExternalButton"), + false, + ); + assert.equal( + canonicalizedDisguisedHandoff.implementation_contract + .visual_token_adapter.css_custom_properties.some( + (entry) => entry.name === "--external-surface", + ), + false, + "Relabeling external authority as judgmentkit_default must not preserve caller-owned tokens or components.", + ); + + const disguisedExternalContract = structuredClone(contract); + disguiseExternalDesignAuthority( + disguisedExternalContract.implementation_contract, + ); + const canonicalizedCustomContractHandoff = createUiGenerationHandoff( + validWorkflowReview, + { contract: disguisedExternalContract }, + ); + assert.deepEqual( + canonicalizedCustomContractHandoff.implementation_contract + .design_system_source, + handoff.implementation_contract.design_system_source, + ); + assert.deepEqual( + canonicalizedCustomContractHandoff.implementation_contract + .visual_token_adapter, + handoff.implementation_contract.visual_token_adapter, + ); + assert.deepEqual( + canonicalizedCustomContractHandoff.implementation_contract + .default_ai_native_design_system, + handoff.implementation_contract.default_ai_native_design_system, + "A caller-supplied activity contract must not redefine JudgmentKit-owned Artifact Inspector design authority.", + ); + + const canonicalizedCustomContractFrontend = + createFrontendGenerationContext({ + ui_generation_handoff: handoff, + contract: disguisedExternalContract, + }); + assert.deepEqual( + canonicalizedCustomContractFrontend.implementation_contract + .design_system_source, + handoff.implementation_contract.design_system_source, + ); + assert.deepEqual( + canonicalizedCustomContractFrontend.implementation_contract + .visual_token_adapter, + handoff.implementation_contract.visual_token_adapter, + ); + assert.equal( + canonicalizedCustomContractFrontend.implementation_guidance + .component_contracts.some((entry) => entry.id === "ExternalButton"), + false, + ); + + const relabeledArtifactHandoff = structuredClone( + serializedExternalHandoff, + ); + relabeledArtifactHandoff.surface_type = "workbench"; + relabeledArtifactHandoff.surface_guidance.recommended_surface_type = + "workbench"; + delete relabeledArtifactHandoff.implementation_contract + .design_system_scopes; + delete relabeledArtifactHandoff.implementation_contract.boundary_contracts; + delete relabeledArtifactHandoff.implementation_contract.artifact_inspector; + delete relabeledArtifactHandoff.artifact_inspector; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: relabeledArtifactHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.details.surface_type_candidates[ + "ui_generation_handoff.workflow.topology" + ], + "artifact_inspector", + ); + return true; + }, + "Artifact-centered serialized handoffs cannot be relabeled to shed scoped authority.", + ); + + const serializedExternalFrontendContext = structuredClone(frontendContext); + serializedExternalFrontendContext.implementation_contract.design_system_source = { + ...serializedExternalFrontendContext.implementation_contract + .design_system_source, + mode: "external_design_system", + name: "External UI", + package: "@example/ui", + }; + serializedExternalFrontendContext.implementation_guidance.design_system_source = + structuredClone( + serializedExternalFrontendContext.implementation_contract + .design_system_source, + ); + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: serializedExternalFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal(error.details.field, "design_system_source.mode"); + assert.equal(error.details.observed, "external_design_system"); + return true; + }, + "Serialized frontend authority must not bypass the inline adapter guard.", + ); + + const disguisedExternalFrontendContext = structuredClone(frontendContext); + disguiseExternalDesignAuthority( + disguisedExternalFrontendContext.implementation_contract, + ); + disguisedExternalFrontendContext.implementation_guidance + .design_system_source = structuredClone( + disguisedExternalFrontendContext.implementation_contract + .design_system_source, + ); + disguisedExternalFrontendContext.implementation_guidance + .component_contracts = structuredClone( + disguisedExternalFrontendContext.implementation_contract + .default_ai_native_design_system.component_contracts, + ); + const canonicalizedDisguisedFrontend = + createFrontendImplementationSkillContext({ + frontend_generation_context: disguisedExternalFrontendContext, + target_client: "codex", + }); + assert.deepEqual( + canonicalizedDisguisedFrontend.design_system_source, + skillContext.design_system_source, + ); + assert.deepEqual( + canonicalizedDisguisedFrontend.visual_token_adapter, + skillContext.visual_token_adapter, + ); + assert.deepEqual( + canonicalizedDisguisedFrontend.component_contracts, + skillContext.component_contracts, + ); + assert.equal( + canonicalizedDisguisedFrontend.component_contracts.some( + (entry) => entry.id === "ExternalButton", + ), + false, + ); + assert.equal( + canonicalizedDisguisedFrontend.instruction_markdown.includes( + "External UI", + ), + false, + "The compiled skill context must not emit disguised external authority.", + ); + + const relabeledArtifactFrontendContext = structuredClone( + serializedExternalFrontendContext, + ); + relabeledArtifactFrontendContext.surface_type = "workbench"; + relabeledArtifactFrontendContext.surface_guidance.recommended_surface_type = + "workbench"; + relabeledArtifactFrontendContext.implementation_guidance.surface_type = + "workbench"; + delete relabeledArtifactFrontendContext.implementation_contract + .design_system_scopes; + delete relabeledArtifactFrontendContext.implementation_contract + .boundary_contracts; + delete relabeledArtifactFrontendContext.implementation_contract + .artifact_inspector; + delete relabeledArtifactFrontendContext.implementation_guidance + .artifact_inspector; + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: relabeledArtifactFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal( + error.details.expected.surface_type, + "artifact_inspector", + ); + assert.deepEqual(error.details.observed.surface_types, ["workbench"]); + assert.equal(error.details.observed.topology, "artifact_centered"); + assert.equal(error.details.observed.authority_bundle_active, false); + return true; + }, + "Artifact-centered serialized frontend contexts cannot be relabeled to shed scoped authority.", + ); + + const broadenedStateHandoff = structuredClone(handoff); + broadenedStateHandoff.implementation_contract.artifact_inspector + .active_state_groups = ["core", "automation"]; + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: broadenedStateHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "frontend_context_blocked", + ); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + assert.deepEqual(error.details.expected, ["core"]); + return true; + }, + "Serialized handoff metadata must not broaden reviewed Artifact Inspector state groups.", + ); + + const broadenedStateFrontendContext = structuredClone(frontendContext); + broadenedStateFrontendContext.implementation_contract.artifact_inspector + .active_state_groups = ["core", "automation"]; + broadenedStateFrontendContext.implementation_guidance.artifact_inspector + .active_state_groups = ["core", "automation"]; + broadenedStateFrontendContext.implementation_guidance.artifact_inspector + .registry.active_state_groups = ["core", "automation"]; + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: broadenedStateFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal( + error.code, + "invalid_artifact_inspector_authority_contract", + ); + assert.equal( + error.details.diagnostic_code, + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + assert.deepEqual(error.details.expected, ["core"]); + return true; + }, + "Serialized frontend metadata must not broaden reviewed Artifact Inspector state groups.", + ); + + const duplicateWorkUnitHandoff = structuredClone(handoff); + duplicateWorkUnitHandoff.workflow.work_units.push({ + ...structuredClone(duplicateWorkUnitHandoff.workflow.work_units[0]), + participant_intent: "Orient to an unrelated secondary artifact.", + }); + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: duplicateWorkUnitHandoff, + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.deepEqual(error.details.duplicate_work_unit_ids, ["orient"]); + return true; + }, + "A serialized handoff cannot add a divergent duplicate work-unit id before frontend generation.", + ); + + const duplicateWorkUnitFrontendContext = structuredClone(frontendContext); + duplicateWorkUnitFrontendContext.workflow.work_units.push({ + ...structuredClone( + duplicateWorkUnitFrontendContext.workflow.work_units[0], + ), + participant_intent: "Orient to an unrelated secondary artifact.", + }); + assert.throws( + () => + createFrontendImplementationSkillContext({ + frontend_generation_context: duplicateWorkUnitFrontendContext, + target_client: "codex", + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.deepEqual(error.details.duplicate_work_unit_ids, ["orient"]); + return true; + }, + "A serialized frontend context cannot add a divergent duplicate work-unit id.", + ); + + assert.throws( + () => + createFrontendGenerationContext({ + ui_generation_handoff: handoff, + supported_surface_profiles: [ + "judgmentkit.workbench.operational-v1", + ], + }), + (error) => { + assert.ok(error instanceof JudgmentKitInputError); + assert.equal(error.code, "artifact_inspector_profile_unsupported"); + assert.equal(error.details.fallback_applied, false); + assert.equal( + error.details.required_surface_profile, + "judgmentkit.artifact-inspector.v1", + ); + return true; + }, + ); +} + +{ + const callerBroadenedContract = createUiImplementationContract({ + surface_type: "artifact_inspector", + artifact_inspector: { + active_state_groups: [ + "core", + "consequential", + "reusable_guidance", + "automation", + ], + }, + }).implementation_contract; + const handoff = createUiGenerationHandoff(validWorkflowReview, { + implementation_contract: callerBroadenedContract, + }); + const frontendContext = createFrontendGenerationContext({ + ui_generation_handoff: handoff, + }); + + assert.deepEqual( + callerBroadenedContract.artifact_inspector.active_state_groups, + ["core", "consequential", "reusable_guidance", "automation"], + ); + assert.deepEqual( + handoff.implementation_contract.artifact_inspector.active_state_groups, + ["core"], + "Caller implementation metadata must not activate groups absent from the reviewed workflow.", + ); + assert.deepEqual( + handoff.artifact_inspector.registry.active_state_groups, + ["core"], + ); + assert.deepEqual( + frontendContext.implementation_guidance.artifact_inspector + .active_state_groups, + ["core"], + ); +} + +{ + const candidate = createCandidate(); + candidate.workflow.topology_contract = structuredClone( + candidate.workflow.topology, + ); + candidate.workflow.topology = "artifact_centered"; + const review = reviewCandidate(candidate); + + assert.equal(review.review_status, "ready_for_review"); + assert.equal(review.guardrails.artifact_inspector.valid, true); + assert.equal(review.candidate.workflow.topology, "artifact_centered"); + assert.deepEqual(review.candidate.workflow.topology_contract, model.topology); +} + +{ + const candidate = createCandidate(); + candidate.workflow.topology = "artifact_centered"; + const review = reviewCandidate(candidate); + const diagnostic = review.guardrails.artifact_inspector.diagnostics.find( + (entry) => + entry.code === "JK_ARTIFACT_INSPECTOR_TOPOLOGY_CONTRACT_MISSING", + ); + + assert.equal(review.review_status, "needs_source_context"); + assert.equal(review.guardrails.artifact_inspector.valid, false); + assert.ok(diagnostic); + assert.equal(diagnostic.field, "workflow.topology_contract"); + assert.equal(review.candidate.workflow.topology, "artifact_centered"); + assert.equal( + "topology_contract" in review.candidate.workflow, + false, + "The reviewer must not synthesize the normative topology from the registry.", + ); +} + +{ + const consequentialAutomationActivity = ` + A policy reviewer inspects one rendered policy document that remains the + primary artifact. They select a specific passage and supporting evidence + attaches to that selected passage. They explicitly approve and publish the + decision, then a declared automation automatically resolves exact matches. + They preview the relation and verify the immutable local receipt + beside the passage. + `; + const conditionalActivityReview = createActivityModelReview( + consequentialAutomationActivity, + ); + const conditionalSurfaceReview = recommendSurfaceTypes( + consequentialAutomationActivity, + { activity_review: conditionalActivityReview }, + ); + const candidate = createCandidate(); + candidate.workflow.state_groups = { + core: structuredClone(model.state_groups.core), + consequential: structuredClone(model.state_groups.consequential), + automation: structuredClone(model.state_groups.automation), + }; + const review = reviewUiWorkflowCandidate( + consequentialAutomationActivity, + candidate, + { + activity_review: conditionalActivityReview, + profile_id: "artifact-inspector-ui", + surface_review: conditionalSurfaceReview, + }, + ); + const coreOnlyImplementationContract = createUiImplementationContract({ + surface_type: "artifact_inspector", + }).implementation_contract; + assert.deepEqual( + coreOnlyImplementationContract.artifact_inspector.active_state_groups, + ["core"], + ); + const handoff = createUiGenerationHandoff(review, { + implementation_contract: coreOnlyImplementationContract, + }); + + assert.equal(review.review_status, "ready_for_review"); + assert.deepEqual( + review.guardrails.candidate_primary_meta_terms_detected, + [], + "The canonical consequential state 'outcome pending' is domain state, not review-packet leakage.", + ); + assert.deepEqual( + handoff.implementation_contract.artifact_inspector.active_state_groups, + ["core", "consequential", "automation"], + ); +} + +{ + const consequentialActivity = ` + A compliance reviewer inspects one rendered policy document that remains + the primary artifact. They must select a specific passage, and the evidence + for approval attaches to that selected passage. They preview the relation, + explicitly approve and publish the decision, then verify the immutable + receipt beside the passage. + `; + const consequentialActivityReview = createActivityModelReview( + consequentialActivity, + ); + const consequentialSurfaceReview = recommendSurfaceTypes( + consequentialActivity, + { activity_review: consequentialActivityReview }, + ); + const review = reviewUiWorkflowCandidate( + consequentialActivity, + createCandidate(), + { + activity_review: consequentialActivityReview, + profile_id: "artifact-inspector-ui", + surface_review: consequentialSurfaceReview, + }, + ); + const diagnostic = review.guardrails.artifact_inspector.diagnostics.find( + (entry) => + entry.code === + "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + ); + + assert.equal(review.review_status, "needs_source_context"); + assert.ok(diagnostic); + assert.equal(diagnostic.field, "workflow.state_groups"); + assert.ok(diagnostic.expected_contract.includes("consequential")); +} + +{ + const request = buildUiWorkflowCandidateRequest({ + brief: ARTIFACT_ACTIVITY, + activity_review: activityReview, + profile_id: "artifact-inspector-ui", + surface_review: surfaceReview, + }); + const payload = JSON.parse(request.messages.at(-1).content); + + assert.equal( + payload.candidate_shape.workflow.topology.kind, + "artifact_centered", + ); + assert.equal( + payload.candidate_shape.workflow.topology.primary_object_id, + "artifact", + ); + assert.deepEqual( + payload.candidate_shape.workflow.work_units.map((entry) => entry.id), + model.work_units.map((entry) => entry.id), + ); + assert.deepEqual( + payload.candidate_shape.workflow.target_model.target_kinds, + model.target_model.target_kinds, + ); + assert.deepEqual( + Object.keys(payload.candidate_shape.workflow.state_groups), + ["core"], + "The generated candidate shape must not activate every conditional state group by default.", + ); + assert.deepEqual( + payload.artifact_inspector_state_group_catalog, + model.state_groups, + "Conditional state definitions remain available without becoming active candidate state by default.", + ); + assert.ok( + request.messages[0].content.includes( + "stable structured work-unit ids", + ), + ); +} + +const MALFORMED_CASES = [ + { + label: "wrong topology kind", + mutate(candidate) { + candidate.workflow.topology.kind = "workspace"; + }, + code: "JK_ARTIFACT_INSPECTOR_TOPOLOGY_KIND_INVALID", + field: "workflow.topology.kind", + }, + { + label: "missing primary artifact reference", + mutate(candidate) { + delete candidate.workflow.topology.primary_object_id; + }, + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.topology.primary_object_id", + }, + { + label: "missing locus model", + mutate(candidate) { + delete candidate.workflow.target_model.target_kinds; + }, + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: "workflow.target_model.target_kinds", + }, + ...[ + "target_identity_sources", + "geometry_policy", + "label_sources", + "artifact_interactivity", + "native_interaction_precedence", + "artifact_id", + ].map((field) => ({ + label: `missing target model ${field}`, + mutate(candidate) { + delete candidate.workflow.target_model[field]; + }, + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: `workflow.target_model.${field}`, + })), + { + label: "noncanonical inferred target model", + mutate(candidate) { + candidate.workflow.target_model = { + target_kinds: ["pixel_guess"], + target_identity_sources: ["visual_inference"], + geometry_policy: "Infer the nearest element after every render.", + label_sources: ["model_generated"], + artifact_interactivity: ["live"], + native_interaction_precedence: + "The inspector always captures native actions.", + artifact_id: "artifact", + }; + }, + code: "JK_ARTIFACT_INSPECTOR_LOCUS_MODEL_MISSING", + field: "workflow.target_model.target_kinds", + }, + { + label: "missing stable work-unit id", + mutate(candidate) { + delete candidate.workflow.work_units[0].id; + }, + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + field: "workflow.work_units[0].id", + }, + { + label: "missing required work units", + mutate(candidate) { + candidate.workflow.work_units = candidate.workflow.work_units.filter( + (workUnit) => ["orient", "select_locus", "recover"].includes(workUnit.id), + ); + candidate.workflow.topology.entry_work_unit_id = "orient"; + candidate.workflow.topology.completion_work_unit_ids = ["orient"]; + candidate.workflow.topology.transitions = [ + { from: "orient", to: "select_locus" }, + { from: "select_locus", to: "recover" }, + { from: "recover", to: "orient" }, + ]; + }, + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_ID_MISSING", + field: "workflow.work_units", + }, + { + label: "divergent duplicate work-unit id", + mutate(candidate) { + candidate.workflow.work_units.push({ + ...structuredClone(candidate.workflow.work_units[0]), + participant_intent: "Orient to an unrelated secondary artifact.", + }); + }, + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + field: "workflow.work_units", + }, + { + label: "invalid work-unit contract", + mutate(candidate) { + candidate.workflow.work_units[0].participant_intent = + "Open a generic workspace."; + }, + code: "JK_ARTIFACT_INSPECTOR_WORK_UNIT_CONTRACT_INVALID", + field: "workflow.work_units[0].participant_intent", + }, + { + label: "missing artifact declaration", + mutate(candidate) { + delete candidate.workflow.artifact; + }, + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.artifact.id", + }, + { + label: "missing artifact boundary", + mutate(candidate) { + delete candidate.workflow.artifact.boundary; + }, + code: "JK_ARTIFACT_INSPECTOR_PRIMARY_ARTIFACT_MISSING", + field: "workflow.artifact.boundary", + }, + { + label: "invalid entry reference", + mutate(candidate) { + candidate.workflow.topology.entry_work_unit_id = "missing_entry"; + }, + code: "JK_ARTIFACT_INSPECTOR_ENTRY_REFERENCE_INVALID", + field: "workflow.topology.entry_work_unit_id", + }, + { + label: "invalid completion reference", + mutate(candidate) { + candidate.workflow.topology.completion_work_unit_ids = [ + "missing_completion", + ]; + }, + code: "JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID", + field: "workflow.topology.completion_work_unit_ids", + }, + { + label: "completion omits artifact-local verification", + mutate(candidate) { + candidate.workflow.topology.completion_work_unit_ids = ["orient"]; + }, + code: "JK_ARTIFACT_INSPECTOR_COMPLETION_REFERENCE_INVALID", + field: "workflow.topology.completion_work_unit_ids", + }, + { + label: "invalid transition reference", + mutate(candidate) { + candidate.workflow.topology.transitions[0].from = "missing_work_unit"; + }, + code: "JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID", + field: "workflow.topology.transitions", + }, + { + label: "disconnected canonical topology", + mutate(candidate) { + candidate.workflow.topology.transitions = [ + { from: "orient", to: "recover" }, + { from: "recover", to: "orient" }, + ]; + }, + code: "JK_ARTIFACT_INSPECTOR_TRANSITION_REFERENCE_INVALID", + field: "workflow.topology.transitions", + }, + { + label: "missing recovery path", + mutate(candidate) { + candidate.workflow.topology.transitions = + candidate.workflow.topology.transitions.filter( + (transition) => + transition.from !== "recover" && transition.to !== "recover", + ); + }, + code: "JK_ARTIFACT_INSPECTOR_RECOVERY_PATH_MISSING", + field: "workflow.topology.transitions", + }, + { + label: "missing state-group contract", + mutate(candidate) { + delete candidate.workflow.state_groups; + }, + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + }, + { + label: "invalid core state set", + mutate(candidate) { + candidate.workflow.state_groups.core = ["artifact ready"]; + }, + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups.core", + }, + { + label: "inactive conditional state groups", + mutate(candidate) { + candidate.workflow.state_groups = structuredClone(model.state_groups); + }, + code: "JK_ARTIFACT_INSPECTOR_STATE_GROUP_CONTRACT_INVALID", + field: "workflow.state_groups", + }, +]; + +for (const malformedCase of MALFORMED_CASES) { + const candidate = createCandidate(); + malformedCase.mutate(candidate); + const review = reviewCandidate(candidate); + const diagnostics = review.guardrails.artifact_inspector.diagnostics; + const diagnostic = diagnostics.find( + (entry) => entry.code === malformedCase.code, + ); + + assert.equal( + review.review_status, + "needs_source_context", + `${malformedCase.label} must block workflow acceptance.`, + ); + assert.equal(review.guardrails.artifact_inspector.valid, false); + assert.ok( + diagnostic, + `${malformedCase.label} must emit ${malformedCase.code}.`, + ); + assert.equal(diagnostic.field, malformedCase.field); + assert.notEqual(diagnostic.expected_contract, undefined); + assert.equal(typeof diagnostic.repair_instruction, "string"); + assert.ok(diagnostic.repair_instruction.length > 0); + assert.ok( + review.review.targeted_questions.includes( + diagnostic.repair_instruction, + ), + `${malformedCase.label} repair must remain user-actionable.`, + ); +} + +console.log("artifact inspector workflow checks passed."); diff --git a/tests/kernel-contract.test.mjs b/tests/kernel-contract.test.mjs index 74fb8bc..6a82cb7 100644 --- a/tests/kernel-contract.test.mjs +++ b/tests/kernel-contract.test.mjs @@ -279,8 +279,8 @@ assert.ok( assert.equal( contract.implementation_contract.default_ai_native_design_system.component_contracts .length, - 17, - "The default AI-native system must include core UI component contracts.", + 34, + "The default AI-native system must include the 17 legacy contracts and 17 Artifact Inspector contracts.", ); assert.ok( contract.implementation_contract.default_ai_native_design_system.component_contracts.some( @@ -425,7 +425,7 @@ assert.deepEqual( assert.equal( contract.implementation_contract.default_ai_native_design_system.pattern_contracts .length, - 8, + 9, "The default AI-native system must include one pattern contract per surface type.", ); assert.ok( @@ -1211,6 +1211,7 @@ assert.deepEqual(contract.workflow.topology_policy.allowed_topologies, [ "dashboard", "report", "conversation", + "artifact_centered", ]); assert.equal(contract.workflow.topology_policy.preferred_default, "workspace"); assert.equal( @@ -1255,6 +1256,7 @@ assert.deepEqual(Object.keys(contract.surface_types), [ "marketing", "workbench", "operator_review", + "artifact_inspector", "form_flow", "dashboard_monitor", "content_report", diff --git a/tests/mcp.test.mjs b/tests/mcp.test.mjs index d9c5dde..64ee78f 100644 --- a/tests/mcp.test.mjs +++ b/tests/mcp.test.mjs @@ -1738,6 +1738,30 @@ MCP endpoint: http://127.0.0.1:3333/mcp`; formatPlanningCard(missingProvenanceReview).includes("This is not an artifact"), ); + const artifactInspectorReviewRequiredCard = formatPlanningCard({ + implementation_review_status: "review_required", + candidate_artifact_status: "not_an_artifact", + design_system_acceptance_status: "review_required", + next_agent_action: "run_trusted_runtime_review", + findings: [], + checks: { + artifact_inspector: { status: "review_required" }, + }, + }); + assert.match( + artifactInspectorReviewRequiredCard, + /\*\*Status:\*\* Trusted runtime review required/, + ); + assert.ok( + artifactInspectorReviewRequiredCard.includes( + "keep the external artifact external_not_reviewed", + ), + ); + assert.equal( + artifactInspectorReviewRequiredCard.includes("Repair the failed gates"), + false, + ); + const implementationReview = await handleToolCall("review_ui_implementation_candidate", { implementation_contract: implementationContract, candidate: withNoApplicableVisualComposition({ diff --git a/tests/site.test.mjs b/tests/site.test.mjs index c316ff9..706125c 100644 --- a/tests/site.test.mjs +++ b/tests/site.test.mjs @@ -2238,7 +2238,7 @@ assert.ok( 'data-surface-presentation-profile="judgmentkit.workbench.operational-v1"', ), ); -assert.ok(designSystemPatterns.includes("Supported surface presentation profiles")); +assert.ok(designSystemPatterns.includes("Surface presentation profiles")); assert.ok(designSystemPatterns.includes('data-specimen-id="pattern.workbench"')); assert.ok(designSystemPatterns.includes('data-pattern-region="work-queue"')); assert.ok(designSystemPatterns.includes('data-pattern-control="decision-action"')); @@ -2581,8 +2581,11 @@ assert.deepEqual( surfacePresentationProfilesExport.profiles, listSurfacePresentationProfiles(), ); -assert.equal(surfacePresentationProfilesExport.profiles.length, 1); -const workbenchPresentationProfile = surfacePresentationProfilesExport.profiles[0]; +assert.equal(surfacePresentationProfilesExport.profiles.length, 2); +const workbenchPresentationProfile = surfacePresentationProfilesExport.profiles.find( + (entry) => entry.id === "judgmentkit.workbench.operational-v1", +); +assert.ok(workbenchPresentationProfile); assert.equal(workbenchPresentationProfile.id, "judgmentkit.workbench.operational-v1"); assert.equal(workbenchPresentationProfile.status, "supported"); assert.equal(workbenchPresentationProfile.surface_type, "workbench"); @@ -2597,6 +2600,23 @@ assert.ok( ), "the supported Workbench presentation profile must bind to the public Workbench pattern contract", ); +const artifactInspectorPresentationProfile = + surfacePresentationProfilesExport.profiles.find( + (entry) => entry.id === "judgmentkit.artifact-inspector.v1", + ); +assert.ok(artifactInspectorPresentationProfile); +assert.equal(artifactInspectorPresentationProfile.status, "proposed"); +assert.equal(artifactInspectorPresentationProfile.surface_type, "artifact_inspector"); +assert.equal(artifactInspectorPresentationProfile.authority.public_contract, true); +assert.equal(artifactInspectorPresentationProfile.authority.runtime_renderer, false); +assert.equal( + artifactInspectorPresentationProfile.authority.pattern_contract_id, + "artifact-inspector", +); +assert.equal( + artifactInspectorPresentationProfile.provenance.external_artifact_review_status, + "external_not_reviewed", +); assert.equal(componentSpecimensExport.source, "judgmentkit.ai-native-default.contract-v1"); assert.equal(componentSpecimensExport.renderer.id, "judgmentkit-static-specimens"); assert.equal(componentSpecimensExport.specimens.length, componentContractsExport.contracts.length); diff --git a/tests/workbench-surface-profile.test.mjs b/tests/workbench-surface-profile.test.mjs index b0705a3..59296ff 100644 --- a/tests/workbench-surface-profile.test.mjs +++ b/tests/workbench-surface-profile.test.mjs @@ -722,7 +722,12 @@ for (const invalidId of [undefined, "auto", "none"]) { assert.deepEqual( new Set(properties.surface_profile.enum), - new Set(["auto", "none", EXPECTED_WORKBENCH_PROFILE_ID]), + new Set([ + "auto", + "none", + EXPECTED_WORKBENCH_PROFILE_ID, + "judgmentkit.artifact-inspector.v1", + ]), ); assert.equal("experimental_surface_profile" in properties, false);