@christang/keel 5.3.1 → 5.3.4
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/assets/bootstrap/AGENTS.md +2 -2
- package/assets/openspec/schemas/keel-spec-driven/schema.yaml +28 -6
- package/assets/openspec/schemas/keel-spec-driven/templates/tasks.md +30 -7
- package/package.json +1 -1
- package/plugins/keel/.claude-plugin/plugin.json +1 -1
- package/plugins/keel/.codex-plugin/plugin.json +1 -1
- package/plugins/keel/scripts/session-start.js +8 -1
- package/scripts/bump_version.js +46 -8
- package/scripts/validate_plugin.py +1008 -129
- package/src/core/gates.js +196 -24
- package/src/core/task-contract.js +62 -16
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<!-- keel:start version=5.3.
|
|
1
|
+
<!-- keel:start version=5.3.4 -->
|
|
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
|
-
- Obey the selected task capsule: `keel gate task-start` before implementing, record its fingerprint in Evidence `Contract`, and pass `keel gate task-complete` before checking complete. Touch
|
|
5
|
+
- Obey the selected task capsule: `keel gate task-start` before implementing, record its fingerprint in Evidence `Contract`, and pass `keel gate task-complete` 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
7
|
- Native plugin projections (SessionStart context) are disposable views, never authority; without the plugin or hook, run the commands manually.
|
|
8
8
|
- 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.
|
|
@@ -93,8 +93,15 @@ artifacts:
|
|
|
93
93
|
Verify selects the least-cost strategy that still proves the resolved
|
|
94
94
|
Acceptance: vertical-tdd, regression-first, characterization,
|
|
95
95
|
snapshot-characterization, rendered-behavior, or evidence-first.
|
|
96
|
-
Red-green strategies record per-label `.red` and `.green` Evidence
|
|
97
|
-
the
|
|
96
|
+
Red-green strategies record per-label `.red` and `.green` Evidence in
|
|
97
|
+
addition to the bare `M<n>` Evidence, which is always required; all three
|
|
98
|
+
must be concrete before completion. An `M<n>` may carry a comma-separated
|
|
99
|
+
tag set after its label, drawn from `fast`, `full`, and `regression`.
|
|
100
|
+
`fast`/`full` marks which checks the fast inner-loop pre-push runs.
|
|
101
|
+
`regression` marks a check asserting that something already green stays
|
|
102
|
+
green: it has no honest red, so it is exempt from `.red`/`.green` while
|
|
103
|
+
still needing its bare `M<n>` Evidence, and a red-green strategy must keep
|
|
104
|
+
at least one check untagged.
|
|
98
105
|
The Task Authoring Gate requires every relevant critical expectation to
|
|
99
106
|
be covered by a slice, deferred to a durable owner, or explicitly
|
|
100
107
|
discarded with rationale before the slice can be selected for execution.
|
|
@@ -137,10 +144,25 @@ artifacts:
|
|
|
137
144
|
Review with Status, Acceptance check, Scope check, and Findings. Status is
|
|
138
145
|
one of pass, passed, complete, completed, ok, or done. Findings
|
|
139
146
|
are `none` or carry a durable owner — a `Discard reason:`/`Discard
|
|
140
|
-
rationale:` prefix,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
rationale:` prefix, an absolute `https://…` reference, or any
|
|
148
|
+
repo-relative path that exists, named after `Durable owner:` so it reads
|
|
149
|
+
as the owner rather than a file the finding mentions;
|
|
150
|
+
`keel/HANDOFF.md` is never the owner.
|
|
151
|
+
tasks.md carries two change-level sections beside its tasks.
|
|
152
|
+
`## Invalidates` is required by task-start, before any task of the
|
|
153
|
+
change runs: one `- I<n>: "the wording that is now wrong" — where it lives.`
|
|
154
|
+
line per statement this change makes stale, closed by `Updated by: <task
|
|
155
|
+
ids>` naming tasks of this change, a `Durable owner:`, or a `Discard
|
|
156
|
+
reason:`, or `- None.` when the change makes no existing statement wrong.
|
|
157
|
+
A `Durable owner:` is an absolute `https://…` reference or any
|
|
158
|
+
repo-relative path that exists; a path with no file behind it is refused,
|
|
159
|
+
and `keel/HANDOFF.md` is a pointer override rather than an owner.
|
|
160
|
+
Quote the wording a reader would search for rather than only listing
|
|
161
|
+
files, because the text that goes stale is the text the author was not
|
|
162
|
+
already holding in mind; naming it while tasks are authored is what lets
|
|
163
|
+
the affected paths be declared in Touch instead of forcing a
|
|
164
|
+
reauthorization mid-task. `## Expectation Coverage` is required by
|
|
165
|
+
change-close: one `- E<n>: <expectation> Covered by: <task ids>` line per
|
|
144
166
|
critical expectation (or a `Durable owner:` / `Discard reason:` closure),
|
|
145
167
|
or `- None.` when the change has no critical expectations.
|
|
146
168
|
requires:
|
|
@@ -20,10 +20,16 @@
|
|
|
20
20
|
rendered-behavior, or evidence-first. Each M<n> check must prove the
|
|
21
21
|
resolved Acceptance through the public interface, not build-only or
|
|
22
22
|
shape-only evidence. Red-green strategies record per-label `.red` and
|
|
23
|
-
`.green` Evidence entries
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
`.green` Evidence entries IN ADDITION TO the bare `M<n>` entry, which
|
|
24
|
+
is always required; all three must be concrete before completion.
|
|
25
|
+
An M<n> check may carry an optional comma-separated tag set after its
|
|
26
|
+
label, drawn from fast, full, and regression (e.g. `M1 (fast): …`,
|
|
27
|
+
`M2 (regression): …`, `M3 (fast, regression): …`). fast/full marks
|
|
28
|
+
which checks the fast inner-loop pre-push runs; an untagged check is
|
|
29
|
+
full. regression marks a check that asserts something already green
|
|
30
|
+
stays green: it has no honest red, so it is exempt from `.red`/`.green`
|
|
31
|
+
but still needs its bare `M<n>` Evidence, and a red-green strategy must
|
|
32
|
+
keep at least one check untagged. change-close still needs every
|
|
27
33
|
M<n>'s Evidence. -->
|
|
28
34
|
- Strategy: <strategy>
|
|
29
35
|
- M1: <public behavior check>
|
|
@@ -32,7 +38,7 @@
|
|
|
32
38
|
- M1: pending
|
|
33
39
|
- Review:
|
|
34
40
|
<!-- Status: one of pass, passed, complete, completed, ok, done -->
|
|
35
|
-
<!-- Findings: none, or carry a durable owner — a "Discard reason:"/"Discard rationale:" prefix,
|
|
41
|
+
<!-- Findings: none, or carry a durable owner — a "Discard reason:"/"Discard rationale:" prefix, an absolute https://… reference, or any repo-relative path that exists (keel/archive/…, an openspec/changes/… artifact, or the repository's own ledger) named after "Durable owner:"; not keel/HANDOFF.md, which is a pointer override -->
|
|
36
42
|
- Status: pending
|
|
37
43
|
- Acceptance check: pending
|
|
38
44
|
- Scope check: pending
|
|
@@ -76,10 +82,27 @@
|
|
|
76
82
|
- Findings: pending
|
|
77
83
|
- Blocker: none
|
|
78
84
|
|
|
85
|
+
## Invalidates
|
|
86
|
+
|
|
87
|
+
<!-- task-start requires this section before any task of this change runs, so
|
|
88
|
+
the statements this change makes stale can be named while their paths can
|
|
89
|
+
still be declared in Touch instead of forcing a reauthorization later.
|
|
90
|
+
Each entry quotes the wording a reader would SEARCH for — not just the
|
|
91
|
+
files you already remembered, because the text that goes stale is the text
|
|
92
|
+
you were not thinking about — then says where it lives, then closes:
|
|
93
|
+
`Updated by: 1.1` (tasks of this change), `Durable owner: <url or path>`
|
|
94
|
+
— an absolute https:// reference, or any repo-relative path that exists;
|
|
95
|
+
keel/HANDOFF.md is refused — or `Discard reason: why it stands`. Use `- None.` when this change makes
|
|
96
|
+
no existing statement wrong.
|
|
97
|
+
|
|
98
|
+
- I1: "the exact wording that is now wrong" — where that wording lives. Updated by: 1.1
|
|
99
|
+
-->
|
|
100
|
+
- None.
|
|
101
|
+
|
|
79
102
|
## Expectation Coverage
|
|
80
103
|
|
|
81
104
|
<!-- change-close requires this section. One line per critical expectation:
|
|
82
105
|
`- E1: the expectation Covered by: 1.1` (task ids that own it), or a
|
|
83
|
-
`Durable owner:
|
|
84
|
-
closure. Use `- None.` only when the change has no critical expectations. -->
|
|
106
|
+
`Durable owner: <url or any repo-relative path that exists>` /
|
|
107
|
+
`Discard reason: why` closure. Use `- None.` only when the change has no critical expectations. -->
|
|
85
108
|
- None.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keel",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.4",
|
|
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.
|
|
3
|
+
"version": "5.3.4",
|
|
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",
|
|
@@ -15,6 +15,11 @@ const fs = require("fs");
|
|
|
15
15
|
const path = require("path");
|
|
16
16
|
const { spawnSync } = require("child_process");
|
|
17
17
|
|
|
18
|
+
// This text is injected into the agent and never rendered for the human, so
|
|
19
|
+
// without an explicit instruction the projection reaches nobody who can catch
|
|
20
|
+
// it being wrong. Every branch carries the same phrase, degraded ones included.
|
|
21
|
+
const DISCLOSURE = "to the user in your first reply";
|
|
22
|
+
|
|
18
23
|
const TIMEOUT_MS = Number(process.env.KEEL_HOOK_TIMEOUT_MS || 8000) || 8000;
|
|
19
24
|
const MAX_REASONS = 3;
|
|
20
25
|
const MAX_REASON_LENGTH = 300;
|
|
@@ -51,7 +56,8 @@ function runKeel(cwd, args) {
|
|
|
51
56
|
function fallback(reason) {
|
|
52
57
|
emit(
|
|
53
58
|
`Keel hook fallback: ${reason} Run \`keel context\` manually; `
|
|
54
|
-
+ "OpenSpec and Git remain the durable authority."
|
|
59
|
+
+ "OpenSpec and Git remain the durable authority. Report this failure "
|
|
60
|
+
+ `and that command ${DISCLOSURE}.`
|
|
55
61
|
);
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -145,6 +151,7 @@ function main() {
|
|
|
145
151
|
+ "does not guess among candidates."
|
|
146
152
|
);
|
|
147
153
|
}
|
|
154
|
+
lines.push(`- report this state ${DISCLOSURE}; it authorizes nothing.`);
|
|
148
155
|
emit(lines.join("\n"));
|
|
149
156
|
return 0;
|
|
150
157
|
}
|
package/scripts/bump_version.js
CHANGED
|
@@ -74,8 +74,45 @@ function replaceInFile(relPath, replacements) {
|
|
|
74
74
|
process.stdout.write(` updated ${relPath}\n`);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Every Keel marker carrying `version=` is a shipped claim about which version
|
|
78
|
+
// this is, and they must all move together. Sweeping for the markers that exist
|
|
79
|
+
// is what keeps a target from falling behind: the `.codex/` overlays sat four
|
|
80
|
+
// versions back because only the surfaces something happened to touch got
|
|
81
|
+
// refreshed, and nothing failed while they drifted.
|
|
82
|
+
const MARKER_SKIP_PREFIXES = [
|
|
83
|
+
"node_modules",
|
|
84
|
+
".git",
|
|
85
|
+
path.join("openspec", "changes", "archive"),
|
|
86
|
+
path.join("keel", "archive"),
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
function sweepVersionMarkers(dir, oldVersion, newVersion, touched) {
|
|
90
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
91
|
+
const full = path.join(dir, entry.name);
|
|
92
|
+
const relative = path.relative(ROOT, full);
|
|
93
|
+
if (MARKER_SKIP_PREFIXES.some((prefix) => relative.startsWith(prefix))) continue;
|
|
94
|
+
if (entry.isDirectory()) {
|
|
95
|
+
sweepVersionMarkers(full, oldVersion, newVersion, touched);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!/\.(md|json)$/.test(entry.name)) continue;
|
|
99
|
+
const content = fs.readFileSync(full, "utf8");
|
|
100
|
+
const updated = content.replace(
|
|
101
|
+
new RegExp(`(keel:[a-z-]+(?::end)?\\s+version=)${oldVersion.replace(/\./g, "\\.")}\\b`, "g"),
|
|
102
|
+
`$1${newVersion}`
|
|
103
|
+
);
|
|
104
|
+
if (updated !== content) {
|
|
105
|
+
fs.writeFileSync(full, updated);
|
|
106
|
+
touched.push(relative.split(path.sep).join("/"));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
77
111
|
function prependChangelogEntry(newVersion) {
|
|
78
|
-
|
|
112
|
+
// Read line endings as the file has them: a CRLF checkout made the header
|
|
113
|
+
// comparison below fail after every version marker had already been written,
|
|
114
|
+
// leaving the repository half-bumped.
|
|
115
|
+
let content = fs.readFileSync(CHANGELOG_PATH, "utf8").replace(/\r\n/g, "\n");
|
|
79
116
|
if (content.includes(`## ${newVersion} `) || content.includes(`## ${newVersion}\n`)) {
|
|
80
117
|
process.stdout.write(` keel/CHANGELOG.md already has a ${newVersion} entry\n`);
|
|
81
118
|
return;
|
|
@@ -118,13 +155,14 @@ function main() {
|
|
|
118
155
|
[`PACKAGE_VERSION = "${oldVersion}"`, `PACKAGE_VERSION = "${newVersion}"`],
|
|
119
156
|
[`PROTOCOL_VERSION = "${oldVersion}"`, `PROTOCOL_VERSION = "${newVersion}"`],
|
|
120
157
|
]);
|
|
121
|
-
replaceInFile("AGENTS.md", [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
replaceInFile("AGENTS.md", [[`v${oldVersion}`, `v${newVersion}`]]);
|
|
159
|
+
|
|
160
|
+
const touched = [];
|
|
161
|
+
sweepVersionMarkers(ROOT, oldVersion, newVersion, touched);
|
|
162
|
+
for (const relative of touched) {
|
|
163
|
+
process.stdout.write(` updated marker ${relative}\n`);
|
|
164
|
+
}
|
|
165
|
+
|
|
128
166
|
prependChangelogEntry(newVersion);
|
|
129
167
|
|
|
130
168
|
process.stdout.write(
|