@static-var/keystone 0.1.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/.agents/plugins/marketplace.json +24 -0
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +12 -0
- package/.codex-plugin/plugin.json +12 -0
- package/.pi/extensions/keystone.ts +172 -0
- package/HOW_IT_WORKS.md +424 -0
- package/Makefile +19 -0
- package/README.md +253 -0
- package/package.json +86 -0
- package/packaging.allowlist +32 -0
- package/scripts/build-metadata.py +99 -0
- package/scripts/package-keystone.sh +59 -0
- package/scripts/validate-keystone.py +261 -0
- package/scripts/validate-package.py +140 -0
- package/skills/keystone/SKILL.md +69 -0
- package/skills/keystone/modules/breakdown.md +239 -0
- package/skills/keystone/modules/build.md +284 -0
- package/skills/keystone/modules/debug.md +198 -0
- package/skills/keystone/modules/gates/isolation.md +56 -0
- package/skills/keystone/modules/gates/proof.md +54 -0
- package/skills/keystone/modules/gates/red.md +59 -0
- package/skills/keystone/modules/gates/review.md +56 -0
- package/skills/keystone/modules/gates/ship.md +57 -0
- package/skills/keystone/modules/health.md +124 -0
- package/skills/keystone/modules/helpers/subagents.md +134 -0
- package/skills/keystone/modules/research.md +86 -0
- package/skills/keystone/modules/review.md +270 -0
- package/skills/keystone/modules/router.md +36 -0
- package/skills/keystone/modules/shape.md +125 -0
- package/skills/keystone/modules/ship.md +130 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate Keystone canonical source and packaging invariants."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
12
|
+
SKILL_DIR = ROOT / "skills" / "keystone"
|
|
13
|
+
SKILL = SKILL_DIR / "SKILL.md"
|
|
14
|
+
MODULE_DIR = SKILL_DIR / "modules"
|
|
15
|
+
PI_EXTENSION = ROOT / ".pi" / "extensions" / "keystone.ts"
|
|
16
|
+
ALLOWLIST = ROOT / "packaging.allowlist"
|
|
17
|
+
SUBAGENT_HELPER = MODULE_DIR / "helpers" / "subagents.md"
|
|
18
|
+
TARGET_HARNESSES = ("Pi", "Claude", "Codex", "T3", "OpenCode", "Copilot")
|
|
19
|
+
COMMON_PLAYBOOK_HEADINGS = (
|
|
20
|
+
"## Core principle",
|
|
21
|
+
"## Load when",
|
|
22
|
+
"## Not for",
|
|
23
|
+
"## Outcome contract",
|
|
24
|
+
"## Subagents and reasoning",
|
|
25
|
+
"## Hard rules",
|
|
26
|
+
"## Failure modes",
|
|
27
|
+
"## Output format",
|
|
28
|
+
)
|
|
29
|
+
MODULE_PLAYBOOK_HEADINGS = {
|
|
30
|
+
"build": ("## Modes", "## Process", "## Architecture taste checklist"),
|
|
31
|
+
"review": ("## Review passes", "## Severity rubric", "## Impact tracing", "## Security and regression checklist"),
|
|
32
|
+
"breakdown": ("## Modes", "## Process", "## Requirements inventory", "## Iteration layering", "## Task quality bar"),
|
|
33
|
+
}
|
|
34
|
+
DEFAULT_PLAYBOOK_HEADINGS = ("## Modes", "## Process")
|
|
35
|
+
FORBIDDEN_TRACKED = {
|
|
36
|
+
"docs",
|
|
37
|
+
"plans",
|
|
38
|
+
"index.html",
|
|
39
|
+
"styles.css",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def fail(message: str) -> None:
|
|
44
|
+
print(f"validate-keystone: {message}", file=sys.stderr)
|
|
45
|
+
raise SystemExit(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def tracked_files() -> list[str]:
|
|
49
|
+
try:
|
|
50
|
+
result = subprocess.run(
|
|
51
|
+
["git", "ls-files"], cwd=ROOT, text=True, check=True, capture_output=True
|
|
52
|
+
)
|
|
53
|
+
except Exception:
|
|
54
|
+
return []
|
|
55
|
+
return [line for line in result.stdout.splitlines() if line]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def parse_frontmatter(text: str) -> dict[str, object]:
|
|
59
|
+
if not text.startswith("---\n"):
|
|
60
|
+
return {}
|
|
61
|
+
end = text.find("\n---", 4)
|
|
62
|
+
if end == -1:
|
|
63
|
+
return {}
|
|
64
|
+
data: dict[str, object] = {}
|
|
65
|
+
current_key = None
|
|
66
|
+
for raw in text[4:end].splitlines():
|
|
67
|
+
line = raw.rstrip()
|
|
68
|
+
if not line or line.lstrip().startswith("#"):
|
|
69
|
+
continue
|
|
70
|
+
if line.startswith(" - ") and current_key:
|
|
71
|
+
data.setdefault(current_key, []).append(line[4:].strip().strip('"\''))
|
|
72
|
+
continue
|
|
73
|
+
if ":" in line:
|
|
74
|
+
key, value = line.split(":", 1)
|
|
75
|
+
current_key = key.strip()
|
|
76
|
+
value = value.strip()
|
|
77
|
+
data[current_key] = [] if value == "" else value.strip('"\'')
|
|
78
|
+
return data
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def check_skill() -> str:
|
|
82
|
+
if not SKILL_DIR.is_dir():
|
|
83
|
+
fail("missing canonical skill directory skills/keystone/")
|
|
84
|
+
if not SKILL.is_file():
|
|
85
|
+
fail("missing canonical skill file skills/keystone/SKILL.md")
|
|
86
|
+
text = SKILL.read_text()
|
|
87
|
+
metadata = parse_frontmatter(text)
|
|
88
|
+
name = metadata.get("name")
|
|
89
|
+
if name is None:
|
|
90
|
+
heading = re.search(r"^#\s+(.+)$", text, re.M)
|
|
91
|
+
name = heading.group(1).strip().lower() if heading else None
|
|
92
|
+
if str(name).lower() != "keystone":
|
|
93
|
+
fail("skill name must be keystone")
|
|
94
|
+
return text
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def check_language(text: str) -> None:
|
|
98
|
+
public_plan = re.compile(r"(?<![\w/.-])/(?:plan)(?![\w/.-])|\bplanner\s+name\s+is\s+plan\b", re.I)
|
|
99
|
+
if public_plan.search(text):
|
|
100
|
+
fail("public /plan mention found; Keystone uses breakdown")
|
|
101
|
+
if not re.search(r"\bbreakdown\b", text, re.I):
|
|
102
|
+
fail("missing required breakdown terminology")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def check_references(text: str) -> None:
|
|
106
|
+
refs = sorted(set(re.findall(r"\(([^)]+\.(?:md|json|yaml|yml|txt|sh|py))\)", text)))
|
|
107
|
+
refs += sorted(set(re.findall(r"(?:^|\s)([A-Za-z0-9_./-]+\.(?:md|json|yaml|yml|txt|sh|py))", text)))
|
|
108
|
+
missing = []
|
|
109
|
+
for ref in refs:
|
|
110
|
+
if ref.startswith(("http://", "https://", "mailto:")) or ref.startswith("/"):
|
|
111
|
+
continue
|
|
112
|
+
candidate = (SKILL_DIR / ref).resolve()
|
|
113
|
+
try:
|
|
114
|
+
candidate.relative_to(ROOT)
|
|
115
|
+
except ValueError:
|
|
116
|
+
continue
|
|
117
|
+
if not candidate.exists():
|
|
118
|
+
missing.append(ref)
|
|
119
|
+
if missing:
|
|
120
|
+
fail("missing referenced module/gate files: " + ", ".join(sorted(set(missing))))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def routing_modules(text: str) -> set[str]:
|
|
124
|
+
return {
|
|
125
|
+
match.group(1)
|
|
126
|
+
for match in re.finditer(r"`modules/([a-z-]+)\.md`", text)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def check_product_modules(skill_text: str) -> None:
|
|
131
|
+
primary_modules = routing_modules(skill_text)
|
|
132
|
+
root_modules = {path.stem for path in MODULE_DIR.glob("*.md")}
|
|
133
|
+
extra = sorted(root_modules - primary_modules)
|
|
134
|
+
missing = sorted(primary_modules - root_modules)
|
|
135
|
+
if extra:
|
|
136
|
+
fail("module files not present in routing table: " + ", ".join(extra))
|
|
137
|
+
if missing:
|
|
138
|
+
fail("routing table modules missing files: " + ", ".join(missing))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def check_module_playbooks(skill_text: str) -> None:
|
|
142
|
+
primary_modules = routing_modules(skill_text)
|
|
143
|
+
pseudo_modules = {"tests", "package", "plan"}
|
|
144
|
+
allowed_terms = {"plan"} # allowed only as a concept; public /plan remains forbidden elsewhere.
|
|
145
|
+
for path in MODULE_DIR.glob("*.md"):
|
|
146
|
+
text = path.read_text()
|
|
147
|
+
invalid = sorted(
|
|
148
|
+
token for token in re.findall(r"`([a-z][a-z-]+)`", text)
|
|
149
|
+
if token in pseudo_modules and token not in primary_modules and token not in allowed_terms
|
|
150
|
+
)
|
|
151
|
+
if invalid:
|
|
152
|
+
fail(f"module {path.stem} references non-shipped pseudo-modules: " + ", ".join(invalid))
|
|
153
|
+
for name in sorted(primary_modules - {"router"}):
|
|
154
|
+
module = MODULE_DIR / f"{name}.md"
|
|
155
|
+
text = module.read_text()
|
|
156
|
+
required = COMMON_PLAYBOOK_HEADINGS + MODULE_PLAYBOOK_HEADINGS.get(
|
|
157
|
+
name,
|
|
158
|
+
DEFAULT_PLAYBOOK_HEADINGS,
|
|
159
|
+
)
|
|
160
|
+
missing = [
|
|
161
|
+
heading for heading in required
|
|
162
|
+
if re.search(rf"^{re.escape(heading)}\s*$", text, re.M) is None
|
|
163
|
+
]
|
|
164
|
+
if missing:
|
|
165
|
+
fail(f"module {name} missing playbook headings: " + ", ".join(missing))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def check_subagent_helper(skill_text: str) -> None:
|
|
169
|
+
if not SUBAGENT_HELPER.is_file():
|
|
170
|
+
fail("missing subagent reasoning helper modules/helpers/subagents.md")
|
|
171
|
+
helper_text = SUBAGENT_HELPER.read_text()
|
|
172
|
+
missing_harnesses = [name for name in TARGET_HARNESSES if name not in helper_text]
|
|
173
|
+
if missing_harnesses:
|
|
174
|
+
fail("subagent helper missing target harnesses: " + ", ".join(missing_harnesses))
|
|
175
|
+
primary_modules = routing_modules(skill_text)
|
|
176
|
+
if not primary_modules:
|
|
177
|
+
fail("no primary modules found in Keystone routing table")
|
|
178
|
+
missing_modules = [
|
|
179
|
+
name for name in sorted(primary_modules)
|
|
180
|
+
if f"modules/{name}.md" not in helper_text
|
|
181
|
+
]
|
|
182
|
+
if missing_modules:
|
|
183
|
+
fail("subagent helper missing module reasoning rows: " + ", ".join(missing_modules))
|
|
184
|
+
for name in sorted(primary_modules):
|
|
185
|
+
module = MODULE_DIR / f"{name}.md"
|
|
186
|
+
if not module.is_file():
|
|
187
|
+
fail(f"missing primary module file: modules/{name}.md")
|
|
188
|
+
if "## Subagents and reasoning" not in module.read_text():
|
|
189
|
+
fail(f"module missing subagent reasoning section: modules/{name}.md")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def check_ignored_not_tracked() -> None:
|
|
193
|
+
bad = []
|
|
194
|
+
for path in tracked_files():
|
|
195
|
+
first = path.split("/", 1)[0]
|
|
196
|
+
if path in FORBIDDEN_TRACKED or first in FORBIDDEN_TRACKED:
|
|
197
|
+
bad.append(path)
|
|
198
|
+
if bad:
|
|
199
|
+
fail("ignored artifacts are tracked: " + ", ".join(bad))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def allowlist_entries() -> set[str]:
|
|
203
|
+
if not ALLOWLIST.is_file():
|
|
204
|
+
fail("missing packaging.allowlist")
|
|
205
|
+
return {
|
|
206
|
+
line.strip()
|
|
207
|
+
for line in ALLOWLIST.read_text().splitlines()
|
|
208
|
+
if line.strip() and not line.lstrip().startswith("#")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def check_package_json() -> None:
|
|
213
|
+
package_path = ROOT / "package.json"
|
|
214
|
+
if not package_path.is_file():
|
|
215
|
+
fail("missing package.json")
|
|
216
|
+
package = json.loads(package_path.read_text())
|
|
217
|
+
for key in ("name", "version", "description", "license", "keywords"):
|
|
218
|
+
if key not in package:
|
|
219
|
+
fail(f"package.json missing {key}")
|
|
220
|
+
if package.get("name") != "@static-var/keystone":
|
|
221
|
+
fail('package.json name must be "@static-var/keystone" for npm/pi.dev publishing')
|
|
222
|
+
keywords = package.get("keywords", [])
|
|
223
|
+
if not isinstance(keywords, list) or "pi-package" not in keywords:
|
|
224
|
+
fail('package.json keywords must include "pi-package"')
|
|
225
|
+
files = package.get("files")
|
|
226
|
+
expected_files = allowlist_entries()
|
|
227
|
+
if set(files or []) != expected_files:
|
|
228
|
+
fail("package.json files must exactly match packaging.allowlist entries")
|
|
229
|
+
pi = package.get("pi", {})
|
|
230
|
+
if pi.get("skills") != ["./skills"]:
|
|
231
|
+
fail('package.json must set pi.skills to ["./skills"]')
|
|
232
|
+
if pi.get("extensions") != ["./.pi/extensions/keystone.ts"]:
|
|
233
|
+
fail('package.json must set pi.extensions to ["./.pi/extensions/keystone.ts"]')
|
|
234
|
+
if not PI_EXTENSION.is_file():
|
|
235
|
+
fail("missing Pi extension .pi/extensions/keystone.ts")
|
|
236
|
+
extension = PI_EXTENSION.read_text()
|
|
237
|
+
if 'registerCommand("keystone"' not in extension:
|
|
238
|
+
fail("Pi extension must register public /keystone command")
|
|
239
|
+
leaked = sorted(
|
|
240
|
+
name for name in routing_modules(check_skill())
|
|
241
|
+
if name != "keystone" and f'registerCommand("{name}"' in extension
|
|
242
|
+
)
|
|
243
|
+
if leaked:
|
|
244
|
+
fail("Pi extension exposes internal modules as slash commands: " + ", ".join(leaked))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def main() -> int:
|
|
248
|
+
text = check_skill()
|
|
249
|
+
check_language(text)
|
|
250
|
+
check_references(text)
|
|
251
|
+
check_product_modules(text)
|
|
252
|
+
check_module_playbooks(text)
|
|
253
|
+
check_subagent_helper(text)
|
|
254
|
+
check_ignored_not_tracked()
|
|
255
|
+
check_package_json()
|
|
256
|
+
print("validate-keystone: ok")
|
|
257
|
+
return 0
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
if __name__ == "__main__":
|
|
261
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate the Keystone release archive contents."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import zipfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
10
|
+
ALLOWLIST = ROOT / "packaging.allowlist"
|
|
11
|
+
|
|
12
|
+
REQUIRED = {
|
|
13
|
+
"README.md",
|
|
14
|
+
"HOW_IT_WORKS.md",
|
|
15
|
+
"package.json",
|
|
16
|
+
"packaging.allowlist",
|
|
17
|
+
"Makefile",
|
|
18
|
+
"scripts/build-metadata.py",
|
|
19
|
+
"scripts/validate-keystone.py",
|
|
20
|
+
"scripts/validate-package.py",
|
|
21
|
+
"scripts/package-keystone.sh",
|
|
22
|
+
".pi/extensions/keystone.ts",
|
|
23
|
+
"skills/keystone/SKILL.md",
|
|
24
|
+
"skills/keystone/modules/router.md",
|
|
25
|
+
"skills/keystone/modules/research.md",
|
|
26
|
+
"skills/keystone/modules/shape.md",
|
|
27
|
+
"skills/keystone/modules/breakdown.md",
|
|
28
|
+
"skills/keystone/modules/build.md",
|
|
29
|
+
"skills/keystone/modules/debug.md",
|
|
30
|
+
"skills/keystone/modules/review.md",
|
|
31
|
+
"skills/keystone/modules/ship.md",
|
|
32
|
+
"skills/keystone/modules/health.md",
|
|
33
|
+
"skills/keystone/modules/helpers/subagents.md",
|
|
34
|
+
"skills/keystone/modules/gates/isolation.md",
|
|
35
|
+
"skills/keystone/modules/gates/proof.md",
|
|
36
|
+
".claude-plugin/plugin.json",
|
|
37
|
+
".claude-plugin/marketplace.json",
|
|
38
|
+
".codex-plugin/plugin.json",
|
|
39
|
+
".agents/plugins/marketplace.json",
|
|
40
|
+
}
|
|
41
|
+
FORBIDDEN_NAMES = {
|
|
42
|
+
"index.html",
|
|
43
|
+
"styles.css",
|
|
44
|
+
"skill-engineering.md",
|
|
45
|
+
".DS_Store",
|
|
46
|
+
}
|
|
47
|
+
FORBIDDEN_PREFIXES = (
|
|
48
|
+
"docs/",
|
|
49
|
+
"plans/",
|
|
50
|
+
"maintainers/",
|
|
51
|
+
"dist/",
|
|
52
|
+
".git/",
|
|
53
|
+
".keystone/plans/",
|
|
54
|
+
".keystone/specs/",
|
|
55
|
+
".keystone/tmp/",
|
|
56
|
+
"__pycache__/",
|
|
57
|
+
)
|
|
58
|
+
FORBIDDEN_SUFFIXES = (
|
|
59
|
+
".pyc",
|
|
60
|
+
".plan.md",
|
|
61
|
+
"-plan.md",
|
|
62
|
+
".design.md",
|
|
63
|
+
"-design.md",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def fail(message: str) -> None:
|
|
68
|
+
print(f"validate-package: {message}", file=sys.stderr)
|
|
69
|
+
raise SystemExit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def forbidden(path: str) -> bool:
|
|
73
|
+
base = path.rsplit("/", 1)[-1]
|
|
74
|
+
return (
|
|
75
|
+
base in FORBIDDEN_NAMES
|
|
76
|
+
or path.startswith(FORBIDDEN_PREFIXES)
|
|
77
|
+
or path.endswith(FORBIDDEN_SUFFIXES)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def expand_allowlist() -> set[str] | None:
|
|
82
|
+
if not ALLOWLIST.is_file():
|
|
83
|
+
return None
|
|
84
|
+
expanded: set[str] = set()
|
|
85
|
+
for raw in ALLOWLIST.read_text().splitlines():
|
|
86
|
+
path = raw.strip()
|
|
87
|
+
if not path or path.startswith("#"):
|
|
88
|
+
continue
|
|
89
|
+
if forbidden(path) or path in {"dist", "docs", "plans", "maintainers", ".git"}:
|
|
90
|
+
fail(f"forbidden allowlist entry: {path}")
|
|
91
|
+
candidate = ROOT / path
|
|
92
|
+
if not candidate.exists():
|
|
93
|
+
fail(f"allowlisted path missing: {path}")
|
|
94
|
+
if candidate.is_dir():
|
|
95
|
+
for child in candidate.rglob("*"):
|
|
96
|
+
if child.is_file():
|
|
97
|
+
rel = child.relative_to(ROOT).as_posix()
|
|
98
|
+
if not forbidden(rel):
|
|
99
|
+
expanded.add(rel)
|
|
100
|
+
else:
|
|
101
|
+
expanded.add(path)
|
|
102
|
+
return expanded
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main(argv: list[str]) -> int:
|
|
106
|
+
archive = Path(argv[1]) if len(argv) > 1 else Path("dist/keystone.zip")
|
|
107
|
+
if not archive.is_file():
|
|
108
|
+
fail(f"archive not found: {archive}")
|
|
109
|
+
with zipfile.ZipFile(archive) as zf:
|
|
110
|
+
names = {name for name in zf.namelist() if not name.endswith("/")}
|
|
111
|
+
missing = sorted(REQUIRED - names)
|
|
112
|
+
if missing:
|
|
113
|
+
fail("missing required files: " + ", ".join(missing))
|
|
114
|
+
forbidden_names = sorted(name for name in names if forbidden(name))
|
|
115
|
+
if forbidden_names:
|
|
116
|
+
fail("forbidden files present: " + ", ".join(forbidden_names))
|
|
117
|
+
|
|
118
|
+
expected = expand_allowlist()
|
|
119
|
+
if expected is None:
|
|
120
|
+
expected_list = archive.parent / "keystone.files"
|
|
121
|
+
if expected_list.is_file():
|
|
122
|
+
expected = {
|
|
123
|
+
line.strip()
|
|
124
|
+
for line in expected_list.read_text().splitlines()
|
|
125
|
+
if line.strip()
|
|
126
|
+
}
|
|
127
|
+
if expected is not None:
|
|
128
|
+
extra = sorted(names - expected)
|
|
129
|
+
absent = sorted(expected - names)
|
|
130
|
+
if extra:
|
|
131
|
+
fail("archive contains files outside expanded allowlist: " + ", ".join(extra))
|
|
132
|
+
if absent:
|
|
133
|
+
fail("expanded allowlist files missing from archive: " + ", ".join(absent))
|
|
134
|
+
|
|
135
|
+
print("validate-package: ok")
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
raise SystemExit(main(sys.argv))
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystone
|
|
3
|
+
description: Use when the user invokes /keystone or asks Keystone to route product, research, writing, UI, design, planning, implementation, debugging, review, shipping, or health work through its canonical orchestrator.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Keystone
|
|
7
|
+
|
|
8
|
+
Keystone is a router/orchestrator skill. It selects the right internal module for the user's current request, carries handoffs between modules, and keeps only one public entrypoint: `/keystone`.
|
|
9
|
+
|
|
10
|
+
## Routing rule
|
|
11
|
+
|
|
12
|
+
Load exactly one primary module for the current task. Load gates or helper modules only when the primary module explicitly needs them. Do not expose internal modules as public slash commands. Actively route the work: name the selected module, preserve the current handoff packet, and state the next gate or check before taking irreversible action.
|
|
13
|
+
|
|
14
|
+
## Orchestration sequences
|
|
15
|
+
|
|
16
|
+
Use these common paths unless the user's immediate next action clearly points elsewhere:
|
|
17
|
+
|
|
18
|
+
- Product or feature work: `research -> shape -> breakdown -> build -> review -> ship`.
|
|
19
|
+
- Existing plan or approved design: `breakdown -> build -> review -> ship`.
|
|
20
|
+
- Direct implementation request: `build -> review -> ship`, loading `breakdown` first only when execution order or verification is unclear.
|
|
21
|
+
- Debug loop: `debug -> build -> proof gate -> review`; return to `debug` if proof fails or new symptoms appear.
|
|
22
|
+
- Health finding that needs repair: `health -> build` for contained fixes, or `health -> review` when the finding needs independent validation before mutation.
|
|
23
|
+
|
|
24
|
+
Do not add these as shipped modules; they are routing sequences over the existing nine modules.
|
|
25
|
+
|
|
26
|
+
## Handoff packet contract
|
|
27
|
+
|
|
28
|
+
Whenever one module hands work to another, carry a concise packet:
|
|
29
|
+
|
|
30
|
+
- `source module`: module producing the handoff
|
|
31
|
+
- `target module`: module to load next
|
|
32
|
+
- `goal`: one-sentence outcome for the next module
|
|
33
|
+
- `evidence`: facts, command output, user decisions, or citations already established
|
|
34
|
+
- `files`: relevant paths and whether they are read-only, mutable, or protected
|
|
35
|
+
- `risks`: known uncertainty, skipped checks, safety constraints, or rollback concerns
|
|
36
|
+
- `next check`: the gate, command, question, or review the target should run first
|
|
37
|
+
|
|
38
|
+
If the user overrides a gate, review, or sequence step, record the explicit override in the packet with the risk accepted. Never silently bypass hard safety constraints such as protected files, secret handling, isolation requirements, destructive operations, or policy restrictions; stop and ask or refuse as appropriate.
|
|
39
|
+
|
|
40
|
+
## Re-entry contract
|
|
41
|
+
|
|
42
|
+
When a module temporarily routes to another module and then resumes, the returning module must receive the handoff packet plus the result.
|
|
43
|
+
|
|
44
|
+
- Build -> Debug -> Build: carry the failing symptom, reproduction command, suspected root cause, files inspected, fix recommendation, and proof command. On return to Build, rerun isolation if mutation scope changed, rerun proof for the original symptom, and rerun review if the fix touches behavior, public API, security, data, or release surfaces.
|
|
45
|
+
- Build -> Research/Shape/Breakdown -> Build: carry the clarified decision or plan delta. On return, rerun the relevant Build preflight only for changed scope; do not redo unchanged gates without reason.
|
|
46
|
+
- Review -> Debug/Build -> Review: carry review findings and changed files. On return, review the addressed findings and any newly touched risk area.
|
|
47
|
+
- Health -> Build/Review: carry health evidence, severity, and recommended first check. Build may fix only contained, well-evidenced issues; uncertain or broad health findings go to Review first.
|
|
48
|
+
|
|
49
|
+
## Subagents and reasoning
|
|
50
|
+
|
|
51
|
+
When a module may delegate work, consult `modules/helpers/subagents.md` for host capability, safe delegation rules, and the recommended reasoning level for each Keystone module. If the host cannot enforce per-subagent reasoning, include the desired level in the subagent prompt or work inline.
|
|
52
|
+
|
|
53
|
+
## Routing table
|
|
54
|
+
|
|
55
|
+
| User intent | Primary module |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Choose the right Keystone path, classify intent, resolve module ambiguity | `modules/router.md` |
|
|
58
|
+
| Read, inspect, summarize, extract, research, compare sources, investigate options | `modules/research.md` |
|
|
59
|
+
| Draft, rewrite, UI/UX, visual direction, product decisions, architecture, feature scope | `modules/shape.md` |
|
|
60
|
+
| Breakdown approved design into executable tasks and verification steps | `modules/breakdown.md` |
|
|
61
|
+
| Implement or change files/code/content | `modules/build.md` |
|
|
62
|
+
| Diagnose failures, failing behavior, errors, bug reports, regressions, unexpected behavior | `modules/debug.md` |
|
|
63
|
+
| Review work without changing it | `modules/review.md` |
|
|
64
|
+
| Finalize completed work for delivery or integration | `modules/ship.md` |
|
|
65
|
+
| Agent/tooling health, repository condition, skill drift, context rot, risks | `modules/health.md` |
|
|
66
|
+
|
|
67
|
+
## Ambiguity rule
|
|
68
|
+
|
|
69
|
+
If several modules could apply, choose the module matching the next irreversible action. If still ambiguous, ask one concise clarifying question before loading a primary module.
|