Skip to content

GitBasedDocs

Specs before code: the plan behind a two-day build

10 September 2026 Updated 10 September 2026 4 min read
GitBasedDocs docs page rendering the project's own documentation

The first GitBasedDocs commit was almost all plan: research notes, feature specs and task cards. How that plan was shaped, and the four places reality corrected it.

The first commit in the GitBasedDocs repo is 58 files and 2,978 added lines, and almost none of it is app code. It is a bare Next.js scaffold plus the plan: 5 research notes, 8 feature specs, 18 task cards and an AGENTS.md with the commands and board rules. It landed at 15:19 on September 9. The last feature commit landed at 19:02 on September 10, and the repo now has 55 commits.

Research notes first

Each research note answers one question and ends with a takeaway.

  • How git-based CMS tools work, and why this app reads GitHub at request time instead of at build time.
  • How to read a private repo. A fine-grained token scoped to one repo won over a GitHub App, a deploy key and OAuth user tokens. The GitHub App is written down as phase two.
  • How to sync without GitHub Actions: webhook first, a Sync now button for repairs, and a timed recheck as the safety net.
  • How to render Markdown in Next.js: frontmatter with fallbacks, remark and rehype, sanitize before the browser, cache HTML per blob sha.
  • Auth and data: the roles, the Drizzle tables and the access check every read path runs.

A sixth note came mid-build. At 14:53 on day two I added a ranked list of the ten Markdown extensions that matter most for a vault that also lives on GitHub, with a pick and a reason for each. The five commits that implemented it followed between 14:56 and 15:10.

Specs with the same skeleton

Every feature spec has the same sections: purpose, users, behavior, data, UI, edge cases and "out of scope for v1".

Some specs carry more than behavior. The access control spec lists the tests that have to pass before the build counts as done: viewer A cannot open a page, an image or a search result in project B, and all three return 404. A removed member loses access on the next request without signing out.

The specs index also sets a build order: 01, 03, 02, 06, 04, 05, 07, 08. That is sign-in, then the GitHub connection, then projects, then sync, and only then the page viewer. The commit history follows it. Nothing renders a page until there is a synced index to render from.

Cards that say when they are done

The task board is a folder of Markdown files, one per card, grouped by area: setup/, auth/, github/, sync/ and so on. Each card has YAML frontmatter for status, priority and order, a Spec: line pointing at the feature file it implements, and a "Done when" checklist.

The checklist is the useful part. The webhook card says done means a bad signature returns 401 with no sync, and a push to another branch returns 200 with a log line. After the build, the card got a table of seven cases tested over HTTP against the running server, with the response for each.

The board started with 18 cards and ended with 30. The extra 12 came from work the plan didn't list: the design system and the reader home, admin navigation, two rounds of UI review with phone layout fixes, the Markdown extensions, the account page, and commit info on pages. All 30 are marked done.

Where the plan was wrong

The plan was wrong in a few places, and the fix each time was to change the spec in the same commit as the code.

The specs were first written against Auth.js v5 patterns. The second commit, 17 minutes after the first, moved the specs, the board and AGENTS.md to next-auth v4, since the v5 adapter packages don't work with v4.

The auth research picked database sessions so an admin could revoke access at once. next-auth v4 only allows email and password sign-in with JWT sessions. The fix commit switched to JWT and kept revoke working another way: the session callback reloads the user row on every call, so a deactivated user fails on their next request.

The GitHub connection spec assumed docs sit in a docs/ folder inside a code repo. My vault is the whole repo. The sync commit made that folder setting optional and empty by default, and let a project own the repo root.

The navigation spec said a folder's index.md sets the folder's label. In practice docs/index.md renamed the docs folder to "Index". The fix made folders keep their real names, as Obsidian does, and rewrote that line of the spec.

Not every document kept up. The auth research note still recommends database sessions, and the admin settings spec still lists AUTH_SECRET and a Postgres deploy, while the app reads NEXTAUTH_SECRET and runs on SQLite only.

The design spec came later

At 17:30 on day one, after the login and the connection form already worked, I wrote a page-by-page design spec. It covers every screen, what goes on it, and the exact theme tokens from globals.css. It opens with "Build this in Figma first, then we match the code to it". Its strongest rule is a radius of 0: every corner sharp, on cards, buttons, inputs, badges and dialogs.