agent-bios 0.7.0 → 0.9.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.
@@ -125,6 +125,13 @@ depends on it, pin it explicitly instead of trusting the environment.
125
125
  leaving stale, unreferenced definitions live. After updating, re-read the
126
126
  resource, check definitions and active references separately, and remove
127
127
  the orphans explicitly.
128
+ - **A new revision is not live traffic**: on a runtime that pins traffic to a
129
+ named revision (e.g. Cloud Run with a fixed split), `gcloud run deploy` (or
130
+ the equivalent) creates the new revision but shifts no traffic to it — the
131
+ previous revision keeps serving until an explicit `gcloud run services
132
+ update-traffic`. Read the "deploy succeeded" message as "a revision exists",
133
+ not "the new code is serving"; verify the live traffic split before
134
+ concluding the deploy took effect.
128
135
  - **Perimeter controls need the enforcement point's own logs**: an agent-side
129
136
  fetch is not an independent external observer — its egress IP and caching
130
137
  path are opaque, and it may share the protected network or serve a stale
@@ -125,6 +125,13 @@ depends on it, pin it explicitly instead of trusting the environment.
125
125
  leaving stale, unreferenced definitions live. After updating, re-read the
126
126
  resource, check definitions and active references separately, and remove
127
127
  the orphans explicitly.
128
+ - **A new revision is not live traffic**: on a runtime that pins traffic to a
129
+ named revision (e.g. Cloud Run with a fixed split), `gcloud run deploy` (or
130
+ the equivalent) creates the new revision but shifts no traffic to it — the
131
+ previous revision keeps serving until an explicit `gcloud run services
132
+ update-traffic`. Read the "deploy succeeded" message as "a revision exists",
133
+ not "the new code is serving"; verify the live traffic split before
134
+ concluding the deploy took effect.
128
135
  - **Perimeter controls need the enforcement point's own logs**: an agent-side
129
136
  fetch is not an independent external observer — its egress IP and caching
130
137
  path are opaque, and it may share the protected network or serve a stale
@@ -1,4 +1,12 @@
1
1
  {
2
2
  "version": 1,
3
- "promotions": []
3
+ "promotions": [
4
+ {
5
+ "learning_id": "a6feee41-2aeb-4fb6-acc6-4584c5c1336c",
6
+ "tier": "domain",
7
+ "domains": [
8
+ "builder-base"
9
+ ]
10
+ }
11
+ ]
4
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bios",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "A thin, low-level instruction layer for LLM CLI agents: one set of principles and behavior whichever model you run. Deploys into $HOME by copy via an explicit `agent-bios install`.",
5
5
  "bin": {
6
6
  "agent-bios": "scripts/install.sh"
@@ -33,7 +33,6 @@ import time
33
33
 
34
34
  REPO = pathlib.Path(__file__).resolve().parent.parent
35
35
  MANIFEST = REPO / "config" / "promotions.json"
36
- DOMAINS = REPO / "config" / "domains.json"
37
36
  UNIVERSAL_TIERS = frozenset({"core", "infra"}) # == assemble.audience UNIVERSAL
38
37
  # learning_id inside a personal bullet's TRAILING comment (collect-learning
39
38
  # prose_bullet: "... <!-- learning_id: <uuid> created: <ts> -->"). Anchored to
@@ -68,23 +67,21 @@ def load_manifest(path=MANIFEST):
68
67
  if isinstance(p, dict) and isinstance(p.get("learning_id"), str)]
69
68
 
70
69
 
71
- def load_domain_keys(path=DOMAINS):
72
- return set(json.loads(path.read_text(encoding="utf-8")).get("domains", {}))
73
-
74
-
75
- def make_in_bundle(full, selection, domain_keys):
76
- """A promotion's placement domain -> is it in THIS user's bundle? Biased to
77
- KEEP on anything not provably installed."""
70
+ def make_in_bundle(full, selection):
71
+ """A promotion's placement AUDIENCE (tier + domains, derived by
72
+ build-promotions from where the bullet actually landed) -> is it in THIS
73
+ user's bundle? Mirrors assemble.py `kept`. Biased to KEEP on anything not
74
+ provably installed."""
78
75
  selection = set(selection or ())
79
76
 
80
- def in_bundle(domain):
77
+ def in_bundle(tier, domains):
81
78
  if full:
82
79
  return True
83
- if domain in UNIVERSAL_TIERS:
80
+ if tier in UNIVERSAL_TIERS:
84
81
  return True
85
- if domain in domain_keys:
86
- return domain in selection
87
- return False # env-personal / unclassified / unknown -> keep
82
+ if tier == "domain":
83
+ return bool(set(domains or ()) & selection)
84
+ return False # env-personal / unknown tier -> keep
88
85
 
89
86
  return in_bundle
90
87
 
@@ -210,7 +207,7 @@ def migrate(home, host, promotions, in_bundle, collect, dry=False, corpus_loaded
210
207
  lid = p["learning_id"]
211
208
  if lid not in local_ids:
212
209
  continue # not held locally (never captured here, or already migrated)
213
- if in_bundle(p.get("domain")):
210
+ if in_bundle(p.get("tier"), p.get("domains", [])):
214
211
  remove_ids.add(lid)
215
212
  else:
216
213
  kept_not_in_bundle += 1 # promoted but not in THIS user's bundle -> keep
@@ -271,7 +268,7 @@ def main():
271
268
  corpus_loaded = True if args.host == "codex" else claude_corpus_loaded(home, args.full)
272
269
 
273
270
  if args.full:
274
- in_bundle = make_in_bundle(True, (), set())
271
+ in_bundle = make_in_bundle(True, ())
275
272
  else:
276
273
  if args.domains is not None:
277
274
  selection = [d for d in args.domains.split(",") if d]
@@ -280,7 +277,7 @@ def main():
280
277
  if not sel_path.is_file():
281
278
  die(f"selection file not found: {sel_path} (pass --full for a non-packaged install)")
282
279
  selection = json.loads(sel_path.read_text(encoding="utf-8")).get("domains", [])
283
- in_bundle = make_in_bundle(False, selection, load_domain_keys())
280
+ in_bundle = make_in_bundle(False, selection)
284
281
 
285
282
  s = migrate(home, args.host, promotions, in_bundle, collect,
286
283
  dry=args.dry_run, corpus_loaded=corpus_loaded)
@@ -311,9 +308,9 @@ def _self_test():
311
308
  L = {"core": "0f8c1c2a-4d1e-4abc-9def-0000000000a1",
312
309
  "bb": "0f8c1c2a-4d1e-4abc-9def-0000000000b2",
313
310
  "off": "0f8c1c2a-4d1e-4abc-9def-0000000000c3"}
314
- promos = [{"learning_id": L["core"], "domain": "core"},
315
- {"learning_id": L["bb"], "domain": "builder-base"},
316
- {"learning_id": L["off"], "domain": "office-work"}]
311
+ promos = [{"learning_id": L["core"], "tier": "core", "domains": []},
312
+ {"learning_id": L["bb"], "tier": "domain", "domains": ["builder-base"]},
313
+ {"learning_id": L["off"], "tier": "domain", "domains": ["office-work"]}]
317
314
 
318
315
  def seed_claude():
319
316
  home = pathlib.Path(tempfile.mkdtemp(prefix="migrate-selftest-"))
@@ -341,7 +338,7 @@ def _self_test():
341
338
  # 1) Packaged, selection={builder-base}: core (universal) + builder-base
342
339
  # removed; office-work KEPT (the silent-loss guard).
343
340
  home = seed_claude()
344
- in_bundle = make_in_bundle(False, ["builder-base"], {"builder-base", "office-work", "llm-pipeline-dev"})
341
+ in_bundle = make_in_bundle(False, ["builder-base"])
345
342
  s = migrate(home, "claude", promos, in_bundle, collect)
346
343
  ids = local_ids(home)
347
344
  checks.append(("packaged: removed core+builder-base", s["removed"] == 2 and s["kept_not_in_bundle"] == 1))
@@ -355,13 +352,13 @@ def _self_test():
355
352
 
356
353
  # 2) Full (non-packaged) install: everything in bundle -> all removed.
357
354
  home = seed_claude()
358
- s = migrate(home, "claude", promos, make_in_bundle(True, (), set()), collect)
355
+ s = migrate(home, "claude", promos, make_in_bundle(True, ()), collect)
359
356
  checks.append(("full install: all 3 removed", s["removed"] == 3 and not local_ids(home)))
360
357
 
361
358
  # 3) dry-run changes nothing on disk.
362
359
  home = seed_claude()
363
360
  before = (home / "personal" / "learnings.jsonl").read_text(encoding="utf-8")
364
- migrate(home, "claude", promos, make_in_bundle(True, (), set()), collect, dry=True)
361
+ migrate(home, "claude", promos, make_in_bundle(True, ()), collect, dry=True)
365
362
  checks.append(("dry-run writes nothing",
366
363
  (home / "personal" / "learnings.jsonl").read_text(encoding="utf-8") == before))
367
364
 
@@ -372,7 +369,7 @@ def _self_test():
372
369
  r = rec(L["bb"], "builder-base")
373
370
  f.write(json.dumps(r, ensure_ascii=False) + "\n")
374
371
  collect.apply_codex(chome, collect.prose_bullet(r), dry=False)
375
- s = migrate(chome, "codex", promos, make_in_bundle(True, (), set()), collect)
372
+ s = migrate(chome, "codex", promos, make_in_bundle(True, ()), collect)
376
373
  agents_txt = (chome / "AGENTS.md").read_text(encoding="utf-8")
377
374
  checks.append(("codex: bullet removed from AGENTS.md region",
378
375
  s["prose_removed"] == 1 and L["bb"] not in agents_txt))
@@ -382,7 +379,7 @@ def _self_test():
382
379
  home = seed_claude() # apply_claude writes an entry WITHOUT @central/bundle.md
383
380
  checks.append(("F2: unwired packaged entry -> corpus not loaded",
384
381
  claude_corpus_loaded(home, full=False) is False))
385
- s = migrate(home, "claude", promos, make_in_bundle(False, ["builder-base"], {"builder-base"}),
382
+ s = migrate(home, "claude", promos, make_in_bundle(False, ["builder-base"]),
386
383
  collect, corpus_loaded=False)
387
384
  checks.append(("F2: corpus-not-loaded keeps ALL",
388
385
  s.get("skipped") == "corpus-not-loaded"