updating-xport
The harness at scripts/xport.mts emits JSON reports with severity ∈ {ok, drift, error} per row. This skill consumes that JSON.
Per-kind action policy:
| Kind | Drift signal | Action |
|---|---|---|
version-pin |
Upstream commits on default ref since pinned SHA | Auto-bump per upgrade_policy: track-latest → advance to latest stable tag; major-gate → advance patch/minor only; locked → advisory only |
file-fork |
Upstream file changed since forked_at_sha |
Advisory - note in PR body; do NOT auto-merge (forks carry local deltas that need human review) |
feature-parity |
Parity score below criticality/10 floor |
Advisory - note in PR body; human decides implement vs downgrade criticality |
spec-conformance |
Spec submodule moved | Advisory - note in PR body; human decides whether to bump spec_version |
lang-parity |
Port divergence / rejected anti-pattern reintroduced |
Advisory - note in PR body; humans fix the port or update the manifest |
The common rule: version-pin is mechanical (safe to auto-apply with track-latest/major-gate policies); everything else is advisory (upstream semantics and local deltas matter, humans decide).
Forbidden:
- Never auto-edit
file-fork,feature-parity,spec-conformance, orlang-parityrows' tracked state - Never bump a
lockedversion-pin without human approval - Never skip the tag-stability filter
- Never use
npx,pnpm dlx,yarn dlx- usepnpm execorpnpm run
CI mode (CI=true or GITHUB_ACTIONS): skip per-row test validation (workflow validates at the end); emit advisory summary to $GITHUB_OUTPUT when present.
Interactive mode (default): validate each auto-bump with pnpm test before committing the next.
Phase 1 - Pre-flight
test -f xport.json || { echo "no xport.json; skill n/a"; exit 0; }
test -f xport.schema.json || { echo "xport.schema.json missing — malformed scaffolding"; exit 1; }
test -f scripts/xport.mts || { echo "scripts/xport.mts missing — malformed scaffolding"; exit 1; }
git status --porcelain | grep -v '^??' && { echo "dirty tree; aborting"; exit 1; } || true
[ "$CI" = "true" ] || [ -n "$GITHUB_ACTIONS" ] && CI_MODE=true || CI_MODE=false
Phase 2 - Collect drift
pnpm run lockstep --json > /tmp/xport-report.json
Parse reports[] from the JSON. Split into:
- auto - rows where
severity == "drift"ANDkind == "version-pin"ANDupgrade_policy∈{ "track-latest", "major-gate" } - advisory - everything else with
severity != "ok"
If both lists empty: exit 0 with "no xport drift".
Phase 3 - Auto-bump version-pin rows
For each row in auto list, in manifest declaration order:
3a. Resolve the upstream submodule + fetch tags
SUBMODULE=$(jq -r --arg a "$UPSTREAM_ALIAS" '.upstreams[$a].submodule' xport.json)
cd "$SUBMODULE"
git fetch origin --tags --quiet
OLD_SHA=$(git rev-parse HEAD)
3b. Find the target tag
Examine existing pinned_tag to identify the tag scheme, then match:
v1.2.3(v-prefixed semver)1.2.3(bare semver)<prefix>-1.2.3(project-prefixed)<prefix>_1_2_3(underscore style; curl, liburing)
For major-gate policy: parse major version from LATEST vs current pinned_tag. If majors differ, skip - add to advisory with note "major bump needs human review".
3c. Check out + capture new SHA
NEW_SHA_FOR_CHECK=$(git rev-parse "$LATEST")
[ "$OLD_SHA" = "$NEW_SHA_FOR_CHECK" ] && { cd -; continue; }
git checkout "$LATEST" --quiet
NEW_SHA=$(git rev-parse HEAD)
cd -
3d. Update xport.json + .gitmodules
Use jq for structured edit:
jq --arg id "$ROW_ID" --arg sha "$NEW_SHA" --arg tag "$LATEST" \
'(.rows[] | select(.id == $id) | .pinned_sha) = $sha
| (.rows[] | select(.id == $id) | .pinned_tag) = $tag' \
xport.json > xport.json.tmp && mv xport.json.tmp xport.json
Update .gitmodules version comment via Edit tool (NOT sed per CLAUDE.md) - replace # <prefix>-<old> with # <prefix>-<new> on the comment line above the submodule block.
3e. Validate + commit
# Confirm xport harness accepts the new state
pnpm run lockstep --json > /tmp/xport-post.json
jq --arg id "$ROW_ID" '.reports[] | select(.id == $id) | .severity' /tmp/xport-post.json
# expect "ok"
if [ "$CI_MODE" = "false" ]; then
pnpm test || {
echo "tests failed; rolling back $ROW_ID"
git checkout xport.json .gitmodules "$SUBMODULE"
continue
}
fi
git add xport.json .gitmodules "$SUBMODULE"
git commit -m "chore(deps): bump $UPSTREAM_ALIAS to $LATEST"
Record bumped row in summary accumulator.
Phase 4 - Compose advisory notes
For each row in advisory, accumulate a markdown line:
- **file-fork** `<id>`: `<local>` — <N> upstream commit(s) since <forked_at_sha[0:12]>. Review diff, cherry-pick if applicable, bump forked_at_sha.
- **feature-parity** `<id>`: parity score <score> below floor <floor>. Implement or downgrade criticality with reason.
- **spec-conformance** `<id>`: upstream spec repo moved. Review for breaking changes before bumping spec_version.
- **lang-parity** `<id>`: <details from messages[]>.
- **version-pin** `<id>`: major bump to <LATEST> — policy=major-gate requires human review.
- **version-pin** `<id>`: upgrade_policy=locked — skipped.
Phase 5 - Report + emit
Final human-readable report to stdout:
## updating-xport report
**Auto-bumped:** <N> row(s)
<list>
**Advisory (human review):** <M> row(s)
<list>
In CI mode, emit the advisory block to $GITHUB_OUTPUT (base64-encoded) under key xport-advisory so the weekly-update workflow can include it in the PR body:
if [ -n "$GITHUB_OUTPUT" ]; then
echo "xport-advisory=$(printf '%s' "$ADVISORY" | base64 | tr -d '\n')" >> "$GITHUB_OUTPUT"
fi
Emit a HANDOFF block per _shared/report-format.md:
=== HANDOFF: updating-xport ===
Status: {pass|fail}
Findings: {auto_bumped: N, advisory: M}
Summary: {one-line description}
=== END HANDOFF ===
Success Criteria
- All actionable
version-pinrows bumped atomically (one commit per row) - Advisory rows collected for PR body / workflow output
- No edits to non-version-pin row state
pnpm run lockstepexits 0 or 2 at end (never 1 - no schema errors introduced).gitmodulesversion comments synchronized withpinned_tag
Commands
pnpm run lockstep --json- drift report (consumed by this skill)jq- parse + editxport.json(structured JSON edits)git submodule status- verify submodule state after bumps
When to use
- Invoked by the
updatingumbrella skill (weekly-update workflow) - Standalone:
/updating-xportwhen syncing just the xport manifest - After manual submodule bumps, to refresh
xport.jsonmetadata