{
  "generatedAt": "2026-11-09T22:51:50.582Z",
  "runId": "41a40027-20261109220201",
  "baseline": "react",
  "current": "android",
  "summary": {
    "total": 151,
    "active": 131,
    "pending": 17,
    "skipped": 3,
    "captureTypes": {
      "component-fixture": {
        "total": 118,
        "active": 113,
        "pending": 3,
        "skipped": 2
      },
      "real-screen": {
        "total": 33,
        "active": 18,
        "pending": 14,
        "skipped": 1
      }
    },
    "providers": {
      "react": {
        "active": 121,
        "pending": 13,
        "unsupported": 0,
        "total": 134
      },
      "android": {
        "active": 79,
        "pending": 18,
        "unsupported": 0,
        "total": 97
      },
      "harmony": {
        "active": 0,
        "pending": 80,
        "unsupported": 0,
        "total": 80
      },
      "ios": {
        "active": 0,
        "pending": 1,
        "unsupported": 0,
        "total": 1
      }
    }
  },
  "rows": [
    {
      "id": "components.agent-context-view.count-matched",
      "title": "View Context: count matched (desktop side column)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#viewBody",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "#wg-ax:#9 (Kirby): a Kimi restore whose message count matched but which can't be checked exactly. The neutral info note above the context names the runtime; no Verified tag. react-only: the panel is web-only.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.agent-context-view.messages",
      "title": "View Context Messages (desktop side column)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#CONTEXT",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "View Context's messages on a context like qizhi-wang's screenshot (#wg-ax, Kirby): a system update in Markdown with long paths, a user message, hidden thinking and an Agent call with a description, its result, a nested Edit call, the ringed send call. react-only: the panel is web-only. Desktop: the 440px side column.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.agent-context-view.messages.dark",
      "title": "View Context Messages (desktop side column, dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#CONTEXT",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "View Context's messages on a context like qizhi-wang's screenshot (#wg-ax, Kirby): a system update in Markdown with long paths, a user message, hidden thinking and an Agent call with a description, its result, a nested Edit call, the ringed send call. react-only: the panel is web-only. Desktop, dark: the code surface against the canvas.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.agent-context-view.messages.phone",
      "title": "View Context Messages (phone page)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#CONTEXT",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "View Context's messages on a context like qizhi-wang's screenshot (#wg-ax, Kirby): a system update in Markdown with long paths, a user message, hidden thinking and an Agent call with a description, its result, a nested Edit call, the ringed send call. react-only: the panel is web-only. Phone: the page over the app at 390px.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.agent-context-view.newer-session-format",
      "title": "View Context: newer session format (desktop side column)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#viewBody",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "#wg-ax:#9 (Kirby): a session saved by a newer runtime than the Computer's Raft reads. The orange banner, no Retry, and Open computer for a viewer who can open that Computer. react-only: the panel is web-only.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.agent-context-view.verified",
      "title": "View Context: verified (desktop side column)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/agent",
      "surface": "agent-context-view",
      "group": "app/agent",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agentContextView/AgentContextViewBody"
          ],
          "fixture": "packages/web/visual-testing/AgentContextViewCases.tsx#viewBody",
          "path": "packages/web/visual-testing/AgentContextViewCases.tsx",
          "reason": ""
        }
      },
      "notes": "#wg-ax:#9 (Kirby): a restore checked against what the model received. The Verified tag on the head's title (PanelStatus, as ChannelSideCard places it); its tooltip says what was checked. react-only: the panel is web-only.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "screens.home.choose-server",
      "title": "Home Choose Server Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "app/home",
      "status": "pending",
      "providers": {},
      "notes": "Missing true Home top-bar choose-server capture after opening the server selector from the app shell; distinct from the post-login auth choose-server gate.",
      "comparison": null
    },
    {
      "id": "components.settings.integrations.permission-review",
      "title": "Settings Integrations Permission Review",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "app/settings",
      "status": "pending",
      "providers": {},
      "notes": "",
      "comparison": null
    },
    {
      "id": "components.action-card.agent-create",
      "title": "Action card, create agent (prepared)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/actions/ActionCard",
            "components/actions/ActionCardSpecCard"
          ],
          "path": "packages/web/visual-testing/ActionCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-apps:#41 (Kirby 1265e067): an agent:create card waiting for a person. The title names the agent to be without an @: it has no record yet, and the name is what it will be called. React-only: the mobile App draws the same spec.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.action-card.expired-agent",
      "title": "Action card, expired after 30 days (agent card, opened)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/actions/ActionCard",
            "components/actions/ActionCardSpecCard"
          ],
          "path": "packages/web/visual-testing/ActionCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-apps:#40 (Kirby 7579a290): an agent's channel:create card no one completed within 30 days, as the server serves it (state expired, expiresAt). Opened: Expired in Withdrawn's grey, \"Expired on {date}. Ask the agent to send a new request.\" under the title, the card's facts and reason, no buttons. React-only: the mobile App draws the card from state and expiresAt.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.action-card.mcp-connect",
      "title": "Action card, MCP connect (prepared, server manager)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/actions/ActionCard",
            "components/actions/ActionCardSpecCard"
          ],
          "path": "packages/web/visual-testing/ActionCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-apps:#41 (Kirby 6c3920b9, 9f8835f9, 1265e067): an integration:connect_mcp card waiting for a grant. Title names the App and the agent by display name, no grey notice, the picker with Grant access in the ink border (no pink). React-only: the mobile App keeps its typed card for this type.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.message-card.fields.phone",
      "title": "Card message Fields, phone width",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageCardBody",
            "components/message/MessageCardElements"
          ],
          "path": "packages/web/visual-testing/MessageCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "RFC 082 v2.0: Fields as raft-ui DescriptionList direction=horizontal on a phone-width card: below 384px each label sits above its value; values wrap, never truncate; mono values in the code face.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.message-card.header",
      "title": "Card message header, three tags",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageCardBody",
            "components/message/MessageCardElements"
          ],
          "path": "packages/web/visual-testing/MessageCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "RFC 082 v2.0 (§4.8): a card message's header: the title in the Card title style (CardTitle, 600), the subtitle muted, three tags as soft badges in their tone (danger, warning, neutral; info, success, neutral); no colour on the header itself. React-only: the mobile App draws card messages from the shared contract vectors (messageCard.v1.vector.json).",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.message-card.header.dark",
      "title": "Card message header, three tags (dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageCardBody",
            "components/message/MessageCardElements"
          ],
          "path": "packages/web/visual-testing/MessageCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "The header case in the dark theme: the tags keep their status tones.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.message-card.markdown",
      "title": "Card message Markdown with a list and a quote",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageCardBody",
            "components/message/MessageCardElements"
          ],
          "path": "packages/web/visual-testing/MessageCardCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "RFC 082 v2.0: a Markdown element drawn by the message renderer restricted to the card subset: bold, inline code, a nested list, a quote, a link; @name stays text (no chip); then a Divider and a button.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.comment-anchor",
      "title": "Thread Attachment Comment Anchor",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {},
      "notes": "Attachment comments surface with an anchored comment plus a pending anchor chip above the composer. Regression net for task #566: the Android tag's decoration modifiers silently no-opped on Kuikly Text, so the anchor text hugged its border; the fix (Box-wrapped tag, 10dp inset matching the field) is pinned by the android case (mobile #366) and this react reference.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9690255855158285,
        "pixelPerfectSimilarity": 0.9139428984215714,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.comment-anchor.side-by-side.png"
      }
    },
    {
      "id": "components.thread.forward-modal",
      "title": "Thread Forward Modal",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {},
      "notes": "Android Forward target modal (SlockUnifiedDialog): header title/close aligned with the 16dp-inset content rows and brutal-shadow action buttons (task #560). React ForwardComposerDialog vs Android ThreadForwardTargetModal, same seeded destinations.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.68% > 96%",
        "rgbSimilarity": 0.9833121278140899,
        "pixelPerfectSimilarity": 0.9667617260787993,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.forward-modal.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message.agent-read-line.all-reachable",
      "title": "Agent read line: everyone reached (no line)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Niko muted #design but read your message (his face on it) and Cindy replied (her face on her reply): the message reaches everyone, so there is no line. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.all-reachable.dark",
      "title": "Agent read line: everyone reached (no line) (dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Niko muted #design but read your message (his face on it) and Cindy replied (her face on her reply): the message reaches everyone, so there is no line. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.all-reachable.en",
      "title": "Agent read line: everyone reached (no line) (en)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Niko muted #design but read your message (his face on it) and Cindy replied (her face on her reply): the message reaches everyone, so there is no line. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.all-reachable.en.dark",
      "title": "Agent read line: everyone reached (no line) (en, dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Niko muted #design but read your message (his face on it) and Cindy replied (her face on her reply): the message reaches everyone, so there is no line. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.many-narrow",
      "title": "Agent read line: four muted agents, narrow",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Four muted agents at 320 wide. The names take the room left over beside the verb, \"+1\", \"·\" and the whole hint (zh: \"已静音：Produ… +1 · @ 对方才会看到\"). When not even a name's first letters fit (en), the segment shows its count instead of names and +1: \"Muted: 4 agents\", and the hint, still too long for the row, loses its tail (\"Muted: 4 agents · Mention them t…\"). Cindy's face: she read it. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.many-narrow.dark",
      "title": "Agent read line: four muted agents, narrow (dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Four muted agents at 320 wide. The names take the room left over beside the verb, \"+1\", \"·\" and the whole hint (zh: \"已静音：Produ… +1 · @ 对方才会看到\"). When not even a name's first letters fit (en), the segment shows its count instead of names and +1: \"Muted: 4 agents\", and the hint, still too long for the row, loses its tail (\"Muted: 4 agents · Mention them t…\"). Cindy's face: she read it. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.many-narrow.en",
      "title": "Agent read line: four muted agents, narrow (en)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Four muted agents at 320 wide. The names take the room left over beside the verb, \"+1\", \"·\" and the whole hint (zh: \"已静音：Produ… +1 · @ 对方才会看到\"). When not even a name's first letters fit (en), the segment shows its count instead of names and +1: \"Muted: 4 agents\", and the hint, still too long for the row, loses its tail (\"Muted: 4 agents · Mention them t…\"). Cindy's face: she read it. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.many-narrow.en.dark",
      "title": "Agent read line: four muted agents, narrow (en, dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Four muted agents at 320 wide. The names take the room left over beside the verb, \"+1\", \"·\" and the whole hint (zh: \"已静音：Produ… +1 · @ 对方才会看到\"). When not even a name's first letters fit (en), the segment shows its count instead of names and +1: \"Muted: 4 agents\", and the hint, still too long for the row, loses its tail (\"Muted: 4 agents · Mention them t…\"). Cindy's face: she read it. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.muted-channel",
      "title": "Agent read line: muted channel",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest message in #design: Cindy's face (she read it) at the end of its last line, and the line under it, \"Muted: Niko · Mention them to reach them\" with the mute bell (Niko muted the channel and has not read it). React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.muted-channel.dark",
      "title": "Agent read line: muted channel (dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest message in #design: Cindy's face (she read it) at the end of its last line, and the line under it, \"Muted: Niko · Mention them to reach them\" with the mute bell (Niko muted the channel and has not read it). React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.muted-channel.en",
      "title": "Agent read line: muted channel (en)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest message in #design: Cindy's face (she read it) at the end of its last line, and the line under it, \"Muted: Niko · Mention them to reach them\" with the mute bell (Niko muted the channel and has not read it). React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.muted-channel.en.dark",
      "title": "Agent read line: muted channel (en, dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest message in #design: Cindy's face (she read it) at the end of its last line, and the line under it, \"Muted: Niko · Mention them to reach them\" with the mute bell (Niko muted the channel and has not read it). React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.stopped-following-thread",
      "title": "Agent read line: stopped following a thread",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest reply in a thread under #design: Cindy's face (she read it) and \"Stopped following: Niko · Mention them to reach them\" with the unfollow icon. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.stopped-following-thread.dark",
      "title": "Agent read line: stopped following a thread (dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest reply in a thread under #design: Cindy's face (she read it) and \"Stopped following: Niko · Mention them to reach them\" with the unfollow icon. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.stopped-following-thread.en",
      "title": "Agent read line: stopped following a thread (en)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest reply in a thread under #design: Cindy's face (she read it) and \"Stopped following: Niko · Mention them to reach them\" with the unfollow icon. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.thread.message.agent-read-line.stopped-following-thread.en.dark",
      "title": "Agent read line: stopped following a thread (en, dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "app/thread",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/message/MessageItem",
            "components/message/AgentReadFaces",
            "components/message/MessageReadStatusLine"
          ],
          "fixture": "packages/web/visual-testing/AgentReadLineCases.tsx#scenario",
          "path": "packages/web/visual-testing/AgentReadLineCases.tsx",
          "reason": ""
        }
      },
      "notes": "Your latest reply in a thread under #design: Cindy's face (she read it) and \"Stopped following: Niko · Mention them to reach them\" with the unfollow icon. React only: the agent read faces and line are web-only. The real MessageItem rows from your latest message on, with the model useAgentReadProgress builds from the pure projection (agentReadProgressDomain) over the fixture in AgentReadLineCases.tsx; the UI language comes from the variant's `lang`, so zh-CN shows the Chinese line.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "screens.auth.choose-server",
      "title": "Auth Choose Server Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "auth",
      "surface": "auth",
      "group": "auth",
      "status": "pending",
      "providers": {},
      "notes": "Missing post-login choose-server gate parity; this is distinct from the Home top-bar server switcher opened inside the app shell.",
      "comparison": null
    },
    {
      "id": "components.members.agent-lifecycle-actions",
      "title": "Members Agent Lifecycle Actions",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "components-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/features/agent/AgentDetailPanel.tsx",
          "fixture": "fixtures/components.members.agent-lifecycle-actions.json",
          "components": [
            "AgentDetailPanel",
            "AgentDetailHeader",
            "AgentStartStopButton"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentLifecycleActions",
          "components": [
            "MembersDetailContent",
            "MembersDetailHeader",
            "SlockBrutalDropdownMenu"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersAgentProfileTab.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real React AgentDetailPanel mobile action menu and KMP MembersDetailContent management dropdown. The Android fixture opens the same lifecycle action menu state so Stop Agent and Restart / Reset are visible.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8257695306406059,
        "pixelPerfectSimilarity": 0.6987092576865964,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.agent-lifecycle-actions.side-by-side.png"
      }
    },
    {
      "id": "components.members.avatar-management",
      "title": "Members Avatar Management",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "components-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/features/agent/AgentDetailPanel.tsx",
          "fixture": "fixtures/components.members.avatar-management.json",
          "components": [
            "AgentDetailPanel",
            "AvatarSlot",
            "PixelAvatar"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAvatarManagement",
          "components": [
            "MembersDetailContent",
            "MembersAgentProfileTab",
            "MembersAvatar"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersAgentProfileTab.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real React AgentDetailPanel avatar picker and KMP MembersAgentProfileTab inline avatar picker, covering generated/default choices, upload action, and active selection states.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9633407486127374,
        "pixelPerfectSimilarity": 0.9216078637879586,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.avatar-management.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-menu.default",
      "title": "Thread Message Context Menu / Default Message",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem",
            "MenuItem",
            "ContextMenuDivider"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#components.thread.message-menu.default plus contextmenu interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageMenuChrome",
            "ThreadMessageReactionRow",
            "ThreadMessageMenuItem"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageMenuDefault",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageMenuChrome"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android menu cases are stable.",
          "fixture": ""
        }
      },
      "notes": "Real React MessageItem right-click menu captured through the portal plus Android shared ThreadMessageMenuChrome with default message actions. Protects quick reactions and grouped row separators.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.974824743037742,
        "pixelPerfectSimilarity": 0.8082138372422732,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-menu.default.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-menu.task",
      "title": "Thread Message Context Menu / Task Message",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem",
            "MenuItem",
            "ContextMenuDivider"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#components.thread.message-menu.task plus contextmenu interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageMenuChrome",
            "ThreadMessageReactionRow",
            "ThreadMessageMenuItem"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageMenuTask",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageMenuChrome"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android menu cases are stable.",
          "fixture": ""
        }
      },
      "notes": "Real React MessageItem right-click menu for a linked task message plus Android shared ThreadMessageMenuChrome task-state action variant.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9678496638479999,
        "pixelPerfectSimilarity": 0.7746698667314781,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-menu.task.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.deleted-human",
      "title": "Thread Message Row Deleted Human",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.deleted-human.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowDeletedHuman",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "fixture": ""
        }
      },
      "notes": "Real React MessageItem vs Android shared ThreadMessageCard fixture for deleted human authors. Protects muted uploaded-avatar styling and gray-border Deleted sender tag.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9882165769260468,
        "pixelPerfectSimilarity": 0.9520087755381873,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.deleted-human.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.long-inline-code",
      "title": "Thread Message Row Long Inline Code",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.long-inline-code.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowLongInlineCode",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Inline-code cross-platform net (task #400): real React MessageItem vs Android shared long_inline_code thread fixture. Covers compact short inline-code chips, char-level wrapping of long paths/commands (#58), trailing-edge inline-code border (#228), and markdown flow around code spans. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9714663089505644,
        "pixelPerfectSimilarity": 0.9192084902985377,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.long-inline-code.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-latest-release",
      "title": "Thread Message Row Markdown Latest Release Landing",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-latest-release.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdLatestRelease",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/ThreadPage.kt",
          "reason": "OHOS visual capture requires the canonical HDC provider; manual screenshots do not count as measured parity evidence.",
          "fixture": ""
        }
      },
      "notes": "Real mixed-markdown status message requested by artin (task #502): leading horizontal rule, Chinese prose, bold emphasis, inline-code angle-bracket placeholders, and a plain PR reference. React and KMP bodies are byte-for-byte identical; OHOS capture remains pending until the provider lane is available.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9083281609272688,
        "pixelPerfectSimilarity": 0.8285388982469274,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-latest-release.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-link-ref",
      "title": "Thread Message Row Markdown Link Ref",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-link-ref.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdLinkRef",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Raft Markdown dialect net (task #519, guarding task #517): real React MessageItem vs Android shared md_link_ref thread fixture. Refs inside a Markdown link label/destination stay part of one external link; standalone refs outside links chip.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.983755704785728,
        "pixelPerfectSimilarity": 0.9215743991677602,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-link-ref.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-wrap-adjacent",
      "title": "Thread Message Row Markdown Adjacent Chips",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-wrap-adjacent.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdWrapAdjacent",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Adjacent-chip acceptance (task #60): real React MessageItem vs Android md_wrap_adjacent fixture; '#31 / #521' two chips + slash on one line. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9408353096191734,
        "pixelPerfectSimilarity": 0.8831933182360533,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-wrap-adjacent.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-wrap-clarify",
      "title": "Thread Message Row Markdown Wrap Clarify",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-wrap-clarify.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdWrapClarify",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Narrow (260px) markdown message-row wrap net: real React MessageItem vs Android shared md_wrap_clarify thread fixture. Reproduces the Android inline-box line-wrap bug where a task chip wraps to the next line. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8777095441654759,
        "pixelPerfectSimilarity": 0.7962240081643067,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-wrap-clarify.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-wrap-slice1",
      "title": "Thread Message Row Markdown Wrap Slice-1",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-wrap-slice1.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdWrapSlice1",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Narrow (260px) markdown message-row wrap net: real React MessageItem vs Android shared md_wrap_slice1 thread fixture. Reproduces the Android inline-box line-wrap bug where a channel/task chip wraps to the next line. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9879260127647773,
        "pixelPerfectSimilarity": 0.9266344634828048,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-wrap-slice1.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-wrap-status606",
      "title": "Thread Message Row Markdown Status606",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-wrap-status606.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdWrapStatus606",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Agent-status markdown specimen: thread-ref chip + long-hex inline code coverage. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9079672645041323,
        "pixelPerfectSimilarity": 0.8213465372105598,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-wrap-status606.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.md-wrap-task607",
      "title": "Thread Message Row Markdown Wrap Task607",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.md-wrap-task607.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowMdWrapTask607",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Narrow (260px) markdown message-row wrap net: real React MessageItem vs Android shared md_wrap_task607 thread fixture. Reproduces the Android inline-box line-wrap bug where a channel/task chip wraps to the next line. OHOS leg pending self-hosted capture harness.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8541264410481696,
        "pixelPerfectSimilarity": 0.8276247153037984,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.md-wrap-task607.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-row.rich-content",
      "title": "Thread Message Row Rich Content",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message-row.rich-content.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRowRichContent",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "fixture": ""
        }
      },
      "notes": "Real React MessageItem vs Android shared ThreadMessageCard component fixture. Fixture covers long markdown body, @mention, #channel, task #222, URL link, two attachment chips, task footer, reply footer, and reaction footer.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9792561097342816,
        "pixelPerfectSimilarity": 0.9358666995287281,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-row.rich-content.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message-share.preview",
      "title": "Thread Message Share Preview",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "SelectShareLightbox"
          ],
          "path": "packages/web/src/components/message/SelectShareLightbox.tsx",
          "fixture": "React share preview lightbox after SelectModeToolbar Share",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "pending",
          "components": [
            "ThreadSharePreviewModal"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageSharePreview",
          "reason": "The user flow has a native screenshot/export bridge, but this visual fixture still instantiates the modal directly and cannot yet produce the real captured PNG preview."
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadSharePreviewModal"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after Android shared fixture lands.",
          "fixture": ""
        }
      },
      "notes": "React source of truth uses SelectShareLightbox with a real captureSelectedMessages image. Android app flow now calls a native screenshot/export bridge, but the visual fixture remains pending until it can drive that real export path instead of instantiating the modal directly.",
      "comparison": null
    },
    {
      "id": "components.thread.message-share.selection",
      "title": "Thread Message Share Selection",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "components-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "SelectModeToolbar"
          ],
          "path": "packages/web/src/components/message/SelectModeToolbar.tsx",
          "fixture": "React select-mode toolbar with two selected messages",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard",
            "ThreadMessageSelectionToggle",
            "ThreadShareSelectionToolbar"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageShareSelection",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadShareSelectionToolbar"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after Android shared fixture lands.",
          "fixture": ""
        }
      },
      "notes": "Android shared/KMP selected-message rows and bottom shared-message toolbar. React source of truth is SelectModeToolbar; KMP adds Generate to seed an editable composer draft.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9699718881264845,
        "pixelPerfectSimilarity": 0.8837262526835986,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message-share.selection.side-by-side.png"
      }
    },
    {
      "id": "components.ui.avatar-list-row.states",
      "title": "Raft UI Avatar List Row",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/AvatarListRow",
            "components/ui/AvatarSlot",
            "components/ui/SurfaceListItem"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.avatar-list-row.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockAvatarListRow",
            "build.raft.app.components.SlockSurfaceListItem"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_avatar_list_row_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9284371993689688,
        "pixelPerfectSimilarity": 0.707789922336706,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.avatar-list-row.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.badge.states",
      "title": "Raft UI Badge",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/Badge",
            "components/ui/BadgeButton"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.badge.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockBrutalBadge",
            "build.raft.app.components.SlockBrutalBadgeButton"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_badge_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9679986081313711,
        "pixelPerfectSimilarity": 0.9298740006215216,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.badge.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.button.states",
      "title": "Raft UI Button",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/Button"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.button.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockBrutalActionButton"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_button_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.973999680068406,
        "pixelPerfectSimilarity": 0.9451332033788175,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.button.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.card.states",
      "title": "Raft UI Card",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "card-brutal",
            "components/ui/Button"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.card.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockBrutalCard",
            "build.raft.app.components.SlockBrutalActionButton"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_card_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9556385082902359,
        "pixelPerfectSimilarity": 0.9108563318627954,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.card.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.check-marker.states",
      "title": "Raft UI Check Marker",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/CheckMarker"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.check-marker.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockCheckMarker"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_check_marker_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 97.98% > 96%",
        "rgbSimilarity": 0.9965059442663788,
        "pixelPerfectSimilarity": 0.9798106377053746,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.check-marker.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.checkbox.states",
      "title": "Raft UI Checkbox",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/Checkbox",
            "components/ui/CheckMarker"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.checkbox.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockCheckbox"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_checkbox_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.66% > 96%",
        "rgbSimilarity": 0.989987672466747,
        "pixelPerfectSimilarity": 0.9666277787622817,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.checkbox.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.form-field.states",
      "title": "Raft UI Form Field",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/FormField"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.form-field.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockFormField",
            "build.raft.app.components.SlockBrutalInputField"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_form_field_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 97.01% > 96%",
        "rgbSimilarity": 0.9872141390903566,
        "pixelPerfectSimilarity": 0.9700676553746729,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.form-field.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.menu-item.states",
      "title": "Raft UI Menu Item",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/MenuItem"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.menu-item.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockMenuItemRow"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_menu_item_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9827445058486265,
        "pixelPerfectSimilarity": 0.7786108660851384,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.menu-item.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.segmented-control.states",
      "title": "Raft UI Segmented Control",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "raft-ui/SegmentedControl",
            "raft-ui/SegmentedControlItem",
            "raft-ui/SegmentedControlLabel",
            "raft-ui/SegmentedControlCount"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.segmented-control.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SegmentedControl"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_segmented_control_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.39% > 96%",
        "rgbSimilarity": 0.9846330864126747,
        "pixelPerfectSimilarity": 0.9639376218323586,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.segmented-control.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.select.states",
      "title": "Raft UI Select",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/Select",
            "components/ui/SelectionPopover"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.select.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockSelectTrigger",
            "build.raft.app.components.SlockSelectionPopover"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_select_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.87% > 96%",
        "rgbSimilarity": 0.9922772902403776,
        "pixelPerfectSimilarity": 0.9687003878539419,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.select.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.selection-popover.states",
      "title": "Raft UI Selection Popover",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/SelectionPopover"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.selection-popover.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockSelectionPopover"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_selection_popover_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9706447373308326,
        "pixelPerfectSimilarity": 0.7963710717122023,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.selection-popover.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.skeleton.states",
      "title": "Raft UI Skeleton",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/Skeleton",
            "components/ui/SkeletonRow"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.skeleton.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockSkeleton",
            "build.raft.app.components.SlockSkeletonRow"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_skeleton_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "pass",
        "rawStatus": "different",
        "acceptance": "pass: pixel perfect 99.99% > 99%",
        "rgbSimilarity": 0.999999558324521,
        "pixelPerfectSimilarity": 0.9998873727528699,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.skeleton.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.slug-input.states",
      "title": "Raft UI Slug Input",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/SlugInput"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.slug-input.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockSlugInput"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_slug_input_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.53% > 96%",
        "rgbSimilarity": 0.9850518366466304,
        "pixelPerfectSimilarity": 0.9653170570628149,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.slug-input.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.surface-list-item.states",
      "title": "Raft UI Surface List Item",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/SurfaceListItem"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.surface-list-item.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockSurfaceListItem"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_surface_list_item_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9781779388418506,
        "pixelPerfectSimilarity": 0.9305885797113868,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.surface-list-item.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.textarea.states",
      "title": "Raft UI Textarea",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/ui/Textarea"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.textarea.states.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.app.components.SlockBrutalTextarea"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiPrimitiveVisualFixtures.kt",
          "fixture": "compose_ui_textarea_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9769897894902655,
        "pixelPerfectSimilarity": 0.9477526119826315,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.textarea.states.side-by-side.png"
      }
    },
    {
      "id": "components.ui.typography.specimen",
      "title": "Raft UI Typography Specimen",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "library",
      "surface": "library",
      "group": "library",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "raft-ui typography (text-* classes)"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "fixtures/components.ui.typography.specimen.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "build.raft.designsystem.theme.RaftTypography"
          ],
          "path": "compose/shared/src/visualFixtures/kotlin/build/raft/app/UiDesignSystemVisualFixtures.kt",
          "fixture": "compose_ui_typography_specimen_visual",
          "reason": ""
        }
      },
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9330404968282954,
        "pixelPerfectSimilarity": 0.8904006930907515,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.ui.typography.specimen.side-by-side.png"
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-held-sign-in",
      "title": "Agent Profile Status: Invalid Credential, Held for another agent's sign-in",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). An agent held for another agent's rejected sign-in on the same machine-shared sign-in (idle status with credentialHold user_reauth): the status chip names the reason (error light) instead of Online, with the Retry TextLink beside it. Owner view. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-held-sign-in.dark",
      "title": "Agent Profile Status: Invalid Credential, Held for another agent's sign-in (Dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). An agent held for another agent's rejected sign-in on the same machine-shared sign-in (idle status with credentialHold user_reauth): the status chip names the reason (error light) instead of Online, with the Retry TextLink beside it. Owner view. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-member-view",
      "title": "Agent Profile Status: Invalid Credential, Member view",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). The held agent seen by a member who did not create it: may restart it (controlAgentRuntime) but not see its details, so the chip is a plain Error with Retry beside it. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-member-view.dark",
      "title": "Agent Profile Status: Invalid Credential, Member view (Dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). The held agent seen by a member who did not create it: may restart it (controlAgentRuntime) but not see its details, so the chip is a plain Error with Retry beside it. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-own-billing",
      "title": "Agent Profile Status: Invalid Credential, Own billing error",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). An agent whose own runtime error is BillingError (stored lastRuntimeError, live error activity): the status chip says the provider balance is exhausted, with Retry; the runtime-error notice stays above. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.agent-detail.status.credential-own-billing.dark",
      "title": "Agent Profile Status: Invalid Credential, Own billing error (Dark)",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "features/agent/AgentDetailPanel",
            "features/agent/AgentStatusBadge"
          ],
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "fixture": "",
          "reason": ""
        }
      },
      "notes": "#proj-raft-computer:#48 (reconciler step 4b). An agent whose own runtime error is BillingError (stored lastRuntimeError, live error activity): the status chip says the provider balance is exhausted, with Retry; the runtime-error notice stays above. react-only: Android has no reconciler status yet.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.create-agent.builtin-provider-dialog",
      "title": "Members Create Agent Dialog - built-in provider",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agent/RuntimeConfigFields"
          ],
          "fixture": "fixtures/components.members.create-agent.dialog.json",
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "reason": ""
        }
      },
      "notes": "task #9: the built-in provider API key and gateway Base URL only render when the machine offers the `builtin` runtime; every earlier case seeds claude/codex, so these migrated inputs were unreachable. providerMatrix is react-only on purpose - this is a Web UI regression surface and must not create Android/iOS capture obligations.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "components.members.create-agent.claude-custom-provider-dialog",
      "title": "Members Create Agent Dialog - Claude Custom Provider",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9240257655477417,
        "pixelPerfectSimilarity": 0.7850440852810521,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.claude-custom-provider-dialog.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.claude-dialog",
      "title": "Members Create Agent Dialog - Claude Code",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9447538462597513,
        "pixelPerfectSimilarity": 0.8700486760913301,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.claude-dialog.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.dialog-empty",
      "title": "Members Create Agent Dialog - empty state",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "No name, no description: the placeholder rendering no other case exercises.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9504320436085912,
        "pixelPerfectSimilarity": 0.8746286844628077,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.dialog-empty.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.dialog-error",
      "title": "Members Create Agent Dialog - inline name error",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Error-state baseline: invalid name triggers inline validation so the raft-ui Input data-invalid styling is captured, not just read from the recipe.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9424520548465524,
        "pixelPerfectSimilarity": 0.8616073236926317,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.dialog-error.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.dialog-no-computer",
      "title": "Members Create Agent Dialog - no computer branch",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "The zero-machine branch: a separate screen with its own close/cancel/CTA buttons.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9581413395935166,
        "pixelPerfectSimilarity": 0.874578388085497,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.dialog-no-computer.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.pi-provider-dialog",
      "title": "Members Create Agent Dialog - Pi provider",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "components/agent/RuntimeConfigFields"
          ],
          "fixture": "fixtures/components.members.create-agent.dialog.json",
          "path": "packages/web/visual-testing/VisualTestingCases.tsx",
          "reason": ""
        }
      },
      "notes": "task #9: the Pi provider API key is gated on runtime === \"pi\", which no earlier case offered. react-only for the same reason as the built-in provider case.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "missing",
        "rawStatus": "missing",
        "acceptance": "",
        "rgbSimilarity": null,
        "pixelPerfectSimilarity": null,
        "sideBySideImage": null
      }
    },
    {
      "id": "screens.members.agent-detail.profile.computer-missing",
      "title": "Members Agent Detail Profile Screen (Computer Missing)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-computer-missing: machineId points at a machine the store never loaded: web = 'No computer assigned', no dot (task #535 state 4; web has the same loading-transient shape, so this is a correctness oracle, not alignment). React route /s/visual/agent/agent-computer-missing with agentTab=profile; Android compose_member_detail pageData with memberId=agent-computer-missing, memberKind=agent, initialAgentTab=profile.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8433671841009733,
        "pixelPerfectSimilarity": 0.761791448821333,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.computer-missing.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.computer-offline",
      "title": "Members Agent Detail Profile Screen (Computer Offline)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-computer-offline: store knows the machine and reports it offline: web = gray dot + Offline (task #535 state 3). React route /s/visual/agent/agent-computer-offline with agentTab=profile; Android compose_member_detail pageData with memberId=agent-computer-offline, memberKind=agent, initialAgentTab=profile.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9172716722631675,
        "pixelPerfectSimilarity": 0.8232632167591517,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.computer-offline.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.daemon-only",
      "title": "Members Agent Detail Profile Screen (Daemon Only)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-daemon-only: bound to a bare daemon (isComputer false): web status line = 'Connected' span + '·' + version segment exactly 'legacy v0.65.0' (machineRunLabel picks daemonVersion for non-computers and, since #7823 retired the standalone daemon lane, names the kind 'legacy'; offline would read 'legacy offline') (task #535 batch 2).",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.830538510448892,
        "pixelPerfectSimilarity": 0.7511008998659774,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.daemon-only.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.loading-state",
      "title": "Members Agent Detail Profile Screen (Loading State)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-product-ux: machines list still in flight; artin 2026-09-03 17:17: the Computer row must not appear until loaded, both ends. Web today flashes 'No computer assigned' here — known-wrong reference, expected to differ until both ends implement (task #259).",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8539904181617295,
        "pixelPerfectSimilarity": 0.7710306456295204,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.loading-state.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.long-machine-name",
      "title": "Members Agent Detail Profile Screen (Long Machine Name)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-long-machine: machine name that cannot fit one 390dp line, so wrap / silent-truncate / overflow is observable (task #537). React route /s/visual/agent/agent-long-machine with agentTab=profile; Android compose_member_detail pageData with memberId=agent-long-machine, memberKind=agent, initialAgentTab=profile.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.820747764459365,
        "pixelPerfectSimilarity": 0.7385146033641207,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.long-machine-name.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.no-computer",
      "title": "Members Agent Detail Profile Screen (No Computer)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-no-computer: no machineId: web = 'No computer assigned', no dot; Android today says Offline, which is a false statement (task #535 state 5). React route /s/visual/agent/agent-no-computer with agentTab=profile; Android compose_member_detail pageData with memberId=agent-no-computer, memberKind=agent, initialAgentTab=profile.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.907503387690339,
        "pixelPerfectSimilarity": 0.818986813078328,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.no-computer.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile.no-membership",
      "title": "Members Agent Detail Profile Screen (No Membership)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "members",
      "status": "active",
      "providers": {},
      "notes": "Profile tab for fixture agent agent-no-membership: serverRole omitted = no membership row; artin 2026-09-03 16:54: hide the role chip on both ends (task #261).",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8434425553337161,
        "pixelPerfectSimilarity": 0.7626462783348315,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.no-membership.side-by-side.png"
      }
    },
    {
      "id": "components.home.server-switcher.open",
      "title": "Home Server Switcher Open",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product",
      "status": "active",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "ServerSwitcherMenu"
          ],
          "path": "packages/web/src/components/ui/ServerSwitcherMenu.tsx",
          "fixture": "fixtures/components.home.server-switcher.open.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "SharedHomeStore",
            "SlockHomeComponents server switcher"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.homeServerSwitcherPageData",
          "reason": "Captured through real SlockKuiklyActivity PAGE_HOME with serverSwitcherExpanded=true, cropped to raw server switcher content.",
          "source": "kmp-shared"
        },
        "harmony": {
          "status": "pending",
          "components": [
            "SlockHomeComponents server switcher"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "reason": "Depends on KMP/shared fixture route and OHOS capture.",
          "source": "kmp-shared-ohos",
          "fixture": ""
        }
      },
      "notes": "React vs Android is active. Android currently renders this through the real KMP/shared Home route; native approximations are unsupported.",
      "comparison": null
    },
    {
      "id": "components.thread.composer.as-task-selected",
      "title": "Thread Composer As Task Selected",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "components.thread.composer.states route plus As Task click interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerAsTaskSelected",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput with As Task checked vs Android connected visual-only rendering of ThreadComposerContent seeded with seedAsTask=true. This is a focused diff for the selected As Task toggle.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9213085414658296,
        "pixelPerfectSimilarity": 0.8903287258550416,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.as-task-selected.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.channel-suggestions",
      "title": "Thread Composer Channel Suggestions",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "fixtures/components.thread.composer.channel-suggestions.json plus # trigger interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page",
            "ComposerSuggestionPanel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerChannelSuggestions",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput #channel autocomplete popover vs Android connected visual-only rendering of ThreadComposerContent with the channel suggestion panel open. Covers the # icon, channel name, and description row layout.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9735914334598915,
        "pixelPerfectSimilarity": 0.8019137833008447,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.channel-suggestions.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.empty",
      "title": "Thread Composer Empty",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "fixtures/components.thread.composer.empty.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerEmpty",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput empty state vs Android connected visual-only rendering of ThreadComposerContent with an empty seed. This case specifically protects the disabled send button color/opacity and empty placeholder treatment.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.944267770320573,
        "pixelPerfectSimilarity": 0.9062462397420162,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.empty.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.file-previews",
      "title": "Thread Composer File Previews",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "skipped",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput",
            "SortableAttachment"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#components.thread.composer.file-previews",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page",
            "ComposerAttachmentTray",
            "ComposerAttachmentTile"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerFilePreviews",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "fixture alignment deferred — artin 2026-07-05: 例子没对齐，但也许不用做了",
      "comparison": null
    },
    {
      "id": "components.thread.composer.image-preview",
      "title": "Thread Composer Image Preview",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput",
            "SortableAttachment"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#components.thread.composer.image-preview",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page",
            "ComposerAttachmentTray",
            "ComposerAttachmentTile"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerImagePreview",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput pending image attachment chip vs Android connected visual-only rendering of ThreadComposerContent with a ready image preview above the composer card.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9427453912761441,
        "pixelPerfectSimilarity": 0.9177991274482502,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.image-preview.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.member-suggestions",
      "title": "Thread Composer Member Suggestions",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput",
            "MentionCandidateBody",
            "MentionCandidateAvatar"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "fixtures/components.thread.composer.member-suggestions.json plus @ trigger interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page",
            "ComposerMemberSuggestionPanel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerMemberSuggestions",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput @mention autocomplete popover vs Android connected visual-only rendering of ThreadComposerContent with the member suggestion panel open. Covers avatar, agent description, channel-membership grouping, and trailing @handle treatment.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9624960541387487,
        "pixelPerfectSimilarity": 0.7622503911340528,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.member-suggestions.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.pending-mention-actions",
      "title": "Thread Composer Pending Mention Actions",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput",
            "PendingMentionActionStrip"
          ],
          "path": "packages/web/src/components/message/PendingMentionActionStrip.tsx",
          "fixture": "fixtures/components.thread.composer.pending-mention-actions.json plus Send interaction",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page",
            "PendingMentionActionBanner"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerPendingMentionActions",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput post-send PendingMentionActionStrip vs Android ThreadComposerContent PendingMentionActionBanner. Seeded action covers an agent outside the current channel with Add, Notify, and Dismiss controls so nightly diffs expose typography, button chrome, spacing, and layout drift.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9289909575146779,
        "pixelPerfectSimilarity": 0.865426497277677,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.pending-mention-actions.side-by-side.png"
      }
    },
    {
      "id": "components.thread.composer.states",
      "title": "Thread Composer States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageInput"
          ],
          "path": "packages/web/src/features/composer/MessageInput.tsx",
          "fixture": "fixtures/components.thread.composer.states.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadComposerContent visual-only page"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadComposerStates",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadComposerPage composer"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadComposerPage.kt",
          "reason": "Depends on shared composer fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageInput draft state vs Android connected visual-only rendering of ThreadComposerContent. Baseline covers the complete composer shell with draft text, attachment buttons, As Task toggle, and Send button using the same component-bounds crop as the empty and selected states.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9225373728622646,
        "pixelPerfectSimilarity": 0.8918276330557032,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.composer.states.side-by-side.png"
      }
    },
    {
      "id": "components.thread.message.row",
      "title": "Thread Message Row",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageItem"
          ],
          "path": "packages/web/src/components/message/MessageItem.tsx",
          "fixture": "fixtures/components.thread.message.row.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadMessageCard"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadMessageRow",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadMessageCard"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Depends on shared thread fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React MessageItem vs Android rendering of shared ThreadMessageCard with matching fixture text, reply count, and task metadata.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9865629512067273,
        "pixelPerfectSimilarity": 0.9474838886603593,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.message.row.side-by-side.png"
      }
    },
    {
      "id": "tasks.card.states",
      "title": "Task Card States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/tasks",
      "surface": "tasks",
      "group": "product",
      "status": "pending",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "TasksPanel",
            "Task card"
          ],
          "path": "packages/web/src/components/task/TasksPanel.tsx",
          "fixture": "fixtures/tasks.card.states.json",
          "reason": ""
        },
        "android": {
          "status": "pending",
          "components": [
            "SharedTasksPage",
            "SharedTasksStore"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedTasksPage.kt",
          "reason": "Needs shared Tasks visual fixture after recent MVI route migration stabilizes.",
          "source": "kmp-shared",
          "fixture": ""
        },
        "harmony": {
          "status": "pending",
          "components": [
            "SharedTasksPage"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedTasksPage.kt",
          "reason": "Depends on shared Tasks fixture route and OHOS capture.",
          "source": "kmp-shared-ohos",
          "fixture": ""
        }
      },
      "notes": "",
      "comparison": null
    },
    {
      "id": "components.channel.members.add-panel",
      "title": "Channel Add Member Panel",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "channel",
      "surface": "channel",
      "group": "product-channel",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ChannelMembers",
            "MemberChoiceList"
          ],
          "path": "packages/web/src/features/channel/ChannelMembers.tsx",
          "fixture": "fixtures/components.channel.members.add-panel.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "ChannelAddMemberPanel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/ChannelAddMemberPanel.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureChannelAddMemberPanel",
          "reason": ""
        }
      },
      "notes": "Phone-width add-member view: real React ChannelMembers presentation=\"page\" (the channel overflow drawer's members page, 390x844) after tapping the bottom-pinned Add Member entry. Android/KMP provider is the shared ChannelAddMemberPanel.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9661458515392091,
        "pixelPerfectSimilarity": 0.922720254017964,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.channel.members.add-panel.side-by-side.png"
      }
    },
    {
      "id": "components.channel.settings.panel",
      "title": "Channel Settings Panel",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "channel",
      "surface": "channel",
      "group": "product-channel",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "EditChannelDialog",
            "FormField"
          ],
          "path": "packages/web/src/features/channel/EditChannelDialog.tsx",
          "fixture": "fixtures/components.channel.settings.panel.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "ThreadSettingsModal",
            "Shared channel settings panel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureChannelSettingsPanel",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "Shared channel settings panel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React channel drawer (ChatPanel settings trigger -> ChannelOverflowMenu with EditChannelDialog as its body); query params channel=design|private|joint|dm, view=members, role=member pick the variant. Android/KMP provider uses the shared thread settings modal route; Harmony capture remains pending.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9362867700561361,
        "pixelPerfectSimilarity": 0.8831834568801393,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.channel.settings.panel.side-by-side.png"
      }
    },
    {
      "id": "components.home.activity.results",
      "title": "Home Activity Results",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ThreadsInbox",
            "PanelHeader",
            "SegmentedControl",
            "ConversationPreviewCard"
          ],
          "path": "packages/web/src/features/thread/ThreadsInbox.tsx",
          "fixture": "fixtures/components.home.activity.results.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedHomeActivityScreen",
            "SharedActivityHeader",
            "SharedActivityRow",
            "SegmentedControl"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedInboxPanel.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeActivityResults",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedHomeActivityScreen"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedInboxPanel.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React ThreadsInbox activity page vs Android rendering of shared Home Activity panel. Covers PanelHeader/back affordance, segmented filters, mark-all-read action, ConversationPreviewCard-like rows, unread/mention/task footer badges, and scroll body spacing.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9330676375855421,
        "pixelPerfectSimilarity": 0.8243869081305414,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.activity.results.side-by-side.png"
      }
    },
    {
      "id": "components.home.create-channel.dialog",
      "title": "Home Create Channel Dialog",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "CreateChannelDialog",
            "FormField",
            "SegmentedControl"
          ],
          "path": "packages/web/src/components/channel/CreateChannelDialog.tsx",
          "fixture": "fixtures/components.home.create-channel.dialog.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedCreateChannelDialog",
            "SharedCreateChannelField",
            "SharedCreateChannelMemberPicker"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeCreateChannelDialog",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedCreateChannelDialog"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React CreateChannelDialog vs Android rendering of the shared SharedCreateChannelDialog/SlockUnifiedDialog route.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9442083517447237,
        "pixelPerfectSimilarity": 0.8904244474149687,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.create-channel.dialog.side-by-side.png"
      }
    },
    {
      "id": "components.home.notification-center.states",
      "title": "Home Notification Center States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "NotificationCenter"
          ],
          "path": "packages/web/src/components/layout/NotificationCenter.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#VISUAL_NOTIFICATION_CENTER_ENTRIES",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "HomeNotificationPopover"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeNotificationCenterStates",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "HomeNotificationPopover"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "React and Android now render the same two-notification fixture: warning agent disconnect plus info release-gate review. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9853427703519025,
        "pixelPerfectSimilarity": 0.9055974129433845,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.notification-center.states.side-by-side.png"
      }
    },
    {
      "id": "components.home.saved.results",
      "title": "Home Saved Results",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "SavedPanel",
            "SavedItem",
            "PanelHeader"
          ],
          "path": "packages/web/src/components/saved/SavedPanel.tsx",
          "fixture": "fixtures/components.home.saved.results.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedHomeSavedPanel",
            "SharedHomeInboxResultRow",
            "SharedHomeLoadMoreRow"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeSavedResults",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedHomeSavedPanel",
            "SharedHomeInboxResultRow",
            "SharedHomeLoadMoreRow"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React SavedPanel saved-message list vs Android rendering of shared Home Saved panel after Search/Saved parity. Covers panel header, saved channel/thread/DM rows, sender avatar/meta, remove affordance, and load-more action.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9558605513792359,
        "pixelPerfectSimilarity": 0.9276551594038861,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.saved.results.side-by-side.png"
      }
    },
    {
      "id": "components.home.search.channel-dropdown",
      "title": "Home Search Channel Dropdown",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageSearchPage",
            "SelectionPopover"
          ],
          "path": "packages/web/src/features/search/MessageSearchPage.tsx",
          "fixture": "fixtures/components.home.search.results.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedHomeSearchPanel",
            "SlockFilterPopover"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeSearchChannelDropdown",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedHomeSearchPanel",
            "SlockFilterPopover"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Focused open-state canonical for the search channel filter. React opens the real MessageSearchPage SelectionPopover; Android opens SharedHomeSearchPanel's real SlockFilterPopover through initialOpenFilterId after the channel trigger anchor is measured. Covers trigger alignment, popover border/shadow, internal search input chrome, and channel option rows without DM leakage.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9672037491087918,
        "pixelPerfectSimilarity": 0.8560369155155885,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.search.channel-dropdown.side-by-side.png"
      }
    },
    {
      "id": "components.home.search.results",
      "title": "Home Search Results",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MessageSearchPage",
            "SelectionPopover",
            "SegmentedControl"
          ],
          "path": "packages/web/src/features/search/MessageSearchPage.tsx",
          "fixture": "fixtures/components.home.search.results.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedHomeSearchPanel",
            "SlockFilterPopover",
            "SharedHomeInboxResultRow"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeSearchResults",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedHomeSearchPanel",
            "SlockFilterPopover",
            "SharedHomeInboxResultRow"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React MessageSearchPage filtered results vs Android rendering of shared Home Search panel. Covers query field, reusable filter primitives, entity results, grouped thread hits, and load-more affordance.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9263889328652288,
        "pixelPerfectSimilarity": 0.7761472022341588,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.search.results.side-by-side.png"
      }
    },
    {
      "id": "components.home.titlebar.states",
      "title": "Home Titlebar States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/home",
      "surface": "home",
      "group": "product-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "Sidebar",
            "NotificationTrigger"
          ],
          "path": "packages/web/src/features/sidebar/Sidebar.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#HomeTitlebarVisualCaseView",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "HomeWorkspaceHeader",
            "HomeNotificationCenter"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeTitlebarStates",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "HomeWorkspaceHeader",
            "HomeNotificationCenter"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockHomeComponents.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Four states declared as the case variants: other-server unread with a long name, short name with a server avatar, long-name ellipsis, and the expanded server switcher. React renders each with the real mobile Sidebar in its own iframe of the visual host (the bar reads global stores); Android renders the same board on its KMP page. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.973309074934043,
        "pixelPerfectSimilarity": 0.8056281308650977,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.home.titlebar.states.side-by-side.png"
      }
    },
    {
      "id": "components.members.agent-detail.profile",
      "title": "Members Agent Detail Profile",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "product-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "AgentDetailPanel",
            "AgentDetailHeader",
            "AgentProfileInfo",
            "AgentSkills"
          ],
          "path": "packages/web/src/features/agent/AgentDetailPanel.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "MembersDetailContent",
            "MembersDetailHeader",
            "MembersAgentProfileTab",
            "MembersAgentLifecycleSection"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailProfile",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "MembersDetailContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React AgentDetailPanel profile tab vs Android rendering of the shared Members detail profile surface. Fixture pins a manageable Codex agent with machine, runtime config, skills, DMs, reminders, and activity summary data; additional private tabs can be split into separate cases.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8293275669244189,
        "pixelPerfectSimilarity": 0.7493427097145865,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.agent-detail.profile.side-by-side.png"
      }
    },
    {
      "id": "components.members.create-agent.dialog",
      "title": "Members Create Agent Dialog - Runtime Matrix",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "members",
      "surface": "members",
      "group": "product-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "CreateAgentDialog",
            "FormField",
            "Select",
            "RuntimeConfigFields"
          ],
          "path": "packages/web/src/features/agent/CreateAgentDialog.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SharedCreateAgentDialog",
            "MachinePicker",
            "AdvancedRuntimeSection",
            "SlockUnifiedDialog"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedCreateAgentDialog.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersCreateAgent*Dialog",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedCreateAgentDialog"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedCreateAgentDialog.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React CreateAgentDialog vs Android rendering of the shared SharedCreateAgentDialog/SlockUnifiedDialog visual route. Matrix covers Codex CLI default, Claude Code default, and Claude custom-provider/advanced configuration because each runtime exposes different selectable fields.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9449480264149016,
        "pixelPerfectSimilarity": 0.8703403275678158,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.members.create-agent.dialog.side-by-side.png"
      }
    },
    {
      "id": "components.navigation.tabbar.states",
      "title": "Navigation Tabbar States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/navigation",
      "surface": "navigation",
      "group": "product-navigation",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MobileTabBar"
          ],
          "path": "packages/web/src/components/layout/MainLayout.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#NavigationTabbarVisualCaseView",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SlockBottomTabBar"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SlockTabBar.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureNavigationTabbarStates",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared",
          "reason": "Needs Harmony/HDS real tabbar provider capture.",
          "components": [],
          "path": "",
          "fixture": ""
        }
      },
      "notes": "React now captures the real MainLayout MobileTabBar from the visual route. The previous hand-built VisualMobileTabBar approximation was removed from the provider path. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9860444729965433,
        "pixelPerfectSimilarity": 0.9317901234567901,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.navigation.tabbar.states.side-by-side.png"
      }
    },
    {
      "id": "components.settings.account.error-state",
      "title": "Settings Account Error State",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "AccountSection"
          ],
          "path": "packages/web/src/features/settings/account/AccountSection.tsx",
          "fixture": "fixtures/components.settings.account.error-state.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsAccountPage",
            "SettingsHomePage"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsAccountPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAccountErrorState",
          "route": "compose_settings_page_visual:account-error",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsAccountPage",
            "SettingsHomePage"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsAccountPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Account save/avatar/password/provider error messages are rendered from typed Store action state rather than raw API responses.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8218233872222827,
        "pixelPerfectSimilarity": 0.7438739660785656,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.account.error-state.side-by-side.png"
      }
    },
    {
      "id": "components.settings.account.page",
      "title": "Settings Account Page",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "AccountSection"
          ],
          "path": "packages/web/src/features/settings/account/AccountSection.tsx",
          "fixture": "fixtures/components.settings.account.page.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAccountPage",
          "route": "compose_settings_page_visual:account",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8176962997564832,
        "pixelPerfectSimilarity": 0.7458919633692923,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.account.page.side-by-side.png"
      }
    },
    {
      "id": "components.settings.administration.admins",
      "title": "Settings Administration Admins",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "AdminsSection"
          ],
          "path": "packages/web/src/features/settings/administration/AdminsSection.tsx",
          "fixture": "fixtures/components.settings.administration.admins.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationAdmins",
          "route": "compose_settings_page_visual:administration-admins",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.invites",
      "title": "Settings Administration Invites",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "InvitesSection"
          ],
          "path": "packages/web/src/features/settings/administration/InvitesSection.tsx",
          "fixture": "fixtures/components.settings.administration.invites.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationInvites",
          "route": "compose_settings_page_visual:administration-invites",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.join-links",
      "title": "Settings Administration Join Links",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "JoinLinksSection"
          ],
          "path": "packages/web/src/features/settings/administration/JoinLinksSection.tsx",
          "fixture": "fixtures/components.settings.administration.join-links.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationJoinLinks",
          "route": "compose_settings_page_visual:administration-join-links",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.member-permissions",
      "title": "Settings Administration Member Permissions",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "MemberPermissionsSection"
          ],
          "path": "packages/web/src/features/settings/administration/MemberPermissionsSection.tsx",
          "fixture": "fixtures/components.settings.administration.member-permissions.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationMemberPermissions",
          "route": "compose_settings_page_visual:administration-member-permissions",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.onboarding",
      "title": "Settings Administration Onboarding",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "OnboardingAgentSection"
          ],
          "path": "packages/web/src/features/settings/administration/OnboardingAgentSection.tsx",
          "fixture": "fixtures/components.settings.administration.onboarding.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationOnboarding",
          "route": "compose_settings_page_visual:administration-onboarding",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.pre-join-agreement",
      "title": "Settings Administration Pre-join Agreement",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "PreJoinAgreementSection"
          ],
          "path": "packages/web/src/features/settings/administration/PreJoinAgreementSection.tsx",
          "fixture": "fixtures/components.settings.administration.pre-join-agreement.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationPreJoinAgreement",
          "route": "compose_settings_page_visual:administration-pre-join-agreement",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.administration.translation",
      "title": "Settings Administration Translation",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ServerTranslationSection"
          ],
          "path": "packages/web/src/features/settings/administration/ServerTranslationSection.tsx",
          "fixture": "fixtures/components.settings.administration.translation.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAdministrationTranslation",
          "route": "compose_settings_page_visual:administration-translation",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": null
    },
    {
      "id": "components.settings.appearance.page",
      "title": "Settings Appearance Page",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "SettingsPanel",
            "AppearanceSection"
          ],
          "path": "packages/web/src/features/settings/appearance/AppearanceSection.tsx#AppearanceSection",
          "fixture": "fixtures/components.settings.appearance.page.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsPageVisualContent",
            "SettingsAppearanceContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsAppearancePage",
          "route": "compose_settings_page_visual:appearance",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "Real React SettingsPanel AppearanceSection mapped to KMP SettingsAppearanceContent through the shared settings visual page; full Settings route coverage remains screens.settings.appearance pending.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.7356399512707181,
        "pixelPerfectSimilarity": 0.6385064180677341,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.appearance.page.side-by-side.png"
      }
    },
    {
      "id": "components.settings.notifications.page",
      "title": "Settings Notifications Page",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "NotificationsTabContent"
          ],
          "path": "packages/web/src/features/settings/notifications/NotificationsTabContent.tsx",
          "fixture": "fixtures/components.settings.notifications.page.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsNotificationsPage",
          "route": "compose_settings_page_visual:notifications",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8910211621539116,
        "pixelPerfectSimilarity": 0.690374342537804,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.notifications.page.side-by-side.png"
      }
    },
    {
      "id": "components.settings.root.page",
      "title": "Settings Root Page",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "Sidebar"
          ],
          "path": "packages/web/src/features/sidebar/Sidebar.tsx",
          "fixture": "fixtures/components.settings.root.page.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsAppShellContent",
            "SettingsHomeContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsRootPage",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsAppShellContent",
            "SettingsHomeContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture for the Settings tab root route.",
          "fixture": ""
        },
        "ios": {
          "id": "ios",
          "status": "pending",
          "source": "kmp-shared-ios",
          "components": [
            "SettingsAppShellContent",
            "SettingsHomeContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/SettingsHomePage.kt",
          "reason": "iOS generator should pick up the manifest case; baseline remains pending until generated capture is published.",
          "fixture": ""
        }
      },
      "notes": "Real mobile Settings tab root / first screen: React Sidebar mobileInline grouped list vs Android shared SettingsAppShellContent home route. Covers PERSONAL, WORKSPACE, ABOUT groups and Mobile, About, Release Notes rows.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9523609247371593,
        "pixelPerfectSimilarity": 0.9354595074892105,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.root.page.side-by-side.png"
      }
    },
    {
      "id": "components.settings.server.profile",
      "title": "Settings Server Profile",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "product-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ProfileSection"
          ],
          "path": "packages/web/src/features/settings/server/ProfileSection.tsx",
          "fixture": "fixtures/components.settings.server.profile.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsServerProfile",
          "route": "compose_settings_page_visual:server-profile",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SettingsHomePage",
            "SettingsDetailShell"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony real-device/provider capture; pending providers are excluded from similarity accounting.",
          "fixture": ""
        }
      },
      "notes": "React SettingsPanel sub-page mapped to the real shared KMP Settings visual route using a pageData variant and typed sample snapshot.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9629128313705797,
        "pixelPerfectSimilarity": 0.9161638880937126,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.settings.server.profile.side-by-side.png"
      }
    },
    {
      "id": "components.tasks.panel.states",
      "title": "Tasks Panel States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/tasks",
      "surface": "tasks",
      "group": "product-tasks",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "TasksPanel",
            "TaskSection",
            "TaskCard",
            "InlineBadgeEditor"
          ],
          "path": "packages/web/src/components/task/TasksPanel.tsx",
          "fixture": "fixtures/components.tasks.panel.states.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "kmp-shared",
          "components": [
            "SharedTasksPanel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/TasksPage.kt",
          "reason": "Needs Android provider fixture for the tasks surface (task #353).",
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedTasksPanel"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/TasksPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React TasksPanel (server mode, list view) with shared tasksFixture.json rows covering todo / in_progress / in_review / done status sections, task numbers, and per-status InlineBadgeEditor chips.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9334358187569234,
        "pixelPerfectSimilarity": 0.8763197752328187,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.tasks.panel.states.side-by-side.png"
      }
    },
    {
      "id": "components.tasks.status-menu",
      "title": "Tasks Status Menu",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/tasks",
      "surface": "tasks",
      "group": "product-tasks",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "InlineBadgeEditor"
          ],
          "path": "packages/web/src/components/InlineBadgeEditor.tsx",
          "fixture": "fixtures/components.tasks.status-menu.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "kmp-shared",
          "components": [
            "SharedTaskStatusMenu"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/TasksPage.kt",
          "reason": "Needs Android provider fixture for the tasks surface (task #353).",
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "SharedTaskStatusMenu"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/TasksPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "Real React InlineBadgeEditor status menu rendered open with all five selectable statuses (Todo / In Progress / In Review / Done / Closed) and the current status check-marked.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9876806679753596,
        "pixelPerfectSimilarity": 0.8673764182536112,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.tasks.status-menu.side-by-side.png"
      }
    },
    {
      "id": "components.thread.files.list",
      "title": "Thread Files List",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ChannelFilesPanel",
            "AvatarListRow"
          ],
          "path": "packages/web/src/components/message/ChannelFilesPanel.tsx",
          "fixture": "packages/visual-testing/tests/react-provider.spec.ts#/channels/visual-thread-composer/files",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "ThreadFilesContent",
            "ThreadFileRow",
            "ThreadFileThumbnail"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadFilesContent.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadFilesList",
          "source": "kmp-shared",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "ThreadFilesContent"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadFilesContent.kt",
          "reason": "Depends on shared files fixture route and OHOS capture.",
          "source": "kmp-shared",
          "fixture": ""
        }
      },
      "notes": "Real React ChannelFilesPanel vs Android connected visual-only rendering of ThreadFilesContent with image, PDF, archive, download/open state, and load-more affordance.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "basic-pass",
        "rawStatus": "different",
        "acceptance": "basic pass: pixel perfect 96.88% > 96%",
        "rgbSimilarity": 0.992816506216288,
        "pixelPerfectSimilarity": 0.9687693890471668,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.files.list.side-by-side.png"
      }
    },
    {
      "id": "components.thread.header.states",
      "title": "Thread Header States",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "product-thread",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ChatPanel",
            "PanelHeader"
          ],
          "path": "packages/web/src/features/channel/ChatPanel.tsx",
          "fixture": "packages/web/visual-testing/VisualTestingCases.tsx#ThreadHeaderVisualCaseView",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "ThreadPageHeader",
            "ThreadPageTabs"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureThreadHeaderStates",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared-ohos",
          "components": [
            "ThreadPageHeader",
            "ThreadPageTabs"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony real-device/provider capture.",
          "fixture": ""
        }
      },
      "notes": "React now captures the real ChatPanel PanelHeader with the visual channel fixture. The previous hand-built PanelHeader wrapper was removed from the provider path. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9734361301975648,
        "pixelPerfectSimilarity": 0.8945275403608737,
        "sideBySideImage": "visual-testing-results/diff/react__android/components.thread.header.states.side-by-side.png"
      }
    },
    {
      "id": "screens.auth.forgot-password",
      "title": "Forgot Password Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "auth",
      "surface": "auth",
      "group": "screens-auth",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/components/auth/ForgotPasswordPage.tsx",
          "fixture": "fixtures/screens.auth.forgot-password.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/AuthPages.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/AuthPages.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Missing forgot-password route parity for the auth recovery flow.",
      "comparison": null
    },
    {
      "id": "screens.auth.login",
      "title": "Auth Login Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "auth",
      "surface": "auth",
      "group": "screens-auth",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/components/auth/LoginPage.tsx",
          "fixture": "fixtures/screens.auth.login.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/AuthPages.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/AuthPages.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Missing true login route parity; old auth.login inputs are not active in the canonical manifest.",
      "comparison": null
    },
    {
      "id": "screens.home.activity",
      "title": "Home Activity Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/ActivityPage.tsx",
          "fixture": "fixtures/screens.home.activity.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Existing components.home.activity.results is a connected visual fixture; this tracks the real Home Activity route/screen capture.",
      "comparison": null
    },
    {
      "id": "screens.home.channel-list",
      "title": "Home Channel List Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/features/sidebar/Sidebar.tsx",
          "fixture": "fixtureData.json#homeChannelList",
          "components": [
            "Sidebar",
            "TabRootBar",
            "NotificationTrigger",
            "MobileTabBar"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/RaftHomeComponents.kt",
          "route": "compose_home_channel_list_visual",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeChannelList",
          "components": [
            "RaftStructuralHomeShell",
            "HomeTitlebar",
            "RaftSidebarItem"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "path": "",
          "fixture": ""
        }
      },
      "notes": "Whole phone Home root (top bar, Saved, Pinned / Joint Channels / Channels / Direct Messages, tab bar) from shared/fixtureData.json homeChannelList, light mode.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9601259898368824,
        "pixelPerfectSimilarity": 0.9331712780468271,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.home.channel-list.side-by-side.png"
      }
    },
    {
      "id": "screens.home.channel-list.dark",
      "title": "Home Channel List Screen (Dark)",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/features/sidebar/Sidebar.tsx",
          "fixture": "fixtureData.json#homeChannelList",
          "components": [
            "Sidebar",
            "TabRootBar",
            "NotificationTrigger",
            "MobileTabBar"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/build/raft/app/RaftHomeComponents.kt",
          "route": "compose_home_channel_list_visual",
          "fixture": "KmpVisualScreenshotCaptureTest.captureHomeChannelListDark",
          "components": [
            "RaftStructuralHomeShell",
            "HomeTitlebar",
            "RaftSidebarItem"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "kmp-shared",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "path": "",
          "fixture": ""
        }
      },
      "notes": "Whole phone Home root (top bar, Saved, Pinned / Joint Channels / Channels / Direct Messages, tab bar) from shared/fixtureData.json homeChannelList, dark mode.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9652862297868355,
        "pixelPerfectSimilarity": 0.9258509466781795,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.home.channel-list.dark.side-by-side.png"
      }
    },
    {
      "id": "screens.home.index",
      "title": "Home Index Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/features/sidebar/Sidebar.tsx",
          "fixture": "fixtures/screens.home.index.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Missing true Home channel/thread list screen parity with realistic server/channel data.",
      "comparison": null
    },
    {
      "id": "screens.home.loading",
      "title": "Home Loading Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/features/sidebar/rows/SidebarRowsSkeleton.tsx",
          "fixture": "fixtures/screens.home.loading.json",
          "components": [
            "MainLayout",
            "Sidebar",
            "SidebarRowsSkeleton",
            "MobileTabBar"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "route": "compose_home_loading_visual",
          "reason": "Android home loading state redesign (task #372) will capture compose_home_loading_visual against this React baseline.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Post-sign-in loading transition: MainLayout mounted at the server home with channels/DMs/agents/computers still fetching, so the mobile home surface shows Sidebar skeleton rows. Parity target for replacing the Android app's gray home loading state (task #372).",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9840084604344498,
        "pixelPerfectSimilarity": 0.9409625173843184,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.home.loading.side-by-side.png"
      }
    },
    {
      "id": "screens.home.saved",
      "title": "Home Saved Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/home",
      "surface": "home",
      "group": "screens-home",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/SavedPage.tsx",
          "fixture": "fixtures/screens.home.saved.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/HomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Existing components.home.saved.results is a connected visual fixture; this tracks the real Home Saved route/screen capture.",
      "comparison": null
    },
    {
      "id": "screens.members.agent-detail.activity",
      "title": "Members Agent Detail Activity Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#AgentRoute",
          "fixture": "fixtures/screens.members.agent-detail.activity.json",
          "components": [
            "MainLayout",
            "AgentRoute",
            "AgentDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailActivityScreen",
          "route": "compose_member_detail:memberId=agent-product-ux,memberKind=agent,initialAgentTab=activity",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersAgentActivityTab"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for the Activity tab with the shared Product UX agent fixture. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9190685279133092,
        "pixelPerfectSimilarity": 0.8560384702658358,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.activity.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.apps",
      "title": "Members Agent Detail Apps Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#AgentRoute",
          "fixture": "fixtures/screens.members.agent-detail.apps.json",
          "components": [
            "MainLayout",
            "AgentRoute",
            "AgentDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailAppsScreen",
          "route": "compose_member_detail:memberId=agent-product-ux,memberKind=agent,initialAgentTab=apps",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersAgentAppsTab"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for the Apps tab with the shared Product UX agent fixture. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9190443427107566,
        "pixelPerfectSimilarity": 0.8706758994430227,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.apps.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.profile",
      "title": "Members Agent Detail Profile Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#AgentRoute",
          "fixture": "fixtures/screens.members.agent-detail.profile.json",
          "components": [
            "MainLayout",
            "AgentRoute",
            "AgentDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailProfileScreen",
          "route": "compose_member_detail:memberId=agent-product-ux,memberKind=agent,initialAgentTab=profile",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersAgentProfileTab"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for the Profile tab with the shared Product UX agent fixture. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.8291884755829109,
        "pixelPerfectSimilarity": 0.7491528313724711,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.profile.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.reminders",
      "title": "Members Agent Detail Reminders Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#AgentRoute",
          "fixture": "fixtures/screens.members.agent-detail.reminders.json",
          "components": [
            "MainLayout",
            "AgentRoute",
            "AgentDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailRemindersScreen",
          "route": "compose_member_detail:memberId=agent-product-ux,memberKind=agent,initialAgentTab=reminders",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersAgentRemindersTab"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for the Reminders tab with the shared Product UX agent fixture. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9282836384220322,
        "pixelPerfectSimilarity": 0.868595153088824,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.reminders.side-by-side.png"
      }
    },
    {
      "id": "screens.members.agent-detail.workspace",
      "title": "Members Agent Detail Workspace Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#AgentRoute",
          "fixture": "fixtures/screens.members.agent-detail.workspace.json",
          "components": [
            "MainLayout",
            "AgentRoute",
            "AgentDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersAgentDetailWorkspaceScreen",
          "route": "compose_member_detail:memberId=agent-product-ux,memberKind=agent,initialAgentTab=workspace",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersAgentWorkspaceTab"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for the Workspace tab with the shared Product UX agent fixture. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9264570125589685,
        "pixelPerfectSimilarity": 0.8856120176120176,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.agent-detail.workspace.side-by-side.png"
      }
    },
    {
      "id": "screens.members.human.profile",
      "title": "Members Human Detail Profile Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "path": "packages/web/src/components/layout/MainLayout.tsx#HumanRoute",
          "fixture": "fixtures/screens.members.human.profile.json",
          "components": [
            "MainLayout",
            "HumanRoute",
            "HumanDetailPanel"
          ],
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureMembersHumanDetailProfileScreen",
          "route": "compose_member_detail:memberId=visual-user,memberKind=human",
          "components": [
            "MemberDetailPageContent",
            "MembersDetailContent",
            "MembersHumanDetailSections"
          ],
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real Members route capture for human member detail profile. React uses MainLayout at /s/visual/human/visual-user; Android uses compose_member_detail pageData with memberId=visual-user and memberKind=human. Harmony remains pending real-device/provider capture.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9668672881338882,
        "pixelPerfectSimilarity": 0.9393881208397338,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.members.human.profile.side-by-side.png"
      }
    },
    {
      "id": "screens.members.index",
      "title": "Members Index Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "members",
      "surface": "members",
      "group": "screens-members",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/MembersPage.tsx",
          "fixture": "fixtures/screens.members.index.json",
          "reason": "Needs React route/provider fixture that renders the real Members page with seeded server members, agents, invite/add states, and loading/empty/error coverage.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs real Android MEMBERS_PAGE route launch with seeded Members store data; the component visual page is not sufficient for a full-screen route case.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/MembersPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending: needs real Members route/provider capture with seeded humans, agents, invite/add states, and loading/empty/error coverage. Do not activate through the component-only agent detail visual page.",
      "comparison": null
    },
    {
      "id": "screens.settings.administration.full-height",
      "title": "Settings Administration Full Height Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "screens-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "ADMINISTRATION_VISUAL_SECTIONS"
          ],
          "path": "packages/web/src/features/settings/administration/administrationVisualSections.ts",
          "fixture": "fixtures/screens.settings.administration.full-height.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "components": [
            "SettingsAppShellContent"
          ],
          "source": "kmp-shared",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsPage.kt",
          "fixture": "",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "components": [
            "SettingsAppShellContent"
          ],
          "source": "kmp-shared-ohos",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsPage.kt",
          "reason": "OHOS visual capture requires a self-hosted runner with an HDC-attached device; blocked on infra approval.",
          "fixture": ""
        }
      },
      "notes": "Full-height (content-height, not viewport-clipped) Settings Administration route for react↔android parity (task #388). React renders all ADMINISTRATION_VISUAL_SECTIONS stacked, unclamped, captured fullPage (verified 1170x6648). Android eager full-height capture on main (#259, verified 1170x7382; density diff normalizes). First react↔android full-height pairing runs next full/nightly.",
      "comparison": null
    },
    {
      "id": "screens.settings.appearance",
      "title": "Settings Appearance Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "screens-settings",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/features/settings/appearance/AppearanceSection.tsx#AppearanceSection",
          "fixture": "fixtures/screens.settings.appearance.json",
          "reason": "Needs a real React Settings route capture for the Appearance screen; component-only SettingsPanel capture is tracked separately.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs real Android Settings route launch capture for Appearance, not the shared component visual page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending as a full Settings screen route. Component coverage lives in components.settings.appearance.page; this screen case needs real Settings route launch on both providers, not a component visual page.",
      "comparison": null
    },
    {
      "id": "screens.settings.billing-portal",
      "title": "Settings Billing External Portal Link",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "screens-settings",
      "status": "skipped",
      "providers": {
        "react": {
          "id": "react",
          "status": "external",
          "path": "packages/web/src/features/settings/billing/BillingTabContent.tsx#BillingTabContent",
          "fixture": "fixtures/screens.settings.billing-portal.json",
          "reason": "Billing portal opens an external website; validate Billing tab action availability instead of a portal screen.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "external",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Android opens https://raft.build/settings/billing in the system browser when available; no in-app route exists.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "external",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Portal management is external-web behavior; no Harmony in-app route is expected.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Skipped: billing portal is an external browser URL action (https://raft.build/settings/billing), not an in-app screen/WebView route. Validate the Billing page/action state instead.",
      "comparison": null
    },
    {
      "id": "screens.settings.server-danger-modal",
      "title": "Settings Server Danger Modal",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "screens-settings",
      "status": "baseline",
      "providers": {
        "react": {
          "id": "react",
          "status": "ready",
          "components": [
            "SettingsPanel",
            "DangerZoneSection",
            "Modal"
          ],
          "path": "packages/web/src/features/settings/server/DangerZoneSection.tsx#DangerZoneSection",
          "fixture": "fixtures/screens.settings.server-danger-modal.json",
          "reason": ""
        },
        "android": {
          "id": "android",
          "status": "ready",
          "source": "kmp-shared",
          "components": [
            "SettingsVisualServerFocusedContent",
            "SettingsServerDeleteConfirmationDialog",
            "Modal"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "fixture": "KmpVisualScreenshotCaptureTest.captureSettingsServerDangerModal",
          "route": "compose_settings_page_visual:server-danger-modal",
          "notes": "Fixture-only safe modal state; delete confirmation is empty and the Delete Server action button is disabled.",
          "reason": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsHomePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Real React SettingsPanel DangerZoneSection modal and KMP SettingsServerDeleteConfirmationDialog safe open state; destructive submit stays disabled and no delete action is invoked.",
      "comparison": {
        "baseline": "react",
        "current": "android",
        "status": "different",
        "rawStatus": "different",
        "acceptance": "",
        "rgbSimilarity": 0.9548812319785791,
        "pixelPerfectSimilarity": 0.7805059342974034,
        "sideBySideImage": "visual-testing-results/diff/react__android/screens.settings.server-danger-modal.side-by-side.png"
      }
    },
    {
      "id": "screens.settings.storage",
      "title": "Settings Storage Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/settings",
      "surface": "settings",
      "group": "screens-settings",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/components/settings/SettingsPanel.tsx",
          "fixture": "fixtures/screens.settings.storage.json",
          "reason": "Needs real React Settings Storage route/provider and seeded storage rows.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsStoragePage.kt",
          "reason": "Needs real Android Settings Storage route capture with seeded storage rows, not the generic settings visual page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SettingsStoragePage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending: needs React Settings Storage route/provider plus Android Settings Storage route capture with seeded storage rows; do not activate through the generic settings visual page.",
      "comparison": null
    },
    {
      "id": "screens.tasks.index",
      "title": "Tasks Index Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/tasks",
      "surface": "tasks",
      "group": "screens-tasks",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/components/task/TasksPanel.tsx",
          "fixture": "fixtures/screens.tasks.index.json",
          "reason": "Needs React visual case/fixture under the new capture taxonomy.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedTasksPage.kt",
          "reason": "Needs real Android page/route launch capture, not a visual fixture page.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/SharedTasksPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Missing true Tasks screen parity: list, filters, task status, and create-task entry need real route captures.",
      "comparison": null
    },
    {
      "id": "screens.thread.channel",
      "title": "Thread Channel Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "screens-thread",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/ThreadPage.tsx",
          "fixture": "fixtures/screens.thread.channel.json",
          "reason": "Needs React ThreadPage route provider with seeded channel conversation data, not a standalone component fixture.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs real Android ThreadPage route launch capture with seeded repository/store state, not pageData-only visual fixture.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending: needs a real ThreadPage channel route capture with seeded channel messages, attachments, replies, task metadata, and composer state on both React and Android. Do not activate with standalone pageData-only fixtures.",
      "comparison": null
    },
    {
      "id": "screens.thread.dm",
      "title": "Thread DM Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "screens-thread",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/ThreadPage.tsx",
          "fixture": "fixtures/screens.thread.dm.json",
          "reason": "Needs React ThreadPage DM route provider with seeded direct-message conversation data, not a standalone component fixture.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs real Android ThreadPage DM route launch capture with seeded peer/member state, not pageData-only visual fixture.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending: needs a real ThreadPage DM route capture with seeded peer identity/status, member-scoped mentions, replies, attachments, and composer state on both React and Android. Do not activate with standalone pageData-only fixtures.",
      "comparison": null
    },
    {
      "id": "screens.thread.task-thread",
      "title": "Task Thread Screen",
      "captureType": "real-screen",
      "captureGroup": "Screens Parity",
      "category": "app/thread",
      "surface": "thread",
      "group": "screens-thread",
      "status": "pending",
      "providers": {
        "react": {
          "id": "react",
          "status": "pending",
          "path": "packages/web/src/pages/ThreadPage.tsx",
          "fixture": "fixtures/screens.thread.task-thread.json",
          "reason": "Needs React ThreadPage task-thread route provider with seeded task-bound conversation data.",
          "components": []
        },
        "android": {
          "id": "android",
          "status": "pending",
          "source": "android-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs real Android task-thread route launch plus seeded task data/status controls; pageData-only fixture would not prove the product path.",
          "components": [],
          "fixture": ""
        },
        "harmony": {
          "id": "harmony",
          "status": "pending",
          "source": "ohos-real-screen",
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/ThreadPage.kt",
          "reason": "Needs Harmony provider capture after React and Android contracts are defined.",
          "components": [],
          "fixture": ""
        }
      },
      "notes": "Pending: needs a real task-thread route plus seeded task data/status controls before capture. Keep pending until React and Android can launch the same task-bound thread without fake pageData.",
      "comparison": null
    },
    {
      "id": "components.tokens.palette",
      "title": "Shared Tokens",
      "captureType": "component-fixture",
      "captureGroup": "Components Parity",
      "category": "tokens",
      "surface": "tokens",
      "group": "tokens",
      "status": "skipped",
      "providers": {
        "react": {
          "status": "active",
          "components": [
            "TokenPaletteCase",
            "sharedTokens.json"
          ],
          "path": "packages/web/src/pages/PaletteAuditPage.tsx",
          "fixture": "packages/visual-testing/shared/sharedTokens.json",
          "reason": ""
        },
        "android": {
          "status": "active",
          "components": [
            "TokenPaletteVisualPage",
            "SlockColor",
            "SlockShadow",
            "SlockType"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose/TokenPaletteVisualPage.kt",
          "fixture": "Slock design token constants",
          "source": "kmp-shared-android",
          "route": "compose_tokens_palette_visual",
          "sharedComponents": [
            "SlockColor",
            "SlockSpacing",
            "SlockType"
          ],
          "sharedPath": "compose/shared/src/commonMain/kotlin/ai/slock/compose",
          "reason": ""
        },
        "harmony": {
          "status": "pending",
          "components": [
            "SlockColor",
            "SlockSpacing",
            "SlockType"
          ],
          "path": "compose/shared/src/commonMain/kotlin/ai/slock/compose",
          "reason": "Needs OHOS connected capture/Hypium provider.",
          "source": "kmp-shared-ohos",
          "fixture": ""
        }
      },
      "notes": "Temporarily hidden: palette screenshot is not a valid parity gate; use token-audit for machine validation.",
      "comparison": null
    }
  ]
}
