@gurupanguji

Homepage Day Timeline Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Change the homepage feed from the latest 5 posts to the latest 5 distinct publishing dates with posts, rendered as a grouped left-rail timeline.

Architecture: Keep the change local to index.html. Add one small verification script in scripts/verify_homepage_day_timeline.py that proves both the grouping logic and the built homepage output, then replace the flat feed markup and styles in the homepage with grouped Liquid and timeline CSS. No archive work, no shared include extraction, and no JavaScript rendering changes in this pass.

Tech Stack: Jekyll, Liquid, HTML, CSS, Python 3 stdlib


File Map

Task 1: Add a Failing Homepage Timeline Verifier

Files:

#!/usr/bin/env python3
from __future__ import annotations

from pathlib import Path
import re
import sys


POST_PATTERN = re.compile(r"(\d{4})-(\d{2})-(\d{2})-(.+)\.md$")
GROUP_PATTERN = re.compile(r'class="[^"]*\bfeed-day-group\b[^"]*"')
LABEL_PATTERN = re.compile(r'<div class="feed-day-label">\s*([^<]+?)\s*</div>')


def slug_to_url(name: str) -> tuple[str, str]:
    match = POST_PATTERN.match(name)
    if not match:
        raise ValueError(f"Unsupported post filename: {name}")
    year, month, day, slug = match.groups()
    return f"{year}-{month}-{day}", f"/blog/{year}/{month}/{day}/{slug}/"


def group_posts_by_day(rows: list[tuple[str, str]], max_days: int = 5) -> list[dict[str, list[str] | str]]:
    groups: list[dict[str, list[str] | str]] = []
    current_day = None
    for day, url in rows:
        if day != current_day:
            if len(groups) == max_days:
                break
            groups.append({"day": day, "urls": []})
            current_day = day
        groups[-1]["urls"].append(url)
    return groups


def fixture_self_test() -> None:
    rows = [
        ("2026-04-13", "/blog/2026/04/13/a/"),
        ("2026-04-12", "/blog/2026/04/12/b/"),
        ("2026-04-12", "/blog/2026/04/12/c/"),
        ("2026-04-11", "/blog/2026/04/11/d/"),
        ("2026-04-10", "/blog/2026/04/10/e/"),
        ("2026-04-09", "/blog/2026/04/09/f/"),
        ("2026-04-08", "/blog/2026/04/08/g/"),
    ]
    groups = group_posts_by_day(rows, max_days=5)
    assert [group["day"] for group in groups] == [
        "2026-04-13",
        "2026-04-12",
        "2026-04-11",
        "2026-04-10",
        "2026-04-09",
    ]
    assert groups[1]["urls"] == [
        "/blog/2026/04/12/b/",
        "/blog/2026/04/12/c/",
    ]
    assert all(group["day"] != "2026-04-08" for group in groups)


def expected_homepage_groups(posts_dir: Path) -> list[dict[str, list[str] | str]]:
    rows: list[tuple[str, str]] = []
    for path in sorted(posts_dir.iterdir(), reverse=True):
        if not path.name.endswith(".md"):
            continue
        day, url = slug_to_url(path.name)
        rows.append((day, url))
    return group_posts_by_day(rows, max_days=5)


def day_label(day: str) -> str:
    year, month, date = day.split("-")
    month_name = {
        "01": "JAN",
        "02": "FEB",
        "03": "MAR",
        "04": "APR",
        "05": "MAY",
        "06": "JUN",
        "07": "JUL",
        "08": "AUG",
        "09": "SEP",
        "10": "OCT",
        "11": "NOV",
        "12": "DEC",
    }[month]
    return f"{month_name} {date}"


def verify_rendered_homepage(site_index: Path, groups: list[dict[str, list[str] | str]]) -> None:
    html = site_index.read_text(encoding="utf-8")

    group_count = len(GROUP_PATTERN.findall(html))
    if group_count != 5:
        raise AssertionError(f"Expected 5 timeline groups, found {group_count}")

    labels = LABEL_PATTERN.findall(html)
    expected_labels = [day_label(group["day"]) for group in groups]
    if labels[:5] != expected_labels:
        raise AssertionError(f"Expected labels {expected_labels}, found {labels[:5]}")

    for group in groups:
        for url in group["urls"]:
            if url not in html:
                raise AssertionError(f"Missing expected post URL in homepage output: {url}")

    all_expected_urls = [url for group in groups for url in group["urls"]]
    outside_group_urls = []
    posts_dir = Path("_posts")
    for path in sorted(posts_dir.iterdir(), reverse=True):
        if not path.name.endswith(".md"):
            continue
        _, url = slug_to_url(path.name)
        if url in all_expected_urls:
            continue
        outside_group_urls.append(url)
    if outside_group_urls and outside_group_urls[0] in html:
        raise AssertionError(f"Found a post from the 6th day or later in homepage output: {outside_group_urls[0]}")


