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.
- package/DEPENDENCIES.md +3 -3
- package/README.md +10 -2
- package/claude/CLAUDE.md +4 -41
- package/claude/guides/cli-multi-model-workflow.md +7 -7
- package/claude/guides/coding-staged-workflow.md +51 -49
- package/claude/guides/concept-economy.md +187 -0
- package/claude/guides/documentation-hygiene.md +112 -0
- package/claude/guides/review-request.md +9 -7
- package/claude/guides/session-distill-workflow.md +3 -3
- package/claude/guides/tooling-gotchas.md +10 -0
- package/claude/guides/verification-discipline.md +166 -0
- package/claude/hooks/tooling-gotchas-hook.py +323 -13
- package/codex/AGENTS.md +4 -41
- package/codex/guides/cli-multi-model-workflow.md +7 -7
- package/codex/guides/coding-staged-workflow.md +51 -49
- package/codex/guides/concept-economy.md +187 -0
- package/codex/guides/documentation-hygiene.md +112 -0
- package/codex/guides/review-request.md +9 -7
- package/codex/guides/session-distill-workflow.md +3 -3
- package/codex/guides/tooling-gotchas.md +10 -0
- package/codex/guides/verification-discipline.md +166 -0
- package/compose/assemble.py +10 -1
- package/compose/check-domains.py +882 -6
- package/compose/domains.json +10 -44
- package/install.sh +176 -16
- package/launch/agent-launch.py +790 -173
- package/launch/agent-launch.toml +56 -107
- package/launch/i18n/en.toml +66 -0
- package/launch/i18n/ja.toml +63 -0
- package/launch/i18n/ko.toml +63 -0
- package/package.json +9 -4
- package/provenance.json +1 -0
- package/wrappers/codex-run.sh +1 -1
- package/claude/hooks/__pycache__/tooling-gotchas-hook.cpython-314.pyc +0 -0
package/compose/domains.json
CHANGED
|
@@ -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": "
|
|
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
|
|
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": "
|
|
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": "
|
|
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
|
-
"
|
|
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
|
|
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"
|
|
626
|
-
|
|
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(
|
|
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 [ ! -
|
|
660
|
-
log "deploy drift:
|
|
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
|
-
|
|
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
|
-
|
|
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" ]
|
|
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 [ -
|
|
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
|
|
1008
|
-
unknown)
|
|
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 [ -
|
|
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 [ -
|
|
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'
|