Phase 6: bump livetemplate v0.8.16 → v0.10.0; migrate literate examples to Subscribe/Publish#279
Conversation
… examples to Subscribe/Publish
Phase 6 of the broadcast-redesign wave (livetemplate#415) — tinkerdown side.
go.mod long jump v0.8.16 → v0.10.0. Crosses the v0.9.0 Sync() auto-dispatch
removal (livetemplate#406) AND the v0.10.0 BroadcastAction removal — the 2
literate examples never used Sync(), so the BroadcastAction migration is
the only behavioral change needed.
Both literate examples migrated:
- examples/literate-counter-include/_app/counter.go: Mount adds
ctx.Subscribe(ctx.SelfTopic()); Increment/Decrement/Reset swap 3
BroadcastAction call sites to ctx.Publish(ctx.SelfTopic(), ...).
- examples/literate-counter-include/_app/main.go: sharedAuth doc comment
rewritten.
- examples/literate-counter-include/index.md: handlers section reframed
around the two-step Subscribe + Publish shape.
- examples/literate-linked-include/_app/{counter,main}.go +
examples/literate-linked-include/index.md: same migration as above.
Go test suite green (all 13 internal packages + root e2e tests after
pre-building the tinkerdown binary — 3 root-package E2E tests fork-exec
it, an existing test infra detail unrelated to this change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b2ea1c3 to
0b7cb9a
Compare
Code Review — Phase 6: Subscribe/Publish migrationOverviewClean, focused migration PR. The mechanical swap from Issues / Suggestions1. PR description says baseline is The PR body describes this as a "long-jump: 2. Silently discarding _ = ctx.Subscribe(ctx.SelfTopic())The comment justifies this (
3. Minor comment inconsistency between the two examples
If the examples are meant to be independently copy-pasteable, they should be in sync. If 4. Awkward line-break in // `ctx.Publish(ctx.SelfTopic(), ...)` from any tab/embed reaches every
// other subscribed peer. Real apps use a per-user authenticator; here a constant groupID isThe phrase "here a constant groupID is" at the end of that line reads oddly because the sentence continues on the next line. This was likely pre-existing prose that got partially updated. Not blocking, but easy to tighten: // `ctx.Publish(ctx.SelfTopic(), ...)` from any tab/embed reaches every other
// subscribed peer. Real apps use a per-user authenticator; the constant groupID here isWhat looks good
VerdictApprove with minor suggestions. Items 1 and 3 are worth a follow-up; item 2 is a judgment call for example code. Nothing here blocks the merge. |
Four items from the review: 1. counter.go (both literate examples): propagate ctx.Subscribe errors rather than `_ = ctx.Subscribe(...)`. The bot's concern is right — example code gets copied into production apps; safe-by-default propagation is the better teaching pattern. SelfTopic() is ACL-exempt today, but a controller that copies this pattern with a developer topic name MUST propagate to trigger the keep-open lvt:error envelope. 2. counter.go (both literate examples): sync the Mount-comment text between counter-include and linked-include. The "Dispatched actions reach this Mount too…" paragraph that was only in counter-include is reframed and applied uniformly. 3. main.go (both literate examples): tighten the awkward "here a constant groupID is" line that read oddly at the wrap point. Rewrites to "the constant groupID here is". PR-description-only item (not pushed): the bot also noted the diff baseline appears as v0.9.0 → v0.10.0, not v0.8.16 → v0.10.0. That's because the rebase onto origin/main pulled in tinkerdown#278's v0.9.0 bump; my PR adds the v0.9.0 → v0.10.0 step. Updated the PR body to clarify. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code ReviewOverviewClean migration of both literate examples from the removed Bug: Stale line-number references in both
|
Two items from the round-2 review: 1. counter.go (both literate examples): propagate ctx.Publish errors alongside the ctx.Subscribe propagation. Same reasoning: example code gets copied; safe-by-default propagation is the right teaching pattern. Consistent with examples#102 (merged upstream). 2. index.md (both literate examples): update stale `lines="13-35"` include directives that referred to pre-Mount-addition line numbers. After Mount + Publish-propagation, the file ends at line 59; the `lines="13-59"` range covers the full "handlers" section (Mount + Increment + Decrement + Reset) the surrounding prose describes. literate-counter-include also updates `highlight="20"` → `highlight="38"` to land on the first ctx.Publish call (matching the narrative emphasis on "each handler also calls ctx.Publish"). Bot's "Mount body simplification" (single-return form) declined: the multi-line if-err shape mirrors the canonical pattern readers will use when they copy this code into a controller subscribing a developer topic (where the error MUST propagate to trigger lvt:error). For a literate-tutorial example that emphasizes safe-by-default copy-paste, the verbose form is the teaching point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — Phase 6: bump livetemplate v0.9.0 → v0.10.0; migrate Subscribe/PublishOverviewThis PR is a clean, focused migration of the two literate examples ( What's Good
Issues / Suggestions1.
|
Two items from the round-3 review applied; two declined with rationale below. 1. counter.go (both literate examples): replace internal "livetemplate#415" reference in the Mount comment with the public "livetemplate v0.10.0" identifier. Issue numbers are repo-internal and external readers landing on the example can't browse them; the release version is the public coordinate. 2. counter.go (both literate examples): add a comment block on the Increment doc explaining the state-mutation-before-Publish ordering and the transient-divergence consequence when Publish errors. The mutate-first ordering is the recursion-guard pattern's requirement; making that contract explicit so readers copying the pattern understand the trade-off. 3. index.md (both literate examples): update lines= directives to reflect the new file length (67 lines after the doc-comment additions). `lines="13-67"` covers the full handlers section. literate-counter-include's highlight bumps to line 46 (the first ctx.Publish call after the expanded Mount comment). Declined (with rationale via this PR description): - go.sum identical /go.mod hashes across v0.9.0 and v0.10.0: verified with `go mod verify` (all modules verified). The hash matches because the livetemplate go.mod file itself didn't change between those releases (no new deps added/removed; only the BroadcastAction API surface and other source-level changes shipped). Hash equivalence is legitimate. - Mount-path explicit test: deferred. The existing 13-package suite is green; a peer-sync e2e for the literate example would require Docker Chrome setup tinkerdown's test infra doesn't currently use. Worth tracking but out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — Phase 6: bump livetemplate v0.8.16 → v0.10.0; migrate literate examples to Subscribe/PublishOverviewThis PR migrates both literate examples ( Code Quality & StyleStrengths:
Minor concern — comment verbosity:
Consider splitting the failure-mode discussion into a separate Potential Bugs / IssuesMutate-before-publish ordering (acknowledged, but worth flagging explicitly): s.Count++
if err := ctx.Publish(ctx.SelfTopic(), "Increment", nil); err != nil {
return s, err // local state already mutated; peers not notified
}The PR comment correctly documents that this is a known tradeoff with the recursion guard. No action required, but since this is tutorial code that readers will copy, it's worth ensuring
func (c *CounterController) Mount(s Counter, ctx *livetemplate.Context) (Counter, error) {
if err := ctx.Subscribe(ctx.SelfTopic()); err != nil {
return s, err
}
return s, nil
}This is correct behavior (Mount here is purely a side-effect, no state change). No bug, but since this is the first time readers see a go.sum observationThe Test Coverage
SecurityNo concerns. SummaryThe migration is correct, well-reasoned, and the error-propagation choices are sound. Main suggestions:
Otherwise, LGTM. |
|
Round-4 review reads as no-functional-issue + 3 cosmetic refinements. Per CLAUDE.md bot review convergence — two consecutive rounds of cosmetic-only feedback is the convergence signal — stopping pushes and addressing via reply. "Comment verbosity in Increment": This is a literate-tutorial example; the failure-mode tradeoff is precisely the kind of decision a reader copying the pattern into production needs to see at the code site, not buried in the surrounding prose. Splitting it into the "index.md should also call out the mutate-first tradeoff": Reasonable suggestion in principle but the failure-mode discussion is genuinely a code-site concern (about how to write similar handlers), not a tutorial-narrative concern (about what the pattern does). The prose already explains the recursion-guard / no-infinite-loop story which is the what; the comment explains the trade-off you accept by adopting this pattern. Keeping that split intentional. " The PR is in good shape; CI is green; the round-3 verifications (go.sum hash legitimacy + new line-include directives + Publish error propagation) all landed correctly. Calling convergence. |
…shm-usage + bump per-file deadline 15s → 60s) Surfaced while running the broadcast-redesign Phase-6 wave through livetemplate/docs#27's build workflow: the same docs content that validates cleanly in ~17s on a devbox failed in 5 successive CI runs with two error modes: ✗ index.md: chrome failed to start: Failed to connect to the bus ✗ recipes/architecture-flow.md: context deadline exceeded ✗ recipes/how-this-site-works.md: context deadline exceeded Each rerun flagged a *different* set of files (sometimes index.md alone; sometimes 3 unrelated files including ones the docs PR didn't touch), with the failures clustering on cold-runner runs. Local repro: 17s total for 55 files / 5 mermaid diagrams against the same content. The bug was in two layers: 1. **Missing `--disable-dev-shm-usage`.** Ubuntu CI runners default `/dev/shm` to 64MB (Docker/Actions cgroup default); Chrome's renderer process attempts to allocate shared memory there, OOMs, and the recovery path manifests as the `"Failed to connect to the bus"` D-Bus negotiation failure rather than a clean OOM error. Switching to `/tmp` via `--disable-dev-shm-usage` eliminates both the OOM and the misleading D-Bus message. Plus `--disable-extensions` + `--no-first-run` for faster cold-start. 2. **15s per-file deadline too tight on slow runners.** `validateMermaidDiagrams` creates a fresh `chromedp.NewExecAllocator` per file. Chrome cold-start on Ubuntu CI is routinely 5-10s; the diagram loop then adds Navigate + Sleep(2s) + Evaluate (~3s per diagram). On devbox: 15s holds. On CI with a slow cold start: only 5-7s budget remains — explaining the per-file randomness in which files fail. Bumped to 60s with a per-file rationale comment. Verified locally: same `time /tmp/tk-bin validate content/` against the docs site = 17s (no slowdown from the new flags), all 55 files pass. The CI side should follow once `docs#27` re-runs against a tinkerdown@main that includes this commit. This unblocks livetemplate/docs#27 (which has been red on `build` since its open) without changing any docs content — the docs content was always valid; the validator was over-tight for slow CI hardware. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — Phase 6: livetemplate v0.10.0 / Subscribe+Publish migrationOverviewThis PR completes the BroadcastAction → Subscribe/Publish migration for the two literate examples and bumps the dependency to v0.10.0. It also hardensthe Mermaid validation CI path with better Chrome flags and a more realistic timeout. The scope is focused and the PR description is thorough.
|
| Area | Verdict |
|---|---|
| API migration correctness | ✅ Looks correct |
| Error propagation | ✅ Good |
| Subscribe idempotency assumption | |
| Mutate-first error semantics | |
| Chrome timeout | |
| Comment length | ℹ️ Minor — acceptable for a literate example |
The two counter.go are the most worth addressing before merge, since they affect what readers will copy from the examples. The Chrome timeout is a pragmatic CI fix and the risk is low.
🤖 Generated with Claude Code
… (no divergence)
Verified against livetemplate v0.10.0's dispatch path: the framework
ONLY assigns the action handler's returned newState to the connection's
persisted state on a (state, nil) return. On (state, err) — including
Publish errors — newState is discarded and connSt.state stays at the
pre-action value.
Code path (livetemplate mount.go, dispatch site):
newState, actionErr := DispatchWithState(...)
if actionErr != nil {
// handle field/multi-errors; connSt.state UNCHANGED
} else {
connSt.state = newState // only on nil error
}
So the previous comment's "the local state mutation has already
committed but peer connections receive nothing... transient divergence"
was wrong on the ground state. The actual semantic is:
- Action returns (s, err) from Publish failure
- Framework discards s; this connection stays at pre-action state
- Peers never got the failed Publish either (Publish errored before
the dispatch loop)
- Both sides at pre-action state ⇒ no divergence by construction
Comment rewritten to reflect this. index.md `lines=` directives also
adjusted (13-67 → 13-66 / highlight 46 → 45) to track the 1-line
shrink in the comment block.
Closes the round-4 bot reviewer's "mutate-first error semantics —
needs one-liner clarification" item with the actual contract instead
of a hedge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Three items from the round-4 review addressed: Subscribe idempotency (verified, no code change). Confirmed in livetemplate v0.10.0's Mutate-first error semantics (verified, comment corrected). Verified livetemplate v0.10.0's dispatch path:
Previous comment incorrectly claimed "transient divergence." Corrected in 74f51ad to reflect the actual contract. Chrome timeout 15s → 60s (declining the "tighter bound + retry" suggestion). The 4× jump is deliberate. A "tight bound + retry" approach (e.g. 30s × 2 retries) would double the worst-case latency on a genuine Chrome hang (60s vs. 30s+30s=60s; same ceiling, more complexity). The current 60s is a single deadline that catches both slow cold-starts (the actual common-case failure) and pathological hangs (extremely rare; would also be caught by the outer CI test-job timeout if Chrome truly zombied). The flag-hardening ( The other round-4 nits (comment length, identical-files cross-example) are stylistic and declined — comment depth is intentional for a literate tutorial; cross-example identity is the right outcome for two intentionally-parallel examples (a future divergence would be a deliberate teaching choice, not duplication-creep). |
Code Review — Phase 6: Subscribe/Publish migration (updated review)
What looks good
Issues / Suggestions1. Timeout increase (15s → 60s) could mask real Chrome hangs The 4× bump is explained and reasonable for the described CI environment. One thing to consider: the timeout wraps the entire file — cold Chrome start plus all 2. -```go include="./_app/counter.go" lines="13-35" highlight="20"
+```go include="./_app/counter.go" lines="13-66" highlight="45"Line 45 of the new 3. Comment in // Mutate-first ordering is safe even when Publish errors: the
// livetemplate dispatcher only assigns the returned newState to the
// connection's persisted state when the action returns (state, nil).This is accurate for v0.10.0. If 4. Minor prose nit in // the constant groupID here is what makes the literate-linked tutorial
// demonstrate cross-region state sharing."literate-linked tutorial" appears in both SummaryThe migration is mechanically correct, error handling is properly added, and the Chrome CI fixes are sound. The suggestions above are non-blocking. LGTM with those minor points in mind. |
Phase 6 of the broadcast-redesign wave (livetemplate#415) — tinkerdown side. Coordinated with livetemplate#430.
Scope (post-rebase)
The Phase-6 worktree branched off pre-
v0.9.0SHA751297a, butorigin/mainhas since advanced via tinkerdown#278's pin bump tov0.9.0. After rebase onto current main, this PR's net change is:go.modlong-jump:livetemplate v0.9.0→v0.10.0(chore: bump livetemplate to v0.9.0 #278 already covered the v0.8.16 → v0.9.0 step). Crosses the v0.10.0 BroadcastAction removal. The earlier v0.9.0 release crossed the Sync() auto-dispatch removal (livetemplate#406) — the 2 literate examples never used Sync(), so only the BroadcastAction migration is needed in practice.ctx.Subscribe(ctx.SelfTopic())(with error-propagation per Claude review feedback — safe-by-default for readers who copy the pattern with a developer topic), swaps 3BroadcastActioncall sites toctx.Publish(ctx.SelfTopic(), ...), and gets matching index.md + main.go comment rewrites.All 13 internal packages green; 3 root-package E2E tests pass once the
tinkerdownbinary is pre-built (make build) — pre-existing test-infra detail, not introduced here.🤖 Generated with Claude Code