kcl-generate: resolve fragment imports and enforce the composition contract #16

Closed
opened 2026-08-06 21:34:00 +00:00 by ginjiruu · 1 comment
Owner

Parent

#14 — Spec: Per-package KCL config fragments — service-owned defaults (db, embeddednats, embeddedfga)

What to build

The config generator stops string-merging an embedded universal schema into an app's schema. Instead it resolves native KCL fragment imports (import toolbelt.<pkg>) through the kcl.mod at build time, so the schema an app authors is exactly the schema that is validated and codegen and runtime can never disagree about which fields exist. This is the mechanism the service packages' own fragments (ticket 3) plug into.

Two composition contracts are enforced at codegen, failing while the app author is still in the editor:

  • A fragment-declared schema may only be composed under its canonical key (db:, nats:, fga:, log:, session:, rebuild:); a mis-named composition (e.g. jetstream:) is rejected. App-authored schemas under any key (e.g. oidc:) stay legal — enforcement keys off fragment provenance, not a closed key list.
  • A field declared without a default must be composed by the app; omitting it fails codegen. The canonical instance is the db fragment's path field (lands with ticket 3).

App-wins-by-not-importing works with no merge-precedence code: an app that defines its own schema and doesn't import the fragment is accepted. Fragment-vs-fragment schema-name collisions are KCL module-level errors. Schema names (NATS, DB, FGA, Log) are preserved for qualified-import composition. The embedded universal Log schema and the tool's -universal flag are removed.

Acceptance criteria

  • A schema.k importing a fragment file resolves the fragment's fields with the fragment's defaults (e.g. the NATS store-dir default) through the full parse → collect → evaluate → render pipeline.
  • A canonical-key violation (jetstream: NATS = NATS {}) fails codegen.
  • A missing required field (no default, not composed) fails codegen; a field with a default composes trivially.
  • App-wins-by-not-importing: an app defining its own schema without importing the fragment is accepted.
  • Two fragments colliding on a schema name surface as an error.
  • The embedded universal Log and the -universal flag are gone; existing generator tests are updated and go test ./... passes in the tools module.
## Parent #14 — Spec: Per-package KCL config fragments — service-owned defaults (db, embeddednats, embeddedfga) ## What to build The config generator stops string-merging an embedded universal schema into an app's schema. Instead it resolves native KCL fragment imports (`import toolbelt.<pkg>`) through the kcl.mod at build time, so the schema an app authors is exactly the schema that is validated and codegen and runtime can never disagree about which fields exist. This is the mechanism the service packages' own fragments (ticket 3) plug into. Two composition contracts are enforced at codegen, failing while the app author is still in the editor: - A fragment-declared schema may only be composed under its canonical key (`db:`, `nats:`, `fga:`, `log:`, `session:`, `rebuild:`); a mis-named composition (e.g. `jetstream:`) is rejected. App-authored schemas under any key (e.g. `oidc:`) stay legal — enforcement keys off fragment provenance, not a closed key list. - A field declared without a default must be composed by the app; omitting it fails codegen. The canonical instance is the db fragment's `path` field (lands with ticket 3). App-wins-by-not-importing works with no merge-precedence code: an app that defines its own schema and doesn't import the fragment is accepted. Fragment-vs-fragment schema-name collisions are KCL module-level errors. Schema names (`NATS`, `DB`, `FGA`, `Log`) are preserved for qualified-import composition. The embedded universal `Log` schema and the tool's `-universal` flag are removed. ## Acceptance criteria - [ ] A schema.k importing a fragment file resolves the fragment's fields with the fragment's defaults (e.g. the NATS store-dir default) through the full parse → collect → evaluate → render pipeline. - [ ] A canonical-key violation (`jetstream: NATS = NATS {}`) fails codegen. - [ ] A missing required field (no default, not composed) fails codegen; a field with a default composes trivially. - [ ] App-wins-by-not-importing: an app defining its own schema without importing the fragment is accepted. - [ ] Two fragments colliding on a schema name surface as an error. - [ ] The embedded universal `Log` and the `-universal` flag are gone; existing generator tests are updated and `go test ./...` passes in the tools module.
Author
Owner

Implemented, verified (go test green in root/tools/example, task check-kcl green), and reviewed via /code-review. Landed in commit f3cbcd0 on master.

Implemented, verified (go test green in root/tools/example, task check-kcl green), and reviewed via /code-review. Landed in commit f3cbcd0 on master.
ginjiruu 2026-08-06 22:42:40 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ginjiruu/toolbelt#16
No description provided.