Skip to content
Open
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
dc5883d
docs: align doc claims with code (tool count + RFC-0100/-0102 accepta…
aimasteracc Jun 3, 2026
7d93bec
feat(budget): RFC-0102 nested budget{} response object + BudgetMode t…
aimasteracc Jun 3, 2026
faabe2e
feat(budget): RFC-0102 per-call budget override knob on mycelium_cont…
aimasteracc Jun 3, 2026
ccb0e4a
fix(budget): RFC-0102 cap callee_paths/caller_paths/dead_symbols/isol…
aimasteracc Jun 3, 2026
5c7af49
docs(rfc): RFC-0109 graph-list output-shape parity + budget roll-out,…
aimasteracc Jun 3, 2026
bb685de
feat(queries): RFC-0109 get_callees shared builder + object shape + b…
aimasteracc Jun 3, 2026
9bd288c
feat(queries): RFC-0109 get_callers shared builder + object shape + b…
aimasteracc Jun 3, 2026
2c13045
feat(queries): RFC-0109 get_dead_symbols shared builder + object shap…
aimasteracc Jun 3, 2026
4bdc4de
docs(adr): ADR-0010 — reject live LSP, prefer static SCIP/LSIF ingestion
aimasteracc Jun 3, 2026
f7739f8
feat(queries): RFC-0109 get_isolated_symbols shared builder + object …
aimasteracc Jun 3, 2026
96dfc9d
feat(queries): RFC-0109 get_reachable shared builder + per-call budge…
aimasteracc Jun 3, 2026
b684648
feat(queries): RFC-0109 get_reachable_to reuses reachable_payload + b…
aimasteracc Jun 3, 2026
3980863
fix(ci): bump sla_ancestors_100k macOS limit 30ms → 100ms (#508)
aimasteracc Jun 4, 2026
9b51c35
feat(queries): RFC-0109 get_all_symbols object shape + budget knob (7…
aimasteracc Jun 4, 2026
bf0399a
chore(pm): dispatch v28 — develop CI fix PR #508; ADR-0010 merged; v0…
aimasteracc Jun 4, 2026
e94acb4
chore(pm): dispatch v29 — PRs #508+#513 merged; RFC-0109 7/7 complete
aimasteracc Jun 4, 2026
c6e598c
feat(npm): RFC-0110 npm/bun CLI distribution scaffolding — Increment …
aimasteracc Jun 4, 2026
a6c36ca
ci(release): RFC-0110 build CLI binaries matrix + attach to GitHub Re…
aimasteracc Jun 4, 2026
746826d
ci(release): RFC-0110 publish-npm rewire + npm smoke test — Implement…
aimasteracc Jun 4, 2026
02b7187
chore(pm): dispatch v33 — DCO fix on release/v0.1.20; Codex P1×2 reso…
aimasteracc Jun 4, 2026
b2fe917
chore(pm): dispatch v36 — v0.2.0 release in progress; PR #522 merged
aimasteracc Jun 4, 2026
ddd6362
chore(pm): dispatch v39 — DCO fix on release/v0.2.0; PR #523 CI green…
aimasteracc Jun 4, 2026
b696953
test(mcp): add exact-count assertions to kill mutation survivors (Iss…
aimasteracc Jun 4, 2026
dff97c4
chore(pm): dispatch v40 — PR #530 merged; Issue #526 mutation fix → P…
aimasteracc Jun 4, 2026
fdd3525
ci(release): graceful npm publish — token absent + E404 scope-not-fou…
aimasteracc Jun 4, 2026
dec8c80
chore(pm): dispatch v42 — PR #533 merged; Issue #526 closed; PR #535 …
aimasteracc Jun 4, 2026
3f81241
fix(npm): use 128+signal exit code in mycelium.cjs launcher (#535)
aimasteracc Jun 4, 2026
4e60400
chore(release): back-merge release/v0.2.0 → develop (Charter §5.12 St…
aimasteracc Jun 4, 2026
e089b66
chore(pm): dispatch v46 — Codex P1+P2 fixes; v0.2.0 ceremony Steps 1+…
aimasteracc Jun 4, 2026
2a7a11b
chore(pm): dispatch v46 — PR #541 merged; security scan CLEAN; anti-p…
aimasteracc Jun 4, 2026
0554ee7
ci(dco-check): grep full body for Signed-off-by instead of trailer pa…
aimasteracc Jun 4, 2026
8418632
chore(pm): dispatch v48 — PR #542 merged; PR #544 opened (systemic DC…
aimasteracc Jun 4, 2026
0fe4f99
chore(pm): dispatch v50 — PRs #544+#545 merged; DCO fix deployed (#546)
aimasteracc Jun 4, 2026
640a8dc
chore(pm): dispatch v51 — PR #546 merged; 2 stale P2 items cleared; p…
aimasteracc Jun 4, 2026
fec60ca
chore(pm): dispatch v53 — PR #547 merged; security scan CLEAN; v0.2.1…
aimasteracc Jun 5, 2026
d598ba5
fix(release): hard-fail npm publish on error — remove E404 graceful-s…
aimasteracc Jun 5, 2026
4818da0
refactor(mcp): Issue #428 god-file-split slice 3 — extract requests.r…
aimasteracc Jun 5, 2026
1a6e3e7
feat(mcp): RFC-0094 Phase 4 — flip stdio MCP default output to text (…
aimasteracc Jun 5, 2026
3791214
chore(pm): dispatch v54+v55 — PR #550 merged (Issue #428 slice 3); Co…
aimasteracc Jun 5, 2026
9e1bd4b
feat(core): RFC-0103 — import-aware Extends-stub resolution (cross-fi…
aimasteracc Jun 5, 2026
7d9e8c0
chore(pm): dispatch v56 — PR #551 merged; RFC-0094 Phase 4 verified; …
aimasteracc Jun 5, 2026
b07a8b0
chore(pm): dispatch v58 — fix Codex P2 on #556 (stale PR #554 ref); v…
aimasteracc Jun 5, 2026
56795f4
chore(pm): dispatch v59 — release/v0.2.1 cut; PR #557 opened; fix cer…
aimasteracc Jun 5, 2026
dad6981
chore(pm): dispatch v60 — PR #558 merged; Issue #560 opened; PR #557 …
aimasteracc Jun 5, 2026
4b7bcc5
chore(pm): dispatch v61 — PR #559 Codex P1+P2 replied; PR #561 merged
aimasteracc Jun 5, 2026
8de57fa
chore(pm): dispatch v62 — PR #562 merged; Issue #560 fixed → PR #563
aimasteracc Jun 5, 2026
19fb6f1
feat(sdk): RFC-0111 Phase 1 — Node/TS thin-CLI-wrapper SDK (#559)
aimasteracc Jun 5, 2026
64e865f
feat(bindings): RFC-0111 Phase 2 — Python SDK (mycelium-rcig) (#565)
aimasteracc Jun 5, 2026
eddb7f8
chore(release): v0.3.0
aimasteracc Jun 5, 2026
38c3214
ci(release): PyPI publish via token auth (TSA-style), not Trusted Pub…
aimasteracc Jun 5, 2026
4d03f3b
chore(release): merge main (v0.2.0 ceremony) into release/v0.3.0 to u…
claude Jun 11, 2026
351e4b5
fix(ci): remove duplicate build-cli-binaries job introduced by merge
claude Jun 11, 2026
83cc68f
fix(ci): skip DCO check on release/* and hotfix/* PRs
claude Jun 11, 2026
f14f80d
fix(ci): rename mutation tee-log mutants.out→mutants.log (Issue #829)
aimasteracc Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,70 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo build --release --workspace --all-features

# RFC-0110: validate the npm packaging path end-to-end on every PR using
# the just-built linux-x64 binary — assemble the packages, install them
# (--install-links copies like a registry install), and run the launcher.
# Catches packaging/launcher regressions without a real publish.
- uses: actions/setup-node@v6
with:
node-version: '20'
- name: npm launcher unit tests
run: node --test
working-directory: npm/mycelium
# RFC-0111: SDK thin-wrapper. Unit tests are hermetic (injected spawn);
# the integration test then round-trips real JSON through the SDK using
# the release binary just built above, proving the --format json contract.
- name: SDK unit tests
run: node --test
working-directory: npm/sdk
- name: SDK integration test (against built binary)
run: node --test
working-directory: npm/sdk
env:
MYCELIUM_BIN: ${{ github.workspace }}/target/release/mycelium
- name: npm packaging smoke test (assemble → install → run)
run: |
set -euo pipefail
mkdir -p dist-bin/linux-x64
cp target/release/mycelium dist-bin/linux-x64/mycelium
node npm/scripts/build-npm.mjs --version 0.0.0-ci --bin-dir dist-bin --out dist-npm
mkdir -p smoke && cd smoke && npm init -y >/dev/null
npm install --install-links ../dist-npm/mycelium-linux-x64-gnu ../dist-npm/mycelium >/dev/null
OUT="$(node_modules/.bin/mycelium --version)"
echo "launcher output: $OUT"
echo "$OUT" | grep -qi mycelium
# RFC-0111: prove the assembled SDK resolves the prebuilt binary from its
# pinned optionalDependency (no MYCELIUM_BIN, no PATH) — the exact
# no-Cargo install path a fresh `npm i @aimasteracc/mycelium-sdk` takes.
- name: SDK packaging smoke test (assemble → install → resolve binary → query)
run: |
set -euo pipefail
mkdir -p sdk-smoke && cd sdk-smoke && npm init -y >/dev/null
npm install --install-links \
../dist-npm/mycelium-linux-x64-gnu ../dist-npm/mycelium-sdk >/dev/null
node -e '
const { Mycelium } = require("@aimasteracc/mycelium-sdk");
const m = new Mycelium({ root: "." });
m.version().then((v) => {
console.log("sdk resolved binary →", v);
if (!/^mycelium /.test(v)) process.exit(1);
}).catch((e) => { console.error(e); process.exit(1); });
'
# RFC-0111 Phase 2: Python SDK (mycelium-rcig). Unit tests are hermetic
# (injected spawn, stdlib unittest — no pip install); the integration run
# round-trips real JSON through the SDK using the release binary above.
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Python SDK unit tests
run: python -m unittest discover -s tests
working-directory: bindings/python
- name: Python SDK integration test (against built binary)
run: python -m unittest discover -s tests
working-directory: bindings/python
env:
MYCELIUM_BIN: ${{ github.workspace }}/target/release/mycelium

doc-build:
name: docs (rustdoc + mdbook)
needs: [unit]
Expand Down Expand Up @@ -217,8 +281,13 @@ jobs:
# --no-merges so historical PR merge commits (which never carried
# sign-off and predate DCO enforcement) don't fail back-merge PRs
# like release/* → develop. Authored commits must still sign off.
#
# Use full body grep instead of %(trailers:key=...) because GitHub
# squash-merge embeds Signed-off-by lines in the middle of the body
# (between individual commit entries) rather than as trailing lines,
# so the trailer parser misses them and false-fails those commits.
for sha in $(git rev-list --no-merges ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}); do
if ! git log -1 --format='%(trailers:key=Signed-off-by,valueonly)' $sha | grep -q .; then
if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then
echo "::error::Commit $sha lacks Signed-off-by trailer (DCO)."
MISSING=$((MISSING+1))
fi
Expand Down
211 changes: 192 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,35 @@ jobs:
needs: validate
uses: ./.github/workflows/ci.yml

check-npm-token:
# Preflight (RFC-0110): warn when NPM_TOKEN is absent so the operator sees
# a clear diagnostic before the publish jobs run. Graceful exit (no hard
# failure) so a missing token produces a SKIPPED npm publish rather than a
# failing release — crates.io and PyPI can still complete independently.
# Charter §5.12: every check must be SUCCESS or SKIPPED before merging
# release/* to main, so this job must never exit non-zero.
name: preflight (npm token present)
needs: validate
runs-on: ubuntu-latest
environment: npm
steps:
- name: Verify NPM_TOKEN is configured
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "${NPM_TOKEN:-}" ]; then
echo "::warning::NPM_TOKEN is not set in the 'npm' environment — npm publish will be skipped. Configure it in repo Settings → Environments → npm."
else
echo "NPM_TOKEN present."
fi

publish-crates:
name: publish to crates.io
needs: [validate, quality-recheck]
# Gate the first irreversible publish on the CLI binaries building
# successfully (RFC-0110 / Codex #519 P1): if any platform build fails we
# must NOT publish to a registry, or the release is partial (crates live but
# binaries missing). npm/pypi need publish-crates, so they are gated too.
needs: [validate, quality-recheck, build-cli-binaries]
runs-on: ubuntu-latest
timeout-minutes: 60
environment: crates-io
Expand Down Expand Up @@ -145,50 +171,172 @@ jobs:

publish-npm:
name: publish to npm
needs: [validate, quality-recheck, publish-crates]
# RFC-0110: assemble the per-platform + launcher packages from the prebuilt
# binaries and publish them. needs build-cli-binaries for the artifacts.
needs: [validate, quality-recheck, publish-crates, build-cli-binaries]
runs-on: ubuntu-latest
timeout-minutes: 30
environment: npm
permissions:
id-token: write # for provenance
env:
VERSION: ${{ needs.validate.outputs.version }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: |
if [ -d "bindings/node" ]; then
cd bindings/node
npm ci
npm publish --access public --provenance
else
echo "no bindings/node yet — skipping"
fi
- name: Download CLI binaries
uses: actions/download-artifact@v7
with:
pattern: cli-*
path: dl
- name: Reshape artifacts into a platform-keyed bin dir
run: |
mkdir -p dist-bin
for d in dl/cli-*; do
key="${d#dl/cli-}"
mkdir -p "dist-bin/$key"
cp "$d"/* "dist-bin/$key/"
done
ls -R dist-bin
- name: Assemble npm packages
run: node npm/scripts/build-npm.mjs --version "$VERSION" --bin-dir dist-bin --out dist-npm
- name: Publish packages (idempotent — platform packages first)
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "${NODE_AUTH_TOKEN:-}" ]; then
echo "::warning::NPM_TOKEN is not set — skipping npm publish. Configure it in repo Settings → Environments → npm."
exit 0
Comment thread
aimasteracc marked this conversation as resolved.
fi
publish_one() {
dir="$1"
name=$(node -p "require('./$dir/package.json').name")
ver=$(node -p "require('./$dir/package.json').version")
if npm view "$name@$ver" version >/dev/null 2>&1; then
echo "$name@$ver already on npm; skipping"
return 0
fi
if ! ( cd "$dir" && npm publish --access public --provenance ) 2>/tmp/npm_pub_err.log; then
# Issue #534: the @aimasteracc scope is registered and NPM_TOKEN
# authenticates (npm whoami -> aimasteracc), so ANY publish failure
# is a real error — fail the release loudly. The prior E404
# graceful-skip (PR #533) masked a non-authenticating token and
# produced false-green releases (the v0.2.0 saga). Never again.
cat /tmp/npm_pub_err.log >&2
return 1
fi
}
# Platform packages must exist before the main package and the SDK
# (whose optionalDependencies reference them). The SDK is published
# explicitly after the main package, so skip it in the platform glob.
for dir in dist-npm/mycelium-*; do
[ -d "$dir" ] || continue
[ "$dir" = "dist-npm/mycelium-sdk" ] && continue
publish_one "$dir"
done
publish_one "dist-npm/mycelium"
# RFC-0111: thin-CLI-wrapper SDK, published from the same release.
publish_one "dist-npm/mycelium-sdk"

publish-pypi:
name: publish to PyPI
needs: [validate, quality-recheck, publish-crates]
# GITFLOW registry order is crates.io → npm → PyPI: depend on publish-npm so
# a failed/incomplete npm release blocks PyPI (no partial registry release).
needs: [validate, quality-recheck, publish-crates, publish-npm]
runs-on: ubuntu-latest
timeout-minutes: 30
environment: pypi
permissions:
id-token: write # for Trusted Publishers
env:
VERSION: ${{ needs.validate.outputs.version }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: |
if [ -d "bindings/python" ]; then
pip install maturin
cd bindings/python
maturin publish
else
# RFC-0111 Phase 2: the Python SDK (mycelium-rcig) is a pure-Python thin
# CLI wrapper — built with the standard `build` backend, not maturin
# (there is no Rust extension). The version is pinned to the release
# version; publishing is idempotent via skip-existing.
- name: Build the pure-Python SDK wheel
id: build
run: |
set -euo pipefail
if [ ! -d "bindings/python" ]; then
echo "no bindings/python yet — skipping"
echo "built=false" >> "$GITHUB_OUTPUT"
exit 0
fi
cd bindings/python
sed -i -E "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
sed -i -E "s/^__version__ = \".*\"/__version__ = \"$VERSION\"/" mycelium_rcig/__init__.py
python -m pip install --upgrade build
python -m build
- name: Publish to PyPI (Trusted Publishers, idempotent)
if: steps.build.outputs.built != 'false'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: bindings/python/dist
skip-existing: true

build-cli-binaries:
# RFC-0110: cross-compile the `mycelium` CLI for each distributed platform,
# upload each as a workflow artifact (consumed by publish-npm in a follow-up
# and attached to the GitHub Release below). Native builds for 4 targets;
# `cross` handles the linux-arm64 C toolchain (tree-sitter grammars are C).
name: build CLI binary (${{ matrix.key }})
needs: [validate, quality-recheck]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# Both macOS targets build on macos-14 (arm64, plentiful runners): arm64
# natively, x86_64 by cross-compiling with the universal Apple toolchain
# (`cargo build --target x86_64-apple-darwin` — verified locally to
# produce an x86_64 Mach-O incl. tree-sitter C). Avoids the deprecated /
# scarce macos-13 (Intel) runner queue that stalled the release ~20min.
include:
- { key: darwin-arm64, os: macos-14, target: aarch64-apple-darwin, exe: mycelium, cross: false }
- { key: darwin-x64, os: macos-14, target: x86_64-apple-darwin, exe: mycelium, cross: false }
- { key: linux-x64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, exe: mycelium, cross: false }
- { key: linux-arm64, os: ubuntu-latest, target: aarch64-unknown-linux-gnu, exe: mycelium, cross: true }
- { key: win32-x64, os: windows-latest, target: x86_64-pc-windows-msvc, exe: mycelium.exe, cross: false }
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: release-${{ matrix.target }}
- name: Install cross (linux-arm64)
if: ${{ matrix.cross }}
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Build release binary
shell: bash
run: |
if [ "${{ matrix.cross }}" = "true" ]; then
cross build --release -p mycelium-rcig-cli --target ${{ matrix.target }}
else
cargo build --release -p mycelium-rcig-cli --target ${{ matrix.target }}
fi
- name: Stage binary under its platform key
shell: bash
run: |
mkdir -p "dist-bin/${{ matrix.key }}"
cp "target/${{ matrix.target }}/release/${{ matrix.exe }}" "dist-bin/${{ matrix.key }}/${{ matrix.exe }}"
- uses: actions/upload-artifact@v7
with:
name: cli-${{ matrix.key }}
path: dist-bin/${{ matrix.key }}/${{ matrix.exe }}
if-no-files-found: error

finalize:
name: merge to main, tag, GitHub Release
Expand All @@ -198,7 +346,7 @@ jobs:
# (scripts/release-ceremony.sh) or a manual workflow_dispatch serves as
# the explicit human authorization step.
if: github.event_name == 'workflow_dispatch'
needs: [validate, publish-crates, publish-npm, publish-pypi]
needs: [validate, publish-crates, publish-npm, publish-pypi, build-cli-binaries]
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
Expand Down Expand Up @@ -263,13 +411,38 @@ jobs:
git tag -a "v$VERSION" -m "Release v$VERSION"
git push origin "v$VERSION"

# Step D — Create the GitHub Release on the tag reachable from main.
# Step D — Download the per-platform CLI binaries and rename them with a
# platform suffix (the raw artifacts are all named `mycelium`/`.exe`, which
# would collide as release assets). RFC-0110.
- name: Download CLI binaries
uses: actions/download-artifact@v7
with:
pattern: cli-*
path: dist-release
- name: Collect release assets
shell: bash
run: |
mkdir -p release-assets
for d in dist-release/cli-*; do
key="${d#dist-release/cli-}"
if [ -f "$d/mycelium.exe" ]; then
cp "$d/mycelium.exe" "release-assets/mycelium-$key.exe"
elif [ -f "$d/mycelium" ]; then
cp "$d/mycelium" "release-assets/mycelium-$key"
fi
done
ls -la release-assets

# Step E — Create the GitHub Release on the tag reachable from main,
# attaching the per-platform binaries (a download path for users who do
# not use npm/bun/cargo).
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
tag_name: v${{ env.VERSION }}
generate_release_notes: true
target_commitish: main
files: release-assets/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
**/*.rs.bk
Cargo.lock.bak

# npm distribution build artifacts (RFC-0110) — assembled in CI, never committed
/dist-bin/
/dist-npm/
node_modules/

# IDE
/.idea/
/.vscode/*
Expand Down
Loading
Loading