Every change to machinebehavior.io moves through the stages below, in this order. The site serves readers, so a stage that fails lets a reader see a wrong or broken page. A stage is done when its exit criteria hold and its record exists. Each record links the one before it, so a line on a live page traces back to the issue that asked for it.
The branch model is short-lived branches and pull requests to main (ADR-0029): agent sessions and the operator branch from origin/main, push the branch and open a pull request; a passing Conformity checks job is required before the merge. Only two automated writers push to main directly, through deploy keys. Only the workflow deploys, on a push to main that passes the conformity gate (ADR-0002). Releases are continuous, several deploys a day.
Stages
| Stage | Entry | Exit | Owner | Record | Served by |
|---|---|---|---|---|---|
| Request | Someone needs a change, or a reader reports a problem | An issue with a type, an area and a priority label; scoped issues carry status: ready | Stefan Coetzee | GitHub issue on uncovertechtalent/machinebehavior.io | Issue templates in .github/ISSUE_TEMPLATE/, triage per Ticket board, sdlc:ticket |
| RFC | The change touches more than one site or the shared gate, or is hard to undo | A decision record with status accepted | Stefan Coetzee | Decision record with status proposed | No comment round; see Gaps |
| Decision | A choice was made that others need to know | Decision record with status accepted | Stefan Coetzee | scripts/docs/eng/adr-NNNN-*.md, listed in Decision records | Numbered records (ADR-0018); the docs build checks numbers and supersede links |
| Build | Issue in status: ready; whoever picks it up assigns it and sets status: in progress | Commits on a branch from origin/main, pushed, a pull request open with the template; merges are squash-only, so the title starts with the issue id and the description says Closes #N | Stefan Coetzee | Commits, pull request | Agent sessions, sdlc:work-item, .github/pull_request_template.md |
| Review | Pull request open, Conformity checks passed on it | The change review is on the pull request: the comment from the change-review workflow, or, until CI has its key, the comment from the local runner review-pr.sh; Stefan Coetzee merges, or a session merges on his go | Stefan Coetzee | Pull request with the review and the merge | sdlc:change-review (in CI: .github/workflows/change-review.yml, advisory); the shared agent settings ask before gh pr merge (Agents in this repository) |
| Test | Code or a page changed | The gate dry run prints overall=pass; the docs build finishes without stopping | Stefan Coetzee | Local dry run output | Gate dry run, checks in scripts/build_docs.py (Docs tree) |
| Gate | Pull request to main (required check), then the push to main the merge makes | The Conformity checks job passes | Stefan Coetzee | Workflow run and its artifacts (90 days), conformity/latest.json in git | Conformity gate, .github/workflows/conformity.yml |
| Release | Gate passed | The Deploy to Pages job succeeded; the rollback path is known | Stefan Coetzee | Deploy run in the github-pages environment, changelog | Deploy pipeline, Roll back a deploy, sdlc:release |
| Observe | Deployed | The status page shows the site as operational and the Website deploys dashboard shows the run green | Stefan Coetzee | /inside/deploys.json, the Website deploys dashboard | Deploy exporter, Status page |
| Incident | A reader-visible symptom, a failed gate run, or an alert someone sees | Record resolved, write-up linked under postmortem, follow-up work as issues under tickets | Stefan Coetzee | incidents/YYYY-MM-DD-short-name.yml | Status page, On-call and escalation |
Follow-up work from an incident goes back to Request as issues, listed in the record's tickets field.
Gaps
Stages with no module or practice yet:
| Stage | What is missing | Fills it |
|---|---|---|
| RFC | No comment round before a decision; a record goes from proposed to accepted on the operator's call | decisions:rfc, or comments on the issue that links the proposed record |
| Review | No second person: every commit and every merge comes from one GitHub account, so GitHub cannot require an approval or tell a session's merge from Stefan's. The ask rule on gh pr merge holds only sessions started in this repository. The branch ruleset waits for the deploy key of the conformity bot | The ruleset per ADR-0029; a second account for reviews if one is ever needed |
| Test | No unit tests for the Python generators in scripts/, and no check after a deploy that the pages answer | sdlc:test-strategy |
| Build | Commit subjects do not name the issue or the decision, so a commit cannot be traced to its request | sdlc.traceability gate check on warn with since: 2026-10-11 |
| Release | No way to redeploy an earlier build: both jobs check out main. Rollback time is not measured | Owner decision; see Roll back a deploy |
| Observe | No SLO for the site, and no alert reaches a person (ADR-0017) | reliability:slo; the proposed routing in On-call and escalation |
Tickets
GitHub Issues on uncovertechtalent/machinebehavior.io is the system of record; the board is a read-only view of it. Labels, as set at triage by a maintainer:
| Group | Labels | Meaning |
|---|---|---|
| Type | type: feature, type: bug, type: chore, type: docs | What kind of work. An issue without a type label is untriaged and the board does not show its text |
| Area | area: inside, area: docs, area: map, area: gate, area: observability, area: legal, area: security, area: search | Which part of the platform. An issue can carry more than one |
| Priority | P0, P1, P2, P3 | P0 must close before anyone outside sees the platform; P3 is optional |
| Status | status: ready, status: in progress, status: blocked | The board column. No status label means Backlog; closed means Done |
The issue templates set no labels, so a new report stays untriaged until a maintainer reads it. Close an issue with a comment that links the commit. Issue text is public: no private addresses, host names, personal or financial details.
Commit subjects should name the issue (#123) or the decision (ADR-0007). Commits before 2026-10-11 do not.
Test strategy
| Level | What it covers | Where it runs |
|---|---|---|
| Unit | None yet for the generators in scripts/ | Nowhere |
| Build checks | scripts/build_docs.py stops on a malformed date, an unknown page type, an unknown doc: link, or decision records that disagree | Locally, when the docs are built |
| Gate | Blocking rule hits, placeholders, prediction hashes, site consistency, page labels, the vendored rule table | Locally as a dry run; in the workflow on every pull request to main and every push to main |
| Smoke | None after a deploy. The status page reads each site's /conformity/latest.json in the visitor's browser and shows a site that does not answer as an outage | Nowhere in the pipeline |
Before pushing a branch, run:
git fetch origin && git rebase origin/main
python3 .github/actions/conformity/run.py --root . --config conformity/site-tier.json --requirements conformity/requirements.json --out conformity --dry-run
Push the branch only when the dry run prints overall=pass. In a script, compare the value; a grep for overall= succeeds on a failing run too. The same checks run on the pull request before the merge. The one-line form matches the allow rule in the shared agent settings.
Release and rollback
- Changelog: changelog, generated by
scripts/build_changelog.pyfrom closed issues and commits, by day, grouped Added, Changed and Fixed. There are no version tags. - Rollback: Roll back a deploy.
- Feature flags: none in this repository.
Review in three layers
Each layer sees only the context it may see.
- CI review.
.github/workflows/change-review.ymlruns/sdlc:change-reviewfrom the CTO powersuit on each pull request from a branch of this repository and posts one comment, updated on each push. It reads the decision records, these docs,CLAUDE.md, the gate's README and config, /man/, /llms.txt and the linked issues first, so a recorded choice is a note ("intentional per ADR-NNNN"), not a finding. The model gets the pull request as files and has no shell, no network and no write token. The workflow is advisory: it reports a neutral check, never gates the deploy and is not a required check. "Block" findings become a failing check once the false-positive rate of the first reviews is known. - Review with private context, on the owner's machine only:
claude "/sdlc:change-review <n> kb"reads the private knowledge base and posts one comment that cites public files only. Nothing private goes into a workflow, a secret or a review bot. - CodeRabbit reads
.coderabbit.yaml: path instructions that point at the decision records and conventions, generated files filtered out, and no MCP knowledge source.
Until the CI review has its key, the local runner takes layer 1's place: review-pr.sh (CTO powersuit, plugins/sdlc/skills/change-review/) runs layers 1 and 2 together on the owner's machine, with the limits of the CI workflow, and posts one advisory comment per pull request. Every session runs it before it reports a pull request ready (CLAUDE.md, step 5).