Merge pull request #1514 from Hellblazer/release/v7.35.0 #242
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |