agent-bios 0.19.1 → 0.19.2

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 (85) hide show
  1. package/DEPENDENCIES.md +27 -27
  2. package/INSTALL.md +4 -4
  3. package/README.md +53 -26
  4. package/claude/CLAUDE.md +1 -1
  5. package/claude/guides/claude-prompting.md +1 -1
  6. package/claude/guides/cli-multi-model-workflow.md +3 -3
  7. package/claude/guides/documentation-hygiene.md +3 -0
  8. package/claude/guides/gpt-prompting.md +1 -1
  9. package/claude/guides/korean-writing.md +153 -0
  10. package/claude/guides/learning-flow.md +4 -4
  11. package/claude/guides/session-distill-workflow.md +8 -8
  12. package/claude/guides/slide-writing/RUNBOOK.md +5 -5
  13. package/claude/skills/repo-charter/SKILL.md +3 -3
  14. package/claude/skills/understand/SKILL.md +5 -5
  15. package/codex/AGENTS.md +1 -1
  16. package/codex/guides/claude-prompting.md +1 -1
  17. package/codex/guides/cli-multi-model-workflow.md +3 -3
  18. package/codex/guides/documentation-hygiene.md +3 -0
  19. package/codex/guides/gpt-prompting.md +1 -1
  20. package/codex/guides/korean-writing.md +153 -0
  21. package/codex/guides/learning-flow.md +4 -4
  22. package/codex/guides/session-distill-workflow.md +8 -8
  23. package/codex/guides/slide-writing/RUNBOOK.md +5 -5
  24. package/compose/app_bridge/SKILL.md +12 -12
  25. package/compose/app_bridge/scripts/bridge.py +15 -7
  26. package/compose/assemble.py +5 -5
  27. package/compose/bootstrap/SKILL.md +18 -18
  28. package/compose/canary.sh +4 -4
  29. package/compose/check-domains.py +6 -6
  30. package/compose/corpus-state.py +16 -1168
  31. package/compose/corpus.py +13 -402
  32. package/compose/corpus_app.py +14 -450
  33. package/compose/corpus_catalog.py +15 -926
  34. package/compose/corpus_import.py +14 -523
  35. package/compose/corpus_install.py +14 -1847
  36. package/compose/corpus_session.py +16 -848
  37. package/compose/corpus_setup.py +16 -672
  38. package/compose/corpus_setup_cli.py +15 -580
  39. package/compose/corpus_setup_i18n.py +20 -324
  40. package/compose/corpus_setup_ui.py +18 -645
  41. package/compose/corpus_store.py +16 -1664
  42. package/compose/corpus_transaction.py +15 -284
  43. package/compose/corpus_ui.py +17 -972
  44. package/compose/corpus_ui_runtime.py +16 -274
  45. package/compose/corpus_understand.py +13 -671
  46. package/compose/domains.json +2 -1
  47. package/compose/instructions-state.py +1175 -0
  48. package/compose/instructions.py +409 -0
  49. package/compose/instructions_app.py +464 -0
  50. package/compose/instructions_catalog.py +931 -0
  51. package/compose/instructions_import.py +529 -0
  52. package/compose/instructions_install.py +1866 -0
  53. package/compose/instructions_session.py +852 -0
  54. package/compose/instructions_setup.py +676 -0
  55. package/compose/instructions_setup_cli.py +586 -0
  56. package/compose/instructions_setup_i18n.py +324 -0
  57. package/compose/instructions_setup_ui.py +647 -0
  58. package/compose/instructions_store.py +1668 -0
  59. package/compose/instructions_transaction.py +306 -0
  60. package/compose/instructions_ui.py +975 -0
  61. package/compose/instructions_ui_runtime.py +278 -0
  62. package/compose/instructions_understand.py +678 -0
  63. package/compose/register-hooks.py +1 -1
  64. package/compose/setup/START.md +11 -11
  65. package/docs/advanced-launch.md +11 -11
  66. package/docs/instructions-compatibility.md +86 -0
  67. package/docs/{corpus.md → instructions.md} +35 -8
  68. package/docs/recovery.md +10 -10
  69. package/docs/releases/0.19.2.md +38 -0
  70. package/docs/session-model.md +23 -20
  71. package/docs/setup.md +27 -26
  72. package/docs/understand.md +6 -6
  73. package/install.sh +70 -69
  74. package/launch/agent-launch.py +298 -290
  75. package/launch/agent-launch.toml +2 -2
  76. package/launch/i18n/en.toml +55 -55
  77. package/launch/i18n/ja.toml +56 -56
  78. package/launch/i18n/ko.toml +56 -56
  79. package/launch/shell_integration.py +4 -4
  80. package/learn/collect-learning.py +10 -10
  81. package/learn/learning.schema.json +1 -1
  82. package/learn/migrate-learnings.py +51 -51
  83. package/package.json +27 -11
  84. package/provenance.json +1 -1
  85. /package/docs/assets/{corpus-studio.svg → instructions-studio.svg} +0 -0
@@ -972,10 +972,10 @@ CRITERION_CLAUSE = (
972
972
  f"refuses unclassified findings, and fold returned rows against the declared enum "
973
973
  f"without guessing a class"
974
974
  )
975
- # The panel that carries corpus/registration text, and the scrolling body that holds it.
975
+ # The panel that carries instructions/registration text, and the scrolling body that holds it.
976
976
  # Named once so the screen, its CSS, its key bindings and the check that drives them cannot
977
977
  # drift apart.
978
- CORPUS_PANEL_ID = "al-corpus"
978
+ INSTRUCTIONS_PANEL_ID = "al-instructions"
979
979
  BODY_PANEL_ID = "al-body"
980
980
  TROPHY_PANEL_ID = "al-understand-trophy"
981
981
 
@@ -1423,8 +1423,8 @@ def parse_capability_offers(name: str, raw: Any) -> tuple:
1423
1423
 
1424
1424
  def review_wrapper_command(host: str, name: str) -> str | None:
1425
1425
  """Resolve a review adapter from the active private package or native installation."""
1426
- if private_corpus_enabled():
1427
- path = corpus_package_root() / "wrappers" / f"{name}.sh"
1426
+ if private_instructions_enabled():
1427
+ path = instructions_package_root() / "wrappers" / f"{name}.sh"
1428
1428
  elif host == "codex":
1429
1429
  path = expand_config_path(f"${{CODEX_HOME}}/bin/{name}")
1430
1430
  else:
