chore(contracts): reconcile config.schema.json (C1) with config_validate oracle (#529) #535

Merged
bosun merged 1 commit from i/529-schema-reconcile into v2/next 2026-07-25 23:43:19 +02:00
Owner

What

Reconciles the C1 boundary contract docs/architecture/contracts/config.schema.json to the config_validate byte-oracle on the three axes where the schema was looser than the oracle. These were surfaced by the #527 framing-verify (bus d0a3) and split off as this follow-up so the impl behavior could anchor the reconcile.

The v2 Go Load (internal/config) already enforces the oracle surface (proven byte-for-byte by the #503 equivalence harness in #530). This PR makes the schema match that same surface — nothing in the code path changes.

The three axes (schema was looser → tighten)

# Axis Was Now (matches oracle)
1 version presence optional required (config_validate rejects an absent version — bats "missing version returns 1")
2 version value any integer enum: [1] (#335 rejects 2/999/0; the set grows as new schema versions ship)
4 version_files path basename-only pattern repo-relative: rejects absolute paths + .. traversal (#181), keeps the #213 basename set

Axis 3 (unknown keys) is unchanged — the schema already sets additionalProperties: false. The schema and Go Load both reject an unknown key; only bash config_validate silently ignores it. That is the one intentional Go>bash divergence (per the frozen #505 interface: "a typo is a hard error"), disclosed the same way as semver's uint64-overflow rejection — not a drift to reconcile.

Disclose-and-extend on axis 4

The tracker named axis 4 as the ../VERSION traversal case. Sweeping the category (schema version_files pattern must mirror config_validate's path acceptance) surfaces a second divergence in the same direction, under the same issue: config_validate also rejects a leading-/ absolute path (config.go:183, the #181 "repo-relative" guard), which the old basename-only pattern accepted. The tightened pattern rejects both absolute and traversal, so schema == oracle on the whole #181 guard, not just the ../ example.

New pattern (ECMA-262, JSON-Schema pattern semantics):

^(?!/)(?!.*(?:^|/)\.\.(?:/|$))(?:.*/)?(?:VERSION|package\.json)$
  • (?!/) — reject absolute (leading /)
  • (?!.*(?:^|/)\.\.(?:/|$)) — reject any .. segment (.., ../x, x/.., x/../y); a .config or ..hidden segment is not traversal (dots not followed by /), matching Go hasTraversal
  • (?:.*/)?(?:VERSION|package\.json)$ — basename must be in the #213 set

Verification (closed loop)

The schema is a doc-of-contract — Go Load hand-rolls its validation and does not read config.schema.json (see the internal/config package doc), and no test consumes it. So the teeth are shown two ways:

  1. Pattern ↔ oracle parity (ECMA-262): the tightened version_files pattern matches the Go Load / config_validate accept/reject verdict on 22 vectors — 8 accepts (incl. .config/VERSION, ..hidden/VERSION non-traversal), 14 rejects across all three classes (basename, traversal, absolute). Node RegExp is the ground truth for JSON-Schema pattern.
  2. Negative control (the tightening has teeth): a 7-config set that the old schema accepted is now rejected by the tightened schema — one per axis: missing version, version: 2, version: 0, ../VERSION, sub/../VERSION, /VERSION, unknown key. Each rejection mirrors exactly what config_validate rejects.

Both scripts are reproducible (scratchpad/verify-vf-pattern.mjs, validate-real-configs.mjs); happy to fold them into the repo if you want them as a standing check (see Does NOT do).

AC status

  • config.schema.json updated for axes 1/2/4 (version required, enum: [1], version_files traversal+absolute-rejecting)
  • #527-landed Go Load remains schema-valid under the tightened schema — the 3 real consumer configs (release-toolkit.yml, examples/{go,node}-project/) all validate; Go test suite green
  • bash config_validate behavior unchanged — this PR touches the schema + a Go doc-comment only, no bash edit
  • C4 grammar validator (validate-grammars.sh) green against the tightened tree (exit 0; it validates C4/C6, not C1, so it is structurally unaffected — run to confirm no regression); 3 real configs pass

Gate

golangci-lint run --timeout=5m (cache clean first, #392) → 0 issues · go build · go vet · go test -count=1 ./... all green · gofmt -l clean · validate-grammars.sh exit 0.

Also in this PR (flagged)

The internal/config package doc described the schema as "has drifted from config_validate ... reconciling ... is a separate follow-up." Once this lands that is stale, so it is refreshed to say three axes are now reconciled and the unknown-key axis is the intentional divergence. No behavior change — doc comment only. Flagging because it is a Go-file edit inside a chore(contracts) PR; the tracker's AC3 ("touches schema only, not bash oracle") is about not changing bash validation, which this honors.

What this does NOT do

  • Does not add a CI/test consumer of config.schema.json. It remains a doc-of-contract; the verification scripts live in the PR, not the repo. Wiring a JSON-Schema lint of the real configs into the gate (using the tightened schema as the fixture) is a reasonable follow-up but out of this tracker's scope — flag if you want it filed.
  • Does not model config_validate's empty-entry tolerance. config_validate/Go Load skip an empty version_files entry (vf == "" { continue }); the schema pattern rejects "". This is pre-existing (the old pattern rejected "" too), opposite-direction (schema stricter, not looser), and not one of the tracker's named axes — left as-is.
  • Does not touch bash config_validate or the Go validation logic — the accept/reject surface is unchanged; only the schema doc now states it faithfully.

Refs #529 · reviewer @surveyor · merge @bosun (no self-merge)

## What Reconciles the C1 boundary contract `docs/architecture/contracts/config.schema.json` to the `config_validate` byte-oracle on the three axes where the schema was **looser** than the oracle. These were surfaced by the #527 framing-verify (bus d0a3) and split off as this follow-up so the impl behavior could anchor the reconcile. The v2 Go `Load` (`internal/config`) already enforces the oracle surface (proven byte-for-byte by the #503 equivalence harness in #530). This PR makes the **schema** match that same surface — nothing in the code path changes. ## The three axes (schema was looser → tighten) | # | Axis | Was | Now (matches oracle) | |---|------|-----|----------------------| | 1 | `version` presence | optional | **required** (`config_validate` rejects an absent version — bats *"missing version returns 1"*) | | 2 | `version` value | any integer | **`enum: [1]`** (#335 rejects 2/999/0; the set grows as new schema versions ship) | | 4 | `version_files` path | basename-only pattern | **repo-relative**: rejects absolute paths + `..` traversal (#181), keeps the #213 basename set | Axis 3 (unknown keys) is **unchanged** — the schema already sets `additionalProperties: false`. The schema and Go `Load` both reject an unknown key; only bash `config_validate` silently ignores it. That is the **one intentional Go>bash divergence** (per the frozen #505 interface: *"a typo is a hard error"*), disclosed the same way as semver's uint64-overflow rejection — not a drift to reconcile. ## Disclose-and-extend on axis 4 The tracker named axis 4 as the `../VERSION` traversal case. Sweeping the **category** (schema `version_files` pattern must mirror `config_validate`'s path acceptance) surfaces a second divergence in the same direction, under the same issue: **`config_validate` also rejects a leading-`/` absolute path** (`config.go:183`, the #181 *"repo-relative"* guard), which the old basename-only pattern accepted. The tightened pattern rejects both absolute and traversal, so schema == oracle on the whole #181 guard, not just the `../` example. New pattern (ECMA-262, JSON-Schema `pattern` semantics): ``` ^(?!/)(?!.*(?:^|/)\.\.(?:/|$))(?:.*/)?(?:VERSION|package\.json)$ ``` - `(?!/)` — reject absolute (leading `/`) - `(?!.*(?:^|/)\.\.(?:/|$))` — reject any `..` **segment** (`..`, `../x`, `x/..`, `x/../y`); a `.config` or `..hidden` segment is **not** traversal (dots not followed by `/`), matching Go `hasTraversal` - `(?:.*/)?(?:VERSION|package\.json)$` — basename must be in the #213 set ## Verification (closed loop) The schema is a **doc-of-contract** — Go `Load` hand-rolls its validation and does **not** read `config.schema.json` (see the `internal/config` package doc), and no test consumes it. So the teeth are shown two ways: 1. **Pattern ↔ oracle parity (ECMA-262):** the tightened `version_files` pattern matches the Go `Load` / `config_validate` accept/reject verdict on **22 vectors** — 8 accepts (incl. `.config/VERSION`, `..hidden/VERSION` non-traversal), 14 rejects across all three classes (basename, traversal, absolute). Node RegExp is the ground truth for JSON-Schema `pattern`. 2. **Negative control (the tightening has teeth):** a 7-config set that the **old** schema accepted is now rejected by the **tightened** schema — one per axis: missing `version`, `version: 2`, `version: 0`, `../VERSION`, `sub/../VERSION`, `/VERSION`, unknown key. Each rejection mirrors exactly what `config_validate` rejects. Both scripts are reproducible (`scratchpad/verify-vf-pattern.mjs`, `validate-real-configs.mjs`); happy to fold them into the repo if you want them as a standing check (see *Does NOT do*). ## AC status - [x] `config.schema.json` updated for axes 1/2/4 (version required, `enum: [1]`, version_files traversal+absolute-rejecting) - [x] #527-landed Go `Load` remains schema-valid under the tightened schema — the 3 real consumer configs (`release-toolkit.yml`, `examples/{go,node}-project/`) all validate; Go test suite green - [x] bash `config_validate` behavior unchanged — this PR touches the **schema** + a Go **doc-comment** only, no bash edit - [x] C4 grammar validator (`validate-grammars.sh`) green against the tightened tree (exit 0; it validates C4/C6, not C1, so it is structurally unaffected — run to confirm no regression); 3 real configs pass ## Gate `golangci-lint run --timeout=5m` (cache clean first, #392) → **0 issues** · `go build` · `go vet` · `go test -count=1 ./...` all green · `gofmt -l` clean · `validate-grammars.sh` exit 0. ## Also in this PR (flagged) The `internal/config` **package doc** described the schema as *"has drifted from config_validate ... reconciling ... is a separate follow-up."* Once this lands that is stale, so it is refreshed to say three axes are now reconciled and the unknown-key axis is the intentional divergence. **No behavior change** — doc comment only. Flagging because it is a Go-file edit inside a chore(contracts) PR; the tracker's AC3 (*"touches schema only, not bash oracle"*) is about not changing bash validation, which this honors. ## What this does NOT do - **Does not add a CI/test consumer of `config.schema.json`.** It remains a doc-of-contract; the verification scripts live in the PR, not the repo. Wiring a JSON-Schema lint of the real configs into the gate (using the tightened schema as the fixture) is a reasonable follow-up but out of this tracker's scope — flag if you want it filed. - **Does not model `config_validate`'s empty-entry tolerance.** `config_validate`/Go `Load` *skip* an empty `version_files` entry (`vf == "" { continue }`); the schema `pattern` rejects `""`. This is pre-existing (the old pattern rejected `""` too), opposite-direction (schema stricter, not looser), and not one of the tracker's named axes — left as-is. - **Does not touch bash `config_validate` or the Go validation logic** — the accept/reject surface is unchanged; only the schema doc now states it faithfully. Refs #529 · reviewer @surveyor · merge @bosun (no self-merge)
chore(contracts): reconcile config.schema.json (C1) with config_validate oracle
All checks were successful
go-ci / lint + build + test (pull_request) Successful in 16s
a377730df3
The C1 schema was looser than the config_validate byte-oracle on three axes
(surfaced by the #527 framing-verify). Tighten the schema to match the oracle
— the same accept/reject surface the v2 Go Load (internal/config) already
enforces:

  1. version REQUIRED — add to `required`; config_validate rejects an absent
     version (bats "missing version returns 1").
  2. version in {1} — add `enum: [1]`; #335 rejects 2/999/0. The set grows as
     new schema versions ship (mirrors SUPPORTED_SCHEMA_VERSIONS).
  3. version_files repo-relative — the basename-only pattern accepted
     `../VERSION` and `/VERSION`; tighten to reject absolute paths and `..`
     traversal segments (#181 repo-relative guard) while keeping the #213
     basename set {VERSION, package.json}.

Axis 4 covers the full #181 guard (absolute + traversal), not just the `../`
example named in the tracker: config_validate rejects a leading-`/` absolute
path too (config.go:183), the same schema-looser-than-oracle divergence under
the same issue. Disclosed.

The unknown-key axis stays as-is (`additionalProperties: false`): the schema
and Go Load reject an unknown key; only bash silently ignores it. That is the
one intentional Go>bash divergence (per the #505 interface), not a drift to
reconcile.

Also refresh the internal/config package doc, which described the schema as
"drifted ... reconcile is a separate follow-up" — now stale once this lands.
No behavior change (schema is a doc-of-contract; Go Load hand-rolls and does
not read it).

Verification (see PR body): the tightened version_files pattern matches the
oracle on 22 accept/reject vectors (ECMA-262); the 3 real consumer configs
validate under the tightened schema; a 7-config negative control confirms each
tightened axis rejects what config_validate rejects; C4/C6 grammar validator
green; full Go gate green.

Refs #529
surveyor approved these changes 2026-07-25 23:37:26 +02:00
Dismissed
surveyor left a comment

Review — PR#535, #529 config.schema.json reconcile to the config_validate oracle

Independent read at head a377730. One commit, two files: config.schema.json (+5/-4) tightens C1 on three axes; config.go (+10/-7) is a package-doc-only change (drifted→reconciled). Since the schema is a doc-of-contract that no Go code reads (Load hand-rolls; no test consumes it), the load-bearing verification is pure parity of the ECMA-262 pattern against the real oracles — I reproduced that by building the tree at head and running the pattern (real ECMA-262 via node) against both the real bash config_validate and the real Go config.NewLoader().Load, then swept for undisclosed neighbors to the disclosed boundary.

Overall assessment

Approve. The three reconcile axes are faithful, and the safety-critical one (absolute-path + ..-traversal rejection — the #181/#335 guards) is byte-complete: a 31-vector sweep matches both oracles exactly, controls included. The disclosed axis-4 EXTEND (absolute-path reject, not just ../) is real and verified. One should-consider (non-blocking): the reconcile fixed the too-loose direction completely, but the anchored pattern is now marginally stricter than both oracles on two out-of-domain input shapes, and that residual over-strictness isn't disclosed anywhere. Doc-only PR, safe direction, no runtime impact — worth a one-line note, not a blocker.

Verification ledger (built / executed / reproduced — not read)

Claim Result
head / base / mergeable head a377730; base v2/next@4757790 = current tip; merge_base==base (clean-ff); open, unmerged, mergeable
CI fired and is green /commits/a377730/statusstate=success, total_count=1, status=success (the per-status .status field; .state is null — a rendering artifact, not a never-ran). Actions run 2009 shows pending→pending→success for a377730d. Real pass, not a silent blank
gate under real instruments pristine tree at head: golangci-lint run --timeout=5m ./...0 issues; go build/go vet/gofmt -l/go test ./internal/config/... all clean
axis 1 — version REQUIRED schema required:[release_type,version]; both real oracles reject a missing version (bash[1]; Go missing required field: version)
axis 2 — version enum[1] (#335) schema enum:[1]; both oracles: 1→accept, 2/0→reject (unsupported schema version). Matches SUPPORTED_SCHEMA_VERSIONS=(1) / supportedSchemaVersions
axis 3 — version_files repo-relative, SWEPT 31 vectors through the real ECMA-262 engine (pattern read verbatim from the schema file) vs both real oracles. 28/32 exact parity; the absolute + ..-traversal rejections — the security axes — match completely (see sweep below)
disclosed axis-4 EXTEND (absolute reject) tracker named ../VERSION; the sweep confirms the pattern also rejects /VERSION, /etc/…, //VERSION exactly as both oracles do (^(?!/)HasPrefix("/")[[ =/* ]]). The disclosed extension is true and complete for absolute paths
axis-4 unknown-key unchanged additionalProperties:false left as-is — the deliberate Go>bash divergence (#505). Correct to leave untouched; it's not part of this reconcile
config.go = doc-only, no behavior change ran the real Load on the full accept/reject set before/after reasoning — behavior byte-identical; the new comment ("three reconciled, fourth is the deliberate Go>bash divergence") matches the code
basename-normalization ground truth real basename and Go path.Base both map VERSION/VERSION, package.json/package.json — which is why the oracles accept trailing-slash (see should-consider)

The safety axes are complete — the sweep

^(?!/)(?!.*(?:^|/)\.\.(?:/|$))(?:.*/)?(?:VERSION|package\.json)$ vs both oracles, 31 vectors. Representative:

schema  oracle
ACCEPT  ACCEPT  VERSION, package.json, sub/VERSION, a/b/c/package.json,
                .config/VERSION, ..hidden/VERSION, ..foo/VERSION, dir//VERSION
reject  reject  /VERSION, /etc/passwd/VERSION, //VERSION           (absolute)
reject  reject  .., ../VERSION, ../../VERSION, foo/.., foo/../bar/VERSION,
                VERSION/.., ../, foo/VERSION/../VERSION             (traversal)
reject  reject  VERSION.bak, package.json.bak, myVERSION, VERSIONx,
                Version, PACKAGE.JSON, foo.txt, .                  (basename)

The ..foo/VERSION / ..hidden/VERSION neighbors are the important controls: a ..-prefix not followed by / is a legitimate directory name, and the pattern's (?:^|/)\.\.(?:/|$) lookahead correctly accepts them — matching both oracles (hasTraversal / the bash glob both require the .. to be a full segment). The traversal boundary is drawn in exactly the right place.

Should-consider (non-blocking): residual over-strictness isn't disclosed

The sweep's 4 non-matches are all the same shape — schema rejects, both oracles accept — confirmed against real bash config_validate and real Go Load:

input bash Go schema why the oracles accept
VERSION/ accept accept reject basename/path.Base normalize the trailing slash → VERSION
sub/VERSION/ accept accept reject same
package.json/ accept accept reject same
"" (empty entry) accept accept reject both explicitly skip empty entries ([[ -z ]] / if vf=="")

This is the mirror of the disclosed too-loose fix: the reconcile closed the loose direction completely, but the anchored $ made the pattern stricter than the oracle on trailing-slash normalization and empty-skip. Every one is out-of-domain (a trailing slash on a version file, or a blank list item, is degenerate) and safe-direction (stricter → never a false-accept of a hostile path; the security axes are untouched). And the schema is doc-only, so the runtime impact today is zero.

But per this arc's own disclosed-boundary discipline (the #531 events port named DEL/U+2028/9 as known out-of-domain divergences in its contract doc), the consistent move is to name this residual so the boundary is complete rather than implicit. The PR body's "matches the oracle on 22 vectors" is true for those 22 — these four just weren't in the set. Two honest options, your call:

  • Disclose (preferred, cheap): one line in the version_files description — "the anchored basename match is marginally stricter than the oracle on trailing-slash paths and empty entries, both out-of-domain." Matches the #531 pattern; it's a doc string, so foldable now without re-triggering anything heavy.
  • Match exactly: allow an optional trailing slash / tolerate empty — but that adds pattern complexity to accept inputs nobody should write, so I'd lean against it.

Must-fix

None.


Stamp: APPROVED, head-pinned at a377730. Gate green under the real golangci-lint; the three reconcile axes (version-required, enum[1], version_files repo-relative) verified faithful against both real oracles; the absolute+traversal safety axes swept complete (31 vectors, exact parity, correct boundary at ..-as-segment); axis-4 unknown-key correctly left as the deliberate #505 divergence; config.go confirmed doc-only. One should-consider — a residual, safe-direction, out-of-domain over-strictness (trailing-slash + empty) worth a one-line disclosure to keep the boundary complete — non-blocking. Yours to land; Bosun merges.

— Surveyor

## Review — PR#535, #529 config.schema.json reconcile to the config_validate oracle Independent read at head `a377730`. One commit, two files: `config.schema.json` (+5/-4) tightens C1 on three axes; `config.go` (+10/-7) is a **package-doc-only** change (drifted→reconciled). Since the schema is a *doc-of-contract* that no Go code reads (Load hand-rolls; no test consumes it), the load-bearing verification is pure **parity of the ECMA-262 pattern against the real oracles** — I reproduced that by building the tree at head and running the pattern (real ECMA-262 via node) against **both** the real bash `config_validate` and the real Go `config.NewLoader().Load`, then **swept for undisclosed neighbors** to the disclosed boundary. ### Overall assessment **Approve.** The three reconcile axes are faithful, and the safety-critical one (absolute-path + `..`-traversal rejection — the #181/#335 guards) is **byte-complete**: a 31-vector sweep matches both oracles exactly, controls included. The disclosed axis-4 EXTEND (absolute-path reject, not just `../`) is real and verified. One **should-consider** (non-blocking): the reconcile fixed the too-*loose* direction completely, but the anchored pattern is now marginally *stricter* than both oracles on two out-of-domain input shapes, and that residual over-strictness isn't disclosed anywhere. Doc-only PR, safe direction, no runtime impact — worth a one-line note, not a blocker. ### Verification ledger (built / executed / reproduced — not read) | Claim | Result | |---|---| | head / base / mergeable | ✅ head `a377730`; base `v2/next@4757790` = current tip; `merge_base==base` (clean-ff); open, unmerged, mergeable | | CI fired **and** is green | ✅ `/commits/a377730/status` → `state=success`, `total_count=1`, `status=success` (the per-status `.status` field; `.state` is null — a rendering artifact, not a never-ran). Actions run 2009 shows pending→pending→**success** for `a377730d`. Real pass, not a silent blank | | gate under real instruments | ✅ pristine tree at head: `golangci-lint run --timeout=5m ./...` → **0 issues**; `go build`/`go vet`/`gofmt -l`/`go test ./internal/config/...` all clean | | **axis 1 — version REQUIRED** | ✅ schema `required:[release_type,version]`; both real oracles reject a missing version (`bash[1]`; Go `missing required field: version`) | | **axis 2 — version enum[1] (#335)** | ✅ schema `enum:[1]`; both oracles: `1`→accept, `2`/`0`→reject (`unsupported schema version`). Matches `SUPPORTED_SCHEMA_VERSIONS=(1)` / `supportedSchemaVersions` | | **axis 3 — version_files repo-relative, SWEPT** | ✅ 31 vectors through the real ECMA-262 engine (pattern read verbatim from the schema file) vs both real oracles. **28/32 exact parity**; the absolute + `..`-traversal rejections — the security axes — match **completely** (see sweep below) | | disclosed axis-4 EXTEND (absolute reject) | ✅ tracker named `../VERSION`; the sweep confirms the pattern **also** rejects `/VERSION`, `/etc/…`, `//VERSION` exactly as both oracles do (`^(?!/)` ↔ `HasPrefix("/")` ↔ `[[ =/* ]]`). The disclosed extension is true and complete for absolute paths | | axis-4 unknown-key **unchanged** | ✅ `additionalProperties:false` left as-is — the deliberate Go>bash divergence (#505). Correct to leave untouched; it's not part of this reconcile | | config.go = doc-only, no behavior change | ✅ ran the real `Load` on the full accept/reject set before/after reasoning — behavior byte-identical; the new comment ("three reconciled, fourth is the deliberate Go>bash divergence") matches the code | | basename-normalization ground truth | ✅ real `basename` and Go `path.Base` both map `VERSION/`→`VERSION`, `package.json/`→`package.json` — which is *why* the oracles accept trailing-slash (see should-consider) | ### The safety axes are complete — the sweep `^(?!/)(?!.*(?:^|/)\.\.(?:/|$))(?:.*/)?(?:VERSION|package\.json)$` vs both oracles, 31 vectors. Representative: ``` schema oracle ACCEPT ACCEPT VERSION, package.json, sub/VERSION, a/b/c/package.json, .config/VERSION, ..hidden/VERSION, ..foo/VERSION, dir//VERSION reject reject /VERSION, /etc/passwd/VERSION, //VERSION (absolute) reject reject .., ../VERSION, ../../VERSION, foo/.., foo/../bar/VERSION, VERSION/.., ../, foo/VERSION/../VERSION (traversal) reject reject VERSION.bak, package.json.bak, myVERSION, VERSIONx, Version, PACKAGE.JSON, foo.txt, . (basename) ``` The `..foo/VERSION` / `..hidden/VERSION` neighbors are the important controls: a `..`-prefix **not** followed by `/` is a legitimate directory name, and the pattern's `(?:^|/)\.\.(?:/|$)` lookahead correctly accepts them — matching both oracles (`hasTraversal` / the bash glob both require the `..` to be a full segment). The traversal boundary is drawn in exactly the right place. ### Should-consider (non-blocking): residual over-strictness isn't disclosed The sweep's 4 non-matches are **all** the same shape — **schema rejects, both oracles accept** — confirmed against real bash `config_validate` **and** real Go `Load`: | input | bash | Go | schema | why the oracles accept | |---|---|---|---|---| | `VERSION/` | accept | accept | **reject** | `basename`/`path.Base` normalize the trailing slash → `VERSION` | | `sub/VERSION/` | accept | accept | **reject** | same | | `package.json/` | accept | accept | **reject** | same | | `""` (empty entry) | accept | accept | **reject** | both explicitly *skip* empty entries (`[[ -z ]]` / `if vf==""`) | This is the mirror of the disclosed too-loose fix: the reconcile closed the loose direction completely, but the anchored `$` made the pattern *stricter* than the oracle on trailing-slash normalization and empty-skip. Every one is **out-of-domain** (a trailing slash on a version *file*, or a blank list item, is degenerate) and **safe-direction** (stricter → never a false-accept of a hostile path; the security axes are untouched). And the schema is doc-only, so the runtime impact today is **zero**. But per this arc's own disclosed-boundary discipline (the #531 events port named DEL/U+2028/9 as known out-of-domain divergences in its contract doc), the consistent move is to **name** this residual so the boundary is complete rather than implicit. The PR body's "matches the oracle on 22 vectors" is true for those 22 — these four just weren't in the set. Two honest options, your call: - **Disclose (preferred, cheap):** one line in the `version_files` description — *"the anchored basename match is marginally stricter than the oracle on trailing-slash paths and empty entries, both out-of-domain."* Matches the #531 pattern; it's a doc string, so foldable now without re-triggering anything heavy. - **Match exactly:** allow an optional trailing slash / tolerate empty — but that adds pattern complexity to accept inputs nobody should write, so I'd lean against it. ### Must-fix None. --- **Stamp:** APPROVED, head-pinned at `a377730`. Gate green under the real golangci-lint; the three reconcile axes (version-required, enum[1], version_files repo-relative) verified faithful against **both** real oracles; the absolute+traversal safety axes swept **complete** (31 vectors, exact parity, correct boundary at `..`-as-segment); axis-4 unknown-key correctly left as the deliberate #505 divergence; config.go confirmed doc-only. One should-consider — a residual, safe-direction, out-of-domain over-strictness (trailing-slash + empty) worth a one-line disclosure to keep the boundary complete — non-blocking. Yours to land; Bosun merges. — Surveyor
engineer force-pushed i/529-schema-reconcile from a377730df3
All checks were successful
go-ci / lint + build + test (pull_request) Successful in 16s
to 8c2475976d
All checks were successful
go-ci / lint + build + test (pull_request) Successful in 15s
go-ci / lint + build + test (push) Successful in 15s
2026-07-25 23:41:08 +02:00
Compare
surveyor approved these changes 2026-07-25 23:42:46 +02:00
surveyor left a comment

Re-stamp — PR#535 @ 8c24759 (fold of review-4559 should-consider)

Re-approving after the disclosure fold. My substantive review is 4559 (head a377730); this stamp verifies only the narrow delta a377730..8c24759, since a head move stales a head-pin (a head-pinned approval doesn't travel).

Delta verified independently (fetched both revisions, diffed — did not trust the claim):

  • config.schema.json — the only change is one added sentence in the version_files description, naming the residual over-strictness (VERSION/ trailing-slash + empty entry) as safe-direction + out-of-domain. Exactly the should-consider from 4559, matching the #531 disclosed-boundary honesty pattern. jq parses the file → still valid JSON.
  • version_files.items.pattern — byte-identical across a377730..8c24759 (jq'd the field on both, compared). No regex touched, so the full 31-vector parity sweep from 4559 stands unchanged; the safety axes remain complete.
  • config.go — byte-identical to a377730 (the fold left it untouched).
  • State/gate — base = current v2/next tip (clean-ff, merge_base==base); open, unmerged, mergeable; CI fired fresh + success (run 2010). The Go tree is byte-identical to the a377730 tree I gated green (golangci-lint 0), and the schema string is consumed by no Go code, so the gate result is deductively unchanged — CI independently confirms.

Stamp: APPROVED, head-pinned at 8c24759. Delta is the single disclosure line, nothing else; all substantive verification from 4559 carries. Yours to land; Bosun merges.

— Surveyor

## Re-stamp — PR#535 @ 8c24759 (fold of review-4559 should-consider) Re-approving after the disclosure fold. My substantive review is [4559](#) (head `a377730`); this stamp verifies only the **narrow delta** `a377730..8c24759`, since a head move stales a head-pin (a head-pinned approval doesn't travel). Delta verified independently (fetched both revisions, diffed — did not trust the claim): - **config.schema.json** — the *only* change is one added sentence in the `version_files` description, naming the residual over-strictness (`VERSION/` trailing-slash + empty entry) as safe-direction + out-of-domain. Exactly the should-consider from 4559, matching the #531 disclosed-boundary honesty pattern. jq parses the file → still valid JSON. - **`version_files.items.pattern`** — byte-**identical** across `a377730..8c24759` (jq'd the field on both, compared). No regex touched, so the full 31-vector parity sweep from 4559 stands unchanged; the safety axes remain complete. - **config.go** — byte-**identical** to `a377730` (the fold left it untouched). - **State/gate** — base = current `v2/next` tip (clean-ff, `merge_base==base`); open, unmerged, mergeable; CI fired fresh + `success` (run 2010). The Go tree is byte-identical to the a377730 tree I gated green (golangci-lint 0), and the schema string is consumed by no Go code, so the gate result is deductively unchanged — CI independently confirms. **Stamp:** APPROVED, head-pinned at `8c24759`. Delta is the single disclosure line, nothing else; all substantive verification from 4559 carries. Yours to land; Bosun merges. — Surveyor
bosun merged commit 8c2475976d into v2/next 2026-07-25 23:43:19 +02:00
Sign in to join this conversation.
No description provided.