agent-bios 0.9.8 → 0.10.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/DEPENDENCIES.md +19 -19
- package/README.md +43 -12
- package/claude/CLAUDE.md +5 -41
- package/claude/guides/claude-prompting.md +1 -1
- package/claude/guides/cli-multi-model-workflow.md +22 -4
- package/claude/guides/coding-staged-workflow.md +49 -15
- package/claude/guides/concept-economy.md +187 -0
- package/claude/guides/documentation-hygiene.md +112 -0
- package/claude/guides/gpt-prompting.md +1 -1
- package/claude/guides/learning-flow.md +5 -5
- package/claude/guides/llm-capability-boundary.md +6 -1
- package/claude/guides/review-request.md +9 -7
- package/claude/guides/session-distill-workflow.md +19 -9
- package/claude/guides/tooling-gotchas.md +26 -0
- package/claude/guides/verification-discipline.md +166 -0
- package/claude/hooks/tooling-gotchas-hook.py +329 -12
- package/codex/AGENTS.md +5 -41
- package/codex/guides/claude-prompting.md +1 -1
- package/codex/guides/cli-multi-model-workflow.md +22 -4
- package/codex/guides/coding-staged-workflow.md +49 -15
- package/codex/guides/concept-economy.md +187 -0
- package/codex/guides/documentation-hygiene.md +112 -0
- package/codex/guides/gpt-prompting.md +1 -1
- package/codex/guides/learning-flow.md +5 -5
- package/codex/guides/llm-capability-boundary.md +6 -1
- package/codex/guides/review-request.md +9 -7
- package/codex/guides/session-distill-workflow.md +19 -9
- package/codex/guides/tooling-gotchas.md +26 -0
- package/codex/guides/verification-discipline.md +166 -0
- package/{scripts → compose}/assemble.py +194 -17
- package/{scripts → compose}/canary.sh +14 -5
- package/compose/check-domains.py +1178 -0
- package/{config → compose}/domains.json +11 -44
- package/{scripts → compose}/pkgid.py +8 -1
- package/compose/prune-backups.py +204 -0
- package/{scripts → compose}/register-hooks.py +3 -3
- package/install.sh +1233 -0
- package/launch/agent-launch.py +5294 -0
- package/launch/agent-launch.toml +376 -0
- package/{scripts → launch}/check-prompting-targets.sh +1 -1
- package/{scripts → launch}/provision-venv.sh +1 -1
- package/{scripts → learn}/check-learning.py +7 -7
- package/{scripts → learn}/collect-learning.py +10 -10
- package/{config → learn}/learning.schema.json +3 -3
- package/{scripts → learn}/migrate-learnings.py +95 -54
- package/{scripts → learn}/redact.py +4 -4
- package/package.json +32 -27
- package/provenance.json +1 -0
- package/wrappers/claude-run.sh +162 -0
- package/{scripts → wrappers}/codex-run.sh +62 -6
- package/config/agent-launch.toml +0 -143
- package/scripts/agent-launch.py +0 -2350
- package/scripts/check-domains.py +0 -296
- package/scripts/check-parity.sh +0 -2003
- package/scripts/install.sh +0 -819
- /package/{shell → launch}/agent-launch.zsh +0 -0
- /package/{config → learn}/promotions.json +0 -0
- /package/{scripts/session-cost.py → session-cost.py} +0 -0
- /package/{scripts → wrappers}/codex-helm.sh +0 -0
package/scripts/check-domains.py
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Bijection/coverage gate: config/domains.json vs the canonical corpus.
|
|
3
|
-
|
|
4
|
-
The manifest is the sole classification authority (tagged-monolith layout);
|
|
5
|
-
claude/CLAUDE.md stays the sole text authority. This gate closes the
|
|
6
|
-
silent-drop window: a bullet reworded without a manifest update, or a
|
|
7
|
-
manifest entry whose anchor no longer matches, fails HERE, before landing.
|
|
8
|
-
|
|
9
|
-
Blocking checks (any violation exits 1, all violations listed):
|
|
10
|
-
1. schema shape + registry legality (tiers/domains from the manifest's own
|
|
11
|
-
registry; domains list non-empty iff tier == "domain")
|
|
12
|
-
2. bullet bijection: every manifest anchor matches exactly ONE `- ` bullet
|
|
13
|
-
in claude/CLAUDE.md, and every bullet is claimed by exactly ONE entry
|
|
14
|
-
3. file coverage: every file in claude/guides|hooks|agents claimed exactly
|
|
15
|
-
once; every claimed file exists on disk
|
|
16
|
-
4. router-guide co-package: a bullet referencing guides/<g>.md must have an
|
|
17
|
-
audience covered by that guide's audience (universal bullet -> universal
|
|
18
|
-
guide; domain bullet -> guide covering all its domains)
|
|
19
|
-
5. hook source_guide: a hook naming its source guide must carry the same
|
|
20
|
-
tier + domain set as that guide
|
|
21
|
-
6. non-vacuity: every subject set this gate judges is non-empty, so a green
|
|
22
|
-
run cannot be vacuous
|
|
23
|
-
|
|
24
|
-
Informational (non-blocking until budgets are set in the manifest): estimated
|
|
25
|
-
token size per package.
|
|
26
|
-
|
|
27
|
-
--self-test: runs negative controls (mutated manifests that MUST fail) and
|
|
28
|
-
exits 0 only if every mutation is caught. Proves the gate can fail.
|
|
29
|
-
"""
|
|
30
|
-
import json
|
|
31
|
-
import pathlib
|
|
32
|
-
import re
|
|
33
|
-
import sys
|
|
34
|
-
|
|
35
|
-
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
36
|
-
import pkgid # noqa: E402 (sibling module; scripts/ is a flat toolbox, not a package)
|
|
37
|
-
|
|
38
|
-
REPO = pathlib.Path(__file__).resolve().parent.parent
|
|
39
|
-
MANIFEST = REPO / "config" / "domains.json"
|
|
40
|
-
MONOLITH = REPO / "claude" / "CLAUDE.md"
|
|
41
|
-
FILE_SECTIONS = { # manifest key -> corpus dir, glob
|
|
42
|
-
"guides": ("claude/guides", "*.md"),
|
|
43
|
-
"hooks": ("claude/hooks", "*"),
|
|
44
|
-
"agents": ("claude/agents", "*.md"),
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def load_manifest(path=MANIFEST):
|
|
49
|
-
with open(path, encoding="utf-8") as f:
|
|
50
|
-
return json.load(f)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def corpus_bullets(path=MONOLITH):
|
|
54
|
-
text = path.read_text(encoding="utf-8")
|
|
55
|
-
return [ln for ln in text.splitlines() if ln.startswith("- ")]
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def audience(entry, errors, ctx):
|
|
59
|
-
"""Universal set marker, or the frozen domain set; records legality errors."""
|
|
60
|
-
tier = entry.get("tier")
|
|
61
|
-
domains = entry.get("domains", [])
|
|
62
|
-
if tier in ("core", "infra"):
|
|
63
|
-
if domains:
|
|
64
|
-
errors.append(f"{ctx}: tier {tier} must not list domains, got {domains}")
|
|
65
|
-
return "UNIVERSAL"
|
|
66
|
-
if tier == "env-personal":
|
|
67
|
-
if domains:
|
|
68
|
-
errors.append(f"{ctx}: env-personal must not list domains")
|
|
69
|
-
return "NEVER"
|
|
70
|
-
if tier == "domain":
|
|
71
|
-
if not domains:
|
|
72
|
-
errors.append(f"{ctx}: tier domain requires >=1 domain")
|
|
73
|
-
return frozenset(domains)
|
|
74
|
-
errors.append(f"{ctx}: illegal tier {tier!r}")
|
|
75
|
-
return "NEVER"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def covers(guide_aud, bullet_aud):
|
|
79
|
-
if guide_aud == "UNIVERSAL":
|
|
80
|
-
return True
|
|
81
|
-
if bullet_aud in ("UNIVERSAL", "NEVER"):
|
|
82
|
-
return False # universal bullet needs universal guide; NEVER refs nothing
|
|
83
|
-
return isinstance(guide_aud, frozenset) and guide_aud >= bullet_aud
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def run_gate(manifest, bullets, repo=REPO):
|
|
87
|
-
errors = []
|
|
88
|
-
tiers = set(manifest.get("tiers", []))
|
|
89
|
-
domains_reg = set(manifest.get("domains", {}))
|
|
90
|
-
if not tiers or not domains_reg:
|
|
91
|
-
errors.append("registry: tiers/domains registry empty")
|
|
92
|
-
|
|
93
|
-
errors += check_settings_template(manifest, repo)
|
|
94
|
-
|
|
95
|
-
# -- 0. package identity ------------------------------------------------
|
|
96
|
-
# Absent means core (the reservation that keeps pre-v2 artifacts valid), but
|
|
97
|
-
# a PRESENT malformed id must fail rather than fall back — silently treating
|
|
98
|
-
# a typo as core would hand it the engine's unconditional-injection audience.
|
|
99
|
-
pid = pkgid.resolve(manifest)
|
|
100
|
-
if not pkgid.is_valid(pid):
|
|
101
|
-
errors.append(f"package_id: {pid!r} is not @scope/name (lowercase, hyphen-separated)")
|
|
102
|
-
|
|
103
|
-
def check_registry(entry, ctx):
|
|
104
|
-
if entry.get("tier") not in tiers:
|
|
105
|
-
errors.append(f"{ctx}: tier {entry.get('tier')!r} not in registry")
|
|
106
|
-
for d in entry.get("domains", []):
|
|
107
|
-
if d not in domains_reg:
|
|
108
|
-
errors.append(f"{ctx}: domain {d!r} not in registry")
|
|
109
|
-
|
|
110
|
-
# -- 2. bullet bijection ------------------------------------------------
|
|
111
|
-
mb = manifest.get("bullets", [])
|
|
112
|
-
if not bullets:
|
|
113
|
-
errors.append("non-vacuity: no bullets found in monolith")
|
|
114
|
-
if not mb:
|
|
115
|
-
errors.append("non-vacuity: manifest has no bullet entries")
|
|
116
|
-
if len(mb) != len(bullets):
|
|
117
|
-
errors.append(f"bijection: manifest has {len(mb)} bullet entries, monolith has {len(bullets)} bullets")
|
|
118
|
-
claimed = [0] * len(bullets)
|
|
119
|
-
seen_anchors = set()
|
|
120
|
-
for i, entry in enumerate(mb):
|
|
121
|
-
ctx = f"bullets[{i}] anchor={entry.get('anchor', '')!r:.60}"
|
|
122
|
-
check_registry(entry, ctx)
|
|
123
|
-
anchor = entry.get("anchor", "")
|
|
124
|
-
if not anchor:
|
|
125
|
-
errors.append(f"{ctx}: empty anchor")
|
|
126
|
-
continue
|
|
127
|
-
if anchor in seen_anchors:
|
|
128
|
-
errors.append(f"{ctx}: duplicate anchor")
|
|
129
|
-
seen_anchors.add(anchor)
|
|
130
|
-
hits = [j for j, b in enumerate(bullets) if anchor in b]
|
|
131
|
-
if len(hits) != 1:
|
|
132
|
-
errors.append(f"{ctx}: anchor matches {len(hits)} bullets (need exactly 1)")
|
|
133
|
-
for j in hits:
|
|
134
|
-
claimed[j] += 1
|
|
135
|
-
for j, n in enumerate(claimed):
|
|
136
|
-
if n != 1:
|
|
137
|
-
errors.append(f"bijection: bullet line {bullets[j][:70]!r} claimed {n} times (need exactly 1)")
|
|
138
|
-
|
|
139
|
-
# -- 3. file coverage ---------------------------------------------------
|
|
140
|
-
entries = {}
|
|
141
|
-
for key, (rel, glob) in FILE_SECTIONS.items():
|
|
142
|
-
section = manifest.get(key, {})
|
|
143
|
-
entries[key] = section
|
|
144
|
-
if not section:
|
|
145
|
-
errors.append(f"non-vacuity: manifest section {key!r} empty")
|
|
146
|
-
on_disk = {p.name for p in (repo / rel).glob(glob) if p.is_file()}
|
|
147
|
-
if not on_disk:
|
|
148
|
-
errors.append(f"non-vacuity: no files on disk under {rel}")
|
|
149
|
-
for name, entry in section.items():
|
|
150
|
-
check_registry(entry, f"{key}/{name}")
|
|
151
|
-
if name not in on_disk:
|
|
152
|
-
errors.append(f"{key}: claimed file {name} does not exist in {rel}")
|
|
153
|
-
for name in sorted(on_disk - set(section)):
|
|
154
|
-
errors.append(f"{key}: file {name} in {rel} not claimed by the manifest")
|
|
155
|
-
|
|
156
|
-
# -- 4. router-guide co-package ----------------------------------------
|
|
157
|
-
guide_aud = {n: audience(e, errors, f"guides/{n}") for n, e in entries["guides"].items()}
|
|
158
|
-
anchor_of = {id(e): e.get("anchor", "?") for e in mb}
|
|
159
|
-
ref_checks = 0
|
|
160
|
-
for entry in mb:
|
|
161
|
-
hits = [b for b in bullets if entry.get("anchor", "\0") in b]
|
|
162
|
-
if len(hits) != 1:
|
|
163
|
-
continue # already reported by bijection
|
|
164
|
-
b_aud = audience(entry, errors, f"bullet {entry.get('anchor', '')!r:.40}")
|
|
165
|
-
for g in re.findall(r"guides/([a-z0-9-]+\.md)", hits[0]):
|
|
166
|
-
ref_checks += 1
|
|
167
|
-
if g not in guide_aud:
|
|
168
|
-
errors.append(f"router: bullet {entry['anchor']!r:.40} references unclaimed guide {g}")
|
|
169
|
-
elif not covers(guide_aud[g], b_aud):
|
|
170
|
-
errors.append(
|
|
171
|
-
f"router: bullet {entry['anchor']!r:.40} (audience {b_aud}) references guide {g} "
|
|
172
|
-
f"(audience {guide_aud[g]}) — user can hold the router without the guide"
|
|
173
|
-
)
|
|
174
|
-
if ref_checks == 0:
|
|
175
|
-
errors.append("non-vacuity: no router->guide references were checked")
|
|
176
|
-
|
|
177
|
-
# -- 5. hook source_guide -----------------------------------------------
|
|
178
|
-
for name, entry in entries["hooks"].items():
|
|
179
|
-
src = entry.get("source_guide")
|
|
180
|
-
if src:
|
|
181
|
-
g = entries["guides"].get(src)
|
|
182
|
-
if g is None:
|
|
183
|
-
errors.append(f"hooks/{name}: source_guide {src} not in manifest guides")
|
|
184
|
-
elif (entry.get("tier"), sorted(entry.get("domains", []))) != (g.get("tier"), sorted(g.get("domains", []))):
|
|
185
|
-
errors.append(f"hooks/{name}: tier/domains differ from source guide {src}")
|
|
186
|
-
|
|
187
|
-
# -- informational token report ------------------------------------------
|
|
188
|
-
sizes = {}
|
|
189
|
-
for entry in mb:
|
|
190
|
-
key = entry.get("tier") if entry.get("tier") != "domain" else ",".join(entry.get("domains", ["?"])[:1])
|
|
191
|
-
hit = next((b for b in bullets if entry.get("anchor", "\0") in b), "")
|
|
192
|
-
sizes[key] = sizes.get(key, 0) + len(hit) // 4
|
|
193
|
-
for name, entry in entries["guides"].items():
|
|
194
|
-
key = entry.get("tier") if entry.get("tier") != "domain" else ",".join(entry.get("domains", ["?"])[:1])
|
|
195
|
-
p = repo / FILE_SECTIONS["guides"][0] / name
|
|
196
|
-
if p.is_file():
|
|
197
|
-
sizes[key] = sizes.get(key, 0) + len(p.read_text(encoding="utf-8")) // 4
|
|
198
|
-
return errors, sizes
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def check_settings_template(manifest, repo=REPO):
|
|
202
|
-
"""Every hook registered in the shipped settings template must be a hook the
|
|
203
|
-
manifest declares.
|
|
204
|
-
|
|
205
|
-
`merge_settings` only re-adds template entries whose command names a
|
|
206
|
-
manifest-declared hook (assemble.py:167-169), so an entry for anything else
|
|
207
|
-
is silently inert — it looks registered, ships to every user, and never runs.
|
|
208
|
-
That is how a machine-local registration ends up committed in a
|
|
209
|
-
deploy-managed file. Make it invalid instead of asking people to remember.
|
|
210
|
-
"""
|
|
211
|
-
errors = []
|
|
212
|
-
spath = repo / "claude" / "settings.template.json"
|
|
213
|
-
if not spath.is_file():
|
|
214
|
-
return ["settings: claude/settings.template.json missing"]
|
|
215
|
-
names = set(manifest.get("hooks", {}))
|
|
216
|
-
entries = [(ev, h.get("command", ""))
|
|
217
|
-
for ev, evs in json.loads(spath.read_text(encoding="utf-8")).get("hooks", {}).items()
|
|
218
|
-
for en in evs for h in en.get("hooks", [])]
|
|
219
|
-
if not entries:
|
|
220
|
-
errors.append("non-vacuity: settings template registers no hooks")
|
|
221
|
-
for ev, cmd in entries:
|
|
222
|
-
if not any(("/hooks/" + n) in cmd for n in names):
|
|
223
|
-
errors.append(f"settings: {ev} hook {cmd!r} names no manifest hook "
|
|
224
|
-
f"(known: {sorted(names)}) — it would never deploy")
|
|
225
|
-
return errors
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def self_test(manifest, bullets):
|
|
229
|
-
"""Negative controls: each mutation MUST make the gate fail."""
|
|
230
|
-
import copy
|
|
231
|
-
|
|
232
|
-
muts = []
|
|
233
|
-
m1 = copy.deepcopy(manifest)
|
|
234
|
-
m1["bullets"] = m1["bullets"][1:]
|
|
235
|
-
muts.append(("dropped bullet entry", m1, bullets))
|
|
236
|
-
m2 = copy.deepcopy(manifest)
|
|
237
|
-
m2["bullets"][0]["anchor"] = "zz-no-such-phrase-zz"
|
|
238
|
-
muts.append(("anchor matches nothing (reword drift)", m2, bullets))
|
|
239
|
-
m3 = copy.deepcopy(manifest)
|
|
240
|
-
m3["bullets"][1]["anchor"] = m3["bullets"][0]["anchor"]
|
|
241
|
-
muts.append(("duplicate anchor claim", m3, bullets))
|
|
242
|
-
m4 = copy.deepcopy(manifest)
|
|
243
|
-
first_guide = next(iter(m4["guides"]))
|
|
244
|
-
del m4["guides"][first_guide]
|
|
245
|
-
muts.append((f"unclaimed guide {first_guide}", m4, bullets))
|
|
246
|
-
b5 = bullets + ["- a brand new bullet the manifest never heard of"]
|
|
247
|
-
muts.append(("bullet added without manifest entry", copy.deepcopy(manifest), b5))
|
|
248
|
-
m6 = copy.deepcopy(manifest)
|
|
249
|
-
m6["package_id"] = "@Acme/Builder" # uppercase is not a legal segment
|
|
250
|
-
muts.append(("malformed package_id", m6, bullets))
|
|
251
|
-
m7 = copy.deepcopy(manifest)
|
|
252
|
-
m7["hooks"] = {"renamed-hook.py": {"tier": "core", "domains": []}}
|
|
253
|
-
muts.append(("settings registers a hook the manifest does not declare", m7, bullets))
|
|
254
|
-
|
|
255
|
-
# Targeted: the settings rule itself must speak, not just some neighbouring
|
|
256
|
-
# check tripping on the same mutation.
|
|
257
|
-
import copy as _c
|
|
258
|
-
m_off = _c.deepcopy(manifest)
|
|
259
|
-
m_off["hooks"] = {"renamed-hook.py": {"tier": "core", "domains": []}}
|
|
260
|
-
settings_errs = [e for e in check_settings_template(m_off) if e.startswith("settings:")]
|
|
261
|
-
print(f"self-test [{'CAUGHT' if settings_errs else 'MISSED'}] settings rule fires on its own")
|
|
262
|
-
|
|
263
|
-
failed = [] if settings_errs else ["settings rule fires on its own"]
|
|
264
|
-
for name, mm, bb in muts:
|
|
265
|
-
errs, _ = run_gate(mm, bb)
|
|
266
|
-
if not errs:
|
|
267
|
-
failed.append(name)
|
|
268
|
-
print(f"self-test [{'CAUGHT' if errs else 'MISSED'}] {name}")
|
|
269
|
-
return failed
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def main():
|
|
273
|
-
manifest = load_manifest()
|
|
274
|
-
bullets = corpus_bullets()
|
|
275
|
-
if "--self-test" in sys.argv:
|
|
276
|
-
missed = self_test(manifest, bullets)
|
|
277
|
-
if missed:
|
|
278
|
-
print(f"SELF-TEST FAIL: gate missed: {missed}")
|
|
279
|
-
return 1
|
|
280
|
-
print("SELF-TEST OK: every negative control was caught")
|
|
281
|
-
return 0
|
|
282
|
-
errors, sizes = run_gate(manifest, bullets)
|
|
283
|
-
for e in errors:
|
|
284
|
-
print(f"FAIL: {e}")
|
|
285
|
-
print("-- package token estimate (informational) --")
|
|
286
|
-
for k in sorted(sizes):
|
|
287
|
-
print(f" {k}: ~{sizes[k]} tokens")
|
|
288
|
-
if errors:
|
|
289
|
-
print(f"DOMAINS GATE FAIL: {len(errors)} violation(s)")
|
|
290
|
-
return 1
|
|
291
|
-
print(f"DOMAINS GATE OK: {len(bullets)} bullets bijective, all files claimed, routers co-packaged")
|
|
292
|
-
return 0
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if __name__ == "__main__":
|
|
296
|
-
sys.exit(main())
|