Skip to main content
Version: MVP

Intent Locks

5 min readCore conceptUpdated 2026-05-19

What you'll learn

  • What an intent lock is and why every task gets one.
  • The split between the user's request and the runtime's accepted interpretation.
  • When a runner should pause for a scope update vs. continue.
  • How locks integrate with case files, handoffs, and the work graph.

Intent lock

The runtime's accepted interpretation of a task — explicit, durable, and separate from the original request. The lock declares what's in scope, what's out of scope, how much autonomy the runner has, and what conditions should stop the run.

Why bother?

Long-running agent work drifts. Mid-run, a new finding makes a nearby change tempting. A tool returns output that implies "I should also fix this related thing." Partial progress nudges the runner toward "while I'm here, let me clean up..." Each step in isolation looks reasonable; the cumulative effect is scope creep that nobody approved.

Intent locks make that drift visible and reviewable. The lock is what the runtime committed to before the work began — every later decision can be checked against it.

What a lock records

Field
Type
Purpose
original_request
text
The user's words. Preserved verbatim — never edited to "make the runtime sound smarter."
objective
text
The runtime's short statement of what success looks like.
accepted_interpretation
text
How the runtime understood the request. The interesting field — this is where ambiguity gets named.
in_scope
item[]
Concrete work the run is allowed to perform.
excluded
item[]
Explicit out-of-scope items. The runner must stop or ask before touching these.
allowed_autonomy
level
How far the runner may go without checking in — file edits, shell, GitHub writes, memory.write, etc.
stop_conditions
condition[]
Trigger conditions that should halt the run pending operator review.
scope_change_rules
rule
How the runner should request a scope update if the lock doesn't fit.

The lock persists as a craik.intent_lock record and is referenced from the matching case file and every subsequent handoff. Handoff contracts include an intent-lock field so future handoff writers can preserve the same boundary across runs.

When a runner should pause

  1. The requested work no longer matches the accepted interpretation. Mid-run discovery: the user really meant something narrower or wider. Stop and ask for a lock update.

  2. A required action is listed as out of scope. The runner identifies a step it would have to take to finish, but excluded forbids it. Stop and ask.

  3. The task would cross a stop condition. A stop condition fired (reviewer required, budget exceeded, contradiction surfaced). Halt and write a handoff that names what stopped the run.

  4. The task requires new autonomy not listed in the lock. The runner needs a capability the lock doesn't grant (e.g., wants to run a shell command but allowed_autonomy doesn't include shell). Request the grant or stop.

How locks compose with the rest of the runtime

Intent locks aren't a separate system — they're a typed object that other parts of the runtime cite:

Case files

Every case file references its intent lock id, so the runner sees the boundary alongside the evidence and verification plan.

Handoffs

Handoffs reference the same lock id. The next agent picks up with the original boundary, not without it.

Policy

The policy envelope and the intent lock together describe what's permitted. The lock declares scope; the envelope declares capability.

Work graph

Locks are queryable nodes — useful when auditing how a task's interpretation evolved across multiple runs.

Use intent locks day-to-day

craik task create --out-of-scope "ADR edits" --out-of-scope "schema migrations"

The task-creation command produces the matching intent lock from the flags you pass.

craik intent show task_review_docs

Print the lock by task id or intent-lock id. Useful for review before authorizing a run.

craik case build task_review_docs

Ensures the task has an intent lock and includes its id in the resulting case file.

Why locks are separate from the request

It's tempting to skip locks and just "do what the user asked." That conflates two distinct things:

  • What the user said — preserved as original_request.
  • What the runtime committed to do about it — the lock.

When those two diverge — and they do, often — having both addressable makes review tractable. You can ask: "Did the runtime's accepted interpretation match what the user actually wanted?" and have a machine-readable answer.

What's next