feat(config): implement Loader + Config against #505 interface + #504 C1 schema (TDD against bash byte-oracle) #527

Closed
opened 2026-07-25 21:03:45 +02:00 by bosun · 1 comment
Owner

Implement internal/config package satisfying the Go interface landed in #505 + the C1 schema landed in #504.

Motivation

Phase 2 continues contract-driven TDD-per-phase per ADR-0009 §3.1. internal/config is one of two Phase 2 packages (sibling: #528 events). Interface (Loader, Config typed) is Surveyor-approved in #505; C1 JSON Schema (config.schema.json) is landed in #504; implementation lands here.

Scope

  • internal/config/*.go — implementation satisfying #505 interface (Loader.Load, Config typed struct matching C1 schema, validation against C1 at load time)
  • Bash byte-oracle: existing bash config parsing (in lib/config.sh or embedded in release-prep.sh / release.sh — verify on-disk before starting) is the behavior-of-record
  • TDD approach (contract-driven per ADR-0009 §3.1):
    1. Test against interface first
    2. Oracle harness case using internal/harness (from #503, reuse the Phase-1 harness vehicle: Go oracleshim + bash dispatcher + PREBUILT BINARY per Engineer's substrate-of-record — NOT go run due to exit-collapse trap)
    3. Implementation to green
  • C1 validation: load-time validation against config.schema.json — parse errors + schema-violation errors have distinct sentinels
  • Fixture corpus at testdata/fixtures/config/ — real release-toolkit.yml shapes from consumer repos + malformed + edge cases

AC

  • internal/config implementation compiles + all #505 interface methods satisfied — verified in PR#530 5c8ec7e; Config completed to full C1 (6 frozen + 5 added fields per Surveyor 4555)
  • C1 schema validation wired at load time — verified: ErrParse additive 3rd sentinel distinct from ErrSchemaViolation via yaml.v3 TypeError-vs-syntax-error discrimination; hand-rolled validation mirrors config_validate byte-faithfully
  • Unit tests green — verified in PR#530 gate run
  • Equivalence-harness cases against bash oracle green — verified: Surveyor reproduced all 4 divergence axes directly (review 4555); intentional Go>bash unknown-key rejection reproduced (bash exit=0 / Go exit=1); RED-by-design divergence case proven non-vacuous by her mutation; traversal self-catch isolated its axis. Milestone-#74 gate pinned as unit test (3 real configs validate unchanged)
  • go vet ./... + golangci-lint run clean — verified: gate own instrument fired, all clean
  • Depends on: #505 (interfaces), #504 (C1 schema), #503 (harness vehicle from Phase 1)
  • Sibling: #528 (events — parallelizable OR sequential Engineer continuation)
  • Adjacent: #337 (feat(ecosystem): broaden version-file basename support — pyproject.toml, Cargo.toml, others) — retro-linked to milestone #74; could fold as scope expansion IF Engineer's judgment on natural fit, or stay separate follow-up
  • Refs ADR-0009 §3.1 (contract-driven TDD), §3.2 C1 (config schema), §3.3 phase 2 row, §5 (fail-loud + trivalent-verdict discipline)

Anchor

Filed 2026-07-25 by Bosun on operator continuation direction for Phase 2 dispatch.

Implement `internal/config` package satisfying the Go interface landed in #505 + the C1 schema landed in #504. ## Motivation Phase 2 continues contract-driven TDD-per-phase per ADR-0009 §3.1. `internal/config` is one of two Phase 2 packages (sibling: [[#528 events]]). Interface (`Loader`, `Config` typed) is Surveyor-approved in #505; C1 JSON Schema (`config.schema.json`) is landed in #504; implementation lands here. ## Scope - `internal/config/*.go` — implementation satisfying #505 interface (Loader.Load, Config typed struct matching C1 schema, validation against C1 at load time) - **Bash byte-oracle**: existing bash config parsing (in `lib/config.sh` or embedded in `release-prep.sh` / `release.sh` — verify on-disk before starting) is the behavior-of-record - **TDD approach** (contract-driven per ADR-0009 §3.1): 1. Test against interface first 2. Oracle harness case using `internal/harness` (from #503, reuse the Phase-1 harness vehicle: Go oracleshim + bash dispatcher + PREBUILT BINARY per Engineer's substrate-of-record — NOT `go run` due to exit-collapse trap) 3. Implementation to green - **C1 validation**: load-time validation against `config.schema.json` — parse errors + schema-violation errors have distinct sentinels - Fixture corpus at `testdata/fixtures/config/` — real `release-toolkit.yml` shapes from consumer repos + malformed + edge cases ## AC - [x] `internal/config` implementation compiles + all `#505` interface methods satisfied — verified in PR#530 5c8ec7e; Config completed to full C1 (6 frozen + 5 added fields per Surveyor 4555) - [x] C1 schema validation wired at load time — verified: ErrParse additive 3rd sentinel distinct from ErrSchemaViolation via yaml.v3 TypeError-vs-syntax-error discrimination; hand-rolled validation mirrors config_validate byte-faithfully - [x] Unit tests green — verified in PR#530 gate run - [x] Equivalence-harness cases against bash oracle green — verified: Surveyor reproduced all 4 divergence axes directly (review 4555); intentional Go>bash unknown-key rejection reproduced (bash exit=0 / Go exit=1); RED-by-design divergence case proven non-vacuous by her mutation; traversal self-catch isolated its axis. Milestone-#74 gate pinned as unit test (3 real configs validate unchanged) - [x] `go vet ./...` + `golangci-lint run` clean — verified: gate own instrument fired, all clean ## Related - Depends on: #505 (interfaces), #504 (C1 schema), #503 (harness vehicle from Phase 1) - Sibling: #528 (events — parallelizable OR sequential Engineer continuation) - Adjacent: #337 (feat(ecosystem): broaden version-file basename support — pyproject.toml, Cargo.toml, others) — retro-linked to milestone #74; could fold as scope expansion IF Engineer's judgment on natural fit, or stay separate follow-up - Refs ADR-0009 §3.1 (contract-driven TDD), §3.2 C1 (config schema), §3.3 phase 2 row, §5 (fail-loud + trivalent-verdict discipline) ## Anchor Filed 2026-07-25 by Bosun on operator continuation direction for Phase 2 dispatch.
bosun closed this issue 2026-07-25 21:38:41 +02:00
Author
Owner

Closed by hand — PR#530 merged as 5c8ec7ef7761b08b18280c3da150d27fed4cb500 on v2/next (fast-forward). All 5 ACs ticked with substrate anchors above.

Substrate landed:

  • internal/config full C1 impl (6 frozen fields + 5 added; hand-rolled validation, no jsonschema lib)
  • Load = config_validate ∪ reject-unknown-keys (one intentional Go>bash divergence; disclosed + RED-by-design harness case verifies)
  • 3rd sentinel ErrParse distinct from ErrSchemaViolation (yaml.v3 KnownFields(true) + TypeError-vs-syntax-error discrimination)
  • Milestone-#74 gate pinned as unit test: 3 real consumer configs validate unchanged under Go Load

substrate-check-design-claim-before-pr discipline fired correctly — Engineer's framing-verify pre-#527 (bus d0a3) caught 4 axes of C1-vs-config_validate divergence BEFORE impl started. Reconcile follow-up filed as #529 (schema-side tighten to match oracle on 3 axes; axis 4 is the intentional Go improvement).

Surveyor S1 (bus 6d88, pass-with-disclosure): 2 frozen Config field-doc enum examples factually wrong (release_type doc says "standard/rolling" but validator accepts node/go/python/multi; publish_mode doc says "draft/publish" but validator accepts draft/immediate). This PR's own validator falsifies them. Doc-correction fold worth taking in #528 — Engineer's call whether to fold at close-time OR bank as forward-note.

Surveyor forward-notes (non-blocking): ErrNotFound sentinel breadth + Skipf-vacuity on #74 gate.

Cross-references:

  • ADR-0009 §3.1 (contract-driven TDD), §3.2 C1, §3.3 phase 2 row, §5 (fail-loud + trivalent-verdict discipline)
  • Depends on: #505 (interfaces), #504 (C1 schema), #503 (harness vehicle from Phase 1)
  • Sibling: #528 (events — Engineer sequential continuation next)
  • Adjacent (retro-linked on #74): #337 (broaden version-file basenames — stays separate per byte-equivalence discipline)
  • Follow-up: #529 (schema reconcile — depends on this landing)

Phase 2 status: 1/2 complete (#527 config). #528 events is Engineer's sequential next.

**Closed by hand** — PR#530 merged as `5c8ec7ef7761b08b18280c3da150d27fed4cb500` on v2/next (fast-forward). All 5 ACs ticked with substrate anchors above. **Substrate landed**: - `internal/config` full C1 impl (6 frozen fields + 5 added; hand-rolled validation, no jsonschema lib) - Load = config_validate ∪ reject-unknown-keys (one intentional Go>bash divergence; disclosed + RED-by-design harness case verifies) - 3rd sentinel `ErrParse` distinct from `ErrSchemaViolation` (yaml.v3 KnownFields(true) + TypeError-vs-syntax-error discrimination) - Milestone-#74 gate pinned as unit test: 3 real consumer configs validate unchanged under Go Load **[[substrate-check-design-claim-before-pr]] discipline fired correctly** — Engineer's framing-verify pre-#527 (bus d0a3) caught 4 axes of C1-vs-config_validate divergence BEFORE impl started. Reconcile follow-up filed as #529 (schema-side tighten to match oracle on 3 axes; axis 4 is the intentional Go improvement). **Surveyor S1** (bus 6d88, pass-with-disclosure): 2 frozen Config field-doc enum examples factually wrong (`release_type` doc says "standard/rolling" but validator accepts node/go/python/multi; `publish_mode` doc says "draft/publish" but validator accepts draft/immediate). This PR's own validator falsifies them. **Doc-correction fold worth taking in #528** — Engineer's call whether to fold at close-time OR bank as forward-note. **Surveyor forward-notes** (non-blocking): ErrNotFound sentinel breadth + Skipf-vacuity on #74 gate. **Cross-references**: - ADR-0009 §3.1 (contract-driven TDD), §3.2 C1, §3.3 phase 2 row, §5 (fail-loud + trivalent-verdict discipline) - Depends on: #505 (interfaces), #504 (C1 schema), #503 (harness vehicle from Phase 1) - Sibling: #528 (events — Engineer sequential continuation next) - Adjacent (retro-linked on #74): #337 (broaden version-file basenames — stays separate per byte-equivalence discipline) - Follow-up: #529 (schema reconcile — depends on this landing) **Phase 2 status**: 1/2 complete (#527 config). #528 events is Engineer's sequential next.
Sign in to join this conversation.
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
frankenbit/release-toolkit#527
No description provided.