agent-bios 0.18.0 → 0.19.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.
Files changed (53) hide show
  1. package/DEPENDENCIES.md +236 -80
  2. package/INSTALL.md +112 -0
  3. package/README.md +184 -524
  4. package/claude/CLAUDE.md +1 -1
  5. package/claude/guides/cli-multi-model-workflow.md +1 -1
  6. package/claude/guides/learning-flow.md +23 -12
  7. package/claude/guides/session-distill-workflow.md +22 -12
  8. package/codex/AGENTS.md +1 -1
  9. package/codex/guides/cli-multi-model-workflow.md +1 -1
  10. package/codex/guides/learning-flow.md +23 -12
  11. package/codex/guides/session-distill-workflow.md +22 -12
  12. package/compose/app_bridge/SKILL.md +75 -0
  13. package/compose/app_bridge/agents/openai.yaml +2 -0
  14. package/compose/app_bridge/scripts/bridge.py +76 -0
  15. package/compose/bootstrap/SKILL.md +12 -1
  16. package/compose/corpus.py +31 -9
  17. package/compose/corpus_app.py +456 -0
  18. package/compose/corpus_import.py +529 -0
  19. package/compose/corpus_install.py +196 -18
  20. package/compose/corpus_session.py +27 -0
  21. package/compose/corpus_setup.py +674 -0
  22. package/compose/corpus_setup_cli.py +582 -0
  23. package/compose/corpus_setup_i18n.py +318 -0
  24. package/compose/corpus_setup_ui.py +633 -0
  25. package/compose/corpus_store.py +167 -29
  26. package/compose/corpus_transaction.py +43 -10
  27. package/compose/corpus_ui_runtime.py +278 -0
  28. package/compose/setup/START.md +147 -0
  29. package/compose/ui_runtime/linkify_it_py-2.2.0-py3-none-any.whl +0 -0
  30. package/compose/ui_runtime/manifest.json +238 -0
  31. package/compose/ui_runtime/markdown_it_py-4.2.0-py3-none-any.whl +0 -0
  32. package/compose/ui_runtime/mdit_py_plugins-0.6.1-py3-none-any.whl +0 -0
  33. package/compose/ui_runtime/mdurl-0.1.2-py3-none-any.whl +0 -0
  34. package/compose/ui_runtime/platformdirs-4.11.8-py3-none-any.whl +0 -0
  35. package/compose/ui_runtime/pygments-2.21.0-py3-none-any.whl +0 -0
  36. package/compose/ui_runtime/rich-15.0.0-py3-none-any.whl +0 -0
  37. package/compose/ui_runtime/textual-8.2.8-py3-none-any.whl +0 -0
  38. package/compose/ui_runtime/typing_extensions-4.16.0-py3-none-any.whl +0 -0
  39. package/docs/advanced-launch.md +131 -0
  40. package/docs/assets/corpus-studio.svg +227 -0
  41. package/docs/corpus.md +117 -0
  42. package/docs/recovery.md +201 -0
  43. package/docs/session-model.md +120 -0
  44. package/docs/setup.md +190 -0
  45. package/docs/understand.md +40 -0
  46. package/install.sh +75 -46
  47. package/launch/agent-launch.py +91 -47
  48. package/launch/provision-venv.sh +44 -13
  49. package/learn/collect-learning.py +14 -5
  50. package/learn/learning.schema.json +2 -2
  51. package/package.json +14 -2
  52. package/provenance.json +1 -1
  53. package/wrappers/claude-run.sh +10 -13
@@ -311,6 +311,7 @@ class CorpusStore:
311
311
  # Optional, so reading an older state does not change its revision or the
312
312
  # exact before/after documents used by prepared-transaction recovery.
313
313
  self._enabled_overrides(state)
314
+ self._selection_mode(state, {})
314
315
  return state
315
316
 
316
317
  @staticmethod
@@ -322,6 +323,13 @@ class CorpusStore:
322
323
  raise CorpusStoreError("personal state has invalid enabled_overrides")
323
324
  return values
324
325
 
