syntaxai/tdd.md · main · CONTRIBUTING.md

CONTRIBUTING.md 125 lines · 7433 bytes raw · source

Contributing to tdd.md

This file is the on-ramp for new contributors — human or agent. It links to canonical sources rather than restating them. If something here drifts from what the spec or workflow memory says, the canonical artifact wins; please file an issue.

Two URLs, one file:

Before you start

Read in this order:

  1. /sama/v2 — the architectural spec (rules, profile, verifier, §5 metrics, §6 evolution policy)
  2. /blog/2026-05/sama-v2-on-ramp-gap — why this file exists
  3. /blog/2026-05/sama-v2-goal-chain-gap — why /goals are now in git

Then check the live state: /sama/v2/verify must report 7/7 ✓ before and after every merge. This is the load-bearing anti-fudge gate.

How contribution works on this codebase

This is a self-hosted project. The canonical surfaces are all on tdd.md:

Editing the live site is admin-only by design — see src/b51_render_edit.ts. The admin uses the GitHub PR flow internally; external contributors engage through the issue tracker on Forgejo, not via GitHub PRs.

The /goal workflow

The contract for every PR on this site is a /goal slash command. The canonical workflow definition lives at /goals/migrate-historical-goals; a 5-line summary:

  1. User fires /goal with Goal: ... Done when: ... Constraints ... Load-bearing files ... structure
  2. Agent's first action: write the verbatim text to goals/<slug>.md with status: pending, branch + commit
  3. Implementation work happens in subsequent commits
  4. PR body MUST include the verbatim /goal under a ## /goal heading (defense-in-depth)
  5. Final commit before deploy flips status: shipped + fills merge_sha

Every shipped /goal is browseable at /goals.

SAMA layer convention

The file prefix encodes the layer. The canonical definition is in /sama/v2 §1.1; the table below shows concrete examples from this repo:

Prefix Layer Means Example
a*_ 0 — Pure data + types, no I/O a31_blog.ts
b*_ 1 — Core pure logic, no I/O b32_sitemap.ts
c*_ 2 — Adapter parses boundaries; DB, network, fs c14_git.ts
d*_ 3 — Entry route handlers, app bootstrap d21_app.ts
b51_ 1 — Core (render) pure HTML rendering b51_render_layout.ts

The Law (§1.2): imports flow downward only. The verifier rejects upward or sideways edges mechanically.

How to add a blog post

  1. Write content/blog/<slug>.md with the body
  2. Add an entry to ALL_POSTS in src/a31_blog.ts with { slug, title, description, date }
  3. The sitemap, blog index, and /blog/<slug> route pick it up automatically — registry is the single source of truth
  4. Branch → PR → merge → deploy

Worked example: /blog/2026-05/sama-v2-sitemap-implementation-plan.

How to add a /goal

Type /goal in conversation with the agent. The agent handles the rest per the workflow above.

If you're not the admin: file an issue at git.tdd.md/syntaxai/tdd.md/issues using the Goal: ... Done when: ... Constraints ... Load-bearing files ... shape. The admin (or agent on their behalf) fires it.

How to add an image

  1. Write public/images/<name>.svg with a https://tdd.md watermark text somewhere on the canvas (typically bottom-right in a muted color)
  2. Render PNG: rsvg-convert -w 1200 -h 600 public/images/<name>.svg -o public/images/<name>.png
  3. Reference from markdown: ![alt](/images/<name>.png?v=1)

The /images/* wildcard is served by the fallback handler in d21_handlers_fallback.ts — no per-image route registration needed.

How to add a Layer-1 helper

  1. Write src/b32_<name>.ts — pure, no I/O, single export per concern
  2. Write the sibling src/b32_<name>.test.ts — required by §4.3 Modeled-tests
  3. Caller (typically Layer 2 or Layer 3) imports from ./b32_<name>.ts

Worked example: b32_sitemap.ts + b32_sitemap.test.ts.

How to add a top-level directory

Important: every new top-level directory needs a corresponding COPY <dir> ./<dir> line in Containerfile. Without it, the directory doesn't ship with the runtime image and request-time disk reads return 404. This was discovered in PR #46 when /goals/<slug> returned 404 in production.

Checklist for a new top-level dir:

  1. Create the directory and any initial files
  2. Add COPY <dir> ./<dir> to Containerfile (after the existing COPY lines)
  3. Live-verify a file from the new directory loads after deploy

Anti-fudge defaults

Every PR satisfies these without exception:

  • Site language: English only. No Dutch in user-facing strings (blog bodies, banners, error text, page copy)
  • Tests stay green: bun test must pass; new behaviour gets a sibling test
  • /sama/v2/verify stays 7/7 ✓: structural choices must preserve the verifier verdict
  • No --no-verify, no force-push to main: hooks exist for a reason
  • GitHub flow via flatpak-spawn: the sandbox doesn't have gh; use flatpak-spawn --host gh ...

Branch / deploy flow (admin path)

The full sequence the agent (or admin) runs end-to-end:

  1. git checkout -b <slug> — branch from main
  2. Implementation work + sibling tests
  3. git push -u origin <slug> + gh pr create (body includes ## /goal verbatim per workflow)
  4. gh pr merge --merge --delete-branch after CI green
  5. git checkout main && git pull origin main — sync local
  6. git push p620 main — push to self-hosted bare repo
  7. flatpak-spawn --host /var/home/scri/Documents/tdd.md/scripts/p620/deploy-tdd-md.sh — rebuild + redeploy container
  8. Live-verify: curl the changed URLs, confirm /sama/v2/verify still 7/7 ✓

When in doubt

  • The spec (/sama/v2) is the single source of truth for architectural rules
  • The verifier (/sama/v2/verify) is the single source of truth for whether a commit is conformant
  • The /goals registry (/goals) is the single source of truth for what every PR was held against
  • This file links to all three. If it contradicts any of them, the canonical artifact wins; please file an issue at git.tdd.md/syntaxai/tdd.md/issues.