Replace SOMA with the LexCAT lexical runtime (v0.0.14) #6
Workflow file for this run
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
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| name: Node ${{ matrix.node }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node: [22, 24] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install root dependencies | |
| run: npm ci | |
| - name: Run offline checks | |
| run: npm run check:offline | |
| lexcat-runtimes: | |
| name: LexCAT ${{ matrix.name }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: Linux x64 | |
| os: ubuntu-latest | |
| platform: linux | |
| arch: x64 | |
| archive: lexcat-v0.0.14-linux-x86_64.tar.gz | |
| executable: lexcat | |
| - name: Linux arm64 | |
| os: ubuntu-24.04-arm | |
| platform: linux | |
| arch: arm64 | |
| archive: lexcat-v0.0.14-linux-arm64.tar.gz | |
| executable: lexcat | |
| - name: macOS arm64 | |
| os: macos-15 | |
| platform: darwin | |
| arch: arm64 | |
| archive: lexcat-v0.0.14-macos-arm64.tar.gz | |
| executable: lexcat | |
| - name: Windows x64 | |
| os: windows-latest | |
| platform: win32 | |
| arch: x64 | |
| archive: lexcat-v0.0.14-windows-x86_64.zip | |
| executable: lexcat.exe | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: 24 | |
| - name: Exercise shipped LexCAT binary | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| runtime_dir="$RUNNER_TEMP/lexcat-runtime" | |
| mkdir -p "$runtime_dir/corpus" | |
| archive="vendor/lexcat/${{ matrix.archive }}" | |
| if [[ "$archive" == *.zip ]]; then | |
| 7z x "$archive" -o"$runtime_dir" | |
| else | |
| tar -xzf "$archive" -C "$runtime_dir" | |
| fi | |
| chmod +x "$runtime_dir/${{ matrix.executable }}" | |
| # 0.0.14 stamps the real release version into the binary (upstream #234), | |
| # so --version pins both the release number and the index schema, on top | |
| # of the manifest checksum verified above. | |
| node -e ' | |
| const { createHash } = require("node:crypto"); | |
| const { readFileSync } = require("node:fs"); | |
| const manifest = JSON.parse(readFileSync("vendor/lexcat/manifest.json", "utf8")); | |
| const artifact = manifest.artifacts.find((entry) => entry.platform === process.argv[2] && entry.arch === process.argv[3]); | |
| if (!artifact) throw new Error("no vendored artifact for " + process.argv[2] + "/" + process.argv[3]); | |
| const digest = createHash("sha256").update(readFileSync(process.argv[1])).digest("hex"); | |
| if (digest !== artifact.executable_sha256) throw new Error("executable checksum mismatch: " + digest); | |
| ' "$runtime_dir/${{ matrix.executable }}" "${{ matrix.platform }}" "${{ matrix.arch }}" | |
| schema="$(node -p 'JSON.parse(require("node:fs").readFileSync("vendor/lexcat/manifest.json","utf8")).index_schema_version')" | |
| version="$(node -p 'JSON.parse(require("node:fs").readFileSync("vendor/lexcat/manifest.json","utf8")).version')" | |
| "$runtime_dir/${{ matrix.executable }}" --version | grep -q "index schema $schema" | |
| "$runtime_dir/${{ matrix.executable }}" --version | grep -q "lexcat $version" | |
| cp -R wiki-mirror/. "$runtime_dir/corpus/" | |
| cd "$runtime_dir" | |
| # Mirrors how WikiKB indexes: no config file, counts read out of the | |
| # machine-readable report. An empty corpus and a collapsed vocabulary | |
| # both build and query at exit 0, so both counts are asserted here. | |
| "./${{ matrix.executable }}" --index ci-smoke.db build corpus --json > build.json | |
| cat build.json | |
| node -e ' | |
| const { readFileSync } = require("node:fs"); | |
| const report = JSON.parse(readFileSync("build.json", "utf8")); | |
| if (!(report.chunks > 0)) throw new Error("build indexed no chunks: " + JSON.stringify(report)); | |
| if (!(report.terms > 0)) throw new Error("build indexed no terms: " + JSON.stringify(report)); | |
| ' | |
| test -f ci-smoke.db | |
| - name: Exercise LexCAT retrieval | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| runtime_dir="$RUNNER_TEMP/lexcat-runtime" | |
| cd "$runtime_dir" | |
| "./${{ matrix.executable }}" --index ci-smoke.db query "agentic workflows" --n 10 --json > hits.json | |
| # --json carries each hit's chunk text, which is how WikiKB reads | |
| # results, so the assertion never touches the on-disk schema. | |
| node -e ' | |
| const { readFileSync } = require("node:fs"); | |
| const { hits } = JSON.parse(readFileSync("hits.json", "utf8")); | |
| if (!Array.isArray(hits) || hits.length === 0) throw new Error("LexCAT returned no hits"); | |
| const matched = hits.some((hit) => String(hit.text ?? "").toLowerCase().includes("agentic workflow")); | |
| if (!matched) throw new Error("no retrieved chunk contained the query terms"); | |
| ' | |
| - name: Exercise incremental reindex | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| runtime_dir="$RUNNER_TEMP/lexcat-runtime" | |
| cd "$runtime_dir" | |
| printf -- '---\ntitle: "CI Delta"\nwikikb_path: "concepts/ci-delta.md"\n---\n\n# CI Delta\n\nA sentinel page mentioning vermiculite telemetry.\n' > corpus/ci-delta.md | |
| "./${{ matrix.executable }}" --index ci-smoke.db sync corpus --json > sync.json | |
| cat sync.json | |
| node -e ' | |
| const { readFileSync } = require("node:fs"); | |
| const report = JSON.parse(readFileSync("sync.json", "utf8")); | |
| if (!(report.chunks > 0) || !(report.terms > 0)) throw new Error("sync left an unsearchable index: " + JSON.stringify(report)); | |
| if (!(report.added >= 1)) throw new Error("sync did not report the added page: " + JSON.stringify(report)); | |
| ' | |
| "./${{ matrix.executable }}" --index ci-smoke.db query "vermiculite telemetry" --n 5 --json > delta.json | |
| # Frontmatter staged with a document must come back on the hit payload, | |
| # because that is where WikiKB reads a result's wiki path and title. | |
| node -e ' | |
| const { readFileSync } = require("node:fs"); | |
| const { hits } = JSON.parse(readFileSync("delta.json", "utf8")); | |
| const hit = hits.find((entry) => String(entry.doc_id ?? "") === "ci-delta.md"); | |
| if (!hit) throw new Error("incremental sync did not make the new page retrievable"); | |
| if (hit.payload?.fields?.wikikb_path !== "concepts/ci-delta.md") { | |
| throw new Error("frontmatter payload missing: " + JSON.stringify(hit.payload)); | |
| } | |
| ' | |
| - name: Exercise WikiKB runtime integration | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| npm ci | |
| npm run build:wkb | |
| node --test \ | |
| --test-name-pattern='vendored LexCAT indexes|concurrent retrieval' \ | |
| tools/wikikb-local/test/smoke.mjs | |
| workflows: | |
| name: Workflows and shell scripts | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Lint GitHub Actions workflows | |
| uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 | |
| with: | |
| args: >- | |
| -ignore | |
| unexpected.key.*queue.*concurrency | |
| - name: Lint shell scripts | |
| run: >- | |
| shellcheck | |
| tools/kb-search.sh | |
| tools/wikikb-local/install.sh | |
| tools/wikikb-local/wkb | |
| gh-wikikb |