Skip to content

Merge pull request #1514 from Hellblazer/release/v7.35.0 #242

Merge pull request #1514 from Hellblazer/release/v7.35.0

Merge pull request #1514 from Hellblazer/release/v7.35.0 #242

# Plugin drift ledger (nexus-ut7ek).
#
# marketplace.json pins plugins[].source.ref to an immutable release tag, so
# Claude Code loads hooks / commands / skills / agents from THAT TAG, never from
# the working tree. Guard changes on develop are therefore INERT in every live
# session until a release ships. On 2026-07-25 three guards were merged, closed
# as "mechanized", and protecting nothing -- a subagent then ran `git stash -u`
# straight past a guard that explicitly covers `stash`.
#
# The ledger (conexus/PENDING_RELEASE.md) requires that drift be DECLARED. This
# workflow is what makes that requirement real on the PR path.
#
# WHY ITS OWN WORKFLOW rather than a job in ci.yml:
# * The drift tests need the pinned TAG resolvable. ci.yml's `test` job checks
# out shallow with no tags, deliberately (nexus-dhs30 -- "every push must
# not pay for a history fetch"). So the tests SKIPPED in the only CI job
# that collected them: the guard against believed-live-but-inert code was
# itself believed-CI-mechanized and was not. Found by review, not by tests.
# * Re-adding tags to `test` would undo that same-day cost decision for the
# whole matrix.
# * A job in ci.yml cannot use native per-path filtering; ci.yml's existing
# dorny/paths-filter pattern needs `fetch-depth: 0`, which would cost more
# than this entire job. Workflow-level `paths:` is free.
#
# COST: runs ONLY when the plugin surface or the pin itself changes. Depth-1
# checkout plus a fetch of EXACTLY the pinned tags (not fetch-depth:0, not
# fetch-tags -- both pull far more than needed). One test file. No matrix, no
# native build, no service, no PG.
name: plugin drift ledger
on:
push:
branches: [develop]
paths: &surface
- 'conexus/hooks/**'
- 'conexus/commands/**'
- 'conexus/skills/**'
- 'conexus/agents/**'
- 'conexus/resources/**'
- 'conexus/PENDING_RELEASE.md'
- 'sn/hooks/**'
- 'sn/.mcp.json'
- 'conexus/.mcp.json'
- '.claude-plugin/marketplace.json'
- 'tests/test_plugin_release_drift_ledger.py'
- '.github/workflows/plugin-drift-ledger.yml'
pull_request:
branches: [main, develop]
paths: *surface
concurrency:
group: plugin-drift-ledger-${{ github.ref }}
cancel-in-progress: true
jobs:
ledger:
name: pinned-tag freshness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Fetch exactly the pinned release tags
env:
# The proof's fallback target before the anchored tag exists is the
# cut PR's HEAD COMMIT (RDR-197 anchoring rule, nexus-a2wmi.4).
# actions/checkout resolves the merge ref at depth 1 on
# pull_request, so the head sha is not in the checkout unless
# fetched explicitly.
PR_HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }}
run: |
set -euo pipefail
# RDR-197 (nexus-a2wmi.4): the channel's checks need MORE than the
# pinned refs — the BASE CLIENT TAG (blind-checkout sentinel and the
# wheel-surface proof's range base; in the both-anchored state no
# pinned ref is a client tag, so without this it is never fetched).
# NOT `fetch-tags: true` on the checkout: release.yml:58-65 records
# that a depth-1 checkout's fetch-tags does NOT deliver tags
# (release.yml chose fetch-depth: 0, unaffordable here — see the
# COST header). Explicit shallow tag fetch instead; if it proves not
# to deliver the tag objects in practice, widen THIS fetch and
# record which form was needed.
git fetch --depth=1 --tags --force origin
if [ -n "${PR_HEAD_SHA:-}" ]; then
git fetch --depth=1 origin "$PR_HEAD_SHA"
fi
refs=$(python3 -c "
import json
d = json.load(open('.claude-plugin/marketplace.json'))
print(' '.join(sorted({p['source']['ref'] for p in d.get('plugins', [])
if isinstance(p.get('source'), dict) and p['source'].get('ref')})))
")
echo "pinned refs: $refs"
# A marketplace with no pinned ref is a defect, not a free pass.
test -n "$refs"
version=$(python3 -c "
import tomllib
print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])
")
version_re=$(printf '%s' "$version" | sed 's/\./\\./g')
for ref in $refs; do
if git fetch --depth=1 origin "refs/tags/$ref:refs/tags/$ref"; then
continue
fi
# RELEASE WINDOW (first hit at the 7.0.0 cut): on the release
# branch the pin names the ABOUT-TO-BE-CUT tag — created only
# after the PR merges — so ref==v<pyproject version> with the
# tag absent is the documented sequencing, not drift. The
# in-repo tests (_in_release_window) apply the same contract:
# in the window the ledger must be EMPTY, and that IS checked.
# The ANCHORED form plugin-v<pyproject>-<n> is the same
# sequencing on a plugin-cut PR (RDR-197 invariant W; this site
# runs on the cut PR by trigger, so it needs the shape check,
# not the branch check — the in-repo per-plugin predicate
# applies all four window conditions). Any other missing tag is
# still a hard failure.
if [ "$ref" = "v$version" ]; then
echo "release window: $ref == v<pyproject> and not yet cut — tolerated"
elif printf '%s' "$ref" | grep -qE "^plugin-v${version_re}-[1-9][0-9]*$"; then
echo "plugin release window: $ref is the anchored form for v$version and not yet cut — tolerated"
else
echo "pinned ref $ref does not exist and is NOT this release's own tag" >&2
exit 1
fi
done
git tag -l
- uses: astral-sh/setup-uv@v7
- name: Run the ledger contract
env:
# Taglessness is a FAILURE here, never a skip. If the fetch step above
# silently breaks, a skip would report green having checked nothing --
# precisely the bug this ledger exists to catch, one level up.
NX_REQUIRE_PLUGIN_DRIFT_CHECK: "1"
# The autouse engine-substrate fixture (nexus-i711w) routes EVERY
# test to the engine unless told otherwise. This minimal runner has
# no jar/PG by design (see the COST header), so without the opt-out
# all 12 tests skip at fixture setup and the job reports green
# having checked nothing -- the require-flag above cannot help,
# because it lives inside the test bodies and a fixture-level skip
# preempts it. Observed live (nexus-05m1i): run 31223172021 on
# develop@599e4980 ended "12 skipped in 0.23s", SUCCESS, while that
# very commit carried undeclared SKILL.md drift. These tests are
# pure git/YAML; they need no substrate anywhere.
NX_TEST_T2_SUBSTRATE: "none"
run: |
set -euo pipefail
uv run pytest tests/test_plugin_release_drift_ledger.py -q -rs 2>&1 \
| tee pytest-drift-ledger.txt
# Non-vacuity belt (nexus-05m1i): on this gate runner a skip is
# NEVER acceptable -- the taglessness skip already hard-fails via
# NX_REQUIRE_PLUGIN_DRIFT_CHECK, so any skip that still appears
# means a NEW vacuity mechanism (fixture- or collection-level)
# crept in around the flag. Fail loudly instead of green-nothing.
if grep -qE '[0-9]+ skipped' pytest-drift-ledger.txt; then
echo "VACUITY: drift-ledger tests skipped on the gate runner -- " \
"the job proved nothing. See nexus-05m1i." >&2
exit 1
fi