Skip to content

Phase 2 step 4: the read-only Blocks catalog and provenance UI - #107

Merged
ryandmonk merged 4 commits into
mainfrom
claude/blocks-catalog-provenance
Oct 16, 2026
Merged

ryandmonk merged 4 commits into
mainfrom
claude/blocks-catalog-provenance

Conversation

@ryandmonk

@ryandmonk ryandmonk commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Phase 2 step 4 (docs/blocks/PHASE-2-RELEASE-PLAN.md, §7 H3 and §8), as the owner authorized it on 2026-10-16: the read-only Blocks catalog and provenance UI. It is experimental-build only and read-only, and it executes no actions. Not for automatic merge.

What it adds

Catalog (H3). The Catalog view gets an "Interface blocks" section after the components. It lists every block this build bundles, by reference:

  • version and status, with any reason the block was disabled at load;
  • intents, declared actions and streaming policy;
  • the sidecar status, the binding, and why the beta withholds a reference.

It contains nothing that can be pressed. Its lead says a block's declared actions are host-owned: Composer never executes them, and a host binds and authorizes each one on its own server.

Preview.

  • Label. A surface with provenance gets one line. "Made with block X vN" appears only by exact expansion identity: the record is trusted against a bundled sidecar that binds to this build's base contract, its call re-expands to the digest it recorded, and the surface is that expansion.
  • Status. The line then gives the §8 status:
    • refinable: "its record verifies against this build's approved block";
    • unavailable: imported or repository project;
    • inert: with the specific reason.
  • No label. Anything less reads "Block provenance inert: (the record names X vN)" with no label, and the surface previews as an ordinary one.
  • Host-owned note. A labelled surface also says its actions are host-owned and not executed here.

Refinable states what the record is. It does not say a build will use the block: the beta switch, Settings and the project's eligibility decide that, and the Build view states it.

The verdict is provenanceVerdict in packages/composer-core/src/blocks/provenance.ts. It is pure, and its fields are {block, version, madeWith, status, reason?}; it carries no action, declaration or grant. The reasons are:

  • trust: reference, base, block, version, validation;
  • identity: expansion, edited;
  • offer: not-offered;
  • stored, a record already reduced to inert.

A record must name a block id and a positive version. Anything else is dropped, so no imported text is shown as a block's name.

Lifecycle (§8).

  • Export/import, duplicate and delete behave as before, now pinned end to end.
  • An import, and a save in a project or an Example session, drop stale records.
  • Duplicating an Example session copies its provenance after every pending write, pruned against the examples it keeps.
  • Preview re-reads after every provenance write.
  • Default builds still ignore and drop provenance, and a default-build guard pins that.

HOST-ACTIONS.md. The first three clarifications from #106's accepted nit list:

  1. §2: which ids H1 accepts, including the $each enum loop variable and numbers only inside $concat.
  2. §3: which surfaces[i] the recipe hashes, that the string's UTF-8 encoding is the bytes, and refuse-to-render on a mismatch.
  3. Rule 4: accept events only under server-issued, unretired surfaceIds, and refuse a repeat with other parameters.

The illustrative recipe-test assertions (the fourth nit) need no change.

Boundaries kept

  • Read-only UI only: no action execution, no host handlers, no receipts or operator telemetry.
  • No shadcn or Stage 4 work, no deploy or Cloudflare change, no paid model calls.
  • Accessibility: no new regressions (below).
  • No production request-byte change, and no default-export change. Default builds never write blockProvenance.

Commits

Commit What
d72ca00 Fail-first tests: verdict, catalog, lifecycle
ac52215 The verdict, the read-only views, the lifecycle, the docs
7a4f8b6 Fail-first tests for the independent review's seven findings (no blocker; three minor, four nits)
6e7141d The seven fixes

Evidence

