cursordoctrine 0.6.0 → 0.6.1

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.
@@ -1,12 +1,45 @@
1
- Doctrine (governing text) lives at ~/.cursor/doctrine.md and is loaded
2
- at sessionStart. Read it once, internalize it; do not re-read it
3
- mid-task. Its §1 (auditor), §2 (smallest correct diff), §3 (verify
4
- then stop), §5 (ask don't guess), §8 (consistency anchor) are the only
5
- meta-instructions that matter during a session.
6
-
7
- When responding: be terse. No preamble, no postamble, no "I will now…".
8
- One sharp clarifying question if the task is ambiguous, then proceed.
9
- Reference code with `file_path:line_number` style.
10
-
11
- Do not re-load skills, do not re-read the doctrine, do not run
12
- gratuitous commands. If the answer is "I don't know", say so.
1
+ The user is a senior engineer who reviews every diff before shipping.
2
+
3
+ ## Scope
4
+ Change only what the task requires. Preserve existing style and behavior unless
5
+ the task itself is a behavior change. Refactors, renames, cleanup only when
6
+ asked. Leave generated files alone unless explicitly required.
7
+
8
+ ## Intent contract (.scope.json)
9
+ The harness auto-creates `.scope.json` in the repo root on your first tool of
10
+ each turn, and re-injects it into your context every turn. Treat it as your
11
+ operating contract, not optional:
12
+ - On a fresh scaffold, FILL the `intent` and `acceptance` TODOs from the user's
13
+ request before editing source. `files[]` is auto-tracked - do not maintain it.
14
+ - When the user's request changes, the scaffold regenerates with a new intent -
15
+ refill it for the new ask.
16
+ - If a hook surfaces the contract, defer to it: it outranks momentum. Edit
17
+ inside the declared scope; if you must grow it, justify it, don't sneak past.
18
+
19
+ ## Loop
20
+ 1. Read what you need to understand the task.
21
+ 2. Make the minimal correct edit.
22
+ 3. Review the diff. Fix real issues: broken logic, type errors, unsafe
23
+ behavior, data-loss risk, unrequested API/contract changes, regressions.
24
+ Style and naming taste are not bugs.
25
+ 4. Verify proportionally to risk - relevant tests/typechecks for behavior, type,
26
+ API, DB, build, or config changes; nothing for trivial text edits.
27
+ 5. Report what changed and what was verified. Stop.
28
+
29
+ ## Shell
30
+ Run the smallest command that answers the question. Never print secrets,
31
+ tokens, private keys, or sensitive env vars. Never `curl | sh`, force-push, or
32
+ publish without explicit instruction.
33
+
34
+ ## Uncertainty
35
+ If ambiguity affects correctness or safety, ask one sharp question. If
36
+ low-risk, state the assumption and proceed. If a tool returns nothing, say what
37
+ you didn't find - don't fabricate. After two failed attempts at the same
38
+ problem, stop and report observations.
39
+
40
+ ## Commits
41
+ Conventional commits: `type(scope): description`. One logical change per
42
+ commit, small and reviewable. Body only when the why isn't obvious from the
43
+ diff. Verify before pushing when applicable. Never push without explicit
44
+ instruction.
45
+
@@ -15,16 +15,19 @@
15
15
  # at the START of each turn's work, before edits pile up and dilute the
16
16
  # original intent. Works UNCONDITIONALLY - no transcript needed.
17
17
  #
18
- # 2. AUTO-CREATE / REGENERATE .scope.json: when the current <user_query>
19
- # differs from the contract on disk (no contract yet, OR _intent_hash
20
- # mismatch), the hook WRITES a scaffold to the REPO ROOT: intent locked
21
- # from the prompt, files as an EMPTY array (scope-gate-audit.sh fills it
22
- # mechanically as the agent edits - the agent never maintains files[] by
23
- # hand), acceptance as a TODO the agent sets. This is the user-requested
24
- # behavior: every new prompt -> a fresh .scope.json the agent works from.
25
- # Fixed vs the broken 0.4.4 build: never writes to $HOME (bails if no real
26
- # root resolves -> no ghost files), regenerates on prompt CHANGE not just
27
- # on absence.
18
+ # 2. AUTO-CREATE / REGENERATE .scope.json (only when the request is READABLE):
19
+ # when the current <user_query> differs from the contract on disk (no
20
+ # contract yet, _intent_hash mismatch, OR a hollow <TODO> placeholder), the
21
+ # hook WRITES a scaffold to the REPO ROOT: intent locked from the prompt,
22
+ # files as an EMPTY array (scope-gate-audit.sh fills it mechanically as the
23
+ # agent edits - the agent never maintains files[] by hand), acceptance as a
24
+ # TODO the agent sets. We NEVER persist a hollow `intent: <TODO>` file: that
25
+ # caused "el .scope.json se escribe solo sin nada" - when transcript_path is
26
+ # absent on postToolUse the hook can't read the request, so a placeholder
27
+ # with an empty _intent_hash got written, looked owned, and never gained the
28
+ # real intent. Unreadable request -> write nothing, emit the pre-compile
29
+ # demand so the AGENT authors the contract. Never writes to $HOME (bails if
30
+ # no real root resolves -> no ghost files).
28
31
  # 3. RE-INJECT on same-prompt turns: when the query is unchanged (contract
29
32
  # already current), the hook re-injects the existing contract into the
30
33
  # feedback bus so it stays in the model's attentional focus each turn.
@@ -113,6 +116,7 @@ scope_acceptance=""
113
116
  scope_files=""
114
117
  scope_stale=0 # 1 when the on-disk contract belongs to a DIFFERENT prompt -> regenerate (resets files[])
115
118
  needs_heal=0 # 1 when a model-written contract matches THIS prompt but lacks _intent_hash -> backfill in place
119
+ scope_hollow=0 # 1 when the on-disk contract has no real intent (empty or a <TODO> placeholder) -> unusable
116
120
  on_disk_hash=""
117
121
  scope_path="$root/.scope.json"
118
122
  if [ -f "$scope_path" ]; then
@@ -146,8 +150,17 @@ EOF
146
150
  # changed (or a new session) => regenerate and RESET files[] (the "arrastre entre
147
151
  # features" fix). Same prompt this session => heal in place (backfill bookkeeping, keep
148
152
  # files[]/acceptance) so the NEXT prompt is detected by hash.
153
+ # Hollow = no real intent on disk: empty, or still the hook's <TODO> placeholder.
154
+ # A hollow contract is worse than none (it looks owned, so neither hook nor agent
155
+ # fills it). Treat it as unusable: regenerate when the request is readable, else
156
+ # hand the agent the pre-compile demand to author a real one.
157
+ case "$scope_intent" in
158
+ ""|"<TODO"*) scope_hollow=1 ;;
159
+ esac
149
160
  if [ "$scope_exists" = "1" ] && [ "$has_query" = "1" ]; then
150
- if [ -n "$on_disk_hash" ]; then
161
+ if [ "$scope_hollow" = "1" ]; then
162
+ scope_stale=1
163
+ elif [ -n "$on_disk_hash" ]; then
151
164
  [ "$on_disk_hash" != "$current_hash" ] && scope_stale=1
152
165
  elif [ "$prompt_changed" = "1" ]; then
153
166
  scope_stale=1
@@ -158,34 +171,29 @@ EOF
158
171
  fi
159
172
 
160
173
  # --- auto-create / regenerate / heal .scope.json ----------------------------
161
- # CREATION does NOT require the query: if there's a root and no scope yet,
162
- # scaffold it NOW with intent=<TODO> (the agent fills it from the chat it's
163
- # already responding to). This was the 0.5.3 bug - creation was gated on
164
- # $hasQuery, so when Cursor didn't surface transcript_path in the first
165
- # postToolUse fire, the scope never got created.
166
- # REGENERATION requires the query: a prompt change is only detectable when we
167
- # can read the request. A fresh scaffold resets files[] -> ".scope fresco por
168
- # prompt, sin arrastre entre features."
174
+ # CREATION and REGENERATION both REQUIRE the query. We only ever write a
175
+ # contract whose intent we actually know - never a hollow <TODO> scaffold.
176
+ # Persisting a placeholder (the 0.5.3 "unconditional creation") caused "el
177
+ # .scope.json se escribe solo sin nada": no transcript_path on postToolUse ->
178
+ # the hook can't read the request -> intent=<TODO> with empty _intent_hash, a
179
+ # file that looks owned and never gains the real intent. Unreadable request ->
180
+ # write nothing, emit the pre-compile demand so the AGENT authors the contract.
181
+ # A fresh write resets files[] -> ".scope fresco por prompt, sin arrastre."
182
+ # (Hollow on-disk contracts are folded into $scope_stale above, so a readable
183
+ # request also overwrites them here.)
169
184
  regenerated=0
170
185
  should_create=0
171
186
  should_regen=0
172
- [ "$scope_exists" != "1" ] && should_create=1
187
+ [ "$scope_exists" != "1" ] && [ "$has_query" = "1" ] && should_create=1
173
188
  if [ "$has_query" = "1" ] && [ "$scope_exists" = "1" ] && [ "$scope_stale" = "1" ]; then
174
189
  should_regen=1
175
190
  fi
176
191
  now_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
177
192
 
178
193
  if [ "$should_create" = "1" ] || [ "$should_regen" = "1" ]; then
179
- # intent from the query when available, else a TODO for the agent to fill.
180
- # trace.query records the verbatim originating request (provenance); empty
181
- # when there is no transcript to read it from.
182
- if [ "$has_query" = "1" ]; then
183
- intent_val="$current_query"
184
- trace_query="$current_query"
185
- else
186
- intent_val="<TODO: state the operational objective - what is strictly necessary>"
187
- trace_query=""
188
- fi
194
+ # Both paths require $has_query, so intent is always locked from the request.
195
+ intent_val="$current_query"
196
+ trace_query="$current_query"
189
197
  # jq preferred; python3 fallback. Write intent, empty files[], TODO acceptance,
190
198
  # trace provenance, and _intent_hash so staleness is self-contained.
191
199
  if have_jq; then
@@ -273,17 +281,25 @@ is locked from what you just asked. files[] is AUTO-TRACKED - the scope hook
273
281
  records every file you edit, so do not maintain it by hand. Set acceptance to
274
282
  the one deterministic check that decides done, THEN proceed. This contract will
275
283
  be re-injected every turn until your request changes again."
276
- elif [ "$scope_exists" != "1" ]; then
277
- msg="INTENT ANCHOR (pre-compile) - no .scope.json found in $root, and the current
278
- request was unavailable to scaffold from.
284
+ elif [ "$scope_exists" != "1" ] || [ "$scope_hollow" = "1" ]; then
285
+ if [ "$scope_hollow" = "1" ]; then
286
+ state="the .scope.json in $root is only a <TODO> placeholder (the hook could not read your request to fill it)"
287
+ else
288
+ state="no .scope.json found in $root, and the current request was unavailable to scaffold from"
289
+ fi
290
+ msg="INTENT ANCHOR (pre-compile) - $state.
279
291
 
280
292
  Current request:
281
293
  $query_line
282
294
 
