feat(blog): shadow ingestion-surface UI — tiered reader + blended /stream + pulse tiers#217
feat(blog): shadow ingestion-surface UI — tiered reader + blended /stream + pulse tiers#217Jesssullivan wants to merge 12 commits into
Conversation
Surface the editorial taxonomy tier (added to the Post type in #212) in the reader UI. Tiers are reader-weight / IA only, never an auth, privacy, or ActivityPub-delivery signal; an absent tier means "not classified yet". - TierBadge.svelte: accessible, aria-labelled badge — stronger accent for noteworthy, muted outline for less-noteworthy, renders nothing when the tier is undefined. - BlogCard: badge in the metadata row; noteworthy cards get a gentle ring accent (featured styling still wins); no layout shift when tier is absent. - BlogArticle header: badge threaded through the static and broker paths. - /blog index: additive reader-weight ordering (date-desc preserved globally, noteworthy floats up only within an equal date via a stable sort); optional view-only tier filter chips that never drop posts. - Tag two posts to demonstrate both tiers (RE writeup -> noteworthy, recipe -> less-noteworthy), per the taxonomy migration guidance. No RSS/JSON feed shape change; no broker Worker path change.
📝 Blog Post ReviewFound 2 blog post(s) in this PR:
Schema Validation✅ All posts pass schema validation. Hyperlink Suggestions
Slash Commands
|
Build the next increment of the "blog as live-streamed ingestion surface" vision on the SHADOW UI only (tss.tinyland.dev). Everything additive and reversible; prod apex stays FROZEN. - /stream route (prerender, noindex+nofollow while held): one blended, reverse-chronological, tier-weighted timeline merging published long-form posts (src/lib/posts) with public pulse items (src/lib/pulse/load). Pure, deterministic blendStream(): date desc primary; noteworthy-first within an equal date (mirrors the #217 comparator); stable id tie-break so ordering never depends on sort stability. Each item shows a kind indicator + TierBadge. - Pulse tier badges: render TierBadge on PulseNoteCard/PulseBirdCard when salience is present; renders nothing when absent (graceful). The hashed static snapshot fixture is untouched — display wires up when real hub data populates salience. - Nav: add Stream + Pulse entries; /pulse highlights on its subroutes. - Tests: vitest for blendStream (interleave by date, noteworthy-first within equal date, empty inputs, absent-tier, deterministic, total-order tie-break) and pulse-card salience gating (TierBadge only when salience is set). Tiers/salience are reader-weight/IA only — never auth, privacy, or AP delivery (docs/blog-editorial-taxonomy-2026-07-03.md). HELD-for-freeze: shadow-only.
Build the next increment of the "blog as live-streamed ingestion surface" vision on the SHADOW UI only (tss.tinyland.dev). Everything additive and reversible; prod apex stays FROZEN. - /stream route (prerender, noindex+nofollow while held): one blended, reverse-chronological, tier-weighted timeline merging published long-form posts (src/lib/posts) with public pulse items (src/lib/pulse/load). Pure, deterministic blendStream(): date desc primary; noteworthy-first within an equal date (mirrors the #217 comparator); stable id tie-break so ordering never depends on sort stability. Each item shows a kind indicator + TierBadge. - Pulse tier badges: render TierBadge on PulseNoteCard/PulseBirdCard when salience is present; renders nothing when absent (graceful). The hashed static snapshot fixture is untouched — display wires up when real hub data populates salience. - Nav: add Stream + Pulse entries; /pulse highlights on its subroutes. - Tests: vitest for blendStream (interleave by date, noteworthy-first within equal date, empty inputs, absent-tier, deterministic, total-order tie-break) and pulse-card salience gating (TierBadge only when salience is set). Tiers/salience are reader-weight/IA only — never auth, privacy, or AP delivery (docs/blog-editorial-taxonomy-2026-07-03.md). HELD-for-freeze: shadow-only.
ab5f820 to
49e82de
Compare
Pushes to feat/blog-tiered-reader-ui now deploy the tss-shadow Pages project (production -> tss.tinyland.dev) so the shadow host tracks the branch. main -> transscendsurvival-org routing is byte-identical; PRs remain build-only. Lives on the held shadow branch only.
With Stream+Pulse added the full desktop nav measures ~830px, which overflows exactly at the md (768px) breakpoint (e2e prose-overflow caught it: 3 slug pages). Nav now appears at lg:, drawer covers tablet. Verified 9/9 (768/1024/375 x slug/stream/index) via vite preview.
Desktop nav moved md:->lg: (768px overflow fix); the spec's hardcoded nav.hidden.md\:flex selector + >=768 desktop assumption follow. Tablet now asserts the drawer trigger; desktop also asserts the new Stream link. Local: 49/50 pass (dark-mode fail is local headless-shell only — CI run 28854281525 passed it).
|
Program disposition — 2026-07-14: keep this shadow-only and do not promote yet. The ratified order is design + data first. Fresh live checks show the hub blog projection has 140 posts and zero Promotion prerequisites remain: TIN-2413 owns the reader-IA decision, TIN-2641 owns the shadow |
|
Status: GREEN and deliberately HELD (ratified 2026-07-14). The post-#228 This PR does not merge yet, by operator ruling ("design + data first"):
Promotion ruling happens only after both exist. The shadow deploy remains available for design iteration via |
|
Program-state correction (2026-07-14): TIN-2413 did run and selected Observatory Ledger for beta; all required checks on this head are now green. Neither fact authorizes promotion. Live hub data still has 140 posts with no reviewed editorial tier and 6 Pulse items with no salience, now owned by TIN-2891. This PR is restored to draft and remains shadow-only until TIN-2891 supplies real producer data, the UI is reconciled with the ratified design, and a separate promotion ruling is recorded. |
|
Product/IA ruling — 2026-07-15 This PR remains draft and will not be promoted as written. The tested primitives here remain useful and will be ported into a clean homepage lane: No production promotion occurs until the homepage lane is QA'd in its immutable PR environment against representative reviewed live tier/salience data and absent-data states, followed by explicit operator review. |
|
Status (2026-07-15, per TIN-2413/TIN-2641 rulings): this PR is ratified as the primitives donor of record — TierBadge, |
…nts (TIN-2903) NoteworthySpread (register 01) + LedgerRegister (register 02, capped 8 + archive link) compose the harvested TierBadge; PulseRail composes the #217 PulseNoteCard/ PulseBirdCard donors and renders every snapshot item in producer order (no silent cap); ConstellationSection exposes buildConstellationGroups as native details/summary disclosures with an honest category/tag label.
…thead control labels (TIN-2903) BlogCard wiring lifted verbatim from feat/blog-tiered-reader-ui (the harvested TierBadge test exercises it); aria-labels added to the masthead toggle/pause buttons flagged by the build a11y pass.
…d, pulse cards) — TIN-2903 Harvested byte-for-byte from origin/feat/blog-tiered-reader-ui: - src/lib/components/TierBadge.svelte - src/lib/components/TierBadge.test.ts - src/lib/stream/blend.ts - src/lib/stream/blend.test.ts - src/lib/components/pulse/PulseNoteCard.svelte (adds TierBadge integration) - src/lib/components/pulse/PulseBirdCard.svelte (adds TierBadge integration) - src/lib/components/pulse/PulseCards.test.ts (co-located tier-badge tests)
…nts (TIN-2903) NoteworthySpread (register 01) + LedgerRegister (register 02, capped 8 + archive link) compose the harvested TierBadge; PulseRail composes the #217 PulseNoteCard/ PulseBirdCard donors and renders every snapshot item in producer order (no silent cap); ConstellationSection exposes buildConstellationGroups as native details/summary disclosures with an honest category/tag label.
…thead control labels (TIN-2903) BlogCard wiring lifted verbatim from feat/blog-tiered-reader-ui (the harvested TierBadge test exercises it); aria-labels added to the masthead toggle/pause buttons flagged by the build a11y pass.
Shadow ingestion-surface UI
This PR grew from the WS-2 tiered blog reader into the shadow ingestion-surface UI — the next increment of the "blog as a live-streamed ingestion surface of tinyland.dev" vision, built and validated only on the shadow (tss.tinyland.dev). Everything is additive and reversible; the prod apex stays frozen.
Per
docs/blog-editorial-taxonomy-2026-07-03.md, tiers/salience are reader-weight / IA only — never an auth, privacy, or ActivityPub-delivery signal. An absent tier/salience means "not classified yet" and never implies private.Ingestion-surface increment (blended /stream + pulse tiers + nav)
/stream— one blended timeline (the centerpiece). New prerendered route (noindex,nofollowwhile held) that merges published long-form posts (src/lib/posts) with public pulse items (src/lib/pulse/load) into a single reverse-chronological, tier-weighted timeline. A small unifiedStreamItemtype (kind: 'post' | 'pulse', title/summary/date/tier/url) plus a pure, deterministicblendStream(): date-desc primary; within an equal datenoteworthyfloats first (mirrors the feat(blog): shadow ingestion-surface UI — tiered reader + blended /stream + pulse tiers #217 blog-index comparator); stableidtie-break so ordering never relies on sort stability. Each row shows a kind indicator (📝 / 💬 / 🐦) +TierBadge, reusing the pulse-card styling. Pulse snapshot load is best-effort — the stream still prerenders from posts alone if it's absent.PulseNoteCard/PulseBirdCardnow renderTierBadgewhensalienceis present (the WS-4PublicPulseItem.saliencefield) and nothing when absent — graceful display-when-present. The hashed static snapshot fixture (static/data/pulse/public-snapshot.v1.json) is untouched (content-hash gate preserved); real hub data populates salience later.+layout.svelte(desktop + mobile drawer);/pulsenow highlights on its subroutes.Validation (this increment)
vitest(touched paths): 21/21 green — newsrc/lib/stream/blend.test.ts(interleave by date, noteworthy-first within equal date, empty inputs, absent-tier, deterministic across input orderings, total-order tie-break) + pulse-card salience gating (TierBadgeonly whensalienceis set).svelte-check: 0 errors / 0 warnings on the new + changed code.vite build(SvelteKit build): the new/streampage and all app code build clean. The only local build failures are pre-existingMissing Mermaid prerender cacheerrors in fivesrc/posts/*.mdblog-content files — the known headless-Chromium prerender-cache sandbox limitation, unrelated to this change (no error references any new file). Relying on the CI shadow build for the full prerender.WS-2 — Tiered reader UI on the blog spoke (already in this branch)
Consumes the
editorial_tierfield added to thePosttype in #212 and surfaces it in the reader UI.What's here
TierBadge.svelte— a small, accessible,aria-labeled badge: stronger primary tint fornoteworthy(with a subtle star), muted outline forless-noteworthy, and it renders nothing when the tier is undefined.noteworthycards get a gentlering-1accent (featured styling still wins). No layout shift when the tier is absent.noteworthyposts only float up within an equal date (stableArray.sort, fully reversible). Untiered posts keep their existing relative order. Optional tier filter chips (default All, view-only, never drops a post; chips only appear when tiered posts exist).noteworthy, a recipe →less-noteworthy.Deliberately not touched
This is a visible reader change plus a new surface, so it is hold-for-freeze. The PR is opened non-draft on purpose so the "Build and optionally deploy Cloudflare Pages shadow" CI job renders a shadow preview at tss.tinyland.dev. Do not merge to prod, do not change the prod default build, and do not touch the live broker Worker while the freeze is in effect — validate on the shadow only.