stayfixed
A methodology harness for coding agents: a bug ledger that lives in the repository, a working memory whose index is rendered rather than written, guards that fail closed where the platform lets them, and an adoption state machine that runs each gate advisory until the repository has earned it. One plugin for Claude Code and Codex, one Python package with no runtime dependencies.
Pre-1.0. What ships: the memory store and its trust gate; the scaffolding engine that writes files into a repository; the guards over a shell call, a commit message and a test run; the bug ledger; the documentation and plan lints;
stayfixed setup, which configures a machine from a preset; the private overlay —overlay create,overlay init,overlay upgrade,overlay publish-template— andattach/detach, which bind a repository to it and unbind it again;stayfixed init, which writes a repository's footprint from the shipped project templates,stayfixed upgrade, which refreshes it, andstayfixed uninstall, which takes it back;stayfixed doctor, which reports on the result;stayfixed assess, which inventories what stands between a repository and enforcement;stayfixed gate, which judges a change against what its base branch enforces;stayfixed adopt, which enforces a repository's gates as each one passes; and the first stack profile,python, whose rules every agent is handed and whose checksstayfixed assessruns. The hooks file that wires all of it into a session ships too, so installing the plugin is enough to make the guards fire and the memory bundles arrive. The first skills ship with them, and so do two command groups meant for a machine rather than for you —hook, which dispatches one harness event, andrelease, whose three commands (check,notes,hashes) are this repository's own discipline. Not yet: the memory MCP server, a hold-the-line baseline, theuvxform of the gate, and adapters for Cursor or Hermes — each leaves this list in the change that ships it. The Quickstart shows the three keys that are enough to start a project by hand, whichinitreads as your answers — a run that writes the file itself writes[stayfixed] version,stateandagents, andprofilewhen the repository carries a shipped profile's markers or--profilenames one, beside[project] name,base_branchandrelease_branch, a[ci]table only when it has a released commit to pin or--no-ciasks for none, a[memory]table only when--memory-modeanswers it, and an[artifacts]table only when--localdoes. docs/cli.md is the reference; the command list below is held to the parser by a test, so it is complete for what ships.
What this is, and what it is not
Two kinds of tool already exist for working with a coding agent. A process layer such as superpowers tells the agent how to work — brainstorm, plan, test first, review. A spec layer such as spec-kit tells it what to build. Neither remembers what went wrong last time, and neither has a way to introduce rules into a repository that does not yet follow them.
This project looked through the community plugin marketplace on 2026-09-05 and counted about 2,300 plugins listed that day. That is one project's dated count rather than a survey with a method — no row in sources.md backs it, and it says what that look found, not what exists. What it did not find anywhere else is the three things stayfixed adds:
- A bug ledger as a first-class repository artifact — one file per bug, a generated index, a "what this evidence does not establish" line the tooling insists on, and skills that teach the agent how to read an entry.
- An enforcement state machine in which each gate runs advisory until the repository has
earned it.
stayfixed assesssays what stands in the way, andstayfixed adopt promoteenforces a gate once it passes. - A personal overlay that is itself a versioned plugin with its own upgrade manifest,
rather than a dotfiles sync.
stayfixed overlay createrenders one andstayfixed attachbinds a repository to it. It also declares the stayfixed it needs, in its plugin manifest;stayfixed doctorreads it and reports it when the stayfixed running is too old — red when this project keeps its notes in that overlay, a warning when it does not — and a session in a bound repository says so once at its start.
Two more practices ride along and are named as such: every assertion ships with the mutation that reddens it, and working memory is a routing table of hand-written lines, not a summary. The principles behind all of it, with dated sources and an honest note where the backing is thin, are in docs/methodology/README.md.
This is not a replacement for superpowers. stayfixed setup --preset recommended installs
it, and context7, on Claude Code — both ship in
Anthropic's own official marketplace, so setup needs no separate registration step for
either. Codex has no verified non-interactive marketplace source for either plugin (checked
against this project's own spike record and each plugin's own published install instructions,
2026-09-18): install superpowers and context7 by hand there if you use Codex, the same way
you would install any other Codex plugin — setup reports this as a note rather than guessing a
marketplace name (nothing is vendored on a guess). The adoption skill will delegate to
superpowers where it is present. The adoption skill that walks a plan with the agent ships in a
later package.
Install
Released as 0.2.0. Both commands below install that release. The plugin form takes the
tag, and uv tool install stayfixed resolves from PyPI:
As a Claude Code plugin:
/plugin marketplace add stayfixed/stayfixed@v0.2.0
/plugin install stayfixed@stayfixed-marketplace
As a command-line tool:
uv tool install stayfixed
In CI, a project calls the reusable workflow at a commit SHA;
docs/cli.md shows the caller stayfixed init writes.
Requirements: Python 3.11 or newer, and a POSIX system. Linux and macOS are supported and
tested; Windows is not. The containment this project is built on uses openat with
O_NOFOLLOW and O_DIRECTORY, which have no Windows equivalent.
Quickstart
Three keys in a stayfixed.toml at the root of a repository start a project; every other key
takes the recommended preset's default, and docs/cli.md lists all
of them, annotated.
[stayfixed]
version = "0.2.0"
[project]
name = "widget" # one lowercase path segment
[memory]
groups = ["developer"] # the preset names four; the store below has one
stayfixed init --yes writes that file for you — the name from origin, the base branch, the
agent surfaces this repository carries — along with the documentation skeleton the other
commands expect and, once there is a stayfixed release to pin, a CI workflow.
stayfixed init --questions shows each value it would take and where it came from, and a flag
on --yes replaces any of them. Read it before it runs; a stayfixed.toml you wrote yourself
is read as your answers rather than replaced:
stayfixed init --yes --dry-run # both plans, every file named, nothing written
stayfixed init --yes
The default memory mode keeps notes under .stayfixed/local/memory/, git-ignored, one
directory per group. Write one note and render the index:
mkdir -p .stayfixed/local/memory/developer
printf -- '---\nname: first-note\ndescription: "When to open this note"\n---\n\nThe note.\n' \
> .stayfixed/local/memory/developer/first-note.md
stayfixed memory index # renders .stayfixed/local/memory/MEMORY.md from the notes
stayfixed doctor # sixteen checks over this installation, one line; --json has the remedies
memory index will tell you the notes reach no session until you say
stayfixed memory trust --in-repo-memory once — that is the trust gate, and
The threat model, in one paragraph says why it exists.
Add .stayfixed/local/ to .gitignore if it is not there already.
The other commands in Commands expect more of a repository than those three keys
create: docs check wants an AGENTS.md, docs trail a docs/roadmap.md, bugs check a
ledger entry. stayfixed init --yes writes all of those, which is what it is for; in a
repository you would rather grow by hand, add each path as you start using the command that
reads it — docs/cli.md lists every default.
What it writes, and where
stayfixed writes files. Being specific about which is the point of this section.
| Path | What it is | Written by |
|---|---|---|
stayfixed.toml |
Your project's configuration, committed | stayfixed init, once — into a file you wrote, only a missing [stayfixed] version; you after |
docs/memory/ (configurable) |
The note store, in in-repo and overlay mode |
stayfixed memory index |
.stayfixed/local/memory/ |
The note store in local-only mode, the default — git-ignored |
stayfixed memory index |
<store>/MEMORY.md |
The rendered routing index. Generated — do not hand-edit | stayfixed memory index |
docs/bugs/ and docs/bug-reports.md (configurable) |
One file per bug, and the generated index over them | stayfixed init writes the empty index and docs/bugs/audits/README.md; stayfixed bugs new, bugs index and bugs renumber after |
docs/roadmap.md and docs/roadmap-history.md (configurable) |
The forward track and the closed phases; in the roadmap, docs trail owns only the listing between its two markers |
stayfixed init writes both files; stayfixed docs trail rewrites the listing |
docs/trail.toml (beside the roadmap) |
Which theme each design or plan document belongs to, and which are not plainly delivered. Read by docs trail, never written by it |
stayfixed init, once; you after |
AGENTS.md, CLAUDE.md, docs/architecture/, docs/adr/, docs/runbooks/, docs/specs/, docs/plans/, .github/workflows/stayfixed.yml |
The project footprint: the documents every other command reads, plus the pinned CI caller. Written by stayfixed init, recorded in the manifest; stayfixed upgrade refreshes what you have not touched, and stayfixed uninstall takes it back |
stayfixed init |
docs/stayfixed/rules/<profile>.md (configurable) and .claude/rules/stayfixed-<profile>.md |
The stack profile's rules, the one copy a project edits, and a path-scoped pointer at it for Claude Code (only when [stayfixed] agents lists claude) |
stayfixed init, when [stayfixed] profile is set |
.stayfixed/assessment.json |
The inventory stayfixed assess last wrote, format 1 — git-ignored |
stayfixed assess; stayfixed uninstall removes it |
the file --summary FILE names |
The gate's summary, appended; in CI the platform's job summary | stayfixed gate --summary FILE |
.stayfixed/manifest.json |
The ledger of every scaffolded artifact | the scaffold engine |
.stayfixed/local/artifacts/ |
The artifacts [artifacts] local keeps out of git, at the path each would have in the repository — git-ignored, never recorded in the manifest |
stayfixed init and stayfixed upgrade, when [artifacts] local lists them; stayfixed uninstall takes them back |
.stayfixed/local/artifacts.json |
The record of the bytes stayfixed last wrote under .stayfixed/local/artifacts/, so an unedited copy is refreshed or retired and an edited one is left. Git-ignored and never committed. Deleted, later runs judge a copy at its artifact's own place by what they render, and no longer find a copy left at an earlier place at all |
the scaffold engine; stayfixed uninstall removes it |
~/.config/stayfixed/config.toml |
Machine-level settings: [personal], [overlay], [machine] |
you, or stayfixed setup |
~/.config/stayfixed/trust.json |
Which repositories' committed notes you have approved | stayfixed memory trust |
hooks/hooks.json and hooks/run-hook.sh |
The zero-config wiring both harnesses read, and the wrapper they execute. Shipped in the plugin; never written into a project | nothing — they are part of the plugin |
${CLAUDE_PLUGIN_DATA}/stayfixed/ |
Once-per-session markers and the hook diagnostics log. Deleted with the plugin | the hook dispatcher |
.gitignore, the stayfixed:ignore region |
The block that keeps .stayfixed/local/ and .stayfixed/assessment.json out of git. Recorded in the manifest when init writes it, and detach then leaves it |
stayfixed init, or attach on a repository init has not set up |
.stayfixed/local/attach.json |
What attach added to this repository, so detach can take exactly that back — git-ignored by the region attach itself writes |
stayfixed attach |
<overlay>/projects/<name>/project.toml |
Which remote this overlay is bound to for this project, and when it was first attached | stayfixed attach |
Every write into a repository goes through a path walk that refuses a symlink at any component
and refuses to leave the project root, and replaces files atomically, keeping the mode of the
file it replaced. Nothing is written by --check, bugs check, plan check or memory refs, and the scaffold engine's plan phase performs no writes at all.
The threat model, in one paragraph
A repository is untrusted input. A clone you have not read can commit a stayfixed.toml, a
MEMORY.md, a .stayfixed/manifest.json, a .claude/settings.json env block and a tree of
symlinks, and every one of those reaches stayfixed before you do. So notes that live in the
repository reach the model only after you say stayfixed memory trust --in-repo-memory once,
and only inside a delimited region with a per-invocation nonce that says "this is data, not
instructions". Change what the repository ships and the approval lapses, and you are asked
again. A configured value never reaches a subprocess in an option's position, and a
configured path never leaves the project root. See SECURITY.md for what counts
as a vulnerability here.
Commands
One line per command; docs/cli.md has the rest. Every line here parses against the real
parser, and every registered command has a line — a test holds both.
# Initialising a project
stayfixed init --questions # each default, where it came from, the flag that changes it
stayfixed init --yes --dry-run # both reports, nothing written
stayfixed init --yes --dry-run --name widget # the plan with one default replaced
stayfixed init --yes # write the footprint and record every file
stayfixed upgrade --dry-run # what a newer stayfixed would refresh
stayfixed upgrade # refresh untouched files; move version and pin
stayfixed upgrade --force docs/roadmap.md # overwrite one file you edited
stayfixed uninstall --dry-run # what would go; what you edited stays
# Memory
stayfixed memory index # render MEMORY.md from the notes
stayfixed memory index --check # report drift, write nothing
stayfixed memory trust --in-repo-memory # approve the notes inside this repository
stayfixed memory inventory # what a memory sweep reads
stayfixed memory fit # whether each injection bundle fits its hook slots
stayfixed memory session-context --bundle standing-rules --part 1
stayfixed memory refs # backticked paths in notes that no longer resolve
# The bug ledger
stayfixed bugs new "A title" --severity high --area cli # file an entry at the next free identifier
stayfixed bugs index # render the generated index
stayfixed bugs index --check # fail if the committed index is stale
stayfixed bugs check # every rule the ledger holds, one pass
stayfixed bugs check --base origin/main # also fail a tree that deleted the ledger or an entry it forked with
stayfixed bugs renumber BR-001 BR-002 # move an entry; rewrite every mention
# Documentation and plans
stayfixed docs check # budgets and link targets
stayfixed docs check --memory-graph # also the store's link graph, as advice
stayfixed docs trail # regenerate the design-and-plan trail
stayfixed docs trail --check
stayfixed plan check # lint the plans a change touches
stayfixed plan check --base origin/main docs/plans/example.md
# Guards
stayfixed guard bg-cleanup # judge one Bash call, read as JSON on stdin
stayfixed commit check --range origin/main..HEAD # attribution lines in commit messages
stayfixed commit strip .git/COMMIT_EDITMSG # take the attribution block out of a message file
stayfixed test hygiene # the faults that make a red run unattributable
stayfixed test audit-entrypoints # tests that never exercise what they name
stayfixed test attribute --command "uv sync --locked && uv run pytest tests/x.py::t" # the change, or the environment: three runs, one verdict
# The private overlay
stayfixed overlay create --owner you --name stayfixed-private --local # render one here, no network call at all
stayfixed overlay create --owner you --name stayfixed-private --template # from your <owner>/stayfixed-overlay-template if you published one, else stayfixed's
stayfixed overlay init --owner you --root ../stayfixed-private # name it after you; install the secret scan
stayfixed overlay upgrade --root ../stayfixed-private --dry-run # what a release would refresh
stayfixed overlay publish-template --owner you # what it would create, mark and push; nothing leaves yet
stayfixed overlay publish-template --owner you --yes # publish the template repository from this checkout
# Binding a repository to the overlay
stayfixed attach --store ../stayfixed-private/projects/widget/memory --check # the binding and the permission diff, writing nothing
stayfixed attach --store ../stayfixed-private/projects/widget/memory --yes # merge the diff you just read, and link the notes in
stayfixed attach --store ../stayfixed-private/projects/widget/memory --trust-remote # record this remote although the overlay recorded another
stayfixed detach # remove what attach added; the binding record stays
# Machine setup
stayfixed setup --preset recommended # the machine configuration, deny rules and preset plugins
stayfixed setup --preset recommended --overlay ../stayfixed-private # record an existing overlay; no --yes needed
stayfixed setup --preset recommended --overlay create:you/stayfixed-private --yes # create one on GitHub; --yes is the consent
stayfixed setup --preset recommended --settings ~/dotfiles/claude/settings.json # a linked settings file, written where it really is
stayfixed setup --git-hooks # install the commit-message hook into this repository
stayfixed setup --git-hooks --uninstall # remove it; restore the hook it chained to
# Assessing a repository
stayfixed assess # every gate and probe; the whole inventory in .stayfixed/assessment.json
stayfixed assess --builtin # the same, running none of the repository's own gate commands
stayfixed gate # judge stayfixed.toml against the base, then run every configured gate
stayfixed gate --only docs --only config # a few of them; config is the configuration check
stayfixed adopt begin docs/plans/2026-09-23-stayfixed-adoption.md # check the adoption plan; the project is adopting
stayfixed adopt promote docs # enforce one gate, if it passes now
stayfixed adopt promote # enforce every gate that passes now; name the rest
stayfixed adopt promote --builtin # the same, running none of the repository's own gate commands
# Diagnosing an installation
stayfixed doctor # sixteen checks over this installation, one line
stayfixed doctor --json # every check with its status, detail and remedy
# Internal and release
stayfixed hook SessionStart # dispatch one harness hook event (internal)
stayfixed release check # one version everywhere (this repository's own)
stayfixed release check --tag v1.2.3 # and the tag agrees, with nothing left in changelog.d
stayfixed release notes --version 1.2.3 --draft # render the section towncrier would write
stayfixed release notes --version 1.2.3 # assemble CHANGELOG.md from changelog.d
stayfixed release hashes --check # the shipped files still match the release record
Every memory, bugs, docs and plan command, and assess, gate and adopt, takes
--root (default: the current directory) and --machine (read a machine configuration file
other than the default); memory commands and docs check take --store as well.
stayfixed overlay is the exception: its --root names the directory an overlay is created in
or the overlay itself, not a project root, and it reads no stayfixed.toml. --json is accepted
anywhere and prints one machine-readable object instead of one line.
The commands that report a list of
findings — bugs check, docs check, memory refs, plan check, test audit-entrypoints —
all spell it findings, whatever their summary line calls them; every other command's keys
are its own and are listed with it in docs/cli.md.
Exit codes are the same everywhere: 0 success, 1 findings, 2 a refusal or an
internal error. A caller must never read 2 as permission. Two commands are deliberately
outside that rule. stayfixed test audit-entrypoints exits 0 even when it has findings,
and lists them under --json, because its candidates are for triage and gating on them
is not shipped yet. And stayfixed hook refuses with 2 on an internal
error only for PreToolUse, the one event a harness blocks on; everywhere else it degrades
open with 0, because on UserPromptSubmit an exit 2 erases what you typed and a bug in
stayfixed must not cost you that. A handler's own deny is a decision, not a breakage, and
refuses on every event. docs/cli.md states it per event.
stayfixed memory trust
The one command with a consequence worth stating twice. It records a hash of everything the
store yields — every note, MEMORY.md, and the repository-controlled configuration that is
rendered into it — against the store's absolute path, in ~/.config/stayfixed/trust.json.
You are saying: I have read what this repository committed under its memory directory, and it
may reach the model as data. Any later change to any of those files makes the hash disagree
and the approval lapse until you look again and re-run it. A store whose notes are yours —
overlay mode, where the notes live in your own machine-level overlay — needs no approval, and
recording one for it is inert.
Memory, in one page
A note is a Markdown file with frontmatter, under a group directory in the store:
---
name: prefer-uv
description: This project uses uv, never pip
index: adding a dependency → use uv add
metadata:
type: project
startup: 1
---
Run `uv add`, not `pip install`. The lockfile is committed and CI runs `uv sync --locked`.
index:is the routing line — the trigger and the answer, not a summary.memory indexwrites one from the description when it is missing and reports it as provisional.metadata.startupflags the note as a standing rule, injected in full at session start and ranked by that number.metadata.as_ofdates a volatile note; one pastvolatile_ttl_daysis injected with a visible warning rather than dropped.group:files a note under a sub-heading inside its section.
MEMORY.md is rendered from the notes and is not a file you edit — curation lives in each
note's index: line. A second writer appending entries to MEMORY.md is expected, and
memory index harvests those back into the notes before it re-renders.
memory.mode decides where the store is. local-only (the default — .stayfixed/local/memory,
git-ignored) and in-repo (committed) both put the notes inside the repository, so both are
behind the trust gate; overlay (a directory of links into a machine-level overlay shared across
your projects) puts them outside it, and notes that are yours need no approval. The gate keys on
where a note actually sits, never on what the repository's own stayfixed.toml declares — a clone
that wrote mode = "local-only" would otherwise gate itself.
The bug ledger, in one paragraph
An entry is one file, docs/bugs/BR-001.md by default, with flat frontmatter — id,
status from open | partial | fixed | rejected | void, severity from high | medium | low, area, related — and a body whose **What this evidence does not establish:** line
must be filled in for the severities the project names. bugs index renders the index as a
pure function of the entries and refuses to overwrite one it did not generate; bugs check
finds identifiers in the code with no entry behind them, entries whose evidence line is still
the template's, and citations that do not resolve. The identifier prefix is one configuration
key. The close-bug skill walks the closing of an entry through these commands.
Skills and agents
skills/ ships two ported skills (close-bug, memory-sweep), six authored ones
(file-bug, sweep-defect-class, review-plan-three-lenses, attribute-failure,
run-correctness-audit, retro-to-guard), and thin wrappers for the commands the CLI
registers; agents/ ships a read-only code-navigator. Every skill is written in action
language — never a harness tool's name — with the per-harness mapping in
skills/README.md, and every stayfixed … invocation in a skill is
parsed against the real parser by a test. A wrapper written ahead of its command is listed in
that test until the command ships; none is today.
Contributing
See CONTRIBUTING.md — it states the three rules a change here has to satisfy, which are not obvious from the code. Security reports go through SECURITY.md, never a public issue.
License
MIT. See LICENSE.
Metadata
Release files for stayfixed 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stayfixed-0.2.0.tar.gz | 2.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stayfixed-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.9 MB
Release files / stayfixed-0.2.0.tar.gz
| Download URL | stayfixed-0.2.0.tar.gz |
|---|---|
| Size | 2.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2835c314a807b426ae530b0be64ed4a14bb30b3daf326cfeee3fca284080a092
|
|
BLAKE2b-256 checksum How to use checksums |
67dd23831a1fef0441919e4649aa72a01cfa641ab0c7a1173b92b8c736e51653
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / stayfixed-0.2.0-py3-none-any.whl
| Download URL | stayfixed-0.2.0-py3-none-any.whl |
|---|---|
| Size | 654.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ad85c75bc4f897f1a8ee619e8aaef4bff482cdac959088d49b8a14b2a6829aef
|
|
BLAKE2b-256 checksum How to use checksums |
faf44ed032577b45264a612ff36e2dea374ed414703fc7edbf875c53524ac5b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log