FIT Automate
FIT Labs · working name: FIT Docs

A documentation pipeline that fails closed.

One template repo. Documents carry their own state, five gates are computed live and never stored, a named human signs the exact bytes, and publishing is a validated git commit. No database. No SaaS. Zero LLM calls in the pipeline.

Invite-only alpha. This page is the tour. There's nothing public to install yet.

Distribution
one template repo
State
frontmatter, not folders
Quality bar
5 gates, computed live
Sign-off
hash-bound attestation
Publish
a validated git commit
Go live
git push → CDN

Status lives in the document, not in a folder or a database.

Every document carries eight core fields in YAML frontmatter. status is a closed set: draft → in-review → promote-ready → published, with two terminal states for the archive. The pipeline reads that field, checks the rules, and is the only writer allowed to change it.

There are no status folders to shuffle files between. Lanes exist only as filtered views in the app. A document physically moves at most twice in its life: once when it publishes, once if it's archived. Everything else is a field change, so everything else is a clean, reviewable git diff.

The app writes all frontmatter through forms. Your editor (VS Code, Obsidian, Notepad, whatever the OS hands the file to) only ever touches prose. Structured data stays structured because no human hand-edits YAML.

drafts/sops/SOP-2-vendor-access-review.md
---
title: Vendor Access Review
status: draft
owner: Sam Okafor          # a person, never a role
audience: internal       # fail-safe default
version: "0.1"
created: "2026-07-05"
updated: "2026-07-05"
review_cycle: quarterly
sop_number: 2            # Library-declared extra
---
A real seed document from the alpha repo. Note what's absent: no gate booleans, no publish flags. Anything that can be computed is never stored.
The pipeline board: every document grouped by status, with lanes as filtered views, not folders

Five gates. Computed live. One red gate stops everything.

There is no force-publish, no admin override, no exception path. The interesting part is where the gates live: one Python module is the single authority. The UI consumes a generated gates.json, and a pytest drift guard fails the build if the two ever disagree. A gate is defined exactly once, or the test suite says no.

Owner

A named person is accountable. No owner, no promotion.

Metadata

All eight core fields present and valid, plus every extra this Library declares.

Structure

The document's headings match its Library template, in order.

Reviewed

A fresh attestation whose hash still matches the current body.

Audience

Only public documents reach the published site. Internal stops at in-review.

gates · computed live, never stored
✗ 2 gates failing. The pipeline fails closed.

✓ 1. Owner        Sam Okafor
✓ 2. Metadata     8 core fields + 1 Library extra, valid
✓ 3. Structure    headings match the sops template
✗ 4. Reviewed     not reviewed yet: 'reviewed_by', 'reviewed', 'reviewed_hash'
                  missing. The review form writes the Attestation.
✗ 5. Audience     audience is 'internal'; internal documents stop at in-review.
The document workspace: frontmatter form on the left, live gate checklist on the right
Actual gate messages from the alpha. The UI shows this checklist live while you edit.

The signature is bound to the bytes.

Review isn't a checkbox. It's an attestation. A named person signs, and the app writes three fields: who, when, and a SHA-256 of the document body at that exact moment (first 12 hex chars).

Edit one character of the content afterward and gate 4 breaks by itself, because the stored hash no longer matches the recomputed one. Nobody has to notice. The signature notices.

We're precise about the claim: it's a tripwire, not a security boundary. It won't stop a malicious insider with git access. It will catch the thing that actually happens: a well-meaning edit after sign-off that silently invalidates the review.

written by the app on sign, never hand-edited
reviewed_by: Stuart Dorfman
reviewed: "2026-07-07"
reviewed_hash: 625a3d00ec2d   # sha256(body)[:12]

# edit the body afterward →
✗ 4. Reviewed: attestation hash no longer matches the current body
The review-and-attest dialog: named reviewer and a content checklist, with the hash explained

The UI's enforcement is UX. The pipeline's is law.

The app refuses to set promote-ready unless every gate is green, and then promote re-validates all of it anyway, from the file on disk. Hand-edited YAML can't sneak past, because the pipeline never trusts that the UI did its job.

agent/promote.py · plan / execute split
def plan(draft) -> PromotePlan:
    # pure function: re-check status + all five gates,
    # resolve doc + asset targets. No side effects -
    # purity is enforced by its own tests.

