Skip to content

Replace the contract-integer compatibility gate with minor-version coupling#468

Merged
clkao merged 9 commits into
mainfrom
spacedock-ensign/minor-version-compat-coupling
Jul 3, 2026
Merged

Replace the contract-integer compatibility gate with minor-version coupling#468
clkao merged 9 commits into
mainfrom
spacedock-ensign/minor-version-compat-coupling

Conversation

@clkao

@clkao clkao commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

The skill↔binary compatibility floor now moves itself each release — the hand-maintained contract integer that silently stayed at 1 across three minors is gone.

What changed

  • Replace the contract-integer gate with minor-exact version coupling, rejecting skew in both directions
  • Freeze two cross-era sentinels so integer-era installs abort with correct remedies
  • Auto-heal too-old-plugin at both front doors, with a re-gate-once guard
  • Stamp the FO prose minor at all three release stamp sites; contractlint binds prose minor to manifest minor
  • Embed the checkout manifest version in unstamped dev builds (0.24.0-pre1+dev)

Evidence

  • Full go test ./... 16/16 packages green; AC-1 stamp-only floor-flip fixture passed
  • Published v0.22.0/v0.23.0 binaries verified cross-era in both directions; 5-edit detached adversarial audit refuted nothing material

Review guidance

Release-critical surface: the release.yml stamp-site wiring and the two frozen sentinels.


kr

Spike Test and others added 9 commits July 2, 2026 19:42
…upling

Removes CONTRACT_VERSION, ParseRange, and the integer Compare math from
internal/contract in favor of a minor-exact major.minor compare over the
binary's own display version and the plugin manifest's version field — the
existing release stamps already couple both sides, so the floor now moves on
every release with zero hand-maintained integer to forget.

- internal/contract: new ParseMajorMinor + Compare(host, pluginVersion,
  binaryVersion); PluginPredatesContract folds into TooOldPlugin;
  MalformedRange renames to MalformedVersion.
- D3: the module root embeds .claude-plugin/plugin.json so an unstamped dev
  build reports <checkout-version>+dev instead of the bare `dev` sentinel,
  closing the always-pass carve-out.
- D4: two frozen cross-era sentinels preserved for integer-era installs — the
  manifest tombstone requires-contract ">=3,<4" and the (contract 3)
  --version token — so an old binary/FO still gets the correct abort remedy.
- D5: the FO shared-core Startup gate rewritten to parse the version line and
  carry a single release-stamped "required binary minor" literal.
- D6: gateHost stops printing for too-old-plugin; both front doors now
  auto-heal it (install, re-gate once, refuse on a second miss) alongside the
  existing no-plugin-found heal. --skip-contract-check renames to
  --skip-compat-check throughout.
- Fixture manifests and cli tests migrate off static contract-range fixtures
  to versions derived from the effective binary version, so gating tests stay
  self-consistent as the repo's own manifest version advances.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ixture

Adds the D5 release-pipeline half of minor-version coupling:

- internal/release: StampProseVersion/ProseMinor rewrite and read back the FO
  shared-core's single "required binary minor" literal, erroring on zero or
  multiple matches (atomic by construction) or an unparseable release version.
- cmd/spacedock-release: stamp-version and manifest-tag-gate now dispatch by
  file extension — a .md argument takes the prose handler/minor-only tag
  compare, a .json argument keeps the existing full-version handling, in the
  same multi-file invocation.
- EvaluateProseMinorTagGate compares a tag's major.minor against the
  prose-stamped minor (the prose carries no patch, unlike the JSON gate).
- TestStampVersionFlipsCompatibilityFloor is AC-1's divergeable proof:
  restamping ONLY the release version (no hand-edited integer) moves the SAME
  fixed binary version from Compatible to TooOldBinary against both the
  manifest and the prose gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… sites

The D5 prose gate needs the same release-stamp coverage the plugin manifests
already get, or a branch's skills contradict their own manifest (next carries
manifests at X.(Y+1).0-pre1 while un-restamped prose still says the prior
minor). Adds skills/first-officer/references/first-officer-shared-core.md to
the manifest-tag-gate arg list, the main-reconcile stamp-version call + its
git diff/commit path lists, and the next edge-bump stamp-version call + its
commit path list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fests

