Mental model

The big idea: replace prompt-time discipline with mechanism-time discipline. Hooks intercept actions; the engine refuses to advance state without acceptance; bypasses are named, scoped, and logged.

Three layers, one shape

xCoder has three independent layers. Each is mechanically enforced — no LLM in the path of decision-making.

LayerWhen it runsWhat it prevents
HooksPreToolUse / Stop (kernel level, every tool call)Edits on integration branch, commits without typecheck, commits without issue ref, sessions ending without a PR.
FlowEngineOn each phase exit (checked by xCoder TS code)Leaving BRANCH without a tracking issue, leaving SPEC without a spec file, leaving COMMIT without a conventional message, leaving PR without a PR number.
Merge gateBefore PR merge (verdict-driven)Removals without stated intent, kernel-zone changes without human review, security regressions, broken preview smokes.

Phases

The FlowEngine drives a task through 12 named phases. Most of them have engine-side acceptance checks (preconditions to leave). Two of them — implement and test — have no engine check; the iteration loop between them is the agent's responsibility.

text
idle → scope → issue → branch → spec → implement → test → commit → qa → pr → review → merge → idle
                                                ↑                ↓
                                                └──── iter ──────┘
PhaseAcceptance checkStrength
idle(entry/exit; no check)n/a
scope(no engine check)n/a
issue(no engine check)n/a
branchI-3 trackingIssueExists, I-4 branchMatchesPrefixstrong
specI-5 specFileExistsIfRequiredmedium
implement(no engine check; iter loop)n/a
test(no engine check; iter loop)n/a
commitI-7 conventionalCommitFormatstrong
qa(no engine check)n/a
prI-9 prOpenedBeforeIdlestrong
review(no engine check)n/a
merge(no engine check; merge-gate runs externally)n/a

Invariants — the contract

The 15 invariants are xCoder's flow-adherence contract. Each invariant has a mechanism (where it's enforced), a strength (how unbypassable it is), and a logging shape (what event it emits).

Strong vs. medium vs. weak

Strong: mechanically blocked unless an explicit, named, logged bypass is in effect. Medium: default-on, with an obvious config opt-out (e.g. workflow.specsRequired: false). Weak: advisory — emitted as a warning event, not a block.

See the invariants reference for the full table.

Bypass discipline

Every overridable check has exactly one or two named bypass paths. Each bypass:

  • Names the invariant being overridden (e.g. I-3).
  • Scopes to a single phase or, for env-var bypasses, a single shell invocation.
  • Logs a flow.bypass event containing the reason, the source (env / config / command / flag), and the timestamp.

That last property is what makes the system honest. You can't quietly skip a gate — you can only loudly skip it.

The four bypass sources

SourceFormUse case
envXCODER_ALLOW_INTEGRATION_EDIT=1 git ...Hotfix on main; one shell invocation only.
configworkflow.commitDirectly: trueRepo opts in to direct-commit (e.g. solo dev, throwaway repo).
commandxc flow override I-7 --reason "..."Recorded ad-hoc bypass for the active task.
flaggit commit --no-verifyStandard git escape hatch; logged via post-commit event.

Asymmetry — autopilot vs. interactive

xCoder treats the two modes differently because their guarantees differ:

ModeWho decidesStrongest guarantee available
AutopilotxCoder TS code (deterministic)Strong — engine acceptance + xCoder evaluates every transition itself.
InteractiveLLM (the coding agent)Strong-mechanical for hooks (kernel-level block); medium for engine acceptance (engine observes; agent moves).

What the LLM can do

A motivated LLM can set XCODER_ALLOW_INTEGRATION_EDIT=1 if you let it run shell commands. xCoder doesn't try to outsmart the LLM — that's a losing game. What it does:

  • Make the bypass visible (named env var, logged event).
  • Make compliance the path of least resistance.
  • Make non-compliance auditable — you can read the bypass log and see exactly when discipline was abandoned.

Why not just prompt the agent harder?

Prompts decay. The agent's context window churns through tens of thousands of tokens; "always create a feature branch first" is a whisper that gets drowned out by the immediate task. Prompts also fail silently — the agent thinks it followed the rule but didn't.

Mechanism doesn't decay. The no-edit-on-integration policy fires every single time git commit is invoked. The FlowEngine evaluates acceptance every time a phase transition is requested. There's no "I forgot."

Pairing prompts and mechanism

xCoder still uses prompts (skills, the boot context, the flow-discipline framing). They're great for positive direction — telling the agent what to do. Mechanism handles negative direction — refusing what shouldn't happen. The two layers don't compete; they compose.

Next