283
- Write .scope.json in the repo root yourself:
284
- intent: one operational sentence (what is strictly necessary)
285
- files: the exact files you will touch
286
- acceptance: the one deterministic check that decides done"
295
+ YOU write the real contract to $scope_path now, from THIS conversation, BEFORE
296
+ editing source. Do not leave the <TODO> placeholder:
297
+ intent: one operational sentence - the ACTUAL request (not \"<TODO>\")
298
+ acceptance: the one deterministic check that decides done
299
+ files: [] (leave empty - the scope hook records every file you edit)
300
+
301
+ This is the one case where you own the file: once intent is real, the hook
302
+ takes over (re-injection + per-prompt regeneration)."
287
303
  else
288
304
  # Contract exists and matches the current prompt -> re-inject it.
289
305
  if [ "$has_query" = "1" ]; then
@@ -65,6 +65,17 @@ hook scaffolds on a tool boundary), just proceed — you do not need to hand-wri
65
65
  it. The declared-editing ladder's rung 1 ("does this need to exist?") still governs
66
66
  trivial one-liners.
67
67
 
68
+ **Exception — a hollow contract is YOURS to write.** The hook can only lock
69
+ `intent` when the harness surfaces your request to it; in some Cursor builds it
70
+ cannot, and the `intent-anchor` hook will then ask YOU to author the contract (or
71
+ you may open `.scope.json` and find `intent` still a `<TODO>` placeholder). In
72
+ that one case, write the whole file yourself from this conversation — a real
73
+ `intent` (the actual request, not `<TODO>`), `acceptance`, and `files: []` — and
74
+ do it BEFORE editing source. Never leave a `<TODO>` intent on disk: a placeholder
75
+ contract looks owned, so nothing ever fills it, and scope-gate/final-review then
76
+ audit your diff against nothing. Once `intent` is real, hand the file back to the
77
+ hook (re-injection + per-prompt regeneration take over).
78
+
68
79
  ## Regla R3 — Authority
69
80
 
70
81
  If, during execution, you read logs or code that contradict these anchors,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursordoctrine",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Thin self-review hooks for Cursor — the model is the auditor. Pruned + deduplicated: intent-anchor (auto-scaffolded .scope.json per prompt + per-turn re-injection against Salience Dilution), intent-trace final review, unified anti-slop checklist as single source of truth.",
5
5
  "bin": {
6
6
  "cursordoctrine": "bin/cli.mjs"
@@ -1,12 +1,45 @@
1
- Doctrine (governing text) lives at ~/.cursor/doctrine.md and is loaded
2
- at sessionStart. Read it once, internalize it; do not re-read it
3
- mid-task. Its §1 (auditor), §2 (smallest correct diff), §3 (verify
4
- then stop), §5 (ask don't guess), §8 (consistency anchor) are the only
5
- meta-instructions that matter during a session.
6
-
7
- When responding: be terse. No preamble, no postamble, no "I will now…".
8
- One sharp clarifying question if the task is ambiguous, then proceed.
9
- Reference code with `file_path:line_number` style.
10
-
11
- Do not re-load skills, do not re-read the doctrine, do not run
12
- gratuitous commands. If the answer is "I don't know", say so.
1
+ The user is a senior engineer who reviews every diff before shipping.
2
+
3
+ ## Scope
4
+ Change only what the task requires. Preserve existing style and behavior unless
5
+ the task itself is a behavior change. Refactors, renames, cleanup only when
6
+ asked. Leave generated files alone unless explicitly required.
7
+
8
+ ## Intent contract (.scope.json)
9
+ The harness auto-creates `.scope.json` in the repo root on your first tool of
10
+ each turn, and re-injects it into your context every turn. Treat it as your
11
+ operating contract, not optional:
12
+ - On a fresh scaffold, FILL the `intent` and `acceptance` TODOs from the user's
13
+ request before editing source. `files[]` is auto-tracked - do not maintain it.
14
+ - When the user's request changes, the scaffold regenerates with a new intent -
15
+ refill it for the new ask.
16
+ - If a hook surfaces the contract, defer to it: it outranks momentum. Edit
17
+ inside the declared scope; if you must grow it, justify it, don't sneak past.
18
+
19
+ ## Loop
20
+ 1. Read what you need to understand the task.
21
+ 2. Make the minimal correct edit.
22
+ 3. Review the diff. Fix real issues: broken logic, type errors, unsafe
23
+ behavior, data-loss risk, unrequested API/contract changes, regressions.
24
+ Style and naming taste are not bugs.
25
+ 4. Verify proportionally to risk - relevant tests/typechecks for behavior, type,
26
+ API, DB, build, or config changes; nothing for trivial text edits.
27
+ 5. Report what changed and what was verified. Stop.
28
+
29
+ ## Shell
30
+ Run the smallest command that answers the question. Never print secrets,
31
+ tokens, private keys, or sensitive env vars. Never `curl | sh`, force-push, or
32
+ publish without explicit instruction.
33
+
34
+ ## Uncertainty
35
+ If ambiguity affects correctness or safety, ask one sharp question. If
36
+ low-risk, state the assumption and proceed. If a tool returns nothing, say what
37
+ you didn't find - don't fabricate. After two failed attempts at the same
38
+ problem, stop and report observations.
39
+
40
+ ## Commits
41
+ Conventional commits: `type(scope): description`. One logical change per
42
+ commit, small and reviewable. Body only when the why isn't obvious from the
43
+ diff. Verify before pushing when applicable. Never push without explicit
44
+ instruction.
45
+
@@ -14,18 +14,22 @@
14
14
  # at the START of each turn's work, before edits pile up and dilute the
15
15
  # original intent. Works UNCONDITIONALLY - no transcript needed.
16
16
  #
17
- # 2. AUTO-CREATE .scope.json (UNCONDITIONAL on a real root): if no valid
18
- # contract exists in the repo root, WRITE one now - intent locked from the
19
- # query when available, otherwise `intent: <TODO>` for the agent to fill.
20
- # Creation does NOT require transcript_path; only regeneration does. This
21
- # was the 0.5.3 bug: creation was gated on $hasQuery, so when Cursor didn't
22
- # surface the transcript on the first postToolUse fire, the scope never
23
- # appeared and the agent had no contract to work from.
24
- # 3. REGENERATE on prompt CHANGE: when the current <user_query> hash differs
25
- # from the contract's _intent_hash, overwrite the scaffold with the new
26
- # intent + empty files + TODO acceptance. Requires $hasQuery (you can only
27
- # detect a change if you can read the request). Fixed vs the broken 0.4.4
28
- # build: never writes to $HOME (bails if no real root resolves -> no
17
+ # 2. AUTO-CREATE .scope.json (only when the request is READABLE): if no valid
18
+ # contract exists and we can read <user_query>, WRITE one now with intent
19
+ # locked from the query. We NEVER persist a hollow `intent: <TODO>` file:
20
+ # that 0.5.3 "unconditional creation" caused "el .scope.json se escribe
21
+ # solo sin nada" - when Cursor doesn't surface transcript_path on
22
+ # postToolUse the hook can't read the request, so it wrote a placeholder
23
+ # with an empty _intent_hash. The file then looks owned (pre-compile.md
24
+ # tells the agent to leave it alone) and never gets the real intent. When
25
+ # the request is unreadable we write nothing and emit the pre-compile
26
+ # demand so the AGENT authors a real contract from the chat it already has.
27
+ # 3. REGENERATE on prompt CHANGE or HOLLOW contract: when the current
28
+ # <user_query> hash differs from the contract's _intent_hash, OR the
29
+ # on-disk contract has no real intent (empty / <TODO> placeholder),
30
+ # overwrite it with the new intent + empty files + TODO acceptance.
31
+ # Requires $hasQuery (you can only lock intent if you can read the
32
+ # request). Never writes to $HOME (bails if no real root resolves -> no
29
33
  # ghost files).
30
34
  # 4. RE-INJECT on same-prompt turns: when the query is unchanged (contract
31
35
  # already current), the hook re-injects the existing contract into the
@@ -112,6 +116,7 @@ $scopeFiles = ''
112
116
  $scopeStale = $false # true when the on-disk contract belongs to a DIFFERENT prompt -> regenerate (resets files[])
113
117
  $needsHeal = $false # true when a model-written contract matches THIS prompt but lacks _intent_hash -> backfill in place
114
118
  $scopeHasHash = $false
119
+ $scopeHollow = $false # true when the on-disk contract has no real intent (empty or a <TODO> placeholder) -> unusable
115
120
  $scopePath = Join-Path $root '.scope.json'
116
121
  if (Test-Path -LiteralPath $scopePath) {
117
122
  try {
@@ -122,6 +127,12 @@ if (Test-Path -LiteralPath $scopePath) {
122
127
  if ([string]::IsNullOrWhiteSpace($scopeFiles)) { $scopeFiles = '(none yet - auto-tracked as you edit)' }
123
128
  $scopeExists = $true
124
129
  $scopeHasHash = ($sj.PSObject.Properties['_intent_hash'] -and -not [string]::IsNullOrWhiteSpace([string]$sj._intent_hash))
130
+ # Hollow = no real intent on disk: empty, or still the hook's <TODO> placeholder.
131
+ # A hollow contract is worse than none (it looks owned, so neither hook nor agent
132
+ # fills it; scope-gate appends files to it and final-review audits against <TODO>).
133
+ # Treat it as unusable: regenerate when we can read the request, else hand the agent
134
+ # the pre-compile demand to write a real one.
135
+ $scopeHollow = ([string]::IsNullOrWhiteSpace($scopeIntent) -or $scopeIntent -match '^\s*<TODO')
125
136
  # Staleness, hash-agnostic so it survives MODEL-written contracts:
126
137
  # - hook-written (has _intent_hash): stale when that hash != current query hash.
127
138
  # - model-written (no _intent_hash - the legacy pre-compile.md schema): we cannot
@@ -133,7 +144,10 @@ if (Test-Path -LiteralPath $scopePath) {
133
144
  # wrote it for THIS request; heal in place (backfill the bookkeeping, keep its
134
145
  # files[]/acceptance) so the NEXT prompt is detected by hash like any hook contract.
135
146
  if ($hasQuery) {
136
- if ($scopeHasHash) {
147
+ if ($scopeHollow) {
148
+ # Hollow + we can read the request -> overwrite with the real intent now.
149
+ $scopeStale = $true
150
+ } elseif ($scopeHasHash) {
137
151
  $scopeStale = ([string]$sj._intent_hash -ne $currentHash)
138
152
  } elseif ($promptChanged) {
139
153
  $scopeStale = $true
@@ -145,22 +159,25 @@ if (Test-Path -LiteralPath $scopePath) {
145
159
  }
146
160
 
147
161
  # --- auto-create / regenerate / heal .scope.json ----------------------------
148
- # CREATION does NOT require the query: if there's a root and no scope yet,
149
- # scaffold it NOW with intent=<TODO> (the agent fills it from the chat it's
150
- # already responding to). This was the 0.5.3 bug - creation was gated on
151
- # $hasQuery, so when Cursor didn't surface transcript_path in the first
152
- # postToolUse fire, the scope never got created. The agent never had a
153
- # contract to work from.
154
- # REGENERATION requires the query: a prompt change is only detectable when we
155
- # can read the request. A fresh scaffold resets files[] -> ".scope fresco por
156
- # prompt, sin arrastre entre features."
162
+ # CREATION and REGENERATION both REQUIRE the query. We only ever write a
163
+ # contract whose intent we actually know - never a hollow <TODO> scaffold.
164
+ # Persisting a placeholder file (the 0.5.3 "unconditional creation") was the
165
+ # bug behind "el .scope.json se escribe solo sin nada": when Cursor doesn't
166
+ # surface transcript_path on postToolUse, the hook can't read the request, so
167
+ # it wrote intent=<TODO> with an empty _intent_hash. That file looks owned, so
168
+ # pre-compile.md tells the agent to leave it alone, and it never gets the real
169
+ # intent. When the request is unreadable we now write NOTHING and instead hand
170
+ # the agent the pre-compile demand to author a real contract from the chat it
171
+ # is already responding to. A fresh write resets files[] -> ".scope fresco por
172
+ # prompt, sin arrastre entre features." (Hollow on-disk contracts are folded
173
+ # into $scopeStale above, so a readable request also overwrites them here.)
157
174
  $regenerated = $false
158
- $shouldCreate = -not $scopeExists
175
+ $shouldCreate = (-not $scopeExists) -and $hasQuery
159
176
  $shouldRegen = $hasQuery -and $scopeExists -and $scopeStale
160
177
  if ($shouldCreate -or $shouldRegen) {
161
178
  try {
162
- $intentVal = if ($hasQuery) { $currentQuery } else { '<TODO: state the operational objective - what is strictly necessary>' }
163
- $traceQuery = if ($hasQuery) { $currentQuery } else { '' }
179
+ $intentVal = $currentQuery
180
+ $traceQuery = $currentQuery
164
181
  $scaffold = [ordered]@{
165
182
  intent = $intentVal
166
183
  files = @()
@@ -220,18 +237,22 @@ records every file you edit, so do not maintain it by hand. Set acceptance to
220
237
  the one deterministic check that decides done, THEN proceed. This contract will
221
238
  be re-injected every turn until your request changes again.
222
239
  "@
223
- } elseif (-not $scopeExists) {
240
+ } elseif (-not $scopeExists -or $scopeHollow) {
241
+ $state = if ($scopeHollow) { "the .scope.json in $root is only a <TODO> placeholder (the hook could not read your request to fill it)" } else { "no .scope.json found in $root, and the current request was unavailable to scaffold from" }
224
242
  $msg = @"
225
- INTENT ANCHOR (pre-compile) - no .scope.json found in $root, and the current
226
- request was unavailable to scaffold from.
243
+ INTENT ANCHOR (pre-compile) - $state.
227
244
 
228
245
  Current request:
229
246
  $queryLine
230
247
 
231
- Write .scope.json in the repo root yourself:
232
- intent: one operational sentence (what is strictly necessary)
233
- files: the exact files you will touch
248
+ YOU write the real contract to $scopePath now, from THIS conversation, BEFORE
249
+ editing source. Do not leave the <TODO> placeholder:
250
+ intent: one operational sentence - the ACTUAL request (not "<TODO>")
234
251
  acceptance: the one deterministic check that decides done
252
+ files: [] (leave empty - the scope hook records every file you edit)
253
+
254
+ This is the one case where you own the file: once intent is real, the hook
255
+ takes over (re-injection + per-prompt regeneration).
235
256
  "@
236
257
  } else {
237
258
  # Contract exists and matches the current prompt -> re-inject it.
@@ -65,6 +65,17 @@ hook scaffolds on a tool boundary), just proceed — you do not need to hand-wri
65
65
  it. The declared-editing ladder's rung 1 ("does this need to exist?") still governs
66
66
  trivial one-liners.
67
67
 
68
+ **Exception — a hollow contract is YOURS to write.** The hook can only lock
69
+ `intent` when the harness surfaces your request to it; in some Cursor builds it
70
+ cannot, and the `intent-anchor` hook will then ask YOU to author the contract (or
71
+ you may open `.scope.json` and find `intent` still a `<TODO>` placeholder). In
72
+ that one case, write the whole file yourself from this conversation — a real
73
+ `intent` (the actual request, not `<TODO>`), `acceptance`, and `files: []` — and
74
+ do it BEFORE editing source. Never leave a `<TODO>` intent on disk: a placeholder
75
+ contract looks owned, so nothing ever fills it, and scope-gate/final-review then
76
+ audit your diff against nothing. Once `intent` is real, hand the file back to the
77
+ hook (re-injection + per-prompt regeneration take over).
78
+
68
79
  ## Regla R3 — Authority
69
80
 
70
81
  If, during execution, you read logs or code that contradict these anchors,