Make the environment-variable surface derive from the framework's real struct tags instead of anyone maintaining it by hand. Adds get_config_env (the truth per module), check_env (drift detection for a .env/.env.example), a config.unknown-env-var rule, and switches get_scaffold to generate .env.example from the index. Also folds in the scaffold-symbol fixes staged as v1.0.1 (never tagged), so the generated scaffold compiles against einherjar v1.0.0.
Type of change
New feature — non-breaking addition of functionality
Bug fix — non-breaking change that resolves an issue
Documentation update
Description
Problem. The v1.0.0 .env.example and the scaffold symbols were hand-authored and drifted from the framework: it shipped EINHERJAR_PG_DATABASE (real: _PG_NAME), EINHERJAR_SERVER_ADDR (real: _HOST/_PORT), and typed loggers as the non-existent logz.Logger. The reconciliation showed the real cause — an env var is only live if its struct is composed and parsed — and that the index already carries every real tag (index.Field.Tag), unused.
Solution.
internal/envspec — parses the indexed struct tags into {name, module, struct, field, required, default}. One source of truth for the three consumers below.
get_config_env — lists the real env vars for a module (or all).
check_env — flags unknown EINHERJAR_* names, required vars missing for the composed modules, and dead vars (set for an uncomposed module).
config.unknown-env-var — a validate_snippet rule that rejects an env:"EINHERJAR_*" tag the framework doesn't declare; the valid-name set is injected from the index at startup, so the rule can't drift.
get_scaffold — .env.example is now derived (required vars uncommented with a dev value; defaulted vars commented with their default), and the scaffold composes logz.Config so EINHERJAR_LOG_LEVEL/_JSON are live and documented instead of silently ignored (log format is now env-driven).
wire builtin — routes the incremental "compose a component later" flow to get_config_env/check_env and spells out framework EINHERJAR_* vs app-owned APP_* (JWT is the app-owned example).
Fixes (was v1.0.1):logging.Logger, postgres.Provider, the real env tags, and the trimmed health hook.
Version → v1.1.0.
Test plan
go build ./... and go test ./... (mcp) — new suites for envspec, the env tools (against the real data/index.json), and the rule
Generated the scaffold to real files and go build ./... against local einherjar v1.0.0 modules → clean (exit 0)
Runtime-verified config.Load() parses the composed logz.Config (EINHERJAR_LOG_LEVEL=DEBUG → slog.LevelDebug, EINHERJAR_LOG_JSON=true)
Rendered .env.example inspected: correct names, required uncommented with dev values, defaults commented, no EINHERJAR_SERVER_CORS_ORIGINS (uncomposed) and no _PG_DATABASE/_SERVER_ADDR
Redeploy and exercise get_config_env / check_env / get_scaffold from a live MCP client
## Summary
Make the environment-variable surface **derive from the framework's real struct tags** instead of anyone maintaining it by hand. Adds `get_config_env` (the truth per module), `check_env` (drift detection for a `.env`/`.env.example`), a `config.unknown-env-var` rule, and switches `get_scaffold` to generate `.env.example` from the index. Also folds in the scaffold-symbol fixes staged as v1.0.1 (never tagged), so the generated scaffold compiles against einherjar v1.0.0.
---
## Type of change
- [x] New feature — non-breaking addition of functionality
- [x] Bug fix — non-breaking change that resolves an issue
- [x] Documentation update
---
## Description
**Problem.** The v1.0.0 `.env.example` and the scaffold symbols were hand-authored and drifted from the framework: it shipped `EINHERJAR_PG_DATABASE` (real: `_PG_NAME`), `EINHERJAR_SERVER_ADDR` (real: `_HOST`/`_PORT`), and typed loggers as the non-existent `logz.Logger`. The reconciliation showed the real cause — an env var is only live if its struct is composed and parsed — and that the index already carries every real tag (`index.Field.Tag`), unused.
**Solution.**
- **`internal/envspec`** — parses the indexed struct tags into `{name, module, struct, field, required, default}`. One source of truth for the three consumers below.
- **`get_config_env`** — lists the real env vars for a module (or all).
- **`check_env`** — flags unknown `EINHERJAR_*` names, required vars missing for the composed modules, and dead vars (set for an uncomposed module).
- **`config.unknown-env-var`** — a `validate_snippet` rule that rejects an `env:"EINHERJAR_*"` tag the framework doesn't declare; the valid-name set is injected from the index at startup, so the rule can't drift.
- **`get_scaffold`** — `.env.example` is now derived (required vars uncommented with a dev value; defaulted vars commented with their default), and the scaffold **composes `logz.Config`** so `EINHERJAR_LOG_LEVEL`/`_JSON` are live and documented instead of silently ignored (log format is now env-driven).
- **`wire` builtin** — routes the incremental "compose a component later" flow to `get_config_env`/`check_env` and spells out framework `EINHERJAR_*` vs app-owned `APP_*` (JWT is the app-owned example).
- **Fixes (was v1.0.1):** `logging.Logger`, `postgres.Provider`, the real env tags, and the trimmed health hook.
Version → v1.1.0.
---
## Test plan
- [x] `go build ./...` and `go test ./...` (mcp) — new suites for `envspec`, the env tools (against the real `data/index.json`), and the rule
- [x] Generated the scaffold to real files and `go build ./...` against local einherjar v1.0.0 modules → clean (exit 0)
- [x] Runtime-verified `config.Load()` parses the composed `logz.Config` (`EINHERJAR_LOG_LEVEL=DEBUG` → `slog.LevelDebug`, `EINHERJAR_LOG_JSON=true`)
- [x] Rendered `.env.example` inspected: correct names, required uncommented with dev values, defaults commented, no `EINHERJAR_SERVER_CORS_ORIGINS` (uncomposed) and no `_PG_DATABASE`/`_SERVER_ADDR`
- [ ] Redeploy and exercise `get_config_env` / `check_env` / `get_scaffold` from a live MCP client
Minor to v1.1.0. Make the whole environment-variable surface derive from the
indexed component-config tags instead of a hand-maintained list that drifts.
Also folds in the scaffold-symbol fixes staged as v1.0.1 (never tagged); the
generated scaffold compiles clean against einherjar v1.0.0.
internal/envspec (new):
- Parse index struct tags into env vars {name, module, struct, field, required,
default}. One source of truth: ParseTag, ForModule, FindStruct, All, KnownNames.
internal/tools:
- get_config_env: list the real env vars a component config reads (or all).
- check_env: flag unknown EINHERJAR_* names, required vars missing for the
composed modules, and dead vars (set for an uncomposed module).
- get_scaffold: .env.example is now DERIVED from the index — required vars
uncommented with a dev value, defaulted vars commented with their default.
The scaffold now composes logz.Config (Log logz.Config) so EINHERJAR_LOG_*
are live and documented, not hardcoded/ignored (log format is env-driven).
- validate_snippet: inject the real env-var name set into the rules package.
internal/index/builtins (wire conventions):
- Route the incremental "compose a component later" flow to get_config_env /
check_env; distinguish framework EINHERJAR_* from app-owned APP_* (JWT).
- Compose logz.Config in the config + Run() examples, to match the scaffold.
internal/rules:
- config.unknown-env-var (twelfth rule): reject an env:"EINHERJAR_*" struct tag
the framework doesn't declare. No-op until the server injects the name set, so
it never fires on incomplete knowledge.
Fixed (was v1.0.1): scaffold health hook + wire builtin used logz.Logger (real:
contracts/logging.Logger) and postgres.Component (hooks take Provider); env tags
were EINHERJAR_SERVER_ADDR / EINHERJAR_PG_DATABASE (real: _HOST/_PORT / _PG_NAME).
Tests: envspec unit tests; env tools against the real data/index.json; the rule.
Verified by generating the scaffold, building it against local einherjar v1.0.0
(exit 0), and runtime-loading the composed logz.Config (EINHERJAR_LOG_LEVEL=DEBUG
-> slog.LevelDebug, EINHERJAR_LOG_JSON=true). Version bumped to v1.1.0 (badge +
serverVersion). No dependency changes.
Rene Nochebuena
requested review from CoreDevelopers 2026-08-07 16:59:55 -06:00
Rene Nochebuena
requested review from Agents 2026-08-07 16:59:55 -06:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Make the environment-variable surface derive from the framework's real struct tags instead of anyone maintaining it by hand. Adds
get_config_env(the truth per module),check_env(drift detection for a.env/.env.example), aconfig.unknown-env-varrule, and switchesget_scaffoldto generate.env.examplefrom the index. Also folds in the scaffold-symbol fixes staged as v1.0.1 (never tagged), so the generated scaffold compiles against einherjar v1.0.0.Type of change
Description
Problem. The v1.0.0
.env.exampleand the scaffold symbols were hand-authored and drifted from the framework: it shippedEINHERJAR_PG_DATABASE(real:_PG_NAME),EINHERJAR_SERVER_ADDR(real:_HOST/_PORT), and typed loggers as the non-existentlogz.Logger. The reconciliation showed the real cause — an env var is only live if its struct is composed and parsed — and that the index already carries every real tag (index.Field.Tag), unused.Solution.
internal/envspec— parses the indexed struct tags into{name, module, struct, field, required, default}. One source of truth for the three consumers below.get_config_env— lists the real env vars for a module (or all).check_env— flags unknownEINHERJAR_*names, required vars missing for the composed modules, and dead vars (set for an uncomposed module).config.unknown-env-var— avalidate_snippetrule that rejects anenv:"EINHERJAR_*"tag the framework doesn't declare; the valid-name set is injected from the index at startup, so the rule can't drift.get_scaffold—.env.exampleis now derived (required vars uncommented with a dev value; defaulted vars commented with their default), and the scaffold composeslogz.ConfigsoEINHERJAR_LOG_LEVEL/_JSONare live and documented instead of silently ignored (log format is now env-driven).wirebuiltin — routes the incremental "compose a component later" flow toget_config_env/check_envand spells out frameworkEINHERJAR_*vs app-ownedAPP_*(JWT is the app-owned example).logging.Logger,postgres.Provider, the real env tags, and the trimmed health hook.Version → v1.1.0.
Test plan
go build ./...andgo test ./...(mcp) — new suites forenvspec, the env tools (against the realdata/index.json), and the rulego build ./...against local einherjar v1.0.0 modules → clean (exit 0)config.Load()parses the composedlogz.Config(EINHERJAR_LOG_LEVEL=DEBUG→slog.LevelDebug,EINHERJAR_LOG_JSON=true).env.exampleinspected: correct names, required uncommented with dev values, defaults commented, noEINHERJAR_SERVER_CORS_ORIGINS(uncomposed) and no_PG_DATABASE/_SERVER_ADDRget_config_env/check_env/get_scaffoldfrom a live MCP clientMinor to v1.1.0. Make the whole environment-variable surface derive from the indexed component-config tags instead of a hand-maintained list that drifts. Also folds in the scaffold-symbol fixes staged as v1.0.1 (never tagged); the generated scaffold compiles clean against einherjar v1.0.0. internal/envspec (new): - Parse index struct tags into env vars {name, module, struct, field, required, default}. One source of truth: ParseTag, ForModule, FindStruct, All, KnownNames. internal/tools: - get_config_env: list the real env vars a component config reads (or all). - check_env: flag unknown EINHERJAR_* names, required vars missing for the composed modules, and dead vars (set for an uncomposed module). - get_scaffold: .env.example is now DERIVED from the index — required vars uncommented with a dev value, defaulted vars commented with their default. The scaffold now composes logz.Config (Log logz.Config) so EINHERJAR_LOG_* are live and documented, not hardcoded/ignored (log format is env-driven). - validate_snippet: inject the real env-var name set into the rules package. internal/index/builtins (wire conventions): - Route the incremental "compose a component later" flow to get_config_env / check_env; distinguish framework EINHERJAR_* from app-owned APP_* (JWT). - Compose logz.Config in the config + Run() examples, to match the scaffold. internal/rules: - config.unknown-env-var (twelfth rule): reject an env:"EINHERJAR_*" struct tag the framework doesn't declare. No-op until the server injects the name set, so it never fires on incomplete knowledge. Fixed (was v1.0.1): scaffold health hook + wire builtin used logz.Logger (real: contracts/logging.Logger) and postgres.Component (hooks take Provider); env tags were EINHERJAR_SERVER_ADDR / EINHERJAR_PG_DATABASE (real: _HOST/_PORT / _PG_NAME). Tests: envspec unit tests; env tools against the real data/index.json; the rule. Verified by generating the scaffold, building it against local einherjar v1.0.0 (exit 0), and runtime-loading the composed logz.Config (EINHERJAR_LOG_LEVEL=DEBUG -> slog.LevelDebug, EINHERJAR_LOG_JSON=true). Version bumped to v1.1.0 (badge + serverVersion). No dependency changes.