stayfixed 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stayfixed-0.1.0/.claude-plugin/marketplace.json +12 -0
- stayfixed-0.1.0/.claude-plugin/plugin.json +29 -0
- stayfixed-0.1.0/.codex-plugin/plugin.json +14 -0
- stayfixed-0.1.0/CHANGELOG.md +373 -0
- stayfixed-0.1.0/CODE_OF_CONDUCT.md +38 -0
- stayfixed-0.1.0/CONTRIBUTING.md +311 -0
- stayfixed-0.1.0/LICENSE +21 -0
- stayfixed-0.1.0/PKG-INFO +421 -0
- stayfixed-0.1.0/README.md +395 -0
- stayfixed-0.1.0/RELEASING.md +349 -0
- stayfixed-0.1.0/SECURITY.md +74 -0
- stayfixed-0.1.0/agents/code-navigator.md +56 -0
- stayfixed-0.1.0/changelog.d/.gitkeep +0 -0
- stayfixed-0.1.0/docs/cli.md +2921 -0
- stayfixed-0.1.0/docs/methodology/README.md +66 -0
- stayfixed-0.1.0/docs/methodology/principles.md +250 -0
- stayfixed-0.1.0/docs/methodology/sources.md +40 -0
- stayfixed-0.1.0/docs/plans/2026-09-05-agent-harness-foundation.md +2672 -0
- stayfixed-0.1.0/docs/plans/2026-09-05-agent-harness-p0-spikes.md +2715 -0
- stayfixed-0.1.0/docs/plans/2026-09-12-memory-engine.md +4258 -0
- stayfixed-0.1.0/docs/plans/2026-09-12-scaffold.md +3157 -0
- stayfixed-0.1.0/docs/plans/2026-09-15-guards.md +4144 -0
- stayfixed-0.1.0/docs/plans/2026-09-16-wave-2-closure-ledger-docs-skills.md +6150 -0
- stayfixed-0.1.0/docs/plans/2026-09-16-wave-2-closure-readme-notes.md +2434 -0
- stayfixed-0.1.0/docs/plans/2026-09-17-wave-3-install-path.md +3391 -0
- stayfixed-0.1.0/docs/plans/2026-09-19-wave-3-closure.md +5610 -0
- stayfixed-0.1.0/docs/plans/2026-09-28-rename-stayfixed.md +385 -0
- stayfixed-0.1.0/docs/plans/README.md +14 -0
- stayfixed-0.1.0/hooks/hashes.json +8 -0
- stayfixed-0.1.0/hooks/hooks.json +101 -0
- stayfixed-0.1.0/hooks/run-hook.sh +323 -0
- stayfixed-0.1.0/mutations.toml +12504 -0
- stayfixed-0.1.0/pyproject.toml +165 -0
- stayfixed-0.1.0/pyproject.toml.orig +160 -0
- stayfixed-0.1.0/scripts/check_artifacts.py +132 -0
- stayfixed-0.1.0/scripts/mutation_oracle.py +935 -0
- stayfixed-0.1.0/scripts/smoke_exfiltration.py +413 -0
- stayfixed-0.1.0/scripts/smoke_hooks.py +377 -0
- stayfixed-0.1.0/scripts/stayfixed +18 -0
- stayfixed-0.1.0/skills/README.md +32 -0
- stayfixed-0.1.0/skills/attach/SKILL.md +31 -0
- stayfixed-0.1.0/skills/attribute-failure/SKILL.md +29 -0
- stayfixed-0.1.0/skills/attribute-failure/references/baselines.md +32 -0
- stayfixed-0.1.0/skills/close-bug/SKILL.md +21 -0
- stayfixed-0.1.0/skills/doctor/SKILL.md +35 -0
- stayfixed-0.1.0/skills/file-bug/SKILL.md +29 -0
- stayfixed-0.1.0/skills/init/SKILL.md +58 -0
- stayfixed-0.1.0/skills/init/references/adoption.md +53 -0
- stayfixed-0.1.0/skills/init/references/asking.md +25 -0
- stayfixed-0.1.0/skills/memory-sweep/SKILL.md +31 -0
- stayfixed-0.1.0/skills/memory-sweep/references/protocol.md +54 -0
- stayfixed-0.1.0/skills/retro-to-guard/SKILL.md +35 -0
- stayfixed-0.1.0/skills/review-plan-three-lenses/SKILL.md +35 -0
- stayfixed-0.1.0/skills/review-plan-three-lenses/references/lenses.md +26 -0
- stayfixed-0.1.0/skills/run-correctness-audit/SKILL.md +32 -0
- stayfixed-0.1.0/skills/run-correctness-audit/references/protocol.md +26 -0
- stayfixed-0.1.0/skills/setup/SKILL.md +44 -0
- stayfixed-0.1.0/skills/sweep-defect-class/SKILL.md +30 -0
- stayfixed-0.1.0/skills/sweep-defect-class/references/vacuous-oracles.md +31 -0
- stayfixed-0.1.0/skills/uninstall/SKILL.md +46 -0
- stayfixed-0.1.0/skills/upgrade/SKILL.md +59 -0
- stayfixed-0.1.0/src/stayfixed/__init__.py +9 -0
- stayfixed-0.1.0/src/stayfixed/__main__.py +3 -0
- stayfixed-0.1.0/src/stayfixed/areas.py +56 -0
- stayfixed-0.1.0/src/stayfixed/assess/__init__.py +1 -0
- stayfixed-0.1.0/src/stayfixed/assess/assessment.py +206 -0
- stayfixed-0.1.0/src/stayfixed/assess/commands.py +385 -0
- stayfixed-0.1.0/src/stayfixed/assess/gates.py +317 -0
- stayfixed-0.1.0/src/stayfixed/assess/model.py +48 -0
- stayfixed-0.1.0/src/stayfixed/assess/probes.py +704 -0
- stayfixed-0.1.0/src/stayfixed/assess/report.py +211 -0
- stayfixed-0.1.0/src/stayfixed/assess/rule.py +426 -0
- stayfixed-0.1.0/src/stayfixed/assess/state.py +265 -0
- stayfixed-0.1.0/src/stayfixed/assess/tracked.py +321 -0
- stayfixed-0.1.0/src/stayfixed/attach/__init__.py +7 -0
- stayfixed-0.1.0/src/stayfixed/attach/api.py +68 -0
- stayfixed-0.1.0/src/stayfixed/attach/binding.py +231 -0
- stayfixed-0.1.0/src/stayfixed/attach/commands.py +176 -0
- stayfixed-0.1.0/src/stayfixed/attach/hooks.py +225 -0
- stayfixed-0.1.0/src/stayfixed/attach/permissions.py +332 -0
- stayfixed-0.1.0/src/stayfixed/attach/write.py +1257 -0
- stayfixed-0.1.0/src/stayfixed/cli.py +183 -0
- stayfixed-0.1.0/src/stayfixed/command.py +91 -0
- stayfixed-0.1.0/src/stayfixed/config/__init__.py +5 -0
- stayfixed-0.1.0/src/stayfixed/config/layout.py +60 -0
- stayfixed-0.1.0/src/stayfixed/config/loader.py +600 -0
- stayfixed-0.1.0/src/stayfixed/config/machine.py +57 -0
- stayfixed-0.1.0/src/stayfixed/config/owned.py +157 -0
- stayfixed-0.1.0/src/stayfixed/config/paths.py +165 -0
- stayfixed-0.1.0/src/stayfixed/config/schema.py +263 -0
- stayfixed-0.1.0/src/stayfixed/docs/__init__.py +5 -0
- stayfixed-0.1.0/src/stayfixed/docs/api.py +88 -0
- stayfixed-0.1.0/src/stayfixed/docs/commands.py +181 -0
- stayfixed-0.1.0/src/stayfixed/docs/graph.py +51 -0
- stayfixed-0.1.0/src/stayfixed/docs/hygiene.py +207 -0
- stayfixed-0.1.0/src/stayfixed/docs/plans.py +413 -0
- stayfixed-0.1.0/src/stayfixed/docs/themes.py +153 -0
- stayfixed-0.1.0/src/stayfixed/docs/trail.py +402 -0
- stayfixed-0.1.0/src/stayfixed/doctor/__init__.py +37 -0
- stayfixed-0.1.0/src/stayfixed/doctor/api.py +36 -0
- stayfixed-0.1.0/src/stayfixed/doctor/checks.py +1806 -0
- stayfixed-0.1.0/src/stayfixed/doctor/commands.py +113 -0
- stayfixed-0.1.0/src/stayfixed/errors.py +13 -0
- stayfixed-0.1.0/src/stayfixed/findings.py +66 -0
- stayfixed-0.1.0/src/stayfixed/fsops.py +448 -0
- stayfixed-0.1.0/src/stayfixed/gitenv.py +272 -0
- stayfixed-0.1.0/src/stayfixed/guards/__init__.py +5 -0
- stayfixed-0.1.0/src/stayfixed/guards/api.py +57 -0
- stayfixed-0.1.0/src/stayfixed/guards/attribute.py +203 -0
- stayfixed-0.1.0/src/stayfixed/guards/audit.py +562 -0
- stayfixed-0.1.0/src/stayfixed/guards/bashscan.py +823 -0
- stayfixed-0.1.0/src/stayfixed/guards/bgcleanup.py +739 -0
- stayfixed-0.1.0/src/stayfixed/guards/commands.py +340 -0
- stayfixed-0.1.0/src/stayfixed/guards/commit.py +488 -0
- stayfixed-0.1.0/src/stayfixed/guards/githooks.py +180 -0
- stayfixed-0.1.0/src/stayfixed/guards/hooks.py +111 -0
- stayfixed-0.1.0/src/stayfixed/guards/hygiene.py +253 -0
- stayfixed-0.1.0/src/stayfixed/guards/roots.py +69 -0
- stayfixed-0.1.0/src/stayfixed/harnesses.py +88 -0
- stayfixed-0.1.0/src/stayfixed/hooks/__init__.py +1 -0
- stayfixed-0.1.0/src/stayfixed/hooks/api.py +178 -0
- stayfixed-0.1.0/src/stayfixed/hooks/commands.py +97 -0
- stayfixed-0.1.0/src/stayfixed/hooks/dispatch.py +295 -0
- stayfixed-0.1.0/src/stayfixed/hooks/policy.py +29 -0
- stayfixed-0.1.0/src/stayfixed/hooks/registry.py +54 -0
- stayfixed-0.1.0/src/stayfixed/hooks/sink.py +209 -0
- stayfixed-0.1.0/src/stayfixed/identifiers.py +80 -0
- stayfixed-0.1.0/src/stayfixed/ledger/__init__.py +6 -0
- stayfixed-0.1.0/src/stayfixed/ledger/api.py +62 -0
- stayfixed-0.1.0/src/stayfixed/ledger/check.py +368 -0
- stayfixed-0.1.0/src/stayfixed/ledger/commands.py +136 -0
- stayfixed-0.1.0/src/stayfixed/ledger/entries.py +307 -0
- stayfixed-0.1.0/src/stayfixed/ledger/index.py +281 -0
- stayfixed-0.1.0/src/stayfixed/ledger/scan.py +307 -0
- stayfixed-0.1.0/src/stayfixed/ledger/write.py +363 -0
- stayfixed-0.1.0/src/stayfixed/memory/__init__.py +1 -0
- stayfixed-0.1.0/src/stayfixed/memory/api.py +164 -0
- stayfixed-0.1.0/src/stayfixed/memory/bundles.py +326 -0
- stayfixed-0.1.0/src/stayfixed/memory/commands.py +488 -0
- stayfixed-0.1.0/src/stayfixed/memory/hooks.py +126 -0
- stayfixed-0.1.0/src/stayfixed/memory/index.py +547 -0
- stayfixed-0.1.0/src/stayfixed/memory/inventory.py +77 -0
- stayfixed-0.1.0/src/stayfixed/memory/notes.py +447 -0
- stayfixed-0.1.0/src/stayfixed/memory/refs.py +173 -0
- stayfixed-0.1.0/src/stayfixed/memory/store.py +572 -0
- stayfixed-0.1.0/src/stayfixed/memory/trust.py +381 -0
- stayfixed-0.1.0/src/stayfixed/memory/worktree.py +647 -0
- stayfixed-0.1.0/src/stayfixed/overlay/__init__.py +1 -0
- stayfixed-0.1.0/src/stayfixed/overlay/api.py +87 -0
- stayfixed-0.1.0/src/stayfixed/overlay/commands.py +155 -0
- stayfixed-0.1.0/src/stayfixed/overlay/create.py +373 -0
- stayfixed-0.1.0/src/stayfixed/overlay/identity.py +108 -0
- stayfixed-0.1.0/src/stayfixed/overlay/layout.py +69 -0
- stayfixed-0.1.0/src/stayfixed/overlay/publish.py +329 -0
- stayfixed-0.1.0/src/stayfixed/overlay/requires.py +127 -0
- stayfixed-0.1.0/src/stayfixed/overlay/sync.py +37 -0
- stayfixed-0.1.0/src/stayfixed/overlay/template.py +80 -0
- stayfixed-0.1.0/src/stayfixed/overlay/upgrade.py +100 -0
- stayfixed-0.1.0/src/stayfixed/presets/__init__.py +54 -0
- stayfixed-0.1.0/src/stayfixed/presets/recommended.toml +184 -0
- stayfixed-0.1.0/src/stayfixed/printed.py +61 -0
- stayfixed-0.1.0/src/stayfixed/profiles/__init__.py +71 -0
- stayfixed-0.1.0/src/stayfixed/profiles/evaluate.py +248 -0
- stayfixed-0.1.0/src/stayfixed/profiles/model.py +197 -0
- stayfixed-0.1.0/src/stayfixed/profiles/python/profile.toml +129 -0
- stayfixed-0.1.0/src/stayfixed/profiles/python/rules.md +52 -0
- stayfixed-0.1.0/src/stayfixed/project/__init__.py +8 -0
- stayfixed-0.1.0/src/stayfixed/project/api.py +53 -0
- stayfixed-0.1.0/src/stayfixed/project/commands.py +463 -0
- stayfixed-0.1.0/src/stayfixed/project/detect.py +198 -0
- stayfixed-0.1.0/src/stayfixed/project/footprint.py +255 -0
- stayfixed-0.1.0/src/stayfixed/project/ignored.py +151 -0
- stayfixed-0.1.0/src/stayfixed/project/init.py +431 -0
- stayfixed-0.1.0/src/stayfixed/project/layout.py +25 -0
- stayfixed-0.1.0/src/stayfixed/project/questions.py +168 -0
- stayfixed-0.1.0/src/stayfixed/project/rewrite.py +62 -0
- stayfixed-0.1.0/src/stayfixed/project/templates.py +644 -0
- stayfixed-0.1.0/src/stayfixed/project/uninstall.py +442 -0
- stayfixed-0.1.0/src/stayfixed/project/upgrade.py +243 -0
- stayfixed-0.1.0/src/stayfixed/prose.py +95 -0
- stayfixed-0.1.0/src/stayfixed/release/__init__.py +1 -0
- stayfixed-0.1.0/src/stayfixed/release/api.py +45 -0
- stayfixed-0.1.0/src/stayfixed/release/commands.py +83 -0
- stayfixed-0.1.0/src/stayfixed/release/hashes.py +133 -0
- stayfixed-0.1.0/src/stayfixed/release/notes.py +33 -0
- stayfixed-0.1.0/src/stayfixed/release/pins.py +75 -0
- stayfixed-0.1.0/src/stayfixed/release/versions.py +239 -0
- stayfixed-0.1.0/src/stayfixed/result.py +13 -0
- stayfixed-0.1.0/src/stayfixed/runner.py +152 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/__init__.py +97 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/engine.py +715 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/entries.py +168 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/local.py +182 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/manifest.py +182 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/model.py +94 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/regions.py +108 -0
- stayfixed-0.1.0/src/stayfixed/scaffold/report.py +59 -0
- stayfixed-0.1.0/src/stayfixed/setup/__init__.py +7 -0
- stayfixed-0.1.0/src/stayfixed/setup/api.py +40 -0
- stayfixed-0.1.0/src/stayfixed/setup/commands.py +189 -0
- stayfixed-0.1.0/src/stayfixed/setup/machine.py +176 -0
- stayfixed-0.1.0/src/stayfixed/setup/run.py +954 -0
- stayfixed-0.1.0/src/stayfixed/templates/__init__.py +23 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.claude-plugin/marketplace.json +11 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.claude-plugin/plugin.json +8 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.codex-plugin/plugin.json +10 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.github/dependabot.yml +15 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.github/workflows/scan.yml +29 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.gitignore +6 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/.pre-commit-config.yaml +8 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/README.md +58 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/common/claude/hooks.json +3 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/common/claude/permissions.json +3 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/common/codex/common.rules +2 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/common/memory/README.md +11 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/common/rules/README.md +12 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/hooks/hooks.json +3 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/projects/README.md +11 -0
- stayfixed-0.1.0/src/stayfixed/templates/overlay/skills/attach/SKILL.md +20 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/adr-template.md +22 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/agents-region.md +8 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/agents-skeleton.md +9 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/audits-readme.md +9 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/bug-reports-runbook.md +14 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/claude.md +1 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/documentation.md +26 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/gitkeep +0 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/roadmap-history.md +3 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/roadmap.md +27 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/stayfixed.yml +34 -0
- stayfixed-0.1.0/src/stayfixed/templates/project/trail.toml +9 -0
- stayfixed-0.1.0/src/stayfixed/tomlout.py +190 -0
- stayfixed-0.1.0/tests/__init__.py +0 -0
- stayfixed-0.1.0/tests/assess/__init__.py +0 -0
- stayfixed-0.1.0/tests/assess/baserepo.py +54 -0
- stayfixed-0.1.0/tests/assess/smoke.py +35 -0
- stayfixed-0.1.0/tests/assess/test_assessment.py +138 -0
- stayfixed-0.1.0/tests/assess/test_command.py +268 -0
- stayfixed-0.1.0/tests/assess/test_gate_command.py +967 -0
- stayfixed-0.1.0/tests/assess/test_gates.py +484 -0
- stayfixed-0.1.0/tests/assess/test_neutral_rows.py +68 -0
- stayfixed-0.1.0/tests/assess/test_principles.py +36 -0
- stayfixed-0.1.0/tests/assess/test_probes.py +1106 -0
- stayfixed-0.1.0/tests/assess/test_read_base.py +331 -0
- stayfixed-0.1.0/tests/assess/test_report.py +231 -0
- stayfixed-0.1.0/tests/assess/test_rule.py +437 -0
- stayfixed-0.1.0/tests/assess/test_state.py +800 -0
- stayfixed-0.1.0/tests/assess/test_tracked.py +701 -0
- stayfixed-0.1.0/tests/attach/__init__.py +0 -0
- stayfixed-0.1.0/tests/attach/test_binding.py +473 -0
- stayfixed-0.1.0/tests/attach/test_commands.py +394 -0
- stayfixed-0.1.0/tests/attach/test_detach.py +662 -0
- stayfixed-0.1.0/tests/attach/test_hooks.py +482 -0
- stayfixed-0.1.0/tests/attach/test_links.py +406 -0
- stayfixed-0.1.0/tests/attach/test_surface.py +47 -0
- stayfixed-0.1.0/tests/attach/test_write.py +1143 -0
- stayfixed-0.1.0/tests/cli.py +32 -0
- stayfixed-0.1.0/tests/config/__init__.py +0 -0
- stayfixed-0.1.0/tests/config/test_deep_toml.py +130 -0
- stayfixed-0.1.0/tests/config/test_layout.py +53 -0
- stayfixed-0.1.0/tests/config/test_loader.py +757 -0
- stayfixed-0.1.0/tests/config/test_machine.py +61 -0
- stayfixed-0.1.0/tests/config/test_owned.py +206 -0
- stayfixed-0.1.0/tests/config/test_paths.py +394 -0
- stayfixed-0.1.0/tests/config/test_schema.py +29 -0
- stayfixed-0.1.0/tests/config/test_symlinked_document.py +91 -0
- stayfixed-0.1.0/tests/conftest.py +75 -0
- stayfixed-0.1.0/tests/crafted.py +19 -0
- stayfixed-0.1.0/tests/docs/__init__.py +0 -0
- stayfixed-0.1.0/tests/docs/test_commands.py +369 -0
- stayfixed-0.1.0/tests/docs/test_graph.py +93 -0
- stayfixed-0.1.0/tests/docs/test_hygiene.py +226 -0
- stayfixed-0.1.0/tests/docs/test_plans.py +589 -0
- stayfixed-0.1.0/tests/docs/test_prose.py +70 -0
- stayfixed-0.1.0/tests/docs/test_surface.py +43 -0
- stayfixed-0.1.0/tests/docs/test_themes.py +123 -0
- stayfixed-0.1.0/tests/docs/test_trail.py +624 -0
- stayfixed-0.1.0/tests/doctor/__init__.py +0 -0
- stayfixed-0.1.0/tests/doctor/test_checks.py +2478 -0
- stayfixed-0.1.0/tests/doctor/test_command.py +173 -0
- stayfixed-0.1.0/tests/doctor/test_surface.py +34 -0
- stayfixed-0.1.0/tests/fixtures/hostile-project/.claude/settings.json +6 -0
- stayfixed-0.1.0/tests/fixtures/hostile-project/README.md +21 -0
- stayfixed-0.1.0/tests/fixtures/hostile-project/docs/memory/MEMORY.md +3 -0
- stayfixed-0.1.0/tests/fixtures/hostile-project/docs/memory/developer/canary.md +9 -0
- stayfixed-0.1.0/tests/fixtures/hostile-project/stayfixed.toml +14 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/.gitignore +5 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/.stayfixed/manifest.json +78 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/AGENTS.md +19 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/CLAUDE.md +1 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/README.md +5 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/adr/0000-template.md +22 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/adr/README.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/architecture/README.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/architecture/documentation.md +26 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/bug-reports.md +31 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/bugs/BR-001.md +27 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/bugs/audits/README.md +9 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/memory/MEMORY.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/memory/developer/house-style.md +11 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/memory/volatile/in-flight.md +10 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/plans/.gitkeep +0 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/plans/2026-09-19-the-fixtures-own-plan.md +29 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/plans/README.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/roadmap-history.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/roadmap.md +25 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/runbooks/bug-reports.md +7 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/specs/.gitkeep +0 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/specs/README.md +4 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/docs/trail.toml +13 -0
- stayfixed-0.1.0/tests/fixtures/smoke-project/stayfixed.toml +21 -0
- stayfixed-0.1.0/tests/floor.py +39 -0
- stayfixed-0.1.0/tests/gitfixture.py +224 -0
- stayfixed-0.1.0/tests/guards/__init__.py +0 -0
- stayfixed-0.1.0/tests/guards/test_attribute.py +528 -0
- stayfixed-0.1.0/tests/guards/test_audit.py +392 -0
- stayfixed-0.1.0/tests/guards/test_bashscan.py +441 -0
- stayfixed-0.1.0/tests/guards/test_bgcleanup.py +718 -0
- stayfixed-0.1.0/tests/guards/test_commands.py +615 -0
- stayfixed-0.1.0/tests/guards/test_commit.py +713 -0
- stayfixed-0.1.0/tests/guards/test_githooks.py +398 -0
- stayfixed-0.1.0/tests/guards/test_hooks.py +338 -0
- stayfixed-0.1.0/tests/guards/test_hygiene.py +408 -0
- stayfixed-0.1.0/tests/guards/test_surface.py +39 -0
- stayfixed-0.1.0/tests/hooks/__init__.py +0 -0
- stayfixed-0.1.0/tests/hooks/test_dispatch.py +606 -0
- stayfixed-0.1.0/tests/hooks/test_hook_command.py +240 -0
- stayfixed-0.1.0/tests/hooks/test_hooks_json.py +187 -0
- stayfixed-0.1.0/tests/hooks/test_registry.py +87 -0
- stayfixed-0.1.0/tests/hooks/test_sink.py +194 -0
- stayfixed-0.1.0/tests/hooks/test_wrapper.py +794 -0
- stayfixed-0.1.0/tests/ledger/__init__.py +0 -0
- stayfixed-0.1.0/tests/ledger/test_check.py +722 -0
- stayfixed-0.1.0/tests/ledger/test_commands.py +349 -0
- stayfixed-0.1.0/tests/ledger/test_entries.py +272 -0
- stayfixed-0.1.0/tests/ledger/test_index.py +322 -0
- stayfixed-0.1.0/tests/ledger/test_scan.py +251 -0
- stayfixed-0.1.0/tests/ledger/test_surface.py +31 -0
- stayfixed-0.1.0/tests/ledger/test_write.py +565 -0
- stayfixed-0.1.0/tests/memory/__init__.py +0 -0
- stayfixed-0.1.0/tests/memory/test_bundles.py +656 -0
- stayfixed-0.1.0/tests/memory/test_commands.py +998 -0
- stayfixed-0.1.0/tests/memory/test_hooks.py +249 -0
- stayfixed-0.1.0/tests/memory/test_index.py +754 -0
- stayfixed-0.1.0/tests/memory/test_notes.py +415 -0
- stayfixed-0.1.0/tests/memory/test_refs.py +450 -0
- stayfixed-0.1.0/tests/memory/test_store.py +628 -0
- stayfixed-0.1.0/tests/memory/test_surface.py +75 -0
- stayfixed-0.1.0/tests/memory/test_trust.py +419 -0
- stayfixed-0.1.0/tests/memory/test_worktree.py +1226 -0
- stayfixed-0.1.0/tests/overlay/__init__.py +0 -0
- stayfixed-0.1.0/tests/overlay/test_create.py +338 -0
- stayfixed-0.1.0/tests/overlay/test_publish.py +333 -0
- stayfixed-0.1.0/tests/overlay/test_requires.py +154 -0
- stayfixed-0.1.0/tests/overlay/test_surface.py +60 -0
- stayfixed-0.1.0/tests/overlay/test_sync.py +62 -0
- stayfixed-0.1.0/tests/overlay/test_template.py +375 -0
- stayfixed-0.1.0/tests/overlay/test_upgrade.py +138 -0
- stayfixed-0.1.0/tests/profiles/__init__.py +0 -0
- stayfixed-0.1.0/tests/profiles/test_evaluate.py +189 -0
- stayfixed-0.1.0/tests/profiles/test_model.py +152 -0
- stayfixed-0.1.0/tests/profiles/test_python.py +190 -0
- stayfixed-0.1.0/tests/project/__init__.py +0 -0
- stayfixed-0.1.0/tests/project/repos.py +95 -0
- stayfixed-0.1.0/tests/project/test_answer_flags.py +197 -0
- stayfixed-0.1.0/tests/project/test_command.py +715 -0
- stayfixed-0.1.0/tests/project/test_detect.py +250 -0
- stayfixed-0.1.0/tests/project/test_fixture.py +119 -0
- stayfixed-0.1.0/tests/project/test_footprint_commands.py +321 -0
- stayfixed-0.1.0/tests/project/test_footprint_invariant.py +576 -0
- stayfixed-0.1.0/tests/project/test_gates.py +104 -0
- stayfixed-0.1.0/tests/project/test_ignored.py +373 -0
- stayfixed-0.1.0/tests/project/test_init.py +676 -0
- stayfixed-0.1.0/tests/project/test_journey.py +215 -0
- stayfixed-0.1.0/tests/project/test_planning_seam.py +189 -0
- stayfixed-0.1.0/tests/project/test_questions.py +171 -0
- stayfixed-0.1.0/tests/project/test_rewrite.py +157 -0
- stayfixed-0.1.0/tests/project/test_round_trip.py +91 -0
- stayfixed-0.1.0/tests/project/test_templates.py +743 -0
- stayfixed-0.1.0/tests/project/test_uninstall.py +965 -0
- stayfixed-0.1.0/tests/project/test_upgrade.py +754 -0
- stayfixed-0.1.0/tests/release/__init__.py +0 -0
- stayfixed-0.1.0/tests/release/test_hashes.py +218 -0
- stayfixed-0.1.0/tests/release/test_notes.py +62 -0
- stayfixed-0.1.0/tests/release/test_pins.py +74 -0
- stayfixed-0.1.0/tests/release/test_surface.py +41 -0
- stayfixed-0.1.0/tests/release/test_versions.py +584 -0
- stayfixed-0.1.0/tests/scaffold/__init__.py +0 -0
- stayfixed-0.1.0/tests/scaffold/test_containment.py +406 -0
- stayfixed-0.1.0/tests/scaffold/test_engine.py +1131 -0
- stayfixed-0.1.0/tests/scaffold/test_entries.py +259 -0
- stayfixed-0.1.0/tests/scaffold/test_local.py +350 -0
- stayfixed-0.1.0/tests/scaffold/test_manifest.py +160 -0
- stayfixed-0.1.0/tests/scaffold/test_model.py +60 -0
- stayfixed-0.1.0/tests/scaffold/test_regions.py +141 -0
- stayfixed-0.1.0/tests/scaffold/test_report.py +82 -0
- stayfixed-0.1.0/tests/scaffold/test_surface.py +69 -0
- stayfixed-0.1.0/tests/scaffold/test_writes.py +196 -0
- stayfixed-0.1.0/tests/scripts/__init__.py +0 -0
- stayfixed-0.1.0/tests/scripts/test_check_artifacts.py +175 -0
- stayfixed-0.1.0/tests/scripts/test_mutation_oracle.py +1028 -0
- stayfixed-0.1.0/tests/scripts/test_smoke_scripts.py +334 -0
- stayfixed-0.1.0/tests/setup/__init__.py +0 -0
- stayfixed-0.1.0/tests/setup/test_commands.py +220 -0
- stayfixed-0.1.0/tests/setup/test_git_hooks.py +168 -0
- stayfixed-0.1.0/tests/setup/test_machine.py +220 -0
- stayfixed-0.1.0/tests/setup/test_setup.py +1839 -0
- stayfixed-0.1.0/tests/setup/test_surface.py +27 -0
- stayfixed-0.1.0/tests/skills/__init__.py +0 -0
- stayfixed-0.1.0/tests/skills/test_skills.py +272 -0
- stayfixed-0.1.0/tests/snapshot.py +81 -0
- stayfixed-0.1.0/tests/test_areas.py +267 -0
- stayfixed-0.1.0/tests/test_check_workflow.py +758 -0
- stayfixed-0.1.0/tests/test_cli.py +335 -0
- stayfixed-0.1.0/tests/test_command.py +129 -0
- stayfixed-0.1.0/tests/test_documents.py +650 -0
- stayfixed-0.1.0/tests/test_findings.py +59 -0
- stayfixed-0.1.0/tests/test_fixtures.py +855 -0
- stayfixed-0.1.0/tests/test_floor.py +72 -0
- stayfixed-0.1.0/tests/test_fork_points.py +97 -0
- stayfixed-0.1.0/tests/test_fsops.py +480 -0
- stayfixed-0.1.0/tests/test_git_answers.py +234 -0
- stayfixed-0.1.0/tests/test_git_run.py +409 -0
- stayfixed-0.1.0/tests/test_gitfixture.py +195 -0
- stayfixed-0.1.0/tests/test_harnesses.py +42 -0
- stayfixed-0.1.0/tests/test_identifiers.py +64 -0
- stayfixed-0.1.0/tests/test_import_boundary.py +46 -0
- stayfixed-0.1.0/tests/test_install_path.py +765 -0
- stayfixed-0.1.0/tests/test_launcher.py +96 -0
- stayfixed-0.1.0/tests/test_manifests.py +167 -0
- stayfixed-0.1.0/tests/test_name.py +40 -0
- stayfixed-0.1.0/tests/test_neutral.py +630 -0
- stayfixed-0.1.0/tests/test_printed.py +81 -0
- stayfixed-0.1.0/tests/test_public_anchors.py +330 -0
- stayfixed-0.1.0/tests/test_runner.py +141 -0
- stayfixed-0.1.0/tests/test_snapshot.py +110 -0
- stayfixed-0.1.0/tests/test_surfaces.py +192 -0
- stayfixed-0.1.0/tests/test_tomlout.py +152 -0
- stayfixed-0.1.0/tests/test_undecodable.py +316 -0
- stayfixed-0.1.0/tests/test_workflow_yaml.py +137 -0
- stayfixed-0.1.0/tests/workflow_yaml.py +256 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stayfixed-marketplace",
|
|
3
|
+
"description": "stayfixed, a methodology harness for coding agents, published from its own repository.",
|
|
4
|
+
"owner": { "name": "stayfixed" },
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"name": "stayfixed",
|
|
8
|
+
"source": "./",
|
|
9
|
+
"description": "A methodology harness for coding agents: bug ledger, curated working memory behind a trust gate, and fail-closed guards."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stayfixed",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A methodology harness for coding agents: bug ledger, curated working memory behind a trust gate, and fail-closed guards.",
|
|
5
|
+
"author": { "name": "Nezhinskiy" },
|
|
6
|
+
"repository": "https://github.com/stayfixed/stayfixed",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": ["methodology", "bug-ledger", "memory", "hooks", "guards", "codex"],
|
|
9
|
+
"userConfig": {
|
|
10
|
+
"reply_language": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"title": "Reply language",
|
|
13
|
+
"description": "Language for chat replies; durable artifacts stay in the artifact language.",
|
|
14
|
+
"default": ""
|
|
15
|
+
},
|
|
16
|
+
"artifact_language": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"title": "Artifact language",
|
|
19
|
+
"description": "Language for documents, comments, commits and pull requests.",
|
|
20
|
+
"default": "en"
|
|
21
|
+
},
|
|
22
|
+
"preset": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"title": "Preset",
|
|
25
|
+
"description": "Preset applied by setup and init.",
|
|
26
|
+
"default": "recommended"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stayfixed",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A methodology harness for coding agents: bug ledger, curated working memory behind a trust gate, and fail-closed guards.",
|
|
5
|
+
"author": { "name": "Nezhinskiy" },
|
|
6
|
+
"repository": "https://github.com/stayfixed/stayfixed",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"interface": {
|
|
9
|
+
"displayName": "stayfixed",
|
|
10
|
+
"shortDescription": "Bug ledger, working memory behind a trust gate, and fail-closed guards for coding agents",
|
|
11
|
+
"developerName": "Nezhinskiy",
|
|
12
|
+
"category": "Developer Tools"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<!-- towncrier release notes start -->
|
|
4
|
+
|
|
5
|
+
## 0.1.0 (2026-09-28)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Installation.** stayfixed installs as a Claude Code and Codex plugin and as a command-line
|
|
10
|
+
tool (`uv tool install stayfixed`). It needs Python 3.11 or newer on Linux or macOS, and
|
|
11
|
+
imports nothing outside the standard library at runtime, so a hook works wherever `python3`
|
|
12
|
+
does. The plugin ships `hooks/hooks.json`, which both harnesses read with no configuration:
|
|
13
|
+
installing the plugin is enough for the guards to fire and the memory bundles to arrive at
|
|
14
|
+
session start.
|
|
15
|
+
- **What a repository can and cannot make stayfixed do.** stayfixed treats a repository as
|
|
16
|
+
untrusted input, since a clone you have not read can commit a `stayfixed.toml`, notes, a
|
|
17
|
+
manifest, a `.claude/settings.json` `env` block and symlinks. Every file stayfixed replaces is
|
|
18
|
+
replaced atomically and keeps its permissions. Every write into a repository walks its path
|
|
19
|
+
one component at a time, refuses a symlink at any component and never leaves the project root,
|
|
20
|
+
so a committed link cannot redirect a write. Your machine configuration is
|
|
21
|
+
`~/.config/stayfixed/config.toml`; neither `STAYFIXED_CONFIG` nor `XDG_CONFIG_HOME` moves it,
|
|
22
|
+
because a repository's settings file could set them, and `--machine` names another file
|
|
23
|
+
explicitly.
|
|
24
|
+
|
|
25
|
+
Text a repository chose (file names, note names, configuration keys and values) is never
|
|
26
|
+
printed as found, so it cannot put escape sequences into your terminal or a workflow command
|
|
27
|
+
such as `::error::` into a CI log. A name made of ASCII letters, digits, `.`, `_` and `-` in
|
|
28
|
+
`/`-separated segments prints as itself; any other prints on a summary line as `<unprintable
|
|
29
|
+
name; see --json>`, with the full name escaped in `--json`, and is quoted with control
|
|
30
|
+
characters escaped in a refusal. Refusals over a configuration value name the key and the rule
|
|
31
|
+
in words, never the value. Lists a repository can make arbitrarily long name at most eight
|
|
32
|
+
entries and count the rest (`--json` has them all). A TOML file that does not parse is
|
|
33
|
+
reported as the file and the parser's line and column only. File names that are not UTF-8 are
|
|
34
|
+
handled as their own bytes, and when `git` cannot run or answer, commands say so rather than
|
|
35
|
+
reading the silence as "nothing".
|
|
36
|
+
|
|
37
|
+
Exit codes are the same everywhere: `0` success, `1` findings, `2` a refusal or an internal
|
|
38
|
+
error, which a caller must never read as permission. Two exceptions are deliberate: `stayfixed
|
|
39
|
+
test audit-entrypoints` exits `0` with findings, and `stayfixed hook` refuses on an internal
|
|
40
|
+
error only for `PreToolUse`, so a bug in stayfixed never erases a prompt you typed.
|
|
41
|
+
- **Machine setup: `stayfixed setup`.** `stayfixed setup --preset recommended` writes your
|
|
42
|
+
personal languages and the preset name into the machine configuration, merges the preset's
|
|
43
|
+
deny rules and your personal values into `~/.claude/settings.json`, registers and installs the
|
|
44
|
+
preset's plugins on every harness that declares a marketplace for them (a missing harness is a
|
|
45
|
+
note, not a failure), and tells you whether `stayfixed` is on `PATH`. `--overlay <path>`
|
|
46
|
+
records a private overlay you already have; `--overlay create:<owner>/<name> --yes` creates
|
|
47
|
+
one on GitHub and records it. Creating a repository is the one outward-facing act `setup`
|
|
48
|
+
performs and the only thing `--yes` confirms.
|
|
49
|
+
|
|
50
|
+
Everything `setup` can refuse, it refuses before its first write. An `--overlay` path must be
|
|
51
|
+
a real overlay (both `.claude-plugin/` manifests naming it `stayfixed-overlay[-<owner>]` and
|
|
52
|
+
`stayfixed-overlay-marketplace[-<owner>]`) and must lie outside every checkout of the
|
|
53
|
+
repository `--root` names, linked worktrees included. The machine configuration is merged key
|
|
54
|
+
by key: a value you or an earlier run recorded is never overwritten by a preset default, and
|
|
55
|
+
unknown tables are carried through, but comments do not survive the rewrite. A
|
|
56
|
+
`~/.claude/settings.json` reached through a symlink (stow, chezmoi, a synced home) is refused
|
|
57
|
+
rather than written through; the refusal names the link, and `--settings <path>` writes the
|
|
58
|
+
file where it really is. `--home` and `--machine` point a run at a scratch destination; they
|
|
59
|
+
are not a dry run.
|
|
60
|
+
|
|
61
|
+
`stayfixed setup --git-hooks` installs the commit-message hook into this repository's own
|
|
62
|
+
hooks directory, never `core.hooksPath`. An existing hook is kept as
|
|
63
|
+
`prepare-commit-msg.local` and chained to, and `--git-hooks --uninstall` restores it exactly.
|
|
64
|
+
- **Starting a project: `stayfixed init`.** `stayfixed init --questions` prints the six values
|
|
65
|
+
`stayfixed init --yes` would take (name, base branch, agents, profile, memory mode, files kept
|
|
66
|
+
out of git), where each came from and the flag that replaces it (`--name`, `--base-branch`,
|
|
67
|
+
`--agent`, `--profile`, `--memory-mode`, `--local`); under `--json` it is a JSON Schema. `init
|
|
68
|
+
--yes` writes the footprint: `stayfixed.toml`, a `CLAUDE.md` pointing at whatever `[paths]
|
|
69
|
+
agents_md` names, an `AGENTS.md` skeleton (or a managed section in an existing one) stating
|
|
70
|
+
your effective budgets, a `.gitignore` block, the documentation skeleton the other commands
|
|
71
|
+
expect, and a CI workflow calling the reusable gate pinned to the commit of the stayfixed
|
|
72
|
+
release that wrote it. Every file is recorded in `.stayfixed/manifest.json`. `--dry-run` shows
|
|
73
|
+
every file first, and a refusal anywhere writes nothing. A file already present, an existing
|
|
74
|
+
`CLAUDE.md` included, is left alone. `init` runs once per repository; after that, use
|
|
75
|
+
`stayfixed upgrade`.
|
|
76
|
+
|
|
77
|
+
The base branch comes from `origin/HEAD` only when that names a plain branch under
|
|
78
|
+
`refs/remotes/origin/`, otherwise it is `main`. With no remote, it is the branch checked out.
|
|
79
|
+
A repository whose remote has no `origin/HEAD` (created locally and pushed, or whose only
|
|
80
|
+
remote is `upstream`) gets `main`, never the branch you are on; a `note:` line names
|
|
81
|
+
`--base-branch` and, for `origin`, `git remote set-head origin --auto`.
|
|
82
|
+
|
|
83
|
+
A `stayfixed.toml` you wrote by hand is read as the answers, not replaced: `init` adds at most
|
|
84
|
+
a missing `[stayfixed] version`, refuses a file the next command could not load, and names the
|
|
85
|
+
custom gates it configures, whose commands `stayfixed assess` runs unless given `--builtin`.
|
|
86
|
+
The workflow is rendered from the `[ci] ref` your file records, so a file with no ref gets no
|
|
87
|
+
workflow: write a released commit into `[ci] ref` before `init`, or run `stayfixed upgrade`
|
|
88
|
+
afterwards. `--no-ci` writes no workflow and records `[ci] mode = "none"` when this run writes
|
|
89
|
+
the file. When no released tag matches the running version, or the public repository cannot be
|
|
90
|
+
reached, the workflow is skipped with a sentence saying why and which command can still act.
|
|
91
|
+
|
|
92
|
+
The first stack profile is `python`. When `[stayfixed] profile` names it (from `--profile`, or
|
|
93
|
+
detected), `init` writes its rules once to `docs/stayfixed/rules/python.md`, the copy the
|
|
94
|
+
project edits; every harness reading `AGENTS.md` gets a pointer and the profile's must-know
|
|
95
|
+
lines, and Claude Code also a path-scoped pointer under `.claude/rules/`. `stayfixed assess`
|
|
96
|
+
runs the profile's eight checks: a lockfile committed or deliberately ignored,
|
|
97
|
+
`requires-python` declared without a cap, one type checker configured, strict pytest markers
|
|
98
|
+
in whichever file pytest reads, marker selection kept out of `addopts`, and Ruff's defaults
|
|
99
|
+
not hidden by a legacy `select`.
|
|
100
|
+
- **Keeping the footprint current: `stayfixed upgrade` and `stayfixed uninstall`.** `stayfixed
|
|
101
|
+
upgrade [--dry-run] [--force PATH]` refreshes untouched files after a stayfixed update; files
|
|
102
|
+
you edited, and files stayfixed never wrote, are skipped and named, and `--force` with a path
|
|
103
|
+
overwrites that one. `[stayfixed] version` moves in place, every other byte of
|
|
104
|
+
`stayfixed.toml` kept. When the workflow pins a commit, `[ci] ref`, the pin and the version
|
|
105
|
+
move together or not at all; a `[ci] ref` that is not a commit is left alone. A caller
|
|
106
|
+
workflow you edited is reported `skip_modified`, and `upgrade` then holds the version and
|
|
107
|
+
`[ci] ref` until `--force .github/workflows/stayfixed.yml` or a hand edit brings it in line. A
|
|
108
|
+
project recording a newer stayfixed (a pre-release included) or a version stayfixed cannot
|
|
109
|
+
order is refused. Each run decides first and writes second, so a dry run shows exactly what a
|
|
110
|
+
real one will do; a run stopped by a failed write keeps and records what it did, and the next
|
|
111
|
+
run continues.
|
|
112
|
+
|
|
113
|
+
`stayfixed uninstall [--dry-run] [--force PATH]` removes files whose bytes are still
|
|
114
|
+
stayfixed's, takes stayfixed's sections out of files holding other text (shown as `(retired;
|
|
115
|
+
stayfixed's part only, the file stays)`), removes the manifest, and keeps and lists every file
|
|
116
|
+
you edited. It refuses while `.stayfixed/local/` holds notes or edited files, and while the
|
|
117
|
+
manifest records a `stayfixed.toml` that is missing. A `stayfixed.toml` you wrote before
|
|
118
|
+
`init` stays.
|
|
119
|
+
|
|
120
|
+
`[artifacts] local` keeps chosen artifacts out of git: they are written under
|
|
121
|
+
`.stayfixed/local/artifacts/`, recorded in `.stayfixed/local/artifacts.json`, refreshed while
|
|
122
|
+
unchanged and never overwritten once edited. It cannot name `config` or `gitignore`. `init`,
|
|
123
|
+
`upgrade` and `uninstall` also refuse, before writing, a `[paths]` layout that aims two
|
|
124
|
+
artifacts at one file (paths differing only in case count as one) and a `[paths]` value
|
|
125
|
+
pointing at an existing file git ignores, such as a `.env`.
|
|
126
|
+
- **The `stayfixed.toml` grammar.** The configuration is committed and repository-controlled, so
|
|
127
|
+
it is a closed grammar: unknown sections and keys are refused, and a file that looks valid can
|
|
128
|
+
be refused on the rules below. Each refusal names the key and states the rule.
|
|
129
|
+
|
|
130
|
+
`[paths]` values are plain relative paths: segments of letters, digits, `.`, `_` and `-`
|
|
131
|
+
joined by single `/`, none empty, `.` or `..`. A leading `./`, a doubled `//` and a trailing
|
|
132
|
+
`/` are refused, not normalised (except in `ledger.code_roots`, where `"./src"` and `"src/"`
|
|
133
|
+
read as `src`). No value may contain a `.git` component, at any depth or case, or point inside
|
|
134
|
+
`.stayfixed/`; `.github/` and `.gitignore` are unaffected. `[paths] stayfixed` (default
|
|
135
|
+
`docs/stayfixed`) is where stayfixed's own project files go.
|
|
136
|
+
|
|
137
|
+
`[project] name` and custom gate names are one lowercase path segment of letters, digits, `.`,
|
|
138
|
+
`_` and `-`, led by a letter or digit. `[project] base_branch`, `release_branch` and `[ci]
|
|
139
|
+
gate_branch` use letters, digits, `.`, `_`, `-` and `/`, led by a letter or digit, and never a
|
|
140
|
+
name git refuses; `release/2.0` is accepted, a name with `+`, `@`, a leading `_` or a
|
|
141
|
+
non-ASCII letter is not. `[ci] gate_branch` defaults to `[project] base_branch`. `[stayfixed]
|
|
142
|
+
preset` is matched exactly, case included, against the shipped presets, and `profile` must be
|
|
143
|
+
a shipped profile. `[stayfixed] state`, `[memory] mode` and `[ci] mode` take only their listed
|
|
144
|
+
values. `[ci] ref` is the full-length commit sha of a released stayfixed tag. Each
|
|
145
|
+
`memory.groups` entry names a subdirectory of `paths.memory` (`""`, `"."` and doubled or
|
|
146
|
+
trailing slashes are refused); a duplicate counts once.
|
|
147
|
+
|
|
148
|
+
`[gates]` names the gates a project runs: the built-in `docs`, `bugs`, `plan`, `commit` and
|
|
149
|
+
`trail` by default, and argv commands under `[gates.custom.<name>]`. `[stayfixed] enforced`,
|
|
150
|
+
written by `stayfixed adopt promote`, lists the gates promoted so far; an `installed` project
|
|
151
|
+
whose `enforced` lists only some gates is refused.
|
|
152
|
+
|
|
153
|
+
**A `stayfixed.toml` that is a symbolic link is refused by every command**, before anything is
|
|
154
|
+
read through it, and while it is one every agent tool call in that checkout is refused at
|
|
155
|
+
`PreToolUse`. Replace the link with the file.
|
|
156
|
+
- **Working memory: `stayfixed memory`.** Notes are Markdown files with frontmatter; `MEMORY.md`
|
|
157
|
+
is a routing index generated from them. Session-start bundles arrive whole: a bundle too large
|
|
158
|
+
for its slot is withheld with a notice rather than cut in half, and `stayfixed memory fit`
|
|
159
|
+
says what fits. In a git worktree the store is linked in, not copied, and every link goes
|
|
160
|
+
through the same contained walk as a file write. `stayfixed memory refs` reports backticked
|
|
161
|
+
repository paths in your notes that no longer exist.
|
|
162
|
+
|
|
163
|
+
Notes committed to a repository you cloned reach the model only after `stayfixed memory trust
|
|
164
|
+
--in-repo-memory`, and then inside a marked region that says they are data. When the
|
|
165
|
+
repository changes what it ships, you are asked again, and the harness's link to the store is
|
|
166
|
+
withdrawn until you are. Notes in your own overlay need no approval.
|
|
167
|
+
|
|
168
|
+
`stayfixed memory session-context --bundle index` emits `MEMORY.md` on Codex only, since
|
|
169
|
+
Claude Code reads it natively; the harness is read from the environment, so the same command
|
|
170
|
+
answers differently in each. The `recommended` preset's five standing rules (ask at a decision
|
|
171
|
+
fork, work in a worktree by default, keep research sources within two months, read CI after a
|
|
172
|
+
push, split languages by audience) arrive at session start through the `preset-rules` bundle;
|
|
173
|
+
they are never repository content and need no trust record.
|
|
174
|
+
- **The private overlay: `stayfixed overlay`.** An overlay is your own repository of standing
|
|
175
|
+
rules, cross-project notes and one record per bound repository. `stayfixed overlay create`
|
|
176
|
+
renders one locally with `--local` (no network call) or on GitHub with `--template`, private
|
|
177
|
+
from the start; neither is a default, so an omitted flag never creates a repository on your
|
|
178
|
+
account. `--template` generates from `<owner>/stayfixed-overlay-template`, which `stayfixed
|
|
179
|
+
overlay publish-template` publishes from your own authenticated checkout. Without `--yes`,
|
|
180
|
+
`publish-template` only reports what it would create, mark and push; a same-named repository
|
|
181
|
+
that is not public is refused rather than flipped, and a `gh` or `git` that cannot answer is
|
|
182
|
+
reported as such, not as "nothing to push".
|
|
183
|
+
|
|
184
|
+
`stayfixed overlay init` names the overlay after you in all three plugin manifests so two
|
|
185
|
+
overlays never collide, and installs a commit-time secret scan beside the template's push-time
|
|
186
|
+
one, whose GitHub Actions are pinned at full commit shas and kept current by
|
|
187
|
+
`.github/dependabot.yml`. `stayfixed overlay upgrade` refreshes files you have not edited and
|
|
188
|
+
always names the two files that can grant a capability, permission rules and hook entries,
|
|
189
|
+
whatever their hashes say; run `--dry-run` first to see that list before anything changes. The
|
|
190
|
+
shipped template has deny-only permissions and no hooks. Both commands refuse a `--root` that
|
|
191
|
+
is not an overlay. An overlay may declare the stayfixed it needs as `stayfixed.requires` in
|
|
192
|
+
its plugin manifest, each version component at most nine ASCII digits.
|
|
193
|
+
- **Binding a repository: `stayfixed attach` and `stayfixed detach`.** `stayfixed attach --store
|
|
194
|
+
<overlay>/projects/<name>/memory` checks that the overlay recorded this repository's remote
|
|
195
|
+
(`--trust-remote` accepts a different one), prints the permissions and hook entries the
|
|
196
|
+
overlay would add, merges them into `.claude/settings.local.json`, copies your Codex rules
|
|
197
|
+
into `.codex/rules/`, and links your notes into the checkout and every worktree. `--check`
|
|
198
|
+
reports the same and writes nothing. Every refusal `attach` can foresee happens before its
|
|
199
|
+
first write: `--store` must be your overlay's directory for this project, since the overlay
|
|
200
|
+
root comes from your machine configuration and never from the path you type; a new permission
|
|
201
|
+
needs `--yes`; `--machine` is accepted only from an interactive shell; a note group that is
|
|
202
|
+
still a real directory must be moved into the overlay first (`--check` counts them); and a
|
|
203
|
+
`~/.claude` that is a symlink into a dotfiles tree is refused by name, here, in `detach` and
|
|
204
|
+
in the worktree session hook. Make that directory real and let your dotfiles manager adopt the
|
|
205
|
+
files inside it.
|
|
206
|
+
|
|
207
|
+
`stayfixed detach` removes exactly what `attach` added, from a ledger under
|
|
208
|
+
`.stayfixed/local/`, and leaves the overlay's binding record so a re-attach does not ask
|
|
209
|
+
again. Run it from the checkout you attached from. The ledger is never an authority: a field
|
|
210
|
+
`attach` could not have written is refused with nothing removed. Directories `attach` created
|
|
211
|
+
are removed only when empty, except the note link directory, and on a repository `stayfixed
|
|
212
|
+
init` set up the `.gitignore` block stays. The overlay's own `hooks/hooks.json` stays empty;
|
|
213
|
+
its hook entries reach a repository only through `attach`'s explicit merge.
|
|
214
|
+
|
|
215
|
+
In a repository with `[memory] mode = "overlay"`, a session hears once at its start what
|
|
216
|
+
stands between it and its notes: no overlay recorded, not attached, a different remote, a
|
|
217
|
+
group still a real directory, an overlay requiring a newer stayfixed, or, when nothing else is
|
|
218
|
+
wrong, a branch with no upstream or unpushed commits and uncommitted changes. Each line is
|
|
219
|
+
fixed text naming the command to run; a healthy repository hears nothing. On a compaction the
|
|
220
|
+
unpushed-work check is skipped, so an overlay that becomes unpushed mid-session is reported at
|
|
221
|
+
the next start or resume, or by `stayfixed doctor`.
|
|
222
|
+
- **The bug ledger: `stayfixed bugs`.** One Markdown file per bug under the directory
|
|
223
|
+
`stayfixed.toml` names, plus a generated index that is a pure function of those files and
|
|
224
|
+
refuses to delete content it did not write. `stayfixed bugs check` holds every entry to a flat
|
|
225
|
+
frontmatter contract, every high-severity entry to a filled evidence-boundary line, and every
|
|
226
|
+
identifier under your code roots to an existing entry. `stayfixed bugs new` files an entry at
|
|
227
|
+
the next number free across every branch this checkout can see, and warns when it could not
|
|
228
|
+
read that history. `stayfixed bugs renumber` moves an entry and every reference, leaving a
|
|
229
|
+
pointer at the old number. Entries are append-only: with `--base`, which the `bugs` gate
|
|
230
|
+
passes, a deleted ledger is a `ledger-removed` finding and a deleted or renamed entry an
|
|
231
|
+
`entry-removed` finding, and a branch is not blamed for an entry filed after it forked.
|
|
232
|
+
- **Documents and plans.** `stayfixed docs check` holds your always-loaded agent document and
|
|
233
|
+
the roadmap's prose to the preset's budgets (a project may lower them, never raise them),
|
|
234
|
+
checks that every local link resolves, and advises, without failing, on broken or repeated
|
|
235
|
+
wiki-links and bracketed bug identifiers in the memory store. `stayfixed docs trail`
|
|
236
|
+
regenerates the design-and-plan listing in your roadmap from the tracked documents and a
|
|
237
|
+
`trail.toml` you own, and will not let a renamed or new document claim it shipped. A
|
|
238
|
+
`trail.toml` theme `pattern` accepts only literal text, `.`, `.*`, `|`, `^`, `$` and `\`
|
|
239
|
+
before punctuation, at most 256 characters, with at most 32 themes; other syntax is refused.
|
|
240
|
+
Inside a git work tree, `docs trail` writes nothing and exits `1` when `git` cannot say which
|
|
241
|
+
documents are ignored, so a local-only document never reaches the committed roadmap.
|
|
242
|
+
|
|
243
|
+
`stayfixed plan check` lints the plans a change touches: every path they name exists, no
|
|
244
|
+
verification step presumes its answer, and a mutation's outcome stays an expectation until
|
|
245
|
+
someone has watched it. It lints every plan the merge could alter, across all merge bases. The
|
|
246
|
+
README places stayfixed beside the tools it sits next to and says what each command writes,
|
|
247
|
+
and `docs/methodology/` states the ten principles behind it with dated sources.
|
|
248
|
+
- **Hooks and guards.** The hook wrapper finds a Python 3.11 or newer, runs from your project
|
|
249
|
+
root, and refuses with a named `SF_*` token rather than letting a broken guard read as
|
|
250
|
+
permission. Its environment may say where something goes, never which program runs:
|
|
251
|
+
`STAYFIXED_PYTHON_CANDIDATES` is honoured only from an interactive terminal, because a
|
|
252
|
+
committed `env` block applies without a trust prompt; no `python3` inside any checkout of the
|
|
253
|
+
repository is ever run, however `PATH` reached it; and `git` comes from a fixed list of
|
|
254
|
+
absolute paths, never `PATH`. If your only Python 3.11 lives inside your checkout, you will
|
|
255
|
+
get `SF_NO_PY` and a red `wrapper` row in `stayfixed doctor` with the remedy.
|
|
256
|
+
|
|
257
|
+
The `PreToolUse` guard (also runnable as `stayfixed guard bg-cleanup`) refuses a backgrounded
|
|
258
|
+
command that would leave an `&` job running past the harness's reap or that begins with
|
|
259
|
+
`sleep`. It advises when a restore line may never be reached, and when a backgrounded command
|
|
260
|
+
ends in `; echo …`, which makes the reported exit code the echo's. `stayfixed commit check`
|
|
261
|
+
fails a pull request whose commit messages carry an AI attribution trailer; `stayfixed commit
|
|
262
|
+
strip`, run by the hook `setup --git-hooks` installs, removes one before the commit exists.
|
|
263
|
+
`stayfixed test hygiene` names the two environment faults that make a red run unattributable,
|
|
264
|
+
and `stayfixed test audit-entrypoints` lists tests that never exercise what they name.
|
|
265
|
+
|
|
266
|
+
`stayfixed test attribute --command CMD` decides whether a failing test is your change or your
|
|
267
|
+
environment. It runs the command on the working tree, on `HEAD`'s committed tree and on the
|
|
268
|
+
merge base with your base branch (the last two extracted with `git archive` into a scratch
|
|
269
|
+
directory) and prints the verdict the three exit codes give. It never moves your checkout, but
|
|
270
|
+
the first run does execute your command in your working tree. Put your environment sync in the
|
|
271
|
+
command (`uv sync --locked && uv run pytest …`). With several merge bases it reports the
|
|
272
|
+
attribution as undetermined and runs nothing.
|
|
273
|
+
- **Assessing and adopting: `assess`, `gate` and `adopt`.** `stayfixed assess` inventories what
|
|
274
|
+
stands between a repository and enforcement: every configured gate and what no gate runs, such
|
|
275
|
+
as marker comments, tracked `.env` files, committed notes, foreign hooks and workflows, a
|
|
276
|
+
CODEOWNERS file that leaves a workflow or the rest of `.github/` without an owner, commit
|
|
277
|
+
subjects outside the vocabulary, and the profile's checks. The inventory goes to
|
|
278
|
+
`.stayfixed/assessment.json` and `--json`. A gate that could not judge the tree is `could not
|
|
279
|
+
run`, never a finding; when the base is not in the checkout, the summary says why `plan`,
|
|
280
|
+
`commit` and `bugs` could not run and suggests `--base refs/heads/<base_branch>`. `--builtin`
|
|
281
|
+
on `assess`, `gate` and `adopt promote` runs none of the commands `[gates.custom]` names, for
|
|
282
|
+
a clone whose commands you have not agreed to run; without it those commands run, as the
|
|
283
|
+
repository's test suite would.
|
|
284
|
+
|
|
285
|
+
The `docs` and `trail` gates judge tracked files, because CI checks out nothing else. When a
|
|
286
|
+
file they read is on disk but untracked, or tracked under a name differing only in case,
|
|
287
|
+
`assess` reports the gate as unable to judge the tree as CI will and `adopt promote` does not
|
|
288
|
+
enforce it. Commit the file, or remove the gate from `[gates] builtin`.
|
|
289
|
+
|
|
290
|
+
`stayfixed gate` is the pull-request gate, runnable locally. It reads the base's
|
|
291
|
+
`stayfixed.toml` at an exact commit, runs the configuration check and every configured gate,
|
|
292
|
+
advisory or enforcing, and prints the verdict. A pull request may only tighten: enforce more
|
|
293
|
+
gates, move the state forward, add a gate, drop or re-command one the base does not enforce,
|
|
294
|
+
lower a budget; the preset, profile, harnesses and project name are free, and an upgrade may
|
|
295
|
+
move the version to the running stayfixed and the pin to a released commit. While the base
|
|
296
|
+
enforces any gate, any other change is refused and lands only by a direct push to the base
|
|
297
|
+
branch. A project's first pull request, with no `stayfixed.toml` on the base, is judged by the
|
|
298
|
+
tree's copy. A custom gate runs only the command the base gives it, so one a pull request adds
|
|
299
|
+
or changes is not run until it lands; the base's enforced custom gates run first, no custom
|
|
300
|
+
gate starts once the run has failed, and each command runs in its own process group.
|
|
301
|
+
|
|
302
|
+
`stayfixed adopt begin PLAN` starts an adoption with a plan that passes `plan check`.
|
|
303
|
+
`stayfixed adopt promote [GATE…]` enforces the named gates together once they pass, or, with
|
|
304
|
+
no names, every gate that passes now; when all enforce, the project is `installed`. A custom
|
|
305
|
+
gate is promoted only once the base branch has its command.
|
|
306
|
+
- **The reusable CI workflow.** Your CI calls stayfixed's gates through one reusable workflow
|
|
307
|
+
pinned at a commit sha; `stayfixed init` writes the caller, `.github/workflows/stayfixed.yml`.
|
|
308
|
+
One `gates` job runs `stayfixed gate` with no resolver and no build backend, in two steps: the
|
|
309
|
+
configuration check and built-in gates, then your `[gates.custom]` gates only if the first
|
|
310
|
+
step passed, so no command your repository wrote runs where the verdict is decided. Python
|
|
311
|
+
starts as `python3 -P -s`, so no module in the checkout and no user-site `.pth` file loads.
|
|
312
|
+
Custom gates run on the bare runner image and install their own toolchain.
|
|
313
|
+
|
|
314
|
+
The caller runs only for pull requests into the gate branch, re-runs when one is retargeted,
|
|
315
|
+
reports for that branch's merge queue, and passes the branch as a literal `base:`, so a check
|
|
316
|
+
cannot be won against a looser branch and then retargeted. The base resolves to a commit from
|
|
317
|
+
`refs/remotes/origin/<base>`, so a tag of the same name cannot stand in for it, and the
|
|
318
|
+
stayfixed that runs is checked against the commit your `uses:` line pins before any gate runs.
|
|
319
|
+
The tree under review cannot turn off the gates it faces, but the caller file is one a pull
|
|
320
|
+
request can edit, so protect `.github/` with code-owner review as usual. The `only` input runs
|
|
321
|
+
a chosen few gates (the configuration check always runs), and `timeout-minutes` sets the job
|
|
322
|
+
limit, 15 by default, a whole number from 5 to 60; any other value fails the job, and a job
|
|
323
|
+
that times out is cancelled, never passed.
|
|
324
|
+
- **Diagnosing an installation: `stayfixed doctor`.** Sixteen checks over the configuration, the
|
|
325
|
+
hook wrapper, the overlay binding and memory path, every hook entry with its provenance,
|
|
326
|
+
budgets, bundle fit, the overlay's secret scan and the stayfixed version it requires, the note
|
|
327
|
+
store, recent hook failures, `[ci] ref`, and ignored environment variables. It writes and
|
|
328
|
+
repairs nothing; every finding carries the command that fixes it, and `--json` carries all
|
|
329
|
+
sixteen. It exits `1` when any check is red; a `skip` never reaches the exit code. The `files`
|
|
330
|
+
row compares the installed wrapper, hook table and launcher with the hashes the release
|
|
331
|
+
shipped, and says whether a file is modified, absent or unrecorded. The `ci-ref` row checks
|
|
332
|
+
that `[ci] ref` is a released commit and matches the workflow's pin; a missing workflow under
|
|
333
|
+
`[ci] mode = "reusable"` is a warning.
|
|
334
|
+
|
|
335
|
+
`doctor --json` is relayed to a model, so no repository-authored text reaches it: hook entries
|
|
336
|
+
are named by position, the hook log is counted and never quoted, and a file it cannot read is
|
|
337
|
+
a warning naming the file, not a red row a clone could force. It reads the workflow only as a
|
|
338
|
+
regular file up to 256 KiB, bounds its one network call at 30 seconds, runs only the hook
|
|
339
|
+
wrapper of its own installation, and trusts the overlay's record over a ledger a clone could
|
|
340
|
+
commit.
|
|
341
|
+
- **Skills and agents.** The plugin ships `close-bug` and `memory-sweep`; six procedures an
|
|
342
|
+
agent can be handed (`file-bug`, `sweep-defect-class`, `review-plan-three-lenses`,
|
|
343
|
+
`attribute-failure`, `run-correctness-audit`, `retro-to-guard`); short wrappers for `init`,
|
|
344
|
+
`upgrade`, `uninstall`, `attach`, `setup` and `doctor`; and a read-only `code-navigator`
|
|
345
|
+
agent. Every `stayfixed` invocation in a skill is checked against the real parser. The `init`
|
|
346
|
+
skill asks what `stayfixed init --questions` lets you answer, shows the dry run, writes only
|
|
347
|
+
on an explicit yes, and then starts the adoption (`stayfixed assess`, a plan, and `stayfixed
|
|
348
|
+
adopt begin` on a second explicit yes). Silence, a timeout or an empty answer is never a yes.
|
|
349
|
+
If you decline to run the repository's own gate commands, it uses `--builtin`.
|
|
350
|
+
- **Release tooling for this repository.** `stayfixed release` is stayfixed's discipline for its
|
|
351
|
+
own repository, not something it asks of yours. `release check` holds one version across
|
|
352
|
+
`pyproject.toml`, `uv.lock`, the package, both plugin manifests and `CHANGELOG.md`, with
|
|
353
|
+
`--tag` adding the tag and treating a pending changelog fragment as a finding. `release notes
|
|
354
|
+
--version X.Y.Z [--draft]` assembles `CHANGELOG.md` through towncrier. `release hashes`
|
|
355
|
+
records the sha256 of the three files a harness runs in `hooks/hashes.json`, which `release
|
|
356
|
+
check` verifies on every run and `stayfixed doctor` reads on your machine.
|
|
357
|
+
|
|
358
|
+
### Known limitations
|
|
359
|
+
|
|
360
|
+
- No moving `v1` alias exists for the reusable workflow during `0.x`, because a `0.x` minor may
|
|
361
|
+
break what came before. Pin the full commit `stayfixed init` writes; `stayfixed doctor`
|
|
362
|
+
reports a `[ci] ref` of `v1` red until the first `1.x` release creates it.
|
|
363
|
+
- The `uvx` form of the gate is not shipped yet.
|
|
364
|
+
- Windows is not supported: path containment relies on `openat` with `O_NOFOLLOW` and
|
|
365
|
+
`O_DIRECTORY`.
|
|
366
|
+
- Ending a custom gate's process group does not end a descendant that left it (a new session or
|
|
367
|
+
a job-control shell's own group) or one stayfixed may not signal (a `sudo` or setuid
|
|
368
|
+
descendant). The base's enforced custom gates share one checkout; only a matrix leg per gate
|
|
369
|
+
separates them.
|
|
370
|
+
- A shallow clone cannot be judged by the `plan` and `bugs` gates or by `stayfixed test
|
|
371
|
+
attribute`: use a full-depth checkout.
|
|
372
|
+
- Each area's `api.py` is its only supported Python import surface; other names may move without
|
|
373
|
+
notice.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project adopts the [Contributor Covenant, version 2.1][covenant] in full. Please read it
|
|
4
|
+
there rather than here, so that this file cannot drift out of step with the version it claims
|
|
5
|
+
to be.
|
|
6
|
+
|
|
7
|
+
[covenant]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/
|
|
8
|
+
|
|
9
|
+
In short: everyone taking part in this project's issues, pull requests, discussions and reviews
|
|
10
|
+
is expected to be respectful, to assume good faith, and to accept that technical disagreement is
|
|
11
|
+
normal and personal attacks are not.
|
|
12
|
+
|
|
13
|
+
## A note specific to this project
|
|
14
|
+
|
|
15
|
+
stayfixed's reviews are direct. A finding will be stated as a finding, a disagreement about an
|
|
16
|
+
approach will be argued on its merits, and "this is wrong because X" is not rudeness. The line
|
|
17
|
+
is the usual one: criticise the code, the design or the argument — never the person who wrote
|
|
18
|
+
it, and never their competence, background or identity.
|
|
19
|
+
|
|
20
|
+
## Reporting
|
|
21
|
+
|
|
22
|
+
To report behaviour that breaks this code, open a private report through GitHub:
|
|
23
|
+
|
|
24
|
+
<https://github.com/stayfixed/stayfixed/security/advisories/new>
|
|
25
|
+
|
|
26
|
+
That channel is private to the maintainers. It is meant for security reports, and using it for
|
|
27
|
+
a conduct report is a deliberate reuse until a dedicated contact address is published here —
|
|
28
|
+
say in the first line that it is a conduct report rather than a vulnerability.
|
|
29
|
+
|
|
30
|
+
Reports are handled confidentially. The maintainers will respond as promptly as a single-
|
|
31
|
+
maintainer project can, and will not discuss a report with anyone who does not need to know.
|
|
32
|
+
|
|
33
|
+
## Enforcement
|
|
34
|
+
|
|
35
|
+
The maintainers may edit, hide or delete comments, commits, issues and pull requests that break
|
|
36
|
+
this code, and may temporarily or permanently ban a contributor. Where a response is not
|
|
37
|
+
obvious, the Contributor Covenant's own enforcement ladder — correction, warning, temporary ban,
|
|
38
|
+
permanent ban — is the one used.
|