@christang/keel 5.62.0 → 5.64.0

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/README.md CHANGED
@@ -273,7 +273,27 @@ designed to — resist widening the policy until it stops happening.
273
273
  Use **Full mode** (the OpenSpec flow above) for new features, interface or protocol changes,
274
274
  cross-module work, or anything over ~3 files / 100 lines. Use **Lite mode** for local fixes,
275
275
  small scripts, docs, or tests with no interface change and locally provable impact; Lite does
276
- not write OpenSpec state.
276
+ not write OpenSpec state. The rule is in the block `keel --init` installs, because routing is
277
+ the first decision of a session and a rule reachable only from this README is reachable only by
278
+ an agent that already went looking.
279
+
280
+ The size bar is a proxy for risk, not risk itself, and some repositories invert it: an
281
+ append-only record whose schema change is a one-field diff can be the highest-risk change in the
282
+ project, and a mechanical rename across twenty files the lowest. Declare the paths the heuristic
283
+ gets wrong, each with the reason it is wrong:
284
+
285
+ ```yaml
286
+ full_mode_paths:
287
+ - results/experiments.jsonl: append-only; a one-field diff is not revertible
288
+ ```
289
+
290
+ `keel context` reports what is declared, so the exception arrives at the decision, and
291
+ `keel --doctor` reports the declaration's health. There is deliberately **no key for the
292
+ opposite direction**: every declaration in that file removes a confirmation and never a gate, and
293
+ an entry that held work *out* of the flow would be the first to break that. Keel gates no routing
294
+ decision either — routing decides whether a change exists, so there is nothing for a gate to bind
295
+ to; what Keel does is make sure the rule and your exceptions are in front of the agent when it
296
+ decides.
277
297
 
278
298
  ## How the agent uses these
279
299
 
@@ -283,8 +303,9 @@ command at the right moment. Three things make that happen.
283
303
 
284
304
  - **The protocol.** `keel --init` writes a bootstrap block into your repo's `AGENTS.md`
285
305
  (imported by `CLAUDE.md` on Claude). It states the rules the agent follows: open every
286
- session with `keel context`, pass the gates at task boundaries, and stay inside the task's
287
- declared write scope. That is how the agent knows *when* to run what.
306
+ session with `keel context`, route the work Full or Lite, pass the gates at task boundaries,
307
+ and stay inside the task's declared write scope. That is how the agent knows *when* to run
308
+ what.
288
309
  - **The skills.** The `keel-*` execution skills and the `/opsx:*` command overlays walk the
289
310
  agent through align → apply → review → complete, invoking the gates at each step.
290
311
  - **The hooks.** A SessionStart hook runs the continuity projection the moment a session
@@ -354,6 +375,45 @@ set — fails `task-start` by name rather than sitting in the contract doing not
354
375
  `Fails with:` marker that names no literal. To *write about* the marker in a check without declaring
355
376
  one, put it in inline code.
356
377
 
378
+ #### A red can be honest and the check still immune
379
+
380
+ A signature predicts the red of an **absent** feature. It says nothing about the red of a **broken**
381
+ one, and the two can be unrelated. A consistency check asserting two tool outputs agree had a real
382
+ red, a correct signature, a real green — and stayed green through a 1000× unit error, the one thing
383
+ it existed to catch, because its tolerance carried a default absolute floor. The only way to find
384
+ that is to put the defect in and watch.
385
+
386
+ `Detects:` declares that injection — the mutation, and the failure it must produce:
387
+
388
+ ```
389
+ - M1: `pytest tests/test_fmax.py` asserts synth fmax == sta fmax. Fails with: `AttributeError` Detects: `sed -i s/0.0005/0.5/ run_sta.py` -> `assert 5e-16 == 5e-13`
390
+ ```
391
+
392
+ Completion requires that second literal in the check's `.detects` Evidence. Clauses chain, so a check
393
+ may carry both — the example above is one check, one line.
394
+
395
+ **Keel does not run the mutation and does not judge it.** It records the claim and puts it where
396
+ review can see it, the same standing every other check result has. You can declare an injection any
397
+ check would catch; what the clause buys is that you decided it before the run, and that editing it
398
+ afterwards moves the fingerprint.
399
+
400
+ A `(regression)` check **may** declare one, and is the best place for it: such a check has no honest
401
+ red by construction, so an injection is the only thing that can show it is not vacuous.
402
+
403
+ #### A number can claim to be a measurement
404
+
405
+ `Basis:`, Evidence prose and `Findings` are free text, and a number in them reads the same whether it
406
+ was measured, estimated, or remembered. `Measured:` binds one to the output behind it:
407
+
408
+ ```
409
+ - M1: `node bench.js` reports the fanout load. Measured: `1799.9`
410
+ ```
411
+
412
+ Completion requires that literal in the check's own `M<n>` Evidence — the entry holding the command
413
+ and its output. Opt-in, and deliberately not a rule over every number: measured against this
414
+ repository's archive, that rule would reach 847 inline-code spans, mostly version strings, counts,
415
+ and quoted references that appear in no command output, and each would be a false stop.
416
+
357
417
  ## Domain lenses
