Code Maintenance
Tasks that run periodically rather than continuously — audits, reports, and sweeps that catch drift, dead code, and knowledge gaps. They are deliberately not part of the per-commit gate (typecheck and lint already cover that).
The system carries a lot of agent-facing surface: AGENTS.md and rule files, schemas with embedded instructions, prompt-builder code, connector rules, procedure templates. Ordinary code changes can quietly invalidate that surface — a refactor renames a method, a schema gains a field, a doc moves — and nothing in the type system or test suite catches it. A second category catches the code-health issues that pile up if nobody looks: dead exports, circular dependencies, unused dependencies.
What runs on its own: schedules
A schedule is one directory, schedules/<name>/ at the monorepo root: a schedule.yaml (cadence, and the agent to start if there is work), an executable run script, a prompt.md when the schedule can start a workstream, and an optional check. One launchd tick drives all of them, due-ness is computed from persisted state (so a laptop that slept catches up once rather than piling up), and each run's report is a durable alert record rather than a notification nobody can read back.
bin/schedules list is the catalog. It is the answer to "what is scheduled, when did it last run, and is anything overdue" — this document does not keep a second copy of it. bin/schedules logs <name> reads a run's output; bin/schedules install registers the tick, once per machine, from the main checkout.
The current jobs and their cadences are listed under recurring work.
Writing one: scheduled work — when a task should be a schedule at all, what a run script owes (exit 0 in silence, hand off only when there is work, keep its own baseline so "new since last time" is real), what belongs in prompt.md, and how to rehearse with bin/schedules run <name> --dry-run. Design: docs/implemented-plans/scheduled-workstreams.md.
Not yet enrolled
Periodic tasks that still wait for someone to remember them. Each needs its own run design before it can become a schedule (issues/closed/docs-and-chores/2026-08-08-maintenance-cadence-framework.md).
| Task | Command | Cadence it wants |
|---|---|---|
| Knowledge audits | pnpm knowledge-audit | After prompt/schema/AGENTS.md changes; monthly otherwise. Guide: docs/testing/knowledge-audits.md |
| Session critiques | @session-critique <id> | After a session that seemed slow or worked around the CLI; periodically otherwise. Guide: docs/testing/session-critiques.md |
| Security overview regeneration | /security-report (skill) | At release boundaries, and when the staleness diff over the surface map is non-empty |
| Doc / prompt refresh | pnpm prompt-report, pnpm prompt-viewer, pnpm doc-graph | The standing tension in issues/docs-and-chores/2026-07-04-doc-refresh-cadence.md |
| Feedback collection | feedback-review/collect.ts | issues/docs-and-chores/2026-07-14-feedback-collection-cadence.md — items rot before review |
| CSP violation review | runbook: docs/scheduled/csp-violation-review.md | An agent analyzes new violations and proposes the harden flip; a human flips it |
Run when you touch the thing
Not cadence tasks — tools you run because of a change you just made.
| Task | Command | When |
|---|---|---|
| Agent SDK update | pnpm update-agent-sdk (monorepo root) | Anytime; the sdk-update schedule does it automatically after the settling window |
| Dead-code sweep | pnpm lint:knip (monorepo root) | Before a release, or when code feels accumulated. Triage each finding: real dead code → delete; legitimate entry point → register it in knip.ts with a comment naming who reaches it |
| Supplemental lint | pnpm lint:oxlint | Weekly through schedules/supplemental-lint; also after broad lint-sensitive changes. Catches what ESLint misses (ambiguous constructors, useless spreads, identical ternary branches) |
| Circular deps | pnpm lint:circular | Weekly through schedules/supplemental-lint; also after big refactors. It runs oxlint's import/no-cycle alone, configured in .oxlint-cycles.json to skip import type, so every finding is a value cycle and means a module needs splitting. There are none as of 2026-10-27, so any finding is news |
| Security regression scan | pnpm security:opengrep (monorepo root) | Before releases, and when touching auth/subprocess/temp-file/prompt boundaries. Discipline and how to add a rule: security/opengrep/README.md |
| Doc images | pnpm generate:doc-images | After editing architecture-diagram text or .mmd sources. Pipeline: docs/architecture/AGENTS.md |
| Box data migrations | bbx migrate (per box) | After adding a migrator to src/core/migrations.ts. Author guide and rollout history: docs/cards/migrations.md |
| Broken-ref cleanup | npx tsx src/scripts/clean-broken-refs.ts <boxRoot> | One-off, when bbx validate shows ref errors that pre-date a migration. Dry-run by default; --apply to write |
| Mobile parity audit | agent procedure (docs/implemented-plans/mobile-parity-sync.md §6) | After a burst of mobile work; quarterly otherwise |
Regression probes
Each of these helpers was consolidated once and later grew hand-rolled copies. When reviewing a broad change, grep for the formula outside its home module:
createHash("sha256")outsidesrc/lib/content-hash.ts.- An
fs.accessexistence probe, or.then(() => true).catch(() => false), outsidesrc/lib/file-exists.ts. - A
config/box.json+PUBLIC_URL/BBX_PUBLIC_URLcascade outsidesrc/lib/public-url.ts. A copy silently ignoresBBX_PUBLIC_URL. - An extension-to-MIME map literal outside
src/lib/mimetype.ts. - A
multipart/form-databoundary builder outsidesrc/lib/multipart.ts.
doc-check validates Markdown links, not backticked paths in prose. For changed docs, confirm each backticked src/ or docs/ path still resolves.
Working with output
Most of these produce artifacts that get committed (the doc graph, the prompt report, regenerated images) or status updated in place (knowledge-audit comments). When the output diff is non-trivial:
- Triage first, fix second. Skim the diff and categorize: intended changes (commit), unintended changes you can explain (decide), unintended changes you can't explain (investigate).
- Don't reflexively suppress noise. When a periodic task starts producing diff noise that's hard to triage, that's a signal the task or its inputs need work — fix the upstream cause rather than tuning down the warning.
- Status notes are durable; reports are ephemeral. Knowledge-audit reports live in a gitignored directory; the durable record is the YAML status comment. Prefer a small in-place note over a large report file nobody reads later.