# bradnichol/personal-blog architecture

> Architecture diagram of the GitHub repository bradnichol/personal-blog, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-28). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/bradnichol/personal-blog
- Repository: https://github.com/bradnichol/personal-blog

## Overview

This repository is a small static personal site plus a publishing workflow for its Recent work feed. The main workflow fetches allowlisted GitHub activity, filters and groups it, prepares sanitized input for an external coding agent, validates the candidate summaries, and writes a public JSON artifact that the site fetches and renders. The README documents this workflow and the reusable skill; sampled source supports the detailed extraction, validation, state, and rendering links shown. Other site content, including RotationLab and contact information, is served as static HTML.

Main entry points: [`scripts/activity/prepare.mjs`](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/prepare.mjs) (Prepare activity), [`scripts/activity/finalize.mjs`](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/finalize.mjs) (Finalize artifact), [`scripts/activity/github.mjs`](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/github.mjs) (GitHub activity extractor), [`skills/publish-recent-work/SKILL.md`](https://github.com/bradnichol/personal-blog/blob/master/skills/publish-recent-work/SKILL.md) (Publishing skill)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_site["Personal site"]
  node_homepage["Homepage<br/>[index.html]"]
  node_contact["Contact page<br/>[index.html]"]
  node_renderer["Recent work renderer<br/>[recent-work.js]"]
  node_public_feed["Recent work feed<br/>[recent-work.json]"]
end

subgraph group_activity["Activity preparation"]
  node_preparer["Prepare activity<br/>[prepare.mjs]"]
  node_config["Activity configuration<br/>[config.mjs]"]
  node_github["GitHub activity extractor<br/>[github.mjs]"]
  node_eligibility["Filter and group activity<br/>[eligibility.mjs]"]
end

subgraph group_summary["Safe summaries"]
  node_redaction["Text redaction<br/>[text.mjs]"]
  node_summarizer_input["Summarizer input<br/>[model.mjs]"]
end

subgraph group_publishing["Publishing and state"]
  node_skill["Publishing skill<br/>[SKILL.md]"]
  node_finalizer["Finalize artifact<br/>[finalize.mjs]"]
  node_contract["Public contract<br/>[contract.mjs]"]
  node_state[("Activity state<br/>[state.mjs]")]
end

node_visitor(("Site visitor"))
node_github_service["GitHub API"]
node_agent(("Coding agent"))

node_visitor -->|"visits"| node_homepage
node_visitor -->|"visits"| node_contact
node_homepage -->|"loads"| node_renderer
node_renderer -->|"fetches"| node_public_feed
node_skill -->|"runs preparation"| node_preparer
node_skill -->|"invokes"| node_agent
node_skill -->|"runs finalization"| node_finalizer
node_preparer -->|"reads config"| node_config
node_preparer -->|"extracts activity"| node_github
node_preparer -->|"groups activity"| node_eligibility
node_preparer -->|"builds input"| node_summarizer_input
node_preparer -->|"reads and records"| node_state
node_github -->|"requests activity"| node_github_service
node_github -->|"selects eligible"| node_eligibility
node_github -->|"redacts source text"| node_redaction
node_github -->|"creates source keys"| node_state
node_summarizer_input -->|"sanitizes input"| node_redaction
node_preparer -->|"provides prepared input"| node_agent
node_agent -->|"returns candidates"| node_finalizer
node_finalizer -->|"validates candidates"| node_contract
node_finalizer -->|"reads private terms"| node_config
node_finalizer -->|"reads and updates"| node_state
node_finalizer -->|"writes artifact"| node_public_feed

click node_homepage "https://github.com/bradnichol/personal-blog/blob/master/public/index.html"
click node_contact "https://github.com/bradnichol/personal-blog/blob/master/public/contact/index.html"
click node_renderer "https://github.com/bradnichol/personal-blog/blob/master/public/scripts/recent-work.js"
click node_public_feed "https://github.com/bradnichol/personal-blog/blob/master/public/data/recent-work.json"
click node_skill "https://github.com/bradnichol/personal-blog/blob/master/skills/publish-recent-work/SKILL.md"
click node_preparer "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/prepare.mjs"
click node_config "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/config.mjs"
click node_github "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/github.mjs"
click node_eligibility "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/eligibility.mjs"
click node_redaction "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/text.mjs"
click node_summarizer_input "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/model.mjs"
click node_finalizer "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/finalize.mjs"
click node_contract "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/contract.mjs"
click node_state "https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/state.mjs"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_homepage,node_contact,node_renderer,node_public_feed toneBlue
class node_preparer,node_config,node_github,node_eligibility toneAmber
class node_redaction,node_summarizer_input,node_github_service toneMint
class node_skill,node_finalizer,node_contract,node_state toneRose
class node_visitor,node_agent toneIndigo
```

## Components

### Personal site

- [Homepage](https://github.com/bradnichol/personal-blog/blob/master/public/index.html): `public/index.html`
- [Contact page](https://github.com/bradnichol/personal-blog/blob/master/public/contact/index.html): `public/contact/index.html`
- [Recent work renderer](https://github.com/bradnichol/personal-blog/blob/master/public/scripts/recent-work.js): `public/scripts/recent-work.js`
- [Recent work feed](https://github.com/bradnichol/personal-blog/blob/master/public/data/recent-work.json): `public/data/recent-work.json`

### Activity preparation

- [Prepare activity](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/prepare.mjs): `scripts/activity/prepare.mjs`
- [Activity configuration](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/config.mjs): `scripts/activity/config.mjs`
- [GitHub activity extractor](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/github.mjs): `scripts/activity/github.mjs`
- [Filter and group activity](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/eligibility.mjs): `scripts/activity/eligibility.mjs`

### Safe summaries

- [Text redaction](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/text.mjs): `scripts/activity/text.mjs`
- [Summarizer input](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/model.mjs): `scripts/activity/model.mjs`

### Publishing and state

- [Publishing skill](https://github.com/bradnichol/personal-blog/blob/master/skills/publish-recent-work/SKILL.md): `skills/publish-recent-work/SKILL.md`
- [Finalize artifact](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/finalize.mjs): `scripts/activity/finalize.mjs`
- [Public contract](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/contract.mjs): `scripts/activity/contract.mjs`
- [Activity state](https://github.com/bradnichol/personal-blog/blob/master/scripts/activity/state.mjs): `scripts/activity/state.mjs`

### Other components

- **Site visitor**
- **GitHub API**
- **Coding agent**

## Connections

- Site visitor → Homepage: visits
- Site visitor → Contact page: visits
- Homepage → Recent work renderer: loads
- Recent work renderer → Recent work feed: fetches
- Publishing skill → Prepare activity: runs preparation
- Publishing skill → Coding agent: invokes
- Publishing skill → Finalize artifact: runs finalization
- Prepare activity → Activity configuration: reads config
- Prepare activity → GitHub activity extractor: extracts activity
- Prepare activity → Filter and group activity: groups activity
- Prepare activity → Summarizer input: builds input
- Prepare activity → Activity state: reads and records
- GitHub activity extractor → GitHub API: requests activity
- GitHub activity extractor → Filter and group activity: selects eligible
- GitHub activity extractor → Text redaction: redacts source text
- GitHub activity extractor → Activity state: creates source keys
- Summarizer input → Text redaction: sanitizes input
- Prepare activity → Coding agent: provides prepared input
- Coding agent → Finalize artifact: returns candidates
- Finalize artifact → Public contract: validates candidates
- Finalize artifact → Activity configuration: reads private terms
- Finalize artifact → Activity state: reads and updates
- Finalize artifact → Recent work feed: writes artifact
