agent-bios 0.9.9 → 0.11.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 (34) hide show
  1. package/DEPENDENCIES.md +3 -3
  2. package/README.md +10 -2
  3. package/claude/CLAUDE.md +4 -41
  4. package/claude/guides/cli-multi-model-workflow.md +7 -7
  5. package/claude/guides/coding-staged-workflow.md +51 -49
  6. package/claude/guides/concept-economy.md +187 -0
  7. package/claude/guides/documentation-hygiene.md +112 -0
  8. package/claude/guides/review-request.md +9 -7
  9. package/claude/guides/session-distill-workflow.md +3 -3
  10. package/claude/guides/tooling-gotchas.md +10 -0
  11. package/claude/guides/verification-discipline.md +166 -0
  12. package/claude/hooks/tooling-gotchas-hook.py +323 -13
  13. package/codex/AGENTS.md +4 -41
  14. package/codex/guides/cli-multi-model-workflow.md +7 -7
  15. package/codex/guides/coding-staged-workflow.md +51 -49
  16. package/codex/guides/concept-economy.md +187 -0
  17. package/codex/guides/documentation-hygiene.md +112 -0
  18. package/codex/guides/review-request.md +9 -7
  19. package/codex/guides/session-distill-workflow.md +3 -3
  20. package/codex/guides/tooling-gotchas.md +10 -0
  21. package/codex/guides/verification-discipline.md +166 -0
  22. package/compose/assemble.py +10 -1
  23. package/compose/check-domains.py +882 -6
  24. package/compose/domains.json +10 -44
  25. package/install.sh +176 -16
  26. package/launch/agent-launch.py +790 -173
  27. package/launch/agent-launch.toml +56 -107
  28. package/launch/i18n/en.toml +66 -0
  29. package/launch/i18n/ja.toml +63 -0
  30. package/launch/i18n/ko.toml +63 -0
  31. package/package.json +9 -4
  32. package/provenance.json +1 -0
  33. package/wrappers/codex-run.sh +1 -1
  34. package/claude/hooks/__pycache__/tooling-gotchas-hook.cpython-314.pyc +0 -0
@@ -48,51 +48,19 @@
48
48
  {"anchor": "Treat a produced field, flag, signal", "tier": "domain", "domains": ["llm-pipeline-dev"]},
49
49
  {"anchor": "Hard-block only deterministically decidable structural", "tier": "domain", "domains": ["llm-pipeline-dev"]},
50
50
  {"anchor": "Runtime/code may enforce the contract", "tier": "domain", "domains": ["llm-pipeline-dev"]},
51
- {"anchor": "Keep the concept graph compact by reusing", "tier": "domain", "domains": ["builder-base"]},
52
- {"anchor": "Treat lasting or shared names as concept", "tier": "domain", "domains": ["builder-base"]},
53
- {"anchor": "Before adding or changing a concept", "tier": "domain", "domains": ["builder-base"]},
54
- {"anchor": "Prefer broad, stable concepts with precise", "tier": "domain", "domains": ["builder-base"]},
51
+ {"anchor": "When adding, changing, renaming, splitting, or exposing anything lasting or shared", "tier": "domain", "domains": ["builder-base"]},
55
52
  {"anchor": "Before fixing a review finding or test failure", "tier": "domain", "domains": ["builder-base"]},
