LifeStarter

LifeStarter

Jekyll static site on GitHub Pages — consumer life-stages content with financial calculators (calculators/, fin_calc/, tools/) organised around life events: first apartment, college freshman, new parent, retirement.

Commands

make serve    # local preview
make build    # jekyll build
make lint     # stylelint (assets/css) + markdownlint (_posts)
make check    # lint + test  ← the gate; run before committing
make test     # build, then htmlproofer + frontmatter/content checks
              # + scripts/check-rendered-affiliate.sh on the built _site
make clean

Gotchas

  • Uses the github-pages gem, not plain Jekyll. Plugin versions are pinned by GitHub, so a plugin that works locally can still be unsupported on Pages. Add plugins only if github-pages already bundles them.
  • Content is YMYL (financial advice adjacent). Calculator logic and any figure presented as guidance needs a real source, not a plausible number. This is the category where an invented rate becomes someone’s actual decision.
  • llms.txt and the “For AI Agents” footer link are deliberate, added for AI discoverability — do not remove them as cruft.
  • _site/ is build output; gitignored, never edit.

SEO pre-commit hooks

This repo is audited by the shared tooling in ~/Sites/agency (the emaxon/epm-agency repo). It supersedes epm-seo-audit, which is retired. The installed .git/hooks/pre-commit runs both checks on staged files:

~/Sites/agency/.venv/bin/python3 \
  ~/Sites/agency/lib/audit_precommit/audit_frontmatter.py --repo-path . --only-staged
~/Sites/agency/.venv/bin/python3 \
  ~/Sites/agency/lib/audit_precommit/audit_content.py --repo-path . --only-staged

Frontmatter completeness and content quality are enforced there, not in make lint. Install or remove the hook with bash ~/Sites/agency/lib/audit_precommit/install_hooks.sh --repo-path . [--uninstall].

Automated PRs from the agency repo

~/Sites/agency also runs the pipelines that open PRs here — tasks/seo_autofix.py, tasks/gumroad_link_autofix.py, tasks/amazon_link_autofix.py. They run against their own clone, so their commits arrive via origin, not from this working copy.

One line in _posts/2026-04-04-how-to-evaluate-a-job-offer.md went through three of them: #38 (Gumroad autofix) stripped a dead product link, #39 (title_too_long) reintroduced it, and #61 (broken_ext_link) stripped it again, leaving a bold call to action that linked nowhere.

When fixing a link one of them touched, prefer a relative in-repo target such as /product/<kit>/. Both strip handlers match an exact dead URL supplied by a finding, so neither rewrites a relative path. That is not blanket immunity: seo_autofix has a separate broken_internal_link handler, so the destination has to actually exist.