Docs

Troubleshooting

Keyed by what you see. Each row: what the signal means, and the one command that fixes it. If a sentence here stops matching the code, the code is right and this doc is a bug. For the model see how-ctrl-ai-works.md; for the full agent contract see agent-guide.md.

The three checks and where they run:

  • ctrlai guard — local pre-commit hook. Blocks the commit.
  • ctrlai auditCI. Boundary + imports + attestations (+ heuristic warns).
  • ctrlai conformCI. Did you build what you configured (ctrl.spec.json)?

Only mechanical checks FAIL the build; heuristics only ever WARN. See the severity tables in trust-and-security.md.


audit / guard failures (these FAIL the build)

parts/<name> was modified (content hash no longer matches parts.lock)BOUNDARY / INTEGRITY

Something edited a file inside parts/<name>/. Interiors are locked.

git checkout HEAD -- parts/        # undo the interior edit

Then make your change on your side of the seam (parts/<name>/seams.md). If the part genuinely needs to change, use a verb instead of an edit:

ctrlai upgrade <name> [--adapter=<vendor>]   # new version / vendor swap
ctrlai eject <name>                          # take ownership (loud, reviewable)

A type error pointing inside parts/** is still fixed on your side — restore the part and re-read its seams.md. The fix is never to edit the interior.

imports part interior "…" — only parts/<name>/src/index is the legal surfaceIMPORTS

App code reached past the seam. Import only the part's index:

import { sendEmail } from "@parts/email.transactional";   // ✅ the seam
// not "parts/email.transactional/src/internal/…"           ❌ interior

(The @parts/* alias maps to parts/*/src, so a deep @parts/<name>/internal/… won't even resolve — TypeScript fails it as "cannot find module".)

ATTESTATION_MISMATCH / SIGNATURE_PIN / NO_ATTESTATION

The ATTESTATION.json and parts.lock disagree, or the attestation is gone — usually a half-applied manual edit. Reinstall the part so it lands clean:

ctrlai add <name>      # re-vendors a verified part + re-pins the lock

npm dependency <pkg>@<range> is not installed / is outside the contract rangeNPM_DEP_MISSING / NPM_DEP_RANGE

A part's declared dependency isn't installed in range.

npm install            # (or your package manager) — installs the contract's deps

audit warnings (these do NOT fail the build)

These guide; they never redden CI (even under --strict for the heuristics).

Warning Meaning Fix
UNSIGNED (dev-tier) the attestation is dev:unsigned expected today — fine locally; real signing is the team tier
STALE the attestation's expires passed ctrlai upgrade <name> to pull a freshly re-attested version
ROUTES a declared route isn't confirmed mounted re-export the part's handler in your route file (see seams.md)
ENV a required env key isn't in .env.example add the key (and set it); or ctrlai provision <name>
SPRAWL app imports an SDK a verified part already covers wire the part instead of the SDK

conform failures — "not built to spec" (these FAIL the build)

conform compares ctrl.spec.json (what you configured) to what's installed.

Check What it means Fix
INSTALLED a capability you configured isn't installed (agent skipped it) ctrlai add <capability>
ATTESTED an installed, specified part has no attestation ctrlai add <capability> (re-install clean)
VENDORS a capability is on the wrong vendor ctrlai upgrade <capability> --adapter=<the one you chose>
HAND_ROLLED app code imports a vendor SDK for a capability you specified delete the hand-rolled code; wire the part via its seams.md
BEYOND_SPEC a part is installed beyond your spec informational only — never fails

N verified parts but no ctrl.spec.json (WARN; FAIL under --strict): the spec-loop has nothing to check against. If you configured this stack, the spec should exist — re-run ctrlai add <…> (it writes ctrl.spec.json). A brand-new empty repo with no parts is benign.

Blind spot: conform's hand-roll detection is import-based. A capability re-implemented with raw fetch() and no SDK import is not flagged. Don't hand-roll a capability a part already covers, even via fetch.


Other common moments

The pre-commit hook blocked my commit

That's ctrlai guard. Don't git commit --no-verify — it only skips the local hook; audit/conform still run in CI and the change still can't land. Use a sanctioned verb (add / upgrade / eject / remove); they move the lock and spec for you, faster than a CI round-trip.

ctrlai doctor says a route/part isn't wired

doctor checks the wired app, not just the lock. Run ctrlai wire <part> to scaffold the route/seam wiring, then follow parts/<name>/seams.md for the code only you can write.

ctrlai provision db / provision <part> / deploy failed

These need real credentials on your accounts and touch the network:

  • provision db → needs NEON_API_KEY (Neon is the implemented provider today).
  • provision <part> → prints the vendor setup; set the env it names.
  • push → needs GITHUB_TOKEN (scopes: repo + workflow).
  • deploy → needs your Vercel token.

A "working ≠ on-spec" gap after a vendor flip is normal: the new vendor needs its credentials before it actually works. Run ctrlai provision <part>.

The /ctrl panel won't let me change anything (403 / empty catalog)

The panel's write route (realize) and registry-fetch route (catalog) are loopback-gated: they only work on a local next dev. In production they're read-only by design. On a remote dev box, set CTRLAI_PANEL_ALLOW_REMOTE=1 only if you accept the risk. See trust-and-security.md.