56
- {"anchor": "Split or promote a concept when it changes", "tier": "domain", "domains": ["builder-base"]},
57
- {"anchor": "Keep derived values as properties or projections", "tier": "domain", "domains": ["builder-base"]},
58
- {"anchor": "Keep internal projections and helper outputs internal", "tier": "domain", "domains": ["builder-base"]},
59
- {"anchor": "Distinguish authority from visibility", "tier": "domain", "domains": ["builder-base"]},
60
- {"anchor": "Reuse existing enum values, failure kinds", "tier": "domain", "domains": ["builder-base"]},
61
- {"anchor": "Use fallback paths, compatibility shims", "tier": "domain", "domains": ["builder-base"]},
62
- {"anchor": "Keep comments and active docs aligned", "tier": "domain", "domains": ["builder-base"]},
63
- {"anchor": "When a split is necessary, name the parent", "tier": "domain", "domains": ["builder-base"]},
64
- {"anchor": "In ontology work, check existing entities", "tier": "domain", "domains": ["builder-base"]},
65
- {"anchor": "In code work, follow existing naming patterns", "tier": "domain", "domains": ["builder-base"]},
66
- {"anchor": "Let the repository's shape mirror its concept graph", "tier": "domain", "domains": ["builder-base"]},
67
53
  {"anchor": "For `.xlsx` editing, generation, reconciliation", "tier": "domain", "domains": ["office-work"]},
68
- {"anchor": "For meaningful development work, read and use", "tier": "domain", "domains": ["builder-base"]},
54
+ {"anchor": "For development work, read and use", "tier": "domain", "domains": ["builder-base"]},
69
55
  {"anchor": "For mock, fixture, fake, stub, simulated-provider", "tier": "domain", "domains": ["builder-base"]},
70
- {"anchor": "When the user asks to", "tier": "domain", "domains": ["builder-base"]},
71
- {"anchor": "Think before coding: state key assumptions", "tier": "domain", "domains": ["builder-base"]},
72
- {"anchor": "Build the smallest viable functional path", "tier": "domain", "domains": ["builder-base"]},
73
- {"anchor": "Treat viability as real behavior against", "tier": "domain", "domains": ["builder-base"]},
74
- {"anchor": "Make surgical changes. Touch only what", "tier": "domain", "domains": ["builder-base"]},
75
- {"anchor": "Clean up issues introduced by the current", "tier": "domain", "domains": ["builder-base"]},
76
56
  {"anchor": "Own the full lifecycle of what you create", "tier": "domain", "domains": ["builder-base"]},
77
- {"anchor": "Define success criteria before multi-step coding work", "tier": "domain", "domains": ["builder-base"]},
78
- {"anchor": "For bugs, prefer a reproducing test", "tier": "domain", "domains": ["builder-base"]},
79
- {"anchor": "Every changed line should trace back", "tier": "domain", "domains": ["builder-base"]},
80
- {"anchor": "Fix the root cause at its authority", "tier": "domain", "domains": ["builder-base"]},
81
57
  {"anchor": "Land risky or behavior-changing work behind", "tier": "domain", "domains": ["builder-base"]},
82
58
  {"anchor": "For composing a review request, packet", "tier": "domain", "domains": ["builder-base"]},
83
59
  {"anchor": "After every meaningful code, ontology, config", "tier": "domain", "domains": ["builder-base"]},
84
- {"anchor": "Use static checks broadly: typecheck, lint", "tier": "domain", "domains": ["builder-base"]},
85
- {"anchor": "Add the narrowest reliable runtime or semantic test", "tier": "domain", "domains": ["builder-base"]},
86
- {"anchor": "Pick each domain's verification mix", "tier": "domain", "domains": ["builder-base"]},
87
- {"anchor": "Let the LLM derive scenarios from the diff", "tier": "domain", "domains": ["builder-base"]},
88
- {"anchor": "Keep E2E stable with deterministic data", "tier": "domain", "domains": ["builder-base"]},
60
+ {"anchor": "For choosing verification depth, the per-domain mix, the case space, what makes a completion criterion falsifiable", "tier": "domain", "domains": ["builder-base"]},
89
61
  {"anchor": "Report the checks run, results", "tier": "core", "domains": []},
90
62
  {"anchor": "Trust a green check only when it traversed", "tier": "domain", "domains": ["builder-base"]},
91
- {"anchor": "Make completion criteria falsifiable", "tier": "domain", "domains": ["builder-base"]},
92
63
  {"anchor": "Before comparing two of anything", "tier": "domain", "domains": ["builder-base"]},
93
- {"anchor": "For non-trivial designs or high-risk changes", "tier": "domain", "domains": ["builder-base"]},
94
- {"anchor": "Proportion verification to cost, risk, and information gain", "tier": "domain", "domains": ["builder-base"]},
95
- {"anchor": "Trust a green / zero-findings verdict only", "tier": "domain", "domains": ["builder-base"]},
96
64
  {"anchor": "For concrete shell/CLI traps", "tier": "domain", "domains": ["builder-base"]},
97
65
  {"anchor": "Ambient state — the active shell", "tier": "domain", "domains": ["builder-base"]},
98
66
  {"anchor": "Before relying on any model id", "tier": "domain", "domains": ["builder-base"]},
@@ -100,7 +68,6 @@
100
68
  {"anchor": "Never accept secrets through transcript- or history-logged channels", "tier": "core", "domains": []},
101
69
  {"anchor": "When a secret must be supplied", "tier": "domain", "domains": ["builder-base"]},
102
70
  {"anchor": "Treat a coarse runtime signal", "tier": "domain", "domains": ["builder-base"]},
103
- {"anchor": "Before reasoning about what a branch contains", "tier": "domain", "domains": ["builder-base"]},
104
71
  {"anchor": "Standing spawn policy: check the spawn gates", "tier": "domain", "domains": ["multi-agent-orchestration"]},
105
72
  {"anchor": "Down-spawns carry a machine-checkable done-when", "tier": "domain", "domains": ["multi-agent-orchestration"]},
106
73
  {"anchor": "For work spanning multiple models or CLI agents", "tier": "domain", "domains": ["multi-agent-orchestration"]},
@@ -111,11 +78,7 @@
111
78
  {"anchor": "Never retry-storm a live rate limit", "tier": "domain", "domains": ["multi-agent-orchestration"]},
112
79
  {"anchor": "On any resumed, cleared, or relocated session", "tier": "domain", "domains": ["multi-agent-orchestration"]},
113
80
  {"anchor": "Keep runtime code, active docs, and execution-facing", "tier": "domain", "domains": ["builder-base"]},
114
- {"anchor": "Use comments for non-obvious current behavior", "tier": "domain", "domains": ["builder-base"]},
115
- {"anchor": "Put backward-compatibility notes, deprecated behavior", "tier": "domain", "domains": ["builder-base"]},
116
- {"anchor": "Link from active docs or code to isolated notes", "tier": "domain", "domains": ["builder-base"]},
117
- {"anchor": "Phrase guidelines as desired behavior", "tier": "domain", "domains": ["builder-base"]},
118
- {"anchor": "Prefer established docs such as", "tier": "domain", "domains": ["builder-base"]},
81
+ {"anchor": "For where a comment, a compatibility note, deprecated behavior, a rejected alternative", "tier": "domain", "domains": ["builder-base"]},
119
82
  {"anchor": "For SVG diagrams, service blueprints", "tier": "domain", "domains": ["visualization-docs"]},
120
83
  {"anchor": "When a concept is easier to understand visually", "tier": "domain", "domains": ["visualization-docs"]},
121
84
  {"anchor": "Use HTML for comparisons, flows, state changes", "tier": "domain", "domains": ["visualization-docs"]},
@@ -126,10 +89,12 @@
126
89
  {"anchor": "`learn!` — session learning", "tier": "core", "domains": []}
127
90
  ],
128
91
  "guides": {
129
- "claude-prompting.md": {"tier": "domain", "domains": ["multi-agent-orchestration"]},
130
- "cli-multi-model-workflow.md": {"tier": "domain", "domains": ["multi-agent-orchestration"]},
92
+ "claude-prompting.md": {"tier": "domain", "domains": ["multi-agent-orchestration"], "handles": ["per-family prompting guide", "계열별 prompting guide"]},
93
+ "cli-multi-model-workflow.md": {"tier": "domain", "domains": ["multi-agent-orchestration"], "handles": ["multi-model guide", "multi-model 가이드"]},
131
94
  "coding-staged-workflow.md": {"tier": "domain", "domains": ["builder-base"]},
132
- "gpt-prompting.md": {"tier": "domain", "domains": ["multi-agent-orchestration"]},
95
+ "concept-economy.md": {"tier": "domain", "domains": ["builder-base"]},
96
+ "documentation-hygiene.md": {"tier": "domain", "domains": ["builder-base"]},
97
+ "gpt-prompting.md": {"tier": "domain", "domains": ["multi-agent-orchestration"], "handles": ["prompting guides for gpt-family"]},
133
98
  "implementation-map.md": {"tier": "domain", "domains": ["visualization-docs"]},
134
99
  "llm-capability-boundary-examples.md": {"tier": "domain", "domains": ["llm-pipeline-dev"]},
135
100
  "llm-capability-boundary-patterns.md": {"tier": "domain", "domains": ["llm-pipeline-dev"]},
@@ -139,6 +104,7 @@
139
104
  "session-distill-workflow.md": {"tier": "infra", "domains": []},
140
105
  "learning-flow.md": {"tier": "infra", "domains": []},
141
106
  "svg-visualization-guide.md": {"tier": "domain", "domains": ["visualization-docs"]},
107
+ "verification-discipline.md": {"tier": "domain", "domains": ["builder-base"]},
142
108
  "tooling-gotchas.md": {"tier": "domain", "domains": ["builder-base"]}
143
109
  },
144
110
  "hooks": {
package/install.sh CHANGED
@@ -238,9 +238,15 @@ assemble_corpus() {
238
238
  for g in "$REPO"/codex/guides/*.md; do
239
239
  [ -f "$g" ] || continue
240
240
  dst="$CODEX_DIR/guides/$(basename "$g")"
241
- [ -f "$dst" ] && printf '%s\n' "$dst" >> "$MANIFEST"
241
+ # if-form, not `[ -f ] && printf`: when the alphabetically LAST guide is
242
+ # not deployed (a partial domain selection on a fresh machine), the
243
+ # compound's false status became the loop's, the loop's became the
244
+ # function's, and the install died silently right after ASSEMBLED —
245
+ # a latent first-user bug the I9 scenario exposed.
246
+ if [ -f "$dst" ]; then printf '%s\n' "$dst" >> "$MANIFEST"; fi
242
247
  done
243
248
  fi
249
+ return 0
244
250
  }
245
251
 
246
252
  add_zsh_hook() {
@@ -602,6 +608,13 @@ cmd_install() {
602
608
  deploy_file "$REPO/launch/agent-launch.toml" "$LAUNCH_DIR/profiles.toml"
603
609
  deploy_file "$REPO/launch/agent-launch.zsh" "$LAUNCH_DIR/shell.zsh"
604
610
  deploy_file "$REPO/launch/agent-launch.py" "$BIN_DIR/agent-launch" "+x"
611
+ # UI text catalogs: deploy-managed siblings of the config, one per language.
612
+ # The launcher resolves them from the config path, so the deployed home is
613
+ # $LAUNCH_DIR/i18n exactly as the checkout's is launch/i18n.
614
+ local lang
615
+ for lang in en ko ja; do
616
+ deploy_file "$REPO/launch/i18n/$lang.toml" "$LAUNCH_DIR/i18n/$lang.toml"
617
+ done
605
618
  log ""
606
619
  log "Optional dependencies (missing ones only degrade the routes that need them)..."
607
620
  handle_capabilities "$WITH" || exit 1
@@ -622,11 +635,27 @@ cmd_install() {
622
635
  # package.json (version + releaseDate) — distinct from the corpus content
623
636
  # version. Best-effort: a failure here never fails the install.
624
637
  if [ "$DRY_RUN" != 1 ]; then
625
- if python3 - "$REPO/package.json" "$STATE_DIR/version.json" <<'PY' 2>/dev/null
626
- import json, sys
638
+ if python3 - "$REPO/package.json" "$STATE_DIR/version.json" "$REPO/provenance.json" \
639
+ "$([ -e "$REPO/.git" ] && echo clone || echo package)" <<'PY' 2>/dev/null
640
+ import json, os, sys
627
641
  pkg = json.load(open(sys.argv[1]))
642
+ out = {"version": pkg.get("version"), "releaseDate": pkg.get("releaseDate"),
643
+ "source": sys.argv[4]}
644
+ # Publication provenance rides along ONLY for a package-layout source: in a
645
+ # clone, git itself is the live provenance and a provenance.json on disk is by
646
+ # definition residue (a failed pack's leftover stamp) — ingesting it once let a
647
+ # clone deployment of commit B carry a stale label A into the state marker.
648
+ if sys.argv[4] == "package" and os.path.isfile(sys.argv[3]):
649
+ try:
650
+ prov = json.load(open(sys.argv[3]))
651
+ if prov.get("commit"):
652
+ out["commit"] = prov["commit"]
653
+ if prov.get("dirty"):
654
+ out["dirty"] = True
655
+ except Exception:
656
+ pass
628
657
  with open(sys.argv[2], "w") as f:
629
- json.dump({"version": pkg.get("version"), "releaseDate": pkg.get("releaseDate")}, f)
658
+ json.dump(out, f)
630
659
  f.write("\n")
631
660
  PY
632
661
  then
@@ -656,8 +685,8 @@ verify_present() { if [ -f "$1" ]; then return 0; else log "missing $1"; return
656
685
 
657
686
  cmd_verify() {
658
687
  local fail=0 gp gb
659
- if [ ! -d "$REPO/.git" ] && [ "$(drift_state)" = "drift" ]; then
660
- log "deploy drift: deployed $(deployed_version), package $(source_version) — run: agent-bios install"
688
+ if [ ! -e "$REPO/.git" ] && [ "$(drift_state)" = "drift" ]; then
689
+ log "deploy drift: $(drift_detail) — run: agent-bios install"
661
690
  fail=1
662
691
  fi
663
692
  # Not mode-specific: an author-only guide must be absent from EVERY directory any
@@ -950,14 +979,44 @@ cmd_onboard() {
950
979
  fi
951
980
  DOMAINS_ARG="$sel"; DOMAINS_SET=1
952
981
  log "selection: ${sel:-<core+infra only>}"
953
- cmd_install
982
+ # Subshelled so a failing install can still record its outcome: the launcher's
983
+ # corpus checklist reads `last_apply` from corpus-status.json, and an exit with
984
+ # nothing recorded reads as "nothing happened". cmd_install's shell state stays
985
+ # in the subshell; everything after here uses only top-level globals.
986
+ local apply_rc=0
987
+ # NOT `( cmd_install ) || apply_rc=$?`: a command list that tests the subshell
988
+ # suppresses errexit for everything inside it, so an unguarded failure in
989
+ # cmd_install would run through to a zero return (probed on bash 3.2/5.x).
990
+ # Toggling -e around a STANDALONE subshell keeps errexit live inside while the
991
+ # outer shell survives to record the outcome.
992
+ set +e
993
+ ( set -e; cmd_install )
994
+ apply_rc=$?
995
+ set -e
996
+ if [ "$apply_rc" -ne 0 ]; then
997
+ python3 "$REPO/compose/corpus-state.py" record-apply \
998
+ --requested "$sel" --outcome install_failed >/dev/null 2>&1 || true
999
+ exit "$apply_rc"
1000
+ fi
954
1001
  log ""
955
1002
  log "Activation canary (proves the bundle loads in a live session)..."
956
1003
  if [ "$DRY_RUN" = 1 ]; then info "[dry-run] skip canary probe"; return; fi
957
- bash "$REPO/compose/canary.sh" || {
1004
+ local canary_rc=0
1005
+ bash "$REPO/compose/canary.sh" || canary_rc=$?
1006
+ if [ "$canary_rc" -ne 0 ]; then
1007
+ # rc=1 is a real probe that answered "not loading"; rc=3 is "could not
1008
+ # probe" (no CLI/auth). Both leave the apply unproven, so both record as
1009
+ # canary_failed — the tail carries which, so the panel's loud line does
1010
+ # not send the operator to debug imports over an auth problem.
1011
+ python3 "$REPO/compose/corpus-state.py" record-apply \
1012
+ --requested "$sel" --outcome canary_failed \
1013
+ --error-tail "canary exit $canary_rc$([ "$canary_rc" = 3 ] && printf ' (could not probe)')" \
1014
+ >/dev/null 2>&1 || true
958
1015
  log "ONBOARDING INCOMPLETE: the bundle is installed but not loading — fix the cause above and re-run: agent-bios verify"
959
1016
  exit 1
960
- }
1017
+ fi
1018
+ python3 "$REPO/compose/corpus-state.py" record-apply \
1019
+ --requested "$sel" --outcome applied >/dev/null 2>&1 || true
961
1020
  # The prune is authorized by the canary's proof, and cmd_install ran before the canary existed
962
1021
  # for this bundle rev — so it kept everything. Now that loading is proven, run it for real.
963
1022
  migrate_learnings
@@ -982,30 +1041,131 @@ sys.exit(0) if v is None else print(v)' "$1" "$2" 2>/dev/null
982
1041
  deployed_version() { json_field "$STATE_DIR/version.json" version; }
983
1042
  source_version() { json_field "$REPO/package.json" version; }
984
1043
 
1044
+ json_true() { # exit 0 iff $2 in $1 is JSON true — json.dump writes '"k": true'
1045
+ # with a space, so byte-matching a compact spelling read the real writer's
1046
+ # output as false forever.
1047
+ [ -f "$1" ] || return 1
1048
+ python3 -c 'import json,sys
1049
+ try:
1050
+ v=json.load(open(sys.argv[1])).get(sys.argv[2])
1051
+ except Exception:
1052
+ sys.exit(1)
1053
+ sys.exit(0 if v is True else 1)' "$1" "$2" 2>/dev/null
1054
+ }
1055
+
1056
+ current_commit() { # the CURRENT side's commit: git HEAD in a clone, the stamp in a package
1057
+ # A clone's provenance is its live HEAD — a provenance.json on its disk is a
1058
+ # failed pack's residue, and reading it once let a stale label A match a
1059
+ # checkout sitting at B.
1060
+ if [ -e "$REPO/.git" ]; then
1061
+ git -C "$REPO" rev-parse HEAD 2>/dev/null || true
1062
+ else
1063
+ json_field "$REPO/provenance.json" commit 2>/dev/null || true
1064
+ fi
1065
+ }
1066
+
1067
+ current_dirty() { # exit 0 iff the current side is a dirty-stamped PACKAGE artifact
1068
+ [ ! -e "$REPO/.git" ] && json_true "$REPO/provenance.json" dirty
1069
+ }
1070
+
985
1071
  drift_state() { # prints: match | drift | unknown
986
- local d s
1072
+ local d s dc sc
987
1073
  d="$(deployed_version)" || { echo unknown; return; }
988
1074
  s="$(source_version)" || { echo unknown; return; }
989
1075
  [ -n "$d" ] && [ -n "$s" ] || { echo unknown; return; }
990
- [ "$d" = "$s" ] && echo match || echo drift
1076
+ [ "$d" = "$s" ] || { echo drift; return; }
1077
+ # Same VERSION is not same SOURCE: two locally packed tarballs keep one
1078
+ # version across different commits (npm pack is an accommodated flow), and
1079
+ # the commit was persisted into version.json exactly so this comparison
1080
+ # could read it. Judged only when both sides carry one — a clone deploy
1081
+ # stamps no commit, and half a comparison is none.
1082
+ dc="$(json_field "$STATE_DIR/version.json" commit 2>/dev/null || true)"
1083
+ sc="$(current_commit)"
1084
+ # Dirt is judged BEFORE the commit comparison: a dirty artifact's bytes are
1085
+ # described by no commit, so differing commit labels prove nothing about
1086
+ # differing payloads — unknown, not drift, or npm-layout verify fails on a
1087
+ # mismatch nobody proved.
1088
+ if json_true "$STATE_DIR/version.json" dirty || current_dirty; then
1089
+ echo unknown; return
1090
+ fi
1091
+ if [ -n "$dc" ] && [ -n "$sc" ] && [ "$dc" != "$sc" ]; then
1092
+ echo drift; return
1093
+ fi
1094
+ # UNPROVABLE is not drift: an unbound side (npm layout without a commit — a
1095
+ # lifecycle-disabled pack, or the hook's materialized tree) and a DIRTY side
1096
+ # (bytes no commit describes; parsed via json_true, never byte-matched,
1097
+ # because json.dump's spacing defeated the compact grep) both make the
1098
+ # correspondence unprovable. Drift is a PROVEN mismatch and makes verify
1099
+ # fail; branding the unprovable as drift made D-0050's accepted install path
1100
+ # fail its own verification. Clone deployments stay version-compared — git
1101
+ # itself is their live provenance, and they persist no commit by design.
1102
+ if [ "$(json_field "$STATE_DIR/version.json" source 2>/dev/null || true)" = "package" ] \
1103
+ && [ -z "$dc" ]; then
1104
+ echo unknown; return
1105
+ fi
1106
+ if [ ! -e "$REPO/.git" ] && [ -z "$sc" ]; then
1107
+ echo unknown; return
1108
+ fi
1109
+ echo match
1110
+ }
1111
+
1112
+ drift_detail() { # one line naming WHAT drifted: versions, or commits behind one version
1113
+ local d s dc sc
1114
+ d="$(deployed_version)"; s="$(source_version)"
1115
+ dc="$(json_field "$STATE_DIR/version.json" commit 2>/dev/null || true)"
1116
+ sc="$(current_commit)"
1117
+ if [ "$d" = "$s" ] && { json_true "$STATE_DIR/version.json" dirty || current_dirty; }; then
1118
+ printf 'a DIRTY-packed artifact is involved — its bytes are described by no commit; a clean-pack redeploy restores a provable match'
1119
+ elif [ "$d" = "$s" ] && [ -n "$dc" ] && [ -n "$sc" ] && [ "$dc" != "$sc" ]; then
1120
+ printf 'deployed commit %s but this source is at %s (both version %s)' "$dc" "$sc" "$s"
1121
+ elif [ "$(json_field "$STATE_DIR/version.json" source 2>/dev/null || true)" = "package" ] \
1122
+ && [ -z "$dc" ]; then
1123
+ printf 'the deployed artifact is UNBOUND (packed with lifecycle scripts disabled) — its bytes are proven by no commit; a bound redeploy restores a provable match'
1124
+ elif [ ! -e "$REPO/.git" ] && [ -z "$sc" ]; then
1125
+ printf 'this package is UNBOUND (no provenance) — its bytes are proven by no commit'
1126
+ else
1127
+ printf 'deployed %s but this package is %s' "$d" "$s"
1128
+ fi
991
1129
  }
992
1130
 
993
1131
  cmd_status() {
994
1132
  local version p
995
- if [ -d "$REPO/.git" ]; then
1133
+ if [ -e "$REPO/.git" ]; then
996
1134
  version="$(git -C "$REPO" describe --tags --always --dirty 2>/dev/null || git -C "$REPO" rev-parse --short HEAD 2>/dev/null || echo '?')"
997
1135
  log "agent-bios (git clone: $version)"
998
1136
  elif [ -f "$REPO/package.json" ]; then
999
1137
  version="$(node -e "try{process.stdout.write(require('$REPO/package.json').version)}catch(e){process.stdout.write('?')}" 2>/dev/null || echo '?')"
1000
1138
  log "agent-bios (npm package: $version)"
1139
+ if [ -f "$REPO/provenance.json" ]; then
1140
+ local built_from
1141
+ # Tolerated, never trusted: a malformed stamp used to kill status via
1142
+ # set -e mid-output, and a well-formed one with no commit passed in
1143
+ # silence — both are UNBOUND states, not absences of the question.
1144
+ built_from="$(json_field "$REPO/provenance.json" commit 2>/dev/null || true)"
1145
+ if [ -n "$built_from" ]; then
1146
+ info "built from commit ${built_from}$(
1147
+ grep -q '"dirty":true' "$REPO/provenance.json" && printf ' (DIRTY tree at pack time)')"
1148
+ else
1149
+ info "UNBOUND provenance.json is unreadable or names no commit — this artifact is not usably bound"
1150
+ fi
1151
+ else
1152
+ # The absence is the finding: D-0050 accepts lifecycle-disabled packs as
1153
+ # residual BECAUSE this line makes an unbound artifact visible — a silent
1154
+ # skip here would unmake that decision's premise.
1155
+ info "UNBOUND no provenance stamp — packed with lifecycle scripts disabled, or a pre-provenance release; this artifact names no commit"
1156
+ fi
1001
1157
  else
1002
1158
  log "agent-bios"
1003
1159
  fi
1004
1160
  log " source: $REPO"
1005
1161
  case "$(drift_state)" in
1006
1162
  match) info "deployed version $(deployed_version) (matches this package)" ;;
1007
- drift) log "DRIFT deployed $(deployed_version) but this package is $(source_version) — run: agent-bios install" ;;
1008
- unknown) info "deployed version unknown (no state marker yet)" ;;
1163
+ drift) log "DRIFT $(drift_detail) — run: agent-bios install" ;;
1164
+ unknown) if [ -f "$STATE_DIR/version.json" ]; then
1165
+ info "correspondence unprovable: $(drift_detail)"
1166
+ else
1167
+ info "deployed version unknown (no state marker yet)"
1168
+ fi ;;
1009
1169
  esac
1010
1170
  for p in "$CLAUDE_DIR/CLAUDE.md" "$CODEX_DIR/AGENTS.md" "$BIN_DIR/agent-launch" \
1011
1171
  "$LAUNCH_DIR/profiles.toml" "$LAUNCH_DIR/shell.zsh"; do
@@ -1015,7 +1175,7 @@ cmd_status() {
1015
1175
  }
1016
1176
 
1017
1177
  cmd_update() {
1018
- if [ -d "$REPO/.git" ]; then
1178
+ if [ -e "$REPO/.git" ]; then
1019
1179
  log "Updating from git..."
1020
1180
  run git -C "$REPO" pull --ff-only
1021
1181
  cmd_install
@@ -1067,7 +1227,7 @@ EOF
1067
1227
  # than naming none: a clone can roll the corpus back to a registered mining window, while an
1068
1228
  # npm install has no git history to read and rolls the whole package back by version instead.
1069
1229
  # Derived from which install this is, for the same reason --with names is.
1070
- if [ -d "$REPO/.git" ]; then
1230
+ if [ -e "$REPO/.git" ]; then
1071
1231
  printf '\nRecover: python3 %s/compose/corpus-state.py list, then rollback --version V\n' "$REPO"
1072
1232
  else
1073
1233
  printf '\nRecover: npm install -g agent-bios@<older-version> && agent-bios install\n'