326
+ @staticmethod
327
+ def _selection_mode(user: dict[str, Any], defaults: dict[str, Any], requested: str | None = None) -> str:
328
+ value = requested if requested is not None else user.get("selection_mode", defaults.get("selection_mode", "default"))
329
+ if not isinstance(value, str) or value not in {"default", "selected", "none"}:
330
+ raise ValidationError("selection_mode must be default, selected, or none")
331
+ return value
332
+
325
333
  def _write_transaction(self, tx_id: str, record: dict[str, Any]) -> None:
326
334
  _atomic_write(self.runtime / "transactions" / tx_id / "journal.json", record)
327
335
 
@@ -645,7 +653,7 @@ class CorpusStore:
645
653
  raise ValidationError("selection must be a list of qualified refs/domains")
646
654
  packages = {p.get("package_id") for p in inventory.get("packages", []) if isinstance(p, dict)}
647
655
  for value in selection:
648
- if value == "all":
656
+ if value == "all" or value in packages:
649
657
  continue
650
658
  if ":" in value:
651
659
  continue
@@ -704,6 +712,18 @@ class CorpusStore:
704
712
  if package not in packages or domain not in packages[package]:
705
713
  raise ValidationError(f"unknown corpus selection domain: {value}")
706
714
 
715
+ def _selection_subjects(self, runtime: dict[str, Any], user: dict[str, Any],
716
+ items: list[dict[str, Any]], selection: list[str] | None) -> list[dict[str, Any]]:
717
+ """Validate stored host-qualified selections without delivering another host's items."""
718
+ subjects = list(items)
719
+ refs = {item["ref"] for item in subjects}
720
+ for host in ("claude", "codex"):
721
+ prefix = f"@local/learnings-{host}:"
722
+ if any(value.startswith(prefix) and value not in refs for value in selection or []):
723
+ subjects.extend(item for item in self._effective_items(runtime, user, host=host)[0]
724
+ if item.get("active", True))
725
+ return subjects
726
+
707
727
  def _validate_candidate_projection(self, runtime: dict[str, Any], user: dict[str, Any]) -> None:
708
728
  """Use the real compiler as a plan validator without publishing output."""
709
729
  catalog = self._catalog_module()
@@ -713,8 +733,10 @@ class CorpusStore:
713
733
  items, _inventory, defaults, _baseline = self._effective_items(runtime, user, host=host)
714
734
  active = [item for item in items if item.get("active", True) is not False]
715
735
  selection = self._effective_selection(user, defaults, None)
716
- self._validate_snapshot_selection(selection, _inventory, active)
717
- selected = self._selected_items(active, selection, self._enabled_overrides(user))
736
+ self._validate_snapshot_selection(selection, _inventory,
737
+ self._selection_subjects(runtime, user, active, selection))
738
+ selected = self._selected_items(active, selection, self._enabled_overrides(user),
739
+ mode=self._selection_mode(user, defaults), host=host)
718
740
  self._require_resolved(selected)
719
741
  catalog.compile_items(_copy_json(selected), Path(temp) / host, host)
720
742
  except Exception as exc:
@@ -802,12 +824,15 @@ class CorpusStore:
802
824
 
803
825
  # ---- public read API ----------------------------------------------------
804
826
 
805
- def install(self, domains: list[str] | None = None) -> dict[str, Any]:
827
+ def install(self, domains: list[str] | None = None, *, selection_mode: str | None = None,
828
+ replace_selection: bool = False) -> dict[str, Any]:
806
829
  """Install one immutable validated baseline tuple without touching user data."""
807
830
  with self._lock():
808
- return self.commit_install(self.prepare_install(domains))
831
+ return self.commit_install(self.prepare_install(domains, selection_mode=selection_mode,
832
+ replace_selection=replace_selection))
809
833
 
810
- def prepare_install(self, domains: list[str] | None = None) -> dict[str, Any]:
834
+ def prepare_install(self, domains: list[str] | None = None, *, selection_mode: str | None = None,
835
+ replace_selection: bool = False) -> dict[str, Any]:
811
836
  """Stage a validated baseline and source plan without advancing any pointer."""
812
837
  with self._lock():
813
838
  self._recover_locked()
@@ -820,9 +845,15 @@ class CorpusStore:
820
845
  items = [self._validate_item(item, allow_origin=True) for item in raw_items]
821
846
  if len({item["ref"] for item in items}) != len(items):
822
847
  raise ValidationError("catalog has duplicate corpus refs")
823
- defaults = {"schema_version": SCHEMA_VERSION,
824
- "selection": self._normalized_install_selection(domains, catalog) +
825
- [LOCAL_PACKAGE, "@local/learnings-claude", "@local/learnings-codex"]}
848
+ mode = self._selection_mode({}, {}, selection_mode)
849
+ selected = self._normalized_install_selection(domains, catalog)
850
+ if mode == "none" and selected:
851
+ raise ValidationError("no-corpus installation cannot include selection targets")
852
+ defaults = {"schema_version": SCHEMA_VERSION, "selection": selected}
853
+ if mode == "default":
854
+ defaults["selection"] += [LOCAL_PACKAGE, "@local/learnings-claude", "@local/learnings-codex"]
855
+ if selection_mode is not None:
856
+ defaults["selection_mode"] = mode
826
857
  promotion_path = self.repo / "learn" / "promotions.json"
827
858
  promotions = _json_read(promotion_path, {"version": 0, "promotions": []})
828
859
  if not isinstance(promotions, dict) or not isinstance(promotions.get("promotions"), list):
@@ -849,6 +880,11 @@ class CorpusStore:
849
880
  # successful-install record changes.
850
881
  next_user = self._rebase_overlays(old_inventory, catalog, user)
851
882
  before = {"runtime": _copy_json(runtime), "user": _copy_json(user)}
883
+ if replace_selection:
884
+ next_user = _copy_json(next_user)
885
+ next_user["selection"] = _copy_json(defaults["selection"])
886
+ next_user["selection_mode"] = mode
887
+ next_user.pop("enabled_overrides", None)
852
888
  _atomic_write(root / "inventory.json", catalog)
853
889
  _atomic_write(root / "defaults.json", defaults)
854
890
  _atomic_write(root / "promotions.json", promotions)
@@ -909,6 +945,7 @@ class CorpusStore:
909
945
  "revision": revision, "baseline_count": len(refs),
910
946
  "personal_items": len(user["items"]), "overrides": len(user["overrides"]),
911
947
  "tombstones": len(user["tombstones"]), "selection": self._effective_selection(user, defaults, None),
948
+ "selection_mode": self._selection_mode(user, defaults),
912
949
  "enabled_overrides": _copy_json(self._enabled_overrides(user)),
913
950
  }
914
951
 
@@ -925,7 +962,8 @@ class CorpusStore:
925
962
  overrides = self._enabled_overrides(user)
926
963
  selection = self._effective_selection(user, defaults, None)
927
964
  enabled = {item["ref"] for item in self._selected_items(
928
- [item for item in effective.values() if item.get("active", True) is not False], selection, overrides)}
965
+ [item for item in effective.values() if item.get("active", True) is not False], selection, overrides,
966
+ mode=self._selection_mode(user, defaults))}
929
967
  revision = self._authoring_revision(runtime, user)
930
968
  rows: list[dict[str, Any]] = []
931
969
  for ref in sorted(set(source) | set(user["items"]) | set(effective)):
@@ -993,11 +1031,12 @@ class CorpusStore:
993
1031
  raise CorpusStoreError("could not allocate an unused personal identity")
994
1032
 
995
1033
  def _prepare_operation(self, payload: dict[str, Any], runtime: dict[str, Any], user: dict[str, Any],
996
- *, allocated_item_id: str | None = None) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
1034
+ *, allocated_item_id: str | None = None,
1035
+ allocated_item_ids: list[str] | None = None) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
997
1036
  if not isinstance(payload, dict):
998
1037
  raise ValidationError("plan payload must be an object")
999
1038
  op = payload.get("operation", payload.get("op"))
1000
- if op not in {"create", "update", "remove", "restore", "recover", "reset", "rollback", "select", "enable"}:
1039
+ if not isinstance(op, str) or op not in {"create", "update", "remove", "restore", "recover", "reset", "rollback", "select", "enable", "import"}:
1001
1040
  raise ValidationError("unknown corpus operation")
1002
1041
  allowed = {
1003
1042
  "create": {"operation", "op", "item", "package_id", "expected_revision"},
@@ -1007,8 +1046,9 @@ class CorpusStore:
1007
1046
  "recover": {"operation", "op", "ref", "expected_revision"},
1008
1047
  "reset": {"operation", "op", "expected_revision"},
1009
1048
  "rollback": {"operation", "op", "baseline_ref", "history_id", "expected_revision"},
1010
- "select": {"operation", "op", "selection", "expected_revision"},
1049
+ "select": {"operation", "op", "selection", "selection_mode", "expected_revision"},
1011
1050
  "enable": {"operation", "op", "items", "expected_revision"},
1051
+ "import": {"operation", "op", "capture_id", "candidates", "excluded", "expected_revision"},
1012
1052
  }[op]
1013
1053
  unknown = set(payload) - allowed
1014
1054
  if unknown:
@@ -1021,7 +1061,31 @@ class CorpusStore:
1021
1061
  items, inventory, defaults, _baseline_ref = self._effective_items(runtime, user)
1022
1062
  effective = {item["ref"]: item for item in items}
1023
1063
  details: dict[str, Any] = {"operation": op}
1024
- if op == "create":
1064
+ if op == "import":
1065
+ import importlib
1066
+ name = "compose.corpus_import" if __package__ else "corpus_import"
1067
+ prepared_import = importlib.import_module(name).prepare_items(self, payload, user)
1068
+ refs = prepared_import["existing_refs"]
1069
+ receipt = prepared_import["receipt"]
1070
+ rows = prepared_import["items"]
1071
+ if rows:
1072
+ if allocated_item_ids is None or len(allocated_item_ids) != len(rows):
1073
+ raise ValidationError("import needs recorded runtime-owned identities")
1074
+ refs = []
1075
+ for raw, item_id in zip(rows, allocated_item_ids):
1076
+ _safe_part(item_id, "personal item id")
1077
+ raw = _copy_json(raw)
1078
+ raw.update(package_id=LOCAL_PACKAGE, item_id=item_id, ref=f"{LOCAL_PACKAGE}:{item_id}")
1079
+ item = self._validate_item(self._normalize_content(raw), allow_origin=True)
1080
+ if item["ref"] in effective or item["ref"] in next_user["items"]:
1081
+ raise ValidationError("import identity is already in use")
1082
+ next_user["items"][item["ref"]] = item
1083
+ refs.append(item["ref"])
1084
+ receipt["refs"] = refs
1085
+ receipt["item_digests"] = {ref: _digest(next_user["items"][ref]) for ref in refs}
1086
+ next_user.setdefault("imports", {})[receipt["request_digest"]] = receipt
1087
+ details.update(refs=refs, import_receipt=receipt, already_imported=not bool(rows))
1088
+ elif op == "create":
1025
1089
  raw = _copy_json(payload.get("item"))
1026
1090
  if not isinstance(raw, dict):
1027
1091
  raise ValidationError("create needs item")
@@ -1168,6 +1232,14 @@ class CorpusStore:
1168
1232
  details["items"] = _copy_json(choices)
1169
1233
  elif op == "select":
1170
1234
  selection = self._validate_selection(payload.get("selection"), inventory)
1235
+ if "selection_mode" in payload:
1236
+ if payload["selection_mode"] is None:
1237
+ raise ValidationError("selection_mode cannot be null")
1238
+ mode = self._selection_mode({}, {}, payload["selection_mode"])
1239
+ if mode == "none" and selection:
1240
+ raise ValidationError("no-corpus selection cannot include targets")
1241
+ next_user["selection_mode"] = mode
1242
+ details["selection_mode"] = mode
1171
1243
  next_user["selection"] = selection
1172
1244
  details["selection"] = selection
1173
1245
  elif op == "reset":
@@ -1181,6 +1253,8 @@ class CorpusStore:
1181
1253
  next_runtime["selected_baseline_ref"] = latest