Fail-first.

  • d72ca00 against the code before it:
    • composer-core provenance: 22 failed of 31;
    • runtime: 6 failed of 55;
    • e2e: 6 failed of 7. The "edited" e2e already passed, because stored projects pruned on save before this PR; the verdict's edited unit test pins it.
  • 7a4f8b6 against ac52215:
    • composer-core: 3 failed of 34;
    • runtime: 5 failed of 60;
    • e2e: the trusted wording and the untrusted-name import fail. The Example-session duplicate test passed on the old code by timing; the copyProvenance unit test pins it.

Suites.

  • CI test passes at 6e7141d (run 38957149819).
  • Full local run at 6e7141d, CI's steps in order, green except the known local agent timeout:
    • check-sync passes and typecheck is clean;
    • unit: 12 of 13 workspaces pass (composer-core 297/297 blocks, composer 431/431). The agent workspace is 49/50, failing only rediscover > restoredConflict (5761 ms against its 5000 ms limit locally; CI passes it);
    • smoke 38/38, agent e2e 49/49, experimental 32/32;
    • exhibit: 106 passed and 4 skipped (the manual, env-gated recordings and benchmark).
    • The same run at ac52215 gave the same results.

Accessibility.

  • In the e2e: axe over each new region, WCAG 2 A/AA.
  • An extra probe (not committed) on all three themes ran wcag2a/aa, wcag21a/aa and wcag22a/aa, plus best-practice on the catalog:
    • 0 violations, and no focusable element added;
    • headings h2 then h3;
    • each table is named by its heading, with column and row headers.

Default build.

  • check-default-artifact: 29 files clean of 11 markers, step 4's included. The experimental positive control finds each step-4 marker in its artifact.
  • Byte-identical to a local build of main's tree (23 of 29 files): every app chunk (page, layout, main-app), every lazy chunk, the CSS and the fonts.
  • The build manifests are identical under their per-build directory names.
  • HTML/TXT differ only in per-build ids and the runtime's filename.
  • The webpack runtime differs from main's only by a consistent renaming of three single-letter minifier locals (f→d, d→s, s→f; 3545 bytes both).
  • Why the runtime differs:
    • Built unminified, main's runtime and this PR's differ in one string: the pre-minification content hash webpack embeds for lazy chunk 314.
    • Step 4 adds code to that chunk which minification then removes as dead, so the hash changes. The minifier sees that hash string while naming locals, so three names change.
    • Webpack then rewrites the hash to the minified chunk's real hash, b63e085f0a44ab96, which is the same on both sides.
    • A comment-only edit on main leaves the runtime's content unchanged.

Independent review.

  • ac52215: no blocker; three minor findings and four nits, all fixed in 6e7141d.
  • Re-review of the exact head 6e7141d: no blocker. All seven are resolved and nothing new is broken. It also confirms:
    • default-build isolation and the experimental-guard ratchet;
    • no new fetch, button, link or onClick, and handleAction unchanged;
    • nothing grants or executes an action.
  • Three non-blocking nits, left for the owner rather than widening this PR:
    • A. Pre-existing session-store timing. A session write still in flight when another Example opens can land in the new session. It is not reachable through the UI, and labels stay truthful.
    • B. Self-copy deadlock. copyProvenance would deadlock if a store were copied onto itself. Unreachable: the duplicate copies "session" into a fresh project id.
    • C. Overwrite instead of merge. copyProvenance overwrites the target instead of merging. That makes no difference for a project created moments earlier.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R6Ax8qvfN3gYUC85oSGnoi

…e lifecycle, fail-first

Step 4 of the Phase 2 release plan (sections 7 H3 and 8). Tests only:

- composer-core: provenanceVerdict earns "made with block X vN" only by
  exact expansion identity (a trusted record whose call re-expands to the
  digest it recorded, on a surface that is that expansion); everything else
  is inert with its specific reason (reference, base, block, version,
  validation, expansion, edited, not-offered, stored) and no label; imported
  and repository projects are unavailable; a verdict carries no action or
  grant, and a labelled surface's altered messages still bind nothing.
  parseProvenance keeps the reason it reduced a record for.
- runtime: blocksCatalog lists every bundled block with reference, version,
  status, intents, declared actions and streaming policy; provenanceVerdicts
  per surface; import keeps reasons.
- e2e (experimental): the read-only Catalog section; Preview's provenance
  line and host-owned note, with no request on any action; edited, duplicated,
  deleted, imported, forged and stale provenance; an Example session's save
  pruning its provenance (it does not today: the duplicate carries 1 stale
  record).
- e2e (default build): an imported beta file's provenance is never stored
  and no block UI appears (a guard; passes today).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R6Ax8qvfN3gYUC85oSGnoi
…d the lifecycle

Step 4 of the Phase 2 release plan (sections 7 H3 and 8), read-only and
experimental-only.

- composer-core: provenanceVerdict. A surface is "made with block X vN"
  only by exact expansion identity: a record trusted against a bundled
  sidecar that binds to this build's base, whose call re-expands to the
  digest it recorded, on a surface that is that expansion. Refinable on a
  reference project whose build offers the block; unavailable on imported
  and repository projects; otherwise inert with its specific reason and,
  unless only the offer is missing, no label. parseProvenance keeps the
  reason it reduced a record for; PROVENANCE_REASON_TEXT words each one.
- runtime: blocksCatalog (every bundled block: reference, version,
  status, intents, declared actions, streaming policy) and
  provenanceVerdicts per surface. Data only.
- Catalog view: a read-only Interface blocks section. Preview: the
  provenance line with its status and reason, and for a block surface the
  note that its actions are host-owned and not executed here. Nothing new
  can be pressed, and Preview still only records what a surface dispatches.
- Lifecycle: an Example session's save now prunes its in-memory
  provenance, so "duplicate to keep" carries nothing stale.
- The guarded views wrap today's JSX rather than adding a child, so a
  default build's application chunks are byte-identical to main's.
- HOST-ACTIONS.md: step 3's first three accepted clarifications (the id
  form and enum loop variables; the recipe's string, surface and refusal;
  surfaceIds the server issued and a repeat with other parameters).
- Release plan status line and a BLOCKS.md note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R6Ax8qvfN3gYUC85oSGnoi
…-first

The independent review of ac52215 found no blocker, three minor issues and
four nits. These tests pin each one and fail against ac52215:

- Preview's refinable line says what the record is, never that a build
  offers the block (the beta switch, Settings and eligibility decide that).
- A record that names no block id or version has no verdict and is dropped
  on parse, so an imported name is never shown.
- Duplicating an Example session copies its provenance after every pending
  write, pruned against the examples the duplicate keeps; the e2e loses its
  sleep.
- An import prunes against the imported project's examples.
- A reference project whose reference this build does not bundle trusts no
  provenance.
- Both default-artifact scans look for step 4's UI and runtime call, and the
  experimental positive control finds each marker in its artifact.
- Every provenance write notifies listeners, so an open Preview re-reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R6Ax8qvfN3gYUC85oSGnoi
- Preview's refinable line now reads "Refinable: its record verifies
  against this build's approved block." It states what the record is; the
  beta switch, Settings and eligibility decide whether a build uses the
  block, as the Build view says. BLOCKS.md says so.
- A record must name a block id (kebab-case, at most 64 characters) and a
  safe positive version. Anything else has no verdict and is dropped on
  parse, so no imported text is ever shown as a block's name.
- Duplicating an Example session copies its provenance on the session
  store's queue, after every pending write, pruned against the examples
  the duplicate keeps (copyProvenance).
- An import prunes against the imported project's examples, as a save
  would.
- A reference project whose reference this build does not bundle trusts
  no provenance.
- Every provenance write notifies listeners (onProvenanceChange), and
  Preview's note re-reads on each, keeping only the latest read.

The default build's app chunks are byte-identical to main's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R6Ax8qvfN3gYUC85oSGnoi
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
dspack-studio-composer 6e7141d Oct 16 2026, 07:44 PM

@ryandmonk
ryandmonk merged commit cb50f84 into main Oct 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants