feat(blog): import the owner-approved backfill articles - #78
Draft
axsaucedo wants to merge 8 commits into
Draft
Conversation
Import eight recoverable owner-approved long-form articles with canonical source metadata, locally stored article imagery, mechanically normalized heading levels, and language-tagged HackerNoon code fences. Add two body-less LinkedIn feature cards using dates decoded from their activity IDs. Extend the blog schema with newsletter-compatible syndication records and render an Also published on row below article bodies using blog-owned metadata styles. Record the new style ownership and refresh the style-document ratchet. Skip the manifest entry whose canonical URL currently serves an article by another author, avoiding an incorrect republication under the site's default byline.
Visual parity — dark theme 🌙1440x1000 — 37 of 37 routes moved.
420x900 — 37 of 37 routes moved.
Differing pixels locate a change; the peak channel delta sizes it. A large count with a Informational: label the PR |
Visual parity — light theme ☀️1440x1000 — 37 of 37 routes moved.
420x900 — 37 of 37 routes moved.
Differing pixels locate a change; the peak channel delta sizes it. A large count with a Informational: label the PR |
Seven review items from the owner's pass over the stacked imports: - Light theme: code windows and the committed dark diagrams keep their dark chrome (the Shiki palette has no light rendition; background and chrome are pinned under :root[data-theme='light']). - Related posts render as bordered cards in a responsive grid instead of bare accent links. - The italic standfirst that duplicated the summary (typed in the hero) is removed from all nine posts that carried one. - Images centre at their natural size: Astro's pipeline stamps width/height, which exempted optimised images from the full-width rule and left them left-aligned inside the centred measure. - Captions (italic-only paragraph after an image) read Medium-style: small, centred, muted; the 21 '(Image by ...)' caption lines are italicised. - The external-link card capability is removed entirely per owner call: the two LinkedIn announcement cards are deleted, every blog entry now has a body, and the hasBlogBody/external plumbing is gone from the utils, the archive and the post page. A queued post in TODO.md will discuss the ACM and UK policy engagements properly instead. Verified: parity vs master zero on homepage, issue 396 and the archive (399 only the intended separator fix); DOM gates green on the reworked routes except the pre-known 20,000px harness height ceiling on the C++ post (20,366px, content-complete); build, lint, format, ratchet green; light-theme code window verified by screenshot.
Owner calls on surfacing the blog: - The Network menu's JOIN column gains a Blog item beside the newsletter, so the drawer, mega menu and llms.txt all pick it up; llms.txt drops the /blog/ exclusion and derives the archive entry (title, post count and date span) the same way as the newsletter archive. - The footnote band's hardcoded two-talk list becomes a publications list: blog, newsletter, talks and open source rows in the same kind/title/arrow format, replacing the single-surface talks column; the all-talks mono link is retired and the DOM gate's footnote assertion updated to the remaining legal link. - The blog archive opens with the flagship Hero (classic mode, cycling the blog's subject areas) instead of the bare ProseLayout header the owner called boring; a fuller landing redesign is proposed separately. Verified: build, lint, format, ratchet green; DOM gates pass on the archive, a post, the homepage at desktop and issue 399 at both viewports. The mobile homepage gate fails identically on a master build (drawer accordion visibility timeout in the harness), so that failure is pre-existing and recorded, not introduced here. Nav and footer changes are deliberate visual changes on every route, so pixel parity vs master no longer applies to this branch's chrome.
…orphs Owner direction on the landing: - Every post now carries a featured image, enforced by the schema through Astro's image() helper (typed metadata, optimised rendering, og:image from the built asset). Missing covers were recovered: the HackerNoon covers for the memory series, the Kompute Android artwork for the 2020 Android post and the talk's YouTube frame for the pilot. On the post page the body's own first image remains the opening visual, per owner preference; the featured image powers the archive and social cards. - The archive body is rebuilt: a featured latest-post card (image and copy in a split card) followed by a ScrollStage showcase, reusing the existing scroll-driven component the talks reel already consumes: the sticky stage crossfades each post's featured image while the post cards scroll past the activation line. On small screens the stage hides and the cards carry the page. - Clicking any archive title morphs into the post's hero title via shared view-transition names (the Hero gains a title_transition prop). - New hero copy: 'Blogs, Tutorials / & Thought Leadership / from The Institute' with a rewritten subtitle, alternatives proposed to the owner. Verified: build, lint, format, ratchet green; DOM gates pass on the archive and two post routes at both viewports.
…olumn Owner-directed iteration on the archive showcase, landed as the interim design after exploring and rejecting a boxed split layout, a mid-screen gradient with backdrop blur, and per-card floating fade boxes: - Each post's featured image fills the showcase container edge to edge behind the cards, with only a light uniform wash; the scroll column sits flush against the container's right edge at 450px. - The card column carries a 70% translucent backdrop so the artwork ghosts through behind readable text, and the only transition is a fixed 80px fade strip off the column's left edge; no hard boundary anywhere. - Cards follow the talks-reel rhythm: the first enters around the lower third (52vh runway) and each gets a 46vh beat with the hairline dividers removed. - Scoping note: ScrollStage's rendered elements do not carry the page's style scope, so every override anchors on the page-owned section wrapper with :global; the same applies to the Image component's img output. TODO.md records the follow-up blog landing redesign with the rejected explorations, per owner verdict: acceptable to ship, not yet good. Verified: build green; DOM gates pass on the archive at both viewports; lint, format and ratchet green.
'& Thought Leadership' becomes '& Thoughts' per owner, so the middle line no longer risks wrapping at the classic display scale.
Owner call: Blog sits under CONNECT beside talks and partners rather than in the JOIN column, and replaces the Policy & standards item there, which was redundant with the top-level Policy & Frameworks menu. llms.txt keeps deriving the blog's section from the menus, now under CONNECT.
The daily 05:43 UTC cron exists only to publish future-dated blog posts, but deployed a byte-identical site on every quiet day. A decision script now gates scheduled runs: deploy only when a post's publish date falls within a three-day lookback window ending today. Push and manual runs never consult it and always deploy. The window is three days rather than one so missed cron runs self-heal: GitHub delays or drops scheduled runs under load, and a post that became due on a skipped day must still publish on the next run instead of waiting for an unrelated push. The script tolerates the empty-match case explicitly (grep -c exits nonzero on zero matches, which would otherwise kill the set -e shell and fail the run on exactly the days it should skip), always exits zero, and takes a date override so the decision table is testable offline. Tested offline across the corner cases: no due posts (skip, exit 0), due same day, due one and two days earlier (all deploy), three days after the last post (skip), month boundary arithmetic, and the zero-match exit-code path. Workflow YAML parse-validated; the guard's skip path leaves every build step and the deploy job unscheduled.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
syndicationrecords and render an “Also published on” row below blog post bodiesImported pieces
The manifest entry “Why Agents Belong in Your Software Architecture (Not Just Your Chatbot)” is intentionally skipped because its canonical URL currently serves an April 2026 HackerNoon article by Filip Mareš. No matching owner-authored source could be recovered, and importing that page would incorrectly attribute it to the blog's default author.
Canonical and syndication scheme
Republished posts retain the manifest source and canonical original URL, so their local pages emit an off-site canonical and stay out of the sitemap through the pilot's existing rule. Additional known copies use the optional
syndicationlist of{ name, url }records and render below the body as external links. Body-less LinkedIn entries link directly out from the archive and generate no local page.Source recovery
Medium returned HTTP 403, so its live article content was recovered through the Jina text rendering service. The available Wayback capture for the game-development article was also checked. Medium's embedded code figures were not exposed by either route and could not be reconstructed verbatim. The Android article mirror exposed no content image URLs, so it includes the full exposed prose without dead image references. HackerNoon article prose and code were recovered from the live pages.
Verification
rm -rf .astro node_modules/.astro && npm run buildnpm run check:ratchetnpm run lint/blog/and three image-rich imported post routesTwo additional, exceptionally long article routes exceed the DOM harness's fixed 20,000px page-height ceiling, with no other DOM findings. They remain full length to preserve the verbatim source requirement.