1182
1254
  _inv, latest_defaults = self._read_baseline(latest)
1183
1255
  next_user["selection"] = latest_defaults.get("selection")
1256
+ if "selection_mode" in latest_defaults:
1257
+ next_user["selection_mode"] = latest_defaults["selection_mode"]
1184
1258
  details["baseline_ref"] = latest
1185
1259
  elif op == "rollback":
1186
1260
  history_id = payload.get("history_id")
@@ -1209,8 +1283,22 @@ class CorpusStore:
1209
1283
  self._recover_locked()
1210
1284
  runtime, user = self._runtime_state(), self._user_state()
1211
1285
  before = self._authoring_revision(runtime, user)
1212
- allocated = self._next_personal_id(user) if isinstance(payload, dict) and payload.get("operation", payload.get("op")) == "create" else None
1213
- next_runtime, next_user, details = self._prepare_operation(payload, runtime, user, allocated_item_id=allocated)
1286
+ op = payload.get("operation", payload.get("op")) if isinstance(payload, dict) else None
1287
+ allocated = self._next_personal_id(user) if op == "create" else None
1288
+ allocated_many = None
1289
+ if op == "import":
1290
+ import importlib
1291
+ name = "compose.corpus_import" if __package__ else "corpus_import"
1292
+ importer = importlib.import_module(name)
1293
+ payload = importer.sanitize_import_payload(payload)
1294
+ prepared_import = importer.prepare_items(self, payload, user)
1295
+ allocated_many, reserved = [], _copy_json(user)
1296
+ for _item in prepared_import["items"]:
1297
+ item_id = self._next_personal_id(reserved)
1298
+ allocated_many.append(item_id)
1299
+ reserved["items"][f"{LOCAL_PACKAGE}:{item_id}"] = {}
1300
+ next_runtime, next_user, details = self._prepare_operation(payload, runtime, user, allocated_item_id=allocated,
1301
+ allocated_item_ids=allocated_many)
1214
1302
  self._validate_candidate_projection(next_runtime, next_user)
1215
1303
  after = self._authoring_revision(next_runtime, next_user)
1216
1304
  plan_id = uuid.uuid4().hex
@@ -1222,6 +1310,8 @@ class CorpusStore:
1222
1310
  }
1223
1311
  if allocated is not None:
1224
1312
  plan["allocated_item_id"] = allocated
1313
+ if allocated_many is not None:
1314
+ plan["allocated_item_ids"] = allocated_many
1225
1315
  self._write_transaction(plan_id, {"state": "PLANNED", "plan": plan})
1226
1316
  return {key: plan[key] for key in ("schema_version", "plan_id", "expected_revision", "result_revision", "details")}
1227
1317
 
@@ -1251,11 +1341,16 @@ class CorpusStore:
1251
1341
  # Persisted pre-allocation plans already resolved their identity in after-state.
1252
1342
  ref = plan.get("details", {}).get("ref")
1253
1343
  allocated = ref.split(":", 1)[1] if isinstance(ref, str) and ref.startswith(LOCAL_PACKAGE + ":") else None
1254
- next_runtime, next_user, details = self._prepare_operation(plan["payload"], runtime, user, allocated_item_id=allocated)
1344
+ next_runtime, next_user, details = self._prepare_operation(plan["payload"], runtime, user, allocated_item_id=allocated,
1345
+ allocated_item_ids=plan.get("allocated_item_ids"))
1255
1346
  self._validate_candidate_projection(next_runtime, next_user)
1256
1347
  result = self._authoring_revision(next_runtime, next_user)
1257
1348
  if result != plan.get("result_revision"):
1258
1349
  raise CorpusStoreError("plan result changed during apply")
1350
+ if details.get("operation") == "import":
1351
+ import importlib
1352
+ name = "compose.corpus_import" if __package__ else "corpus_import"
1353
+ importlib.import_module(name).verify_import_sources(self, details)
1259
1354
  prepared = {"state": "PREPARED", "plan": plan, "prior_revision": current, "prepared_at": _utcnow()}
1260
1355
  history_id = f"{prepared['prepared_at'].replace(':', '').replace('+00:00', 'Z')}-{plan_id[:12]}"
