Skip to content

fix(glyph): release-readiness — number parity, patch base, CI fail-closed, doc reconciliation - #13

Merged
phenomenon0 merged 2 commits into
mainfrom
release-readiness-fixes
Jun 21, 2026
Merged

fix(glyph): release-readiness — number parity, patch base, CI fail-closed, doc reconciliation#13
phenomenon0 merged 2 commits into
mainfrom
release-readiness-fixes

Conversation

@phenomenon0

@phenomenon0 phenomenon0 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Release-readiness pass that closes the external review's blockers (reviewed at 95f868f), plus a cross-language 8-scenario acceptance gauntlet that proves every major capability is byte-for-byte conformant across Go/Python/JS (see Update below). Verified across cross-language runs: Python 444 + gauntlet 81 · JS 579 · Go vet/build clean + glyph/stream pass · cross-impl parity gate (24/14/stress) green · gauntlet 8/8.

Blockers closed

  • Numbers (the review's Harden glyph codecs across runtimes #1 issue). Unified Python's JSON-domain number typing to Go/JS via a safe-integer window in from_json_loose (integer-valued |n| <= 2^53-1 → int, else float64). Golden corpus 51/51, 0 xfail; byte-identical across Go/Python/JS (all_impl_parity_test.py 24/24). canon_float untouched.
  • Patch base. Python records/parses @base= and gains verify_patch_base / compute_base_fingerprint. Final basis: sha256(canonicalize_loose(state))[:16], matching Go/JS as the source of truth and LOOSE_MODE_SPEC §"Patch Base Fingerprint" (this supersedes the no-tabular basis the first commit shipped — see Update). The previously dormant TestTripleImpl_PatchParse is live and checks Go↔Python↔JS @base parse parity; _parse_path also accepts the bare Go/JS path form so Python can parse their patches.
  • README patch example. Replaced the unparseable @ops=[...] block with real syntax that parses identically in Py/JS/Go; corrected the prose (JS enforces base via the GS1 cursor).
  • CI fail-closed. Removed every silent || true on the typechecks and the npm publish build; added a release-meta job so a v* tag publishes only the registry whose package version matches the tag.
  • pytest tests/ hygiene. tests/conftest.py excludes the three cross-impl scripts (they're __main__ scripts, not pytest modules); the scripts still run and pass; stale vectors corrected to Go's golden truth.
  • Doc reconciliation. Collapsed the float rule to one authoritative source (CANONICAL_FORMS §3); removed stale "open divergence / threshold rule / known canonFloat bug" claims now that the rule is verified byte-identical.

Update — cross-language 8-scenario gauntlet (a248ae6)

Added a scenario-based acceptance suite under gauntlet/scenarios/ that exercises every major capability as realistic AI-workflow usage, runs each scenario identically across Go/Python/JS with one pass/fail evaluator + recorded evidence (report.json) + a hard exit-code gate.

  • Harness: gen_inputs.py → inputs.json (single shared fixture source) · runner.py/runner.cjs/go/cmd/gauntletrunner (per-language evidence runners, measure-only) · gauntlet.py (orchestrator + single evaluator, byte-for-byte cross-language checks).
  • Scenarios: S1 JSON bridge · S2 canonicalization · S3 fingerprint parity · S4 tabular compaction · S5 patch apply · S6 patch-base fail-closed · S7 GS1 framing + wire parity · S8 streaming firewall.

First run was 6/8. Both failures were cross-language divergences where Go and JS agreed and Python was the outlier; fixed Python to match Go (the source of truth):

  1. Tabular header. Go/JS emit @tab _ rows=N cols=M [cols] and Python emitted the bare @tab _ [cols] and its parser rejected the Go/JS form — i.e. Python could not read Go/JS tabular output. Python now emits + parses the metadata form (py/glyph/loose.py, py/glyph/parse.py).
  2. Patch @base basis. Switched Python from the no-tabular fingerprint to canonicalize_loose, matching Go/JS. This reverses the earlier "no-tabular" decision and resolves the deferred patch-base tabular-root edge item — all three now agree on Go's basis. @base is therefore intentionally distinct from the value-identity fingerprint_loose (which remains no-tabular); the README invariant block is updated to match.

Result: 8/8, with existing suites still green.

Deferred / known (not in this PR)

  • cogs go get. Default go build/vet are clean and go.mod/README document the constraint, but external go get still fails on the unpublished cowrie require. Structural fix is a follow-up: (A) park cogs to attic/ + drop the require (recommended); (B) extract cogs to its own module; (C) publish cowrie.
  • Out of scope: bytes (D6) / time (D5) doc claims (CANONICAL_FORMS:257,318) not verified or touched; JS lacks a standalone verifyPatchBase (Go+Py have it; JS enforces base via the GS1 cursor — covered by gauntlet S7).

🤖 Generated with Claude Code

…osed, doc reconciliation

Closes the external review's release blockers (reviewed at 95f868f).

- Numbers: unify Python JSON-domain number typing to Go/JS via a safe-integer
  window in from_json_loose (integer-valued |n|<=2^53-1 -> int, else float64).
  Golden corpus 51/51 with 0 xfail; byte-identical across Go/Python/JS
  (all_impl_parity 24/24). canon_float untouched.

- Patch base: Python now records/parses @base= and gains verify_patch_base /
  compute_base_fingerprint, standardized on the no-tabular state fingerprint
  (= fingerprint_loose[:16], the README invariant). The previously dormant Go
  TestTripleImpl_PatchParse is live and now checks Go<->Python<->JS base parity
  (canon.py and canon.mjs both emit baseFingerprint). _parse_path accepts the
  bare Go/JS path form so Python can parse their patches.

- README: replace the unparseable @ops=[...] patch example with real syntax that
  parses identically in Py/JS/Go; correct the prose (no false JS standalone
  verifyPatchBase claim — JS enforces base via the GS1 cursor).

- CI: remove silent `|| true` from the typechecks and the npm publish build
  (fail closed); add a release-meta job so a v* tag publishes only the registry
  whose package version matches the tag (no blind PyPI+npm fan-out).

- Tests: tests/conftest.py excludes the three cross-impl scripts so
  `pytest tests/` is clean (no fixture errors / return-value warnings); the
  scripts still run and pass; stale vectors corrected to Go golden truth.

- Docs: reconcile the float rule to one authoritative source (CANONICAL_FORMS
  §3) with the verified exp<=-5/>=6 boundary and a loose-vs-typed layer note;
  remove the stale "open divergence / threshold rule / known canonFloat bug"
  claims now that the rule is verified byte-identical.

- cogs: default `go build`/`go vet` are clean; document that the cowrie require
  is dev-only and breaks external `go get` until cowrie is published or cogs is
  extracted to its own module (structural decision deferred). gitignore the AI
  context dumps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@phenomenon0
phenomenon0 force-pushed the release-readiness-fixes branch from 614fe4f to 70a9a61 Compare June 21, 2026 08:51
@phenomenon0

Copy link
Copy Markdown
Contributor Author

Code review (high effort, recall-biased) — addressed in 70a9a61

Ran line-by-line + removed-behavior + cross-file finders over the diff. One real bug found and fixed, plus two hardening items; the rest verified as refuted or documented-intentional.

Fixed (amended into this PR)

  1. CI fail-open (important). release-meta had if: startsWith(github.ref, 'refs/tags/v') — a custom if: on a job with needs: overrides the implicit success(), so a tag push whose suite failed would still run it and trigger the publish jobs. This silently defeated the whole fail-closed intent (and pre-existed in the original publish jobs). Now: ... && needs.publish-gate.result == 'success'.
  2. Stale field comment. patch.py base_fingerprint comment said canonicalize_loose; updated to canonicalize_loose_no_tabular to match the implementation.
  3. Robustness. release-meta version extraction now anchors the key (^version[[:space:]]*=) and accepts either quote style (PEP 621 allows '...' and "...").

Verified and intentionally not changed

  • PatchBaseMismatch(got, want)matches Go's FingerprintMismatch convention exactly (Got = fingerprint of the presented base, Want = recorded). Refuted.
  • Large-int precision loss in from_json_loose (9007199254740993 → 9.007199254740992e+15) — the intended, documented JSON-domain (float64) contract, byte-identical with Go/JS.
  • compute_base_fingerprint no-tabular vs Go/JS tabular for a bare list root — the documented edge; no-tabular is the spec-correct choice (Go is itself inconsistent here). Recommended follow-up: migrate Go/JS to no-tabular.
  • _parse_path now accepting bare paths — required so Python can parse Go/JS-emitted patches; the lost dot-required validation only affected malformed value-only lines (pre-existing edge, not valid cross-impl input).

Re-validated after the fixes: Python 444 · Go vet/build + cross-impl green · JS tsc clean · YAML valid · version gate simulated for v1.0.1/v1.0.0/mismatch.

Add a scenario-based acceptance suite that exercises every major GLYPH
capability as realistic AI-workflow usage, runs each scenario identically
across Go/Python/JS, applies one pass/fail evaluator with recorded evidence,
and gates on a hard exit code.

Harness (gauntlet/scenarios/):
- gen_inputs.py -> inputs.json: single shared fixture source (same conditions)
- runner.py / runner.cjs / go/cmd/gauntletrunner: per-language evidence runners
  (measure only; the orchestrator is the single evaluator)
- gauntlet.py: runs all three, applies criteria incl. byte-for-byte cross-lang
  equality, writes report.json, exits non-zero on any failure

Scenarios: S1 JSON bridge - S2 canonicalization - S3 fingerprint parity -
S4 tabular compaction - S5 patch apply - S6 patch-base fail-closed -
S7 GS1 framing + wire parity - S8 streaming firewall.

First run was 6/8. Both failures were cross-language divergences where Go and
JS agreed and Python was the outlier; fixed Python to match Go (source of
truth):
- Tabular header: emit `@tab _ rows=N cols=M [cols]` and accept it on parse
  (py/glyph/loose.py, py/glyph/parse.py). Python previously could not read
  Go/JS tabular output.
- Patch @base: compute over canonicalize_loose, not the no-tabular fingerprint,
  matching Go/JS and LOOSE_MODE_SPEC (py/glyph/patch.py); reconcile the README
  invariant block accordingly.

Result: 8/8. Existing suites stay green (py 444, py-gauntlet 81, go all,
js 579, cross-impl parity gate all).

Also lands in-progress branch work already staged in the tree: the per-language
gauntlet tests (go/py/js), the JS loose-text parser (parse_loose.ts), the
gauntlet web demo, and release-readiness doc updates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@phenomenon0
phenomenon0 merged commit c197077 into main Jun 21, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant