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
scripts/verify_homepage_day_timeline.py
Responsibility: verify the built homepage renders exactly 5 day groups, includes all posts from those 5 days, excludes the 6th day, and keeps a small fixture test for multi-post grouping behavior.index.html
Responsibility: replace the flat homepage feed with grouped Liquid markup and page-scoped timeline CSS.Files:
Step 1: Add the verifier script
#!/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"
Files:
index.htmlIn 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"
Files:
index.html if QA reveals a real issueRun:
bundle exec jekyll serve --host 127.0.0.1 --port 4000
Expected:
Server address: http://127.0.0.1:4000/
Verify:
spacing between day groups is larger than spacing between post links
At roughly 390px wide, verify:
post titles do not collide with the date or viewport edge
Using Tab, verify:
the focus outline is readable in both Chrome and Black theme finishes
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"
index.html.feed-day-groupfeed-day-labelfeed-day-threadfeed-post-linkfeed-post-titleIf 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."
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?