1261
1356
  prepared["history_id"] = history_id
@@ -1279,18 +1374,42 @@ class CorpusStore:
1279
1374
  # ---- immutable snapshots and history -----------------------------------
1280
1375
 
1281
1376
  def _selected_items(self, items: list[dict[str, Any]], selection: list[str] | None,
1282
- overrides: dict[str, bool] | None = None) -> list[dict[str, Any]]:
1377
+ overrides: dict[str, bool] | None = None, *, mode: str = "default",
1378
+ host: str | None = None, cwd: str | Path | None = None) -> list[dict[str, Any]]:
1379
+ self._selection_mode({}, {}, mode)
1380
+ if mode == "none":
1381
+ return []
1283
1382
  overrides = overrides or {}
1383
+ working = Path(cwd or Path.cwd()).resolve()
1284
1384
  selected: list[dict[str, Any]] = []
1285
1385
  for item in items:
1286
- if item["ref"] in overrides:
1386
+ origin = item.get("origin", {})
1387
+ if origin.get("type") == "instruction_import":
1388
+ scope = origin.get("scope", {})
1389
+ if not isinstance(scope, dict) or scope.get("kind") not in {"global", "project"}:
1390
+ raise ValidationError("imported item has invalid source scope")
1391
+ hosts = origin.get("hosts", ["claude", "codex"])
1392
+ if not isinstance(hosts, list) or not hosts or any(value not in {"claude", "codex"} for value in hosts):
1393
+ raise ValidationError("imported item has invalid host scope")
1394
+ if host is not None and host not in hosts:
1395
+ continue
1396
+ if scope["kind"] == "project":
1397
+ root = scope.get("root")
1398
+ if not isinstance(root, str) or not Path(root).is_absolute():
1399
+ raise ValidationError("imported project root must be absolute")
1400
+ project_root = Path(root)
1401
+ if project_root.resolve() != project_root:
1402
+ raise ValidationError("imported project root is no longer canonical; review its scope")
1403
+ if not working.is_relative_to(project_root):
1404
+ continue
1405
+ if item["ref"] in overrides and (mode == "default" or overrides[item["ref"]] is False):
1287
1406
  if overrides[item["ref"]]:
1288
1407
  selected.append(item)
1289
1408
  continue
1290
1409
  if selection and "all" in selection:
1291
1410
  selected.append(item)
1292
1411
  continue
1293
- if item.get("tier") in {"core", "infra"}:
1412
+ if mode == "default" and item.get("tier") in {"core", "infra"}:
1294
1413
  selected.append(item)
1295
1414
  continue
1296
1415
  if not selection:
@@ -1303,7 +1422,8 @@ class CorpusStore:
1303
1422
  return selected
1304
1423
 
1305
1424
  def snapshot(self, host: str, selection: list[str] | None = None, dry_run: bool = False,
1306
- native: bool = False) -> dict[str, Any]:
1425
+ native: bool = False, *, selection_mode: str | None = None,
1426
+ cwd: str | Path | None = None) -> dict[str, Any]:
1307
1427
  """Compose an immutable activated-session snapshot.
1308
1428
 
1309
1429
  A dry run has no durable write path: it compiles in an OS temporary
@@ -1327,8 +1447,16 @@ class CorpusStore:
1327
1447
  items, inventory, defaults, baseline_ref = self._effective_items(runtime, user, host=host)
1328
1448
  active = [item for item in items if item.get("active", True) is not False]
1329
1449
  effective_selection = self._effective_selection(user, defaults, selection)
1330
- self._validate_snapshot_selection(effective_selection, inventory, active)
1331
- selected = self._selected_items(active, effective_selection, self._enabled_overrides(user))
1450
+ mode = self._selection_mode(user, defaults, selection_mode)
1451
+ if selection is not None and selection_mode is None and mode == "none":
1452
+ mode = "default"
1453
+ if mode == "none":
1454
+ effective_selection = []
1455
+ working = Path(cwd or Path.cwd()).resolve()
1456
+ self._validate_snapshot_selection(effective_selection, inventory,
1457
+ self._selection_subjects(runtime, user, active, effective_selection))
1458
+ selected = self._selected_items(active, effective_selection, self._enabled_overrides(user),
1459
+ mode=mode, host=host, cwd=working)
1332
1460
  self._require_resolved(selected)
1333
1461
  selected, promotion_warnings = self._resolve_promotions(selected, user, host, baseline_ref)
1334
1462
  bootstrap_path = self.repo / "compose" / "bootstrap" / "SKILL.md"
@@ -1338,6 +1466,7 @@ class CorpusStore:
1338
1466
  inputs = {
1339
1467
  "schema_version": SCHEMA_VERSION, "host": host, "baseline_ref": baseline_ref,
1340
1468
  "selection": effective_selection,
1469
+ "selection_mode": mode, "cwd": str(working),
1341
1470
  "selection_digest": _digest(effective_selection),
1342
1471
  "authoring_revision": self._authoring_revision(runtime, user),
1343
1472
  "item_digests": {item["ref"]: _digest(item) for item in sorted(selected, key=lambda x: x["ref"])},
@@ -1379,14 +1508,21 @@ class CorpusStore:
1379
1508
  files = _snapshot_relative_paths(compiled.get("files"))
1380
1509
  output = {
1381
1510
  "instruction_text": compiled.get("instruction_text", ""),
1382
- "files": sorted(set(files) | {"bootstrap/SKILL.md"}), "item_refs": compiled.get("item_refs", []),
1511
+ "files": sorted(set(files) | ({"bootstrap/SKILL.md"} if mode != "none" else set())), "item_refs": compiled.get("item_refs", []),
1383
1512
  "unavailable": compiled.get("unavailable", []) + promotion_warnings,
1384
1513
  }
1385
1514
  assets = _snapshot_assets(compiled.get("assets", {}), files)
1386
1515
  if assets:
1387
1516
  output["assets"] = assets
1388
- invocation = f"Corpus management: invoke $corpus using {root / 'bootstrap' / 'SKILL.md'}."
1389
- output["instruction_text"] = output["instruction_text"].rstrip() + "\n\n" + invocation + "\n"
1517
+ invocation = (f"Corpus management: invoke $agent-bios using {root / 'bootstrap' / 'SKILL.md'}."
1518
+ if mode != "none" else "")
1519
+ if mode == "none":
1520
+ output["instruction_text"] = ""
1521
+ for relative in files:
1522
+ if relative == "launch-content/instructions.md":
1523
+ (staging / relative).write_text("", encoding="utf-8")
1524
+ else:
1525
+ output["instruction_text"] = output["instruction_text"].rstrip() + "\n\n" + invocation + "\n"
1390
1526
  for plugin in assets.get("claude_plugins", []):
1391
1527
  for agent_path in (staging / plugin / "agents").glob("*.md"):
1392
1528
  with agent_path.open("a", encoding="utf-8") as agent_file:
@@ -1394,9 +1530,10 @@ class CorpusStore:
1394
1530
  if dry_run:
1395
1531
  output["instruction_text"] = output["instruction_text"].replace(str(staging), str(root))
1396
1532
  else:
1397
- bootstrap_target = staging / "bootstrap" / "SKILL.md"
1398
- bootstrap_target.parent.mkdir(parents=True, exist_ok=True)
1399
- bootstrap_target.write_bytes(bootstrap_path.read_bytes())
1533
+ if mode != "none":
1534
+ bootstrap_target = staging / "bootstrap" / "SKILL.md"
1535
+ bootstrap_target.parent.mkdir(parents=True, exist_ok=True)
1536
+ bootstrap_target.write_bytes(bootstrap_path.read_bytes())
1400
1537
  _rewrite_staged_paths(staging, root)
1401
1538
  output["instruction_text"] = output["instruction_text"].replace(str(staging), str(root))
1402
1539
  _atomic_write(staging / "inventory.json", {"inputs": inputs, "items": selected})
@@ -1419,6 +1556,7 @@ class CorpusStore:
1419
1556
  shutil.rmtree(staging)
1420
1557
  return {"content_ref": content_ref, "path": str(root), "instruction_text": output["instruction_text"],
1421
1558
  "revision": inputs["authoring_revision"], "unavailable": output.get("unavailable", []),
1559
+ "item_refs": output.get("item_refs", []), "selection_mode": mode,
1422
1560
  "assets": output.get("assets", {})}
1423
1561
 
1424
1562
  def history(self, ref: str | None = None) -> list[dict[str, Any]]:
@@ -14,6 +14,7 @@ import hashlib
14
14
  import json
15
15
  import os
16
16
  from pathlib import Path
17
+ import stat
17
18
  import sys
18
19
  import threading
19
20
  from typing import Any, Iterator
@@ -63,34 +64,66 @@ def _depths(name: str) -> dict[str, int]:
63
64
 
64
65
 
65
66
  @contextlib.contextmanager
66
- def transaction_lock(state_root: Path) -> Iterator[None]:
67
- """The single re-entrant cross-process lock for store and installer work."""
67
+ def _transaction_lock(state_root: Path, *, readonly: bool) -> Iterator[bool]:
68
68
  root = Path(state_root).expanduser()
69
69
  if root.is_symlink():
70
70
  raise TransactionError(f"unsafe corpus state root: {root}")
71
+ if readonly:
72
+ reject_symlink_ancestors(root)
71
73
  key = _key(root)
72
74
  depths = _depths("lock_depths")
73
75
  if depths.get(key, 0):
74
76
  depths[key] += 1
75
77
  try:
76
- yield
78
+ yield True
77
79
  finally:
78
80
  depths[key] -= 1
79
81
  return
80
- root.mkdir(parents=True, exist_ok=True, mode=0o700)
82
+ if not readonly:
83
+ root.mkdir(parents=True, exist_ok=True, mode=0o700)
81
84
  lock = root / ".corpus-store.lock"
82
85
  if lock.is_symlink():
83
86
  raise TransactionError(f"unsafe corpus transaction lock: {lock}")
84
- flags = os.O_CREAT | os.O_RDWR | getattr(os, "O_NOFOLLOW", 0)
85
- descriptor = os.open(lock, flags, 0o600)
86
- with os.fdopen(descriptor, "a+", encoding="utf-8") as handle:
87
- fcntl.flock(handle, fcntl.LOCK_EX)
87
+ flags = (os.O_RDONLY | os.O_NONBLOCK if readonly else os.O_CREAT | os.O_RDWR) | getattr(os, "O_NOFOLLOW", 0)
88
+ try:
89
+ descriptor = os.open(lock, flags, 0o600)
90
+ except FileNotFoundError:
91
+ if not readonly:
92
+ raise
93
+ yield False
94
+ return
95
+ try:
96
+ if readonly and not stat.S_ISREG(os.fstat(descriptor).st_mode):
97
+ raise TransactionError(f"unsafe corpus transaction lock: {lock}")
98
+ try:
99
+ fcntl.flock(descriptor, fcntl.LOCK_EX | (fcntl.LOCK_NB if readonly else 0))
100
+ except BlockingIOError:
101
+ if not readonly:
102
+ raise
103
+ yield False
104
+ return
88
105
  depths[key] = 1
89
106
  try:
90
- yield
107
+ yield True
91
108
  finally:
92
109
  depths.pop(key, None)
93
- fcntl.flock(handle, fcntl.LOCK_UN)
110
+ fcntl.flock(descriptor, fcntl.LOCK_UN)
111
+ finally:
112
+ os.close(descriptor)
113
+
114
+
115
+ @contextlib.contextmanager
116
+ def transaction_lock(state_root: Path) -> Iterator[None]:
117
+ """The single re-entrant cross-process lock for store and installer work."""
118
+ with _transaction_lock(state_root, readonly=False):
119
+ yield
120
+
121
+
122
+ @contextlib.contextmanager
123
+ def try_transaction_lock(state_root: Path) -> Iterator[bool]:
124
+ """Observe under the existing lock, or defer without blocking or creating state."""
125
+ with _transaction_lock(state_root, readonly=True) as acquired:
126
+ yield acquired
94
127
 
95
128
 
96
129
  @contextlib.contextmanager