Commit graph CultLib/.github/workflows
Author SHA1 Message Date
Meta Vens
0c5207b55f One typed owner of document-payload reads and writes: DocumentPayload is opaque, the definition is required
DocumentPayload is an opaque object type over the same runtime Uint8Array, keyed by a string
property, and types CultCacheEnvelope.payload, CultNetRawDocumentRecord.payload and
CultMeshRealtimeFrame.payload. decodeDocumentPayload(definition, payload) and
encodeDocumentPayload(definition, value) are the only reader and writer; documentPayloadFromBytes
and documentPayloadBytes are the two named escapes at the edges where bytes enter and leave.
resolveDocumentFormatter leaves the package index. The cache, cultnet replication, the mesh read
paths, the provider command (now a CultCache document), and the browser's decodeCultNetPayload
(definition required, cultcache-ts/document entry) all go through the owner.

Compile-fail fixtures per payload-carrying package pin the type with expect-error lines.
check-typescript-tests-wired.mjs fails when a test file is run by no script; cultcache-ts now
runs dist-test/test/*.test.js, and the portability workflow runs the check and the cache suite.

Builds in every package (tsc, src and tests); the suites run once, at the end of the pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:33:42 +02:00
Meta Vens
a126914efc Release guard tidy: the Python build guard starts without NODE_OPTIONS too
The python-build step runs the same guard whose output is the dist PyPI
uploads, so a NODE_OPTIONS preload in the workflow or job env could stub
its checks exactly as it could on the npm Publish line. It now starts
under env -u NODE_OPTIONS; the pin's exact text and semver-policy.md say
so. The pin test's if: always() edit matched the npm line's old text and
had become a no-op; it now matches the pinned line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:16:57 +02:00
Meta Vens
49a5526ede Release guard tidy: threat model stated, preload route pinned shut, dated changelog headings read
- The npm Publish step starts the guard under `env -u NODE_OPTIONS`, and the
  pin's exact text carries it, so a workflow or job NODE_OPTIONS preload
  cannot run inside the guard (preload-fakes-guard).
- The pin's shell/defaults ban and its test are gone: it defended only a
  deliberate edit, which is review's job (suite-gate-structural-not-parsed),
  and refused a harmless defaults.run.working-directory. A step-level shell
  still fails the exact comparison (shell-ban-deliberate-only).
- docs/semver-policy.md states that the guard and pin stop accidental edits,
  that the cleaned environment removes known variables rather than proving no
  inherited one matters (env-denylist-overclaim), recommends a
  required-reviewer environment for the npm job, and gives the first-release
  note with the valid next versions.
- check-changelog-semver accepts `## [x.y.z] - YYYY-MM-DD` and
  `## x.y.z - YYYY-MM-DD` beside the plain forms (changelog-first-entry).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:02:55 +02:00
Meta Vens
7a36848b98 Release guard: the checks run in a clean env, and every per-tag check runs in the guard
release-after-suite.mjs now takes the run's git ref and, from one process, checks
the tag against the declared prefix and manifest version, runs the release-checks
suite, the semver policy check and the package's own tests, then releases. The
checks run without NODE_OPTIONS, NODE_TEST_*, or NODE_AUTH_TOKEN; only the release
action gets the token. The workflow's separate tag, semver and test steps are gone,
and so is the pin's continue-on-error/shell/defaults ban, which no release check
needs any more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:05:28 +02:00
Meta Vens
bc4af4fa58 Release gate: publish-packages.yml releases through one entrypoint that runs the suite first; the if: pin is gone
scripts/release-after-suite.mjs runs the release-checks suite and then the
release action (npm publish, or the Python sdist and wheel build that the PyPI
step uploads) from one process, and releases only if the suite passed. No step
condition can release what the suite did not pass, so the pin that read if:
conditions (status functions, multi-line if:, the leading-conjunct rule) is
deleted with its tests and docs. What remains compares each job's release steps
with their expected text exactly, less their if: line, and refuses
continue-on-error, shell and defaults in the workflow.

Ruling asura:ruling:suite-gate-structural-not-parsed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:43:47 +02:00
Meta Vens
7ec300a5aa Semver release: the suite runs first in CI and in the Unity scripts, and the Caching Unity prefix is its own tag family
publish-packages.yml runs the suite in both jobs, after .NET setup on a full-depth
checkout and before any check or publish step; the three Unity release scripts run
it before the checker. A test reads each file and fails when the step is removed,
moved, or made non-fatal. The Caching Unity pin now needs a prefix declared once
whose exact tags hold the package's own manifest at the tag's version. No NUL byte
in the checker is a test. The policy records that releases run from a full clone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 11:43:43 +02:00
Meta Vens
23d7beac20 Semver check derives what callers used to declare: first release, tag prefix, changelog and measured set
The checker reads each package's tag prefix, changelog and assemblies directory
from scripts/release-packages.mjs and derives the release record from git tags.
A first release is the absence of any older tag in a checkout that holds other
tags; a malformed same-prefix tag, a checkout without tags and an unreadable
repository refuse. The measured set is every managed assembly (CLI header in
the PE bytes) tracked at the previous tag at any depth; an unbuilt one is a
break, a built assembly with no namesake is an addition.

Deleted with their branches, refusals and tests: --first-release, --api-new,
--tag-prefix, --changelog, --api-baseline-path and the workflow first-release
decision line. Any unknown option is now a usage error.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 02:36:54 +02:00
Meta Vens
4c7ae3adc6 Publish workflow: a package's first release passes --first-release, which the checker verifies
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 00:58:33 +02:00
Meta Vens
52abce9191 Interop workflow: every command in a step counts
The Windows runner's default pwsh does not stop when a native command fails, so
a multi-command step reported only its last command: a failing cultnet-py suite
went green inside "Verify Python RUDP reliability". The job now defaults to Git
Bash, which the runner starts with -e -o pipefail. The pwsh consolidation guard
checks git ls-files' exit code instead of treating a failed listing as clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:32:28 +02:00
Meta Vens
beb7c46162 Interop guard: accept cultnet-rs's vendored schema copies
The consolidation guard rejected every *.schema.json outside contracts/cultnet/,
including the R-AB byte copies vendored into packages/cultnet-rs/contracts/cultnet/.
The interop workflow has been red at that step on main ever since, so no later
cross-runtime step ran. The guard now accepts that one path; drift of those copies
is judged by the Rust equality test in tests/schema_discovery_contracts.rs.
The retired-namespace check is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:17:58 +02:00
Meta Vens
2785f28f5b Rename the release tarballs to stop tripping 11.7's parked-package grep
`cultmesh-quic-native-{win32-x64,linux-x64}.tar.gz` collided by substring
with the negative grep for the parked `@gamecult/cultmesh-quic-native-*`
platform package names (11.9, not built in this campaign). Rename to
`quic-native-{win32-x64,linux-x64}.tar.gz`, matching the upload-artifact
names already used. The release job has not run (no tag pushed), so this
has no runtime effect yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 22:03:38 +02:00
Meta Vens
4fccdf17ff Remove the TEMPORARY push-to-quic-cut6 trigger
The binaries are committed and verify-committed went green on the trigger's
own run (https://github.com/GameCult/CultLib/actions/runs/35912901846).
Reverts quic-native.yml to the triggers named in cut 6 (docs/typescript-
quic-realtime-cut.md:2153-2156): push to main and pull_request run
verify-committed only, workflow_dispatch runs the two builds, a
cultmesh-quic-native-v* tag runs all four. Diff against 4d75d90 (the file
before the temporary trigger was added) is empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 22:00:25 +02:00
Meta Vens
e5bf1c9243 Add a TEMPORARY push trigger to exercise quic-native.yml on this branch
workflow_dispatch cannot be dispatched against quic-cut6 until this workflow
file is registered from the default branch (a GitHub constraint, not a
permissions gap: `gh workflow run` and a raw dispatch API call both 404).
Rather than touch main or push a tag, add push: branches: [quic-cut6] and
extend verify-committed's and both build jobs' `if:` guards with a
`refs/heads/quic-cut6` arm, so this push itself runs the three jobs. The
release job's guard is untouched: it still matches only a
cultmesh-quic-native-v* tag, so this push does not create a release.

Every added line is marked TEMPORARY and paired with its match; both are
removed in one commit once the binaries are committed and verify-committed
goes green on that push.

Cut 6 (packaging).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 21:54:02 +02:00
Meta Vens
4d75d90da8 Add the QUIC native release workflow
Four jobs: verify-committed checks packages/cultmesh-ts/native/SHA256SUMS
against the tree on every push to main and every pull request; build-win32-x64
and build-linux-x64 run scripts/build-quic-native.{ps1,sh} on
workflow_dispatch or a cultmesh-quic-native-v* tag and upload their output as
artifacts; release, needing both builds and gated to the tag push, is the
only job with write permission and attaches the two platform archives plus a
committed-format SHA256SUMS and a draft MANIFEST.txt to a GitHub Release.

build-linux-x64 runs inside the pinned debian:13@sha256:f324c7ff… container
per Q6 A: the Actions container is the only permitted Linux release builder,
because the artifact links against that image's glibc and the deploy host
(Yggdrasil) runs the same OS. No release or tag has been pushed by this
commit; the release job exists but has not run.

Cut 6 (packaging), docs/typescript-quic-realtime-cut.md:2131-2156, narrowed
per StreamPixels cut map section 4.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 21:51:28 +02:00
Meta Vens
089859deb4 Write and enforce the semver policy, per operator ruling on 1.0.60
1.0.60 removed public API (CultInspectorAssetPath) under a patch bump; its
own changelog says "Breaking" and the version number lied. Operator ruling:
semver is policy, strictly, for every published package family.

docs/semver-policy.md states what counts as breaking per surface (managed
API, Unity inspector attributes, CultCache wire format/store compatibility,
CultNet protocol, native bridge exports), the 0.y.z pre-1.0 convention, tag
naming, and required changelog shape. Records the 1.0.60 scar as history;
does not renumber it.

scripts/check-changelog-semver.mjs is the mechanical check: refuses a
changelog "### Breaking" section under too small a bump, and refuses a
version that skips, reverses, or repeats the previous published tag. Skips
re-litigating a version that is already tagged (rebuilding immutable
history is not a release action). Wired into publish-packages.yml for the
four npm/PyPI packages, and into build-unity-package.ps1 (cultlib),
packages/cultmath/scripts/build-unity-package.ps1 (cultmath), and a new
scripts/verify-caching-unity-release.ps1 (caching.unity has no build script
of its own to hook into).

18 node:test cases cover the required table (breaking+major/minor/patch,
non-breaking, 0.y.z, skip, downgrade) plus CLI-level git-tag resolution.
Hand-mutated each rule (bump classification, pre-1.0 lane requirement,
breaking-heading detection, breaking early-return, already-published skip)
and confirmed each kill, then restored byte-exact. No mutation harness is
committed; CultLib has none today.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:03:22 +02:00
Meta Vens
6ed0ff75af Run both halves of the realtime frame parity in CI
The codec's byte parity rests on two vector files written from opposite sides:
`realtime-frame-vectors.json` by the C# reference and decoded by TypeScript,
and `realtime-frame-vectors.ts-written.json` by the TypeScript codec and
decoded by C#. Neither half ran in CI. The TypeScript suite ran nowhere —
cultmesh-ts was not in any job — and the C# test ran nowhere either, because
`cultnet-interop.yml` is windows-latest and main-only and does not filter for
it. A codec that agreed only with itself would have gone green on every push,
which is the exact failure the two-file scheme exists to prevent.

Both now run in the `reactive-and-resource-lifetime` job beside the authority
steps, on windows-latest and ubuntu-latest, for main, codex/** and pull
requests. That job was already the one place the TypeScript side of CultMesh
gets exercised on Linux at all.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 20:21:27 +02:00
Meta Vens
0dfef0e5eb Run the TypeScript authority tests in CI, on both operating systems
Nothing ran `npm run test --workspace packages/cultnet-ts` in CI. The shared
route verifier's tests, which are the whole TypeScript side of the CultMesh
authority contract, ran only on a developer's machine, and the browser client's
tests ran nowhere. `cultnet-interop.yml` is windows-latest and triggers on main
only, so even that would not have covered Linux or a pull request.

Both now run in `cultmesh-portability.yml`'s `reactive-and-resource-lifetime`
job, beside the step that verifies the same vectors with the C# reference. That
job already matrixes windows-latest and ubuntu-latest, already triggers on
main, codex/** and pull requests, and already installs the TypeScript workspace
dependencies, so the steps cost two npm runs and no new setup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:40:39 +02:00
Meta Vens
cf1203a607 Run the C# authority vector checks in CI
`CultMeshAuthorityProofTests` verifies both vector files with the C#
reference, but no workflow ran it. The portability workflow's
reactive-and-resource-lifetime job is the one that already builds and
runs `GameCult.Mesh.Tests` on every push to main and codex branches and
on every pull request, on Windows and Ubuntu, so it gains one step:
`dotnet test --filter FullyQualifiedName~AuthorityProof`. The interop
workflow was not chosen: it runs on Windows only, on main pushes only,
and runs no Mesh tests. It already covers the TypeScript direction
through `npm test --workspace packages/cultnet-ts`.

CULTMESH_WRITE_VECTORS stays unset in CI; the committed files are the
contract.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:28:29 +02:00
Meta Vens
7d3a3dc42c Let the CultNet contract drift guard pass on a clean tree
git grep exits 1 when it finds nothing, and the GitHub pwsh step wrapper
ends the step with that $LASTEXITCODE, so the guard failed on exactly the
clean tree it should accept. End the step with an explicit exit 0 after the
checks; a real drift still throws first.

REC-032.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:52:15 +02:00
Meta Vens
59aca27b67 Add CI drift guard for the CultNet contracts consolidation
REC-032 commit 2: fail cultnet-interop.yml before it installs Node
dependencies if either negative check from commit 1 finds anything:
a *.schema.json file living outside contracts/cultnet/, or a lingering
reference to the retired https://github.com/GameCult/cultnet-ts/contracts
(or .../integration/contracts) namespace anywhere but .voidbot/birth
snapshots. The old-namespace grep uses git grep -I so the deferred
Unity plugin DLL (D4, not rebuilt in this pass) doesn't trip a
permanent false failure.

Verified locally: staging a stray contracts/stray.schema.json and a
stray-id.ts referencing the old namespace makes the guard's logic
report failure for both; removing them makes it pass again against the
real tree.

REC-032.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 17:45:53 +02:00
Meta Vens
830a5d718f Make the Eve onboarding checkpoint run against the contract that exists
The portability workflow has been red since 2026-08-22. Three things,
each hidden behind the one before it:

- Eve started refusing to build a command intent for a surface that has
  not advertised a command boundary and receipt schema (Eve 4338a42,
  2026-08-22). The two-runtime sample never advertised one. It now passes
  a provider advertisement with worldInteraction for its surface, which is
  the declaration the lowering was right to demand.
- The sample and the package-closure script still imported "cultcache-ts";
  the package became @gamecult/cultcache-ts on 2026-09-04 (8cb3b72) and
  CI never got far enough to say so. Two READMEs carried the same import.
- The job pinned Eve to codex/eve-browser-host-isolation, a branch that was
  never merged and stopped moving on 2026-08-25. The sample is written
  against Eve main and passes there; the pin now says so.

The interop workflow's Python step ran one test file by path; it now
discovers the three split suites.

  scripts/verify-eve-two-runtime-sample.ps1 -EveRoot F:\Projects\Eve
  (Eve main ccc3ffe) -> browserCount 1, headlessCount 1, restartCount 1

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:55:46 +02:00
Meta Vens
95493862bd Split the Python runtime into cultcache-py, cultnet-py, cultmesh-py
One distribution named cultcache-py carried three libraries. Rust and
TypeScript layer them as cultcache < cultnet < cultmesh; Python now does
the same, one package per layer, module names unchanged so every
`from cultmesh_py import ...` keeps working.

Two imports ran against that layering and are resolved by moving code to
where the dependency direction says it lives, not by adding a shim:

- cultnet_py.interop_peer imported the Verse/peer catalog, authority
  lease, stream, and simulation-fact contracts from cultmesh_py. They are
  wire contracts a bare peer has to speak without owning a node, which is
  why cultnet-rs and cultnet-ts keep them in the net layer. They now live
  in cultnet_py.cultmesh_contracts; cultmesh_py re-exports them.
- cultcache_py.benchmark imported cultnet_py. It measures framed CultNet
  parse beside cache paths, so it and compare_csharp move to cultnet_py.
  verify checks all three packages and moves to cultmesh_py.

The single 6,693-line test module is split by what each test touches: a
test using any cultmesh_py name lands in cultmesh-py, else cultnet_py in
cultnet-py, else cultcache-py. Each suite runs with only its layer and the
layers below on the path, so a test that reaches up fails to import
rather than quietly binding the packages back together. 11 / 53 / 78 =
the same 142.

Workflows install all three; the publish job is one matrix over the
three packages keyed by tag prefix. The TS test harnesses put all three
src directories on PYTHONPATH. cultcache-py bumps to 0.2.0 because it no
longer contains cultnet_py or cultmesh_py; the new packages start at
0.1.0. Nothing was ever published, so no PyPI consumer moves.

  cultcache-py  11 tests, path: cultcache-py/src
  cultnet-py    53 tests, path: + cultnet-py/src
  cultmesh-py   78 tests, path: + cultmesh-py/src
  python -m cultmesh_py.verify -> ok, all five checks
  cultnet_py.interop_peer imports with cultmesh_py absent

Not run here: the cultnet-ts interop ring and cultcache-ts store matrix
need dotnet/kotlin peers; their PYTHONPATH change is four lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:10:47 +02:00
Meta Vens
8cb3b7282b Publish the non-Rust packages and make the licensing uniform
Rust consumers pin a CultLib revision through cargo git dependencies, which is
how Ghostlight, Epiphany, CodexConnector and Odin already consume the core
stack. npm and pip cannot take a package from a subdirectory of a git repo at
all, which is why the TypeScript and Python consumers vendored forks instead:
there was nothing else to depend on. This gives them a versioned artifact.

Tagging cultcache-ts-v* or cultcache-py-v* builds, tests and publishes. The tag
version must match the manifest version or the workflow refuses rather than
shipping a mislabelled artifact. PyPI uses trusted publishing, so no long-lived
token is stored; npm needs an NPM_TOKEN secret.

cultcache-ts is now @gamecult/cultcache-ts. The scope is not cosmetic: it makes
the package unambiguously org-owned on a public registry, and it is what lets
CultLib hold the name rather than racing for an unscoped one. CultLib's own
cultmesh-ts and cultnet-ts and their imports move with it; all three packages
typecheck and cultcache-ts passes 12 of 13 tests, the exception being the
cross-runtime store test that shells out to Python, which cannot run on this
machine because its Python has no msgpack.

Licensing was inconsistent with the repo. cultcache-py declared itself "Private
GameCult infrastructure" and cultcache-rs and cultmesh-rs declared nothing,
inside a repo whose LICENSE is MIT and whose every other package says MIT. All
three now say MIT. That was load-bearing here: a package that calls itself
private is not one to push to a public registry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 04:41:44 +02:00
Meta Vens
67cbb4ea36 Give the Rust CultMesh runtime a home
The only Rust CultMesh implementation in existence was 1,363 lines inside
Ghostlight's vendor directory. It was in no library repo and no standalone
repo, so it had never been checked against the browser, Kotlin, and
TypeScript CultMesh bodies it must interoperate with.

It is not a duplicate of the facade in cultnet-rs. That one is the small
surface the parity table claims for Rust: peer cards, authority leases,
endpoint parsing. This is the stream and session layer -- node, document
sets, RUDP document publication, stream descriptors, negotiation, stream
catalog, and shared-memory frame rings -- which the same table lists under
Rust's "Not claimed". A consumer grew the unclaimed surface because there was
nowhere else to put it.

Its path dependencies already matched this layout and it builds against
CultLib's cultnet-rs unchanged, so it does not depend on the forked RUDP
extras. Seven runtime-local tests pass, and CI now runs them.

The parity table gains no claim from this. The Rust row says plainly that no
interop lane exercises the stream surface, and expansion work records that
runtime-local tests are not a parity claim. Landing code is not evidence.

packages/cultmesh-rs needed an explicit .gitignore re-include; the NuGet
packages rule hides every language package until one is added by name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 23:09:45 +02:00
Meta Vens
289b792284 Exercise RUDP reliability in fleet CI 2026-08-23 18:43:17 +02:00
Meta Vens
21ce4f0ec6 Repair CultNet interop workflow setup 2026-08-23 18:42:07 +02:00
Meta Vens
3c19e54d91 Make Eve onboarding one executable path 2026-08-18 03:17:52 +02:00
Meta Vens
44abedfc9f Measure reactive performance across runtimes 2026-08-18 03:08:01 +02:00
Meta Vens
5e86427a64 Run portability gates on branch pushes 2026-08-18 02:49:52 +02:00
Meta Vens
913ed627c6 Gate CultMesh portability on Windows and Linux 2026-08-18 02:42:57 +02:00
Meta Vens
c80703172a Add Rust interop RUDP serve dial path 2026-06-17 10:07:37 +01:00
Meta Vens
d595f5a525 Install Python dependencies in interop workflow 2026-06-16 18:55:08 +01:00
Meta Vens
e9ee0b59a1 Harden interop lane diagnostics 2026-06-16 18:52:02 +01:00
Meta Vens
1bce34511c Raise CI budget for discovery interop lane 2026-06-16 18:16:15 +01:00
Meta Vens
9dc22eca99 Split cross-runtime interop workflow lanes 2026-06-16 17:49:06 +01:00
Meta Vens
fa2f20977e Fix interop workflow TypeScript build prerequisites 2026-06-16 17:13:32 +01:00
Meta Vens
e06e3f5b3a Add cross-runtime interop workflow 2026-06-16 17:09:33 +01:00