[3.2.0] — 2026-05-18
Architecture release: state store + service registry + golden tests + Go scaffolding.
Closes the v3.2.0 milestone (58 REQ-IDs across 7 phases, 35/35 plans complete).
v3.2.0 is infrastructure code only — zero new container images, zero new
daemons, zero new product features. Net diff ~+3500 / −500 LOC. Driven by
AGmind-Autofix-Architecture-Spec-v1.0.2 §9.2 (deferred from v3.1.2 hotfix).
The release lands four substrates that future milestones (v3.3+, v4.0 Go port)
build on: versioned state store at /var/lib/agmind/state/, declarative service
registry at templates/services/registry.yaml with build-time codegen, byte-exact
golden tests under tests/golden/, and namespace-reserving Go scaffolding placeholders.
Three High findings deferred from v3.1.2 close in this milestone: HEALTH-02B
(resolver consolidation), ENV-PARSE-01 (legacy grep|cut env parser migration),
DUPLICATION-01 (lib/X.sh ↔ scripts/X.sh reconcile).
Target platform is unchanged: DGX Spark (GB10, aarch64), LAN profile only.
Added — Architecture
State store substrate (Phases 11 + 14):
- STATE-01 —
/var/lib/agmind/state/directory created on install with0700 root:rootperms. - STATE-02 —
lib/state.shAPI:state_get/state_set/state_get_secret/state_set_secretwithflockper-file locking. - STATE-03 — Schema versioning via
${STATE_DIR}/schema_versiontext file (integer, monotonic). - STATE-04 — Migration framework:
lib/migrations.shrunner + discretelib/migrations/NNN-<name>.shscripts. - STATE-05 — First migration
001-initial.shcopies legacy*.preservedfiles into versioned state. - STATE-06 —
agmind upgrade --checkreports current schema, pending migrations, and config diff vsversions.env. - STATE-07 —
agmind upgrade --applyruns pending migrations atomically (flock+ temp-then-rename + tar-backup). - STATE-08 —
agmind upgrade --rollback <schema_version>restores from the auto-backup tarball. - STATE-09 — ADR-0011 documents state store architecture decisions.
- STATE-10 — Integration test
test_upgrade_v3_1_2_to_v3_2_0.shagainst a real v3.1.2 baseline. - STATE-11 — Consumer migration:
lib/config.sh, compose renderers, CLI commands now read secrets viastate_get_secret(not direct.envparsing). Closes BACKUP-01.
Service registry (Phase 12):
- REG-01 —
templates/services/registry.yamlis the single source of truth for the service catalog. - REG-02 —
lib/registry.shdual-backend API (yq + python3+PyYAML fallback for airgapped hosts). - REG-03 — Build-time codegen
lib/_registry.indexed.sh(fast bash assoc-arrays, generated artifact). - REG-04 —
lib/service-map.sh::SERVICE_GROUPSderives from registry, not hand-curated. - REG-05 —
lib/service-map.sh::ALL_COMPOSE_PROFILESderives from registry. - REG-06 —
lib/service-map.sh::NAMED_PROFILE_EXPANSIONderives from registry. - REG-07 —
tests/compose/test_registry_compose_parity.shenforces 1:1 registry ↔ compose match. - REG-08 —
tests/compose/test_no_hardcoded_service_lists.shforbids new hand-edited lists. - REG-09 — ADR-0012 documents the registry schema + codegen pipeline + drift-prevention strategy.
Golden tests + lint + mock infra (Phase 13):
- TEST-01 —
tests/golden/directory structure:inputs/,expected/,scenarios.list. - TEST-02 — 5 baseline scenarios:
minimal_lan,full_lan,rag_milvus,ragflow,cluster_peer. - TEST-03 — Per-scenario byte-exact diff:
.env, compose, nginx.conf, monitoring configs. - TEST-04 —
generate_random_nameddeterministic mode underAGMIND_TEST_SEED(name-based, not counter-based). - TEST-05 —
tests/lint/LANDMINES.mdcodifies project "learned the hard way" invariants as machine-readable patterns. - TEST-06 —
tests/unit/test_golden_no_known_landmines.shenforces LANDMINES against rendered configs. - TEST-07 —
make golden-updatedocumented; commit-msggolden-accept-reason: <text>trailer required. - TEST-08 —
tests/mocks/README.mddocuments PATH-override mock pattern + 28-mock inventory. - TEST-09 — Pre-commit hooks added: golden-update guard + ASCII-only-bash (manual stage).
- TEST-10 — CI lane
golden-testsmatrix (5 scenarios parallel) runs on every push.
Go migration scaffolding — zero Go code (Phase 15):
- GO-01 —
cmd/agmind/.gitkeepreserves the binary namespace. - GO-02 —
internal/.gitkeepreserves the internal-packages namespace. - GO-03 —
docs/ROADMAP-GO.mddocuments Stage 0.5/0.7 (already shipped) plus Stage 1-6 plan. - GO-04 — ADR-0010 captures Go migration intent + equivalence-proof requirements + arm64-only enforcement.
- GO-05 — ADR-0013 captures the single Go binary
cmd/agmind/decision withinternal/packages (Q-07). - GO-06 — README mentions Go scaffolding + "no Go code in v3.2.0" disclaimer (EN + RU).
Added — CLI surface
agmind upgrade --check— read-only schema + pending-migrations report (STATE-06).agmind upgrade --apply— atomic migration with auto-backup tarball (STATE-07).agmind upgrade --rollback <schema_version>— restore from auto-backup (STATE-08).
Changed — Refactors
HEALTH-02B resolver consolidation (Phase 14):
- RESOLVER-01 —
lib/health.sh::resolve_active_servicesreplacesget_service_listplus ad-hoc compose detection. - RESOLVER-02 — Resolver reads from the service registry (REG-02 path); no compose round-trip in the hot path.
- RESOLVER-03 — Existing 65+ health tests pass against the new resolver via a thin backward-compat alias.
- RESOLVER-04 —
tests/unit/test_resolve_active_services.shadds 14 test cases / 43 assertions.
ENV-PARSE-01 migration (Phase 14, 73 callsites across 7 files):
- ENV-03b — Canary migration: 3 callsites in
lib/health.sh::_resolve_active_services_uncached(VECTOR_STORE/LLM_PROVIDER/EMBED_PROVIDER). - ENV-03c — Bulk sweep: 41 (boolean/enum) + 3 (numeric) + 29 (secrets) callsites = 73 total across
lib/{health,compose,authelia,openwebui,config,restore}.sh+install.sh+scripts/agmind.sh.
Dormant helpers shipped in Phase 10 — activated by ENV-03b / ENV-03c migration above:
- ENV-01 —
lib/common.sh::_env_get(source-based reader for boolean/enum toggles where bash expansion is desired).- ENV-02 —
lib/common.sh::_env_get_raw(awk byte-exact reader for secrets and literal values).- ENV-03 — Lint gate
tests/lint/test_no_legacy_env_parse.shforbids newgrep ^X= | cut -d=patterns.- ENV-04 —
tests/unit/test_env_get.shcovers all edge cases (#-in-value, escaped quotes, missing trailing newline, multiline heredocs).- ENV-05 —
docs/env-parsing.mddocuments the migration recipe + when to use_env_getvs_env_get_raw.
DUPLICATION-01 closure (Phase 14):
- DUP-01 —
scripts/health.sh+scripts/detect.share verified symlinks to../lib/X.sh. - DUP-02 —
docs/lib-scripts-pairs.mdinventory documents 4 pairs across 3 types (symlink × 2, justified-divergence × 2). - DUP-03 —
install.sh::_copy_runtime_filesusescp -P(preserves symlinks; previously converted them to regular files). - DUP-04 —
tests/compose/test_lib_scripts_parity.shCI gate enforces per-row contract (symlink target / byte-identity / justified-divergence). - DUP-05 — Gate fails on any byte-divergence or symlink-target move.
Breaking Changes
1. State store at /var/lib/agmind/state/
What changed: Fresh installs initialize a versioned state directory under
/var/lib/agmind/state/ with schema_version=1. Upgrades from v3.1.x trigger
the agmind upgrade --check flow that detects the missing directory and
migrates legacy .preserved files (n8n_encryption_key, surrealdb_password,
portainer_agent_secret) plus ${INSTALL_DIR}/docker/.env secrets into the
versioned namespace via migration 001-initial.sh.
Why: Closes BACKUP-01 — the v3.1.x regression where re-running install.sh
on an existing host regenerated secrets (DB password, JWT keys, etc.) without
preserving the existing Postgres volume's hashes, breaking auth on next boot.
Rollback: Restore the auto-backup tarball:
sudo tar -xzf /var/lib/agmind/state/state.bak.<timestamp>/state.tar.gz -C /
git checkout v3.1.2
sudo bash install.shThe migration is non-destructive of the legacy .preserved files and legacy
${INSTALL_DIR}/docker/.env — both remain in place after the migration for one
full release cycle.
2. ENV-PARSE-01 semantics
What changed: All legacy grep ^X=... | cut -d= patterns in lib/*.sh
and install.sh are now migrated to _env_get (source-based, for default
cases) or _env_get_raw (awk byte-exact, for secrets and literal cases). The
migration spans 73 callsites across 7 source files (lib/{health,compose,authelia,openwebui,config,restore}.sh,
install.sh, scripts/agmind.sh) plus the new lint gate tests/lint/test_no_legacy_env_parse.sh
forbids new occurrences.
Why: The old grep|cut pattern matched KEY=value literally but silently
truncated values containing # outside quotes, escaped quotes, multiline
heredocs, and missing-trailing-newline edge cases. _env_get_raw (awk-based,
no shell interpretation) preserves byte-exact secret values; _env_get
(source-based) is appropriate for boolean/enum toggles where bash expansion
is desired.
Rollback: git revert <ENV-MIGRATION-COMMITS> (Phase 14 plans 14-03 through
14-06). Behavior is byte-identical for valid .env files; only error reporting
and edge-case handling differ. Golden tests (TEST-01..03) confirm byte-identical
rendered .env pre/post migration.
3. Service registry codegen artifact
What changed: lib/_registry.indexed.sh is now a generated file —
regenerate via make registry-codegen whenever templates/services/registry.yaml
changes. The CI registry-verify gate fails on drift between registry.yaml
and the generated _registry.indexed.sh.
Why: Phase 12 promotes service definitions from 5 hand-edited bash
assoc-arrays in lib/service-map.sh to a single YAML source of truth. Hand-editing
_registry.indexed.sh directly is forbidden (file header declares
# DO NOT HAND-EDIT — generated from templates/services/registry.yaml). The
codegen step locks down the single source of truth and prevents
PROFILES-ALL-01-class regressions.
Rollback: git checkout v3.1.2 -- lib/service-map.sh (restore hand-edited
arrays) + remove templates/services/registry.yaml + revert
install.sh::_copy_runtime_files whitelist additions. Not recommended —
v3.1.2 hand-maintained service lists are the regression Phase 12 closed.
Stay on v3.2.0 and edit registry.yaml instead.
Internal — Tooling
Documentation + release-cut substrate (Phase 16, this phase):
- DOCS-01 — This CHANGELOG v3.2.0 entry: all 58 REQ-IDs enumerated with one-line descriptions, three Breaking Changes (state store / ENV parser / registry codegen) with What/Why/Rollback, footer REQ category → Phase mapping.
- DOCS-02 —
docs/adr/INDEX.mdupdated with ADR-0010..0013 rows; sentinel-marker bootstrap. - DOCS-03 —
scripts/generate-adr-index.py+make adr-index/make adr-index-checkauto-regenerate the INDEX table between sentinel markers; pre-commit hook + CI job enforce drift gate. - DOCS-04 —
README.md/README.ru.mdupdated with v3.2.0 architecture overview, Go scaffolding disclaimer, link todocs/ROADMAP-GO.md. - DOCS-05 —
release-manifest.jsonpublished (v3.2.0version, REQ-ID list, commit SHA, release-date single source of truth). - DOCS-06 — Version bumps across
install.sh/lib/common.sh::AGMIND_VERSION/templates/versions.env/ README badges, all derived fromrelease-manifest.json(D-18 single-source-of-truth rule).
Build + CI surface:
make adr-index/make adr-index-check— auto-regeneratedocs/adr/INDEX.md
table between sentinel markers (DOCS-03, this phase).make golden-test/make golden-update/make golden-update-all/
make landmines-check/make landmines-sync— golden-test surface (Phase 13).make registry-codegen/make registry-verify— registry codegen + drift gate (Phase 12).- Pre-commit hooks: 16 total — 14 inherited (shellcheck, yamllint, gitleaks,
markdownlint, standard hygiene) + 2 added in Phase 13 (golden-update-guard
commit-msg hook, ascii-only-bash manual-stage hook) + 1 added in Phase 16
(adr-index local hook). - CI workflow
.github/workflows/test.ymljobs:syntax,shellcheck,
unit-tests(amd64),unit-tests-arm64,image-tags,manifest-consistency,
adr-index-check(new),trivy,golden-tests(matrix × 5),
golden-accept-reason-check.