def main() -> int:
    fixture_self_test()
    groups = expected_homepage_groups(Path("_posts"))
    verify_rendered_homepage(Path("_site/index.html"), groups)
    print(f"Verified homepage timeline for {len(groups)} distinct publish dates.")
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except AssertionError as error:
        print(f"FAIL: {error}", file=sys.stderr)
        raise SystemExit(1)

Run:

bundle exec jekyll build
python3 scripts/verify_homepage_day_timeline.py

Expected:

FAIL: Expected 5 timeline groups, found 0
git add scripts/verify_homepage_day_timeline.py
git commit -m "test: add homepage day timeline verifier"

Task 2: Replace the Homepage Feed With Grouped Timeline Markup

Files:

In index.html, replace the existing .feed-item, .feed-item.loaded, .feed-item:hover, .feed-item:focus-visible, .feed-item-title, and .feed-item-date rules with this block:

      .feed-timeline {
        display: flex;
        flex-direction: column;
        gap: 28px;
      }

      .feed-day-group {
        display: grid;
        grid-template-columns: 84px minmax(0, 1fr);
        column-gap: 22px;
        align-items: start;
      }

      .feed-day-rail {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        min-height: 100%;
      }

      .feed-day-label {
        font-family: var(--font-ui);
        font-size: 0.58rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 3px;
        color: var(--text-muted);
        white-space: nowrap;
      }

      .feed-day-thread {
        width: 1px;
        min-height: 52px;
        flex: 1;
        margin-top: 10px;
        margin-left: 10px;
        background: linear-gradient(
          to bottom,
          var(--border-subtle) 0%,
          rgba(0, 0, 0, 0) 100%
        );
      }

      html[data-theme="black"] .feed-day-thread {
        background: linear-gradient(
          to bottom,
          var(--border-subtle) 0%,
          rgba(255, 255, 255, 0) 100%
        );
      }

      .feed-day-posts {
        display: flex;
        flex-direction: column;
      }

      .feed-post-link {
        display: block;
        text-decoration: none;
        color: var(--text-primary);
        padding: 12px 0;
        border-bottom: 1px solid var(--border-subtle);
        transition: color 0.3s ease, transform 0.3s ease, padding-left 0.3s ease;
      }

      .feed-post-link:hover {
        padding-left: 10px;
        color: var(--interaction-emphasis);
      }

      .feed-post-link:focus-visible {
        padding-left: 10px;
        color: var(--interaction-emphasis);
        outline: 2px solid var(--interaction-emphasis);
        outline-offset: 4px;
      }

      .feed-post-title {
        display: block;
        font-family: var(--font-ui);
        font-size: 0.75rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 1px;
        line-height: 1.25;
      }

In the existing @media (max-width: 768px) block in index.html, add these rules before the closing brace:

        .blog-feed {
          max-width: 560px;
        }

        .feed-timeline {
          gap: 24px;
        }

        .feed-day-group {
          grid-template-columns: 1fr;
          row-gap: 10px;
        }

        .feed-day-thread {
          width: 48px;
          min-height: 1px;
          height: 1px;
          margin-top: 8px;
          margin-left: 0;
        }

In index.html, replace the current contents of #feed-container with this grouped timeline markup:

        <div id="feed-container" class="feed-timeline">
          
          
          
          
            
            
              

              
              

              <section class="feed-day-group">
                <div class="feed-day-rail">
                  <div class="feed-day-label">APR 06</div>
                  <div class="feed-day-thread" aria-hidden="true"></div>
                </div>
                <div class="feed-day-posts">
              
              
            

            <a href="/blog/2026/04/06/gemini-on-google-maps-inspiration/" class="feed-post-link">
              <span class="feed-post-title">🔗 I let Gemini in Google Maps plan my day and it went surprisingly well</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/you-will-never-be-satisfied/" class="feed-post-link">
              <span class="feed-post-title">🔗 You Will Never Be Satisfied</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/the-machines-are-fine-im-worried-about-us/" class="feed-post-link">
              <span class="feed-post-title">🔗 The machines are fine. I'm worried about us.</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/nobody-gets-promoted-for-simplicity/" class="feed-post-link">
              <span class="feed-post-title">🔗 Nobody gets promoted for simplicity</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/long-term-money/" class="feed-post-link">
              <span class="feed-post-title">🔗 Long-Term Money</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/are-you-noticing-this/" class="feed-post-link">
              <span class="feed-post-title">🔗 Are You Noticing This?</span>
            </a>
          
            
            

            <a href="/blog/2026/04/06/are-we-becoming-too-harsh-without-even-realizing/" class="feed-post-link">
              <span class="feed-post-title">🔗 Are we becoming too harsh without even realizing?</span>
            </a>
          
            
            
              
                </div>
              </section>
              

              
              

              <section class="feed-day-group">
                <div class="feed-day-rail">
                  <div class="feed-day-label">APR 05</div>
                  <div class="feed-day-thread" aria-hidden="true"></div>
                </div>
                <div class="feed-day-posts">
              
              
            

            <a href="/blog/2026/04/05/samsung-messages-discontinued/" class="feed-post-link">
              <span class="feed-post-title">🔗 Samsung Messages will be discontinued for Google Messages</span>
            </a>
          
            
            

            <a href="/blog/2026/04/05/maintenance-overload/" class="feed-post-link">
              <span class="feed-post-title">🔗 The Last Quiet Thing</span>
            </a>
          
            
            
              
                </div>
              </section>
              

              
              

              <section class="feed-day-group">
                <div class="feed-day-rail">
                  <div class="feed-day-label">APR 04</div>
                  <div class="feed-day-thread" aria-hidden="true"></div>
                </div>
                <div class="feed-day-posts">
              
              
            

            <a href="/blog/2026/04/04/we-are-in-weird-times/" class="feed-post-link">
              <span class="feed-post-title">🔗 We are in weird times</span>
            </a>
          
            
            
              
                </div>
              </section>
              

              
              

              <section class="feed-day-group">
                <div class="feed-day-rail">
                  <div class="feed-day-label">APR 03</div>
                  <div class="feed-day-thread" aria-hidden="true"></div>
                </div>
                <div class="feed-day-posts">
              
              
            

            <a href="/blog/2026/04/03/amazon-phone/" class="feed-post-link">
              <span class="feed-post-title">🔗 Report: Amazon is making another phone, this time for the AI era</span>
            </a>
          
            
            
              
                </div>
              </section>
              

              
              

              <section class="feed-day-group">
                <div class="feed-day-rail">
                  <div class="feed-day-label">APR 02</div>
                  <div class="feed-day-thread" aria-hidden="true"></div>
                </div>
                <div class="feed-day-posts">
              
              
            

            <a href="/blog/2026/04/02/llm-in-a-flash/" class="feed-post-link">
              <span class="feed-post-title">🔗 Autoresearching Apple's "LLM in a Flash" to run Qwen 397B locally</span>
            </a>
          
            
            
              
                </div>
              </section>
              

              
              
                

          
            </div>
          </section>
          
        </div>

Run:

bundle exec jekyll build
python3 scripts/verify_homepage_day_timeline.py

Expected:

Verified homepage timeline for 5 distinct publish dates.
git add index.html scripts/verify_homepage_day_timeline.py
git commit -m "feat: group homepage posts by day"

Task 3: Run Manual QA For Layout And Accessibility

Files:

Run:

bundle exec jekyll serve --host 127.0.0.1 --port 4000

Expected:

Server address: http://127.0.0.1:4000/

Verify:

At roughly 390px wide, verify:

Using Tab, verify:

Run:

bundle exec jekyll build
python3 scripts/verify_homepage_day_timeline.py

Expected:

Verified homepage timeline for 5 distinct publish dates.
git add index.html scripts/verify_homepage_day_timeline.py
git commit -m "fix: polish homepage day timeline layout"

Self-Review

Spec Coverage

Placeholder Scan

Type Consistency

GitHub Issue / PR Fallback

If gh auth is still broken in the execution environment, create the issue and later the PR manually with these commands after re-auth:

gh auth login -h github.com
gh issue create \
  --title "Homepage timeline groups latest posts by day" \
  --body "Implement the approved homepage-only day timeline design from docs/superpowers/specs/2026-04-06-homepage-day-timeline-design.md."

Execution Handoff

Plan complete and saved to docs/superpowers/plans/2026-04-06-homepage-day-timeline-implementation-plan.md. Two execution options:

1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration

2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints

Which approach?