Skip to content

fix(version): single source of truth via lib/version.ml + tag-time bake - #300

Merged
hyperpolymath merged 1 commit into
mainfrom
fix-297-version-string
May 20, 2026
Merged

fix(version): single source of truth via lib/version.ml + tag-time bake#300
hyperpolymath merged 1 commit into
mainfrom
fix-297-version-string

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Closes #297 — the five-site 0.1.0 drift that left the v0.1.1 release binary self-reporting 0.1.0.

Mechanism

Single source of truth in lib/version.ml:

let value = "0.1.1"

Five hardcoded "0.1.0" strings collapsed onto it:

Site Before After
bin/main.ml let version = "0.1.0" let version = Affinescript.Version.value
lib/repl.ml print_endline "AffineScript REPL v0.1.0" Printf.printf "AffineScript REPL v%s\n" Version.value
lib/lsp_server.ml ("version", \String "0.1.0")` ("version", \String Version.value)`
lib/onnx_codegen.ml m_producer_version = "0.1.0" m_producer_version = Version.value
dune-project (version 0.1.0) (version 0.1.1)

affinescript.opam was auto-regenerated by dune from dune-project (version: "0.1.1").

Release pipeline bake step

New step in .github/workflows/release.yml, between Install dependencies and Build release:

- name: Bake release version into source (#297)
  run: |
    v="${GITHUB_REF_NAME#v}"
    sed -i "s/^let value = .*/let value = \"$v\"/" lib/version.ml
    sed -i "s/^(version .*)/(version $v)/" .build/dune-project

Future tag bumps need zero hand-edits — cut vX.Y.Z and the binary self-reports X.Y.Z.

Why option (1), not (2)

Issue #297 proposed two shapes. I went with (1) (generated module + CI substitution) because it doesn't add an opam-package dependency (dune-build-info), keeps the runtime path trivial (one string literal), and doesn't tie semantics to any particular install method.

Verification

$ opam exec -- dune build --release         # clean
$ _build/default/bin/main.exe --version
0.1.1

$ grep '^version'   affinescript.opam       # 0.1.1
$ grep '^let value' lib/version.ml          # 0.1.1
$ grep '^(version'  .build/dune-project     # 0.1.1

All four sites coherent.

Test plan

  • Local dune build --release clean
  • Built binary --version reports 0.1.1
  • CI: regular checks green
  • On next v* tag push, the release.yml bake step rewrites lib/version.ml + .build/dune-project from the tag, and the resulting binary self-reports the tag.

Closes #297. Refs #282 (the closure-PR smoke that surfaced the drift).

🤖 Generated with Claude Code

…ke (#297)

Closes the five-site `0.1.0` drift documented in #297: the compiler
binary's `--version`, REPL banner, LSP `initialize` response, and the
ONNX `m_producer_version` field were all hand-edited to "0.1.0" and
never bumped when v0.1.1 was tagged, so the v0.1.1 binary still
reported "0.1.0" (caught during the #282/#295 shim smoke).

Approach: option (1) from #297 — generated `Version` module + CI
substitution.  One small module + one small workflow step; no
runtime dependency on `dune-build-info` or any new opam package.

Source-of-truth single file
---------------------------
  lib/version.ml            new — `let value = "0.1.1"`, 1 string,
                                  baked from the tag at release time

Five sites collapsed onto it
----------------------------
  bin/main.ml         `let version = Affinescript.Version.value`
  lib/repl.ml         `Printf.printf "AffineScript REPL v%s\n"
                                     Version.value`
  lib/lsp_server.ml   `("version", `String Version.value)`
  lib/onnx_codegen.ml `m_producer_version = Version.value`
  dune-project        `(version 0.1.1)` (matches the latest release;
                       opam metadata regenerated)
  lib/dune            `version` added to the modules list

Release pipeline bake step
--------------------------
`.github/workflows/release.yml`: new step between `Install
dependencies` and `Build release` that rewrites BOTH `lib/version.ml`
and `dune-project` from `${GITHUB_REF_NAME#v}` (stripping the leading
`v`) before `dune build --release`.  Subsequent tag bumps need no
hand-edit — cut a `vX.Y.Z` tag and the binary self-reports `X.Y.Z`.

Cross-checked locally
---------------------
  $ opam exec -- dune build --release
  (clean, no warnings introduced)

  $ _build/default/bin/main.exe --version
  0.1.1

  $ grep '^version'     affinescript.opam   # 0.1.1
  $ grep '^let value'   lib/version.ml      # 0.1.1
  $ grep '^(version'    .build/dune-project # 0.1.1

All four sites coherent.

Closes #297.  Refs #282 (the closure-PR smoke that surfaced the drift).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 42 issues detected

Severity Count
🔴 Critical 13
🟠 High 17
🟡 Medium 12

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stray AI.a2ml in root -- use 0-AI-MANIFEST.a2ml only",
    "type": "banned",
    "file": "AI.a2ml",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "high"
  },
  {
    "reason": "Superseded by 0-AI-MANIFEST.a2ml",
    "type": "banned",
    "file": "AI.djot",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "high"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Action actions/checkout@v4 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action denoland/setup-deno@v2 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/example/smoke_driver.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/cli.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit 8110548 into main May 20, 2026
16 checks passed
@hyperpolymath
hyperpolymath deleted the fix-297-version-string branch May 20, 2026 07:54
hyperpolymath added a commit that referenced this pull request May 20, 2026
…JSR publish (#303)

INT-04 and INT-10 were both marked DONE in TECH-DEBT, but the wording
predated the actual JSR publish:

  - INT-04 line said "JSR publish authorised + dispatched (owner go
    2026-05-19)" — speculative; the real first-time publish landed
    2026-05-20 (six dispatched runs through the scope/package/trusted-
    publisher/SPDX-licence/.d.ts/cross-runtime gates).
  - INT-10 said DONE but didn't note the LSP's shim-resolution path
    only became *executable* after today's publish (code was correct
    but inert against a 404 JSR URL).

Updates
- docs/TECH-DEBT.adoc — INT-04 + INT-10 lines reflect the live publish,
  with explicit note that @hyperpolymath/affine-js + affinescript-tea
  remain dry-run-OK / not-yet-dispatched.
- docs/PACKAGING.adoc — JSR-publishable table now has 3 rows with a
  Status column; @hyperpolymath/affinescript row records the LIVE
  0.1.2 (2026-05-20) state, cross-runtime carve-out, MPL-2.0 licence
  pointer.  New "First-time JSR publish gotchas" subsection captures
  the five gates that fail one at a time (scope claim, package create,
  trusted GH repo link, SPDX licence, sibling .d.ts + triple-slash
  reference) — dry-run misses gates 2 + 3 so they cost a round-trip.
- .machine_readable/6a2/STATE.a2ml — new session-note-2026-05-20
  following the existing `session-note-YYYY-MM-DD = "…"` convention,
  capturing every landed PR (#291/#292/#293/#294/#295/#298/#299) and
  the estate macos-13 sweep (casket-ssg#8, proven#29, repos-monorepo#9),
  plus the open follow-up tickets (#297/#300, #301, #302).  This file
  is the STATE.a2ml mirror, not the lead; TECH-DEBT.adoc is the spine.

No code change.  TECH-DEBT update protocol calls for ledger + STATE
mirror in lockstep; PACKAGING is the human-facing companion.  Memory
entries (`reference_macos_13_runner_retired`,
`reference_estate_gitbot_auto_closes_on_refs`,
`reference_estate_mirror_state_2026_05_20`,
`reference_jsr_publish_recipe`) live in `~/.claude/projects/.../memory/`
and are out of repo scope.

Refs #282 (closed), #301, #302, #297, #300.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 20, 2026
…retirement (#305)

Follow-up to #303.  Captures the ledger deltas that happened after
#303 merged but in the same session:

- #297 closed (#300 merged): single-source-of-truth Version module
  in `lib/version.ml`; release.yml sed-bakes the tag value into both
  `lib/version.ml` and `dune-project` before `dune build --release`.
  Binary `--version` now self-reports the release tag.
- #301 in flight as #304: repo-wide PMPL-1.0-or-later → MPL-2.0
  SPDX-surface relicense (Option 3 of the three in the issue —
  machine-readable headers/fields flip, root LICENSE narrative
  retains PMPL as the preferred licence).  673 files, 1116 SPDX
  headers, opam regenerated, LICENSES/LICENSE-MPL-2.0 added, no
  behaviour change.
- #302 closed: GitLab + Bitbucket mirroring confirmed intentionally
  off by owner; Codeberg + Radicle are the active forges.
- `repos-monorepo` retirement decided: snapshot tarred to
  `/mnt/c/Users/USER/Downloads/repos-monorepo-snapshot-2026-05-20.tar.gz`
  (23 MB gzip / 144 MB extracted / 23,970 entries / canonical
  origin/main / .git excluded) for owner upload to Google Drive, then
  `gh repo delete`.  Once deleted, `repos-monorepo#9` (the nested
  casket-ssg mirror coherence fix) becomes moot — the standalone
  hyperpolymath/casket-ssg repo's own merged fix (casket-ssg#8) +
  its own `instant-sync.yml` cover forge propagation.

Changes
- `docs/TECH-DEBT.adoc` INT-04 line: appended `#297/#300 fix` and
  `#301/#304 sweep` notes alongside the existing "first JSR publish
  landed" entry.
- `.machine_readable/6a2/STATE.a2ml` adds
  `session-note-2026-05-20-pm` following the existing
  `session-note-YYYY-MM-DD` (with optional `-suffix`) convention.
  STATE.a2ml file remains marked STALE (mirrors, doesn't lead).

No code change.  TECH-DEBT.adoc remains the authoritative ledger;
STATE.a2ml is the machine-readable mirror.

Refs #297 (closed), #300 (merged), #301 (in-flight #304), #302
(closed), #303 (merged — this is its follow-up).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant