@onedotmint/charterleaf 0.4.0 → 0.4.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.
package/AGENTS_SNIPPET.md CHANGED
@@ -28,7 +28,12 @@ With automation enabled, use supplied constraints or explicitly read the
28
28
  named specs before retrying a gated write/edit. Use standard `write`/`edit`
29
29
  for spec changes: Confirm requires each approval; Reference allows no
30
30
  spec changes. Do not bypass these cooperative checks with Bash or another
31
- writer. No durable change means no spec edit; never relax a spec merely
31
+ writer. During normal development or investigation, capture newly established
32
+ or newly discovered knowledge only with reliable evidence, long-term value and a
33
+ documentation/discoverability gap; no code change or prior spec is required.
34
+ Prefer short scoped references to authoritative owners over copying documents.
35
+ Explicit no-file-write/read-only instructions override Auto. No justified durable
36
+ knowledge means no spec edit; never relax a spec merely
32
37
  to accommodate conflicting code. Preserve user/parallel work and keep
33
38
  state, plans, evidence and history in their existing authoritative owners,
34
39
  not in a new Charterleaf workflow.
package/README.md CHANGED
@@ -36,10 +36,10 @@ Install once in Pi to discover the bundled Extension and Skill:
36
36
  pi install npm:@onedotmint/charterleaf
37
37
  ```
38
38
 
39
- **v0.4 is not yet published.** Until publication, install the local package
40
- with `pi install /absolute/path/to/charterleaf` (a package directory, not a
41
- `.tgz` file). For the standalone CLI, pack with `npm pack`, then use `npm install -g ./onedotmint-charterleaf-0.4.0.tgz`; after publication,
42
- use `npm install -g @onedotmint/charterleaf`.
39
+ For the standalone CLI, use `npm install -g @onedotmint/charterleaf`.
40
+ For local development, use `pi install /absolute/path/to/charterleaf` (a package
41
+ directory, not a `.tgz` file). To test the CLI from a local package, run `npm pack`
42
+ then `npm install -g ./onedotmint-charterleaf-0.4.1.tgz`.
43
43
 
44
44
  Restart Pi or run `/reload` after installation, then use `/charterleaf` in each
45
45
  project you want to enable. To stop automation, choose **Not Enable** in that
@@ -128,18 +128,55 @@ requires fresh delivery; successful standard spec writes invalidate loaded state
128
128
  Compaction invalidates loaded context so constraints must be delivered
129
129
  again. This is task-local bookkeeping, not a watcher or persistent index.
130
130
 
131
+ Knowledge capture happens **during normal work**, not just after code changes:
132
+ development, debugging, substantive code/test/architecture investigation and
133
+ accepted technical decisions can establish or uncover durable knowledge. A project
134
+ with only `specs/.charterleaf.json` is a valid initial state. When evidence warrants
135
+ it, the Agent creates a concise scoped spec using ordinary authorized `write`/`edit`;
136
+ Core immediately routes its `id`/`applies_to` on later related code reads or edits.
137
+ There is no initialization scan, document quota or adoption wizard.
138
+
131
139
  At successful, actionable `agent_before_settle`, the Extension may request
132
- **at most one final semantic review per task** after development. It
133
- separates successful standard-tool paths whose observed content is still
140
+ **at most one final semantic review per task**. Actual development remains a
141
+ fallback, but an unresolved evidenced knowledge candidate can also trigger it
142
+ without file changes or Git. The existing Agent makes the semantic judgment; the
143
+ Extension observes standalone assistant lifecycle comments outside fenced examples
144
+ (`charterleaf: review` or an explicit `covered`/`no-value`/`proposed` knowledge
145
+ conclusion; an exact terminal conclusion also works inline), never interprets read
146
+ counts as knowledge. A generic `done` cannot suppress the development fallback.
147
+ In an empty-spec project, a development fallback
148
+ remains eligible when the Agent says `no-value`; this gives the existing single
149
+ review a chance to catch a missed discovery gap. These are
150
+ cooperative intent signals, not evidence or permissions. Already judged/captured,
151
+ proposed or effectively documented knowledge needs no repeat review when a
152
+ completion signal is observed. If the Agent
153
+ omits its completion signal, the bounded fallback can repeat an already answered
154
+ knowledge judgment; zero redundant reviews is not guaranteed. Successful
155
+ structurally valid standard spec writes mark capture complete; later actual code
156
+ edits or distinct candidates may rearm. Opaque activity after capture still uses
157
+ bounded Git comparison when available. Ordinary views, queries and chat do not
158
+ by themselves request another model call.
159
+
160
+ For the development fallback, the Extension separates successful standard-tool
161
+ paths whose observed content is still
134
162
  unchanged from **unattributed** changes observed against the task's Git
135
163
  baseline. Neither a touched file nor a dirty worktree proves ownership
136
164
  of every line. Preserve existing user dirt and parallel work; unattributed
137
165
  changes alone must not justify spec edits. No automatic revert, stage,
138
166
  commit or push is performed.
139
167
 
140
- **No durable knowledge means no spec edit.** Keep only established,
141
- non-obvious, mistake-preventing contracts, invariants, boundaries,
142
- pitfalls or accepted decisions — not code summaries or task logs. A
168
+ **No justified durable knowledge means no spec edit.** Distinguish **newly
169
+ established** contracts/decisions from **newly discovered** existing constraints;
170
+ both can qualify even in a code-read-only investigation. Require reliable evidence
171
+ (accepted decisions, authoritative docs, tests or sufficiently verified behavior),
172
+ future mistake-prevention value, and a documentation/discoverability gap. If an
173
+ existing authoritative document covers it, prefer a short routed reference when
174
+ needed; otherwise do nothing. Keep non-obvious contracts, invariants, boundaries,
175
+ pitfalls or accepted decisions — not code summaries, progress, debugging logs,
176
+ temporary plans/todos or speculation. Current code alone is not an accepted
177
+ correct contract. Explicit **no-file-write/read-only** instructions override Auto:
178
+ report a finding/proposal without writing. Confirm/Reference retain their existing
179
+ permissions. A
143
180
  code/spec conflict is not permission to accommodate incorrect code:
144
181
  distinguish implementation bugs, authoritatively obsolete specs and
145
182
  explicitly intended contract changes. When uncertain, preserve the spec
@@ -256,7 +293,8 @@ specs/
256
293
 
257
294
  A spec records a constraint whose absence would plausibly cause a future
258
295
  developer or agent to make a wrong decision. It should be non-obvious,
259
- durable, **already true**, and mistake-preventing. Good fits: contracts,
296
+ durable, **already true or explicitly accepted**, evidenced, mistake-preventing,
297
+ and not already effectively documented. Good fits: contracts,
260
298
  invariants, architecture boundaries, non-goals, recurring pitfalls,
261
299
  accepted rationale, verification constraints.
262
300
 
@@ -274,8 +312,8 @@ review comes first, or a multi-step migration is underway. A change must
274
312
  contain at least one of `ADD`, `MODIFY`, `REMOVE`; `PRESERVE` alone is
275
313
  not a change.
276
314
 
277
- Otherwise, implement, verify, and update the living spec if durable truth
278
- changed. After an accepted change is implemented, merge the durable
315
+ Otherwise, implement/verify as needed and maintain the living spec when durable
316
+ truth changed **or an existing valuable constraint lacks effective documentation**. After an accepted change is implemented, merge the durable
279
317
  result into living specs and delete the change file. Git keeps history.
280
318
 
281
319
  ## Agent conventions
@@ -348,34 +386,59 @@ Verification commands:
348
386
 
349
387
  ```bash
350
388
  npm test
351
- npm run test:pi # requires compatible Pi on PATH; isolated RPC UI smoke, no model calls
389
+ npm run test:pi # isolated real RPC/UI smoke; no model calls
390
+ CHARTERLEAF_MODEL=deepseek/deepseek-flash npm run test:organic # paid isolated A–G acceptance
391
+ CHARTERLEAF_MODEL=deepseek/deepseek-flash CHARTERLEAF_BASELINE=5d470db npm run test:context # paid comparison against v0.4.0
392
+ node scripts/organic-smoke.js --self-test # harness/oracle check, no model calls
352
393
  node --test tests/extension.test.js
353
394
  npm pack --dry-run
354
395
  node bin/charterleaf.js related extensions/charterleaf.js
355
396
  node bin/charterleaf.js lint
356
397
  ```
357
398
 
358
- Validated on Node.js 24.21.0 and Pi 1.1.0: 149 automated Core/Extension tests,
399
+ Validated on Node.js 24.21.0 and Pi 1.1.0: 183 automated Core/Extension tests,
359
400
  structural lint, dry-run npm packaging, and real Pi RPC loading/native selection
360
401
  for cancellation, mode changes, disable and cross-process restore. The RPC smoke
361
402
  uses isolated temporary projects/settings and makes no model calls.
362
403
 
363
- Independent acceptance also exercised real `openai/gpt-5.6-terra` sessions in
364
- isolated Git projects: ordinary refactors, intentional API updates, durable
365
- rounding knowledge, conflicts, all three modes, no-UI Confirm, parallel nested
366
- tools, oversized specs, dirty/concurrently modified files, Bash writes and
367
- invalid metadata. Evidence was checked against tool results and file diffs,
368
- not assistant claims. These bounded examples do not prove model reliability
369
- on every task or provider. The original activation message measured 8,450 UTF-8
370
- bytes (8.25 KiB), mostly the full Skill; it was not all native Pi context or a
371
- Token count. After the dedicated context audit, compact rules plus activation
372
- measure about 2 KiB: in a controlled first-request comparison the Charterleaf
373
- increment fell from 1,720 to 403 provider input tokens, with identical native
374
- prompt/tools. Smaller total context does not guarantee the same reduction in
375
- uncached input charges: cache-hit sizes vary. Substantive work still requests
376
- at most one extra model review. A model
377
- initially misreported a blocked edit as completed; clearer error text corrected
378
- the observed regression, but final claims still need independent verification.
404
+ The 0.4.1 organic-capture acceptance used real `deepseek/deepseek-flash` sessions
405
+ in isolated Git projects. Actual tool results, approval records, specs, routing,
406
+ file snapshots/diffs, original tests and an independent exact-money oracle were
407
+ checked, not just assistant claims. Empty-spec development and investigation,
408
+ ordinary-query non-triggers, authoritative-document references, bug-versus-contract
409
+ handling, cross-session reuse and mode/read-only/no-UI permissions all have passing
410
+ evidence. **The strict matrix was not consistently all-green:** the latest focused
411
+ A→F1→F2 run passed A and F1, while F2 omitted its knowledge-completion comment and
412
+ received one redundant fallback review. Its actual routing, code behavior and
413
+ unchanged-spec checks passed. These bounded examples do not prove model reliability
414
+ on every task or provider, or guarantee zero redundant reviews.
415
+
416
+ The controlled organic-capture comparison (`scripts/context-smoke.js`) keeps cwd,
417
+ package paths, prompt and native tools identical. On `deepseek/deepseek-flash`:
418
+ disabled 1,616 input tokens; v0.4.0 baseline 2,036 (increment 420); organic runtime
419
+ 2,142 (increment 526), or **106 additional fixed input tokens**. Input here includes
420
+ cached input. Each chat made exactly one request and no tool calls; the full Skill
421
+ remains on demand. Absolute path tokenization and provider tokenizers vary, and
422
+ cache-hit sizes affect uncached charges, so these are measured examples, not fixed
423
+ billing guarantees. The script defaults to `HEAD`; set `CHARTERLEAF_BASELINE` to the
424
+ older revision being compared (the command above uses the v0.4.0 release commit).
425
+ Substantive work requests **zero or at most one** extra model review: observed
426
+ capture/coverage/proposal completion skips the fallback; empty-spec development can
427
+ still get the one fallback after a `no-value` judgment.
428
+
429
+ The real-model acceptance script retains raw RPC/JSONL, conversations, actual tool
430
+ records, before/after files, tracked/untracked diffs, validation output and individual
431
+ assertions in the printed private temporary directory, including failures. It copies
432
+ credentials only to isolated settings and removes that copy on normal/error
433
+ completion; a forced process kill may require deleting the private credential
434
+ copy manually. It does not
435
+ import personal extensions/instructions or modify other repositories, and it is not
436
+ an OS security sandbox. Both paid scripts require explicit `CHARTERLEAF_MODEL=provider/id`; neither chooses
437
+ another provider automatically. The requested model must be available;
438
+ `--case A,B,...` selects bounded scenarios. F cases run A first, then B only if A failed to capture; they reuse only
439
+ model-written knowledge in the same project across fresh sessions. A's failure
440
+ remains a failed assertion; a successful B→F chain does not conceal it. Model usage counts visible assistant responses, not hidden
441
+ provider HTTP retries. Cost fixtures also remove copied credentials on exit.
379
442
 
380
443
  For an interactive smoke check in a disposable project, install the local
381
444
  package in a compatible Pi host and run `/charterleaf`: check immediate
@@ -26,7 +26,7 @@ module.exports = function charterleaf(pi) {
26
26
  if (mode === 'off') { state = undefined; return; }
27
27
  if (!state || state.root !== root) state = {
28
28
  root, mode, loaded: new Map(), staged: new Map(), pages: new Map(), pending: new Map(), denied: new Set(),
29
- owned: new Map(), parents: new Map(), envelopes: new Map(), before: gitSnapshot(root), budget: 24000, reviewed: false, miss: false, opaque: false,
29
+ owned: new Map(), parents: new Map(), envelopes: new Map(), before: gitSnapshot(root), budget: 24000, reviewed: false, miss: false, opaque: false, knowledge: undefined,
30
30
  };
31
31
  state.mode = mode;
32
32
  return state;
@@ -148,6 +148,23 @@ module.exports = function charterleaf(pi) {
148
148
  } };
149
149
  });
150
150
 
151
+ // Semantic intent comes from the existing agent, never from file/read counts.
152
+ pi.on('message_end', (event, ctx) => {
153
+ if (event.message.role !== 'assistant' || ['error', 'aborted'].includes(event.message.stopReason)) return;
154
+ let s;
155
+ try { s = current(ctx); } catch { return; }
156
+ if (!s) return;
157
+ const text = event.message.content.filter((part) => part.type === 'text').map((part) => part.text).join('\n');
158
+ const prose = text.replace(/```[\s\S]*?(?:```|$)|~~~[\s\S]*?(?:~~~|$)/g, '');
159
+ const signal = prose.match(/<!-- charterleaf: (covered|no-value|proposed) -->\s*$/) ||
160
+ [...prose.matchAll(/^<!-- charterleaf: (review|covered|no-value|proposed) -->[ \t]*$/gm)].at(-1);
161
+ if (signal) {
162
+ if (signal[1] === 'proposed' && s.mode === 'confirm' && !s.proposed) return;
163
+ s.knowledge = signal[1];
164
+ if (s.knowledge !== 'review') { s.capture = gitSnapshot(s.root); s.opaque = false; }
165
+ }
166
+ });
167
+
151
168
  // A sibling tool call has not seen another sibling's result yet. Promote only at the next model request.
152
169
  pi.on('context', (_event, ctx) => {
153
170
  if (!state || state.root !== projectRoot(ctx.cwd)) return;
@@ -181,13 +198,14 @@ module.exports = function charterleaf(pi) {
181
198
  if (mutation && (isSpec(file) || (lexical && isSpec(lexical)))) {
182
199
  if (s.mode === 'reference') return { block: true, reason: 'Charterleaf Reference mode: report a proposal; do not modify specs.' };
183
200
  if (s.mode === 'confirm') {
201
+ s.proposed = true;
184
202
  if (!ctx.hasUI) return { block: true, reason: 'Charterleaf Confirm mode requires interactive approval. Report the proposal without writing.' };
185
203
  const proposal = JSON.stringify(event.input, null, 2);
186
204
  if (proposal.length > 12000) return { block: true, reason: 'Spec proposal too large to approve safely. Use smaller standard edits.' };
187
205
  if (s.denied.has(proposal)) return { block: true, reason: 'This exact spec proposal was already refused in this task. Do not retry; report it without writing.' };
188
206
  const before = fingerprint(s.root, file);
189
207
  const approved = await ctx.ui.confirm(`Charterleaf: modify ${file}?`,
190
- `Approve this exact ${event.toolName} only if the durable contract change is intended. Never relax a spec just to match conflicting code.\n\n${proposal}`, { timeout: 60000 });
208
+ `Approve this exact ${event.toolName} only if the evidenced durable knowledge maintenance is intended. Never relax a spec just to match conflicting code.\n\n${proposal}`, { timeout: 60000 });
191
209
  if (!approved) {
192
210
  s.denied.add(proposal);
193
211
  return { block: true, reason: 'Spec modification not approved. Preserve the current spec and report the proposal; do not retry.' };
@@ -223,7 +241,12 @@ module.exports = function charterleaf(pi) {
223
241
  s.envelopes.delete(event.toolCallId);
224
242
  if (event.isError) {
225
243
  if (call?.mutation) {
226
- try { if (fingerprint(s.root, call.file) !== call.before) s.opaque = true; } catch { s.opaque = true; }
244
+ try {
245
+ if (fingerprint(s.root, call.file) !== call.before) {
246
+ s.opaque = true;
247
+ if (s.knowledge !== 'review') s.knowledge = undefined;
248
+ }
249
+ } catch { s.opaque = true; s.knowledge = undefined; }
227
250
  }
228
251
  if (envelope) return { content: [...event.content, { type: 'text', text: `[Charterleaf]\n${envelope}` }] };
229
252
  return;
@@ -233,13 +256,18 @@ module.exports = function charterleaf(pi) {
233
256
  if (call) {
234
257
  const hash = fingerprint(s.root, call.file);
235
258
  if (call.mutation && hash !== call.before) {
259
+ if (s.knowledge !== 'review') s.knowledge = undefined;
236
260
  s.owned.set(call.file, hash);
237
261
  if (isSpec(call.file)) {
238
262
  s.loaded.delete(call.file);
239
263
  s.pages.delete(call.file);
240
264
  let output = '';
241
265
  const stream = { write: (text) => { output += text; } };
242
- lint(s.root, { stdout: stream, stderr: stream });
266
+ if (lint(s.root, { stdout: stream, stderr: stream }) === 0) {
267
+ s.knowledge = 'captured';
268
+ s.capture = gitSnapshot(s.root);
269
+ s.opaque = false;
270
+ }
243
271
  note = `Charterleaf structural check (not semantic verification):\n${output.slice(0, 4000)}`;
244
272
  }
245
273
  } else if (!call.mutation) {
@@ -278,30 +306,43 @@ module.exports = function charterleaf(pi) {
278
306
  if (!s || s.reviewed || event.outcome !== 'completed') return;
279
307
  s.reviewed = true;
280
308
  try {
281
- if (!s.owned.size && !s.opaque) return;
309
+ if (['captured', 'covered', 'proposed'].includes(s.knowledge)) {
310
+ if (!s.opaque) return;
311
+ const afterCapture = gitSnapshot(s.root);
312
+ if (s.capture && afterCapture && !changedSince(s.capture, afterCapture).some((file) => !isSpec(file))) return;
313
+ }
314
+ if (!['review', 'no-value'].includes(s.knowledge) && !s.owned.size && !s.opaque) return;
282
315
  const after = gitSnapshot(s.root);
283
316
  const comparable = s.before && after;
284
317
  const observed = changedSince(s.before, after);
285
318
  const owned = [...s.owned].filter(([file, hash]) => fingerprint(s.root, file) === hash && !isSpec(file)).map(([file]) => file);
286
319
  const unattributed = observed.filter((file) => !s.owned.has(file) || fingerprint(s.root, file) !== s.owned.get(file));
287
- if (!owned.length && !unattributed.length && !(s.opaque && !comparable)) return;
320
+ const emptySpecs = s.knowledge === 'no-value' && resolveRelated('', s.root).available.length === 0;
321
+ if (s.knowledge === 'no-value' && !(emptySpecs && owned.length > 0)) return;
322
+ const candidate = s.knowledge === 'review';
323
+ if (!candidate && !owned.length && !unattributed.length && !(s.opaque && !comparable)) return;
288
324
  const relevant = new Set();
289
325
  for (const file of [...owned, ...unattributed].slice(0, 50)) {
290
326
  const result = resolveRelated(file, s.root);
291
327
  for (const spec of [...result.global, ...result.scoped, ...result.changes]) relevant.add(spec);
292
328
  }
293
329
  const content = `Charterleaf final review — once only; mode=${s.mode}.\n` +
330
+ (candidate ? 'The agent identified unresolved durable knowledge in this task; file changes are not required. Judge the conversation evidence, not the signal itself.\n' : '') +
331
+ (emptySpecs ? 'Development in an empty-spec project: verify the no-value conclusion against evidence of existing undocumented constraints, not just newly changed contracts.\n' : '') +
294
332
  `Successful standard tool edits still unchanged at settlement: ${JSON.stringify(owned.slice(0, 50))}.\n` +
295
333
  `Observed worktree changes NOT attributable to this agent: ${JSON.stringify(unattributed.slice(0, 50))}. ` +
296
334
  'These may belong to the user or a parallel session; preserve them; do not modify specs based solely on them. Existing dirty content is not yours.\n' +
297
335
  `Possibly affected specs (routing hints only): ${JSON.stringify([...relevant].slice(0, 50))}.\n` +
298
336
  (!comparable ? 'Git change inspection unavailable (baseline or final snapshot missing). Opaque tool changes cannot be identified or attributed. Review only evidence in this conversation.\n' : '') +
299
- 'No durable change means no spec edit; no code summary, task log or plans. ' +
300
- 'Record only established, non-obvious contracts, invariants, boundaries, pitfalls or accepted decisions. ' +
337
+ 'Consider newly established AND newly discovered knowledge, including read-only investigation or accepted decisions; no code change is required. In an empty-spec project, tests or accepted behavior that reveal a non-obvious invariant without effective code-path guidance is a discovery gap; do not dismiss it merely because tests pass or the implementation already works. ' +
338
+ 'No justified durable knowledge means no spec edit; no code summary, task log or plans. ' +
339
+ 'Record only evidenced, non-obvious contracts, invariants, boundaries, pitfalls or accepted decisions whose absence risks future mistakes and which are not effectively documented. ' +
340
+ 'Current code alone is not an accepted contract. Prefer a short routed reference to an authoritative document over copying it; do not duplicate knowledge already captured in this task. ' +
341
+ 'Honor explicit no-file-write/read-only instructions even in Auto; report only. ' +
301
342
  'A code/spec conflict is not permission to rewrite the spec: distinguish implementation bug, obsolete spec and explicitly intended contract change. ' +
302
343
  'If uncertain, preserve the spec and report the conflict. ' +
303
344
  (s.mode === 'reference' ? 'Reference: report necessary proposals/conflicts only; never write specs. ' :
304
- s.mode === 'confirm' ? 'Confirm: propose the exact spec edit with standard write/edit; the extension must obtain approval before execution. If already refused/no UI, report without retrying. ' :
345
+ s.mode === 'confirm' ? 'Confirm: invoke the exact spec write/edit to open the Extension approval dialog; the extension must obtain approval before execution. If already refused/no UI, report without retrying. ' :
305
346
  'Auto: update only when durable knowledge is justified by this task; otherwise leave specs unchanged. ') +
306
347
  'For unfamiliar authoring/staging details, read the guide. Use standard write/edit and lint. ' +
307
348
  'Report task results/tests, not routine review bookkeeping; do not start another development cycle.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onedotmint/charterleaf",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Lightweight Agent-Native Constraint Layer: opt-in spec discovery and maintenance for Pi, with a standalone zero-dependency CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,7 +38,9 @@
38
38
  ],
39
39
  "scripts": {
40
40
  "test": "node --test",
41
- "test:pi": "node scripts/pi-smoke.js"
41
+ "test:pi": "node scripts/pi-smoke.js",
42
+ "test:organic": "node scripts/organic-smoke.js",
43
+ "test:context": "node scripts/context-smoke.js"
42
44
  },
43
45
  "pi": {
44
46
  "extensions": [
@@ -14,14 +14,40 @@ Read this guide only for unfamiliar spec formats, staged changes or manual CLI u
14
14
 
15
15
  Source owns implementation; tests own executable verification; `AGENTS.md` owns project entry/navigation; specs own durable constraints. Never copy signatures, inventories, current state or existing documentation into specs. Link the authoritative owner instead. Charterleaf is not task management, planning, memory, evidence, history or a project workflow.
16
16
 
17
- Place only established, non-obvious knowledge whose absence could cause a future mistake:
17
+ Capture during ordinary development, debugging, code/test/architecture investigation or technical decisions; do not wait for settlement or require existing specs. An enabled project containing only `specs/.charterleaf.json` is a valid starting state, not a bootstrap request.
18
+
19
+ Distinguish **newly established knowledge** (an accepted new contract/decision) from **newly discovered knowledge** (an existing but ineffectively documented constraint). Either can qualify without code changes; neither obliges every task to produce a document. Apply all three checks:
20
+
21
+ 1. Reliable evidence: accepted user intent, authoritative documentation, tests or sufficiently verified implementation behavior. Current code alone does not establish an accepted correct contract.
22
+ 2. Long-term value: ignoring it could plausibly cause a future developer/Agent to make a wrong decision.
23
+ 3. A documentation or discoverability gap: inspect relevant existing owners/specs before writing. If an authoritative document already covers it, add only a short scoped reference when routing is genuinely missing; otherwise do nothing. Never duplicate a captured rule. Executable tests provide evidence, not automatically effective code-path discovery: accepted non-obvious invariants scattered among tests may warrant a short scoped constraint/reference even when no new contract was established. Do not copy test cases or document trivial assertions; judge whether a future code change could miss the boundary.
24
+
25
+ Honor explicit no-file-write/read-only restrictions in every mode. Otherwise **code edits are allowed normally in all three modes**, including Reference and Confirm. Auto can create or update specs immediately when justified; Confirm uses the existing exact-spec-write approval; Reference only proposes spec changes, never restricts implementation edits. **In Confirm, invoke standard `write`/`edit` with the exact spec proposal to open the Extension's approval dialog; do not merely ask for prose permission and stop.** The call is an approval request, not an unauthorized mutation: the Extension pauses it before execution, denies it without UI, and only executes the exact operation if approved. If evidence conflicts, verify authority and distinguish a bug from an accepted change, never rewrite truth to match suspect code.
26
+
27
+ Place qualifying non-obvious knowledge in:
18
28
 
19
29
  - `specs/constitution.md`: rare project-wide boundaries;
20
30
  - `specs/capabilities/`: observable contracts, invariants and non-goals;
21
31
  - `specs/engineering/`: long-lived implementation boundaries, recurring pitfalls and verification constraints;
22
32
  - `specs/decisions/`: accepted rationale and revisit conditions.
23
33
 
24
- Living capability/engineering specs need a unique `id` and non-empty `applies_to` list. Keep scope narrow, preserve existing formats and requirement IDs, and verify explicit local references normally. `anchors` and `verified_by` are not required. Do not create speculative standards or code summaries to satisfy a final review.
34
+ Living capability/engineering specs need a unique `id` and non-empty `applies_to` list, including the first organically created spec. For example:
35
+
36
+ ```markdown
37
+ ---
38
+ id: engineering.amounts
39
+ applies_to:
40
+ - src/amounts.js
41
+ - tests/amounts.test.js
42
+ ---
43
+ # Amount boundary
44
+
45
+ Preserve the accepted rounding contract; see [accounting policy](../../docs/accounting.md#rounding) and its executable cases in `tests/amounts.test.js`.
46
+ ```
47
+
48
+ Use this shape, not this example's rule, for the actual project evidence. Route rationale in `decisions/` through a relevant capability/engineering reference when automatic code-path discovery is needed. Keep scope narrow, preserve existing formats and requirement IDs, and verify explicit local references normally. `anchors` and `verified_by` are not required. Do not create speculative standards or code summaries to satisfy a final review. Task progress, implementation inventories, one-off debugging, temporary plans/todos and unverified behavior never belong in Specs.
49
+
50
+ The runtime's standalone assistant comments (outside code fences; exact terminal completion comments also work inline) `<!-- charterleaf: review -->`, `<!-- charterleaf: covered -->`, `<!-- charterleaf: no-value -->` and `<!-- charterleaf: proposed -->` are task-local lifecycle signals, not spec contents, evidence or permissions. `review` means a concrete evidenced candidate still needs knowledge judgment; `covered` means relevant authority is already effectively discoverable; `no-value` means the evidence/value/gap checks found no qualifying knowledge; `proposed` means a Reference/read-only proposal was reported or a Confirm proposal passed through the gate and was refused/no UI. Use these exact comments; put explanation outside, never inside them. If an explicit no-write task surfaced a qualified constraint, report the finding/proposal in the normal answer and finish the knowledge judgment there rather than requesting a continuation just to repeat it. Ordinary code/test completion is not any of those conclusions: a generic `done` comment is deliberately ignored. Never use `covered` merely because tests exercise a boundary without effective code-path discovery. In an enabled empty-spec project, do not reject a non-obvious accepted invariant merely because the tests already exercise it. Waiting for human approval is not unresolved knowledge judgment: do not request a semantic continuation just to ask the same permission again. After submitting an exact proposal to the approval gate, handle approval/refusal/no UI and finish normally. New distinct findings may rearm, but the Extension continues at most once. Successful structurally valid standard spec writes also mark capture complete; later code edits rearm the development fallback. In an empty-spec project, owned development after `no-value` still permits the single fallback to check for missed discovery gaps. Omitting a recognized conclusion can cause a redundant fallback even when the normal answer already states that nothing needs documenting. Plain reads, file counts, search results and task wording are not semantic discovery signals.
25
51
 
26
52
  ## Optional staged changes
27
53
 
@@ -1,11 +1,15 @@
1
1
  # Charterleaf runtime
2
2
 
3
- Use constraints supplied with standard read results; do not load all specs or reread unchanged supplied bodies. A blocked write/edit requires inspecting the constraints or reading the named complete spec before retrying; never bypass it through another writer. Routing is a hint, not proof of coverage.
3
+ Use supplied specs; no bulk/unchanged rereads. Blocked write/edit: inspect constraints/read the complete spec, then retry; never bypass via another writer. Routing: a hint.
4
4
 
5
- Modes govern specs, not code: Auto maintains only justified durable knowledge; Confirm edits code normally and submits exact spec write/edit to the Extension's approval dialog (refusal/no UI: preserve spec, do not retry); Reference reports proposals/conflicts, never writes specs. Use standard write/edit only. Activation config is user-owned via /charterleaf. These guards are not a filesystem sandbox.
5
+ Modes govern specs, not code: Auto maintains justified knowledge; Confirm calls spec write/edit to open Extension approval (refusal/no UI: preserve, no retry); Reference proposes, never writes specs. All modes allow normal code edits. Use standard write/edit only. User-owned config. read-only/no-file-write requests override Auto. Not a filesystem sandbox.
6
6
 
7
- A code/spec conflict is not permission to relax the spec. Fix implementation bugs; change contracts only with authoritative evidence or explicit accepted intent. Internal/refactor requests do not authorize changing return or ownership contracts. If a requested method violates an invariant, preserve spec/tests and ask for an explicit contract decision before coding. When uncertain, preserve the spec and report the conflict. Preserve user/parallel work; a touched file or dirty worktree does not prove line ownership. Never automatically revert, commit or push.
7
+ Internal/refactor requests do not authorize changing return or ownership contracts. Conflicts: verify authority, fix bugs; never relax specs to match code. Invariant-breaking method? Preserve spec/tests; get an explicit contract decision before coding. Uncertain: preserve and report. Preserve user/parallel work; Git dirt/touched files aren't line ownership. No automatic revert/commit/push.
8
+
9
+ ## Organic knowledge capture
10
+
11
+ During development/debugging/investigation/decisions capture newly established OR newly discovered knowledge, even without code changes/prior specs. Need reliable evidence (accepted decisions, authoritative docs, tests or verified behavior), mistake-prevention value and a documentation/discoverability gap. Code alone is not an accepted contract. Check owners; tests aren't automatic code-path routing; prefer short routed references over copying docs. New capability/engineering specs: unique id, narrow applies_to; see guide. No justified knowledge: no spec. No code summaries/progress/logs/plans/todos/speculation.
8
12
 
9
13
  ## One final semantic review
10
14
 
11
- Only established, non-obvious, mistake-preventing contracts, invariants, boundaries, pitfalls or accepted decisions belong in specs. No durable change means no spec edit: exclude implementation summaries, tasks, progress, debugging and plans. At most one final review may follow development; do not start another cycle. Report task results/tests, not routine review bookkeeping. Spec writes receive structural lint, not semantic verification.
15
+ Judge during work, not by read counts. Use one exact comment: <!-- charterleaf: review --> (evidenced pending), <!-- charterleaf: covered --> (effective authority), <!-- charterleaf: no-value --> (no value), <!-- charterleaf: proposed --> (Reference/refused Confirm/read-only report). Reasons outside, never inside comments. NOT merely finishing code/tests. Signals aren't evidence/permission. At most one extra review; no loop. Report results/tests, not review logs. Lint is structural only.