Pins D5's cross-file invariant in the quarantine-sanctioned home for a check
that reads an instruction file: the FO shared-core's release-stamped "required
binary minor" literal must equal the major.minor each vendored plugin manifest
parses to, and the D4 tombstone (requires-contract ">=3,<4") stays frozen on
both manifests. skills/integration's manifest tests drop the now-duplicated
tombstone assertion, keeping only the manifest-shape checks that don't need an
instruction-file read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le docs

docs/releasing.md's stamp-then-tag step now carries the FO prose file
alongside the manifests, notes the go-install proxy build's cosmetic +dev
suffix; docs/runtime-live-ci.md's --skip-contract-check example renames to
--skip-compat-check; the proof-policy worked example (docs/site/contributing/
proof-policy.md and docs/dev/README.md) rewords "the plugin manifest's
contract range bracketing the binary's contract version" to "the plugin
manifest's version sharing a major.minor with the binary's version" — the old
example named a mechanism this task deletes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…heck

Mechanical follow-through of the front-door flag rename in the (build-tag
live) runtime-live drive fixtures, so a live-tagged run of these tests still
invokes a flag the binary recognizes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t prose

Grep-level sweep for CONTRACT_VERSION / ParseRange / MalformedRange /
PluginPredatesContract / --skip-contract-check turned up three leftover
prose spots the earlier passes missed: a subtest name, a doc comment citing
the renamed MalformedVersion verdict, and pi's --help negative-assertion list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tested

resolveHealableGate's too-old-plugin arm (announce, install, re-gate once,
refuse on a second miss) had no direct test — the tooOldPluginManifest fixture
helper existed but nothing called it. Adds: gateHost stays silent for
too-old-plugin (mirrors the NoPluginFound silence test); both front doors
auto-refresh + re-gate + launch on success; --no-install refuses with the
gate's own mismatch remedy; a persisted mismatch after refresh refuses without
looping (exactly one Install call).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fixture

displayVersion()'s dev-fallback (D3) had implementation but no dedicated
test: the unit half (embeddedManifestVersion reads this checkout's own
manifest, displayVersion falls back only when unstamped) and the behavior
fixture half (an actual `go build ./cmd/spacedock` + `--version` reports
<checkout-version>+dev on line 1) were both missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
clkao pushed a commit that referenced this pull request Jul 2, 2026
@clkao
clkao temporarily deployed to CI-E2E-CODEX July 2, 2026 12:56 — with GitHub Actions Inactive
@clkao
clkao temporarily deployed to CI-E2E-OPUS July 2, 2026 21:33 — with GitHub Actions Inactive
@clkao
clkao merged commit 511dae1 into main Jul 3, 2026
11 of 13 checks passed
gcko added a commit that referenced this pull request Jul 8, 2026
Bring PR 435 up to date with main (65 commits), reconciling the bridge-seam
work with two paradigm shifts main introduced:

- Contract gate: main replaced the integer contract with minor-version
  coupling (#468). plugin.json now carries the frozen D4 tombstone
  requires-contract ">=3,<4" alongside the branch's "hooks" registration.

- FO Startup collapse: main collapsed the FO Startup recipe to <=4 steps
  ("boot identifies, engage converges") and caps first-officer-shared-core.md
  at 26755 bytes. Rewove PR 435's boot additions onto that spine:
    * launch-branch pin -> a sub-bullet of the boot-identify step
    * before-greet Bridge liveness heartbeat -> step 2b (non-counting),
      an explicit carve-out from main's "boot writes nothing" rule
    * gate drain-before-present kept in «gate.assemble-verdict»
  To fit the byte cap while preserving the opt-in fleet feature, relocated
  the Fleet Mode section to a deferred references/fo-fleet.md (idiomatic to
  main's defer-non-common-prose philosophy), rewritten against the new
  boot-identify + engage paradigm, and repointed fo-dispatch-core + SKILL.md.

.gitignore: kept the _bridge/ block, dropped main's duplicate /spacedock.

All contractlint + bridge/fleet integration tests pass; full suite green
except the pre-existing agentsview TestSurveyCodexPresenceThroughSync
(fails identically on clean main — local binary quirk).

Co-Authored-By: Claude Opus 4.8 (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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant