# Issue 24375 — evidence capture, 2026-10-24

Head `9e2b63d478d62a41ee818620b9e17a9531b76a46`, base `27e0c684c5`. Evidence-only pass. No implementation/test commits, pushes, PR creation, direct DB writes, server launches or shutdowns. Both primary-owned dev servers remain running. Temporary controlled skill removed from both worktrees; existing lead-created demo retained. No full private skill inventory or credentials included.

## Result and coverage

Base registry ACP has empty skills with managed OpenCode 2.1.4. Head discovers the installed global ghdrop and ancestor project demo, exposes them in the existing client UI, opens the correct instructions, and actually loads skills through ACP. The controlled long skill returned the exact marker `ISSUE_24375_CONTROLLED_LOADED`. This establishes selected-fixture runtime behavior, not comprehensive scanner correctness.

| Matrix row | Executed evidence | Remaining gap / exact reason |
|---|---|---|
| V1 web | Matched base/head `$issue-24375` filter at 1440/390 × light/dark; keyboard selection, dismiss/reopen recorded. Global and long-name narrow stills. Correct name, description, Provider Skill badge. | Genuine zero-supported-roots case not configured: installed global skills intentionally retained. Base empty ACP and head no-match are distinct states. No full four-state gallery for global/long variants; narrow dark is the clipping stress case. |
| V2 web | Selected chip/details in all four W states; narrow View instructions actual ancestor SKILL.md; plain mode draft insertion assertion; interaction recording. | No matched base chip/details or V2 diff: base ACP inventory is empty and cannot reach this state through the real picker. Did not fabricate a base inventory. Keyboard removal verified separately: three Backspaces removed trailing space/skill node, final skillNodes0. |
| V3 web | Matched base/head slash-on four W states; slash-off excludes skill while `$` still works, recorded. | Slash Enter selection and subsequent keyboard removal verified in after-slash-select-remove.mp4. |
| V4 web | Real stopped ACP turns on each build, user context and assistant paragraph/list, matched four W states. | Timeline skill chips are static references in the current web component; no actionable instruction navigation exists there. |
| Desktop | Shared web renderer covered above. | Desktop shell smoke unverified: no attached Electron client, no DISPLAY/WAYLAND display; browser desktopBridge undefined. Existing servers were not replaced. |
| Native mobile V1–V4 | None; responsive web is explicitly not native coverage. | Device tool returned `Agent device access is turned off for this environment.` No authorized app-owned device was available. |
| F1 | Exact managed binary native selected fixtures, base empty snapshot, head per-cwd snapshot, real ACP load. | No regression test executions in this evidence-only assignment. Head server discovery-related environment allowlist matches native shell; full environment parity beyond those variables was not asserted. |
| F2 | Ancestor project + global roots and `/tmp` cwd isolation. | Other roots and complete same-binary inventory semantics not tested; scanner fixture suite is primary-agent scope. |
| F3 | No duplicate demo in picker; advertised init/review/compact preserved in snapshot. | Controlled machine-advertised/workspace/home precedence fixture not run. |
| F4 | Recorded current HOME and shell unset flags. | HOME/USERPROFILE/XDG overrides and Claude-disable flag matrix not run; no environment reconfiguration within capture scope. |
| F5 | Valid demo and controlled long YAML metadata loaded and invoked. | Invalid/absent frontmatter, CRLF, multiline, folder/name mismatch, flat md, oversized inputs not tested. |
| F6 | Installed symlinked global ghdrop resolves and is visible. | Unreadable/cycle/depth6/7/cap/hidden/node_modules/>1MB cases not tested. |
| F7 | Base empty registry snapshot still has usable composer and commands; head unchanged advertised commands. | Other registry/custom/disabled provider guards and genuine empty filesystem roots not tested. |
| F8 | Add → fresh refresh → select/send/load exact marker → remove → fresh refresh; `/tmp` excludes project skill and retains global. | Explicit provider/environment switching not tested; cwd isolation was tested through real refresh RPC, not a new project UI walkthrough. |

Config-declared and plugin-provided skills are outside the scanner's stated supported discovery roots; exhaustive native parity is not claimed.

## Capture environment and comparison method

Actual T3 web client, own preview tabs, same local environment and project cwd on final V1/V3 pairs. Chrome 154 Linux x86_64, DPR 2, browser zoom/visualViewport scale 1. CSS viewports 1440×900 and 390×900. The screenshot exporter produces 1280×800 for desktop and 780×1800 for narrow. Body font `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif`; body16px, app UI16/prompt14/code13. Fonts ready before capture. Light/dark use the actual app default theme palette preview, not a new renderer. Animations/transitions and caret hidden with CSS, panel duration0. Composer collapse-on-scroll disabled for stable layouts. Rich editor and slash skills restored enabled. Details/sidebar state matched per pair; V1 desktop has details pane visible on both, V3/V4 hidden.

Each main W matrix still has a second capture at least500ms later: all28 pairs are byte/pixel identical. `comparison-results.json` contains actual counts and bounding boxes for12 before/after comparisons. No masks. Zero diff is absolute RGB difference amplified4. Separate threshold is maximum absolute channel >16/255, magenta. All12 comparisons have **zero changed pixels outside the declared skill-content ROI at both tolerances**. ROI coordinates are exported-image pixels. `compare.py` reproduces results.

Every final still, contact sheet, zero/threshold diff, and recording was visually inspected before upload. Recordings inspected as timestamped extracted frames (10-second cadence) plus snapshots during interaction; no audio narration. Written narration below is the recording guide. Intermediate UI states in continuous recordings are not used as matched comparison claims.

## Every visible final base/head difference

- V1: base no-match text is replaced by the Issue24375Demo row, its description and Provider Skill badge; menu row/background/height changes with content. Narrow long fixture truncates with ellipsis and keeps badge readable. These are expected existing-renderer consequences of supplied data.
- V3: base No matching command is replaced by `/skill:Issue 24375 Demo`, description and Provider Skill badge; menu content and size change accordingly.
- V4: literal `$issue-24375-demo` in the base user message becomes a skill context chip after; matching assistant paragraph/list references become chips. Chip padding/icon/background alters line widths and narrow wrapping/vertical placement. Identical requested response, title, displayed time, provider, branch, footer, and panel state outside skill-content region; diff confirms no outside changes.
- V2 after only: chip description popover and instruction panel are newly reachable because the skill is supplied. Base has no corresponding reachable inventory state, hence no fabricated comparison.
- Runtime metadata includes expected task-specific tool IDs and timing; not a visual before/after comparison. No performance or animation regression claim is made.

## Recording narration (times approximate, seconds from clip start)

| Recording | Timestamp / action / observed result |
|---|---|
| after-V1-V3-runtime.mp4 (129s) | 0–35 picker/filter and viewport/theme passes; ~40 keyboard select;45–55 details and View instructions;65–80 slash on;85 submit harmless demo-load prompt;90–110 provider work;120 expanded tool metadata and required reply. |
| before-V1-V3-runtime.mp4 (128s) | Earlier baseline reproduction with detached baseline project: `$` and slash no-match, then real V4 prompt. Has Select ref versus head branch; **superseded for matched comparison by final stills and before-matched-cwd.mp4**. Retained as provenance, not pixel-proof. |
| after-slash-off-V4.mp4 (~244s) | 0 slash off no-match; first10s `$` still works and selection;10–30 V4 prompt and real stopped answer. Later viewport/layout setup; a transient reconnect notice around140s resolves. Final stills supersede intermediate layout states. |
| after-global-long-refresh-marker.mp4 (~181s) | 0–5 global/filter/long fixture;6 select and send;10 working;~20 exact controlled marker;40–95 matched-cwd picker/slash recaptures;100 reload;110–180 V4 normalization. |
| after-slash-select-remove.mp4 (11s) | 0 slash picker;1 Enter creates composer skill node;9–10 Backspaces remove it; final textbox empty and skillNodes0. Reviewed at1-second cadence. |
| before-matched-cwd.mp4 (~128s) | 0–25 baseline picker on the same head project cwd, still empty;30–40 slash negative;50–120 V4 layout normalization. Final stills are the settled matched endpoints. |

## Reproduction and artifacts

`seed-fixture.sh` is the exact reversible long-skill fixture setup/removal script. `capture-driver.js` records the browser evaluation setup and real refresh RPC helper; actions were performed through app-owned preview tools, not standalone automation. `runtime-transcript.md` records exact prompts, selected paths, provider/version, native command, fresh snapshots and limitations. Public JSON files filter to ghdrop/demo only. Screenshot filenames identify row/build/client/width/theme; `-settle` is the second stable frame. `contact-*` files are review aids; original PNGs are authoritative. See `uploads.md` for individual public links and `bundle-upload.md` for the archive.

## FRICTION

| Category | Happened | Avoid |
|---|---|---|
| Browser pairing | Initial pairing navigation needed the actual Continue flow; existing copied thread briefly showed a reconnect state. Resolved within own tabs; earlier clarification no longer needed. | Inspect actual snapshot and action result, preserve own tab IDs, never publish pairing URLs. |
| Async UI | Settings/resize updates were not always reflected in the immediately following evaluation. | Separate action and inspection boundaries; compare final settled frames. |
| Native API | Wrong endpoint returned HTML; deepObject query needed `location[directory]`, first initialized location returned empty. | Use exact CLI API help and completed initialized response; never substitute older cached binary. |
| State matching | Detached baseline branch and auto-open details/collapse states initially polluted comparisons. | Point both final drafts at the same project cwd, normalize panel state, recapture; final outside-ROI count0. |
| Client availability | Native device access disabled; no desktop shell attached. | Report surface gaps explicitly; do not equate narrow web with native. |
| Evidence scope | V2 base cannot reach chip with empty ACP inventory. | Preserve real repro and label after-only evidence instead of seeding fake baseline provider data. |

No new implementation defects identified in the captured paths. Overall proof remains partial for scanner edge semantics and unavailable clients listed above.

Browser diagnostics included LegendList recycleItems warnings and React flushSync lifecycle errors during interactions. No causal regression comparison was performed for these messages; they are not represented as new failures of this change.

Supplemental global/long/instructions/runtime stills have one saved PNG each, with settled state observed during recordings; they do not have separate500ms paired PNG checks. The28 automated stability checks apply only to the main W gallery. This is a supplemental stability-proof gap.

Isolated state changed through real UI/RPC: created controlled stopped ACP threads (demo invocation, V4 pair, long marker), a temporary baseline project record, several drafts; selected managed OpenCode V2 and model; client theme previews, sidebar/details state, slash/rich settings and collapse-on-scroll setting. No user live data was changed. Native API oracle may retain its own initialized local cache. Both task server discovery environment allowlists were inspected read-only: HOME=/home/ubuntu, USERPROFILE/XDG_CONFIG_HOME/OPENCODE_DISABLE_CLAUDE_CODE/OPENCODE_DISABLE_CLAUDE_CODE_SKILLS unset; base process cwd /tmp/issue-24375-before/apps/server, head cwd /home/ubuntu/wt/issue-24375/apps/server; final provider refresh input cwd was the head project on both.