def execute(plan, publisher) -> PromoteResult:
    # publisher is an adapter (Astro today).
    # strict build validates BEFORE anything ships;
    # any failure restores the previous state.
The site generator sits behind a seam. Tests run against a fake publisher, with no subprocess mocking, and swapping generators later is an adapter, not a rewrite.
promote · vendor-support-contacts.md
> re-validating status + 5 gates ......... all green
> astro build (strict) ................... ok
> copy doc + assets → web/ .............. ok
[master 5bc0084] docs: promote vendor-support-contacts.md
[master 59a586c] audit: record promote vendor-support-contacts.md

status: published; go-live is git push, and stays human.
The staged promote flow in the app, ending in a published confirmation

Every promote and rollback leaves an append-only trail.

One audit module owns the line format, the timestamp, and the append-only write. The writer and the reader are the same code, so they can't drift. Each transaction lands as two commits: the change itself, then the audit record. Your compliance story is git log.

app/logs/audit-log.md · real entries from the alpha repo
[2026-07-05T21:10:31Z] [PROMOTE_SUCCESS]  [drafts/kb/sample-kb-article.md] [web/src/content/docs/kb/sample-kb-article.md] [e93372e]
[2026-07-05T21:10:51Z] [ROLLBACK_SUCCESS] [drafts/kb/sample-kb-article.md] [web/src/content/docs/kb/sample-kb-article.md] [c35c010]
[2026-07-06T21:31:39Z] [PROMOTE_SUCCESS]  [drafts/procedures/ssl-certificate-renewal.md] [web/src/content/docs/procedures/ssl-certificate-renewal.md] [3a7863b]
[2026-07-07T05:08:10Z] [PROMOTE_SUCCESS]  [drafts/kb/vendor-support-contacts.md] [web/src/content/docs/kb/vendor-support-contacts.md] [5bc0084]
Yes, that second line is a rollback. Un-publishing is a first-class verb with the same receipt, not a support ticket.
The audit page in the app, listing promote and rollback entries newest-first

Decisions that look boring until you sit with them.

Your docs ship agent-readable

The published site emits /llms.txt, generated from the same content collection as the human pages, so the agent feed and the site can't drift. A document is in the feed exactly when it has a published page.

# FIT Docs

> Published documents from the pipeline.
> Every entry is gate-checked and promoted.

## Documents
- [SSL Certificate Renewal](/procedures/...)
- [Vendor Support Contacts](/kb/...)

Deterministic to the bone

Zero LLM calls, zero API keys, zero network dependencies in the pipeline. Every gate check, every promote, every hash is reproducible: the same input produces the same answer on any machine, including an air-gapped one. In 2026, that's the contrarian feature.

Delete us, keep everything

There is no database. State is frontmatter; history is git; the site is static files. Every document is plain Markdown you can grep, diff, and blame. If you abandon the product tomorrow, you keep a complete, readable, versioned archive, because that's all it ever was.

Windows is first-class

Not "should work on Windows." First-tested on Windows, with PowerShell-safe setup scripts and the execution-policy wall designed out. setup checks prerequisites and tells you exactly what's missing; it never installs anything outside its own folder. "I couldn't install it" is treated as a sev-1 product bug.

The product is a template repo

Click "Use this template," run one command, and you own a complete instance: drafts, pipeline, app, and published site in one private repo. Going live is git push to Cloudflare Pages. The pipeline never holds deploy credentials; the last mile stays human.

Assets travel with the doc

Images live per-document with relative links. Promote, rollback, and archive move the document and its assets as one unit and verify every link still resolves, so screenshots don't orphan when a doc changes state.

The published documentation site, generated by the pipeline
  • Python pipeline · ~180 pytest green
  • Node + Astro app · Starlight site
  • three CI gates: ruff+pytest / lint+build / astro build
  • no Docker
  • one setup command
  • works offline

The alpha is small on purpose.

A handful of invited operators, four weeks, GitHub-native feedback, and a named go/no-go at the end. If you want a seat when one opens, or you just want to argue about the architecture, we'd genuinely like to hear it.

Private alpha · evaluation use only · the product name is a working name while the real one clears the paperwork.

Start your AI Readiness Audit

It begins with a 30-minute call.

Calendar not loading? Open it in a new tab.