358
418
 
359
419
  Keel's core is pure process; it ships no domain knowledge and no decisions of its own. Alongside
@@ -1,9 +1,10 @@
1
- <!-- keel:start version=5.62.0 -->
1
+ <!-- keel:start version=5.64.0 -->
2
2
  ## Keel Bootstrap
3
3
 
4
4
  - Start every session with `keel context`; OpenSpec artifacts and Git are the only durable authority — never native memory, goals, or transcripts.
5
5
  - Obey the selected task capsule: `keel gate task-start` before implementing, `--record` its fingerprint to Evidence `Contract`, which `task-complete` requires, before checking complete. Touch bounds product writes; the change's own dir is exempt. On Claude a passing `task-start` guards it by default (`--no-guard` opts out).
6
6
  - One current agent owns writes; helpers return read-only report/evidence only. No commit, sync, or archive without explicit authorization.
7
+ - Route Full (the OpenSpec flow) for new features, interface or protocol changes, cross-module work, or over ~3 files / 100 lines; Lite for local fixes with no interface change. No gate checks routing; a project corrects the size bar with `full_mode_paths:` in `keel/config.yaml`, which `keel context` reports.
7
8
  - Native plugin projections (SessionStart context) are disposable views, never authority; without the plugin or hook, run the commands manually.
8
9
  - Keel skills and hooks come from the `keel` native plugin (`codex plugin add` / `claude plugin install`); `keel --init` owns only the OpenSpec schema, overlays, and this bootstrap.
9
10
  <!-- keel:end -->
package/bin/keel.js CHANGED
@@ -48,6 +48,8 @@ const {
48
48
  STANDING_AUTHORIZATION_ACTIONS,
49
49
  readPrecedentStore,
50
50
  readStandingAuthorization,
51
+ readFullModePaths,
52
+ fullModePathsUnreadableMessage,
51
53
  readTriagePolicy,
52
54
  triageIssue,
53
55
  } = require("../src/core/config");
@@ -1728,6 +1730,7 @@ function runDoctor(options) {
1728
1730
  const authorizationOk = printStandingAuthorizationSurface(repo);
1729
1731
  printPrecedentSurface(repo);
1730
1732
  printTriageSurface(repo);
1733
+ printRoutingSurface(repo);
1731
1734
  printFastPrePushSurface(repo);
1732
1735
  printSourceRepoCliResolution(repo);
1733
1736
 
@@ -1820,6 +1823,38 @@ function printStandingAuthorizationSurface(repo) {
1820
1823
  return true;
1821
1824
  }
1822
1825
 
1826
+ function printRoutingSurface(repo) {
1827
+ process.stdout.write("\nFull/Lite routing:\n");
1828
+ const { paths, unreadable } = readFullModePaths(repo);
1829
+ if (unreadable.length > 0) {
1830
+ // Same verdict the projection reaches, from the same read: a diagnostic
1831
+ // that reported health while `keel context` reported the conservative
1832
+ // state would leave a reader to pick which one to believe.
1833
+ printDoctorLine(
1834
+ "full_mode_paths",
1835
+ "failed",
1836
+ `${fullModePathsUnreadableMessage(unreadable)} Every change routes Full `
1837
+ + "until it is corrected."
1838
+ );
1839
+ return;
1840
+ }
1841
+ if (paths.length === 0) {
1842
+ printDoctorLine(
1843
+ "full_mode_paths",
1844
+ "none",
1845
+ "undeclared; routing follows the size heuristic alone"
1846
+ );
1847
+ return;
1848
+ }
1849
+ printDoctorLine(
1850
+ "full_mode_paths",
1851
+ "ok",
1852
+ `declared in keel/config.yaml: ${paths.length} `
1853
+ + `${paths.length === 1 ? "path always routes" : "paths always route"} Full`
1854
+ );
1855
+ for (const entry of paths) printDoctorLine(entry.path, "Full", entry.reason);
1856
+ }
1857
+
1823
1858
  function printTriageSurface(repo) {
1824
1859
  process.stdout.write("\nUnattended triage:\n");
1825
1860
  const { labels, issues, unreadable } = readTriagePolicy(repo);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@christang/keel",
3
3
  "displayName": "Keel",
4
4
  "description": "Keel OpenSpec execution discipline CLI for Claude Code, Codex, and OpenCode.",
5
- "version": "5.62.0",
5
+ "version": "5.64.0",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keel",
3
- "version": "5.62.0",
3
+ "version": "5.64.0",
4
4
  "description": "Keel OpenSpec execution discipline: stateless continuity, task capsules, deterministic gates, and expectation alignment for Codex and Claude Code.",
5
5
  "author": {
6
6
  "name": "TanglmChris",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keel",
3
- "version": "5.62.0",
3
+ "version": "5.64.0",
4
4
  "description": "Keel OpenSpec execution discipline: stateless continuity, task capsules, deterministic gates, and expectation alignment for Codex and Claude Code.",
5
5
  "author": {
6
6
  "name": "TanglmChris",