P05 · Experience · Rendered from source

decision ledger

Living UI component layer

213 lines18,704 bytessha256 888654334b08
{
  "part": "P05",
  "run_id": "2026-08-27-sprint-1-fable",
  "observed": "2026-08-27",
  "boundaries": "research-only; no cloning, no execution of candidate source, no build/deploy, no admission claims, no authenticated vendor access",
  "note": "Decisions are research conclusions with named falsifiers, not admissions or authorizations. The rights question (D-04) is a recorded counsel gate this lane does not discharge.",
  "decisions": [
    {
      "id": "D-01",
      "question": "What is the canonical component taxonomy? (parts.json P05 open question 1)",
      "options_considered": [
        "Adopt the existing 75-tag flat vocabulary as the canonical taxonomy",
        "Design a curated hierarchy (e.g. Ant Design's General/Layout/Navigation/Data Entry/Data Display/Feedback)",
        "Canonical component entities with alias sets, plus the 75 tags retained as a retrieval index",
        "Both: hierarchy for browsing, flat tags for search, with no canonical entity layer"
      ],
      "chosen": "Canonical component entities with alias sets, plus the 75 tags retained as a retrieval index (option 3)",
      "evidence": [
        "S-006: the 75-tag vocabulary averages 2.09 tags/component with 52.6% single-tagged — it already behaves as a single-label category scheme, not the facet grid it is described as.",
        "S-023: Component Gallery's 60 canonical components with explicit alias lists is the only verified production system solving cross-source naming, and its data actively falsifies flat naming — Banner is an alias of both Alert and Hero, Stepper is both an alias of Progress indicator and its own canonical, Label is an alias of Badge and standalone.",
        "S-005: tag semantics are already tier-dependent (sidebar = 47 page-tier vs 143 all-tier), so the vocabulary cannot be presented as a single flat truth.",
        "S-033: Design Systems Repo ships zero taxonomy at ~55 items — evidence flat browsing does not survive to 7,949."
      ],
      "confidence": "medium-high",
      "falsifier": "Two independent raters assigning a 200-component sample to a ~60-name canonical set achieve under 80% agreement, which would mean the canonical layer is not stable enough to be authoritative and faceted tags should win.",
      "status": "resolved_pending_falsifier_test"
    },
    {
      "id": "D-02",
      "question": "How are near-duplicates clustered visually and structurally? (open question 2)",
      "options_considered": [
        "Keep the name-collision proxy (2,058 surplus) as the working dedupe signal",
        "Perceptual hashing (pHash/dHash/wHash) only",
        "CLIP/semantic embeddings only",
        "Two-stage: cheap exact/pixel pass, then perceptual hash, then CLIP for semantic near-duplicates",
        "Structural clustering over bundle DOM/CSS instead of images"
      ],
      "chosen": "Two-stage measure-first: run perceptual hash AND CLIP over the 7,678 previews to establish the real rate before committing to a clustering strategy (option 4, gated on measurement)",
      "evidence": [
        "S-008: NEW — exact byte-identical previews are only 102 surplus of 7,678 (1.3%), while the name-collision proxy is 2,058 (25.9%). The truth lies between and the gap is far too wide to design against.",
        "S-034: the visual near-duplicate rate is explicitly UNMEASURED; the prior audit proposed perceptual hashing and never ran it.",
        "R-002 idealo/imagededup (Apache-2.0) bundles pHash/dHash/wHash/CNN plus an evaluation harness, so the measurement is cheap.",
        "R-006 openai/CLIP: cross-framework ports (shadcn-vue 10,498 stars, shadcn-svelte 9,061) guarantee semantically identical, pixel-different components that hashing alone will miss."
      ],
      "confidence": "high_on_method_low_on_outcome",
      "falsifier": "If pHash at a standard threshold collapses the 2,058 name-collision surplus into under ~200 clusters, cheap hashing is sufficient and the CLIP stage is unnecessary spend.",
      "status": "method_resolved_quantity_unresolved"
    },
    {
      "id": "D-03",
      "question": "Which base UI structures must Actionist OWN rather than harvest? (open question 3)",
      "options_considered": [
        "Own nothing; harvest and re-theme everything",
        "Own everything; use the corpus only as visual reference",
        "Own the archetype shell plus the ~20 app primitives; harvest decorative and marketing surfaces",
        "Decide per-archetype with no general rule"
      ],
      "chosen": "Own the archetype shell and the ~20 AutoSaaS app primitives; harvest decorative/marketing surfaces (option 3)",
      "evidence": [
        "S-007: supply is NOT the constraint — every one of the 20 AutoSaaS-required primitives has ground-truth page-tier supply, from 32 (onboarding) to 164 (spinner). The argument for ownership therefore cannot be scarcity.",
        "S-016: harvested bundles carry NO component source (demo.tsx median 346 B is an import wrapper to a path absent from the corpus), hence no props, no states, no accessibility contract — exactly the metadata AutoSaaS requires per component (S-018).",
        "S-018: AutoSaaS's contract demands name, use case, props, states, accessibility notes, responsive behavior, source path, examples and tests. A compiled bundle can satisfy none of these.",
        "S-020: archetype demand concentrates — case_workflow is primary for 6 of 17 industries and portal secondary for 6 of 17 — so a small owned set covers most demand.",
        "Universal-necessity argument: navigation encodes route topology and permission state from P10; forms encode validation and submission contracts; tables encode sorting/pagination/selection behaviour; feedback must be coherent app-wide. These are contract surfaces, not visual choices."
      ],
      "confidence": "high",
      "falsifier": "If five sampled harvested components (a table, a form, a nav, an empty state, a modal) can each be wired to the P09 data plane and pass accessibility checks in under a day apiece, then owning them is wasted effort and harvest-plus-adapt wins.",
      "status": "resolved"
    },
    {
      "id": "D-04",
      "question": "How are new 21st.dev releases discovered and re-indexed, given the terms-of-service finding? (open question 4)",
      "options_considered": [
        "Continue the existing scrape-and-harvest mechanism",
        "Poll licensed registry.json endpoints from vendors who publish them by design",
        "Use the vendor MCP/API under a paid key within its metering",
        "Design the mechanism now, gate execution on counsel, and build against a source-agnostic index"
      ],
      "chosen": "Design the mechanism and build a source-agnostic index; execute refresh ONLY against licensed registry.json endpoints and/or a paid metered API. The existing scrape path stays designed-but-gated (option 4 + 2)",
      "evidence": [
        "S-017: 21st.dev terms (fetched 2026-07-20) prohibit scraping/bots, prohibit using marketplace content to train AI/ML without consent, and prohibit republishing structured metadata such as titles, descriptions and tags. Both local stores were built by that prohibited mechanism.",
        "S-017: rights coverage is 2 license fields across 11,549 third-party records (0.017%), and SISO harvest rule 6 makes both stores summarize-only where licence is unclear.",
        "S-010: NEW — the mechanism itself is already trivially available: urls-union holds 8,225 discovered URLs vs 7,949 harvested, so the diff (276) IS the refresh queue. The blocker is legal, not technical.",
        "S-009: NEW — no meta.json carries a timestamp and all mtimes fall in one 67-minute window on 2026-08-18, so today the corpus is a dated snapshot with uncomputable staleness. A timestamp field is a precondition for any 'living' claim.",
        "S-027: nine vendors serve machine-readable registry.json BY DESIGN (ReactBits 672, Tailark 469, Aceternity 278, Magic UI 247, and others) — a licensed refresh path that needs no scraping.",
        "S-026: registry.directory demonstrates cross-registry freshness telemetry in production."
      ],
      "confidence": "high_on_mechanism_blocked_on_rights",
      "falsifier": "If counsel clears the existing harvest mechanism, the licensed-endpoint constraint is unnecessary and the cheaper scrape path should be restored. Conversely, if the per-vendor registry licences also prohibit redistribution, the licensed path is illusory too and refresh must rest entirely on first-party components.",
      "status": "mechanism_resolved_execution_gated"
    },
    {
      "id": "D-05",
      "question": "What metadata helps a client visually choose without understanding component names? (open question 5)",
      "options_considered": [
        "Name and category search only",
        "Visual grid with style/colour/density facets and no names required",
        "Guided pick-1-of-10 per category under the client's own token pack",
        "Natural-language job description matched against use_cases"
      ],
      "chosen": "Guided pick-1-of-10 per category, presented as a visual grid pre-rendered in the client's token pack, with style/density facets and a plain-language job-description fallback (options 2+3+4 combined)",
      "evidence": [
        "S-012: the legacy store already carries the needed axes populated — visual_style across 24 values (minimalist 1739, flat 1315, gradient 768), complexity (composite 2201, atomic 1261), and 16,241 use_case strings across 3,507 components (mean 4.63).",
        "S-031: Awwwards ships hex colour-swatch and font-name filtering — the best verified precedent for choosing without vocabulary.",
        "S-030: UIverse (4,456 elements) proves strong visual browse with style tag facets works for non-technical users.",
        "S-025: Mobbin's Text-in-Screenshots dimension lets users retrieve by words printed on a screen, needing no taxonomy at all.",
        "S-007: supply per category (32-164 ground truth) means the task is choosing 10, not finding any — curation, not search, is the binding constraint.",
        "S-016: previews are a sufficient primary visual surface (median 11,410 B vs 337,515 B median bundle)."
      ],
      "confidence": "medium-high",
      "falsifier": "If usability testing shows clients select faster and report higher satisfaction from a named-category list than from a visual grid, the visual-first premise is wrong and the picker should lead with names.",
      "status": "resolved_pending_usability_test"
    },
    {
      "id": "D-06",
      "question": "Should the industry axis be part of the component layer, given the 17-industry denominator?",
      "options_considered": [
        "Build the J-4 36->17 crosswalk and expose industry as a client-facing facet",
        "Build the crosswalk but use industry only as a weak ranking prior",
        "Drop industry from the component layer entirely and let P12 composition carry industry variance",
        "Re-classify the corpus directly against the 17 industries"
      ],
      "chosen": "Drop industry as a component-layer facet; carry industry variance at the archetype/composition layer instead. Optionally re-classify a sample to test recoverability (option 3, with option 4 as the falsifier test)",
      "evidence": [
        "S-013: NEW — best_for_industries does not discriminate. Mean 3.93 industries per component; 171 components claim all 10 of the dominant set; 89.6% (3,143/3,507) claim saas; the top exact industry-set repeats wholesale (180 components share creator-tools+e-commerce+media+saas).",
        "S-014: NEW — only 2 of the 17 target industries (real_estate, saas) have a direct legacy analogue, and 5 of 17 (construction, logistics_freight, insurance_agencies, mortgage_brokers, property_management) have ZERO reachable supply under a best-effort crosswalk. Reachable counts are wildly unbalanced: saas 3,153 vs law_firms 2 vs recruiting_staffing 1.",
        "S-020: the b2b shelf lane already places industry variance at the archetype level (variant_delta per industry), where entities, states and compliance regimes differ — not at the button level.",
        "This CONTRADICTS the inherited framing of J-4 as a '36->17 crosswalk' merge task: the axis is not merely mis-bucketed, it is near-uniform and materially absent for 5 industries."
      ],
      "confidence": "high",
      "falsifier": "Re-classifying a 200-component sample directly against the 17 industries yields a balanced, discriminating distribution — which would mean the axis is recoverable by reclassification rather than genuinely absent.",
      "status": "resolved_contradicts_inherited_plan"
    },
    {
      "id": "D-07",
      "question": "What must a canonical joined index record carry? (lane task 3)",
      "options_considered": [
        "Join on raw canonical URL string equality as proposed in J-1",
        "Join on normalised author+slug with the URL retained as a display field",
        "Treat the two stores as separate indexes and never join"
      ],
      "chosen": "Join on case-insensitive normalised author+slug; retain both raw URLs; carry artifact availability, tag tier, theme eligibility and rights per record (option 2)",
      "evidence": [
        "S-003: NEW — raw URL equality does NOT work. The stores encode the same identity differently: meta.json.url is https://21st.dev/@author/components/slug while _provenance.fetchedFrom is https://21st.dev/r/Author/slug. Normalised joining yields intersection 2,945, union 8,483 (vs the inherited 2,942/8,515); 28 legacy records have no parseable provenance URL.",
        "S-002: source availability is the highest-value field — only 3,506 records are source-bearing, so the 7,949 headline overstates usable supply by roughly 2.3x.",
        "S-011: NEW — the legacy registry-item.json yields a real 237-package dependency surface, closing the prior audit's UNMEASURED item 5 (which saw only 18 packages from import wrappers).",
        "S-005: tag tier must be a field, since page/api/local materially changes reported supply.",
        "S-016: theme eligibility must be a per-component fact, not a corpus-wide assumption.",
        "S-017: rights_status must default to gated/reference_only per harvest rule 6."
      ],
      "confidence": "high",
      "falsifier": "Upstream author renames break the normalised key and silently split identities; if observed rename frequency is material, the record must carry a stable upstream numeric id instead.",
      "status": "resolved"
    },
    {
      "id": "D-08",
      "question": "What is the agent-facing query interface for the component layer?",
      "options_considered": [
        "Bespoke REST API designed for Actionist",
        "MCP server only",
        "Adopt the shadcn Dynamic Search registry contract, optionally fronted by MCP",
        "Static llms.txt plus a downloadable index"
      ],
      "chosen": "Adopt the shadcn Dynamic Search registry contract as the wire format, optionally fronted by an MCP server (option 3)",
      "evidence": [
        "S-022: VERIFIED BY THIS LANE from the shadcn repo — the contract already specifies GET /r/registry.json?q&type&limit&offset with a `pagination` object as the opt-in server-side-search signal, explicitly designed for 'large registries with thousands of items' and backwards compatible with static registries.",
        "S-028: shadcn's MCP is registry-agnostic and works against any shadcn-compatible registry, so adopting the format makes Actionist queryable by existing agents with zero bespoke client work.",
        "S-027: nine registries already serve this format, making it the de-facto interchange standard.",
        "S-025: Mobbin's MCP demonstrates the image-context cost constraint (3-8 images per call recommended, 20-screen default cap), arguing for structured metadata over images by default."
      ],
      "confidence": "high",
      "falsifier": "If the contract cannot express P05's required facets (theme eligibility, visual similarity, archetype role) even as ignorable extensions, a bespoke API is required after all.",
      "status": "resolved"
    },
    {
      "id": "D-09",
      "question": "Is this corpus a 'living' layer today?",
      "options_considered": [
        "Yes — it is a maintained corpus",
        "No — it is a dated snapshot",
        "Partially — living for metadata, snapshot for artifacts"
      ],
      "chosen": "No. It is a dated snapshot and cannot currently support a living claim",
      "evidence": [
        "S-009: NEW — no meta.json across 7,949 directories carries a harvest timestamp, and directory mtimes across a 400-dir sample span a single 67-minute window (2026-08-18T20:03Z to 21:10Z). Staleness is uncomputable from the data.",
        "S-010: NEW — a 276-component discovery backlog already exists and has never been closed.",
        "S-004: classification.json's `generated` field literally reads 'run classify.mjs to refresh' rather than a date, and its stats block is stale.",
        "This is a precondition finding: P05's own thesis says 'continuously refreshed', and no refresh telemetry exists to support it."
      ],
      "confidence": "high",
      "falsifier": "A per-component updated_at is discoverable from the upstream API at query time, making a local timestamp unnecessary.",
      "status": "resolved"
    },
    {
      "id": "D-10",
      "question": "Should we build fresh or extend the existing board.html picker?",
      "options_considered": [
        "Extend board.html",
        "Build fresh and reuse serve.mjs/find.mjs/CSS",
        "Buy or adopt an existing gallery product"
      ],
      "chosen": "Build fresh; reuse serve.mjs unchanged, find.mjs's ranker, and board.html's CSS and interaction model",
      "evidence": [
        "S-016: board.html is a static snapshot of 832 of 7,949 components (10.5%), 885,238 B pre-rendered, pointing at a different preview store (previews/, 832 files) than the corpus (harvest/*/preview.webp, 7,678 files), and carrying a hand-written 16-chip taxonomy rather than the 75-tag vocabulary.",
        "S-016: serve.mjs already serves harvest/ correctly and implements the POST /api/picks manifest contract end to end; find.mjs already implements a defensible ranker and a --json agent surface.",
        "This confirms rather than revises the prior audit's BUILD FRESH verdict."
      ],
      "confidence": "high",
      "falsifier": "The CSS proves entangled with the pre-rendered markup and cannot be lifted cleanly, making a from-scratch stylesheet cheaper.",
      "status": "resolved_confirms_prior"
    }
  ]
}