@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 +63 -3
- package/assets/bootstrap/AGENTS.md +2 -1
- package/bin/keel.js +35 -0
- package/package.json +1 -1
- package/plugins/keel/.claude-plugin/plugin.json +1 -1
- package/plugins/keel/.codex-plugin/plugin.json +1 -1
- package/scripts/validate_plugin.py +699 -16
- package/src/core/config.js +61 -0
- package/src/core/context.js +35 -1
- package/src/core/gates.js +62 -0
- package/src/core/task-contract.js +119 -22
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`,
|
|
287
|
-
declared write scope. That is how the agent knows *when* to run
|
|
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.
|
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keel",
|
|
3
|
-
"version": "5.
|
|
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.
|
|
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",
|