@@ -3635,7 +3635,7 @@ def emit_receipt_command(
3635
3635
  fields[key] = value
3636
3636
  # Present-but-empty is a configuration error, never a silent prose downgrade: an
3637
3637
  # exported empty value made the whole validation vacuous while the caller believed
3638
- # it armed (criterion round, #3 — the empty-variable class the corpus already
3638
+ # it armed (criterion round, #3 — the empty-variable class the instructions already
3639
3639
  # names for shell checks).
3640
3640
  criterion_env = os.environ.get(RECEIPT_CRITERION_ENV)
3641
3641
  if criterion_env is not None and not criterion_env.strip():
@@ -4126,41 +4126,49 @@ def exec_backend(command: str, args: list[str], env: dict[str, str] | None = Non
4126
4126
  os.execve(command, [command, *args], os.environ.copy() if env is None else env)
4127
4127
 
4128
4128
 
4129
- def private_corpus_enabled() -> bool:
4130
- explicit = os.environ.get("AGENT_BIOS_PRIVATE_CORPUS")
4129
+ def _instructions_environment(canonical: str, legacy: str, default=None):
4130
+ # The launcher also runs standalone, before any private package is located.
4131
+ current, previous = os.environ.get(canonical), os.environ.get(legacy)
4132
+ if current is not None and previous is not None and current != previous:
4133
+ raise LaunchError(f"conflicting {canonical} and {legacy}; set one value")
4134
+ return current if current is not None else previous if previous is not None else default
4135
+
4136
+
4137
+ def private_instructions_enabled() -> bool:
4138
+ explicit = _instructions_environment("AGENT_BIOS_PRIVATE_INSTRUCTIONS", "AGENT_BIOS_PRIVATE_CORPUS")
4131
4139
  if explicit is not None:
4132
4140
  return explicit == "1"
4133
4141
  state = pathlib.Path(os.environ.get("AGENT_BIOS_STATE_DIR", str(pathlib.Path.home() / ".local/share/agent-bios")))
4134
4142
  return (state / "runtime/private-install.json").is_file()
4135
4143
 
4136
4144
 
4137
- def corpus_package_root() -> pathlib.Path:
4145
+ def instructions_package_root() -> pathlib.Path:
4138
4146
  explicit = os.environ.get("AGENT_BIOS_PACKAGE_ROOT")
4139
4147
  if explicit:
4140
4148
  return pathlib.Path(explicit).resolve()
4141
4149
  source = pathlib.Path(__file__).resolve().parents[1]
4142
- if (source / "compose/corpus_store.py").is_file():
4150
+ if any((source / "compose" / name).is_file() for name in ("instructions_store.py", "corpus_store.py")):
4143
4151
  return source
4144
4152
  state = pathlib.Path(os.environ.get("AGENT_BIOS_STATE_DIR", str(pathlib.Path.home() / ".local/share/agent-bios")))
4145
4153
  try:
4146
4154
  root = pathlib.Path(json.loads((state / "runtime/private-install.json").read_text())["package_root"])
4147
4155
  except (OSError, ValueError, KeyError, TypeError) as exc:
4148
- raise LaunchError("private corpus runtime missing; run agent-bios install") from exc
4149
- if not (root / "compose/corpus_store.py").is_file():
4150
- raise LaunchError("private corpus runtime missing; run agent-bios install")
4156
+ raise LaunchError("private instructions runtime missing; run agent-bios install") from exc
4157
+ if not any((root / "compose" / name).is_file() for name in ("instructions_store.py", "corpus_store.py")):
4158
+ raise LaunchError("private instructions runtime missing; run agent-bios install")
4151
4159
  return root
4152
4160
 
4153
4161
 
4154
- def corpus_store():
4155
- root = corpus_package_root()
4162
+ def instructions_store():
4163
+ root = instructions_package_root()
4156
4164
  module_root = str(root / "compose")
4157
4165
  if module_root not in sys.path:
4158
4166
  sys.path.insert(0, module_root)
4159
- from corpus_store import CorpusStore
4160
- return CorpusStore(root)
4167
+ from instructions_store import InstructionsStore
4168
+ return InstructionsStore(root)
4161
4169
 
4162
4170
 
4163
- def _corpus_generation(state_root: pathlib.Path, config_path: pathlib.Path) -> str:
4171
+ def _instructions_generation(state_root: pathlib.Path, config_path: pathlib.Path) -> str:
4164
4172
  paths = {state_root / "runtime/private-install.json", state_root / "runtime/state.json", config_path}
4165
4173
  digest = hashlib.sha256()
4166
4174
  for path in sorted(paths):
@@ -4172,12 +4180,12 @@ def _corpus_generation(state_root: pathlib.Path, config_path: pathlib.Path) -> s
4172
4180
 
4173
4181
  def _load_private_config(config_path: pathlib.Path, *, replay_only: bool = False):
4174
4182
  """Read one coherent configuration; verify its generation again at activation."""
4175
- root = corpus_package_root()
4183
+ root = instructions_package_root()
4176
4184
  module_root = str(root / "compose")
4177
4185
  if module_root not in sys.path:
4178
4186
  sys.path.insert(0, module_root)
4179
- from corpus_transaction import transaction_lock, guard_pending, confirmed_release, TransactionPendingError
4180
- state_root = corpus_store().state_root
4187
+ from instructions_transaction import transaction_lock, guard_pending, confirmed_release, TransactionPendingError
4188
+ state_root = instructions_store().state_root
4181
4189
  with transaction_lock(state_root):
4182
4190
  try:
4183
4191
  guard_pending(state_root)
@@ -4186,36 +4194,36 @@ def _load_private_config(config_path: pathlib.Path, *, replay_only: bool = False
4186
4194
  raise
4187
4195
  config_path = confirmed_release(state_root) / "launch/agent-launch.toml"
4188
4196
  config = load_config(config_path)
4189
- return config, _corpus_generation(state_root, config_path)
4197
+ return config, _instructions_generation(state_root, config_path)
4190
4198
 
4191
4199
 
4192
4200
  def _snapshot_from_config(store, config_path, generation, host, selected, *, dry_run, native):
4193
- from corpus_transaction import transaction_lock, guard_pending
4201
+ from instructions_transaction import transaction_lock, guard_pending
4194
4202
  with transaction_lock(store.state_root):
4195
4203
  guard_pending(store.state_root)
4196
- if generation != _corpus_generation(store.state_root, config_path):
4197
- raise LaunchError("corpus installation or launch settings changed during setup; reopen the launcher")
4204
+ if generation != _instructions_generation(store.state_root, config_path):
4205
+ raise LaunchError("instructions installation or launch settings changed during setup; reopen the launcher")
4198
4206
  return store.snapshot(host, selected, dry_run=dry_run, native=native)
4199
4207
 
4200
4208
 
4201
- def open_corpus_studio() -> None:
4202
- root = corpus_package_root()
4203
- result = subprocess.run([sys.executable, str(root / "compose/corpus.py"), "--repo", str(root)])
4209
+ def open_instructions_studio() -> None:
4210
+ root = instructions_package_root()
4211
+ result = subprocess.run([sys.executable, str(root / "compose/instructions.py"), "--repo", str(root)])
4204
4212
  if result.returncode:
4205
- print(f"agent-launch: Corpus Studio exited {result.returncode}", file=sys.stderr)
4213
+ print(f"agent-launch: Instructions Studio exited {result.returncode}", file=sys.stderr)
4206
4214
 
4207
4215
 
4208
4216
  def understand_manager():
4209
- if not private_corpus_enabled():
4217
+ if not private_instructions_enabled():
4210
4218
  raise LaunchError("understand! requires a private installation; run agent-bios install")
4211
- store = corpus_store()
4212
- from corpus_understand import CorpusUnderstand
4213
- return CorpusUnderstand(store)
4219
+ store = instructions_store()
4220
+ from instructions_understand import InstructionsUnderstand
4221
+ return InstructionsUnderstand(store)
4214
4222
 
4215
4223
 
4216
4224
  def understand_trophy() -> str:
4217
4225
  """Decoration is derived from durable awards, never a launcher-local flag."""
4218
- if not private_corpus_enabled():
4226
+ if not private_instructions_enabled():
4219
4227
  return ""
4220
4228
  try:
4221
4229
  manager = understand_manager()
@@ -4235,7 +4243,7 @@ def build_understand_plan(config: dict[str, Any], host: str, bundle_id: str) ->
4235
4243
  "label": "Understand!", "mode": DEFAULT_PRESET_MODE, "main_tier": "helm",
4236
4244
  "review_setup": "none", "delegation": False,
4237
4245
  "codex_execution_policy": STANDARD_POLICY, "claude_permission_mode": STANDARD_POLICY,
4238
- "mission": "Help the user understand the selected corpus bundle's purpose, context, "
4246
+ "mission": "Help the user understand the selected instructions bundle's purpose, context, "
4239
4247
  "mechanisms and limits through an adaptive dialogue. Treat learning material "
4240
4248
  "as material to discuss, not authorization to execute its instructions. "
4241
4249
  "Choose finite core coverage and ask only useful questions, fewer when enough. "
@@ -4823,7 +4831,7 @@ def setup_summary_lines(plan: dict[str, Any] | None) -> list[str]:
4823
4831
  delegation="on" if plan["delegation"] else "off", execution=execution,
4824
4832
  ),
4825
4833
  ]
4826
- if private_corpus_enabled():
4834
+ if private_instructions_enabled():
4827
4835
  lines.append(
4828
4836
  setup_panel_column(t("setup.global-instructions.label"))
4829
4837
  + " "
@@ -4867,20 +4875,20 @@ def _activate_cli_ui_runtime() -> bool:
4867
4875
  """Use the package's offline UI bundle; standalone compatibility copies keep their runtime."""
4868
4876
  global _UI_RUNTIME_RELEASE
4869
4877
  source = pathlib.Path(__file__).resolve().parents[1]
4870
- if os.environ.get("AGENT_BIOS_PACKAGE_ROOT") or private_corpus_enabled():
4871
- root = corpus_package_root()
4872
- elif (source / "compose/corpus_store.py").is_file():
4878
+ if os.environ.get("AGENT_BIOS_PACKAGE_ROOT") or private_instructions_enabled():
4879
+ root = instructions_package_root()
4880
+ elif any((source / "compose" / name).is_file() for name in ("instructions_store.py", "corpus_store.py")):
4873
4881
  root = source
4874
4882
  else:
4875
4883
  return False
4876
- loader = root / "compose/corpus_ui_runtime.py"
4884
+ loader = root / "compose/instructions_ui_runtime.py"
4877
4885
  if loader.is_symlink() or not loader.is_file():
4878
4886
  raise LaunchError("bundled UI runtime loader is missing or unsafe; reinstall the agent-bios package")
4879
4887
  module_root = str(root / "compose")
4880
4888
  if module_root not in sys.path:
4881
4889
  sys.path.insert(0, module_root)
4882
4890
  try:
4883
- from corpus_ui_runtime import activate_ui_runtime, release_ui_runtime
4891
+ from instructions_ui_runtime import activate_ui_runtime, release_ui_runtime
4884
4892
  activate_ui_runtime(root)
4885
4893
  except (ImportError, OSError, RuntimeError) as exc:
4886
4894
  raise LaunchError(f"bundled terminal UI could not start: {exc}") from exc
@@ -5033,10 +5041,10 @@ def _build_app_class():
5033
5041
  height: auto; min-height: 5; max-height: 45vh;
5034
5042
  }
5035
5043
  #al-detail-scroll > #al-detail { border: none; padding: 0; height: auto; }
5036
- #al-corpus-title { background: $warning; color: black; text-style: bold; padding: 0 1; }
5044
+ #al-instructions-title { background: $warning; color: black; text-style: bold; padding: 0 1; }
5037
5045
  /* No cap and no scroller of its own: one nested scroll region inside another is a
5038
5046
  worse answer than a body that simply scrolls. */
5039
- #al-corpus {
5047
+ #al-instructions {
5040
5048
  border: round $warning; padding: 0 1; height: auto;
5041
5049
  }
5042
5050
  #al-hdr { color: $text-muted; text-style: bold; padding: 0 1; }
@@ -5121,7 +5129,7 @@ def _build_app_class():
5121
5129
  self.call_after_refresh(self._size_detail)
5122
5130
 
5123
5131
  def __init__(
5124
- self, title, options, default, allow_back, plan, preview=None, corpus=None,
5132
+ self, title, options, default, allow_back, plan, preview=None, instructions=None,
5125
5133
  confirm=None,
5126
5134
  ):
5127
5135
  super().__init__()
@@ -5131,7 +5139,7 @@ def _build_app_class():
5131
5139
  self._allow_back = allow_back
5132
5140
  self._plan = plan
5133
5141
  self._preview = preview
5134
- self._corpus = corpus
5142
+ self._instructions = instructions
5135
5143
  self._trophy = understand_trophy()
5136
5144
  # The value Enter decides on, for a screen whose rows are changes rather than
5137
5145
  # choices. Without it Enter and Space would both mean "act on the highlighted
@@ -5148,9 +5156,9 @@ def _build_app_class():
5148
5156
  with Horizontal(id="al-reference-row"):
5149
5157
  with Vertical(id="al-reference-content"):
5150
5158
  yield setup_panel(self._plan)
5151
- if self._corpus:
5152
- yield Static(t("tui.corpus.title"), id="al-corpus-title")
5153
- yield Static("\n".join(self._corpus), id=CORPUS_PANEL_ID)
5159
+ if self._instructions:
5160
+ yield Static(t("tui.instructions.title"), id="al-instructions-title")
5161
+ yield Static("\n".join(self._instructions), id=INSTRUCTIONS_PANEL_ID)
5154
5162
  yield Static(self._trophy, id=TROPHY_PANEL_ID, markup=False)
5155
5163
  detail = VerticalScroll(Static("", id="al-detail", markup=False), id="al-detail-scroll")
5156
5164
  detail.border_title = t("tui.detail.title")
@@ -5357,13 +5365,13 @@ class TextualUI:
5357
5365
  default: str,
5358
5366
  allow_back: bool,
5359
5367
  preview=None,
5360
- corpus_lines: list[str] | None = None,
5368
+ instructions_lines: list[str] | None = None,
5361
5369
  confirm: str | None = None,
5362
5370
  ) -> str:
5363
5371
  result = self.app.call_from_thread(
5364
5372
  self.app.push_screen_wait,
5365
5373
  self._menu_screen(
5366
- title, options, default, allow_back, self.plan, preview, corpus_lines,
5374
+ title, options, default, allow_back, self.plan, preview, instructions_lines,
5367
5375
  confirm,
5368
5376
  ),
5369
5377
  )
@@ -5429,12 +5437,12 @@ def choose_lines(
5429
5437
  options: list[MenuOption],
5430
5438
  default: str,
5431
5439
  allow_back: bool,
5432
- corpus_lines: list[str] | None = None,
5440
+ instructions_lines: list[str] | None = None,
5433
5441
  ) -> str:
5434
5442
  print(f"\n{title}")
5435
- if corpus_lines:
5436
- print(" " + t("prompt.corpus.header"))
5437
- for line in corpus_lines:
5443
+ if instructions_lines:
5444
+ print(" " + t("prompt.instructions.header"))
5445
+ for line in instructions_lines:
5438
5446
  print(f" {line}")
5439
5447
  print(" --")
5440
5448
  for index, option in enumerate(options, 1):
@@ -5480,16 +5488,16 @@ def choose(
5480
5488
  ui: TextualUI | None = None,
5481
5489
  allow_back: bool = False,
5482
5490
  preview=None,
5483
- corpus_lines: list[str] | None = None,
5491
+ instructions_lines: list[str] | None = None,
5484
5492
  confirm: str | None = None,
5485
5493
  ) -> str:
5486
5494
  if not any(option.enabled for option in options):
5487
5495
  raise LaunchError(f"no available options for {title}")
5488
5496
  if ui is not None:
5489
5497
  return ui.choose(
5490
- title, options, default, allow_back, preview, corpus_lines, confirm
5498
+ title, options, default, allow_back, preview, instructions_lines, confirm
5491
5499
  )
5492
- return choose_lines(title, options, default, allow_back, corpus_lines)
5500
+ return choose_lines(title, options, default, allow_back, instructions_lines)
5493
5501
 
5494
5502
 
5495
5503
  def read_input(prompt: str) -> str:
@@ -6530,7 +6538,7 @@ def register_reviewer_wizard(
6530
6538
  if perspectives:
6531
6539
  answers["perspectives"] = perspectives
6532
6540
  break
6533
- _corpus_info(ui, t("wizard.title"), [t("wizard.perspectives.refused")])
6541
+ _instructions_info(ui, t("wizard.title"), [t("wizard.perspectives.refused")])
6534
6542
  while True:
6535
6543
  trials_raw = prompt_text(
6536
6544
  t("wizard.trials.label"), str(answers.get("trials", 2)), ui
@@ -6538,10 +6546,10 @@ def register_reviewer_wizard(
6538
6546
  try:
6539
6547
  trials = int(trials_raw)
6540
6548
  except ValueError:
6541
- _corpus_info(ui, t("wizard.title"), [t("wizard.trials.refused")])
6549
+ _instructions_info(ui, t("wizard.title"), [t("wizard.trials.refused")])
6542
6550
  continue
6543
6551
  if trials < 1:
6544
- _corpus_info(ui, t("wizard.title"), [t("wizard.trials.refused")])
6552
+ _instructions_info(ui, t("wizard.title"), [t("wizard.trials.refused")])
6545
6553
  continue
6546
6554
  answers["trials"] = trials
6547
6555
  break
@@ -6596,7 +6604,7 @@ def register_reviewer_wizard(
6596
6604
  MenuOption("edit", t("wizard.confirm.edit.label"), ""),
6597
6605
  ],
6598
6606
  "write", ui, allow_back=True,
6599
- corpus_lines=[t("wizard.confirm.target").format(target=target),
6607
+ instructions_lines=[t("wizard.confirm.target").format(target=target),
6600
6608
  *block.splitlines()],
6601
6609
  )
6602
6610
  if confirm == "edit":
@@ -6618,16 +6626,16 @@ def register_reviewer_wizard(
6618
6626
  # since the trial must read as a race even when its bytes happen
6619
6627
  # to equal the header the wizard would have invented.
6620
6628
  if write_target.is_file() != existed:
6621
- _corpus_info(ui, t("wizard.title"), [t("wizard.raced.line")],
6622
- back_hint=t("corpus.back.hint"))
6629
+ _instructions_info(ui, t("wizard.title"), [t("wizard.raced.line")],
6630
+ back_hint=t("instructions.back.hint"))
6623
6631
  return False
6624
6632
  current = (
6625
6633
  write_target.read_bytes() if write_target.is_file()
6626
6634
  else USER_METHODS_HEADER.encode()
6627
6635
  )
6628
6636
  if current != before:
6629
- _corpus_info(ui, t("wizard.title"), [t("wizard.raced.line")],
6630
- back_hint=t("corpus.back.hint"))
6637
+ _instructions_info(ui, t("wizard.title"), [t("wizard.raced.line")],
6638
+ back_hint=t("instructions.back.hint"))
6631
6639
  return False
6632
6640
  mode = (write_target.stat().st_mode & 0o777) if existed else 0o600
6633
6641
  try:
@@ -6663,9 +6671,9 @@ def register_reviewer_wizard(
6663
6671
  # merge. Sharing the restore branch's message told the user the
6664
6672
  # file had been restored in the one case where it was left
6665
6673
  # exactly as the other writer wrote it.
6666
- _corpus_info(
6674
+ _instructions_info(
6667
6675
  ui, t("wizard.title"), [t("wizard.postwrite.raced.line")],
6668
- back_hint=t("corpus.back.hint"),
6676
+ back_hint=t("instructions.back.hint"),
6669
6677
  )
6670
6678
  return False
6671
6679
  if existed:
@@ -6677,18 +6685,18 @@ def register_reviewer_wizard(
6677
6685
  os.replace(restore, write_target)
6678
6686
  else:
6679
6687
  write_target.unlink(missing_ok=True)
6680
- _corpus_info(ui, t("wizard.title"), [t("wizard.postwrite.line")],
6681
- back_hint=t("corpus.back.hint"))
6688
+ _instructions_info(ui, t("wizard.title"), [t("wizard.postwrite.line")],
6689
+ back_hint=t("instructions.back.hint"))
6682
6690
  return False # pre-write state restored above
6683
6691
  if verdict is None:
6684
6692
  config.clear()
6685
6693
  config.update(fresh)
6686
6694
  registry.clear()
6687
6695
  registry.update(load_review_methods(config))
6688
- _corpus_info(
6696
+ _instructions_info(
6689
6697
  ui, t("wizard.title"),
6690
6698
  [t("wizard.done.line").format(method_id=answers["id"])],
6691
- back_hint=t("corpus.back.hint"),
6699
+ back_hint=t("instructions.back.hint"),
6692
6700
  )
6693
6701
  return True
6694
6702
  action = choose(
@@ -6699,7 +6707,7 @@ def register_reviewer_wizard(
6699
6707
  MenuOption("contract", t("wizard.refused.contract.label"), ""),
6700
6708
  ],
6701
6709
  "edit", ui, allow_back=True,
6702
- corpus_lines=[t("wizard.refused.header"), "", *verdict.splitlines()],
6710
+ instructions_lines=[t("wizard.refused.header"), "", *verdict.splitlines()],
6703
6711
  )
6704
6712
  if action == "contract":
6705
6713
  register_reviewer_info(ui, config_path)
@@ -6712,7 +6720,7 @@ def register_reviewer_info(ui: TextualUI | None, config_path: pathlib.Path) -> N
6712
6720
  user owns and what a descriptor must contain, rather than offering a form that
6713
6721
  would have to duplicate the reader's validation and then drift from it."""
6714
6722
  path = user_methods_path(config_path)
6715
- _corpus_info(
6723
+ _instructions_info(
6716
6724
  ui,
6717
6725
  "Register another reviewer",
6718
6726
  [
@@ -6800,7 +6808,7 @@ def review_editor(
6800
6808
  except BackRequested:
6801
6809
  return None
6802
6810
  except LaunchError as exc:
6803
- _corpus_info(
6811
+ _instructions_info(
6804
6812
  ui, f"{title} — cannot be seated", [str(exc)],
6805
6813
  back_hint="Return to the review editor.",
6806
6814
  )
@@ -8038,7 +8046,7 @@ def customize(
8038
8046
  preview=lambda value: {**plan, policy_field: value},
8039
8047
  )
8040
8048
  elif action == "global-instructions":
8041
- exclude_available = private_corpus_enabled() and plan["host"] == "claude"
8049
+ exclude_available = private_instructions_enabled() and plan["host"] == "claude"
8042
8050
  exclude_reason = (
8043
8051
  t("global-instructions.exclude.codex-unavailable")
8044
8052
  if plan["host"] == "codex"
@@ -8203,7 +8211,7 @@ def customize(
8203
8211
  )
8204
8212
  save_preset(plan, config, config_path, name)
8205
8213
  except LaunchError as exc:
8206
- _corpus_info(ui, t("custom.save.prompt"), [str(exc)],
8214
+ _instructions_info(ui, t("custom.save.prompt"), [str(exc)],
8207
8215
  back_hint=t("custom.title"))
8208
8216
  continue
8209
8217
  plan["_launch_confirmed"] = True
@@ -8213,9 +8221,9 @@ def customize(
8213
8221
 
8214
8222
 
8215
8223
  DISTILL_PRESET = "session-distill"
8216
- CORPUS_STATUS_PATH = pathlib.Path(
8217
- os.environ.get(
8218
- "AGENT_BIOS_CORPUS_STATUS",
8224
+ INSTRUCTIONS_STATUS_PATH = pathlib.Path(
8225
+ _instructions_environment(
8226
+ "AGENT_BIOS_INSTRUCTIONS_STATUS", "AGENT_BIOS_CORPUS_STATUS",
8219
8227
  str(pathlib.Path.home() / ".local/share/agent-bios/corpus-status.json"),
8220
8228
  )
8221
8229
  )
@@ -8238,7 +8246,7 @@ UPDATE_CHECK_PATH = pathlib.Path(
8238
8246
  # at most SPAWNS the installer, detached, to refresh it. Two reasons, both load-bearing:
8239
8247
  # `npm view` routinely takes seconds and a launcher that blocks on the network is worse
8240
8248
  # than one that shows a stale badge, and the network operation belongs to the component
8241
- # that already owns fetch-corpus (ENDPOINTS.md) rather than to the UI.
8249
+ # that already owns fetch-instructions (ENDPOINTS.md) rather than to the UI.
8242
8250
  UPDATE_CHECK_INTERVAL_S = 24 * 60 * 60
8243
8251
 
8244
8252
 
@@ -8316,24 +8324,24 @@ def spawn_update_check() -> None:
8316
8324
  pass
8317
8325
 
8318
8326
 
8319
- def load_corpus_status() -> dict[str, Any] | None:
8320
- """The corpus status projection, or None when the file is unreadable or is not
8327
+ def load_instructions_status() -> dict[str, Any] | None:
8328
+ """The instructions status projection, or None when the file is unreadable or is not
8321
8329
  an object at all. Nothing deeper is validated here on purpose.
8322
8330
 
8323
8331
  Validating the shape field by field was tried and is a queue that refills:
8324
8332
  guarding `summary` and `versions` still left `summary.placed_by_layer: null`,
8325
8333
  `domains.applied: [1,2]` and `last_apply.requested: 5` killing the ROOT MENU
8326
8334
  before it drew, and each new consumer would add another field to remember. The
8327
- depth is unbounded, so the readers DEGRADE instead — see corpus_summary_lines.
8335
+ depth is unbounded, so the readers DEGRADE instead — see instructions_summary_lines.
8328
8336
  """
8329
- if private_corpus_enabled():
8337
+ if private_instructions_enabled():
8330
8338
  try:
8331
- return {"private": corpus_store().status()}
8339
+ return {"private": instructions_store().status()}
8332
8340
  except (OSError, ValueError, RuntimeError) as exc:
8333
- print(f"agent-launch: cannot read private corpus: {exc}", file=sys.stderr)
8341
+ print(f"agent-launch: cannot read private instructions: {exc}", file=sys.stderr)
8334
8342
  return None
8335
8343
  try:
8336
- data = json.loads(CORPUS_STATUS_PATH.read_text(encoding="utf-8"))
8344
+ data = json.loads(INSTRUCTIONS_STATUS_PATH.read_text(encoding="utf-8"))
8337
8345
  except (OSError, ValueError):
8338
8346
  return None
8339
8347
  return data if isinstance(data, dict) else None
@@ -8343,7 +8351,7 @@ def version_label() -> str | None:
8343
8351
  """The deployed agent-bios version + release date for the TUI, or None when
8344
8352
  the marker is absent (uninstalled / dev checkout). `agent-bios install`
8345
8353
  writes it from package.json (version + releaseDate). This is the deploy /
8346
- system version — distinct from the corpus content version in the distill hub."""
8354
+ system version — distinct from the instructions content version in the distill hub."""
8347
8355
  try:
8348
8356
  info = json.loads(VERSION_INFO_PATH.read_text(encoding="utf-8"))
8349
8357
  except (OSError, ValueError):
@@ -8377,7 +8385,7 @@ def display_width(text: str) -> int:
8377
8385
  )
8378
8386
 
8379
8387
 
8380
- def corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8388
+ def instructions_summary_lines(status: dict[str, Any] | None) -> list[str]:
8381
8389
  """Panel body for the Session Distill area, or the not-projected line when the
8382
8390
  status cannot be rendered.
8383
8391
 
@@ -8389,10 +8397,10 @@ def corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8389
8397
  The exception is printed, never swallowed, so a defect in the rendering code
8390
8398
  below stays visible instead of hiding behind a degraded panel."""
8391
8399
  try:
8392
- return _corpus_summary_lines(status)
8400
+ return _instructions_summary_lines(status)
8393
8401
  except Exception as exc: # noqa: BLE001 — the root menu must survive any shape
8394
8402
  print(
8395
- f"agent-launch: cannot render the corpus status from {CORPUS_STATUS_PATH} "
8403
+ f"agent-launch: cannot render the instructions status from {INSTRUCTIONS_STATUS_PATH} "
8396
8404
  f"({type(exc).__name__}: {exc}); showing it as not projected. "
8397
8405
  "Run: agent-bios install", file=sys.stderr,
8398
8406
  )
@@ -8414,7 +8422,7 @@ def status_list(value: Any) -> list:
8414
8422
  return value if isinstance(value, list) else []
8415
8423
 
8416
8424
 
8417
- _CORPUS_FACTS: dict[tuple, dict[str, Any] | None] = {}
8425
+ _INSTRUCTIONS_FACTS: dict[tuple, dict[str, Any] | None] = {}
8418
8426
 
8419
8427
 
8420
8428
  def size_label(chars: int) -> str:
@@ -8424,7 +8432,7 @@ def size_label(chars: int) -> str:
8424
8432
  return f"{chars / 1024:.1f} KB"
8425
8433
 
8426
8434
 
8427
- def corpus_facts(status: dict[str, Any] | None) -> dict[str, Any] | None:
8435
+ def instructions_facts(status: dict[str, Any] | None) -> dict[str, Any] | None:
8428
8436
  """What each domain package holds and what it costs, or None when the package the
8429
8437
  projection points at cannot be read.
8430
8438
 
@@ -8453,20 +8461,20 @@ def corpus_facts(status: dict[str, Any] | None) -> dict[str, Any] | None:
8453
8461
  stamp = (repo, manifest_path.stat().st_mtime, applied)
8454
8462
  except OSError:
8455
8463
  return None
8456
- if stamp in _CORPUS_FACTS:
8457
- return _CORPUS_FACTS[stamp]
8458
- _CORPUS_FACTS[stamp] = None # a repeat of a failing read must not repeat the cost
8464
+ if stamp in _INSTRUCTIONS_FACTS:
8465
+ return _INSTRUCTIONS_FACTS[stamp]
8466
+ _INSTRUCTIONS_FACTS[stamp] = None # a repeat of a failing read must not repeat the cost
8459
8467
  try:
8460
- facts = _corpus_facts(pathlib.Path(repo), manifest_path, set(applied))
8468
+ facts = _instructions_facts(pathlib.Path(repo), manifest_path, set(applied))
8461
8469
  except SystemExit:
8462
8470
  # assemble.die() on a manifest/monolith disagreement. Not an Exception, so it
8463
8471
  # would otherwise leave the launcher through every absorber in this file.
8464
8472
  return None
8465
8473
  except Exception as exc: # noqa: BLE001 — sizing is decoration; picking is not
8466
- print(f"agent-launch: cannot size the corpus packages in {repo} "
8474
+ print(f"agent-launch: cannot size the instructions packages in {repo} "
8467
8475
  f"({type(exc).__name__}: {exc})", file=sys.stderr)
8468
8476
  return None
8469
- _CORPUS_FACTS[stamp] = facts
8477
+ _INSTRUCTIONS_FACTS[stamp] = facts
8470
8478
  return facts
8471
8479
 
8472
8480
 
@@ -8490,7 +8498,7 @@ def _load_assembler(repo: pathlib.Path):
8490
8498
  return module
8491
8499
 
8492
8500
 
8493
- def _corpus_facts(repo: pathlib.Path, manifest_path: pathlib.Path, applied: set) -> dict[str, Any]:
8501
+ def _instructions_facts(repo: pathlib.Path, manifest_path: pathlib.Path, applied: set) -> dict[str, Any]:
8494
8502
  """The derivation proper, with every failure left to the caller to absorb."""
8495
8503
  assemble = _load_assembler(repo)
8496
8504
  manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
@@ -8539,14 +8547,14 @@ def _corpus_facts(repo: pathlib.Path, manifest_path: pathlib.Path, applied: set)
8539
8547
  "guides": len(selected_guides),
8540
8548
  "guide_chars": guide_chars(selected_guides),
8541
8549
  # A projection can name a domain the installed package does not carry;
8542
- # the figures above then describe a smaller corpus than the row above them
8550
+ # the figures above then describe a smaller instructions than the row above them
8543
8551
  # claims, so the gap is reported rather than folded in.
8544
8552
  "unknown": sorted(applied - set(domains)),
8545
8553
  },
8546
8554
  }
8547
8555
 
8548
8556
 
8549
- def _corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8557
+ def _instructions_summary_lines(status: dict[str, Any] | None) -> list[str]:
8550
8558
  """The panel body proper. The label column is derived at render time from the
8551
8559
  widest label in the ACTIVE language and measured in display cells: a pad written
8552
8560
  for English cannot align a translated panel."""
@@ -8555,20 +8563,20 @@ def _corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8555
8563
  if isinstance(status.get("private"), dict):
8556
8564
  current = status["private"]
8557
8565
  selection = current.get("selection")
8558
- selected = ", ".join(selection) if selection else t("corpus.launch.base")
8559
- return [t("corpus.private.relationship"),
8566
+ selected = ", ".join(selection) if selection else t("instructions.launch.base")
8567
+ return [t("instructions.private.relationship"),
8560
8568
  f"{t('panel.domains.label')} {selected}",
8561
8569
  f"{t('panel.version.label')} {current.get('selected_baseline_ref') or '—'}"]
8562
8570
  # `versions is None` is the projection saying "this install cannot know" — a packaged
8563
- # install has no author-side registry (compose/corpus-state.py). Three rows then read
8564
- # "unavailable", which is true of the ledger and says nothing about the corpus the
8571
+ # install has no author-side registry (compose/instructions-state.py). Three rows then read
8572
+ # "unavailable", which is true of the ledger and says nothing about the instructions the
8565
8573
  # user actually has. The package carries the manifest and the monolith the installer
8566
8574
  # assembled from, so those rows are answerable from it; the absence is still stated,
8567
8575
  # once, on the row it belongs to.
8568
8576
  unavailable = status.get("versions") is None
8569
8577
  domains = status.get("domains")
8570
8578
  applied_raw = domains.get("applied") if isinstance(domains, dict) else None
8571
- facts = corpus_facts(status)
8579
+ facts = instructions_facts(status)
8572
8580
  # An unapplied install is NOT sized: what is deployed is whatever the install put
8573
8581
  # there, and the manifest cannot say which. A guess here would read as a reading.
8574
8582
  packaged = unavailable and facts is not None and applied_raw is not None
@@ -8595,10 +8603,10 @@ def _corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8595
8603
  # rows above describe less than the domain row below them claims.
8596
8604
  lines.append(t("panel.domains.unknown").format(
8597
8605
  names=", ".join(selected["unknown"])))
8598
- return lines + _corpus_domain_lines(status, applied_raw, row, labels[3])
8606
+ return lines + _instructions_domain_lines(status, applied_raw, row, labels[3])
8599
8607
 
8600
8608
  # `versions is None` is the projection saying "this install cannot know" — a packaged
8601
- # install has no author version/ledger registry (compose/corpus-state.py). It is not
8609
+ # install has no author version/ledger registry (compose/instructions-state.py). It is not
8602
8610
  # `[]`, which would mean a checkout whose registry is genuinely empty, and it must not
8603
8611
  # render as `0 placed · 0 versions`: a fabricated zero is worse than a blank, because
8604
8612
  # a reader cannot tell it from a real count. The domain rows below stay real, which is
@@ -8628,10 +8636,10 @@ def _corpus_summary_lines(status: dict[str, Any] | None) -> list[str]:
8628
8636
  versions=len(status_list(status.get("versions"))),
8629
8637
  )),
8630
8638
  ]
8631
- return lines + _corpus_domain_lines(status, applied_raw, row, labels[3])
8639
+ return lines + _instructions_domain_lines(status, applied_raw, row, labels[3])
8632
8640
 
8633
8641
 
8634
- def _corpus_domain_lines(status, applied_raw, row, label: str) -> list[str]:
8642
+ def _instructions_domain_lines(status, applied_raw, row, label: str) -> list[str]:
8635
8643
  """The rows both panels end on: what is applied, and a failed apply if there is one.
8636
8644
 
8637
8645
  Shared rather than written twice — a packaged panel that quietly dropped the failed
@@ -8640,23 +8648,23 @@ def _corpus_domain_lines(status, applied_raw, row, label: str) -> list[str]:
8640
8648
  if isinstance(status.get("domains"), dict):
8641
8649
  applied = None if applied_raw is None else status_list(applied_raw)
8642
8650
  lines.append(row(label, t("panel.domains.unset") if applied is None
8643
- else ", ".join(applied) or t("corpus.core-only")))
8651
+ else ", ".join(applied) or t("instructions.core-only")))
8644
8652
  last_apply = status.get("last_apply")
8645
8653
  if isinstance(last_apply, dict) and last_apply.get("outcome") not in (None, "applied"):
8646
8654
  # A failed apply must be loud on the panel, not a fact buried in a log:
8647
- # the deployed corpus and the requested selection disagree right now.
8655
+ # the deployed instructions and the requested selection disagree right now.
8648
8656
  lines.append(t("panel.last-apply").format(
8649
8657
  outcome=last_apply.get("outcome"),
8650
8658
  at=last_apply.get("at"),
8651
- requested=", ".join(status_list(last_apply.get("requested"))) or t("corpus.core-only"),
8659
+ requested=", ".join(status_list(last_apply.get("requested"))) or t("instructions.core-only"),
8652
8660
  ))
8653
8661
  return lines
8654
8662
 
8655
8663
 
8656
- def _corpus_screen(ui: TextualUI | None, screen: "Callable[[], None]") -> None:
8657
- """Run a corpus screen, absorbing a status shape it cannot render.
8664
+ def _instructions_screen(ui: TextualUI | None, screen: "Callable[[], None]") -> None:
8665
+ """Run an instruction screen, absorbing a status shape it cannot render.
8658
8666
 
8659
- `corpus_summary_lines` argued this for the root panel: the projection's depth is
8667
+ `instructions_summary_lines` argued this for the root panel: the projection's depth is
8660
8668
  unbounded, its writer is not the only thing that can produce the file, so a status
8661
8669
  that cannot be rendered is treated as one that is not projected. That argument was
8662
8670
  implemented for the panel alone, and the three screens one keypress deeper read the
@@ -8666,25 +8674,25 @@ def _corpus_screen(ui: TextualUI | None, screen: "Callable[[], None]") -> None:
8666
8674
  the top and fatal underneath.
8667
8675
 
8668
8676
  Control flow passes through untouched: LaunchError is the launcher's own refusal,
8669
- BackRequested and CorpusApplyRequested are how these screens exit normally, and
8677
+ BackRequested and InstructionsApplyRequested are how these screens exit normally, and
8670
8678
  KeyboardInterrupt/SystemExit are not Exception. The rest is printed and shown rather
8671
8679
  than swallowed, so a defect in the rendering code stays visible.
8672
8680
  """
8673
8681
  try:
8674
8682
  screen()
8675
- except (LaunchError, BackRequested, CorpusApplyRequested):
8683
+ except (LaunchError, BackRequested, InstructionsApplyRequested):
8676
8684
  raise
8677
8685
  except Exception as exc: # noqa: BLE001 — a status shape must not end the session
8678
8686
  print(
8679
- f"agent-launch: cannot render the corpus status from {CORPUS_STATUS_PATH} "
8687
+ f"agent-launch: cannot render the instructions status from {INSTRUCTIONS_STATUS_PATH} "
8680
8688
  f"({type(exc).__name__}: {exc}); showing it as not projected. "
8681
8689
  "Run: agent-bios install",
8682
8690
  file=sys.stderr,
8683
8691
  )
8684
- _corpus_info(ui, t("distill.title"), [t("panel.unprojected")])
8692
+ _instructions_info(ui, t("distill.title"), [t("panel.unprojected")])
8685
8693
 
8686
8694
 
8687
- def _corpus_info(
8695
+ def _instructions_info(
8688
8696
  ui: TextualUI | None, title: str, lines: list[str],
8689
8697
  back_hint: str | None = None,
8690
8698
  ) -> None:
@@ -8702,12 +8710,12 @@ def _corpus_info(
8702
8710
  )
8703
8711
  ]
8704
8712
  try:
8705
- choose(title, options, "back", ui, allow_back=True, corpus_lines=lines)
8713
+ choose(title, options, "back", ui, allow_back=True, instructions_lines=lines)
8706
8714
  except BackRequested:
8707
8715
  pass
8708
8716
 
8709
8717
 
8710
- def _corpus_rollback_choice(version: str, ui: "TextualUI | None") -> str:
8718
+ def _instructions_rollback_choice(version: str, ui: "TextualUI | None") -> str:
8711
8719
  """The rollback confirmation prompt, so the Esc handling wraps exactly it."""
8712
8720
  return choose(
8713
8721
  t("distill.rollback.title").format(version=version),
@@ -8729,13 +8737,13 @@ def _corpus_rollback_choice(version: str, ui: "TextualUI | None") -> str:
8729
8737
  )
8730
8738
 
8731
8739
 
8732
- def _corpus_rollback(status: dict[str, Any], version: str, ui: TextualUI | None) -> None:
8740
+ def _instructions_rollback(status: dict[str, Any], version: str, ui: TextualUI | None) -> None:
8733
8741
  # Esc means the same thing the Cancel entry means, and this screen offered both while
8734
8742
  # honouring only one: BackRequested propagated out of a destructive confirmation as an
8735
8743
  # exception, past every caller that had nothing to do with it. A screen that shows a
8736
8744
  # Cancel option and a back hint has already told the user those are the same answer.
8737
8745
  try:
8738
- confirm = _corpus_rollback_choice(version, ui)
8746
+ confirm = _instructions_rollback_choice(version, ui)
8739
8747
  except BackRequested:
8740
8748
  return
8741
8749
  if confirm != "rollback":
@@ -8746,10 +8754,10 @@ def _corpus_rollback(status: dict[str, Any], version: str, ui: TextualUI | None)
8746
8754
  # an outcome this screen already knows how to report.
8747
8755
  repo = status.get("repo")
8748
8756
  if not isinstance(repo, str) or not repo:
8749
- _corpus_info(ui, t("distill.rollback.failed"),
8757
+ _instructions_info(ui, t("distill.rollback.failed"),
8750
8758
  [t("panel.unprojected")], back_hint=t("distill.back.hint"))
8751
8759
  return
8752
- script = pathlib.Path(repo) / "compose/corpus-state.py"
8760
+ script = pathlib.Path(repo) / "compose/instructions-state.py"
8753
8761
  result = subprocess.run(
8754
8762
  [sys.executable, str(script), "rollback", "--version", version],
8755
8763
  capture_output=True,
@@ -8761,14 +8769,14 @@ def _corpus_rollback(status: dict[str, Any], version: str, ui: TextualUI | None)
8761
8769
  t("distill.rollback.done") if result.returncode == 0
8762
8770
  else t("distill.rollback.failed")
8763
8771
  )
8764
- _corpus_info(ui, verdict, [tail] if tail else [])
8772
+ _instructions_info(ui, verdict, [tail] if tail else [])
8765
8773
 
8766
8774
 
8767
- def _corpus_versions(ui: TextualUI | None) -> None:
8775
+ def _instructions_versions(ui: TextualUI | None) -> None:
8768
8776
  while True:
8769
- status = load_corpus_status()
8777
+ status = load_instructions_status()
8770
8778
  if status is None:
8771
- _corpus_info(ui, t("distill.versions.label"), corpus_summary_lines(None))
8779
+ _instructions_info(ui, t("distill.versions.label"), instructions_summary_lines(None))
8772
8780
  return
8773
8781
  current = status.get("current_version")
8774
8782
  options = []
@@ -8809,28 +8817,28 @@ def _corpus_versions(ui: TextualUI | None) -> None:
8809
8817
  options[0].value,
8810
8818
  ui,
8811
8819
  allow_back=True,
8812
- corpus_lines=corpus_summary_lines(status),
8820
+ instructions_lines=instructions_summary_lines(status),
8813
8821
  )
8814
8822
  except BackRequested:
8815
8823
  return
8816
8824
  if selected == "back":
8817
8825
  return
8818
8826
  if selected == current:
8819
- _corpus_info(
8827
+ _instructions_info(
8820
8828
  ui,
8821
8829
  t("distill.version.applied.title").format(version=selected),
8822
8830
  [t("distill.version.applied.line")],
8823
8831
  )
8824
8832
  continue
8825
- _corpus_rollback(status, selected, ui)
8833
+ _instructions_rollback(status, selected, ui)
8826
8834
 
8827
8835
 
8828
- def _corpus_packages(ui: TextualUI | None) -> None:
8829
- """v1: the corpus ships as a single core package; the list shape is ready
8836
+ def _instructions_packages(ui: TextualUI | None) -> None:
8837
+ """v1: the instructions ships as a single core package; the list shape is ready
8830
8838
  for the domain-packaging backlog to populate with real packages."""
8831
- status = load_corpus_status()
8839
+ status = load_instructions_status()
8832
8840
  if status is None:
8833
- _corpus_info(ui, t("distill.packages.label"), corpus_summary_lines(None))
8841
+ _instructions_info(ui, t("distill.packages.label"), instructions_summary_lines(None))
8834
8842
  return
8835
8843
  layers = status.get("summary", {}).get("placed_by_layer", {})
8836
8844
  options = [
@@ -8852,7 +8860,7 @@ def _corpus_packages(ui: TextualUI | None) -> None:
8852
8860
  "back",
8853
8861
  ui,
8854
8862
  allow_back=True,
8855
- corpus_lines=corpus_summary_lines(status),
8863
+ instructions_lines=instructions_summary_lines(status),
8856
8864
  )
8857
8865
  except BackRequested:
8858
8866
  pass
@@ -8864,7 +8872,7 @@ def distill_hub(config: dict[str, Any], ui: TextualUI | None) -> str:
8864
8872
  Returns "start" to launch the session-distill preset, "back" otherwise.
8865
8873
  """
8866
8874
  while True:
8867
- status = load_corpus_status()
8875
+ status = load_instructions_status()
8868
8876
  options = [
8869
8877
  MenuOption(
8870
8878
  "start",
@@ -8894,7 +8902,7 @@ def distill_hub(config: dict[str, Any], ui: TextualUI | None) -> str:
8894
8902
  "start",
8895
8903
  ui,
8896
8904
  allow_back=True,
8897
- corpus_lines=corpus_summary_lines(status),
8905
+ instructions_lines=instructions_summary_lines(status),
8898
8906
  )
8899
8907
  except BackRequested:
8900
8908
  return "back"
@@ -8903,9 +8911,9 @@ def distill_hub(config: dict[str, Any], ui: TextualUI | None) -> str:
8903
8911
  if selected == "start":
8904
8912
  return "start"
8905
8913
  if selected == "packages":
8906
- _corpus_screen(ui, lambda: _corpus_packages(ui))
8914
+ _instructions_screen(ui, lambda: _instructions_packages(ui))
8907
8915
  elif selected == "versions":
8908
- _corpus_screen(ui, lambda: _corpus_versions(ui))
8916
+ _instructions_screen(ui, lambda: _instructions_versions(ui))
8909
8917
 
8910
8918
 
8911
8919
  def preset_mode(data: dict[str, Any]) -> str:
@@ -8927,8 +8935,8 @@ def mode_default_preset(presets: dict[str, Any], mode: str) -> str | None:
8927
8935
  return names[0] if names else None
8928
8936
 
8929
8937
 
8930
- class CorpusApplyRequested(Exception):
8931
- """Raised out of the interactive flow when the user confirms a corpus
8938
+ class InstructionsApplyRequested(Exception):
8939
+ """Raised out of the interactive flow when the user confirms an instruction
8932
8940
  selection: the Textual app must be torn down before the installer owns the
8933
8941
  terminal, so the request travels as control flow and main() runs the apply
8934
8942
  in the plain terminal, then re-enters the picker."""
@@ -8938,22 +8946,22 @@ class CorpusApplyRequested(Exception):
8938
8946
  self.selection = selection
8939
8947
 
8940
8948
 
8941
- class CorpusStudioRequested(Exception):
8949
+ class InstructionsStudioRequested(Exception):
8942
8950
  """Release Textual's terminal before starting the standalone manager."""
8943
8951
 
8944
8952
 
8945
- CORPUS_OPTION = "__corpus__"
8953
+ INSTRUCTIONS_OPTION = "__instructions__"
8946
8954
  # A plain value, because the numbered prompt prints it as the default label.
8947
8955
  # Collision with a domain id is structurally impossible: domain slugs come from
8948
8956
  # compose/domains.json, which this repo owns and which carries no "apply".
8949
- CORPUS_APPLY = "apply"
8957
+ INSTRUCTIONS_APPLY = "apply"
8950
8958
 
8951
8959
 
8952
- def run_corpus_apply(selection: list[str]) -> None:
8960
+ def run_instructions_apply(selection: list[str]) -> None:
8953
8961
  """Run the installer's onboard loop for the selection, in the caller's
8954
8962
  terminal. The installer alone deploys, canaries, and records the outcome;
8955
8963
  this function only streams it and reports the exit."""
8956
- status = load_corpus_status() or {}
8964
+ status = load_instructions_status() or {}
8957
8965
  domains = ",".join(selection) if selection else "none"
8958
8966
  interaction = [] if os.environ.get("AGENT_BIOS_LEGACY_INSTALL") == "1" else ["--non-interactive"]
8959
8967
  front = shutil.which("agent-bios")
@@ -8976,15 +8984,15 @@ def run_corpus_apply(selection: list[str]) -> None:
8976
8984
  )
8977
8985
  return
8978
8986
  argv = ["bash", str(installer), "onboard", *interaction, "--domains", domains]
8979
- print(f"\nagent-launch: applying corpus selection: {' '.join(argv)}\n", flush=True)
8987
+ print(f"\nagent-launch: applying instructions selection: {' '.join(argv)}\n", flush=True)
8980
8988
  proc = subprocess.run(argv)
8981
8989
  if proc.returncode != 0:
8982
8990
  print(
8983
- f"\nagent-launch: corpus apply FAILED (exit {proc.returncode}); the "
8984
- "corpus panel shows the recorded outcome.", file=sys.stderr,
8991
+ f"\nagent-launch: instructions apply FAILED (exit {proc.returncode}); the "
8992
+ "instructions panel shows the recorded outcome.", file=sys.stderr,
8985
8993
  )
8986
8994
  else:
8987
- print("\nagent-launch: corpus selection applied.", flush=True)
8995
+ print("\nagent-launch: instructions selection applied.", flush=True)
8988
8996
 
8989
8997
 
8990
8998
  def checkbox_label(checked: bool, name: str, trailing: str = ""):
@@ -9002,25 +9010,25 @@ def checkbox_label(checked: bool, name: str, trailing: str = ""):
9002
9010
  return Text.assemble(*box, name, (trailing, "dim"))
9003
9011
 
9004
9012
 
9005
- def _corpus_domain_content(name: str, fallback: str = "") -> str:
9013
+ def _instructions_domain_content(name: str, fallback: str = "") -> str:
9006
9014
  """Localized content help; unfamiliar packages retain their manifest description."""
9007
9015
  descriptions = {
9008
- "builder-base": t("corpus.domain.builder-base"),
9009
- "llm-pipeline-dev": t("corpus.domain.llm-pipeline-dev"),
9010
- "multi-agent-orchestration": t("corpus.domain.multi-agent-orchestration"),
9011
- "visualization-docs": t("corpus.domain.visualization-docs"),
9012
- "office-work": t("corpus.domain.office-work"),
9016
+ "builder-base": t("instructions.domain.builder-base"),
9017
+ "llm-pipeline-dev": t("instructions.domain.llm-pipeline-dev"),
9018
+ "multi-agent-orchestration": t("instructions.domain.multi-agent-orchestration"),
9019
+ "visualization-docs": t("instructions.domain.visualization-docs"),
9020
+ "office-work": t("instructions.domain.office-work"),
9013
9021
  }
9014
9022
  return descriptions.get(name, fallback).strip()
9015
9023
 
9016
9024
 
9017
- def corpus_domain_labels() -> dict[str, str]:
9025
+ def instructions_domain_labels() -> dict[str, str]:
9018
9026
  return {
9019
- "builder-base": t("corpus.domain.builder-base.label"),
9020
- "llm-pipeline-dev": t("corpus.domain.llm-pipeline-dev.label"),
9021
- "multi-agent-orchestration": t("corpus.domain.multi-agent-orchestration.label"),
9022
- "visualization-docs": t("corpus.domain.visualization-docs.label"),
9023
- "office-work": t("corpus.domain.office-work.label"),
9027
+ "builder-base": t("instructions.domain.builder-base.label"),
9028
+ "llm-pipeline-dev": t("instructions.domain.llm-pipeline-dev.label"),
9029
+ "multi-agent-orchestration": t("instructions.domain.multi-agent-orchestration.label"),
9030
+ "visualization-docs": t("instructions.domain.visualization-docs.label"),
9031
+ "office-work": t("instructions.domain.office-work.label"),
9024
9032
  }
9025
9033
 
9026
9034
 
@@ -9031,16 +9039,16 @@ def preset_descriptions() -> dict[str, str]:
9031
9039
  "deep-review": t("preset.deep-review.description"),
9032
9040
  "fast-batch": t("preset.fast-batch.description"),
9033
9041
  "solo": t("preset.solo.description"),
9034
- "vanilla": (t("corpus.private.vanilla") if private_corpus_enabled()
9042
+ "vanilla": (t("instructions.private.vanilla") if private_instructions_enabled()
9035
9043
  else t("preset.vanilla.description")),
9036
9044
  }
9037
9045
 
9038
9046
 
9039
- def _corpus_launch_description(status: dict[str, Any] | None) -> str:
9047
+ def _instructions_launch_description(status: dict[str, Any] | None) -> str:
9040
9048
  """Explain the installed selection shared by modes without claiming a launch applies it."""
9041
- if private_corpus_enabled():
9042
- return t("corpus.private.relationship")
9043
- lines = [t("corpus.launch.relationship")]
9049
+ if private_instructions_enabled():
9050
+ return t("instructions.private.relationship")
9051
+ lines = [t("instructions.launch.relationship")]
9044
9052
  domains = (status or {}).get("domains")
9045
9053
  applied = domains.get("applied") if isinstance(domains, dict) else None
9046
9054
  if not isinstance(applied, list) or not all(isinstance(name, str) for name in applied):
@@ -9048,12 +9056,12 @@ def _corpus_launch_description(status: dict[str, Any] | None) -> str:
9048
9056
  if isinstance(domains, dict) and "applied" in domains and applied is None
9049
9057
  else t("panel.unprojected"))
9050
9058
  return "\n".join([*lines, message])
9051
- lines += [t("corpus.launch.applied"), t("corpus.launch.base")]
9052
- facts = (corpus_facts(status) or {}).get("domains", {})
9053
- labels = corpus_domain_labels()
9059
+ lines += [t("instructions.launch.applied"), t("instructions.launch.base")]
9060
+ facts = (instructions_facts(status) or {}).get("domains", {})
9061
+ labels = instructions_domain_labels()
9054
9062
  for name in applied:
9055
- content = _corpus_domain_content(
9056
- name, facts.get(name, {}).get("description") or t("corpus.launch.unknown"),
9063
+ content = _instructions_domain_content(
9064
+ name, facts.get(name, {}).get("description") or t("instructions.launch.unknown"),
9057
9065
  )
9058
9066
  lines.append(f"• {labels.get(name, name)}: {content.splitlines()[0]}")
9059
9067
  return "\n".join(lines)
@@ -9068,53 +9076,53 @@ def _domain_description(info: dict[str, Any] | None, name: str = "") -> str:
9068
9076
  and they are kept apart because they are spent differently: rules are in the global
9069
9077
  that every session and every subagent loads, guides are on disk and read only when a
9070
9078
  rule points at one."""
9071
- content = _corpus_domain_content(name, info.get("description", "") if info else "")
9079
+ content = _instructions_domain_content(name, info.get("description", "") if info else "")
9072
9080
  if info is None:
9073
- return "\n\n".join(filter(None, [content, t("corpus.toggle.description")]))
9081
+ return "\n\n".join(filter(None, [content, t("instructions.toggle.description")]))
9074
9082
  # Both keys are named at a literal call site rather than chosen into a variable:
9075
9083
  # the catalog gate finds keys by reading the quoted argument of each t() call, so a
9076
9084
  # computed key is a string no language is ever checked for.
9077
9085
  if info["guides"]:
9078
- sizes = t("corpus.size.detail").format(
9086
+ sizes = t("instructions.size.detail").format(
9079
9087
  rules=info["rules"],
9080
9088
  size=size_label(info["chars"]),
9081
9089
  guides=info["guides"],
9082
9090
  guide_size=size_label(info["guide_chars"]),
9083
9091
  )
9084
9092
  else:
9085
- sizes = t("corpus.size.detail.noguides").format(
9093
+ sizes = t("instructions.size.detail.noguides").format(
9086
9094
  rules=info["rules"], size=size_label(info["chars"]),
9087
9095
  )
9088
- return "\n\n".join(filter(None, [content, sizes, t("corpus.toggle.description")]))
9096
+ return "\n\n".join(filter(None, [content, sizes, t("instructions.toggle.description")]))
9089
9097
 
9090
9098
 
9091
- def corpus_checklist(ui: "TextualUI | None") -> None:
9099
+ def instructions_checklist(ui: "TextualUI | None") -> None:
9092
9100
  """Toggle-and-apply loop over the optional domain packages.
9093
9101
 
9094
9102
  The list and the applied set come from corpus-status.json — the installer's
9095
9103
  projection — never from repo paths this launcher cannot know. Selection state
9096
9104
  lives only on this screen; Apply hands the exact set to the installer
9097
9105
  (raising through the Textual app so the terminal is free), and Esc leaves the
9098
- deployed corpus untouched."""
9099
- status = load_corpus_status()
9106
+ deployed instructions untouched."""
9107
+ status = load_instructions_status()
9100
9108
  domains = (status or {}).get("domains")
9101
9109
  if not isinstance(domains, dict) or not isinstance(domains.get("available"), list):
9102
- _corpus_info(
9103
- ui, t("corpus.title"),
9104
- [t("corpus.unavailable.line1"), t("corpus.unavailable.line2")],
9105
- back_hint=t("corpus.back.hint"),
9110
+ _instructions_info(
9111
+ ui, t("instructions.title"),
9112
+ [t("instructions.unavailable.line1"), t("instructions.unavailable.line2")],
9113
+ back_hint=t("instructions.back.hint"),
9106
9114
  )
9107
9115
  return
9108
9116
  available = domains["available"]
9109
9117
  if not available:
9110
9118
  # A list that is empty passes the shape check above and then builds a menu whose
9111
9119
  # only row is a disabled Apply, which `choose` refuses as "no available options"
9112
- # — a LaunchError, which `_corpus_screen` rethrows by design, so the session ends
9120
+ # — a LaunchError, which `_instructions_screen` rethrows by design, so the session ends
9113
9121
  # on a projection that is not even malformed. Told, not raised. Its own message
9114
9122
  # rather than the one above: "run the installer" is wrong advice for a projection
9115
9123
  # that read fine and simply lists nothing optional.
9116
- _corpus_info(ui, t("corpus.title"), [t("corpus.none.line")],
9117
- back_hint=t("corpus.back.hint"))
9124
+ _instructions_info(ui, t("instructions.title"), [t("instructions.none.line")],
9125
+ back_hint=t("instructions.back.hint"))
9118
9126
  return
9119
9127
  applied_raw = domains.get("applied")
9120
9128
  # None is "never selected", [] is a verified core+infra-only selection —
@@ -9127,9 +9135,9 @@ def corpus_checklist(ui: "TextualUI | None") -> None:
9127
9135
  # read, and every use below falls back to the bare name — a checklist that cannot
9128
9136
  # size its packages is still a checklist, and this screen is how a user reaches the
9129
9137
  # installer that would repair the projection.
9130
- facts = corpus_facts(status)
9138
+ facts = instructions_facts(status)
9131
9139
  sized = (facts or {}).get("domains", {})
9132
- labels = corpus_domain_labels()
9140
+ labels = instructions_domain_labels()
9133
9141
  display_names = {name: f"{labels[name]} ({name})" if name in labels else name for name in available}
9134
9142
  column = max(display_width(label) for label in display_names.values()) + 2
9135
9143
  while True:
@@ -9147,35 +9155,35 @@ def corpus_checklist(ui: "TextualUI | None") -> None:
9147
9155
  ]
9148
9156
  changed = toggles != applied or never_applied
9149
9157
  pending = "" if not changed else (
9150
- " → " + (", ".join(sorted(toggles)) or t("corpus.core-only"))
9158
+ " → " + (", ".join(sorted(toggles)) or t("instructions.core-only"))
9151
9159
  )
9152
9160
  options.append(
9153
9161
  MenuOption(
9154
- CORPUS_APPLY,
9155
- t("corpus.apply.label").format(pending=pending),
9156
- t("corpus.apply.description"),
9162
+ INSTRUCTIONS_APPLY,
9163
+ t("instructions.apply.label").format(pending=pending),
9164
+ t("instructions.apply.description"),
9157
9165
  enabled=changed,
9158
- unavailable_reason=t("corpus.apply.unchanged"),
9166
+ unavailable_reason=t("instructions.apply.unchanged"),
9159
9167
  )
9160
9168
  )
9161
9169
  try:
9162
9170
  choice = choose(
9163
- t("corpus.title"), options, CORPUS_APPLY, ui, allow_back=True,
9164
- corpus_lines=[
9165
- t("corpus.core.line"),
9166
- *([] if not sized else [t("corpus.size.legend")]),
9167
- *corpus_summary_lines(status),
9171
+ t("instructions.title"), options, INSTRUCTIONS_APPLY, ui, allow_back=True,
9172
+ instructions_lines=[
9173
+ t("instructions.core.line"),
9174
+ *([] if not sized else [t("instructions.size.legend")]),
9175
+ *instructions_summary_lines(status),
9168
9176
  ],
9169
- confirm=CORPUS_APPLY,
9177
+ confirm=INSTRUCTIONS_APPLY,
9170
9178
  )
9171
9179
  except BackRequested:
9172
9180
  return
9173
- if choice == CORPUS_APPLY:
9181
+ if choice == INSTRUCTIONS_APPLY:
9174
9182
  selection = sorted(toggles)
9175
9183
  if ui is None:
9176
- run_corpus_apply(selection)
9184
+ run_instructions_apply(selection)
9177
9185
  return
9178
- raise CorpusApplyRequested(selection)
9186
+ raise InstructionsApplyRequested(selection)
9179
9187
  if choice in toggles:
9180
9188
  toggles.discard(choice)
9181
9189
  else:
@@ -9197,7 +9205,7 @@ def understand_menu(ui: TextualUI | None) -> str | None:
9197
9205
  try:
9198
9206
  bundles = understand_manager().list_bundles()
9199
9207
  if not bundles:
9200
- _corpus_info(ui, t("understand.title"), [t("understand.empty")])
9208
+ _instructions_info(ui, t("understand.title"), [t("understand.empty")])
9201
9209
  return None
9202
9210
  return choose(t("understand.title"), [
9203
9211
  MenuOption(row["id"], row["title"],
@@ -9205,11 +9213,11 @@ def understand_menu(ui: TextualUI | None) -> str | None:
9205
9213
  count=row["item_count"]))
9206
9214
  for row in bundles
9207
9215
  ], bundles[0]["id"], ui, allow_back=True,
9208
- corpus_lines=[t("understand.description"), t("understand.session.scope")])
9216
+ instructions_lines=[t("understand.description"), t("understand.session.scope")])
9209
9217
  except BackRequested:
9210
9218
  return None
9211
9219
  except (OSError, RuntimeError, ValueError) as exc:
9212
- _corpus_info(ui, t("understand.title"), [str(exc)])
9220
+ _instructions_info(ui, t("understand.title"), [str(exc)])
9213
9221
  return None
9214
9222
 
9215
9223
 
@@ -9228,24 +9236,24 @@ def shell_connection_menu(ui: TextualUI | None, dry_run: bool = False) -> None:
9228
9236
  MenuOption("restore", t("shell.restore"), t("shell.restore.description")),
9229
9237
  MenuOption("remove", t("shell.remove"), t("shell.remove.description")),
9230
9238
  MenuOption("back", t("distill.back.label"), t("shell.back")),
9231
- ], "back", ui, allow_back=True, corpus_lines=lines)
9239
+ ], "back", ui, allow_back=True, instructions_lines=lines)
9232
9240
  if selected == "back":
9233
9241
  return
9234
9242
  decision = choose(t("shell.confirm"), [
9235
9243
  MenuOption("cancel", t("shell.cancel"), t("shell.back")),
9236
9244
  MenuOption("apply", t("shell.apply"), t("shell.scope")),
9237
- ], "cancel", ui, allow_back=True, corpus_lines=lines)
9245
+ ], "cancel", ui, allow_back=True, instructions_lines=lines)
9238
9246
  if decision != "apply":
9239
9247
  continue
9240
9248
  result = manager.apply(selected, dry_run=dry_run)
9241
9249
  message = (t("shell.preview") if dry_run else
9242
9250
  t("shell.restored") if selected == "restore" else t("shell.removed"))
9243
- _corpus_info(ui, t("shell.title"), [message, *result["changed_paths"],
9251
+ _instructions_info(ui, t("shell.title"), [message, *result["changed_paths"],
9244
9252
  *result.get("needs_action", [])])
9245
9253
  except BackRequested:
9246
9254
  return
9247
9255
  except (ShellIntegrationError, OSError, RuntimeError) as exc:
9248
- _corpus_info(ui, t("shell.title"), [str(exc)])
9256
+ _instructions_info(ui, t("shell.title"), [str(exc)])
9249
9257
  # Language names render in their own language BY DESIGN — a reader hunting for
9250
9258
  # their language must be able to recognise it whatever UI language is active — so
9251
9259
  # these labels are deliberately catalog-independent.
@@ -9285,7 +9293,7 @@ def choose_language(
9285
9293
  # refusal is reported and returns to the hub; this is that, for the same
9286
9294
  # reason. The catalogs are deliberately not reloaded: the file still holds the
9287
9295
  # previous language, so the session keeps rendering what was actually saved.
9288
- _corpus_info(ui, t("language.title"), [str(exc)])
9296
+ _instructions_info(ui, t("language.title"), [str(exc)])
9289
9297
  return
9290
9298
  load_catalogs(config_path)
9291
9299
 
@@ -9314,31 +9322,31 @@ def pick_mode_and_preset(
9314
9322
  user_names = set(load_user_presets(user_presets_path(config_path))) if config_path else set()
9315
9323
  mode = resume_mode
9316
9324
  while True:
9317
- status = load_corpus_status()
9318
- corpus_description = _corpus_launch_description(status)
9325
+ status = load_instructions_status()
9326
+ instructions_description = _instructions_launch_description(status)
9319
9327
  if mode is None:
9320
9328
  mode_options = [
9321
9329
  MenuOption(SWE_MODE, t("mode.swe.label"),
9322
- t("mode.swe.description") + "\n\n" + corpus_description),
9330
+ t("mode.swe.description") + "\n\n" + instructions_description),
9323
9331
  MenuOption(
9324
9332
  DEFAULT_PRESET_MODE,
9325
9333
  t("mode.builder.label"),
9326
- t("mode.builder.description") + "\n\n" + corpus_description,
9334
+ t("mode.builder.description") + "\n\n" + instructions_description,
9327
9335
  ),
9328
9336
  MenuOption(
9329
9337
  DISTILL_MODE,
9330
9338
  t("mode.distill.label"),
9331
- t("mode.distill.description") + "\n\n" + corpus_description,
9339
+ t("mode.distill.description") + "\n\n" + instructions_description,
9332
9340
  ),
9333
9341
  ]
9334
9342
  mode_options.append(
9335
9343
  MenuOption(
9336
- CORPUS_OPTION,
9337
- t("corpus.private.title") if private_corpus_enabled() else t("corpus.label"),
9338
- t("corpus.private.description") if private_corpus_enabled() else t("corpus.description"),
9344
+ INSTRUCTIONS_OPTION,
9345
+ t("instructions.private.title") if private_instructions_enabled() else t("instructions.label"),
9346
+ t("instructions.private.description") if private_instructions_enabled() else t("instructions.description"),
9339
9347
  )
9340
9348
  )
9341
- if private_corpus_enabled():
9349
+ if private_instructions_enabled():
9342
9350
  mode_options.append(MenuOption(UNDERSTAND_OPTION, t("understand.title"), t("understand.description")))
9343
9351
  mode_options.append(MenuOption(SHELL_CONNECTION_OPTION, t("shell.title"), t("shell.description")))
9344
9352
  if config_path is not None:
@@ -9386,16 +9394,16 @@ def pick_mode_and_preset(
9386
9394
  DEFAULT_PRESET_MODE,
9387
9395
  ui,
9388
9396
  preview=preview_mode,
9389
- corpus_lines=corpus_summary_lines(status),
9397
+ instructions_lines=instructions_summary_lines(status),
9390
9398
  )
9391
- if mode == CORPUS_OPTION:
9392
- if private_corpus_enabled():
9399
+ if mode == INSTRUCTIONS_OPTION:
9400
+ if private_instructions_enabled():
9393
9401
  if ui is not None:
9394
- raise CorpusStudioRequested()
9395
- open_corpus_studio()
9402
+ raise InstructionsStudioRequested()
9403
+ open_instructions_studio()
9396
9404
  mode = None
9397
9405
  continue
9398
- _corpus_screen(ui, lambda: corpus_checklist(ui))
9406
+ _instructions_screen(ui, lambda: instructions_checklist(ui))
9399
9407
  mode = None
9400
9408
  continue
9401
9409
  if mode == SHELL_CONNECTION_OPTION:
@@ -9427,7 +9435,7 @@ def pick_mode_and_preset(
9427
9435
  data["label"],
9428
9436
  (preset_descriptions().get(name, data.get("description", ""))
9429
9437
  if name not in user_names else data.get("description", ""))
9430
- + "\n\n" + corpus_description,
9438
+ + "\n\n" + instructions_description,
9431
9439
  )
9432
9440
  for name, data in presets.items()
9433
9441
  if preset_mode(data) == mode
@@ -9438,7 +9446,7 @@ def pick_mode_and_preset(
9438
9446
  CUSTOM_PRESET,
9439
9447
  t("preset.custom.label"),
9440
9448
  t("preset.custom.description")
9441
- + "\n\n" + corpus_description,
9449
+ + "\n\n" + instructions_description,
9442
9450
  )
9443
9451
  )
9444
9452
  default = mode_default_preset(presets, mode) or CUSTOM_PRESET
@@ -9609,11 +9617,11 @@ def child_agent_registrations(
9609
9617
  raise LaunchError(f"Codex agent template requires description: {source}")
9610
9618
  data["model"] = plan["tiers"][tier]["model"]
9611
9619
  data["model_reasoning_effort"] = tier_effort(plan, tier)
9612
- if plan.get("corpus_instruction_text"):
9613
- # The child's own instruction body and the selected parent corpus are
9620
+ if plan.get("instructions_instruction_text"):
9621
+ # The child's own instruction body and the selected parent instructions are
9614
9622
  # composed before the content-addressed config path is derived.
9615
9623
  existing = data.get("developer_instructions", "")
9616
- data["developer_instructions"] = existing + "\n\n" + plan["corpus_instruction_text"]
9624
+ data["developer_instructions"] = existing + "\n\n" + plan["instructions_instruction_text"]
9617
9625
  lines = []
9618
9626
  for key, value in data.items():
9619
9627
  if not isinstance(value, (str, int, float, bool)):
@@ -10123,7 +10131,7 @@ def delegation_clause(plan: dict[str, Any]) -> str:
10123
10131
 
10124
10132
  The CLI's line carries its own exception, so the fix is to satisfy it rather than argue
10125
10133
  with it. Delegation IS the user's standing request — they chose the preset, and the
10126
- corpus's standing spawn policy is their instruction — so the contract says so in the
10134
+ instructions's standing spawn policy is their instruction — so the contract says so in the
10127
10135
  words that clause is looking for. Nothing is overridden; a fact that was already true
10128
10136
  is simply stated where the reader can see it."""
10129
10137
  if sweep_main(plan):
@@ -10164,8 +10172,8 @@ def claude_agents(plan: dict[str, Any]) -> str:
10164
10172
  # allowlist enforces its one-rule-per-item contract instead of merely
10165
10173
  # restating it in the prompt.
10166
10174
  prompt += " Apply one explicit read-only rule per item; do not make semantic judgments."
10167
- if plan.get("corpus_instruction_text"):
10168
- prompt += "\n\n" + plan["corpus_instruction_text"]
10175
+ if plan.get("instructions_instruction_text"):
10176
+ prompt += "\n\n" + plan["instructions_instruction_text"]
10169
10177
  role = {
10170
10178
  "description": f"{tier.upper()} tier: {format_model_effort(binding['model'], effort)}",
10171
10179
  "prompt": prompt,
@@ -10647,7 +10655,7 @@ def print_summary(
10647
10655
  "(permission mode, not OS sandbox)",
10648
10656
  file=stream,
10649
10657
  )
10650
- if private_corpus_enabled():
10658
+ if private_instructions_enabled():
10651
10659
  print(
10652
10660
  " Global files "
10653
10661
  + t("global-instructions.summary").format(
@@ -10729,16 +10737,16 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
10729
10737
  )
10730
10738
  parser.add_argument("--yes", action="store_true", help="skip launch confirmation")
10731
10739
  parser.add_argument("--dry-run", action="store_true", help="print projection without launching")
10732
- parser.add_argument("--corpus", action="store_true", help="open private Corpus Studio")
10733
- parser.add_argument("--understand", metavar="BUNDLE", help="start an interactive learning session for a corpus bundle; list with agent-bios understand list")
10734
- parser.add_argument("--corpus-domains", help="domain selection for this activated session only")
10735
- parser.add_argument("--corpus-native", action="store_true", help="opt into selected corpus hooks on either host and Claude native agents for this session only")
10740
+ parser.add_argument("--instructions", "--corpus", action="store_true", help="open private Instructions Studio")
10741
+ parser.add_argument("--understand", metavar="BUNDLE", help="start an interactive learning session for an instruction bundle; list with agent-bios understand list")
10742
+ parser.add_argument("--instructions-domains", "--corpus-domains", help="domain selection for this activated session only")
10743
+ parser.add_argument("--instructions-native", "--corpus-native", action="store_true", help="opt into selected instructions hooks on either host and Claude native agents for this session only")
10736
10744
  parser.add_argument(
10737
10745
  "--exclude-global-instructions",
10738
10746
  action="store_true",
10739
10747
  help="omit only personal global AGENTS.md/CLAUDE.md files and imports for this private Claude session",
10740
10748
  )
10741
- parser.add_argument("--resume-session", help="resume a host session with its pinned corpus")
10749
+ parser.add_argument("--resume-session", help="resume a host session with its pinned instructions")
10742
10750
  parser.add_argument(
10743
10751
  "--verify-receipts", nargs=2, metavar=("PLAN", "RECEIPTS"),
10744
10752
  help="adjudicate a ReviewPlan/v1 record against a ReviewReceipts/v1 bundle and exit",
@@ -10810,14 +10818,14 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
10810
10818
  hostless = (
10811
10819
  args.verify_receipts or args.emit_receipt or args.fold_receipts
10812
10820
  or args.check_adapter or args.compile_criterion or args.check_findings
10813
- or args.check_schema_flag or args.corpus
10821
+ or args.check_schema_flag or args.instructions
10814
10822
  )
10815
10823
  if args.host is None and not hostless:
10816
10824
  parser.error("the following arguments are required: host")
10817
- if args.understand and (args.preset or args.custom or args.corpus or args.resume_session
10818
- or args.corpus_native or args.corpus_domains is not None
10825
+ if args.understand and (args.preset or args.custom or args.instructions or args.resume_session
10826
+ or args.instructions_native or args.instructions_domains is not None
10819
10827
  or args.exclude_global_instructions or args.forward):
10820
- parser.error("--understand selects its own learning setup and cannot be combined with preset, corpus, resume, custom, global-exclusion or forwarded options")
10828
+ parser.error("--understand selects its own learning setup and cannot be combined with preset, instructions, resume, custom, global-exclusion or forwarded options")
10821
10829
  if args.evidence and not args.emit_receipt:
10822
10830
  parser.error("--evidence only applies to --emit-receipt")
10823
10831
  if args.packet and not args.verify_receipts:
@@ -11054,8 +11062,8 @@ def _tolerate_narrow_stdout() -> None:
11054
11062
  def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11055
11063
  _tolerate_narrow_stdout()
11056
11064
  args = parse_args(drop_check_adapter_separator(argv))
11057
- if args.corpus:
11058
- open_corpus_studio()
11065
+ if args.instructions:
11066
+ open_instructions_studio()
11059
11067
  return 0
11060
11068
  if args.verify_receipts:
11061
11069
  return verify_receipts_command(
@@ -11080,7 +11088,7 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11080
11088
  return check_adapter_command(args.check_adapter[0], args.check_adapter[1:])
11081
11089
  config_path = args.config.expanduser()
11082
11090
  generation = None
11083
- if private_corpus_enabled():
11091
+ if private_instructions_enabled():
11084
11092
  bare = not args.preset and not args.custom and not args.understand and not args.dry_run and (
11085
11093
  args.no_tui or bool(args.forward) or os.environ.get("AGENT_LAUNCH_TUI") == "0"
11086
11094
  or not (sys.stdin.isatty() and sys.stdout.isatty()))
@@ -11090,7 +11098,7 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11090
11098
  config = load_config(config_path)
11091
11099
  command, bare_args = resolve_backend(config, args.host)
11092
11100
  if args.exclude_global_instructions and not args.resume_session:
11093
- if not private_corpus_enabled():
11101
+ if not private_instructions_enabled():
11094
11102
  raise LaunchError(
11095
11103
  "excluding global instruction files requires an agent-bios activated session"
11096
11104
  )
@@ -11107,30 +11115,30 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11107
11115
  for preset in config.get("presets", {}).values():
11108
11116
  if isinstance(preset, dict):
11109
11117
  preset["include_global_instructions"] = False
11110
- if args.corpus_native and not private_corpus_enabled():
11111
- raise LaunchError("--corpus-native requires a private installation; run agent-bios install first")
11112
- if private_corpus_enabled():
11118
+ if args.instructions_native and not private_instructions_enabled():
11119
+ raise LaunchError("--instructions-native requires a private installation; run agent-bios install first")
11120
+ if private_instructions_enabled():
11113
11121
  # Private templates retain the native host's config home and registrations.
11114
- private_root = corpus_package_root()
11122
+ private_root = instructions_package_root()
11115
11123
  os.environ["AGENT_BIOS_PACKAGE_ROOT"] = str(private_root)
11116
11124
  config["hosts"]["codex"]["agent_templates"] = {
11117
11125
  tier: str(private_root / "codex/agents" / f"{tier}.toml") for tier in SPAWNABLE_TIERS
11118
11126
  }
11119
11127
  if args.resume_session:
11120
11128
  if (
11121
- args.corpus_native
11122
- or args.corpus_domains is not None
11129
+ args.instructions_native
11130
+ or args.instructions_domains is not None
11123
11131
  or args.exclude_global_instructions
11124
11132
  ):
11125
11133
  raise LaunchError(
11126
- "resume uses its pinned corpus/native/global-instruction configuration; "
11134
+ "resume uses its pinned instructions/native/global-instruction configuration; "
11127
11135
  "start a new session to change it"
11128
11136
  )
11129
- if not private_corpus_enabled():
11130
- raise LaunchError("a corpus-pinned resume requires a private installation")
11131
- store = corpus_store()
11132
- import corpus_session
11133
- return corpus_session.launch(command, [], store.state_root, args.host, {},
11137
+ if not private_instructions_enabled():
11138
+ raise LaunchError("an instruction-pinned resume requires a private installation")
11139
+ store = instructions_store()
11140
+ import instructions_session
11141
+ return instructions_session.launch(command, [], store.state_root, args.host, {},
11134
11142
  resume_id=args.resume_session)
11135
11143
  nudge = session_distill_nudge(config)
11136
11144
  if nudge:
@@ -11148,11 +11156,11 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11148
11156
  bypass = args.no_tui or bool(args.forward) or os.environ.get("AGENT_LAUNCH_TUI") == "0"
11149
11157
  if (bypass or not tty) and not args.preset and not args.custom and not args.understand and not args.dry_run:
11150
11158
  if (
11151
- args.corpus_native
11152
- or args.corpus_domains is not None
11159
+ args.instructions_native
11160
+ or args.instructions_domains is not None
11153
11161
  or args.exclude_global_instructions
11154
11162
  ):
11155
- raise LaunchError("corpus launch options require --preset NAME or an interactive configured launch")
11163
+ raise LaunchError("instructions launch options require --preset NAME or an interactive configured launch")
11156
11164
  # The bare launch: nothing decides policy but the backend's own bare-launch
11157
11165
  # arguments. Every other path below projects the preset's policy instead.
11158
11166
  exec_backend(command, [*bare_args, *args.forward])
@@ -11212,26 +11220,26 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11212
11220
  shell_dry_run=args.dry_run,
11213
11221
  )
11214
11222
  break
11215
- except CorpusApplyRequested as request:
11223
+ except InstructionsApplyRequested as request:
11216
11224
  # The Textual app is already torn down; the installer owns the
11217
11225
  # terminal for the duration, and the picker re-opens on a fresh
11218
11226
  # status projection afterwards.
11219
- run_corpus_apply(request.selection)
11227
+ run_instructions_apply(request.selection)
11220
11228
  continue
11221
- except CorpusStudioRequested:
11222
- open_corpus_studio()
11229
+ except InstructionsStudioRequested:
11230
+ open_instructions_studio()
11223
11231
  continue
11224
11232
  except UnderstandRequested as request:
11225
11233
  plan = build_understand_plan(config, args.host, request.bundle_id)
11226
11234
  break
11227
11235
  learning = None
11228
11236
  if plan.get("understand_bundle"):
11229
- if args.forward or args.corpus_native or args.corpus_domains is not None:
11230
- raise LaunchError("understand! cannot use forwarded arguments or native corpus activation")
11237
+ if args.forward or args.instructions_native or args.instructions_domains is not None:
11238
+ raise LaunchError("understand! cannot use forwarded arguments or native instructions activation")
11231
11239
  learning = understand_manager().show(plan["understand_bundle"])
11232
11240
  validate_review_setup(plan)
11233
11241
  if not plan.get("include_global_instructions", True):
11234
- if not private_corpus_enabled():
11242
+ if not private_instructions_enabled():
11235
11243
  raise LaunchError(
11236
11244
  "excluding global instruction files requires an agent-bios activated session"
11237
11245
  )
@@ -11246,19 +11254,19 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11246
11254
  )
11247
11255
  snapshot = None
11248
11256
  store = None
11249
- if private_corpus_enabled() and not plan_projects_nothing(plan):
11250
- store = corpus_store()
11257
+ if private_instructions_enabled() and not plan_projects_nothing(plan):
11258
+ store = instructions_store()
11251
11259
  selected = None
11252
- if args.corpus_domains is not None:
11253
- raw = [x.strip() for x in args.corpus_domains.split(",") if x.strip()]
11260
+ if args.instructions_domains is not None:
11261
+ raw = [x.strip() for x in args.instructions_domains.split(",") if x.strip()]
11254
11262
  if "none" in raw and raw != ["none"]:
11255
- raise LaunchError("--corpus-domains none cannot be combined with other domains")
11263
+ raise LaunchError("--instructions-domains none cannot be combined with other domains")
11256
11264
  selected = [] if raw == ["none"] else [
11257
11265
  x if x.startswith("@") else f"@agent-bios/core/{x}" for x in raw
11258
11266
  ]
11259
11267
  snapshot = _snapshot_from_config(store, config_path, generation, args.host, selected,
11260
- dry_run=args.dry_run, native=args.corpus_native)
11261
- plan["corpus_instruction_text"] = snapshot["instruction_text"]
11268
+ dry_run=args.dry_run, native=args.instructions_native)
11269
+ plan["instructions_instruction_text"] = snapshot["instruction_text"]
11262
11270
  projected_args = project_args(plan, materialize_agents=not args.dry_run)
11263
11271
  collisions = forwarded_collisions(projected_args, args.forward)
11264
11272
  if collisions:
@@ -11271,8 +11279,8 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11271
11279
  if learning is not None and args.dry_run:
11272
11280
  projected += [understand_initial_prompt("<pinned-understand-session-prompt>")]
11273
11281
  if snapshot is not None and args.dry_run:
11274
- import corpus_session
11275
- projected = corpus_session.compose_argv(
11282
+ import instructions_session
11283
+ projected = instructions_session.compose_argv(
11276
11284
  command,
11277
11285
  projected,
11278
11286
  args.host,
@@ -11281,17 +11289,17 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11281
11289
  )
11282
11290
  summary_stream = sys.stdout if tty or args.dry_run else sys.stderr
11283
11291
  if snapshot is not None:
11284
- print(f" Corpus snapshot {snapshot['content_ref']} · private · next session only", file=summary_stream)
11285
- if args.corpus_native:
11292
+ print(f" Instructions snapshot {snapshot['content_ref']} · private · next session only", file=summary_stream)
11293
+ if args.instructions_native:
11286
11294
  plugins = snapshot.get("assets", {}).get("claude_plugins", [])
11287
11295
  hooks = snapshot.get("assets", {}).get("codex_hooks", {})
11288
11296
  if args.host == "codex":
11289
11297
  count = sum(len(group["hooks"]) for groups in hooks.values() for group in groups)
11290
- print(f" Native corpus opt-in: {count} session-only hook(s); Codex enablement and /hooks trust review apply.", file=summary_stream)
11298
+ print(f" Native instructions opt-in: {count} session-only hook(s); Codex enablement and /hooks trust review apply.", file=summary_stream)
11291
11299
  else:
11292
- print(f" Native corpus opt-in: {len(plugins)} session-only plugin(s); selected hook code can execute.", file=summary_stream)
11300
+ print(f" Native instructions opt-in: {len(plugins)} session-only plugin(s); selected hook code can execute.", file=summary_stream)
11293
11301
  for unavailable in snapshot.get("unavailable", []):
11294
- print(f" Corpus unavailable: {unavailable}", file=summary_stream)
11302
+ print(f" Instructions unavailable: {unavailable}", file=summary_stream)
11295
11303
  if learning is not None:
11296
11304
  print(f" Understand! {learning['title']} · {learning['source_ref']}", file=summary_stream)
11297
11305
  print(" Learning Pinned bundle as reference material; native global files remain unchanged.", file=summary_stream)
@@ -11327,8 +11335,8 @@ def main(argv: list[str], *, bundled_ui: bool = False) -> int:
11327
11335
  env["AGENT_BIOS_UNDERSTAND_SESSION"] = session["session_id"]
11328
11336
  summary_stream.flush()
11329
11337
  if snapshot is not None:
11330
- import corpus_session
11331
- return corpus_session.launch(command, projected, store.state_root, args.host,
11338
+ import instructions_session
11339
+ return instructions_session.launch(command, projected, store.state_root, args.host,
11332
11340
  snapshot, env=env,
11333
11341
  include_global_instructions=plan["include_global_instructions"])
11334
11342
  exec_backend(command, projected, env)