M5: save & share the captured skill (capture completeness + gallery + writable-WASM CLI parity) - #310
Conversation
Expands the outline-only M5 into full design + 3 phase blocks (convention 9), informed by a direct read (Explore 2026-07-24) that found the plan's five folded features heterogeneous and mostly mis-framed vs reality: - Persistence is greenfield but the durable unit already exists — "persist to the repo" IS git-committed skill dirs (M1 Phase 6); playground.go's RAM store is a paste sandbox, irrelevant. The gallery is a read-only listing, not a new persistence subsystem. - Capture omits the house style (styling/generation/style_guide) — the M4 Phase 2 feed-forward. - #216 writable WASM is already implemented in code; honest disposition is verify + fix the one real residual (cli.go createCLISource omits wasm/exec) + docs, not "build it." - The embed framing is wrong twice over — today's embed-lvt is inline (not iframe) AND unsandboxed; #249's real ask (any-framework, sandboxed) is a different, security-sensitive, design-only mechanism. Operator decision (in-grain spine over the bigger capstone): save = real (capture-completeness + a dogfooded gallery over the committed skill dirs + #216 CLI parity); share = git portability (commit -> pull -> re-run). No hosted service, no share-link subsystem, no collaborative review. Deferred as named follow-ups (remain open issues, not pretend-in-scope): #282 review-mode, #249 sandboxed embed, #223 hosted service, #222 custom Go sources. M5 touches no client code, so the artifact-provenance checklist no longer applies. Roadmap / gap-table / provenance-table / issue-walkthrough updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018M9pJSPmG6i1D8s6rpEV4h
/tinkerdown:save now captures the manifest whole — the styling block (theme + design tokens) and any generation.style_guide → style-guide.md — not trimmed to sources + actions, so a saved skill re-runs on-brand instead of on bare defaults. Closes the M4 Phase 2 feed-forward. The honest scope (advisor's point-vs-bundle asymmetry): a committed *point* capture already loads its whole tinkerdown.yaml on serve, so house style already travels there — the round-trip passes with zero runtime change. The gap this prose edit closes is the *bundle* case: a hand-assembled throwaway skill dir that trims the yaml or drops the separate style-guide.md file. - skills/tinkerdown-save/SKILL.md: step 2 captures the house style under the same bundle-or-point rule as seed.sql/app.md; bundle list gains style-guide.md; verify checklist item 4 confirms the style travels. - captured_skills_test.go: add the style-guide.md ref (the one genuinely-new regression guard — catches the style file failing to travel) + fold a real-state config.LoadFromDir round-trip (styling.tokens + generation.style_guide) anchored on the captured skill dir, no dangling clone of TestPIIManifestHouseStyleConsumable. TestSaveSkillCapturesHouseStyle pins the instruction itself (the only guard on the prose deliverable). - CHANGELOG.md + plan: Audit decisions (chose option a; deferred a machine-readable skill→manifest pointer to Phase 2), ticked boxes, Learn. Verified: 16 non-E2E packages green; new tests green; phase-relevant E2E green (TestPIIAccessApproval, TestPIIAccessApprovalShadowing, TestStylingTokensComputedStyle). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018M9pJSPmG6i1D8s6rpEV4h
A discoverable home for captured workflows — itself a plain Tinkerdown app: an
lvt-source over a read-only CSV, no custom JavaScript.
STOP gate (Audit): no source type reads a directory of SKILL.md frontmatter
(verified — no ReadDir/glob in internal/source, no frontmatter parsing), so a
*dynamic* directory index is not buildable in-grain without exec (friction on a
listing page). tinkerdown build is an app->binary compiler, not an index
generator, so the "build-generated index" fallback would be new machinery. This
was a material divergence (the plan imagined a dynamic index; what is in-grain is
a hand-maintained static list, and only one captured skill exists), so it went to
the operator, who chose the minimal static-index fallback.
The gallery renders a committed examples/gallery/skills.csv, kept in step with the
captured skills on disk by TestSavedSkillsGalleryInSync (skills/ dirs minus the
explicit framework set {tinkerdown, tinkerdown-save}). The framework set is
explicit, not a detection heuristic — a new capture fails the test until listed:
a forced decision, never silent drift.
- examples/gallery/{index.md, tinkerdown.yaml, skills.csv, README.md}: the app.
Named index.md (canonical / route) — an app.md serves at / only by
discovery-order luck, which surfaced here as the gallery's README winning /
instead and rendering the wrong page (caught by the four-channel rendered-HTML
capture, not "0 blocks / 0 WS frames").
- saved_skills_gallery_test.go: the sync-guard + a manifest-loads structural check.
- saved_skills_gallery_e2e_test.go: four-channel chromedp E2E (browser console,
server log, WS frames, rendered HTML, screenshot).
- CHANGELOG.md + plan: the STOP-gate decision, ticked boxes, Learn.
Verified: gofmt/vet clean; ci-tagged build compiles; gallery E2E + unit tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M9pJSPmG6i1D8s6rpEV4h
`tinkerdown cli` now constructs `wasm` sources — createCLISource previously fell through to "unsupported source type for CLI". A WASM source now has the same CRUD surface on the CLI as under serve: list works for any module; add/update/delete work when the module exports `write`; a read-only module refuses a write with a clear error. `exec` stays out of CLI CRUD (read-oriented, --allow-exec-gated), now documented with a comment rather than a silent default fallthrough. Verify, don't infer — the plan's premise was wrong: - examples/lvt-source-wasm-test/ is READ-ONLY (quotes.wasm exports fetch, no write → IsReadonly()==true). It is the only .wasm in the repo, and there is no TinyGo here to build a write-exporting fixture. So a *successful write* is not empirically verifiable in this environment, in WS or CLI. The honest #216 disposition (parity closed; read + readonly-guard verified; write-landing needs a reproducible TinyGo fixture) is presented at PR signoff — #216 is NOT closed here. Verified running: `tinkerdown cli … list quotes` returns rows; `add` is refused ("source is read-only"); TestCLIWasmSourceReadParityAndReadonlyGuard covers construct + Fetch + IsReadonly + the WriteItem guard error. Also fixed a load-breaking doc/template bug found in the Audit: the wasm docs and the wasm-source template used a non-existent `module:` key (real: `path:`) and `config:` (real: `options:`), so following them failed to load. Added a Read-only vs. writable section documenting the write-export contract + CLI parity. Footgun noted: the test file is cli_wasmsource_test.go, not cli_wasm_test.go — `wasm` is a GOARCH, so a `*_wasm_test.go` file is an implicit build constraint and compiles only for GOARCH=wasm (silently excluded on amd64). - cmd/tinkerdown/commands/cli.go: wasm case + exec-omission comment. - cmd/tinkerdown/commands/cli_wasmsource_test.go: read-parity + readonly-guard test. - docs/sources/wasm.md, templates/wasm-source/test-app/tinkerdown.yaml: config-key fixes + writability docs. - CHANGELOG.md + plan: Audit findings, drift, Learn, the #216 follow-up. Verified: gofmt/vet clean; ci build compiles; commands tests green; CLI read + readonly-refuse confirmed via the real binary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018M9pJSPmG6i1D8s6rpEV4h
Review: PR 310 -- M5 save and share the captured skillVerified against the actual code (not just the PR description) -- the claims all check out. Spot checks performed
Code quality
Honesty of scope (a strength, not a gap) The PR is unusually candid about what it does not verify: issue 216 (writable WASM) is deliberately left open because there's no TinyGo available to build a write-exporting fixture, and the PR doesn't fake coverage by mocking one. The only committed .wasm (quotes.wasm) is read-only, and the new TestCLIWasmSourceReadParityAndReadonlyGuard only asserts what's actually exercisable (read path plus readonly guard rejection). This is the right call -- a committed mystery writable binary would reintroduce the artifact-provenance risk called out from issues 295/297. Minor observations (non-blocking)
Test coverage Strong -- unit (TestCapturedSkillsWellFormed extended, TestSaveSkillCapturesHouseStyle, TestSavedSkillsGalleryInSync, TestSavedSkillsGalleryManifestLoads, TestCLIWasmSourceReadParityAndReadonlyGuard) and E2E (TestSavedSkillsGalleryRenders, four-channel capture) are all real-state assertions rather than prose scraping, with one explicit, well-justified exception (the SKILL.md instruction-pin, which the PR itself flags as unable to prove future-LLM compliance). Overall: solid, well-scoped PR with honest treatment of what remains unverified. No blocking issues found. |
|
Thanks for the thorough, code-verified review. On the two non-blocking observations:
No code changes to #310 from these; both are addressed (kept-with-rationale / tracked-as-follow-up). |
M5 — Save & share the captured skill (spine-only)
Makes the ephemeral-UI model's "keep it, the need recurs" path real and honest: a saved skill re-runs on-brand and self-contained, saved workflows have a discoverable home, and writable WASM has CLI parity. Scope was settled at kickoff as tinkerdown-only, spine-only — save is real; share = git portability (commit the skill dir, a teammate pulls and re-runs). No hosted service, share-link subsystem, or collaborative review — those stay deferred (#282, #249, #223, #222).
Phase 1 — Capture completeness: the house style travels
/tinkerdown:savenow captures the manifest whole — thestylingblock (theme + design tokens) and anygeneration.style_guide→style-guide.md— not trimmed to sources + actions. So a saved workflow re-runs on-brand instead of on bare defaults, closing the M4 Phase 2 feed-forward.Honest framing: a committed point-capture already loads its whole
tinkerdown.yamlon serve, so house style already travels there — the real gap this closes is the bundle case (a hand-assembled throwaway skill dir that trims the yaml or drops the separate style file). The deliverable is a prose instruction whose future-LLM effect is untestable; the guards target what's real — thestyle-guide.mdref travels, and the instruction survives.Phase 2 — Saved-skills gallery (
examples/gallery/)A discoverable home for captured workflows — itself a plain Tinkerdown app (
lvt-sourceover a read-only CSV, no custom JS).STOP-gate decision (Audit): no source type reads a directory of
SKILL.mdfrontmatter (noReadDir/glob ininternal/source, no frontmatter parsing), so a dynamic directory index isn't buildable in-grain withoutexec(friction on a listing page).tinkerdown buildis an app→binary compiler, not an index generator. This was a material divergence from the plan's imagined dynamic index, so it was put to the operator, who chose the minimal static-index fallback: the gallery renders a committedskills.csv, kept in sync with the captured skills on disk byTestSavedSkillsGalleryInSync(skills/dirs minus the explicit framework set{tinkerdown, tinkerdown-save}— a new capture fails the test until listed; no silent drift).Note: a skill's Location renders as displayed text (
skills/pii-access-approval), a discoverable pointer — not a clickable link.Phase 3 — Writable-WASM CLI parity + honest #216 disposition
tinkerdown clinow constructswasmsources (previously "unsupported source type for CLI"), so a WASM source has the same CRUD surface on the CLI as underserve:listfor any module,add/update/deletewhen the module exportswrite, and a read-only module refuses a write with a clear error.execstays out of CLI CRUD (read-oriented,--allow-exec-gated), now documented in the switch rather than a silent fallthrough. Also fixed a load-breaking wasm doc/template bug:module:→path:andconfig:→options:(the docs used keys that don't exist), plus a Read-only vs. writable doc section.#216 stays OPEN (not closed by this PR). "Verify, don't infer" found the plan's premise wrong: the only committed
.wasm(quotes.wasm) is read-only, there's no TinyGo here to build a write-exporting fixture, and a committed mystery binary would be the #295/#297 provenance trap. So a successful write has zero verified coverage in any transport (WS or CLI). What is verified running: the wasm read path via CLI, and the readonly guard (WriteItemrefuses;cli addreturns "source is read-only"). A follow-up issue will track the reproducible TinyGo fixture + write-landing test.Verification
ok, full root E2E (117 tests) + the new four-channel gallery E2Eok … 870s,EXIT=0(GOWORK=off go test ./...)./simplifyrun on each phase's diff.Plan
docs/plans/2026-07-09-ephemeral-ui-reframe.md§ M5 — every phase's Audit/Implementation/Acceptance/Learn is filled in, including the STOP-gate and the write-verification blocker, per the drift-in-same-commit convention.🤖 Generated with Claude Code
https://claude.ai/code/session_018M9pJSPmG6i1D8s6rpEV4h