diff --git a/.claude/skills/apr-dogfood/SKILL.md b/.claude/skills/apr-dogfood/SKILL.md index 3221b34fde..97abd3384c 100644 --- a/.claude/skills/apr-dogfood/SKILL.md +++ b/.claude/skills/apr-dogfood/SKILL.md @@ -1591,7 +1591,7 @@ The nine clusters at zero, largest first: `http-orchestrate-banco` (95), `test-harness` (49), `rag-eval` (44), `qa-cgp` (37), `simulation` (18), `orchestrate-pacha-secrets` (17). -**Report both numbers or neither (T2).** "5 of 14 clusters gated (35.7%)" without "144 of 864 features gated (16.7%)" beside it is a proxy masquerading as coverage, and the gate refuses to emit it — *on this line too*. +**Report both numbers or neither (T2).** "5 of 14 clusters gated (35.7%)" without "144 of 894 features gated (16.1%)" beside it is a proxy masquerading as coverage, and the gate refuses to emit it — *on this line too*. The rule is about the NUMBER, not about a phrasing, and it is enforced on every surface that can emit one: the gate's own report, the receipt, the output of diff --git a/.claude/skills/pr-review/SKILL.md b/.claude/skills/pr-review/SKILL.md index fd15cd5abe..01dcf7679b 100644 --- a/.claude/skills/pr-review/SKILL.md +++ b/.claude/skills/pr-review/SKILL.md @@ -22,7 +22,7 @@ difference is a defect in this file. **Contract**: `contracts/pr-review-skill-v2.yaml` (§1 grounding, §7 blocking, §8 metrics) **Guard**: `scripts/check_pr_review_receipt.sh` — it validates what you emit here, it has its own positive controls, and its mutation set (`scripts/mutate-guard.sh`) reports -233/233. **Run it on your own receipt before you post anything.** +237/237. **Run it on your own receipt before you post anything.** ## Context @@ -438,7 +438,7 @@ Bash guards are exercised with `bats-core` fixtures. For the receipt guard itsel mutation set already exists and is a derivation, not a list: ```bash -bash scripts/mutate-guard.sh # 233/233 on scripts/check_pr_review_receipt.sh +bash scripts/mutate-guard.sh # 237/237 on scripts/check_pr_review_receipt.sh ``` **`attempted: 0` with `status: consulted` is rejected** (fixture row 2). A mutation set @@ -1192,7 +1192,7 @@ the real merged commits, not from the spec's reasoning about them. ## §13 Autonomous merge on quorum (DESIGNED AND BUILT, **NOT ARMED**) Spec §13. Operator instruction, 2026-08-31: PRs auto-merge once the review quorum passes. -The mechanism exists — `scripts/pr_review_quorum_arm.sh`, a table of 83 rows, a 134-mutant +The mechanism exists — `scripts/pr_review_quorum_arm.sh`, a table of 90 rows, a 134-mutant set at 100% — and **it is reachable from no workflow that can merge anything.** §13.11 is the arming ladder; rung 0 is where this file is written. @@ -1345,7 +1345,7 @@ That is the first falsifiable property of the section, and `q-44` plus the ### §13.9 Verifying the mechanism ```bash -bats tests/pr-review-quorum.bats # 83 rows: one per refusal path, four that PERMIT +bats tests/pr-review-quorum.bats # 90 rows: one per refusal path, four that PERMIT bash scripts/mutate_quorum_arm.sh # 134/134 — §13.10 fixes this at one, no ratchet bash scripts/mutate_quorum_arm.sh --list # the catalogue, no mutants run ``` diff --git a/.claude/skills/pre-release/SKILL.md b/.claude/skills/pre-release/SKILL.md index d9d1cd1d47..cb39fc0254 100644 --- a/.claude/skills/pre-release/SKILL.md +++ b/.claude/skills/pre-release/SKILL.md @@ -326,6 +326,11 @@ regression this repo has on record, and its power *falls* as data accumulates Richer fields (surface counts, findings, notable, verdict) are encouraged — the receipts already under `evidence/dogfood/` are the worked examples. +**The tracked implementation of this step** is `scripts/release/host_receipt.sh`, which +`scripts/release/autopilot.sh` ships to each host after publish (#3731). Never hand-roll +the sweep; `scripts/check_dogfood_shim.sh` row 1b allows those two scripts only while +this paragraph names them. + **The sweep must `cargo install` the PUBLISHED crate**, not build the local tree. Building the tree tests what you have; installing tests what a user gets. On a box with a pre-existing `apr` the install correctly fails closed (rc=101) *before* compiling — use diff --git a/.gitattributes b/.gitattributes index 579e0ba6cd..8302eb59e3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -23,3 +23,8 @@ # # scripts/check_append_only_ledgers.sh holds that distinction as a case table. docs/audits/*.jsonl merge=union +# Append-only ledgers one level down. `docs/audits/*.jsonl` does not cross a `/`, +# so these two resolved no merge driver (check_append_only_ledgers, after the +# main merge into #4431). Named file by file: the scope rule above still holds. +docs/audits/review-corpus/corpus-v1.jsonl merge=union +docs/audits/rex-001/rex-04-admission.jsonl merge=union diff --git a/.githooks/pre-push b/.githooks/pre-push index e2686ad76f..d51a06732c 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -19,4 +19,14 @@ cargo doc --no-deps -q 2>/dev/null || echo "WARN: cargo doc failed" echo "── ComputeBrick Compliance ──" pmat comply check --failures-only 2>/dev/null || true +echo "── #4415/#4416: CI guard steps (scripts/ci_guards.sh, the script CI runs) ──" +# The drift check is a YAML parse, so it always runs. The full guard run takes +# many minutes; opt in with APR_PREPUSH_GUARDS=1 (or run `make guards-local`). +rc=0; bash scripts/ci_guards.sh --check-coverage || rc=$? +[ "$rc" -eq 1 ] && exit 1 +[ "$rc" -eq 0 ] || echo "WARN: guard drift check could not run (rc=$rc)" +if [ "${APR_PREPUSH_GUARDS:-0}" = 1 ]; then + bash scripts/ci_guards.sh || exit 1 +fi + echo "✅ Pre-push passed" diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index f1162eaef4..074889486a 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -24,3 +24,5 @@ self-hosted-runner: - apple-silicon - m4 - mini + # #4292 / infra#1042: the mini's dedicated repo-level release runner + - mini-builder diff --git a/.github/workflows/binary-release.yml b/.github/workflows/binary-release.yml index 3f461c4f5d..aaa8635ddb 100644 --- a/.github/workflows/binary-release.yml +++ b/.github/workflows/binary-release.yml @@ -53,6 +53,10 @@ name: Binary Release # - `release: published` — fires automatically on each tagged release # - `workflow_dispatch` — manual re-run / backfill, takes a tag input # +# All the other workspace [[bin]]s (G5, #4189): `build-all-bins` ships each one, derived by +# scripts/nightly_manifest.py bins exactly as nightly.yml does, as --.tar.gz for +# x86_64/aarch64 gnu, each checked by asset_version_check.sh under its own name before upload. +# # pv targets: 4 Linux (x86_64/aarch64 × musl/gnu), each built natively on # the box of its architecture (x86_64 on yoga, aarch64 on gx10): gnu inside # rust:1.93.0-bullseye, musl inside rust:1.93.0-bookworm. musl variants are static and ideal for Docker / @@ -79,6 +83,34 @@ concurrency: jobs: + # A release that already carries every asset is NOT rebuilt (#4286). A promoted final + # is its rc's exact bytes (scripts/release/promote_rc.sh); publishing it with a + # personal token fires `release: published` here, and the upload steps below delete + # and replace same-named assets, so an ungated rebuild would silently swap the bytes + # that were dogfooded for new ones. With all eighteen present the build jobs skip and + # verify + smoke still run, on the promoted bytes. A partial release (a lane died) + # still rebuilds. An unreadable release (exit 2) fails this job, so the builds skip: + # when in doubt, never overwrite. + assets: + name: Does the release already carry every asset? + runs-on: [self-hosted, Linux, X64, clean-room] + timeout-minutes: 5 + outputs: + present: ${{ steps.check.outputs.present }} + steps: + - uses: actions/checkout@v7 + - id: check + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name || inputs.tag }} + run: | + setsid --wait bash scripts/check_release_assets.sh "$TAG"; rc=$? + case "$rc" in + 0) echo "present=true" >> "$GITHUB_OUTPUT"; echo "$TAG already carries every asset: nothing is rebuilt" >> "$GITHUB_STEP_SUMMARY" ;; + 1) echo "present=false" >> "$GITHUB_OUTPUT" ;; + *) echo "::error::could not read release $TAG (rc=$rc); refusing to build over assets that may exist"; exit 1 ;; + esac + # The pv lane on the fleet (#3073; operator rule 2026-09-10: no GitHub-hosted runners). It ran on # ubuntu-latest, with cross for aarch64. Each target now builds natively on the box of its architecture # (x86_64 on yoga, aarch64 on gx10) in a sibling container, the pattern of build-apr-cpu: gnu targets @@ -86,6 +118,8 @@ jobs: # well, so no floor rises), musl targets inside rust:1.93.0-bookworm (static). Asset names unchanged. build: name: pv ${{ matrix.target }} on ${{ matrix.host }} + needs: assets + if: needs.assets.outputs.present == 'false' runs-on: ${{ fromJSON(matrix.labels) }} strategy: fail-fast: false @@ -124,6 +158,20 @@ jobs: with: ref: ${{ steps.tag.outputs.tag }} + # #4110/#4290: an rc asset's crate version IS its tag. The rc commit's Cargo.toml says + # X.Y.Z, so without this every vX.Y.Z-rc.N asset printed bare `X.Y.Z` (v0.69.3-rc.2: + # `apr 0.69.3 (v0.69.3+no-git)`). The stamper comes from THIS workflow's commit, not the + # tag's tree, which an rc cut from release/X.Y.Z predates. A final tag stamps nothing. + # verify-apr-assets then requires `apr --version` to equal the tag exactly. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + git show "FETCH_HEAD:scripts/release/stamp_rc_version.sh" > "$RUNNER_TEMP/stamp_rc_version.sh" + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + - name: Preflight - this box has the tools this job assumes run: bash scripts/ci_self_hosted_preflight.sh --need docker objdump @@ -160,7 +208,7 @@ jobs: esac ldd --version | head -1 > /workspace/target/BUILDER_GLIBC rustc --version > /workspace/target/BUILDER_RUSTC - cargo build --release --locked --bin pv -p aprender-contracts-cli --target "$T" + cargo build --release --locked --bin pv -p aprender-contracts-cli --features update-check,build-sha --target "$T" strip "/workspace/target/$T/release/pv"' mkdir -p "target/$T/release" cp "$CACHE/target/$T/release/pv" "target/$T/release/pv" @@ -220,7 +268,10 @@ jobs: run: | TAG="${{ steps.tag.outputs.tag }}" API="https://api.github.com/repos/${GITHUB_REPOSITORY}" - rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases/tags/$TAG") + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327), + # and releases/tags/ never returns a draft + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') for f in "${{ steps.package.outputs.archive }}" "${{ steps.package.outputs.sha }}"; do # --clobber semantics: delete an asset of the same name first @@ -231,8 +282,182 @@ jobs: | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' done + # G5 (#4189): every other workspace [[bin]] on every tag. The set is the nightly's, derived by + # `scripts/nightly_manifest.py bins` from `cargo metadata` of the TAG tree -- never a hand list, so a + # new [[bin]] ships on the next tag with no edit here. apr and pv are the only bins left out: their + # dedicated lanes above ship them (cuda/cpu/darwin apr, musl+gnu pv), and a second `pv--` + # from this lane would clobber the pv lane's asset of the same name. gnu only, inside + # rust:1.93.0-bullseye (the 2.31 floor), natively on the box of each architecture as the pv lane. + # Each bin must answer `--help`, and its `--version` must print ` ()` -- + # asset_version_check.sh with the bin's own name, the check verify-apr-assets makes of apr -- + # before anything is uploaded; nightly_manifest.py smoke then gives the nightly's verdicts on the + # same executables. Assets are `--.tar.gz` + .sha256: they carry the tag once, so + # promote_rc.sh renames them for a final like every other asset. + build-all-bins: + name: all [[bin]]s ${{ matrix.target }} on ${{ matrix.host }} + needs: assets + if: needs.assets.outputs.present == 'false' + runs-on: ${{ fromJSON(matrix.labels) }} + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + host: yoga + labels: '["self-hosted", "Linux", "X64", "cuda", "yoga"]' + - target: aarch64-unknown-linux-gnu + host: gx10 + labels: '["self-hosted", "Linux", "ARM64", "cuda", "gx10"]' + steps: + - name: Resolve release tag + id: tag + run: | + TAG="${{ github.event.release.tag_name || inputs.tag }}" + if [ -z "$TAG" ]; then + echo "::error::No tag resolved (release event missing tag_name and no dispatch input)" + exit 1 + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + - name: Checkout at tag + uses: actions/checkout@v7 + with: + ref: ${{ steps.tag.outputs.tag }} + + # As every lane: the stamper and the checker come from THIS workflow's commit, not the tag tree. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + git show "FETCH_HEAD:scripts/release/asset_version_check.sh" > "$RUNNER_TEMP/asset_version_check.sh" + git show "FETCH_HEAD:scripts/nightly_manifest.py" > "$RUNNER_TEMP/nightly_manifest.py" + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + git show "FETCH_HEAD:scripts/release/stamp_rc_version.sh" > "$RUNNER_TEMP/stamp_rc_version.sh" + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + + - name: Preflight - this box has the tools this job assumes + run: bash scripts/ci_self_hosted_preflight.sh --need docker objdump + + - name: Build every [[bin]] inside rust:1.93.0-bullseye (glibc 2.31 floor) + run: | + set -euo pipefail + DOCKER=docker; docker ps >/dev/null 2>&1 || DOCKER="sudo -n docker" + T="${{ matrix.target }}" + CACHE="$(cd "$GITHUB_WORKSPACE/../.." && pwd)/cache-allbins/$T" + mkdir -p "$CACHE/registry" "$CACHE/target" "$GITHUB_WORKSPACE/target" + $DOCKER run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -v "$CACHE/registry:/usr/local/cargo/registry" \ + -w /workspace \ + rust:1.93.0-bullseye \ + cargo metadata --locked --no-deps --format-version 1 > "$RUNNER_TEMP/metadata.json" + ALL=$(python3 "$RUNNER_TEMP/nightly_manifest.py" bins --metadata "$RUNNER_TEMP/metadata.json") + BINS=$(echo "$ALL" | tr ',' '\n' | grep -vxE 'apr|pv' | paste -sd, -) + if [ -z "$BINS" ]; then echo "::error::no [[bin]] derived from the tag tree"; exit 1; fi + # the nightly's own cargo selection, verbatim (it builds apr and pv too; they are not packaged) + ARGS=$(python3 "$RUNNER_TEMP/nightly_manifest.py" bins --metadata "$RUNNER_TEMP/metadata.json" --format cargo) + echo "BINS=$BINS" >> "$GITHUB_ENV" + echo "Shipping $(echo "$BINS" | tr ',' '\n' | wc -l) bins (+ apr, pv from their own lanes): $BINS" + APR_SHA=$(git rev-parse --short=9 HEAD) + $DOCKER run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -v "$CACHE/registry:/usr/local/cargo/registry" \ + -v "$CACHE/target:/workspace/target" \ + -w /workspace \ + -e CARGO_TARGET_DIR=/workspace/target \ + -e CARGO_INCREMENTAL=0 \ + -e APR_GIT_SHA_OVERRIDE="$APR_SHA" \ + -e T="$T" \ + -e ARGS="$ARGS" \ + rust:1.93.0-bullseye \ + sh -c 'set -e; ldd --version | head -1; cargo build --locked --release $ARGS --target "$T"' + mkdir -p "target/$T/release" + # a bin that did not build fails this cp, which fails the step + for b in ${BINS//,/ }; do cp "$CACHE/target/$T/release/$b" "target/$T/release/$b"; done + + # Checks that can fail, on the box that built them (native arch): every bin answers --help, its + # --version belongs to the tag (stdout only: aprender-ptx-debug's deprecation notice is stderr), + # the nightly's verdicts hold, and no gnu asset imports above GLIBC_2.31. + - name: Every bin runs, belongs to the tag, and keeps the 2.31 floor + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -uo pipefail + T="${{ matrix.target }}"; SHA=$(git rev-parse HEAD); bad=0 + V=$(awk '/^\[workspace.package\]/{p=1;next} /^\[/{p=0} p&&/^version *=/{gsub(/"/,"",$3);print $3;exit}' Cargo.toml) + for b in ${BINS//,/ }; do + BIN="target/$T/release/$b" + line=$(timeout 30 "$BIN" --version 2>/dev/null | head -1) + if ! bash "$RUNNER_TEMP/asset_version_check.sh" "$TAG" "$SHA" "$line" "$b"; then bad=1; fi + if ! timeout 30 "$BIN" --help > /dev/null 2>&1; then echo "::error::$b --help failed"; bad=1; fi + FLOOR=$(objdump -T "$BIN" | grep -oE 'GLIBC_[0-9]+\.[0-9]+(\.[0-9]+)?' | sort -u -t_ -k2 -V | tail -1) + got=${FLOOR#GLIBC_} + if [ -n "$got" ] && [ "$(printf '%s\n%s\n' "$got" 2.31 | sort -V | tail -1)" != 2.31 ]; then + echo "::error::$b needs $FLOOR, above the GLIBC_2.31 floor"; bad=1 + fi + done + python3 "$RUNNER_TEMP/nightly_manifest.py" smoke --sha "$SHA" --bins "$BINS" \ + --bin-dir "target/$T/release" --version "$V" > "$RUNNER_TEMP/smoke.json" || bad=1 + { echo "### all [[bin]]s $T: $(echo "$BINS" | tr ',' '\n' | wc -l) bins at $TAG" + echo '```json'; cat "$RUNNER_TEMP/smoke.json"; echo '```'; } >> "$GITHUB_STEP_SUMMARY" + if [ "$bad" -ne 0 ]; then echo "::error::a [[bin]] does not belong to $TAG; nothing is uploaded"; exit 1; fi + + - name: Package archives + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + T="${{ matrix.target }}" + mkdir -p dist + for b in ${BINS//,/ }; do + A="$b-$TAG-$T" + mkdir -p "$A" + cp "target/$T/release/$b" "$A/" + for f in README.md LICENSE LICENSE-MIT LICENSE-APACHE; do + if [ -f "$f" ]; then cp "$f" "$A/"; fi + done + tar czf "dist/$A.tar.gz" "$A" + (cd dist && shasum -a 256 "$A.tar.gz" > "$A.tar.gz.sha256") + done + find dist -type f | wc -l + + # The pv lane's REST upload (these boxes carry no `gh`), then a read-back: every packaged file + # must be on the release, or the lane is red. + - name: Upload assets to release (REST, no gh) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + API="https://api.github.com/repos/${GITHUB_REPOSITORY}" + rel_json() { + curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG" + } + rel=$(rel_json) + rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') + cd dist + for f in *; do + aid=$(printf '%s' "$rel" | python3 -c 'import json,sys; n=sys.argv[1]; print(next((a["id"] for a in json.load(sys.stdin)["assets"] if a["name"]==n), ""))' "$f") + if [ -n "$aid" ]; then curl -sSf -X DELETE -H "Authorization: Bearer $GH_TOKEN" "$API/releases/assets/$aid" > /dev/null; fi + curl -sSf -X POST -H "Authorization: Bearer $GH_TOKEN" -H "Content-Type: application/octet-stream" \ + --data-binary @"$f" "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/$rid/assets?name=$f" \ + | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' + done + have=$(rel_json | python3 -c 'import json,sys; print("\n".join(a["name"] for a in json.load(sys.stdin)["assets"]))') + missing=0 + for f in *; do + if ! grep -qxF -- "$f" <<< "$have"; then echo "::error::$f is not on $TAG after upload"; missing=1; fi + done + [ "$missing" -eq 0 ] + echo "- $(find . -type f | wc -l) assets on $TAG, read back" >> "$GITHUB_STEP_SUMMARY" + build-apr-cuda: name: apr (cuda) ${{ matrix.target }} on ${{ matrix.host }} + needs: assets + if: needs.assets.outputs.present == 'false' runs-on: ${{ fromJSON(matrix.labels) }} strategy: fail-fast: false @@ -261,6 +486,20 @@ jobs: with: ref: ${{ steps.tag.outputs.tag }} + # #4110/#4290: an rc asset's crate version IS its tag. The rc commit's Cargo.toml says + # X.Y.Z, so without this every vX.Y.Z-rc.N asset printed bare `X.Y.Z` (v0.69.3-rc.2: + # `apr 0.69.3 (v0.69.3+no-git)`). The stamper comes from THIS workflow's commit, not the + # tag's tree, which an rc cut from release/X.Y.Z predates. A final tag stamps nothing. + # verify-apr-assets then requires `apr --version` to equal the tag exactly. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + git show "FETCH_HEAD:scripts/release/stamp_rc_version.sh" > "$RUNNER_TEMP/stamp_rc_version.sh" + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + # FIRST thing this box does after it has a working tree. Run 34448908554 # built a 21 MB CUDA asset here and then died on `gh: command not found` # at the upload — forty minutes of GPU spent on a shell error a one-second @@ -291,7 +530,7 @@ jobs: # build.rs runs `git rev-parse` INSIDE this container, where the checkout is owned by # the runner uid and git refuses it — so every release asset printed `(vX+no-git)` and # scripts/apr_bin.sh (STRICT) could not attribute it to its tag. Ask the host checkout. - APR_SHA=$(git rev-parse --short HEAD) + APR_SHA=$(git rev-parse --short=9 HEAD) mkdir -p "$GITHUB_WORKSPACE/target" $DOCKER run --rm \ -v "$GITHUB_WORKSPACE:/workspace" \ @@ -354,7 +593,10 @@ jobs: run: | TAG="${{ steps.tag.outputs.tag }}" API="https://api.github.com/repos/${GITHUB_REPOSITORY}" - rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases/tags/$TAG") + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327), + # and releases/tags/ never returns a draft + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') for f in "${{ steps.package.outputs.archive }}" "${{ steps.package.outputs.sha }}"; do # --clobber semantics: delete an asset of the same name first @@ -385,6 +627,8 @@ jobs: # same step, so the merge moves both or neither. build-apr-cpu: name: apr (cpu) ${{ matrix.target }} on ${{ matrix.host }} + needs: assets + if: needs.assets.outputs.present == 'false' runs-on: ${{ fromJSON(matrix.labels) }} strategy: fail-fast: false @@ -413,6 +657,20 @@ jobs: with: ref: ${{ steps.tag.outputs.tag }} + # #4110/#4290: an rc asset's crate version IS its tag. The rc commit's Cargo.toml says + # X.Y.Z, so without this every vX.Y.Z-rc.N asset printed bare `X.Y.Z` (v0.69.3-rc.2: + # `apr 0.69.3 (v0.69.3+no-git)`). The stamper comes from THIS workflow's commit, not the + # tag's tree, which an rc cut from release/X.Y.Z predates. A final tag stamps nothing. + # verify-apr-assets then requires `apr --version` to equal the tag exactly. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + git show "FETCH_HEAD:scripts/release/stamp_rc_version.sh" > "$RUNNER_TEMP/stamp_rc_version.sh" + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + # 67-C2: the first thing this box does after it has a working tree. No # `--cuda` here — a CPU asset must build on a box with no visible device, # and asserting one would make this lane lie about what it needs. `docker` @@ -442,7 +700,7 @@ jobs: # build.rs runs `git rev-parse` INSIDE this container, where the checkout is owned by # the runner uid and git refuses it — so every release asset printed `(vX+no-git)` and # scripts/apr_bin.sh (STRICT) could not attribute it to its tag. Ask the host checkout. - APR_SHA=$(git rev-parse --short HEAD) + APR_SHA=$(git rev-parse --short=9 HEAD) mkdir -p "$GITHUB_WORKSPACE/target" $DOCKER run --rm \ -v "$GITHUB_WORKSPACE:/workspace" \ @@ -527,7 +785,10 @@ jobs: run: | TAG="${{ steps.tag.outputs.tag }}" API="https://api.github.com/repos/${GITHUB_REPOSITORY}" - rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases/tags/$TAG") + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327), + # and releases/tags/ never returns a draft + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') for f in "${{ steps.package.outputs.archive }}" "${{ steps.package.outputs.sha }}"; do # --clobber semantics: delete an asset of the same name first @@ -538,6 +799,101 @@ jobs: | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' done + # mini (Darwin arm64) is fleet hardware and could not take v0.69.3-rc.2: the release shipped + # no darwin asset, so mini was RED on every rc (#4327, infra-64 evidence/fleet-versions.md). + # Native build on the self-hosted mini-m4 runner (no hosted runners, #3073). No container: + # macOS has no glibc floor to pin, and the stamped version + the sha are checked below + # before anything is uploaded. + build-apr-darwin: + name: apr (cpu) aarch64-apple-darwin on mini + needs: assets + if: needs.assets.outputs.present == 'false' + # mini-builder (#4292, infra#1042): the mini's dedicated repo-level release runner, used + # only through this label so it never joins the general pool. + runs-on: [self-hosted, macOS, ARM64, mini-builder] + env: + # The macOS floor, recorded in the Mach-O load command and read back below (#4292). + MACOSX_DEPLOYMENT_TARGET: "11.0" + steps: + - name: Resolve release tag + id: tag + run: | + TAG="${{ github.event.release.tag_name || inputs.tag }}" + [ -n "$TAG" ] || { echo "::error::No tag resolved"; exit 1; } + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + - name: Checkout at tag + uses: actions/checkout@v7 + with: + ref: ${{ steps.tag.outputs.tag }} + + # The release scripts come from THIS workflow's commit: the tag tree predates them. + - name: Stamp the rc version into the tag tree (#4110) + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + git fetch --no-tags --depth 1 origin "$GITHUB_WORKFLOW_SHA" + for f in stamp_rc_version.sh asset_version_check.sh; do + git show "FETCH_HEAD:scripts/release/$f" > "$RUNNER_TEMP/$f" + done + case "$TAG" in *-rc.*) ;; *) echo "final tag $TAG: nothing to stamp"; exit 0 ;; esac + bash "$RUNNER_TEMP/stamp_rc_version.sh" "$GITHUB_WORKSPACE" "$TAG" + + - name: Build apr on the default features (no cuda) + run: | + APR_GIT_SHA_OVERRIDE="$(git rev-parse --short=9 HEAD)" \ + cargo build --release -p apr-cli --bin apr --target aarch64-apple-darwin --locked + strip target/aarch64-apple-darwin/release/apr + + - name: The binary names its tag and commit (#4110) + run: | + line=$(target/aarch64-apple-darwin/release/apr --version) + bash "$RUNNER_TEMP/asset_version_check.sh" "${{ steps.tag.outputs.tag }}" "$(git rev-parse HEAD)" "$line" + + # The same discriminator as the linux cpu lane, plus the arch and the minimum + # macOS actually recorded in the binary (#4292). + - name: Prove the asset is an arm64 CPU build + run: | + BIN=target/aarch64-apple-darwin/release/apr + n=$(grep -ac 'libcuda.so' "$BIN" || true) + if [ "$n" -ne 0 ]; then + echo "::error::$BIN carries $n libcuda.so loader string(s) — this is a CUDA build wearing a -cpu name" + exit 1 + fi + file "$BIN" | grep -q 'arm64' || { echo "::error::$BIN is not arm64: $(file "$BIN")"; exit 1; } + MINOS=$(otool -l "$BIN" | awk '/LC_BUILD_VERSION/{f=1} f&&$1=="minos"{print $2; exit}') + echo "- minimum macOS (LC_BUILD_VERSION minos): ${MINOS:-unknown}" >> "$GITHUB_STEP_SUMMARY" + + - name: Package archive + id: package + run: | + ARCHIVE="apr-${{ steps.tag.outputs.tag }}-aarch64-apple-darwin-cpu" + mkdir -p "$ARCHIVE" + cp target/aarch64-apple-darwin/release/apr "$ARCHIVE/" + for f in README.md LICENSE LICENSE-MIT LICENSE-APACHE; do [ -f "$f" ] && cp "$f" "$ARCHIVE/"; done + tar czf "${ARCHIVE}.tar.gz" "$ARCHIVE" + shasum -a 256 "${ARCHIVE}.tar.gz" > "${ARCHIVE}.tar.gz.sha256" + echo "archive=${ARCHIVE}.tar.gz" >> "$GITHUB_OUTPUT" + echo "sha=${ARCHIVE}.tar.gz.sha256" >> "$GITHUB_OUTPUT" + + - name: Upload assets to release (REST, no gh) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ steps.tag.outputs.tag }}" + API="https://api.github.com/repos/${GITHUB_REPOSITORY}" + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327) + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") + rid=$(printf '%s' "$rel" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') + for f in "${{ steps.package.outputs.archive }}" "${{ steps.package.outputs.sha }}"; do + aid=$(printf '%s' "$rel" | python3 -c 'import json,sys; n=sys.argv[1]; print(next((a["id"] for a in json.load(sys.stdin)["assets"] if a["name"]==n), ""))' "$f") + [ -n "$aid" ] && curl -sSf -X DELETE -H "Authorization: Bearer $GH_TOKEN" "$API/releases/assets/$aid" > /dev/null + curl -sSf -X POST -H "Authorization: Bearer $GH_TOKEN" -H "Content-Type: application/octet-stream" \ + --data-binary @"$f" "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/$rid/assets?name=$f" \ + | python3 -c 'import json,sys; a=json.load(sys.stdin); print("uploaded", a["name"], a["size"], "bytes")' + done + # The hard requirement, as a check that can fail: all FOUR apr assets (tarball + # sha256) and the eight pv assets must be on the release. A missing one is a red # workflow, never a partial summary. @@ -550,7 +906,7 @@ jobs: # mutation (RED) that this job cannot demonstrate on a real release. verify-apr-assets: name: All four apr assets are on the release - needs: [build, build-apr-cuda, build-apr-cpu] + needs: [build, build-apr-cuda, build-apr-cpu, build-apr-darwin] runs-on: [self-hosted, Linux, X64, clean-room] if: always() steps: @@ -583,6 +939,8 @@ jobs: smoke-cuda: name: smoke apr (cuda) on ${{ matrix.host }} needs: verify-apr-assets + # verify runs under always(); a skipped build two levels up must not skip this + if: ${{ !cancelled() && needs.verify-apr-assets.result == 'success' }} strategy: fail-fast: false matrix: @@ -617,16 +975,25 @@ jobs: T="${{ matrix.target }}" D="$RUNNER_TEMP/apr-cuda-smoke"; rm -rf "$D"; mkdir -p "$D"; cd "$D" API="https://api.github.com/repos/${GITHUB_REPOSITORY}" - rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases/tags/$TAG") + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327), + # and releases/tags/ never returns a draft + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") for f in "apr-${TAG}-${T}-cuda.tar.gz" "apr-${TAG}-${T}-cuda.tar.gz.sha256"; do aid=$(printf '%s' "$rel" | python3 -c 'import json,sys; n=sys.argv[1]; print(next(a["id"] for a in json.load(sys.stdin)["assets"] if a["name"]==n))' "$f") curl -sSfL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$f" "$API/releases/assets/$aid" done sha256sum -c "apr-${TAG}-${T}-cuda.tar.gz.sha256" tar xzf "apr-${TAG}-${T}-cuda.tar.gz" - BIN="$D/apr-${TAG}-${T}-cuda/apr" - v=$("$BIN" --version); echo "$v" - case "$v" in *"${TAG#v}"*) ;; *) echo "::error::asset reports '$v', not ${TAG#v}"; exit 1 ;; esac + # A promoted final (#4286) is its rc's exact bytes, so the archive's top directory + # may carry the rc label (apr-vX.Y.Z-rc.N-...). Take the one apr directory it holds. + set -- "$D"/apr-v*-"${T}"-cuda/apr + if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then echo "::error::expected exactly one apr binary in the archive, found: $*"; exit 1; fi + BIN=$1 + v=$("$BIN" --version | head -1); echo "$v" + # #4275: X.Y.Z whole (an rc tag's -rc.N stripped) AND the asset's sha is the tag's commit + COMMIT=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/commits/$TAG" | python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])') + bash "$GITHUB_WORKSPACE/scripts/release/asset_version_check.sh" "$TAG" "$COMMIT" "$v" || { echo "::error::asset reports '$v', which does not belong to $TAG ($COMMIT)"; exit 1; } [ "$(grep -ac 'libcuda.so' "$BIN")" -ge 1 ] || { echo "::error::downloaded asset is not a CUDA build"; exit 1; } floor=$(objdump -T "$BIN" 2>/dev/null | grep -oE 'GLIBC_[0-9.]+' | sort -Vu | tail -1) echo "highest GLIBC symbol version required: $floor" @@ -645,6 +1012,8 @@ jobs: smoke-cpu: name: smoke apr (cpu) on ${{ matrix.host }} needs: verify-apr-assets + # verify runs under always(); a skipped build two levels up must not skip this + if: ${{ !cancelled() && needs.verify-apr-assets.result == 'success' }} env: # The CPU assets build inside rust:1.93.0-bullseye (glibc 2.31) like the cuda lane, so the floor is # declared and GATED: an asset whose bytes import a newer GLIBC_* symbol fails here. 0.66.0 shipped a @@ -684,16 +1053,25 @@ jobs: T="${{ matrix.target }}" D="$RUNNER_TEMP/apr-cpu-smoke"; rm -rf "$D"; mkdir -p "$D"; cd "$D" API="https://api.github.com/repos/${GITHUB_REPOSITORY}" - rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases/tags/$TAG") + # by listing, not releases/tags/: an rc is a DRAFT until every fleet host runs it (#4327), + # and releases/tags/ never returns a draft + rel=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/releases?per_page=100" \ + | python3 -c 'import json,sys; t=sys.argv[1]; print(json.dumps(next(r for r in json.load(sys.stdin) if r["tag_name"]==t)))' "$TAG") for f in "apr-${TAG}-${T}-cpu.tar.gz" "apr-${TAG}-${T}-cpu.tar.gz.sha256"; do aid=$(printf '%s' "$rel" | python3 -c 'import json,sys; n=sys.argv[1]; print(next(a["id"] for a in json.load(sys.stdin)["assets"] if a["name"]==n))' "$f") curl -sSfL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$f" "$API/releases/assets/$aid" done sha256sum -c "apr-${TAG}-${T}-cpu.tar.gz.sha256" tar xzf "apr-${TAG}-${T}-cpu.tar.gz" - BIN="$D/apr-${TAG}-${T}-cpu/apr" - v=$("$BIN" --version); echo "$v" - case "$v" in *"${TAG#v}"*) ;; *) echo "::error::asset reports '$v', not ${TAG#v}"; exit 1 ;; esac + # A promoted final (#4286) is its rc's exact bytes, so the archive's top directory + # may carry the rc label (apr-vX.Y.Z-rc.N-...). Take the one apr directory it holds. + set -- "$D"/apr-v*-"${T}"-cpu/apr + if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then echo "::error::expected exactly one apr binary in the archive, found: $*"; exit 1; fi + BIN=$1 + v=$("$BIN" --version | head -1); echo "$v" + # #4275: X.Y.Z whole (an rc tag's -rc.N stripped) AND the asset's sha is the tag's commit + COMMIT=$(curl -sSf -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API/commits/$TAG" | python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])') + bash "$GITHUB_WORKSPACE/scripts/release/asset_version_check.sh" "$TAG" "$COMMIT" "$v" || { echo "::error::asset reports '$v', which does not belong to $TAG ($COMMIT)"; exit 1; } n=$(grep -ac 'libcuda.so' "$BIN" || true) [ "$n" -eq 0 ] || { echo "::error::the downloaded -cpu asset carries $n libcuda.so string(s) — it is a CUDA build"; exit 1; } # The floor is READ on every row and ASSERTED only where the build was @@ -716,7 +1094,7 @@ jobs: summary: name: Summary - needs: [build, build-apr-cuda, build-apr-cpu, verify-apr-assets, smoke-cuda, smoke-cpu] + needs: [assets, build, build-all-bins, build-apr-cuda, build-apr-cpu, build-apr-darwin, verify-apr-assets, smoke-cuda, smoke-cpu] runs-on: [self-hosted, Linux, X64, clean-room] if: always() steps: @@ -736,9 +1114,12 @@ jobs: echo "" if [ "$STATUS" = "success" ]; then echo "- Build matrix: **4/4 targets succeeded**" + elif [ "$STATUS" = "skipped" ] && [ "${{ needs.assets.outputs.present }}" = "true" ]; then + echo "- Build matrix: **not rebuilt** — every asset was already on the release (a promoted rc, #4286)" else echo "- Build matrix: **partial** (status=$STATUS)" fi + echo "- every other [[bin]] (G5 #4189, x86_64+aarch64 gnu): ${{ needs.build-all-bins.result }}" echo "- Targets:" echo " - x86_64-unknown-linux-musl" echo " - x86_64-unknown-linux-gnu" @@ -746,10 +1127,11 @@ jobs: echo " - aarch64-unknown-linux-gnu" echo "" echo "### apr Binary Release: $TAG - four assets, {cuda,cpu} x {x86_64,aarch64}" - echo "- cuda build: ${{ needs.build-apr-cuda.result }} · cpu build: ${{ needs.build-apr-cpu.result }}" + echo "- cuda build: ${{ needs.build-apr-cuda.result }} · cpu build: ${{ needs.build-apr-cpu.result }} · darwin build (mini): ${{ needs.build-apr-darwin.result }}" echo "- all four on the release: ${{ needs.verify-apr-assets.result }} · smoke cuda (yoga+gx10): ${{ needs.smoke-cuda.result }} · smoke cpu (clean-room+yoga+gx10): ${{ needs.smoke-cpu.result }}" echo " - apr-$TAG-x86_64-unknown-linux-gnu-cuda.tar.gz (+ .sha256)" echo " - apr-$TAG-aarch64-unknown-linux-gnu-cuda.tar.gz (+ .sha256)" echo " - apr-$TAG-x86_64-unknown-linux-gnu-cpu.tar.gz (+ .sha256)" echo " - apr-$TAG-aarch64-unknown-linux-gnu-cpu.tar.gz (+ .sha256)" + echo " - apr-$TAG-aarch64-apple-darwin-cpu.tar.gz (+ .sha256)" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/book-contracts.yml b/.github/workflows/book-contracts.yml index e1ca6e9c65..b28d4404fe 100644 --- a/.github/workflows/book-contracts.yml +++ b/.github/workflows/book-contracts.yml @@ -39,6 +39,15 @@ on: schedule: - cron: '45 22 * * *' + +# #3676: no concurrency group meant every superseded PR push ran the ~18 min +# chapter suite to completion. Sampled 24 h to 2026-09-24 19:15Z: ~1650 of ~3700 +# wall-minutes of this workflow's pull_request runs came after a newer push +# to the same PR had already started. Same shape as ci.yml: one group per PR, a +# new push cancels the old run, and push/schedule runs are never cancelled. +concurrency: + group: book-contracts-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: read diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 6dd9126865..f695c6ed4e 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -60,9 +60,14 @@ permissions: pages: write id-token: write +# #3676: one group per PR, and a newer push cancels the superseded run. The old +# workflow-level group "pages" (cancel false) held EVERY run -- all PRs, main and +# the nightly -- in one global queue, so each PR's mdBook check waited behind +# every other PR's. Only the Pages deploy needs that serialisation, so the +# "pages" group now sits on the deploy job alone. Non-PR events never cancel. concurrency: - group: "pages" - cancel-in-progress: false + group: book-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build: @@ -203,6 +208,10 @@ jobs: runs-on: [self-hosted, Linux, X64, clean-room] needs: build if: github.event_name == 'push' && github.ref == 'refs/heads/main' + # GitHub Pages deploys never overlap and are never cancelled (was workflow-level). + concurrency: + group: "pages" + cancel-in-progress: false permissions: pages: write id-token: write diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7719baedbf..36363f7809 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,6 @@ jobs: # with the same rule the old job of that name applied. # ========================================================================== x86-main: - # Any free X64 runner in the pool takes it: intel, framework16 or yoga. runs-on: [self-hosted, Linux, X64, clean-room] timeout-minutes: 240 # The sections carry their own permissions blocks (the sov provenance section @@ -92,6 +91,24 @@ jobs: - uses: actions/checkout@v7 with: fetch-depth: 0 + # Fail fast: the reds car#4429 took four fat runs to see, in ~2 min, before + # any section starts. rustfmt runs in the image sov.lint uses (same digest, + # so the same verdict); the ID-order rule is contract_data_integrity's, + # without cargo; the case tables are guard-tree's cheapest. + - name: Cheap checks (fmt, falsification-ID order, guard case tables) + run: | + set -euo pipefail + python3 scripts/ci/fail_fast.py self-test + python3 scripts/ci/fail_fast.py ids --base origin/main + docker run --rm -v "$GITHUB_WORKSPACE:/w:ro" -w /w \ + localhost:5000/sovereign-ci:stable@sha256:70f2cdc9e537fe4447306d33c317fea33017f78f1ffa3b7af99095391419ef36 \ + cargo fmt --all -- --check + bash scripts/tests/guard_tree_test.sh + bash scripts/bump-version.sh --self-test + setsid --wait bash scripts/check_no_claim_literals.sh --selftest + setsid --wait bash scripts/check_hardcoded_paths.sh --self-test + setsid --wait bash scripts/check_pr_review_wiring.sh --self-test + setsid --wait bash scripts/check_ci_gate_mutants_rule.sh --self-test # The driver decides what every section's if:/matrix/needs means and whether # the vendored workflow is the upstream one; its case table must go RED on # each planted defect before it is trusted to report a section green. @@ -107,7 +124,7 @@ jobs: - name: Sections (the determinism raster returns first; the rest keep running) run: >- python3 scripts/ci/fat_driver.py run - --sections 'sov.*,guard-tree,guard-cargo,vendored-schemas,pr-review-shadow,pr-review-sign,mutants,determinism[X64]' + --sections 'sov.*,guard-tree,guard-cargo,vendored-schemas,pr-review-shadow,pr-review-sign,mutants,determinism[X64],provable-ladder' --external-job workspace-test --results "$RUNNER_TEMP/fat/results.json" --background-until 'determinism[X64]' @@ -407,6 +424,173 @@ jobs: python3 scripts/ci/sigma_executed.py explicit --kind "explicit (3 shards)" "${l[@]}" --step "Integration tests" --list "$d/sigma-shard-1/explicit.list.txt" || rc=1 exit "$rc" + # #4587 A' (operator 2026-09-29 06:36Z, via the cop): "CI generates the survivor table itself: the + # split runs become CI matrix jobs (run IDs), each bound to PR-1's head sha and diff hash ... The gate + # verifies before trusting it ... Any gap -> RED." Scope: the head refs in ci/mutants-table-refs.txt + # (PR-1 and PR-2 of 0.70) and nothing else; every other PR keeps the one-job `mutants` section and its + # 60-mutant cap. For a listed ref that section hands over, and `gate` requires mutants-table. + mutants-table-scope: + runs-on: [self-hosted, Linux, clean-room] + timeout-minutes: 10 + if: github.event_name == 'pull_request' + outputs: + in_scope: ${{ steps.decide.outputs.in_scope }} + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.head.sha }} + # minisign for the equivalents-signature rows (pinned + digest-checked, per-run private bin dir). + - name: Install minisign (the checker refuses an unverifiable equivalent) + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + bash scripts/install_pr_review_tools.sh "$RUNNER_TEMP/pr-review-tools/bin" + echo "$RUNNER_TEMP/pr-review-tools/bin" >> "$GITHUB_PATH" + # Every PR re-proves the gate's survivor-table rule and the checker's case table (planted defects RED). + # The checker's case table needs the minisign installed above, so this guard is wired HERE with its input + # named (guard_tree.sh rule 1 then leaves it to this step); the sovereign-ci image has no minisign. + - name: The survivor-table gate rule and checker still turn RED + env: + GATE_RULE_WORKFLOW: .github/workflows/ci.yml + run: | + set -euo pipefail + setsid --wait bash scripts/check_ci_gate_mutants_table_rule.sh --self-test + setsid --wait bash scripts/check_ci_gate_mutants_table_rule.sh + - name: The mutants feature groups cover every feature-gated module + run: setsid --wait bash scripts/check_mutants_feature_groups.sh + - name: Is this head ref judged by the survivor table? + id: decide + env: + HEAD_REF: ${{ github.head_ref }} + run: | + set -euo pipefail + if grep -v '^#' ci/mutants-table-refs.txt | grep -qxF -- "$HEAD_REF"; then v=true; else v=false; fi + echo "head ref $HEAD_REF -> in_scope=$v" + echo "in_scope=$v" >> "$GITHUB_OUTPUT" + + # One shard of the diff's mutants per job, on the PR HEAD sha (not the merge commit), cap 0. + mutants-shard: + runs-on: [self-hosted, Linux, clean-room] + timeout-minutes: 720 + needs: [mutants-table-scope] + if: needs.mutants-table-scope.outputs.in_scope == 'true' + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + env: + IMAGE: localhost:5000/sovereign-ci:stable + SHARDS: '16' + MUTANTS_TABLE_JOBS: ${{ vars.MUTANTS_TABLE_JOBS || '2' }} + steps: + - name: Pre-checkout ownership restore (EACCES self-heal) + run: | + if docker image inspect "$IMAGE" > /dev/null 2>&1; then + docker run --rm -v "${GITHUB_WORKSPACE}:/workspace" "$IMAGE" \ + bash -c 'chown -R 1000:1000 /workspace 2>/dev/null || true' + fi + - uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: The PR diff this shard is bound to + run: bash scripts/mutants_table_diff.sh "${{ github.event.pull_request.base.ref }}" pr.diff + - name: Pull sovereign-ci image (with retry + local-cache fallback) + run: | + if docker image inspect "$IMAGE" > /dev/null 2>&1; then exit 0; fi + for i in $(seq 1 15); do + docker pull "$IMAGE" && exit 0 + sleep $((4 + 6 * i)) + done + echo "::error::Registry localhost:5000 unreachable after 15 attempts AND image not in local cache" + exit 1 + - name: Shard ${{ matrix.shard }} of the survivor table + run: | + set -euo pipefail + docker run --rm \ + -e CI -e GITHUB_ACTIONS -e GITHUB_RUN_ID -e GITHUB_RUN_ATTEMPT -e SHARDS -e MUTANTS_TABLE_JOBS \ + -e SHARD=${{ matrix.shard }} \ + -v "${GITHUB_WORKSPACE}:/workspace" -w /workspace "$IMAGE" \ + bash -c ' + set -uo pipefail + export CARGO_INSTALL_ROOT=/usr/local/cargo-mutants-root + export PATH="$CARGO_INSTALL_ROOT/bin:$PATH" + if ! cargo mutants --version > /dev/null 2>&1; then + cargo install cargo-mutants --locked --version 27.1.0 > /tmp/mi.log 2>&1 || { + echo "::error::cargo-mutants install failed; the shard cannot run"; tail -20 /tmp/mi.log; exit 1; } + fi + cargo mutants --version || { echo "::error::cargo-mutants not runnable after install"; exit 1; } + # The same exclusions as the mutants section: derived from the workspace-test nextest line. + EXCL=$(grep -m1 -E "^[[:space:]]+cargo nextest run --profile ci --workspace --lib" ci/sections.yml \ + | grep -oE -- "--exclude [a-z0-9_-]+" | sed "s/^--exclude /--exclude-crate /" | tr "\n" " ") + [ -n "$EXCL" ] || { echo "::error::no --exclude list found on the workspace-test nextest line"; exit 1; } + # shellcheck disable=SC2086 + bash scripts/mutants_table_shard.sh pr.diff "$SHARD" "$SHARDS" "$MUTANTS_TABLE_JOBS" \ + "mutants-shard-$SHARD" $EXCL + ' + - uses: actions/upload-artifact@v7 + if: always() + with: + name: mutants-shard-${{ matrix.shard }} + path: | + mutants-shard-${{ matrix.shard }}/meta.json + mutants-shard-${{ matrix.shard }}/list.txt + mutants-shard-${{ matrix.shard }}/outcomes.json + if-no-files-found: error + + # The verifier: trusts no shard until scripts/ci/mutants_survivor_table.py has checked the union. + mutants-table: + runs-on: [self-hosted, Linux, clean-room] + timeout-minutes: 20 + needs: [mutants-table-scope, mutants-shard] + if: always() && needs.mutants-table-scope.outputs.in_scope == 'true' + steps: + - name: Every shard ran to the end + env: + SHARD_RESULT: ${{ needs.mutants-shard.result }} + run: | + echo "mutants-shard matrix result: $SHARD_RESULT" + [ "$SHARD_RESULT" = success ] || { echo "::error::mutants-table: shard matrix result is '$SHARD_RESULT' (a dead, cancelled or skipped shard is not a pass)"; exit 1; } + - uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - uses: actions/download-artifact@v4 + with: + pattern: mutants-shard-* + path: ${{ runner.temp }}/mt + # minisign for the equivalents-signature rows (pinned + digest-checked, per-run private bin dir). + - name: Install minisign (the checker refuses an unverifiable equivalent) + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + bash scripts/install_pr_review_tools.sh "$RUNNER_TEMP/pr-review-tools/bin" + echo "$RUNNER_TEMP/pr-review-tools/bin" >> "$GITHUB_PATH" + - name: The checker's case table must still turn RED + run: python3 scripts/ci/mutants_survivor_table.py --self-test + - name: "Survivor table: every listed mutant judged once, on the PR head + diff (#4587 A')" + env: + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR: ${{ github.event.pull_request.number }} + MUTANTS_MAX_MISSED: ${{ vars.MUTANTS_MAX_MISSED || '0' }} + run: | + set -euo pipefail + bash scripts/mutants_table_diff.sh "${{ github.event.pull_request.base.ref }}" pr.diff + # The key an equivalent's receipt must verify against comes from the BASE branch: the PR's own copy + # of .github/pr-review.pub is editable by the PR, so trusting it would let a PR sign its own proof. + git show "origin/${{ github.event.pull_request.base.ref }}:.github/pr-review.pub" > "$RUNNER_TEMP/base-pr-review.pub" + python3 scripts/ci/mutants_survivor_table.py check --dir "$RUNNER_TEMP/mt" --shards 16 \ + --head-sha "$HEAD_SHA" --diff pr.diff --equiv "evidence/mutants-equiv/$PR.tsv" \ + --max-missed "$MUTANTS_MAX_MISSED" --repo . --pubkey "$RUNNER_TEMP/base-pr-review.pub" --table "$RUNNER_TEMP/mt/survivor-table.tsv" + - uses: actions/upload-artifact@v7 + if: always() + with: + name: mutants-survivor-table + path: ${{ runner.temp }}/mt/survivor-table.tsv + if-no-files-found: ignore + ci-gate: name: ci / gate runs-on: [self-hosted, Linux, clean-room] @@ -426,7 +610,7 @@ jobs: gate: runs-on: [self-hosted, Linux, clean-room] # arch-neutral: intel, yoga or gx10 (#3100) timeout-minutes: 22 - needs: [x86-main, workspace-test, determinism] # as on main: the GPU jobs are advisory (68-C3 / 68-D2) + needs: [x86-main, workspace-test, determinism, mutants-table-scope, mutants-table] # as on main: the GPU jobs are advisory (68-C3 / 68-D2) if: always() steps: - name: Check required sections @@ -435,6 +619,9 @@ jobs: DET: ${{ needs.determinism.outputs.results }} WT: ${{ needs.workspace-test.result }} EVT: ${{ github.event_name }} + SCOPE: ${{ needs.mutants-table-scope.result }} + IN_SCOPE: ${{ needs.mutants-table-scope.outputs.in_scope }} + TABLE: ${{ needs.mutants-table.result }} run: | set -euo pipefail res() { printf '%s' "${1:-null}" | SEC="$2" jq -r '(. // {})[env.SEC].result // "missing"'; } @@ -480,4 +667,16 @@ jobs: *) echo "mutants (diff-scoped mutation) on $EVT: $MUT"; exit 1 ;; esac # --- GATE-MUTANTS-RULE-END --- + # --- GATE-MUTANTS-TABLE-RULE-BEGIN (#4587 A'; scripts/check_ci_gate_mutants_table_rule.sh runs this block) --- + # For a head ref in ci/mutants-table-refs.txt the mutants section hands over, so here the survivor + # table must have RUN and succeeded. The scope decision itself must have run on every pull_request: + # a missing decision would let a listed ref pass on the section's hand-over alone. + case "$EVT:$SCOPE:$IN_SCOPE:$TABLE" in + pull_request:success:true:success) echo "mutants-table: survivor table GREEN" ;; + pull_request:success:false:skipped) echo "mutants-table: not in scope (the mutants section judged this PR)" ;; + pull_request:*) echo "mutants-table on a pull_request: scope=$SCOPE in_scope=$IN_SCOPE table=$TABLE"; exit 1 ;; + *:skipped::skipped) echo "mutants-table: skipped on $EVT (pull_request only)" ;; + *) echo "mutants-table on $EVT: scope=$SCOPE in_scope=$IN_SCOPE table=$TABLE"; exit 1 ;; + esac + # --- GATE-MUTANTS-TABLE-RULE-END --- echo "All required sections passed" diff --git a/.github/workflows/conleche-nightly.yml b/.github/workflows/conleche-nightly.yml new file mode 100644 index 0000000000..63285936a7 --- /dev/null +++ b/.github/workflows/conleche-nightly.yml @@ -0,0 +1,79 @@ +# conleche-nightly.yml -- PVL-F7 (#3142): an independent kernel re-checks the Lean tree once a day. ADVISORY. +# +# Every other verdict on crates/aprender-contracts-staging/lean comes from Lean's own C++ kernel (lake build, +# leanchecker via `pv discharge check`). con-leche is a second checker that shares no code with it or with pv, +# and it rejects every axiom beyond propext / Classical.choice / Quot.sound -- so it also re-proves that the +# escape allowlist is empty (#4347). The whole procedure, its pins and its controls live in conleche.sh; this +# file only provisions elan and runs it. +# +# ADVISORY. Nothing requires this job; a red run blocks no PR and no release. It is a finding to file, not a +# gate: exit 1 = RED (a rejected declaration or an escape axiom), exit 2 = NOT A VERDICT (unsupported feature, +# OOM, unpinned toolchain, or a control that failed). Both turn the run red, with an annotation naming which -- +# "not a verdict" is never shown green. +# +# Measured on lambda 2026-09-25 at 9018366cf (warm pins): controls ok, 402163 declarations accepted, 7:11 wall, +# 5.7 GB max RSS. Cold adds the con-leche build (395 s) and two toolchain downloads. +# +# RUNNER. [self-hosted, Linux, X64, clean-room] -- the sovereign-ci pool, never a hosted runner (operator rule, +# 2026-09-10). elan is not part of the pool image, so the job installs a PINNED, sha256-checked elan into its +# own directory and touches nothing else on the host. Toolchains, pins and the Mathlib cache persist there +# between runs; the concurrency group keeps two runs from sharing them at once. +name: conleche-nightly + +on: + schedule: + # 21:23 UTC -> lands ~02:23 UTC. GitHub dispatches schedules ~5h late on this account (#3292); the slot is + # free and clear of guards-nightly (21:47) and cuda-nightly (20:30). + - cron: '23 21 * * *' + workflow_dispatch: + +concurrency: + group: conleche-nightly + cancel-in-progress: false + +permissions: + contents: read + +jobs: + conleche: + runs-on: [self-hosted, Linux, X64, clean-room] + # 90: BSE-05 T = max(15, ceil(1.5*p99), p99+20) with p99 taken as a cold ~45 min (no history yet: con-leche + # build 6.6 min, toolchains + Mathlib cache, a from-scratch ProvableContracts build, export 4 min, check + # 2.6 min) -> T = max(15, 68, 65) = 68, rounded up for a shared host. The first three runs replace this basis. + timeout-minutes: 90 + env: + CI_CACHE: /mnt/nvme-raid0/ci-cache/pvl-conleche + ELAN_VERSION: v4.2.4 + ELAN_SHA256: 42b94d4244e8353142c456ec0e4ca6528fd898a6c604d4059f494e706e431f63 + LEAN_NUM_THREADS: '8' + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 1 + - name: Verdict case table (the classifier this job trusts) + run: bash crates/aprender-contracts-staging/lean/conleche.sh --self-test + - name: Pinned elan in the job's own directory (sha256-checked, never a host install) + run: | + set -euo pipefail + export ELAN_HOME="$CI_CACHE/elan" + mkdir -p "$CI_CACHE" + if [ ! -x "$ELAN_HOME/bin/elan" ] || ! "$ELAN_HOME/bin/elan" --version | grep -q "${ELAN_VERSION#v}"; then + tgz="$RUNNER_TEMP/elan.tgz" + curl -fsSL -o "$tgz" "https://github.com/leanprover/elan/releases/download/$ELAN_VERSION/elan-x86_64-unknown-linux-gnu.tar.gz" + echo "$ELAN_SHA256 $tgz" | sha256sum -c - + tar xzf "$tgz" -C "$RUNNER_TEMP" + "$RUNNER_TEMP/elan-init" -y --no-modify-path --default-toolchain none + fi + echo "ELAN_HOME=$ELAN_HOME" >> "$GITHUB_ENV" + echo "$ELAN_HOME/bin" >> "$GITHUB_PATH" + - name: con-leche re-check (advisory) + run: | + set -uo pipefail + rc=0 + PVL_CONLECHE_CACHE="$CI_CACHE/pins" bash crates/aprender-contracts-staging/lean/conleche.sh || rc=$? + case "$rc" in + 0) ;; + 1) echo "::error title=con-leche RED::a declaration was rejected or an escape axiom is present -- file it against #3142" ;; + *) echo "::warning title=con-leche NOT A VERDICT::exit $rc -- the tree was not checked (see the log above); not a pass" ;; + esac + exit "$rc" diff --git a/.github/workflows/cuda-nightly.yml b/.github/workflows/cuda-nightly.yml index d4c033e735..53dafd909d 100644 --- a/.github/workflows/cuda-nightly.yml +++ b/.github/workflows/cuda-nightly.yml @@ -327,6 +327,43 @@ jobs: nice -n 19 cargo test -p aprender-serve --lib --release cb008_checker_controls APR_FALSIFY_MODEL="$model" nice -n 19 cargo test -p aprender-serve --features cuda \ --release --test falsify_cb008_no_frozen_slots_2753 -- --nocapture + + # aprender#4251: #4250's serve batched-prefill test. ci.yml's cuda-only step selects it, but + # there a missing Qwen3.5 file is a legitimate skip, and the runners do not carry one. So it + # passed without running. Here the model is resolved first, and a `SKIP:` line or a + # run that is not exactly `1 passed` is RED. `!cancelled()` so an earlier red falsifier + # cannot hide this one. + - name: aprender#4251 - every serve route prefills through the batched prefill (Qwen3.5) + if: ${{ !cancelled() && steps.decide.outputs.proceed == 'true' }} + env: + APR_QWEN35_MODEL_VAR: ${{ vars.APR_QWEN35_MODEL }} + run: | + set -euo pipefail + export APR_QWEN35_MODEL="${APR_QWEN35_MODEL_VAR:-$HOME/models/Qwen3.5-0.8B-Q4_K_M.gguf}" + if [ ! -f "$APR_QWEN35_MODEL" ]; then + echo "::error::#4251 UNMEASURABLE on ${{ matrix.name }} (provisioning, not a code defect): $APR_QWEN35_MODEL is absent" + exit 1 + fi + echo "#4251 model: $APR_QWEN35_MODEL" + log="$RUNNER_TEMP/serve-batched-prefill.log" + rc=0 + nice -n 19 cargo test -p aprender-serve --features cuda --lib --release \ + api::openai_handlers::qwen35_serve_tests::gpu_every_serve_route_prefills_through_the_batched_prefill \ + -- --exact --nocapture \ + >"$log" 2>&1 || rc=$? + cat "$log" + if [ "$rc" -ne 0 ]; then + echo "::error::#4251 serve batched-prefill test FAILED (exit $rc)" + exit "$rc" + fi + if grep -q '^SKIP:' "$log"; then + echo "::error::#4251 the test SKIPPED on a CUDA runner with the model named: $(grep -m1 '^SKIP:' "$log")" + exit 1 + fi + if ! grep -q 'test result: ok\. 1 passed' "$log"; then + echo "::error::#4251 the filter did not run exactly one test" + exit 1 + fi # PP-26 / L0 CORRECTNESS: the batch-invariance witness (FALSIFY-CB-006, # #2753, §9 #1a on Blackwell). PP-LLAMA-001 §7.0 puts correctness at layer # L0 and says perf041 runs "nightly + release, missing marker = RED"; the diff --git a/.github/workflows/install-script.yml b/.github/workflows/install-script.yml index 9fd9092a59..f8daec95c5 100644 --- a/.github/workflows/install-script.yml +++ b/.github/workflows/install-script.yml @@ -37,6 +37,12 @@ on: schedule: - cron: '0 23 * * *' + +# #3676: a newer push to the same PR cancels the superseded run +# (scripts/check_pr_workflows_cancel_superseded.sh). +concurrency: + group: install-script-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: read diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 671c849d34..1f34b0e156 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -29,9 +29,9 @@ # job runs on the fleet. x86_64 builds on yoga and aarch64 natively on gx10, both # inside rust:1.93.0-bullseye (binary-release.yml's CPU-lane pattern), so the # nightly's glibc floor is 2.31; the ubuntu-latest build needed GLIBC_2.39 and did -# not run on lambda's 22.04. The macOS and Windows targets are gone: no self-hosted -# macOS or Windows runner is registered in the org, and there is no hosted -# fallback. They come back when a box for them does. +# not run on lambda's 22.04. Windows is gone: no self-hosted Windows runner is +# registered and there is no hosted fallback. macOS came back with a box for it +# (#3204): aarch64-apple-darwin builds host-native on mini-m4 (build-darwin). name: Nightly @@ -231,6 +231,60 @@ jobs: path: dist/* if-no-files-found: warn + # ── Build apr: macOS aarch64 natively on mini-m4 (#3204) ── + # Host-native: there is no docker on macOS and no glibc floor to pin, so the + # toolchain is rust-toolchain.toml via the runner's rustup, as in ci.yml's + # mac-check. A LITERAL runs-on list, not fromJSON, so check_runner_labels.sh + # sees the apple-silicon label. Gated on the same `gate` decision as the Linux + # matrix (car's `check-activity` job does not exist on this branch -- folded + # into `gate`'s reused/build/red-ci/ci-pending verdict, #4189). One box with + # 16 GB: this leg must not hold the Linux nightly hostage -- `publish-darwin` + # below only uploads when this job is green, and the darwin tarball is + # simply absent that night otherwise; it never blocks or is blocked by + # `publish` (that job's per-arch manifest tracks Linux targets only). + build-darwin: + needs: gate + if: needs.gate.outputs.decision == 'build' + name: apr aarch64-apple-darwin on mini + runs-on: [self-hosted, macOS, ARM64, apple-silicon, m4, mini] + timeout-minutes: 90 + env: + CARGO_INCREMENTAL: 0 + CARGO_TERM_COLOR: never + T: aarch64-apple-darwin + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Build apr (host-native, pinned toolchain) + run: | + set -euo pipefail + rustup show active-toolchain + cargo build --release --locked -p apr-cli --target "$T" + "target/$T/release/apr" --version + + - name: Package + run: | + set -euo pipefail + ARCHIVE="apr-$T" + rm -rf "$ARCHIVE" "$ARCHIVE.tar.gz" "$ARCHIVE.tar.gz.sha256" + mkdir -p "$ARCHIVE" + cp "target/$T/release/apr" "$ARCHIVE/" + for f in README.md LICENSE LICENSE-MIT COPYING UNLICENSE; do + if [ -f "$f" ]; then cp "$f" "$ARCHIVE/"; fi + done + tar czf "${ARCHIVE}.tar.gz" "$ARCHIVE" + shasum -a 256 "${ARCHIVE}.tar.gz" > "${ARCHIVE}.tar.gz.sha256" + echo "Packaged: ${ARCHIVE}.tar.gz ($(du -h "${ARCHIVE}.tar.gz" | cut -f1))" + + - name: Upload artifact + uses: actions/upload-artifact@v7 + with: + name: apr-aarch64-apple-darwin + path: | + apr-aarch64-apple-darwin.tar.gz + apr-aarch64-apple-darwin.tar.gz.sha256 + # ── Publish per arch, then the manifest ────────────────── publish: needs: [gate, build] @@ -283,3 +337,31 @@ jobs: ci-pending) echo "::warning::required checks not finished on ${NIGHTLY_SHA:0:9}; last green kept"; exit 0 ;; *) echo "::error::nightly $DECISION at ${NIGHTLY_SHA:0:9}; see nightly-manifest.json"; exit 1 ;; esac + + # ── Attach the darwin asset to the nightly release ──────── + # `publish`'s manifest system (scripts/nightly_manifest.py) tracks the Linux + # targets only; it neither knows about nor deletes unlisted assets, so this + # upload is additive and independent -- a red/skipped darwin leg leaves the + # Linux assets alone, and a red Linux leg (this job needs `publish` only so + # the "nightly" release already exists) does not block the mac asset. + publish-darwin: + needs: [gate, build-darwin, publish] + if: always() && needs.gate.outputs.decision == 'build' && needs.build-darwin.result == 'success' + runs-on: [self-hosted, Linux, X64, clean-room] + steps: + - name: Download darwin artifact + uses: actions/download-artifact@v4 + with: + name: apr-aarch64-apple-darwin + path: dist-darwin + + - name: Upload to the nightly release + uses: softprops/action-gh-release@v3 + with: + tag_name: nightly + name: Nightly Build + prerelease: true + make_latest: false + files: | + dist-darwin/apr-aarch64-apple-darwin.tar.gz + dist-darwin/apr-aarch64-apple-darwin.tar.gz.sha256 diff --git a/.github/workflows/rc-cut.yml b/.github/workflows/rc-cut.yml new file mode 100644 index 0000000000..ea86e867c4 --- /dev/null +++ b/.github/workflows/rc-cut.yml @@ -0,0 +1,89 @@ +name: RC Cut + +# Cut vX.Y.Z-rc.N as a GitHub PRERELEASE when CI goes green on the tip of +# release/X.Y.Z (#4285, RC-DOGFOOD-001 §4.1). The decision and every write live in +# scripts/release/rc_cut.sh; this file only wires the trigger and the token. +# +# TRIGGER. ci.yml does not run on a push to release/**. A release branch is gated by +# CI on its release PR (release/X.Y.Z -> main), which re-runs on every push with +# head_sha = the branch tip. `workflow_run` fires when any CI run completes. The +# `branches` filter matches the run's HEAD branch, so a release PR's runs arrive +# here and a feature PR's do not. rc_cut.sh then requires, on that run: +# `ci / gate` and `workspace-test` success, head repository = this repository +# (fork PRs can name a branch release/X.Y.Z), head_sha still the tip, and no rc +# tag already on that commit. Anything else is a logged skip, not a failure. +# +# `workflow_run` always runs this file as it is on the DEFAULT branch, with this +# repository's token, so a release branch cannot change what the cut does. +# +# ASSETS. A release created with GITHUB_TOKEN does not fire `release: published` +# in binary-release.yml (GitHub suppresses recursive triggers), so rc_cut.sh +# dispatches binary-release.yml with inputs.tag. That needs `actions: write`. +# +# PUBLISH GATE (#4287). Before anything is written, the job decides with --dry-run, +# checks the rc commit out beside the default-branch checkout (in RUNNER_TEMP, so no +# cargo config is inherited from this tree) and runs scripts/release/rc_publish_gate.sh +# on it: the publish graph (R6), the path-only dev-dep rule that #3425 broke v0.68.0 +# on, and a `cargo package --no-verify` walk of every publishable crate. Red means the +# job fails and no rc exists; the defect is found on the rc, never at the final tag. +# The real cut then re-decides from the same run, so it tags the commit that was gated. +# +# CI cuts the tag and the prerelease. Publishing a crate stays out of CI (RP-001). + +on: + workflow_run: + workflows: [CI] + types: [completed] + branches: ['release/**'] + +permissions: + contents: write # create the tag ref and the prerelease + actions: write # dispatch binary-release.yml for the assets + +# One cut at a time per release branch, never cancelled: N is read then written, +# and two green runs racing would both read the same N. The tag ref is also the +# lock (a duplicate gets HTTP 422 and writes nothing else). +concurrency: + group: rc-cut-${{ github.event.workflow_run.head_branch }} + cancel-in-progress: false + +jobs: + cut: + # Operator rule (2026-09-10): self-hosted fleet only. The job needs bash, curl + # and python3, which clean-room carries (binary-release.yml's verify job). + runs-on: [self-hosted, Linux, clean-room] + # the package walk is ~65 s warm on lambda; a cold registry index is the rest + timeout-minutes: 30 + # Cheap pre-filter only; rc_cut.sh re-judges every condition itself. + if: github.event.workflow_run.conclusion != 'cancelled' + steps: + - uses: actions/checkout@v7 + with: + # the default branch's script, never the release branch's + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 1 + - name: Case tables (the decision and the gate must still be able to refuse) + run: | + bash scripts/release/rc_cut.sh --self-test + bash scripts/release/rc_publish_gate.sh --self-test + - name: Decide (writes nothing) + id: decide + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.event.workflow_run.id }} + run: bash scripts/release/rc_cut.sh --run-id "$RUN_ID" --dry-run + - name: Publish gate on the rc commit (#4287) + if: steps.decide.outputs.tag != '' + env: + SHA: ${{ steps.decide.outputs.sha }} + run: | + git fetch --no-tags --depth 1 origin "$SHA" + git worktree add --detach "$RUNNER_TEMP/rc-tree" "$SHA" + bash scripts/release/rc_publish_gate.sh "$RUNNER_TEMP/rc-tree" + - name: Cut the rc + if: success() && steps.decide.outputs.tag != '' # explicit: a red publish gate never reaches the cut + id: cut + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.event.workflow_run.id }} + run: bash scripts/release/rc_cut.sh --run-id "$RUN_ID" diff --git a/.gitignore b/.gitignore index fe9cf7f63e..d39aeb2a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ lcov.info # mdBook generated output book/book/ *.log +# Kernel A/B receipt logs are tracked evidence (ont-kernel-receipts-v1), not build noise. +!/evidence/kernels/*/*.ab.log # Shell artifacts (generated by aprender-shell) crates/aprender-shell/corpus/ @@ -131,3 +133,7 @@ docs/roadmaps/*.lock # The VERDICT artifact (quorum-*.json) is committed; its .lanes/ working # directory is transient and a lane review correctly refused a PR carrying it. docs/audits/quorum-*.json.lanes/ + +# PVL-001 EV-8a (#4202): `pv discharge run`'s full log. The tracked summary is its sibling +# crates/aprender-contracts-staging/discharge-summary.json, outside the tree it hashes. +crates/aprender-contracts-staging/lean/discharge.json diff --git a/.quorum/0694-batch1/receipt.json b/.quorum/0694-batch1/receipt.json new file mode 100644 index 0000000000..69ec5c734a --- /dev/null +++ b/.quorum/0694-batch1/receipt.json @@ -0,0 +1,23 @@ +{ + "pr": 4318, + "batch": "batch/0.69.4-epic-4249", + "reviewed_head": "01c71e648", + "fix_head": "3c4e19242", + "base": "a5efa1443", + "author_model": "claude-opus-5-5", + "degraded": "same-family", + "mandatory_scope": ".github/workflows/binary-release.yml reviewed explicitly by every lane (cop GO condition)", + "round_1": [ + {"lane": 1, "model": "claude-sonnet-5", "scope": "workflows + scripts/release + install", "verdict": "FAIL", "finding": "build-apr-darwin did not stamp the rc version; the rc darwin asset printed bare X.Y.Z"}, + {"lane": 2, "model": "claude-haiku-4-5-20251001", "scope": "binary-release.yml line-by-line + ci/ + contracts/", "verdict": "PASS"}, + {"lane": 3, "model": "claude-sonnet-5", "scope": "crates/** + binary-release.yml", "verdict": "PASS"} + ], + "fix": "3c4e19242: darwin lane stamps the rc version (stamper from the workflow commit) and runs asset_version_check.sh against the tag and commit", + "round_2_on_fix": [ + {"lane": "r1", "model": "claude-sonnet-5", "verdict": "FAIL", "reason": "darwin is not a required asset and has no post-upload smoke; this is aprender#4353 scope (milestone 0.70.1), and the operator ruled no scope change for 0.69.4"}, + {"lane": "r2", "model": "claude-haiku-4-5-20251001", "verdict": "PASS"}, + {"lane": "r3", "model": "claude-sonnet-5", "verdict": "PASS", "note": "#4353 gap is non-blocking and out of scope; smoke the stamper on the mini"} + ], + "extra_evidence": "stamp_rc_version.sh --self-test on mini (GNU bash 5.3.20, aarch64-apple-darwin): PASS", + "result": "3/3 PASS (lanes 2 and 3 on the whole batch; r2 and r3 on the fix to lane 1's finding), degraded: same-family" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index c09873c456..5bd5ff011b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Known issues +- **Qwen3 MoE (Qwen3-30B-A3B, Qwen3-Coder-30B-A3B) on GB10 is not claimed for 0.70 (#3715); planned for 0.70.1.** + On GB10 (sm_121) every verb (run, chat, serve, code) times out at 600 s on the 20k-token rung. The 0.69.1 + pre-load refusal is gone in 0.70.0, so these models now load and run, but too slowly to be claimed. The + release gate does not owe the qwen3moe x GB10 cells (`cells.declaimed` in + `contracts/model-capability-ladder-v1.yaml`), and the ladder gate is RED if any rung or long-rung + representative still claims them. Withdrawn, not waived: lambda holds both files and still owes every + qwen3moe cell. +- **GB10 dense models: prompt processing unbatched; planned for 0.70.1 (#4590).** Widens the Qwen3 entry below to every dense model on GB10 (operator ruling 2026-09-28 + 16:15Z): the serial prefill is the sm_12x default for every non-hybrid arch, so dense Qwen2.5 on GB10 + is de-claimed too (`cells.declaimed`: qwen2 and qwen3 on gx10). Output is correct; only prefill speed + is affected. Withdrawn, not waived: lambda still owes every dense cell, gx10 still owes every Qwen3.5 cell. +- **Qwen3 on GB10 (sm_121, and the sm_12x family) is correct but very slow, and is not claimed for + 0.70 (#4590).** Dense Qwen3 on sm_12x prefills one token at a time, the sm_12x default, because the + generic batched prefill corrupts the KV cache there (FALSIFY-CPU-GPU-009). Measured on 0.70.0 + (8d021f61e) at an 8k think-on prompt: Qwen3-1.7B 967 s, Qwen3-8B 2163 s, against 63-80 s for + Qwen3.5-9B on the same host (Qwen3.5 has its own batched prefill, #3596). `apr` prints a + `[KNOWN-ISSUE #4590]` warning when it loads Qwen3 on sm_12x. The release gate does not owe the + Qwen3 x GB10 cells (`cells.declaimed` in `contracts/model-capability-ladder-v1.yaml`); every other + host still owes every Qwen3 cell. Planned for 0.70.1: native sm_121 SASS, then a re-test of batched + prefill on GB10. + ## [0.69.3] - 2026-09-24 0.69.3 is an emergency early release, authorized by the operator: "we need near parity apr serve diff --git a/CLAUDE.md b/CLAUDE.md index 6cc9c0e5a3..00c06c31b1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,7 @@ +--- +tools: [pmat, pv, cargo, gh, bashrs] +--- + # CLAUDE.md ## Project Overview @@ -83,7 +87,7 @@ When using `/loop`, treat fallback wakeups as cheap and merge events as primary. ## Build Commands -```bash +```text cargo build --release # Optimized build (every workspace member; no default-members) cargo test -p aprender-core --lib # Core ML library only cargo test -p apr-cli --lib # CLI tests only @@ -108,7 +112,7 @@ make tier1 # Fast feedback (<1s): fmt, clippy, check make tier2 # Pre-commit (<5s): tests + strict clippy make tier3 # Pre-push (1-5min): full validation + coverage make tier4 # CI/CD: includes pmat analysis -make coverage # Coverage report (enforced floor 88%, target ≥95%) +make coverage # Coverage report (enforced floor: COV_FLOOR in Makefile, target ≥95%) ``` ## Debugging: Use apr Tools First (MANDATORY) @@ -121,17 +125,17 @@ GH-202 lesson: we read code instead of running `apr qa` which would have instant an absolute path to one. Four `apr` binaries were found coexisting on the dev box (0.60.0 ×2, 0.61.0, 0.62.0); a bare `apr` resolved to a **26-day-old** copy, and the path this file used to call "canonical" was two minor versions stale. There is -no correct path to hardcode — `.cargo/config.toml` [gitignored] redirects cargo's +no correct path to hardcode — the gitignored `config.toml` under `.cargo/` redirects cargo's target-dir, so the main checkout and a fresh worktree build to different places. -```bash +```text . scripts/apr_bin.sh || exit 1 # exports $APR, proves it was built from HEAD ``` Everything below uses `"$APR"`. A diagnostic run against the wrong binary is worse than no diagnostic: it produces a confident answer about code you are not running. -```bash +```text # Step 1: ALWAYS start here (catches 80% of issues) "$APR" qa model.apr @@ -168,7 +172,7 @@ All tools support GGUF, APR, and SafeTensors formats. If a tool says "format not `aprender-serve` package (`[lib] name = "realizar"`, `crates/aprender-serve/Cargo.toml`); that package ships no `[[bin]]`. Tracing is driven through `apr run`: -```bash +```text "$APR" run model.safetensors --prompt "2+2?" --trace "$APR" run model.gguf --prompt "Hi" --trace --trace-steps tokenize,sample,decode "$APR" run model.gguf --prompt "Hi" --trace --trace-level payload # or --trace-payload @@ -215,17 +219,17 @@ the crate count run the command in the Project Overview table, don't trust a num | CUDA/GPU Inference | Never | Primary | Kernels | ```rust -// WRONG - bypasses realizar, 0.3 tok/s +// WRONG - bypasses realizar use aprender::models::Qwen2Model; let output = model.generate(&input_ids, 32, 0.7, 0.9); -// CORRECT - uses realizar, 225+ tok/s +// CORRECT - uses realizar use realizar::Model; let model = Model::load_safetensors(&path)?; let output = model.generate(&input_ids, config)?; ``` -```bash +```text # BEST - apr CLI uses realizar automatically cargo run --bin apr --features inference -- run model.safetensors \ --prompt "What is 2+2?" --max-tokens 32 @@ -235,7 +239,7 @@ Feature flag (`crates/apr-cli/Cargo.toml`): `inference = ["realizar", "trueno", "axum", "futures-util"]`, and `default = ["hf-hub", "safetensors-compare", "inference", "training", "visualization", "zram"]` — so `inference` is on unless you pass `--no-default-features`. GPU work needs `--features cuda`, which pulls in `inference`, -`realizar/cuda` and `entrenar/cuda`. +the `cuda` features of `realizar` and `entrenar`. Always profile with `apr profile`/`apr trace`/`apr bench` before optimizing. ### Performance Targets (Ollama Parity) @@ -307,28 +311,34 @@ CONTRACT.validate_apr_shape("lm_head.weight", &[vocab, hidden], vocab, hidden)?; ### Code Scheduled for Deletion - ~~`src/models/qwen2/mod.rs::generate()` / `forward()`~~ - DELETED (Refs #224, #1977). `Qwen2Model` has no inference path; all inference (incl. KV caching) goes through `realizar`. Only construction, weight loading, and introspection remain. -- ~~`examples/qwen_inference.rs`~~ - DELETED (Refs #224). Use the `apr` CLI / `realizar` for inference. +- ~~examples/qwen_inference.rs~~ - DELETED (Refs #224). Use the `apr` CLI / `realizar` for inference. ## Publishing Safety (CB-510 Lesson) **CRITICAL: `.gitignore` and `Cargo.toml` exclude patterns must use root-anchored paths.** -The `models/` pattern silently matches `src/models/` — hiding source code from git and crates.io. Always use `/models/` (root-anchored). +The unanchored pattern **models/** silently matches **src/models/** — hiding source code from git and crates.io. Always use **/models/** (root-anchored). -```bash +```text # Pre-publish checks (also in make tier3). Both print the count they checked — # read it off the output, don't quote a number from this file. bash scripts/check_include_files.sh # scans src/ AND crates/; printed 1771 on 2026-08-13 -bash scripts/check_package_includes.sh # scans src/ ONLY, against `cargo package -p aprender --list` # After creating new include!() files, verify they're not gitignored: git ls-files --others --exclude-standard crates/ git check-ignore -v crates//src/path/to/new_file.rs # exit 1 == not ignored (good) ``` +The one of the two that a merge-path CI step runs verbatim — so it stays a `bash` claim fence (ONT-4c, +`contracts/claude-md.yaml`); the fence above is `text` because no workflow runs those lines: + +```bash +setsid --wait bash scripts/check_package_includes.sh # as CI runs it; scans src/ ONLY, against `cargo package -p aprender --list` +``` + **`check_package_includes.sh` is currently vacuous — know this before trusting it.** It greps `include!(` in the root `src/` only, and the root `src/` is a two-file facade -(`lib.rs`, `bin/`) with **zero** `include!()` directives. So it reports +(`src/lib.rs`, `src/bin/`) with **zero** `include!()` directives. So it reports `OK: All 0 include!() files are included in cargo package` and can never fail. The real coverage against CB-510 today comes from `check_include_files.sh` (1771 files, `src/` + `crates/`). Extending the package check to the 70+ publishable member crates @@ -338,7 +348,7 @@ is open work — do not treat its green as evidence. ## Shell Scripts: Use bashrs (NOT shellcheck) -```bash +```text bashrs lint scripts/*.sh # Lint bashrs purify scripts/ci.sh # Determinism + idempotency bashrs make lint Makefile # Makefile linting @@ -360,7 +370,7 @@ These are not style notes. Each cost real time, and in every case the general principle was known and the specific instance still went wrong. **1. Never read `$?` through a pipe.** It is the LAST command's status. -```bash +```text cmd > /tmp/out.log 2>&1; rc=$? # correct cmd | tee /tmp/out.log; rc=${PIPESTATUS[0]} # correct (bash) cmd | grep -E "^error"; echo "exit=$?" # WRONG — that is grep's status @@ -404,9 +414,9 @@ ran (#2361). When a fix seems to have no effect, ask what else claims that name ## Testing -Target: 60% unit, 30% property, 10% integration. Coverage: **88.78% line** (786448/885829, measured 2026-07-29 by coverage-nightly on 95145584f; target ≥95%, enforced floor 88% via COV_FLOOR). The long-quoted "96.35%" predates the measurement ever working - the pipeline reported 0/0 until #2333. +Target: 60% unit, 30% property, 10% integration. Coverage: **90.84% line** (849706/935347, measured 2026-09-24 by a manual `workflow_dispatch` of coverage-nightly (run 36065904363) on branch `PMAT-3839-cov-regex-scope` @ 237f10f8b, folded into batch/0.70.0 and NOT on `main` until that batch merges; `main`'s own nightly (49fe19c28, 2026-09-24) still measured 826969/941649 = 87.82% under the old regex (run 35937679357); target ≥95%, enforced floor 89% via COV_FLOOR). Before #3839 the exclude regex still described the pre-monorepo tree, so the 88.78% of 2026-07-29 measured a different set of files. The long-quoted "96.35%" predates the measurement ever working - the pipeline reported 0/0 until #2333. -```bash +```text cargo test -p --lib # Unit tests for one crate (what you run while working) cargo test -p --test # ONE integration target. There is NO root # `tests/` dir, so `cargo test --test integration` @@ -491,7 +501,7 @@ the `commands:` list, never `grep -c '^ - name:'` (117; other same-indent `name:` keys exist in the file). Key commands: `run`, `chat`, `serve`, `pull`, `finetune`, `prune`, `distill`, `merge`, `quantize`, `inspect`, `debug`, `validate`, `diff`, `tensors`, `trace`, `lint`, `explain`, `export`, `import`, `convert`, `compile`, `train`, `tune`, `eval`, `bench`, `profile`, `qa`, `mcp`, `probar`, `cbtop`, `tui`, `hex`, `tree`, `flow`, `qualify` -```bash +```text apr validate model.apr --quality apr convert model.safetensors --quantize int8 -o model-int8.apr apr export model.apr --format gguf -o model.gguf @@ -508,7 +518,7 @@ still carries a header comment claiming it was "Updated for PMAT v2.215.0". | Score | Value | Provenance | |-------|-------|------------| -| Line coverage | **88.78%** (786448/885829) | coverage-nightly, 2026-07-29, commit `95145584f`. The long-quoted "96.35%"/"96.94%" predates the pipeline ever working — it reported 0/0 until #2333 | +| Line coverage | **90.84%** (849706/935347) | coverage-nightly `workflow_dispatch` run 36065904363, 2026-09-24, on branch commit `237f10f8b` (monorepo scope, #3839; in batch/0.70.0, not yet `main`, whose scheduled nightly 35937679357 read 87.82%). The previous 88.78% (2026-07-29, `95145584f`) used the pre-monorepo exclude regex. The long-quoted "96.35%"/"96.94%" predates the pipeline ever working — it reported 0/0 until #2333 | | Project score / TDG / mutation % | **re-derive** — `pmat rust-project-score`, `pmat tdg . --include-components`, `cargo mutants` | The previously published "124/134", "TDG 95.2/100" and "Mutation 85.3%" carried no date or commit and could not be reproduced from the tree | **Thresholds — read from the config, which does not say what this file used to say:** @@ -516,7 +526,7 @@ still carries a header comment claiming it was "Updated for PMAT v2.215.0". | Gate | Configured as | Where | |------|---------------|-------| | Coverage (aspirational) | `min_coverage = 95.0` | `.pmat-gates.toml` | -| Coverage (**enforced**) | `COV_FLOOR := 88` — the last *measured* value, and the one that actually fails a build | `Makefile:287`, `.github/workflows/coverage-nightly.yml` | +| Coverage (**enforced**) | `COV_FLOOR := 89` — a ratchet under the last *measured* value, and the one that actually fails a build | `Makefile` (`COV_FLOOR`), `.github/workflows/coverage-nightly.yml` | | Cyclomatic complexity | `max_complexity = 10` per fn | `.pmat-gates.toml` | | TDG | `min_grade = "B"` — **not** "≥95" | `.pmat-gates.toml` `[tdg]` | | Mutation | `MUTANTS_MAX_MISSED` (default **0**) surviving mutants **on the PR diff** — not a global 85% score | `.github/workflows/ci.yml:517` | @@ -526,7 +536,7 @@ still carries a header comment claiming it was "Updated for PMAT v2.215.0". SATD is checked by `pmat analyze satd`, but no SATD threshold is configured in `.pmat-gates.toml`; the pre-commit hook there is `pmat comply check --failures-only`. -```bash +```text pmat quality-gates # Run all gates (config: .pmat-gates.toml) pmat rust-project-score # Project analysis pmat analyze complexity # Cyclomatic/cognitive complexity @@ -547,7 +557,7 @@ unwrap() banned via `.clippy.toml` disallowed-methods. Use `expect()` or `ok_or_ **`pv` is THE dogfooded contract CLI.** When you need to validate, lint, score, scaffold, diff, audit, generate proofs, or run falsification tests on a YAML contract in `contracts/`, use `pv`. Writing a bash/yq/python script that re-implements what `pv` already does is **muda** (waste) and will be rejected. -```bash +```text pv validate contracts/apr-code-parity-v1.yaml # schema + falsification gates pv lint contracts/ # validate + audit + score on all pv status contracts/tensor-layout-v1.yaml # equations, obligations, coverage @@ -565,10 +575,10 @@ verify-bindings, migrate`. **If `pv validate` rejects a contract** (wrong kind, missing required fields), the fix is one of: 1. Restructure the contract to fit the existing schema (usually `KernelContract` shape with `equations`, `proof_obligations`, `falsification_tests`). -2. Extend `aprender-contracts/src/schema/` to add the new `kind` + validator rule (real engineering task, own PMAT ticket). +2. Extend `crates/aprender-contracts/src/schema/` to add the new `kind` + validator rule (real engineering task, own PMAT ticket). 3. If it genuinely isn't a provable contract, use a different YAML schema under a different directory and a purpose-built `apr` subcommand — not `contracts/`. -**Never** work around `pv` with a shell script. The in-tree tool is the source of truth. +**Never:** work around `pv` with a shell script. The in-tree tool is the source of truth. ## CRITICAL: Code Search Policy @@ -586,7 +596,7 @@ verify-bindings, migrate`. ### Examples -```bash +```text # BAD - Raw text search returns 500+ noisy matches with no context # GOOD - Semantic search returns 10 ranked functions with quality metrics pmat query "error handling" --limit 10 @@ -597,7 +607,7 @@ pmat query "error handling" --limit 10 The index automatically includes sibling projects (aprender, trueno, realizar). Query from any project to search 60k+ functions across all three codebases. -```bash +```text # Build index in each project first (one-time setup) cd ~/src/aprender && pmat query "init" --rebuild-index --limit 1 cd ~/src/trueno && pmat query "init" --rebuild-index --limit 1 @@ -616,7 +626,7 @@ pmat query "matrix multiplication" --limit 5 ### Quick Reference -```bash +```text pmat query "" # Basic search pmat query "" --rank-by pagerank # Most important functions pmat query "" --format json # Machine-readable @@ -638,7 +648,7 @@ pmat query "model training" --churn --duplicates --entropy --faults -G # full a **Use `pmat query --coverage` to find untested code. NEVER parse coverage JSON manually.** -```bash +```text # Find top uncovered functions (no query needed) pmat query --coverage-gaps @@ -668,7 +678,7 @@ See monorepo spec Rule 7: Coverage + Contracts Co-Evolution. ## Stack Documentation Search -```bash +```text batuta oracle --rag "your question here" # Search entire Sovereign AI Stack batuta oracle --rag-index # Reindex (the command prints the doc count) ``` diff --git a/Cargo.lock b/Cargo.lock index 06139c722b..e0aa875dee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,7 +248,7 @@ dependencies = [ [[package]] name = "apr-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", @@ -266,6 +266,7 @@ dependencies = [ "aprender-present-core", "aprender-present-terminal", "aprender-profile", + "aprender-ptx-debug", "aprender-quant", "aprender-rag-cli", "aprender-registry", @@ -324,7 +325,7 @@ dependencies = [ [[package]] name = "apr-format" -version = "0.69.3" +version = "0.70.0" dependencies = [ "bincode", "cargo_metadata", @@ -342,7 +343,7 @@ dependencies = [ [[package]] name = "aprender" -version = "0.69.3" +version = "0.70.0" dependencies = [ "apr-cli", "aprender-core", @@ -350,7 +351,7 @@ dependencies = [ [[package]] name = "aprender-bench-compute" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-core", "criterion 0.7.0", @@ -359,7 +360,7 @@ dependencies = [ [[package]] name = "aprender-bench-tokenizer" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-core", "criterion 0.7.0", @@ -368,11 +369,14 @@ dependencies = [ [[package]] name = "aprender-build-sha" -version = "0.69.3" +version = "0.70.0" +dependencies = [ + "tempfile", +] [[package]] name = "aprender-cbtop" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", @@ -398,7 +402,7 @@ dependencies = [ [[package]] name = "aprender-cgp" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", @@ -419,17 +423,19 @@ dependencies = [ [[package]] name = "aprender-common" -version = "0.69.3" +version = "0.70.0" dependencies = [ + "clap", "lz4_flex 0.11.6", "zstd", ] [[package]] name = "aprender-compute" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", + "aprender-contracts", "aprender-contracts-macros", "aprender-core", "aprender-cuda-edge", @@ -476,7 +482,7 @@ dependencies = [ [[package]] name = "aprender-compute-xtask" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", @@ -491,11 +497,13 @@ dependencies = [ [[package]] name = "aprender-contracts" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-contracts-macros", + "blake3", "criterion 0.5.1", "half", + "proc-macro2", "proptest", "regex", "serde", @@ -509,9 +517,10 @@ dependencies = [ [[package]] name = "aprender-contracts-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", + "aprender-common", "aprender-contracts", "aprender-update", "clap", @@ -524,7 +533,7 @@ dependencies = [ [[package]] name = "aprender-contracts-macros" -version = "0.69.3" +version = "0.70.0" dependencies = [ "proc-macro2", "quote", @@ -534,7 +543,7 @@ dependencies = [ [[package]] name = "aprender-contrastive-data" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-contracts-macros", "aprender-rand", @@ -549,7 +558,7 @@ dependencies = [ [[package]] name = "aprender-core" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aes-gcm", "alsa", @@ -603,7 +612,7 @@ dependencies = [ [[package]] name = "aprender-cuda-edge" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-gpu", "proptest", @@ -614,7 +623,7 @@ dependencies = [ [[package]] name = "aprender-cupti" -version = "0.69.3" +version = "0.70.0" dependencies = [ "bindgen", "bitflags 2.13.0", @@ -624,7 +633,7 @@ dependencies = [ [[package]] name = "aprender-data" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aes-gcm", "aprender-build-sha", @@ -677,12 +686,13 @@ dependencies = [ [[package]] name = "aprender-db" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", "aprender-common", "aprender-compute", + "aprender-contracts", "arrow 59.2.0", "axum 0.7.9", "bytemuck", @@ -723,7 +733,7 @@ dependencies = [ [[package]] name = "aprender-distribute" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-compute", "aprender-db", @@ -753,7 +763,7 @@ dependencies = [ [[package]] name = "aprender-explain" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-gpu", @@ -772,7 +782,7 @@ dependencies = [ [[package]] name = "aprender-fft" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "proptest", @@ -781,7 +791,7 @@ dependencies = [ [[package]] name = "aprender-gemm-codegen" -version = "0.69.3" +version = "0.70.0" dependencies = [ "proc-macro2", "quote", @@ -790,7 +800,7 @@ dependencies = [ [[package]] name = "aprender-gpu" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-common", "aprender-simulate", @@ -808,7 +818,7 @@ dependencies = [ [[package]] name = "aprender-graph" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-compute", @@ -829,7 +839,7 @@ dependencies = [ [[package]] name = "aprender-image" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "proptest", @@ -838,7 +848,7 @@ dependencies = [ [[package]] name = "aprender-mcp" -version = "0.69.3" +version = "0.70.0" dependencies = [ "inventory", "jsonschema", @@ -851,7 +861,7 @@ dependencies = [ [[package]] name = "aprender-monte-carlo" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender", "assert_cmd", @@ -870,7 +880,7 @@ dependencies = [ [[package]] name = "aprender-orchestrate" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", @@ -941,9 +951,10 @@ dependencies = [ [[package]] name = "aprender-present-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", + "aprender-contracts", "aprender-present-yaml", "clap", "notify", @@ -955,8 +966,9 @@ dependencies = [ [[package]] name = "aprender-present-core" -version = "0.69.3" +version = "0.70.0" dependencies = [ + "aprender-contracts", "aprender-contracts-macros", "criterion 0.7.0", "proptest", @@ -967,7 +979,7 @@ dependencies = [ [[package]] name = "aprender-present-layout" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-present-core", "criterion 0.7.0", @@ -978,7 +990,7 @@ dependencies = [ [[package]] name = "aprender-present-lib" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-contracts", "aprender-present-core", @@ -1007,9 +1019,10 @@ dependencies = [ [[package]] name = "aprender-present-terminal" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", + "aprender-contracts", "aprender-present-core", "bitvec", "clap", @@ -1028,7 +1041,7 @@ dependencies = [ [[package]] name = "aprender-present-test" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-present-core", "aprender-present-test-macros", @@ -1039,7 +1052,7 @@ dependencies = [ [[package]] name = "aprender-present-test-macros" -version = "0.69.3" +version = "0.70.0" dependencies = [ "proc-macro2", "quote", @@ -1048,7 +1061,7 @@ dependencies = [ [[package]] name = "aprender-present-widgets" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-present-core", "aprender-present-yaml", @@ -1060,7 +1073,7 @@ dependencies = [ [[package]] name = "aprender-present-yaml" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-present-core", "proptest", @@ -1070,7 +1083,7 @@ dependencies = [ [[package]] name = "aprender-profile" -version = "0.69.3" +version = "0.70.0" dependencies = [ "addr2line 0.25.1", "anyhow", @@ -1123,7 +1136,7 @@ dependencies = [ [[package]] name = "aprender-profile-core" -version = "0.69.3" +version = "0.70.0" dependencies = [ "hex", "serde", @@ -1133,7 +1146,7 @@ dependencies = [ [[package]] name = "aprender-ptx-debug" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "clap", @@ -1143,7 +1156,7 @@ dependencies = [ [[package]] name = "aprender-qa-certify" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "chrono", @@ -1155,7 +1168,7 @@ dependencies = [ [[package]] name = "aprender-qa-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-qa-certify", @@ -1175,7 +1188,7 @@ dependencies = [ [[package]] name = "aprender-qa-gen" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "chrono", @@ -1192,7 +1205,7 @@ dependencies = [ [[package]] name = "aprender-qa-report" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-qa-gen", @@ -1210,7 +1223,7 @@ dependencies = [ [[package]] name = "aprender-qa-runner" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-qa-gen", @@ -1235,7 +1248,7 @@ dependencies = [ [[package]] name = "aprender-quant" -version = "0.69.3" +version = "0.70.0" dependencies = [ "half", "serde_json", @@ -1243,10 +1256,11 @@ dependencies = [ [[package]] name = "aprender-rag" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-common", "aprender-compute", + "aprender-contracts", "aprender-serve", "async-trait", "bincode", @@ -1271,10 +1285,11 @@ dependencies = [ [[package]] name = "aprender-rag-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-build-sha", + "aprender-common", "aprender-rag", "assert_cmd", "blake3", @@ -1290,7 +1305,7 @@ dependencies = [ [[package]] name = "aprender-rand" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "proptest", @@ -1299,7 +1314,7 @@ dependencies = [ [[package]] name = "aprender-registry" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "aprender-core", @@ -1327,9 +1342,19 @@ dependencies = [ "zstd", ] +[[package]] +name = "aprender-review-experiment" +version = "0.70.0" +dependencies = [ + "serde", + "serde_json", + "sha2 0.10.9", + "ureq 2.12.1", +] + [[package]] name = "aprender-serve" -version = "0.69.3" +version = "0.70.0" dependencies = [ "anyhow", "approx", @@ -1400,7 +1425,7 @@ dependencies = [ [[package]] name = "aprender-shell" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-core", "clap", @@ -1415,7 +1440,7 @@ dependencies = [ [[package]] name = "aprender-simulate" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-contracts", @@ -1439,6 +1464,7 @@ dependencies = [ "proptest", "rand 0.9.4", "rand_pcg", + "regex", "serde", "serde-wasm-bindgen", "serde_json", @@ -1456,7 +1482,7 @@ dependencies = [ [[package]] name = "aprender-solve" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "half", @@ -1466,7 +1492,7 @@ dependencies = [ [[package]] name = "aprender-sparse" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "proptest", @@ -1475,7 +1501,7 @@ dependencies = [ [[package]] name = "aprender-tensor" -version = "0.69.3" +version = "0.70.0" dependencies = [ "criterion 0.7.0", "proptest", @@ -1484,7 +1510,7 @@ dependencies = [ [[package]] name = "aprender-test-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-test-lib", @@ -1513,7 +1539,7 @@ dependencies = [ [[package]] name = "aprender-test-derive" -version = "0.69.3" +version = "0.70.0" dependencies = [ "proc-macro2", "quote", @@ -1523,7 +1549,7 @@ dependencies = [ [[package]] name = "aprender-test-js-gen" -version = "0.69.3" +version = "0.70.0" dependencies = [ "blake3", "chrono", @@ -1539,7 +1565,7 @@ dependencies = [ [[package]] name = "aprender-test-lib" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-compute", "aprender-present-core", @@ -1585,7 +1611,7 @@ dependencies = [ [[package]] name = "aprender-test-showcase" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-present-terminal", "aprender-test-lib", @@ -1602,7 +1628,7 @@ dependencies = [ [[package]] name = "aprender-train" -version = "0.69.3" +version = "0.70.0" dependencies = [ "approx", "aprender-common", @@ -1671,7 +1697,7 @@ dependencies = [ [[package]] name = "aprender-train-bench" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-train", @@ -1689,7 +1715,7 @@ dependencies = [ [[package]] name = "aprender-train-common" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-viz", "clap", @@ -1701,7 +1727,7 @@ dependencies = [ [[package]] name = "aprender-train-distill" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-train", @@ -1723,7 +1749,7 @@ dependencies = [ [[package]] name = "aprender-train-inspect" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-train", @@ -1740,7 +1766,7 @@ dependencies = [ [[package]] name = "aprender-train-lora" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-train", @@ -1757,7 +1783,7 @@ dependencies = [ [[package]] name = "aprender-train-shell" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-train", @@ -1774,7 +1800,7 @@ dependencies = [ [[package]] name = "aprender-train-wasm" -version = "0.69.3" +version = "0.70.0" dependencies = [ "proptest", "serde", @@ -1785,7 +1811,7 @@ dependencies = [ [[package]] name = "aprender-tsp" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender", "assert_cmd", @@ -1800,7 +1826,7 @@ dependencies = [ [[package]] name = "aprender-update" -version = "0.69.3" +version = "0.70.0" dependencies = [ "flate2", "semver", @@ -1814,7 +1840,7 @@ dependencies = [ [[package]] name = "aprender-verify" -version = "0.69.3" +version = "0.70.0" dependencies = [ "chrono", "criterion 0.5.1", @@ -1826,7 +1852,7 @@ dependencies = [ [[package]] name = "aprender-verify-ml" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-compute", @@ -1857,7 +1883,7 @@ dependencies = [ [[package]] name = "aprender-viz" -version = "0.69.3" +version = "0.70.0" dependencies = [ "approx", "aprender-common", @@ -1886,7 +1912,7 @@ dependencies = [ [[package]] name = "aprender-zram" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-zram-adaptive", "aprender-zram-core", @@ -1896,7 +1922,7 @@ dependencies = [ [[package]] name = "aprender-zram-adaptive" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-zram-core", "proptest", @@ -1905,7 +1931,7 @@ dependencies = [ [[package]] name = "aprender-zram-cli" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-zram-adaptive", @@ -1920,7 +1946,7 @@ dependencies = [ [[package]] name = "aprender-zram-core" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-gpu", "criterion 0.7.0", @@ -1932,7 +1958,7 @@ dependencies = [ [[package]] name = "aprender-zram-generator" -version = "0.69.3" +version = "0.70.0" dependencies = [ "aprender-build-sha", "aprender-zram-core", @@ -3833,9 +3859,9 @@ dependencies = [ [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.3.0", @@ -10455,7 +10481,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "chacha20 0.10.0", + "chacha20 0.10.2", "getrandom 0.4.3", "rand_core 0.10.1", ] diff --git a/Cargo.toml b/Cargo.toml index e837d10f42..150b0cb5ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,8 @@ members = [ "crates/aprender-zram-adaptive", "crates/aprender-zram-cli", "crates/aprender-zram-generator", + # --- REX-001 (#4354): review-lane experiment harness + frozen analysis --- + "crates/aprender-review-experiment", # --- APR-MONO Phase 3b: presentar sub-crates (enabled) --- "crates/aprender-present-core", "crates/aprender-present-terminal", @@ -104,10 +106,9 @@ members = [ "crates/aprender-qa-report", "crates/aprender-qa-certify", "crates/aprender-qa-cli", - # Remaining excluded (workspace root shells with no src): - # "crates/aprender-viz-ttop", - # "crates/aprender-present", - # "crates/aprender-test", + # Remaining excluded (see `exclude` below): aprender-present and + # aprender-test are workspace root shells with no package; aprender-viz-ttop + # IS a package and ships a binary (ledgered in contracts/binary-debt-v1.yaml). ] exclude = [ # Pulls burn 0.21 -> rusqlite 0.37 -> libsqlite3-sys 0.35, while aprender-rag @@ -126,8 +127,11 @@ exclude = [ 'crates/aprender-zram/bins/trueno-ublk', "tools/ccpa-sft-export", "fuzz", - # Old workspace root shells (no package, just held sub-crates): + # A real package that ships the `aprender-viz-ttop` binary, NOT a shell; + # excluded, so cargo metadata cannot see it. scripts/check_binary_debt.sh + # derives it from this list (#4058). "crates/aprender-viz-ttop", + # Old workspace root shells (no package, just held sub-crates): "crates/aprender-present", "crates/aprender-test", # crates.io compatibility facades for the names APR-MONO renamed @@ -153,7 +157,7 @@ exclude = [ resolver = "2" [workspace.package] -version = "0.69.3" +version = "0.70.0" edition = "2021" license = "MIT" repository = "https://github.com/paiml/aprender" @@ -234,7 +238,7 @@ tokenizers = { version = "0.23.1", default-features = false, features = ["fancy- # aprender-rand (lib name `trueno_rand`) is a workspace MEMBER but had no # [workspace.dependencies] entry, so every consumer had to re-declare the path. # It is the counter-based Philox RNG behind the SetFit encoder's dropout masks. -aprender-rand = { path = "crates/aprender-rand", version = "0.69.3" } +aprender-rand = { path = "crates/aprender-rand", version = "0.70.0" } # Chat templates minijinja = { version = "2.14", features = ["loader", "serde"] } @@ -244,9 +248,9 @@ minijinja = { version = "2.14", features = ["loader", "serde"] } # `crates/aprender-contracts` (lib name `provable_contracts`). Declaring it from # crates.io compiled a SECOND copy of the contract engine alongside the in-tree # one — exactly the duplicate-crate + publish-cycle the consolidation removed. -provable-contracts = { path = "crates/aprender-contracts", version = "0.69.3", package = "aprender-contracts" } -provable-contracts-macros = { path = "crates/aprender-contracts-macros", version = "0.69.3", package = "aprender-contracts-macros" } -aprender-contracts-macros = { path = "crates/aprender-contracts-macros", version = "0.69.3" } +provable-contracts = { path = "crates/aprender-contracts", version = "0.70.0", package = "aprender-contracts" } +provable-contracts-macros = { path = "crates/aprender-contracts-macros", version = "0.70.0", package = "aprender-contracts-macros" } +aprender-contracts-macros = { path = "crates/aprender-contracts-macros", version = "0.70.0" } # YAML serde_yaml = "0.9" @@ -258,68 +262,69 @@ hf-hub = { version = "0.4", default-features = false, features = ["ureq"] } hf-xet = "1.5.1" # Workspace-internal crates (paths within the monorepo) -aprender-compute = { path = "crates/aprender-compute", version = "0.69.3" } -aprender-gpu = { path = "crates/aprender-gpu", version = "0.69.3" } -aprender-quant = { path = "crates/aprender-quant", version = "0.69.3" } -aprender-serve = { path = "crates/aprender-serve", version = "0.69.3" } -realizar = { path = "crates/aprender-serve", version = "0.69.3", package = "aprender-serve" } -alimentar = { path = "crates/aprender-data", version = "0.69.3", package = "aprender-data" } -pacha = { path = "crates/aprender-registry", version = "0.69.3", package = "aprender-registry" } +aprender-compute = { path = "crates/aprender-compute", version = "0.70.0" } +aprender-gpu = { path = "crates/aprender-gpu", version = "0.70.0" } +aprender-quant = { path = "crates/aprender-quant", version = "0.70.0" } +aprender-serve = { path = "crates/aprender-serve", version = "0.70.0" } +realizar = { path = "crates/aprender-serve", version = "0.70.0", package = "aprender-serve" } +alimentar = { path = "crates/aprender-data", version = "0.70.0", package = "aprender-data" } +pacha = { path = "crates/aprender-registry", version = "0.70.0", package = "aprender-registry" } # APR-MONO self-containment: legacy sibling lib-name aliases -> in-monorepo crates (2026-06-12) -trueno-quant = { path = "crates/aprender-quant", version = "0.69.3", package = "aprender-quant" } -trueno-db = { path = "crates/aprender-db", version = "0.69.3", package = "aprender-db" } -trueno-viz = { path = "crates/aprender-viz", version = "0.69.3", package = "aprender-viz" } -trueno-rag = { path = "crates/aprender-rag", version = "0.69.3", package = "aprender-rag" } -trueno-cuda-edge = { path = "crates/aprender-cuda-edge", version = "0.69.3", package = "aprender-cuda-edge" } -trueno-sparse = { path = "crates/aprender-sparse", version = "0.69.3", package = "aprender-sparse" } -trueno-solve = { path = "crates/aprender-solve", version = "0.69.3", package = "aprender-solve" } -renacer-core = { path = "crates/aprender-profile-core", version = "0.69.3", package = "aprender-profile-core" } -repartir = { path = "crates/aprender-distribute", version = "0.69.3", package = "aprender-distribute" } -simular = { path = "crates/aprender-simulate", version = "0.69.3", package = "aprender-simulate" } -probar = { path = "crates/aprender-test-lib", version = "0.69.3", package = "aprender-test-lib" } +trueno-quant = { path = "crates/aprender-quant", version = "0.70.0", package = "aprender-quant" } +trueno-db = { path = "crates/aprender-db", version = "0.70.0", package = "aprender-db" } +trueno-viz = { path = "crates/aprender-viz", version = "0.70.0", package = "aprender-viz" } +trueno-rag = { path = "crates/aprender-rag", version = "0.70.0", package = "aprender-rag" } +trueno-cuda-edge = { path = "crates/aprender-cuda-edge", version = "0.70.0", package = "aprender-cuda-edge" } +trueno-sparse = { path = "crates/aprender-sparse", version = "0.70.0", package = "aprender-sparse" } +trueno-solve = { path = "crates/aprender-solve", version = "0.70.0", package = "aprender-solve" } +renacer-core = { path = "crates/aprender-profile-core", version = "0.70.0", package = "aprender-profile-core" } +repartir = { path = "crates/aprender-distribute", version = "0.70.0", package = "aprender-distribute" } +simular = { path = "crates/aprender-simulate", version = "0.70.0", package = "aprender-simulate" } +probar = { path = "crates/aprender-test-lib", version = "0.70.0", package = "aprender-test-lib" } # English key for the same crate, named after the command it serves (`apr test`). # The dep KEY, not the package name, is what becomes the extern crate name when # the two differ — the lib target is still `jugar_probar`, so keying it by its # package name would have imported Spanish anyway. Renaming that lib target is # 196 call sites in 17 crates and gets its own PR; this key is how new code # reads English today. -apr-test = { path = "crates/aprender-test-lib", version = "0.69.3", package = "aprender-test-lib", default-features = false, features = ["llm"] } +apr-test = { path = "crates/aprender-test-lib", version = "0.70.0", package = "aprender-test-lib", default-features = false, features = ["llm"] } # `jugar-probar` is the crates.io/lib name of the same in-tree crate as `probar` # above. Both aliases resolve to ONE package; consumers that spelled it # `jugar-probar = "0.4"|"0.5"|"1.0"` were building three extra copies. -jugar-probar = { path = "crates/aprender-test-lib", version = "0.69.3", package = "aprender-test-lib" } +jugar-probar = { path = "crates/aprender-test-lib", version = "0.70.0", package = "aprender-test-lib" } # NOTE: the matching `jugar-probar-derive` alias already exists further down this # same table (it points at crates/aprender-test-derive). crates/aprender-test-lib # was pinning `jugar-probar-derive = "=1.0.3"` from crates.io regardless — the # alias was sitting there unused. -aprender-train = { path = "crates/aprender-train", version = "0.69.3" } -entrenar = { path = "crates/aprender-train", version = "0.69.3", package = "aprender-train" } -aprender-train-common = { path = "crates/aprender-train-common", version = "0.69.3" } -aprender-train-distill = { path = "crates/aprender-train-distill", version = "0.69.3" } -aprender-orchestrate = { path = "crates/aprender-orchestrate", version = "0.69.3" } -aprender-contracts = { path = "crates/aprender-contracts", version = "0.69.3" } -aprender-build-sha = { path = "crates/aprender-build-sha", version = "0.69.3" } -aprender-profile = { path = "crates/aprender-profile", version = "0.69.3" } -aprender-profile-core = { path = "crates/aprender-profile-core", version = "0.69.3" } -aprender-zram-core = { path = "crates/aprender-zram-core", version = "0.69.3" } -aprender-zram-adaptive = { path = "crates/aprender-zram-adaptive", version = "0.69.3" } -aprender-zram-cli = { path = "crates/aprender-zram-cli", version = "0.69.3" } -aprender-present-core = { path = "crates/aprender-present-core", version = "0.69.3" } -aprender-present-terminal = { path = "crates/aprender-present-terminal", version = "0.69.3" } -aprender-present-widgets = { path = "crates/aprender-present-widgets", version = "0.69.3" } -aprender-present-layout = { path = "crates/aprender-present-layout", version = "0.69.3" } -aprender-present-yaml = { path = "crates/aprender-present-yaml", version = "0.69.3" } -aprender-present-test = { path = "crates/aprender-present-test", version = "0.69.3" } -aprender-db = { path = "crates/aprender-db", version = "0.69.3" } -aprender-graph = { path = "crates/aprender-graph", version = "0.69.3" } -aprender-rag = { path = "crates/aprender-rag", version = "0.69.3" } -aprender-rag-cli = { path = "crates/aprender-rag-cli", version = "0.69.3" } -aprender-qa-cli = { path = "crates/aprender-qa-cli", version = "0.69.3" } -aprender-cgp = { path = "crates/aprender-cgp", version = "0.69.3" } -aprender-contrastive-data = { path = "crates/aprender-contrastive-data", version = "0.69.3" } -aprender-contracts-cli = { path = "crates/aprender-contracts-cli", version = "0.69.3" } -aprender-update = { path = "crates/aprender-update", version = "0.69.3" } -aprender-data = { path = "crates/aprender-data", version = "0.69.3" } +aprender-train = { path = "crates/aprender-train", version = "0.70.0" } +entrenar = { path = "crates/aprender-train", version = "0.70.0", package = "aprender-train" } +aprender-train-common = { path = "crates/aprender-train-common", version = "0.70.0" } +aprender-train-distill = { path = "crates/aprender-train-distill", version = "0.70.0" } +aprender-orchestrate = { path = "crates/aprender-orchestrate", version = "0.70.0" } +aprender-contracts = { path = "crates/aprender-contracts", version = "0.70.0" } +aprender-build-sha = { path = "crates/aprender-build-sha", version = "0.70.0" } +aprender-profile = { path = "crates/aprender-profile", version = "0.70.0" } +aprender-profile-core = { path = "crates/aprender-profile-core", version = "0.70.0" } +aprender-zram-core = { path = "crates/aprender-zram-core", version = "0.70.0" } +aprender-zram-adaptive = { path = "crates/aprender-zram-adaptive", version = "0.70.0" } +aprender-zram-cli = { path = "crates/aprender-zram-cli", version = "0.70.0" } +aprender-present-core = { path = "crates/aprender-present-core", version = "0.70.0" } +aprender-present-terminal = { path = "crates/aprender-present-terminal", version = "0.70.0" } +aprender-present-widgets = { path = "crates/aprender-present-widgets", version = "0.70.0" } +aprender-present-layout = { path = "crates/aprender-present-layout", version = "0.70.0" } +aprender-present-yaml = { path = "crates/aprender-present-yaml", version = "0.70.0" } +aprender-present-test = { path = "crates/aprender-present-test", version = "0.70.0" } +aprender-db = { path = "crates/aprender-db", version = "0.70.0" } +aprender-graph = { path = "crates/aprender-graph", version = "0.70.0" } +aprender-rag = { path = "crates/aprender-rag", version = "0.70.0" } +aprender-rag-cli = { path = "crates/aprender-rag-cli", version = "0.70.0" } +aprender-qa-cli = { path = "crates/aprender-qa-cli", version = "0.70.0" } +aprender-cgp = { path = "crates/aprender-cgp", version = "0.70.0" } +aprender-contrastive-data = { path = "crates/aprender-contrastive-data", version = "0.70.0" } +aprender-contracts-cli = { path = "crates/aprender-contracts-cli", version = "0.70.0" } +aprender-update = { path = "crates/aprender-update", version = "0.70.0" } +aprender-data = { path = "crates/aprender-data", version = "0.70.0" } +aprender-ptx-debug = { path = "crates/aprender-ptx-debug", version = "0.70.0" } # Additional external deps needed by sub-crates serde_yaml_ng = "0.10" @@ -378,25 +383,25 @@ wasmtime = "48.0.3" bollard = "0.17" # Old crate name aliases (workspace = true refs in migrated sub-crates) -trueno-zram-core = { path = "crates/aprender-zram-core", version = "0.69.3", package = "aprender-zram-core" } -trueno-zram-adaptive = { path = "crates/aprender-zram-adaptive", version = "0.69.3", package = "aprender-zram-adaptive" } -trueno = { path = "crates/aprender-compute", version = "0.69.3", package = "aprender-compute" } -presentar-core = { path = "crates/aprender-present-core", version = "0.69.3", package = "aprender-present-core" } -presentar-terminal = { path = "crates/aprender-present-terminal", version = "0.69.3", package = "aprender-present-terminal" } -presentar-widgets = { path = "crates/aprender-present-widgets", version = "0.69.3", package = "aprender-present-widgets" } -presentar-layout = { path = "crates/aprender-present-layout", version = "0.69.3", package = "aprender-present-layout" } -presentar-yaml = { path = "crates/aprender-present-yaml", version = "0.69.3", package = "aprender-present-yaml" } -presentar-test = { path = "crates/aprender-present-test", version = "0.69.3", package = "aprender-present-test" } -presentar-test-macros = { path = "crates/aprender-present-test-macros", version = "0.69.3", package = "aprender-present-test-macros" } -jugar-probar-derive = { path = "crates/aprender-test-derive", version = "0.69.3", package = "aprender-test-derive" } -batuta-common = { path = "crates/aprender-common", version = "0.69.3", package = "aprender-common" } +trueno-zram-core = { path = "crates/aprender-zram-core", version = "0.70.0", package = "aprender-zram-core" } +trueno-zram-adaptive = { path = "crates/aprender-zram-adaptive", version = "0.70.0", package = "aprender-zram-adaptive" } +trueno = { path = "crates/aprender-compute", version = "0.70.0", package = "aprender-compute" } +presentar-core = { path = "crates/aprender-present-core", version = "0.70.0", package = "aprender-present-core" } +presentar-terminal = { path = "crates/aprender-present-terminal", version = "0.70.0", package = "aprender-present-terminal" } +presentar-widgets = { path = "crates/aprender-present-widgets", version = "0.70.0", package = "aprender-present-widgets" } +presentar-layout = { path = "crates/aprender-present-layout", version = "0.70.0", package = "aprender-present-layout" } +presentar-yaml = { path = "crates/aprender-present-yaml", version = "0.70.0", package = "aprender-present-yaml" } +presentar-test = { path = "crates/aprender-present-test", version = "0.70.0", package = "aprender-present-test" } +presentar-test-macros = { path = "crates/aprender-present-test-macros", version = "0.70.0", package = "aprender-present-test-macros" } +jugar-probar-derive = { path = "crates/aprender-test-derive", version = "0.70.0", package = "aprender-test-derive" } +batuta-common = { path = "crates/aprender-common", version = "0.70.0", package = "aprender-common" } # APR-MONO sibling aliases — MUST use workspace refs, NEVER crates.io versions. # Hard-pinning these to crates.io creates a diamond: sub-crates pull older # aprender versions → multiple `aprender` crates in Cargo.lock = MUDA. -renacer = { path = "crates/aprender-profile", version = "0.69.3", package = "aprender-profile" } -entrenar-lora = { path = "crates/aprender-train-lora", version = "0.69.3", package = "aprender-train-lora" } -batuta = { path = "crates/aprender-orchestrate", version = "0.69.3", package = "aprender-orchestrate" } -trueno-graph = { path = "crates/aprender-graph", version = "0.69.3", package = "aprender-graph" } +renacer = { path = "crates/aprender-profile", version = "0.70.0", package = "aprender-profile" } +entrenar-lora = { path = "crates/aprender-train-lora", version = "0.70.0", package = "aprender-train-lora" } +batuta = { path = "crates/aprender-orchestrate", version = "0.70.0", package = "aprender-orchestrate" } +trueno-graph = { path = "crates/aprender-graph", version = "0.70.0", package = "aprender-graph" } [workspace.lints.rust] # Safety @@ -578,7 +583,7 @@ semicolon_if_nothing_returned = "allow" # Style preference [package] name = "aprender" -version = "0.69.3" +version = "0.70.0" edition = "2021" rust-version = "1.91" authors = ["Noah Gift "] @@ -670,6 +675,21 @@ gates = [ # Release time only, like the entry above; the PR-time half (the helpers' case table) is # scripts/check_tokenizer_parity.sh, which guard_tree.sh dispatches. "scripts/tokenizer_parity.sh", + # #4476 ONT-4g: docs/audits/surface_audit.csv has > 0 rows and names exactly the [[bin]] + # targets cargo metadata declares at the release sha, printed N/N; any mismatch is RED by + # name. The PR-time half is its case table, scripts/check_surface_audit_bins.sh, which + # guard_tree.sh dispatches (an empty ledger must turn it RED). + "scripts/surface_audit_bins_gate.sh", + # #3740 — in-tree tests that compare against llama.cpp read ONLY the pinned build that + # scripts/llama_bin.sh proves and exports; without it they skip. The release runs them WITH it, + # and a skip here is a FAIL, so a skip is never the release path. + "scripts/dogfood_comparator_env_tests.sh", + # #4114: every publishable crate's tests compile FROM ITS .crate (packaged, unpacked outside + # the tree, siblings patched to their tarballs). The source reader check_package_includes.sh + # skipped #[cfg(test)] modules and passed aprender-serve 0.69.1, whose tarball cannot compile + # its tests. Release time only: one workspace test build. The PR-time half is its case table, + # rows 14-16 of check_package_includes.sh --self-test (ci.yml guard-cargo). + "scripts/package_tarball_build.sh", ] # The interfaces this release SHIPS, each paired with the e2e target that proves @@ -726,9 +746,9 @@ required-features = ["cli"] # (e.g. apr-cookbook, downstream apps) don't pull apr-cli's heavy transitive # dep graph (renacer/profile/inference/zram/visualization). `cargo install # aprender` still works because `cli` is in default features. See GH-1599. -apr-cli = { path = "crates/apr-cli", version = "0.69.3", default-features = true, optional = true } +apr-cli = { path = "crates/apr-cli", version = "0.70.0", default-features = true, optional = true } # Core ML library (lib name = "aprender") -aprender_ml = { path = "crates/aprender-core", version = "0.69.3", package = "aprender-core" } +aprender_ml = { path = "crates/aprender-core", version = "0.70.0", package = "aprender-core" } [features] default = ["cli"] @@ -888,4 +908,15 @@ debug = "line-tables-only" [profile.dev.package.proptest] debug-assertions = false +# ---------------------------------------------------------------------------- +# x86-main SLOW tests: debug builds do not inline trueno's SIMD intrinsics +# ---------------------------------------------------------------------------- +# gdb samples of the LoRA falsify tests sat ~all in gemm_blis -> +# gemm_small_avx512_16x8 / gemm_direct_rowmajor, each `_mm256_fmadd_ps` an +# out-of-line call at opt-level 0. Optimizing only the compute crate keeps every +# other crate debuggable. Interleaved A/B on lambda (aprender-e6, 2026-09-26): +# f_conv_008 67.5s -> 32.2s, f_conv_003 92.7s -> 47.5s, ship_021 63.3s -> 23.1s. +[profile.dev.package.aprender-compute] +opt-level = 3 + diff --git a/Makefile b/Makefile index b0ba092e05..7488716e38 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ SHELL := /bin/bash # Multi-line recipes execute in same shell .ONESHELL: -.PHONY: all build test test-smoke test-fast test-quick test-full test-heavy lint lint-current fmt clean doc book book-build book-serve book-test tier1 tier2 tier3 tier4 coverage coverage-fast profile hooks-install hooks-verify lint-scripts bashrs-score bashrs-lint-makefile chaos-test chaos-test-full chaos-test-lite fuzz bench dev pre-push ci gate check run-ci run-bench audit deps-validate deny pmat-score pmat-gates quality-report semantic-search examples mutants mutants-fast property-test install-alsa test-alsa test-audio-full contract-validate contract-test contract-audit contract-regen contract-check dev-setup check-siblings check-wasm32 contrastive-data-boundary contrastive-data-boundary-cases +.PHONY: all build guards-local test test-smoke test-fast test-quick test-full test-heavy lint lint-current fmt clean doc book book-build book-serve book-test tier1 tier2 tier3 tier4 coverage coverage-fast profile hooks-install hooks-verify lint-scripts bashrs-score bashrs-lint-makefile chaos-test chaos-test-full chaos-test-lite fuzz bench dev pre-push ci gate check run-ci run-bench audit deps-validate deny pmat-score pmat-gates quality-report semantic-search examples mutants mutants-fast property-test install-alsa test-alsa test-audio-full contract-validate contract-test contract-audit contract-regen contract-check dev-setup check-siblings check-wasm32 contrastive-data-boundary contrastive-data-boundary-cases # Default target all: tier2 @@ -493,10 +493,14 @@ COV_REFUSE_GLOBAL_MOLD = @if [ -f "$${CARGO_HOME:-$$HOME/.cargo}/config.toml" ] # Test infrastructure: # - test_factory : Test code, not production # - demo/ : Demo/example code -# NOTE: Coverage tracks the main aprender library only. -# Subcrate tests still RUN (--workspace), exercising main lib code paths, -# but subcrate source files are excluded from the coverage REPORT. -# External deps (trueno, realizar, .cargo) also excluded. +# NOTE (#3839): coverage measures the MONOREPO. #4023 scopes every report by a derived +# `-p` list, so aprender-serve/-train/-compute (formerly realizar/entrenar/trueno) are +# measured. The pre-monorepo `trueno|realizar/|entrenar/` alternatives were removed: in-tree +# `entrenar/` matched 0 files, `realizar/` 7 unrelated aprender-train files, and `trueno` +# 54/56 of aprender-zram while missing aprender-compute entirely. +# aprender-compute/src/backends/gpu/ : no coverage runner executes it (no GPU lane), so it +# is kept out of the denominator until one exists - excluded AND unrun, never "measured 0%". +# Named subcrates below (apr-cli, aprender-shell, ...) and .cargo stay excluded. # Subcrate code, external deps, and modules requiring external model files for coverage. # models/ = dead code per UCBD §9.1 (scheduled for deletion). # serialization/ = SafeTensors IO (needs actual .safetensors files). @@ -506,7 +510,7 @@ COV_REFUSE_GLOBAL_MOLD = @if [ -f "$${CARGO_HOME:-$$HOME/.cargo}/config.toml" ] # format/rosetta = cross-format parity (needs model files). # transfer/ = transfer learning (needs pretrained models). # bench/ = benchmark visualization (non-core). -COVERAGE_EXCLUDE_REGEX := \.cargo/|trueno|realizar/|entrenar/|fuzz/|golden_traces/|hf_hub/|demo/|test_factory|pacha/|showcase/|apr-cli/|aprender-shell/|aprender-tsp/|aprender-monte-carlo/|chaos\.rs|audio/|format/quantize\.rs|format/signing\.rs|voice/|playback\.rs|rustlib/src/rust|models/|serialization/|speech/|format/onnx|format/converter|format/rosetta|transfer/|bench_viz/ +COVERAGE_EXCLUDE_REGEX := \.cargo/|aprender-compute/src/backends/gpu/|fuzz/|golden_traces/|hf_hub/|demo/|test_factory|pacha/|showcase/|apr-cli/|aprender-shell/|aprender-tsp/|aprender-monte-carlo/|chaos\.rs|audio/|format/quantize\.rs|format/signing\.rs|voice/|playback\.rs|rustlib/src/rust|models/|serialization/|speech/|format/onnx|format/converter|format/rosetta|transfer/|bench_viz/ # Coverage threshold (enforced: fail if below) COV_THRESHOLD := 95 @@ -564,7 +568,7 @@ COV_CARGO_ENV := $(if $(COV_TARGET_DIR),CARGO_TARGET_DIR=$(COV_TARGET_DIR)) # DERIVED from `cargo metadata` (scripts/coverage_report_scope.py), the verified alternative # above, and scripts/check_coverage_report_scoped.sh refuses any unscoped `llvm-cov report`. profraw # survive it (31 present afterwards), so coverage-html still has data to work from. -.PHONY: coverage-check contracts +.PHONY: coverage-check contracts census # BSE-03 phase A (Pmat-Ticket: PMAT-1068). The README's contract count is # DERIVED: scripts/readme_sync.sh rewrites the text between the @@ -588,6 +592,7 @@ readme-sync-check: ## Fail if README.md is not what the generator produces # merge-tree measurement READS A FILE ON DISK must turn the hand-edited rows # GREEN, which is what makes their RED load-bearing rather than incidental. # `--class complexity` and `--class satd` are stubs and exit 3, never 0. +.PHONY: oracle-owl oracle-owl-check .PHONY: roadmap-aggregate roadmap-aggregate-check roadmap-aggregate: ## Regenerate docs/roadmaps/roadmap.yaml from docs/roadmaps/entries/ (#3296) @python3 scripts/lib/roadmap_fragments.py aggregate --write @@ -605,45 +610,42 @@ ratchet-semantics-test: ## BSE-03: D2 ratchet polarity rows (--class readme) # enforces COV_FLOOR, so this is a name, not a new policy. coverage-check: coverage +# PVL-001 EV-6a (#4139): the ONLY writer of the Lean label ratchet. `pv discharge check` never writes +# unresolved-labels.json; this rewrites it DOWNWARD (a label that resolves now leaves; a new one is never added). +.PHONY: label-ratchet +label-ratchet: + @. scripts/pv_bin.sh && "$$PV" discharge label-ratchet crates/aprender-contracts-staging/lean --contracts contracts + # Ditto for `contracts`. The provable-contract tier is a HARD release gate per # CLAUDE.md, and the dogfood protocol looked for a target that did not exist, so # it WARNed instead of checking. `pv lint` runs validate + audit + score across # contracts/ and is the documented entry point (never hand-rolled bash). +# The release train's one census writer (#3569). A PR that runs this and commits +# the result is refused by scripts/check_census_derived.sh; the train runs it on +# release/X.Y.Z, where the guard exempts the edit. +census: + @. scripts/pv_bin.sh && t=$$(mktemp contracts/census.json.XXXXXX) && "$$PV" census contracts --format json > "$$t" && bash scripts/check_census_derived.sh --census "$$t" && mv "$$t" contracts/census.json || { rm -f "$$t"; exit 1; } + @bash scripts/readme_sync.sh --write + # EXIT PROPAGATION (PVL-001 EV-4, aprender#4168). Under .ONESHELL this whole # recipe is ONE shell script, so without errexit its status is the LAST line's # and every earlier step -- `pv lint` included -- was advisory: a failing lint -# printed its tail and the gate exited 0. `set -e` stops at a failing step. -# It is NOT enough on the pv lines: errexit ignores a failure on the LEFT of -# `&&`, so a pv_bin.sh that REFUSES the binary (stale, wrong identity) would -# fall through to the next step. Hence `|| exit` there as well, which exits -# with that list's own status (pv's rc through the pipe, via -o pipefail). -# Case table + mutants: scripts/tests/make_contracts_propagates.sh. +# printed its tail and the gate exited 0. So every line that can fail ends in +# `|| exit`, which ends the recipe with that line's own status. +# Case table + mutants: scripts/tests/make_contracts_propagates.sh (this recipe) +# and `scripts/contracts_gate.sh --self-test` (the gate's steps). contracts: - @set -e - @echo "== provable contracts: pv lint contracts/ ==" -# `| tail -5` DISCARDED THE VERDICT: the pipeline's status is tail's, so the armed-meet -# result was PRINTED and NOT ENFORCED (found by aprender-d8, 0.69.1 tail rehearsal). That -# is Verification Discipline #1 in the release's own contract gate, and -# contracts-exit-integrity does not catch it -- it looks for `|| true` and bare for-loops, -# not for a pipe. The output is kept to a tail for readability by writing it to a file and -# tailing THAT, so the exit status belongs to pv and nothing else. -# .ONESHELL: the whole recipe is ONE shell, so an unconditional `exit $$rc` here ended the -# recipe green after lint -- census, graph, README, provenance and the engine tests never ran. -# Exit only on failure (#4315, caught by scripts/tests/make_contracts_propagates.sh). - @. scripts/pv_bin.sh && { "$$PV" lint contracts/ > /tmp/pv-lint-contracts.$$$$.log 2>&1; rc=$$?; tail -5 /tmp/pv-lint-contracts.$$$$.log; rm -f /tmp/pv-lint-contracts.$$$$.log; [ $$rc -eq 0 ] || exit $$rc; } || exit - @echo "== census: tracked contracts/census.json == a fresh one (ONT-001 ONT-1, F-1) ==" - @git ls-files --error-unmatch contracts/census.json >/dev/null || { echo "FAIL: contracts/census.json is not tracked, so diffing it proves nothing"; exit 1; } - @. scripts/pv_bin.sh && "$$PV" census contracts --format json > contracts/census.json || exit - @git diff --exit-code contracts/census.json || { echo "FAIL: the tracked census differs from a fresh one — commit the regenerated contracts/census.json"; exit 1; } - @echo "== graph: tracked contracts/contracts.nt + shapes.ttl == a fresh extraction (ONT-001 ONT-4b, R-18) ==" - @. scripts/pv_bin.sh && "$$PV" extract contracts --check >/dev/null || exit - @echo "== README states the censused count ==" - @bash scripts/readme_sync.sh --check - @echo "== provenance marks, interim (ONT-001 R-10) ==" - @bash scripts/lint-provenance.sh --self-test - @bash scripts/lint-provenance.sh contracts/external-corpora.yaml +# #4475: the steps live in scripts/contracts_gate.sh. The recipe used to hold them as lines, and under +# `.ONESHELL` + `.SHELLFLAGS := -o pipefail -c` (no -e) the whole recipe is ONE bash script: the lint line's +# unconditional `exit $$rc` ended it on a GREEN lint, so the census diff, `extract --check`, the README sync +# and provenance never ran — and had they run, a failing middle line would not have failed the recipe. The +# gate runs EVERY step, prints `N of 8 step(s) RAN, M FAILED` (ONT-10 added the pv-sat consistency step), fails closed on a shapes verdict it cannot +# measure, and regenerates census.json / contracts.nt / shapes.ttl then asks `git diff --exit-code`. + @bash scripts/contracts_gate.sh || exit 1 @echo "== contract engine tests ==" - @cargo test -p aprender-contracts --lib 2>&1 | grep -E "test result" | tail -1 + @# The old `| grep | tail -1` printed the verdict and + @# discarded it (the exit status was tail's), so a failing engine test passed the gate. + @t=$$(mktemp) && ( cargo test -p aprender-contracts --lib > "$$t" 2>&1; rc=$$?; grep -E "test result" "$$t" | tail -1; [ $$rc -eq 0 ] || tail -30 "$$t"; rm -f "$${t:?}"; exit $$rc ) || exit # #3839: skips are EXACT full test paths from scripts/coverage-skips.txt, one reason # per entry. They used to be 19 --skip substrings that removed 2,713 tests (2,702 of @@ -682,7 +684,7 @@ coverage: ## Coverage summary + threshold check (warm: ~3min) @$(COV_CARGO_ENV) cargo llvm-cov test --no-report -p aprender-serve --lib -- --list \ > target/coverage/serve-list.txt 2>> target/coverage/test.log || \ { echo "❌ coverage DID NOT MEASURE: could not list aprender-serve's lib tests. No coverage verdict."; exit 1; } - @python3 scripts/coverage_serve_shards.py target/coverage/serve-list.txt scripts/coverage-skips.txt \ + @bash scripts/coverage_serve_shards.sh target/coverage/serve-list.txt scripts/coverage-skips.txt \ target/coverage/serve-shards scripts/coverage-solo.txt @# scripts/coverage-solo.txt: run FIRST, each in its OWN process, and print its test binary's peak RSS @# (RUSAGE_CHILDREN.ru_maxrss), so a later skip carries a measured per-test reason. @@ -860,6 +862,11 @@ dev: tier1 # Pre-push checks pre-push: tier3 +# Run CI's guard steps locally, every step, with the SAME script CI runs (#4415, #4416) +guards-local: ## Run every guard-cargo/guard-tree step CI runs, all of them, streaming + @bash scripts/ci_guards.sh --check-coverage + @bash scripts/ci_guards.sh + # CI/CD checks ci: tier4 @@ -1257,7 +1264,7 @@ test-audio-full: ## Run all audio tests including ALSA (if available) # run ...` handed scripts/pv_bin.sh the string "cargo run ..." whenever a caller # exported PV_BIN=/path/to/pv -- the one override pv_bin.sh honours -- and every # `. scripts/pv_bin.sh` step refused with `not executable: cargo run ...`. -PV_CARGO_RUN := cargo run --release -p aprender-contracts-cli --bin pv -- +PV_CARGO_RUN := cargo run --release -p aprender-contracts-cli --bin pv --features update-check,build-sha -- BINDING := contracts/aprender/binding.yaml CONTRACTS := contracts/softmax-kernel-v1.yaml \ contracts/rmsnorm-kernel-v1.yaml \ @@ -1488,9 +1495,11 @@ check-siblings: ## Verify sibling repos exist and versions are compatible echo " Remove [patch.crates-io] from .cargo/config.toml"; \ fi -# APR-RELEASE-001 §11.2 (ONT R-6): the five ontology counters move ONLY through -# this target. `--check` is what guard_tree.sh runs on every PR; `--write` is the -# deliberate restamp, and it is the only way a counter is allowed to change. +# APR-RELEASE-001 §11.2 (ONT R-6). Since #3569 the ontology counters are MEASURED, +# never committed: `--check` (guard_tree.sh, every PR) measures them at the +# comparand tree and at the working tree and refuses any move the wrong way. +# `--write` stores DECISIONS only (armed_gates, armed_shapes + the Rust gates' +# foreign keys); it can no longer restamp a counter to hide a regression. .PHONY: ont-ratchet ont-ratchet-check ont-ratchet: @bash scripts/check_ont_ratchet.sh --write @@ -1498,6 +1507,16 @@ ont-ratchet: ont-ratchet-check: @bash scripts/check_ont_ratchet.sh --check +# PVL-001 EV-11 (PMAT-4166): the two `pv lint` ratchets (theorem-pairing, depends-on-present) move ONLY +# through this target, and only DOWN. The gates read contracts/lint-baseline.json and never write it. +# NEVER in CI: a CI job that could rewrite the baseline is a ratchet that turns both ways. +.PHONY: lint-ratchet lint-ratchet-self-test +lint-ratchet: + @bash scripts/lint_ratchet.sh + +lint-ratchet-self-test: + @bash scripts/lint_ratchet.sh --self-test + # ONT-001 §5 ONT-4b2 / R-13 — the out-of-gate SHACL differential oracle. # # NOT a PR check, by the rule that puts it here: `shacl` is 316 crates and pinned at ONE version (ONT-0's @@ -1517,3 +1536,22 @@ oracle: oracle-check: oracle @git diff --exit-code tests/oracle/differential.json \ || { echo "FAIL: tests/oracle/differential.json differs from a fresh run — commit it"; exit 1; } + +# ONT-001 §3.8 / ONT-2c — the OWL oracle (release gate only, R-13; never per PR). Three arms: +# horned-owl re-parses the fixture's written .ofn and must equal the HAND-WRITTEN axiom list; every live +# axiom must be a told-closure-admitted kind; ELK 0.4.3 (pinned by sha256, needs a JVM) must agree with +# contracts/tbox-report.json, with a planted positive control turning it RED every run. No JVM exits 2 with +# `decline: NOT MEASURED`, which is RED at the release gate and never a skip. The crate is detached from the +# workspace AND from tests/oracle's SHACL crate (feature unification breaks horned-owl there). +oracle-owl: + @echo "== OWL oracle: horned-owl round-trip + admitted kinds + ELK TBox differential (out of gate) ==" + @. scripts/pv_bin.sh && "$$PV" ontology export --owl tests/fixtures/ont/owl/ontology.yaml > "$${TMPDIR:-/tmp}/ont2c-fixture.ofn" + @cargo build --release --quiet --manifest-path tests/oracle/owl/Cargo.toml + @O="$$(cargo metadata --no-deps --format-version 1 --manifest-path tests/oracle/owl/Cargo.toml | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p')/release/owl-oracle"; \ + "$$O" roundtrip "$${TMPDIR:-/tmp}/ont2c-fixture.ofn" tests/fixtures/ont/owl/axioms.txt && \ + "$$O" kinds contracts/ontology.ofn && \ + "$$O" elk . + +oracle-owl-check: oracle-owl + @git diff --exit-code tests/oracle/tbox-differential.json \ + || { echo "FAIL: tests/oracle/tbox-differential.json differs from a fresh run — commit it"; exit 1; } diff --git a/README.md b/README.md index 7a7920919d..1ddb3c191d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +--- +schema_version: "1.0" +kind: library +entrypoints: [crates/aprender-contracts-cli] +contract_count: 1889 +--- +

aprender — Next-generation ML framework in pure Rust

@@ -22,7 +29,7 @@ Pre-built and ready to use (Linux x86_64/aarch64 only today — other platforms fall through to `cargo install` below): -```bash +```text curl -LsSf https://paiml.com/apr/install.sh | sh ``` @@ -30,7 +37,7 @@ To read the script before it runs, download it with `curl -LsSf https://paiml.co Compile and build: -```bash +```text cargo install aprender # CPU ONLY - no GPU backend is compiled in cargo install aprender --features cuda # NVIDIA GPU acceleration cargo install aprender --features full # everything (training, visualization, zram) @@ -52,10 +59,10 @@ publishing — all backed by YAML provable contracts that fail CI on drift. | Metric | Count | Source of truth | |-------:|------:|---| | Workspace crates | **81** workspace crates | `cargo metadata --no-deps` (NOT `ls crates/` — 4 are `exclude`d, 1 has no Cargo.toml) | -| Provable contracts | **1837** provable contracts | `contracts/census.json` `.n_files` — the set `pv lint` walks (`pv census`, ONT-001 ONT-1; regenerated by `make contracts`, written by `make readme-sync`, guarded by `scripts/check_readme_claims.sh`) | -| CLI commands | **111** CLI commands | `contracts/apr-cli-commands-v1.yaml` §`commands` (parse the list; `apr --help` prints 112 because it lists `help` itself, and `grep -c '^ - name:'` gives 117 — other same-indent `name:` keys exist in the file) | -| Book CLI chapters | **113** chapters | `ls book/src/cli/*.md` | -| Book lib chapters | **72** chapters | `ls book/src/lib/*.md` (parity with `pub mod`) | +| Provable contracts | **1889** provable contracts | `contracts/census.json` `.n_files` — the set `pv lint` walks (`pv census`, ONT-001 ONT-1; regenerated by `make contracts`, written by `make readme-sync`, guarded by `scripts/check_readme_claims.sh`) | +| CLI commands | **113** CLI commands | `contracts/apr-cli-commands-v1.yaml` §`commands` (parse the list; `apr --help` also lists `help` itself, and `grep -c '^ - name:'` gives 119 — other same-indent `name:` keys exist in the file) | +| Book CLI chapters | **114** chapters | `ls book/src/cli/*.md` | +| Book lib chapters | **73** chapters | `ls book/src/lib/*.md` (parity with `pub mod`) | These numbers are enforced by [`contracts/readme-claims-v1.yaml`](contracts/readme-claims-v1.yaml). Drift between this table and live repo state fails `bash scripts/check_readme_claims.sh` @@ -108,6 +115,15 @@ A non-`pass` cell names the gates the receipt records, which is not always the c +> **Known issue — Qwen3 MoE (30B-A3B) on GB10: not claimed for 0.70; planned for 0.70.1 (#3715).** Every verb +> times out at 600 s on the 20k-token rung on GB10 (sm_121), so qwen3moe on GB10 is withdrawn from this +> release's claims (`cells.declaimed`). + +> **Known issue — GB10 dense models: prompt processing unbatched; planned for 0.70.1 (#4590).** Output is correct; only prefill speed is affected. On GB10 (sm_121) every dense +> model (Qwen2.5, Qwen3) is withdrawn from this release's claims (`cells.declaimed` in +> [`contracts/model-capability-ladder-v1.yaml`](contracts/model-capability-ladder-v1.yaml)); Qwen3.5 on +> GB10 and every model on other hosts are still claimed and gated. + ### Command surface | Stage | Commands | @@ -130,7 +146,7 @@ with local `book/src/` walkthroughs. The count is the [`contracts/readme-claims-v1.yaml`](contracts/readme-claims-v1.yaml) — it lives in another repository, so it carries the date it was measured rather than pretending to be live. -```bash +```text git clone https://github.com/paiml/apr-cookbook cd apr-cookbook cargo run --example bundle_static_model # any example @@ -139,7 +155,7 @@ mdbook serve book # walkthrough docs ## Install -```bash +```text cargo install aprender # installs the `apr` binary apr --version ``` @@ -154,21 +170,21 @@ form is [`scripts/qwen-story.sh`](scripts/qwen-story.sh); nightly cron is [`.github/workflows/qwen-story-daily.yml`](.github/workflows/qwen-story-daily.yml); the dogfood gate is `/apr-dogfood` Gate 18. -```bash +```text # Reproduce locally (uses ~/models cache; ~3-5 min on RTX 4090): bash scripts/qwen-story.sh ``` ### Beat 1 — Discover (Registry) -```bash +```text apr pull hf://Qwen/Qwen2.5-Coder-0.5B-Instruct # 494 MB safetensors apr list # confirm cached ``` ### Beat 2 — Trust (QA gates) -```bash +```text apr qa qwen2.5-coder-1.5b-instruct-q4k # 12 falsifiable gates apr validate qwen2.5-coder-1.5b-instruct-q4k --quality # scores the checks that ran apr lint qwen2.5-coder-1.5b-instruct-q4k # best-practice signals @@ -176,7 +192,7 @@ apr lint qwen2.5-coder-1.5b-instruct-q4k # best-practice signals ### Beat 3 — Explore (Inspection) -```bash +```text apr inspect --json qwen2.5-coder-1.5b-instruct-q4k # arch, params, tensors apr tensors --json qwen2.5-coder-1.5b-instruct-q4k # 339 tensors with shapes apr tree qwen2.5-coder-1.5b-instruct-q4k # layer architecture @@ -184,7 +200,7 @@ apr tree qwen2.5-coder-1.5b-instruct-q4k # layer architecture ### Beat 4 — Adapt (Model ops) -```bash +```text apr export qwen2.5-coder-1.5b-instruct-q4k --format gguf -o roundtrip.gguf apr diff qwen2.5-coder-1.5b-instruct-q4k roundtrip.gguf # tensor-by-tensor delta apr convert model.safetensors --quantize q4_k -o quantized.apr @@ -192,7 +208,7 @@ apr convert model.safetensors --quantize q4_k -o quantized.apr ### Beat 5 — Use (Inference) -```bash +```text apr run qwen2.5-coder-1.5b-instruct-q4k "fn sum(a: i32, b: i32) -> i32 {" --max-tokens 16 apr chat qwen2.5-coder-1.5b-instruct-q4k # interactive REPL apr code -p "review this Python function" --max-turns 1 # agent mode (PMAT-182) @@ -200,7 +216,7 @@ apr code -p "review this Python function" --max-turns 1 # agent mode (PMAT-182 ### Beat 6 — Serve (REST API) -```bash +```text apr serve run qwen2.5-coder-1.5b-instruct-q4k --port 8080 curl -s localhost:8080/v1/chat/completions \ -H 'Content-Type: application/json' \ @@ -210,7 +226,7 @@ curl -s localhost:8080/v1/chat/completions \ ### Beat 7 — Operate (Profiling) -```bash +```text apr profile qwen2.5-coder-7b-instruct-q4_k_m # Roofline analysis apr gpu --json # VRAM, sm_*, cuda version apr serve plan qwen2.5-coder-7b-instruct-q4_k_m # capacity plan before run @@ -218,7 +234,7 @@ apr serve plan qwen2.5-coder-7b-instruct-q4_k_m # capacity plan before run ### Beat 8 — Scale (MoE introspection) -```bash +```text apr inspect --json Qwen3-Coder-30B-A3B-Instruct # arch=qwen3moe, 30 B params apr tensors --json Qwen3-Coder-30B-A3B-Instruct # 579 tensors (MoE expert layout) ``` @@ -234,7 +250,7 @@ apr tensors --json Qwen3-Coder-30B-A3B-Instruct # 579 tensors (MoE expert la ### Publish (separate flow) -```bash +```text # Publish a derived model to HuggingFace Hub (see SPEC-HF-PUBLISH-001 for the 12-file pipeline) apr stamp ckpt.apr --tokenizer /path/to/qwen-tokenizer --license Apache-2.0 -o staging/model.apr apr export staging/model.apr --format gguf --quantize int4 -o staging/model-q4k.gguf @@ -332,7 +348,7 @@ falsification_tests: prediction: apr validate bad-model.apr exits non-zero ``` -The tree carries 1837 contracts across inference, training, quantization, attention, FFN, +The tree carries 1889 contracts across inference, training, quantization, attention, FFN, tokenization, model formats, CLI safety — and this README itself. ## Migration from old crates @@ -348,13 +364,18 @@ Old repositories are archived. All development happens here. ## Contributing -```bash +```text git clone https://github.com/paiml/aprender cd aprender cargo test --workspace --lib cargo check --workspace apr --help -bash scripts/check_readme_claims.sh # README contract gate +``` + +The README's own gate — a merge-path CI step runs exactly this line, which is what lets this fence stay `bash`: + +```bash +setsid --wait bash scripts/check_readme_claims.sh # README contract gate (as CI runs it) ``` ## License diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 539233f765..5fd811dc90 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -353,6 +353,7 @@ - [apr prune](./cli/prune.md) - [apr ptx-map](./cli/ptx-map.md) - [apr ptx](./cli/ptx.md) +- [apr ptx-debug](./cli/ptx-debug.md) - [apr publish](./cli/publish.md) - [apr pull](./cli/pull.md) - [apr pv](./cli/pv.md) diff --git a/book/src/cli/ptx-debug.md b/book/src/cli/ptx-debug.md new file mode 100644 index 0000000000..e3fbf09261 --- /dev/null +++ b/book/src/cli/ptx-debug.md @@ -0,0 +1,34 @@ +# apr ptx-debug + +PTX falsification analysis and FKR test generation (was the `aprender-ptx-debug` binary) + +**Category**: Hardware + +Pure Rust: unlike `apr ptx`, it needs no build feature. + +## Synopsis + +```text +apr ptx-debug analyze [--falsify] [--min-score N] [--html FILE] [--json] +apr ptx-debug gen-fkr [-o FILE] +apr ptx-debug version +``` + +- `analyze` scores a PTX file against the falsification framework and fails when the score is below `--min-score` (default 70). +- `gen-fkr` generates FKR tests for jugar-probar from a PTX file (stdout unless `-o`). + +## Example + + +```bash +apr ptx-debug version +``` + +## Full help + +Run `apr ptx-debug --help` for the complete option list. + +## See also + +- Source: [`crates/aprender-ptx-debug/src/cli.rs`](https://github.com/paiml/aprender/blob/main/crates/aprender-ptx-debug/src/cli.rs) +- [apr ptx](./ptx.md) diff --git a/ci/explicit-test-commands.d/447-apr-cli-surface-binary-pin.cmd b/ci/explicit-test-commands.d/447-apr-cli-surface-binary-pin.cmd new file mode 100644 index 0000000000..08a5c395a5 --- /dev/null +++ b/ci/explicit-test-commands.d/447-apr-cli-surface-binary-pin.cmd @@ -0,0 +1 @@ +cargo test -p apr-cli --test surface_binary_pin diff --git a/ci/explicit-test-commands.d/456-aprender-contracts-cli-ev11-lint-ratchets.cmd b/ci/explicit-test-commands.d/456-aprender-contracts-cli-ev11-lint-ratchets.cmd new file mode 100644 index 0000000000..7f427a94da --- /dev/null +++ b/ci/explicit-test-commands.d/456-aprender-contracts-cli-ev11-lint-ratchets.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ev11_lint_ratchets diff --git a/ci/explicit-test-commands.d/457-aprender-contracts-cli-shapes-vacuity.cmd b/ci/explicit-test-commands.d/457-aprender-contracts-cli-shapes-vacuity.cmd new file mode 100644 index 0000000000..75beb0e63c --- /dev/null +++ b/ci/explicit-test-commands.d/457-aprender-contracts-cli-shapes-vacuity.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test shapes_vacuity diff --git a/ci/explicit-test-commands.d/457-apr-format-golden-fixtures.cmd b/ci/explicit-test-commands.d/459-apr-format-golden-fixtures.cmd similarity index 100% rename from ci/explicit-test-commands.d/457-apr-format-golden-fixtures.cmd rename to ci/explicit-test-commands.d/459-apr-format-golden-fixtures.cmd diff --git a/ci/explicit-test-commands.d/461-aprender-contracts-cli-pvl-discharge-check.cmd b/ci/explicit-test-commands.d/461-aprender-contracts-cli-pvl-discharge-check.cmd new file mode 100644 index 0000000000..f10fbfea13 --- /dev/null +++ b/ci/explicit-test-commands.d/461-aprender-contracts-cli-pvl-discharge-check.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test pvl_discharge_check diff --git a/ci/explicit-test-commands.d/462-aprender-contracts-cli-pvl-obligations-golden.cmd b/ci/explicit-test-commands.d/462-aprender-contracts-cli-pvl-obligations-golden.cmd new file mode 100644 index 0000000000..7f4042f19d --- /dev/null +++ b/ci/explicit-test-commands.d/462-aprender-contracts-cli-pvl-obligations-golden.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test pvl_obligations_golden diff --git a/ci/explicit-test-commands.d/463-aprender-contracts-cli-ont2c-owl-tbox.cmd b/ci/explicit-test-commands.d/463-aprender-contracts-cli-ont2c-owl-tbox.cmd new file mode 100644 index 0000000000..16f5bbe212 --- /dev/null +++ b/ci/explicit-test-commands.d/463-aprender-contracts-cli-ont2c-owl-tbox.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont2c_owl_tbox diff --git a/ci/explicit-test-commands.d/471-aprender-contracts-cli-pvl-discharge-leanchecker.cmd b/ci/explicit-test-commands.d/471-aprender-contracts-cli-pvl-discharge-leanchecker.cmd new file mode 100644 index 0000000000..663ef8f32a --- /dev/null +++ b/ci/explicit-test-commands.d/471-aprender-contracts-cli-pvl-discharge-leanchecker.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test pvl_discharge_leanchecker diff --git a/ci/explicit-test-commands.d/472-aprender-contracts-cli-ont4d-subsumption.cmd b/ci/explicit-test-commands.d/472-aprender-contracts-cli-ont4d-subsumption.cmd new file mode 100644 index 0000000000..84cc173d0a --- /dev/null +++ b/ci/explicit-test-commands.d/472-aprender-contracts-cli-ont4d-subsumption.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont4d_subsumption diff --git a/ci/explicit-test-commands.d/480-aprender-contracts-cli-ont4c5-capability-cells.cmd b/ci/explicit-test-commands.d/480-aprender-contracts-cli-ont4c5-capability-cells.cmd new file mode 100644 index 0000000000..633f6310ab --- /dev/null +++ b/ci/explicit-test-commands.d/480-aprender-contracts-cli-ont4c5-capability-cells.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont4c5_capability_cells diff --git a/ci/explicit-test-commands.d/482-aprender-contracts-cli-pvl-challenge.cmd b/ci/explicit-test-commands.d/482-aprender-contracts-cli-pvl-challenge.cmd new file mode 100644 index 0000000000..1f5f60d80e --- /dev/null +++ b/ci/explicit-test-commands.d/482-aprender-contracts-cli-pvl-challenge.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test pvl_challenge diff --git a/ci/explicit-test-commands.d/480-aprender-facade-e2e.cmd b/ci/explicit-test-commands.d/483-aprender-facade-e2e.cmd similarity index 100% rename from ci/explicit-test-commands.d/480-aprender-facade-e2e.cmd rename to ci/explicit-test-commands.d/483-aprender-facade-e2e.cmd diff --git a/ci/explicit-test-commands.d/491-aprender-contracts-cli-pvl-ghost-binding.cmd b/ci/explicit-test-commands.d/491-aprender-contracts-cli-pvl-ghost-binding.cmd new file mode 100644 index 0000000000..ddafe0fb95 --- /dev/null +++ b/ci/explicit-test-commands.d/491-aprender-contracts-cli-pvl-ghost-binding.cmd @@ -0,0 +1,3 @@ +# PVL-001 EV-2 (#4080): pv proof-status --binding resolves bindings; a ghost binding is a reject. +# Reads tests/fixtures/pvl/ and contracts/ from the workspace root (the resolver is CWD-sensitive). +cargo test -p aprender-contracts-cli --test pvl_ghost_binding diff --git a/ci/explicit-test-commands.d/510-aprender-contracts-cli-ont5-consistency-gate.cmd b/ci/explicit-test-commands.d/510-aprender-contracts-cli-ont5-consistency-gate.cmd new file mode 100644 index 0000000000..4245d9c682 --- /dev/null +++ b/ci/explicit-test-commands.d/510-aprender-contracts-cli-ont5-consistency-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont5_consistency_gate diff --git a/ci/explicit-test-commands.d/520-aprender-contracts-cli-pv-sat-bin.cmd b/ci/explicit-test-commands.d/520-aprender-contracts-cli-pv-sat-bin.cmd new file mode 100644 index 0000000000..c489d7f13b --- /dev/null +++ b/ci/explicit-test-commands.d/520-aprender-contracts-cli-pv-sat-bin.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --bin pv-sat diff --git a/ci/explicit-test-commands.d/530-aprender-contracts-cli-ont4c-doc-contracts.cmd b/ci/explicit-test-commands.d/530-aprender-contracts-cli-ont4c-doc-contracts.cmd new file mode 100644 index 0000000000..360976bafb --- /dev/null +++ b/ci/explicit-test-commands.d/530-aprender-contracts-cli-ont4c-doc-contracts.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont4c_doc_contracts diff --git a/ci/explicit-test-commands.d/575-aprender-contracts-cli-ont9-self-contract.cmd b/ci/explicit-test-commands.d/575-aprender-contracts-cli-ont9-self-contract.cmd new file mode 100644 index 0000000000..de771831df --- /dev/null +++ b/ci/explicit-test-commands.d/575-aprender-contracts-cli-ont9-self-contract.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont9_self_contract diff --git a/ci/explicit-test-commands.d/580-aprender-contracts-cli-ont4f-github-entities.cmd b/ci/explicit-test-commands.d/580-aprender-contracts-cli-ont4f-github-entities.cmd new file mode 100644 index 0000000000..4b2e98664b --- /dev/null +++ b/ci/explicit-test-commands.d/580-aprender-contracts-cli-ont4f-github-entities.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont4f_github_entities diff --git a/ci/explicit-test-commands.d/600-aprender-contracts-cli-ont4e-refines-gate.cmd b/ci/explicit-test-commands.d/600-aprender-contracts-cli-ont4e-refines-gate.cmd new file mode 100644 index 0000000000..1d168b3861 --- /dev/null +++ b/ci/explicit-test-commands.d/600-aprender-contracts-cli-ont4e-refines-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont4e_refines_gate diff --git a/ci/explicit-test-commands.d/610-aprender-cgp-cli-surface-gate.cmd b/ci/explicit-test-commands.d/610-aprender-cgp-cli-surface-gate.cmd new file mode 100644 index 0000000000..9ce71e6cf6 --- /dev/null +++ b/ci/explicit-test-commands.d/610-aprender-cgp-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-cgp --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/620-aprender-verify-ml-cli-surface-gate.cmd b/ci/explicit-test-commands.d/620-aprender-verify-ml-cli-surface-gate.cmd new file mode 100644 index 0000000000..664cb6571c --- /dev/null +++ b/ci/explicit-test-commands.d/620-aprender-verify-ml-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-verify-ml --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/630-aprender-zram-cli-cli-surface-gate.cmd b/ci/explicit-test-commands.d/630-aprender-zram-cli-cli-surface-gate.cmd new file mode 100644 index 0000000000..00e868ad66 --- /dev/null +++ b/ci/explicit-test-commands.d/630-aprender-zram-cli-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-zram-cli --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/640-aprender-train-inspect-cli-surface-gate.cmd b/ci/explicit-test-commands.d/640-aprender-train-inspect-cli-surface-gate.cmd new file mode 100644 index 0000000000..ed012b9e2e --- /dev/null +++ b/ci/explicit-test-commands.d/640-aprender-train-inspect-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-inspect --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/650-aprender-train-distill-cli-surface-gate.cmd b/ci/explicit-test-commands.d/650-aprender-train-distill-cli-surface-gate.cmd new file mode 100644 index 0000000000..48037f83ab --- /dev/null +++ b/ci/explicit-test-commands.d/650-aprender-train-distill-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-distill --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/660-aprender-train-bench-cli-surface-gate.cmd b/ci/explicit-test-commands.d/660-aprender-train-bench-cli-surface-gate.cmd new file mode 100644 index 0000000000..01f5d63a90 --- /dev/null +++ b/ci/explicit-test-commands.d/660-aprender-train-bench-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-bench --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/670-aprender-data-cli-surface-gate.cmd b/ci/explicit-test-commands.d/670-aprender-data-cli-surface-gate.cmd new file mode 100644 index 0000000000..3f25ed627b --- /dev/null +++ b/ci/explicit-test-commands.d/670-aprender-data-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-data --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/671-aprender-data-cli-output.cmd b/ci/explicit-test-commands.d/671-aprender-data-cli-output.cmd new file mode 100644 index 0000000000..5767f255ba --- /dev/null +++ b/ci/explicit-test-commands.d/671-aprender-data-cli-output.cmd @@ -0,0 +1 @@ +cargo test -p aprender-data --test cli_output diff --git a/ci/explicit-test-commands.d/680-aprender-qa-cli-cli-surface-gate.cmd b/ci/explicit-test-commands.d/680-aprender-qa-cli-cli-surface-gate.cmd new file mode 100644 index 0000000000..b4d0e64891 --- /dev/null +++ b/ci/explicit-test-commands.d/680-aprender-qa-cli-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-qa-cli --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/690-aprender-train-lora-cli-surface-gate.cmd b/ci/explicit-test-commands.d/690-aprender-train-lora-cli-surface-gate.cmd new file mode 100644 index 0000000000..c1d6daf06d --- /dev/null +++ b/ci/explicit-test-commands.d/690-aprender-train-lora-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-lora --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/700-aprender-db-cli-surface-gate.cmd b/ci/explicit-test-commands.d/700-aprender-db-cli-surface-gate.cmd new file mode 100644 index 0000000000..c3c802c0f4 --- /dev/null +++ b/ci/explicit-test-commands.d/700-aprender-db-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-db --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/710-aprender-explain-cli-surface-gate.cmd b/ci/explicit-test-commands.d/710-aprender-explain-cli-surface-gate.cmd new file mode 100644 index 0000000000..6ca67da3d0 --- /dev/null +++ b/ci/explicit-test-commands.d/710-aprender-explain-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-explain --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/720-aprender-present-cli-cli-surface-gate.cmd b/ci/explicit-test-commands.d/720-aprender-present-cli-cli-surface-gate.cmd new file mode 100644 index 0000000000..9a967174c6 --- /dev/null +++ b/ci/explicit-test-commands.d/720-aprender-present-cli-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-present-cli --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/730-aprender-present-terminal-cli-surface-gate.cmd b/ci/explicit-test-commands.d/730-aprender-present-terminal-cli-surface-gate.cmd new file mode 100644 index 0000000000..813f6684a7 --- /dev/null +++ b/ci/explicit-test-commands.d/730-aprender-present-terminal-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-present-terminal --features ptop --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/740-aprender-profile-cli-surface-gate.cmd b/ci/explicit-test-commands.d/740-aprender-profile-cli-surface-gate.cmd new file mode 100644 index 0000000000..8f9e9d07f0 --- /dev/null +++ b/ci/explicit-test-commands.d/740-aprender-profile-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-profile --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/750-aprender-ptx-debug-cli-surface-gate.cmd b/ci/explicit-test-commands.d/750-aprender-ptx-debug-cli-surface-gate.cmd new file mode 100644 index 0000000000..f6c8146786 --- /dev/null +++ b/ci/explicit-test-commands.d/750-aprender-ptx-debug-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-ptx-debug --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/760-aprender-simulate-cli-surface-gate.cmd b/ci/explicit-test-commands.d/760-aprender-simulate-cli-surface-gate.cmd new file mode 100644 index 0000000000..2d9b95a8ad --- /dev/null +++ b/ci/explicit-test-commands.d/760-aprender-simulate-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-simulate --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/761-aprender-contracts-cli-ont8-evidence-gate.cmd b/ci/explicit-test-commands.d/761-aprender-contracts-cli-ont8-evidence-gate.cmd new file mode 100644 index 0000000000..61c839eb5b --- /dev/null +++ b/ci/explicit-test-commands.d/761-aprender-contracts-cli-ont8-evidence-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test ont8_evidence_gate diff --git a/ci/explicit-test-commands.d/762-aprender-contracts-cli-pvl-comparator.cmd b/ci/explicit-test-commands.d/762-aprender-contracts-cli-pvl-comparator.cmd new file mode 100644 index 0000000000..02c3c9635d --- /dev/null +++ b/ci/explicit-test-commands.d/762-aprender-contracts-cli-pvl-comparator.cmd @@ -0,0 +1 @@ +cargo test -p aprender-contracts-cli --test pvl_comparator diff --git a/ci/explicit-test-commands.d/770-aprender-train-shell-cli-surface-gate.cmd b/ci/explicit-test-commands.d/770-aprender-train-shell-cli-surface-gate.cmd new file mode 100644 index 0000000000..3181d9cfbf --- /dev/null +++ b/ci/explicit-test-commands.d/770-aprender-train-shell-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-shell --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/780-aprender-zram-generator-cli-surface-gate.cmd b/ci/explicit-test-commands.d/780-aprender-zram-generator-cli-surface-gate.cmd new file mode 100644 index 0000000000..7b4daf622c --- /dev/null +++ b/ci/explicit-test-commands.d/780-aprender-zram-generator-cli-surface-gate.cmd @@ -0,0 +1 @@ +cargo test -p aprender-zram-generator --test cli_surface_gate diff --git a/ci/explicit-test-commands.d/790-aprender-train-shell-cli-version-name.cmd b/ci/explicit-test-commands.d/790-aprender-train-shell-cli-version-name.cmd new file mode 100644 index 0000000000..021db4a9d9 --- /dev/null +++ b/ci/explicit-test-commands.d/790-aprender-train-shell-cli-version-name.cmd @@ -0,0 +1 @@ +cargo test -p aprender-train-shell --test cli_version_name diff --git a/ci/mutants-feature-groups.txt b/ci/mutants-feature-groups.txt new file mode 100644 index 0000000000..8d42464354 --- /dev/null +++ b/ci/mutants-feature-groups.txt @@ -0,0 +1,30 @@ +# ci/mutants-feature-groups.txt -- files whose mutants only compile under a NON-default feature (#4587 A', M1 FALSE-RED). +# +# cargo-mutants tests with default features, so a module behind `#[cfg(feature = "x")]` never compiles and every +# mutant in it reads MISSED, whatever its tests say. `--features pkg/feat` cannot be passed workspace-wide (cargo +# refuses it for a selected package that does not own the feature), so mutants_table_shard.sh runs each row below as +# its own `cargo mutants -p PACKAGE --features FEATURES --file FILE`, excludes FILE from the default run, and merges +# the outcomes. The --list universe is untouched: every listed mutant must still be tested exactly once, so a row +# whose run tests nothing is a MISSING gap (RED), never a skip. cuda is never a row: no runner has a GPU. +# +# package|features|file|test args (what goes after `--`) +aprender-common|cli-roles|crates/aprender-common/src/cli_roles.rs|--lib +# main.rs is the pv BINARY: no --lib test can observe it, and on intel (-j 2) the --lib suite alone ran past the +# 900 s mutant budget, so `main with ()` read TIMEOUT (= survived). The binary:: tests exec pv and assert on it: +# both main.rs mutants measured caught with these args (intel, 2026-09-29; local -- the CI matrix is the evidence). +aprender-contracts-cli|update-check,build-sha|crates/aprender-contracts-cli/src/main.rs|--test cli_integration binary:: +# +# Rows with NO features (#4588/#4587, C97/C127): the file compiles by default, but its tests live OUTSIDE --lib -- the +# pv subcommands are tested by exec'ing pv from tests/*.rs. Under the default `-- --lib` every such mutant reads +# MISSED whatever those tests say. Each row runs the targets that exercise the file (lint.rs is driven by 24 of them, +# so it takes all of --tests). +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/obligations.rs|--lib --test pvl_obligations_golden --test ont4e_refines_gate --test ont_release_readiness +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/ontology.rs|--lib --test ont2c_owl_tbox +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/lint.rs|--tests +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/discharge.rs|--lib --test pvl_discharge_check --test pvl_discharge_leanchecker --test pvl_comparator --test pvl_challenge +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/extract_rdf.rs|--lib --test ont_release_readiness --test ont_extract_json --test ont4b_shapes_gate +aprender-contracts-cli||crates/aprender-contracts-cli/src/commands/proof_status.rs|--lib --test pvl_ghost_binding --test pvl_zero_contracts +aprender-contracts-macros||crates/aprender-contracts-macros/src/lib.rs|--lib --test contract_macros +aprender-data||crates/aprender-data/src/cli/quality.rs|--lib --test cli_output +# hub.rs is behind `#[cfg(feature = "hf-hub")]` AND its observable effect is the binary's stderr. +aprender-data|hf-hub|crates/aprender-data/src/cli/hub.rs|--lib --test cli_output diff --git a/ci/mutants-table-refs.txt b/ci/mutants-table-refs.txt new file mode 100644 index 0000000000..1836866696 --- /dev/null +++ b/ci/mutants-table-refs.txt @@ -0,0 +1,5 @@ +# Head refs whose PR is judged by the CI survivor table (mutants-shard matrix + mutants-table), not by the +# one-job `mutants` section (#4587 A', operator 2026-09-29 06:36Z). Scope: PR-1 and PR-2 of 0.70 ONLY. +# After 0.70 this file is emptied and the 60-mutant cap is checked at PR OPEN (L26). +# Option (a): emptied for PR-1/PR-2 too -- their mutants are judged by the #4647 ratchet section +# (report-only + shrink-only ci/mutants-debt.tsv), because the intel shards hit the 720m timeout. diff --git a/ci/sections.yml b/ci/sections.yml index 25d2b01fc8..09d5572f38 100644 --- a/ci/sections.yml +++ b/ci/sections.yml @@ -298,6 +298,16 @@ jobs: } parent="/mnt/nvme-raid0/targets/aprender-ci/${PR_OR_REF}" reg="/mnt/nvme-raid0/cargo-ci/registry/${PR_OR_REF}" + # infra#1059: admit this target unit against gx10's disk budget BEFORE creating it + # (free >= 50G + 25G x (live+1), live < 2; waits <=20m, then exit 1). The admit + # record is what the runner's JOB_COMPLETED hook reaps, and nothing else. Hosts + # without the gate skip; gx10 exports CI_TARGET_ADMIT_REQUIRED=1, so a missing + # gate there is red, never a silent skip. + if command -v ci-target-admit >/dev/null 2>&1; then + ci-target-admit "${parent}/run-${GITHUB_RUN_ID}" || exit 1 + elif [ "${CI_TARGET_ADMIT_REQUIRED:-0}" = 1 ]; then + echo "::error::infra#1059: ci-target-admit is required on ${RUNNER_NAME:-this runner} and is missing"; exit 1 + fi heal_path /mnt/nvme-raid0/targets/aprender-ci "${parent}/run-${GITHUB_RUN_ID}" heal_path /mnt/nvme-raid0/cargo-ci/registry "$reg" ls -ld "$parent" "${parent}/run-${GITHUB_RUN_ID}" "$reg" @@ -391,6 +401,44 @@ jobs: esac bash scripts/ci_test_tier.sh "${args[@]}" | tee "$RUNNER_TEMP/tier.txt" grep -E '^(tier|crates|targets|check_workspace|reason|cite)=' "$RUNNER_TEMP/tier.txt" >> "$GITHUB_OUTPUT" + # #4502 row 2: lint_passes_on_real_contracts (aprender-contracts) requires the + # refinement gate to RUN whenever CI is set, and that gate compares HEAD with + # merge-base(HEAD, origin/main), else the origin/main tip. This section clones at + # depth 1 and only the tier step's pull_request arm fetched a main, so a + # workflow_dispatch / merge_group / push shard had no base and the test panicked + # "the refinement gate declined". Operator ruling (2026-09-28 09:41): compare HEAD + # with the EVENT's base commit -- pull_request.base.sha, merge_group.base_sha, + # push's `before` -- and fall back to the origin/main tip only for + # workflow_dispatch. That commit is pinned as refs/remotes/origin/main, so at + # depth 1 (no merge-base) the gate's tip fallback compares against exactly it. + # AFTER the tier step: its pull_request arm fetches main's CURRENT tip into + # origin/main, which would replace the base. A missing base fails the step here, + # loudly, instead of reaching the test as a skipped gate. + - name: Pin the event's base commit as origin/main (refinement gate comparand, aprender-4502) + env: + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + MG_BASE_SHA: ${{ github.event.merge_group.base_sha }} + PUSH_BEFORE: ${{ github.event.before }} + run: | + set -euo pipefail + case "${GITHUB_EVENT_NAME}" in + pull_request) base="${PR_BASE_SHA:-}" ;; + merge_group) base="${MG_BASE_SHA:-}" ;; + push) base="${PUSH_BEFORE:-}" ;; + workflow_dispatch) + git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main + printf 'refinement base: origin/main tip %s (workflow_dispatch has no event base)\n' "$(git rev-parse origin/main)" + exit 0 + ;; + *) printf '::error::refinement base: event %s has no base rule\n' "${GITHUB_EVENT_NAME}"; exit 1 ;; + esac + if ! printf '%s' "$base" | grep -Eq '^[0-9a-f]{40}$' || [ "$base" = 0000000000000000000000000000000000000000 ]; then + printf '::error::refinement base: %s event carries no base sha (got "%s")\n' "${GITHUB_EVENT_NAME}" "$base" + exit 1 + fi + git fetch --no-tags --depth=1 origin "$base" + git update-ref refs/remotes/origin/main "$base" + printf 'refinement base: %s base %s pinned as origin/main\n' "${GITHUB_EVENT_NAME}" "$(git rev-parse 'origin/main^{commit}')" - name: Workspace lib tests (25,300+) if: steps.tier.outputs.tier == 'full' # Excluded: aprender-compute (SIMD SIGSEGV at exit), aprender-gpu and aprender-cuda-edge @@ -428,8 +476,12 @@ jobs: # comes from root Cargo.toml [profile.test]; the CARGO_PROFILE_*_DEBUG # env vars are kept as belt-and-suspenders. timeout-minutes: 110 + # safe.directory: the container runs as root over a host-owned /workspace, so + # git refuses it ("dubious ownership") and the pinned origin/main above never + # resolves -- the refinement gate then skips and its CI assert fails (aprender-4502). run: | docker run --rm \ + -e GIT_CONFIG_COUNT=1 -e GIT_CONFIG_KEY_0=safe.directory -e GIT_CONFIG_VALUE_0=/workspace \ -e CI -e GITHUB_ACTIONS -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -e GITHUB_EVENT_NAME -e GITHUB_WORKFLOW \ -v "${GITHUB_WORKSPACE}:/workspace" \ -v "/mnt/nvme-raid0/cargo-ci/registry/${PR_OR_REF}:/usr/local/cargo/registry" \ @@ -955,11 +1007,17 @@ jobs: # workspace. Runner (noah:1000) can't git-clean them on next run # → checkout fails → CI breaks. This runs inside the container # (as root) restoring host ownership for subsequent bare-metal jobs. + # infra#1059: mount the target unit only if it exists. When the disk + # admit gate refused it, a bind mount here would make docker create the + # refused unit as root, unrecorded, so the reap hook would never free it. + unit="/mnt/nvme-raid0/targets/aprender-ci/${PR_OR_REF}/run-${GITHUB_RUN_ID}" + target_mount=() + if [ -d "$unit" ]; then target_mount=(-v "${unit}:/workspace/target"); fi docker run --rm \ -e CI -e GITHUB_ACTIONS -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -e GITHUB_EVENT_NAME -e GITHUB_WORKFLOW \ -v "${GITHUB_WORKSPACE}:/workspace" \ -v "/mnt/nvme-raid0/cargo-ci/registry/${PR_OR_REF}:/usr/local/cargo/registry" \ - -v "/mnt/nvme-raid0/targets/aprender-ci/${PR_OR_REF}/run-${GITHUB_RUN_ID}:/workspace/target" \ + "${target_mount[@]}" \ "$IMAGE" \ bash -c 'chown -R 1000:1000 /workspace || true; chown -R 1000:1000 /usr/local/cargo/registry || true; chown -R 1000:1000 /workspace/target || true' @@ -1073,13 +1131,146 @@ jobs: # ratchets fall back to the origin/main TIP. That is the STRICTER # comparand, and it means the CI path is the tip path: a local run that # passes on the merge-base can still red here, and CI is authoritative. - - name: Fetch origin/main as the comparand for every ratchet in this job + - id: guard-setup + name: Fetch origin/main as the comparand for every ratchet in this job run: | git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main # push shape: HEAD IS the origin/main tip, so every differential ratchet judges it against # its FIRST PARENT (scripts/lib/resolve_base.sh: HEAD vs HEAD would pass vacuously, the # G-10 quorum's finding), and a depth-1 checkout has not fetched that parent. Deepen by one. if [ "${GITHUB_EVENT_NAME:-}" = push ]; then git fetch --no-tags --deepen=1 origin +refs/heads/main:refs/remotes/origin/main; fi + # #4415 (operator A4, 2026-09-25): ONE step runs every guard step of guard-tree-steps + # below, keeps going past a red one, and reports every failure. GitHub would + # stop this job at its first red step, and the others would never be seen. + # `make guards-local` and the pre-push hook run this same script and print + # the same `ci_guards: sha256` line. + - name: Every guard step runs, and every failure is reported (scripts/ci_guards.sh, #4415) + if: ${{ !cancelled() && steps.guard-setup.outcome == 'success' }} + env: + GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} + run: bash scripts/ci_guards.sh guard-tree + # APR-RELEASE-001 §6 R-2, WIRED (#3200 shipped the guard; this is its + # caller). This step is its ONLY runner: guard_tree.sh skips it as + # wired-with-args (it needs a body), so its case table ran in NO job until + # this step called `--self-test` beside `--body` (found by aprender-f0, + # 2026-09-21; the comment here used to claim guard_tree ran it bare). The + # 0.67.0 reconcile found 48 merged PRs since the previous tag of which only + # 9 closed anything; that is what this guard exists to stop. + # + # THE BODY IS NEVER INTERPOLATED INTO THE SCRIPT. `${{ }}` is substituted + # textually BEFORE bash sees the line, so a body containing a backtick or + # $(...) would execute on the runner -- and this body is attacker-controlled + # by construction. Since #3298 it is fetched live by `gh api` straight into + # a file (it used to arrive through `env:` from the stale event payload); + # either way it is data. scripts/check_workflow_env_defined.sh enforces + # the env: side of that rule. + # + # WHY THIS STEP AND ITS TWO NEIGHBOURS ARE NOT IN guard-tree-steps (#4415): + # they read the pull_request payload (PR_NUMBER), which ci_guards.sh cannot + # supply to a local run, so they stay here. They still run all: both carry + # !cancelled(), the report is always(), and check_guard_steps_run_all.sh + # refuses any other fail-fast step in a guard section over the baseline. + # + # pull_request only: on merge_group there is no pull_request payload, and + # a body is fixed before a PR is ever enqueued. + - name: A PR body must close every issue it cites, or say why not (§6 R-2) + # `!cancelled()` and NOT a bare event check. Actions steps are fail-fast: + # measured on run 34875945193, step 8 went red and 48 of this job's 59 + # steps were SKIPPED -- including this one and its §11.1 neighbour. This + # guard exists because the 0.67.0 reconcile found 48 merged PRs of which + # only 9 closed anything; masked by an unrelated guard, it is dark exactly + # as it was before it had a caller. It reads a PR body and nothing else, + # so no guard result can be its precondition. (#3282 classifies the other + # 46, which is a judgment call per step rather than one blanket edit.) + if: "!cancelled() && github.event_name == 'pull_request'" + # #3298: the body is read LIVE from the API, not from the event payload. + # `github.event.pull_request.body` is frozen when the event is minted and + # `gh run rerun` replays that payload, so fixing the body per the guard's + # own FAIL message could never be re-graded by a rerun -- only by a push. + # Still data, never interpolated: gh writes it straight to the file. + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + setsid --wait bash scripts/check_pr_closes_issue.sh --self-test + gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \ + --jq '.body // ""' > "$RUNNER_TEMP/pr-body.txt" + setsid --wait bash scripts/check_pr_closes_issue.sh --body "$RUNNER_TEMP/pr-body.txt" + # APR-RELEASE-001 §11.1, WIRED. The section shipped as prose in #3268 and + # nothing read it: `grep -rl ont-delta scripts/ .github/ Makefile` returned + # NOTHING, while §11.1's own text says "Absent is a PR-body lint failure, + # not a review comment". A rule whose enforcement is "not a review comment" + # and whose only mechanism IS a review comment is this repo's anti-theater + # class, one level up from the guard it sits beside. + # + # Same body-handling rule as the step above: the body is fetched live by + # `gh api` into a file as DATA and is never interpolated into the script. + # It is attacker-controlled by construction. + # + # The changed-file list is the other input, and it is what makes the guard + # non-trivial: half the sweep surface ("a known-red list anywhere") is + # DERIVED from the tree, not typed. Measured on #3277, which touches + # scripts/cb200_baseline.txt and no prose sink at all -- a hand-typed sink + # list would have passed it. + - name: A sweep PR closes with an ontology delta, not a paragraph (§11.1) + # `!cancelled()` and NOT a bare event check. Actions steps are fail-fast: + # measured on run 34875945193, step 8 went red and 48 of this job's 59 + # steps were SKIPPED -- including this one and its §11.1 neighbour. This + # guard exists because the 0.67.0 reconcile found 48 merged PRs of which + # only 9 closed anything; masked by an unrelated guard, it is dark exactly + # as it was before it had a caller. It reads a PR body and nothing else, + # so no guard result can be its precondition. (#3282 classifies the other + # 46, which is a judgment call per step rather than one blanket edit.) + if: "!cancelled() && github.event_name == 'pull_request'" + # #3298: body and file list now come from the same place (the live API) + # at the same time. The body used to be the event SNAPSHOT while the file + # list was live, so a rerun after a body edit re-graded the OLD body. + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \ + --jq '.body // ""' > "$RUNNER_TEMP/ont-body.txt" + gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate \ + --jq '.[].filename' > "$RUNNER_TEMP/ont-files.txt" + setsid --wait bash scripts/check_pr_ont_delta.sh \ + --body "$RUNNER_TEMP/ont-body.txt" --changed "$RUNNER_TEMP/ont-files.txt" + # aprender#2822: THE REPORT. Runs even when the job dies mid-cargo, + # and takes its own final probe first -- without that, a failure at + # step K leaves the last recorded boundary BEFORE K and the table + # reads clean, which is the whole failure mode being investigated. + # + # It also pulls the host journal for the job's window into the job + # log. That is the decisive evidence and the only evidence that + # expires: `_work/*/target` is deleted by HOST automation (a sibling + # runner's job-started hook, a systemd timer) which logs under its own + # tag and appears in NO GitHub log. See + # scripts/runner-infra/runner-disk-guard.sh, whose own header records + # this exact signature. + # + # continue-on-error is deliberate and is NOT a disarmed gate: nothing + # here asserts anything. The gate that can fail is the case table + # wired above. + - name: "target-watch: report (aprender#2822)" + if: always() + continue-on-error: true + run: | + bash scripts/ci_target_watch.sh probe post-job + bash scripts/ci_target_watch.sh report + + guard-tree-steps: + # MANIFEST, never run by GitHub (`if: false`). These are guard-tree's guard steps, + # executed by the single `scripts/ci_guards.sh guard-tree` step of guard-tree above, and by + # `make guards-local` (#4415). They live here, verbatim, so every guard that + # reads this file for an invocation (check_guards_are_wired.sh, guard_tree.sh, + # the contracts' `test:` greps) still finds it. A step here is name/run/env + # only; scripts/check_guard_steps_run_all.sh refuses anything else. + if: false + runs-on: [self-hosted, Linux, clean-room] # arch-neutral: intel, yoga or gx10 (#3100) + steps: # aprender#2822. Case table FIRST, per the convention above: the probe # below is only worth its 0.15s if the classifier it feeds can still turn # RED. Seven rows over a throwaway tree - no cargo, no network, no host. @@ -1126,6 +1317,16 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: bash scripts/guard_tree.sh --no-cargo + # The gate's mutants skip rule, executed from ci.yml's own marker block. It + # was run by no step: guard_tree.sh files it as a cargo guard (its text + # names guard-cargo) and no workflow dispatches --cargo-only, so the mutants + # rule (#3676) passed check_guards_are_wired.sh on a comment mention alone. + # It reads only ci.yml and invokes no build tool, so it runs here -- the job + # that runs on every PR, including the one that edits the gate. + - name: gate accepts a skipped mutants job only when that is its filter (#3676) + run: setsid --wait bash scripts/check_ci_gate_mutants_rule.sh + - name: gate mutants rule can still go RED (#3676) + run: setsid --wait bash scripts/check_ci_gate_mutants_rule.sh --self-test # scripts/tests/guard_tree_test.sh is guard_tree.sh's OWN case table # (BSE-01, PMAT-1062) -- it proves guard_tree.sh's no-fail-fast # behaviour against a fixture, proves a fail-fast MUTANT of it goes RED @@ -1411,6 +1612,13 @@ jobs: run: setsid --wait bash scripts/check_pr_review_wiring.sh --self-test - name: The PR-review receipt guard is wired job-level, not path-filtered run: setsid --wait bash scripts/check_pr_review_wiring.sh + # #4441 passed FAT_SECRET_PR_REVIEW_SIGNING_KEY_B (no "64"), so pr-review-sign + # read an empty key and Arm 4 went RED on every new PR. A section's + # a secrets. read reaches it only through ci.yml's FAT_SECRET_. Case table first. + - name: "Fat-job secret pass-through case table (6 rows)" + run: setsid --wait bash scripts/check_fat_secrets_passed.sh --self-test + - name: Every secret a section reads is passed to the fat job under its own name + run: setsid --wait bash scripts/check_fat_secrets_passed.sh # PRQ-013 (PP-066 C0-5, #2982): the job that judges a PR's OWN receipt must be # defined by the base — a workflow on pull_request_target + merge_group, never # pull_request — and `gate` must not wait on a head-defined receipt job. Case @@ -1429,98 +1637,10 @@ jobs: # HARNESS-BROKEN rather than as a dead mutant. Text-only, no build. - name: "Receipt-wiring guard: every rule it states, mutated (15/15 kill)" run: bash scripts/mutate_pr_review_wiring_guard.sh - # APR-RELEASE-001 §6 R-2, WIRED (#3200 shipped the guard; this is its - # caller). This step is its ONLY runner: guard_tree.sh skips it as - # wired-with-args (it needs a body), so its case table ran in NO job until - # this step called `--self-test` beside `--body` (found by aprender-f0, - # 2026-09-21; the comment here used to claim guard_tree ran it bare). The - # 0.67.0 reconcile found 48 merged PRs since the previous tag of which only - # 9 closed anything; that is what this guard exists to stop. - # - # THE BODY ARRIVES THROUGH `env:`, NEVER INTERPOLATED INTO THE SCRIPT. - # `${{ }}` is substituted textually BEFORE bash sees the line, so a body - # containing a backtick or $(...) would execute on the runner -- and this - # body is attacker-controlled by construction. `env:` passes it as data. - # scripts/check_workflow_env_defined.sh enforces the same rule. - # - # pull_request only: on merge_group there is no pull_request payload, and - # a body is fixed before a PR is ever enqueued. - - name: A PR body must close every issue it cites, or say why not (§6 R-2) - # `!cancelled()` and NOT a bare event check. Actions steps are fail-fast: - # measured on run 34875945193, step 8 went red and 48 of this job's 59 - # steps were SKIPPED -- including this one and its §11.1 neighbour. This - # guard exists because the 0.67.0 reconcile found 48 merged PRs of which - # only 9 closed anything; masked by an unrelated guard, it is dark exactly - # as it was before it had a caller. It reads a PR body and nothing else, - # so no guard result can be its precondition. (#3282 classifies the other - # 46, which is a judgment call per step rather than one blanket edit.) - if: "!cancelled() && github.event_name == 'pull_request'" - env: - PR_BODY: ${{ github.event.pull_request.body }} - run: | - set -euo pipefail - setsid --wait bash scripts/check_pr_closes_issue.sh --self-test - printf '%s' "$PR_BODY" > "$RUNNER_TEMP/pr-body.txt" - setsid --wait bash scripts/check_pr_closes_issue.sh --body "$RUNNER_TEMP/pr-body.txt" - # APR-RELEASE-001 §11.1, WIRED. The section shipped as prose in #3268 and - # nothing read it: `grep -rl ont-delta scripts/ .github/ Makefile` returned - # NOTHING, while §11.1's own text says "Absent is a PR-body lint failure, - # not a review comment". A rule whose enforcement is "not a review comment" - # and whose only mechanism IS a review comment is this repo's anti-theater - # class, one level up from the guard it sits beside. - # - # Same body-handling rule as the step above: the body arrives through - # `env:` as DATA and is never interpolated into the script. It is - # attacker-controlled by construction. - # - # The changed-file list is the other input, and it is what makes the guard - # non-trivial: half the sweep surface ("a known-red list anywhere") is - # DERIVED from the tree, not typed. Measured on #3277, which touches - # scripts/cb200_baseline.txt and no prose sink at all -- a hand-typed sink - # list would have passed it. - - name: A sweep PR closes with an ontology delta, not a paragraph (§11.1) - # `!cancelled()` and NOT a bare event check. Actions steps are fail-fast: - # measured on run 34875945193, step 8 went red and 48 of this job's 59 - # steps were SKIPPED -- including this one and its §11.1 neighbour. This - # guard exists because the 0.67.0 reconcile found 48 merged PRs of which - # only 9 closed anything; masked by an unrelated guard, it is dark exactly - # as it was before it had a caller. It reads a PR body and nothing else, - # so no guard result can be its precondition. (#3282 classifies the other - # 46, which is a judgment call per step rather than one blanket edit.) - if: "!cancelled() && github.event_name == 'pull_request'" - env: - PR_BODY: ${{ github.event.pull_request.body }} - PR_NUMBER: ${{ github.event.pull_request.number }} - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - printf '%s' "$PR_BODY" > "$RUNNER_TEMP/ont-body.txt" - gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate \ - --jq '.[].filename' > "$RUNNER_TEMP/ont-files.txt" - setsid --wait bash scripts/check_pr_ont_delta.sh \ - --body "$RUNNER_TEMP/ont-body.txt" --changed "$RUNNER_TEMP/ont-files.txt" - # aprender#2822: THE REPORT. Runs even when the job dies mid-cargo, - # and takes its own final probe first -- without that, a failure at - # step K leaves the last recorded boundary BEFORE K and the table - # reads clean, which is the whole failure mode being investigated. - # - # It also pulls the host journal for the job's window into the job - # log. That is the decisive evidence and the only evidence that - # expires: `_work/*/target` is deleted by HOST automation (a sibling - # runner's job-started hook, a systemd timer) which logs under its own - # tag and appears in NO GitHub log. See - # scripts/runner-infra/runner-disk-guard.sh, whose own header records - # this exact signature. - # - # continue-on-error is deliberate and is NOT a disarmed gate: nothing - # here asserts anything. The gate that can fail is the case table - # wired above. - - name: "target-watch: report (aprender#2822)" - if: always() - continue-on-error: true + - name: "Guard jobs run all: no fail-fast step over baseline, every guard script reachable (#4415)" run: | - bash scripts/ci_target_watch.sh probe post-job - bash scripts/ci_target_watch.sh report + setsid --wait bash scripts/check_guard_steps_run_all.sh --self-test + setsid --wait bash scripts/check_guard_steps_run_all.sh # ── THE CARGO-USING GUARDS, PARALLEL TO workspace-test ──────────────────── # @@ -1637,7 +1757,8 @@ jobs: # .package-cache-mutate) are created by cargo itself INSIDE this same # directory — which is the entire reason the mount is $CARGO_HOME and # not $CARGO_HOME/registry. - - name: Create this job's own target dir and CARGO_HOME (before any bind mount) + - id: guard-setup + name: Create this job's own target dir and CARGO_HOME (before any bind mount) run: | # A root-owned parent (created by the docker daemon for another job's # bind mount, #3039) is chowned through the image before the mkdir; @@ -1661,10 +1782,42 @@ jobs: done mkdir -p "$path" } + # infra#1059: admit this target unit against gx10's disk budget BEFORE creating it + # (free >= 50G + 25G x (live+1), live < 2; waits <=20m, then exit 1). The admit + # record is what the runner's JOB_COMPLETED hook reaps, and nothing else. Hosts + # without the gate skip; gx10 exports CI_TARGET_ADMIT_REQUIRED=1, so a missing + # gate there is red, never a silent skip. + if command -v ci-target-admit >/dev/null 2>&1; then + CI_TARGET_ADMIT_WAIT_S=300 ci-target-admit "$GUARD_TARGET_DIR" || exit 1 # 5 min, not 20: the 30 min job budget is rule E and is never widened; gx10 guard-cargo max 23 min (46 runs, 2026-09-24) + elif [ "${CI_TARGET_ADMIT_REQUIRED:-0}" = 1 ]; then + echo "::error::infra#1059: ci-target-admit is required on ${RUNNER_NAME:-this runner} and is missing"; exit 1 + fi heal_path /mnt/nvme-raid0/targets/aprender-ci "$GUARD_TARGET_DIR" heal_path /mnt/nvme-raid0/cargo-ci/home "$GUARD_CARGO_HOME/registry" printf 'target dir: %s\n' "$GUARD_TARGET_DIR" printf 'cargo home: %s (registry/ .package-cache .package-cache-mutate travel together)\n' "$GUARD_CARGO_HOME" + # #4415 (operator A4, 2026-09-25): ONE step runs every guard step of guard-cargo-steps + # below, keeps going past a red one, and reports every failure. GitHub would + # stop this job at its first red step, and the others would never be seen. + # `make guards-local` and the pre-push hook run this same script and print + # the same `ci_guards: sha256` line. + - name: Every guard step runs, and every failure is reported (scripts/ci_guards.sh, #4415) + if: ${{ !cancelled() && steps.guard-setup.outcome == 'success' }} + env: + GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} + run: bash scripts/ci_guards.sh guard-cargo + + guard-cargo-steps: + # MANIFEST, never run by GitHub (`if: false`). These are guard-cargo's guard steps, + # executed by the single `scripts/ci_guards.sh guard-cargo` step of guard-cargo above, and by + # `make guards-local` (#4415). They live here, verbatim, so every guard that + # reads this file for an invocation (check_guards_are_wired.sh, guard_tree.sh, + # the contracts' `test:` greps) still finds it. A step here is name/run/env + # only; scripts/check_guard_steps_run_all.sh refuses anything else. + if: false + runs-on: [self-hosted, Linux, clean-room] # arch-neutral: intel, yoga or gx10 (#3100) + steps: # Poka-yoke: a beat that no workflow executes reads as enforcement, is # counted as enforcement, and proves nothing. The Pillar-4 marquee decode # beat sat in ZERO workflows while being quoted as an enforced win (#2319). @@ -1974,6 +2127,17 @@ jobs: # same binary. Nothing extra is compiled by placing it here. - name: pv_bin.sh must resolve the HEAD-built pv and refuse a stale one run: setsid --wait bash scripts/check_pv_bin_resolution.sh + # #4475: the contract gate, blocking. `make contracts` stopped after its first step (an + # unconditional `exit` under .ONESHELL) and nothing on a PR ran `extract --check`, so a contract + # could land without its census / contracts.nt / shapes.ttl regenerated. The shapes step fails + # CLOSED here with the HEAD pv: a decline (pv exit 2: NoShapes / NoFocus), an Unknown verdict, + # shapes_n == 0, focus_nodes_n == 0 or a silent planted control is RED, unlike the fleet-pin + # andon (check_fleet_pv_shapes_advisory.sh), which reports fleet state and is not this gate. + # The self-test runs first so a gate that can no longer refuse is named as such. + - name: contracts gate refuses what it cannot measure (self-test + mutant) + run: setsid --wait bash scripts/contracts_gate.sh --self-test + - name: contracts gate — lint, fail-closed shapes, regenerate + git diff, README, provenance + run: setsid --wait bash scripts/contracts_gate.sh - name: Every contract cites a test that exists (strict-test-binding) run: setsid --wait bash scripts/check_contract_test_binding.sh # Poka-yoke (#2551): `cargo test -p aprender-contracts --test @@ -2009,6 +2173,21 @@ jobs: # (text-only, no build) runs first, per the convention above. - name: Renamed-crate facades case table run: setsid --wait bash scripts/check_facade_compat.sh --self-test + # #4587 review B3 (PR-REVIEW-SKILL-002 v2 S3.D): a guard with only its own + # self-test is a guard nobody has shown can turn RED. Each mutation set applies + # every drop/flip to a COPY and fails on any survivor or any patch that did not + # apply. The two lean guards were added with no CI caller at all; they run here. + - name: scoped facade allowlist can still turn RED (17 mutants) + run: setsid --wait bash scripts/mutate_facade_compat_guard.sh + - name: lean guards (leanchecker scoped, modules reachable) + their mutation sets + run: | + set -euo pipefail + setsid --wait bash scripts/check_leanchecker_scoped.sh --self-test + setsid --wait bash scripts/check_leanchecker_scoped.sh + setsid --wait bash scripts/check_lean_modules_reachable.sh --self-test + setsid --wait bash scripts/check_lean_modules_reachable.sh + setsid --wait bash scripts/mutate_leanchecker_scoped_guard.sh + setsid --wait bash scripts/mutate_lean_modules_reachable_guard.sh - name: provable-contracts 0.3.1 code still compiles against the facades run: setsid --wait bash scripts/check_facade_compat.sh # aprender#2628: check_facade_compat.sh proves the facades COMPILE and are @@ -2886,6 +3065,7 @@ jobs: outputs: gpu_touched: ${{ steps.decide.outputs.gpu_touched }} gpu_crates: ${{ steps.decide.outputs.gpu_crates }} + cuda_lint: ${{ steps.decide.outputs.cuda_lint }} steps: - uses: actions/checkout@v7 with: @@ -2916,7 +3096,7 @@ jobs: bash scripts/ci_gpu_touched.sh --diff-from "$RUNNER_TEMP/touched.txt" \ > "$RUNNER_TEMP/gpu.txt" cat "$RUNNER_TEMP/gpu.txt" - grep -E '^(gpu_touched|gpu_crates|reason)=' "$RUNNER_TEMP/gpu.txt" >> "$GITHUB_OUTPUT" + grep -E '^(gpu_touched|gpu_crates|cuda_lint|reason)=' "$RUNNER_TEMP/gpu.txt" >> "$GITHUB_OUTPUT" # ROW 67-C1 — `gpu-quick` on gx10 (GB10, sm_121, aarch64). # Promotion to required: row 68-C3, after 5 consecutive green gx10 nightlies. @@ -3012,14 +3192,20 @@ jobs: # (the yoga nightly axis is #3060 / YOGA-NIGHTLY-001). cuda-unit: needs: [gpu-touched] - if: github.event_name == 'pull_request' && needs.gpu-touched.outputs.gpu_touched == '1' + # gpu_touched=1 runs every step. cuda_lint=1 alone (an apr-cli-only diff, + # #4336) runs ONLY the clippy step: the test steps carry their own + # gpu_touched `if:`, so yoga is held for a check build, not the test builds. + if: github.event_name == 'pull_request' && (needs.gpu-touched.outputs.gpu_touched == '1' || needs.gpu-touched.outputs.cuda_lint == '1') runs-on: [self-hosted, Linux, X64, cuda, yoga, ephemeral, docker] # 15 -> 35 (#3810): the derived cuda-only set needs a second, default-feature # test build of aprender-serve to list against. MEASURED on this PR's own run # (job 107295428985, yoga-eph, sharing yoga with a coverage job): the step # took 18 m 20 s and the job 21 m 18 s (tests alone 51 s). 25 left 3.7 min # of margin on a shared runner, a future flaky timeout, so 35. - timeout-minutes: 35 + # 35 -> 50 (#3636): the clippy step is a check-profile build of the cuda tree, + # separate from the release test builds. Not yet measured on yoga; 15 min is a + # ceiling, not an estimate. Re-derive from the first run and tighten. + timeout-minutes: 50 continue-on-error: false concurrency: group: perf-yoga @@ -3058,6 +3244,7 @@ jobs: # (operator, 2026-09-09), not a training host. Nothing to yield to. The # `perf-yoga` concurrency group above is what keeps two runs off it. - name: aprender-gpu cuda unit tests (--features cuda --lib --release) + if: needs.gpu-touched.outputs.gpu_touched == '1' run: | nice -n 19 cargo test -p aprender-gpu --features cuda --lib --release # THE SERVE CUDA SET IS DERIVED, NOT FILTERED (#3810). The step used to filter @@ -3093,6 +3280,7 @@ jobs: # counts as a pass. The grep is UNANCHORED, because under --nocapture the # message lands on the `test X ... ` line. - name: "aprender-serve cuda unit tests (the derived cuda-only set, by name)" + if: needs.gpu-touched.outputs.gpu_touched == '1' run: | set -euo pipefail export LC_ALL=C @@ -3142,6 +3330,19 @@ jobs: echo "$summary" printf 'cuda-only: %s passed + %s ignored = %s derived, 0 device skips, %s model-absent skip line(s)\n' \ "$passed" "$ignored" "$n_only" "${model_skips:-0}" + # #3636: `clippy --features cuda` was never run by any gate, and 62 findings + # piled up in cuda-gated code (compute, train, serve) before anyone looked. + # This is the only job with nvcc, so the lint lives here. The script runs its + # own planted-mutant self-test first: an unused import under + # #[cfg(feature = "cuda")] must turn it RED, or the gate is refused as vacuous. + # No step `if:`: this step runs whenever the job does, including on an + # apr-cli-only diff (cuda_lint=1, gpu_touched=0) — apr-cli has 30 files of + # #[cfg(feature = "cuda")] code that only this lint builds (#4336). + - name: "clippy --features cuda is clean (#3636)" + run: | + set -euo pipefail + setsid --wait bash scripts/check_clippy_cuda.sh --self-test + nice -n 19 setsid --wait bash scripts/check_clippy_cuda.sh # APEX-001 EV-2a: the same figure, rendered on two architectures, must be the same bytes. # @@ -3292,6 +3493,153 @@ jobs: path: target/aprender-viz/raster-receipt.json if-no-files-found: error + # ── PVL-001 EV-9 (#4083): the provable ladder, ADVISORY ──────────────────── + # + # `pv discharge run` over the Lean tree: build.sh (Mathlib cache get, and a cache + # MISS declines instead of silently building Mathlib), then `check` with every arm + # (--strict, the comparator, --leanchecker), then the tracked discharge-summary.json. + # The committed summary must be what the run writes (summary-fresh: the tree carries + # the summary and CI regenerates it and diffs it; a gate never rewrites its own + # baseline, PVL-001 §5.3). + # + # ADVISORY: NOT in `gate`'s `needs:` (cop ruling, 2026-09-24). Measured at 4a870daf5 on + # lambda (4m21s, 1.4 GB RSS), the ladder is RED: 7 escape-allowlist.yaml entries are still + # `confirmed_by: pending`, and only a person may confirm them (§5.3). Flip to required -- + # add it to `gate.needs` and read its result there -- only when that is clear. + # + # The clean-room X64 pool is intel-clean-room-* plus yoga-build, yoga-build2 and yoga-build3, + # and infra-8d verified elan and a writable /mnt/nvme-raid0/lean-cache on every one of them + # (2026-09-25). A host added to the pool without them DECLINES here, never builds cold. + # X64 is mandatory: `ci / lint` once landed on aarch64 gx10 and failed on x86-only code. + # 90 min: under the merge queue's 180 min. + # build.sh is ~6 min on a warm cache, and the leanchecker arm's own cap is 3600 s. + provable-ladder: + runs-on: [self-hosted, Linux, clean-room, X64] + timeout-minutes: 90 + # #4433: a section in x86-main. ADVISORY there too: fat_driver's verdict skips a + # continue-on-error section, so a RED ladder does not turn the x86-main job red. + continue-on-error: true + env: + LEAN: crates/aprender-contracts-staging/lean + SUMMARY: crates/aprender-contracts-staging/discharge-summary.json + LEAN_CACHE_ROOT: /mnt/nvme-raid0/lean-cache + CARGO_TARGET_DIR: /mnt/nvme-raid0/targets/aprender-ci/${{ github.event.pull_request.number || github.ref_name }}/provable-ladder + PV: /mnt/nvme-raid0/targets/aprender-ci/${{ github.event.pull_request.number || github.ref_name }}/provable-ladder/debug/pv + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 1 + + - name: Preconditions -- elan, the lean cache, and the files the ladder judges + id: pre + # A missing precondition DECLINES (rc 2); it is never a silent cold build. The toolchain + # is read from lean-toolchain, so this workflow never names a Lean version. The .lake dir + # lives in the cache, keyed on lean-toolchain + lake-manifest.json. + shell: bash + run: | + set -euo pipefail + decline() { echo "decline: $1" | tee -a "$GITHUB_STEP_SUMMARY"; exit 2; } + # A file the ladder judges that this TREE does not carry is a SKIP, not a host defect: + # no ladder to run, so no verdict either way (#4429: build.sh and discharge-summary.json + # have never been committed). skip=true turns build + discharge off; summary-fresh and + # pv lint key on their outcomes and skip with them. Host defects below still DECLINE. + skip() { echo "SKIP (no verdict, not a pass): $1" | tee -a "$GITHUB_STEP_SUMMARY"; { echo "skip=true"; echo "skip_reason=$1"; } >> "$GITHUB_OUTPUT"; exit 0; } + # Planted falsifier (scripts/ci/ladder_skip_is_red.sh): force the skip path. A skip is + # RED (step "No verdict is not a pass"), so this knob can only turn the ladder red. + [ "${LADDER_FORCE_SKIP:-}" != 1 ] || skip "LADDER_FORCE_SKIP=1 (planted falsifier)" + for f in "$LEAN/lean-toolchain" "$LEAN/lake-manifest.json" "$LEAN/build.sh"; do + [ -f "$f" ] || skip "$f is not in this tree" + done + # summary-fresh diffs a TRACKED file: `git diff` exits 0 on an untracked path. + git ls-files --error-unmatch -- "$SUMMARY" >/dev/null 2>&1 || skip "$SUMMARY is not tracked" + # elan's own default, from the runner user's $HOME: no machine-specific literal. + ELAN_HOME="${ELAN_HOME:-$HOME/.elan}" + echo "ELAN_HOME=$ELAN_HOME" >> "$GITHUB_ENV" + export PATH="$ELAN_HOME/bin:$PATH" + command -v elan >/dev/null || decline "no elan in $ELAN_HOME on $(hostname) (proof-toolchain, infra)" + [ -d "$LEAN_CACHE_ROOT" ] || decline "no $LEAN_CACHE_ROOT on $(hostname) (proof-toolchain, infra)" + key=$(cat "$LEAN/lean-toolchain" "$LEAN/lake-manifest.json" | sha256sum | cut -c1-16) + cache=$LEAN_CACHE_ROOT/$key + mkdir -p "$cache" + # elan >= 4 exits 1 on `toolchain install` of a toolchain that is already installed + # ("error: '...' is already installed"), so a warm runner failed this step every time + # after the first (#4502 row 13). Install only when it is missing, and decide that + # inside the lock so two jobs cannot both see "missing" and race the install. + tc=$(cat "$LEAN/lean-toolchain") + flock "$cache.lock" bash -c 'elan toolchain list | awk "{print \$1}" | grep -qxF -- "$1" || elan toolchain install "$1"' _ "$tc" + # A real .lake dir would make `ln` write the link INSIDE it instead of replacing it. + [ ! -e "$LEAN/.lake" ] || [ -L "$LEAN/.lake" ] || decline "$LEAN/.lake is a real directory, not the cache link" + ln -sfn "$cache" "$LEAN/.lake" + echo "$ELAN_HOME/bin" >> "$GITHUB_PATH" + echo "cache=$cache" >> "$GITHUB_OUTPUT" + + - name: Build the in-tree pv + id: build + if: ${{ steps.pre.outputs.skip != 'true' }} + shell: bash + run: | + set -euo pipefail + cargo build --locked -p aprender-contracts-cli --bin pv + test -x "$PV" + + - name: pv discharge run (build.sh, then check --strict + comparator + leanchecker) + id: discharge + if: ${{ steps.pre.outputs.skip != 'true' }} + shell: bash + # One run at a time per cache: two runs unpacking into the same .lake would race. + # set +e: Actions runs `bash -e`, which would abort at a failing run before its rc and + # its log tail are recorded. + run: | + set +e + flock "${{ steps.pre.outputs.cache }}.lock" "$PV" discharge run "$LEAN" > "$RUNNER_TEMP/discharge-run.log" 2>&1 + rc=$? + tail -n 40 "$RUNNER_TEMP/discharge-run.log" + echo "pv discharge run: rc=$rc" >> "$GITHUB_STEP_SUMMARY" + exit "$rc" + + - name: summary-fresh -- the committed discharge-summary.json is what the run wrote + # Only when the run RAN (it writes the summary on failure too). Skipped means nothing + # was regenerated, and a diff of an unregenerated file would pass vacuously. + if: ${{ !cancelled() && (steps.discharge.outcome == 'success' || steps.discharge.outcome == 'failure') }} + shell: bash + run: git diff --exit-code -- "$SUMMARY" + + - name: pv lint contracts/ + if: ${{ !cancelled() && steps.build.outcome == 'success' }} + shell: bash + run: '"$PV" lint contracts/' + + - name: Upload the discharge logs + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v7 + with: + name: provable-ladder + path: | + ${{ runner.temp }}/discharge-run.log + crates/aprender-contracts-staging/lean/discharge.json + crates/aprender-contracts-staging/discharge-summary.json + if-no-files-found: warn + + # EV-9 finding a2-fat-driver-skip-is-success (operator 2026-09-28: "skipped ≠ pass"). A + # skip ran nothing, so it must not leave the section `success`: fat_driver would report + # that as a ladder pass. RED here; the section stays continue-on-error (advisory). + - name: No verdict is not a pass -- a skipped ladder is RED + if: ${{ !cancelled() && steps.pre.outputs.skip == 'true' }} + shell: bash + run: | + echo "::error::provable-ladder SKIPPED (${{ steps.pre.outputs.skip_reason }}): no verdict, so not a pass" | tee -a "$GITHUB_STEP_SUMMARY" + exit 1 + + - name: Advisory verdict + if: always() + shell: bash + run: | + { + echo "### provable-ladder (PVL-001 EV-9, #4083): ADVISORY, not in gate.needs" + echo "Job status: ${{ job.status }}. It becomes required only when this blocker is clear:" + echo "1. escape-allowlist.yaml: 7 entries are \`confirmed_by: pending\`, and a person must confirm them or delete the axioms (PVL-001 §5.3)" + } >> "$GITHUB_STEP_SUMMARY" + # Mutation testing — DIFF-SCOPED + BLOCKING on PRs (PMAT build-system audit gap #1). # # BEFORE: full-tree `cargo mutants -- --lib`, push-to-main only, and @@ -3351,8 +3699,20 @@ jobs: # cargo-mutants --in-diff takes a unified diff and mutates ONLY the # lines it adds/changes. We diff the PR head against the merge-base with # the target branch so the scope is exactly "what this PR introduces". + env: + HEAD_REF: ${{ github.head_ref }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + BASE_REPO: ${{ github.repository }} run: | set -euo pipefail + # Rule 0 (operator 2026-09-27, verbatim "exempt the car"): a car/* PR of this repo is judged by + # mutants-nightly (uncapped) after it lands; every other PR keeps MUTANTS_CAP. Case table: + # scripts/mutants_diff_gate.sh --self-test (car, non-car, nested, fork rows). + if bash scripts/mutants_diff_gate.sh --exempt-ref "$HEAD_REF" "$HEAD_REPO" "$BASE_REPO"; then + echo "has_rs=false" >> "$GITHUB_OUTPUT" + : > pr.diff + exit 0 + fi BASE_REF="${{ github.event.pull_request.base.ref }}" # NOT --depth=1: a shallow base commit has no shared ancestor with the # fetch-depth:0 PR head, so `git merge-base` finds none and (under @@ -3381,6 +3741,14 @@ jobs: echo "has_rs=false" >> "$GITHUB_OUTPUT" echo "No .rs path in the diff ($(grep -c . pr.files || true) path(s)) -- nothing to mutate; the mutation steps skip and the job passes" fi + # #4587 A' (operator 2026-09-29 06:36Z): a head ref in ci/mutants-table-refs.txt (PR-1 and PR-2 of 0.70 + # only) is judged by the CI survivor table -- the mutants-shard matrix over the WHOLE diff, cap 0, verified + # by mutants-table -- and `gate` requires mutants-table to succeed for exactly these refs, read from the + # same file at the same commit. So this section hands over; it does not pass anything unjudged. + if grep -v '^#' ci/mutants-table-refs.txt | grep -qxF -- "$HEAD_REF"; then + echo "has_rs=false" >> "$GITHUB_OUTPUT" + echo "head ref $HEAD_REF is in ci/mutants-table-refs.txt: judged by the mutants-shard matrix + mutants-table (gate requires it)" + fi - name: Pull sovereign-ci image (with retry + local-cache fallback) if: steps.prdiff.outputs.has_rs == 'true' # Same two-layer resilience as workspace-test — see that job for full context. diff --git a/contracts/alibi-slopes-v1.yaml b/contracts/alibi-slopes-v1.yaml index 688538bf36..5d299a6655 100644 --- a/contracts/alibi-slopes-v1.yaml +++ b/contracts/alibi-slopes-v1.yaml @@ -55,7 +55,7 @@ proof_obligations: mathlib_imports: - Mathlib.Analysis.SpecialFunctions.Pow.Real notes: 'Exponent -8(h+1)/n < 0 for h >= 0, n > 0; base 2 > 1 so 2^(neg) < 1. - Companion alibi_slope_pos proves 0 < m[h] (strictly positive geometric sequence).' + Companion alibi_slope_real_pos proves 0 < m[h] (strictly positive geometric sequence).' - id: AS-EQ-003 type: equivalence property: Matches ggml reference exponent @@ -89,7 +89,7 @@ verification_summary: theorem: Alibi.alibi_slope_lt_one file: ProvableContracts/Theorems/Alibi/Slopes.lean note: 'exponent -8(h+1)/n < 0 for h>=0, n>0, and base 2>1, so 2^(neg) < 1 - (Real.rpow_lt_one_of_one_lt_of_neg). alibi_slope_pos additionally proves 0 < m[h].' + (Real.rpow_lt_one_of_one_lt_of_neg). alibi_slope_real_pos additionally proves 0 < m[h].' - obligation: Matches ggml reference exponent theorem: Alibi.alibi_slope_ggml file: ProvableContracts/Theorems/Alibi/Slopes.lean @@ -97,7 +97,7 @@ verification_summary: matching llama.cpp soft_max_ext.' notes: 'All 3 obligations proved sorry-free in Lean 4 over the reals (Mathlib Real.rpow). Zero N/A — the contract is fully analytic. Slopes are a positive, - strictly-decreasing geometric sequence: alibi_slope_pos (m[h]>0) and + strictly-decreasing geometric sequence: alibi_slope_real_pos (m[h]>0) and alibi_slope_strict_anti (m[0]>m[1]>...>m[n-1]) additionally proved. Verified via lake env lean ProvableContracts/Theorems/Alibi/Slopes.lean (exit 0, 0 sorry).' kernel_structure: diff --git a/contracts/apr-cli-commands-v1.yaml b/contracts/apr-cli-commands-v1.yaml index 8e94cce4f5..65489d4bdf 100644 --- a/contracts/apr-cli-commands-v1.yaml +++ b/contracts/apr-cli-commands-v1.yaml @@ -387,6 +387,13 @@ commands: requires_model: false side_effects: [] + - name: ptx-debug + category: hardware + description: "PTX falsification analysis and FKR test generation (was the aprender-ptx-debug binary)" + requires_model: false + side_effects: [filesystem] + subcommands: [analyze, gen-fkr, version] + - name: ptx-map category: hardware description: "Model-to-PTX source mapping" @@ -786,7 +793,15 @@ commands: description: "Provable-contracts CLI (also ships as the standalone pv binary)" requires_model: false side_effects: [filesystem] - subcommands: [audit, book, census, certify, check-parity, codegen, coq, coverage, diff, equations, explain, extract, extract-pytorch, flux, fuzz, generate, graph, infer, invariants, kaizen, kani, lean, lean-status, lint, migrate, mirai, pipeline, probar, proof-status, query, roofline, scaffold, score, status, tla, unlock, validate, verify-bindings, verify-pipeline, verify-structure] + subcommands: [audit, book, census, certify, challenge, check-parity, codegen, coq, coverage, diff, discharge, equations, explain, extract, extract-pytorch, flux, fuzz, generate, graph, infer, invariants, kaizen, kani, lean, lean-status, lint, migrate, mirai, obligations, ontology, pipeline, probar, proof-status, query, roofline, scaffold, score, status, tla, unlock, validate, verify-bindings, verify-pipeline, verify-structure] + + # Hidden (#[command(hide = true)]): a gate input, not a user command, so it is + # absent from `apr --help` and from cli_commands.rs registered_commands(). + - name: surface + category: tools + description: "Print apr's own command surface as JSON (apr-cli-surface/v1, #3745); hidden gate input" + requires_model: false + side_effects: [stdio] # ── Falsification Conditions ── diff --git a/contracts/apr-cli-coverage-v1.yaml b/contracts/apr-cli-coverage-v1.yaml index b09e842449..a0f20e02b6 100644 --- a/contracts/apr-cli-coverage-v1.yaml +++ b/contracts/apr-cli-coverage-v1.yaml @@ -44,7 +44,7 @@ falsification_tests: if_fails: "apr-cli coverage below 95%" verification_summary: - total_obligations: 1 + total_obligations: 0 proven: 0 tested: 0 status: pending diff --git a/contracts/apr-cli-dep-migration-v1.yaml b/contracts/apr-cli-dep-migration-v1.yaml index 0f62f46a46..b324069149 100644 --- a/contracts/apr-cli-dep-migration-v1.yaml +++ b/contracts/apr-cli-dep-migration-v1.yaml @@ -58,7 +58,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 2 + total_obligations: 1 proven: 0 tested: 0 status: pending diff --git a/contracts/apr-cli-publish-v1.yaml b/contracts/apr-cli-publish-v1.yaml index 286aeb97d4..647baf21d6 100644 --- a/contracts/apr-cli-publish-v1.yaml +++ b/contracts/apr-cli-publish-v1.yaml @@ -74,7 +74,7 @@ falsification_tests: rule: "publishing goes only through scripts/check_publish_preflight.sh, and the cascade carries no --allow-dirty (F-9)" prediction: "check_publish_preflight.sh --selftest drives R1, R3, R4 and R5 to both verdicts (16 rows); cascade-publish.sh calls the gate before its first upload and its cargo publish line has no --allow-dirty" test: "bash scripts/check_publish_preflight.sh --selftest && grep -q 'scripts/check_publish_preflight.sh' scripts/cascade-publish.sh && ! grep -q -- '--allow-dirty' scripts/cascade-publish.sh" - if_fails: "a dirty, untagged, off-main or NO-GO tree can be uploaded to an immutable registry" + if_fails: "a dirty, untagged, off-release-branch or NO-GO tree can be uploaded to an immutable registry" proof_obligations: - id: ACP-INV-001 @@ -88,7 +88,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 4 + total_obligations: 1 proven: 0 tested: 0 status: pending diff --git a/contracts/apr-cli-qa-v1.yaml b/contracts/apr-cli-qa-v1.yaml index 86805829f2..8fbec732e1 100644 --- a/contracts/apr-cli-qa-v1.yaml +++ b/contracts/apr-cli-qa-v1.yaml @@ -254,7 +254,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 11 + total_obligations: 5 proven: 0 tested: 11 status: tested diff --git a/contracts/apr-cli-safety-v1.yaml b/contracts/apr-cli-safety-v1.yaml index 7d2ad8b6fc..ddf18d4487 100644 --- a/contracts/apr-cli-safety-v1.yaml +++ b/contracts/apr-cli-safety-v1.yaml @@ -88,8 +88,8 @@ qa_gate: - falsification pass_criteria: All 5 falsification tests + 4 proof obligations pass verification_summary: - total_obligations: 4 - l2_property_tested: 4 + total_obligations: 1 + l2_property_tested: 1 l3_kani_proved: 0 l4_lean_proved: 0 l4_sorry_count: 0 diff --git a/contracts/apr-cpu-vs-gpu-output-parity-v1.yaml b/contracts/apr-cpu-vs-gpu-output-parity-v1.yaml index 7c1e0690ff..12a93756d7 100644 --- a/contracts/apr-cpu-vs-gpu-output-parity-v1.yaml +++ b/contracts/apr-cpu-vs-gpu-output-parity-v1.yaml @@ -607,7 +607,7 @@ proof_obligations: property: "on Blackwell (cc>=120) default apr run --gpu decode throughput for a 1.5B Q4_K_M model is >= 100 tok/s (on-GPU resident path, no silent CPU/wgpu fallback); ~10 tok/s falsifies it as an F2 false-fallback / stale binary (PMAT-885)" verification_summary: - total_obligations: 11 + total_obligations: 10 proven: 0 tested: 1 status: pending diff --git a/contracts/apr-docs-v1.yaml b/contracts/apr-docs-v1.yaml index b610f4082a..537a6da980 100644 --- a/contracts/apr-docs-v1.yaml +++ b/contracts/apr-docs-v1.yaml @@ -159,7 +159,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 9 + total_obligations: 3 proven: 0 tested: 9 status: tested diff --git a/contracts/apr-dogfood-coverage-v1.yaml b/contracts/apr-dogfood-coverage-v1.yaml index 075c5bfa95..2697fbe38e 100644 --- a/contracts/apr-dogfood-coverage-v1.yaml +++ b/contracts/apr-dogfood-coverage-v1.yaml @@ -3,7 +3,7 @@ metadata: created: '2026-08-22' author: PAIML Engineering references: - - 'docs/audits/surface_audit.csv — 142 of 830 features gated, across 28 binaries and 14 clusters, measured 2026-08-22 @ 4bbfeb07f; 142 of 832 features gated after the PERF-046 partial re-audit (PR #2742); 143 of 833 features gated after the PP-LLAMA-001 re-audit (GET /v1/effective-config added and swept by the dogfood route enumeration, router.rs rows re-pointed); 144 of 837 features gated after PP-066 L0-1b added the three `apr parity` flag rows (--per-op, --out, --threshold), which the dogfood sweep does not reach -- the denominator grew and the numerator did not; 144 of 838 features gated after #3726 added `apr tokenize encode` (the tokenizer-parity gate''s id source), which the dogfood sweep does not reach either; 144 of 864 features gated after #4219 added one ` --version` row for each of the 26 build-SHA bins (each run and observed printing its SHA), which the dogfood sweep does not reach' + - 'docs/audits/surface_audit.csv — 142 of 830 features gated, across 28 binaries and 14 clusters, measured 2026-08-22 @ 4bbfeb07f; 142 of 832 features gated after the PERF-046 partial re-audit (PR #2742); 143 of 833 features gated after the PP-LLAMA-001 re-audit (GET /v1/effective-config added and swept by the dogfood route enumeration, router.rs rows re-pointed); 144 of 837 features gated after PP-066 L0-1b added the three `apr parity` flag rows (--per-op, --out, --threshold), which the dogfood sweep does not reach -- the denominator grew and the numerator did not; 144 of 838 features gated after #3726 added `apr tokenize encode` (the tokenizer-parity gate''s id source), which the dogfood sweep does not reach either; 144 of 839 features gated after ONT-10 S19 ledgered the bare `aprender-cbtop` root surface (interactive TUI / --headless), which the dogfood sweep does not reach; 144 of 851 features gated after ONT-4g (#4476) added the twelve pv/apr command rows the binary snapshot found unledgered, none of them gated; 144 of 853 features gated after the ONT-4g bin gate (surface_audit_bins_gate.sh) found `pv-sat` shipping with no row and its two surfaces were ledgered, neither gated; 144 of 857 features gated after ONT-10 S4 ledgered the 4 hf_* tools `aprender-orchestrate mcp` lists, none of them gated; 144 of 868 features gated after ONT-10 S9/S7 mirrored the eleven ONT-4g pv rows as `apr pv` rows (the apr node joins binary=apr), none of them gated; 144 of 894 features gated once main (with #4219''s 26 ` --version` rows, each run and observed printing its SHA, which the dogfood sweep does not reach) merged into ONT-10''s 868' - 'docs/audits/surface_audit_elbow.png — k-selection evidence: inertia knee and silhouette over k in [2,45]' - 'scripts/dogfood_cluster.py — the k-sweep that produced the elbow plot (k-selection ONLY; it does not write the columns)' - 'scripts/check_no_cluster_id_keys.sh — T1 enforcement: nothing may key on `cluster_id`' @@ -62,55 +62,63 @@ metadata: # crates/apr-cli/src/commands_enum.rs. All 64 citations of that file in 55 rows # were re-derived: 42 at lines >= 139 shifted by +4, 22 at <= 138 unchanged, and # every cited line's text was checked identical between 79a3af79d and the batch. + # PARTIAL RE-AUDIT, PVL-001 EV-2 / #4095 (G2.1). Same rule: `measured_commit` does not + # move. #4095 rewrites one doc comment in crates/aprender-contracts-cli/src/cli.rs + # line-for-line (4 for 4, no shift), but re-deriving the 76 `pv`/`apr pv` rows that + # cite it found 54 ALREADY stale on main (+6 from `Coverage` on, +12 from `Lint` on -- + # variants inserted since 2026-08-22, never re-audited). Every row now cites the line of + # its own `Commands::` declaration, checked by reading that line back; 22 were + # already exact. `pv census` and `pv extract` ship with no row at all -- not added here. ledger: docs/audits/surface_audit.csv overall: command: "python3 scripts/dogfood_baseline.py # section: totals" - rows: 864 + rows: 894 covered: 144 - ratio: 0.1667 # 144/864 -- 16.7%, NOT "about 17%" - binaries: 28 - binaries_at_zero_coverage: 27 + ratio: 0.1611 # 144/894 -- 16.1%, NOT "about 17%" + binaries: 29 + binaries_at_zero_coverage: 28 clusters: 14 clusters_at_zero_coverage: 9 note: > - All 142 covered features are in `apr`. 27 of 28 binaries have no gate at - all, including aprender-orchestrate (184 features), aprender-test-cli (39) - and pv (38). + All 144 covered features are in `apr`. 28 of 29 binaries have no gate at + all, including aprender-orchestrate (189 features), aprender-test-cli (40) + and pv (49). per_binary: command: "python3 scripts/dogfood_baseline.py # section: per_binary" note: > covered may never DECREASE for any binary; total may never decrease without a reconciliation finding recording which direction moved. - apr: {total: 375, covered: 144} - aprender-orchestrate: {total: 185, covered: 0} + apr: {total: 387, covered: 144} + aprender-orchestrate: {total: 189, covered: 0} aprender-test-cli: {total: 40, covered: 0} - pv: {total: 38, covered: 0} - alimentar: {total: 36, covered: 0} + pv: {total: 49, covered: 0} + aprender-data: {total: 36, covered: 0} aprender-cgp: {total: 26, covered: 0} aprender-profile: {total: 17, covered: 0} apr-qa: {total: 16, covered: 0} + aprender-rag: {total: 14, covered: 0} aprender-train-shell: {total: 14, covered: 0} - trueno-rag: {total: 14, covered: 0} - simular: {total: 13, covered: 0} - verificar: {total: 12, covered: 0} + aprender-simulate: {total: 13, covered: 0} + aprender-verify: {total: 12, covered: 0} aprender-explain: {total: 8, covered: 0} - presentar: {total: 8, covered: 0} + aprender-present: {total: 8, covered: 0} aprender-train-bench: {total: 7, covered: 0} aprender-train-inspect: {total: 7, covered: 0} - aprender-cbtop: {total: 5, covered: 0} + aprender-cbtop: {total: 6, covered: 0} aprender-db: {total: 5, covered: 0} + aprender-ptop: {total: 5, covered: 0} aprender-train-distill: {total: 5, covered: 0} aprender-train-lora: {total: 5, covered: 0} - ptop: {total: 5, covered: 0} - trueno-zram: {total: 5, covered: 0} + aprender-zram: {total: 5, covered: 0} aprender-compute-xtask: {total: 4, covered: 0} aprender-ptx-debug: {total: 4, covered: 0} - apr-corpus-ingest: {total: 3, covered: 0} - score: {total: 3, covered: 0} - apr-qa-readme-sync: {total: 2, covered: 0} + aprender-corpus-ingest: {total: 3, covered: 0} + aprender-score: {total: 3, covered: 0} + aprender-qa-readme-sync: {total: 2, covered: 0} aprender-zram-generator: {total: 2, covered: 0} + pv-sat: {total: 2, covered: 0} per_cluster: command: "python3 scripts/dogfood_baseline.py # section: per_cluster" @@ -147,15 +155,15 @@ metadata: A PRIOR, NEVER EVIDENCE (T3). Cluster membership says where to look. It cannot assert that a feature works, and quality_1_10 is never derived from it -- severity comes from the 0.63.0 ledger. - apr-core-commands: {total: 111, covered: 39} # 35.1% of cluster, 27.1% of gate effort - entrypoint-and-mcp: {total: 115, covered: 9} # 7.8% of cluster, 6.3% of gate effort + apr-core-commands: {total: 112, covered: 39} # 34.8% of cluster, 27.1% of gate effort + entrypoint-and-mcp: {total: 120, covered: 9} # 7.5% of cluster, 6.2% of gate effort http-orchestrate-banco: {total: 96, covered: 0} # ZERO -- one HTTP harness, the cheapest large win - contracts-pv: {total: 76, covered: 0} # ZERO -- one contract-CLI harness, 76 pv subcommands + contracts-pv: {total: 100, covered: 0} # ZERO -- one contract-CLI harness, 100 pv rows data-pipeline: {total: 77, covered: 0} # ZERO - apr-lint-diag: {total: 71, covered: 55} # 77.5% of cluster, 38.5% of gate effort + apr-lint-diag: {total: 71, covered: 55} # 77.5% of cluster, 38.2% of gate effort orchestrate-agent-stack: {total: 56, covered: 0} # ZERO test-harness: {total: 51, covered: 0} # ZERO - http-apr-serve: {total: 45, covered: 40} # 88.9% of cluster, 28.0% of gate effort + http-apr-serve: {total: 45, covered: 40} # 88.9% of cluster, 27.8% of gate effort profiling: {total: 46, covered: 1} # 2.2% of cluster, 0.7% of gate effort rag-eval: {total: 46, covered: 0} # ZERO qa-cgp: {total: 38, covered: 0} # ZERO @@ -172,7 +180,7 @@ metadata: the 4.3% coverage of that band is why. q1_2: {total: 80, covered: 58} # 72.5% q3_4: {total: 49, covered: 27} # 55.1% - q5_6: {total: 676, covered: 29} # 4.3% + q5_6: {total: 706, covered: 29} # 4.1% q7_8: {total: 38, covered: 10} # 26.3% q9_10: {total: 21, covered: 20} # 95.2% @@ -283,7 +291,7 @@ equations: codomain: bool invariants: - "T2. Cluster coverage is a PROXY: one gate in a 95-member cluster is 1%, not covered" - - "5/14 clusters (35.7%) and 144/864 features (16.7%) are reported together, always" + - "5/14 clusters (35.7%) and 144/894 features (16.1%) are reported together, always" - "Enforced mechanically by enforce_pairing(); deleting the feature fraction from the emitter turns the gate RED" - "This is the single most important trap: reporting the proxy alone is how a coverage gate becomes theatre while looking stricter" @@ -672,7 +680,7 @@ falsification_tests: Every emitted line stating a CLUSTER-level ratio also states the underlying FEATURE-level ratio, on EVERY channel one can be emitted through: this gate's report, the receipt, scripts/dogfood_baseline.py and - .claude/skills/apr-dogfood/SKILL.md. 5 of 14 clusters (35.7%) is a proxy and 144 of 864 features (16.7%) is the thing. + .claude/skills/apr-dogfood/SKILL.md. 5 of 14 clusters (35.7%) is a proxy and 144 of 894 features (16.1%) is the thing. One gate in a 95-member cluster is 1%. The rule keys on the NUMBER -- a denominator equal to the cluster count -- not on a phrase, because a phrase can be reworded and a denominator cannot; @@ -842,7 +850,7 @@ qa_gate: pass_criteria: "F-DOGCOV-{001..016} all PASS; any one FAIL is NO-GO" verification_summary: - total_obligations: 16 + total_obligations: 9 proven: 0 tested: 16 status: proposed diff --git a/contracts/apr-mono-binary-rule-v1.yaml b/contracts/apr-mono-binary-rule-v1.yaml index 0c25bbac95..26a900d8ae 100644 --- a/contracts/apr-mono-binary-rule-v1.yaml +++ b/contracts/apr-mono-binary-rule-v1.yaml @@ -1,7 +1,7 @@ metadata: - version: "2.0.0" + version: "2.1.0" created: "2026-04-08" - updated: "2026-04-10" + updated: "2026-09-25" author: PAIML Engineering registry: true references: @@ -41,8 +41,8 @@ equations: INTERNAL-HELPER (8 crates, 9 binaries): aprender-cbtop -> aprender-cbtop # GPU monitor, called by `apr monitor` - aprender-present-cli -> presentar # TUI server, called by `apr present` - aprender-present-terminal -> score, ptop # TUI widgets, used by presentar + aprender-present-cli -> aprender-present # TUI server, called by `apr present` + aprender-present-terminal -> aprender-score, aprender-ptop # TUI widgets, used by presentar aprender-ptx-debug -> aprender-ptx-debug # CUDA PTX debugger (dev-only) aprender-test-cli -> aprender-test-cli # WASM test runner (dev-only) aprender-train-bench -> aprender-train-bench # Training benchmark harness @@ -51,7 +51,7 @@ equations: QA-TOOL (2 crates, 2 binaries — from Phase 2g port): aprender-qa-cli -> apr-qa # QA playbook runner (to wire into `apr qa`) - aprender-qa-certify -> apr-qa-readme-sync # README badge sync (CI tool) + aprender-qa-certify -> aprender-qa-readme-sync # README badge sync (CI tool) MIGRATED (2 crates — [[bin]] → [[example]]): aprender-serve -> [[example]] aprender-serve # Was: inference server. Use `apr serve`. @@ -59,13 +59,13 @@ equations: LEGACY-TO-MIGRATE (8 crates, 9 binaries): aprender-cgp -> aprender-cgp # Contract graph processor → `apr contracts graph` - aprender-data -> alimentar # Data loading → `apr data` + aprender-data -> aprender-data # Data loading → `apr data` aprender-db -> aprender-db # Embedded DB → `apr db` - aprender-simulate -> simular # Simulation → `apr simulate` + aprender-simulate -> aprender-simulate # Simulation → `apr simulate` aprender-train-distill -> aprender-train-distill # Distillation → `apr distill` aprender-train-inspect -> aprender-train-inspect # Weight inspector → `apr train inspect` aprender-train-lora -> aprender-train-lora # LoRA training → `apr finetune` - aprender-zram-cli -> trueno-zram # ZRAM manager → `apr zram` + aprender-zram-cli -> aprender-zram # ZRAM manager → `apr zram` domain: 20 crates (22 → 20 after 2 migrated to [[example]]) codomain: classification @@ -103,6 +103,14 @@ falsification_tests: done if_fails: "legacy binary has no apr subcommand migration path" +- id: FALSIFY-BINRULE-004 + rule: every shipped bin is aprender-* (#4430) + prediction: "every [[bin]] in the root and facade workspaces (from cargo metadata) is apr, pv, + aprender-[-...], or a pending-fold row in scripts/bin_names_pending_fold.txt" + test: | + bash scripts/check_bin_names_aprender.sh --self-test && bash scripts/check_bin_names_aprender.sh + if_fails: "a bin ships under a non-aprender name (rename it) or a pending-fold row is stale (delete it)" + proof_obligations: - id: AMBR-INV-001 type: invariant @@ -114,5 +122,5 @@ proof_obligations: verification_summary: total_obligations: 2 proven: 0 - tested: 3 + tested: 4 status: audited diff --git a/contracts/apr-pretrain-from-init-v1.yaml b/contracts/apr-pretrain-from-init-v1.yaml index 4a61a70ae4..0aa30b1e6c 100644 --- a/contracts/apr-pretrain-from-init-v1.yaml +++ b/contracts/apr-pretrain-from-init-v1.yaml @@ -313,7 +313,7 @@ kani_harnesses: bound: 8 # 4 failure modes × 2 mode values verification_summary: - total_obligations: 6 + total_obligations: 7 proven: 0 tested: 0 status: pending diff --git a/contracts/apr-serve-cancellation-v1.yaml b/contracts/apr-serve-cancellation-v1.yaml index 081b8dc777..005c2b9a21 100644 --- a/contracts/apr-serve-cancellation-v1.yaml +++ b/contracts/apr-serve-cancellation-v1.yaml @@ -204,18 +204,21 @@ falsification_tests: - id: FALSIFY-SERVE-CANCEL-009 name: q4k_scheduler_decode_stops_at_the_cancel_point_not_max_tokens prediction: 'A Q4K request (Session over AprQ4kForward, q4k_generate_config) with - max_tokens=64 and CancelToken::with_budget(8) emits 8 tokens, performs exactly 8 - decode forward passes and polls 9 times; the same call with CancelToken::never() - emits 64 tokens over 63 decode forward passes at contiguous positions, and the - cancelled output is a prefix of it. (Pre-#4269-M2b q4k_decode emitted 9: it - returned the prefill token unpolled.)' + max_tokens=64 and CancelToken::with_budget(8) emits 8 tokens, performs exactly 7 + decode forward passes (emitted - 1: the first token is the prefill''s, and no + forward runs after the poll that cancelled) and polls 9 times; the same call with + CancelToken::never() emits 64 tokens over 63 decode forward passes at contiguous + positions, and the cancelled output is a prefix of it. (Pre-#4269-M2b q4k_decode + emitted 9: it returned the prefill token unpolled. #4269 M2b to #4325 performed 8: + the forward after the 8th token was computed and thrown away.)' test_harness: cargo test -p aprender-serve --lib q4k_scheduler_decode_stops_at_the_cancel_point_not_max_tokens expected_output: 'test result: ok' if_fails: 'PRE-FIX (aprender#2465(1)): AprQ4kRequest had no cancel field and the Q4K decode loop''s only exit was EOS, so /v1/chat/completions, /v1/completions, /generate, /api/chat and /api/generate all ran the GPU to max_tokens for a client that had hung up. Mutation-verified (#4269 M2b) by deleting the session''s poll: - emitted 64 tokens where 8 were required.' + emitted 64 tokens where 8 were required. #4325: moving the session''s decode + forward back after the emit (before the next poll) performs 8 where 7 are required.' - id: FALSIFY-SERVE-CANCEL-010 name: q4k_scheduler_decode_cancelled_before_start_does_no_forward_passes prediction: 'A Q4K request (Session over AprQ4kForward) with an already-cancelled diff --git a/contracts/apr-version-traceability-v1.yaml b/contracts/apr-version-traceability-v1.yaml index 8c5bf391cd..e1eac5897c 100644 --- a/contracts/apr-version-traceability-v1.yaml +++ b/contracts/apr-version-traceability-v1.yaml @@ -1,11 +1,12 @@ metadata: - version: 1.1.0 + version: 1.2.0 created: '2026-04-05' author: PAIML Engineering description: "apr --version traceability contract — the version output must always contain an informative build identifier; never the bare '(unknown)' sentinel when a CARGO_PKG_VERSION or any alternative identifier is available; AND the embedded git SHA must match the actual HEAD of the source tree at build time (including in git worktrees)" references: - "paiml/aprender#597 (Version string shows (unknown) instead of git hash)" - "paiml/aprender#1862 (build.rs misses HEAD changes in worktrees because ../../.git is a file pointer, not a directory)" + - "paiml/aprender#4110 (crates.io installs print +no-git although the tarball's .cargo_vcs_info.json carries the commit)" registry: true tags: - cli @@ -36,11 +37,13 @@ equations: - "Common sentinel forms are forbidden: (unknown), 0000000, , null" fallback_hierarchy: - formula: "APR_GIT_SHA = override ∨ git_hash ∨ committed_sha ∨ 'v{version}+no-git'" + formula: "APR_GIT_SHA = override ∨ vcs_info_sha ∨ git_hash ∨ committed_sha ∨ 'v{version}+no-git'" domain: "build-time environment" codomain: "APR_GIT_SHA environment variable" invariants: - "Env var APR_GIT_SHA_OVERRIDE, if set, wins (for CI/release)" + - "Otherwise: read git.sha1 from the package's .cargo_vcs_info.json (written by cargo package into every published crate), short 9-hex form, -dirty suffix when cargo recorded a dirty tree; a value that is not 40 hex is ignored (#4110)" + - "The vcs rung precedes git rev-parse: an unpacked crate may sit inside an UNRELATED repository whose HEAD is not the crate's commit" - "Otherwise: try git rev-parse --short HEAD" - "Otherwise: try reading crates/apr-cli/.git-sha committed file" - "Otherwise: emit 'v{CARGO_PKG_VERSION}+no-git' as informative fallback" @@ -92,8 +95,13 @@ falsification_tests: - id: FALSIFY-VERSION-004 rule: "build.rs registers worktree-aware rerun-if-changed triggers" prediction: "build.rs invokes `git rev-parse --git-dir` and `--git-common-dir` (not a hardcoded `../../.git/HEAD` path)" - test: "grep -qE 'rev-parse.*--git-(dir|common-dir)' crates/apr-cli/build.rs && ! grep -qE '\\.\\./\\.\\./\\.git/HEAD' crates/apr-cli/build.rs" + test: "grep -qE 'rev-parse.*--git-(dir|common-dir)' crates/aprender-build-sha/src/lib.rs && ! grep -qE '\\.\\./\\.\\./\\.git/HEAD' crates/aprender-build-sha/src/lib.rs crates/apr-cli/build.rs" if_fails: "build.rs still relies on a hardcoded relative .git/HEAD path; worktree HEAD changes won't trigger rebuild" + - id: FALSIFY-VERSION-005 + rule: "a packaged apr-cli (no .git of its own) names the commit in its .cargo_vcs_info.json, even inside a foreign repository" + prediction: "The shipped build.rs, compiled and run as cargo runs it on a package dir holding the byte-exact apr-cli-0.69.1 vcs file, emits APR_GIT_SHA=d8a6df53a; inside an unrelated repo it still does; with the vcs rung removed or placed behind git rev-parse the table goes RED" + test: "cargo test -p aprender-build-sha --lib emit_tests" + if_fails: "crates.io installs regress to '+no-git' (0.68.1/0.69.1 shape), or report an enclosing repository's HEAD" kani_harnesses: - id: KANI-VERSION-001 diff --git a/contracts/aprender/binding.yaml b/contracts/aprender/binding.yaml index 18d9f39a96..943e5b7e59 100644 --- a/contracts/aprender/binding.yaml +++ b/contracts/aprender/binding.yaml @@ -1,6 +1,174 @@ version: 1.0.0 target_crate: aprender bindings: +- contract: adamw-kernel-v1.yaml + equation: weight_update + module_path: aprender::nn::optim::rm_sprop + function: step + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/optim/rm_sprop.rs:97 had no binding row; row added when the binding moved in-tree' +- contract: batchnorm-kernel-v1.yaml + equation: batchnorm_train + module_path: aprender::nn::normalization + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/normalization/mod.rs:391 had no binding row; row added when the binding moved in-tree' +- contract: cma-es-kernel-v1.yaml + equation: mean_update + module_path: aprender::metaheuristics::cmaes_include_01 + function: optimize + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/metaheuristics/cmaes_include_01.rs:5 had no binding row; row added when the binding moved in-tree' +- contract: cma-es-kernel-v1.yaml + equation: sample + module_path: aprender::metaheuristics::cmaes + function: sample + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/metaheuristics/cmaes.rs:274 had no binding row; row added when the binding moved in-tree' +- contract: conv1d-kernel-v1.yaml + equation: conv1d + module_path: aprender::nn::conv + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/conv/mod.rs:292 had no binding row; row added when the binding moved in-tree' +- contract: cross-entropy-kernel-v1.yaml + equation: cross_entropy + module_path: aprender::nn::loss + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/loss.rs:257 had no binding row; row added when the binding moved in-tree' +- contract: cross-entropy-kernel-v1.yaml + equation: log_softmax + module_path: aprender::nn::functional + function: log_softmax + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/functional.rs:307 had no binding row; row added when the binding moved in-tree' +- contract: embedding-algebra-v1.yaml + equation: embedding_lookup + module_path: aprender::models::qwen2 + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/models/qwen2/mod.rs:121 had no binding row; row added when the binding moved in-tree' +- contract: f16-conversion-v1.yaml + equation: f16_to_f32_bias + module_path: aprender::format::gguf::dequant + function: f16_to_f32 + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/format/gguf/dequant.rs:18 had no binding row; row added when the binding moved in-tree' +- contract: gqa-kernel-v1.yaml + equation: gqa + module_path: aprender::nn::transformer + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/transformer/mod.rs:409 had no binding row; row added when the binding moved in-tree' +- contract: kmeans-kernel-v1.yaml + equation: assignment + module_path: aprender::cluster::kmeans_impl + function: predict + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/cluster/kmeans_impl.rs:406 had no binding row; row added when the binding moved in-tree' +- contract: kmeans-kernel-v1.yaml + equation: update + module_path: aprender::cluster::kmeans_impl + function: fit + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/cluster/kmeans_impl.rs:362 had no binding row; row added when the binding moved in-tree' +- contract: layernorm-kernel-v1.yaml + equation: layernorm + module_path: aprender::nn::normalization + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/normalization/mod.rs:131 had no binding row; row added when the binding moved in-tree' +- contract: matmul-kernel-v1.yaml + equation: matmul + module_path: aprender::autograd::ops::activation + function: matmul + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/activation.rs:272 had no binding row; row added when the binding moved in-tree' +- contract: rmsnorm-kernel-v1.yaml + equation: rmsnorm + module_path: aprender::nn::normalization::group_norm + function: forward + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/normalization/group_norm.rs:348 had no binding row; row added when the binding moved in-tree' +- contract: rope-kernel-v1.yaml + equation: rope + module_path: aprender::nn::transformer::attention_helpers + function: apply + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/transformer/attention_helpers.rs:268 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: additive_attention_mask + module_path: aprender::autograd::ops::masking + function: additive_attention_mask + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/masking.rs:43 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: apply_additive_mask + module_path: aprender::nn::transformer::positional_encoding + function: add_mask + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/transformer/positional_encoding.rs:476 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: cosine_similarity_rows + module_path: aprender::autograd::ops::similarity + function: cosine_similarity_rows + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/similarity.rs:55 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: embedding_gather + module_path: aprender::autograd::ops::embedding + function: embedding_gather + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/embedding.rs:38 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: gelu_exact + module_path: aprender::autograd::ops::activation + function: gelu_exact + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/activation.rs:181 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: l2_normalize_rows + module_path: aprender::autograd::ops::normalize + function: l2_normalize_rows + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/normalize.rs:56 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: masked_mean_pool + module_path: aprender::autograd::ops::pooling + function: masked_mean_pool + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/pooling.rs:50 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: mse_loss + module_path: aprender::autograd::ops::similarity + function: mse_loss + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/autograd/ops/similarity.rs:196 had no binding row; row added when the binding moved in-tree' +- contract: setfit-encoder-conformance-v1.yaml + equation: pair_cosine_mse + module_path: aprender::setfit::loss + function: pair_cosine_mse + status: implemented + notes: '#4429: #[contract] site at crates/aprender-core/src/setfit/loss.rs is behind feature setfit, so the #4369 sweep missed it' +- contract: setfit-encoder-conformance-v1.yaml + equation: setfit_encoder_forward + module_path: aprender::setfit::encoder + function: BertSentenceEncoder::forward_tokens + status: implemented + notes: '#4429: #[contract] site at crates/aprender-core/src/setfit/encoder.rs:845 is behind feature setfit, so the #4369 sweep missed it' +- contract: silu-kernel-v1.yaml + equation: sigmoid + module_path: aprender::nn::functional + function: sigmoid + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/functional.rs:58 had no binding row; row added when the binding moved in-tree' +- contract: silu-kernel-v1.yaml + equation: silu + module_path: aprender::nn::functional + function: silu + status: implemented + notes: '#4369: #[contract] site at crates/aprender-core/src/nn/functional.rs:90 had no binding row; row added when the binding moved in-tree' - contract: softmax-kernel-v1.yaml equation: softmax module_path: aprender::nn::functional::softmax @@ -30,15 +198,15 @@ bindings: status: implemented - contract: pagerank-kernel-v1.yaml equation: pagerank - module_path: aprender::graph::pagerank - function: pagerank + module_path: aprender::graph::centrality + function: Graph::pagerank signature: 'fn pagerank(adj: &Tensor, damping: f64, max_iter: usize, tol: f64) -> Tensor' status: implemented notes: Power iteration PageRank - contract: pagerank-kernel-v1.yaml equation: power_iteration - module_path: aprender::graph::pagerank - function: pagerank + module_path: aprender::graph::centrality + function: Graph::pagerank signature: 'fn pagerank(adj: &Tensor, damping: f64, max_iter: usize, tol: f64) -> Tensor' status: implemented notes: Iteration loop inside pagerank() @@ -186,7 +354,7 @@ bindings: notes: Weighted Gini impurity for binary split - contract: decision-tree-v1.yaml equation: mse_split - module_path: aprender::tree::regression_helpers + module_path: aprender::tree::helpers function: compute_mse signature: 'fn compute_mse(y_left: &[f32], y_right: &[f32]) -> f32' status: implemented @@ -253,43 +421,43 @@ bindings: notes: Header validation with model type check before loading - contract: model-format-conversion-v1.yaml equation: format_conversion_roundtrip - module_path: aprender::convert + module_path: apr_cli::commands::convert function: run status: implemented notes: Tensor count/names/shapes preserved across format conversion - contract: model-format-conversion-v1.yaml equation: quantization_bounds - module_path: aprender::quantize - function: quantize_tensor + module_path: aprender::format + function: quantize_data status: implemented notes: Error bounded by dtype-specific tolerance - contract: model-format-conversion-v1.yaml equation: import_integrity - module_path: aprender::import + module_path: apr_cli::commands::import function: run status: implemented notes: Content-based format detection, not extension-based - contract: model-format-conversion-v1.yaml equation: export_fidelity - module_path: aprender::export + module_path: apr_cli::commands::export function: run status: implemented notes: Atomic write via temp file + rename - contract: model-format-conversion-v1.yaml equation: apr_tokenizer_embedding - module_path: aprender::convert + module_path: aprender::format::converter function: save_model_tensors_with_gguf_config_and_tokenizer status: implemented notes: "P0 invariant \u2014 every APR file embeds tokenizer (PMAT-154 fix, Q4K passthrough/fallback paths)" - contract: apr-cli-operations-v1.yaml equation: side_effect_classification - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command status: implemented notes: All 48 commands classified as ReadOnly/Mutating/LongRunning - contract: apr-cli-operations-v1.yaml equation: resource_cleanup - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command status: implemented notes: GPU/tmp/thread cleanup on all exit paths via RAII @@ -297,36 +465,36 @@ bindings: equation: inference_determinism module_path: aprender::inference function: generate - status: implemented + status: not_implemented notes: temperature=0 greedy decoding is deterministic - contract: apr-cli-operations-v1.yaml equation: tokenizer_consistency - module_path: aprender::tokenizer - function: encode + module_path: aprender::text::tokenize::bpe_impl + function: BpeTokenizer::encode status: implemented notes: BPE encode/decode roundtrip for valid UTF-8 - contract: apr-cli-operations-v1.yaml equation: concurrent_model_access - module_path: aprender::serve + module_path: apr_cli::commands::serve function: run status: implemented notes: Per-request KV cache, immutable model weights - contract: apr-cli-v1.yaml equation: contract_gate_enforcement - module_path: apr_cli::dispatch + module_path: apr_cli function: execute_command status: implemented notes: "PMAT-237 contract gate \u2014 extract_model_paths + validate_model_contract before dispatch" - contract: apr-cli-v1.yaml equation: training_plan_apply_semantics module_path: apr_cli::commands::train - function: train::run + function: run_apply status: implemented notes: Plan is pure (no GPU/filesystem), Apply writes to --output directory only - contract: apr-cli-v1.yaml equation: tokenizer_training_correctness module_path: apr_cli::commands::tokenize - function: tokenize::run + function: run_apply status: implemented notes: "BPE plan/apply \u2014 vocab.json size equals requested vocab_size" - contract: apr-cli-v1.yaml @@ -392,20 +560,20 @@ bindings: - contract: apr-data-pipeline-v1.yaml equation: data_validation module_path: apr_cli::commands::data - function: run + function: run_audit status: implemented notes: JSONL/CSV/text validation with line-level error reporting - contract: apr-data-pipeline-v1.yaml equation: data_split_determinism module_path: apr_cli::commands::data - function: run + function: run_split status: implemented notes: Seed-deterministic train/val/test split - contract: apr-data-pipeline-v1.yaml equation: streaming_data_loader module_path: aprender::data::dataloader function: DataLoader::next_batch - status: implemented + status: not_implemented notes: Epoch-seeded shuffle, no sample drop on final partial batch - contract: apr-model-qa-v1.yaml equation: model_integrity_check @@ -440,26 +608,26 @@ bindings: - contract: apr-architecture-schema-v1.yaml equation: architecture_config_invariants module_path: aprender::format::gguf::api - function: GgufModelConfig::validate + function: GgufModelConfig::warn_out_of_bounds status: implemented notes: Validates hidden_size/num_heads divisibility, GQA ratio, MoE fields - contract: apr-architecture-schema-v1.yaml equation: rope_position_encoding - module_path: aprender::nn::rope - function: RotaryPositionEmbedding::validate_config + module_path: aprender::nn::transformer::attention_helpers + function: RotaryPositionEmbedding::with_base status: implemented notes: RoPE theta, type (NORM vs NEOX), frequency vector length - contract: apr-format-safety-v1.yaml equation: magic_byte_validation module_path: aprender::gguf::reader function: detect_format - status: implemented + status: not_implemented notes: GGUF/SafeTensors/APR magic byte prefix detection, panic-free on truncated input - contract: apr-format-safety-v1.yaml equation: header_integrity module_path: aprender::gguf::reader function: validate_header - status: implemented + status: not_implemented notes: Bounded allocation, no OOM from crafted tensor_count - contract: apr-chat-session-v1.yaml equation: session_state_machine @@ -469,14 +637,14 @@ bindings: notes: Init->WaitInput->Generating->WaitInput->Exit state machine - contract: apr-chat-session-v1.yaml equation: session_persistence - module_path: apr_cli::commands::chat_session + module_path: apr_cli::commands::chat function: run status: implemented notes: JSON serialization, KV-cache not persisted (rebuilt from history) # --- cli-dispatch-v1 bindings (PMAT-495) --- - contract: cli-dispatch-v1.yaml equation: dispatch_completeness - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command signature: 'fn dispatch_core_command(cli: &Cli) -> Option>' status: implemented @@ -490,21 +658,21 @@ bindings: notes: Distinct error classes map to distinct non-zero exit codes (0-11) - contract: cli-dispatch-v1.yaml equation: output_format_fidelity - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command signature: 'fn dispatch_core_command(cli: &Cli) -> Option>' status: implemented notes: --json/--yaml/--csv produce parseable output per respective RFC - contract: cli-dispatch-v1.yaml equation: feature_gated_dispatch - module_path: apr_cli::dispatch_analysis + module_path: apr_cli function: dispatch_extended_command signature: 'fn dispatch_extended_command(cli: &Cli) -> Result<(), CliError>' status: implemented notes: Code variant dispatches to batuta::agent::code::cmd_code (PMAT-182) - contract: cli-dispatch-v1.yaml equation: idempotent_inspection - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_inspection_commands signature: 'fn dispatch_inspection_commands(cli: &Cli) -> Option>' status: implemented @@ -666,7 +834,7 @@ bindings: module_path: aprender::inference function: generate signature: 'fn generate(&mut self, prompt_tokens: &[u32], max_tokens: usize, ...) -> Vec' - status: implemented + status: not_implemented notes: temperature=0 produces deterministic greedy output on GPU - contract: apr-gpu-backend-v1.yaml equation: gpu_cpu_parity @@ -685,21 +853,21 @@ bindings: # --- tokenizer-loading-v1 bindings (PMAT-495) --- - contract: tokenizer-loading-v1.yaml equation: identity - module_path: aprender::format::converter::tokenizer_loader + module_path: aprender::format::converter::import function: load_tokenizer_from_explicit_path signature: 'fn load_tokenizer_from_explicit_path(path: &Path) -> Result' status: implemented notes: Loads BPE tokenizer from tokenizer.json or vocab.json - contract: tokenizer-loading-v1.yaml equation: roundtrip_encoding - module_path: aprender::tokenizer - function: encode + module_path: aprender::text::tokenize::bpe_impl + function: BpeTokenizer::encode signature: 'fn encode(&self, text: &str) -> Vec' status: implemented notes: decode(encode(text)) == text for valid UTF-8 - contract: tokenizer-loading-v1.yaml equation: byte_encoder_coverage - module_path: apr_cli::commands::chat_load_tokenizers + module_path: apr_cli::commands::chat::realizar_chat function: load_tokenizers signature: 'fn load_tokenizers(...) -> Result' status: implemented @@ -721,22 +889,22 @@ bindings: notes: KV projection shape [hidden_size, kv_heads * head_dim] - contract: qwen2-weight-loading-v1.yaml equation: swiglu_expansion - module_path: aprender::models::qwen2::constructors - function: new + module_path: aprender::models::qwen2 + function: Qwen2Model::new signature: 'pub fn new(config: &Qwen2Config) -> Self' status: implemented notes: FFN intermediate = 2/3 * 4 * hidden_size (SwiGLU) - contract: qwen2-weight-loading-v1.yaml equation: total_parameters - module_path: aprender::models::qwen2::constructors - function: weight_names + module_path: aprender::models::qwen2 + function: Qwen2Model::weight_names signature: 'fn weight_names() -> Vec' status: implemented notes: Total params matches HuggingFace config.json num_parameters # --- apr-cli-mutating-v1 bindings (GH-689) --- - contract: apr-cli-mutating-v1.yaml equation: output_path_validation - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_model_commands signature: 'fn dispatch_model_commands(cli: &Cli) -> Option>' status: implemented @@ -757,7 +925,7 @@ bindings: notes: Temp file + rename pattern for atomic writes - contract: apr-cli-mutating-v1.yaml equation: rm_confirmation_gate - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_model_commands signature: 'fn dispatch_model_commands(cli: &Cli) -> Option>' status: implemented @@ -765,14 +933,14 @@ bindings: # --- apr-cli-readonly-v1 bindings (GH-688) --- - contract: apr-cli-readonly-v1.yaml equation: no_side_effects - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_inspection_commands signature: 'fn dispatch_inspection_commands(cli: &Cli) -> Option>' status: implemented notes: 28 readonly commands never modify filesystem or model state - contract: apr-cli-readonly-v1.yaml equation: idempotent_output - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_inspection_commands signature: 'fn dispatch_inspection_commands(cli: &Cli) -> Option>' status: implemented @@ -809,8 +977,8 @@ bindings: # --- encoder-forward-v1 bindings (GH-326, BERT inference) --- - contract: ../encoder-forward-v1.yaml equation: encoder_layer - module_path: aprender::models::bert - function: forward + module_path: aprender::models::bert::layer + function: BertLayer::forward signature: 'fn forward(&self, input: &Tensor) -> Tensor' status: implemented notes: "BERT encoder layer: LayerNorm(x + BiAttn(x)) → LayerNorm(h + FFN(h))" @@ -819,7 +987,7 @@ bindings: module_path: aprender::models::bert function: cls_embedding signature: 'fn cls_embedding(&self, encoder_output: &Tensor) -> Tensor' - status: implemented + status: not_implemented notes: "CLS pooling: encoder_output[0] (first token embedding)" # --- format-parity-v1 bindings (model format conversion) --- - contract: ../format-parity-v1.yaml @@ -827,19 +995,19 @@ bindings: module_path: aprender::format::layout function: swap_axes signature: 'fn swap_axes(tensor: &Tensor) -> Tensor' - status: implemented + status: not_implemented notes: "swap(swap(shape)) == shape — transpose is its own inverse" - contract: ../format-parity-v1.yaml equation: element_count module_path: aprender::format::layout function: validate_element_count signature: 'fn validate_element_count(gguf_shape: &[usize], apr_shape: &[usize]) -> bool' - status: implemented + status: not_implemented notes: product(gguf_shape) == product(apr_shape) across format conversion - contract: ../format-parity-v1.yaml equation: name_bijection - module_path: aprender::format::converter - function: map_tensor_name + module_path: aprender::format::converter_types + function: Architecture::map_name signature: 'fn map_tensor_name(name: &str, arch: Architecture) -> String' status: implemented notes: "Bijective tensor name mapping: GGUF ↔ APR ↔ SafeTensors" @@ -849,7 +1017,7 @@ bindings: module_path: aprender::nn::transformer function: bidirectional_attention signature: 'fn bidirectional_attention(q: &Tensor, k: &Tensor, v: &Tensor) -> Tensor' - status: implemented + status: not_implemented notes: Full attention matrix (no causal mask) for BERT-class models # ============================================================================ # Phase 2 bindings — contrastive-pair-protocol-v1 (24 equations) and @@ -959,7 +1127,7 @@ bindings: module_path: entrenar::train::setfit::baseline function: fit signature: 'fn fit(self) -> Result<(MultinomialLogisticRegression, FrozenProbeReport), SetFitTrainError>' - status: implemented + status: not_implemented notes: 'FrozenProbeRun — a separate type that is NOT a SetFitRun state and has no conversion to one. Encodes the selected rows in eval mode inside autograd::no_grad with no tuning step, fits MultinomialLogisticRegression on the frozen embeddings, and reports kind @@ -1030,7 +1198,7 @@ bindings: equation: doc_bundle_bijection module_path: entrenar::train::setfit::apr_codec function: deserialize - status: implemented + status: not_implemented notes: 'Lands with plan 04-05. The closure equation serialize(deserialize(bytes)) == bytes at Tolerance::EXACT; depends on bundle field 20 (ProvenanceRecord) from 04-13.' - contract: setfit-apr-v1.yaml equation: canonical_tensor_names @@ -1104,7 +1272,7 @@ bindings: equation: selection_lock_lifecycle module_path: entrenar::train::setfit::lock function: mint_test_token - status: implemented + status: not_implemented notes: 'The durable lock artifact and the CLI workflow land with 04-07 / 04-14 (review B4); the underlying lock, token minting and grant already exist from Phase 3 and are consumed as shipped.' - contract: setfit-apr-v1.yaml equation: d02_typed_key_amendment diff --git a/contracts/batuta/binding.yaml b/contracts/batuta/binding.yaml new file mode 100644 index 0000000000..92009766a1 --- /dev/null +++ b/contracts/batuta/binding.yaml @@ -0,0 +1,316 @@ +version: 1.0.0 +target_crate: batuta +critical_path: +- transpile +- analyze +- build_transpiler +- recommend_transpiler +- cmd_transpile +bindings: +- contract: builder-pattern-v1.yaml + equation: transpile + function: build_transpiler_args + module_path: batuta::src::cli::mod::build_transpiler_args + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: cmd_transpile + module_path: batuta::src::cli::pipeline_cmds_transpile::cmd_transpile + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: recommend_transpiler + module_path: batuta::types::recommend_transpiler + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: has_transpiler + module_path: batuta::tools::has_transpiler + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: get_transpiler_for_language + module_path: batuta::tools::get_transpiler_for_language + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: transpile_python + module_path: batuta::tools::transpile_python + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: transpile_shell + module_path: batuta::tools::transpile_shell + status: implemented +- contract: configuration-v1.yaml + equation: transpile + function: transpile_c_cpp + module_path: batuta::tools::transpile_c_cpp + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_numpy_usage + module_path: batuta::pipeline_analysis::analyze_numpy_usage + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_sklearn_usage + module_path: batuta::pipeline_analysis::analyze_sklearn_usage + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_pytorch_usage + module_path: batuta::pipeline_analysis::analyze_pytorch_usage + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_contract_gaps + module_path: batuta::bug_hunter::contracts::analyze_contract_gaps + status: implemented +- contract: linear-models-v1.yaml + equation: analyze + function: analyze_model_parity_gaps + module_path: batuta::bug_hunter::model_parity::analyze_model_parity_gaps + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze + module_path: batuta::recipes::benchmark::analyze + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_optimizations + module_path: batuta::pipeline::stages::optimization::analyze_optimizations + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: cmd_analyze + module_path: batuta::src::cli::pipeline_cmds::cmd_analyze + status: implemented +- contract: gpu-decode-profiling-v1.yaml + equation: analyze + function: analyze_code + module_path: batuta::wasm::analyze_code + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_quality + module_path: batuta::tools::analyze_quality + status: implemented +- contract: configuration-v1.yaml + equation: analyze + function: analyze_dependencies + module_path: batuta::parf::analyze_dependencies + status: implemented +- contract: fused-qkv-projection-v1.yaml + equation: analyze + function: analyze_project + module_path: batuta::analyzer::analyze_project + status: implemented +# --- CCX-RS-inspired features (2026-04-02) --- +- contract: provider-routing-v1.yaml + equation: privacy_enforcement + function: route_to_provider + module_path: batuta::agent::driver::routing::route_to_provider + status: pending +- contract: provider-routing-v1.yaml + equation: failover_cascade + function: failover_next + module_path: batuta::agent::driver::routing::failover_next + status: pending +- contract: provider-routing-v1.yaml + equation: cost_budget + function: estimate_cost + module_path: batuta::agent::driver::remote::estimate_cost + status: pending +- contract: provider-routing-v1.yaml + equation: backoff_jitter + function: delay_for + module_path: batuta::agent::driver::remote::backoff::delay_for + status: pending +- contract: provider-routing-v1.yaml + equation: format_translation + function: to_openai + module_path: batuta::agent::driver::remote::translator::to_openai + status: pending +- contract: agent-loop-v1.yaml + equation: loop_termination + function: run_loop + module_path: batuta::agent::run_loop + status: pending +- contract: agent-loop-v1.yaml + equation: state_machine + function: transition + module_path: batuta::agent::state::transition + status: pending +- contract: agent-loop-v1.yaml + equation: context_compaction + function: compact + module_path: batuta::agent::context::compact + status: pending +- contract: agent-loop-v1.yaml + equation: sandbox_enforcement + function: check_sandbox + module_path: batuta::agent::sandbox::check_sandbox + status: pending +- contract: agent-loop-v1.yaml + equation: parallel_tool_safety + function: execute_all + module_path: batuta::agent::tool::execute_all + status: pending +- contract: agent-loop-v1.yaml + equation: session_crash_recovery + function: resume + module_path: batuta::agent::session::resume + status: pending +- contract: agent-loop-v1.yaml + equation: hook_ordering + function: execute_with_hooks + module_path: batuta::agent::hook::execute_with_hooks + status: pending +# --- Presentar/Probar UX contracts (2026-04-02) --- +- contract: agent-ux-v1.yaml + equation: streaming_responsiveness + function: push_token + module_path: batuta::agent::tui::streaming::push_token + status: pending +- contract: agent-ux-v1.yaml + equation: frame_budget + function: render_frame + module_path: batuta::agent::tui::layout::render_frame + status: pending +- contract: agent-ux-v1.yaml + equation: brick_verification + function: can_render + module_path: batuta::agent::brick::can_render + status: pending +- contract: agent-ux-v1.yaml + equation: cost_display_accuracy + function: update_cost + module_path: batuta::agent::tui::cost::update_cost + status: pending +- contract: agent-ux-v1.yaml + equation: layout_correctness + function: compute_layout + module_path: batuta::agent::tui::layout::compute_layout + status: pending +- contract: agent-ux-v1.yaml + equation: state_machine_validity + function: transition + module_path: batuta::agent::state::transition + status: pending +# --- apr code contracts (2026-04-02) --- +- contract: apr-code-v1.yaml + equation: sovereignty_guarantee + function: enforce_offline + module_path: batuta::agent::sandbox::enforce_offline + status: pending +- contract: apr-code-v1.yaml + equation: tool_safety + function: execute_tool_checked + module_path: batuta::agent::tool::execute_tool_checked + status: pending +- contract: apr-code-v1.yaml + equation: session_integrity + function: resume_session + module_path: batuta::agent::session::resume_session + status: pending +- contract: apr-code-v1.yaml + equation: apr_md_compliance + function: load_apr_md + module_path: batuta::agent::config::load_apr_md + status: pending +- contract: apr-code-v1.yaml + equation: startup_latency + function: run_apr_code + module_path: batuta::agent::run_apr_code + status: pending +# --- Oracle CLI contracts (2026-04-03) --- +- contract: cli-oracle-v1.yaml + equation: dispatch_correctness + function: dispatch_oracle + module_path: batuta::cli::oracle::dispatch_oracle + status: pending + notes: Every oracle subcommand variant has a dispatch handler +- contract: cli-oracle-v1.yaml + equation: rag_query_correctness + function: rag_search + module_path: batuta::cli::oracle::rag::rag_search + status: pending + notes: RAG results sorted by BM25 score descending, empty query returns empty +- contract: cli-oracle-v1.yaml + equation: index_freshness + function: check_freshness + module_path: batuta::cli::oracle::rag_index::check_freshness + status: pending + notes: Stale index warns but proceeds, missing index triggers auto-build +# --- HTTP API contracts (2026-04-04, PMAT-189) --- +- contract: http-api-v1.yaml + equation: body_schema_compliance + function: build_openai_body + module_path: batuta::agent::driver::apr_serve::build_openai_body + status: implemented + notes: AprServeDriver builds OpenAI-compatible JSON request bodies +- contract: http-api-v1.yaml + equation: max_tokens_cap + function: build_openai_body + module_path: batuta::agent::driver::apr_serve::build_openai_body + status: implemented + notes: max_tokens capped at 1024 (PMAT-170) +- contract: http-api-v1.yaml + equation: thinking_block_strip + function: strip_thinking_blocks + module_path: batuta::agent::driver::apr_serve::strip_thinking_blocks + status: implemented + notes: Removes ... from Qwen3 responses (PMAT-180) +# --- Session contracts (2026-04-04, PMAT-189) --- +- contract: session-v1.yaml + equation: jsonl_roundtrip + function: append_messages + module_path: batuta::agent::session::append_messages + status: implemented + notes: JSONL append-only persistence +- contract: session-v1.yaml + equation: manifest_serde + function: new + module_path: batuta::src::agent::session::Session::new + status: implemented + notes: Session manifest serialization +- contract: session-v1.yaml + equation: age_filter + function: find_recent_for_cwd + module_path: batuta::agent::session::find_recent_for_cwd + status: implemented + notes: 24h age filter (PMAT-165) +# --- Tokenizer contracts (2026-04-04, PMAT-189) --- +- contract: tokenizer-v1.yaml + equation: deterministic_encode + function: encode + module_path: batuta::src::agent::driver::validate::tokenizer_encode + status: implemented + notes: Tokenizer used for context window management +- contract: tokenizer-v1.yaml + equation: vocab_size_bound + function: encode + module_path: batuta::src::agent::driver::validate::tokenizer_encode + status: implemented + notes: Token IDs must be within vocabulary bounds +# --- Model discovery contracts (2026-04-04, PMAT-188) --- +- contract: apr-model-discovery-v1.yaml + equation: search_order + function: discover_model + module_path: batuta::agent::manifest::discover_model + status: implemented + notes: ~/.apr/models/ > ~/.cache/huggingface/ > ./models/ +- contract: apr-model-discovery-v1.yaml + equation: sort_priority + function: discover_model + module_path: batuta::agent::manifest::discover_model + status: implemented + notes: valid > mtime > APR (PMAT-185) +- contract: apr-model-discovery-v1.yaml + equation: jidoka_validation + function: is_valid_model_file + module_path: batuta::agent::driver::validate::is_valid_model_file + status: implemented + notes: Invalid APR deprioritized behind valid GGUF (PMAT-150) diff --git a/contracts/bin-apr-cli--apr-http-mcp-v1.yaml b/contracts/bin-apr-cli--apr-http-mcp-v1.yaml new file mode 100644 index 0000000000..fe240e4e67 --- /dev/null +++ b/contracts/bin-apr-cli--apr-http-mcp-v1.yaml @@ -0,0 +1,183 @@ +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# bin-apr-cli--apr-http-mcp-v1 — the `apr` bin target of package apr-cli (ONT-10 slice S8), drafted to the exemplar +# contracts/bin-aprender-contracts-cli--pv-v1.yaml over the binary-surface-v1 vocabulary (#4476, #4079, aprender#4502). +# +# package `apr-cli` exposes (ONT-001 v4.17 ONT-4g; #4476, #4079). Identity and every CLI command are S9, +# binary-apr-cli-apr-v1.yaml; the split is by KIND (S9 header): set equality over bin:command cannot be halved. +# +# MEASURED (2026-09-26, batch/ont-10 @ ff9587b596, release build, default features, private CARGO_TARGET_DIR): +# MCP: `apr mcp` (stdio) answers tools/list with 9 tools, EQUAL to surface_audit.csv's 9 `mcp:apr.*` rows. +# HTTP: `apr serve` mounts one of several routers by model format (#3979): aprender-serve api/router.rs +# route_table (GGUF; its openai routes add POST /v1/batch/completions only when caps.gpu_batch) and the apr-cli +# serve/ routers (APR-CPU, SafeTensors, the ollama stubs). The union of their ("METHOD", "/path") tables plus +# the `GET /` index each serves is 43 routes, EQUAL to the 43 distinct routes of the 47 HTTP ledger rows (the +# other 4 rows are stream:true modes of a route already counted: POST /api/chat, /api/generate, +# /v1/chat/completions, /v1/completions). 2 of the 43 — POST /v1/logprobs, POST /v1/perplexity — are in +# cuda_routes(), behind #[cfg(feature = "cuda")], so the DEFAULT build mounts 41. Their 2 ledger rows are +# bin:ledgerOrphanRoute on a default snapshot (LIVE RED), the same class as S9's 15 feature-gated command rows. +# They are NOT a ledger defect: they are the only 2 HTTP rows whose verified_hardware is `nvidia-cuda` alone +# (every CPU-servable row reads `x86_64-linux[;nvidia-cuda]`), so the ledger already declares them cuda-only. +# dogfood_reconcile.py scopes HTTP rows out, so its FEATURE_GATED set cannot carry them; the RED clears when +# extract:binary honours the declared gate (snapshot the --features cuda router, or treat a cuda-only +# verified_hardware row as an enumerated allowance) — never by a count tolerance, never by deleting the rows. +# The 10th non-CLI row, "HTTP 404 fallback route index", is a behaviour of every router (the fallback lists +# `routes`), shaped here as bin:notFoundListsRoutes. +# +# GAP THIS DRAFT ASKS OF extract:binary (ONT-4g, aprender-98): scripts/dogfood_surfaces.sh strips the METHOD +# from both enumerations (enumerate_routes_live keeps the path only; enumerate_routes greps paths only), and its +# source fallback reads crates/aprender-serve/src/api/ only, so GET /tensors, POST /api/pull and DELETE +# /api/delete (apr-cli serve/) are invisible to it. bin:route here is "METHOD /path", the union over formats. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +name: bin-apr-cli--apr-http-mcp-v1 +version: "1.0.0" +scope: > + The HTTP and MCP surface of the `apr` binary (package apr-cli, src crates/apr-cli/src/main.rs): the routes `apr serve` can mount, the tools `apr mcp` lists, and their surface-audit rows. Identity, help and the CLI command set are S9, bin-apr-cli--apr-v1. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + The HTTP and MCP surface of the `apr` binary (package apr-cli, src crates/apr-cli/src/main.rs): the routes `apr serve` can mount, the tools `apr mcp` lists, and their surface-audit rows. Identity, help and the CLI command set are S9, bin-apr-cli--apr-v1. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-serve/src/api/router.rs — route_table, cuda_routes' + - 'crates/apr-cli/src/commands/serve/ — the APR-CPU, SafeTensors and ollama-stub routers' + - 'crates/aprender-mcp/src/tools/ — the 9 MCP tools' + - 'docs/audits/surface_audit.csv — the ledger' + - 'aprender#4502 — ONT-10, slice S8' + +entity: + type: binary + ref: binary/apr-cli/apr + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-apr-http-mcp + targetClass: bin:target/apr-cli/apr + properties: + - path: bin:route + minCount: 41 + maxCount: 41 + in: + - "DELETE /api/delete" + - "GET /" + - "GET /api/tags" + - "GET /api/version" + - "GET /health" + - "GET /health/live" + - "GET /health/ready" + - "GET /metrics" + - "GET /metrics/dispatch" + - "GET /models" + - "GET /ready" + - "GET /realize/model" + - "GET /tensors" + - "GET /v1/audit/:request_id" + - "GET /v1/effective-config" + - "GET /v1/gpu/status" + - "GET /v1/metrics" + - "GET /v1/models" + - "POST /api/chat" + - "POST /api/embeddings" + - "POST /api/generate" + - "POST /api/pull" + - "POST /api/show" + - "POST /batch/generate" + - "POST /batch/tokenize" + - "POST /generate" + - "POST /metrics/dispatch/reset" + - "POST /realize/batch" + - "POST /realize/embed" + - "POST /realize/generate" + - "POST /realize/reload" + - "POST /stream/generate" + - "POST /tokenize" + - "POST /v1/batch/completions" + - "POST /v1/chat/completions" + - "POST /v1/chat/completions/stream" + - "POST /v1/completions" + - "POST /v1/embeddings" + - "POST /v1/explain" + - "POST /v1/gpu/warmup" + - "POST /v1/predict" + # LIVE RED 2026-09-26 on a default snapshot: POST /v1/logprobs and POST /v1/perplexity mount only under --features cuda; + # their ledger rows declare it (verified_hardware = nvidia-cuda, router.rs:222-223 cuda_routes) + - {path: bin:unledgeredRoute, maxCount: 0} + - {path: bin:ledgerOrphanRoute, maxCount: 0} + - path: bin:mcpTool + minCount: 9 + maxCount: 9 + in: ["apr.bench", "apr.finetune", "apr.qa", "apr.run", "apr.serve", "apr.tensors", "apr.trace", "apr.validate", "apr.version"] + - {path: bin:unledgeredTool, maxCount: 0} + +equations: + route_set: + formula: "routes(snapshot(apr)) = GET / ∪ the (METHOD, /path) tables of every `apr serve` router in the default-feature build, over every model format and runtime capability" + domain: "aprender-serve api/router.rs route_table (cuda off) and the apr-cli commands/serve/ Indexed routers" + codomain: "set equality (41 routes on 2026-09-26), or a Fail naming each route in the symmetric difference" + invariants: + - "a ledger row's trailing ` (stream:...)` mode is stripped before the join: 47 HTTP rows -> 43 routes" + - "a --features cuda build mounts 43; this shape is over the default build, as S9's command set is" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "each route is written \"METHOD /path\", axum :param segments verbatim" + lean_theorem: none — L4 not declared + mcp_tool_set: + formula: "mcpTools(snapshot(apr)) = names(tools/list of `apr mcp`) = {f.feature minus the mcp: prefix : f ∈ surface_audit, f.binary = apr, f.feature starts with mcp:}" + domain: "a live tools/list over stdio and the 9 mcp:apr.* ledger rows" + codomain: "set equality (9 tools on 2026-09-26)" + invariants: + - "S9 joins the CLI rows; this contract joins only HTTP and mcp:* rows, so no row is judged twice" + preconditions: + - "tools/list was read from the binary built at this commit" + postconditions: + - "an mcp:* row naming no listed tool is a bin:ledgerOrphan" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APR_HTTP_MCP-INV-001 + property: every route and MCP tool apr exposes has an audited ledger row + formal: '∀ x ∈ routes(apr) ∪ mcpTools(apr): |{r ∈ surface_audit : r.binary = apr ∧ strip_mode(r.feature) ∈ {x, "mcp:" ++ x}}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APR_HTTP_MCP-001 + rule: 'G1.2 help answers' + prediction: '`apr --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APR_HTTP_MCP-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`apr --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APR_HTTP_MCP-003 + rule: 'G0.1 the version names the commit' + prediction: '`apr --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: "GREEN 2026-09-26: `apr 0.69.3 (ff9587b596)` — the only binary in S2/S8/S20 that names its commit" + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APR_HTTP_MCP-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the enumerated surface of apr equals the in: list of this shape, no more and no fewer' + test: 'pv lint contracts/ --gate shapes, once extract:binary writes the node' + if_fails: "GREEN 2026-09-26: 41 default-build routes and 9 tools, measured from the router tables and a live tools/list" + mutation: 'add a subcommand/route/tool without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APR_HTTP_MCP-005 + rule: 'ledger rows exist and resolve' + prediction: "docs/audits/surface_audit.csv has ≥1 row per route and per MCP tool with binary = apr (47 HTTP + 9 mcp on 2026-09-26)" + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: "LIVE RED 2026-09-26 on a default-feature snapshot: 2 ledger rows (POST /v1/logprobs, POST /v1/perplexity) name cuda-only routes; the 9 mcp rows and the other 41 routes join. Not a ledger defect: both rows declare verified_hardware = nvidia-cuda (the only cuda-only HTTP rows), so the RED clears when extract:binary honours that declared gate, as S12 and S9/S10 clear via FEATURE_GATED" + mutation: 'delete every apr row; must turn RED naming apr' + diff --git a/contracts/bin-apr-cli--apr-v1.yaml b/contracts/bin-apr-cli--apr-v1.yaml new file mode 100644 index 0000000000..6fc944e2f4 --- /dev/null +++ b/contracts/bin-apr-cli--apr-v1.yaml @@ -0,0 +1,397 @@ +# ────────────────────────────────────────────── +# bin-apr-cli--apr-v1 — the `apr` bin target of package apr-cli (ONT-10 slice S9; S8 adds routes + tools here) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# WHY S8/S9 SPLIT BY KIND: `in` + minCount + maxCount on bin:command is set equality over the WHOLE node, and pv's +# shapes subset refuses the qualifiedValueShape family (crates/aprender-contracts/src/ontology/shapes.rs), so a +# command set cannot be halved. S9 owns identity + every CLI command. S8 (docs/ont-10/binary-contracts/ +# binary-apr-cli-2of2-v1.yaml) owns bin:route and bin:mcpTool of the same node. When S8 moves it appends its +# properties to the ONE shape below; it does not add a second file. +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, release build, default features, private CARGO_TARGET_DIR): +# `apr --version` = "apr 0.69.3 (b1cd8c0b08)": G0.1 GREEN. +# `apr --help` exits 0; `apr --no-such-flag` exits 2; `apr qa --format nosuch x` exits 2. +# Recursive --help walk: 262 leaves (clap's auto `help` excluded) + 2 runnable non-leaves = 264 commands: +# `debug` (Usage shows [COMMAND]) and `sim help` (a real subcommand, "Show help"). +# surface_audit.csv rows with binary=apr: 375 = 318 CLI + 47 HTTP + 10 other. The 318 CLI rows reduce to 267 +# command paths (options stripped). Join against the 264, LIVE RED: +# 15 ledgerOrphan, all feature-gated off in the default build: +# data x doctest extract|merge, data x hub push, data x import hf (aprender-data features doctest / hf-hub) +# mono archive|audit|publish|shims (apr-cli feature `dev`) +# rag eval compare|gate|generate|judge|metrics|retrieve|sample (aprender-rag-cli feature `eval`) +# 12 unledgeredCommand: capability, pv census, pv challenge check|gen, +# pv discharge check|gen-axioms|label-ratchet|run, pv extract, pv obligations, pv ontology export|tbox. +# FIXED 2026-09-27: all 12 now have binary=apr ledger rows (capability by ONT-4g #4476; the 11 `apr pv` mirrors by ONT-10 S9/S7). +# +# CONVENTION ASKED OF extract:binary (ONT-4g, aprender-98): (1) a non-leaf whose Usage shows `[COMMAND]` is also a +# bin:command; (2) only clap's auto-generated `help` is excluded, a user-defined `help` is a command; (3) the +# ledger's binary column says `apr` for BOTH apr targets (apr-cli:apr, aprender:apr), so both nodes join the same +# rows. G0.5 (the two apr targets' help equal) spans two nodes; it stays with the surface_audit_bins gate. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-apr-cli--apr-v1 +version: "1.0.0" +scope: > + The command surface of the `apr` binary of package apr-cli (src crates/apr-cli/src/main.rs): its help, its usage + errors, its version string, its exact CLI command set, and its surface-audit rows. HTTP routes and MCP tools are + slice S8. Out of scope: what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + apr (apr-cli) answers --help, rejects an unknown flag as a usage error, names the commit it was built from, + exposes exactly the 264 commands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/apr-cli/src/lib.rs — the clap Commands enum' + - 'docs/audits/surface_audit.csv — the rows whose binary column is apr' + - 'aprender#4502 — ONT-10, slices S8/S9' + +entity: + type: binary + ref: binary/apr-cli/apr + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-apr-cli-apr-surface + targetClass: bin:target/apr-cli/apr + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["apr"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["apr-cli"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^apr [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-26 ("apr 0.69.3 (b1cd8c0b08)"); kept a warning to match the exemplar + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 264 + maxCount: 264 + in: + - "attn-parity-lint" + - "attn-viz-lint" + - "audio-inspect-lint" + - "awq-lint" + - "beat-run" + - "bench" + - "canary check" + - "canary create" + - "capability" + - "cbtop" + - "cgp baseline" + - "cgp bench" + - "cgp compete" + - "cgp contract generate" + - "cgp contract verify" + - "cgp diff" + - "cgp doctor" + - "cgp explain" + - "cgp profile binary" + - "cgp profile compare" + - "cgp profile cublas" + - "cgp profile kernel" + - "cgp profile library" + - "cgp profile metal" + - "cgp profile parallel" + - "cgp profile python" + - "cgp profile quant" + - "cgp profile scalar" + - "cgp profile scaling" + - "cgp profile simd" + - "cgp profile wasm" + - "cgp profile wgpu" + - "cgp roofline" + - "cgp trace" + - "cgp tui" + - "chat" + - "check" + - "check-finite-lint" + - "code" + - "compare-hf" + - "compile" + - "convert" + - "data audit" + - "data balance" + - "data decontaminate" + - "data dedup" + - "data split" + - "data x convert" + - "data x dedup" + - "data x drift compare" + - "data x drift detect" + - "data x drift merge" + - "data x drift report" + - "data x drift sketch" + - "data x fed manifest" + - "data x fed plan" + - "data x fed split" + - "data x fed verify" + - "data x filter-text" + - "data x fim" + - "data x head" + - "data x import local" + - "data x info" + - "data x mix" + - "data x quality check" + - "data x quality profiles" + - "data x quality report" + - "data x quality score" + - "data x registry delete" + - "data x registry init" + - "data x registry list" + - "data x registry pull" + - "data x registry push" + - "data x registry search" + - "data x registry show-info" + - "data x repl" + - "data x schema" + - "data x view" + - "dataset audio-inspect" + - "ddp-metrics-lint" + - "debug" + - "debug embed-viz" + - "decrypt" + - "devices" + - "diagnose" + - "diff" + - "distill" + - "dry-sampling-lint" + - "embed" + - "embed-viz-lint" + - "embeddings-lint" + - "encrypt" + - "eval" + - "experiment view" + - "explain" + - "explain-token-lint" + - "export" + - "finetune" + - "flow" + - "fp8-lint" + - "gbnf-lint" + - "gptq-lint" + - "gpu" + - "gpu-memtrace-lint" + - "grad-norm" + - "hang-trace-lint" + - "hex" + - "imatrix-lint" + - "import" + - "inspect" + - "kernel parity" + - "kv-timeline-lint" + - "lint" + - "list" + - "manifest" + - "mcp" + - "merge" + - "modelfile parse" + - "monitor" + - "nccl-diag-lint" + - "nf4-lint" + - "ollama-chat-lint" + - "ollama-tools-lint" + - "oom-lint" + - "oracle" + - "otlp-lint" + - "parity" + - "pipeline apply" + - "pipeline plan" + - "pipeline status" + - "pipeline validate" + - "ppl" + - "pretrain" + - "profile" + - "prometheus-lint" + - "prune" + - "ptx" + - "ptx-map" + - "publish" + - "pull" + - "pv audit" + - "pv book" + - "pv census" + - "pv certify" + - "pv challenge check" + - "pv challenge gen" + - "pv check-parity" + - "pv codegen" + - "pv coq" + - "pv coverage" + - "pv diff" + - "pv discharge check" + - "pv discharge gen-axioms" + - "pv discharge label-ratchet" + - "pv discharge run" + - "pv equations" + - "pv explain" + - "pv extract" + - "pv extract-pytorch" + - "pv flux" + - "pv fuzz" + - "pv generate" + - "pv graph" + - "pv infer" + - "pv invariants" + - "pv kaizen" + - "pv kani" + - "pv lean" + - "pv lean-status" + - "pv lint" + - "pv migrate" + - "pv mirai" + - "pv obligations" + - "pv ontology export" + - "pv ontology tbox" + - "pv pipeline" + - "pv probar" + - "pv proof-status" + - "pv query" + - "pv roofline" + - "pv scaffold" + - "pv score" + - "pv status" + - "pv tla" + - "pv unlock" + - "pv validate" + - "pv verify-bindings" + - "pv verify-pipeline" + - "pv verify-structure" + - "qa" + - "qualify" + - "quant-preservation-lint" + - "quantize" + - "rag demo" + - "rag extract-frames" + - "rag index" + - "rag info" + - "rag query" + - "rag transcribe" + - "react-trace-lint" + - "registry aliases" + - "registry-quota-lint" + - "rerank" + - "rm" + - "rm-gc-lint" + - "rosetta chain" + - "rosetta compare-inference" + - "rosetta convert" + - "rosetta diff-tensors" + - "rosetta fingerprint" + - "rosetta inspect" + - "rosetta validate-stats" + - "rosetta verify" + - "run" + - "runs diff" + - "runs ls" + - "runs show" + - "serve plan" + - "serve run" + - "shard" + - "shared-cache-lint" + - "showcase" + - "sim emc-check" + - "sim emc-validate" + - "sim help" + - "sim list-emc" + - "sim render" + - "sim run" + - "sim validate" + - "sim verify" + - "sim version" + - "stamp" + - "tensors" + - "test llm bench" + - "test tensor" + - "tokenize apply" + - "tokenize encode" + - "tokenize encode-corpus" + - "tokenize import-hf" + - "tokenize plan" + - "tokenize repair-manifest" + - "tokenize train" + - "tool-use-lint" + - "trace" + - "train apply" + - "train archive" + - "train cluster-status" + - "train halving" + - "train plan" + - "train submit" + - "train sweep" + - "train watch" + - "tree" + - "tui" + - "tune" + - "typical-p-lint" + - "unified-search-lint" + - "unshard" + - "validate" + - "validate-manifest" + - "zram benchmark" + - "zram create" + - "zram remove" + - "zram status" + # LIVE RED: 15 feature-gated ledger rows (header). The 12 unledgered commands are FIXED 2026-09-27 + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(apr)) = C, |C| = 264, C = leaves(--help walk) ∪ {non-leaves whose Usage shows [COMMAND]} ∪ {user-defined help} ∧ C = {path(f) : f ∈ surface_audit, f.binary = apr, f is a CLI row}" + domain: "the apr-cli:apr line of the tracked snapshot and the CLI ledger rows with binary = apr (318 rows, 267 paths)" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "in + minCount 264 + maxCount 264 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "a feature-gated command is a member only if the default build exposes it; its ledger row is an orphan until then" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "path(r) = r.feature minus the leading 'apr ', cut at the first token starting with -, <, [ or (" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APR-CLI-APR-INV-001 + property: every apr (apr-cli) command has an audited ledger row, and every CLI ledger row names a command + formal: '∀ c ∈ commands(apr): ∃ r ∈ surface_audit : r.binary = apr ∧ path(r) = c; ∀ r CLI row with r.binary = apr: path(r) ∈ commands(apr)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APR-CLI-APR-001 + rule: 'G1.2 help answers' + prediction: '`apr --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APR-CLI-APR-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`apr --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APR-CLI-APR-003 + rule: 'G0.1 the version names the commit' + prediction: '`apr --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale apr reads as HEAD (observed 2026-09-26: "apr 0.69.3 (b1cd8c0b08)", GREEN)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APR-CLI-APR-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: '`apr qa --format nosuch x` exits 2; commands(snapshot) = the 264 literals above' + test: 'NOT YET WRITTEN — pv lint contracts/ --gate shapes on the snapshot, after extract:binary (ONT-4g)' + if_fails: 'a leaves-only walk drops `debug` and `sim help`: minCount 264 RED and the `apr debug` row an orphan (observed 2026-09-26: --format exits 2, GREEN)' + mutation: 'remove `qa` from the snapshot; must turn RED on bin:command and bin:ledgerOrphan naming qa' + - id: FALSIFY-BIN-APR-CLI-APR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 CLI row per command with binary = apr (318 CLI rows on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'LIVE RED 2026-09-26: 15 rows orphaned by default-off features (data x doctest|hub|import hf, mono *, rag eval *)' + mutation: 'delete every apr row; must turn RED naming apr' diff --git a/contracts/bin-apr-cli--aprender-corpus-ingest-v1.yaml b/contracts/bin-apr-cli--aprender-corpus-ingest-v1.yaml new file mode 100644 index 0000000000..b1be05c385 --- /dev/null +++ b/contracts/bin-apr-cli--aprender-corpus-ingest-v1.yaml @@ -0,0 +1,116 @@ +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `apr-corpus-ingest` -> `aprender-corpus-ingest`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-apr-cli--aprender-corpus-ingest-v1 — the `aprender-corpus-ingest` bin target of package apr-cli (ONT-10 slice S20), drafted to the exemplar +# contracts/bin-aprender-contracts-cli--pv-v1.yaml over the binary-surface-v1 vocabulary (#4476, #4079, aprender#4502). +# +# +# MEASURED (2026-09-26, batch/ont-10 @ ff9587b596, release build, default features, LC_ALL=C recursive --help walk): +# - `aprender-corpus-ingest --version` = "aprender-corpus-ingest 0.69.3"; G0.1 (git sha) is a warning and RED today. +# - the walk gives 2 leaves (plan, validate-contract), set-equal to surface_audit.csv's 2 rows. +# - contracts/pretokenize-bin-v1.yaml names an `aprender-corpus-ingest run` subcommand that the binary does not have, +# and contracts/apr-mono-binary-rule-v1.yaml does not list this [[bin]] (both logged to the cop, 18:54Z). +# +# CONVENTION THIS DRAFT ASKS OF extract:binary (ONT-4g, aprender-98): nodes also typed bin:target// +# (no targetNode in pv's subset); commands as bin:command literals (full path below the binary name, space-joined); +# the csv join yields bin:ledgerOrphan / bin:unledgeredCommand. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +name: bin-apr-cli--aprender-corpus-ingest-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-corpus-ingest` binary (package apr-cli, src crates/apr-cli/src/bin/aprender-corpus-ingest.rs): its help, usage errors, version string, its 2 subcommands, and its surface-audit rows. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + The command surface of the `aprender-corpus-ingest` binary (package apr-cli, src crates/apr-cli/src/bin/aprender-corpus-ingest.rs): its help, usage errors, version string, its 2 subcommands, and its surface-audit rows. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/apr-cli/src/bin/aprender-corpus-ingest.rs — the clap Command enum' + - 'docs/audits/surface_audit.csv — the ledger' + - 'aprender#4502 — ONT-10, slice S20' + +entity: + type: binary + ref: binary/apr-cli/aprender-corpus-ingest + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-corpus-ingest-surface + targetClass: bin:target/apr-cli/aprender-corpus-ingest + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-corpus-ingest"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["apr-cli"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-corpus-ingest [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-26 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 2 + maxCount: 2 + in: + - "plan" + - "validate-contract" + # GREEN 2026-09-26: plan, validate-contract are set-equal to the 2 ledger rows + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-corpus-ingest)) = invocable paths of the recursive --help walk ∧ commands ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-corpus-ingest}" + domain: "the aprender-corpus-ingest line of the tracked snapshot, the clap tree of the default-feature build, and the ledger rows with binary = aprender-corpus-ingest" + codomain: "set equality (2 paths on 2026-09-26), or a Fail naming each path in the symmetric difference" + invariants: + - "a leaf is a command; a group whose Usage shows [COMMAND] is also one; clap's auto `help` is not" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-corpus-ingest --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APR_CORPUS_INGEST-INV-001 + property: every aprender-corpus-ingest subcommand has an audited ledger row + formal: '∀ c ∈ commands(aprender-corpus-ingest): |{r ∈ surface_audit : r.binary = aprender-corpus-ingest ∧ r.feature = c}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APR_CORPUS_INGEST-001 + rule: 'G1.2 help answers' + prediction: '`aprender-corpus-ingest --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-corpus-ingest' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APR_CORPUS_INGEST-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-corpus-ingest --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-corpus-ingest' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APR_CORPUS_INGEST-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-corpus-ingest --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: "LIVE RED 2026-09-26: `aprender-corpus-ingest 0.69.3` — no sha" + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APR_CORPUS_INGEST-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the enumerated surface of aprender-corpus-ingest equals the in: list of this shape, no more and no fewer' + test: 'pv lint contracts/ --gate shapes, once extract:binary writes the node' + if_fails: "GREEN on the walk 2026-09-26: 2 leaves" + mutation: 'add a subcommand/route/tool without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APR_CORPUS_INGEST-005 + rule: 'ledger rows exist and resolve' + prediction: "docs/audits/surface_audit.csv has ≥1 row per subcommand with binary = aprender-corpus-ingest (2 on 2026-09-26)" + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: "GREEN 2026-09-26: 2 rows, set-equal to the 2 commands" + mutation: 'delete every aprender-corpus-ingest row; must turn RED naming aprender-corpus-ingest' + diff --git a/contracts/bin-aprender--apr-http-mcp-v1.yaml b/contracts/bin-aprender--apr-http-mcp-v1.yaml new file mode 100644 index 0000000000..806b3239bf --- /dev/null +++ b/contracts/bin-aprender--apr-http-mcp-v1.yaml @@ -0,0 +1,182 @@ +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# bin-aprender--apr-http-mcp-v1 — the `apr` bin target of the root facade package aprender (ONT-10 slice S6, +# apr(aprender) 2/2), drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml over the +# binary-surface-v1 vocabulary (#4476, #4079, aprender#4502). Moved from docs/ont-10/binary-contracts/ +# binary-aprender-apr-2of2-v1.yaml (aprender-a2). +# +# THE SPLIT WITH S7 IS BY KIND, as S9/S8 split apr(apr-cli): S7 (bin-aprender--apr-v1) owns identity and every +# CLI command of binary/aprender/apr; this contract owns the HTTP route rows and the mcp:* rows of the same node. +# +# MEASURED (2026-09-27, batch/ont-10 @ dee666f82a, release build, default features = ["cli"], private +# CARGO_TARGET_DIR): `apr --version` = "apr 0.69.3 (dee666f82a)", sha256 a12ce715c7319903… +# MCP: `apr mcp` (stdio) answers tools/list with 9 tools, EQUAL to S8's 9 and to surface_audit.csv's 9 +# `mcp:apr.*` rows. +# HTTP: `apr serve run qwen2.5-coder-1.5b-instruct-q4_k_m.gguf` (GGUF router, CPU, no gpu_batch) answers an +# unknown path with 404 and a `routes` index of 37 "METHOD /path" entries, every one in S8's 41. The 4 it does +# not mount are the ones S8 attributes to other formats or capabilities: DELETE /api/delete, GET /tensors, +# POST /api/pull (apr-cli serve/ APR-CPU and SafeTensors routers) and POST /v1/batch/completions +# (caps.gpu_batch). The facade is `fn main() { apr_cli::cli_main() }` over apr-cli with apr-cli's default +# features, so its route union is S8's by construction; the live index is the evidence the build engaged it. +# The ledger's binary column says `apr` for both targets, so this node joins the same rows as S8 and is RED on +# the same 2 cuda-only orphans (POST /v1/logprobs, POST /v1/perplexity). One ledger or build fix clears both. +# The 2 cuda-only rows are NOT a ledger defect: they are the only 2 HTTP rows whose verified_hardware is +# `nvidia-cuda` alone, so the ledger already declares them cuda-only (S8, cadc832542). The RED clears when +# extract:binary honours the declared gate — never by a count tolerance, never by deleting the rows. +# +# GAP THIS CONTRACT ASKS OF extract:binary: S8's (METHOD dropped, apr-cli serve/ routes invisible), unchanged. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +name: bin-aprender--apr-http-mcp-v1 +version: "1.0.0" +scope: > + The HTTP and MCP surface of the `apr` binary (root facade package aprender, src src/bin/apr.rs = apr_cli::cli_main()): the routes `apr serve` can mount, the tools `apr mcp` lists, and their surface-audit rows. Identity, help and the CLI command set are S7, bin-aprender--apr-v1. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + The HTTP and MCP surface of the `apr` binary (root facade package aprender, src src/bin/apr.rs = apr_cli::cli_main()): the routes `apr serve` can mount, the tools `apr mcp` lists, and their surface-audit rows. Identity, help and the CLI command set are S7, bin-aprender--apr-v1. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-serve/src/api/router.rs — route_table, cuda_routes' + - 'crates/apr-cli/src/commands/serve/ — the APR-CPU, SafeTensors and ollama-stub routers' + - 'crates/aprender-mcp/src/tools/ — the 9 MCP tools' + - 'docs/audits/surface_audit.csv — the ledger' + - 'src/bin/apr.rs — the facade entry point' + - 'contracts/bin-apr-cli--apr-http-mcp-v1.yaml — S8, the same surface on binary/apr-cli/apr' + - 'aprender#4502 — ONT-10, slice S6' + +entity: + type: binary + ref: binary/aprender/apr + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-apr-http-mcp + targetClass: bin:target/aprender/apr + properties: + - path: bin:route + minCount: 41 + maxCount: 41 + in: + - "DELETE /api/delete" + - "GET /" + - "GET /api/tags" + - "GET /api/version" + - "GET /health" + - "GET /health/live" + - "GET /health/ready" + - "GET /metrics" + - "GET /metrics/dispatch" + - "GET /models" + - "GET /ready" + - "GET /realize/model" + - "GET /tensors" + - "GET /v1/audit/:request_id" + - "GET /v1/effective-config" + - "GET /v1/gpu/status" + - "GET /v1/metrics" + - "GET /v1/models" + - "POST /api/chat" + - "POST /api/embeddings" + - "POST /api/generate" + - "POST /api/pull" + - "POST /api/show" + - "POST /batch/generate" + - "POST /batch/tokenize" + - "POST /generate" + - "POST /metrics/dispatch/reset" + - "POST /realize/batch" + - "POST /realize/embed" + - "POST /realize/generate" + - "POST /realize/reload" + - "POST /stream/generate" + - "POST /tokenize" + - "POST /v1/batch/completions" + - "POST /v1/chat/completions" + - "POST /v1/chat/completions/stream" + - "POST /v1/completions" + - "POST /v1/embeddings" + - "POST /v1/explain" + - "POST /v1/gpu/warmup" + - "POST /v1/predict" + # LIVE RED 2026-09-26 on a default snapshot: POST /v1/logprobs and POST /v1/perplexity mount only under --features cuda; + # their ledger rows declare it (verified_hardware = nvidia-cuda, router.rs:222-223 cuda_routes) + - {path: bin:unledgeredRoute, maxCount: 0} + - {path: bin:ledgerOrphanRoute, maxCount: 0} + - path: bin:mcpTool + minCount: 9 + maxCount: 9 + in: ["apr.bench", "apr.finetune", "apr.qa", "apr.run", "apr.serve", "apr.tensors", "apr.trace", "apr.validate", "apr.version"] + - {path: bin:unledgeredTool, maxCount: 0} + +equations: + route_set: + formula: "routes(snapshot(apr)) = GET / ∪ the (METHOD, /path) tables of every `apr serve` router in the default-feature build, over every model format and runtime capability" + domain: "aprender-serve api/router.rs route_table (cuda off) and the apr-cli commands/serve/ Indexed routers" + codomain: "set equality (41 routes on 2026-09-26), or a Fail naming each route in the symmetric difference" + invariants: + - "a ledger row's trailing ` (stream:...)` mode is stripped before the join: 47 HTTP rows -> 43 routes" + - "a --features cuda build mounts 43; this shape is over the default build, as S9's command set is" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "each route is written \"METHOD /path\", axum :param segments verbatim" + lean_theorem: none — L4 not declared + mcp_tool_set: + formula: "mcpTools(snapshot(apr)) = names(tools/list of `apr mcp`) = {f.feature minus the mcp: prefix : f ∈ surface_audit, f.binary = apr, f.feature starts with mcp:}" + domain: "a live tools/list over stdio and the 9 mcp:apr.* ledger rows" + codomain: "set equality (9 tools on 2026-09-26)" + invariants: + - "S7 joins the CLI rows; this contract joins only HTTP and mcp:* rows, so no row is judged twice" + preconditions: + - "tools/list was read from the binary built at this commit" + postconditions: + - "an mcp:* row naming no listed tool is a bin:ledgerOrphan" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-APR_HTTP_MCP-INV-001 + property: every route and MCP tool apr exposes has an audited ledger row + formal: '∀ x ∈ routes(apr) ∪ mcpTools(apr): |{r ∈ surface_audit : r.binary = apr ∧ strip_mode(r.feature) ∈ {x, "mcp:" ++ x}}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-APR_HTTP_MCP-001 + rule: 'G1.2 help answers' + prediction: '`apr --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27 on the facade @ cadc832542: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR_HTTP_MCP-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`apr --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'a typo is silently ignored (observed 2026-09-27 on the facade @ cadc832542: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR_HTTP_MCP-003 + rule: 'G0.1 the version names the commit' + prediction: '`apr --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: "GREEN 2026-09-27: `apr 0.69.3 (cadc832542)` (facade release build)" + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR_HTTP_MCP-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the enumerated surface of apr equals the in: list of this shape, no more and no fewer' + test: 'pv lint contracts/ --gate shapes, once extract:binary writes the node' + if_fails: "GREEN 2026-09-27: 9 tools from a live facade tools/list @ cadc832542 (equal to S8); 41 routes = S8's router tables, live GGUF 404 index 37 ⊂ 41 @ dee666f82a" + mutation: 'add a subcommand/route/tool without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-APR_HTTP_MCP-005 + rule: 'ledger rows exist and resolve' + prediction: "docs/audits/surface_audit.csv has ≥1 row per route and per MCP tool with binary = apr (47 HTTP + 9 mcp on 2026-09-26)" + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: "LIVE RED 2026-09-26 on a default-feature snapshot: 2 ledger rows (POST /v1/logprobs, POST /v1/perplexity) name cuda-only routes; the 9 mcp rows and the other 41 routes join. Not a ledger defect: both rows declare verified_hardware = nvidia-cuda (the only cuda-only HTTP rows), so the RED clears when extract:binary honours that declared gate, as S12 and S9/S10 clear via FEATURE_GATED" + mutation: 'delete every apr row; must turn RED naming apr' + diff --git a/contracts/bin-aprender--apr-v1.yaml b/contracts/bin-aprender--apr-v1.yaml new file mode 100644 index 0000000000..e68d5b03f9 --- /dev/null +++ b/contracts/bin-aprender--apr-v1.yaml @@ -0,0 +1,412 @@ +# ────────────────────────────────────────────── +# bin-aprender--apr-v1 — the `apr` bin target of package aprender (ONT-10 slice S7 (apr(aprender) 1/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-apr-v1 — ONT-10 slice S7 (apr(aprender) 1/2): the `apr` target of the root facade package +# `aprender` (what `cargo install aprender` installs) as an entity — identity and the full CLI command set +# (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# THE SPLIT WITH S6 IS BY KIND, as S9/S8 split apr(apr-cli): `in` + minCount + maxCount on bin:command is set +# equality over the whole node, and pv's shapes subset refuses the qualifiedValueShape family, so one contract owns +# every CLI command. S7 owns identity + every CLI command of binary/aprender/apr; S6 (apr(aprender) 2/2) owns the +# HTTP route rows and the mcp:* rows of the same node. +# +# MEASURED (2026-09-26, batch/ont-10 @ b6cf6d2ed, release build, default features = ["cli"], private +# CARGO_TARGET_DIR): +# `apr --version` = "apr 0.69.3 (b6cf6d2ede)" — name right, git sha present: G0.1 green. +# Recursive --help walk, S9's convention (leaves + non-leaves whose Usage shows [COMMAND] + user-defined `help`; +# clap's auto `help` excluded): 264 commands, and the set is byte-identical to S9's (binary/apr-cli/apr). The +# facade target is a thin entry point over apr-cli, so the two command sets are equal by construction; G0.5 (the +# two targets' helpSha256 equal) spans two nodes and stays with the ONT-4g surface_audit_bins gate. +# The ledger's binary column says `apr` for both targets, so this node joins the same rows as S9: 375 apr rows = +# 318 CLI + 57 other; the 318 CLI rows reduce to 267 command paths (ledger at cf038d37f). Join against the 264: +# 15 ledger rows name commands the DEFAULT build lacks — RED today, all feature-gated: +# data x doctest extract|merge, data x hub push, data x import hf (aprender-data features doctest / hf-hub) +# mono archive|audit|publish|shims (apr-cli feature `dev`) +# rag eval compare|gate|generate|judge|metrics|retrieve|sample (aprender-rag-cli feature `eval`) +# 12 commands had no ledger row (RED 2026-09-26): capability, pv census, pv challenge check|gen, +# pv discharge check|gen-axioms|label-ratchet|run, pv extract, pv obligations, pv ontology export|tbox. +# FIXED 2026-09-27: all 12 now have binary=apr ledger rows (capability by ONT-4g #4476; the 11 `apr pv` mirrors by ONT-10 S9/S7). +# One fix in the ledger or the build turns both S7 and S9 green: the same 27 paths are named on both nodes. +# +# CONVENTION: S9's, unchanged (a runnable non-leaf is a command; only clap's auto help is excluded; the join keys +# on the ledger's binary column). +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender--apr-v1 +version: "1.0.0" +scope: > + The built `apr` target of the root facade package `aprender` (default features) as the focus node binary/aprender/apr: its identity, its exact CLI command set, and the surface_audit.csv CLI rows that must resolve to it. HTTP and MCP rows are S6. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S7 (apr(aprender) 1/2) (apr). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender/apr, on the binary-surface-v1 vocabulary. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = apr, feature starts with "apr ")' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'docs/ont-10/binary-contracts/binary-apr-cli-apr-v1.yaml (S9: the same command set on binary/apr-cli/apr)' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender/apr + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-apr-surface + targetClass: bin:target/aprender/apr + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["apr"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^apr [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-27 ("apr 0.69.3 (8e88b8fc5d)") + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:command + minCount: 264 + maxCount: 264 + in: + - "attn-parity-lint" + - "attn-viz-lint" + - "audio-inspect-lint" + - "awq-lint" + - "beat-run" + - "bench" + - "canary check" + - "canary create" + - "capability" + - "cbtop" + - "cgp baseline" + - "cgp bench" + - "cgp compete" + - "cgp contract generate" + - "cgp contract verify" + - "cgp diff" + - "cgp doctor" + - "cgp explain" + - "cgp profile binary" + - "cgp profile compare" + - "cgp profile cublas" + - "cgp profile kernel" + - "cgp profile library" + - "cgp profile metal" + - "cgp profile parallel" + - "cgp profile python" + - "cgp profile quant" + - "cgp profile scalar" + - "cgp profile scaling" + - "cgp profile simd" + - "cgp profile wasm" + - "cgp profile wgpu" + - "cgp roofline" + - "cgp trace" + - "cgp tui" + - "chat" + - "check" + - "check-finite-lint" + - "code" + - "compare-hf" + - "compile" + - "convert" + - "data audit" + - "data balance" + - "data decontaminate" + - "data dedup" + - "data split" + - "data x convert" + - "data x dedup" + - "data x drift compare" + - "data x drift detect" + - "data x drift merge" + - "data x drift report" + - "data x drift sketch" + - "data x fed manifest" + - "data x fed plan" + - "data x fed split" + - "data x fed verify" + - "data x filter-text" + - "data x fim" + - "data x head" + - "data x import local" + - "data x info" + - "data x mix" + - "data x quality check" + - "data x quality profiles" + - "data x quality report" + - "data x quality score" + - "data x registry delete" + - "data x registry init" + - "data x registry list" + - "data x registry pull" + - "data x registry push" + - "data x registry search" + - "data x registry show-info" + - "data x repl" + - "data x schema" + - "data x view" + - "dataset audio-inspect" + - "ddp-metrics-lint" + - "debug" + - "debug embed-viz" + - "decrypt" + - "devices" + - "diagnose" + - "diff" + - "distill" + - "dry-sampling-lint" + - "embed" + - "embed-viz-lint" + - "embeddings-lint" + - "encrypt" + - "eval" + - "experiment view" + - "explain" + - "explain-token-lint" + - "export" + - "finetune" + - "flow" + - "fp8-lint" + - "gbnf-lint" + - "gptq-lint" + - "gpu" + - "gpu-memtrace-lint" + - "grad-norm" + - "hang-trace-lint" + - "hex" + - "imatrix-lint" + - "import" + - "inspect" + - "kernel parity" + - "kv-timeline-lint" + - "lint" + - "list" + - "manifest" + - "mcp" + - "merge" + - "modelfile parse" + - "monitor" + - "nccl-diag-lint" + - "nf4-lint" + - "ollama-chat-lint" + - "ollama-tools-lint" + - "oom-lint" + - "oracle" + - "otlp-lint" + - "parity" + - "pipeline apply" + - "pipeline plan" + - "pipeline status" + - "pipeline validate" + - "ppl" + - "pretrain" + - "profile" + - "prometheus-lint" + - "prune" + - "ptx" + - "ptx-map" + - "publish" + - "pull" + - "pv audit" + - "pv book" + - "pv census" + - "pv certify" + - "pv challenge check" + - "pv challenge gen" + - "pv check-parity" + - "pv codegen" + - "pv coq" + - "pv coverage" + - "pv diff" + - "pv discharge check" + - "pv discharge gen-axioms" + - "pv discharge label-ratchet" + - "pv discharge run" + - "pv equations" + - "pv explain" + - "pv extract" + - "pv extract-pytorch" + - "pv flux" + - "pv fuzz" + - "pv generate" + - "pv graph" + - "pv infer" + - "pv invariants" + - "pv kaizen" + - "pv kani" + - "pv lean" + - "pv lean-status" + - "pv lint" + - "pv migrate" + - "pv mirai" + - "pv obligations" + - "pv ontology export" + - "pv ontology tbox" + - "pv pipeline" + - "pv probar" + - "pv proof-status" + - "pv query" + - "pv roofline" + - "pv scaffold" + - "pv score" + - "pv status" + - "pv tla" + - "pv unlock" + - "pv validate" + - "pv verify-bindings" + - "pv verify-pipeline" + - "pv verify-structure" + - "qa" + - "qualify" + - "quant-preservation-lint" + - "quantize" + - "rag demo" + - "rag extract-frames" + - "rag index" + - "rag info" + - "rag query" + - "rag transcribe" + - "react-trace-lint" + - "registry aliases" + - "registry-quota-lint" + - "rerank" + - "rm" + - "rm-gc-lint" + - "rosetta chain" + - "rosetta compare-inference" + - "rosetta convert" + - "rosetta diff-tensors" + - "rosetta fingerprint" + - "rosetta inspect" + - "rosetta validate-stats" + - "rosetta verify" + - "run" + - "runs diff" + - "runs ls" + - "runs show" + - "serve plan" + - "serve run" + - "shard" + - "shared-cache-lint" + - "showcase" + - "sim emc-check" + - "sim emc-validate" + - "sim help" + - "sim list-emc" + - "sim render" + - "sim run" + - "sim validate" + - "sim verify" + - "sim version" + - "stamp" + - "tensors" + - "test llm bench" + - "test tensor" + - "tokenize apply" + - "tokenize encode" + - "tokenize encode-corpus" + - "tokenize import-hf" + - "tokenize plan" + - "tokenize repair-manifest" + - "tokenize train" + - "tool-use-lint" + - "trace" + - "train apply" + - "train archive" + - "train cluster-status" + - "train halving" + - "train plan" + - "train submit" + - "train sweep" + - "train watch" + - "tree" + - "tui" + - "tune" + - "typical-p-lint" + - "unified-search-lint" + - "unshard" + - "validate" + - "validate-manifest" + - "zram benchmark" + - "zram create" + - "zram remove" + - "zram status" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender/apr) = C, |C| = 264, C = the leaves of the recursive --help walk of the default-feature build ∪ the non-leaves whose Usage shows [COMMAND] ∪ user-defined `help` subcommands ∧ ledgerOrphan = { r ∈ csv : r.binary = apr ∧ r.feature starts with 'apr ' ∧ path(r) ∉ C }; unledgeredCommand = C ∖ { path(r) }, path(r) = r.feature minus the leading 'apr ', cut at the first token starting with -, <, [ or (" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv CLI rows whose binary column is apr (318 rows, 267 paths, on 2026-09-26)" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan path or unledgered command" + invariants: + - "in + minCount 264 + maxCount 264 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "clap's auto-generated help is never a member; a feature-gated command is a member only if the default build exposes it" + - "a ledger row for a command the default build does not expose is a violation, never a silent skip; the fix is a row naming its feature or a default-build change, never deleting the shape" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `apr --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-APR-INV-001 + property: every apr surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(apr): ∃ r ∈ surface_audit : r.binary = apr ∧ path(r) = c; ∀ r with r.binary = apr: path(r) ∈ surface(apr)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-APR-001 + rule: 'G1.2 help answers' + prediction: '`apr --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`apr --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli apr' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR-003 + rule: 'G0.1 the version names the commit' + prediction: '`apr --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale apr reads as HEAD (observed 2026-09-27: "apr 0.69.3 (8e88b8fc5d)", GREEN)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-APR-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the surface literals in the shape above = the snapshot''s, set-equal' + test: 'NOT YET WRITTEN — pv lint contracts/ --gate shapes on the snapshot, after extract:binary (ONT-4g)' + if_fails: 'an added, dropped or renamed surface member ships with the ontology green (enumerated-value parsing not probed for this binary)' + mutation: 'remove `attn-parity-lint` from the snapshot; must turn RED on bin:command and bin:ledgerOrphan naming it' + - id: FALSIFY-BIN-APRENDER-APR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = apr (375 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every apr row; must turn RED naming apr' + - id: FALSIFY-BIN-APRENDER-APR-006 + rule: 'feature gated ledger rows are red today' + prediction: 'on today''s snapshot the 15 feature-gated rows (mono ×4, rag eval ×7, data x doctest/hub/import hf ×4) are bin:ledgerOrphan and the 12 pv/capability commands are bin:unledgeredCommand: the shape is RED with 27 results' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on the b6cf6d2ed snapshot' + if_fails: 'The dogfood coverage ratio would count commands users of the default install cannot run.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail, 27 results on binary/aprender/apr naming each path' + - id: FALSIFY-BIN-APRENDER-APR-007 + rule: 'runnable group counts' + prediction: 'an extractor that emits leaves only (dropping `debug` and `sim help`) makes the shape RED on minCount 264 and turns the `apr debug` ledger row into an orphan' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on a snapshot built by a leaves-only walk' + if_fails: '`apr debug model.apr`, a documented Step-4 diagnostic, would be outside the modelled surface.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail naming debug and sim help' diff --git a/contracts/bin-aprender-cbtop--aprender-cbtop-v1.yaml b/contracts/bin-aprender-cbtop--aprender-cbtop-v1.yaml new file mode 100644 index 0000000000..6ed5dc29ba --- /dev/null +++ b/contracts/bin-aprender-cbtop--aprender-cbtop-v1.yaml @@ -0,0 +1,114 @@ +# ────────────────────────────────────────────── +# bin-aprender-cbtop--aprender-cbtop-v1 — the `aprender-cbtop` bin target of package aprender-cbtop (ONT-10 slice S19 1/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml (ONT-4g, dafb8194f). +# MEASURED 2026-09-26, batch/ont-10 @ dafb8194f, release build, default features: +# --help rc 0; --no-such-flag rc 2; --version = "cbtop 0.69.3" (clap #[command(name = "cbtop")], src/main.rs:19); +# the --help walk gives 4 leaf commands and surface_audit.csv has exactly those 4 rows (set-equal). +# LIVE RED 2026-09-26, all GREEN 2026-09-27 (aprender-5d): G0.1 (no sha) — build.rs embeds APR_GIT_SHA; name pair — +# --version said "cbtop", now aprender-cbtop; G1.3 — bogus --backend/--workload/--format/--load/--compare exited 0 with +# a default, now clap value_parsers (exit 2); the bare invocation (TUI / --headless) now has its own ledger row. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-cbtop--aprender-cbtop-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-cbtop` binary (package aprender-cbtop, src crates/aprender-cbtop/src/main.rs): + its help, its usage errors, its version string, its subcommand set, and its surface-audit rows. Out of scope: what + the benchmarks measure and the TUI's rendering. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-cbtop answers --help, rejects an unknown flag as a usage error, names the commit it was built from, + exposes exactly the 4 leaf commands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-cbtop/src/main.rs — the clap Cli and Commands enums' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-cbtop' + - 'aprender#4502 — ONT-10, slice S19' + +entity: + type: binary + ref: binary/aprender-cbtop/aprender-cbtop + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-cbtop-surface + targetClass: bin:target/aprender-cbtop/aprender-cbtop + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-cbtop"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-cbtop"]} + # was LIVE RED 2026-09-26 ("cbtop 0.69.3"); GREEN 2026-09-27: #[command(name = "aprender-cbtop")] + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-cbtop [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-27: build.rs embeds APR_GIT_SHA + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}"} + - path: bin:command + minCount: 4 + maxCount: 4 + in: + - "bench" + - "optimize analyze" + - "optimize baseline" + - "optimize check" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-cbtop)) = leaves(clap(Cli)) ∧ commands(snapshot(aprender-cbtop)) ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-cbtop}" + domain: "the aprender-cbtop line of the tracked snapshot, the clap Commands/OptimizeCommands enums, and the ledger rows with binary = aprender-cbtop" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a leaf clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-cbtop --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-CBTOP-INV-001 + property: every aprender-cbtop leaf command has an audited ledger row + formal: '∀ c ∈ commands(aprender-cbtop): |{r ∈ surface_audit : r.binary = aprender-cbtop ∧ r.feature = "aprender-cbtop " ++ c}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-CBTOP-001 + rule: 'G1.2 help answers' + prediction: '`aprender-cbtop --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-cbtop' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-CBTOP-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-cbtop --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-cbtop' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-CBTOP-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-cbtop --version` starts with the target name and contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'the build cannot be traced to a commit (was LIVE RED 2026-09-26: `cbtop 0.69.3`, no sha; GREEN 2026-09-27: `aprender-cbtop 0.69.3 (2d367b4d98)`)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-CBTOP-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: 'every enumerated flag (--backend, --load, --workload, --format, --compare items) is a clap value_parser: `aprender-cbtop bench --backend nosuch` exits 2 naming simd, wgpu, cuda, all; leaves(clap) = commands(snapshot)' + test: 'cargo test -p aprender-cbtop --bin aprender-cbtop g13_ (3 tests; the leaf-set half waits for extract:binary)' + if_fails: 'a typo in an enumerated flag runs with a silent default (was LIVE RED 2026-09-26: `bench --backend/--workload/--format nosuch` exit 0; GREEN 2026-09-27: those, `--load nosuch`, `bench --workload dot`, `bench --compare simd,nosuch` and `optimize analyze|check --format nosuch` all exit 2)' + mutation: 'drop value_parser = BACKENDS from bench --backend; `bench --backend nosuch` exits 0 and must turn RED. Add a subcommand without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-CBTOP-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-cbtop per leaf (4) and one for the bare root surface (5 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a shipped command has no audited row (observed 2026-09-27: 4 of 4 leaves ledgered, plus the root row `aprender-cbtop (interactive TUI; --headless)` added 2026-09-27, GREEN)' + mutation: 'delete every aprender-cbtop row; must turn RED naming aprender-cbtop' diff --git a/contracts/bin-aprender-cgp--aprender-cgp-v1.yaml b/contracts/bin-aprender-cgp--aprender-cgp-v1.yaml new file mode 100644 index 0000000000..76a14f405c --- /dev/null +++ b/contracts/bin-aprender-cgp--aprender-cgp-v1.yaml @@ -0,0 +1,101 @@ +# ────────────────────────────────────────────── +# bin-aprender-cgp--aprender-cgp-v1 — the `aprender-cgp` bin target of package aprender-cgp (ONT-10 slice S5) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# RED today beyond G0.1: the version names "cgp", not the bin name; 0 ledger rows under "cgp". +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-cgp--aprender-cgp-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-cgp` binary (package aprender-cgp, src crates/aprender-cgp/src/main.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-cgp answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-cgp/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-cgp' + - 'aprender#4502 — ONT-10, slice S5' + +entity: + type: binary + ref: binary/aprender-cgp/aprender-cgp + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-cgp)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-cgp/tests/cli_surface_gate.rs) ∧ leaves(aprender-cgp) = {f.feature : f ∈ surface_audit, f.binary = aprender-cgp}" + domain: "the aprender-cgp line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-cgp" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {profile, bench, roofline, diff, contract, trace, explain, tui, baseline, doctor, compete}; 25 leaves, set-equal to the 25 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-cgp --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-CGP-INV-001 + property: every aprender-cgp command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-cgp): |{r ∈ surface_audit : r.binary = aprender-cgp ∧ r.feature = "aprender-cgp " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-CGP-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-cgp/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-CGP-001 + rule: 'G1.2 help answers' + prediction: '`aprender-cgp --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-cgp --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-CGP-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-cgp --no-such-flag` and `aprender-cgp no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-cgp --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-CGP-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-cgp --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-cgp --test cli_surface_gate version_reports_the_crate_version pins only the crate version today' + if_fails: 'LIVE RED 2026-09-26: `cgp 0.69.3` — no sha; a stale aprender-cgp reads as HEAD; the version names "cgp", not the bin name' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-CGP-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly profile, bench, roofline, diff, contract, trace, explain, tui, baseline, doctor, compete' + test: 'cargo test -p aprender-cgp --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-CGP-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-cgp per leaf (25 rows for 25 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-cgp ships a command with no audited row' + mutation: 'delete every aprender-cgp row; must turn RED naming aprender-cgp' + - id: FALSIFY-BIN-APRENDER-CGP-006 + rule: 'unusable input is rejected cleanly' + prediction: '`diff --baseline/--current`, `baseline --load` and `explain ` on a missing, empty and garbage input: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-cgp --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): with the explain fix reverted the gate is RED on this test (`explain no-such-target` got Some(0)); restored, GREEN' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-compute-xtask--aprender-compute-xtask-v1.yaml b/contracts/bin-aprender-compute-xtask--aprender-compute-xtask-v1.yaml new file mode 100644 index 0000000000..1423b246a6 --- /dev/null +++ b/contracts/bin-aprender-compute-xtask--aprender-compute-xtask-v1.yaml @@ -0,0 +1,111 @@ +# ────────────────────────────────────────────── +# bin-aprender-compute-xtask--aprender-compute-xtask-v1 — the `aprender-compute-xtask` bin target of package +# aprender-compute-xtask (ONT-10 slice S19 2/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml (ONT-4g, dafb8194f). +# MEASURED 2026-09-26, batch/ont-10 @ dafb8194f, release build, default features: +# --help rc 0; --no-such-flag rc 2; --version = "aprender-compute-xtask 0.69.3"; options -h/-V only; +# 3 subcommands, and surface_audit.csv has exactly those 3 rows (set-equal). +# G0.1 was LIVE RED 2026-09-26 (no sha); GREEN 2026-09-27 (build.rs embeds APR_GIT_SHA). No subcommand takes an enumerated value, so G1.3 is vacuous here. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-compute-xtask--aprender-compute-xtask-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-compute-xtask` binary (package aprender-compute-xtask, the trueno developer + task runner): its help, its usage errors, its version string, its subcommand set, and its surface-audit rows. + Out of scope: what each task checks. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-compute-xtask answers --help, rejects an unknown flag as a usage error, names the commit it was built + from, exposes exactly the 3 subcommands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-compute-xtask/src/main.rs — the clap Commands enum' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-compute-xtask' + - 'aprender#4502 — ONT-10, slice S19' + +entity: + type: binary + ref: binary/aprender-compute-xtask/aprender-compute-xtask + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-compute-xtask-surface + targetClass: bin:target/aprender-compute-xtask/aprender-compute-xtask + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-compute-xtask"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-compute-xtask"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-compute-xtask [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-27: build.rs embeds APR_GIT_SHA + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}"} + - path: bin:command + minCount: 3 + maxCount: 3 + in: + - "check-simd" + - "install-hooks" + - "validate-examples" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-compute-xtask)) = subcommands(clap(Cli)) ∧ commands(snapshot(aprender-compute-xtask)) ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-compute-xtask}" + domain: "the aprender-compute-xtask line of the tracked snapshot, the clap Commands enum, and the ledger rows with binary = aprender-compute-xtask" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-compute-xtask --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-COMPUTE-XTASK-INV-001 + property: every aprender-compute-xtask subcommand has an audited ledger row + formal: '∀ c ∈ commands(aprender-compute-xtask): |{r ∈ surface_audit : r.binary = aprender-compute-xtask ∧ r.feature = "aprender-compute-xtask " ++ c}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-COMPUTE-XTASK-001 + rule: 'G1.2 help answers' + prediction: '`aprender-compute-xtask --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-compute-xtask' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-COMPUTE-XTASK-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-compute-xtask --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-compute-xtask' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-COMPUTE-XTASK-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-compute-xtask --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'the build cannot be traced to a commit (was LIVE RED 2026-09-26: `aprender-compute-xtask 0.69.3`, no sha; GREEN 2026-09-27: `aprender-compute-xtask 0.69.3 (2d367b4d98)`)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-COMPUTE-XTASK-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: 'subcommands(clap) = commands(snapshot); no flag takes an enumerated value (only -h/-V)' + test: 'NOT YET WRITTEN — S19 (aprender-5d)' + if_fails: 'a subcommand shipped without being snapshotted (observed 2026-09-26: 3 = 3, GREEN)' + mutation: 'add a subcommand without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-COMPUTE-XTASK-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-compute-xtask per subcommand (3 on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a shipped command has no audited row (observed 2026-09-26: 3 of 3 ledgered, GREEN)' + mutation: 'delete every aprender-compute-xtask row; must turn RED naming aprender-compute-xtask' diff --git a/contracts/bin-aprender-contracts-cli--pv-v1.yaml b/contracts/bin-aprender-contracts-cli--pv-v1.yaml new file mode 100644 index 0000000000..e80fefc402 --- /dev/null +++ b/contracts/bin-aprender-contracts-cli--pv-v1.yaml @@ -0,0 +1,169 @@ +# ────────────────────────────────────────────── +# bin-aprender-contracts-cli--pv-v1 — the `pv` bin target of package aprender-contracts-cli (ONT-10 slice S1) +# +# THE EXEMPLAR. Every ONT-10 slice drafts its per-binary contract to this file: +# · file name contracts/bin----v1.yaml (package and target exactly as cargo metadata spells them) +# · entity {type: binary, ref: binary//} +# · depends_on [binary-surface-v1] — the shared shapes live there; do NOT restate them here +# · shapes ONE shape, targetClass bin:target//, pinning bin:name, bin:package, bin:versionString +# (G0.1 as severity: warning while RED), bin:command / bin:route / bin:mcpTool with in: + exact +# min/maxCount, and bin:unledgered{Command,Route,Tool} + bin:ledgerOrphan{,Route} at maxCount 0 +# · equations surface_equals_snapshot (the command set), plus one per surface kind the binary has (http, mcp) +# · falsifiers FALSIFY-BIN--001..005, one per gate below; each names a planted mutation that turns it RED +# 001 G1.2 --help exits 0 and is non-empty +# 002 G1.2 an unknown flag exits 2 (clap usage error), never 0 or a panic +# 003 G0.1 --version names the commit (first 9 hex of git_sha) +# 004 G1.3/G1.4 every enumerated flag value is a clap value_parser; the command set equals the snapshot +# 005 ledger ≥1 docs/audits/surface_audit.csv row per command, `binary` column = the bin name +# A gate that is live RED today stays in the contract, RED, with the observed value in `if_fails` — never dropped. +# Slices staged under docs/ont-10/binary-contracts/ move here with `git mv` once Σ binary + this vocabulary are on +# the batch (they are, as of this commit); `pv lint contracts/` must stay PASS after the move. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-contracts-cli--pv-v1 +version: "1.0.0" +scope: > + The command surface of the `pv` binary (package aprender-contracts-cli, src crates/aprender-contracts-cli/src/main.rs): + its help, its usage errors, its version string, its subcommand set, and its surface-audit rows. Out of scope: what + each subcommand computes (the contract that subcommand implements). +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + pv answers --help, rejects an unknown flag as a usage error, names the commit it was built from, exposes exactly + the subcommands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-contracts-cli/src/cli.rs — the clap Command enum' + - 'docs/audits/surface_audit.csv — the rows whose binary column is pv' + - 'aprender#4502 — ONT-10, slice S1' + +entity: + type: binary + ref: binary/aprender-contracts-cli/pv + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-pv-surface + targetClass: bin:target/aprender-contracts-cli/pv + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["pv"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-contracts-cli"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^pv [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-27 at a3def2970 (`pv 0.69.3 (a3def2970) (...)`); an error now, no longer a warning + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "\\([0-9a-f]{9}\\)"} + - path: bin:command + minCount: 49 + maxCount: 49 + in: + - "audit" + - "book" + - "census" + - "certify" + - "challenge check" + - "challenge gen" + - "check-parity" + - "codegen" + - "coq" + - "coverage" + - "diff" + - "discharge check" + - "discharge gen-axioms" + - "discharge label-ratchet" + - "discharge run" + - "equations" + - "explain" + - "extract" + - "extract-pytorch" + - "flux" + - "fuzz" + - "generate" + - "graph" + - "infer" + - "invariants" + - "kaizen" + - "kani" + - "lean" + - "lean-status" + - "lint" + - "migrate" + - "mirai" + - "obligations" + - "ontology export" + - "ontology tbox" + - "pipeline" + - "probar" + - "proof-status" + - "query" + - "roofline" + - "scaffold" + - "score" + - "status" + - "tla" + - "unlock" + - "validate" + - "verify-bindings" + - "verify-pipeline" + - "verify-structure" + # GREEN 2026-09-27: all 49 paths have a ledger row (was 11 unledgered on 2026-09-26) + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(pv)) = subcommands(clap(Cli)) ∧ commands(snapshot(pv)) ⊆ {f.feature : f ∈ surface_audit, f.binary = pv}" + domain: "the pv line of the tracked snapshot, the clap Command enum, and the ledger rows with binary = pv" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `pv --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-PV-INV-001 + property: every pv subcommand has an audited ledger row + formal: '∀ c ∈ commands(pv): |{r ∈ surface_audit : r.binary = pv ∧ r.feature = "pv " ++ c}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-PV-001 + rule: 'G1.2 help answers' + prediction: '`pv --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli pv' + if_fails: 'the binary cannot describe its own surface' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-PV-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`pv --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli pv' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-PV-003 + rule: 'G0.1 the version names the commit' + prediction: '`pv --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale pv reads as HEAD (RED 2026-09-26: `pv 0.69.3 (aprender provable-contracts verifier)`, no sha; GREEN 2026-09-27 at a3def2970)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-PV-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: '`pv lint contracts/ --format nosuch` exits 2 naming the allowed values; subcommands(clap) = commands(snapshot)' + test: 'NOT YET WRITTEN — S1 (aprender-60)' + if_fails: 'a bad --format is a runtime error the help never lists (RED 2026-09-26: exit 1; GREEN 2026-09-27 at a3def2970: exit 2 with the value list)' + mutation: 'add a subcommand without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-PV-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = pv (38 on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a pv command ships unaudited (RED 2026-09-26: 11 of 49 paths had no row; GREEN 2026-09-27: 49 of 49)' + mutation: 'delete every pv row; must turn RED naming pv' diff --git a/contracts/bin-aprender-data--aprender-data-v1.yaml b/contracts/bin-aprender-data--aprender-data-v1.yaml new file mode 100644 index 0000000000..de594d7f63 --- /dev/null +++ b/contracts/bin-aprender-data--aprender-data-v1.yaml @@ -0,0 +1,161 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `alimentar` -> `aprender-data`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-data--aprender-data-v1 — the `aprender-data` bin target of package aprender-data (ONT-10 slice S10) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-data-v1 — ONT-10 slice S10: the `aprender-data` binary as an entity (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, `cargo build --release -p aprender-data --features cli`, default +# features otherwise): `aprender-data --version` = "aprender-data 0.69.3"; the recursive --help walk gives 31 leaf commands, +# the set below. surface_audit.csv has 35 aprender-data rows: the 31, plus 4 for commands the DEFAULT build does not +# expose, because they sit behind non-default features: +# doctest extract, doctest merge (feature `doctest`) +# hub push, import hf (feature `hf-hub`) +# Those 4 are bin:ledgerOrphan today, so `aprender-data-commands` is RED until either the ledger rows name their +# feature or the release build enables it. Recorded, not hidden: that is the defect this shape exists to find. +# G0.1 (--version carries the git sha) is a warning here and RED on every current build: the string is "0.69.3". +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-data--aprender-data-v1 +version: "1.0.0" +scope: > + The built `aprender-data` target of package `aprender-data` (default features) as the focus node binary/aprender-data/aprender-data: its identity and its exact CLI command set, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S10 (aprender-data). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-data/aprender-data, on the binary-surface-v1 vocabulary. + references: + - 'crates/aprender-data/tests/cli_surface_gate.rs — the gate falsifiers 001, 002, 004 and 006 run' + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = aprender-data)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-data/aprender-data + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-data-surface + targetClass: bin:target/aprender-data/aprender-data + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-data"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-data"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-data [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:command + minCount: 31 + maxCount: 31 + in: + - "convert" + - "dedup" + - "drift compare" + - "drift detect" + - "drift merge" + - "drift report" + - "drift sketch" + - "fed manifest" + - "fed plan" + - "fed split" + - "fed verify" + - "filter-text" + - "fim" + - "head" + - "import local" + - "info" + - "mix" + - "quality check" + - "quality profiles" + - "quality report" + - "quality score" + - "registry delete" + - "registry init" + - "registry list" + - "registry pull" + - "registry push" + - "registry search" + - "registry show-info" + - "repl" + - "schema" + - "view" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-data/aprender-data) = C, |C| = 31, where C = the leaves of the recursive --help walk of the default-feature build ∧ ledgerOrphan = { r ∈ csv : r.binary = aprender-data ∧ r.feature ∉ C }; unledgeredCommand = C ∖ { r.feature : r.binary = aprender-data }" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is aprender-data" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount 31 + maxCount 31 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "a ledger row for a command the built binary does not expose is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-data --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-ALIMENTAR-INV-001 + property: every aprender-data surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-data): ∃ r ∈ surface_audit : r.binary = aprender-data ∧ path(r) = c; ∀ r with r.binary = aprender-data: path(r) ∈ surface(aprender-data)' + prose: false + + - id: BIN-ALIMENTAR-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-data/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-ALIMENTAR-001 + rule: 'G1.2 help answers' + prediction: '`aprender-data --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-data --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-ALIMENTAR-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-data --no-such-flag` and `aprender-data no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-data --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-ALIMENTAR-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-data --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-data 0.69.3" — no sha; a stale aprender-data reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-ALIMENTAR-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly convert, info, head, schema, mix, fim, dedup, filter-text, view, import, registry, drift, quality, fed, repl' + test: 'cargo test -p aprender-data --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-ALIMENTAR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-data (35 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-data row; must turn RED naming aprender-data' + - id: FALSIFY-BIN-ALIMENTAR-006 + rule: 'unusable input is rejected cleanly' + prediction: '`info`, `head`, `schema` and `convert ` on a missing, empty and garbage input: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-data --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN on an unmutated tree; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-db--aprender-db-v1.yaml b/contracts/bin-aprender-db--aprender-db-v1.yaml new file mode 100644 index 0000000000..fb93a76aff --- /dev/null +++ b/contracts/bin-aprender-db--aprender-db-v1.yaml @@ -0,0 +1,144 @@ +# ────────────────────────────────────────────── +# bin-aprender-db--aprender-db-v1 — the `aprender-db` bin target of package aprender-db (ONT-10 slice S18, 3/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-27, batch/ont-10 @ 8e88b8fc5d, release build, `--features server`, private CARGO_TARGET_DIR): +# The target has required-features = ["server"]: a default-feature build does not produce it at all, so the +# snapshot must be taken with --features server. +# `--version` = "trueno-db 0.69.3": the pre-monorepo name (#[command(name = "trueno-db")]) and no sha. LIVE RED on +# the name pattern and on G0.1. +# FIXED in the same PR: clap name aprender-db (measured after the fix: "aprender-db 0.69.3"). +# `--help` exits 0; `--no-such-flag` exits 2. No subcommands; one required option, --config ; no enumerated +# flag values. +# HTTP: src/main.rs mounts 3 routes via .route() (no nest/merge/fallback/route_service in src/): GET /health, +# GET /status, POST /query. +# surface_audit.csv: 4 rows = the 3 routes (set-equal) + `aprender-db --config `. +# +# VOCABULARY EXTENSION ASKED OF binary-surface-v1 (aprender-88), as in bin-aprender-profile--aprender-profile-v1: +# bin:option. The `--config ` row keys on option --config; joined against bin:command alone it is an orphan. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-db--aprender-db-v1 +version: "1.0.0" +scope: > + The CLI and HTTP surface of the `aprender-db` binary (package aprender-db, src crates/aprender-db/src/main.rs, built + with feature `server`): its help, its usage errors, its version string, its (empty) command set, its option, its + routes, and its surface-audit rows. Out of scope: what a query computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-db answers --help, rejects an unknown flag as a usage error, names itself and the commit it was built + from, serves exactly the routes the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-db/src/main.rs — the clap Args and the axum router' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-db' + - 'aprender#4502 — ONT-10, slice S18' + +entity: + type: binary + ref: binary/aprender-db/aprender-db + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-db-surface + targetClass: bin:target/aprender-db/aprender-db + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-db"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-db"]} + # was RED at 8e88b8fc5d ("trueno-db 0.69.3"); FIXED on batch/ont-10 (clap name = aprender-db) + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-db [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - {path: bin:command, maxCount: 0} + # vocabulary extension (header) + - {path: bin:option, minCount: 1, maxCount: 1, in: ["--config"]} + - path: bin:route + minCount: 3 + maxCount: 3 + in: + - "GET /health" + - "GET /status" + - "POST /query" + - {path: bin:unledgeredRoute, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + - {path: bin:ledgerOrphanRoute, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-db)) = ∅ ∧ options(snapshot) = {--config} ∧ key(r) ∈ options for every CLI ledger row r with r.binary = aprender-db" + domain: "the aprender-db line of the tracked snapshot (built with --features server) and its 1 CLI ledger row" + codomain: "set equality, or a Fail naming the added command, the changed option or the orphan row" + invariants: + - "a subcommand added to a single-mode server is a violation naming it" + preconditions: + - "the snapshot was written at this commit with --features server" + postconditions: + - "key(`aprender-db --config …`) = --config" + lean_theorem: none — L4 not declared + http_routes: + formula: "routes(aprender-db) = R = {GET /health, GET /status, POST /query}, the (METHOD, /path) pairs main.rs mounts via .route(); R = {f.feature minus the trailing ` (aprender-db)` : f ∈ surface_audit, f.binary = aprender-db, f is an HTTP row}" + domain: "the axum router in crates/aprender-db/src/main.rs and the 3 HTTP ledger rows" + codomain: "one bin:route literal \"METHOD /path\" per pair; set equality or a Fail naming the difference" + invariants: + - "in + minCount 3 + maxCount 3 is set equality: an added, dropped or re-methoded route is a violation naming it" + +invariants: + - id: BIN-APRENDER-DB-INV-001 + property: every aprender-db route has an audited ledger row and every ledger row names a route or option the binary has + formal: '∀ p ∈ routes(aprender-db): ∃ r ∈ surface_audit : r.binary = aprender-db ∧ route(r) = p; ∀ r with r.binary = aprender-db: key(r) ∈ routes ∪ options' + prose: false + - id: BIN-APRENDER-DB-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-db/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-DB-001 + rule: 'G1.2 help answers' + prediction: '`aprender-db --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-db --test cli_surface_gate advertised_options_are_the_pinned_surface' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-DB-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-db --no-such-flag` exits 2' + test: 'cargo test -p aprender-db --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all; must turn RED' + - id: FALSIFY-BIN-APRENDER-DB-003 + rule: 'G0.1 the version names the binary and the commit' + prediction: '`aprender-db --version` starts with "aprender-db " and contains the first 9 hex of the git sha' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'name: FIXED on batch/ont-10 (was "trueno-db 0.69.3"); sha: LIVE RED on 0.69.3, clears at rc.1 (car/0.70.0 carries #4219)' + mutation: 'restore #[command(name = "trueno-db")] after a fix; must turn RED on bin:versionString' + - id: FALSIFY-BIN-APRENDER-DB-004 + rule: 'G1.3/G1.4 the surface equals the snapshot' + prediction: 'no flag takes an enumerated value (observed 2026-09-27); no command, options = {--config}, routes = the 3 literals above' + test: 'cargo test -p aprender-db --test cli_surface_gate -- advertised_options_are_the_pinned_surface' + if_fails: 'a route could be added or dropped unmodelled; the extractor must build with --features server or the node is absent' + mutation: 'drop POST /query from the snapshot; must turn RED on bin:route and bin:ledgerOrphanRoute' + - id: FALSIFY-BIN-APRENDER-DB-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per route with binary = aprender-db (4 rows on 2026-09-27: 3 routes + --config)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the binary would ship unaudited (observed 2026-09-27: 3/3 routes, GREEN)' + mutation: 'delete every aprender-db row; must turn RED naming aprender-db' + - id: FALSIFY-BIN-APRENDER-DB-006 + rule: 'unusable input is rejected cleanly' + prediction: '`--config` on a missing, empty and garbage file: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-db --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 1 with no panic, GREEN; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-explain--aprender-explain-v1.yaml b/contracts/bin-aprender-explain--aprender-explain-v1.yaml new file mode 100644 index 0000000000..e2b3484519 --- /dev/null +++ b/contracts/bin-aprender-explain--aprender-explain-v1.yaml @@ -0,0 +1,134 @@ +# ────────────────────────────────────────────── +# bin-aprender-explain--aprender-explain-v1 — the `aprender-explain` bin target of package aprender-explain +# (ONT-10 slice S18, 2/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-27, batch/ont-10 @ 8e88b8fc5d, release build, default features, private CARGO_TARGET_DIR): +# `--version` = "trueno-explain 0.69.3": the pre-monorepo name (#[command(name = "trueno-explain")]) and no sha. +# LIVE RED on the name pattern and on G0.1. +# `--help` exits 0; `--no-such-flag` exits 2. +# Commands: bugs, compare, diff, ptx, simd, tui, wgpu (clap's `help` excluded); no nested subcommands. +# `-K/--kernel ` takes one of 7 kernels, but as a free String checked after clap: `ptx -K nosuch` exits 1 +# and --help says "see --help for list" without listing them (G1.3 LIVE RED). +# FIXED in the same PR: clap name aprender-explain; -K/-a/-b parse through kernel_names() (rc=2 on nosuch). +# surface_audit.csv: 7 rows, set-equal to the commands. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-explain--aprender-explain-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-explain` binary (package aprender-explain, src + crates/aprender-explain/src/main.rs): its help, its usage errors, its version string, its subcommand set, and its + surface-audit rows. Out of scope: what each analysis computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-explain answers --help, rejects an unknown flag as a usage error, names itself and the commit it was + built from, exposes exactly the subcommands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-explain/src/main.rs — the clap Commands enum' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-explain' + - 'aprender#4502 — ONT-10, slice S18' + +entity: + type: binary + ref: binary/aprender-explain/aprender-explain + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-explain-surface + targetClass: bin:target/aprender-explain/aprender-explain + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-explain"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-explain"]} + # was RED at 8e88b8fc5d ("trueno-explain 0.69.3"); FIXED on batch/ont-10 (clap name = aprender-explain) + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-explain [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 7 + maxCount: 7 + in: + - "bugs" + - "compare" + - "diff" + - "ptx" + - "simd" + - "tui" + - "wgpu" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-explain)) = subcommands(clap(Cli)) = {bugs, compare, diff, ptx, simd, tui, wgpu} ∧ commands ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-explain}" + domain: "the aprender-explain line of the tracked snapshot, the clap Commands enum, and the 7 ledger rows" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-explain --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-EXPLAIN-INV-001 + property: every aprender-explain subcommand has an audited ledger row + formal: '∀ c ∈ commands(aprender-explain): |{r ∈ surface_audit : r.binary = aprender-explain ∧ r.feature = "aprender-explain " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-EXPLAIN-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-explain/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-EXPLAIN-001 + rule: 'G1.2 help answers' + prediction: '`aprender-explain --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-explain --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-EXPLAIN-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-explain --no-such-flag` exits 2' + test: 'cargo test -p aprender-explain --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-EXPLAIN-003 + rule: 'G0.1 the version names the binary and the commit' + prediction: '`aprender-explain --version` starts with "aprender-explain " and contains the first 9 hex of the git sha' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'name: FIXED on batch/ont-10 (was "trueno-explain 0.69.3"); sha: LIVE RED on 0.69.3, clears at rc.1 (car/0.70.0 carries #4219)' + mutation: 'restore #[command(name = "trueno-explain")] after a fix; must turn RED on bin:versionString' + - id: FALSIFY-BIN-APRENDER-EXPLAIN-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: '`aprender-explain ptx -K nosuch` exits 2 naming the allowed kernels; subcommands(clap) = the 7 literals above' + test: 'cargo test -p aprender-explain --test cli_surface_gate -- advertised_subcommands_are_the_pinned_surface malformed_arguments_are_usage_errors' + if_fails: 'a bad kernel name runs past clap (was RED at 8e88b8fc5d: exit 1, "Unknown kernel: nosuch. Available: vector_add, gemm_naive, gemm_tiled, softmax, q4k_gemm, q5k_gemm, q6k_gemm"); FIXED on batch/ont-10: kernel_names() value_parser on all 6 kernel flags, exit 2, --help lists them' + mutation: 'after the fix, turn -K back into a String; must turn RED' + - id: FALSIFY-BIN-APRENDER-EXPLAIN-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per command with binary = aprender-explain (7 on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the binary would ship unaudited (observed 2026-09-27: 7/7, GREEN)' + mutation: 'delete every aprender-explain row; must turn RED naming aprender-explain' + - id: FALSIFY-BIN-APRENDER-EXPLAIN-006 + rule: 'unusable input is rejected cleanly' + prediction: '`diff -K vector_add --baseline` on a missing, empty and garbage file: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-explain --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 1 with no panic, GREEN; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-orchestrate--aprender-orchestrate-v1.yaml b/contracts/bin-aprender-orchestrate--aprender-orchestrate-v1.yaml new file mode 100644 index 0000000000..fb5281d01d --- /dev/null +++ b/contracts/bin-aprender-orchestrate--aprender-orchestrate-v1.yaml @@ -0,0 +1,331 @@ +# ────────────────────────────────────────────── +# bin-aprender-orchestrate--aprender-orchestrate-v1 — the `aprender-orchestrate` bin target of package +# aprender-orchestrate (ONT-10 slices S4 CLI+MCP and S3 HTTP routes, folded into one node) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# ONE SHAPE, THREE KINDS: `in` + minCount + maxCount is set equality over the whole node, and pv's shapes subset +# refuses qualifiedValueShape, so S3's routes cannot sit in a second file. They are properties of the one shape +# below. S3's staged draft (docs/ont-10/binary-contracts/binary-aprender-orchestrate-http-v1.yaml) is superseded. +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0 / ff9587b59, release build, default features): +# `aprender-orchestrate --version` = "batuta 0.69.3": the pre-monorepo name, no sha. LIVE RED on the name +# pattern and on G0.1 (warning). NAME FIXED 2026-09-27 (c1): clap `#[command(name)]` in src/main_cli.rs now says +# "aprender-orchestrate"; the release build prints "aprender-orchestrate 0.69.3". G0.1 (sha) clears when car/0.70.0's +# version string lands (#4219), so it stays a warning here. +# Recursive --help walk: 86 leaf commands, set-equal to surface_audit.csv's 86 CLI rows. +# MCP: `aprender-orchestrate mcp` (stdio) answers tools/list with 4 tools (hf_info, hf_integration, hf_search, +# hf_tree; server "batuta-hf"). unledgeredTool FIXED 2026-09-27 (c1): 4 `mcp:hf_*` ledger rows, evidence +# src/mcp/server.rs, verified_hardware x86_64-linux because CI's workspace-test runs test_tool_hf_* there. +# STILL LIVE RED (ledgerOrphan): the ledger's 6 other `mcp:*` rows are not stdio tools/list tools. Traced +# 2026-09-27 (c1), they are two different classes: +# - calculator, code_execution, web_search: NOT a ledger defect. banco mounts POST /api/v1/mcp (router.rs:196; +# GET /api/v1/mcp/info says transport "http") and its ToolRegistry lists these three. Each row DECLARES that +# transport in its feature text, "(banco POST /api/v1/mcp)", as S8's cuda-only rows declare theirs in +# verified_hardware. The RED clears when extract:binary honours the declaration (join those rows against +# banco's tools/list, not stdio's), never by a count tolerance. +# - compute, memory, rag "(agent tool registry)": a REAL orphan. HandlerRegistry (agent/tool/mcp_server.rs) has +# no caller outside its tests and examples/agent_demo.rs, so no transport of this binary serves them. Recorded +# as docs/findings/aprender-c1.jsonl c1-ont10-orchestrate-agent-mcp-tools-unserved (serve them, or declare +# a library-surface allowance). +# Neither class may be fixed by a rename or a delete: check_dogfood_coverage.sh G2.2 reads a renamed row as a +# deleted one, and a deleted row takes its defect with it. +# HTTP (aprender-1c, S3): crates/aprender-orchestrate/src/serve/banco/router.rs mounts 92 (METHOD, /path) pairs via +# .route(), with no .nest/.merge/.route_service/.fallback under serve/. That set equals the ledger's 92 HTTP rows. +# EXTRACT GAP, LIVE RED: scripts/dogfood_surfaces.sh reads only aprender-serve routes, so bin:route is empty on +# this node until extract:binary reads the banco router. minCount 92 makes that a Fail, never a vacuous pass. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-orchestrate--aprender-orchestrate-v1 +version: "1.0.0" +scope: > + The surface of the `aprender-orchestrate` binary of package aprender-orchestrate (src crates/aprender-orchestrate/src/main.rs): + its version string, its exact CLI command set, the tools its MCP server lists, the HTTP routes its banco server + mounts, and the surface-audit rows for each. Out of scope: what each command, tool or route computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-orchestrate names itself and the commit it was built from, exposes exactly the 86 commands, 4 MCP + tools and 92 HTTP routes the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-orchestrate/src/serve/banco/router.rs — the route table' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-orchestrate' + - 'aprender#4502 — ONT-10, slices S3/S4' + +entity: + type: binary + ref: binary/aprender-orchestrate/aprender-orchestrate + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-orchestrate-aprender-orchestrate-surface + targetClass: bin:target/aprender-orchestrate/aprender-orchestrate + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-orchestrate"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-orchestrate"]} + # was LIVE RED 2026-09-26 ("batuta 0.69.3"); fixed 2026-09-27 + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-orchestrate [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-26 (no sha); a warning to match the exemplar + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 86 + maxCount: 86 + in: + - "agent chat" + - "agent contracts" + - "agent pool" + - "agent run" + - "agent sign" + - "agent status" + - "agent validate" + - "agent verify-sig" + - "analyze" + - "bug-hunter analyze" + - "bug-hunter deep-hunt" + - "bug-hunter diff" + - "bug-hunter ensemble" + - "bug-hunter falsify" + - "bug-hunter fuzz" + - "bug-hunter hunt" + - "bug-hunter spec" + - "bug-hunter stack" + - "bug-hunter ticket" + - "bug-hunter trend" + - "bug-hunter triage" + - "build" + - "code" + - "content emit" + - "content types" + - "content validate" + - "data tree" + - "deploy cloudflare" + - "deploy docker" + - "deploy fly" + - "deploy k8s" + - "deploy lambda" + - "experiment tree" + - "falsify" + - "hf catalog" + - "hf course" + - "hf info" + - "hf pull" + - "hf push" + - "hf search" + - "hf tree" + - "init" + - "mcp" + - "optimize" + - "oracle" + - "pacha alias" + - "pacha aliases" + - "pacha decrypt" + - "pacha encrypt" + - "pacha keygen" + - "pacha list" + - "pacha pin" + - "pacha prune" + - "pacha pull" + - "pacha rm" + - "pacha run" + - "pacha search" + - "pacha show" + - "pacha sign" + - "pacha stats" + - "pacha unpin" + - "pacha verify" + - "parf" + - "playbook lock" + - "playbook run" + - "playbook status" + - "playbook validate" + - "report" + - "reset" + - "serve" + - "stack check" + - "stack comply" + - "stack drift" + - "stack gate" + - "stack publish-status" + - "stack quality" + - "stack release" + - "stack status" + - "stack sync" + - "stack tree" + - "stack versions" + - "status" + - "transpile" + - "validate" + - "viz dashboard" + - "viz tree" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + # 4 tools, ledgered 2026-09-27 (header); 6 non-stdio mcp:* rows still ledgerOrphan RED: 3 banco-declared, 3 unserved (finding) + - path: bin:mcpTool + minCount: 4 + maxCount: 4 + in: ["hf_info", "hf_integration", "hf_search", "hf_tree"] + - {path: bin:unledgeredTool, maxCount: 0} + # S3 (aprender-1c). LIVE RED 2026-09-26: empty until extract:binary reads the banco router (header) + - path: bin:route + minCount: 92 + maxCount: 92 + in: + - "DELETE /api/delete" + - "DELETE /api/v1/conversations/:id" + - "DELETE /api/v1/data/files/:id" + - "DELETE /api/v1/models/registry/:name" + - "DELETE /api/v1/prompts/:id" + - "DELETE /api/v1/rag/index" + - "DELETE /api/v1/train/runs/:id" + - "GET /" + - "GET /api/tags" + - "GET /api/v1/audio/formats" + - "GET /api/v1/audit" + - "GET /api/v1/batch" + - "GET /api/v1/batch/:id" + - "GET /api/v1/chat/parameters" + - "GET /api/v1/config" + - "GET /api/v1/conversations" + - "GET /api/v1/conversations/:id" + - "GET /api/v1/conversations/export" + - "GET /api/v1/conversations/search" + - "GET /api/v1/data/datasets" + - "GET /api/v1/data/datasets/:id/preview" + - "GET /api/v1/data/files" + - "GET /api/v1/data/files/:id/info" + - "GET /api/v1/data/recipes" + - "GET /api/v1/data/recipes/:id" + - "GET /api/v1/eval/runs" + - "GET /api/v1/eval/runs/:id" + - "GET /api/v1/experiments" + - "GET /api/v1/experiments/:id/compare" + - "GET /api/v1/mcp/info" + - "GET /api/v1/metrics" + - "GET /api/v1/models" + - "GET /api/v1/models/merge/strategies" + - "GET /api/v1/models/registry" + - "GET /api/v1/models/status" + - "GET /api/v1/prompts" + - "GET /api/v1/prompts/:id" + - "GET /api/v1/rag/search" + - "GET /api/v1/rag/status" + - "GET /api/v1/system" + - "GET /api/v1/tools" + - "GET /api/v1/train/presets" + - "GET /api/v1/train/runs" + - "GET /api/v1/train/runs/:id" + - "GET /api/v1/train/runs/:id/metrics" + - "GET /api/v1/ws" + - "GET /assets/*path" + - "GET /health" + - "GET /health/live" + - "GET /health/ready" + - "GET /v1/models" + - "GET /v1/models/:id" + - "PATCH /api/v1/conversations/:id" + - "POST /api/chat" + - "POST /api/generate" + - "POST /api/pull" + - "POST /api/show" + - "POST /api/v1/audio/transcriptions" + - "POST /api/v1/batch" + - "POST /api/v1/chat/completions" + - "POST /api/v1/conversations" + - "POST /api/v1/conversations/import" + - "POST /api/v1/data/recipes" + - "POST /api/v1/data/recipes/:id/run" + - "POST /api/v1/data/upload" + - "POST /api/v1/data/upload/json" + - "POST /api/v1/detokenize" + - "POST /api/v1/embeddings" + - "POST /api/v1/eval/perplexity" + - "POST /api/v1/experiments" + - "POST /api/v1/experiments/:id/runs" + - "POST /api/v1/mcp" + - "POST /api/v1/models/load" + - "POST /api/v1/models/merge" + - "POST /api/v1/models/pull" + - "POST /api/v1/models/unload" + - "POST /api/v1/prompts" + - "POST /api/v1/rag/index" + - "POST /api/v1/tokenize" + - "POST /api/v1/tools" + - "POST /api/v1/tools/execute" + - "POST /api/v1/train/runs/:id/export" + - "POST /api/v1/train/runs/:id/stop" + - "POST /api/v1/train/start" + - "POST /ui/chat" + - "POST /v1/audio/transcriptions" + - "POST /v1/chat/completions" + - "POST /v1/completions" + - "POST /v1/embeddings" + - "PUT /api/v1/chat/parameters" + - "PUT /api/v1/config" + - "PUT /api/v1/tools/:name/config" + - {path: bin:unledgeredRoute, maxCount: 0} + - {path: bin:ledgerOrphanRoute, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-orchestrate)) = C, |C| = 86, C = leaves(--help walk) ∧ tools(mcp tools/list) = T, |T| = 4 ∧ routes(banco router) = R, |R| = 92 ∧ C ∪ T ∪ R = {feature(f) : f ∈ surface_audit, f.binary = aprender-orchestrate}" + domain: "the aprender-orchestrate line of the tracked snapshot and the ledger rows with binary = aprender-orchestrate (86 CLI + 92 HTTP + 6 mcp rows on 2026-09-26)" + codomain: "set equality per kind, or a Fail naming each member of the symmetric difference" + invariants: + - "in + minCount + maxCount over distinct literals is set equality: an added, dropped or renamed command, tool or route is a violation naming it" + - "one path served under two methods is two bin:route literals, `METHOD /path`, axum :param segments verbatim" + - "a CLI row joins C, a row whose feature starts with an HTTP method joins R, an mcp row joins T: no row is judged twice" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "an empty bin:route is a minCount Fail, never a pass over an empty set" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-ORCHESTRATE-INV-001 + property: every aprender-orchestrate command, MCP tool and route has an audited ledger row, and every ledger row names one + formal: '∀ s ∈ C ∪ T ∪ R: ∃ r ∈ surface_audit : r.binary = aprender-orchestrate ∧ feature(r) = s; ∀ r with r.binary = aprender-orchestrate: feature(r) ∈ C ∪ T ∪ R' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-ORCHESTRATE-001 + rule: 'G1.2 help answers' + prediction: '`aprender-orchestrate --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-orchestrate' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-ORCHESTRATE-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-orchestrate --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-orchestrate' + if_fails: 'a typo is silently ignored' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-ORCHESTRATE-003 + rule: 'G0.1 the version names the binary and the commit' + prediction: '`aprender-orchestrate --version` starts with "aprender-orchestrate x.y.z" and contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale aprender-orchestrate reads as HEAD. Was LIVE RED 2026-09-26 ("batuta 0.69.3"); name fixed 2026-09-27, sha still absent until #4219 reaches this branch' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-ORCHESTRATE-004 + rule: 'G1.3/G1.4 the command, tool and route sets equal the snapshot' + prediction: 'commands(snapshot) = the 86 literals above; tools/list = the 4; routes = the 92' + test: 'NOT YET WRITTEN — pv lint contracts/ --gate shapes on the snapshot, after extract:binary (ONT-4g) reads tools/list and the banco router' + if_fails: 'LIVE RED 2026-09-26: bin:route is empty (extract gap), so minCount 92 fails; a command could vanish with the ontology green if the walk were not pinned' + mutation: 'remove `stack gate` from the snapshot, or DELETE /api/delete from the routes; must turn RED naming it on bin:command/bin:route and on the orphan property' + - id: FALSIFY-BIN-APRENDER-ORCHESTRATE-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has a row per command, tool and route with binary = aprender-orchestrate' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: '4 MCP tools unledgered 2026-09-26, ledgered 2026-09-27. STILL LIVE RED: 6 mcp:* rows are not stdio tools/list tools. 3 declare banco POST /api/v1/mcp (clears when extract:binary honours the declared transport); 3 (agent registry) no transport serves, recorded as finding c1-ont10-orchestrate-agent-mcp-tools-unserved' + mutation: 'delete every aprender-orchestrate row; must turn RED naming aprender-orchestrate' diff --git a/contracts/bin-aprender-present-cli--aprender-present-v1.yaml b/contracts/bin-aprender-present-cli--aprender-present-v1.yaml new file mode 100644 index 0000000000..fe922314bb --- /dev/null +++ b/contracts/bin-aprender-present-cli--aprender-present-v1.yaml @@ -0,0 +1,130 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `presentar` -> `aprender-present`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-present-cli--aprender-present-v1 — the `aprender-present` bin target of package aprender-present-cli (ONT-10 slice S14 (2/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-present-v1 — ONT-10 slice S14 (2/2): the `aprender-present` binary (package aprender-present-cli) as an entity (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ ff9587b596, release build, default features, LC_ALL=C recursive --help walk): +# - `aprender-present --version` = "aprender-present 0.69.3"; G0.1 (git sha) is a warning and RED today. +# - the walk gives 7 leaves, set-equal to surface_audit.csv's 7 rows. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-present-cli--aprender-present-v1 +version: "1.0.0" +scope: > + The built `aprender-present` target of package `aprender-present-cli` (default features) as the focus node binary/aprender-present-cli/aprender-present: its identity and its exact command surface, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S14 (2/2) (aprender-present). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-present-cli/aprender-present, on the binary-surface-v1 vocabulary. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'crates/aprender-present-cli/src/main.rs' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-present-cli/aprender-present + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-present-surface + targetClass: bin:target/aprender-present-cli/aprender-present + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-present"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-present-cli"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-present [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:command + minCount: 7 + maxCount: 7 + in: + - "bundle" + - "check" + - "deploy" + - "gate" + - "new" + - "score" + - "serve" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-present-cli/aprender-present) = C, |C| = 7, where C = the invocable command paths of the recursive --help walk of the default-feature build ∧ ledgerOrphan = { r ∈ csv : r.binary = aprender-present ∧ strip(r.feature) ∉ C }; unledgeredCommand = C ∖ { strip(r.feature) : r.binary = aprender-present }" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is aprender-present; strip removes the leading binary name" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount 7 + maxCount 7 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "a ledger row for a command the built binary does not expose is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-present --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-PRESENTAR-INV-001 + property: every aprender-present surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-present): ∃ r ∈ surface_audit : r.binary = aprender-present ∧ path(r) = c; ∀ r with r.binary = aprender-present: path(r) ∈ surface(aprender-present)' + prose: false + - id: BIN-PRESENTAR-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-present-cli/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-PRESENTAR-001 + rule: 'G1.2 help answers' + prediction: '`aprender-present --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-present-cli --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-PRESENTAR-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-present --no-such-flag` exits 2' + test: 'cargo test -p aprender-present-cli --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-PRESENTAR-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-present --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-present 0.69.3" — no sha; a stale aprender-present reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-PRESENTAR-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the surface literals in the shape above = the snapshot''s, set-equal' + test: 'cargo test -p aprender-present-cli --test cli_surface_gate -- advertised_subcommands_are_the_pinned_surface' + if_fails: 'an added, dropped or renamed surface member ships with the ontology green (enumerated-value parsing not probed for this binary)' + mutation: 'remove `bundle` from the snapshot; must turn RED on bin:command and bin:ledgerOrphan naming it' + - id: FALSIFY-BIN-PRESENTAR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-present (7 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-present row; must turn RED naming aprender-present' + - id: FALSIFY-BIN-PRESENTAR-006 + rule: 'unusable input is rejected cleanly' + prediction: '`check`, `score` and `gate` on a missing, empty and garbage manifest: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-present-cli --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 1 with no panic, GREEN; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-present-terminal--aprender-ptop-v1.yaml b/contracts/bin-aprender-present-terminal--aprender-ptop-v1.yaml new file mode 100644 index 0000000000..94f913f07e --- /dev/null +++ b/contracts/bin-aprender-present-terminal--aprender-ptop-v1.yaml @@ -0,0 +1,158 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `ptop` -> `aprender-ptop`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-present-terminal--aprender-ptop-v1 — the `aprender-ptop` bin target of package aprender-present-terminal (ONT-10 slice S13 (2/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-ptop-v1 — ONT-10 slice S13 (2/2): the `aprender-ptop` target of package `aprender-present-terminal` as an entity +# (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ b6cf6d2ed, release build, private CARGO_TARGET_DIR). aprender-ptop declares +# `required-features = ["aprender-ptop"]` and the package's default features are empty, so a default build does not produce +# it at all; it was built with `--features aprender-ptop`, the smallest build that has it. +# `aprender-ptop --version` = "aprender-ptop 0.69.3". Name right; no git sha, so G0.1 (warning) is RED today. +# (G0.1 FIXED 2026-09-27 on batch/ont-10: `aprender-ptop 0.69.3 ()`, semver still field 2; the shape is now a violation.) +# --help: no subcommands (0 commands) and 10 long options, --help/--version excluded: --config, --deterministic, +# --dump-config, --explode, --height, --no-color, --qa-timing, --refresh, --render-once, --width. The --help +# description line is the library's ("Terminal backend for Presentar UI framework…"), not aprender-ptop's; not shaped. +# surface_audit.csv holds 4 rows with binary=aprender-ptop (ledger at cf038d37f): the bare entry row `aprender-ptop (interactive TUI +# system monitor)`, and `aprender-ptop --dump-config`, `aprender-ptop --render-once`, `aprender-ptop --render-once --explode `. Every +# option a row names is in --help: GREEN (G0.1 too, since the fix above). +# +# CONVENTION (S15's option join, plus one rule): a row whose feature after the binary name starts with `-` joins +# bin:option on its first long option; a row that is the bare binary name (optionally with a parenthetical) is the +# default mode and resolves to the focus node itself. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-present-terminal--aprender-ptop-v1 +version: "1.0.0" +scope: > + The built `aprender-ptop` target of package `aprender-present-terminal` (features = ["aprender-ptop"], its required feature) as the focus node binary/aprender-present-terminal/aprender-ptop: its identity, its options, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S13 (2/2) (aprender-ptop). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-present-terminal/aprender-ptop, on the binary-surface-v1 vocabulary. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = aprender-ptop)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'crates/aprender-present-terminal/Cargo.toml ([[bin]] aprender-ptop, required-features = ["aprender-ptop"])' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-present-terminal/aprender-ptop + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-ptop-surface + targetClass: bin:target/aprender-present-terminal/aprender-ptop + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-ptop"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-present-terminal"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-ptop [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — a violation since batch/ont-10 2026-09-27 (build.rs embeds APR_GIT_SHA); was a warning while RED + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}"} + - {path: "bin:command", maxCount: 0} + - path: bin:option + minCount: 10 + maxCount: 10 + in: + - "--config" + - "--deterministic" + - "--dump-config" + - "--explode" + - "--height" + - "--no-color" + - "--qa-timing" + - "--refresh" + - "--render-once" + - "--width" + - {path: "bin:ledgerOrphan", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-present-terminal/aprender-ptop) = ∅; options = O, |O| = 10, O = the long options of --help of the --features aprender-ptop build, --help and --version excluded ∧ ledgerOrphan = { r ∈ csv : r.binary = aprender-ptop ∧ key(r) ≠ ⊤ ∧ key(r) ∉ O }; key(r) = ⊤ (the node) for the bare entry row, else the first long option of r.feature" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is aprender-ptop (4 on 2026-09-26)" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row" + invariants: + - "in + minCount 10 + maxCount 10 is set equality on options: an added, dropped or renamed option is a violation naming it" + - "a ledgered option the built binary does not accept is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-ptop --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-PTOP-INV-001 + property: every aprender-ptop surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-ptop): ∃ r ∈ surface_audit : r.binary = aprender-ptop ∧ path(r) = c; ∀ r with r.binary = aprender-ptop: path(r) ∈ surface(aprender-ptop)' + prose: false + - id: BIN-PTOP-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-present-terminal/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-PTOP-001 + rule: 'G1.2 help answers' + prediction: '`aprender-ptop --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-present-terminal --features aprender-ptop --test cli_surface_gate advertised_options_are_the_pinned_surface' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-PTOP-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-ptop --no-such-flag` exits 2' + test: 'cargo test -p aprender-present-terminal --features aprender-ptop --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-PTOP-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-ptop --version` contains the first 9 hex of the git sha it was built from' + test: 'crates/aprender-present-terminal/src/bin/aprender-ptop.rs tests::version_flag_names_the_commit; the shape: bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale aprender-ptop reads as HEAD (observed 2026-09-26 at b6cf6d2ed: "aprender-ptop 0.69.3", no sha; fixed on batch/ont-10: build.rs embeds APR_GIT_SHA, same resolver as pv)' + mutation: 'set VERSION back to CARGO_PKG_VERSION alone; the unit test turns RED (field 3 missing)' + - id: FALSIFY-BIN-PTOP-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: '`aprender-ptop --explode nosuch` exits 2, and the surface literals in the shape above = the snapshot''s, set-equal' + test: 'cargo test -p aprender-present-terminal --features aprender-ptop --test cli_surface_gate -- advertised_options_are_the_pinned_surface malformed_arguments_are_usage_errors' + if_fails: 'an added, dropped or renamed surface member ships with the ontology green ; a misspelt --explode panel rendered the whole screen with exit 0 (observed 2026-09-26 at b6cf6d2ed; fixed on batch/ont-10: parse_panel_type is the clap value_parser, exit 2)' + mutation: 'map the unknown-panel arm of parse_panel_type to Ok(PanelType::Cpu); the unit test turns RED (checked 2026-09-27). Remove one surface literal from the snapshot; must turn RED naming it' + - id: FALSIFY-BIN-PTOP-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-ptop (4 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-ptop row; must turn RED naming aprender-ptop' + - id: FALSIFY-BIN-PTOP-006 + rule: 'ledgered option removed is red' + prediction: 'deleting --render-once from the snapshot''s option list makes aprender-ptop-options RED: |O| = 9 and the two --render-once rows become bin:ledgerOrphan' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on the snapshot with --render-once removed' + if_fails: 'aprender-ptop''s headless render mode, the one CI can exercise, could vanish with the ontology green.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail, results on binary/aprender-present-terminal/aprender-ptop paths bin:option and bin:ledgerOrphan' + - id: FALSIFY-BIN-PTOP-007 + rule: 'default feature build has no node' + prediction: 'an extractor that builds aprender-present-terminal with default features produces no aprender-ptop target, so no focus node exists and the 4 ledger rows resolve to nothing' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on a snapshot from a default-feature build' + if_fails: 'A binary the ledger lists could drop out of the ontology silently because its feature is opt-in.' + mutation: 'the edit in the prediction; must turn RED — the ONT-4g surface_audit_bins gate names aprender-ptop as a ledgered binary with no node' + - id: FALSIFY-BIN-PTOP-008 + rule: 'unusable input is rejected cleanly' + prediction: 'a non-numeric `--width`, `--refresh` and `--height` (aprender-ptop reads no input file it could reject: a missing or garbage `-c` config warns and falls back to the defaults): each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-present-terminal --features aprender-ptop --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 2, GREEN. `-c --render-once` exits 0 with a warning — NOT asserted, recorded as finding a01-ont10-ptop-config-falls-back' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-present-terminal--aprender-score-v1.yaml b/contracts/bin-aprender-present-terminal--aprender-score-v1.yaml new file mode 100644 index 0000000000..8ba2c3e862 --- /dev/null +++ b/contracts/bin-aprender-present-terminal--aprender-score-v1.yaml @@ -0,0 +1,129 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `score` -> `aprender-score`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-present-terminal--aprender-score-v1 — the `aprender-score` bin target of package aprender-present-terminal +# (ONT-10 slice S12, 2/2) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-27, batch/ont-10 @ acc3827954, release build, private CARGO_TARGET_DIR, LC_ALL=C). aprender-score declares +# `required-features = ["aprender-score"]` and the package's default features are empty, so a default build does not produce +# it; it was built with `--features aprender-score`, the smallest build that has it (as S13 does for ptop). +# `aprender-score --version` = "aprender-score 0.69.3": name right, no sha. LIVE RED on G0.1. +# `--help` exits 0; `--no-such-flag` exits 2; `--output nosuch` exits 2 (clap possible values, G1.3 GREEN). +# --help: 0 commands, one optional positional [PATH], 7 long options (--help/--version excluded): --ci, --config, +# --no-color, --output, --quiet, --threshold, --verbose. +# surface_audit.csv holds 2 rows: `aprender-score [PATH] (TUI-crate quality scoring)` keys on positional PATH, and +# `aprender-score --ci --threshold (…)` keys on --ci. Both resolve: GREEN apart from G0.1. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-present-terminal--aprender-score-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-score` binary (package aprender-present-terminal, src + crates/aprender-present-terminal/src/bin/aprender-score.rs): its help, its usage errors, its version string, its options, + its positional, and its surface-audit rows. Out of scope: how the quality aprender-score is computed. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-score answers --help, rejects an unknown flag or output format as a usage error, names the commit it was built + from, exposes exactly the options the snapshot records, and has an audited ledger row for each mode. The + extractor must build it with its required feature `aprender-score`, or there is no node. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-present-terminal/src/bin/aprender-score.rs — the clap Args' + - 'crates/aprender-present-terminal/Cargo.toml — [[bin]] aprender-score, required-features = ["aprender-score"]' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-score' + - 'aprender#4502 — ONT-10, slice S12' + +entity: + type: binary + ref: binary/aprender-present-terminal/aprender-score + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-score-surface + targetClass: bin:target/aprender-present-terminal/aprender-score + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-score"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-present-terminal"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-score [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - {path: bin:command, maxCount: 0} + - path: bin:option + minCount: 7 + maxCount: 7 + in: + - "--ci" + - "--config" + - "--no-color" + - "--output" + - "--quiet" + - "--threshold" + - "--verbose" + - {path: bin:positional, minCount: 1, maxCount: 1, in: ["PATH"]} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-score)) = ∅ ∧ options(snapshot) = {--ci, --config, --no-color, --output, --quiet, --threshold, --verbose} ∧ positionals = {PATH} ∧ key(r) ∈ options ∪ {PATH} for every ledger row r with r.binary = aprender-score" + domain: "the aprender-score line of the tracked snapshot (a --features aprender-score build) and the 2 ledger rows with binary = aprender-score" + codomain: "set equality on options, or a Fail naming each option or orphan row" + invariants: + - "an added, dropped or renamed long option is a violation naming it" + - "a ledgered option the built binary does not accept is an orphan, never a silent skip" + preconditions: + - "the snapshot was written at this commit from a build with feature aprender-score" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-score --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-SCORE-INV-001 + property: every aprender-score ledger row keys on an option or positional the built binary accepts + formal: '∀ r ∈ surface_audit, r.binary = aprender-score: key(r) ∈ options(aprender-score) ∪ positionals(aprender-score)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-SCORE-001 + rule: 'G1.2 help answers' + prediction: '`aprender-score --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-score' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make main exit 1 on DisplayHelp; must turn RED' + - id: FALSIFY-BIN-SCORE-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-score --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-score' + if_fails: 'a CI wrapper cannot tell a typo from a below-threshold aprender-score, which also exits nonzero (observed 2026-09-27: exit 2, GREEN)' + mutation: 'map every clap error to exit 1; must turn RED' + - id: FALSIFY-BIN-SCORE-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-score --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-score 0.69.3" — no sha; a stale binary reads as HEAD' + mutation: 'drop the sha from the clap version string; must turn RED' + - id: FALSIFY-BIN-SCORE-004 + rule: 'G1.3 enumerated values are parsed by clap' + prediction: '`aprender-score --output nosuch` exits 2 naming text, json, yaml' + test: 'scripts/dogfood_surfaces.sh --cli aprender-score' + if_fails: 'an unknown output format would fail later, or print text anyway (observed 2026-09-27: exit 2, GREEN)' + mutation: 'turn --output into a String; must turn RED' + - id: FALSIFY-BIN-SCORE-005 + rule: 'the option set equals the snapshot, and the extractor builds the required feature' + prediction: 'options = the 7 above; a default-feature snapshot has no aprender-score node, which the surface_audit_bins gate names as a ledgered binary with no node' + test: 'pv lint contracts/ --gate shapes on the snapshot (ONT-4g extract:binary); scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-score could drop out of the ontology silently because its feature is opt-in' + mutation: 'delete --ci from the snapshot; must turn RED on bin:option (6) and bin:ledgerOrphan naming `aprender-score --ci --threshold `' diff --git a/contracts/bin-aprender-profile--aprender-profile-v1.yaml b/contracts/bin-aprender-profile--aprender-profile-v1.yaml new file mode 100644 index 0000000000..bd2e34fd88 --- /dev/null +++ b/contracts/bin-aprender-profile--aprender-profile-v1.yaml @@ -0,0 +1,135 @@ +# ────────────────────────────────────────────── +# bin-aprender-profile--aprender-profile-v1 — the `aprender-profile` bin target of package aprender-profile +# (ONT-10 slice S15, 1/2) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, release build, default features, private CARGO_TARGET_DIR): +# `aprender-profile --version` = "renacer 0.69.3": the pre-monorepo name and no sha. LIVE RED on the name pattern +# and on G0.1. +# FIXED on batch/ont-10 (2026-09-27): clap name aprender-profile (measured after the fix: "aprender-profile 0.69.3"). +# `--help` exits 0; `--no-such-flag` exits 2; `--format nosuch` exits 2. +# The --help walk gives 1 command (`validate`; clap's `help` excluded), 46 long options (--help/--version +# excluded) and a trailing `-- ...` (trace a program). +# surface_audit.csv holds 16 rows: 1 command row (validate), 1 trace-mode row (`-- `), 14 option rows. +# +# VOCABULARY EXTENSION ASKED OF binary-surface-v1 (aprender-88) AND extract:binary (aprender-98): this binary's +# surface is mostly OPTIONS. bin:option (one literal per long option) and bin:positional (one per Usage +# positional) are not in the shared vocabulary yet. A ledger row whose feature starts with `-` keys on its long +# form (`-p ` → --pid); a row starting `-- ` keys on positional COMMAND. Joined against bin:command alone, +# 15 of the 16 rows would read as ledgerOrphan. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-profile--aprender-profile-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-profile` binary (package aprender-profile): its help, its usage errors, its + version string, its command set, its long options, and its surface-audit rows. Out of scope: what a trace or a + profile computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-profile answers --help, rejects an unknown flag as a usage error, names itself and the commit it was + built from, exposes exactly the command and options the snapshot records, and has an audited ledger row for + each ledgered mode. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-profile/src/cli.rs — the clap Cli struct' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-profile' + - 'aprender#4502 — ONT-10, slice S15' + +entity: + type: binary + ref: binary/aprender-profile/aprender-profile + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-profile-surface + targetClass: bin:target/aprender-profile/aprender-profile + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-profile"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-profile"]} + # was RED at b1cd8c0b0 ("renacer 0.69.3"); FIXED on batch/ont-10 (clap name = aprender-profile) + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-profile [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-26 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - {path: bin:command, minCount: 1, maxCount: 1, in: ["validate"]} + # vocabulary extension (header): 46 long options counted exactly; the ledger names 14, the other 32 tune a + # ledgered mode (e.g. --dl-epochs under --dl-anomaly), so no unledgered check applies to options + - {path: bin:option, minCount: 46, maxCount: 46} + - {path: bin:positional, minCount: 1, maxCount: 1, in: ["COMMAND"]} + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-profile)) = {validate} ∧ |options(snapshot)| = 46 ∧ positionals = {COMMAND} ∧ key(r) ∈ {validate} ∪ options ∪ {COMMAND} for every ledger row r with r.binary = aprender-profile" + domain: "the aprender-profile line of the tracked snapshot and the 16 ledger rows with binary = aprender-profile" + codomain: "set equality on commands, an exact option count, or a Fail naming each orphan row" + invariants: + - "a new or renamed subcommand is a violation naming it; an added or dropped long option is RED until re-measured" + - "`--save-model / --load-model` keys on --save-model; both are options" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "key(r) = the command, the long form of the first option, or COMMAND for a `-- ` row" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-PROFILE-INV-001 + property: every aprender-profile ledger row names a command, option or positional the built binary accepts + formal: '∀ r ∈ surface_audit : r.binary = aprender-profile ⇒ key(r) ∈ commands ∪ options ∪ positionals' + prose: false + - id: BIN-APRENDER-PROFILE-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-profile/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-PROFILE-001 + rule: 'G1.2 help answers' + prediction: '`aprender-profile --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-profile --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-PROFILE-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-profile --no-such-flag` exits 2' + test: 'cargo test -p aprender-profile --test cli_surface_gate unknown_' + if_fails: 'a typo would be traced as a program argument (observed 2026-09-26: exit 2, GREEN)' + mutation: 'mark the trailing COMMAND trailing_var_arg + allow_hyphen_values; must turn RED' + - id: FALSIFY-BIN-APRENDER-PROFILE-003 + rule: 'G0.1 the version names the binary and the commit' + prediction: '`aprender-profile --version` starts with "aprender-profile " and contains the first 9 hex of the git sha' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'name: FIXED on batch/ont-10 (was "renacer 0.69.3"); sha: LIVE RED on 0.69.3, clears at rc.1 (car/0.70.0 carries #4219)' + mutation: 'restore the renacer name after a fix; must turn RED on bin:versionString' + - id: FALSIFY-BIN-APRENDER-PROFILE-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the surface equals the snapshot' + prediction: '`aprender-profile --format nosuch` exits 2; the snapshot has exactly {validate}, 46 options, {COMMAND}' + test: 'cargo test -p aprender-profile --test cli_surface_gate -- advertised_subcommands_are_the_pinned_surface malformed_arguments_are_usage_errors' + if_fails: 'a profiling mode could be added or dropped unmodelled (observed 2026-09-26: --format exits 2, GREEN)' + mutation: 'delete --chaos from the snapshot; must turn RED on bin:option (45) and bin:ledgerOrphan naming `--chaos `' + - id: FALSIFY-BIN-APRENDER-PROFILE-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-profile (16 on 2026-09-26), each keying on the surface' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a ledgered profiling mode could vanish from the shipped binary with the ontology green' + mutation: 'delete every aprender-profile row; must turn RED naming aprender-profile' + - id: FALSIFY-BIN-APRENDER-PROFILE-006 + rule: 'unusable input is rejected cleanly' + prediction: '`validate --baseline -- true` on a missing, empty and garbage path: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-profile --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 2 ("Baseline not found", the documented code) with no panic, GREEN; an existing FILE is also reported "not found" because a baseline is a directory' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-ptx-debug--aprender-ptx-debug-v1.yaml b/contracts/bin-aprender-ptx-debug--aprender-ptx-debug-v1.yaml new file mode 100644 index 0000000000..db490f1c31 --- /dev/null +++ b/contracts/bin-aprender-ptx-debug--aprender-ptx-debug-v1.yaml @@ -0,0 +1,128 @@ +# ────────────────────────────────────────────── +# bin-aprender-ptx-debug--aprender-ptx-debug-v1 — the `aprender-ptx-debug` bin target of package aprender-ptx-debug +# (ONT-10 slice S18, 1/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-27, batch/ont-10 @ 8e88b8fc5d, release build, default features, private CARGO_TARGET_DIR): +# `--version` = "aprender-ptx-debug 0.69.3": name right, no sha (G0.1 LIVE RED). +# `--help` exits 0. `--no-such-flag` exits 1, not 2 (G1.2 LIVE RED): src/cli.rs exit_code_for_parse_error maps every +# parse failure except help/version to 1 on purpose ("preserving the exit status" of the pre-clap parser), and +# tests/cli_args.rs pins it. Turning it GREEN is a code + test change, not a contract edit. +# FIXED in the same PR: usage errors now exit 2 (measured after the fix: `--no-such-flag` rc=2). +# Commands: analyze, gen-fkr, version (clap's `help` excluded); no nested subcommands; no enumerated flag values. +# surface_audit.csv: 3 rows, set-equal to the commands. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-ptx-debug--aprender-ptx-debug-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-ptx-debug` binary (package aprender-ptx-debug, src + crates/aprender-ptx-debug/src/bin/main.rs): its help, its usage errors, its version string, its subcommand set, and + its surface-audit rows. Out of scope: what the PTX analysis computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-ptx-debug answers --help, rejects an unknown flag as a usage error, names the commit it was built from, + exposes exactly the subcommands the snapshot records, and has an audited ledger row for each. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-ptx-debug/src/cli.rs — the clap Cli and exit_code_for_parse_error' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-ptx-debug' + - 'aprender#4502 — ONT-10, slice S18' + +entity: + type: binary + ref: binary/aprender-ptx-debug/aprender-ptx-debug + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-ptx-debug-surface + targetClass: bin:target/aprender-ptx-debug/aprender-ptx-debug + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-ptx-debug"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-ptx-debug"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-ptx-debug [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 3 + maxCount: 3 + in: + - "analyze" + - "gen-fkr" + - "version" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-ptx-debug)) = subcommands(clap(Cli)) = {analyze, gen-fkr, version} ∧ commands ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-ptx-debug}" + domain: "the aprender-ptx-debug line of the tracked snapshot, the clap Command enum, and the 3 ledger rows" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-ptx-debug --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-PTX-DEBUG-INV-001 + property: every aprender-ptx-debug subcommand has an audited ledger row + formal: '∀ c ∈ commands(aprender-ptx-debug): |{r ∈ surface_audit : r.binary = aprender-ptx-debug ∧ r.feature = "aprender-ptx-debug " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-PTX-DEBUG-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-ptx-debug/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-001 + rule: 'G1.2 help answers' + prediction: '`aprender-ptx-debug --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-ptx-debug --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'map DisplayHelp to 1 in exit_code_for_parse_error; must turn RED' + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-ptx-debug --no-such-flag` exits 2' + test: 'cargo test -p aprender-ptx-debug --test cli_surface_gate unknown_' + if_fails: 'a wrapper cannot tell a typo from an analysis failure (was RED at 8e88b8fc5d: exit 1 by design; FIXED on batch/ont-10: usage errors exit 2, a failed analysis keeps 1; tests/cli_args.rs + cli_binary.rs pin it)' + mutation: 'map the usage-error arm of exit_code_for_parse_error back to 1; cli_args.rs and cli_binary.rs must turn RED' + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-ptx-debug --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-ptx-debug 0.69.3" — no sha; a stale binary reads as HEAD' + mutation: 'drop the sha from version_string(); must turn RED' + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: 'no flag takes an enumerated value (observed 2026-09-27); subcommands(clap) = {analyze, gen-fkr, version}' + test: 'cargo test -p aprender-ptx-debug --test cli_surface_gate -- advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand could be added or dropped unmodelled' + mutation: 'add a subcommand without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per command with binary = aprender-ptx-debug (3 on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the binary would ship unaudited (observed 2026-09-27: 3/3, GREEN)' + mutation: 'delete every aprender-ptx-debug row; must turn RED naming aprender-ptx-debug' + - id: FALSIFY-BIN-APRENDER-PTX-DEBUG-006 + rule: 'unusable input is rejected cleanly' + prediction: '`analyze` and `gen-fkr` on a missing, empty and garbage file: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-ptx-debug --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): before the fix `analyze` on an empty file printed "Score: 97.1/100" with F001–F003 failed and exited 0; with the fix reverted this test is RED; fixed (a module with no .version is refused), GREEN' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-qa-certify--aprender-qa-readme-sync-v1.yaml b/contracts/bin-aprender-qa-certify--aprender-qa-readme-sync-v1.yaml new file mode 100644 index 0000000000..73ad9c3832 --- /dev/null +++ b/contracts/bin-aprender-qa-certify--aprender-qa-readme-sync-v1.yaml @@ -0,0 +1,109 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `apr-qa-readme-sync` -> `aprender-qa-readme-sync`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-qa-certify--aprender-qa-readme-sync-v1 — the `aprender-qa-readme-sync` bin target of package aprender-qa-certify +# (ONT-10 slice S19 3/3) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml (ONT-4g, dafb8194f). +# MEASURED 2026-09-26, batch/ont-10 @ dafb8194f, release build, default features: +# --help rc 0; --no-such-flag rc 2; --version = "aprender-qa-readme-sync 0.69.3"; +# ROOT-ONLY: 0 subcommands; options --csv [docs/certifications/models.csv], --readme [README.md]; +# one ledger row, keyed as the bare binary: "aprender-qa-readme-sync (README certification-table sync)". +# G0.1 was LIVE RED 2026-09-26 (no sha); GREEN 2026-09-27 (build.rs embeds APR_GIT_SHA). No flag takes an enumerated value, so G1.3 is vacuous here. +# Root-only convention (as the staged zram-generator slice): bin:command maxCount 0; the root row is the ledger surface. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-qa-certify--aprender-qa-readme-sync-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-qa-readme-sync` binary (package aprender-qa-certify): its help, its usage errors, + its version string, the absence of subcommands, and its surface-audit row. Out of scope: the table it writes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-qa-readme-sync answers --help, rejects an unknown flag as a usage error, names the commit it was built + from, exposes no subcommand, and has an audited ledger row for its root surface. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-qa-certify/src/main.rs — the clap Args struct of the aprender-qa-readme-sync bin' + - 'docs/audits/surface_audit.csv — the row whose binary column is aprender-qa-readme-sync' + - 'aprender#4502 — ONT-10, slice S19' + +entity: + type: binary + ref: binary/aprender-qa-certify/aprender-qa-readme-sync + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-qa-readme-sync-surface + targetClass: bin:target/aprender-qa-certify/aprender-qa-readme-sync + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-qa-readme-sync"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-qa-certify"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-qa-readme-sync [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — GREEN 2026-09-27: build.rs embeds APR_GIT_SHA + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}"} + # root-only: no subcommand + - {path: bin:command, maxCount: 0} + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-qa-readme-sync)) = subcommands(clap(Args)) = ∅ ∧ |{f ∈ surface_audit : f.binary = aprender-qa-readme-sync}| ≥ 1" + domain: "the aprender-qa-readme-sync line of the tracked snapshot, the clap Args struct, and the ledger rows with binary = aprender-qa-readme-sync" + codomain: "both sets empty with a root ledger row, or a Fail naming each subcommand that appeared" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-qa-readme-sync --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APR-QA-README-SYNC-INV-001 + property: the root surface of aprender-qa-readme-sync has an audited ledger row + formal: '|{r ∈ surface_audit : r.binary = aprender-qa-readme-sync}| ≥ 1 ∧ commands(aprender-qa-readme-sync) = ∅' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APR-QA-README-SYNC-001 + rule: 'G1.2 help answers' + prediction: '`aprender-qa-readme-sync --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-qa-readme-sync' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APR-QA-README-SYNC-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-qa-readme-sync --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-qa-readme-sync' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APR-QA-README-SYNC-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-qa-readme-sync --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'the build cannot be traced to a commit (was LIVE RED 2026-09-26: `aprender-qa-readme-sync 0.69.3`, no sha; GREEN 2026-09-27: `aprender-qa-readme-sync 0.69.3 (2d367b4d98)`)' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APR-QA-README-SYNC-004 + rule: 'G1.3/G1.4 enumerated values are parsed by clap and the command set equals the snapshot' + prediction: 'subcommands(clap) = ∅ = commands(snapshot); --csv and --readme take paths, not enumerated values' + test: 'NOT YET WRITTEN — S19 (aprender-5d)' + if_fails: 'a subcommand shipped without being snapshotted (observed 2026-09-26: 0 = 0, GREEN)' + mutation: 'add a subcommand without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APR-QA-README-SYNC-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-qa-readme-sync (1 on 2026-09-26, the root row)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a shipped binary has no audited row (observed 2026-09-26: 1 root row, GREEN)' + mutation: 'delete the aprender-qa-readme-sync row; must turn RED naming aprender-qa-readme-sync' diff --git a/contracts/bin-aprender-qa-cli--apr-qa-v1.yaml b/contracts/bin-aprender-qa-cli--apr-qa-v1.yaml new file mode 100644 index 0000000000..3d687dfd53 --- /dev/null +++ b/contracts/bin-aprender-qa-cli--apr-qa-v1.yaml @@ -0,0 +1,137 @@ +# ────────────────────────────────────────────── +# bin-aprender-qa-cli--apr-qa-v1 — the `apr-qa` bin target of package aprender-qa-cli (ONT-10 slice S16 (1/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-apr-qa-v1 — ONT-10 slice S16 (1/2): the `apr-qa` binary as an entity (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, release build, default features): `apr-qa --version` = +# "apr-qa 0.69.3"; the --help walk gives 15 leaf commands, and surface_audit.csv has exactly those 15 rows +# (set-equal). G0.1 (--version carries the git sha) is a warning here and RED today. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-qa-cli--apr-qa-v1 +version: "1.0.0" +scope: > + The built `apr-qa` target of package `aprender-qa-cli` (default features) as the focus node binary/aprender-qa-cli/apr-qa: its identity and its exact CLI command set, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S16 (1/2) (apr-qa). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-qa-cli/apr-qa, on the binary-surface-v1 vocabulary. + references: + - 'crates/aprender-qa-cli/tests/cli_surface_gate.rs — the gate falsifiers 001, 002, 004 and 006 run' + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = apr-qa)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-qa-cli/apr-qa + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-apr-qa-surface + targetClass: bin:target/aprender-qa-cli/apr-qa + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["apr-qa"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-qa-cli"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^apr-qa [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:command + minCount: 15 + maxCount: 15 + in: + - "bootstrap" + - "certify" + - "export-csv" + - "export-evidence" + - "generate" + - "kernel-coverage" + - "list" + - "lock-playbooks" + - "parity" + - "report" + - "run" + - "score" + - "tickets" + - "tools" + - "validate-contract" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-qa-cli/apr-qa) = C, |C| = 15, where C = the leaves of the recursive --help walk of the default-feature build ∧ ledgerOrphan = { r ∈ csv : r.binary = apr-qa ∧ r.feature ∉ C }; unledgeredCommand = C ∖ { r.feature : r.binary = apr-qa }" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is apr-qa" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount 15 + maxCount 15 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "a ledger row for a command the built binary does not expose is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `apr-qa --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APR-QA-INV-001 + property: every apr-qa surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(apr-qa): ∃ r ∈ surface_audit : r.binary = apr-qa ∧ path(r) = c; ∀ r with r.binary = apr-qa: path(r) ∈ surface(apr-qa)' + prose: false + + - id: BIN-APR-QA-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-qa-cli/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APR-QA-001 + rule: 'G1.2 help answers' + prediction: '`apr-qa --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-qa-cli --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APR-QA-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`apr-qa --no-such-flag` and `apr-qa no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-qa-cli --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APR-QA-003 + rule: 'G0.1 the version names the commit' + prediction: '`apr-qa --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "apr-qa 0.69.3" — no sha; a stale apr-qa reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APR-QA-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly certify, run, tools, generate, score, report, list, lock-playbooks, tickets, parity, export-csv, export-evidence, bootstrap, validate-contract, kernel-coverage' + test: 'cargo test -p aprender-qa-cli --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APR-QA-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = apr-qa (15 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every apr-qa row; must turn RED naming apr-qa' + - id: FALSIFY-BIN-APR-QA-006 + rule: 'unusable input is rejected cleanly' + prediction: '`score --model m`, `report --model m`, `run` and `validate-contract` on a missing, empty and garbage input: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-qa-cli --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN on an unmutated tree; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-rag-cli--aprender-rag-v1.yaml b/contracts/bin-aprender-rag-cli--aprender-rag-v1.yaml new file mode 100644 index 0000000000..8ce4eef6fc --- /dev/null +++ b/contracts/bin-aprender-rag-cli--aprender-rag-v1.yaml @@ -0,0 +1,133 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `trueno-rag` -> `aprender-rag`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-rag-cli--aprender-rag-v1 — the `aprender-rag` bin target of package aprender-rag-cli +# (ONT-10 slice S12, 1/2) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-27, batch/ont-10 @ acc3827954, `cargo build --release -p aprender-rag-cli --bin aprender-rag`, +# default features = [], private CARGO_TARGET_DIR, LC_ALL=C): +# `aprender-rag --version` = "aprender-rag 0.69.3": name right, no sha. LIVE RED on G0.1. +# `--help` exits 0; `--no-such-flag` exits 2. +# The --help walk gives 6 commands (clap's `help` excluded): demo, extract-frames, index, info, query, +# transcribe; none has subcommands. +# G1.3: `index --embedder|--model|--chunk-strategy nosuch` exit 2 (clap ValueEnum, GREEN). `query --format|--mode| +# --fusion|--rerank nosuch` exit 1 — they are `String` fields (crates/aprender-rag-cli/src/lib.rs:198-) checked +# after clap, `--mode` before the index loads and the other three not at all until the index is read. LIVE RED. +# surface_audit.csv holds 13 rows: 6 are set-equal to the walk; 7 name `eval compare|gate|generate|judge|metrics| +# retrieve|sample`, behind `#[cfg(feature = "eval")]` (lib.rs:309) which the default build lacks. LIVE RED: +# 7 bin:ledgerOrphan on a default snapshot. All 7 are in scripts/dogfood_reconcile.py FEATURE_GATED (the +# enumerated, rebuild-proven allowance the dogfood gate already honours); extract:binary must honour it too. +# G1.3 FIXED on d1/ont10-s12-aprender-rag-g13 @134e256bb9 (value_parser lists; live exit 2), pending fold into car. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-rag-cli--aprender-rag-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-rag` binary (package aprender-rag-cli, src crates/aprender-rag-cli/src/main.rs): + its help, its usage errors, its version string, its command set, and its surface-audit rows. Out of scope: what + indexing or retrieval computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-27' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-rag answers --help, rejects an unknown flag or an unknown enumerated value as a usage error, names the + commit it was built from, exposes exactly the commands the snapshot records, and has an audited ledger row for + each — and no ledger row for a command the default build lacks. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-rag-cli/src/lib.rs — the clap Commands enum; Eval behind feature eval' + - 'crates/aprender-rag-cli/Cargo.toml — [[bin]] aprender-rag; default = []' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-rag' + - 'aprender#4502 — ONT-10, slice S12' + +entity: + type: binary + ref: binary/aprender-rag-cli/aprender-rag + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-rag-surface + targetClass: bin:target/aprender-rag-cli/aprender-rag + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-rag"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-rag-cli"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-rag [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 6 + maxCount: 6 + in: + - "demo" + - "extract-frames" + - "index" + - "info" + - "query" + - "transcribe" + - {path: bin:unledgeredCommand, maxCount: 0} + # LIVE RED 2026-09-27: the 7 `eval *` rows (feature eval, not default) + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-rag)) = subcommands(clap(Commands), default features) = {demo, extract-frames, index, info, query, transcribe} ∧ {strip(r.feature) : r ∈ surface_audit, r.binary = aprender-rag} = commands" + domain: "the aprender-rag line of the tracked snapshot, the clap Commands enum, and the 13 ledger rows" + codomain: "set equality, or a Fail naming each command or row in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "a ledger row for a command the default build does not expose is an orphan, never a silent skip" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-rag --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-TRUENO-RAG-INV-001 + property: every aprender-rag command has an audited ledger row, and every ledger row names a built command + formal: '∀ c ∈ commands(aprender-rag): ∃ r ∈ surface_audit: r.binary = aprender-rag ∧ r.feature = "aprender-rag " ++ c; ∀ r ∈ surface_audit, r.binary = aprender-rag: strip(r.feature) ∈ commands(aprender-rag)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-TRUENO-RAG-001 + rule: 'G1.2 help answers' + prediction: '`aprender-rag --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-rag' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make main exit 1 on DisplayHelp; must turn RED' + - id: FALSIFY-BIN-TRUENO-RAG-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-rag --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-rag' + if_fails: 'a wrapper cannot tell a typo from a retrieval failure (observed 2026-09-27: exit 2, GREEN)' + mutation: 'map every clap error to exit 1 in main; must turn RED' + - id: FALSIFY-BIN-TRUENO-RAG-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-rag --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-rag 0.69.3" — no sha; a stale binary reads as HEAD' + mutation: 'drop the sha from the clap version string; must turn RED' + - id: FALSIFY-BIN-TRUENO-RAG-004 + rule: 'G1.3 enumerated values are parsed by clap' + prediction: '`aprender-rag query --format nosuch --index I Q` (and --mode, --fusion, --rerank) exits 2 naming the possible values, as `index --embedder nosuch` already does' + test: 'scripts/dogfood_surfaces.sh --cli aprender-rag' + if_fails: 'LIVE RED 2026-09-27: exit 1 — the four query flags are String fields validated after clap; --format/--fusion/--rerank are not checked until an index has been loaded' + mutation: 'after the fix, turn --format back into a String; must turn RED' + - id: FALSIFY-BIN-TRUENO-RAG-005 + rule: 'the command set equals the snapshot and every ledger row resolves' + prediction: 'commands = {demo, extract-frames, index, info, query, transcribe}; the 13 ledger rows minus the 7 `eval *` rows are set-equal to it' + test: 'pv lint contracts/ --gate shapes on the snapshot (ONT-4g extract:binary)' + if_fails: 'LIVE RED 2026-09-27 on a default-feature snapshot: 7 `eval *` rows are bin:ledgerOrphan. They are NOT a ledger defect: all 7 are enumerated in scripts/dogfood_reconcile.py FEATURE_GATED (proven by rebuilding with --features eval), so the RED clears when extract:binary reads that enumerated set as an allowance (same fix clears the S9 apr and S10 alimentar feature-gated rows) — never by a count tolerance, and never by deleting the rows' + mutation: 'delete `query` from the snapshot; must turn RED on bin:command (5) and bin:ledgerOrphan naming `aprender-rag query`' diff --git a/contracts/bin-aprender-simulate--aprender-simulate-v1.yaml b/contracts/bin-aprender-simulate--aprender-simulate-v1.yaml new file mode 100644 index 0000000000..7a81ab271c --- /dev/null +++ b/contracts/bin-aprender-simulate--aprender-simulate-v1.yaml @@ -0,0 +1,161 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `simular` -> `aprender-simulate`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-simulate--aprender-simulate-v1 — the `aprender-simulate` bin target of package aprender-simulate (ONT-10 slice S13 (1/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-simulate-v1 — ONT-10 slice S13 (1/2): the `aprender-simulate` target of package `aprender-simulate` as an entity +# (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ b6cf6d2ed, release build, default features (none), private +# CARGO_TARGET_DIR): +# `aprender-simulate --version` = "aprender-simulate 0.69.3". Name right; no git sha, so G0.1 (warning) is RED today. +# FIXED 2026-09-27 on batch/ont-10: `aprender-simulate 0.69.3 ()`, the semver still field 2 (G0.1 GREEN, now a violation). +# Recursive --help walk (S9's convention): 9 commands, all top-level leaves. Two of them are user-defined +# `help` ("Show help") and `version` ("Show version") subcommands, not clap's auto help: both are members. +# surface_audit.csv holds 12 rows with binary=aprender-simulate (ledger at cf038d37f): 9 CLI rows, one per command, and 3 +# HTTP rows — GET /, GET /health, GET /ws — cited at crates/aprender-simulate/src/visualization/web.rs:82-89. +# Those routes belong to the library module `visualization::web`, compiled only under feature `web` (not +# default), and no command of the aprender-simulate CLI starts that server under any feature: nothing in src/cli +# references it. So the binary serves no route and the 3 rows are bin:ledgerOrphan — RED today. The fix is +# in the ledger (attribute the rows to the library, or to a command that serves them), not in this shape. +# RULED 2026-09-27 (cop, for 0.70): no new command and no new row kind. The 3 rows keep the ledger's existing +# unjoinable kind: their feature now opens `unserved: GET …`, which is neither a route row (`VERB /path`) nor a +# command row (`aprender-simulate …`), so the extractor counts them in bin:auditRow and joins them to nothing +# (crates/aprender-contracts/src/ontology/extract/binary.rs parse_ledger). A web serve command is post-0.70 +# scope, under its own ticket. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-simulate--aprender-simulate-v1 +version: "1.0.0" +scope: > + The built `aprender-simulate` target of package `aprender-simulate` (default features) as the focus node binary/aprender-simulate/aprender-simulate: its identity, its exact command set, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S13 (1/2) (aprender-simulate). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-simulate/aprender-simulate, on the binary-surface-v1 vocabulary. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = aprender-simulate)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'crates/aprender-simulate/src/visualization/mod.rs (web is cfg(feature = "web"))' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-simulate/aprender-simulate + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-simulate-surface + targetClass: bin:target/aprender-simulate/aprender-simulate + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-simulate"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-simulate"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-simulate [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — a violation since batch/ont-10 2026-09-27 (build.rs embeds APR_GIT_SHA); was a warning while RED + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}"} + - path: bin:command + minCount: 9 + maxCount: 9 + in: + - "emc-check" + - "emc-validate" + - "help" + - "list-emc" + - "render" + - "run" + - "validate" + - "verify" + - "version" + - {path: "bin:route", maxCount: 0} + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-simulate/aprender-simulate) = C, |C| = 9, C = the leaves of the --help walk of the default-feature build ∪ user-defined `help` subcommands; routes(aprender-simulate) = ∅ ∧ ledgerOrphan = { r ∈ csv : r.binary = aprender-simulate ∧ key(r) ∉ C ∪ routes(aprender-simulate) }; key(r) = the command after 'aprender-simulate ', or 'METHOD /path' for an HTTP row" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is aprender-simulate (12 on 2026-09-26: 9 CLI, 3 HTTP; since 2026-09-27 the 3 HTTP rows are `unserved:` rows, joined to nothing)" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount 9 + maxCount 9 is set equality: an added, dropped or renamed command is a violation naming it" + - "a route the binary starts serving is a violation until this contract is re-measured" + - "a ledger row for a surface the built binary does not serve is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-simulate --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-SIMULAR-INV-001 + property: every aprender-simulate surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-simulate): ∃ r ∈ surface_audit : r.binary = aprender-simulate ∧ path(r) = c; ∀ r with r.binary = aprender-simulate: path(r) ∈ surface(aprender-simulate)' + prose: false + - id: BIN-SIMULAR-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-simulate/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-SIMULAR-001 + rule: 'G1.2 help answers' + prediction: '`aprender-simulate --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-simulate --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-SIMULAR-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-simulate --no-such-flag` exits 2' + test: 'cargo test -p aprender-simulate --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-SIMULAR-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-simulate --version` contains the first 9 hex of the git sha it was built from' + test: 'crates/aprender-simulate/src/cli/tests.rs test_version_flag_names_the_commit; the shape: bin:versionLacksSha, once extract:binary lands' + if_fails: 'a stale aprender-simulate reads as HEAD (observed 2026-09-26 at b6cf6d2ed: "aprender-simulate 0.69.3", no sha; fixed on batch/ont-10: build.rs embeds APR_GIT_SHA, same resolver as pv)' + mutation: 'set VERSION back to CARGO_PKG_VERSION alone; the unit test turns RED (field 3 missing)' + - id: FALSIFY-BIN-SIMULAR-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: '`aprender-simulate render --domain nosuch` and `render --format nosuch` exit 2, and the surface literals in the shape above = the snapshot''s, set-equal' + test: 'cargo test -p aprender-simulate --test cli_surface_gate -- advertised_subcommands_are_the_pinned_surface malformed_arguments_are_usage_errors' + if_fails: 'an added, dropped or renamed surface member ships with the ontology green ; an unknown render --domain parsed and failed at run time with exit 1 after printing the render header (observed 2026-09-26 at b6cf6d2ed; fixed on batch/ont-10 by a clap value_parser, exit 2)' + mutation: 'remove the --domain value_parser; the unit test turns RED (checked 2026-09-27). Remove `emc-check` from the snapshot; must turn RED on bin:command and bin:ledgerOrphan naming it' + - id: FALSIFY-BIN-SIMULAR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-simulate (12 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-simulate row; must turn RED naming aprender-simulate' + - id: FALSIFY-BIN-SIMULAR-006 + rule: 'a library route is never credited to the binary' + prediction: 'the 3 visualization rows open `unserved: GET …`: parse_ledger puts them in bin:auditRow only, so aprender-simulate has 0 bin:ledgerOrphanRoute and 0 bin:ledgerOrphan (observed RED 2026-09-26 at b6cf6d2ed with 3 results, while the rows read `GET / …`)' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g); parse_ledger in crates/aprender-contracts/src/ontology/extract/binary.rs' + if_fails: 'The ledger would credit the aprender-simulate binary with a web UI no user of it can reach.' + mutation: 'restore any one row to `GET /health (visualization)`; must turn RED, verdict Fail on binary/aprender-simulate/aprender-simulate path bin:ledgerOrphanRoute naming GET /health' + - id: FALSIFY-BIN-SIMULAR-007 + rule: 'user defined help counts' + prediction: 'an extractor that drops every subcommand named help (not only clap''s auto one) makes aprender-simulate-commands RED on minCount 9 and the `aprender-simulate help` ledger row an orphan' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on a snapshot from a walk that drops all help' + if_fails: 'A real, ledgered command would sit outside the modelled surface.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail naming help' + - id: FALSIFY-BIN-SIMULAR-008 + rule: 'unusable input is rejected cleanly' + prediction: '`run`, `validate`, `verify`, `emc-check` and `emc-validate` on a missing, empty and garbage file and on `foo: 1`: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-simulate --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): before the fix `validate` on an empty file or `foo: 1` printed "Schema validation PASSED" and exited 0 — without the (never-enabled) schema-validation feature the check was a stub returning Ok; with the fix reverted this test is RED; fixed (a structural check of the embedded schema), GREEN' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-test-cli--aprender-test-cli-v1.yaml b/contracts/bin-aprender-test-cli--aprender-test-cli-v1.yaml new file mode 100644 index 0000000000..da4c336289 --- /dev/null +++ b/contracts/bin-aprender-test-cli--aprender-test-cli-v1.yaml @@ -0,0 +1,153 @@ +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# bin-aprender-test-cli--aprender-test-cli-v1 — the `aprender-test-cli` bin target of package aprender-test-cli (ONT-10 slice S2), drafted to the exemplar +# contracts/bin-aprender-contracts-cli--pv-v1.yaml over the binary-surface-v1 vocabulary (#4476, #4079, aprender#4502). +# +# +# MEASURED (2026-09-26, batch/ont-10 @ ff9587b596, release build, default features, LC_ALL=C recursive --help walk): +# - `aprender-test-cli --version` printed "probador 0.69.3", the pre-monorepo name. FIXED 2026-09-27 on batch/ont-10: +# the clap name is now aprender-test-cli. G0.1 (git sha in --version) is a warning and RED today. +# - the walk gives 34 leaves; `comply` and `serve` take an OPTIONAL subcommand (Usage `[COMMAND]`) and run on +# their own. `llm experiment` required one (``) and was a ledger orphan; FIXED 2026-09-27: a bare +# `llm experiment` runs `status` (Usage `[COMMAND]`), so C = 37 invocable paths. +# - surface_audit.csv: 39 rows = the 37 + 2 HTTP routes of the dev +# server (GET /, GET /ws), which are the route kind and out of this CLI shape. +# +# CONVENTION THIS DRAFT ASKS OF extract:binary (ONT-4g, aprender-98): nodes also typed bin:target// +# (no targetNode in pv's subset); commands as bin:command literals (full path below the binary name, space-joined); +# the csv join yields bin:ledgerOrphan / bin:unledgeredCommand. +# A group whose Usage line shows `[COMMAND]` is itself a command path (it runs without a subcommand); one showing +# `` is not. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────── +name: bin-aprender-test-cli--aprender-test-cli-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-test-cli` binary (package aprender-test-cli, src crates/aprender-test-cli/src/main.rs): its help, usage errors, version string, the 37 invocable command paths, and its surface-audit rows. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + The command surface of the `aprender-test-cli` binary (package aprender-test-cli, src crates/aprender-test-cli/src/main.rs): its help, usage errors, version string, the 37 invocable command paths, and its surface-audit rows. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'crates/aprender-test-cli/src/main.rs — the clap Command tree' + - 'docs/audits/surface_audit.csv — the ledger' + - 'aprender#4502 — ONT-10, slice S2' + +entity: + type: binary + ref: binary/aprender-test-cli/aprender-test-cli + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-test-cli-surface + targetClass: bin:target/aprender-test-cli/aprender-test-cli + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-test-cli"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-test-cli"]} + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-test-cli [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-26 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - path: bin:command + minCount: 37 + maxCount: 37 + in: + - "animation check" + - "audio check" + - "av-sync check" + - "av-sync report" + - "build" + - "comply" + - "comply check" + - "comply diff" + - "comply enforce" + - "comply migrate" + - "comply report" + - "config" + - "coverage" + - "init" + - "llm bench" + - "llm data-audit" + - "llm experiment" + - "llm experiment compare" + - "llm experiment init" + - "llm experiment status" + - "llm gen-dataset" + - "llm load" + - "llm report" + - "llm score" + - "llm sweep" + - "llm test" + - "playbook" + - "record" + - "report" + - "serve" + - "serve score" + - "serve tree" + - "serve viz" + - "stress" + - "test" + - "video check" + - "watch" + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-test-cli)) = invocable paths of the recursive --help walk ∧ commands ⊆ {f.feature : f ∈ surface_audit, f.binary = aprender-test-cli}" + domain: "the aprender-test-cli line of the tracked snapshot, the clap tree of the default-feature build, and the ledger rows with binary = aprender-test-cli" + codomain: "set equality (37 paths on 2026-09-27), or a Fail naming each path in the symmetric difference" + invariants: + - "a leaf is a command; a group whose Usage shows [COMMAND] is also one; clap's auto `help` is not" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-test-cli --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER_TEST_CLI-INV-001 + property: every aprender-test-cli command path has an audited ledger row, and every CLI row names a command path + formal: '∀ c ∈ commands(aprender-test-cli): |{r ∈ surface_audit : r.binary = aprender-test-cli ∧ r.feature = c}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER_TEST_CLI-001 + rule: 'G1.2 help answers' + prediction: '`aprender-test-cli --help` exits 0 with non-empty stdout' + test: 'scripts/dogfood_surfaces.sh --cli aprender-test-cli' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER_TEST_CLI-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-test-cli --no-such-flag` exits 2' + test: 'scripts/dogfood_surfaces.sh --cli aprender-test-cli' + if_fails: 'a typo is silently ignored (observed 2026-09-26: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER_TEST_CLI-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-test-cli --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: "LIVE RED 2026-09-27: `aprender-test-cli 0.69.3` — no sha" + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER_TEST_CLI-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the enumerated surface of aprender-test-cli equals the in: list of this shape, no more and no fewer' + test: 'pv lint contracts/ --gate shapes, once extract:binary writes the node' + if_fails: "GREEN on the walk 2026-09-27: 37 = 34 leaves + comply, serve, llm experiment" + mutation: 'add a subcommand/route/tool without re-snapshotting; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER_TEST_CLI-005 + rule: 'ledger rows exist and resolve' + prediction: "docs/audits/surface_audit.csv has ≥1 row per command path with binary = aprender-test-cli, and no CLI row names a non-command" + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: "GREEN 2026-09-27: 39 rows = 37 commands + 2 dev-server routes (GET /, GET /ws) the CLI shape does not judge" + mutation: 'delete every aprender-test-cli row; must turn RED naming aprender-test-cli' + diff --git a/contracts/bin-aprender-train-bench--aprender-train-bench-v1.yaml b/contracts/bin-aprender-train-bench--aprender-train-bench-v1.yaml new file mode 100644 index 0000000000..475ad6f42e --- /dev/null +++ b/contracts/bin-aprender-train-bench--aprender-train-bench-v1.yaml @@ -0,0 +1,102 @@ +# ────────────────────────────────────────────── +# bin-aprender-train-bench--aprender-train-bench-v1 — the `aprender-train-bench` bin target of package aprender-train-bench (ONT-10 slice S17) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# Was RED beyond G0.1: the version named "entrenar-bench", not the bin name. FIXED on batch/ont-10 (2026-09-27): +# clap name aprender-train-bench (measured after the fix: "aprender-train-bench 0.69.3"); cli_surface_gate pins the prefix. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-train-bench--aprender-train-bench-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-train-bench` binary (package aprender-train-bench, src crates/aprender-train-bench/src/main.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-train-bench answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-train-bench/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-train-bench' + - 'aprender#4502 — ONT-10, slice S17' + +entity: + type: binary + ref: binary/aprender-train-bench/aprender-train-bench + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-train-bench)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-train-bench/tests/cli_surface_gate.rs) ∧ leaves(aprender-train-bench) = {f.feature : f ∈ surface_audit, f.binary = aprender-train-bench}" + domain: "the aprender-train-bench line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-train-bench" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {temperature, alpha, compare, ablation, cost-performance, recommend}; 6 leaves, set-equal to the 6 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-train-bench --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-TRAIN-BENCH-INV-001 + property: every aprender-train-bench command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-train-bench): |{r ∈ surface_audit : r.binary = aprender-train-bench ∧ r.feature = "aprender-train-bench " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-TRAIN-BENCH-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-train-bench/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-001 + rule: 'G1.2 help answers' + prediction: '`aprender-train-bench --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-train-bench --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-train-bench --no-such-flag` and `aprender-train-bench no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-train-bench --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-train-bench --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-train-bench --test cli_surface_gate version_reports_the_crate_version pins the "aprender-train-bench " prefix, not the sha' + if_fails: 'sha: LIVE RED on 0.69.3 (a stale aprender-train-bench reads as HEAD), clears at rc.1 (car/0.70.0 carries #4219); name: FIXED on batch/ont-10 (was "entrenar-bench 0.69.3")' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly temperature, alpha, compare, ablation, cost-performance, recommend' + test: 'cargo test -p aprender-train-bench --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-train-bench per leaf (6 rows for 6 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-train-bench ships a command with no audited row' + mutation: 'delete every aprender-train-bench row; must turn RED naming aprender-train-bench' + - id: FALSIFY-BIN-APRENDER-TRAIN-BENCH-006 + rule: 'unusable input is rejected cleanly' + prediction: '`ablation -c`, `cost-performance --results` and `recommend --results` on a missing, empty and garbage input: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-train-bench --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN on an unmutated tree; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-train-distill--aprender-train-distill-v1.yaml b/contracts/bin-aprender-train-distill--aprender-train-distill-v1.yaml new file mode 100644 index 0000000000..6e35f7037c --- /dev/null +++ b/contracts/bin-aprender-train-distill--aprender-train-distill-v1.yaml @@ -0,0 +1,102 @@ +# ────────────────────────────────────────────── +# bin-aprender-train-distill--aprender-train-distill-v1 — the `aprender-train-distill` bin target of package aprender-train-distill (ONT-10 slice S17) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# Was RED beyond G0.1: the version named "entrenar-distill", not the bin name. FIXED on batch/ont-10 (2026-09-27): +# clap name aprender-train-distill (measured after the fix: "aprender-train-distill 0.69.3"); cli_surface_gate pins the prefix. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-train-distill--aprender-train-distill-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-train-distill` binary (package aprender-train-distill, src crates/aprender-train-distill/src/main.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-train-distill answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-train-distill/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-train-distill' + - 'aprender#4502 — ONT-10, slice S17' + +entity: + type: binary + ref: binary/aprender-train-distill/aprender-train-distill + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-train-distill)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-train-distill/tests/cli_surface_gate.rs) ∧ leaves(aprender-train-distill) = {f.feature : f ∈ surface_audit, f.binary = aprender-train-distill}" + domain: "the aprender-train-distill line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-train-distill" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {run, estimate, validate, export}; 4 leaves, set-equal to the 4 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-train-distill --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-TRAIN-DISTILL-INV-001 + property: every aprender-train-distill command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-train-distill): |{r ∈ surface_audit : r.binary = aprender-train-distill ∧ r.feature = "aprender-train-distill " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-TRAIN-DISTILL-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-train-distill/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-001 + rule: 'G1.2 help answers' + prediction: '`aprender-train-distill --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-train-distill --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-train-distill --no-such-flag` and `aprender-train-distill no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-train-distill --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-train-distill --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-train-distill --test cli_surface_gate version_reports_the_crate_version pins the "aprender-train-distill " prefix, not the sha' + if_fails: 'sha: LIVE RED on 0.69.3 (a stale aprender-train-distill reads as HEAD), clears at rc.1 (car/0.70.0 carries #4219); name: FIXED on batch/ont-10 (was "entrenar-distill 0.69.3")' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly run, estimate, validate, export' + test: 'cargo test -p aprender-train-distill --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-train-distill per leaf (4 rows for 4 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-train-distill ships a command with no audited row' + mutation: 'delete every aprender-train-distill row; must turn RED naming aprender-train-distill' + - id: FALSIFY-BIN-APRENDER-TRAIN-DISTILL-006 + rule: 'unusable input is rejected cleanly' + prediction: '`validate -c` and `run -c --dry-run` on a missing, empty and garbage config: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-train-distill --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN on an unmutated tree; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-train-inspect--aprender-train-inspect-v1.yaml b/contracts/bin-aprender-train-inspect--aprender-train-inspect-v1.yaml new file mode 100644 index 0000000000..d732ba07ac --- /dev/null +++ b/contracts/bin-aprender-train-inspect--aprender-train-inspect-v1.yaml @@ -0,0 +1,102 @@ +# ────────────────────────────────────────────── +# bin-aprender-train-inspect--aprender-train-inspect-v1 — the `aprender-train-inspect` bin target of package aprender-train-inspect (ONT-10 slice S17) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# Was RED beyond G0.1: the version named "entrenar-inspect", not the bin name. FIXED on batch/ont-10 (2026-09-27): +# clap name aprender-train-inspect (measured after the fix: "aprender-train-inspect 0.69.3"); cli_surface_gate pins the prefix. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-train-inspect--aprender-train-inspect-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-train-inspect` binary (package aprender-train-inspect, src crates/aprender-train-inspect/src/main.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-train-inspect answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-train-inspect/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-train-inspect' + - 'aprender#4502 — ONT-10, slice S17' + +entity: + type: binary + ref: binary/aprender-train-inspect/aprender-train-inspect + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-train-inspect)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-train-inspect/tests/cli_surface_gate.rs) ∧ leaves(aprender-train-inspect) = {f.feature : f ∈ surface_audit, f.binary = aprender-train-inspect}" + domain: "the aprender-train-inspect line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-train-inspect" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {info, layers, memory, validate, convert, compare}; 6 leaves, set-equal to the 6 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-train-inspect --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-TRAIN-INSPECT-INV-001 + property: every aprender-train-inspect command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-train-inspect): |{r ∈ surface_audit : r.binary = aprender-train-inspect ∧ r.feature = "aprender-train-inspect " ++ c}| ≥ 1' + prose: false + - id: BIN-APRENDER-TRAIN-INSPECT-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-train-inspect/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-001 + rule: 'G1.2 help answers' + prediction: '`aprender-train-inspect --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-train-inspect --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-train-inspect --no-such-flag` and `aprender-train-inspect no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-train-inspect --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-train-inspect --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-train-inspect --test cli_surface_gate version_reports_the_crate_version pins the "aprender-train-inspect " prefix, not the sha' + if_fails: 'sha: LIVE RED on 0.69.3 (a stale aprender-train-inspect reads as HEAD), clears at rc.1 (car/0.70.0 carries #4219); name: FIXED on batch/ont-10 (was "entrenar-inspect 0.69.3")' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly info, layers, memory, validate, convert, compare' + test: 'cargo test -p aprender-train-inspect --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-train-inspect per leaf (6 rows for 6 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-train-inspect ships a command with no audited row' + mutation: 'delete every aprender-train-inspect row; must turn RED naming aprender-train-inspect' + - id: FALSIFY-BIN-APRENDER-TRAIN-INSPECT-006 + rule: 'unusable input is rejected cleanly' + prediction: '`info`, `layers`, `memory` on a missing, empty and garbage input; `validate` on a missing input only: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-train-inspect --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN on an unmutated tree; no source mutation was run. `validate` on an empty or garbage file prints PASS and exits 0 — NOT asserted here, recorded as finding a01-ont10-inspect-validate-passes-garbage' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-train-lora--aprender-train-lora-v1.yaml b/contracts/bin-aprender-train-lora--aprender-train-lora-v1.yaml new file mode 100644 index 0000000000..b080687645 --- /dev/null +++ b/contracts/bin-aprender-train-lora--aprender-train-lora-v1.yaml @@ -0,0 +1,129 @@ +# ────────────────────────────────────────────── +# bin-aprender-train-lora--aprender-train-lora-v1 — the `aprender-train-lora` bin target of package aprender-train-lora (ONT-10 slice S16 (2/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-train-lora-v1 — ONT-10 slice S16 (2/2): the `aprender-train-lora` binary as an entity +# (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, release build, default features): the --help walk gives 4 leaf +# commands, set-equal to surface_audit.csv's 4 rows. `aprender-train-lora --version` printed "entrenar-lora 0.69.3": +# the pre-monorepo name. The identity shape below requires the version string to start with the target name; +# FIXED 2026-09-27 (clap name; tests/cli_surface_gate.rs pins the prefix). G0.1 (git sha in --version) is a +# warning and RED until rc.1. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-train-lora--aprender-train-lora-v1 +version: "1.0.0" +scope: > + The built `aprender-train-lora` target of package `aprender-train-lora` (default features) as the focus node binary/aprender-train-lora/aprender-train-lora: its identity and its exact CLI command set, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S16 (2/2) (aprender-train-lora). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-train-lora/aprender-train-lora, on the binary-surface-v1 vocabulary. + references: + - 'crates/aprender-train-lora/tests/cli_surface_gate.rs — the gate falsifiers 001, 002, 004 and 006 run' + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger; binary column = aprender-train-lora)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-train-lora/aprender-train-lora + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-train-lora-surface + targetClass: bin:target/aprender-train-lora/aprender-train-lora + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-train-lora"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-train-lora"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-train-lora [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:command + minCount: 4 + maxCount: 4 + in: + - "compare" + - "inspect" + - "merge" + - "plan" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(binary/aprender-train-lora/aprender-train-lora) = C, |C| = 4, where C = the leaves of the recursive --help walk of the default-feature build ∧ ledgerOrphan = { r ∈ csv : r.binary = aprender-train-lora ∧ r.feature ∉ C }; unledgeredCommand = C ∖ { r.feature : r.binary = aprender-train-lora }" + domain: "the snapshot record dogfood_surfaces.sh writes for this target; docs/audits/surface_audit.csv rows whose binary column is aprender-train-lora" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount 4 + maxCount 4 over distinct literals is set equality: an added, dropped or renamed command is a violation naming it" + - "a ledger row for a command the built binary does not expose is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-train-lora --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-TRAIN-LORA-INV-001 + property: every aprender-train-lora surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-train-lora): ∃ r ∈ surface_audit : r.binary = aprender-train-lora ∧ path(r) = c; ∀ r with r.binary = aprender-train-lora: path(r) ∈ surface(aprender-train-lora)' + prose: false + + - id: BIN-APRENDER-TRAIN-LORA-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-train-lora/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-001 + rule: 'G1.2 help answers' + prediction: '`aprender-train-lora --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-train-lora --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-train-lora --no-such-flag` and `aprender-train-lora no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-train-lora --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-train-lora --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-train-lora 0.69.3" — no sha; a stale aprender-train-lora reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly plan, compare, merge, inspect' + test: 'cargo test -p aprender-train-lora --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-train-lora (4 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-train-lora row; must turn RED naming aprender-train-lora' + - id: FALSIFY-BIN-APRENDER-TRAIN-LORA-006 + rule: 'unusable input is rejected cleanly' + prediction: '`merge --base/--adapter` on a missing, empty and garbage input (and no output file left behind); `inspect` on a missing input only: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-train-lora --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): before the fix `merge` on existing garbage printed "Merged adapter into base model", exited 0 and wrote no file (merge_from_file returned a placeholder Ok); with the fix reverted this test is RED; fixed, GREEN. `inspect` on an empty or garbage file prints a header and exits 0 — NOT asserted, recorded as finding a01-ont10-lora-inspect-reads-nothing' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-train-shell--aprender-train-shell-v1.yaml b/contracts/bin-aprender-train-shell--aprender-train-shell-v1.yaml new file mode 100644 index 0000000000..30f01af27e --- /dev/null +++ b/contracts/bin-aprender-train-shell--aprender-train-shell-v1.yaml @@ -0,0 +1,155 @@ +# ────────────────────────────────────────────── +# bin-aprender-train-shell--aprender-train-shell-v1 — the `aprender-train-shell` bin target of package aprender-train-shell (ONT-10 slice S14 (1/2)) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha, helpSha, +# gitSha, …) live in binary-surface-v1 and are not restated. Moved from docs/ont-10/binary-contracts/ (aprender-bb). +# +# binary-aprender-train-shell-v1 — ONT-10 slice S14 (1/2): the `aprender-train-shell` binary as an entity (ONT-001 v4.17 ONT-4g; #4476, #4079) +# +# MEASURED (2026-09-26, batch/ont-10 @ ff9587b596, release build, default features, LC_ALL=C recursive --help walk): +# - `aprender-train-shell --version` printed "entrenar-shell 0.69.3", the pre-monorepo name. FIXED 2026-09-27 +# (clap name; tests/cli_version_name.rs). G0.1 (git sha) is a warning and RED until rc.1. +# - no clap subcommands: the surface is the bare REPL, two flags (-c , -s ; -h/-V excluded) and +# the REPL's own commands. `-c help` lists 9 REPL commands; the parser (crates/aprender-train-shell/src/ +# commands.rs:71) also accepts `clear`/`cls`, which help omitted. C_repl below is the parser's 10 canonical names; +# FIXED 2026-09-27: help lists `clear` (test_general_help_lists_every_parsed_command pins help == parser). +# - surface_audit.csv: 13 rows = bare REPL + 2 flag rows + 10 `repl: ` rows. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-train-shell--aprender-train-shell-v1 +version: "1.0.0" +scope: > + The built `aprender-train-shell` target of package `aprender-train-shell` (default features) as the focus node binary/aprender-train-shell/aprender-train-shell: its identity and its exact command surface, and the surface_audit.csv rows that must resolve to it. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + ONT-10 binary slice S14 (1/2) (aprender-train-shell). Per-binary shapes over the node extract:binary (ONT-4g, #4476) draws + for binary/aprender-train-shell/aprender-train-shell, on the binary-surface-v1 vocabulary. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'paiml/infra docs/specifications/paiml-ontology.md v4.17 §5 ONT-4g, ONT-10' + - 'docs/audits/surface_audit.csv (the ledger)' + - 'scripts/dogfood_surfaces.sh (the snapshot extract:binary reads)' + - 'crates/aprender-train-shell/src/commands.rs' + - 'aprender#4502 — ONT-10' + +entity: + type: binary + ref: binary/aprender-train-shell/aprender-train-shell + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-train-shell-surface + targetClass: bin:target/aprender-train-shell/aprender-train-shell + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-train-shell"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-train-shell"]} + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "^aprender-train-shell [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-27 (no sha); a warning until the version string carries it + - {path: "bin:versionString", minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: "warning"} + - path: bin:replCommand + minCount: 10 + maxCount: 10 + in: + - "clear" + - "distill" + - "export" + - "fetch" + - "help" + - "history" + - "inspect" + - "memory" + - "quit" + - "set" + - {path: "bin:command", maxCount: 0} + - path: bin:flag + minCount: 2 + maxCount: 2 + in: + - "-c" + - "-s" + - {path: "bin:ledgerOrphan", maxCount: 0} + - {path: "bin:unledgeredCommand", maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "replCommands(binary/aprender-train-shell/aprender-train-shell) = R, |R| = 10; flags = {-c, -s}; commands = ∅ ∧ ledgerOrphan = ledger rows for this binary whose REPL command or flag is not on the node; unledgeredCommand = (R ∪ flags) ∖ ledger" + domain: "the snapshot record dogfood_surfaces.sh writes for this target, plus ` -c help`; docs/audits/surface_audit.csv rows whose binary column is aprender-train-shell" + codomain: "set equality, or a Fail naming each member of the symmetric difference; literals on the focus node, one per orphan row or unledgered command" + invariants: + - "in + minCount + maxCount over distinct literals is set equality: an added, dropped or renamed REPL command or flag is a violation naming it" + - "a ledger row for a REPL command the built binary does not expose is a violation, never a silent skip" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-train-shell --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-TRAIN-SHELL-INV-001 + property: every aprender-train-shell surface member has an audited ledger row, and every ledger row names one + formal: '∀ c ∈ surface(aprender-train-shell): ∃ r ∈ surface_audit : r.binary = aprender-train-shell ∧ path(r) = c; ∀ r with r.binary = aprender-train-shell: path(r) ∈ surface(aprender-train-shell)' + prose: false + - id: BIN-APRENDER-TRAIN-SHELL-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-train-shell/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-001 + rule: 'G1.2 help answers' + prediction: '`aprender-train-shell --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-train-shell --test cli_surface_gate advertised_options_are_the_pinned_surface' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-27: exit 0, GREEN)' + mutation: 'make the top-level --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-train-shell --no-such-flag` exits 2' + test: 'cargo test -p aprender-train-shell --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-27: exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-train-shell --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'LIVE RED 2026-09-27: "aprender-train-shell 0.69.3" — no sha; a stale aprender-train-shell reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-004 + rule: 'G1.3/G1.4 the surface set equals the snapshot' + prediction: 'the surface literals in the shape above = the snapshot''s, set-equal' + test: 'cargo test -p aprender-train-shell --test cli_surface_gate -- advertised_options_are_the_pinned_surface' + if_fails: 'an added, dropped or renamed surface member ships with the ontology green (enumerated-value parsing not probed for this binary)' + mutation: 'remove one surface literal from the snapshot; must turn RED naming it' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row per surface member with binary = aprender-train-shell (13 rows on 2026-09-27)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the dogfood coverage ratio is computed over a ledger that silently omits a shipped surface member' + mutation: 'delete every aprender-train-shell row; must turn RED naming aprender-train-shell' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-006 + rule: 'dropped repl command is red' + prediction: 'deleting one REPL command from the snapshot makes aprender-train-shell-surface RED naming it (minCount 10)' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on the snapshot with one REPL command removed' + if_fails: 'A REPL command could vanish from the shipped shell with the ontology green.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail on binary/aprender-train-shell/aprender-train-shell path bin:replCommand' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-007 + rule: 'stray subcommand is red' + prediction: 'a snapshot that records any clap subcommand for this binary makes bin:command maxCount 0 RED' + test: 'pv lint contracts/ --gate shapes --format json (after ONT-4g), on a snapshot with one bin:command added' + if_fails: 'A subcommand surface could be added to the REPL binary without a ledger row or a contract change.' + mutation: 'the edit in the prediction; must turn RED — verdict Fail on path bin:command' + - id: FALSIFY-BIN-APRENDER-TRAIN-SHELL-008 + rule: 'unusable input is rejected cleanly' + prediction: '`--session --command help` on a missing, empty and garbage session file: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-train-shell --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): each exits 1 with no panic, GREEN; no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-verify-ml--aprender-verify-v1.yaml b/contracts/bin-aprender-verify-ml--aprender-verify-v1.yaml new file mode 100644 index 0000000000..480d606d3a --- /dev/null +++ b/contracts/bin-aprender-verify-ml--aprender-verify-v1.yaml @@ -0,0 +1,102 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `verificar` -> `aprender-verify`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-verify-ml--aprender-verify-v1 — the `aprender-verify` bin target of package aprender-verify-ml (ONT-10 slice S5) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-verify-ml--aprender-verify-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-verify` binary (package aprender-verify-ml, src crates/aprender-verify-ml/src/bin/aprender-verify.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-verify answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-verify-ml/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-verify' + - 'aprender#4502 — ONT-10, slice S5' + +entity: + type: binary + ref: binary/aprender-verify-ml/aprender-verify + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-verify)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-verify-ml/tests/cli_surface_gate.rs) ∧ leaves(aprender-verify) = {f.feature : f ∈ surface_audit, f.binary = aprender-verify}" + domain: "the aprender-verify line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-verify" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {generate, stats, corpus, depyler, verify, train, evaluate, distill, codex, export, mutate}; 11 leaves, set-equal to the 11 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-verify --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-VERIFICAR-INV-001 + property: every aprender-verify command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-verify): |{r ∈ surface_audit : r.binary = aprender-verify ∧ r.feature = "aprender-verify " ++ c}| ≥ 1' + prose: false + - id: BIN-VERIFICAR-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-verify-ml/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-VERIFICAR-001 + rule: 'G1.2 help answers' + prediction: '`aprender-verify --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-verify-ml --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-VERIFICAR-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-verify --no-such-flag` and `aprender-verify no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-verify-ml --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-VERIFICAR-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-verify --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-verify-ml --test cli_surface_gate version_reports_the_crate_version pins only the crate version today' + if_fails: 'LIVE RED 2026-09-26: `aprender-verify 0.69.3` — no sha; a stale aprender-verify reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-VERIFICAR-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly generate, stats, corpus, depyler, verify, train, evaluate, distill, codex, export, mutate' + test: 'cargo test -p aprender-verify-ml --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-VERIFICAR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-verify per leaf (11 rows for 11 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-verify ships a command with no audited row' + mutation: 'delete every aprender-verify row; must turn RED naming aprender-verify' + - id: FALSIFY-BIN-VERIFICAR-006 + rule: 'unusable input is rejected cleanly' + prediction: '`verify -i` on a missing, empty and garbage input; `train -i` on a missing input: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-verify-ml --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): verify fix reverted -> RED (empty.bin got Some(101), a panic); train guard removed alone -> RED on the train row; restored, GREEN' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-zram-cli--aprender-zram-v1.yaml b/contracts/bin-aprender-zram-cli--aprender-zram-v1.yaml new file mode 100644 index 0000000000..bcea2dba44 --- /dev/null +++ b/contracts/bin-aprender-zram-cli--aprender-zram-v1.yaml @@ -0,0 +1,102 @@ +# ────────────────────────────────────────────── +# RENAMED 2026-09-27 (untangle, #4430 bin renames): bin `trueno-zram` -> `aprender-zram`. Measurements quoted below +# were taken on the old name and are re-taken by the next snapshot. +# bin-aprender-zram-cli--aprender-zram-v1 — the `aprender-zram` bin target of package aprender-zram-cli (ONT-10 slice S11) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes live in +# binary-surface-v1; this file restates none of them. Measured 2026-09-26 on a 0.69.3 build of this tree. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-zram-cli--aprender-zram-v1 +version: "1.0.0" +scope: > + The command surface of the `aprender-zram` binary (package aprender-zram-cli, src crates/aprender-zram-cli/src/main.rs): its help, its usage errors, its + version string, its subcommand set, its rejection of unusable input, and its surface-audit rows. Out of scope: + what each subcommand computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + aprender-zram answers --help, rejects an unknown flag or subcommand as a usage error, exposes exactly the subcommands + the snapshot records, rejects unusable input without a panic, and has an audited ledger row for each command. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'crates/aprender-zram-cli/tests/cli_surface_gate.rs — the gate every falsifier below runs' + - 'docs/audits/surface_audit.csv — the rows whose binary column is aprender-zram' + - 'aprender#4502 — ONT-10, slice S11' + +entity: + type: binary + ref: binary/aprender-zram-cli/aprender-zram + +relations: + depends_on: [binary-surface-v1] + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-zram)) = subcommands(clap(Cli)) = SUBCOMMANDS(crates/aprender-zram-cli/tests/cli_surface_gate.rs) ∧ leaves(aprender-zram) = {f.feature : f ∈ surface_audit, f.binary = aprender-zram}" + domain: "the aprender-zram line of the tracked snapshot, the clap Command enum, the gate's pinned list, and the ledger rows with binary = aprender-zram" + codomain: "set equality, or a Fail naming each command in the symmetric difference" + invariants: + - "a subcommand clap accepts and the snapshot omits is an unaudited surface" + - "measured 2026-09-26: top-level = {create, remove, status, benchmark}; 4 leaves, set-equal to the 4 ledger rows" + preconditions: + - "the snapshot was written at this commit" + postconditions: + - "help_sha256 in the snapshot is the sha256 of `aprender-zram --help` at this commit" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-TRUENO-ZRAM-INV-001 + property: every aprender-zram command leaf has an audited ledger row + formal: '∀ c ∈ leaves(aprender-zram): |{r ∈ surface_audit : r.binary = aprender-zram ∧ r.feature = "aprender-zram " ++ c}| ≥ 1' + prose: false + - id: BIN-TRUENO-ZRAM-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-zram-cli/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-TRUENO-ZRAM-001 + rule: 'G1.2 help answers' + prediction: '`aprender-zram --help` exits 0 and advertises ≥1 subcommand; every advertised ` --help` exits 0' + test: 'cargo test -p aprender-zram-cli --test cli_surface_gate every_advertised_subcommand_is_reachable' + if_fails: 'the binary cannot describe its own surface, or advertises a command it cannot reach (observed 2026-09-26: GREEN)' + mutation: 'make one subcommand''s --help return an error; must turn RED naming it' + - id: FALSIFY-BIN-TRUENO-ZRAM-002 + rule: 'G1.2 an unknown flag or subcommand is a usage error' + prediction: '`aprender-zram --no-such-flag` and `aprender-zram no-such-subcommand` each exit 2' + test: 'cargo test -p aprender-zram-cli --test cli_surface_gate unknown_' + if_fails: 'a typo is silently ignored (observed 2026-09-26: both exit 2, GREEN)' + mutation: 'add #[arg(allow_hyphen_values)] catch-all at the top level; must turn RED' + - id: FALSIFY-BIN-TRUENO-ZRAM-003 + rule: 'G0.1 the version names the commit' + prediction: '`aprender-zram --version` contains the first 9 hex of the git sha it was built from' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands. cargo test -p aprender-zram-cli --test cli_surface_gate version_reports_the_crate_version pins only the crate version today' + if_fails: 'LIVE RED 2026-09-26: `aprender-zram 0.69.3` — no sha; a stale aprender-zram reads as HEAD' + mutation: 'drop the sha from the version string; must turn RED' + - id: FALSIFY-BIN-TRUENO-ZRAM-004 + rule: 'G1.3/G1.4 the command set equals the snapshot' + prediction: 'the advertised top-level subcommands are exactly create, remove, status, benchmark' + test: 'cargo test -p aprender-zram-cli --test cli_surface_gate advertised_subcommands_are_the_pinned_surface' + if_fails: 'a subcommand was added or removed without updating the pinned surface (observed 2026-09-26: GREEN)' + mutation: 'add a subcommand without editing SUBCOMMANDS; must turn RED naming it' + - id: FALSIFY-BIN-TRUENO-ZRAM-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-zram per leaf (4 rows for 4 leaves on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'aprender-zram ships a command with no audited row' + mutation: 'delete every aprender-zram row; must turn RED naming aprender-zram' + - id: FALSIFY-BIN-TRUENO-ZRAM-006 + rule: 'unusable input is rejected cleanly' + prediction: '`create -d 17 -s 1G` and `--format bogus status` (create/remove are never run with valid arguments: they touch /dev/zram*): each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-zram-cli --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-26 (aprender-01): GREEN; both rows are clap value checks, and no source mutation was run for this binary' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/bin-aprender-zram-generator--aprender-zram-generator-v1.yaml b/contracts/bin-aprender-zram-generator--aprender-zram-generator-v1.yaml new file mode 100644 index 0000000000..ee9963f50a --- /dev/null +++ b/contracts/bin-aprender-zram-generator--aprender-zram-generator-v1.yaml @@ -0,0 +1,127 @@ +# ────────────────────────────────────────────── +# bin-aprender-zram-generator--aprender-zram-generator-v1 — the `aprender-zram-generator` bin target of package +# aprender-zram-generator (ONT-10 slice S15, 2/2) +# +# Drafted to the exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml. The shared shapes (sha256, helpSha256, +# gitSha, …) live in binary-surface-v1 and are not restated. +# +# MEASURED (2026-09-26, batch/ont-10 @ b1cd8c0b0, release build, default features, private CARGO_TARGET_DIR): +# A systemd generator (systemd.generator(7)): no subcommands; one required positional NORMAL_DIR and two optional, +# EARLY_DIR and LATE_DIR. `--help` exits 0; `--no-such-flag` exits 2. +# `aprender-zram-generator --version` = "trueno-zram-generator 0.69.3": the pre-monorepo name and no sha. LIVE RED +# on the name pattern and on G0.1. +# FIXED on batch/ont-10 (2026-09-27): clap name aprender-zram-generator (measured after the fix: "aprender-zram-generator 0.69.3"). +# surface_audit.csv holds 1 row, ``. +# +# VOCABULARY EXTENSION ASKED OF binary-surface-v1 (aprender-88), as in bin-aprender-profile--aprender-profile-v1: +# bin:positional, one literal per Usage positional; a ledger row ` …` keys on positional NAME. +# No enumerated flag values exist, so G1.3/G1.4 reduces to the command/positional set. +# +# KIND: pattern (one binary's surface over the binary-surface-v1 vocabulary). +# ────────────────────────────────────────────── +name: bin-aprender-zram-generator--aprender-zram-generator-v1 +version: "1.0.0" +scope: > + The argv surface of the `aprender-zram-generator` binary (package aprender-zram-generator): its help, its usage + errors, its version string, its (empty) command set, the three directories systemd passes, and its surface-audit + row. Out of scope: the units it writes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-27' + author: PAIML Engineering + description: > + aprender-zram-generator answers --help, rejects an unknown flag as a usage error, names itself and the commit it + was built from, takes exactly systemd's three generator directories and no subcommand, and has an audited + ledger row. + references: + - 'contracts/binary-surface-v1.yaml — the shared shapes and vocabulary' + - 'contracts/bin-aprender-contracts-cli--pv-v1.yaml — the exemplar' + - 'docs/audits/surface_audit.csv — the row whose binary column is aprender-zram-generator' + - 'systemd.generator(7) — the argv a generator is invoked with' + - 'aprender#4502 — ONT-10, slice S15' + +entity: + type: binary + ref: binary/aprender-zram-generator/aprender-zram-generator + +relations: + depends_on: [binary-surface-v1] + +shapes: + - id: binary-aprender-zram-generator-surface + targetClass: bin:target/aprender-zram-generator/aprender-zram-generator + properties: + - {path: bin:name, minCount: 1, maxCount: 1, in: ["aprender-zram-generator"]} + - {path: bin:package, minCount: 1, maxCount: 1, in: ["aprender-zram-generator"]} + # was RED at b1cd8c0b0 ("trueno-zram-generator 0.69.3"); FIXED on batch/ont-10 (clap name = aprender-zram-generator) + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "^aprender-zram-generator [0-9]+\\.[0-9]+\\.[0-9]+"} + # G0.1 — LIVE RED 2026-09-26 (no sha); a warning until the version string carries it + - {path: bin:versionString, minCount: 1, maxCount: 1, pattern: "[0-9a-f]{9}", severity: warning} + - {path: bin:command, maxCount: 0} + - {path: bin:positional, minCount: 3, maxCount: 3, in: ["EARLY_DIR", "LATE_DIR", "NORMAL_DIR"]} + - {path: bin:ledgerOrphan, maxCount: 0} + +equations: + surface_equals_snapshot: + formula: "commands(snapshot(aprender-zram-generator)) = ∅ ∧ positionals(snapshot) = {NORMAL_DIR, EARLY_DIR, LATE_DIR} ∧ key(r) ∈ positionals for every ledger row r with r.binary = aprender-zram-generator" + domain: "the aprender-zram-generator line of the tracked snapshot and its 1 ledger row" + codomain: "set equality, or a Fail naming the dropped positional, the added command or the orphan row" + invariants: + - "systemd calls a generator with three directory arguments; a build that drops one or grows a subcommand is a violation" + preconditions: + - "the snapshot was written at this commit, default features" + postconditions: + - "key(` …`) = NORMAL_DIR" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-APRENDER-ZRAM-GENERATOR-INV-001 + property: the generator takes exactly systemd's three directories and every ledger row names one of them + formal: 'positionals(aprender-zram-generator) = {NORMAL_DIR, EARLY_DIR, LATE_DIR} ∧ ∀ r ∈ surface_audit : r.binary = aprender-zram-generator ⇒ key(r) ∈ positionals' + prose: false + - id: BIN-APRENDER-ZRAM-GENERATOR-INV-002 + property: unusable input is a clean rejection, never a success and never a panic + formal: '∀ a ∈ rejection_rows(crates/aprender-zram-generator/tests/cli_surface_gate.rs): exit(a) ∉ {0, 101} ∧ "panicked" ∉ stderr(a)' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-001 + rule: 'G1.2 help answers' + prediction: '`aprender-zram-generator --help` exits 0 with non-empty stdout' + test: 'cargo test -p aprender-zram-generator --test cli_surface_gate advertised_options_are_the_pinned_surface' + if_fails: 'the binary cannot describe its own surface (observed 2026-09-26: exit 0, GREEN)' + mutation: 'make --help return an error; must turn RED' + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-002 + rule: 'G1.2 an unknown flag is a usage error' + prediction: '`aprender-zram-generator --no-such-flag` exits 2' + test: 'cargo test -p aprender-zram-generator --test cli_surface_gate unknown_' + if_fails: 'a flag would be taken as NORMAL_DIR and units written under it (observed 2026-09-26: exit 2, GREEN)' + mutation: 'mark NORMAL_DIR allow_hyphen_values; must turn RED' + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-003 + rule: 'G0.1 the version names the binary and the commit' + prediction: '`aprender-zram-generator --version` starts with "aprender-zram-generator " and contains the first 9 hex of the git sha' + test: 'NOT YET WRITTEN — the binary-target shape bin:versionLacksSha, once extract:binary lands' + if_fails: 'name: FIXED on batch/ont-10 (was "trueno-zram-generator 0.69.3"); sha: LIVE RED on 0.69.3, clears at rc.1 (car/0.70.0 carries #4219)' + mutation: 'restore the trueno name after a fix; must turn RED on bin:versionString' + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-004 + rule: 'G1.3/G1.4 the argv surface equals the snapshot' + prediction: 'the snapshot has no command and exactly {EARLY_DIR, LATE_DIR, NORMAL_DIR}' + test: 'cargo test -p aprender-zram-generator --test cli_surface_gate -- advertised_options_are_the_pinned_surface' + if_fails: 'the generator could stop accepting systemd''s argv with the ontology green; zram units would silently not be written' + mutation: 'remove LATE_DIR from the snapshot, or add a subcommand; each must turn RED' + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-005 + rule: 'ledger rows exist' + prediction: 'docs/audits/surface_audit.csv has ≥1 row with binary = aprender-zram-generator (1 on 2026-09-26)' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'the generator would ship unaudited' + mutation: 'delete the aprender-zram-generator row; must turn RED naming aprender-zram-generator' + - id: FALSIFY-BIN-APRENDER-ZRAM-GENERATOR-006 + rule: 'unusable input is rejected cleanly' + prediction: 'a NORMAL_DIR under a regular file (a missing NORMAL_DIR is valid: a generator creates it), with stderr prefixed by the binary name: each exits non-zero, not 101, with no "panicked" on stderr' + test: 'cargo test -p aprender-zram-generator --test cli_surface_gate unusable_input_is_rejected' + if_fails: 'MEASURED 2026-09-27 (aprender-01): exits 1 with no panic, GREEN; before the fix the error said "trueno-zram-generator:" and the prefix assertion was RED' + mutation: 'make a rejection path return Ok(()) or unwrap the read; must turn RED on that row' diff --git a/contracts/binary-debt-v1.yaml b/contracts/binary-debt-v1.yaml new file mode 100644 index 0000000000..a592027b4e --- /dev/null +++ b/contracts/binary-debt-v1.yaml @@ -0,0 +1,144 @@ +metadata: + version: "1.0.0" + created: "2026-09-25" + author: "PAIML Engineering" + description: > + Binary-debt ledger (#4058, EPIC #4057). Every binary the workspace ships has + exactly one row here, classified against the one-`apr`-entry-point plan, and + the two debt counters (BINARY_DEBT, LEGACY_NAMES) never exceed their ceilings. + The binary universe is DERIVED by scripts/check_binary_debt.sh from every + package manifest (root, crates/*, workspace members AND excludes), so an + excluded crate's binary (aprender-viz-ttop, trueno-ublk, ccpa-sft-export) + cannot hide from it. + kind: pattern + references: + - "paiml/aprender#4057 — EPIC: one apr binary; binary-debt census and classes" + - "paiml/aprender#4058 — this ledger and its derived-universe guard" + - "paiml/aprender#4060 — 0.70.0: delete the absorbed duplicates" + - "paiml/aprender#4061 — 0.70.0: DECIDE-row quorum" + - "scripts/check_binary_debt.sh" + +five_whys: + why1: "Why a ledger? The #4057 census was a hand-typed table; nothing failed when a new binary appeared." + why2: "Why derive the universe? The census scanned crates/*/ only and missed trueno-ublk and ccpa-sft-export, both excluded crates." + why3: "Why not cargo metadata? It lists workspace members only; every excluded crate is invisible to it." + why4: "Why a STALE finding? A universe scan that silently collapses would otherwise read as zero debt, a clean pass." + why5: "Why ceilings? Debt that is only counted drifts up; a shrink-only ceiling makes each deletion stick." + +equations: + binary_debt: + formula: "BINARY_DEBT = |{ b in ledger : class(b) != KEEP }|" + domain: "ledger rows, one per derived binary" + codomain: "non-negative integer" + invariants: + - "BINARY_DEBT <= ceilings.current.binary_debt" + - "for each armed release r with workspace version >= r: BINARY_DEBT <= r.binary_debt" + legacy_names: + formula: "LEGACY_NAMES = |{ n in legacy_names : sunset(n) = null }|" + domain: "legacy crates.io names" + codomain: "non-negative integer" + invariants: + - "LEGACY_NAMES <= ceilings.current.legacy_names" + ledger_is_the_universe: + formula: "keys(ledger) = { (package, bin) : bin shipped by any package manifest in root ∪ crates/* ∪ members ∪ exclude, cargo-fuzz packages removed }" + domain: "the repository tree" + codomain: "boolean" + invariants: + - "no NEW: every derived binary has a row" + - "no STALE: every row names a derived binary" + +proof_obligations: + - type: invariant + property: "Every shipped binary is ledgered" + formal: "universe ⊆ keys(ledger)" + applies_to: all + - type: invariant + property: "Every ledger row is live" + formal: "keys(ledger) ⊆ universe" + applies_to: all + - type: bound + property: "Debt never exceeds its ceiling" + formal: "BINARY_DEBT <= ceiling ∧ LEGACY_NAMES <= ceiling" + applies_to: all + +falsification_tests: + - id: FALSIFY-BIN-DEBT-001 + rule: "The ledger matches the derived universe and both counters are within ceiling" + prediction: "bash scripts/check_binary_debt.sh exits 0 on the tree" + test: "bash scripts/check_binary_debt.sh" + if_fails: "A binary was added or removed without a ledger row change, or debt rose above its ceiling" + - id: FALSIFY-BIN-DEBT-002 + rule: "The guard turns RED on each defect class (NEW, excluded-crate NEW, STALE, CLASS, DUP, CEILING, armed release ceiling)" + prediction: "every row of the case table gets its expected verdict" + test: "bash scripts/check_binary_debt.sh --self-test" + if_fails: "A finding class went vacuous; the guard would pass a tree it should fail" + +# ---- the ledger --------------------------------------------------------------- +# Class vocabulary is #4057's. KEEP + RENAME counts as debt until the rename lands. +classes: [KEEP, KEEP + RENAME, MERGE, MERGE-INTO-apr, DEPRECATE→DELETE, DECIDE] + +ceilings: + # ENFORCED now: the measured baseline, 2026-09-25. Lower it as rows retire; + # never raise it without a cop ruling. + current: {binary_debt: 28, legacy_names: 13} + # #4057's release table, each binary_debt +1 for trueno-ublk (DECIDE, missed by + # the census). UNARMED: arming one blocks that release until its deletions land; + # arming is a cop ruling. + releases: + - {release: 0.70.0, binary_debt: 23, legacy_names: 13, armed: false} + - {release: 0.71.0, binary_debt: 17, legacy_names: 13, armed: false} + - {release: 0.72.0, binary_debt: 11, legacy_names: 13, armed: false} + - {release: 0.73.0, binary_debt: 5, legacy_names: 13, armed: false} + - {release: 0.74.0, binary_debt: 0, legacy_names: 6, armed: false} + - {release: 0.75.0, binary_debt: 0, legacy_names: 0, armed: false} + +legacy_names: + - {name: renacer, sunset: null} + - {name: trueno-db, sunset: null} + - {name: entrenar, sunset: null} + - {name: alimentar, sunset: null} + - {name: ttop, sunset: null} + - {name: presentar, sunset: null} + - {name: trueno-rag, sunset: null} + - {name: verificar, sunset: null} + - {name: trueno-explain, sunset: null} + - {name: simular, sunset: null} + - {name: batuta, sunset: null} + - {name: probar, sunset: null} + - {name: probador, sunset: null} + +binaries: + - {crate: aprender, bin: apr, class: KEEP, reason: "the one entry point", target: null} + - {crate: aprender-compute-xtask, bin: aprender-compute-xtask, class: KEEP, reason: "build tooling, never installed", target: null} + - {crate: aprender-contracts-cli, bin: pv, class: KEEP, reason: "contract CLI, dogfooded by every gate", target: null} + - {crate: aprender-contracts-cli, bin: pv-sat, class: KEEP, reason: "ONT-5 SHACL reasoner (added after the census); gates call it as its own ELF. Folding it into `pv sat` is a cop ruling, and would count as debt", target: null} + - {crate: aprender-zram-generator, bin: aprender-zram-generator, class: KEEP, reason: "systemd generator, must be a standalone ELF", target: null} + - {crate: ccpa-sft-export, bin: ccpa-sft-export, class: KEEP, reason: "publish = false dev tool (excluded crate; missed by the #4057 census)", target: null} + - {crate: aprender-orchestrate, bin: aprender-orchestrate, class: KEEP + RENAME, reason: "rename to batuta", target: "0.73.0"} + - {crate: aprender-profile, bin: aprender-profile, class: KEEP + RENAME, reason: "rename to renacer", target: "0.73.0"} + - {crate: apr-cli, bin: apr, class: MERGE, reason: "duplicate of the root facade's apr", target: "0.71.0"} + - {crate: apr-cli, bin: aprender-corpus-ingest, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.71.0"} + - {crate: aprender-explain, bin: aprender-explain, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.71.0"} + - {crate: aprender-ptx-debug, bin: aprender-ptx-debug, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.71.0"} + - {crate: aprender-qa-certify, bin: aprender-qa-readme-sync, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.71.0"} + - {crate: aprender-qa-cli, bin: apr-qa, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.71.0"} + - {crate: aprender-train-bench, bin: aprender-train-bench, class: MERGE-INTO-apr, reason: "becomes an apr train subcommand", target: "0.72.0"} + - {crate: aprender-train-distill, bin: aprender-train-distill, class: MERGE-INTO-apr, reason: "apr distill exists", target: "0.72.0"} + - {crate: aprender-train-inspect, bin: aprender-train-inspect, class: MERGE-INTO-apr, reason: "apr inspect exists", target: "0.72.0"} + - {crate: aprender-train-lora, bin: aprender-train-lora, class: MERGE-INTO-apr, reason: "apr finetune exists", target: "0.72.0"} + - {crate: aprender-data, bin: aprender-data, class: MERGE-INTO-apr, reason: "becomes apr data", target: "0.73.0"} + - {crate: aprender-test-cli, bin: aprender-test-cli, class: MERGE-INTO-apr, reason: "becomes an apr subcommand", target: "0.73.0"} + - {crate: aprender-cbtop, bin: aprender-cbtop, class: DEPRECATE→DELETE, reason: "superseded by apr cbtop", target: "0.70.0"} + - {crate: aprender-cgp, bin: aprender-cgp, class: DEPRECATE→DELETE, reason: "absorbed duplicate", target: "0.70.0"} + - {crate: aprender-present-terminal, bin: aprender-ptop, class: DEPRECATE→DELETE, reason: "monitor duplicate", target: "0.73.0"} + - {crate: aprender-rag-cli, bin: aprender-rag, class: DEPRECATE→DELETE, reason: "absorbed duplicate", target: "0.70.0"} + - {crate: aprender-simulate, bin: aprender-simulate, class: DEPRECATE→DELETE, reason: "absorbed duplicate", target: "0.70.0"} + - {crate: aprender-train-canary, bin: aprender-train-canary, class: DEPRECATE→DELETE, reason: "excluded crate, canary harness", target: "0.72.0"} + - {crate: aprender-train-shell, bin: aprender-train-shell, class: DEPRECATE→DELETE, reason: "superseded by apr train", target: "0.72.0"} + - {crate: aprender-zram-cli, bin: aprender-zram, class: DEPRECATE→DELETE, reason: "absorbed duplicate", target: "0.70.0"} + - {crate: aprender-db, bin: aprender-db, class: DECIDE, reason: "#4061 quorum", target: "0.70.0"} + - {crate: aprender-present-cli, bin: aprender-present, class: DECIDE, reason: "#4061 quorum", target: "0.70.0"} + - {crate: aprender-present-terminal, bin: aprender-score, class: DECIDE, reason: "#4061 quorum", target: "0.70.0"} + - {crate: aprender-verify-ml, bin: aprender-verify, class: DECIDE, reason: "#4061 quorum", target: "0.70.0"} + - {crate: aprender-viz-ttop, bin: aprender-viz-ttop, class: DECIDE, reason: "#4061 quorum (excluded crate)", target: "0.70.0"} + - {crate: trueno-ublk, bin: trueno-ublk, class: DECIDE, reason: "excluded, broken rust-version (#2470); missed by the #4057 census", target: "0.70.0"} diff --git a/contracts/binary-surface-v1.yaml b/contracts/binary-surface-v1.yaml new file mode 100644 index 0000000000..fa623cef01 --- /dev/null +++ b/contracts/binary-surface-v1.yaml @@ -0,0 +1,178 @@ +# ────────────────────────────────────────────── +# binary-surface-v1 — every cargo [[bin]] target as a focus node, and the surface it exposes (ONT-001 §5 ONT-4g; +# ONT-10 epic aprender#4502) +# +# WHAT THIS IS. The shared vocabulary and shapes for the Σ entity type `binary` (extractor `binary`). A node is +# keyed `binary//`, NEVER by name alone: `apr` is declared by two packages (apr-cli and the root +# facade aprender), so 29 bin targets carry 28 names. Each exposed surface — a cli:Command, an http route, an mcp +# tool — is a sibling Entity reached by the role `bin:exposes`. +# bin:command is every command PATH under the binary, space-joined, without the binary name (`ontology tbox`): each +# leaf, plus each non-leaf whose Usage shows `[COMMAND]` (it runs on its own). clap's auto `help` is excluded; a +# user-defined `help` is a command (S9 convention). Shapes count paths, not top-level subcommands. +# Each node is ALSO typed `bin:target//`, the class a per-binary contract's shapes target. +# +# A SNAPSHOT, NEVER A LIVE BINARY. extract:binary reads the tracked record `scripts/dogfood_surfaces.sh --snapshot` +# writes to evidence/binary/snapshot.jsonl (one JSON line per bin target, schema "binary-snapshot/v1", LC_ALL=C +# sorted by package,target, no timestamps, `--snapshot --twice` byte-identical; key list in the script's header). +# The gate never execs a binary, so its verdict is a function of the tree. +# +# PER-BINARY CONTRACTS. Each bin target ALSO gets one contract of its own, drafted by its ONT-10 slice owner to the +# exemplar contracts/bin-aprender-contracts-cli--pv-v1.yaml (S1). File name: bin----v1.yaml; +# `entity.ref` = binary//. This file holds what every binary shares; the per-binary file holds +# only what that binary's surface must do. +# +# NOT ARMED YET. Σ `binary` is `implemented: false` until extract:binary lands (aprender-98); until then these +# shapes have no focus nodes and are not in contracts/lint-baseline.json armed_shapes[]. +# +# KIND: pattern. Vocabulary, an extractor and shapes over a graph; the proof is the gate's own case table. +# ────────────────────────────────────────────── +name: binary-surface-v1 +version: "1.0.0" +scope: > + How a cargo bin target becomes a focus node typed ont:Binary from the tracked surface snapshot, the shapes every + binary node and the surface-audit ledger must satisfy, and the dogfood gates (G0.1, G0.5, G1.2–G1.4, G4.1–G4.2) + those shapes absorb. Out of scope: building or running a binary (the snapshot writer does that), and what any one + binary's surface must do (its per-binary contract). +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-26' + last_modified: '2026-09-26' + author: PAIML Engineering + description: > + Every cargo bin target is a focus node keyed by package and target, read from a tracked, hashed snapshot; its + version names the commit it was built from, two targets sharing a name agree on help and version, and every + binary has at least one row in the surface-audit ledger. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §5 ONT-4g (a1c73918)' + - 'aprender#4476 — the surface_audit ledger gate (scripts/surface_audit_bins_gate.sh)' + - 'aprender#4502 — ONT-10 epic batch; gates/ont10-sizing.md slices S1–S20' + - 'scripts/dogfood_surfaces.sh — the snapshot writer (--snapshot, --twice)' + - 'contracts/ontology.yaml — entity_types binary, extractors binary, role bin:exposes' + - 'docs/audits/surface_audit.csv — the ledger, a csv entity' + +entity: + type: binary + +relations: + depends_on: [ont-shapes-v1, ont-sigma-v1] + +shapes: + - id: binary-target + targetClass: ont:Binary + properties: + - {path: bin:package, minCount: 1, maxCount: 1} + - {path: bin:target, minCount: 1, maxCount: 1} + - {path: bin:name, minCount: 1, maxCount: 1} + - {path: bin:srcPath, minCount: 1, maxCount: 1} + - {path: bin:gitSha, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{40}$"} + - {path: bin:versionString, minCount: 1, maxCount: 1, minLength: 1} + - {path: bin:versionSha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$"} + - {path: bin:sha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$"} + - {path: bin:helpSha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$"} + # the surface, as sorted literals (the slices' per-binary shapes pin these with in: + min/maxCount) and as + # bin:exposes IRI edges to the sibling cli:Command / http-route / mcp-tool entities + - {path: bin:command} + - {path: bin:route} + - {path: bin:mcpTool} + # top-level long options, --help/--version excluded (S13 ptop, S15 aprender-profile are option-only binaries) + - {path: bin:option} + # top-level positionals, as the `Arguments:` section of --help names them, brackets and `...` stripped + # (aprender-profile `-- ...` → COMMAND; zram-generator EARLY_DIR, LATE_DIR, NORMAL_DIR) + - {path: bin:positional} + - {path: bin:exposes, nodeKind: IRI} + # ledger reconciliation, materialized by the extractor against docs/audits/surface_audit.csv + - {path: bin:unledgeredCommand, maxCount: 0} + - {path: bin:unledgeredRoute, maxCount: 0} + - {path: bin:unledgeredTool, maxCount: 0} + - {path: bin:ledgerOrphan, maxCount: 0} + - {path: bin:ledgerOrphanRoute, maxCount: 0} + # G0.1 — the extractor materializes this when bin:versionString does not contain the first 9 hex of bin:gitSha. + - {path: bin:versionLacksSha, maxCount: 0} + # G0.5 — materialized on BOTH nodes when two targets share bin:name and differ in helpSha256 or versionSha256. + - {path: bin:namePairMismatch, maxCount: 0} + # G1.2 — a target whose `--help` exits non-zero or prints nothing. + - {path: bin:helpFailed, maxCount: 0} + # every binary has ≥1 feature row in docs/audits/surface_audit.csv (resolved by bin:name). + - {path: bin:auditRow, minCount: 1} + + - id: surface-audit-ledger + targetClass: csv:Dataset + properties: + - {path: csv:header, minCount: 1, maxCount: 1, + in: ["binary,feature,quality_1_10,verified_hardware,top_competitor,in_dogfood_skill,cluster_id,cluster_label,evidence_path,confidence"]} + - {path: csv:rows, minCount: 1, maxCount: 1, datatype: xsd:integer} + - {path: csv:sha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$"} + - {path: csv:producer, minCount: 1, maxCount: 1, resolves: path} + +equations: + one_node_per_bin_target: + formula: "|nodes(ont:Binary)| = |{t ∈ targets(cargo metadata --no-deps) : bin ∈ kind(t)}|" + domain: "the tracked snapshot and `cargo metadata --no-deps --format-version 1` at the same commit" + codomain: "by_entity_type.binary, an integer the probe compares to the metadata count" + invariants: + - "a node's key is binary//; two packages declaring one name are two nodes" + preconditions: + - "the snapshot was written by scripts/dogfood_surfaces.sh --snapshot at this commit" + postconditions: + - "two extractions are byte-identical (R-15)" + lean_theorem: none — L4 not declared + version_names_its_commit: + formula: "∀ b ∈ Binary: sha9(gitSha(b)) ⊑ version(b)" + domain: "every ont:Binary node" + codomain: "no bin:versionLacksSha triple, or a Fail naming the node" + invariants: + - "a --version that names no commit cannot be placed against a release (G0.1)" + preconditions: + - "gitSha is the commit the snapshot writer built" + postconditions: + - "bin:versionLacksSha has maxCount 0" + lean_theorem: none — L4 not declared + +invariants: + - id: BIN-INV-001 + property: every bin target in cargo metadata has exactly one node, keyed by package and target + formal: '∀ t ∈ bin_targets: |{b ∈ Binary : key(b) = (pkg(t), target(t))}| = 1' + prose: false + - id: BIN-INV-002 + property: two targets sharing a name agree on help and version (G0.5) + formal: 'name(a) = name(b) ⇒ helpSha256(a) = helpSha256(b) ∧ versionSha256(a) = versionSha256(b)' + prose: false + - id: BIN-INV-003 + property: every binary has at least one surface-audit row + formal: '∀ b ∈ Binary: |{r ∈ surface_audit : r.binary = name(b)}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-BIN-001 + rule: 'mutation 1 — drop a bin target from the snapshot' + prediction: 'by_entity_type.binary ≠ the cargo metadata bin count, and the ONT-4g probe fails' + test: 'crates/aprender-contracts/src/ontology/extract/binary_tests.rs::falsify_bin_001_a_dropped_line_is_red_naming_the_target (fixture crates/aprender-contracts/tests/fixtures/ont4g/)' + if_fails: 'a binary nobody snapshots has no surface and no gate' + mutation: 'delete one line of the snapshot; the probe must turn RED naming the count' + - id: FALSIFY-BIN-002 + rule: 'mutation 2 — key by name alone' + prediction: 'the two apr targets collapse into one node and the count is one short' + test: 'crates/aprender-contracts/src/ontology/extract/binary_tests.rs::falsify_bin_002_the_apr_pair_is_two_nodes' + if_fails: 'the facade apr and the apr-cli apr can diverge unseen (G0.5)' + mutation: 'build the node IRI from bin:name only; must turn RED' + - id: FALSIFY-BIN-003 + rule: 'mutation 3 — a --version without the commit' + prediction: 'bin:versionLacksSha fires on that node and binary-target fails naming it' + test: 'crates/aprender-contracts/src/ontology/extract/binary_tests.rs::falsify_bin_003_a_version_without_the_commit_fires' + if_fails: 'a stale binary reads as the one built from HEAD (G0.1)' + mutation: 'strip the sha from one snapshot version; must turn RED' + - id: FALSIFY-BIN-004 + rule: 'mutation 4 — the apr pair diverges' + prediction: 'bin:namePairMismatch fires on both apr nodes' + test: 'crates/aprender-contracts/src/ontology/extract/binary_tests.rs::falsify_bin_004_the_apr_pair_diverging_fires_on_both' + if_fails: 'cargo install aprender ships a different apr than the one tested' + mutation: 'change one apr helpSha256 in the snapshot; must turn RED' + - id: FALSIFY-BIN-005 + rule: 'mutation 5 — a binary with no ledger row' + prediction: 'bin:auditRow minCount 1 fails naming the node; scripts/surface_audit_bins_gate.sh --require-rows exits 1' + test: 'scripts/check_surface_audit_bins.sh (aprender#4476)' + if_fails: 'a binary ships with no audited feature' + mutation: 'delete every pv row from docs/audits/surface_audit.csv; must turn RED naming pv' diff --git a/contracts/binding-allowlist.json b/contracts/binding-allowlist.json new file mode 100644 index 0000000000..155e4b5e41 --- /dev/null +++ b/contracts/binding-allowlist.json @@ -0,0 +1,819 @@ +{ + "entries": [ + { + "symbol": "aprender::serialization::apr::AprReader::read", + "reason": "ghost: no `fn read` in an `impl AprReader` or `trait AprReader` in `crates/aprender-core/src/serialization/apr/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "batuta::src::agent::driver::validate::tokenizer_encode::encode", + "reason": "ghost: no `mod src` or `use \u2026 src` in `crates/aprender-orchestrate/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "batuta::src::agent::session::Session::new", + "reason": "ghost: no `mod src` or `use \u2026 src` in `crates/aprender-orchestrate/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "batuta::src::cli::mod::build_transpiler_args", + "reason": "ghost: no `mod src` or `use \u2026 src` in `crates/aprender-orchestrate/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "batuta::src::cli::pipeline_cmds::cmd_analyze", + "reason": "ghost: no `mod src` or `use \u2026 src` in `crates/aprender-orchestrate/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "batuta::src::cli::pipeline_cmds_transpile::cmd_transpile", + "reason": "ghost: no `mod src` or `use \u2026 src` in `crates/aprender-orchestrate/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::accuracy", + "reason": "ghost: no `fn accuracy` (free or in an impl) or item `accuracy` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::attention", + "reason": "ghost: no `fn attention` (free or in an impl) or item `attention` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::autograd::cuda_training::ComputeDevice::auto_detect", + "reason": "ghost: no `mod ComputeDevice` or `use \u2026 ComputeDevice` in `crates/aprender-train/src/autograd/cuda_training.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::autograd::tensor::Tensor::clone", + "reason": "ghost: no `fn clone` in an `impl Tensor` or `trait Tensor` in `crates/aprender-train/src/autograd/tensor.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::bounds", + "reason": "ghost: no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::confusion_matrix", + "reason": "ghost: no `fn confusion_matrix` (free or in an impl) or item `confusion_matrix` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::classify_pipeline::estimate_wgpu_vram", + "reason": "ghost: no `fn estimate_wgpu_vram` (free or in an impl) or item `estimate_wgpu_vram` in `crates/aprender-train/src/finetune/classify_pipeline/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::classify_trainer::load_checkpoint", + "reason": "ghost: no `fn load_checkpoint` (free or in an impl) or item `load_checkpoint` in `crates/aprender-train/src/finetune/classify_trainer.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::data_parallel::DataParallelCoordinator::allreduce", + "reason": "ghost: no `fn allreduce` in an `impl DataParallelCoordinator` or `trait DataParallelCoordinator` in `crates/aprender-train/src/finetune/data_parallel.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::data_parallel::DataParallelCoordinator::shard_data", + "reason": "ghost: no `fn shard_data` in an `impl DataParallelCoordinator` or `trait DataParallelCoordinator` in `crates/aprender-train/src/finetune/data_parallel.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::effective_batch_size", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::epochs", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::gradient_clip_norm", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::learning_rate", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::lora_alpha", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::max_seq_len", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::finetune::qlora_config::QLoraConfig::warmup_steps", + "reason": "ghost: no `mod qlora_config` or `use \u2026 qlora_config` in `crates/aprender-train/src/finetune/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::gelu", + "reason": "ghost: no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::greedy", + "reason": "ghost: no `fn greedy` (free or in an impl) or item `greedy` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::matmul", + "reason": "ghost: no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::ndcg_at_k", + "reason": "ghost: no `fn ndcg_at_k` (free or in an impl) or item `ndcg_at_k` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::output", + "reason": "ghost: no `fn output` (free or in an impl) or item `output` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::predict", + "reason": "ghost: no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::quantize", + "reason": "ghost: no `fn quantize` (free or in an impl) or item `quantize` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::relu", + "reason": "ghost: no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::rmse", + "reason": "ghost: no `fn rmse` (free or in an impl) or item `rmse` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::sample", + "reason": "ghost: no `fn sample` (free or in an impl) or item `sample` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::sigmoid", + "reason": "ghost: no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::statistics", + "reason": "ghost: no `fn statistics` (free or in an impl) or item `statistics` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::temperature", + "reason": "ghost: no `fn temperature` (free or in an impl) or item `temperature` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::top_k", + "reason": "ghost: no `fn top_k` (free or in an impl) or item `top_k` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::total_bytes", + "reason": "ghost: no `fn total_bytes` (free or in an impl) or item `total_bytes` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::transformer::cuda_block::CudaTransformerBlock::gpu_forward", + "reason": "ghost: no `fn gpu_forward` in an `impl CudaTransformerBlock` or `trait CudaTransformerBlock` in `crates/aprender-train/src/transformer/cuda_block.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::transformer::cuda_block::CudaTransformerBlock::swiglu_ffn", + "reason": "ghost: no `fn swiglu_ffn` in an `impl CudaTransformerBlock` or `trait CudaTransformerBlock` in `crates/aprender-train/src/transformer/cuda_block.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::transpose", + "reason": "ghost: no `fn transpose` (free or in an impl) or item `transpose` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "entrenar::update", + "reason": "ghost: no `fn update` (free or in an impl) or item `update` in `crates/aprender-train/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-core::src::virtualization::cells_to_render", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-core::src::virtualization::render_count", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-core::src::virtualization::render_range", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-core::src::virtualization::should_render", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-core::src::virtualization::should_render_cell", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-layout::src::compute_block::render_order", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::app::record_render", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::compute_block::render", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::direct::diff_renderer::render_full", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::perf_trace::trackers::for_render", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::tools::bench::render_frame", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::widgets::micro_heat_bar::render_string", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::widgets::micro_heat_bar::render_text", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-terminal::src::widgets::ux::render_lines", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar-widgets::src::list::render_with", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar::src::browser::app::render_counter", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar::src::browser::app::render_dashboard", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar::src::browser::app::render_json", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar::src::browser::app::render_widget", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "presentar::crates::presentar::src::webgpu::render_instances", + "reason": "ghost: crate `presentar` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "realizar::api::cuda_batch_scheduler::CudaBatchScheduler::schedule_step", + "reason": "ghost: no `mod CudaBatchScheduler` or `use \u2026 CudaBatchScheduler` in `crates/aprender-serve/src/api/cuda_batch_scheduler.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::apr::cuda_part_02_part_03::AprV2ModelCuda::upload_quantized_weights_to_gpu", + "reason": "ghost: no `mod cuda_part_02_part_03` or `use \u2026 cuda_part_02_part_03` in `crates/aprender-serve/src/apr/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::arch_requirements_fallback::WeightRole::field_name", + "reason": "ghost: no `mod arch_requirements_fallback` or `use \u2026 arch_requirements_fallback` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::arch_requirements_fallback::required_roles", + "reason": "ghost: no `mod arch_requirements_fallback` or `use \u2026 arch_requirements_fallback` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::bounds", + "reason": "ghost: no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::attention::paged_attention_into", + "reason": "ghost: no `fn paged_attention_into` (free or in an impl) or item `paged_attention_into` in `crates/aprender-serve/src/cuda/executor/attention.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::executor_api::ExecutorApi::start_brick_id", + "reason": "ghost: no `mod ExecutorApi` or `use \u2026 ExecutorApi` in `crates/aprender-serve/src/cuda/executor/executor_api.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::executor_api::ExecutorApi::stop_brick_id", + "reason": "ghost: no `mod ExecutorApi` or `use \u2026 ExecutorApi` in `crates/aprender-serve/src/cuda/executor/executor_api.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::kv_cache::BlockAllocator::allocate", + "reason": "ghost: no `mod BlockAllocator` or `use \u2026 BlockAllocator` in `crates/aprender-serve/src/cuda/executor/kv_cache.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::kv_cache::PagedKvCache::slot", + "reason": "ghost: no `mod PagedKvCache` or `use \u2026 PagedKvCache` in `crates/aprender-serve/src/cuda/executor/kv_cache.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::kv_cache_part_02_part_03::flash_attention_cached", + "reason": "ghost: no `mod kv_cache_part_02_part_03` or `use \u2026 kv_cache_part_02_part_03` in `crates/aprender-serve/src/cuda/executor/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::layers::apply::apply_qk_norm", + "reason": "ghost: no `mod apply` or `use \u2026 apply` in `crates/aprender-serve/src/cuda/executor/layers/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::layers::graphed_capture::try_graph_capture", + "reason": "ghost: no `mod graphed_capture` or `use \u2026 graphed_capture` in `crates/aprender-serve/src/cuda/executor/layers/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::layers::prefill::CudaExecutor::chunked_prefill", + "reason": "ghost: no `fn chunked_prefill` in an `impl CudaExecutor` or `trait CudaExecutor` in `crates/aprender-serve/src/cuda/executor/layers/prefill.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::executor::q4k_mwv_gemv::CudaExecutor::batched_qkv_dp4a_q4k_gemv_into", + "reason": "ghost: no `mod q4k_mwv_gemv` or `use \u2026 q4k_mwv_gemv` in `crates/aprender-serve/src/cuda/executor/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::cuda::kernel_generator::CudaKernels::generate_ptx", + "reason": "ghost: no `fn generate_ptx` in an `impl CudaKernels` or `trait CudaKernels` in `crates/aprender-serve/src/cuda/kernel_generator.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::effective_context", + "reason": "ghost: no `fn effective_context` (free or in an impl) or item `effective_context` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::element_count", + "reason": "ghost: no `fn element_count` (free or in an impl) or item `element_count` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::flash_attention", + "reason": "ghost: no `fn flash_attention` (free or in an impl) or item `flash_attention` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gelu", + "reason": "ghost: no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::OwnedQuantizedModel::forward", + "reason": "ghost: no `mod OwnedQuantizedModel` or `use \u2026 OwnedQuantizedModel` in `crates/aprender-serve/src/gguf/inference_types.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::config::ArchConstraints::from_gguf_metadata", + "reason": "ghost: no `fn from_gguf_metadata` in an `impl ArchConstraints` or `trait ArchConstraints` in `crates/aprender-serve/src/gguf/config.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::cuda::generate_batched_streaming", + "reason": "ghost: no `fn generate_batched_streaming` (free or in an impl) or item `generate_batched_streaming` in `crates/aprender-serve/src/gguf/cuda/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::inference::attention_part_02::flash_attention_tiled", + "reason": "ghost: no `mod attention_part_02` or `use \u2026 attention_part_02` in `crates/aprender-serve/src/gguf/inference/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::inference::cached::single_flattened_batched_owned::flattened_batched_gemm", + "reason": "ghost: no `mod single_flattened_batched_owned` or `use \u2026 single_flattened_batched_owned` in `crates/aprender-serve/src/gguf/inference/cached/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::inference::forward::Model::forward", + "reason": "ghost: no `mod Model` or `use \u2026 Model` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::inference::forward::batch_part_02_part_03::prefill_batch", + "reason": "ghost: no `mod batch_part_02_part_03` or `use \u2026 batch_part_02_part_03` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::inference::forward::generate_with_batched_prefill", + "reason": "ghost: no `fn generate_with_batched_prefill` (free or in an impl) or item `generate_with_batched_prefill` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gguf::token::GgufTokenizer::merge_rules", + "reason": "ghost: no `mod GgufTokenizer` or `use \u2026 GgufTokenizer` in `crates/aprender-serve/src/gguf/token.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::adapters::using::forward_layer", + "reason": "ghost: no `mod using` or `use \u2026 using` in `crates/aprender-serve/src/gpu/adapters/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::forward_from_model::forward_block_traced", + "reason": "ghost: no `mod forward_from_model` or `use \u2026 forward_from_model` in `crates/aprender-serve/src/gpu/scheduler/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_decay", + "reason": "ghost: no `fn gated_delta_net_decay` (free or in an impl) or item `gated_delta_net_decay` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_delta", + "reason": "ghost: no `fn gated_delta_net_delta` (free or in an impl) or item `gated_delta_net_delta` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_forward", + "reason": "ghost: no `fn gated_delta_net_forward` (free or in an impl) or item `gated_delta_net_forward` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_output", + "reason": "ghost: no `fn gated_delta_net_output` (free or in an impl) or item `gated_delta_net_output` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_read", + "reason": "ghost: no `fn gated_delta_net_read` (free or in an impl) or item `gated_delta_net_read` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::linear_attn::gated_delta_net_write", + "reason": "ghost: no `fn gated_delta_net_write` (free or in an impl) or item `gated_delta_net_write` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu::scheduler::model_forward_block_gpu::fused_qkv_projection", + "reason": "ghost: no `mod model_forward_block_gpu` or `use \u2026 model_forward_block_gpu` in `crates/aprender-serve/src/gpu/scheduler/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gpu_threshold", + "reason": "ghost: no `fn gpu_threshold` (free or in an impl) or item `gpu_threshold` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::gqa", + "reason": "ghost: no `fn gqa` (free or in an impl) or item `gqa` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::greedy", + "reason": "ghost: no `fn greedy` (free or in an impl) or item `greedy` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::layers::position::ScaledRope::new", + "reason": "ghost: no `mod ScaledRope` or `use \u2026 ScaledRope` in `crates/aprender-serve/src/layers/position.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::matmul", + "reason": "ghost: no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::position_embedding", + "reason": "ghost: no `fn position_embedding` (free or in an impl) or item `position_embedding` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::predict", + "reason": "ghost: no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::quantize", + "reason": "ghost: no `fn quantize` (free or in an impl) or item `quantize` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::relu", + "reason": "ghost: no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::rmsnorm", + "reason": "ghost: no `fn rmsnorm` (free or in an impl) or item `rmsnorm` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::safetensors::safetensors_config::SafeTensorsConfig::is_hybrid_attention", + "reason": "ghost: no `mod safetensors_config` or `use \u2026 safetensors_config` in `crates/aprender-serve/src/safetensors/mod.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::sample", + "reason": "ghost: no `fn sample` (free or in an impl) or item `sample` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::sigmoid", + "reason": "ghost: no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::silu", + "reason": "ghost: no `fn silu` (free or in an impl) or item `silu` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::softmax", + "reason": "ghost: no `fn softmax` (free or in an impl) or item `softmax` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::standard_softmax", + "reason": "ghost: no `fn standard_softmax` (free or in an impl) or item `standard_softmax` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::tensor_count", + "reason": "ghost: no `fn tensor_count` (free or in an impl) or item `tensor_count` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::top_k", + "reason": "ghost: no `fn top_k` (free or in an impl) or item `top_k` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::top_p", + "reason": "ghost: no `fn top_p` (free or in an impl) or item `top_p` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::total_bytes", + "reason": "ghost: no `fn total_bytes` (free or in an impl) or item `total_bytes` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::transpose", + "reason": "ghost: no `fn transpose` (free or in an impl) or item `transpose` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::update", + "reason": "ghost: no `fn update` (free or in an impl) or item `update` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "realizar::verification_specs::verify_kv_cache_size", + "reason": "ghost: no `mod verification_specs` or `use \u2026 verification_specs` in `crates/aprender-serve/src/arch_requirements.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::bounds", + "reason": "ghost: no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::bytes_per_param", + "reason": "ghost: no `fn bytes_per_param` (free or in an impl) or item `bytes_per_param` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::cbtop::adaptive_ml::types::ClassificationMetrics::precision", + "reason": "ghost: no `mod cbtop` or `use \u2026 cbtop` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::cbtop::adaptive_ml::types::ClassificationMetrics::recall", + "reason": "ghost: no `mod cbtop` or `use \u2026 cbtop` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::cbtop::quantize::gguf::GgufReader::tensor_count", + "reason": "ghost: no `mod cbtop` or `use \u2026 cbtop` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::cbtop::roofline::HardwareProfile::theoretical_peak_at_oi", + "reason": "ghost: no `mod cbtop` or `use \u2026 cbtop` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::crates::trueno_quant::quantize::quantize_q4_k", + "reason": "ghost: no `mod crates` or `use \u2026 crates` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::eigenvector", + "reason": "ghost: no `fn eigenvector` (free or in an impl) or item `eigenvector` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::embedding_lookup", + "reason": "ghost: no `fn embedding_lookup` (free or in an impl) or item `embedding_lookup` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::gelu", + "reason": "ghost: no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::log_softmax", + "reason": "ghost: no `fn log_softmax` (free or in an impl) or item `log_softmax` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::matmul", + "reason": "ghost: no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::predict", + "reason": "ghost: no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::read", + "reason": "ghost: no `fn read` (free or in an impl) or item `read` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::relu", + "reason": "ghost: no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::sigmoid", + "reason": "ghost: no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::silu", + "reason": "ghost: no `fn silu` (free or in an impl) or item `silu` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::softmax", + "reason": "ghost: no `fn softmax` (free or in an impl) or item `softmax` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::update", + "reason": "ghost: no `fn update` (free or in an impl) or item `update` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno::write", + "reason": "ghost: no `fn write` (free or in an impl) or item `write` in `crates/aprender-compute/src/lib.rs`", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::chunk::mod::set_embedding", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::average_precision", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::compute", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::f1_at_k", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::hit_rate_at_k", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::mean_reciprocal_rank", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::ndcg_at_k", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::precision_at_k", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::metrics::recall_at_k", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::multivector::codec::compress", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::multivector::codec::find_nearest_centroid", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::multivector::codec::train", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::multivector::index::insert", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::multivector::types::new", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::retrieve::embedder", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::retrieve::retrieve", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::retrieve::retrieve_dense", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::retrieve::retrieve_sparse", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::sqlite::mod::chunk_count", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + }, + { + "symbol": "trueno_rag::src::sqlite::mod::get_chunk", + "reason": "ghost: crate `trueno_rag` is not a workspace member", + "ticket": "#4502" + } + ] +} diff --git a/contracts/binding.yaml b/contracts/binding.yaml index 1391a732f4..2731e3e301 100644 --- a/contracts/binding.yaml +++ b/contracts/binding.yaml @@ -30,15 +30,15 @@ bindings: status: implemented - contract: pagerank-kernel-v1.yaml equation: pagerank - module_path: aprender::graph::pagerank - function: pagerank + module_path: aprender::graph::centrality + function: Graph::pagerank signature: 'fn pagerank(adj: &Tensor, damping: f64, max_iter: usize, tol: f64) -> Tensor' status: implemented notes: Power iteration PageRank - contract: pagerank-kernel-v1.yaml equation: power_iteration - module_path: aprender::graph::pagerank - function: pagerank + module_path: aprender::graph::centrality + function: Graph::pagerank signature: 'fn pagerank(adj: &Tensor, damping: f64, max_iter: usize, tol: f64) -> Tensor' status: implemented notes: Iteration loop inside pagerank() @@ -186,7 +186,7 @@ bindings: notes: Weighted Gini impurity for binary split - contract: decision-tree-v1.yaml equation: mse_split - module_path: aprender::tree::helpers_part_02 + module_path: aprender::tree::helpers function: compute_mse signature: 'fn compute_mse(y_left: &[f32], y_right: &[f32]) -> f32' status: implemented @@ -235,61 +235,61 @@ bindings: notes: Max aggregation of node features per graph - contract: ../gguf-format-safety-v1.yaml equation: magic_validation - module_path: aprender::gguf::reader + module_path: aprender::bundle::format function: validate_magic status: implemented notes: Magic byte check before any allocation - contract: ../gguf-format-safety-v1.yaml equation: metadata_kv_safety - module_path: aprender::gguf::reader - function: parse_metadata + module_path: aprender::format::gguf::reader + function: read_metadata_value status: implemented notes: String length checked before alloc (CVE-2024-25664 mitigation) - contract: ../safetensors-format-safety-v1.yaml equation: header_size_validation - module_path: aprender::safetensors::reader - function: validate_header + module_path: aprender::format::core_io + function: parse_and_validate_header status: implemented notes: Header size bounded before JSON parsing - contract: model-format-conversion-v1.yaml equation: format_conversion_roundtrip - module_path: aprender::convert + module_path: apr_cli::commands::convert function: run status: implemented notes: Tensor count/names/shapes preserved across format conversion - contract: model-format-conversion-v1.yaml equation: quantization_bounds - module_path: aprender::quantize - function: quantize_tensor + module_path: aprender::format + function: quantize_data status: implemented notes: Error bounded by dtype-specific tolerance - contract: model-format-conversion-v1.yaml equation: import_integrity - module_path: aprender::import + module_path: apr_cli::commands::import function: run status: implemented notes: Content-based format detection, not extension-based - contract: model-format-conversion-v1.yaml equation: export_fidelity - module_path: aprender::export + module_path: apr_cli::commands::export function: run status: implemented notes: Atomic write via temp file + rename - contract: model-format-conversion-v1.yaml equation: apr_tokenizer_embedding - module_path: aprender::convert + module_path: aprender::format::converter function: save_model_tensors_with_gguf_config_and_tokenizer status: implemented notes: "P0 invariant \u2014 every APR file embeds tokenizer (PMAT-154 fix, Q4K passthrough/fallback paths)" - contract: apr-cli-operations-v1.yaml equation: side_effect_classification - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command status: implemented notes: All 48 commands classified as ReadOnly/Mutating/LongRunning - contract: apr-cli-operations-v1.yaml equation: resource_cleanup - module_path: apr_cli::dispatch + module_path: apr_cli function: dispatch_core_command status: implemented notes: GPU/tmp/thread cleanup on all exit paths via RAII @@ -297,36 +297,36 @@ bindings: equation: inference_determinism module_path: aprender::inference function: generate - status: implemented + status: not_implemented notes: temperature=0 greedy decoding is deterministic - contract: apr-cli-operations-v1.yaml equation: tokenizer_consistency - module_path: aprender::tokenizer - function: encode + module_path: aprender::text::tokenize::bpe_impl + function: BpeTokenizer::encode status: implemented notes: BPE encode/decode roundtrip for valid UTF-8 - contract: apr-cli-operations-v1.yaml equation: concurrent_model_access - module_path: aprender::serve + module_path: apr_cli::commands::serve function: run status: implemented notes: Per-request KV cache, immutable model weights - contract: apr-cli-v1.yaml equation: contract_gate_enforcement - module_path: apr_cli::dispatch + module_path: apr_cli function: execute_command status: implemented notes: "PMAT-237 contract gate \u2014 extract_model_paths + validate_model_contract before dispatch" - contract: apr-cli-v1.yaml equation: training_plan_apply_semantics module_path: apr_cli::commands::train - function: train::run + function: run_apply status: implemented notes: Plan is pure (no GPU/filesystem), Apply writes to --output directory only - contract: apr-cli-v1.yaml equation: tokenizer_training_correctness module_path: apr_cli::commands::tokenize - function: tokenize::run + function: run_apply status: implemented notes: "BPE plan/apply \u2014 vocab.json size equals requested vocab_size" - contract: apr-cli-v1.yaml @@ -392,20 +392,20 @@ bindings: - contract: apr-data-pipeline-v1.yaml equation: data_validation module_path: apr_cli::commands::data - function: run + function: run_audit status: implemented notes: JSONL/CSV/text validation with line-level error reporting - contract: apr-data-pipeline-v1.yaml equation: data_split_determinism module_path: apr_cli::commands::data - function: run + function: run_split status: implemented notes: Seed-deterministic train/val/test split - contract: apr-data-pipeline-v1.yaml equation: streaming_data_loader module_path: aprender::data::dataloader function: DataLoader::next_batch - status: implemented + status: not_implemented notes: Epoch-seeded shuffle, no sample drop on final partial batch - contract: apr-model-qa-v1.yaml equation: model_integrity_check @@ -440,26 +440,26 @@ bindings: - contract: apr-architecture-schema-v1.yaml equation: architecture_config_invariants module_path: aprender::format::gguf::api - function: GgufModelConfig::validate + function: GgufModelConfig::warn_out_of_bounds status: implemented notes: Validates hidden_size/num_heads divisibility, GQA ratio, MoE fields - contract: apr-architecture-schema-v1.yaml equation: rope_position_encoding - module_path: aprender::nn::rope - function: RotaryPositionEmbedding::validate_config + module_path: aprender::nn::transformer::attention_helpers + function: RotaryPositionEmbedding::with_base status: implemented notes: RoPE theta, type (NORM vs NEOX), frequency vector length - contract: apr-format-safety-v1.yaml equation: magic_byte_validation module_path: aprender::gguf::reader function: detect_format - status: implemented + status: not_implemented notes: GGUF/SafeTensors/APR magic byte prefix detection, panic-free on truncated input - contract: apr-format-safety-v1.yaml equation: header_integrity module_path: aprender::gguf::reader function: validate_header - status: implemented + status: not_implemented notes: Bounded allocation, no OOM from crafted tensor_count - contract: apr-chat-session-v1.yaml equation: session_state_machine @@ -469,17 +469,18 @@ bindings: notes: Init->WaitInput->Generating->WaitInput->Exit state machine - contract: apr-chat-session-v1.yaml equation: session_persistence - module_path: apr_cli::commands::chat_session + module_path: apr_cli::commands::chat function: run status: implemented notes: JSON serialization, KV-cache not persisted (rebuilt from history) # --- apr-model-graph-v1 (full LLM forward pass) --- - contract: apr-model-graph-v1.yaml equation: forward_pass_completeness - module_path: aprender::inference::forward - function: forward_pass + module_path: realizar::gguf + function: OwnedQuantizedModel::forward + signature: 'fn forward(&self, token_ids: &[u32]) -> Result>' status: implemented - notes: Full forward pass from tokens to logits + notes: Full forward pass from tokens to logits (crates/aprender-serve/src/gguf/inference/forward/forward_fused_q4k.rs); returns last-position logits [V], not [B,S,V]. Was ghost aprender::inference::forward::forward_pass (#4094) - contract: apr-model-graph-v1.yaml equation: attention_mechanism module_path: aprender::nn::transformer @@ -521,14 +522,14 @@ bindings: # --- apr-model-security-v1 (encrypt, decrypt, publish) --- - contract: apr-model-security-v1.yaml equation: encryption_roundtrip - module_path: apr_cli::commands::encrypt - function: run + module_path: aprender::format::encryption + function: save_encrypted status: implemented notes: AES-256-GCM with Argon2id key derivation - contract: apr-model-security-v1.yaml equation: publish_manifest_integrity module_path: apr_cli::commands::publish - function: run + function: execute status: implemented notes: SHA-256 manifest with signed model publishing # --- apr-model-diagnostics-v1 (hex, rosetta, oracle, diagnose) --- @@ -547,7 +548,7 @@ bindings: - contract: apr-model-diagnostics-v1.yaml equation: rosetta_fingerprint_determinism module_path: apr_cli::commands::rosetta - function: run + function: run_fingerprint status: implemented notes: Cross-format fingerprint comparison (GGUF/SafeTensors/APR) # ── apr-code-harness-ir (Pillar-5): the first L5 contract. Every obligation @@ -666,7 +667,7 @@ bindings: status: implemented notes: >- S8, the four jidoka parameters. guard_mutation_score is REPRODUCIBLE: - scripts/mutate-guard.sh is committed and its DERIVED set is 233/233 over + scripts/mutate-guard.sh is committed and its DERIVED set is 237/237 over scripts/check_pr_review_receipt.sh. THE SET SIZE IS DERIVED AND CERTAIN; THE KILL COUNT ON THIS MERGED TREE IS PENDING, and is the `pr-review-receipt` job's Arm 3. 215/215 was measured on PRREV-015's tree and 185/185 on PRREV-013's, and neither @@ -804,6 +805,21 @@ bindings: notes: 'compose(embedding, L x block, final_norm, unembed) as a shape pipeline: [S] -> [S,d] -> [S,d] (xL) -> [S,d] -> [S,V]. composition_is_well_formed() checks it chains; every block stage satisfies QE2E-INV-006 and the last stage is QE2E-CON-007''s [seq_len, V].' +- contract: gated-delta-net-v1.yaml + equation: gated_rmsnorm + module_path: realizar::gguf::forward_qwen35 + function: gated_rmsnorm + signature: 'fn gated_rmsnorm(input: &[f32], gate: &[f32], weight: &[f32], eps: f32, head_v_dim: usize, output: &mut [f32])' + status: implemented + notes: 'CPU reference of the GDN output norm; the kernel-parity shape joins the #[kernel] below to this symbol by the shared equation.' +- contract: gated-delta-net-v1.yaml + equation: gated_rmsnorm + module_path: gated_rmsnorm_oxide::kernels + function: gated_rmsnorm + signature: 'fn gated_rmsnorm(input: &[f32], gate: &[f32], weight: &[f32], out: DisjointSlice, head_dim: u32, eps: f32)' + status: implemented + kernel: true + notes: '#3522 O-1: the cuda-oxide #[kernel] (experiments/cuda-oxide/gated-rmsnorm), beside the hand PTX gdn_gated_rmsnorm; graded by evidence/kernels/gated_rmsnorm/.json.' critical_path: - softmax - rmsnorm diff --git a/contracts/census.json b/contracts/census.json index f004942d87..59cb0573c4 100644 --- a/contracts/census.json +++ b/contracts/census.json @@ -1,8 +1,8 @@ { "schema": "ont.paiml.dev/census/v1alpha1", "git_sha": null, - "n_files": 1837, - "n_parsed": 1837, + "n_files": 1889, + "n_parsed": 1889, "n_parse_errors": 0, "parse_errors": [], "quarantined_n": 0, @@ -12,26 +12,58 @@ "kernel": 387, "model-family": 28, "model-family-variant": 1, - "pattern": 95, + "pattern": 142, "pretraining-corpus": 2, "registry": 521, - "schema": 768, + "schema": 773, "tokenizer": 1, "training-loop": 8, "training-precondition-gate": 1 }, "by_entity_type": { - "gguf": 2, + "apr-model": 1, + "binary": 32, + "csv": 1, + "gguf": 3, "json": 3, - "pv-contract": 2, - "release-evidence": 1 + "kernel": 1, + "llm-context": 1, + "pv-contract": 3, + "readme": 1, + "release-evidence": 1, + "repo": 1 + }, + "by_concept": { + "AprModel": 0, + "Binary": 30, + "Code": 514, + "Contract": 1824, + "Dataset": 0, + "Doc": 0, + "Entity": 0, + "Example": 980, + "Issue": 1, + "Json": 4, + "Kernel": 836, + "KernelSymbol": 1, + "LadderRung": 0, + "LlmContext": 0, + "Milestone": 1, + "Model": 0, + "Proof": 0, + "PullRequest": 1, + "Readme": 0, + "Repo": 1, + "Statement": 410, + "Symbol": 514, + "Test": 0 }, "by_anchoring": { - "unanchored": 1829, - "class": 5, - "instance": 3 + "unanchored": 1841, + "class": 9, + "instance": 39 }, - "id_set_sha256": "2055792eb2f6a0f696509d418e57868758e669988ea6e167867b02ec9983f1d7", + "id_set_sha256": "ce0f9c175ba1345ff18e9e0f68797488dc56f63b968073b707bbb347a5db0eac", "declared_external": [ { "name": "provable-contracts", diff --git a/contracts/ci-infra-v1.yaml b/contracts/ci-infra-v1.yaml index 4b62a2239b..902a1c5e24 100644 --- a/contracts/ci-infra-v1.yaml +++ b/contracts/ci-infra-v1.yaml @@ -231,7 +231,7 @@ falsification_tests: if_fails: "RUSTFLAGS differ between workflows — code passes one, fails another" verification_summary: - total_obligations: 6 + total_obligations: 0 proven: 0 tested: 0 status: proposed diff --git a/contracts/claude-md.yaml b/contracts/claude-md.yaml new file mode 100644 index 0000000000..cc524aeb43 --- /dev/null +++ b/contracts/claude-md.yaml @@ -0,0 +1,85 @@ +# ───────────────────────────────────────────────────────────────────────────── +# claude-md — the repository's LLM context file as a graded document (ONT-001 §5 ONT-4c, Appendix B.5; #3847) +# +# WHAT IS GRADED. `extract:llm-context` reads CLAUDE.md: frontmatter `tools:` → llm:declaredTool (graded `in:` +# Σ's tool names); every `## ` heading → llm:section; a heading listed under a role in Σ +# `llm_context_role_synonyms` → that role's own predicate (v4.16 D5b), each a plain minCount 1; every inline code +# span that is a relative path → llm:referencedPath, refused by name when it does not exist; every claim-fence +# command → llm:command, refused `llm:command unresolved: ` when no merge-path workflow runs it; every +# `Never:` line → llm:neverRule. The claim set is measured into lint-baseline.json `claude_md` (F-33, F-34). +# +# KIND: pattern. The edit that makes it fail: delete `## Project Overview` (Purpose's only synonym). +# ───────────────────────────────────────────────────────────────────────────── +name: claude-md +version: "1.0.0" +scope: > + CLAUDE.md at the repository root: its role sections, the paths and commands it tells an agent about, its + declared tools and its Never rules. Out of scope: whether the advice is good. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + CLAUDE.md becomes an llm:LlmContext node; every path it names exists, every claim-fence command is one CI + runs, its tools are Σ's, and the four role sections plus a Never rule are present. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md v4.16 §5 ONT-4c, Appendix B.5 (D5b)' + - 'crates/aprender-contracts/src/ontology/extract/{llm_context,claims}.rs' + - 'contracts/ontology.yaml llm_context_role_synonyms' + +entity: + type: llm-context + ref: CLAUDE.md + +relations: + depends_on: [ont-sigma-v1, ont-shapes-v1] + +shape: + targetClass: llm:LlmContext + closed: true + properties: + - {path: llm:section, minCount: 0} + - {path: llm:purposeSection, minCount: 1} + - {path: llm:rulesSection, minCount: 1} + - {path: llm:commandsSection, minCount: 1} + - {path: llm:layoutSection, minCount: 1} + - {path: llm:referencedPath, resolves: path} + - {path: llm:command, resolves: ci-step} + - {path: llm:declaredTool, in: [pmat, pv, forjar, bashrs, cargo, gh]} + - {path: llm:neverRule, minCount: 1} + +equations: + context_resolves: + formula: "∀ p ∈ paths(CLAUDE.md): exists(p) ∧ ∀ c ∈ claims(CLAUDE.md): normalise(c) ∈ run_lines(W_merge)" + domain: CLAUDE.md's relative-path code spans and claim-fence commands + codomain: llm:referencedPath / llm:command values, or PV-ONT-012 refusals naming each + invariants: + - a path or command an agent is told about and cannot use is refused by name + preconditions: + - Σ llm_context_role_synonyms is declared + postconditions: + - the committed claude_md.verified_commands[] equals the live set (F-33) + lean_theorem: "none — L4 not declared" + +invariants: + - id: LLM-INV-001 + property: every relative path CLAUDE.md names exists + formal: '∀ p ∈ paths(CLAUDE.md): exists(root/p)' + prose: false + - id: LLM-INV-002 + property: every role has a section + formal: '∀ r ∈ {Purpose, Rules, Commands, Layout}: |{h ∈ headings₂ : h ∈ synonyms(r)}| ≥ 1' + prose: false + +falsification_tests: + - id: FALSIFY-LLM-001 + rule: a stale path is refused + prediction: > + naming a path that does not exist in a code span makes the shapes gate exit 1 naming + `llm:referencedPath unresolved:

`; renaming `## Project Overview` fails llm:purposeSection minCount + test: cargo test -p aprender-contracts --lib ontology::extract::llm_context + if_fails: an agent is steered at a file or command that does not exist diff --git a/contracts/contracts.nt b/contracts/contracts.nt index dd58e94bcf..a792e2e331 100644 --- a/contracts/contracts.nt +++ b/contracts/contracts.nt @@ -1,3 +1,2754 @@ + . + . + . + . + "DELETE /api/delete"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/version"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /metrics"^^ . + "GET /metrics/dispatch"^^ . + "GET /models"^^ . + "GET /ready"^^ . + "GET /realize/model"^^ . + "GET /tensors"^^ . + "GET /v1/audit/:request_id"^^ . + "GET /v1/effective-config"^^ . + "GET /v1/gpu/status"^^ . + "GET /v1/metrics"^^ . + "GET /v1/models"^^ . + "HTTP 404 fallback route index"^^ . + "POST /api/chat"^^ . + "POST /api/chat (stream:true, NDJSON)"^^ . + "POST /api/embeddings"^^ . + "POST /api/generate"^^ . + "POST /api/generate (stream:true, NDJSON)"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /batch/generate"^^ . + "POST /batch/tokenize"^^ . + "POST /generate"^^ . + "POST /metrics/dispatch/reset"^^ . + "POST /realize/batch"^^ . + "POST /realize/embed"^^ . + "POST /realize/generate"^^ . + "POST /realize/reload"^^ . + "POST /stream/generate"^^ . + "POST /tokenize"^^ . + "POST /v1/batch/completions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/chat/completions (stream:true, SSE)"^^ . + "POST /v1/chat/completions/stream"^^ . + "POST /v1/completions"^^ . + "POST /v1/completions (stream:true, SSE)"^^ . + "POST /v1/embeddings"^^ . + "POST /v1/explain"^^ . + "POST /v1/gpu/warmup"^^ . + "POST /v1/logprobs"^^ . + "POST /v1/perplexity"^^ . + "POST /v1/predict"^^ . + "apr attn-parity-lint"^^ . + "apr attn-viz-lint"^^ . + "apr audio-inspect-lint"^^ . + "apr awq-lint"^^ . + "apr beat-run"^^ . + "apr bench"^^ . + "apr canary check"^^ . + "apr canary create"^^ . + "apr capability"^^ . + "apr cbtop"^^ . + "apr cgp baseline"^^ . + "apr cgp bench"^^ . + "apr cgp compete"^^ . + "apr cgp contract generate"^^ . + "apr cgp contract verify"^^ . + "apr cgp diff"^^ . + "apr cgp doctor"^^ . + "apr cgp explain"^^ . + "apr cgp profile binary"^^ . + "apr cgp profile compare"^^ . + "apr cgp profile cublas"^^ . + "apr cgp profile kernel"^^ . + "apr cgp profile library"^^ . + "apr cgp profile metal"^^ . + "apr cgp profile parallel"^^ . + "apr cgp profile python"^^ . + "apr cgp profile quant"^^ . + "apr cgp profile scalar"^^ . + "apr cgp profile scaling"^^ . + "apr cgp profile simd"^^ . + "apr cgp profile wasm"^^ . + "apr cgp profile wgpu"^^ . + "apr cgp roofline"^^ . + "apr cgp trace"^^ . + "apr cgp tui"^^ . + "apr chat"^^ . + "apr chat --backend cpu"^^ . + "apr chat --backend cuda"^^ . + "apr chat --backend wgpu"^^ . + "apr check"^^ . + "apr check-finite-lint"^^ . + "apr code"^^ . + "apr code --input-format json"^^ . + "apr code --output-format json"^^ . + "apr compare-hf"^^ . + "apr compile"^^ . + "apr convert"^^ . + "apr data audit"^^ . + "apr data balance"^^ . + "apr data decontaminate"^^ . + "apr data dedup"^^ . + "apr data split"^^ . + "apr data x convert"^^ . + "apr data x dedup"^^ . + "apr data x doctest extract"^^ . + "apr data x doctest merge"^^ . + "apr data x drift compare"^^ . + "apr data x drift detect"^^ . + "apr data x drift merge"^^ . + "apr data x drift report"^^ . + "apr data x drift sketch"^^ . + "apr data x fed manifest"^^ . + "apr data x fed plan"^^ . + "apr data x fed split"^^ . + "apr data x fed verify"^^ . + "apr data x filter-text"^^ . + "apr data x fim"^^ . + "apr data x head"^^ . + "apr data x hub push"^^ . + "apr data x import hf"^^ . + "apr data x import local"^^ . + "apr data x info"^^ . + "apr data x mix"^^ . + "apr data x quality check"^^ . + "apr data x quality profiles"^^ . + "apr data x quality report"^^ . + "apr data x quality score"^^ . + "apr data x registry delete"^^ . + "apr data x registry init"^^ . + "apr data x registry list"^^ . + "apr data x registry pull"^^ . + "apr data x registry push"^^ . + "apr data x registry search"^^ . + "apr data x registry show-info"^^ . + "apr data x repl"^^ . + "apr data x schema"^^ . + "apr data x view"^^ . + "apr dataset audio-inspect"^^ . + "apr ddp-metrics-lint"^^ . + "apr debug"^^ . + "apr debug embed-viz"^^ . + "apr decrypt"^^ . + "apr devices"^^ . + "apr diagnose"^^ . + "apr diff"^^ . + "apr distill"^^ . + "apr dry-sampling-lint"^^ . + "apr embed"^^ . + "apr embed-viz-lint"^^ . + "apr embeddings-lint"^^ . + "apr encrypt"^^ . + "apr eval"^^ . + "apr eval --task classify"^^ . + "apr eval --task code"^^ . + "apr eval --task compare"^^ . + "apr eval --task contamination"^^ . + "apr eval --task correlation"^^ . + "apr eval --task human"^^ . + "apr eval --task humaneval"^^ . + "apr eval --task mbpp"^^ . + "apr eval --task plan"^^ . + "apr eval --task verify"^^ . + "apr experiment view"^^ . + "apr explain"^^ . + "apr explain-token-lint"^^ . + "apr export"^^ . + "apr export --format coreml"^^ . + "apr export --format gguf"^^ . + "apr export --format mlx"^^ . + "apr export --format onnx"^^ . + "apr export --format openvino"^^ . + "apr export --format safetensors"^^ . + "apr finetune"^^ . + "apr finetune --task classify"^^ . + "apr flow"^^ . + "apr fp8-lint"^^ . + "apr gbnf-lint"^^ . + "apr gptq-lint"^^ . + "apr gpu"^^ . + "apr gpu-memtrace-lint"^^ . + "apr grad-norm"^^ . + "apr hang-trace-lint"^^ . + "apr hex"^^ . + "apr imatrix-lint"^^ . + "apr import"^^ . + "apr inspect"^^ . + "apr kernel parity"^^ . + "apr kv-timeline-lint"^^ . + "apr lint"^^ . + "apr list"^^ . + "apr manifest"^^ . + "apr mcp"^^ . + "apr merge"^^ . + "apr modelfile parse"^^ . + "apr monitor"^^ . + "apr mono archive"^^ . + "apr mono audit"^^ . + "apr mono publish"^^ . + "apr mono shims"^^ . + "apr nccl-diag-lint"^^ . + "apr nf4-lint"^^ . + "apr ollama-chat-lint"^^ . + "apr ollama-tools-lint"^^ . + "apr oom-lint"^^ . + "apr oracle"^^ . + "apr otlp-lint"^^ . + "apr parity"^^ . + "apr parity --out"^^ . + "apr parity --per-op"^^ . + "apr parity --threshold"^^ . + "apr pipeline apply"^^ . + "apr pipeline plan"^^ . + "apr pipeline status"^^ . + "apr pipeline validate"^^ . + "apr ppl"^^ . + "apr pretrain"^^ . + "apr profile"^^ . + "apr prometheus-lint"^^ . + "apr prune"^^ . + "apr ptx"^^ . + "apr ptx-map"^^ . + "apr publish"^^ . + "apr pull"^^ . + "apr pv audit"^^ . + "apr pv book"^^ . + "apr pv census"^^ . + "apr pv certify"^^ . + "apr pv challenge check"^^ . + "apr pv challenge gen"^^ . + "apr pv check-parity"^^ . + "apr pv codegen"^^ . + "apr pv coq"^^ . + "apr pv coverage"^^ . + "apr pv diff"^^ . + "apr pv discharge check"^^ . + "apr pv discharge gen-axioms"^^ . + "apr pv discharge label-ratchet"^^ . + "apr pv discharge run"^^ . + "apr pv equations"^^ . + "apr pv explain"^^ . + "apr pv extract"^^ . + "apr pv extract-pytorch"^^ . + "apr pv flux"^^ . + "apr pv fuzz"^^ . + "apr pv generate"^^ . + "apr pv graph"^^ . + "apr pv infer"^^ . + "apr pv invariants"^^ . + "apr pv kaizen"^^ . + "apr pv kani"^^ . + "apr pv lean"^^ . + "apr pv lean-status"^^ . + "apr pv lint"^^ . + "apr pv migrate"^^ . + "apr pv mirai"^^ . + "apr pv obligations"^^ . + "apr pv ontology export"^^ . + "apr pv ontology tbox"^^ . + "apr pv pipeline"^^ . + "apr pv probar"^^ . + "apr pv proof-status"^^ . + "apr pv query"^^ . + "apr pv roofline"^^ . + "apr pv scaffold"^^ . + "apr pv score"^^ . + "apr pv status"^^ . + "apr pv tla"^^ . + "apr pv unlock"^^ . + "apr pv validate"^^ . + "apr pv verify-bindings"^^ . + "apr pv verify-pipeline"^^ . + "apr pv verify-structure"^^ . + "apr qa"^^ . + "apr qualify"^^ . + "apr quant-preservation-lint"^^ . + "apr quantize"^^ . + "apr quantize --scheme fp16"^^ . + "apr quantize --scheme int4"^^ . + "apr quantize --scheme int8"^^ . + "apr quantize --scheme q4k"^^ . + "apr rag demo"^^ . + "apr rag eval compare"^^ . + "apr rag eval gate"^^ . + "apr rag eval generate"^^ . + "apr rag eval judge"^^ . + "apr rag eval metrics"^^ . + "apr rag eval retrieve"^^ . + "apr rag eval sample"^^ . + "apr rag extract-frames"^^ . + "apr rag index"^^ . + "apr rag info"^^ . + "apr rag query"^^ . + "apr rag transcribe"^^ . + "apr react-trace-lint"^^ . + "apr registry aliases"^^ . + "apr registry-quota-lint"^^ . + "apr rerank"^^ . + "apr rm"^^ . + "apr rm-gc-lint"^^ . + "apr rosetta chain"^^ . + "apr rosetta compare-inference"^^ . + "apr rosetta convert"^^ . + "apr rosetta diff-tensors"^^ . + "apr rosetta fingerprint"^^ . + "apr rosetta inspect"^^ . + "apr rosetta validate-stats"^^ . + "apr rosetta verify"^^ . + "apr run"^^ . + "apr run --backend cpu"^^ . + "apr run --backend cuda"^^ . + "apr run --backend wgpu"^^ . + "apr run --gpu"^^ . + "apr run --no-gpu"^^ . + "apr run --stream"^^ . + "apr run --task transcribe"^^ . + "apr run --task translate"^^ . + "apr run --trace-level basic"^^ . + "apr run --trace-level chrome"^^ . + "apr run --trace-level layer"^^ . + "apr run --trace-level payload"^^ . + "apr runs diff"^^ . + "apr runs ls"^^ . + "apr runs show"^^ . + "apr serve plan"^^ . + "apr serve run"^^ . + "apr serve run --backend cpu"^^ . + "apr serve run --backend cuda"^^ . + "apr serve run --backend wgpu"^^ . + "apr serve run --no-cors"^^ . + "apr serve run --no-metrics"^^ . + "apr shard"^^ . + "apr shared-cache-lint"^^ . + "apr showcase"^^ . + "apr sim emc-check"^^ . + "apr sim emc-validate"^^ . + "apr sim help"^^ . + "apr sim list-emc"^^ . + "apr sim render"^^ . + "apr sim run"^^ . + "apr sim validate"^^ . + "apr sim verify"^^ . + "apr sim version"^^ . + "apr stamp"^^ . + "apr tensors"^^ . + "apr test llm bench"^^ . + "apr test llm bench --band"^^ . + "apr test tensor"^^ . + "apr tokenize apply"^^ . + "apr tokenize encode"^^ . + "apr tokenize encode-corpus"^^ . + "apr tokenize import-hf"^^ . + "apr tokenize plan"^^ . + "apr tokenize repair-manifest"^^ . + "apr tokenize train"^^ . + "apr tool-use-lint"^^ . + "apr trace"^^ . + "apr train apply"^^ . + "apr train apply --task classify"^^ . + "apr train apply --task pretrain"^^ . + "apr train archive"^^ . + "apr train cluster-status"^^ . + "apr train halving"^^ . + "apr train plan"^^ . + "apr train plan --task classify"^^ . + "apr train plan --task pretrain"^^ . + "apr train submit"^^ . + "apr train sweep"^^ . + "apr train watch"^^ . + "apr tree"^^ . + "apr tui"^^ . + "apr tune"^^ . + "apr typical-p-lint"^^ . + "apr unified-search-lint"^^ . + "apr unshard"^^ . + "apr validate"^^ . + "apr validate-manifest"^^ . + "apr zram benchmark"^^ . + "apr zram create"^^ . + "apr zram remove"^^ . + "apr zram status"^^ . + "mcp:apr.bench"^^ . + "mcp:apr.finetune"^^ . + "mcp:apr.qa"^^ . + "mcp:apr.run"^^ . + "mcp:apr.serve"^^ . + "mcp:apr.tensors"^^ . + "mcp:apr.trace"^^ . + "mcp:apr.validate"^^ . + "mcp:apr.version"^^ . + "attn-parity-lint"^^ . + "attn-viz-lint"^^ . + "audio-inspect-lint"^^ . + "awq-lint"^^ . + "beat-run"^^ . + "bench"^^ . + "canary check"^^ . + "canary create"^^ . + "capability"^^ . + "cbtop"^^ . + "cgp baseline"^^ . + "cgp bench"^^ . + "cgp compete"^^ . + "cgp contract generate"^^ . + "cgp contract verify"^^ . + "cgp diff"^^ . + "cgp doctor"^^ . + "cgp explain"^^ . + "cgp profile binary"^^ . + "cgp profile compare"^^ . + "cgp profile cublas"^^ . + "cgp profile kernel"^^ . + "cgp profile library"^^ . + "cgp profile metal"^^ . + "cgp profile parallel"^^ . + "cgp profile python"^^ . + "cgp profile quant"^^ . + "cgp profile scalar"^^ . + "cgp profile scaling"^^ . + "cgp profile simd"^^ . + "cgp profile wasm"^^ . + "cgp profile wgpu"^^ . + "cgp roofline"^^ . + "cgp trace"^^ . + "cgp tui"^^ . + "chat"^^ . + "check"^^ . + "check-finite-lint"^^ . + "code"^^ . + "compare-hf"^^ . + "compile"^^ . + "convert"^^ . + "data audit"^^ . + "data balance"^^ . + "data decontaminate"^^ . + "data dedup"^^ . + "data split"^^ . + "data x convert"^^ . + "data x dedup"^^ . + "data x drift compare"^^ . + "data x drift detect"^^ . + "data x drift merge"^^ . + "data x drift report"^^ . + "data x drift sketch"^^ . + "data x fed manifest"^^ . + "data x fed plan"^^ . + "data x fed split"^^ . + "data x fed verify"^^ . + "data x filter-text"^^ . + "data x fim"^^ . + "data x head"^^ . + "data x import local"^^ . + "data x info"^^ . + "data x mix"^^ . + "data x quality check"^^ . + "data x quality profiles"^^ . + "data x quality report"^^ . + "data x quality score"^^ . + "data x registry delete"^^ . + "data x registry init"^^ . + "data x registry list"^^ . + "data x registry pull"^^ . + "data x registry push"^^ . + "data x registry search"^^ . + "data x registry show-info"^^ . + "data x repl"^^ . + "data x schema"^^ . + "data x view"^^ . + "dataset audio-inspect"^^ . + "ddp-metrics-lint"^^ . + "debug"^^ . + "debug embed-viz"^^ . + "decrypt"^^ . + "devices"^^ . + "diagnose"^^ . + "diff"^^ . + "distill"^^ . + "dry-sampling-lint"^^ . + "embed"^^ . + "embed-viz-lint"^^ . + "embeddings-lint"^^ . + "encrypt"^^ . + "eval"^^ . + "experiment view"^^ . + "explain"^^ . + "explain-token-lint"^^ . + "export"^^ . + "finetune"^^ . + "flow"^^ . + "fp8-lint"^^ . + "gbnf-lint"^^ . + "gptq-lint"^^ . + "gpu"^^ . + "gpu-memtrace-lint"^^ . + "grad-norm"^^ . + "hang-trace-lint"^^ . + "hex"^^ . + "imatrix-lint"^^ . + "import"^^ . + "inspect"^^ . + "kernel parity"^^ . + "kv-timeline-lint"^^ . + "lint"^^ . + "list"^^ . + "manifest"^^ . + "mcp"^^ . + "merge"^^ . + "modelfile parse"^^ . + "monitor"^^ . + "nccl-diag-lint"^^ . + "nf4-lint"^^ . + "ollama-chat-lint"^^ . + "ollama-tools-lint"^^ . + "oom-lint"^^ . + "oracle"^^ . + "otlp-lint"^^ . + "parity"^^ . + "pipeline apply"^^ . + "pipeline plan"^^ . + "pipeline status"^^ . + "pipeline validate"^^ . + "ppl"^^ . + "pretrain"^^ . + "profile"^^ . + "prometheus-lint"^^ . + "prune"^^ . + "ptx"^^ . + "ptx-debug analyze"^^ . + "ptx-debug gen-fkr"^^ . + "ptx-debug version"^^ . + "ptx-map"^^ . + "publish"^^ . + "pull"^^ . + "pv audit"^^ . + "pv book"^^ . + "pv census"^^ . + "pv certify"^^ . + "pv challenge check"^^ . + "pv challenge gen"^^ . + "pv check-parity"^^ . + "pv codegen"^^ . + "pv coq"^^ . + "pv coverage"^^ . + "pv diff"^^ . + "pv discharge check"^^ . + "pv discharge gen-axioms"^^ . + "pv discharge label-ratchet"^^ . + "pv discharge run"^^ . + "pv equations"^^ . + "pv explain"^^ . + "pv extract"^^ . + "pv extract-pytorch"^^ . + "pv flux"^^ . + "pv fuzz"^^ . + "pv generate"^^ . + "pv graph"^^ . + "pv infer"^^ . + "pv invariants"^^ . + "pv kaizen"^^ . + "pv kani"^^ . + "pv lean"^^ . + "pv lean-status"^^ . + "pv lint"^^ . + "pv migrate"^^ . + "pv mirai"^^ . + "pv obligations"^^ . + "pv ontology export"^^ . + "pv ontology tbox"^^ . + "pv pipeline"^^ . + "pv probar"^^ . + "pv proof-status"^^ . + "pv query"^^ . + "pv roofline"^^ . + "pv scaffold"^^ . + "pv score"^^ . + "pv status"^^ . + "pv tla"^^ . + "pv unlock"^^ . + "pv validate"^^ . + "pv verify-bindings"^^ . + "pv verify-pipeline"^^ . + "pv verify-structure"^^ . + "qa"^^ . + "qualify"^^ . + "quant-preservation-lint"^^ . + "quantize"^^ . + "rag demo"^^ . + "rag extract-frames"^^ . + "rag index"^^ . + "rag info"^^ . + "rag query"^^ . + "rag transcribe"^^ . + "react-trace-lint"^^ . + "registry aliases"^^ . + "registry-quota-lint"^^ . + "rerank"^^ . + "rm"^^ . + "rm-gc-lint"^^ . + "rosetta chain"^^ . + "rosetta compare-inference"^^ . + "rosetta convert"^^ . + "rosetta diff-tensors"^^ . + "rosetta fingerprint"^^ . + "rosetta inspect"^^ . + "rosetta validate-stats"^^ . + "rosetta verify"^^ . + "run"^^ . + "runs diff"^^ . + "runs ls"^^ . + "runs show"^^ . + "serve plan"^^ . + "serve run"^^ . + "shard"^^ . + "shared-cache-lint"^^ . + "showcase"^^ . + "sim emc-check"^^ . + "sim emc-validate"^^ . + "sim help"^^ . + "sim list-emc"^^ . + "sim render"^^ . + "sim run"^^ . + "sim validate"^^ . + "sim verify"^^ . + "sim version"^^ . + "stamp"^^ . + "tensors"^^ . + "test llm bench"^^ . + "test tensor"^^ . + "tokenize apply"^^ . + "tokenize encode"^^ . + "tokenize encode-corpus"^^ . + "tokenize import-hf"^^ . + "tokenize plan"^^ . + "tokenize repair-manifest"^^ . + "tokenize train"^^ . + "tool-use-lint"^^ . + "trace"^^ . + "train apply"^^ . + "train archive"^^ . + "train cluster-status"^^ . + "train halving"^^ . + "train plan"^^ . + "train submit"^^ . + "train sweep"^^ . + "train watch"^^ . + "tree"^^ . + "tui"^^ . + "tune"^^ . + "typical-p-lint"^^ . + "unified-search-lint"^^ . + "unshard"^^ . + "validate"^^ . + "validate-manifest"^^ . + "zram benchmark"^^ . + "zram create"^^ . + "zram remove"^^ . + "zram status"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b7a93598835cd6d0fe750fa42a685a497045bf126c7a0e47533fed7ca5e28b9d"^^ . + "data x doctest extract"^^ . + "data x doctest merge"^^ . + "data x hub push"^^ . + "data x import hf"^^ . + "mono archive"^^ . + "mono audit"^^ . + "mono publish"^^ . + "mono shims"^^ . + "rag eval compare"^^ . + "rag eval gate"^^ . + "rag eval generate"^^ . + "rag eval judge"^^ . + "rag eval metrics"^^ . + "rag eval retrieve"^^ . + "rag eval sample"^^ . + "POST /v1/logprobs"^^ . + "POST /v1/perplexity"^^ . + "apr.bench"^^ . + "apr.finetune"^^ . + "apr.qa"^^ . + "apr.run"^^ . + "apr.serve"^^ . + "apr.tensors"^^ . + "apr.trace"^^ . + "apr.validate"^^ . + "apr.version"^^ . + "apr"^^ . + "apr-cli"^^ . + "DELETE /api/delete"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/version"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /metrics"^^ . + "GET /metrics/dispatch"^^ . + "GET /models"^^ . + "GET /ready"^^ . + "GET /realize/model"^^ . + "GET /tensors"^^ . + "GET /v1/audit/:request_id"^^ . + "GET /v1/effective-config"^^ . + "GET /v1/gpu/status"^^ . + "GET /v1/metrics"^^ . + "GET /v1/models"^^ . + "POST /api/chat"^^ . + "POST /api/embeddings"^^ . + "POST /api/generate"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /batch/generate"^^ . + "POST /batch/tokenize"^^ . + "POST /generate"^^ . + "POST /metrics/dispatch/reset"^^ . + "POST /realize/batch"^^ . + "POST /realize/embed"^^ . + "POST /realize/generate"^^ . + "POST /realize/reload"^^ . + "POST /stream/generate"^^ . + "POST /tokenize"^^ . + "POST /v1/batch/completions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/chat/completions/stream"^^ . + "POST /v1/completions"^^ . + "POST /v1/embeddings"^^ . + "POST /v1/explain"^^ . + "POST /v1/gpu/warmup"^^ . + "POST /v1/predict"^^ . + "31384c3c30ad47bfcaae4dd8352bd25b2ccf146e102159f63546c212a79aebc9"^^ . + "crates/apr-cli/src/main.rs"^^ . + "apr"^^ . + "ptx-debug analyze"^^ . + "ptx-debug gen-fkr"^^ . + "ptx-debug version"^^ . + "9c7d74d55399bdaf139a088a4a021006fdde1a2721d86c333cc890ada9804801"^^ . + "apr 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-corpus-ingest --version"^^ . + "aprender-corpus-ingest plan"^^ . + "aprender-corpus-ingest validate-contract"^^ . + "plan"^^ . + "validate-contract"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "3aacb0d04dcfea82adf867ce13ea8ee82d827b7935f9b1aded576458eeed880e"^^ . + "aprender-corpus-ingest"^^ . + "apr-cli"^^ . + "72254e647f8e6125949043ac24ee644e24cdd9f8fcdc54a6ef3ebeb7b2c5ce75"^^ . + "crates/apr-cli/src/bin/apr-corpus-ingest.rs"^^ . + "aprender-corpus-ingest"^^ . + "16c3b1de2c1b82707a2fa8ae8bd88a7143131c28cd741c6893d989c8492c60df"^^ . + "aprender-corpus-ingest 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-cbtop (interactive TUI; --headless)"^^ . + "aprender-cbtop --version"^^ . + "aprender-cbtop bench"^^ . + "aprender-cbtop optimize analyze"^^ . + "aprender-cbtop optimize baseline"^^ . + "aprender-cbtop optimize check"^^ . + "bench"^^ . + "optimize analyze"^^ . + "optimize baseline"^^ . + "optimize check"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "c4dc2690a55b6d36248ac132a7b1253eb387b8eddc04daf5f342270f96d5ef03"^^ . + "aprender-cbtop"^^ . + "aprender-cbtop"^^ . + "bcd44383284afe6db0c9cfe1b56dd34aa93618dcb968bb1fe73365c7709a62a9"^^ . + "crates/aprender-cbtop/src/main.rs"^^ . + "aprender-cbtop"^^ . + "30b28d29712282263664bb5f0daa2e6211b55af0ac05b995fa01908d871023b5"^^ . + "aprender-cbtop 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-cgp --version"^^ . + "aprender-cgp baseline"^^ . + "aprender-cgp bench"^^ . + "aprender-cgp compete"^^ . + "aprender-cgp contract generate"^^ . + "aprender-cgp contract verify"^^ . + "aprender-cgp diff"^^ . + "aprender-cgp doctor"^^ . + "aprender-cgp explain"^^ . + "aprender-cgp profile binary"^^ . + "aprender-cgp profile compare"^^ . + "aprender-cgp profile cublas"^^ . + "aprender-cgp profile kernel"^^ . + "aprender-cgp profile library"^^ . + "aprender-cgp profile metal"^^ . + "aprender-cgp profile parallel"^^ . + "aprender-cgp profile python"^^ . + "aprender-cgp profile quant"^^ . + "aprender-cgp profile scalar"^^ . + "aprender-cgp profile scaling"^^ . + "aprender-cgp profile simd"^^ . + "aprender-cgp profile wasm"^^ . + "aprender-cgp profile wgpu"^^ . + "aprender-cgp roofline"^^ . + "aprender-cgp trace"^^ . + "aprender-cgp tui"^^ . + "baseline"^^ . + "bench"^^ . + "compete"^^ . + "contract generate"^^ . + "contract verify"^^ . + "diff"^^ . + "doctor"^^ . + "explain"^^ . + "profile binary"^^ . + "profile compare"^^ . + "profile cublas"^^ . + "profile kernel"^^ . + "profile library"^^ . + "profile metal"^^ . + "profile parallel"^^ . + "profile python"^^ . + "profile quant"^^ . + "profile scalar"^^ . + "profile scaling"^^ . + "profile simd"^^ . + "profile wasm"^^ . + "profile wgpu"^^ . + "roofline"^^ . + "trace"^^ . + "tui"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b8af590c9699f603777465783d50c195dfa1c1b1ee756cecc6e331cb6be666f8"^^ . + "aprender-cgp"^^ . + "aprender-cgp"^^ . + "8955bc6a879ac73d914aa4194355f434e191657617422aed47148000cf8a5aff"^^ . + "crates/aprender-cgp/src/main.rs"^^ . + "aprender-cgp"^^ . + "8d1c881f906f48c3a0e674af69bc09edf765d3a54b9913094c156e00ac70f90e"^^ . + "cgp 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-compute-xtask --version"^^ . + "aprender-compute-xtask check-simd"^^ . + "aprender-compute-xtask install-hooks"^^ . + "aprender-compute-xtask validate-examples"^^ . + "check-simd"^^ . + "install-hooks"^^ . + "validate-examples"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "0fd1378010ef06964de87702bde179b3ec1bd1d2c773017ef442bba01bce7c8b"^^ . + "aprender-compute-xtask"^^ . + "aprender-compute-xtask"^^ . + "7d2d3e79853f22187544b57969e67fa53a1550f595ea8611e49246243cd36040"^^ . + "crates/aprender-compute-xtask/src/main.rs"^^ . + "aprender-compute-xtask"^^ . + "7c87b626a1eca6d1178ed19ad9935bd5371476bcb594b2e34363cf3ba420329d"^^ . + "aprender-compute-xtask 0.70.0 (47d198202)"^^ . + . + . + . + "pv audit"^^ . + "pv book"^^ . + "pv census"^^ . + "pv certify"^^ . + "pv challenge check"^^ . + "pv challenge gen"^^ . + "pv check-parity"^^ . + "pv codegen"^^ . + "pv coq"^^ . + "pv coverage"^^ . + "pv diff"^^ . + "pv discharge check"^^ . + "pv discharge gen-axioms"^^ . + "pv discharge label-ratchet"^^ . + "pv discharge run"^^ . + "pv equations"^^ . + "pv explain"^^ . + "pv extract"^^ . + "pv extract-pytorch"^^ . + "pv flux"^^ . + "pv fuzz"^^ . + "pv generate"^^ . + "pv graph"^^ . + "pv infer"^^ . + "pv invariants"^^ . + "pv kaizen"^^ . + "pv kani"^^ . + "pv lean"^^ . + "pv lean-status"^^ . + "pv lint"^^ . + "pv migrate"^^ . + "pv mirai"^^ . + "pv obligations"^^ . + "pv ontology export"^^ . + "pv ontology tbox"^^ . + "pv pipeline"^^ . + "pv probar"^^ . + "pv proof-status"^^ . + "pv query"^^ . + "pv roofline"^^ . + "pv scaffold"^^ . + "pv score"^^ . + "pv status"^^ . + "pv tla"^^ . + "pv unlock"^^ . + "pv validate"^^ . + "pv verify-bindings"^^ . + "pv verify-pipeline"^^ . + "pv verify-structure"^^ . + "audit"^^ . + "book"^^ . + "census"^^ . + "certify"^^ . + "challenge check"^^ . + "challenge gen"^^ . + "check-parity"^^ . + "codegen"^^ . + "coq"^^ . + "coverage"^^ . + "diff"^^ . + "discharge check"^^ . + "discharge gen-axioms"^^ . + "discharge label-ratchet"^^ . + "discharge run"^^ . + "equations"^^ . + "explain"^^ . + "extract"^^ . + "extract-pytorch"^^ . + "flux"^^ . + "fuzz"^^ . + "generate"^^ . + "graph"^^ . + "infer"^^ . + "invariants"^^ . + "kaizen"^^ . + "kani"^^ . + "lean"^^ . + "lean-status"^^ . + "lint"^^ . + "migrate"^^ . + "mirai"^^ . + "obligations"^^ . + "ontology export"^^ . + "ontology tbox"^^ . + "pipeline"^^ . + "probar"^^ . + "proof-status"^^ . + "query"^^ . + "roofline"^^ . + "scaffold"^^ . + "score"^^ . + "status"^^ . + "tla"^^ . + "unlock"^^ . + "validate"^^ . + "verify-bindings"^^ . + "verify-pipeline"^^ . + "verify-structure"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "89531431006777244e533a7a98ee1a7977a108514e222dda12df1e44834e7733"^^ . + "pv"^^ . + "aprender-contracts-cli"^^ . + "188818e209c9af1861339f5a70c6a11adf50e2dbeb1d004bcfacf92fcab64fad"^^ . + "crates/aprender-contracts-cli/src/main.rs"^^ . + "pv"^^ . + "b97ae0ee2b274082af78439eadb6168d2bb1551f1a3646938c74e268d3f067c2"^^ . + "pv 0.70.0 (47d198202) (aprender provable-contracts verifier)\ncrate aprender-contracts-cli — https://github.com/paiml/aprender\nVerifies YAML contracts under contracts/; run `pv --help` for the command surface.\nThis is NOT pv(1), the pipe viewer (distro package `pv`, or the `pv` crate on crates.io)."^^ . + . + . + . + "pv-sat --self-test"^^ . + "pv-sat [CONTRACT_DIR] (SAT witness write)"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "58bca35c2499aaf32a3c6535b86ec7e60c77ca0a4f53a6ecec61bc4fd3c14564"^^ . + "pv-sat"^^ . + "aprender-contracts-cli"^^ . + "349c75a188b705a67dbec0383174e088d7c73b12363745e79570fa18aa1f893b"^^ . + "crates/aprender-contracts-cli/src/bin/pv-sat/main.rs"^^ . + "pv-sat"^^ . + "ccc3a35f79fb5a2114a3caa2e5fb1243539a9004f4478c571ad14c77f7287bfd"^^ . + "pv-sat 0.70.0 (47d198202) (aprender ONT-5 reasoner)"^^ . + . + . + . + "aprender-data --version"^^ . + "aprender-data convert"^^ . + "aprender-data dedup"^^ . + "aprender-data doctest extract"^^ . + "aprender-data doctest merge"^^ . + "aprender-data drift compare"^^ . + "aprender-data drift detect"^^ . + "aprender-data drift merge"^^ . + "aprender-data drift report"^^ . + "aprender-data drift sketch"^^ . + "aprender-data fed manifest"^^ . + "aprender-data fed plan"^^ . + "aprender-data fed split"^^ . + "aprender-data fed verify"^^ . + "aprender-data filter-text"^^ . + "aprender-data fim"^^ . + "aprender-data head"^^ . + "aprender-data hub push"^^ . + "aprender-data import hf"^^ . + "aprender-data import local"^^ . + "aprender-data info"^^ . + "aprender-data mix"^^ . + "aprender-data quality check"^^ . + "aprender-data quality profiles"^^ . + "aprender-data quality report"^^ . + "aprender-data quality score"^^ . + "aprender-data registry delete"^^ . + "aprender-data registry init"^^ . + "aprender-data registry list"^^ . + "aprender-data registry pull"^^ . + "aprender-data registry push"^^ . + "aprender-data registry search"^^ . + "aprender-data registry show-info"^^ . + "aprender-data repl"^^ . + "aprender-data schema"^^ . + "aprender-data view"^^ . + "convert"^^ . + "dedup"^^ . + "drift compare"^^ . + "drift detect"^^ . + "drift merge"^^ . + "drift report"^^ . + "drift sketch"^^ . + "fed manifest"^^ . + "fed plan"^^ . + "fed split"^^ . + "fed verify"^^ . + "filter-text"^^ . + "fim"^^ . + "head"^^ . + "import local"^^ . + "info"^^ . + "mix"^^ . + "quality check"^^ . + "quality profiles"^^ . + "quality report"^^ . + "quality score"^^ . + "registry delete"^^ . + "registry init"^^ . + "registry list"^^ . + "registry pull"^^ . + "registry push"^^ . + "registry search"^^ . + "registry show-info"^^ . + "repl"^^ . + "schema"^^ . + "view"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b8214ec953dfe3ebb99617eb2ea4200c517ce11e5b4374851a917439d0ba6bf3"^^ . + "doctest extract"^^ . + "doctest merge"^^ . + "hub push"^^ . + "import hf"^^ . + "aprender-data"^^ . + "aprender-data"^^ . + "03f4d3209c6facc3b0603b26d209d8a310117a3239571c780e3b3b0fc1c1011e"^^ . + "crates/aprender-data/src/main.rs"^^ . + "aprender-data"^^ . + "a2b1007b2427d651f306e97d0bd753eb9b7f329a765e036b0f8fbc138e50f73f"^^ . + "aprender-data 0.70.0 (47d198202)"^^ . + . + . + . + "GET /health (aprender-db)"^^ . + "GET /status (aprender-db)"^^ . + "POST /query (aprender-db)"^^ . + "aprender-db --config (single-mode DB server)"^^ . + "aprender-db --version"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "0f906ee0b41275dd8edfab3dda71d1251b6a962552d9c241e717f156f8a48857"^^ . + "GET /health"^^ . + "GET /status"^^ . + "POST /query"^^ . + "aprender-db"^^ . + "aprender-db"^^ . + "81cb86f2c811665a6a8ca04de4f6f5237e007314a5ec869ec72457279ab3555c"^^ . + "crates/aprender-db/src/main.rs"^^ . + "aprender-db"^^ . + "c2b613c616c04920a4f4b0678dfd3f78eb2891f33ef862d65b9c275a530e3ebc"^^ . + "aprender-db 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-explain --version"^^ . + "aprender-explain bugs"^^ . + "aprender-explain compare"^^ . + "aprender-explain diff"^^ . + "aprender-explain ptx"^^ . + "aprender-explain simd"^^ . + "aprender-explain tui"^^ . + "aprender-explain wgpu"^^ . + "bugs"^^ . + "compare"^^ . + "diff"^^ . + "ptx"^^ . + "simd"^^ . + "tui"^^ . + "wgpu"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "152f7c9b34f1ad69065ca65f6606d59eefdc7972d7e7eac3bbb18f2e49181d6e"^^ . + "aprender-explain"^^ . + "aprender-explain"^^ . + "4d0c33d4f8156e69147dd9f9202416e9b9f710b4ed000d8c71f6402d39ece6e6"^^ . + "crates/aprender-explain/src/main.rs"^^ . + "aprender-explain"^^ . + "598a96c3ce54b733ecdfc808719e232a27c46945c7bdd5a813790c1b08665871"^^ . + "aprender-explain 0.70.0 (47d198202)"^^ . + . + . + . + "DELETE /api/delete"^^ . + "DELETE /api/v1/conversations/:id"^^ . + "DELETE /api/v1/data/files/:id"^^ . + "DELETE /api/v1/models/registry/:name"^^ . + "DELETE /api/v1/prompts/:id"^^ . + "DELETE /api/v1/rag/index"^^ . + "DELETE /api/v1/train/runs/:id"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/v1/audio/formats"^^ . + "GET /api/v1/audit"^^ . + "GET /api/v1/batch"^^ . + "GET /api/v1/batch/:id"^^ . + "GET /api/v1/chat/parameters"^^ . + "GET /api/v1/config"^^ . + "GET /api/v1/conversations"^^ . + "GET /api/v1/conversations/:id"^^ . + "GET /api/v1/conversations/export"^^ . + "GET /api/v1/conversations/search"^^ . + "GET /api/v1/data/datasets"^^ . + "GET /api/v1/data/datasets/:id/preview"^^ . + "GET /api/v1/data/files"^^ . + "GET /api/v1/data/files/:id/info"^^ . + "GET /api/v1/data/recipes"^^ . + "GET /api/v1/data/recipes/:id"^^ . + "GET /api/v1/eval/runs"^^ . + "GET /api/v1/eval/runs/:id"^^ . + "GET /api/v1/experiments"^^ . + "GET /api/v1/experiments/:id/compare"^^ . + "GET /api/v1/mcp/info"^^ . + "GET /api/v1/metrics"^^ . + "GET /api/v1/models"^^ . + "GET /api/v1/models/merge/strategies"^^ . + "GET /api/v1/models/registry"^^ . + "GET /api/v1/models/status"^^ . + "GET /api/v1/prompts"^^ . + "GET /api/v1/prompts/:id"^^ . + "GET /api/v1/rag/search"^^ . + "GET /api/v1/rag/status"^^ . + "GET /api/v1/system"^^ . + "GET /api/v1/tools"^^ . + "GET /api/v1/train/presets"^^ . + "GET /api/v1/train/runs"^^ . + "GET /api/v1/train/runs/:id"^^ . + "GET /api/v1/train/runs/:id/metrics"^^ . + "GET /api/v1/ws"^^ . + "GET /assets/*path"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /v1/models"^^ . + "GET /v1/models/:id"^^ . + "PATCH /api/v1/conversations/:id"^^ . + "POST /api/chat"^^ . + "POST /api/generate"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /api/v1/audio/transcriptions"^^ . + "POST /api/v1/batch"^^ . + "POST /api/v1/chat/completions"^^ . + "POST /api/v1/conversations"^^ . + "POST /api/v1/conversations/import"^^ . + "POST /api/v1/data/recipes"^^ . + "POST /api/v1/data/recipes/:id/run"^^ . + "POST /api/v1/data/upload"^^ . + "POST /api/v1/data/upload/json"^^ . + "POST /api/v1/detokenize"^^ . + "POST /api/v1/embeddings"^^ . + "POST /api/v1/eval/perplexity"^^ . + "POST /api/v1/experiments"^^ . + "POST /api/v1/experiments/:id/runs"^^ . + "POST /api/v1/mcp"^^ . + "POST /api/v1/models/load"^^ . + "POST /api/v1/models/merge"^^ . + "POST /api/v1/models/pull"^^ . + "POST /api/v1/models/unload"^^ . + "POST /api/v1/prompts"^^ . + "POST /api/v1/rag/index"^^ . + "POST /api/v1/tokenize"^^ . + "POST /api/v1/tools"^^ . + "POST /api/v1/tools/execute"^^ . + "POST /api/v1/train/runs/:id/export"^^ . + "POST /api/v1/train/runs/:id/stop"^^ . + "POST /api/v1/train/start"^^ . + "POST /ui/chat"^^ . + "POST /v1/audio/transcriptions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/completions"^^ . + "POST /v1/embeddings"^^ . + "PUT /api/v1/chat/parameters"^^ . + "PUT /api/v1/config"^^ . + "PUT /api/v1/tools/:name/config"^^ . + "aprender-orchestrate --version"^^ . + "aprender-orchestrate agent chat"^^ . + "aprender-orchestrate agent contracts"^^ . + "aprender-orchestrate agent pool"^^ . + "aprender-orchestrate agent run"^^ . + "aprender-orchestrate agent sign"^^ . + "aprender-orchestrate agent status"^^ . + "aprender-orchestrate agent validate"^^ . + "aprender-orchestrate agent verify-sig"^^ . + "aprender-orchestrate analyze"^^ . + "aprender-orchestrate bug-hunter analyze"^^ . + "aprender-orchestrate bug-hunter deep-hunt"^^ . + "aprender-orchestrate bug-hunter diff"^^ . + "aprender-orchestrate bug-hunter ensemble"^^ . + "aprender-orchestrate bug-hunter falsify"^^ . + "aprender-orchestrate bug-hunter fuzz"^^ . + "aprender-orchestrate bug-hunter hunt"^^ . + "aprender-orchestrate bug-hunter spec"^^ . + "aprender-orchestrate bug-hunter stack"^^ . + "aprender-orchestrate bug-hunter ticket"^^ . + "aprender-orchestrate bug-hunter trend"^^ . + "aprender-orchestrate bug-hunter triage"^^ . + "aprender-orchestrate build"^^ . + "aprender-orchestrate code"^^ . + "aprender-orchestrate content emit"^^ . + "aprender-orchestrate content types"^^ . + "aprender-orchestrate content validate"^^ . + "aprender-orchestrate data tree"^^ . + "aprender-orchestrate deploy cloudflare"^^ . + "aprender-orchestrate deploy docker"^^ . + "aprender-orchestrate deploy fly"^^ . + "aprender-orchestrate deploy k8s"^^ . + "aprender-orchestrate deploy lambda"^^ . + "aprender-orchestrate experiment tree"^^ . + "aprender-orchestrate falsify"^^ . + "aprender-orchestrate hf catalog"^^ . + "aprender-orchestrate hf course"^^ . + "aprender-orchestrate hf info"^^ . + "aprender-orchestrate hf pull"^^ . + "aprender-orchestrate hf push"^^ . + "aprender-orchestrate hf search"^^ . + "aprender-orchestrate hf tree"^^ . + "aprender-orchestrate init"^^ . + "aprender-orchestrate mcp"^^ . + "aprender-orchestrate optimize"^^ . + "aprender-orchestrate oracle"^^ . + "aprender-orchestrate pacha alias"^^ . + "aprender-orchestrate pacha aliases"^^ . + "aprender-orchestrate pacha decrypt"^^ . + "aprender-orchestrate pacha encrypt"^^ . + "aprender-orchestrate pacha keygen"^^ . + "aprender-orchestrate pacha list"^^ . + "aprender-orchestrate pacha pin"^^ . + "aprender-orchestrate pacha prune"^^ . + "aprender-orchestrate pacha pull"^^ . + "aprender-orchestrate pacha rm"^^ . + "aprender-orchestrate pacha run"^^ . + "aprender-orchestrate pacha search"^^ . + "aprender-orchestrate pacha show"^^ . + "aprender-orchestrate pacha sign"^^ . + "aprender-orchestrate pacha stats"^^ . + "aprender-orchestrate pacha unpin"^^ . + "aprender-orchestrate pacha verify"^^ . + "aprender-orchestrate parf"^^ . + "aprender-orchestrate playbook lock"^^ . + "aprender-orchestrate playbook run"^^ . + "aprender-orchestrate playbook status"^^ . + "aprender-orchestrate playbook validate"^^ . + "aprender-orchestrate report"^^ . + "aprender-orchestrate reset"^^ . + "aprender-orchestrate serve"^^ . + "aprender-orchestrate stack check"^^ . + "aprender-orchestrate stack comply"^^ . + "aprender-orchestrate stack drift"^^ . + "aprender-orchestrate stack gate"^^ . + "aprender-orchestrate stack publish-status"^^ . + "aprender-orchestrate stack quality"^^ . + "aprender-orchestrate stack release"^^ . + "aprender-orchestrate stack status"^^ . + "aprender-orchestrate stack sync"^^ . + "aprender-orchestrate stack tree"^^ . + "aprender-orchestrate stack versions"^^ . + "aprender-orchestrate status"^^ . + "aprender-orchestrate transpile"^^ . + "aprender-orchestrate validate"^^ . + "aprender-orchestrate viz dashboard"^^ . + "aprender-orchestrate viz tree"^^ . + "mcp:calculator (banco POST /api/v1/mcp)"^^ . + "mcp:code_execution (banco POST /api/v1/mcp)"^^ . + "mcp:compute (agent tool registry)"^^ . + "mcp:hf_info"^^ . + "mcp:hf_integration"^^ . + "mcp:hf_search"^^ . + "mcp:hf_tree"^^ . + "mcp:memory (agent tool registry)"^^ . + "mcp:rag (agent tool registry)"^^ . + "mcp:web_search (banco POST /api/v1/mcp)"^^ . + "agent chat"^^ . + "agent contracts"^^ . + "agent pool"^^ . + "agent run"^^ . + "agent sign"^^ . + "agent status"^^ . + "agent validate"^^ . + "agent verify-sig"^^ . + "analyze"^^ . + "bug-hunter analyze"^^ . + "bug-hunter deep-hunt"^^ . + "bug-hunter diff"^^ . + "bug-hunter ensemble"^^ . + "bug-hunter falsify"^^ . + "bug-hunter fuzz"^^ . + "bug-hunter hunt"^^ . + "bug-hunter spec"^^ . + "bug-hunter stack"^^ . + "bug-hunter ticket"^^ . + "bug-hunter trend"^^ . + "bug-hunter triage"^^ . + "build"^^ . + "code"^^ . + "content emit"^^ . + "content types"^^ . + "content validate"^^ . + "data tree"^^ . + "deploy cloudflare"^^ . + "deploy docker"^^ . + "deploy fly"^^ . + "deploy k8s"^^ . + "deploy lambda"^^ . + "experiment tree"^^ . + "falsify"^^ . + "hf catalog"^^ . + "hf course"^^ . + "hf info"^^ . + "hf pull"^^ . + "hf push"^^ . + "hf search"^^ . + "hf tree"^^ . + "init"^^ . + "mcp"^^ . + "optimize"^^ . + "oracle"^^ . + "pacha alias"^^ . + "pacha aliases"^^ . + "pacha decrypt"^^ . + "pacha encrypt"^^ . + "pacha keygen"^^ . + "pacha list"^^ . + "pacha pin"^^ . + "pacha prune"^^ . + "pacha pull"^^ . + "pacha rm"^^ . + "pacha run"^^ . + "pacha search"^^ . + "pacha show"^^ . + "pacha sign"^^ . + "pacha stats"^^ . + "pacha unpin"^^ . + "pacha verify"^^ . + "parf"^^ . + "playbook lock"^^ . + "playbook run"^^ . + "playbook status"^^ . + "playbook validate"^^ . + "report"^^ . + "reset"^^ . + "serve"^^ . + "stack check"^^ . + "stack comply"^^ . + "stack drift"^^ . + "stack gate"^^ . + "stack publish-status"^^ . + "stack quality"^^ . + "stack release"^^ . + "stack status"^^ . + "stack sync"^^ . + "stack tree"^^ . + "stack versions"^^ . + "status"^^ . + "transpile"^^ . + "validate"^^ . + "viz dashboard"^^ . + "viz tree"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "75d3d25a35e94ea7654c7bc12ba89da8885ee5b61546cb679bb4e632bd8560d6"^^ . + "aprender-orchestrate"^^ . + "aprender-orchestrate"^^ . + "DELETE /api/delete"^^ . + "DELETE /api/v1/conversations/:id"^^ . + "DELETE /api/v1/data/files/:id"^^ . + "DELETE /api/v1/models/registry/:name"^^ . + "DELETE /api/v1/prompts/:id"^^ . + "DELETE /api/v1/rag/index"^^ . + "DELETE /api/v1/train/runs/:id"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/v1/audio/formats"^^ . + "GET /api/v1/audit"^^ . + "GET /api/v1/batch"^^ . + "GET /api/v1/batch/:id"^^ . + "GET /api/v1/chat/parameters"^^ . + "GET /api/v1/config"^^ . + "GET /api/v1/conversations"^^ . + "GET /api/v1/conversations/:id"^^ . + "GET /api/v1/conversations/export"^^ . + "GET /api/v1/conversations/search"^^ . + "GET /api/v1/data/datasets"^^ . + "GET /api/v1/data/datasets/:id/preview"^^ . + "GET /api/v1/data/files"^^ . + "GET /api/v1/data/files/:id/info"^^ . + "GET /api/v1/data/recipes"^^ . + "GET /api/v1/data/recipes/:id"^^ . + "GET /api/v1/eval/runs"^^ . + "GET /api/v1/eval/runs/:id"^^ . + "GET /api/v1/experiments"^^ . + "GET /api/v1/experiments/:id/compare"^^ . + "GET /api/v1/mcp/info"^^ . + "GET /api/v1/metrics"^^ . + "GET /api/v1/models"^^ . + "GET /api/v1/models/merge/strategies"^^ . + "GET /api/v1/models/registry"^^ . + "GET /api/v1/models/status"^^ . + "GET /api/v1/prompts"^^ . + "GET /api/v1/prompts/:id"^^ . + "GET /api/v1/rag/search"^^ . + "GET /api/v1/rag/status"^^ . + "GET /api/v1/system"^^ . + "GET /api/v1/tools"^^ . + "GET /api/v1/train/presets"^^ . + "GET /api/v1/train/runs"^^ . + "GET /api/v1/train/runs/:id"^^ . + "GET /api/v1/train/runs/:id/metrics"^^ . + "GET /api/v1/ws"^^ . + "GET /assets/*path"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /v1/models"^^ . + "GET /v1/models/:id"^^ . + "PATCH /api/v1/conversations/:id"^^ . + "POST /api/chat"^^ . + "POST /api/generate"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /api/v1/audio/transcriptions"^^ . + "POST /api/v1/batch"^^ . + "POST /api/v1/chat/completions"^^ . + "POST /api/v1/conversations"^^ . + "POST /api/v1/conversations/import"^^ . + "POST /api/v1/data/recipes"^^ . + "POST /api/v1/data/recipes/:id/run"^^ . + "POST /api/v1/data/upload"^^ . + "POST /api/v1/data/upload/json"^^ . + "POST /api/v1/detokenize"^^ . + "POST /api/v1/embeddings"^^ . + "POST /api/v1/eval/perplexity"^^ . + "POST /api/v1/experiments"^^ . + "POST /api/v1/experiments/:id/runs"^^ . + "POST /api/v1/mcp"^^ . + "POST /api/v1/models/load"^^ . + "POST /api/v1/models/merge"^^ . + "POST /api/v1/models/pull"^^ . + "POST /api/v1/models/unload"^^ . + "POST /api/v1/prompts"^^ . + "POST /api/v1/rag/index"^^ . + "POST /api/v1/tokenize"^^ . + "POST /api/v1/tools"^^ . + "POST /api/v1/tools/execute"^^ . + "POST /api/v1/train/runs/:id/export"^^ . + "POST /api/v1/train/runs/:id/stop"^^ . + "POST /api/v1/train/start"^^ . + "POST /ui/chat"^^ . + "POST /v1/audio/transcriptions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/completions"^^ . + "POST /v1/embeddings"^^ . + "PUT /api/v1/chat/parameters"^^ . + "PUT /api/v1/config"^^ . + "PUT /api/v1/tools/:name/config"^^ . + "f33909d49eaa195870889c319c2c5e759b5bfd65b9b3ff894e3085ce467e16f1"^^ . + "crates/aprender-orchestrate/src/main.rs"^^ . + "aprender-orchestrate"^^ . + "bef9a228bc7a9611d6434f8eaf14560aad6d08c12d599a4547dd88314edb4b82"^^ . + "aprender-orchestrate 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-present --version"^^ . + "aprender-present bundle"^^ . + "aprender-present check"^^ . + "aprender-present deploy"^^ . + "aprender-present gate"^^ . + "aprender-present new"^^ . + "aprender-present score"^^ . + "aprender-present serve"^^ . + "bundle"^^ . + "check"^^ . + "deploy"^^ . + "gate"^^ . + "new"^^ . + "score"^^ . + "serve"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b098b9327edab97690a6feffa6c5b7aede7254125b922a59d135aeb2d23ea70a"^^ . + "aprender-present"^^ . + "aprender-present-cli"^^ . + "5779553d8501238b97ccab98b2386ebac950a14de67b268f7e9000e30621a828"^^ . + "crates/aprender-present-cli/src/main.rs"^^ . + "aprender-present"^^ . + "9189e3c58390b77ff546157ee126b57b6aff3397e539fc248928c02b8644406a"^^ . + "aprender-present 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-ptop (interactive TUI system monitor)"^^ . + "aprender-ptop --dump-config"^^ . + "aprender-ptop --render-once"^^ . + "aprender-ptop --render-once --explode "^^ . + "aprender-ptop --version"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "6e44c6b6793846711bed695354d8fdfcaa760f3c53f22b7e0264523a35aca036"^^ . + "aprender-ptop"^^ . + "aprender-present-terminal"^^ . + "bb6bb8271d3e277ebcb622892199a6959a7fcf9dc55eb4c56c5eb55fbc38fe9a"^^ . + "crates/aprender-present-terminal/src/bin/ptop.rs"^^ . + "aprender-ptop"^^ . + "022221b68f660356ca6923ddeebab5b72e0cc688b072597408885eb318513b9c"^^ . + "aprender-ptop 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-score --ci --threshold (gate mode, exit 1 below threshold)"^^ . + "aprender-score --version"^^ . + "aprender-score [PATH] (TUI-crate quality scoring)"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "472f30797188a3f73cd85bd866fa1546f0ced41cf232c969edd7452b3f915f0a"^^ . + "aprender-score"^^ . + "aprender-present-terminal"^^ . + "c2fff5d89890757f96005998634f4cd4e312fcf9bafa9903ea6a16f5aa5e6731"^^ . + "crates/aprender-present-terminal/src/bin/score.rs"^^ . + "aprender-score"^^ . + "84b51a94f4382c0d0c888ae368530b947130978f2966a8874a73ec6ea63a939a"^^ . + "aprender-score 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-profile -- (trace a launched command)"^^ . + "aprender-profile --anomaly-realtime (sigma anomaly detection)"^^ . + "aprender-profile --chaos (chaos engineering)"^^ . + "aprender-profile --dl-anomaly (autoencoder)"^^ . + "aprender-profile --hpu-analysis"^^ . + "aprender-profile --ml-anomaly (k-means clustering)"^^ . + "aprender-profile --ml-outliers (isolation forest)"^^ . + "aprender-profile --otlp-endpoint (OpenTelemetry export)"^^ . + "aprender-profile --save-model / --load-model"^^ . + "aprender-profile --trace-compute"^^ . + "aprender-profile --transpiler-map (transpiler correlation)"^^ . + "aprender-profile --version"^^ . + "aprender-profile -e/--expr (syscall filtering)"^^ . + "aprender-profile -f/--follow-forks"^^ . + "aprender-profile -p (attach to running process)"^^ . + "aprender-profile -s/--source (DWARF source correlation)"^^ . + "aprender-profile validate -- (golden-baseline trace validation)"^^ . + "validate"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "5e34c71f7393013aacb5cc8c217d31287cac5d719797fe50d4c2a8e4881e45da"^^ . + "aprender-profile"^^ . + "aprender-profile"^^ . + "191e14b470c2011dc801bcb94072b46e35ad4168bc22fc3d4b68b78bd128bfb7"^^ . + "crates/aprender-profile/src/main.rs"^^ . + "aprender-profile"^^ . + "a96a92dd7dde45441a30254f1157268705ee20e56480cd51b9686537a65b351a"^^ . + "aprender-profile 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-ptx-debug --version"^^ . + "aprender-ptx-debug analyze"^^ . + "aprender-ptx-debug gen-fkr"^^ . + "aprender-ptx-debug version"^^ . + "analyze"^^ . + "gen-fkr"^^ . + "version"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "f12815327ac0f82a16b762881cb82c7ab20209614e682e27bf4ea47be2de8de7"^^ . + "aprender-ptx-debug"^^ . + "aprender-ptx-debug"^^ . + "5a26e4d5675c4b0e9d8972fee59d0c110c549fe4b823ce4dd450cdf8c7f21e8a"^^ . + "crates/aprender-ptx-debug/src/bin/main.rs"^^ . + "aprender-ptx-debug"^^ . + "6b733fabbd4a40a85a8481bf02a99c1931aa1cd96a37b71951fd09497081b03c"^^ . + "aprender-ptx-debug 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-qa-readme-sync (README certification-table sync)"^^ . + "aprender-qa-readme-sync --version"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "edfc1fade8953df6a4cbfd49e5e0a1da55f604dd8aeffa0d49763b3c39bc5fa3"^^ . + "aprender-qa-readme-sync"^^ . + "aprender-qa-certify"^^ . + "5a1a71cc88d2eafe930c6578e27e526e06b88f77ce8547197b7677c78df68453"^^ . + "crates/aprender-qa-certify/src/main.rs"^^ . + "aprender-qa-readme-sync"^^ . + "f5272da6ac0c9ce78272891e6fa9fab2d004480c80c75a0ce56a42be65a4d507"^^ . + "aprender-qa-readme-sync 0.70.0 (47d198202)"^^ . + . + . + . + "apr-qa --version"^^ . + "apr-qa bootstrap"^^ . + "apr-qa certify"^^ . + "apr-qa export-csv"^^ . + "apr-qa export-evidence"^^ . + "apr-qa generate"^^ . + "apr-qa kernel-coverage"^^ . + "apr-qa list"^^ . + "apr-qa lock-playbooks"^^ . + "apr-qa parity"^^ . + "apr-qa report"^^ . + "apr-qa run"^^ . + "apr-qa score"^^ . + "apr-qa tickets"^^ . + "apr-qa tools"^^ . + "apr-qa validate-contract"^^ . + "bootstrap"^^ . + "certify"^^ . + "export-csv"^^ . + "export-evidence"^^ . + "generate"^^ . + "kernel-coverage"^^ . + "list"^^ . + "lock-playbooks"^^ . + "parity"^^ . + "report"^^ . + "run"^^ . + "score"^^ . + "tickets"^^ . + "tools"^^ . + "validate-contract"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "a8cb537ca5744570d2eb338170b8da5d48088358f17dbd88aa8a538d3a5f102f"^^ . + "apr-qa"^^ . + "aprender-qa-cli"^^ . + "1b9087ae6055960718263d8491d18e93712130d37a597ea2a4c4140b81b3303c"^^ . + "crates/aprender-qa-cli/src/main.rs"^^ . + "apr-qa"^^ . + "31ef071f9c7b471a1fb09798ad92435623491583a674cdb5de7e1e3c88c41aa2"^^ . + "apr-qa 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-rag --version"^^ . + "aprender-rag demo"^^ . + "aprender-rag eval compare"^^ . + "aprender-rag eval gate"^^ . + "aprender-rag eval generate"^^ . + "aprender-rag eval judge"^^ . + "aprender-rag eval metrics"^^ . + "aprender-rag eval retrieve"^^ . + "aprender-rag eval sample"^^ . + "aprender-rag extract-frames"^^ . + "aprender-rag index"^^ . + "aprender-rag info"^^ . + "aprender-rag query"^^ . + "aprender-rag transcribe"^^ . + "demo"^^ . + "extract-frames"^^ . + "index"^^ . + "info"^^ . + "query"^^ . + "transcribe"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "49786a548b7c25c6a67c16db5bd8b1ab336b410556955ef5f7f6657a6fdbd26d"^^ . + "eval compare"^^ . + "eval gate"^^ . + "eval generate"^^ . + "eval judge"^^ . + "eval metrics"^^ . + "eval retrieve"^^ . + "eval sample"^^ . + "aprender-rag"^^ . + "aprender-rag-cli"^^ . + "d6945efe30bd87ec5865d7bd48a7f232399f9b1a3e03c5f972ff04ac7d99e857"^^ . + "crates/aprender-rag-cli/src/main.rs"^^ . + "aprender-rag"^^ . + "a7a00dd65876e1a486acf580879c8528d9262d20a6211a293b8558b58838967d"^^ . + "aprender-rag 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-simulate --version"^^ . + "aprender-simulate emc-check"^^ . + "aprender-simulate emc-validate"^^ . + "aprender-simulate help"^^ . + "aprender-simulate list-emc"^^ . + "aprender-simulate render"^^ . + "aprender-simulate run"^^ . + "aprender-simulate validate"^^ . + "aprender-simulate verify"^^ . + "aprender-simulate version"^^ . + "unserved: GET / (visualization UI; library feature web; no aprender-simulate command starts the server)"^^ . + "unserved: GET /health (visualization; library feature web; no aprender-simulate command starts the server)"^^ . + "unserved: GET /ws (visualization WebSocket; library feature web; no aprender-simulate command starts the server)"^^ . + "emc-check"^^ . + "emc-validate"^^ . + "help"^^ . + "list-emc"^^ . + "render"^^ . + "run"^^ . + "validate"^^ . + "verify"^^ . + "version"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "adaaa5979da2b555e584a1ca8e918e8c4859c1bab12c89c3924044a1a002f0ee"^^ . + "aprender-simulate"^^ . + "aprender-simulate"^^ . + "1d57fceab026705fbcb61e62721b0cbdc4299277f2e4cfd76340981781ccb241"^^ . + "crates/aprender-simulate/src/main.rs"^^ . + "aprender-simulate"^^ . + "a7ad86bad2c4090750f8d805b282e4002f21b3e62ddf30924c6549b080f64532"^^ . + "aprender-simulate 0.70.0 (47d198202)"^^ . + . + . + . + "GET / (dev server, static)"^^ . + "GET /ws (dev server, WebSocket)"^^ . + "aprender-test-cli --version"^^ . + "aprender-test-cli animation check"^^ . + "aprender-test-cli audio check"^^ . + "aprender-test-cli av-sync check"^^ . + "aprender-test-cli av-sync report"^^ . + "aprender-test-cli build"^^ . + "aprender-test-cli comply"^^ . + "aprender-test-cli comply check"^^ . + "aprender-test-cli comply diff"^^ . + "aprender-test-cli comply enforce"^^ . + "aprender-test-cli comply migrate"^^ . + "aprender-test-cli comply report"^^ . + "aprender-test-cli config"^^ . + "aprender-test-cli coverage"^^ . + "aprender-test-cli init"^^ . + "aprender-test-cli llm bench"^^ . + "aprender-test-cli llm data-audit"^^ . + "aprender-test-cli llm experiment"^^ . + "aprender-test-cli llm experiment compare"^^ . + "aprender-test-cli llm experiment init"^^ . + "aprender-test-cli llm experiment status"^^ . + "aprender-test-cli llm gen-dataset"^^ . + "aprender-test-cli llm load"^^ . + "aprender-test-cli llm report"^^ . + "aprender-test-cli llm score"^^ . + "aprender-test-cli llm sweep"^^ . + "aprender-test-cli llm test"^^ . + "aprender-test-cli playbook"^^ . + "aprender-test-cli record"^^ . + "aprender-test-cli report"^^ . + "aprender-test-cli serve"^^ . + "aprender-test-cli serve score"^^ . + "aprender-test-cli serve tree"^^ . + "aprender-test-cli serve viz"^^ . + "aprender-test-cli stress"^^ . + "aprender-test-cli test"^^ . + "aprender-test-cli video check"^^ . + "aprender-test-cli watch"^^ . + "animation check"^^ . + "audio check"^^ . + "av-sync check"^^ . + "av-sync report"^^ . + "build"^^ . + "comply"^^ . + "comply check"^^ . + "comply diff"^^ . + "comply enforce"^^ . + "comply migrate"^^ . + "comply report"^^ . + "config"^^ . + "coverage"^^ . + "init"^^ . + "llm bench"^^ . + "llm data-audit"^^ . + "llm experiment"^^ . + "llm experiment compare"^^ . + "llm experiment init"^^ . + "llm experiment status"^^ . + "llm gen-dataset"^^ . + "llm load"^^ . + "llm report"^^ . + "llm score"^^ . + "llm sweep"^^ . + "llm test"^^ . + "playbook"^^ . + "record"^^ . + "report"^^ . + "serve"^^ . + "serve score"^^ . + "serve tree"^^ . + "serve viz"^^ . + "stress"^^ . + "test"^^ . + "video check"^^ . + "watch"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "097224de11e0b6ba9e38e363e4d4238f17aa9aca0a9425627faed1abba0b1d36"^^ . + "GET /"^^ . + "GET /ws"^^ . + "aprender-test-cli"^^ . + "aprender-test-cli"^^ . + "879b610adf5b3ad8e501d4393a3baa5bf33e37a8564f588c440596260a9ed809"^^ . + "crates/aprender-test-cli/src/main.rs"^^ . + "aprender-test-cli"^^ . + "419743418d4db8edf98ce3859c7dbf8339e7f6e1518078ebeea25cccf9edbfba"^^ . + "aprender-test-cli 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-train-bench --version"^^ . + "aprender-train-bench ablation"^^ . + "aprender-train-bench alpha"^^ . + "aprender-train-bench compare"^^ . + "aprender-train-bench cost-performance"^^ . + "aprender-train-bench recommend"^^ . + "aprender-train-bench temperature"^^ . + "ablation"^^ . + "alpha"^^ . + "compare"^^ . + "cost-performance"^^ . + "recommend"^^ . + "temperature"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b5aa1cc255f9c515f81b46bee28321b64bbf1e66cbae739517d188b7dc0ddf6a"^^ . + "aprender-train-bench"^^ . + "aprender-train-bench"^^ . + "056d8f340d7d22a182be7ccf869033513c527d91218df7d9d0cdf77a170e1d4e"^^ . + "crates/aprender-train-bench/src/main.rs"^^ . + "aprender-train-bench"^^ . + "1500e4fce255af39f1026cfdb9a0347ad4d81834fd9590fa9d552508b028cff1"^^ . + "aprender-train-bench 0.70.0 (47d198202)"^^ . + . + . + . + "aprender-train-distill --version"^^ . + "aprender-train-distill estimate"^^ . + "aprender-train-distill export"^^ . + "aprender-train-distill run"^^ . + "aprender-train-distill validate"^^ . + "estimate"^^ . + "export"^^ . + "run"^^ . + "validate"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "9c48b2c611ef6282ff8ede6d950668f754fe1ed71663a5fa2c50aff8d35a6619"^^ . + "aprender-train-distill"^^ . + "aprender-train-distill"^^ . + "a28c2633f1eb0cdc59740d05428718848e7ca0821c7489256c97e003948e7aba"^^ . + "crates/aprender-train-distill/src/main.rs"^^ . + "aprender-train-distill"^^ . + "47d198202"^^ . + "dea595cb552113cc22123332952be20c5066293226a4267f8a2ba366aadac34f"^^ . + "aprender-train-distill 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-train-inspect --version"^^ . + "aprender-train-inspect compare"^^ . + "aprender-train-inspect convert"^^ . + "aprender-train-inspect info"^^ . + "aprender-train-inspect layers"^^ . + "aprender-train-inspect memory"^^ . + "aprender-train-inspect validate"^^ . + "compare"^^ . + "convert"^^ . + "info"^^ . + "layers"^^ . + "memory"^^ . + "validate"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "0b114be87b733dc322c8d7951aa8ca796155e44fd63345ca3cd274c01a326807"^^ . + "aprender-train-inspect"^^ . + "aprender-train-inspect"^^ . + "ff02409c717c8fc17cb31bbf1abad35dd5572d63e613801d80b30bd09493f0ae"^^ . + "crates/aprender-train-inspect/src/main.rs"^^ . + "aprender-train-inspect"^^ . + "47d198202"^^ . + "b542a4039dc8027694cad11f5e67626df29538f338000f1e159ffd3d07f47e06"^^ . + "aprender-train-inspect 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-train-lora --version"^^ . + "aprender-train-lora compare"^^ . + "aprender-train-lora inspect"^^ . + "aprender-train-lora merge"^^ . + "aprender-train-lora plan"^^ . + "compare"^^ . + "inspect"^^ . + "merge"^^ . + "plan"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "2db88803c3d4ccf6ba5b28ce8ad22c39496872c8a026bc448a219f4838a5b749"^^ . + "aprender-train-lora"^^ . + "aprender-train-lora"^^ . + "8df40581a5002b99a5b32af40157d1b70d6782a8e9120dbe4a2288138f3a4cd0"^^ . + "crates/aprender-train-lora/src/main.rs"^^ . + "aprender-train-lora"^^ . + "47d198202"^^ . + "6140671332e554af21451c9cdd1bbedf64ccfcbc7210469d8bff70c2e05ec423"^^ . + "aprender-train-lora 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-train-shell (interactive REPL)"^^ . + "aprender-train-shell --version"^^ . + "aprender-train-shell -c (one-shot REPL command)"^^ . + "aprender-train-shell -s (resume saved session)"^^ . + "repl: clear"^^ . + "repl: distill"^^ . + "repl: export "^^ . + "repl: fetch "^^ . + "repl: help"^^ . + "repl: history"^^ . + "repl: inspect"^^ . + "repl: memory"^^ . + "repl: quit"^^ . + "repl: set "^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "33befe892dd03f346595dd245b554ee67a1b203e3086835c935320a5d57ebb61"^^ . + "aprender-train-shell"^^ . + "aprender-train-shell"^^ . + "598a25ea9e13bba763e78a0e3c8c83007411597f382fa356e97c2c71695550cc"^^ . + "crates/aprender-train-shell/src/main.rs"^^ . + "aprender-train-shell"^^ . + "47d198202"^^ . + "eb96c7b80514044e623eea57073198ad0d4dc9c96d7f5d5626e1c822a467c00e"^^ . + "aprender-train-shell 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-verify --version"^^ . + "aprender-verify codex"^^ . + "aprender-verify corpus"^^ . + "aprender-verify depyler"^^ . + "aprender-verify distill"^^ . + "aprender-verify evaluate"^^ . + "aprender-verify export"^^ . + "aprender-verify generate"^^ . + "aprender-verify mutate"^^ . + "aprender-verify stats"^^ . + "aprender-verify train"^^ . + "aprender-verify verify"^^ . + "codex"^^ . + "corpus"^^ . + "depyler"^^ . + "distill"^^ . + "evaluate"^^ . + "export"^^ . + "generate"^^ . + "mutate"^^ . + "stats"^^ . + "train"^^ . + "verify"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "9c14999cd467b14fd5eb53f32c76d1b651af4a6de348c68458c55cf16f033a5c"^^ . + "aprender-verify"^^ . + "aprender-verify-ml"^^ . + "48912f807c8e1ece39c3d3bc60d86a0939d5baaabf18a3018836b269cba6a313"^^ . + "crates/aprender-verify-ml/src/bin/verificar.rs"^^ . + "aprender-verify"^^ . + "47d198202"^^ . + "bfe884f17d58d3eb9b352f93ad94c911c9f37f4e8f625eb11910efeefa1f020f"^^ . + "aprender-verify 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-zram --version"^^ . + "aprender-zram benchmark"^^ . + "aprender-zram create"^^ . + "aprender-zram remove"^^ . + "aprender-zram status"^^ . + "benchmark"^^ . + "create"^^ . + "remove"^^ . + "status"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "d0e946e5ddeda21f85994e2e8ad8f6eac5810b54ddf31bf1e1625462d709a754"^^ . + "aprender-zram"^^ . + "aprender-zram-cli"^^ . + "ee0724de3942bb8c552da7e4f3c541c5e9e6db766d75edf636651f8eadba2f91"^^ . + "crates/aprender-zram-cli/src/main.rs"^^ . + "aprender-zram"^^ . + "47d198202"^^ . + "9a028d8fc31d6d781d864695adff182ed2dd60fb1447d286f7823ec8478b3f0b"^^ . + "aprender-zram 0.70.0 (30c82831a)"^^ . + . + . + . + "aprender-zram-generator --version"^^ . + "aprender-zram-generator (systemd.generator unit emission)"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "fe009e6965a2894e5f381f06dd2440ebc9249c71c718cf52259e1d338eadf591"^^ . + "aprender-zram-generator"^^ . + "aprender-zram-generator"^^ . + "7f854ce96bc21cb53dd6ee8dcce73c8dd2f20521a7e2dae9a493c217a76644ee"^^ . + "crates/aprender-zram-generator/src/main.rs"^^ . + "aprender-zram-generator"^^ . + "47d198202"^^ . + "252be0957d56d20f81a305700a7b698cff8e4a48873560e71b167c569fbbcda9"^^ . + "aprender-zram-generator 0.70.0 (30c82831a)"^^ . + . + . + . + "DELETE /api/delete"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/version"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /metrics"^^ . + "GET /metrics/dispatch"^^ . + "GET /models"^^ . + "GET /ready"^^ . + "GET /realize/model"^^ . + "GET /tensors"^^ . + "GET /v1/audit/:request_id"^^ . + "GET /v1/effective-config"^^ . + "GET /v1/gpu/status"^^ . + "GET /v1/metrics"^^ . + "GET /v1/models"^^ . + "HTTP 404 fallback route index"^^ . + "POST /api/chat"^^ . + "POST /api/chat (stream:true, NDJSON)"^^ . + "POST /api/embeddings"^^ . + "POST /api/generate"^^ . + "POST /api/generate (stream:true, NDJSON)"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /batch/generate"^^ . + "POST /batch/tokenize"^^ . + "POST /generate"^^ . + "POST /metrics/dispatch/reset"^^ . + "POST /realize/batch"^^ . + "POST /realize/embed"^^ . + "POST /realize/generate"^^ . + "POST /realize/reload"^^ . + "POST /stream/generate"^^ . + "POST /tokenize"^^ . + "POST /v1/batch/completions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/chat/completions (stream:true, SSE)"^^ . + "POST /v1/chat/completions/stream"^^ . + "POST /v1/completions"^^ . + "POST /v1/completions (stream:true, SSE)"^^ . + "POST /v1/embeddings"^^ . + "POST /v1/explain"^^ . + "POST /v1/gpu/warmup"^^ . + "POST /v1/logprobs"^^ . + "POST /v1/perplexity"^^ . + "POST /v1/predict"^^ . + "apr attn-parity-lint"^^ . + "apr attn-viz-lint"^^ . + "apr audio-inspect-lint"^^ . + "apr awq-lint"^^ . + "apr beat-run"^^ . + "apr bench"^^ . + "apr canary check"^^ . + "apr canary create"^^ . + "apr capability"^^ . + "apr cbtop"^^ . + "apr cgp baseline"^^ . + "apr cgp bench"^^ . + "apr cgp compete"^^ . + "apr cgp contract generate"^^ . + "apr cgp contract verify"^^ . + "apr cgp diff"^^ . + "apr cgp doctor"^^ . + "apr cgp explain"^^ . + "apr cgp profile binary"^^ . + "apr cgp profile compare"^^ . + "apr cgp profile cublas"^^ . + "apr cgp profile kernel"^^ . + "apr cgp profile library"^^ . + "apr cgp profile metal"^^ . + "apr cgp profile parallel"^^ . + "apr cgp profile python"^^ . + "apr cgp profile quant"^^ . + "apr cgp profile scalar"^^ . + "apr cgp profile scaling"^^ . + "apr cgp profile simd"^^ . + "apr cgp profile wasm"^^ . + "apr cgp profile wgpu"^^ . + "apr cgp roofline"^^ . + "apr cgp trace"^^ . + "apr cgp tui"^^ . + "apr chat"^^ . + "apr chat --backend cpu"^^ . + "apr chat --backend cuda"^^ . + "apr chat --backend wgpu"^^ . + "apr check"^^ . + "apr check-finite-lint"^^ . + "apr code"^^ . + "apr code --input-format json"^^ . + "apr code --output-format json"^^ . + "apr compare-hf"^^ . + "apr compile"^^ . + "apr convert"^^ . + "apr data audit"^^ . + "apr data balance"^^ . + "apr data decontaminate"^^ . + "apr data dedup"^^ . + "apr data split"^^ . + "apr data x convert"^^ . + "apr data x dedup"^^ . + "apr data x doctest extract"^^ . + "apr data x doctest merge"^^ . + "apr data x drift compare"^^ . + "apr data x drift detect"^^ . + "apr data x drift merge"^^ . + "apr data x drift report"^^ . + "apr data x drift sketch"^^ . + "apr data x fed manifest"^^ . + "apr data x fed plan"^^ . + "apr data x fed split"^^ . + "apr data x fed verify"^^ . + "apr data x filter-text"^^ . + "apr data x fim"^^ . + "apr data x head"^^ . + "apr data x hub push"^^ . + "apr data x import hf"^^ . + "apr data x import local"^^ . + "apr data x info"^^ . + "apr data x mix"^^ . + "apr data x quality check"^^ . + "apr data x quality profiles"^^ . + "apr data x quality report"^^ . + "apr data x quality score"^^ . + "apr data x registry delete"^^ . + "apr data x registry init"^^ . + "apr data x registry list"^^ . + "apr data x registry pull"^^ . + "apr data x registry push"^^ . + "apr data x registry search"^^ . + "apr data x registry show-info"^^ . + "apr data x repl"^^ . + "apr data x schema"^^ . + "apr data x view"^^ . + "apr dataset audio-inspect"^^ . + "apr ddp-metrics-lint"^^ . + "apr debug"^^ . + "apr debug embed-viz"^^ . + "apr decrypt"^^ . + "apr devices"^^ . + "apr diagnose"^^ . + "apr diff"^^ . + "apr distill"^^ . + "apr dry-sampling-lint"^^ . + "apr embed"^^ . + "apr embed-viz-lint"^^ . + "apr embeddings-lint"^^ . + "apr encrypt"^^ . + "apr eval"^^ . + "apr eval --task classify"^^ . + "apr eval --task code"^^ . + "apr eval --task compare"^^ . + "apr eval --task contamination"^^ . + "apr eval --task correlation"^^ . + "apr eval --task human"^^ . + "apr eval --task humaneval"^^ . + "apr eval --task mbpp"^^ . + "apr eval --task plan"^^ . + "apr eval --task verify"^^ . + "apr experiment view"^^ . + "apr explain"^^ . + "apr explain-token-lint"^^ . + "apr export"^^ . + "apr export --format coreml"^^ . + "apr export --format gguf"^^ . + "apr export --format mlx"^^ . + "apr export --format onnx"^^ . + "apr export --format openvino"^^ . + "apr export --format safetensors"^^ . + "apr finetune"^^ . + "apr finetune --task classify"^^ . + "apr flow"^^ . + "apr fp8-lint"^^ . + "apr gbnf-lint"^^ . + "apr gptq-lint"^^ . + "apr gpu"^^ . + "apr gpu-memtrace-lint"^^ . + "apr grad-norm"^^ . + "apr hang-trace-lint"^^ . + "apr hex"^^ . + "apr imatrix-lint"^^ . + "apr import"^^ . + "apr inspect"^^ . + "apr kernel parity"^^ . + "apr kv-timeline-lint"^^ . + "apr lint"^^ . + "apr list"^^ . + "apr manifest"^^ . + "apr mcp"^^ . + "apr merge"^^ . + "apr modelfile parse"^^ . + "apr monitor"^^ . + "apr mono archive"^^ . + "apr mono audit"^^ . + "apr mono publish"^^ . + "apr mono shims"^^ . + "apr nccl-diag-lint"^^ . + "apr nf4-lint"^^ . + "apr ollama-chat-lint"^^ . + "apr ollama-tools-lint"^^ . + "apr oom-lint"^^ . + "apr oracle"^^ . + "apr otlp-lint"^^ . + "apr parity"^^ . + "apr parity --out"^^ . + "apr parity --per-op"^^ . + "apr parity --threshold"^^ . + "apr pipeline apply"^^ . + "apr pipeline plan"^^ . + "apr pipeline status"^^ . + "apr pipeline validate"^^ . + "apr ppl"^^ . + "apr pretrain"^^ . + "apr profile"^^ . + "apr prometheus-lint"^^ . + "apr prune"^^ . + "apr ptx"^^ . + "apr ptx-map"^^ . + "apr publish"^^ . + "apr pull"^^ . + "apr pv audit"^^ . + "apr pv book"^^ . + "apr pv census"^^ . + "apr pv certify"^^ . + "apr pv challenge check"^^ . + "apr pv challenge gen"^^ . + "apr pv check-parity"^^ . + "apr pv codegen"^^ . + "apr pv coq"^^ . + "apr pv coverage"^^ . + "apr pv diff"^^ . + "apr pv discharge check"^^ . + "apr pv discharge gen-axioms"^^ . + "apr pv discharge label-ratchet"^^ . + "apr pv discharge run"^^ . + "apr pv equations"^^ . + "apr pv explain"^^ . + "apr pv extract"^^ . + "apr pv extract-pytorch"^^ . + "apr pv flux"^^ . + "apr pv fuzz"^^ . + "apr pv generate"^^ . + "apr pv graph"^^ . + "apr pv infer"^^ . + "apr pv invariants"^^ . + "apr pv kaizen"^^ . + "apr pv kani"^^ . + "apr pv lean"^^ . + "apr pv lean-status"^^ . + "apr pv lint"^^ . + "apr pv migrate"^^ . + "apr pv mirai"^^ . + "apr pv obligations"^^ . + "apr pv ontology export"^^ . + "apr pv ontology tbox"^^ . + "apr pv pipeline"^^ . + "apr pv probar"^^ . + "apr pv proof-status"^^ . + "apr pv query"^^ . + "apr pv roofline"^^ . + "apr pv scaffold"^^ . + "apr pv score"^^ . + "apr pv status"^^ . + "apr pv tla"^^ . + "apr pv unlock"^^ . + "apr pv validate"^^ . + "apr pv verify-bindings"^^ . + "apr pv verify-pipeline"^^ . + "apr pv verify-structure"^^ . + "apr qa"^^ . + "apr qualify"^^ . + "apr quant-preservation-lint"^^ . + "apr quantize"^^ . + "apr quantize --scheme fp16"^^ . + "apr quantize --scheme int4"^^ . + "apr quantize --scheme int8"^^ . + "apr quantize --scheme q4k"^^ . + "apr rag demo"^^ . + "apr rag eval compare"^^ . + "apr rag eval gate"^^ . + "apr rag eval generate"^^ . + "apr rag eval judge"^^ . + "apr rag eval metrics"^^ . + "apr rag eval retrieve"^^ . + "apr rag eval sample"^^ . + "apr rag extract-frames"^^ . + "apr rag index"^^ . + "apr rag info"^^ . + "apr rag query"^^ . + "apr rag transcribe"^^ . + "apr react-trace-lint"^^ . + "apr registry aliases"^^ . + "apr registry-quota-lint"^^ . + "apr rerank"^^ . + "apr rm"^^ . + "apr rm-gc-lint"^^ . + "apr rosetta chain"^^ . + "apr rosetta compare-inference"^^ . + "apr rosetta convert"^^ . + "apr rosetta diff-tensors"^^ . + "apr rosetta fingerprint"^^ . + "apr rosetta inspect"^^ . + "apr rosetta validate-stats"^^ . + "apr rosetta verify"^^ . + "apr run"^^ . + "apr run --backend cpu"^^ . + "apr run --backend cuda"^^ . + "apr run --backend wgpu"^^ . + "apr run --gpu"^^ . + "apr run --no-gpu"^^ . + "apr run --stream"^^ . + "apr run --task transcribe"^^ . + "apr run --task translate"^^ . + "apr run --trace-level basic"^^ . + "apr run --trace-level chrome"^^ . + "apr run --trace-level layer"^^ . + "apr run --trace-level payload"^^ . + "apr runs diff"^^ . + "apr runs ls"^^ . + "apr runs show"^^ . + "apr serve plan"^^ . + "apr serve run"^^ . + "apr serve run --backend cpu"^^ . + "apr serve run --backend cuda"^^ . + "apr serve run --backend wgpu"^^ . + "apr serve run --no-cors"^^ . + "apr serve run --no-metrics"^^ . + "apr shard"^^ . + "apr shared-cache-lint"^^ . + "apr showcase"^^ . + "apr sim emc-check"^^ . + "apr sim emc-validate"^^ . + "apr sim help"^^ . + "apr sim list-emc"^^ . + "apr sim render"^^ . + "apr sim run"^^ . + "apr sim validate"^^ . + "apr sim verify"^^ . + "apr sim version"^^ . + "apr stamp"^^ . + "apr tensors"^^ . + "apr test llm bench"^^ . + "apr test llm bench --band"^^ . + "apr test tensor"^^ . + "apr tokenize apply"^^ . + "apr tokenize encode"^^ . + "apr tokenize encode-corpus"^^ . + "apr tokenize import-hf"^^ . + "apr tokenize plan"^^ . + "apr tokenize repair-manifest"^^ . + "apr tokenize train"^^ . + "apr tool-use-lint"^^ . + "apr trace"^^ . + "apr train apply"^^ . + "apr train apply --task classify"^^ . + "apr train apply --task pretrain"^^ . + "apr train archive"^^ . + "apr train cluster-status"^^ . + "apr train halving"^^ . + "apr train plan"^^ . + "apr train plan --task classify"^^ . + "apr train plan --task pretrain"^^ . + "apr train submit"^^ . + "apr train sweep"^^ . + "apr train watch"^^ . + "apr tree"^^ . + "apr tui"^^ . + "apr tune"^^ . + "apr typical-p-lint"^^ . + "apr unified-search-lint"^^ . + "apr unshard"^^ . + "apr validate"^^ . + "apr validate-manifest"^^ . + "apr zram benchmark"^^ . + "apr zram create"^^ . + "apr zram remove"^^ . + "apr zram status"^^ . + "mcp:apr.bench"^^ . + "mcp:apr.finetune"^^ . + "mcp:apr.qa"^^ . + "mcp:apr.run"^^ . + "mcp:apr.serve"^^ . + "mcp:apr.tensors"^^ . + "mcp:apr.trace"^^ . + "mcp:apr.validate"^^ . + "mcp:apr.version"^^ . + "attn-parity-lint"^^ . + "attn-viz-lint"^^ . + "audio-inspect-lint"^^ . + "awq-lint"^^ . + "beat-run"^^ . + "bench"^^ . + "canary check"^^ . + "canary create"^^ . + "capability"^^ . + "cbtop"^^ . + "cgp baseline"^^ . + "cgp bench"^^ . + "cgp compete"^^ . + "cgp contract generate"^^ . + "cgp contract verify"^^ . + "cgp diff"^^ . + "cgp doctor"^^ . + "cgp explain"^^ . + "cgp profile binary"^^ . + "cgp profile compare"^^ . + "cgp profile cublas"^^ . + "cgp profile kernel"^^ . + "cgp profile library"^^ . + "cgp profile metal"^^ . + "cgp profile parallel"^^ . + "cgp profile python"^^ . + "cgp profile quant"^^ . + "cgp profile scalar"^^ . + "cgp profile scaling"^^ . + "cgp profile simd"^^ . + "cgp profile wasm"^^ . + "cgp profile wgpu"^^ . + "cgp roofline"^^ . + "cgp trace"^^ . + "cgp tui"^^ . + "chat"^^ . + "check"^^ . + "check-finite-lint"^^ . + "code"^^ . + "compare-hf"^^ . + "compile"^^ . + "convert"^^ . + "data audit"^^ . + "data balance"^^ . + "data decontaminate"^^ . + "data dedup"^^ . + "data split"^^ . + "data x convert"^^ . + "data x dedup"^^ . + "data x drift compare"^^ . + "data x drift detect"^^ . + "data x drift merge"^^ . + "data x drift report"^^ . + "data x drift sketch"^^ . + "data x fed manifest"^^ . + "data x fed plan"^^ . + "data x fed split"^^ . + "data x fed verify"^^ . + "data x filter-text"^^ . + "data x fim"^^ . + "data x head"^^ . + "data x import local"^^ . + "data x info"^^ . + "data x mix"^^ . + "data x quality check"^^ . + "data x quality profiles"^^ . + "data x quality report"^^ . + "data x quality score"^^ . + "data x registry delete"^^ . + "data x registry init"^^ . + "data x registry list"^^ . + "data x registry pull"^^ . + "data x registry push"^^ . + "data x registry search"^^ . + "data x registry show-info"^^ . + "data x repl"^^ . + "data x schema"^^ . + "data x view"^^ . + "dataset audio-inspect"^^ . + "ddp-metrics-lint"^^ . + "debug"^^ . + "debug embed-viz"^^ . + "decrypt"^^ . + "devices"^^ . + "diagnose"^^ . + "diff"^^ . + "distill"^^ . + "dry-sampling-lint"^^ . + "embed"^^ . + "embed-viz-lint"^^ . + "embeddings-lint"^^ . + "encrypt"^^ . + "eval"^^ . + "experiment view"^^ . + "explain"^^ . + "explain-token-lint"^^ . + "export"^^ . + "finetune"^^ . + "flow"^^ . + "fp8-lint"^^ . + "gbnf-lint"^^ . + "gptq-lint"^^ . + "gpu"^^ . + "gpu-memtrace-lint"^^ . + "grad-norm"^^ . + "hang-trace-lint"^^ . + "hex"^^ . + "imatrix-lint"^^ . + "import"^^ . + "inspect"^^ . + "kernel parity"^^ . + "kv-timeline-lint"^^ . + "lint"^^ . + "list"^^ . + "manifest"^^ . + "mcp"^^ . + "merge"^^ . + "modelfile parse"^^ . + "monitor"^^ . + "nccl-diag-lint"^^ . + "nf4-lint"^^ . + "ollama-chat-lint"^^ . + "ollama-tools-lint"^^ . + "oom-lint"^^ . + "oracle"^^ . + "otlp-lint"^^ . + "parity"^^ . + "pipeline apply"^^ . + "pipeline plan"^^ . + "pipeline status"^^ . + "pipeline validate"^^ . + "ppl"^^ . + "pretrain"^^ . + "profile"^^ . + "prometheus-lint"^^ . + "prune"^^ . + "ptx"^^ . + "ptx-debug analyze"^^ . + "ptx-debug gen-fkr"^^ . + "ptx-debug version"^^ . + "ptx-map"^^ . + "publish"^^ . + "pull"^^ . + "pv audit"^^ . + "pv book"^^ . + "pv census"^^ . + "pv certify"^^ . + "pv challenge check"^^ . + "pv challenge gen"^^ . + "pv check-parity"^^ . + "pv codegen"^^ . + "pv coq"^^ . + "pv coverage"^^ . + "pv diff"^^ . + "pv discharge check"^^ . + "pv discharge gen-axioms"^^ . + "pv discharge label-ratchet"^^ . + "pv discharge run"^^ . + "pv equations"^^ . + "pv explain"^^ . + "pv extract"^^ . + "pv extract-pytorch"^^ . + "pv flux"^^ . + "pv fuzz"^^ . + "pv generate"^^ . + "pv graph"^^ . + "pv infer"^^ . + "pv invariants"^^ . + "pv kaizen"^^ . + "pv kani"^^ . + "pv lean"^^ . + "pv lean-status"^^ . + "pv lint"^^ . + "pv migrate"^^ . + "pv mirai"^^ . + "pv obligations"^^ . + "pv ontology export"^^ . + "pv ontology tbox"^^ . + "pv pipeline"^^ . + "pv probar"^^ . + "pv proof-status"^^ . + "pv query"^^ . + "pv roofline"^^ . + "pv scaffold"^^ . + "pv score"^^ . + "pv status"^^ . + "pv tla"^^ . + "pv unlock"^^ . + "pv validate"^^ . + "pv verify-bindings"^^ . + "pv verify-pipeline"^^ . + "pv verify-structure"^^ . + "qa"^^ . + "qualify"^^ . + "quant-preservation-lint"^^ . + "quantize"^^ . + "rag demo"^^ . + "rag extract-frames"^^ . + "rag index"^^ . + "rag info"^^ . + "rag query"^^ . + "rag transcribe"^^ . + "react-trace-lint"^^ . + "registry aliases"^^ . + "registry-quota-lint"^^ . + "rerank"^^ . + "rm"^^ . + "rm-gc-lint"^^ . + "rosetta chain"^^ . + "rosetta compare-inference"^^ . + "rosetta convert"^^ . + "rosetta diff-tensors"^^ . + "rosetta fingerprint"^^ . + "rosetta inspect"^^ . + "rosetta validate-stats"^^ . + "rosetta verify"^^ . + "run"^^ . + "runs diff"^^ . + "runs ls"^^ . + "runs show"^^ . + "serve plan"^^ . + "serve run"^^ . + "shard"^^ . + "shared-cache-lint"^^ . + "showcase"^^ . + "sim emc-check"^^ . + "sim emc-validate"^^ . + "sim help"^^ . + "sim list-emc"^^ . + "sim render"^^ . + "sim run"^^ . + "sim validate"^^ . + "sim verify"^^ . + "sim version"^^ . + "stamp"^^ . + "tensors"^^ . + "test llm bench"^^ . + "test tensor"^^ . + "tokenize apply"^^ . + "tokenize encode"^^ . + "tokenize encode-corpus"^^ . + "tokenize import-hf"^^ . + "tokenize plan"^^ . + "tokenize repair-manifest"^^ . + "tokenize train"^^ . + "tool-use-lint"^^ . + "trace"^^ . + "train apply"^^ . + "train archive"^^ . + "train cluster-status"^^ . + "train halving"^^ . + "train plan"^^ . + "train submit"^^ . + "train sweep"^^ . + "train watch"^^ . + "tree"^^ . + "tui"^^ . + "tune"^^ . + "typical-p-lint"^^ . + "unified-search-lint"^^ . + "unshard"^^ . + "validate"^^ . + "validate-manifest"^^ . + "zram benchmark"^^ . + "zram create"^^ . + "zram remove"^^ . + "zram status"^^ . + "47d198202ff793c91cb790beba358f531505cb51"^^ . + "b7a93598835cd6d0fe750fa42a685a497045bf126c7a0e47533fed7ca5e28b9d"^^ . + "data x doctest extract"^^ . + "data x doctest merge"^^ . + "data x hub push"^^ . + "data x import hf"^^ . + "mono archive"^^ . + "mono audit"^^ . + "mono publish"^^ . + "mono shims"^^ . + "rag eval compare"^^ . + "rag eval gate"^^ . + "rag eval generate"^^ . + "rag eval judge"^^ . + "rag eval metrics"^^ . + "rag eval retrieve"^^ . + "rag eval sample"^^ . + "POST /v1/logprobs"^^ . + "POST /v1/perplexity"^^ . + "apr.bench"^^ . + "apr.finetune"^^ . + "apr.qa"^^ . + "apr.run"^^ . + "apr.serve"^^ . + "apr.tensors"^^ . + "apr.trace"^^ . + "apr.validate"^^ . + "apr.version"^^ . + "apr"^^ . + "aprender"^^ . + "DELETE /api/delete"^^ . + "GET /"^^ . + "GET /api/tags"^^ . + "GET /api/version"^^ . + "GET /health"^^ . + "GET /health/live"^^ . + "GET /health/ready"^^ . + "GET /metrics"^^ . + "GET /metrics/dispatch"^^ . + "GET /models"^^ . + "GET /ready"^^ . + "GET /realize/model"^^ . + "GET /tensors"^^ . + "GET /v1/audit/:request_id"^^ . + "GET /v1/effective-config"^^ . + "GET /v1/gpu/status"^^ . + "GET /v1/metrics"^^ . + "GET /v1/models"^^ . + "POST /api/chat"^^ . + "POST /api/embeddings"^^ . + "POST /api/generate"^^ . + "POST /api/pull"^^ . + "POST /api/show"^^ . + "POST /batch/generate"^^ . + "POST /batch/tokenize"^^ . + "POST /generate"^^ . + "POST /metrics/dispatch/reset"^^ . + "POST /realize/batch"^^ . + "POST /realize/embed"^^ . + "POST /realize/generate"^^ . + "POST /realize/reload"^^ . + "POST /stream/generate"^^ . + "POST /tokenize"^^ . + "POST /v1/batch/completions"^^ . + "POST /v1/chat/completions"^^ . + "POST /v1/chat/completions/stream"^^ . + "POST /v1/completions"^^ . + "POST /v1/embeddings"^^ . + "POST /v1/explain"^^ . + "POST /v1/gpu/warmup"^^ . + "POST /v1/predict"^^ . + "6193c1184ad0e0b43128dbd4a5c89ffa01b576a47241874f9495e3493d5109c8"^^ . + "src/bin/apr.rs"^^ . + "apr"^^ . + "ptx-debug analyze"^^ . + "ptx-debug gen-fkr"^^ . + "ptx-debug version"^^ . + "9c7d74d55399bdaf139a088a4a021006fdde1a2721d86c333cc890ada9804801"^^ . + "apr 0.70.0 (47d198202)"^^ . . . "contracts/work/APR-ANTIGRAVITY-INTEGRATION-001.yaml"^^ . @@ -339,6 +3090,16 @@ "PMAT-342"^^ . "schema"^^ . "PMAT-342"^^ . + . + . + "contracts/work/PMAT-4080.yaml"^^ . + "PMAT-4080"^^ . + "pattern"^^ . + . + . + "contracts/work/PMAT-4081.yaml"^^ . + "PMAT-4081"^^ . + "pattern"^^ . . . "contracts/work/PMAT-480.yaml"^^ . @@ -1840,42 +4601,52 @@ "model-family"^^ . . . + . "contracts/absolute-position-v1.yaml"^^ . "absolute-position-v1"^^ . . . + . "contracts/accelerator-request-v1.yaml"^^ . "accelerator-request-v1"^^ . . . + . "contracts/activation-kernel-v1.yaml"^^ . "activation-kernel-v1"^^ . . . + . "contracts/active-learning-v1.yaml"^^ . "active-learning-v1"^^ . . . + . "contracts/adamw-kernel-v1.yaml"^^ . "adamw-kernel-v1"^^ . . . + . "contracts/batuta/agent-loop-v1.yaml"^^ . "agent-loop-v1"^^ . . . + . "contracts/duende/agent-orchestration-v1.yaml"^^ . "agent-orchestration-v1"^^ . . . + . "contracts/batuta/agent-ux-v1.yaml"^^ . "agent-ux-v1"^^ . . . + . "contracts/alibi-kernel-v1.yaml"^^ . "alibi-kernel-v1"^^ . . . + . "contracts/alibi-slopes-v1.yaml"^^ . "alibi-slopes-v1"^^ . "kernel"^^ . @@ -1889,6 +4660,7 @@ "1.0.0"^^ . . . + . "contracts/apr-architecture-schema-v1.yaml"^^ . "contracts/aprender/apr-architecture-schema-v1.yaml"^^ . "apr-architecture-schema-v1"^^ . @@ -1902,90 +4674,112 @@ "1.0.0"^^ . . . + . "contracts/apr-bench-receipt-v1.yaml"^^ . "apr-bench-receipt-v1"^^ . . . + . "contracts/apr-book-build-v1.yaml"^^ . "apr-book-build-v1"^^ . . . + . "contracts/apr-book-ch01-v1.yaml"^^ . "apr-book-ch01-v1"^^ . . . + . "contracts/apr-book-ch02-v1.yaml"^^ . "apr-book-ch02-v1"^^ . . . + . "contracts/apr-book-ch03-v1.yaml"^^ . "apr-book-ch03-v1"^^ . . . + . "contracts/apr-book-ch04-v1.yaml"^^ . "apr-book-ch04-v1"^^ . . . + . "contracts/apr-book-ch05-v1.yaml"^^ . "apr-book-ch05-v1"^^ . . . + . "contracts/apr-book-ch06-v1.yaml"^^ . "apr-book-ch06-v1"^^ . . . + . "contracts/apr-book-ch07-v1.yaml"^^ . "apr-book-ch07-v1"^^ . . . + . "contracts/apr-book-ch08-v1.yaml"^^ . "apr-book-ch08-v1"^^ . . . + . "contracts/apr-book-ch09-v1.yaml"^^ . "apr-book-ch09-v1"^^ . . . + . "contracts/apr-book-ch10-v1.yaml"^^ . "apr-book-ch10-v1"^^ . . . + . "contracts/apr-book-ch11-v1.yaml"^^ . "apr-book-ch11-v1"^^ . . . + . "contracts/apr-book-ch12-v1.yaml"^^ . "apr-book-ch12-v1"^^ . . . + . "contracts/apr-book-ch13-v1.yaml"^^ . "apr-book-ch13-v1"^^ . . . + . "contracts/apr-book-ch14-v1.yaml"^^ . "apr-book-ch14-v1"^^ . . . + . "contracts/apr-book-ch15-v1.yaml"^^ . "apr-book-ch15-v1"^^ . . . + . "contracts/apr-book-ch16-v1.yaml"^^ . "apr-book-ch16-v1"^^ . . . + . "contracts/apr-book-ch17-v1.yaml"^^ . "apr-book-ch17-v1"^^ . . . + . "contracts/apr-book-ch18-v1.yaml"^^ . "apr-book-ch18-v1"^^ . . . + . "contracts/apr-book-ch19-v1.yaml"^^ . "apr-book-ch19-v1"^^ . . . + . "contracts/apr-book-ch20-v1.yaml"^^ . "apr-book-ch20-v1"^^ . . @@ -2051,15 +4845,18 @@ "1"^^ . . . + . "contracts/apr-chat-session-v1.yaml"^^ . "contracts/aprender/apr-chat-session-v1.yaml"^^ . "apr-chat-session-v1"^^ . . . + . "contracts/entrenar/apr-checkpoint-v1.yaml"^^ . "apr-checkpoint-v1"^^ . . . + . "contracts/apr-chrome-trace-v1.yaml"^^ . "apr-chrome-trace-v1"^^ . . @@ -2072,20 +4869,24 @@ "1.0.0"^^ . . . + . "contracts/apr-cli-command-safety-v1.yaml"^^ . "apr-cli-command-safety-v1"^^ . . . + . "contracts/apr-cli-commands-v1.yaml"^^ . "apr-cli-commands-v1"^^ . "apr-cli-commands"^^ . "1.1.0"^^ . . . + . "contracts/apr-cli-coverage-v1.yaml"^^ . "apr-cli-coverage-v1"^^ . . . + . "contracts/apr-cli-dep-migration-v1.yaml"^^ . "apr-cli-dep-migration-v1"^^ . . @@ -2095,6 +4896,7 @@ "schema"^^ . . . + . "contracts/aprender/apr-cli-longrunning-v1.yaml"^^ . "apr-cli-longrunning-v1"^^ . . @@ -2104,19 +4906,23 @@ "schema"^^ . . . + . "contracts/aprender/apr-cli-mutating-v1.yaml"^^ . "apr-cli-mutating-v1"^^ . . . + . "contracts/apr-cli-operations-v1.yaml"^^ . "contracts/aprender/apr-cli-operations-v1.yaml"^^ . "apr-cli-operations-v1"^^ . . . + . "contracts/apr-cli-publish-extra-v1.yaml"^^ . "apr-cli-publish-extra-v1"^^ . . . + . "contracts/apr-cli-publish-v1.yaml"^^ . "apr-cli-publish-v1"^^ . . @@ -2126,18 +4932,22 @@ "schema"^^ . . . + . "contracts/apr-cli-qa-v1.yaml"^^ . "apr-cli-qa-v1"^^ . . . + . "contracts/aprender/apr-cli-readonly-v1.yaml"^^ . "apr-cli-readonly-v1"^^ . . . + . "contracts/apr-cli-safety-v1.yaml"^^ . "apr-cli-safety-v1"^^ . . . + . "contracts/aprender/apr-cli-sampling-v1.yaml"^^ . "apr-cli-sampling-v1"^^ . . @@ -2157,6 +4967,7 @@ "schema"^^ . . . + . "contracts/apr-cli-v1.yaml"^^ . "contracts/aprender/apr-cli-v1.yaml"^^ . "apr-cli-v1"^^ . @@ -2170,6 +4981,7 @@ "1.0.0"^^ . . . + . "contracts/apr-code-no-arg-startup-v1.yaml"^^ . "apr-code-no-arg-startup-v1"^^ . "kernel"^^ . @@ -2182,15 +4994,18 @@ "ACTIVE"^^ . . . + . "contracts/apr-code-toolcall-retention-v1.yaml"^^ . "apr-code-toolcall-retention-v1"^^ . "kernel"^^ . . . + . "contracts/batuta/apr-code-v1.yaml"^^ . "apr-code-v1"^^ . . . + . "contracts/apr-compare-hf-nonvacuous-v1.yaml"^^ . "apr-compare-hf-nonvacuous-v1"^^ . . @@ -2215,54 +5030,67 @@ "1.0.0"^^ . . . + . "contracts/apr-corpus-algorithm-competition-corpus-v1.yaml"^^ . "apr-corpus-algorithm-competition-corpus-v1"^^ . . . + . "contracts/apr-corpus-databricks-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-databricks-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-databricks-scala-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-databricks-scala-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-hugging-face-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-hugging-face-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-jax-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-jax-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-lean-ground-truth-v1.yaml"^^ . "apr-corpus-lean-ground-truth-v1"^^ . . . + . "contracts/apr-corpus-ludwig-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-ludwig-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-mixed-python-rust-ground-truth-v1.yaml"^^ . "apr-corpus-mixed-python-rust-ground-truth-v1"^^ . . . + . "contracts/apr-corpus-mixed-rust-lean-ground-truth-v1.yaml"^^ . "apr-corpus-mixed-rust-lean-ground-truth-v1"^^ . . . + . "contracts/apr-corpus-safe-lua-groundtruth-v1.yaml"^^ . "apr-corpus-safe-lua-groundtruth-v1"^^ . . . + . "contracts/apr-corpus-tgi-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-tgi-ground-truth-corpus-v1"^^ . . . + . "contracts/apr-corpus-tiny-model-ground-truth-v1.yaml"^^ . "apr-corpus-tiny-model-ground-truth-v1"^^ . . . + . "contracts/apr-corpus-vllm-ground-truth-corpus-v1.yaml"^^ . "apr-corpus-vllm-ground-truth-corpus-v1"^^ . . @@ -2272,6 +5100,7 @@ "schema"^^ . . . + . "contracts/apr-data-pipeline-v1.yaml"^^ . "contracts/aprender/apr-data-pipeline-v1.yaml"^^ . "apr-data-pipeline-v1"^^ . @@ -2285,38 +5114,45 @@ "1.0.0"^^ . . . + . "contracts/apr-distill-smoke-validation-v1.yaml"^^ . "apr-distill-smoke-validation-v1"^^ . "apr-distill-smoke-validation"^^ . "1.0.0"^^ . . . + . "contracts/apr-distill-teacher-backend-selection-v1.yaml"^^ . "apr-distill-teacher-backend-selection-v1"^^ . "apr-distill-teacher-backend-selection"^^ . "1.0.0"^^ . . . + . "contracts/apr-distill-teacher-vocab-alignment-v1.yaml"^^ . "apr-distill-teacher-vocab-alignment-v1"^^ . "apr-distill-teacher-vocab-alignment"^^ . "1.0.0"^^ . . . + . "contracts/apr-docs-v1.yaml"^^ . "apr-docs-v1"^^ . . . + . "contracts/apr-dogfood-coverage-v1.yaml"^^ . "apr-dogfood-coverage-v1"^^ . . . + . "contracts/apr-eval-humaneval-harness-invariant-v1.yaml"^^ . "apr-eval-humaneval-harness-invariant-v1"^^ . "kernel"^^ . "ACTIVE"^^ . . . + . "contracts/apr-eval-humaneval-inference-failure-handling-v1.yaml"^^ . "apr-eval-humaneval-inference-failure-handling-v1"^^ . "apr-eval-humaneval-inference-failure-handling"^^ . @@ -2342,28 +5178,34 @@ "1"^^ . . . + . "contracts/apr-finetune-metrics-v1.yaml"^^ . "apr-finetune-metrics-v1"^^ . . . + . "contracts/aprender/apr-finetune-v1.yaml"^^ . "apr-finetune-v1"^^ . . . + . "contracts/apr-format-extraction-v1.yaml"^^ . "apr-format-extraction-v1"^^ . "kernel"^^ . . . + . "contracts/apr-format-invariants-v1.yaml"^^ . "apr-format-invariants-v1"^^ . . . + . "contracts/apr-format-leaf-sovereignty-v1.yaml"^^ . "apr-format-leaf-sovereignty-v1"^^ . "kernel"^^ . . . + . "contracts/apr-format-safety-v1.yaml"^^ . "contracts/aprender/apr-format-safety-v1.yaml"^^ . "apr-format-safety-v1"^^ . @@ -2377,29 +5219,35 @@ "1.0.0"^^ . . . + . "contracts/apr-gguf-export-symmetry-v1.yaml"^^ . "apr-gguf-export-symmetry-v1"^^ . "apr-gguf-export-symmetry"^^ . "1.0.0"^^ . . . + . "contracts/apr-global-verbosity-wiring-v1.yaml"^^ . "apr-global-verbosity-wiring-v1"^^ . "kernel"^^ . . . + . "contracts/aprender/apr-gpu-backend-v1.yaml"^^ . "apr-gpu-backend-v1"^^ . . . + . "contracts/apr-gpu-diagnostics-v1.yaml"^^ . "apr-gpu-diagnostics-v1"^^ . . . + . "contracts/apr-gpu-parity-consistency-v1.yaml"^^ . "apr-gpu-parity-consistency-v1"^^ . . . + . "contracts/apr-gpu-presence-v1.yaml"^^ . "apr-gpu-presence-v1"^^ . . @@ -2440,18 +5288,22 @@ "schema"^^ . . . + . "contracts/apr-inspect-dtype-naming-v1.yaml"^^ . "apr-inspect-dtype-naming-v1"^^ . . . + . "contracts/apr-inspect-flags-v1.yaml"^^ . "apr-inspect-flags-v1"^^ . . . + . "contracts/apr-inspect-metadata-propagation-v1.yaml"^^ . "apr-inspect-metadata-propagation-v1"^^ . . . + . "contracts/apr-inspect-quantization-v1.yaml"^^ . "apr-inspect-quantization-v1"^^ . . @@ -2461,15 +5313,18 @@ "pattern"^^ . . . + . "contracts/apr-lint-producers-v1.yaml"^^ . "apr-lint-producers-v1"^^ . "kernel"^^ . . . + . "contracts/apr-list-disk-reconciliation-v1.yaml"^^ . "apr-list-disk-reconciliation-v1"^^ . . . + . "contracts/apr-list-quiet-wiring-v1.yaml"^^ . "apr-list-quiet-wiring-v1"^^ . . @@ -2502,6 +5357,7 @@ "1"^^ . . . + . "contracts/apr-mcp-serve-liveness-v1.yaml"^^ . "apr-mcp-serve-liveness-v1"^^ . . @@ -2514,6 +5370,7 @@ "1.0.0"^^ . . . + . "contracts/apr-mcp-stdio-drain-v1.yaml"^^ . "apr-mcp-stdio-drain-v1"^^ . "kernel"^^ . @@ -2530,6 +5387,7 @@ "1.0.0"^^ . . . + . "contracts/apr-mcp-tool-schemas-v1.yaml"^^ . "apr-mcp-tool-schemas-v1"^^ . "apr-mcp-tool-schemas"^^ . @@ -2553,36 +5411,44 @@ "1.0.0"^^ . . . + . "contracts/apr-model-diagnostics-v1.yaml"^^ . "apr-model-diagnostics-v1"^^ . . . + . "contracts/batuta/apr-model-discovery-v1.yaml"^^ . "apr-model-discovery-v1"^^ . . . + . "contracts/apr-model-graph-v1.yaml"^^ . "apr-model-graph-v1"^^ . . . + . "contracts/apr-model-lifecycle-v1.yaml"^^ . "contracts/aprender/apr-model-lifecycle-v1.yaml"^^ . "apr-model-lifecycle-v1"^^ . . . + . "contracts/apr-model-optimization-v1.yaml"^^ . "apr-model-optimization-v1"^^ . . . + . "contracts/apr-model-qa-v1.yaml"^^ . "contracts/aprender/apr-model-qa-v1.yaml"^^ . "apr-model-qa-v1"^^ . . . + . "contracts/apr-model-security-v1.yaml"^^ . "apr-model-security-v1"^^ . . . + . "contracts/apr-mono-binary-rule-v1.yaml"^^ . "apr-mono-binary-rule-v1"^^ . . @@ -2602,6 +5468,7 @@ "1.0.0"^^ . . . + . "contracts/apr-org-taxonomy-v1.yaml"^^ . "apr-org-taxonomy-v1"^^ . . @@ -5378,6 +8245,7 @@ "1.2.0"^^ . . . + . "contracts/apr-publish-hf-large-file-v1.yaml"^^ . "apr-publish-hf-large-file-v1"^^ . . @@ -5389,22 +8257,27 @@ "1"^^ . . . + . "contracts/apr-qa-chaos-v1.yaml"^^ . "apr-qa-chaos-v1"^^ . . . + . "contracts/apr-qa-coverage-v1.yaml"^^ . "apr-qa-coverage-v1"^^ . . . + . "contracts/apr-qa-differential-v1.yaml"^^ . "apr-qa-differential-v1"^^ . . . + . "contracts/apr-qa-metamorphic-v1.yaml"^^ . "apr-qa-metamorphic-v1"^^ . . . + . "contracts/apr-qa-silent-fallback-v1.yaml"^^ . "apr-qa-silent-fallback-v1"^^ . . @@ -5487,6 +8360,7 @@ "1.0.0"^^ . . . + . "contracts/apr-serve-cancellation-v1.yaml"^^ . "apr-serve-cancellation-v1"^^ . . @@ -5505,11 +8379,13 @@ "1"^^ . . . + . "contracts/apr-serve-v1.yaml"^^ . "contracts/aprender/apr-serve-v1.yaml"^^ . "apr-serve-v1"^^ . . . + . "contracts/apr-ship-007-gpu-stage-bisection-v1.yaml"^^ . "apr-ship-007-gpu-stage-bisection-v1"^^ . "kernel"^^ . @@ -5544,6 +8420,7 @@ "1"^^ . . . + . "contracts/apr-stochastic-lr-v1.yaml"^^ . "apr-stochastic-lr-v1"^^ . . @@ -5558,94 +8435,117 @@ "pattern"^^ . . . + . "contracts/apr-tool-bashrs-v1.yaml"^^ . "apr-tool-bashrs-v1"^^ . . . + . "contracts/apr-tool-ccpo-v1.yaml"^^ . "apr-tool-ccpo-v1"^^ . . . + . "contracts/apr-tool-cohete-v1.yaml"^^ . "apr-tool-cohete-v1"^^ . . . + . "contracts/apr-tool-copia-v1.yaml"^^ . "apr-tool-copia-v1"^^ . . . + . "contracts/apr-tool-decy-v1.yaml"^^ . "apr-tool-decy-v1"^^ . . . + . "contracts/apr-tool-depyler-v1.yaml"^^ . "apr-tool-depyler-v1"^^ . . . + . "contracts/apr-tool-duende-v1.yaml"^^ . "apr-tool-duende-v1"^^ . . . + . "contracts/apr-tool-forjar-v1.yaml"^^ . "apr-tool-forjar-v1"^^ . . . + . "contracts/apr-tool-manzana-v1.yaml"^^ . "apr-tool-manzana-v1"^^ . . . + . "contracts/apr-tool-microgpt-v1.yaml"^^ . "apr-tool-microgpt-v1"^^ . . . + . "contracts/apr-tool-organizational-intelligence-plugin-v1.yaml"^^ . "apr-tool-organizational-intelligence-plugin-v1"^^ . . . + . "contracts/apr-tool-paiml-mcp-agent-toolkit-v1.yaml"^^ . "apr-tool-paiml-mcp-agent-toolkit-v1"^^ . . . + . "contracts/apr-tool-pcode-v1.yaml"^^ . "apr-tool-pcode-v1"^^ . . . + . "contracts/apr-tool-pdmt-v1.yaml"^^ . "apr-tool-pdmt-v1"^^ . . . + . "contracts/apr-tool-pepita-v1.yaml"^^ . "apr-tool-pepita-v1"^^ . . . + . "contracts/apr-tool-pforge-v1.yaml"^^ . "apr-tool-pforge-v1"^^ . . . + . "contracts/apr-tool-rascal-v1.yaml"^^ . "apr-tool-rascal-v1"^^ . . . + . "contracts/apr-tool-rmedia-v1.yaml"^^ . "apr-tool-rmedia-v1"^^ . . . + . "contracts/apr-tool-rust-mcp-sdk-v1.yaml"^^ . "apr-tool-rust-mcp-sdk-v1"^^ . . . + . "contracts/apr-tool-rust-mdipierro-nlib-v1.yaml"^^ . "apr-tool-rust-mdipierro-nlib-v1"^^ . . . + . "contracts/apr-tool-spydecy-v1.yaml"^^ . "apr-tool-spydecy-v1"^^ . . . + . "contracts/apr-train-shell-model-provenance-v1.yaml"^^ . "apr-train-shell-model-provenance-v1"^^ . . . + . "contracts/entrenar/apr-training-parity-v1.yaml"^^ . "apr-training-parity-v1"^^ . . @@ -5667,6 +8567,7 @@ "pattern"^^ . . . + . "contracts/apr-version-traceability-v1.yaml"^^ . "apr-version-traceability-v1"^^ . . @@ -5676,6 +8577,7 @@ "schema"^^ . . . + . "contracts/apr-wgpu-adapter-enumeration-excludes-gles-v1.yaml"^^ . "apr-wgpu-adapter-enumeration-excludes-gles-v1"^^ . "kernel"^^ . @@ -5689,30 +8591,36 @@ "1.0.0"^^ . . . + . "contracts/apr-zero-feature-gate-v1.yaml"^^ . "apr-zero-feature-gate-v1"^^ . . . + . "contracts/arch-constraints-v1.yaml"^^ . "arch-constraints-v1"^^ . . . + . "contracts/architecture-requirements-v1.yaml"^^ . "architecture-requirements-v1"^^ . . . + . "contracts/archive-repos-v1.yaml"^^ . "archive-repos-v1"^^ . "archive-repos"^^ . "1.0.0"^^ . . . + . "contracts/arima-ar-centering-v1.yaml"^^ . "arima-ar-centering-v1"^^ . "arima-ar-centering"^^ . "1.0.0"^^ . . . + . "contracts/arima-v1.yaml"^^ . "arima-v1"^^ . . @@ -5729,6 +8637,7 @@ "1"^^ . . . + . "contracts/attention-backward-v1.yaml"^^ . "contracts/entrenar/attention-backward-v1.yaml"^^ . "attention-backward-v1"^^ . @@ -5736,23 +8645,28 @@ "1.0.0"^^ . . . + . "contracts/entrenar/attention-head-extraction-v1.yaml"^^ . "attention-head-extraction-v1"^^ . . . + . "contracts/attention-kernel-v1.yaml"^^ . "contracts/realizar/attention-kernel-v1.yaml"^^ . "attention-kernel-v1"^^ . . . + . "contracts/attention-scaling-v1.yaml"^^ . "attention-scaling-v1"^^ . . . + . "contracts/avx2-fma-dot-v1.yaml"^^ . "avx2-fma-dot-v1"^^ . . . + . "contracts/avx512-blis-v1.yaml"^^ . "contracts/trueno/avx512-blis-v1.yaml"^^ . "avx512-blis-v1"^^ . @@ -5760,6 +8674,7 @@ "1.0.0"^^ . . . + . "contracts/avx512-q4k-v1.yaml"^^ . "contracts/trueno/avx512-q4k-v1.yaml"^^ . "avx512-q4k-v1"^^ . @@ -5767,6 +8682,7 @@ "1.1.0"^^ . . . + . "contracts/backend-dispatch-v1.yaml"^^ . "backend-dispatch-v1"^^ . . @@ -5777,39 +8693,47 @@ "baseline-v1"^^ . . . + . "contracts/batch-admission-v1.yaml"^^ . "batch-admission-v1"^^ . . . + . "contracts/aprender/batch-training-v1.yaml"^^ . "contracts/batch-training-v1.yaml"^^ . "batch-training-v1"^^ . . . + . "contracts/batched-beam-search-v1.yaml"^^ . "batched-beam-search-v1"^^ . . . + . "contracts/batchnorm-kernel-v1.yaml"^^ . "batchnorm-kernel-v1"^^ . . . + . "contracts/batchnorm-running-stats-v1.yaml"^^ . "batchnorm-running-stats-v1"^^ . "batchnorm-running-stats"^^ . "1.0.0"^^ . . . + . "contracts/bayesian-logistic-map-v1.yaml"^^ . "bayesian-logistic-map-v1"^^ . "bayesian-logistic-map"^^ . "1.0.0"^^ . . . + . "contracts/bayesian-v1.yaml"^^ . "bayesian-v1"^^ . . . + . "contracts/faro/beacon-dispatch-v1.yaml"^^ . "beacon-dispatch-v1"^^ . . @@ -5913,6 +8837,7 @@ "1"^^ . . . + . "contracts/beat-sklearn-nmi-v1.yaml"^^ . "beat-sklearn-nmi-v1"^^ . . @@ -5929,28 +8854,423 @@ "model-family"^^ . . . + . "contracts/bf16-dequant-v1.yaml"^^ . "bf16-dequant-v1"^^ . "bf16-dequant"^^ . "1.0.0"^^ . . . + . "contracts/bias-add-v1.yaml"^^ . "bias-add-v1"^^ . . . + . "contracts/bidirectional-attention-v1.yaml"^^ . "bidirectional-attention-v1"^^ . + . + . + . + . + "binary/apr-cli/apr"^^ . + "binary"^^ . + "contracts/bin-apr-cli--apr-http-mcp-v1.yaml"^^ . + "bin-apr-cli--apr-http-mcp-v1"^^ . + "pattern"^^ . + "bin-apr-cli--apr-http-mcp-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/apr-cli/apr"^^ . + "binary"^^ . + "contracts/bin-apr-cli--apr-v1.yaml"^^ . + "bin-apr-cli--apr-v1"^^ . + "pattern"^^ . + "bin-apr-cli--apr-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/apr-cli/aprender-corpus-ingest"^^ . + "binary"^^ . + "contracts/bin-apr-cli--aprender-corpus-ingest-v1.yaml"^^ . + "bin-apr-cli--aprender-corpus-ingest-v1"^^ . + "pattern"^^ . + "bin-apr-cli--aprender-corpus-ingest-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender/apr"^^ . + "binary"^^ . + "contracts/bin-aprender--apr-http-mcp-v1.yaml"^^ . + "bin-aprender--apr-http-mcp-v1"^^ . + "pattern"^^ . + "bin-aprender--apr-http-mcp-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender/apr"^^ . + "binary"^^ . + "contracts/bin-aprender--apr-v1.yaml"^^ . + "bin-aprender--apr-v1"^^ . + "pattern"^^ . + "bin-aprender--apr-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-cbtop/aprender-cbtop"^^ . + "binary"^^ . + "contracts/bin-aprender-cbtop--aprender-cbtop-v1.yaml"^^ . + "bin-aprender-cbtop--aprender-cbtop-v1"^^ . + "pattern"^^ . + "bin-aprender-cbtop--aprender-cbtop-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-cgp/aprender-cgp"^^ . + "binary"^^ . + "contracts/bin-aprender-cgp--aprender-cgp-v1.yaml"^^ . + "bin-aprender-cgp--aprender-cgp-v1"^^ . + "pattern"^^ . + "bin-aprender-cgp--aprender-cgp-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-compute-xtask/aprender-compute-xtask"^^ . + "binary"^^ . + "contracts/bin-aprender-compute-xtask--aprender-compute-xtask-v1.yaml"^^ . + "bin-aprender-compute-xtask--aprender-compute-xtask-v1"^^ . + "pattern"^^ . + "bin-aprender-compute-xtask--aprender-compute-xtask-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-contracts-cli/pv"^^ . + "binary"^^ . + "contracts/bin-aprender-contracts-cli--pv-v1.yaml"^^ . + "bin-aprender-contracts-cli--pv-v1"^^ . + "pattern"^^ . + "bin-aprender-contracts-cli--pv-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-data/aprender-data"^^ . + "binary"^^ . + "contracts/bin-aprender-data--aprender-data-v1.yaml"^^ . + "bin-aprender-data--aprender-data-v1"^^ . + "pattern"^^ . + "bin-aprender-data--aprender-data-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-db/aprender-db"^^ . + "binary"^^ . + "contracts/bin-aprender-db--aprender-db-v1.yaml"^^ . + "bin-aprender-db--aprender-db-v1"^^ . + "pattern"^^ . + "bin-aprender-db--aprender-db-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-explain/aprender-explain"^^ . + "binary"^^ . + "contracts/bin-aprender-explain--aprender-explain-v1.yaml"^^ . + "bin-aprender-explain--aprender-explain-v1"^^ . + "pattern"^^ . + "bin-aprender-explain--aprender-explain-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-orchestrate/aprender-orchestrate"^^ . + "binary"^^ . + "contracts/bin-aprender-orchestrate--aprender-orchestrate-v1.yaml"^^ . + "bin-aprender-orchestrate--aprender-orchestrate-v1"^^ . + "pattern"^^ . + "bin-aprender-orchestrate--aprender-orchestrate-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-present-cli/aprender-present"^^ . + "binary"^^ . + "contracts/bin-aprender-present-cli--aprender-present-v1.yaml"^^ . + "bin-aprender-present-cli--aprender-present-v1"^^ . + "pattern"^^ . + "bin-aprender-present-cli--aprender-present-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-present-terminal/aprender-ptop"^^ . + "binary"^^ . + "contracts/bin-aprender-present-terminal--aprender-ptop-v1.yaml"^^ . + "bin-aprender-present-terminal--aprender-ptop-v1"^^ . + "pattern"^^ . + "bin-aprender-present-terminal--aprender-ptop-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-present-terminal/aprender-score"^^ . + "binary"^^ . + "contracts/bin-aprender-present-terminal--aprender-score-v1.yaml"^^ . + "bin-aprender-present-terminal--aprender-score-v1"^^ . + "pattern"^^ . + "bin-aprender-present-terminal--aprender-score-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-profile/aprender-profile"^^ . + "binary"^^ . + "contracts/bin-aprender-profile--aprender-profile-v1.yaml"^^ . + "bin-aprender-profile--aprender-profile-v1"^^ . + "pattern"^^ . + "bin-aprender-profile--aprender-profile-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-ptx-debug/aprender-ptx-debug"^^ . + "binary"^^ . + "contracts/bin-aprender-ptx-debug--aprender-ptx-debug-v1.yaml"^^ . + "bin-aprender-ptx-debug--aprender-ptx-debug-v1"^^ . + "pattern"^^ . + "bin-aprender-ptx-debug--aprender-ptx-debug-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-qa-certify/aprender-qa-readme-sync"^^ . + "binary"^^ . + "contracts/bin-aprender-qa-certify--aprender-qa-readme-sync-v1.yaml"^^ . + "bin-aprender-qa-certify--aprender-qa-readme-sync-v1"^^ . + "pattern"^^ . + "bin-aprender-qa-certify--aprender-qa-readme-sync-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-qa-cli/apr-qa"^^ . + "binary"^^ . + "contracts/bin-aprender-qa-cli--apr-qa-v1.yaml"^^ . + "bin-aprender-qa-cli--apr-qa-v1"^^ . + "pattern"^^ . + "bin-aprender-qa-cli--apr-qa-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-rag-cli/aprender-rag"^^ . + "binary"^^ . + "contracts/bin-aprender-rag-cli--aprender-rag-v1.yaml"^^ . + "bin-aprender-rag-cli--aprender-rag-v1"^^ . + "pattern"^^ . + "bin-aprender-rag-cli--aprender-rag-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-simulate/aprender-simulate"^^ . + "binary"^^ . + "contracts/bin-aprender-simulate--aprender-simulate-v1.yaml"^^ . + "bin-aprender-simulate--aprender-simulate-v1"^^ . + "pattern"^^ . + "bin-aprender-simulate--aprender-simulate-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-test-cli/aprender-test-cli"^^ . + "binary"^^ . + "contracts/bin-aprender-test-cli--aprender-test-cli-v1.yaml"^^ . + "bin-aprender-test-cli--aprender-test-cli-v1"^^ . + "pattern"^^ . + "bin-aprender-test-cli--aprender-test-cli-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-train-bench/aprender-train-bench"^^ . + "binary"^^ . + "contracts/bin-aprender-train-bench--aprender-train-bench-v1.yaml"^^ . + "bin-aprender-train-bench--aprender-train-bench-v1"^^ . + "pattern"^^ . + "bin-aprender-train-bench--aprender-train-bench-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-train-distill/aprender-train-distill"^^ . + "binary"^^ . + "contracts/bin-aprender-train-distill--aprender-train-distill-v1.yaml"^^ . + "bin-aprender-train-distill--aprender-train-distill-v1"^^ . + "pattern"^^ . + "bin-aprender-train-distill--aprender-train-distill-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-train-inspect/aprender-train-inspect"^^ . + "binary"^^ . + "contracts/bin-aprender-train-inspect--aprender-train-inspect-v1.yaml"^^ . + "bin-aprender-train-inspect--aprender-train-inspect-v1"^^ . + "pattern"^^ . + "bin-aprender-train-inspect--aprender-train-inspect-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-train-lora/aprender-train-lora"^^ . + "binary"^^ . + "contracts/bin-aprender-train-lora--aprender-train-lora-v1.yaml"^^ . + "bin-aprender-train-lora--aprender-train-lora-v1"^^ . + "pattern"^^ . + "bin-aprender-train-lora--aprender-train-lora-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-train-shell/aprender-train-shell"^^ . + "binary"^^ . + "contracts/bin-aprender-train-shell--aprender-train-shell-v1.yaml"^^ . + "bin-aprender-train-shell--aprender-train-shell-v1"^^ . + "pattern"^^ . + "bin-aprender-train-shell--aprender-train-shell-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-verify-ml/aprender-verify"^^ . + "binary"^^ . + "contracts/bin-aprender-verify-ml--aprender-verify-v1.yaml"^^ . + "bin-aprender-verify-ml--aprender-verify-v1"^^ . + "pattern"^^ . + "bin-aprender-verify-ml--aprender-verify-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-zram-cli/aprender-zram"^^ . + "binary"^^ . + "contracts/bin-aprender-zram-cli--aprender-zram-v1.yaml"^^ . + "bin-aprender-zram-cli--aprender-zram-v1"^^ . + "pattern"^^ . + "bin-aprender-zram-cli--aprender-zram-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + . + . + "binary/aprender-zram-generator/aprender-zram-generator"^^ . + "binary"^^ . + "contracts/bin-aprender-zram-generator--aprender-zram-generator-v1.yaml"^^ . + "bin-aprender-zram-generator--aprender-zram-generator-v1"^^ . + "pattern"^^ . + "bin-aprender-zram-generator--aprender-zram-generator-v1"^^ . + "active"^^ . + "1.0.0"^^ . + . + . + "contracts/binary-debt-v1.yaml"^^ . + "binary-debt-v1"^^ . + "pattern"^^ . + . + . + . + . + . + "binary"^^ . + "contracts/binary-surface-v1.yaml"^^ . + "binary-surface-v1"^^ . + "pattern"^^ . + "binary-surface-v1"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/forjar/blake3-state-v1.yaml"^^ . "blake3-state-v1"^^ . . . + . "contracts/trueno/blis-gemm-v1.yaml"^^ . "blis-gemm-v1"^^ . . . + . "contracts/trueno/blis-thread-cap-v1.yaml"^^ . "blis-thread-cap-v1"^^ . . @@ -5960,16 +9280,19 @@ "model-family"^^ . . . + . "contracts/bpe-encode-bytes-to-unicode-v1.yaml"^^ . "bpe-encode-bytes-to-unicode-v1"^^ . "bpe-encode-bytes-to-unicode"^^ . "1.0.0"^^ . . . + . "contracts/bpe-tokenization-v1.yaml"^^ . "bpe-tokenization-v1"^^ . . . + . "contracts/bpe-training-perf-v1.yaml"^^ . "bpe-training-perf-v1"^^ . "kernel"^^ . @@ -5977,35 +9300,42 @@ "1.2.0"^^ . . . + . "contracts/builder-pattern-v1.yaml"^^ . "contracts/repartir/builder-pattern-v1.yaml"^^ . "builder-pattern-v1"^^ . . . + . "contracts/calibration-v1.yaml"^^ . "calibration-v1"^^ . . . + . "contracts/entrenar/canary-metrics-schema-v1.yaml"^^ . "canary-metrics-schema-v1"^^ . . . + . "contracts/entrenar/canary-score-gate-v1.yaml"^^ . "canary-score-gate-v1"^^ . . . + . "contracts/cgp-monorepo-build-v1.yaml"^^ . "cgp-monorepo-build-v1"^^ . "cgp-monorepo-build"^^ . "1.0.0"^^ . . . + . "contracts/cgp-monorepo-consolidation-v1.yaml"^^ . "cgp-monorepo-consolidation-v1"^^ . "cgp-monorepo-consolidation"^^ . "1.2.0"^^ . . . + . "contracts/chat-template-v1.yaml"^^ . "contracts/realizar/chat-template-v1.yaml"^^ . "chat-template-v1"^^ . @@ -6025,14 +9355,17 @@ "pattern"^^ . . . + . "contracts/ci-infra-v1.yaml"^^ . "ci-infra-v1"^^ . . . + . "contracts/classification-finetune-v1.yaml"^^ . "classification-finetune-v1"^^ . . . + . "contracts/classifier-pipeline-v1.yaml"^^ . "classifier-pipeline-v1"^^ . . @@ -6043,53 +9376,77 @@ "claude-code-parity-apr"^^ . "ACTIVE_RUNTIME"^^ . "1.32.0"^^ . + . + . + . + . + . + "CLAUDE.md"^^ . + "llm-context"^^ . + "contracts/claude-md.yaml"^^ . + "claude-md"^^ . + "pattern"^^ . + "claude-md"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/clean-chat-output-v1.yaml"^^ . "clean-chat-output-v1"^^ . "clean-chat-output"^^ . "1.0.0"^^ . . . + . "contracts/rclean/cleanup-safety-v1.yaml"^^ . "cleanup-safety-v1"^^ . . . + . "contracts/aprender/cli-dispatch-v1.yaml"^^ . "contracts/cli-dispatch-v1.yaml"^^ . "cli-dispatch-v1"^^ . . . + . "contracts/pmat/cli-interface-v1.yaml"^^ . "cli-interface-v1"^^ . . . + . "contracts/bashrs/cli-lint-v1.yaml"^^ . "cli-lint-v1"^^ . . . + . "contracts/batuta/cli-oracle-v1.yaml"^^ . "cli-oracle-v1"^^ . . . + . "contracts/depyler/cli-transpile-v1.yaml"^^ . "cli-transpile-v1"^^ . . . + . "contracts/clustering-metrics-relabel-invariant-v1.yaml"^^ . "clustering-metrics-relabel-invariant-v1"^^ . "clustering-metrics-relabel-invariant"^^ . "1.0.0"^^ . . . + . "contracts/cma-es-kernel-v1.yaml"^^ . "cma-es-kernel-v1"^^ . . . + . "contracts/codebert-tokenizer-validation-v1.yaml"^^ . "codebert-tokenizer-validation-v1"^^ . . . + . "contracts/codegen-dispatch-v1.yaml"^^ . "contracts/forjar/codegen-dispatch-v1.yaml"^^ . "codegen-dispatch-v1"^^ . @@ -6097,10 +9454,12 @@ "1.0.0"^^ . . . + . "contracts/trueno-db/columnar-storage-v1.yaml"^^ . "columnar-storage-v1"^^ . . . + . "contracts/comply-check-v1.yaml"^^ . "contracts/pmat/comply-check-v1.yaml"^^ . "comply-check-v1"^^ . @@ -6114,10 +9473,12 @@ "PROPOSED"^^ . . . + . "contracts/trueno-zram/compression-codec-v1.yaml"^^ . "compression-codec-v1"^^ . . . + . "contracts/compression-roundtrip-v1.yaml"^^ . "contracts/pmat/compression-roundtrip-v1.yaml"^^ . "contracts/trueno-zram/compression-roundtrip-v1.yaml"^^ . @@ -6131,14 +9492,17 @@ "pattern"^^ . . . + . "contracts/pmat/concurrency-safety-v1.yaml"^^ . "concurrency-safety-v1"^^ . . . + . "contracts/pmat/configuration-schema-v1.yaml"^^ . "configuration-schema-v1"^^ . . . + . "contracts/alimentar/configuration-v1.yaml"^^ . "contracts/certeza/configuration-v1.yaml"^^ . "contracts/configuration-v1.yaml"^^ . @@ -6149,6 +9513,7 @@ "configuration-v1"^^ . . . + . "contracts/context-generation-v1.yaml"^^ . "contracts/pmat/context-generation-v1.yaml"^^ . "context-generation-v1"^^ . @@ -6156,32 +9521,34 @@ "1.0.0"^^ . . . + . "contracts/continuous-batching-v1.yaml"^^ . "continuous-batching-v1"^^ . . . + . "contracts/contrastive-pair-protocol-v1.yaml"^^ . "contrastive-pair-protocol-v1"^^ . . . + . "contracts/conv1d-kernel-v1.yaml"^^ . "conv1d-kernel-v1"^^ . . . + . "contracts/conversation-generation-v1.yaml"^^ . "conversation-generation-v1"^^ . . . + . "contracts/converter-moe-headdim-import-v1.yaml"^^ . "converter-moe-headdim-import-v1"^^ . "converter-moe-headdim-import"^^ . "1.0.0"^^ . - . - . - "contracts/cooperative-matrix-gemm-v1.yaml"^^ . - "cooperative-matrix-gemm-v1"^^ . . . + . "contracts/forjar/copia-delta-v1.yaml"^^ . "copia-delta-v1"^^ . . @@ -6193,6 +9560,7 @@ "1.0.0"^^ . . . + . "contracts/cpp-type-preservation-v1.yaml"^^ . "cpp-type-preservation-v1"^^ . . @@ -6202,1316 +9570,1638 @@ "pattern"^^ . . . + . "contracts/cpu-q4k-activation-quant-v1.yaml"^^ . "cpu-q4k-activation-quant-v1"^^ . . . + . "contracts/cpu-work-stealing-v1.yaml"^^ . "cpu-work-stealing-v1"^^ . . . + . "contracts/crate-hygiene-v1.yaml"^^ . "crate-hygiene-v1"^^ . . . + . "contracts/crate-readme-v1.yaml"^^ . "crate-readme-v1"^^ . . . + . "contracts/cross-entropy-kernel-v1.yaml"^^ . "cross-entropy-kernel-v1"^^ . . . + . "contracts/crux-A-01-v1.yaml"^^ . "crux-A-01-v1"^^ . . . + . "contracts/crux-A-02-v1.yaml"^^ . "crux-A-02-v1"^^ . . . + . "contracts/crux-A-03-v1.yaml"^^ . "crux-A-03-v1"^^ . . . + . "contracts/crux-A-04-v1.yaml"^^ . "crux-A-04-v1"^^ . . . + . "contracts/crux-A-05-v1.yaml"^^ . "crux-A-05-v1"^^ . . . + . "contracts/crux-A-06-v1.yaml"^^ . "crux-A-06-v1"^^ . . . + . "contracts/crux-A-07-v1.yaml"^^ . "crux-A-07-v1"^^ . . . + . "contracts/crux-A-08-v1.yaml"^^ . "crux-A-08-v1"^^ . . . + . "contracts/crux-A-09-v1.yaml"^^ . "crux-A-09-v1"^^ . . . + . "contracts/crux-A-10-v1.yaml"^^ . "crux-A-10-v1"^^ . . . + . "contracts/crux-A-11-v1.yaml"^^ . "crux-A-11-v1"^^ . . . + . "contracts/crux-A-12-v1.yaml"^^ . "crux-A-12-v1"^^ . . . + . "contracts/crux-A-13-v1.yaml"^^ . "crux-A-13-v1"^^ . . . + . "contracts/crux-A-14-v1.yaml"^^ . "crux-A-14-v1"^^ . . . + . "contracts/crux-A-15-v1.yaml"^^ . "crux-A-15-v1"^^ . . . + . "contracts/crux-A-16-v1.yaml"^^ . "crux-A-16-v1"^^ . . . + . "contracts/crux-A-17-v1.yaml"^^ . "crux-A-17-v1"^^ . . . + . "contracts/crux-A-18-v1.yaml"^^ . "crux-A-18-v1"^^ . . . + . "contracts/crux-A-19-v1.yaml"^^ . "crux-A-19-v1"^^ . . . + . "contracts/crux-A-20-v1.yaml"^^ . "crux-A-20-v1"^^ . . . + . "contracts/crux-A-21-v1.yaml"^^ . "crux-A-21-v1"^^ . . . + . "contracts/crux-A-22-v1.yaml"^^ . "crux-A-22-v1"^^ . . . + . "contracts/crux-A-23-v1.yaml"^^ . "crux-A-23-v1"^^ . . . + . "contracts/crux-A-24-v1.yaml"^^ . "crux-A-24-v1"^^ . . . + . "contracts/crux-A-25-v1.yaml"^^ . "crux-A-25-v1"^^ . . . + . "contracts/crux-B-01-v1.yaml"^^ . "crux-B-01-v1"^^ . . . + . "contracts/crux-B-02-v1.yaml"^^ . "crux-B-02-v1"^^ . . . + . "contracts/crux-B-03-v1.yaml"^^ . "crux-B-03-v1"^^ . . . + . "contracts/crux-B-04-v1.yaml"^^ . "crux-B-04-v1"^^ . . . + . "contracts/crux-B-05-v1.yaml"^^ . "crux-B-05-v1"^^ . "kernel"^^ . . . + . "contracts/crux-B-06-v1.yaml"^^ . "crux-B-06-v1"^^ . . . + . "contracts/crux-B-07-v1.yaml"^^ . "crux-B-07-v1"^^ . . . + . "contracts/crux-B-08-v1.yaml"^^ . "crux-B-08-v1"^^ . . . + . "contracts/crux-B-09-v1.yaml"^^ . "crux-B-09-v1"^^ . . . + . "contracts/crux-B-10-v1.yaml"^^ . "crux-B-10-v1"^^ . . . + . "contracts/crux-B-11-v1.yaml"^^ . "crux-B-11-v1"^^ . . . + . "contracts/crux-B-12-v1.yaml"^^ . "crux-B-12-v1"^^ . . . + . "contracts/crux-B-13-v1.yaml"^^ . "crux-B-13-v1"^^ . . . + . "contracts/crux-B-14-v1.yaml"^^ . "crux-B-14-v1"^^ . . . + . "contracts/crux-B-15-v1.yaml"^^ . "crux-B-15-v1"^^ . . . + . "contracts/crux-B-16-v1.yaml"^^ . "crux-B-16-v1"^^ . . . + . "contracts/crux-B-17-v1.yaml"^^ . "crux-B-17-v1"^^ . . . + . "contracts/crux-B-18-v1.yaml"^^ . "crux-B-18-v1"^^ . . . + . "contracts/crux-B-19-v1.yaml"^^ . "crux-B-19-v1"^^ . "kernel"^^ . . . + . "contracts/crux-B-20-v1.yaml"^^ . "crux-B-20-v1"^^ . "kernel"^^ . . . + . "contracts/crux-C-01-v1.yaml"^^ . "crux-C-01-v1"^^ . . . + . "contracts/crux-C-02-v1.yaml"^^ . "crux-C-02-v1"^^ . . . + . "contracts/crux-C-03-v1.yaml"^^ . "crux-C-03-v1"^^ . . . + . "contracts/crux-C-04-v1.yaml"^^ . "crux-C-04-v1"^^ . "kernel"^^ . . . + . "contracts/crux-C-05-v1.yaml"^^ . "crux-C-05-v1"^^ . . . + . "contracts/crux-C-06-v1.yaml"^^ . "crux-C-06-v1"^^ . . . + . "contracts/crux-C-07-v1.yaml"^^ . "crux-C-07-v1"^^ . . . + . "contracts/crux-C-08-v1.yaml"^^ . "crux-C-08-v1"^^ . . . + . "contracts/crux-C-09-v1.yaml"^^ . "crux-C-09-v1"^^ . . . + . "contracts/crux-C-10-v1.yaml"^^ . "crux-C-10-v1"^^ . . . + . "contracts/crux-C-11-v1.yaml"^^ . "crux-C-11-v1"^^ . . . + . "contracts/crux-C-12-v1.yaml"^^ . "crux-C-12-v1"^^ . . . + . "contracts/crux-C-13-v1.yaml"^^ . "crux-C-13-v1"^^ . . . + . "contracts/crux-C-15-v1.yaml"^^ . "crux-C-15-v1"^^ . . . + . "contracts/crux-C-16-v1.yaml"^^ . "crux-C-16-v1"^^ . . . + . "contracts/crux-C-17-v1.yaml"^^ . "crux-C-17-v1"^^ . . . + . "contracts/crux-C-18-v1.yaml"^^ . "crux-C-18-v1"^^ . . . + . "contracts/crux-C-19-v1.yaml"^^ . "crux-C-19-v1"^^ . . . + . "contracts/crux-C-20-v1.yaml"^^ . "crux-C-20-v1"^^ . . . + . "contracts/crux-C-21-v1.yaml"^^ . "crux-C-21-v1"^^ . . . + . "contracts/crux-C-22-v1.yaml"^^ . "crux-C-22-v1"^^ . "kernel"^^ . . . + . "contracts/crux-C-23-v1.yaml"^^ . "crux-C-23-v1"^^ . "kernel"^^ . . . + . "contracts/crux-C-24-v1.yaml"^^ . "crux-C-24-v1"^^ . . . + . "contracts/crux-C-25-v1.yaml"^^ . "crux-C-25-v1"^^ . . . + . "contracts/crux-C-26-v1.yaml"^^ . "crux-C-26-v1"^^ . . . + . "contracts/crux-C-27-v1.yaml"^^ . "crux-C-27-v1"^^ . . . + . "contracts/crux-C-28-v1.yaml"^^ . "crux-C-28-v1"^^ . . . + . "contracts/crux-C-29-v1.yaml"^^ . "crux-C-29-v1"^^ . . . + . "contracts/crux-C-30-v1.yaml"^^ . "crux-C-30-v1"^^ . . . + . "contracts/crux-C-31-v1.yaml"^^ . "crux-C-31-v1"^^ . . . + . "contracts/crux-C-32-v1.yaml"^^ . "crux-C-32-v1"^^ . . . + . "contracts/crux-C-33-v1.yaml"^^ . "crux-C-33-v1"^^ . . . + . "contracts/crux-C-34-v1.yaml"^^ . "crux-C-34-v1"^^ . . . + . "contracts/crux-C-35-v1.yaml"^^ . "crux-C-35-v1"^^ . . . + . "contracts/crux-C-36-v1.yaml"^^ . "crux-C-36-v1"^^ . . . + . "contracts/crux-C-37-v1.yaml"^^ . "crux-C-37-v1"^^ . . . + . "contracts/crux-D-01-v1.yaml"^^ . "crux-D-01-v1"^^ . . . + . "contracts/crux-D-02-v1.yaml"^^ . "crux-D-02-v1"^^ . . . + . "contracts/crux-D-03-v1.yaml"^^ . "crux-D-03-v1"^^ . . . + . "contracts/crux-D-04-v1.yaml"^^ . "crux-D-04-v1"^^ . . . + . "contracts/crux-D-05-v1.yaml"^^ . "crux-D-05-v1"^^ . . . + . "contracts/crux-D-06-v1.yaml"^^ . "crux-D-06-v1"^^ . . . + . "contracts/crux-D-07-v1.yaml"^^ . "crux-D-07-v1"^^ . . . + . "contracts/crux-D-08-v1.yaml"^^ . "crux-D-08-v1"^^ . . . + . "contracts/crux-D-09-v1.yaml"^^ . "crux-D-09-v1"^^ . . . + . "contracts/crux-D-10-v1.yaml"^^ . "crux-D-10-v1"^^ . . . + . "contracts/crux-D-11-v1.yaml"^^ . "crux-D-11-v1"^^ . "kernel"^^ . . . + . "contracts/crux-D-12-v1.yaml"^^ . "crux-D-12-v1"^^ . . . + . "contracts/crux-D-13-v1.yaml"^^ . "crux-D-13-v1"^^ . . . + . "contracts/crux-D-14-v1.yaml"^^ . "crux-D-14-v1"^^ . . . + . "contracts/crux-D-15-v1.yaml"^^ . "crux-D-15-v1"^^ . . . + . "contracts/crux-D-16-v1.yaml"^^ . "crux-D-16-v1"^^ . . . + . "contracts/crux-D-17-v1.yaml"^^ . "crux-D-17-v1"^^ . . . + . "contracts/crux-D-18-v1.yaml"^^ . "crux-D-18-v1"^^ . . . + . "contracts/crux-D-19-v1.yaml"^^ . "crux-D-19-v1"^^ . . . + . "contracts/crux-D-20-v1.yaml"^^ . "crux-D-20-v1"^^ . . . + . "contracts/crux-D-21-v1.yaml"^^ . "crux-D-21-v1"^^ . . . + . "contracts/crux-D-22-v1.yaml"^^ . "crux-D-22-v1"^^ . . . + . "contracts/crux-D-23-v1.yaml"^^ . "crux-D-23-v1"^^ . . . + . "contracts/crux-D-24-v1.yaml"^^ . "crux-D-24-v1"^^ . . . + . "contracts/crux-D-25-v1.yaml"^^ . "crux-D-25-v1"^^ . . . + . "contracts/crux-D-26-v1.yaml"^^ . "crux-D-26-v1"^^ . . . + . "contracts/crux-D-27-v1.yaml"^^ . "crux-D-27-v1"^^ . . . + . "contracts/crux-D-28-v1.yaml"^^ . "crux-D-28-v1"^^ . . . + . "contracts/crux-D-29-v1.yaml"^^ . "crux-D-29-v1"^^ . . . + . "contracts/crux-D-30-v1.yaml"^^ . "crux-D-30-v1"^^ . . . + . "contracts/crux-D-31-v1.yaml"^^ . "crux-D-31-v1"^^ . . . + . "contracts/crux-D-32-v1.yaml"^^ . "crux-D-32-v1"^^ . . . + . "contracts/crux-D-33-v1.yaml"^^ . "crux-D-33-v1"^^ . . . + . "contracts/crux-D-34-v1.yaml"^^ . "crux-D-34-v1"^^ . . . + . "contracts/crux-D-35-v1.yaml"^^ . "crux-D-35-v1"^^ . . . + . "contracts/crux-E-01-v1.yaml"^^ . "crux-E-01-v1"^^ . . . + . "contracts/crux-E-02-v1.yaml"^^ . "crux-E-02-v1"^^ . "kernel"^^ . . . + . "contracts/crux-E-03-v1.yaml"^^ . "crux-E-03-v1"^^ . . . + . "contracts/crux-E-04-v1.yaml"^^ . "crux-E-04-v1"^^ . . . + . "contracts/crux-E-05-v1.yaml"^^ . "crux-E-05-v1"^^ . . . + . "contracts/crux-E-06-v1.yaml"^^ . "crux-E-06-v1"^^ . . . + . "contracts/crux-E-07-v1.yaml"^^ . "crux-E-07-v1"^^ . "kernel"^^ . . . + . "contracts/crux-E-08-v1.yaml"^^ . "crux-E-08-v1"^^ . . . + . "contracts/crux-E-09-v1.yaml"^^ . "crux-E-09-v1"^^ . . . + . "contracts/crux-E-10-v1.yaml"^^ . "crux-E-10-v1"^^ . . . + . "contracts/crux-E-11-v1.yaml"^^ . "crux-E-11-v1"^^ . . . + . "contracts/crux-E-12-v1.yaml"^^ . "crux-E-12-v1"^^ . . . + . "contracts/crux-E-13-v1.yaml"^^ . "crux-E-13-v1"^^ . . . + . "contracts/crux-E-14-v1.yaml"^^ . "crux-E-14-v1"^^ . . . + . "contracts/crux-E-15-v1.yaml"^^ . "crux-E-15-v1"^^ . . . + . "contracts/crux-E-16-v1.yaml"^^ . "crux-E-16-v1"^^ . . . + . "contracts/crux-E-17-v1.yaml"^^ . "crux-E-17-v1"^^ . . . + . "contracts/crux-E-18-v1.yaml"^^ . "crux-E-18-v1"^^ . . . + . "contracts/crux-E-19-v1.yaml"^^ . "crux-E-19-v1"^^ . . . + . "contracts/crux-E-20-v1.yaml"^^ . "crux-E-20-v1"^^ . . . + . "contracts/crux-E-21-v1.yaml"^^ . "crux-E-21-v1"^^ . . . + . "contracts/crux-E-22-v1.yaml"^^ . "crux-E-22-v1"^^ . . . + . "contracts/crux-E-23-v1.yaml"^^ . "crux-E-23-v1"^^ . . . + . "contracts/crux-E-24-v1.yaml"^^ . "crux-E-24-v1"^^ . . . + . "contracts/crux-E-25-v1.yaml"^^ . "crux-E-25-v1"^^ . . . + . "contracts/crux-F-01-v1.yaml"^^ . "crux-F-01-v1"^^ . . . + . "contracts/crux-F-02-v1.yaml"^^ . "crux-F-02-v1"^^ . . . + . "contracts/crux-F-03-v1.yaml"^^ . "crux-F-03-v1"^^ . . . + . "contracts/crux-F-04-v1.yaml"^^ . "crux-F-04-v1"^^ . . . + . "contracts/crux-F-05-v1.yaml"^^ . "crux-F-05-v1"^^ . . . + . "contracts/crux-F-06-v1.yaml"^^ . "crux-F-06-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-07-v1.yaml"^^ . "crux-F-07-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-08-v1.yaml"^^ . "crux-F-08-v1"^^ . . . + . "contracts/crux-F-09-v1.yaml"^^ . "crux-F-09-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-11-v1.yaml"^^ . "crux-F-11-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-12-v1.yaml"^^ . "crux-F-12-v1"^^ . . . + . "contracts/crux-F-13-v1.yaml"^^ . "crux-F-13-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-14-v1.yaml"^^ . "crux-F-14-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-15-v1.yaml"^^ . "crux-F-15-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-16-v1.yaml"^^ . "crux-F-16-v1"^^ . . . + . "contracts/crux-F-17-v1.yaml"^^ . "crux-F-17-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-18-v1.yaml"^^ . "crux-F-18-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-19-v1.yaml"^^ . "crux-F-19-v1"^^ . "kernel"^^ . . . + . "contracts/crux-F-20-v1.yaml"^^ . "crux-F-20-v1"^^ . . . + . "contracts/crux-F-21-v1.yaml"^^ . "crux-F-21-v1"^^ . . . + . "contracts/crux-G-01-v1.yaml"^^ . "crux-G-01-v1"^^ . . . + . "contracts/crux-G-02-v1.yaml"^^ . "crux-G-02-v1"^^ . . . + . "contracts/crux-G-03-v1.yaml"^^ . "crux-G-03-v1"^^ . . . + . "contracts/crux-G-04-v1.yaml"^^ . "crux-G-04-v1"^^ . . . + . "contracts/crux-G-05-v1.yaml"^^ . "crux-G-05-v1"^^ . "kernel"^^ . . . + . "contracts/crux-G-06-v1.yaml"^^ . "crux-G-06-v1"^^ . . . + . "contracts/crux-G-07-v1.yaml"^^ . "crux-G-07-v1"^^ . . . + . "contracts/crux-G-08-v1.yaml"^^ . "crux-G-08-v1"^^ . . . + . "contracts/crux-G-09-v1.yaml"^^ . "crux-G-09-v1"^^ . . . + . "contracts/crux-G-10-v1.yaml"^^ . "crux-G-10-v1"^^ . . . + . "contracts/crux-G-11-v1.yaml"^^ . "crux-G-11-v1"^^ . . . + . "contracts/crux-G-12-v1.yaml"^^ . "crux-G-12-v1"^^ . . . + . "contracts/crux-G-13-v1.yaml"^^ . "crux-G-13-v1"^^ . . . + . "contracts/crux-G-14-v1.yaml"^^ . "crux-G-14-v1"^^ . . . + . "contracts/crux-G-15-v1.yaml"^^ . "crux-G-15-v1"^^ . . . + . "contracts/crux-H-01-v1.yaml"^^ . "crux-H-01-v1"^^ . . . + . "contracts/crux-H-02-v1.yaml"^^ . "crux-H-02-v1"^^ . . . + . "contracts/crux-H-03-v1.yaml"^^ . "crux-H-03-v1"^^ . . . + . "contracts/crux-H-05-v1.yaml"^^ . "crux-H-05-v1"^^ . . . + . "contracts/crux-H-06-v1.yaml"^^ . "crux-H-06-v1"^^ . . . + . "contracts/crux-H-07-v1.yaml"^^ . "crux-H-07-v1"^^ . . . + . "contracts/crux-H-08-v1.yaml"^^ . "crux-H-08-v1"^^ . . . + . "contracts/crux-H-09-v1.yaml"^^ . "crux-H-09-v1"^^ . . . + . "contracts/crux-H-10-v1.yaml"^^ . "crux-H-10-v1"^^ . . . + . "contracts/crux-H-11-v1.yaml"^^ . "crux-H-11-v1"^^ . . . + . "contracts/crux-H-12-v1.yaml"^^ . "crux-H-12-v1"^^ . . . + . "contracts/crux-H-13-v1.yaml"^^ . "crux-H-13-v1"^^ . "kernel"^^ . . . + . "contracts/crux-H-14-v1.yaml"^^ . "crux-H-14-v1"^^ . . . + . "contracts/crux-H-15-v1.yaml"^^ . "crux-H-15-v1"^^ . . . + . "contracts/crux-H-16-v1.yaml"^^ . "crux-H-16-v1"^^ . . . + . "contracts/crux-H-17-v1.yaml"^^ . "crux-H-17-v1"^^ . . . + . "contracts/crux-H-18-v1.yaml"^^ . "crux-H-18-v1"^^ . . . + . "contracts/crux-H-19-v1.yaml"^^ . "crux-H-19-v1"^^ . . . + . "contracts/crux-H-20-v1.yaml"^^ . "crux-H-20-v1"^^ . . . + . "contracts/crux-H-21-v1.yaml"^^ . "crux-H-21-v1"^^ . . . + . "contracts/crux-I-01-v1.yaml"^^ . "crux-I-01-v1"^^ . . . + . "contracts/crux-I-02-v1.yaml"^^ . "crux-I-02-v1"^^ . . . + . "contracts/crux-I-03-v1.yaml"^^ . "crux-I-03-v1"^^ . . . + . "contracts/crux-I-04-v1.yaml"^^ . "crux-I-04-v1"^^ . "kernel"^^ . . . + . "contracts/crux-I-06-v1.yaml"^^ . "crux-I-06-v1"^^ . "kernel"^^ . . . + . "contracts/crux-I-07-v1.yaml"^^ . "crux-I-07-v1"^^ . . . + . "contracts/crux-I-08-v1.yaml"^^ . "crux-I-08-v1"^^ . . . + . "contracts/crux-I-09-v1.yaml"^^ . "crux-I-09-v1"^^ . . . + . "contracts/crux-I-10-v1.yaml"^^ . "crux-I-10-v1"^^ . . . + . "contracts/crux-I-11-v1.yaml"^^ . "crux-I-11-v1"^^ . . . + . "contracts/crux-I-12-v1.yaml"^^ . "crux-I-12-v1"^^ . . . + . "contracts/crux-I-13-v1.yaml"^^ . "crux-I-13-v1"^^ . . . + . "contracts/crux-I-14-v1.yaml"^^ . "crux-I-14-v1"^^ . . . + . "contracts/crux-I-15-v1.yaml"^^ . "crux-I-15-v1"^^ . . . + . "contracts/crux-I-16-v1.yaml"^^ . "crux-I-16-v1"^^ . . . + . "contracts/crux-J-01-v1.yaml"^^ . "crux-J-01-v1"^^ . . . + . "contracts/crux-J-02-v1.yaml"^^ . "crux-J-02-v1"^^ . . . + . "contracts/crux-J-03-v1.yaml"^^ . "crux-J-03-v1"^^ . . . + . "contracts/crux-J-04-v1.yaml"^^ . "crux-J-04-v1"^^ . . . + . "contracts/crux-J-05-v1.yaml"^^ . "crux-J-05-v1"^^ . . . + . "contracts/crux-J-06-v1.yaml"^^ . "crux-J-06-v1"^^ . . . + . "contracts/crux-J-07-v1.yaml"^^ . "crux-J-07-v1"^^ . . . + . "contracts/crux-J-08-v1.yaml"^^ . "crux-J-08-v1"^^ . . . + . "contracts/crux-J-09-v1.yaml"^^ . "crux-J-09-v1"^^ . . . + . "contracts/crux-J-10-v1.yaml"^^ . "crux-J-10-v1"^^ . . . + . "contracts/crux-J-11-v1.yaml"^^ . "crux-J-11-v1"^^ . . . + . "contracts/crux-J-12-v1.yaml"^^ . "crux-J-12-v1"^^ . . . + . "contracts/crux-J-13-v1.yaml"^^ . "crux-J-13-v1"^^ . . . + . "contracts/crux-J-14-v1.yaml"^^ . "crux-J-14-v1"^^ . . . + . "contracts/crux-J-15-v1.yaml"^^ . "crux-J-15-v1"^^ . . . + . "contracts/crux-J-16-v1.yaml"^^ . "crux-J-16-v1"^^ . . . + . "contracts/crux-J-17-v1.yaml"^^ . "crux-J-17-v1"^^ . . . + . "contracts/crux-J-18-v1.yaml"^^ . "crux-J-18-v1"^^ . . . + . "contracts/crux-J-19-v1.yaml"^^ . "crux-J-19-v1"^^ . . . + . "contracts/crux-J-20-v1.yaml"^^ . "crux-J-20-v1"^^ . . . + . "contracts/crux-K-01-v1.yaml"^^ . "crux-K-01-v1"^^ . . . + . "contracts/crux-K-02-v1.yaml"^^ . "crux-K-02-v1"^^ . . . + . "contracts/crux-K-03-v1.yaml"^^ . "crux-K-03-v1"^^ . . . + . "contracts/crux-K-04-v1.yaml"^^ . "crux-K-04-v1"^^ . . . + . "contracts/crux-K-05-v1.yaml"^^ . "crux-K-05-v1"^^ . . . + . "contracts/crux-K-07-v1.yaml"^^ . "crux-K-07-v1"^^ . "kernel"^^ . . . + . "contracts/crux-K-08-v1.yaml"^^ . "crux-K-08-v1"^^ . "kernel"^^ . . . + . "contracts/crux-K-09-v1.yaml"^^ . "crux-K-09-v1"^^ . . . + . "contracts/crux-K-10-v1.yaml"^^ . "crux-K-10-v1"^^ . . . + . "contracts/crux-K-11-v1.yaml"^^ . "crux-K-11-v1"^^ . . . + . "contracts/crux-K-12-v1.yaml"^^ . "crux-K-12-v1"^^ . . . + . "contracts/crux-K-13-v1.yaml"^^ . "crux-K-13-v1"^^ . . . + . "contracts/crux-K-14-v1.yaml"^^ . "crux-K-14-v1"^^ . . . + . "contracts/crux-K-15-v1.yaml"^^ . "crux-K-15-v1"^^ . . . + . "contracts/crux-K-16-v1.yaml"^^ . "crux-K-16-v1"^^ . . . + . "contracts/crux-K-17-v1.yaml"^^ . "crux-K-17-v1"^^ . . . + . "contracts/crux-K-18-v1.yaml"^^ . "crux-K-18-v1"^^ . . . + . "contracts/crux-K-19-v1.yaml"^^ . "crux-K-19-v1"^^ . . . + . "contracts/crux-K-20-v1.yaml"^^ . "crux-K-20-v1"^^ . . . + . "contracts/crux-K-21-v1.yaml"^^ . "crux-K-21-v1"^^ . . . + . "contracts/crux-L-01-v1.yaml"^^ . "crux-L-01-v1"^^ . . . + . "contracts/crux-L-02-v1.yaml"^^ . "crux-L-02-v1"^^ . "kernel"^^ . . . + . "contracts/crux-L-03-v1.yaml"^^ . "crux-L-03-v1"^^ . . . + . "contracts/crux-L-04-v1.yaml"^^ . "crux-L-04-v1"^^ . . . + . "contracts/crux-L-05-v1.yaml"^^ . "crux-L-05-v1"^^ . . . + . "contracts/crux-L-06-v1.yaml"^^ . "crux-L-06-v1"^^ . . . + . "contracts/crux-L-07-v1.yaml"^^ . "crux-L-07-v1"^^ . . . + . "contracts/crux-L-08-v1.yaml"^^ . "crux-L-08-v1"^^ . . . + . "contracts/crux-L-09-v1.yaml"^^ . "crux-L-09-v1"^^ . . . + . "contracts/crux-L-10-v1.yaml"^^ . "crux-L-10-v1"^^ . . . + . "contracts/crux-L-11-v1.yaml"^^ . "crux-L-11-v1"^^ . . . + . "contracts/crux-L-12-v1.yaml"^^ . "crux-L-12-v1"^^ . . . + . "contracts/crux-L-13-v1.yaml"^^ . "crux-L-13-v1"^^ . . . + . "contracts/crux-L-14-v1.yaml"^^ . "crux-L-14-v1"^^ . . . + . "contracts/crux-L-15-v1.yaml"^^ . "crux-L-15-v1"^^ . . . + . "contracts/crux-M-01-v1.yaml"^^ . "crux-M-01-v1"^^ . . . + . "contracts/crux-M-02-v1.yaml"^^ . "crux-M-02-v1"^^ . . . + . "contracts/crux-M-04-v1.yaml"^^ . "crux-M-04-v1"^^ . . . + . "contracts/crux-M-05-v1.yaml"^^ . "crux-M-05-v1"^^ . . . + . "contracts/crux-M-06-v1.yaml"^^ . "crux-M-06-v1"^^ . . . + . "contracts/crux-M-07-v1.yaml"^^ . "crux-M-07-v1"^^ . . . + . "contracts/crux-M-08-v1.yaml"^^ . "crux-M-08-v1"^^ . . . + . "contracts/crux-M-09-v1.yaml"^^ . "crux-M-09-v1"^^ . . . + . "contracts/crux-M-10-v1.yaml"^^ . "crux-M-10-v1"^^ . . . + . "contracts/crux-N-01-v1.yaml"^^ . "crux-N-01-v1"^^ . . . + . "contracts/crux-N-02-v1.yaml"^^ . "crux-N-02-v1"^^ . . . + . "contracts/crux-N-03-v1.yaml"^^ . "crux-N-03-v1"^^ . . . + . "contracts/crux-N-04-v1.yaml"^^ . "crux-N-04-v1"^^ . . . + . "contracts/crux-N-05-v1.yaml"^^ . "crux-N-05-v1"^^ . . . + . "contracts/crux-N-06-v1.yaml"^^ . "crux-N-06-v1"^^ . . . + . "contracts/crux-N-07-v1.yaml"^^ . "crux-N-07-v1"^^ . . . + . "contracts/crux-N-08-v1.yaml"^^ . "crux-N-08-v1"^^ . . . + . "contracts/crux-N-09-v1.yaml"^^ . "crux-N-09-v1"^^ . . . + . "contracts/crux-N-10-v1.yaml"^^ . "crux-N-10-v1"^^ . . . + . "contracts/crux-N-11-v1.yaml"^^ . "crux-N-11-v1"^^ . . . + . "contracts/crux-N-12-v1.yaml"^^ . "crux-N-12-v1"^^ . . . + . "contracts/crux-N-13-v1.yaml"^^ . "crux-N-13-v1"^^ . . . + . "contracts/crux-N-14-v1.yaml"^^ . "crux-N-14-v1"^^ . . . + . "contracts/crux-N-15-v1.yaml"^^ . "crux-N-15-v1"^^ . . . + . "contracts/crux-N-16-v1.yaml"^^ . "crux-N-16-v1"^^ . . . + . "contracts/crux-N-17-v1.yaml"^^ . "crux-N-17-v1"^^ . . . + . "contracts/crux-O-01-v1.yaml"^^ . "crux-O-01-v1"^^ . . . + . "contracts/crux-O-02-v1.yaml"^^ . "crux-O-02-v1"^^ . . . + . "contracts/crux-O-03-v1.yaml"^^ . "crux-O-03-v1"^^ . . . + . "contracts/crux-O-04-v1.yaml"^^ . "crux-O-04-v1"^^ . . . + . "contracts/crux-O-05-v1.yaml"^^ . "crux-O-05-v1"^^ . . . + . "contracts/crux-O-06-v1.yaml"^^ . "crux-O-06-v1"^^ . . . + . "contracts/crux-O-07-v1.yaml"^^ . "crux-O-07-v1"^^ . . . + . "contracts/crux-O-08-v1.yaml"^^ . "crux-O-08-v1"^^ . . . + . "contracts/crux-O-09-v1.yaml"^^ . "crux-O-09-v1"^^ . . . + . "contracts/crux-O-10-v1.yaml"^^ . "crux-O-10-v1"^^ . . . + . "contracts/crux-O-11-v1.yaml"^^ . "crux-O-11-v1"^^ . . . + . "contracts/crux-O-12-v1.yaml"^^ . "crux-O-12-v1"^^ . . . + . "contracts/crux-O-13-v1.yaml"^^ . "crux-O-13-v1"^^ . . . + . "contracts/crux-O-14-v1.yaml"^^ . "crux-O-14-v1"^^ . . . + . "contracts/crux-O-15-v1.yaml"^^ . "crux-O-15-v1"^^ . . . + . "contracts/crux-O-16-v1.yaml"^^ . "crux-O-16-v1"^^ . . . + . "contracts/crux-O-17-v1.yaml"^^ . "crux-O-17-v1"^^ . . . + . "contracts/crux-O-18-v1.yaml"^^ . "crux-O-18-v1"^^ . . . + . "contracts/crux-O-19-v1.yaml"^^ . "crux-O-19-v1"^^ . . . + . "contracts/crux-O-20-v1.yaml"^^ . "crux-O-20-v1"^^ . . . + . "contracts/crux-O-21-v1.yaml"^^ . "crux-O-21-v1"^^ . . . + . "contracts/crux-O-22-v1.yaml"^^ . "crux-O-22-v1"^^ . . . + . "contracts/crux-O-23-v1.yaml"^^ . "crux-O-23-v1"^^ . . . + . "contracts/crux-O-24-v1.yaml"^^ . "crux-O-24-v1"^^ . . . + . "contracts/crux-competitive-research-ux-v1.yaml"^^ . "crux-competitive-research-ux-v1"^^ . . @@ -7519,6 +11209,20 @@ "contracts/crux-intake-metadata-domains-v1.yaml"^^ . "crux-intake-metadata-domains-v1"^^ . "pattern"^^ . + . + . + . + "crates/aprender-train/prepare_data.sh"^^ . + . + . + "crates/aprender-train/data/train.csv"^^ . + "csv"^^ . + "contracts/csv-train.yaml"^^ . + "csv-train"^^ . + "pattern"^^ . + "csv-train"^^ . + "active"^^ . + "1.0.0"^^ . . . "contracts/cublas-fp8-7b-determinism-v1.yaml"^^ . @@ -7531,6 +11235,7 @@ "pattern"^^ . . . + . "contracts/cuda-classify-training-v1.yaml"^^ . "contracts/entrenar/cuda-classify-training-v1.yaml"^^ . "cuda-classify-training-v1"^^ . @@ -7543,18 +11248,22 @@ "pattern"^^ . . . + . "contracts/cuda-graph-backward-v1.yaml"^^ . "cuda-graph-backward-v1"^^ . . . + . "contracts/cuda-graph-batched-inference-v1.yaml"^^ . "cuda-graph-batched-inference-v1"^^ . . . + . "contracts/entrenar/cuda-graph-training-step-v1.yaml"^^ . "cuda-graph-training-step-v1"^^ . . . + . "contracts/cuda-kernel-safety-v1.yaml"^^ . "cuda-kernel-safety-v1"^^ . . @@ -7569,27 +11278,32 @@ "pattern"^^ . . . + . "contracts/cuda-oxide-rope-parity-v1.yaml"^^ . "cuda-oxide-rope-parity-v1"^^ . "kernel"^^ . . . + . "contracts/cuda-q4k-frozen-teacher-v1.yaml"^^ . "cuda-q4k-frozen-teacher-v1"^^ . "cuda-q4k-frozen-teacher"^^ . "1.0.0"^^ . . . + . "contracts/trueno-gpu/cuda-unified-memory-allocator-v1.yaml"^^ . "cuda-unified-memory-allocator-v1"^^ . "cuda-unified-memory-allocator"^^ . "1.0.0"^^ . . . + . "contracts/forjar/dag-ordering-v1.yaml"^^ . "dag-ordering-v1"^^ . . . + . "contracts/alimentar/data-feed-v1.yaml"^^ . "data-feed-v1"^^ . . @@ -7601,29 +11315,35 @@ "1.0.0"^^ . . . + . "contracts/decy/decision-engine-v1.yaml"^^ . "decision-engine-v1"^^ . . . + . "contracts/decision-tree-v1.yaml"^^ . "decision-tree-v1"^^ . . . + . "contracts/decode-gpu-resident-sampling-v1.yaml"^^ . "decode-gpu-resident-sampling-v1"^^ . "FALSIFIED"^^ . . . + . "contracts/decode-hot-path-first-tokens-diagnostic-v1.yaml"^^ . "decode-hot-path-first-tokens-diagnostic-v1"^^ . "SHIPPED"^^ . . . + . "contracts/decode-hot-path-prefix-cache-diagnostic-v1.yaml"^^ . "decode-hot-path-prefix-cache-diagnostic-v1"^^ . "SHIPPED"^^ . . . + . "contracts/decode-hot-path-zero-syscalls-v1.yaml"^^ . "decode-hot-path-zero-syscalls-v1"^^ . . @@ -7633,36 +11353,43 @@ "model-family"^^ . . . + . "contracts/copia/delta-sync-v1.yaml"^^ . "delta-sync-v1"^^ . . . + . "contracts/dimension-independent-kernels-v1.yaml"^^ . "dimension-independent-kernels-v1"^^ . "dimension-independent-kernels"^^ . "1.0.0"^^ . . . + . "contracts/discriminant-analysis-v1.yaml"^^ . "discriminant-analysis-v1"^^ . . . + . "contracts/display-format-v1.yaml"^^ . "display-format-v1"^^ . . . + . "contracts/distill-per-position-kd-v1.yaml"^^ . "distill-per-position-kd-v1"^^ . "distill-per-position-kd"^^ . "1.0.0"^^ . . . + . "contracts/distill-pipeline-observability-v1.yaml"^^ . "distill-pipeline-observability-v1"^^ . "distill-pipeline-observability"^^ . "1.0.0"^^ . . . + . "contracts/distributed-training-v1.yaml"^^ . "contracts/entrenar/distributed-training-v1.yaml"^^ . "distributed-training-v1"^^ . @@ -7670,49 +11397,60 @@ "1.0.0"^^ . . . + . "contracts/repartir/distribution-v1.yaml"^^ . "distribution-v1"^^ . . . + . "contracts/document-integrity-v1.yaml"^^ . "document-integrity-v1"^^ . "kernel"^^ . . . + . "contracts/dogfood-runner-unification-v1.yaml"^^ . "dogfood-runner-unification-v1"^^ . . . + . "contracts/dpo-loss-v1.yaml"^^ . "dpo-loss-v1"^^ . . . + . "contracts/drift-detection-v1.yaml"^^ . "drift-detection-v1"^^ . . . + . "contracts/dropout-v1.yaml"^^ . "dropout-v1"^^ . . . + . "contracts/dry-penalty-repeat-len-v1.yaml"^^ . "dry-penalty-repeat-len-v1"^^ . "dry-penalty-repeat-len"^^ . "1.0.0"^^ . . . + . "contracts/embedding-algebra-v1.yaml"^^ . "embedding-algebra-v1"^^ . . . + . "contracts/embedding-lookup-v1.yaml"^^ . "embedding-lookup-v1"^^ . . . + . "contracts/encoder-forward-v1.yaml"^^ . "encoder-forward-v1"^^ . . . + . "contracts/bashrs/encoder-roundtrip-v1.yaml"^^ . "contracts/encoder-roundtrip-v1.yaml"^^ . "contracts/rmedia/encoder-roundtrip-v1.yaml"^^ . @@ -7721,45 +11459,54 @@ "1.0.0"^^ . . . + . "contracts/error-handling-v1.yaml"^^ . "contracts/pepita/error-handling-v1.yaml"^^ . "error-handling-v1"^^ . . . + . "contracts/eval-harness-humaneval-v1.yaml"^^ . "eval-harness-humaneval-v1"^^ . "FALSIFIED"^^ . . . + . "contracts/eval-passk-single-sample-v1.yaml"^^ . "eval-passk-single-sample-v1"^^ . "eval-passk-single-sample"^^ . "1.0.0"^^ . . . + . "contracts/eval-sharding-v1.yaml"^^ . "eval-sharding-v1"^^ . "ACTIVE"^^ . . . + . "contracts/forjar/event-rulebook-v1.yaml"^^ . "event-rulebook-v1"^^ . . . + . "contracts/forjar/execution-safety-v1.yaml"^^ . "execution-safety-v1"^^ . . . + . "contracts/export-user-metadata-roundtrip-v1.yaml"^^ . "export-user-metadata-roundtrip-v1"^^ . "export-user-metadata-roundtrip"^^ . "1.0.0"^^ . . . + . "contracts/f16-conversion-v1.yaml"^^ . "f16-conversion-v1"^^ . . . + . "contracts/f16-to-f32-subnormal-v1.yaml"^^ . "f16-to-f32-subnormal-v1"^^ . "f16-to-f32-subnormal"^^ . @@ -7791,6 +11538,7 @@ "pattern"^^ . . . + . "contracts/flash-attention-v1.yaml"^^ . "flash-attention-v1"^^ . . @@ -7800,48 +11548,59 @@ "pattern"^^ . . . + . "contracts/format-parity-v1.yaml"^^ . "format-parity-v1"^^ . . . + . "contracts/fp16-cublas-gemm-v1.yaml"^^ . "fp16-cublas-gemm-v1"^^ . "fp16-cublas-gemm"^^ . "1.0.0"^^ . . . + . "contracts/fp8-interchange-v1.yaml"^^ . "fp8-interchange-v1"^^ . . . + . "contracts/entrenar/fused-backward-gemm-v1.yaml"^^ . "fused-backward-gemm-v1"^^ . . . + . "contracts/fused-qkv-projection-v1.yaml"^^ . "fused-qkv-projection-v1"^^ . . . + . "contracts/garbage-oracle-v1.yaml"^^ . "garbage-oracle-v1"^^ . . . + . "contracts/gated-delta-net-v1.yaml"^^ . "gated-delta-net-v1"^^ . . . + . "contracts/gateway-contract-v1.yaml"^^ . "gateway-contract-v1"^^ . . . + . "contracts/gbm-v1.yaml"^^ . "gbm-v1"^^ . . . + . "contracts/gelu-kernel-v1.yaml"^^ . "gelu-kernel-v1"^^ . . . + . "contracts/gemm-backward-tiled-v1.yaml"^^ . "contracts/trueno-gpu/gemm-backward-tiled-v1.yaml"^^ . "gemm-backward-tiled-v1"^^ . @@ -7861,20 +11620,24 @@ "model-family"^^ . . . + . "contracts/ggml-type-v1.yaml"^^ . "ggml-type-v1"^^ . "ggml-type"^^ . "1.0.0"^^ . . . + . "contracts/gguf-cpu-cache-v1.yaml"^^ . "gguf-cpu-cache-v1"^^ . . . + . "contracts/gguf-format-safety-v1.yaml"^^ . "gguf-format-safety-v1"^^ . . . + . "contracts/gguf-kquant-element-size-v1.yaml"^^ . "gguf-kquant-element-size-v1"^^ . "gguf-kquant-element-size"^^ . @@ -7884,22 +11647,38 @@ "contracts/gguf-prompt-sensitivity-v1.yaml"^^ . "gguf-prompt-sensitivity-v1"^^ . "pattern"^^ . + . + . + . + . + . + "repo"^^ . + "contracts/github-entities-v1.yaml"^^ . + "github-entities-v1"^^ . + "pattern"^^ . + "github-entities-v1"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/glm-irls-link-derivative-v1.yaml"^^ . "glm-irls-link-derivative-v1"^^ . "glm-irls-link-derivative"^^ . "1.0.0"^^ . . . + . "contracts/glm-v1.yaml"^^ . "glm-v1"^^ . . . + . "contracts/gnn-v1.yaml"^^ . "gnn-v1"^^ . . . + . "contracts/golden-trace-v1.yaml"^^ . "contracts/renacer/golden-trace-v1.yaml"^^ . "golden-trace-v1"^^ . @@ -7912,6 +11691,7 @@ "model-family"^^ . . . + . "contracts/gpt2-bpe-decode-roundtrip-v1.yaml"^^ . "gpt2-bpe-decode-roundtrip-v1"^^ . "gpt2-bpe-decode-roundtrip"^^ . @@ -7928,6 +11708,7 @@ "model-family"^^ . . . + . "contracts/gpu-context-health-v1.yaml"^^ . "gpu-context-health-v1"^^ . . @@ -7937,11 +11718,13 @@ "pattern"^^ . . . + . "contracts/gpu-decode-profiling-v1.yaml"^^ . "contracts/rmedia/gpu-decode-profiling-v1.yaml"^^ . "gpu-decode-profiling-v1"^^ . . . + . "contracts/gpu-multi-backend-parity-v1.yaml"^^ . "gpu-multi-backend-parity-v1"^^ . . @@ -7953,20 +11736,24 @@ "1.5.0"^^ . . . + . "contracts/gpu-weight-residency-v1.yaml"^^ . "gpu-weight-residency-v1"^^ . . . + . "contracts/gqa-kernel-v1.yaml"^^ . "gqa-kernel-v1"^^ . . . + . "contracts/gqa-kv-dim-fail-closed-v1.yaml"^^ . "gqa-kv-dim-fail-closed-v1"^^ . "gqa-kv-dim-fail-closed"^^ . "1.0.0"^^ . . . + . "contracts/gradient-accumulation-mean-v1.yaml"^^ . "gradient-accumulation-mean-v1"^^ . "gradient-accumulation-mean"^^ . @@ -7978,55 +11765,67 @@ "model-family"^^ . . . + . "contracts/graph-centrality-v1.yaml"^^ . "graph-centrality-v1"^^ . . . + . "contracts/pmat/graph-index-v1.yaml"^^ . "graph-index-v1"^^ . . . + . "contracts/trueno-graph/graph-query-v1.yaml"^^ . "graph-query-v1"^^ . . . + . "contracts/hero-svg-v1.yaml"^^ . "hero-svg-v1"^^ . . . + . "contracts/aprender/http-api-v1.yaml"^^ . "contracts/batuta/http-api-v1.yaml"^^ . "contracts/http-api-v1.yaml"^^ . "http-api-v1"^^ . . . + . "contracts/rurl/http-client-v1.yaml"^^ . "http-client-v1"^^ . . . + . "contracts/hybrid-layer-dispatch-v1.yaml"^^ . "hybrid-layer-dispatch-v1"^^ . . . + . "contracts/ica-v1.yaml"^^ . "ica-v1"^^ . . . + . "contracts/ica-whitening-v1.yaml"^^ . "ica-whitening-v1"^^ . . . + . "contracts/incomplete-beta-correctness-v1.yaml"^^ . "incomplete-beta-correctness-v1"^^ . "incomplete-beta-correctness"^^ . "1.1.0"^^ . . . + . "contracts/inference-pipeline-v1.yaml"^^ . "contracts/realizar/inference-pipeline-v1.yaml"^^ . "inference-pipeline-v1"^^ . . . + . "contracts/int8-symmetric-quant-v1.yaml"^^ . "int8-symmetric-quant-v1"^^ . . @@ -8036,82 +11835,100 @@ "model-family"^^ . . . + . "contracts/isotonic-pav-flatness-v1.yaml"^^ . "isotonic-pav-flatness-v1"^^ . . . + . "contracts/iterator-v1.yaml"^^ . "iterator-v1"^^ . . . + . "contracts/kd-loss-forward-kl-v1.yaml"^^ . "kd-loss-forward-kl-v1"^^ . . . + . "contracts/aprender/kernel-fusion-v1.yaml"^^ . "contracts/kernel-fusion-v1.yaml"^^ . "kernel-fusion-v1"^^ . . . + . "contracts/kernel-launch-budget-v1.yaml"^^ . "kernel-launch-budget-v1"^^ . . . + . "contracts/kmeans-kernel-v1.yaml"^^ . "kmeans-kernel-v1"^^ . . . + . "contracts/knn-tie-smallest-label-v1.yaml"^^ . "knn-tie-smallest-label-v1"^^ . "knn-tie-smallest-label"^^ . "1.1.0"^^ . . . + . "contracts/kv-cache-equivalence-v1.yaml"^^ . "kv-cache-equivalence-v1"^^ . . . + . "contracts/kv-cache-sizing-v1.yaml"^^ . "kv-cache-sizing-v1"^^ . . . + . "contracts/lasso-elasticnet-alpha-v1.yaml"^^ . "lasso-elasticnet-alpha-v1"^^ . "lasso-elasticnet-alpha"^^ . "1.0.0"^^ . . . + . "contracts/aprender/layer-parity-v1.yaml"^^ . "contracts/layer-parity-v1.yaml"^^ . "layer-parity-v1"^^ . . . + . "contracts/layernorm-kernel-v1.yaml"^^ . "layernorm-kernel-v1"^^ . . . + . "contracts/lbfgs-kernel-v1.yaml"^^ . "lbfgs-kernel-v1"^^ . . . + . "contracts/learned-position-embedding-v1.yaml"^^ . "learned-position-embedding-v1"^^ . . . + . "contracts/linear-bias-init-v1.yaml"^^ . "linear-bias-init-v1"^^ . "linear-bias-init"^^ . "1.0.0"^^ . . . + . "contracts/linear-models-v1.yaml"^^ . "linear-models-v1"^^ . . . + . "contracts/linear-probe-classifier-v1.yaml"^^ . "linear-probe-classifier-v1"^^ . . . + . "contracts/linear-projection-v1.yaml"^^ . "linear-projection-v1"^^ . . @@ -8128,12 +11945,14 @@ "1.6.0"^^ . . . + . "contracts/lora-adapter-merge-cli-v1.yaml"^^ . "lora-adapter-merge-cli-v1"^^ . "lora-adapter-merge-cli"^^ . "1.0.0"^^ . . . + . "contracts/lora-adapter-scale-roundtrip-v1.yaml"^^ . "lora-adapter-scale-roundtrip-v1"^^ . "lora-adapter-scale-roundtrip"^^ . @@ -8147,14 +11966,17 @@ "1"^^ . . . + . "contracts/lora-algebra-v1.yaml"^^ . "lora-algebra-v1"^^ . . . + . "contracts/lora-dropout-placement-v1.yaml"^^ . "lora-dropout-placement-v1"^^ . . . + . "contracts/entrenar/lora-gradient-flow-v1.yaml"^^ . "contracts/lora-gradient-flow-v1.yaml"^^ . "lora-gradient-flow-v1"^^ . @@ -8162,16 +11984,19 @@ "1.1.0"^^ . . . + . "contracts/lora-merge-forward-equivalence-v1.yaml"^^ . "lora-merge-forward-equivalence-v1"^^ . . . + . "contracts/lora-merge-peft-layout-v1.yaml"^^ . "lora-merge-peft-layout-v1"^^ . "lora-merge-peft-layout"^^ . "1.0.0"^^ . . . + . "contracts/entrenar/lora-target-selection-v1.yaml"^^ . "contracts/lora-target-selection-v1.yaml"^^ . "lora-target-selection-v1"^^ . @@ -8179,10 +12004,12 @@ "1.0.0"^^ . . . + . "contracts/loss-functions-v1.yaml"^^ . "loss-functions-v1"^^ . . . + . "contracts/machine-specific-paths-v1.yaml"^^ . "machine-specific-paths-v1"^^ . . @@ -8192,27 +12019,33 @@ "model-family"^^ . . . + . "contracts/matmul-kernel-v1.yaml"^^ . "matmul-kernel-v1"^^ . . . + . "contracts/pmcp/mcp-protocol-sdk-v1.yaml"^^ . "mcp-protocol-sdk-v1"^^ . . . + . "contracts/pmat/mcp-protocol-v1.yaml"^^ . "mcp-protocol-v1"^^ . . . + . "contracts/aprender/mcp-tool-schema-v1.yaml"^^ . "contracts/mcp-tool-schema-v1.yaml"^^ . "mcp-tool-schema-v1"^^ . . . + . "contracts/rmedia/media-pipeline-v1.yaml"^^ . "media-pipeline-v1"^^ . . . + . "contracts/depyler/memory-safety-v1.yaml"^^ . "contracts/memory-safety-v1.yaml"^^ . "contracts/pmat/memory-safety-v1.yaml"^^ . @@ -8221,36 +12054,44 @@ "1.0.0"^^ . . . + . "contracts/metaheuristics-v1.yaml"^^ . "metaheuristics-v1"^^ . . . + . "contracts/metrics-classification-v1.yaml"^^ . "metrics-classification-v1"^^ . . . + . "contracts/metrics-clustering-v1.yaml"^^ . "metrics-clustering-v1"^^ . . . + . "contracts/metrics-macro-average-v1.yaml"^^ . "metrics-macro-average-v1"^^ . "metrics-macro-average"^^ . "1.0.0"^^ . . . + . "contracts/metrics-ranking-v1.yaml"^^ . "metrics-ranking-v1"^^ . . . + . "contracts/metrics-regression-v1.yaml"^^ . "metrics-regression-v1"^^ . . . + . "contracts/metrics-sklearn-eps-parity-v1.yaml"^^ . "metrics-sklearn-eps-parity-v1"^^ . . . + . "contracts/mirostat-bits-v1.yaml"^^ . "mirostat-bits-v1"^^ . "mirostat-bits"^^ . @@ -8264,52 +12105,76 @@ . . "gguf"^^ . + "L2"^^ . "contracts/model-capability-ladder-v1.yaml"^^ . "model-capability-ladder-v1"^^ . "pattern"^^ . . . + . "contracts/model-config-algebra-v1.yaml"^^ . "model-config-algebra-v1"^^ . . . + . "contracts/model-family-parity-v1.yaml"^^ . "model-family-parity-v1"^^ . "model-family-parity"^^ . "1.0.0"^^ . . . + . "contracts/aprender/model-format-conversion-v1.yaml"^^ . "contracts/model-format-conversion-v1.yaml"^^ . "model-format-conversion-v1"^^ . . . + . "contracts/model-metadata-bounds-v1.yaml"^^ . "model-metadata-bounds-v1"^^ . . . + . "contracts/apr-model-qa-playbook/model-qa-v1.yaml"^^ . "model-qa-v1"^^ . + . + . + . + . + . + . + "crates/aprender-core/tests/fixtures/setfit/slice_model.apr"^^ . + "apr-model"^^ . + "contracts/model-setfit-slice.yaml"^^ . + "model-setfit-slice"^^ . + "pattern"^^ . + "model-setfit-slice"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/moe-expert-dispatch-v1.yaml"^^ . "moe-expert-dispatch-v1"^^ . "moe-expert-dispatch"^^ . "1.0.0"^^ . . . + . "contracts/moe-load-balance-loss-v1.yaml"^^ . "moe-load-balance-loss-v1"^^ . "moe-load-balance-loss"^^ . "1.0.0"^^ . . . + . "contracts/moe-router-v1.yaml"^^ . "moe-router-v1"^^ . "moe-router"^^ . "1.0.0"^^ . . . + . "contracts/zenith/monitor-metrics-v1.yaml"^^ . "monitor-metrics-v1"^^ . . @@ -8319,19 +12184,23 @@ "model-family"^^ . . . + . "contracts/apr-model-qa-playbook/mqs-scoring-v1.yaml"^^ . "contracts/mqs-scoring-v1.yaml"^^ . "mqs-scoring-v1"^^ . . . + . "contracts/multinomial-head-v1.yaml"^^ . "multinomial-head-v1"^^ . . . + . "contracts/naive-bayes-v1.yaml"^^ . "naive-bayes-v1"^^ . . . + . "contracts/pepita/namespace-isolation-v1.yaml"^^ . "namespace-isolation-v1"^^ . . @@ -8341,10 +12210,12 @@ "model-family"^^ . . . + . "contracts/trueno/neon-dequant-v1.yaml"^^ . "neon-dequant-v1"^^ . . . + . "contracts/nf4-backward-tensor-core-gemm-v1.yaml"^^ . "contracts/trueno/nf4-backward-tensor-core-gemm-v1.yaml"^^ . "nf4-backward-tensor-core-gemm-v1"^^ . @@ -8352,22 +12223,27 @@ "1.0.0"^^ . . . + . "contracts/nf4-fused-gate-up-swiglu-v1.yaml"^^ . "nf4-fused-gate-up-swiglu-v1"^^ . . . + . "contracts/nf4-fused-qkv-gemm-v1.yaml"^^ . "nf4-fused-qkv-gemm-v1"^^ . . . + . "contracts/nf4-fused-rmsnorm-gemv-v1.yaml"^^ . "nf4-fused-rmsnorm-gemv-v1"^^ . . . + . "contracts/nf4-tensor-core-gemm-v1.yaml"^^ . "nf4-tensor-core-gemm-v1"^^ . . . + . "contracts/nn-softmax-dim-v1.yaml"^^ . "nn-softmax-dim-v1"^^ . "nn-softmax-dim"^^ . @@ -8388,6 +12264,7 @@ "1"^^ . . . + . "contracts/forjar/oci-manifest-v1.yaml"^^ . "oci-manifest-v1"^^ . . @@ -8397,8 +12274,23 @@ "model-family"^^ . . . + . "contracts/online-softmax-v1.yaml"^^ . "online-softmax-v1"^^ . + . + . + . + . + . + . + . + "gguf"^^ . + "contracts/ont-capability-cells-v1.yaml"^^ . + "ont-capability-cells-v1"^^ . + "pattern"^^ . + "ont-capability-cells"^^ . + "active"^^ . + "1.0.0"^^ . . . . @@ -8409,6 +12301,28 @@ "ont-code-symbols"^^ . "active"^^ . "1.0.0"^^ . + . + . + "contracts/ont-consistency-v1.yaml"^^ . + "ont-consistency-v1"^^ . + "pattern"^^ . + . + . + . + . + "contracts/ont-docs-corpus-v1.yaml"^^ . + "ont-docs-corpus-v1"^^ . + "pattern"^^ . + . + . + . + . + "contracts/ont-evidence-v1.yaml"^^ . + "ont-evidence-v1"^^ . + "pattern"^^ . + "ont-evidence"^^ . + "active"^^ . + "1.0.0"^^ . . . . @@ -8419,6 +12333,20 @@ "ont-extract-json"^^ . "active"^^ . "1.0.0"^^ . + . + . + . + . + . + . + . + "kernel"^^ . + "contracts/ont-kernel-receipts-v1.yaml"^^ . + "ont-kernel-receipts-v1"^^ . + "pattern"^^ . + "ont-kernel-receipts"^^ . + "active"^^ . + "1.0.0"^^ . . . . @@ -8433,6 +12361,11 @@ "ont-model-receipts"^^ . "active"^^ . "1.0.0"^^ . + . + . + "contracts/ont-refines-v1.yaml"^^ . + "ont-refines-v1"^^ . + "pattern"^^ . . . . @@ -8443,6 +12376,18 @@ "ont-relations"^^ . "active"^^ . "1.0.0"^^ . + . + . + . + . + . + . + . + "contracts/"^^ . + "pv-contract"^^ . + "contracts/ont-self-v1.yaml"^^ . + "ont-self-v1"^^ . + "kernel"^^ . . . . @@ -8451,6 +12396,7 @@ . "contracts/"^^ . "pv-contract"^^ . + "L1"^^ . "contracts/ont-shapes-v1.yaml"^^ . "ont-shapes-v1"^^ . "pattern"^^ . @@ -8478,11 +12424,13 @@ "1.0.0"^^ . . . + . "contracts/ont-verdict-lattice-v1.yaml"^^ . "ont-verdict-lattice-v1"^^ . "kernel"^^ . . . + . "contracts/openai-serve-sampling-determinism-v1.yaml"^^ . "openai-serve-sampling-determinism-v1"^^ . "openai-serve-sampling-determinism"^^ . @@ -8499,16 +12447,19 @@ "model-family"^^ . . . + . "contracts/optimization-v1.yaml"^^ . "optimization-v1"^^ . . . + . "contracts/orchestrate-env-test-hermeticity-v1.yaml"^^ . "orchestrate-env-test-hermeticity-v1"^^ . "orchestrate-env-test-hermeticity"^^ . "1.0.0"^^ . . . + . "contracts/orchestrate-macos-portability-v1.yaml"^^ . "orchestrate-macos-portability-v1"^^ . "orchestrate-macos-portability"^^ . @@ -8522,23 +12473,28 @@ "1"^^ . . . + . "contracts/pacha/package-resolve-v1.yaml"^^ . "package-resolve-v1"^^ . . . + . "contracts/paged-attention-v1.yaml"^^ . "paged-attention-v1"^^ . . . + . "contracts/paged-kv-cache-v1.yaml"^^ . "paged-kv-cache-v1"^^ . . . + . "contracts/pagerank-kernel-v1.yaml"^^ . "contracts/trueno-graph/pagerank-kernel-v1.yaml"^^ . "pagerank-kernel-v1"^^ . . . + . "contracts/entrenar/parity-profiling-system-v1.yaml"^^ . "parity-profiling-system-v1"^^ . . @@ -8568,6 +12524,7 @@ "2.0.0"^^ . . . + . "contracts/bashrs/parser-soundness-v1.yaml"^^ . "contracts/parser-soundness-v1.yaml"^^ . "contracts/ruchy/parser-soundness-v1.yaml"^^ . @@ -8576,14 +12533,17 @@ "1.0.0"^^ . . . + . "contracts/pca-v1.yaml"^^ . "pca-v1"^^ . . . + . "contracts/entrenar/per-operation-training-profiling-v1.yaml"^^ . "per-operation-training-profiling-v1"^^ . . . + . "contracts/performance-grading-v1.yaml"^^ . "performance-grading-v1"^^ . . @@ -8593,6 +12553,7 @@ "model-family"^^ . . . + . "contracts/pipeline-cache-v1.yaml"^^ . "contracts/trueno/pipeline-cache-v1.yaml"^^ . "pipeline-cache-v1"^^ . @@ -8600,10 +12561,12 @@ "1.0.0"^^ . . . + . "contracts/forjar/plugin-lifecycle-v1.yaml"^^ . "plugin-lifecycle-v1"^^ . . . + . "contracts/pmat/pmat-work-lifecycle-v1.yaml"^^ . "pmat-work-lifecycle-v1"^^ . . @@ -8635,11 +12598,13 @@ "pattern"^^ . . . + . "contracts/pr-review-skill-v2.yaml"^^ . "pr-review-skill-v2"^^ . "kernel"^^ . . . + . "contracts/preprocessing-normalization-v1.yaml"^^ . "preprocessing-normalization-v1"^^ . . @@ -8659,15 +12624,18 @@ "1.0.0"^^ . . . + . "contracts/profile-graph-vs-per-op-methodology-v1.yaml"^^ . "profile-graph-vs-per-op-methodology-v1"^^ . "PROPOSED"^^ . . . + . "contracts/projected-gradient-armijo-v1.yaml"^^ . "projected-gradient-armijo-v1"^^ . . . + . "contracts/probar/property-testing-v1.yaml"^^ . "property-testing-v1"^^ . . @@ -8677,29 +12645,35 @@ "pattern"^^ . . . + . "contracts/batuta/provider-routing-v1.yaml"^^ . "provider-routing-v1"^^ . . . + . "contracts/prune-sparsity-correctness-v1.yaml"^^ . "prune-sparsity-correctness-v1"^^ . "prune-sparsity-correctness"^^ . "1.0.0"^^ . . . + . "contracts/trueno/ptx-codegen-safety-v1.yaml"^^ . "ptx-codegen-safety-v1"^^ . . . + . "contracts/ptx-target-parity-v1.yaml"^^ . "ptx-target-parity-v1"^^ . . . + . "contracts/publish-manifest-v1.yaml"^^ . "publish-manifest-v1"^^ . "DRAFT"^^ . . . + . "contracts/publish-workspace-v1.yaml"^^ . "publish-workspace-v1"^^ . . @@ -8720,50 +12694,68 @@ "contracts/pv-version-identity-v1.yaml"^^ . "pv-version-identity-v1"^^ . "pattern"^^ . + . + . + . + "contracts/pvl-lint-ratchets-v1.yaml"^^ . + "pvl-lint-ratchets-v1"^^ . + "pattern"^^ . + "pvl-lint-ratchets"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/q2k-dequant-parity-v1.yaml"^^ . "q2k-dequant-parity-v1"^^ . "q2k-dequant-parity"^^ . "1.0.0"^^ . . . + . "contracts/q3k-dequant-correctness-v1.yaml"^^ . "q3k-dequant-correctness-v1"^^ . "q3k-dequant-correctness"^^ . "1.0.0"^^ . . . + . "contracts/q3k-dequant-v1.yaml"^^ . "q3k-dequant-v1"^^ . "q3k-dequant"^^ . "1.0.0"^^ . . . + . "contracts/q4k-interleaved-scale-min-v1.yaml"^^ . "q4k-interleaved-scale-min-v1"^^ . "q4k-interleaved-scale-min"^^ . "1.0.0"^^ . . . + . "contracts/q4k-q6k-superblock-v1.yaml"^^ . "q4k-q6k-superblock-v1"^^ . . . + . "contracts/q5k-dequant-correctness-v1.yaml"^^ . "q5k-dequant-correctness-v1"^^ . "q5k-dequant-correctness"^^ . "1.0.0"^^ . . . + . "contracts/qk-norm-apr-loader-v1.yaml"^^ . "qk-norm-apr-loader-v1"^^ . . . + . "contracts/qk-norm-v1.yaml"^^ . "qk-norm-v1"^^ . . . + . "contracts/entrenar/qlora-hyperparameters-v1.yaml"^^ . "contracts/qlora-hyperparameters-v1.yaml"^^ . "qlora-hyperparameters-v1"^^ . @@ -8776,30 +12768,36 @@ "pattern"^^ . . . + . "contracts/certeza/quality-validation-v1.yaml"^^ . "quality-validation-v1"^^ . . . + . "contracts/quant-roundtrip-fidelity-v1.yaml"^^ . "quant-roundtrip-fidelity-v1"^^ . "quant-roundtrip-fidelity"^^ . "1.0.0"^^ . . . + . "contracts/quant-solve-f16-round-v1.yaml"^^ . "quant-solve-f16-round-v1"^^ . "quant-solve-f16-round"^^ . "1.1.0"^^ . . . + . "contracts/quantization-ordering-v1.yaml"^^ . "quantization-ordering-v1"^^ . . . + . "contracts/trueno/quantize-dequant-roundtrip-v1.yaml"^^ . "quantize-dequant-roundtrip-v1"^^ . . . + . "contracts/aprender/quantized-dot-product-v1.yaml"^^ . "contracts/quantized-dot-product-v1.yaml"^^ . "quantized-dot-product-v1"^^ . @@ -8815,14 +12813,17 @@ "model-family"^^ . . . + . "contracts/qwen2-e2e-verification-v1.yaml"^^ . "qwen2-e2e-verification-v1"^^ . . . + . "contracts/qwen2-shapes-v1.yaml"^^ . "qwen2-shapes-v1"^^ . . . + . "contracts/aprender/qwen2-weight-loading-v1.yaml"^^ . "contracts/qwen2-weight-loading-v1.yaml"^^ . "qwen2-weight-loading-v1"^^ . @@ -8833,10 +12834,12 @@ "model-family"^^ . . . + . "contracts/qwen3-e2e-verification-v1.yaml"^^ . "qwen3-e2e-verification-v1"^^ . . . + . "contracts/qwen3-moe-forward-gpu-v1.yaml"^^ . "qwen3-moe-forward-gpu-v1"^^ . "kernel"^^ . @@ -8845,6 +12848,7 @@ "1.7.2"^^ . . . + . "contracts/qwen3-moe-forward-v1.yaml"^^ . "qwen3-moe-forward-v1"^^ . "qwen3-moe-forward"^^ . @@ -8852,30 +12856,35 @@ "1.5.0"^^ . . . + . "contracts/qwen3-moe-repetition-penalty-v1.yaml"^^ . "qwen3-moe-repetition-penalty-v1"^^ . "qwen3-moe-repetition-penalty"^^ . "1.1.0"^^ . . . + . "contracts/qwen3-moe-sampling-v1.yaml"^^ . "qwen3-moe-sampling-v1"^^ . "qwen3-moe-sampling"^^ . "1.1.0"^^ . . . + . "contracts/qwen3-moe-serve-dispatch-v1.yaml"^^ . "qwen3-moe-serve-dispatch-v1"^^ . "qwen3-moe-serve-dispatch"^^ . "1.2.0"^^ . . . + . "contracts/qwen3-moe-streaming-sse-v1.yaml"^^ . "qwen3-moe-streaming-sse-v1"^^ . "qwen3-moe-streaming-sse"^^ . "1.0.0"^^ . . . + . "contracts/qwen3-shapes-v1.yaml"^^ . "qwen3-shapes-v1"^^ . . @@ -8887,20 +12896,24 @@ "1.0.0"^^ . . . + . "contracts/qwen35-e2e-verification-v1.yaml"^^ . "qwen35-e2e-verification-v1"^^ . . . + . "contracts/qwen35-hybrid-forward-v1.yaml"^^ . "qwen35-hybrid-forward-v1"^^ . . . + . "contracts/qwen35-hybrid-serve-dispatch-v1.yaml"^^ . "qwen35-hybrid-serve-dispatch-v1"^^ . "qwen35-hybrid-serve-dispatch"^^ . "1.0.0"^^ . . . + . "contracts/qwen35-shapes-v1.yaml"^^ . "qwen35-shapes-v1"^^ . . @@ -8910,28 +12923,34 @@ "model-family"^^ . . . + . "contracts/qwen3moe-e2e-verification-v1.yaml"^^ . "qwen3moe-e2e-verification-v1"^^ . . . + . "contracts/qwen3moe-rope-theta-v1.yaml"^^ . "qwen3moe-rope-theta-v1"^^ . "qwen3moe-rope-theta"^^ . "1.0.0"^^ . . . + . "contracts/qwen3moe-shapes-v1.yaml"^^ . "qwen3moe-shapes-v1"^^ . . . + . "contracts/trueno-rag/rag-pipeline-v1.yaml"^^ . "rag-pipeline-v1"^^ . . . + . "contracts/random-forest-v1.yaml"^^ . "random-forest-v1"^^ . . . + . "contracts/ratatui-migration-v1.yaml"^^ . "ratatui-migration-v1"^^ . . @@ -8944,12 +12963,27 @@ "contracts/readme-claims-v1.yaml"^^ . "readme-claims-v1"^^ . "pattern"^^ . + . + . + . + . + . + "README.md"^^ . + "readme"^^ . + "contracts/readme-root.yaml"^^ . + "readme-root"^^ . + "pattern"^^ . + "readme-root"^^ . + "active"^^ . + "1.0.0"^^ . . . + . "contracts/forjar/recipe-determinism-v1.yaml"^^ . "recipe-determinism-v1"^^ . . . + . "contracts/reduce-lr-plateau-v1.yaml"^^ . "reduce-lr-plateau-v1"^^ . "reduce-lr-plateau-patience-strictly-greater"^^ . @@ -8959,6 +12993,7 @@ . "evidence/verbs/refusals.json"^^ . "json"^^ . + "L1"^^ . "contracts/refusal-receipt-v1.yaml"^^ . "refusal-receipt-v1"^^ . "pattern"^^ . @@ -8967,6 +13002,7 @@ "1.0.0"^^ . . . + . "contracts/pacha/registry-integrity-v1.yaml"^^ . "registry-integrity-v1"^^ . . @@ -8976,6 +13012,7 @@ . . "release-evidence"^^ . + "L1"^^ . "contracts/release-readiness-v1.yaml"^^ . "release-readiness-v1"^^ . "pattern"^^ . @@ -8989,30 +13026,62 @@ "pattern"^^ . . . + . "contracts/trueno-viz/render-primitives-v1.yaml"^^ . "render-primitives-v1"^^ . . . + . "contracts/repo-filesystem-v1.yaml"^^ . "repo-filesystem-v1"^^ . . . + . "contracts/trueno-rag/retrieval-quality-v1.yaml"^^ . "retrieval-quality-v1"^^ . + . + . + "contracts/review-corpus-contamination-v1.yaml"^^ . + "review-corpus-contamination-v1"^^ . + "schema"^^ . + . + . + "contracts/review-corpus-v1.yaml"^^ . + "review-corpus-v1"^^ . + "schema"^^ . + . + . + "contracts/review-experiment-receipt-v1.yaml"^^ . + "review-experiment-receipt-v1"^^ . + "schema"^^ . + . + . + "contracts/rex-cell-admission-v1.yaml"^^ . + "rex-cell-admission-v1"^^ . + "schema"^^ . + . + . + "contracts/rex-prereg-v1.yaml"^^ . + "rex-prereg-v1"^^ . + "schema"^^ . . . + . "contracts/rmsnorm-kernel-v1.yaml"^^ . "rmsnorm-kernel-v1"^^ . . . + . "contracts/roofline-model-v1.yaml"^^ . "roofline-model-v1"^^ . . . + . "contracts/rope-extrapolation-v1.yaml"^^ . "rope-extrapolation-v1"^^ . . . + . "contracts/rope-kernel-v1.yaml"^^ . "rope-kernel-v1"^^ . . @@ -9022,110 +13091,133 @@ "model-family"^^ . . . + . "contracts/safetensors-bf16-round-v1.yaml"^^ . "safetensors-bf16-round-v1"^^ . "safetensors-bf16-round"^^ . "1.0.0"^^ . . . + . "contracts/safetensors-cpu-dispatch-v1.yaml"^^ . "safetensors-cpu-dispatch-v1"^^ . . . + . "contracts/safetensors-f16-round-v1.yaml"^^ . "safetensors-f16-round-v1"^^ . "safetensors-f16-round"^^ . "1.0.0"^^ . . . + . "contracts/safetensors-format-safety-v1.yaml"^^ . "safetensors-format-safety-v1"^^ . . . + . "contracts/bashrs/safety-classifier-v1.yaml"^^ . "safety-classifier-v1"^^ . . . + . "contracts/sampling-algorithms-v1.yaml"^^ . "sampling-algorithms-v1"^^ . . . + . "contracts/forjar/sandbox-isolation-v1.yaml"^^ . "sandbox-isolation-v1"^^ . . . + . "contracts/pmat/score-composite-v1.yaml"^^ . "score-composite-v1"^^ . . . + . "contracts/forjar/secret-provider-v1.yaml"^^ . "secret-provider-v1"^^ . . . + . "contracts/depyler/semantic-equivalence-v1.yaml"^^ . "semantic-equivalence-v1"^^ . . . + . "contracts/alimentar/serialization-v1.yaml"^^ . "contracts/serialization-v1.yaml"^^ . "serialization-v1"^^ . . . + . "contracts/serve-batched-gpu-gqa-dispatch-v1.yaml"^^ . "serve-batched-gpu-gqa-dispatch-v1"^^ . "serve-batched-gpu-gqa-dispatch"^^ . "1.0.0"^^ . . . + . "contracts/batuta/session-v1.yaml"^^ . "session-v1"^^ . . . + . "contracts/setfit-apr-v1.yaml"^^ . "setfit-apr-v1"^^ . "kernel"^^ . . . + . "contracts/setfit-encoder-conformance-v1.yaml"^^ . "setfit-encoder-conformance-v1"^^ . . . + . "contracts/sgd-momentum-lrsched-v1.yaml"^^ . "sgd-momentum-lrsched-v1"^^ . "sgd-momentum-lrsched"^^ . "1.0.0"^^ . . . + . "contracts/shannon-entropy-v1.yaml"^^ . "shannon-entropy-v1"^^ . . . + . "contracts/sharded-gguf-merge-v1.yaml"^^ . "sharded-gguf-merge-v1"^^ . "sharded-gguf-merge"^^ . "1.0.0"^^ . . . + . "contracts/sharded-gguf-pull-v1.yaml"^^ . "sharded-gguf-pull-v1"^^ . "sharded-gguf-pull"^^ . "1.0.0"^^ . . . + . "contracts/pzsh/shell-execution-v1.yaml"^^ . "shell-execution-v1"^^ . . . + . "contracts/silhouette-singleton-v1.yaml"^^ . "silhouette-singleton-v1"^^ . "silhouette-singleton-zero"^^ . "1.0.0"^^ . . . + . "contracts/silu-kernel-v1.yaml"^^ . "silu-kernel-v1"^^ . . . + . "contracts/simd-scalar-parity-v1.yaml"^^ . "contracts/trueno/simd-scalar-parity-v1.yaml"^^ . "simd-scalar-parity-v1"^^ . @@ -9133,40 +13225,49 @@ "1.0.0"^^ . . . + . "contracts/simular/simulation-determinism-v1.yaml"^^ . "simulation-determinism-v1"^^ . . . + . "contracts/simular/simulation-step-v1.yaml"^^ . "simulation-step-v1"^^ . . . + . "contracts/sliding-window-attention-v1.yaml"^^ . "sliding-window-attention-v1"^^ . . . + . "contracts/softmax-kernel-v1.yaml"^^ . "softmax-kernel-v1"^^ . . . + . "contracts/entrenar/sovereign-tensor-v1.yaml"^^ . "sovereign-tensor-v1"^^ . . . + . "contracts/sparse-spmv-v1.yaml"^^ . "sparse-spmv-v1"^^ . "sparse-spmv"^^ . "1.0.0"^^ . . . + . "contracts/special-tokens-registry-v1.yaml"^^ . "special-tokens-registry-v1"^^ . . . + . "contracts/speculative-decoding-v1.yaml"^^ . "speculative-decoding-v1"^^ . . . + . "contracts/ssm-kernel-v1.yaml"^^ . "ssm-kernel-v1"^^ . . @@ -9181,32 +13282,39 @@ "model-family"^^ . . . + . "contracts/pmat/state-machine-v1.yaml"^^ . "state-machine-v1"^^ . . . + . "contracts/forjar/store-cas-v1.yaml"^^ . "store-cas-v1"^^ . . . + . "contracts/stratified-kfold-balance-v1.yaml"^^ . "stratified-kfold-balance-v1"^^ . "stratified-kfold-balance"^^ . "1.0.0"^^ . . . + . "contracts/streaming-tpot-v1.yaml"^^ . "streaming-tpot-v1"^^ . . . + . "contracts/svc-rbf-v1.yaml"^^ . "svc-rbf-v1"^^ . . . + . "contracts/svm-v1.yaml"^^ . "svm-v1"^^ . . . + . "contracts/swiglu-kernel-v1.yaml"^^ . "swiglu-kernel-v1"^^ . . @@ -9216,10 +13324,12 @@ "pattern"^^ . . . + . "contracts/forjar/task-pipeline-v1.yaml"^^ . "task-pipeline-v1"^^ . . . + . "contracts/pmat/tdg-scoring-v1.yaml"^^ . "contracts/tdg-scoring-v1.yaml"^^ . "tdg-scoring-v1"^^ . @@ -9227,31 +13337,38 @@ "1.0.0"^^ . . . + . "contracts/tensor-inventory-v1.yaml"^^ . "tensor-inventory-v1"^^ . . . + . "contracts/aprender/tensor-layout-v1.yaml"^^ . "contracts/tensor-layout-v1.yaml"^^ . "tensor-layout-v1"^^ . . . + . "contracts/tensor-names-v1.yaml"^^ . "tensor-names-v1"^^ . . . + . "contracts/entrenar/tensor-rc-data-v1.yaml"^^ . "tensor-rc-data-v1"^^ . . . + . "contracts/tensor-shape-flow-v1.yaml"^^ . "tensor-shape-flow-v1"^^ . . . + . "contracts/tensor-transpose-roundtrip-v1.yaml"^^ . "tensor-transpose-roundtrip-v1"^^ . . . + . "contracts/tfidf-l2-norm-v1.yaml"^^ . "tfidf-l2-norm-v1"^^ . "tfidf-l2-norm"^^ . @@ -9268,10 +13385,12 @@ "pattern"^^ . . . + . "contracts/tied-embeddings-v1.yaml"^^ . "tied-embeddings-v1"^^ . . . + . "contracts/trueno/tiled-matmul-shader-v1.yaml"^^ . "tiled-matmul-shader-v1"^^ . . @@ -9283,11 +13402,13 @@ "1.2.0"^^ . . . + . "contracts/aprender/tokenizer-loading-v1.yaml"^^ . "contracts/tokenizer-loading-v1.yaml"^^ . "tokenizer-loading-v1"^^ . . . + . "contracts/batuta/tokenizer-v1.yaml"^^ . "contracts/tokenizer-v1.yaml"^^ . "tokenizer-v1"^^ . @@ -9295,6 +13416,7 @@ "1.1.0"^^ . . . + . "contracts/tokenizer-vocab-v1.yaml"^^ . "tokenizer-vocab-v1"^^ . . @@ -9309,11 +13431,13 @@ "pattern"^^ . . . + . "contracts/trace-ffn-sub-block-v1.yaml"^^ . "trace-ffn-sub-block-v1"^^ . "PROPOSED"^^ . . . + . "contracts/renacer/trace-integrity-v1.yaml"^^ . "trace-integrity-v1"^^ . . @@ -9323,6 +13447,7 @@ "pattern"^^ . . . + . "contracts/pmat/tracing-observability-v1.yaml"^^ . "contracts/tracing-observability-v1.yaml"^^ . "tracing-observability-v1"^^ . @@ -9330,12 +13455,14 @@ "1.0.0"^^ . . . + . "contracts/train-test-split-ceil-v1.yaml"^^ . "train-test-split-ceil-v1"^^ . "train-test-split-ceil"^^ . "1.0.0"^^ . . . + . "contracts/trainer-grad-clip-v1.yaml"^^ . "trainer-grad-clip-v1"^^ . "trainer-grad-clip"^^ . @@ -9349,15 +13476,18 @@ "1.5.0"^^ . . . + . "contracts/aprender/training-loop-v1.yaml"^^ . "contracts/training-loop-v1.yaml"^^ . "training-loop-v1"^^ . . . + . "contracts/entrenar/training-step-profiling-v1.yaml"^^ . "training-step-profiling-v1"^^ . . . + . "contracts/probar/training-step-scorecard-v1.yaml"^^ . "training-step-scorecard-v1"^^ . . @@ -9369,10 +13499,12 @@ "1"^^ . . . + . "contracts/decy/transpile-pipeline-v1.yaml"^^ . "transpile-pipeline-v1"^^ . . . + . "contracts/ruchy/transpile-soundness-v1.yaml"^^ . "transpile-soundness-v1"^^ . . @@ -9382,78 +13514,95 @@ "pattern"^^ . . . + . "contracts/transpose-kernel-v1.yaml"^^ . "transpose-kernel-v1"^^ . . . + . "contracts/tree-feature-importances-mdi-v1.yaml"^^ . "tree-feature-importances-mdi-v1"^^ . "tree-feature-importances-mdi"^^ . "1.0.0"^^ . . . + . "contracts/trueno-f16-rne-v1.yaml"^^ . "trueno-f16-rne-v1"^^ . "trueno-f16-rne"^^ . "1.0.0"^^ . . . + . "contracts/ttest-exact-pvalue-v1.yaml"^^ . "ttest-exact-pvalue-v1"^^ . "ttest-exact-pvalue"^^ . "1.0.0"^^ . . . + . "contracts/presentar/tui-lifecycle-v1.yaml"^^ . "tui-lifecycle-v1"^^ . . . + . "contracts/presentar/tui-panels-v1.yaml"^^ . "tui-panels-v1"^^ . . . + . "contracts/tui-rendering-ux-v1.yaml"^^ . "tui-rendering-ux-v1"^^ . . . + . "contracts/presentar/tui-rendering-v1.yaml"^^ . "tui-rendering-v1"^^ . . . + . "contracts/depyler/type-preservation-v1.yaml"^^ . "type-preservation-v1"^^ . . . + . "contracts/unified-specs-v1.yaml"^^ . "unified-specs-v1"^^ . . . + . "contracts/validated-tensor-v1.yaml"^^ . "validated-tensor-v1"^^ . . . + . "contracts/verificar/verification-engine-v1.yaml"^^ . "verification-engine-v1"^^ . . . + . "contracts/trueno-viz/visualization-render-v1.yaml"^^ . "visualization-render-v1"^^ . . . + . "contracts/ward-linkage-v1.yaml"^^ . "ward-linkage-v1"^^ . "ward-linkage"^^ . "1.0.0"^^ . . . + . "contracts/wasmtime-upgrade-v1.yaml"^^ . "wasmtime-upgrade-v1"^^ . . . + . "contracts/entrenar/wgpu-production-training-v1.yaml"^^ . "wgpu-production-training-v1"^^ . . . + . "contracts/entrenar/wgpu-resident-weights-v1.yaml"^^ . "wgpu-resident-weights-v1"^^ . . @@ -9463,22 +13612,7570 @@ "model-family"^^ . . . + . "contracts/pmat/work-dbc-v1.yaml"^^ . "contracts/work-dbc-v1.yaml"^^ . "work-dbc-v1"^^ . "pattern"^^ . . . + . "contracts/xtc-sampling-correctness-v1.yaml"^^ . "xtc-sampling-correctness-v1"^^ . "xtc-sampling-correctness"^^ . "1.0.0"^^ . . . + . "contracts/yarn-rope-original-base-v1.yaml"^^ . "yarn-rope-original-base-v1"^^ . "yarn-rope-original-base"^^ . "1.0.0"^^ . + . + "feature1"^^ . + "feature2"^^ . + "label"^^ . + "int"^^ . + "feature1,feature2,label"^^ . + "crates/aprender-train/prepare_data.sh"^^ . + "100"^^ . + "e92a2460d310818411a48fdb440336024818d1d8fac5f2da853427ba9888bd52"^^ . + . + . + "apr-cli"^^ . + "crates/apr-cli/examples/gpu_chat_inference.rs"^^ . + "false"^^ . + "gpu_chat_inference"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "apr-cli"^^ . + "crates/apr-cli/examples/ptx_parity_validation.rs"^^ . + "false"^^ . + "ptx_parity_validation"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "apr-cli"^^ . + "crates/apr-cli/examples/serve_with_tracing.rs"^^ . + "true"^^ . + "serve_with_tracing"^^ . + "false"^^ . + . + . + "apr-cli"^^ . + "crates/apr-cli/examples/tool_calling_demo.rs"^^ . + "true"^^ . + "tool_calling_demo"^^ . + "false"^^ . + . + . + "aprender-cbtop"^^ . + "crates/aprender-cbtop/examples/adaptive_ml_demo.rs"^^ . + "true"^^ . + "adaptive_ml_demo"^^ . + "false"^^ . + . + . + "aprender-cbtop"^^ . + "crates/aprender-cbtop/examples/federated_metrics_demo.rs"^^ . + "true"^^ . + "federated_metrics_demo"^^ . + "false"^^ . + . + . + "aprender-cbtop"^^ . + "crates/aprender-cbtop/examples/incremental_snapshot_demo.rs"^^ . + "true"^^ . + "incremental_snapshot_demo"^^ . + "false"^^ . + . + . + "aprender-cbtop"^^ . + "crates/aprender-cbtop/examples/predictive_scheduler_demo.rs"^^ . + "true"^^ . + "predictive_scheduler_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/activation_functions.rs"^^ . + "true"^^ . + "activation_functions"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/b4_fault_simulation.rs"^^ . + "true"^^ . + "b4_fault_simulation"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/backend_detection.rs"^^ . + "true"^^ . + "backend_detection"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/bench_profiling_primitives.rs"^^ . + "true"^^ . + "bench_profiling_primitives"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/benchmark_matrix_suite.rs"^^ . + "false"^^ . + "benchmark_matrix_suite"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/benchmark_matvec.rs"^^ . + "true"^^ . + "benchmark_matvec"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/benchmark_matvec_parallel.rs"^^ . + "true"^^ . + "benchmark_matvec_parallel"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/benchmark_parallel.rs"^^ . + "true"^^ . + "benchmark_parallel"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/blis_benchmark.rs"^^ . + "true"^^ . + "blis_benchmark"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/brick_profiler_v2.rs"^^ . + "true"^^ . + "brick_profiler_v2"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/design_by_contract.rs"^^ . + "true"^^ . + "design_by_contract"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/execution_graph.rs"^^ . + "true"^^ . + "execution_graph"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/gpu_batch_demo.rs"^^ . + "true"^^ . + "gpu_batch_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/gpu_monitor_demo.rs"^^ . + "true"^^ . + "gpu_monitor_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/gpu_tiled_reduction.rs"^^ . + "true"^^ . + "gpu_tiled_reduction"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/hash_demo.rs"^^ . + "true"^^ . + "hash_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/inference_demo.rs"^^ . + "false"^^ . + "inference_demo"^^ . + "llama"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/matrix_operations.rs"^^ . + "true"^^ . + "matrix_operations"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/ml_similarity.rs"^^ . + "true"^^ . + "ml_similarity"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/ml_tuner_demo.rs"^^ . + "false"^^ . + "ml_tuner_demo"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/ml_tuner_evolution.rs"^^ . + "true"^^ . + "ml_tuner_evolution"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/model_tracing.rs"^^ . + "true"^^ . + "model_tracing"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/perf_tui.rs"^^ . + "false"^^ . + "perf_tui"^^ . + "gpt2"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/performance_demo.rs"^^ . + "true"^^ . + "performance_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/profile_vocab.rs"^^ . + "true"^^ . + "profile_vocab"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/quickstart.rs"^^ . + "true"^^ . + "quickstart"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/regression_test.rs"^^ . + "true"^^ . + "regression_test"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/simd_comparison.rs"^^ . + "true"^^ . + "simd_comparison"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/simd_softmax_quant.rs"^^ . + "true"^^ . + "simd_softmax_quant"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/symmetric_eigen.rs"^^ . + "true"^^ . + "symmetric_eigen"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/tile_profiler_demo.rs"^^ . + "true"^^ . + "tile_profiler_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/tiled_reduction_demo.rs"^^ . + "true"^^ . + "tiled_reduction_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/tiling_demo.rs"^^ . + "true"^^ . + "tiling_demo"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/tuner_usage.rs"^^ . + "true"^^ . + "tuner_usage"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/vocab_bench.rs"^^ . + "true"^^ . + "vocab_bench"^^ . + "false"^^ . + . + . + "aprender-compute"^^ . + "crates/aprender-compute/examples/wgpu_backward_demo.rs"^^ . + "true"^^ . + "wgpu_backward_demo"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/audit.rs"^^ . + "true"^^ . + "audit"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/codegen.rs"^^ . + "true"^^ . + "codegen"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/coq.rs"^^ . + "true"^^ . + "coq"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/coverage.rs"^^ . + "true"^^ . + "coverage"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/cross_project_query.rs"^^ . + "true"^^ . + "cross_project_query"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/design_by_contract.rs"^^ . + "false"^^ . + "design_by_contract"^^ . + "llama"^^ . + "mistral"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/doc_integrity.rs"^^ . + "true"^^ . + "doc_integrity"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/explain.rs"^^ . + "true"^^ . + "explain"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/extract_pytorch.rs"^^ . + "true"^^ . + "extract_pytorch"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/flux.rs"^^ . + "true"^^ . + "flux"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/fuzz.rs"^^ . + "true"^^ . + "fuzz"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/infrastructure_contracts.rs"^^ . + "true"^^ . + "infrastructure_contracts"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/invariants.rs"^^ . + "true"^^ . + "invariants"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/lean_codegen.rs"^^ . + "true"^^ . + "lean_codegen"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/lean_proofs.rs"^^ . + "true"^^ . + "lean_proofs"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/lean_status.rs"^^ . + "true"^^ . + "lean_status"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/lint.rs"^^ . + "true"^^ . + "lint"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/mirai.rs"^^ . + "true"^^ . + "mirai"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/pipeline.rs"^^ . + "true"^^ . + "pipeline"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/proof_status.rs"^^ . + "true"^^ . + "proof_status"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/query.rs"^^ . + "true"^^ . + "query"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/scaffold.rs"^^ . + "true"^^ . + "scaffold"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/scaffold_generation.rs"^^ . + "true"^^ . + "scaffold_generation"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/score.rs"^^ . + "true"^^ . + "score"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/score_contracts.rs"^^ . + "true"^^ . + "score_contracts"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/tla.rs"^^ . + "true"^^ . + "tla"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/validate.rs"^^ . + "true"^^ . + "validate"^^ . + "false"^^ . + . + . + "aprender-contracts"^^ . + "crates/aprender-contracts/examples/validate_contracts.rs"^^ . + "true"^^ . + "validate_contracts"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/aco_tsp.rs"^^ . + "true"^^ . + "aco_tsp"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/admm_optimization.rs"^^ . + "true"^^ . + "admm_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/advanced_merge.rs"^^ . + "true"^^ . + "advanced_merge"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_cache.rs"^^ . + "true"^^ . + "apr_cache"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_checkpoint_lifecycle.rs"^^ . + "false"^^ . + "apr_checkpoint_lifecycle"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_cli_commands.rs"^^ . + "false"^^ . + "apr_cli_commands"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_embed.rs"^^ . + "true"^^ . + "apr_embed"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_inspection.rs"^^ . + "true"^^ . + "apr_inspection"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_loading_modes.rs"^^ . + "true"^^ . + "apr_loading_modes"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_scoring.rs"^^ . + "true"^^ . + "apr_scoring"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/apr_with_metadata.rs"^^ . + "true"^^ . + "apr_with_metadata"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/automl_clustering.rs"^^ . + "true"^^ . + "automl_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/batch_optimization.rs"^^ . + "true"^^ . + "batch_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bayesian_blocks_histogram.rs"^^ . + "true"^^ . + "bayesian_blocks_histogram"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bayesian_linear_regression.rs"^^ . + "true"^^ . + "bayesian_linear_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bayesian_logistic_regression.rs"^^ . + "true"^^ . + "bayesian_logistic_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bench_bpe.rs"^^ . + "true"^^ . + "bench_bpe"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bench_comparison.rs"^^ . + "false"^^ . + "bench_comparison"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/beta_binomial_inference.rs"^^ . + "true"^^ . + "beta_binomial_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/boston_housing.rs"^^ . + "true"^^ . + "boston_housing"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/bundle_trace_demo.rs"^^ . + "true"^^ . + "bundle_trace_demo"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch01_hello_apr.rs"^^ . + "true"^^ . + "ch01_hello_apr"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch02_tensors.rs"^^ . + "true"^^ . + "ch02_tensors"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch03_apr_format.rs"^^ . + "true"^^ . + "ch03_apr_format"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch04_supervised.rs"^^ . + "true"^^ . + "ch04_supervised"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch05_unsupervised.rs"^^ . + "true"^^ . + "ch05_unsupervised"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch06_ensembles.rs"^^ . + "true"^^ . + "ch06_ensembles"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch07_model_selection.rs"^^ . + "true"^^ . + "ch07_model_selection"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch08_transformer.rs"^^ . + "false"^^ . + "ch08_transformer"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch09_inference.rs"^^ . + "true"^^ . + "ch09_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch10_training.rs"^^ . + "true"^^ . + "ch10_training"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch11_formats.rs"^^ . + "true"^^ . + "ch11_formats"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch12_serving.rs"^^ . + "true"^^ . + "ch12_serving"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch13_profiling.rs"^^ . + "true"^^ . + "ch13_profiling"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch14_contracts.rs"^^ . + "true"^^ . + "ch14_contracts"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch15_orchestrate.rs"^^ . + "true"^^ . + "ch15_orchestrate"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch16_timeseries.rs"^^ . + "true"^^ . + "ch16_timeseries"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch17_bayesian.rs"^^ . + "true"^^ . + "ch17_bayesian"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch18_graphs.rs"^^ . + "true"^^ . + "ch18_graphs"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch19_text.rs"^^ . + "true"^^ . + "ch19_text"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch20_rag.rs"^^ . + "true"^^ . + "ch20_rag"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch21_vs_candle.rs"^^ . + "false"^^ . + "ch21_vs_candle"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch22_vs_llamacpp.rs"^^ . + "false"^^ . + "ch22_vs_llamacpp"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch23_training_bench.rs"^^ . + "false"^^ . + "ch23_training_bench"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch24_switch_pytorch.rs"^^ . + "true"^^ . + "ch24_switch_pytorch"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch25_switch_ollama.rs"^^ . + "false"^^ . + "ch25_switch_ollama"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch26_switch_ndarray.rs"^^ . + "true"^^ . + "ch26_switch_ndarray"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/ch27_switch_unsloth.rs"^^ . + "false"^^ . + "ch27_switch_unsloth"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/chat_template.rs"^^ . + "false"^^ . + "chat_template"^^ . + "llama"^^ . + "mistral"^^ . + "phi"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/classification_training.rs"^^ . + "true"^^ . + "classification_training"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/code_analysis.rs"^^ . + "true"^^ . + "code_analysis"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/community_detection.rs"^^ . + "true"^^ . + "community_detection"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/constrained_optimization.rs"^^ . + "true"^^ . + "constrained_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/continual_pretraining.rs"^^ . + "true"^^ . + "continual_pretraining"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/conv_layout_dogfood.rs"^^ . + "true"^^ . + "conv_layout_dogfood"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/convex_optimization.rs"^^ . + "true"^^ . + "convex_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/create_test_apr.rs"^^ . + "true"^^ . + "create_test_apr"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/create_test_transformer_apr.rs"^^ . + "false"^^ . + "create_test_transformer_apr"^^ . + "llama"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/cross_validation.rs"^^ . + "true"^^ . + "cross_validation"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/cuda_backend.rs"^^ . + "true"^^ . + "cuda_backend"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/dam_merge.rs"^^ . + "true"^^ . + "dam_merge"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/data_preprocessing_scalers.rs"^^ . + "true"^^ . + "data_preprocessing_scalers"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/data_quality_pipeline.rs"^^ . + "true"^^ . + "data_quality_pipeline"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/dataframe_basics.rs"^^ . + "true"^^ . + "dataframe_basics"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/dbscan_clustering.rs"^^ . + "true"^^ . + "dbscan_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/decision_tree_iris.rs"^^ . + "true"^^ . + "decision_tree_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/decision_tree_regression.rs"^^ . + "true"^^ . + "decision_tree_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/descriptive_statistics.rs"^^ . + "true"^^ . + "descriptive_statistics"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/design_by_contract.rs"^^ . + "true"^^ . + "design_by_contract"^^ . + "qwen3.5"^^ . + "true"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/dirichlet_multinomial_inference.rs"^^ . + "true"^^ . + "dirichlet_multinomial_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/distillation_advanced.rs"^^ . + "true"^^ . + "distillation_advanced"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/dpo_preference.rs"^^ . + "true"^^ . + "dpo_preference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/eval_harness.rs"^^ . + "true"^^ . + "eval_harness"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/evolutionary_merge.rs"^^ . + "true"^^ . + "evolutionary_merge"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/gamma_poisson_inference.rs"^^ . + "true"^^ . + "gamma_poisson_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/gbm_iris.rs"^^ . + "true"^^ . + "gbm_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/gmm_clustering.rs"^^ . + "true"^^ . + "gmm_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/gpu_fallback_dogfood.rs"^^ . + "false"^^ . + "gpu_fallback_dogfood"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/graph_algorithms_comprehensive.rs"^^ . + "true"^^ . + "graph_algorithms_comprehensive"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/graph_social_network.rs"^^ . + "true"^^ . + "graph_social_network"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/grid_search_tuning.rs"^^ . + "true"^^ . + "grid_search_tuning"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/hex_forensics.rs"^^ . + "true"^^ . + "hex_forensics"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/hierarchical_clustering.rs"^^ . + "true"^^ . + "hierarchical_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/iris_clustering.rs"^^ . + "true"^^ . + "iris_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/isolation_forest_anomaly.rs"^^ . + "true"^^ . + "isolation_forest_anomaly"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/knn_iris.rs"^^ . + "true"^^ . + "knn_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/layout_guard_probe.rs"^^ . + "true"^^ . + "layout_guard_probe"^^ . + "qwen2"^^ . + "qwen3"^^ . + "qwen3.5"^^ . + "true"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/lof_anomaly.rs"^^ . + "true"^^ . + "lof_anomaly"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/logic_family_tree.rs"^^ . + "true"^^ . + "logic_family_tree"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/logistic_regression.rs"^^ . + "true"^^ . + "logistic_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/lottery_ticket_pruning.rs"^^ . + "true"^^ . + "lottery_ticket_pruning"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/market_basket_apriori.rs"^^ . + "true"^^ . + "market_basket_apriori"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/mem_test.rs"^^ . + "false"^^ . + "mem_test"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/metaheuristics_optimization.rs"^^ . + "true"^^ . + "metaheuristics_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/mixture_of_experts.rs"^^ . + "true"^^ . + "mixture_of_experts"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/model_merge_strategies.rs"^^ . + "true"^^ . + "model_merge_strategies"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/model_serialization.rs"^^ . + "true"^^ . + "model_serialization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/model_zoo.rs"^^ . + "true"^^ . + "model_zoo"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/moe_construction.rs"^^ . + "true"^^ . + "moe_construction"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/naive_bayes_iris.rs"^^ . + "true"^^ . + "naive_bayes_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/negative_binomial_glm.rs"^^ . + "true"^^ . + "negative_binomial_glm"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/neural_network_training.rs"^^ . + "true"^^ . + "neural_network_training"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/nlp_advanced.rs"^^ . + "true"^^ . + "nlp_advanced"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/normal_inverse_gamma_inference.rs"^^ . + "true"^^ . + "normal_inverse_gamma_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/online_learning.rs"^^ . + "true"^^ . + "online_learning"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/optimizer_demo.rs"^^ . + "true"^^ . + "optimizer_demo"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/pca_iris.rs"^^ . + "true"^^ . + "pca_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/per_layer_merge.rs"^^ . + "false"^^ . + "per_layer_merge"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/phi_hf_import.rs"^^ . + "false"^^ . + "phi_hf_import"^^ . + "llama"^^ . + "phi"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/pii_filtering.rs"^^ . + "true"^^ . + "pii_filtering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/pipeline_verification.rs"^^ . + "true"^^ . + "pipeline_verification"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/poka_yoke_validation.rs"^^ . + "true"^^ . + "poka_yoke_validation"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/predator_prey_optimization.rs"^^ . + "true"^^ . + "predator_prey_optimization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/pruning_magnitude.rs"^^ . + "true"^^ . + "pruning_magnitude"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/publish_shell_safety.rs"^^ . + "false"^^ . + "publish_shell_safety"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qa_chat.rs"^^ . + "false"^^ . + "qa_chat"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qa_falsify.rs"^^ . + "false"^^ . + "qa_falsify"^^ . + "gpt2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qa_run.rs"^^ . + "false"^^ . + "qa_run"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qa_serve.rs"^^ . + "false"^^ . + "qa_serve"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qa_verify.rs"^^ . + "true"^^ . + "qa_verify"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qwen_apr_native.rs"^^ . + "false"^^ . + "qwen_apr_native"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/qwen_chat.rs"^^ . + "false"^^ . + "qwen_chat"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/random_forest_iris.rs"^^ . + "true"^^ . + "random_forest_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/random_forest_regression.rs"^^ . + "true"^^ . + "random_forest_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/recommend_content.rs"^^ . + "true"^^ . + "recommend_content"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/regularized_regression.rs"^^ . + "true"^^ . + "regularized_regression"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/rlvr.rs"^^ . + "true"^^ . + "rlvr"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/rosetta_stone.rs"^^ . + "true"^^ . + "rosetta_stone"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/shell_encryption_demo.rs"^^ . + "true"^^ . + "shell_encryption_demo"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/shell_model_format.rs"^^ . + "true"^^ . + "shell_model_format"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/shell_safety_inference.rs"^^ . + "true"^^ . + "shell_safety_inference"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/shell_safety_training.rs"^^ . + "true"^^ . + "shell_safety_training"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/showcase_benchmark.rs"^^ . + "false"^^ . + "showcase_benchmark"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/sovereign_offline.rs"^^ . + "false"^^ . + "sovereign_offline"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/sovereign_stack.rs"^^ . + "true"^^ . + "sovereign_stack"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/spectral_clustering.rs"^^ . + "true"^^ . + "spectral_clustering"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/svm_iris.rs"^^ . + "true"^^ . + "svm_iris"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/synthetic_data_generation.rs"^^ . + "true"^^ . + "synthetic_data_generation"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/tabu_tsp.rs"^^ . + "true"^^ . + "tabu_tsp"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/text_classification.rs"^^ . + "true"^^ . + "text_classification"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/text_preprocessing.rs"^^ . + "true"^^ . + "text_preprocessing"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/time_series_forecasting.rs"^^ . + "true"^^ . + "time_series_forecasting"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/tokenizer_surgery.rs"^^ . + "true"^^ . + "tokenizer_surgery"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/topic_sentiment_analysis.rs"^^ . + "true"^^ . + "topic_sentiment_analysis"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/trueno_compute_integration.rs"^^ . + "true"^^ . + "trueno_compute_integration"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/tsne_visualization.rs"^^ . + "true"^^ . + "tsne_visualization"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/validated_tensors.rs"^^ . + "true"^^ . + "validated_tensors"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/whisper_transcribe.rs"^^ . + "true"^^ . + "whisper_transcribe"^^ . + "false"^^ . + . + . + "aprender-core"^^ . + "crates/aprender-core/examples/xor_training.rs"^^ . + "true"^^ . + "xor_training"^^ . + "false"^^ . + . + . + "aprender-cuda-edge"^^ . + "crates/aprender-cuda-edge/examples/falsification_report_demo.rs"^^ . + "true"^^ . + "falsification_report_demo"^^ . + "false"^^ . + . + . + "aprender-cuda-edge"^^ . + "crates/aprender-cuda-edge/examples/null_fuzzer_demo.rs"^^ . + "true"^^ . + "null_fuzzer_demo"^^ . + "false"^^ . + . + . + "aprender-cuda-edge"^^ . + "crates/aprender-cuda-edge/examples/ptx_verifier_demo.rs"^^ . + "true"^^ . + "ptx_verifier_demo"^^ . + "false"^^ . + . + . + "aprender-cuda-edge"^^ . + "crates/aprender-cuda-edge/examples/quant_oracle_demo.rs"^^ . + "true"^^ . + "quant_oracle_demo"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/basic_loading.rs"^^ . + "true"^^ . + "basic_loading"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/cli_batch_commands.rs"^^ . + "true"^^ . + "cli_batch_commands"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/dataloader_batching.rs"^^ . + "true"^^ . + "dataloader_batching"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/doctest_extraction.rs"^^ . + "true"^^ . + "doctest_extraction"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/drift_detection.rs"^^ . + "true"^^ . + "drift_detection"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/federated_split.rs"^^ . + "true"^^ . + "federated_split"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/generate_fixtures.rs"^^ . + "true"^^ . + "generate_fixtures"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/hub_publishing.rs"^^ . + "true"^^ . + "hub_publishing"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/prose_detection.rs"^^ . + "true"^^ . + "prose_detection"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/quality_check.rs"^^ . + "true"^^ . + "quality_check"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/registry_publish.rs"^^ . + "true"^^ . + "registry_publish"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/repl_commands.rs"^^ . + "true"^^ . + "repl_commands"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/repl_completer.rs"^^ . + "true"^^ . + "repl_completer"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/repl_display_config.rs"^^ . + "true"^^ . + "repl_display_config"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/repl_health_status.rs"^^ . + "true"^^ . + "repl_health_status"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/repl_session.rs"^^ . + "true"^^ . + "repl_session"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/streaming_large.rs"^^ . + "true"^^ . + "streaming_large"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/transforms_pipeline.rs"^^ . + "true"^^ . + "transforms_pipeline"^^ . + "false"^^ . + . + . + "aprender-data"^^ . + "crates/aprender-data/examples/tui_viewer.rs"^^ . + "true"^^ . + "tui_viewer"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/backend_selection.rs"^^ . + "true"^^ . + "backend_selection"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/basic_usage.rs"^^ . + "true"^^ . + "basic_usage"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/benchmark_shootout.rs"^^ . + "true"^^ . + "benchmark_shootout"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/complete_pipeline.rs"^^ . + "true"^^ . + "complete_pipeline"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/compressed_kv.rs"^^ . + "true"^^ . + "compressed_kv"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/experiment_tracking.rs"^^ . + "true"^^ . + "experiment_tracking"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/gaming_leaderboards.rs"^^ . + "true"^^ . + "gaming_leaderboards"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/gpu_aggregations.rs"^^ . + "true"^^ . + "gpu_aggregations"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/gpu_sales_analytics.rs"^^ . + "true"^^ . + "gpu_sales_analytics"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/kv_store.rs"^^ . + "true"^^ . + "kv_store"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/market_crashes.rs"^^ . + "true"^^ . + "market_crashes"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/simd_acceleration.rs"^^ . + "true"^^ . + "simd_acceleration"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/sql_query_interface.rs"^^ . + "true"^^ . + "sql_query_interface"^^ . + "false"^^ . + . + . + "aprender-db"^^ . + "crates/aprender-db/examples/topk_selection.rs"^^ . + "true"^^ . + "topk_selection"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/checkpoint_example.rs"^^ . + "true"^^ . + "checkpoint_example"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/gpu_detect.rs"^^ . + "true"^^ . + "gpu_detect"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/hello_repartir.rs"^^ . + "true"^^ . + "hello_repartir"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/pubsub_example.rs"^^ . + "true"^^ . + "pubsub_example"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/pushpull_example.rs"^^ . + "true"^^ . + "pushpull_example"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/tensor_example.rs"^^ . + "true"^^ . + "tensor_example"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/test_mac_worker.rs"^^ . + "true"^^ . + "test_mac_worker"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/tls_example.rs"^^ . + "true"^^ . + "tls_example"^^ . + "false"^^ . + . + . + "aprender-distribute"^^ . + "crates/aprender-distribute/examples/v1_1_showcase.rs"^^ . + "true"^^ . + "v1_1_showcase"^^ . + "false"^^ . + . + . + "aprender-explain"^^ . + "crates/aprender-explain/examples/analyze_realizar.rs"^^ . + "true"^^ . + "analyze_realizar"^^ . + "false"^^ . + . + . + "aprender-explain"^^ . + "crates/aprender-explain/examples/deep_bug_hunt.rs"^^ . + "true"^^ . + "deep_bug_hunt"^^ . + "false"^^ . + . + . + "aprender-explain"^^ . + "crates/aprender-explain/examples/ptx_inspector.rs"^^ . + "true"^^ . + "ptx_inspector"^^ . + "false"^^ . + . + . + "aprender-fft"^^ . + "crates/aprender-fft/examples/fft_demo.rs"^^ . + "true"^^ . + "fft_demo"^^ . + "false"^^ . + . + . + "aprender-graph"^^ . + "crates/aprender-graph/examples/comprehensive_demo.rs"^^ . + "true"^^ . + "comprehensive_demo"^^ . + "false"^^ . + . + . + "aprender-graph"^^ . + "crates/aprender-graph/examples/graph_algorithms.rs"^^ . + "true"^^ . + "graph_algorithms"^^ . + "false"^^ . + . + . + "aprender-graph"^^ . + "crates/aprender-graph/examples/paging_demo.rs"^^ . + "true"^^ . + "paging_demo"^^ . + "false"^^ . + . + . + "aprender-graph"^^ . + "crates/aprender-graph/examples/simple_graph.rs"^^ . + "true"^^ . + "simple_graph"^^ . + "false"^^ . + . + . + "aprender-image"^^ . + "crates/aprender-image/examples/image_demo.rs"^^ . + "true"^^ . + "image_demo"^^ . + "false"^^ . + . + . + "aprender-monte-carlo"^^ . + "crates/aprender-monte-carlo/examples/aprender-monte-carlo.rs"^^ . + "true"^^ . + "aprender-monte-carlo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_contracts.rs"^^ . + "true"^^ . + "agent_contracts"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_demo.rs"^^ . + "false"^^ . + "agent_demo"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_guard.rs"^^ . + "true"^^ . + "agent_guard"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_memory.rs"^^ . + "false"^^ . + "agent_memory"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_pool.rs"^^ . + "true"^^ . + "agent_pool"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_routing.rs"^^ . + "true"^^ . + "agent_routing"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/agent_signing.rs"^^ . + "false"^^ . + "agent_signing"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/backend_selection.rs"^^ . + "true"^^ . + "backend_selection"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/bug_hunter_demo.rs"^^ . + "true"^^ . + "bug_hunter_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/content_demo.rs"^^ . + "true"^^ . + "content_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/custom_plugin.rs"^^ . + "true"^^ . + "custom_plugin"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/design_by_contract.rs"^^ . + "true"^^ . + "design_by_contract"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/full_transpilation.rs"^^ . + "true"^^ . + "full_transpilation"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/graph_tui_demo.rs"^^ . + "true"^^ . + "graph_tui_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/hf_catalog_demo.rs"^^ . + "true"^^ . + "hf_catalog_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/int8_rescore_demo.rs"^^ . + "true"^^ . + "int8_rescore_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/mcp_demo.rs"^^ . + "false"^^ . + "mcp_demo"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/moe_routing.rs"^^ . + "true"^^ . + "moe_routing"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/multi_machine_demo.rs"^^ . + "true"^^ . + "multi_machine_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/numpy_conversion.rs"^^ . + "true"^^ . + "numpy_conversion"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/oracle_demo.rs"^^ . + "true"^^ . + "oracle_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/oracle_local_demo.rs"^^ . + "true"^^ . + "oracle_local_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/parf_analysis.rs"^^ . + "true"^^ . + "parf_analysis"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/pepita_kernel_demo.rs"^^ . + "true"^^ . + "pepita_kernel_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/pipeline_demo.rs"^^ . + "true"^^ . + "pipeline_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/playbook_demo.rs"^^ . + "true"^^ . + "playbook_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/pmat_query_demo.rs"^^ . + "true"^^ . + "pmat_query_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/publish_status_demo.rs"^^ . + "true"^^ . + "publish_status_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/pytorch_conversion.rs"^^ . + "false"^^ . + "pytorch_conversion"^^ . + "gpt2"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/rag_oracle_demo.rs"^^ . + "true"^^ . + "rag_oracle_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/rag_profiling_demo.rs"^^ . + "true"^^ . + "rag_profiling_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/repartir_distributed.rs"^^ . + "true"^^ . + "repartir_distributed"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/serve_demo.rs"^^ . + "false"^^ . + "serve_demo"^^ . + "llama"^^ . + "mistral"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/sklearn_conversion.rs"^^ . + "true"^^ . + "sklearn_conversion"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/sovereign_stack_e2e.rs"^^ . + "false"^^ . + "sovereign_stack_e2e"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/stack_comply_demo.rs"^^ . + "true"^^ . + "stack_comply_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/stack_diagnostics_demo.rs"^^ . + "true"^^ . + "stack_diagnostics_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/stack_dogfood.rs"^^ . + "true"^^ . + "stack_dogfood"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/stack_graph_tui.rs"^^ . + "true"^^ . + "stack_graph_tui"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/stack_quality_demo.rs"^^ . + "true"^^ . + "stack_quality_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/svg_generation_demo.rs"^^ . + "true"^^ . + "svg_generation_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/trueno_ublk_demo.rs"^^ . + "true"^^ . + "trueno_ublk_demo"^^ . + "false"^^ . + . + . + "aprender-orchestrate"^^ . + "crates/aprender-orchestrate/examples/trueno_zram_demo.rs"^^ . + "true"^^ . + "trueno_zram_demo"^^ . + "false"^^ . + . + . + "aprender-present-core"^^ . + "crates/aprender-present-core/examples/hello_widget.rs"^^ . + "true"^^ . + "hello_widget"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/ald_batch_upload.rs"^^ . + "true"^^ . + "ald_batch_upload"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/ald_correlation_heatmap.rs"^^ . + "true"^^ . + "ald_correlation_heatmap"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/ald_lineage.rs"^^ . + "true"^^ . + "ald_lineage"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/apr_ald_display.rs"^^ . + "true"^^ . + "apr_ald_display"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/apr_architecture.rs"^^ . + "true"^^ . + "apr_architecture"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/apr_size_breakdown.rs"^^ . + "true"^^ . + "apr_size_breakdown"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/apr_version_history.rs"^^ . + "true"^^ . + "apr_version_history"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/apr_weight_histograms.rs"^^ . + "true"^^ . + "apr_weight_histograms"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/brick_computer.rs"^^ . + "true"^^ . + "brick_computer"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_area_stacked.rs"^^ . + "true"^^ . + "cht_area_stacked"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_boxplot.rs"^^ . + "true"^^ . + "cht_boxplot"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_donut.rs"^^ . + "true"^^ . + "cht_donut"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_heatmap_basic.rs"^^ . + "true"^^ . + "cht_heatmap_basic"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_multi_axis.rs"^^ . + "true"^^ . + "cht_multi_axis"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_scatter_bubble.rs"^^ . + "true"^^ . + "cht_scatter_bubble"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/cht_sparkline.rs"^^ . + "true"^^ . + "cht_sparkline"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dashboard.rs"^^ . + "true"^^ . + "dashboard"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/demo_10x_comparison.rs"^^ . + "true"^^ . + "demo_10x_comparison"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dsh_alerts.rs"^^ . + "true"^^ . + "dsh_alerts"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dsh_infrastructure.rs"^^ . + "true"^^ . + "dsh_infrastructure"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dsh_performance.rs"^^ . + "true"^^ . + "dsh_performance"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dsh_pipeline.rs"^^ . + "true"^^ . + "dsh_pipeline"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/dsh_research.rs"^^ . + "true"^^ . + "dsh_research"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_a11y_audit.rs"^^ . + "true"^^ . + "edg_a11y_audit"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_high_cardinality.rs"^^ . + "true"^^ . + "edg_high_cardinality"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_memory_soak.rs"^^ . + "true"^^ . + "edg_memory_soak"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_numeric.rs"^^ . + "true"^^ . + "edg_numeric"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_rtl.rs"^^ . + "true"^^ . + "edg_rtl"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_slow_data.rs"^^ . + "true"^^ . + "edg_slow_data"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_theme_switching.rs"^^ . + "true"^^ . + "edg_theme_switching"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/edg_unicode.rs"^^ . + "true"^^ . + "edg_unicode"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/form_inputs.rs"^^ . + "true"^^ . + "form_inputs"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/generate_demo_assets.rs"^^ . + "true"^^ . + "generate_demo_assets"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/hello_world.rs"^^ . + "true"^^ . + "hello_world"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/showcase_gpu.rs"^^ . + "true"^^ . + "showcase_gpu"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/showcase_shell.rs"^^ . + "true"^^ . + "showcase_shell"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/visual_demo.rs"^^ . + "true"^^ . + "visual_demo"^^ . + "false"^^ . + . + . + "aprender-present-lib"^^ . + "crates/aprender-present-lib/examples/yaml_manifest.rs"^^ . + "true"^^ . + "yaml_manifest"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/batch_progress.rs"^^ . + "true"^^ . + "batch_progress"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/cbtop-bench.rs"^^ . + "true"^^ . + "cbtop-bench"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/cluster_status.rs"^^ . + "true"^^ . + "cluster_status"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/cpu_monitor.rs"^^ . + "true"^^ . + "cpu_monitor"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/direct_benchmark.rs"^^ . + "true"^^ . + "direct_benchmark"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/direct_canvas_demo.rs"^^ . + "true"^^ . + "direct_canvas_demo"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/gpu_compute.rs"^^ . + "true"^^ . + "gpu_compute"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/inference_server.rs"^^ . + "false"^^ . + "inference_server"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/memory_monitor.rs"^^ . + "true"^^ . + "memory_monitor"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/ml_visualization.rs"^^ . + "true"^^ . + "ml_visualization"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/network_traffic.rs"^^ . + "true"^^ . + "network_traffic"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/queue_monitor.rs"^^ . + "true"^^ . + "queue_monitor"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/sensor_dashboard.rs"^^ . + "true"^^ . + "sensor_dashboard"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/system_dashboard.rs"^^ . + "true"^^ . + "system_dashboard"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/test_connections.rs"^^ . + "true"^^ . + "test_connections"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/test_display_rules.rs"^^ . + "true"^^ . + "test_display_rules"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/training_metrics.rs"^^ . + "true"^^ . + "training_metrics"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/ttop_cpu_pane.rs"^^ . + "true"^^ . + "ttop_cpu_pane"^^ . + "false"^^ . + . + . + "aprender-present-terminal"^^ . + "crates/aprender-present-terminal/examples/tui-compare.rs"^^ . + "true"^^ . + "tui-compare"^^ . + "false"^^ . + . + . + "aprender-present-yaml"^^ . + "crates/aprender-present-yaml/examples/validate_prs.rs"^^ . + "true"^^ . + "validate_prs"^^ . + "false"^^ . + . + . + "aprender-profile"^^ . + "crates/aprender-profile/examples/brick_trace_demo.rs"^^ . + "true"^^ . + "brick_trace_demo"^^ . + "false"^^ . + . + . + "aprender-profile"^^ . + "crates/aprender-profile/examples/buggy_server.rs"^^ . + "true"^^ . + "buggy_server"^^ . + "false"^^ . + . + . + "aprender-profile"^^ . + "crates/aprender-profile/examples/build_time_assertions.rs"^^ . + "true"^^ . + "build_time_assertions"^^ . + "false"^^ . + . + . + "aprender-profile"^^ . + "crates/aprender-profile/examples/process_tracer_demo.rs"^^ . + "true"^^ . + "process_tracer_demo"^^ . + "false"^^ . + . + . + "aprender-profile"^^ . + "crates/aprender-profile/examples/validate_golden_trace.rs"^^ . + "true"^^ . + "validate_golden_trace"^^ . + "false"^^ . + . + . + "aprender-qa-cli"^^ . + "crates/aprender-qa-cli/examples/fail_fast_demo.rs"^^ . + "false"^^ . + "fail_fast_demo"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-qa-cli"^^ . + "crates/aprender-qa-cli/examples/integrity_lock_demo.rs"^^ . + "true"^^ . + "integrity_lock_demo"^^ . + "false"^^ . + . + . + "aprender-qa-gen"^^ . + "crates/aprender-qa-gen/examples/bootstrap_playbook.rs"^^ . + "false"^^ . + "bootstrap_playbook"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-qa-gen"^^ . + "crates/aprender-qa-gen/examples/generate_scenarios.rs"^^ . + "false"^^ . + "generate_scenarios"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-qa-report"^^ . + "crates/aprender-qa-report/examples/calculate_mqs.rs"^^ . + "false"^^ . + "calculate_mqs"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-qa-report"^^ . + "crates/aprender-qa-report/examples/generate_certificate.rs"^^ . + "false"^^ . + "generate_certificate"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-qa-report"^^ . + "crates/aprender-qa-report/examples/generate_rag_markdown.rs"^^ . + "false"^^ . + "generate_rag_markdown"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-qa-runner"^^ . + "crates/aprender-qa-runner/examples/collect_evidence.rs"^^ . + "false"^^ . + "collect_evidence"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-qa-runner"^^ . + "crates/aprender-qa-runner/examples/contract_demo.rs"^^ . + "true"^^ . + "contract_demo"^^ . + "false"^^ . + . + . + "aprender-qa-runner"^^ . + "crates/aprender-qa-runner/examples/provable_contracts_demo.rs"^^ . + "true"^^ . + "provable_contracts_demo"^^ . + "false"^^ . + . + . + "aprender-qa-runner"^^ . + "crates/aprender-qa-runner/examples/rosetta_testing.rs"^^ . + "false"^^ . + "rosetta_testing"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/basic_rag.rs"^^ . + "true"^^ . + "basic_rag"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/chunking_strategies.rs"^^ . + "true"^^ . + "chunking_strategies"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/compressed_index.rs"^^ . + "true"^^ . + "compressed_index"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/eval_hybrid.rs"^^ . + "true"^^ . + "eval_hybrid"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/hybrid_search.rs"^^ . + "true"^^ . + "hybrid_search"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/metrics_evaluation.rs"^^ . + "true"^^ . + "metrics_evaluation"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/nemotron_embeddings.rs"^^ . + "true"^^ . + "nemotron_embeddings"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/semantic_embeddings.rs"^^ . + "true"^^ . + "semantic_embeddings"^^ . + "false"^^ . + . + . + "aprender-rag"^^ . + "crates/aprender-rag/examples/sqlite_export.rs"^^ . + "true"^^ . + "sqlite_export"^^ . + "false"^^ . + . + . + "aprender-rand"^^ . + "crates/aprender-rand/examples/rng_demo.rs"^^ . + "true"^^ . + "rng_demo"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/content_addressing.rs"^^ . + "true"^^ . + "content_addressing"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/crypto_demo.rs"^^ . + "true"^^ . + "crypto_demo"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/experiment_tracking.rs"^^ . + "true"^^ . + "experiment_tracking"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/lineage_tracking.rs"^^ . + "false"^^ . + "lineage_tracking"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/model_versioning.rs"^^ . + "true"^^ . + "model_versioning"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/quick_start.rs"^^ . + "true"^^ . + "quick_start"^^ . + "false"^^ . + . + . + "aprender-registry"^^ . + "crates/aprender-registry/examples/signing_demo.rs"^^ . + "true"^^ . + "signing_demo"^^ . + "false"^^ . + . + . + "aprender-review-experiment"^^ . + "crates/aprender-review-experiment/examples/rex.rs"^^ . + "true"^^ . + "rex"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/api_server.rs"^^ . + "true"^^ . + "api_server"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/apr_benchmark.rs"^^ . + "true"^^ . + "apr_benchmark"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/apr_gpu_benchmark.rs"^^ . + "false"^^ . + "apr_gpu_benchmark"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/apr_loading.rs"^^ . + "true"^^ . + "apr_loading"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/apr_mmap_loading.rs"^^ . + "true"^^ . + "apr_mmap_loading"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/attention_manual_verify.rs"^^ . + "true"^^ . + "attention_manual_verify"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_apr_gpu.rs"^^ . + "false"^^ . + "bench_apr_gpu"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_apr_vs_gguf.rs"^^ . + "false"^^ . + "bench_apr_vs_gguf"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_barrier_overhead.rs"^^ . + "true"^^ . + "bench_barrier_overhead"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_batched_forward.rs"^^ . + "false"^^ . + "bench_batched_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_batched_gemv.rs"^^ . + "false"^^ . + "bench_batched_gemv"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_chunk_sizes.rs"^^ . + "true"^^ . + "bench_chunk_sizes"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_chunked_matmul.rs"^^ . + "true"^^ . + "bench_chunked_matmul"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_comparison.rs"^^ . + "false"^^ . + "bench_comparison"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_continuous_batching.rs"^^ . + "false"^^ . + "bench_continuous_batching"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_cpu_util.rs"^^ . + "true"^^ . + "bench_cpu_util"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_flash_decoding.rs"^^ . + "false"^^ . + "bench_flash_decoding"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_forward.rs"^^ . + "true"^^ . + "bench_forward"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_gemv.rs"^^ . + "true"^^ . + "bench_gemv"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_kernel_variants.rs"^^ . + "true"^^ . + "bench_kernel_variants"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_manual_threads.rs"^^ . + "true"^^ . + "bench_manual_threads"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_matmul_breakdown.rs"^^ . + "false"^^ . + "bench_matmul_breakdown"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_matmul_only.rs"^^ . + "false"^^ . + "bench_matmul_only"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_multisequence_graph.rs"^^ . + "false"^^ . + "bench_multisequence_graph"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_q4k_detect.rs"^^ . + "true"^^ . + "bench_q4k_detect"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_q4k_parallel.rs"^^ . + "true"^^ . + "bench_q4k_parallel"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_q4k_simd.rs"^^ . + "true"^^ . + "bench_q4k_simd"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_q8k_speedup.rs"^^ . + "true"^^ . + "bench_q8k_speedup"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_qwen.rs"^^ . + "false"^^ . + "bench_qwen"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_rayon_overhead.rs"^^ . + "true"^^ . + "bench_rayon_overhead"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_realistic.rs"^^ . + "false"^^ . + "bench_realistic"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_scaling.rs"^^ . + "true"^^ . + "bench_scaling"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_scratch.rs"^^ . + "false"^^ . + "bench_scratch"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_seq_vs_par.rs"^^ . + "false"^^ . + "bench_seq_vs_par"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_simd_dot.rs"^^ . + "true"^^ . + "bench_simd_dot"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_speculative.rs"^^ . + "false"^^ . + "bench_speculative"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_tiled_q4k.rs"^^ . + "false"^^ . + "bench_tiled_q4k"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_toks.rs"^^ . + "true"^^ . + "bench_toks"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_v2_kernel.rs"^^ . + "true"^^ . + "bench_v2_kernel"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/bench_vnni_vs_avx2.rs"^^ . + "true"^^ . + "bench_vnni_vs_avx2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/benchmark_cpu.rs"^^ . + "false"^^ . + "benchmark_cpu"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/brick_divergence_trace.rs"^^ . + "false"^^ . + "brick_divergence_trace"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/chat_template.rs"^^ . + "false"^^ . + "chat_template"^^ . + "llama"^^ . + "mistral"^^ . + "phi"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_apr_tensors.rs"^^ . + "true"^^ . + "check_apr_tensors"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_arch_detection.rs"^^ . + "false"^^ . + "check_arch_detection"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_bias.rs"^^ . + "false"^^ . + "check_bias"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_bias_format.rs"^^ . + "false"^^ . + "check_bias_format"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_bias_raw.rs"^^ . + "false"^^ . + "check_bias_raw"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_biases.rs"^^ . + "false"^^ . + "check_biases"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_dims.rs"^^ . + "false"^^ . + "check_dims"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_embed.rs"^^ . + "false"^^ . + "check_embed"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_embed_layout.rs"^^ . + "false"^^ . + "check_embed_layout"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_embedding.rs"^^ . + "false"^^ . + "check_embedding"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_embeddings.rs"^^ . + "false"^^ . + "check_embeddings"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_ffn_down_col_5475.rs"^^ . + "false"^^ . + "check_ffn_down_col_5475"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_ffn_down_types.rs"^^ . + "false"^^ . + "check_ffn_down_types"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_ffn_down_weight.rs"^^ . + "false"^^ . + "check_ffn_down_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_ffn_hidden.rs"^^ . + "false"^^ . + "check_ffn_hidden"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_final_hidden.rs"^^ . + "false"^^ . + "check_final_hidden"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_forward.rs"^^ . + "false"^^ . + "check_forward"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_gate_up_correlation.rs"^^ . + "false"^^ . + "check_gate_up_correlation"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_gguf_meta.rs"^^ . + "false"^^ . + "check_gguf_meta"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_gguf_metadata.rs"^^ . + "false"^^ . + "check_gguf_metadata"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_gguf_model_version.rs"^^ . + "false"^^ . + "check_gguf_model_version"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_gpu_logits.rs"^^ . + "false"^^ . + "check_gpu_logits"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_idx_5475.rs"^^ . + "false"^^ . + "check_idx_5475"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_layer2_ffn_down.rs"^^ . + "false"^^ . + "check_layer2_ffn_down"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_layer4.rs"^^ . + "true"^^ . + "check_layer4"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_layer_structure.rs"^^ . + "false"^^ . + "check_layer_structure"^^ . + "llama"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_layer_weights.rs"^^ . + "false"^^ . + "check_layer_weights"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_lm_head.rs"^^ . + "false"^^ . + "check_lm_head"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_matmul.rs"^^ . + "false"^^ . + "check_matmul"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_newlines.rs"^^ . + "false"^^ . + "check_newlines"^^ . + "gpt2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_norm_weight.rs"^^ . + "false"^^ . + "check_norm_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_norm_weights.rs"^^ . + "false"^^ . + "check_norm_weights"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_paris.rs"^^ . + "false"^^ . + "check_paris"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_prefill_logits.rs"^^ . + "false"^^ . + "check_prefill_logits"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_q4k_layout.rs"^^ . + "false"^^ . + "check_q4k_layout"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_q4k_population.rs"^^ . + "false"^^ . + "check_q4k_population"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_q8k_accuracy.rs"^^ . + "true"^^ . + "check_q8k_accuracy"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_qkv_bias.rs"^^ . + "false"^^ . + "check_qkv_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_qkv_dims.rs"^^ . + "false"^^ . + "check_qkv_dims"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_qkv_tensors.rs"^^ . + "false"^^ . + "check_qkv_tensors"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_qtype.rs"^^ . + "false"^^ . + "check_qtype"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_qtypes.rs"^^ . + "false"^^ . + "check_qtypes"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_raw_bias.rs"^^ . + "false"^^ . + "check_raw_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_raw_bias_v2.rs"^^ . + "false"^^ . + "check_raw_bias_v2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_raw_tensors.rs"^^ . + "false"^^ . + "check_raw_tensors"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_rmsnorm_params.rs"^^ . + "false"^^ . + "check_rmsnorm_params"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_space_token.rs"^^ . + "false"^^ . + "check_space_token"^^ . + "gpt2"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tensor_names.rs"^^ . + "false"^^ . + "check_tensor_names"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tensor_order.rs"^^ . + "false"^^ . + "check_tensor_order"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tensors.rs"^^ . + "false"^^ . + "check_tensors"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_theta.rs"^^ . + "false"^^ . + "check_theta"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_token0.rs"^^ . + "false"^^ . + "check_token0"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_token0_emb.rs"^^ . + "false"^^ . + "check_token0_emb"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_token_74403.rs"^^ . + "false"^^ . + "check_token_74403"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_token_scores.rs"^^ . + "false"^^ . + "check_token_scores"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_token_scores_v2.rs"^^ . + "false"^^ . + "check_token_scores_v2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tokenization.rs"^^ . + "false"^^ . + "check_tokenization"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tokenizer.rs"^^ . + "false"^^ . + "check_tokenizer"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_tokens.rs"^^ . + "false"^^ . + "check_tokens"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_v_weight.rs"^^ . + "false"^^ . + "check_v_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_v_weights.rs"^^ . + "false"^^ . + "check_v_weights"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_weight_layout.rs"^^ . + "false"^^ . + "check_weight_layout"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_weight_scales.rs"^^ . + "false"^^ . + "check_weight_scales"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/check_weight_stats.rs"^^ . + "false"^^ . + "check_weight_stats"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_all_layers.rs"^^ . + "false"^^ . + "compare_all_layers"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_apr_gguf_forward.rs"^^ . + "false"^^ . + "compare_apr_gguf_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_configs.rs"^^ . + "false"^^ . + "compare_configs"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_cpu_gpu.rs"^^ . + "false"^^ . + "compare_cpu_gpu"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_cpu_paths.rs"^^ . + "false"^^ . + "compare_cpu_paths"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_embed.rs"^^ . + "false"^^ . + "compare_embed"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_embedding_index.rs"^^ . + "false"^^ . + "compare_embedding_index"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_forward_methods.rs"^^ . + "false"^^ . + "compare_forward_methods"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_forward_paths.rs"^^ . + "false"^^ . + "compare_forward_paths"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_hidden_before_norm.rs"^^ . + "false"^^ . + "compare_hidden_before_norm"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_l0_v.rs"^^ . + "false"^^ . + "compare_l0_v"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_layer0.rs"^^ . + "false"^^ . + "compare_layer0"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_layer0_full.rs"^^ . + "false"^^ . + "compare_layer0_full"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_layer_outputs.rs"^^ . + "false"^^ . + "compare_layer_outputs"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_layers.rs"^^ . + "false"^^ . + "compare_layers"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_lm_head_input.rs"^^ . + "false"^^ . + "compare_lm_head_input"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_logits.rs"^^ . + "false"^^ . + "compare_logits"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_matmul.rs"^^ . + "false"^^ . + "compare_matmul"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_matmul2.rs"^^ . + "false"^^ . + "compare_matmul2"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_q4k_bytes.rs"^^ . + "false"^^ . + "compare_q4k_bytes"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_q_projection.rs"^^ . + "false"^^ . + "compare_q_projection"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_qkv_structure.rs"^^ . + "false"^^ . + "compare_qkv_structure"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_rope.rs"^^ . + "false"^^ . + "compare_rope"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_v_weight.rs"^^ . + "false"^^ . + "compare_v_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/compare_weights.rs"^^ . + "false"^^ . + "compare_weights"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/constrain_mask_overhead.rs"^^ . + "true"^^ . + "constrain_mask_overhead"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/contract_pipeline_demo.rs"^^ . + "true"^^ . + "contract_pipeline_demo"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/convert_and_bench_apr.rs"^^ . + "false"^^ . + "convert_and_bench_apr"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/convert_apr_q4k.rs"^^ . + "false"^^ . + "convert_apr_q4k"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cpu_hidden.rs"^^ . + "false"^^ . + "cpu_hidden"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cpu_hidden_state_trace.rs"^^ . + "false"^^ . + "cpu_hidden_state_trace"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cpu_hidden_trace.rs"^^ . + "false"^^ . + "cpu_hidden_trace"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cublas_fp8_7b_reproducer.rs"^^ . + "false"^^ . + "cublas_fp8_7b_reproducer"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cuda_chat_completions.rs"^^ . + "false"^^ . + "cuda_chat_completions"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/cuda_debug.rs"^^ . + "true"^^ . + "cuda_debug"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_apr_divergence.rs"^^ . + "false"^^ . + "debug_apr_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_apr_embedding.rs"^^ . + "false"^^ . + "debug_apr_embedding"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_attention_output.rs"^^ . + "false"^^ . + "debug_attention_output"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_chat_template.rs"^^ . + "false"^^ . + "debug_chat_template"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_cpu_forward.rs"^^ . + "false"^^ . + "debug_cpu_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_cpu_gpu_divergence.rs"^^ . + "false"^^ . + "debug_cpu_gpu_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_early_layers.rs"^^ . + "false"^^ . + "debug_early_layers"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_embed_test.rs"^^ . + "false"^^ . + "debug_embed_test"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_embedding.rs"^^ . + "false"^^ . + "debug_embedding"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_embedding_qtype.rs"^^ . + "false"^^ . + "debug_embedding_qtype"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_embedding_simple.rs"^^ . + "false"^^ . + "debug_embedding_simple"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_embeddings.rs"^^ . + "false"^^ . + "debug_embeddings"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_first_q_proj.rs"^^ . + "false"^^ . + "debug_first_q_proj"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_forward.rs"^^ . + "false"^^ . + "debug_forward"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_forward_bias.rs"^^ . + "false"^^ . + "debug_forward_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_gen_detailed.rs"^^ . + "true"^^ . + "debug_gen_detailed"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_gpu_divergence.rs"^^ . + "false"^^ . + "debug_gpu_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_gpu_layer4.rs"^^ . + "false"^^ . + "debug_gpu_layer4"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_hidden_state.rs"^^ . + "false"^^ . + "debug_hidden_state"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_inference.rs"^^ . + "false"^^ . + "debug_inference"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer0_compare.rs"^^ . + "false"^^ . + "debug_layer0_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer0_divergence.rs"^^ . + "false"^^ . + "debug_layer0_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer0_stepwise.rs"^^ . + "false"^^ . + "debug_layer0_stepwise"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer0_trace.rs"^^ . + "false"^^ . + "debug_layer0_trace"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer21_ffn.rs"^^ . + "false"^^ . + "debug_layer21_ffn"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer2_ffn.rs"^^ . + "false"^^ . + "debug_layer2_ffn"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer2_gate.rs"^^ . + "false"^^ . + "debug_layer2_gate"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer_by_layer.rs"^^ . + "false"^^ . + "debug_layer_by_layer"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_layer_compare.rs"^^ . + "false"^^ . + "debug_layer_compare"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_lm_head.rs"^^ . + "false"^^ . + "debug_lm_head"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_lm_head_direct.rs"^^ . + "false"^^ . + "debug_lm_head_direct"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_lm_head_divergence.rs"^^ . + "false"^^ . + "debug_lm_head_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_lm_head_weights.rs"^^ . + "false"^^ . + "debug_lm_head_weights"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_matmul_convention.rs"^^ . + "false"^^ . + "debug_matmul_convention"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_matvec_compare.rs"^^ . + "false"^^ . + "debug_matvec_compare"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_normed_hidden.rs"^^ . + "false"^^ . + "debug_normed_hidden"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_normed_hidden_compare.rs"^^ . + "false"^^ . + "debug_normed_hidden_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_o_weight.rs"^^ . + "false"^^ . + "debug_o_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_o_weight_layout.rs"^^ . + "false"^^ . + "debug_o_weight_layout"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_pos1.rs"^^ . + "false"^^ . + "debug_pos1"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_ptx.rs"^^ . + "false"^^ . + "debug_ptx"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4_0.rs"^^ . + "false"^^ . + "debug_q4_0"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_attn_output.rs"^^ . + "false"^^ . + "debug_q4k_attn_output"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_controlled.rs"^^ . + "false"^^ . + "debug_q4k_controlled"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_down_weight.rs"^^ . + "false"^^ . + "debug_q4k_down_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_embedding_raw.rs"^^ . + "false"^^ . + "debug_q4k_embedding_raw"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_gemv.rs"^^ . + "false"^^ . + "debug_q4k_gemv"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_gemv_layer0.rs"^^ . + "false"^^ . + "debug_q4k_gemv_layer0"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_gemv_tiled.rs"^^ . + "false"^^ . + "debug_q4k_gemv_tiled"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_real_input.rs"^^ . + "false"^^ . + "debug_q4k_real_input"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q4k_rmsnorm_input.rs"^^ . + "false"^^ . + "debug_q4k_rmsnorm_input"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_controlled.rs"^^ . + "true"^^ . + "debug_q6k_controlled"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_gemv.rs"^^ . + "false"^^ . + "debug_q6k_gemv"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_layout.rs"^^ . + "false"^^ . + "debug_q6k_layout"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_lm_head_test.rs"^^ . + "false"^^ . + "debug_q6k_lm_head_test"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_row.rs"^^ . + "false"^^ . + "debug_q6k_row"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q6k_single_row.rs"^^ . + "false"^^ . + "debug_q6k_single_row"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_q_weight_compare.rs"^^ . + "false"^^ . + "debug_q_weight_compare"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_qkv_compare.rs"^^ . + "false"^^ . + "debug_qkv_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_qkv_matmul.rs"^^ . + "false"^^ . + "debug_qkv_matmul"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_qwen.rs"^^ . + "false"^^ . + "debug_qwen"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_rmsnorm_layer0.rs"^^ . + "false"^^ . + "debug_rmsnorm_layer0"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_single_row.rs"^^ . + "false"^^ . + "debug_single_row"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_speculative.rs"^^ . + "false"^^ . + "debug_speculative"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_tensor_layout.rs"^^ . + "false"^^ . + "debug_tensor_layout"^^ . + "deepseek"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_tiled_q4k.rs"^^ . + "false"^^ . + "debug_tiled_q4k"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_v_weight.rs"^^ . + "false"^^ . + "debug_v_weight"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_v_weight_layout.rs"^^ . + "false"^^ . + "debug_v_weight_layout"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/debug_weights.rs"^^ . + "false"^^ . + "debug_weights"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/design_by_contract.rs"^^ . + "false"^^ . + "design_by_contract"^^ . + "gpt2"^^ . + "llama"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/detailed_profile.rs"^^ . + "false"^^ . + "detailed_profile"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_apr_determinism.rs"^^ . + "false"^^ . + "diag_apr_determinism"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_apr_qkv_layer0.rs"^^ . + "false"^^ . + "diag_apr_qkv_layer0"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_compare_layer3_ffn.rs"^^ . + "false"^^ . + "diag_compare_layer3_ffn"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_compare_qkv_bias.rs"^^ . + "false"^^ . + "diag_compare_qkv_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_q4k_dequant_cpu_vs_gpu.rs"^^ . + "false"^^ . + "diag_q4k_dequant_cpu_vs_gpu"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/diag_qkv_bisection_layer0.rs"^^ . + "false"^^ . + "diag_qkv_bisection_layer0"^^ . + "qwen2.5"^^ . + "qwen3"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/digit_combo_test.rs"^^ . + "false"^^ . + "digit_combo_test"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/dump_config.rs"^^ . + "false"^^ . + "dump_config"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/dump_def_embedding.rs"^^ . + "false"^^ . + "dump_def_embedding"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/dump_gguf_dims.rs"^^ . + "false"^^ . + "dump_gguf_dims"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/dump_layer0_tensors.rs"^^ . + "false"^^ . + "dump_layer0_tensors"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/dump_q6k_ptx.rs"^^ . + "true"^^ . + "dump_q6k_ptx"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/final_hidden_compare.rs"^^ . + "false"^^ . + "final_hidden_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/find_ffn_outliers.rs"^^ . + "false"^^ . + "find_ffn_outliers"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/fresh_compare.rs"^^ . + "false"^^ . + "fresh_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/gguf_debug.rs"^^ . + "false"^^ . + "gguf_debug"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/gguf_loading.rs"^^ . + "true"^^ . + "gguf_loading"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/gpu_gemm_benchmark.rs"^^ . + "false"^^ . + "gpu_gemm_benchmark"^^ . + "llama"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/gpu_matvec_benchmark.rs"^^ . + "false"^^ . + "gpu_matvec_benchmark"^^ . + "llama"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/gpu_showcase_benchmark.rs"^^ . + "false"^^ . + "gpu_showcase_benchmark"^^ . + "deepseek"^^ . + "llama"^^ . + "phi"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/hidden_compare.rs"^^ . + "false"^^ . + "hidden_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp800_gpu_parity.rs"^^ . + "false"^^ . + "imp800_gpu_parity"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp900_optimized_gpu.rs"^^ . + "false"^^ . + "imp900_optimized_gpu"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp_1010_full_cuda_benchmark.rs"^^ . + "false"^^ . + "imp_1010_full_cuda_benchmark"^^ . + "llama"^^ . + "phi"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp_700_realworld_verification.rs"^^ . + "false"^^ . + "imp_700_realworld_verification"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp_701_performance_gap.rs"^^ . + "false"^^ . + "imp_701_performance_gap"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp_800_kv_cache_falsification.rs"^^ . + "false"^^ . + "imp_800_kv_cache_falsification"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/imp_801_flash_attention_falsification.rs"^^ . + "false"^^ . + "imp_801_flash_attention_falsification"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/inference.rs"^^ . + "true"^^ . + "inference"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/instrumented_forward.rs"^^ . + "false"^^ . + "instrumented_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer0_attention_compare.rs"^^ . + "false"^^ . + "layer0_attention_compare"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer0_step_compare.rs"^^ . + "false"^^ . + "layer0_step_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer_by_layer_debug.rs"^^ . + "false"^^ . + "layer_by_layer_debug"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer_by_layer_trace.rs"^^ . + "false"^^ . + "layer_by_layer_trace"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer_compare.rs"^^ . + "false"^^ . + "layer_compare"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/layer_profile.rs"^^ . + "false"^^ . + "layer_profile"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/measure_forward_time.rs"^^ . + "false"^^ . + "measure_forward_time"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/micro_profile.rs"^^ . + "false"^^ . + "micro_profile"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/model_cache.rs"^^ . + "false"^^ . + "model_cache"^^ . + "gpt2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/observability_demo.rs"^^ . + "false"^^ . + "observability_demo"^^ . + "llama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_dims.rs"^^ . + "false"^^ . + "par_001_check_dims"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_embed.rs"^^ . + "false"^^ . + "par_001_check_embed"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_embeddings.rs"^^ . + "false"^^ . + "par_001_check_embeddings"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_lm_head.rs"^^ . + "false"^^ . + "par_001_check_lm_head"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_output_norm.rs"^^ . + "false"^^ . + "par_001_check_output_norm"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_check_q6k_superblocks.rs"^^ . + "false"^^ . + "par_001_check_q6k_superblocks"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_compare_data.rs"^^ . + "false"^^ . + "par_001_compare_data"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_debug_forward.rs"^^ . + "false"^^ . + "par_001_debug_forward"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_full_forward.rs"^^ . + "false"^^ . + "par_001_full_forward"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_fused_vs_naive.rs"^^ . + "false"^^ . + "par_001_fused_vs_naive"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_list_q6k.rs"^^ . + "false"^^ . + "par_001_list_q6k"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_qkv_parity.rs"^^ . + "false"^^ . + "par_001_qkv_parity"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_qv_comparison.rs"^^ . + "false"^^ . + "par_001_qv_comparison"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_tensor_offset.rs"^^ . + "false"^^ . + "par_001_tensor_offset"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_test_chat.rs"^^ . + "false"^^ . + "par_001_test_chat"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_test_chatml.rs"^^ . + "false"^^ . + "par_001_test_chatml"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_test_math.rs"^^ . + "false"^^ . + "par_001_test_math"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_all_layers.rs"^^ . + "false"^^ . + "par_001_trace_all_layers"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_hidden.rs"^^ . + "false"^^ . + "par_001_trace_hidden"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_layers.rs"^^ . + "false"^^ . + "par_001_trace_layers"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_pos1.rs"^^ . + "false"^^ . + "par_001_trace_pos1"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_token.rs"^^ . + "false"^^ . + "par_001_trace_token"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_trace_v.rs"^^ . + "false"^^ . + "par_001_trace_v"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_transpose_test.rs"^^ . + "false"^^ . + "par_001_transpose_test"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_colmajor.rs"^^ . + "false"^^ . + "par_001_verify_colmajor"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_ffn_down.rs"^^ . + "false"^^ . + "par_001_verify_ffn_down"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_lm_head.rs"^^ . + "false"^^ . + "par_001_verify_lm_head"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_q4k_dot.rs"^^ . + "false"^^ . + "par_001_verify_q4k_dot"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_q4k_matvec.rs"^^ . + "false"^^ . + "par_001_verify_q4k_matvec"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_q6k_rowmajor.rs"^^ . + "false"^^ . + "par_001_verify_q6k_rowmajor"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_verify_v_real.rs"^^ . + "false"^^ . + "par_001_verify_v_real"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/par_001_weight_alignment.rs"^^ . + "false"^^ . + "par_001_weight_alignment"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/parity_035_m4_verification.rs"^^ . + "false"^^ . + "parity_035_m4_verification"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/parity_036_gpu_attention.rs"^^ . + "false"^^ . + "parity_036_gpu_attention"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/parity_038_async_streams.rs"^^ . + "false"^^ . + "parity_038_async_streams"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/parity_039_flash_attention.rs"^^ . + "false"^^ . + "parity_039_flash_attention"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/parity_040_fp16_attention.rs"^^ . + "false"^^ . + "parity_040_fp16_attention"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/performance_parity.rs"^^ . + "false"^^ . + "performance_parity"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/pipeline_tui.rs"^^ . + "true"^^ . + "pipeline_tui"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/pmat_benchmark_matrix.rs"^^ . + "false"^^ . + "pmat_benchmark_matrix"^^ . + "llama"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/position_trace.rs"^^ . + "false"^^ . + "position_trace"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/predict_after_layer1.rs"^^ . + "false"^^ . + "predict_after_layer1"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_7b.rs"^^ . + "false"^^ . + "profile_7b"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_all_layers.rs"^^ . + "false"^^ . + "profile_all_layers"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_all_matmuls.rs"^^ . + "false"^^ . + "profile_all_matmuls"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_attention.rs"^^ . + "true"^^ . + "profile_attention"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_cached_forward.rs"^^ . + "false"^^ . + "profile_cached_forward"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_cpu_breakdown.rs"^^ . + "false"^^ . + "profile_cpu_breakdown"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_cpu_kernel.rs"^^ . + "true"^^ . + "profile_cpu_kernel"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_detailed.rs"^^ . + "false"^^ . + "profile_detailed"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_ffn.rs"^^ . + "false"^^ . + "profile_ffn"^^ . + "phi"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_forward_breakdown.rs"^^ . + "false"^^ . + "profile_forward_breakdown"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_forward_detailed.rs"^^ . + "false"^^ . + "profile_forward_detailed"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_forward_instrumented.rs"^^ . + "false"^^ . + "profile_forward_instrumented"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_forward_pass.rs"^^ . + "false"^^ . + "profile_forward_pass"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_full_forward.rs"^^ . + "false"^^ . + "profile_full_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_inference.rs"^^ . + "false"^^ . + "profile_inference"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_layer_breakdown.rs"^^ . + "false"^^ . + "profile_layer_breakdown"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_lm_head.rs"^^ . + "false"^^ . + "profile_lm_head"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_matmul_cold.rs"^^ . + "false"^^ . + "profile_matmul_cold"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_matmul_detail.rs"^^ . + "false"^^ . + "profile_matmul_detail"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_matmul_sizes.rs"^^ . + "false"^^ . + "profile_matmul_sizes"^^ . + "llama"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_non_matmul.rs"^^ . + "true"^^ . + "profile_non_matmul"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_phi2_simple.rs"^^ . + "false"^^ . + "profile_phi2_simple"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_q8k_quant.rs"^^ . + "true"^^ . + "profile_q8k_quant"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_rayon_overhead.rs"^^ . + "true"^^ . + "profile_rayon_overhead"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/profile_scratch_vs_cache.rs"^^ . + "false"^^ . + "profile_scratch_vs_cache"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/q6k_bench.rs"^^ . + "false"^^ . + "q6k_bench"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/quick_generate.rs"^^ . + "false"^^ . + "quick_generate"^^ . + "gpt2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/qwen35_parity.rs"^^ . + "true"^^ . + "qwen35_parity"^^ . + "llama"^^ . + "qwen3.5"^^ . + "true"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/qwen35_prefill_parity.rs"^^ . + "true"^^ . + "qwen35_prefill_parity"^^ . + "llama"^^ . + "qwen3.5"^^ . + "true"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/qwen_apr_demo.rs"^^ . + "false"^^ . + "qwen_apr_demo"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/safetensors_loading.rs"^^ . + "true"^^ . + "safetensors_loading"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_0_5b_raw.rs"^^ . + "false"^^ . + "test_0_5b_raw"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_1_5b_raw.rs"^^ . + "false"^^ . + "test_1_5b_raw"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_apr_q4k_forward.rs"^^ . + "false"^^ . + "test_apr_q4k_forward"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_apr_q4k_generate.rs"^^ . + "false"^^ . + "test_apr_q4k_generate"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_apr_quantized_cache.rs"^^ . + "true"^^ . + "test_apr_quantized_cache"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_attention_debug.rs"^^ . + "true"^^ . + "test_attention_debug"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_attention_phi2_dims.rs"^^ . + "false"^^ . + "test_attention_phi2_dims"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_chat_format.rs"^^ . + "false"^^ . + "test_chat_format"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_coherence.rs"^^ . + "false"^^ . + "test_coherence"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_cpu_chat.rs"^^ . + "false"^^ . + "test_cpu_chat"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_cuda_minimal.rs"^^ . + "true"^^ . + "test_cuda_minimal"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_ffn_q4k.rs"^^ . + "false"^^ . + "test_ffn_q4k"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_forward.rs"^^ . + "false"^^ . + "test_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_gemv_correctness.rs"^^ . + "true"^^ . + "test_gemv_correctness"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_generation.rs"^^ . + "false"^^ . + "test_generation"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_gguf_baseline.rs"^^ . + "false"^^ . + "test_gguf_baseline"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_gpu_bias.rs"^^ . + "false"^^ . + "test_gpu_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_graphed.rs"^^ . + "false"^^ . + "test_graphed"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_inference.rs"^^ . + "false"^^ . + "test_inference"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_lm_head_direct.rs"^^ . + "false"^^ . + "test_lm_head_direct"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_lm_head_only.rs"^^ . + "false"^^ . + "test_lm_head_only"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_m16.rs"^^ . + "false"^^ . + "test_m16"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_multiple_prompts.rs"^^ . + "false"^^ . + "test_multiple_prompts"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_no_bias.rs"^^ . + "false"^^ . + "test_no_bias"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_ollama_match.rs"^^ . + "false"^^ . + "test_ollama_match"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q4_0_parity.rs"^^ . + "false"^^ . + "test_q4_0_parity"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q4k_cuda.rs"^^ . + "true"^^ . + "test_q4k_cuda"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q4k_phi2_dims.rs"^^ . + "false"^^ . + "test_q4k_phi2_dims"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q6k_correctness.rs"^^ . + "false"^^ . + "test_q6k_correctness"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q6k_gemv_direct.rs"^^ . + "false"^^ . + "test_q6k_gemv_direct"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_q6k_single_row.rs"^^ . + "false"^^ . + "test_q6k_single_row"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_qkv_matmul.rs"^^ . + "false"^^ . + "test_qkv_matmul"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_qkv_with_bias.rs"^^ . + "false"^^ . + "test_qkv_with_bias"^^ . + "qwen2.5"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_qwen_prompt.rs"^^ . + "false"^^ . + "test_qwen_prompt"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_qwen_seq.rs"^^ . + "false"^^ . + "test_qwen_seq"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_rope_override.rs"^^ . + "false"^^ . + "test_rope_override"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_tc_attention.rs"^^ . + "true"^^ . + "test_tc_attention"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_tiled_vs_cpu.rs"^^ . + "false"^^ . + "test_tiled_vs_cpu"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_tinyllama.rs"^^ . + "false"^^ . + "test_tinyllama"^^ . + "llama"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_tinyllama_simple.rs"^^ . + "false"^^ . + "test_tinyllama_simple"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_transpose.rs"^^ . + "false"^^ . + "test_transpose"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/test_v_matvec.rs"^^ . + "false"^^ . + "test_v_matvec"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/think_ab.rs"^^ . + "true"^^ . + "think_ab"^^ . + "qwen3.5"^^ . + "true"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/tinyllama_survey.rs"^^ . + "false"^^ . + "tinyllama_survey"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/token_survey.rs"^^ . + "false"^^ . + "token_survey"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/tokenization.rs"^^ . + "true"^^ . + "tokenization"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_2plus2.rs"^^ . + "false"^^ . + "trace_2plus2"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_all_layers.rs"^^ . + "false"^^ . + "trace_all_layers"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_attention.rs"^^ . + "false"^^ . + "trace_attention"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_attention_scores.rs"^^ . + "false"^^ . + "trace_attention_scores"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_attn_weights.rs"^^ . + "false"^^ . + "trace_attn_weights"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_buggy_vs_ok.rs"^^ . + "false"^^ . + "trace_buggy_vs_ok"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_causal_attn.rs"^^ . + "false"^^ . + "trace_causal_attn"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_cpu_forward.rs"^^ . + "false"^^ . + "trace_cpu_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_divergence.rs"^^ . + "false"^^ . + "trace_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_final.rs"^^ . + "false"^^ . + "trace_final"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_final_hidden.rs"^^ . + "false"^^ . + "trace_final_hidden"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_forward.rs"^^ . + "false"^^ . + "trace_forward"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_forward_detailed.rs"^^ . + "false"^^ . + "trace_forward_detailed"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_full_layer0.rs"^^ . + "false"^^ . + "trace_full_layer0"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_hidden_flow.rs"^^ . + "false"^^ . + "trace_hidden_flow"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_layer0_detailed.rs"^^ . + "false"^^ . + "trace_layer0_detailed"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_layer0_qkv.rs"^^ . + "false"^^ . + "trace_layer0_qkv"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_layer0_qkv_fixed.rs"^^ . + "false"^^ . + "trace_layer0_qkv_fixed"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_layer21.rs"^^ . + "false"^^ . + "trace_layer21"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_layer2_detail.rs"^^ . + "false"^^ . + "trace_layer2_detail"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_lm_head.rs"^^ . + "false"^^ . + "trace_lm_head"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_pos1_divergence.rs"^^ . + "false"^^ . + "trace_pos1_divergence"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_qkv_output.rs"^^ . + "false"^^ . + "trace_qkv_output"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_qkv_step.rs"^^ . + "false"^^ . + "trace_qkv_step"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_qwen_qkv.rs"^^ . + "false"^^ . + "trace_qwen_qkv"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_safetensors.rs"^^ . + "false"^^ . + "trace_safetensors"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_single_layer.rs"^^ . + "false"^^ . + "trace_single_layer"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_single_token_full.rs"^^ . + "false"^^ . + "trace_single_token_full"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trace_single_vs_multi.rs"^^ . + "false"^^ . + "trace_single_vs_multi"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trueno_ab_test.rs"^^ . + "false"^^ . + "trueno_ab_test"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/trueno_dot_test.rs"^^ . + "false"^^ . + "trueno_dot_test"^^ . + "phi"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/validated_tensors.rs"^^ . + "true"^^ . + "validated_tensors"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_apr_q4k.rs"^^ . + "false"^^ . + "verify_apr_q4k"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_attention_kernel.rs"^^ . + "true"^^ . + "verify_attention_kernel"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_embed.rs"^^ . + "false"^^ . + "verify_embed"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_embedding.rs"^^ . + "false"^^ . + "verify_embedding"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_embeddings.rs"^^ . + "false"^^ . + "verify_embeddings"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_input_token.rs"^^ . + "false"^^ . + "verify_input_token"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_lm_head.rs"^^ . + "false"^^ . + "verify_lm_head"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_offsets.rs"^^ . + "false"^^ . + "verify_offsets"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_q4_0_correct.rs"^^ . + "false"^^ . + "verify_q4_0_correct"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_q4_0_full.rs"^^ . + "false"^^ . + "verify_q4_0_full"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_q4_0_matmul.rs"^^ . + "false"^^ . + "verify_q4_0_matmul"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_q4k_layout.rs"^^ . + "false"^^ . + "verify_q4k_layout"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_q8_0.rs"^^ . + "false"^^ . + "verify_q8_0"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_rmsnorm.rs"^^ . + "false"^^ . + "verify_rmsnorm"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_rope.rs"^^ . + "false"^^ . + "verify_rope"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_rope_pos0.rs"^^ . + "true"^^ . + "verify_rope_pos0"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_rope_type.rs"^^ . + "false"^^ . + "verify_rope_type"^^ . + "qwen2"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_tensor_offsets.rs"^^ . + "false"^^ . + "verify_tensor_offsets"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_tinyllama_multitoken.rs"^^ . + "false"^^ . + "verify_tinyllama_multitoken"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/verify_v_parallel_matvec.rs"^^ . + "false"^^ . + "verify_v_parallel_matvec"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/weight_check.rs"^^ . + "false"^^ . + "weight_check"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/wgpu_parity_test.rs"^^ . + "false"^^ . + "wgpu_parity_test"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-serve"^^ . + "crates/aprender-serve/examples/wine_lambda.rs"^^ . + "true"^^ . + "wine_lambda"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/contract_pipeline_demo.rs"^^ . + "true"^^ . + "contract_pipeline_demo"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/edd_falsification.rs"^^ . + "true"^^ . + "edd_falsification"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/edd_model_card.rs"^^ . + "true"^^ . + "edd_model_card"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/edd_operations.rs"^^ . + "true"^^ . + "edd_operations"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/edd_tps_validation.rs"^^ . + "true"^^ . + "edd_tps_validation"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/edd_yaml_loader.rs"^^ . + "true"^^ . + "edd_yaml_loader"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/gui_coverage_demo.rs"^^ . + "true"^^ . + "gui_coverage_demo"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/jidoka_guards.rs"^^ . + "true"^^ . + "jidoka_guards"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/monte_carlo.rs"^^ . + "true"^^ . + "monte_carlo"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/optimization.rs"^^ . + "true"^^ . + "optimization"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/orbit_demo.rs"^^ . + "true"^^ . + "orbit_demo"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/physics_simulation.rs"^^ . + "true"^^ . + "physics_simulation"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/reproducibility.rs"^^ . + "true"^^ . + "reproducibility"^^ . + "false"^^ . + . + . + "aprender-simulate"^^ . + "crates/aprender-simulate/examples/tsp_grasp_demo.rs"^^ . + "true"^^ . + "tsp_grasp_demo"^^ . + "false"^^ . + . + . + "aprender-solve"^^ . + "crates/aprender-solve/examples/solver_demo.rs"^^ . + "true"^^ . + "solver_demo"^^ . + "false"^^ . + . + . + "aprender-sparse"^^ . + "crates/aprender-sparse/examples/sparse_spmv.rs"^^ . + "true"^^ . + "sparse_spmv"^^ . + "false"^^ . + . + . + "aprender-tensor"^^ . + "crates/aprender-tensor/examples/tensor_demo.rs"^^ . + "true"^^ . + "tensor_demo"^^ . + "false"^^ . + . + . + "aprender-test-cli"^^ . + "crates/aprender-test-cli/examples/stress_testing.rs"^^ . + "true"^^ . + "stress_testing"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/accessibility_demo.rs"^^ . + "true"^^ . + "accessibility_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/audio_emulation.rs"^^ . + "true"^^ . + "audio_emulation"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/basic_test.rs"^^ . + "true"^^ . + "basic_test"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/brick_demo.rs"^^ . + "true"^^ . + "brick_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/brick_tui_demo.rs"^^ . + "true"^^ . + "brick_tui_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/comply_demo.rs"^^ . + "true"^^ . + "comply_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/coverage_demo.rs"^^ . + "true"^^ . + "coverage_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/distributed_worker_demo.rs"^^ . + "true"^^ . + "distributed_worker_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/docker_demo.rs"^^ . + "true"^^ . + "docker_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/element_assertions.rs"^^ . + "true"^^ . + "element_assertions"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/equation_verify.rs"^^ . + "true"^^ . + "equation_verify"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/execution_trace.rs"^^ . + "true"^^ . + "execution_trace"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/gui_coverage.rs"^^ . + "true"^^ . + "gui_coverage"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/locator_demo.rs"^^ . + "true"^^ . + "locator_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/locator_operations.rs"^^ . + "true"^^ . + "locator_operations"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/mouse_actions.rs"^^ . + "true"^^ . + "mouse_actions"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/multi_context.rs"^^ . + "true"^^ . + "multi_context"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/network_intercept.rs"^^ . + "true"^^ . + "network_intercept"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/page_object.rs"^^ . + "true"^^ . + "page_object"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/panic_paths_demo.rs"^^ . + "true"^^ . + "panic_paths_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/performance_profile.rs"^^ . + "true"^^ . + "performance_profile"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/pixel_coverage_heatmap.rs"^^ . + "true"^^ . + "pixel_coverage_heatmap"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/playbook_testing.rs"^^ . + "true"^^ . + "playbook_testing"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/pmat_bridge_demo.rs"^^ . + "true"^^ . + "pmat_bridge_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/pong_simulation.rs"^^ . + "true"^^ . + "pong_simulation"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/presentar_demo.rs"^^ . + "true"^^ . + "presentar_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/retry_assertions.rs"^^ . + "true"^^ . + "retry_assertions"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/semantic_locators.rs"^^ . + "true"^^ . + "semantic_locators"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/soft_assertions.rs"^^ . + "true"^^ . + "soft_assertions"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/streaming_ux_demo.rs"^^ . + "true"^^ . + "streaming_ux_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/visual_regression_demo.rs"^^ . + "true"^^ . + "visual_regression_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/wait_mechanisms.rs"^^ . + "true"^^ . + "wait_mechanisms"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/wasm_capabilities.rs"^^ . + "true"^^ . + "wasm_capabilities"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/wasm_pixel_gui_demo.rs"^^ . + "true"^^ . + "wasm_pixel_gui_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/watch_mode.rs"^^ . + "true"^^ . + "watch_mode"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/web_builders_demo.rs"^^ . + "true"^^ . + "web_builders_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/web_sys_gen_demo.rs"^^ . + "true"^^ . + "web_sys_gen_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/web_validation_demo.rs"^^ . + "true"^^ . + "web_validation_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/websocket_monitor.rs"^^ . + "true"^^ . + "websocket_monitor"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/worker_brick_demo.rs"^^ . + "true"^^ . + "worker_brick_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/worker_harness_demo.rs"^^ . + "true"^^ . + "worker_harness_demo"^^ . + "false"^^ . + . + . + "aprender-test-lib"^^ . + "crates/aprender-test-lib/examples/zero_js_demo.rs"^^ . + "true"^^ . + "zero_js_demo"^^ . + "false"^^ . + . + . + "aprender-test-showcase"^^ . + "crates/aprender-test-showcase/examples/calculator_tui.rs"^^ . + "true"^^ . + "calculator_tui"^^ . + "false"^^ . + . + . + "aprender-test-showcase"^^ . + "crates/aprender-test-showcase/examples/calculator_tui_demo.rs"^^ . + "true"^^ . + "calculator_tui_demo"^^ . + "false"^^ . + . + . + "aprender-test-showcase"^^ . + "crates/aprender-test-showcase/examples/calculator_wasm_demo.rs"^^ . + "true"^^ . + "calculator_wasm_demo"^^ . + "false"^^ . + . + . + "aprender-test-showcase"^^ . + "crates/aprender-test-showcase/examples/gui_coverage_report.rs"^^ . + "true"^^ . + "gui_coverage_report"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/calibration_check.rs"^^ . + "true"^^ . + "calibration_check"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/citl.rs"^^ . + "true"^^ . + "citl"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/classify_tune_demo.rs"^^ . + "false"^^ . + "classify_tune_demo"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cli_audit.rs"^^ . + "true"^^ . + "cli_audit"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cli_bench.rs"^^ . + "true"^^ . + "cli_bench"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cli_inspect.rs"^^ . + "true"^^ . + "cli_inspect"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cli_monitor.rs"^^ . + "true"^^ . + "cli_monitor"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cluster_training.rs"^^ . + "true"^^ . + "cluster_training"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/contract_pipeline_demo.rs"^^ . + "true"^^ . + "contract_pipeline_demo"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cuda_backend.rs"^^ . + "true"^^ . + "cuda_backend"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/cuda_training_benchmark.rs"^^ . + "true"^^ . + "cuda_training_benchmark"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/design_by_contract.rs"^^ . + "false"^^ . + "design_by_contract"^^ . + "llama"^^ . + "mistral"^^ . + "qwen2"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/distillation.rs"^^ . + "true"^^ . + "distillation"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/drift_simulation.rs"^^ . + "true"^^ . + "drift_simulation"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/encoder_linear_probe.rs"^^ . + "true"^^ . + "encoder_linear_probe"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/explainability.rs"^^ . + "true"^^ . + "explainability"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/finetune_real.rs"^^ . + "false"^^ . + "finetune_real"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/finetune_test_gen.rs"^^ . + "false"^^ . + "finetune_test_gen"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/gemm_bench.rs"^^ . + "true"^^ . + "gemm_bench"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/gpu_ledger.rs"^^ . + "true"^^ . + "gpu_ledger"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/hf_distillation.rs"^^ . + "false"^^ . + "hf_distillation"^^ . + "llama"^^ . + "tinyllama"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/inference_monitor.rs"^^ . + "true"^^ . + "inference_monitor"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/merge_models.rs"^^ . + "true"^^ . + "merge_models"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/mnist_train.rs"^^ . + "true"^^ . + "mnist_train"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/mnist_train_gpu.rs"^^ . + "true"^^ . + "mnist_train_gpu"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/model_io.rs"^^ . + "true"^^ . + "model_io"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/monitoring.rs"^^ . + "true"^^ . + "monitoring"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/multi_adapter_training.rs"^^ . + "true"^^ . + "multi_adapter_training"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/nvml_test.rs"^^ . + "true"^^ . + "nvml_test"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/open_asr_leaderboard.rs"^^ . + "true"^^ . + "open_asr_leaderboard"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/profile_cuda_trainer.rs"^^ . + "true"^^ . + "profile_cuda_trainer"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/pruning_pipeline.rs"^^ . + "true"^^ . + "pruning_pipeline"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/research.rs"^^ . + "true"^^ . + "research"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/shell_safety_classify.rs"^^ . + "false"^^ . + "shell_safety_classify"^^ . + "qwen2"^^ . + "qwen2.5"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/sovereign.rs"^^ . + "true"^^ . + "sovereign"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/ssc_eval.rs"^^ . + "false"^^ . + "ssc_eval"^^ . + "qwen3"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/ssc_preflight.rs"^^ . + "false"^^ . + "ssc_preflight"^^ . + "qwen3"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/train_from_yaml.rs"^^ . + "true"^^ . + "train_from_yaml"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/train_from_yaml_example.rs"^^ . + "true"^^ . + "train_from_yaml_example"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/training_loop.rs"^^ . + "true"^^ . + "training_loop"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/wgpu_canary.rs"^^ . + "true"^^ . + "wgpu_canary"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/wgpu_eval.rs"^^ . + "false"^^ . + "wgpu_eval"^^ . + "qwen3"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/wgpu_train.rs"^^ . + "false"^^ . + "wgpu_train"^^ . + "qwen3"^^ . + "false"^^ . + . + . + "aprender-train"^^ . + "crates/aprender-train/examples/wgpu_train_step.rs"^^ . + "true"^^ . + "wgpu_train_step"^^ . + "false"^^ . + . + . + "aprender-tsp"^^ . + "crates/aprender-tsp/examples/aprender-tsp.rs"^^ . + "true"^^ . + "aprender-tsp"^^ . + "false"^^ . + . + . + "aprender-tsp"^^ . + "crates/aprender-tsp/examples/tsp_algorithm_comparison.rs"^^ . + "true"^^ . + "tsp_algorithm_comparison"^^ . + "false"^^ . + . + . + "aprender-tsp"^^ . + "crates/aprender-tsp/examples/tsp_benchmark.rs"^^ . + "true"^^ . + "tsp_benchmark"^^ . + "false"^^ . + . + . + "aprender-tsp"^^ . + "crates/aprender-tsp/examples/tsp_model_persistence.rs"^^ . + "true"^^ . + "tsp_model_persistence"^^ . + "false"^^ . + . + . + "aprender-verify-ml"^^ . + "crates/aprender-verify-ml/examples/backend_selector.rs"^^ . + "true"^^ . + "backend_selector"^^ . + "false"^^ . + . + . + "aprender-verify-ml"^^ . + "crates/aprender-verify-ml/examples/experiment_tracking.rs"^^ . + "true"^^ . + "experiment_tracking"^^ . + "false"^^ . + . + . + "aprender-verify-ml"^^ . + "crates/aprender-verify-ml/examples/generator_basic.rs"^^ . + "true"^^ . + "generator_basic"^^ . + "false"^^ . + . + . + "aprender-verify-ml"^^ . + "crates/aprender-verify-ml/examples/grammar_validation.rs"^^ . + "true"^^ . + "grammar_validation"^^ . + "false"^^ . + . + . + "aprender-verify"^^ . + "crates/aprender-verify/examples/quality_check.rs"^^ . + "true"^^ . + "quality_check"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/aprender_integration.rs"^^ . + "true"^^ . + "aprender_integration"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/box_violin.rs"^^ . + "true"^^ . + "box_violin"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/confusion_matrix_ml.rs"^^ . + "true"^^ . + "confusion_matrix_ml"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/dashboard_widgets.rs"^^ . + "true"^^ . + "dashboard_widgets"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/force_graph.rs"^^ . + "true"^^ . + "force_graph"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/grammar_of_graphics.rs"^^ . + "true"^^ . + "grammar_of_graphics"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/heatmap_correlation.rs"^^ . + "true"^^ . + "heatmap_correlation"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/loss_training.rs"^^ . + "true"^^ . + "loss_training"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/readme_demo.rs"^^ . + "true"^^ . + "readme_demo"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/roc_pr_curves.rs"^^ . + "true"^^ . + "roc_pr_curves"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/scatter_basic.rs"^^ . + "true"^^ . + "scatter_basic"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/svg_output.rs"^^ . + "true"^^ . + "svg_output"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/terminal_output.rs"^^ . + "true"^^ . + "terminal_output"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/text_prompt.rs"^^ . + "true"^^ . + "text_prompt"^^ . + "false"^^ . + . + . + "aprender-viz"^^ . + "crates/aprender-viz/examples/trueno_graph_integration.rs"^^ . + "true"^^ . + "trueno_graph_integration"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/compress_benchmark.rs"^^ . + "true"^^ . + "compress_benchmark"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/debug_batch_compressor.rs"^^ . + "true"^^ . + "debug_batch_compressor"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/debug_decompress.rs"^^ . + "true"^^ . + "debug_decompress"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/decompress_throughput.rs"^^ . + "true"^^ . + "decompress_throughput"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/g119_parallel_benchmark.rs"^^ . + "true"^^ . + "g119_parallel_benchmark"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/gpu_decompress_benchmark.rs"^^ . + "true"^^ . + "gpu_decompress_benchmark"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/gpu_info.rs"^^ . + "true"^^ . + "gpu_info"^^ . + "false"^^ . + . + . + "aprender-zram-core"^^ . + "crates/aprender-zram-core/examples/parallel_scaling.rs"^^ . + "true"^^ . + "parallel_scaling"^^ . + "false"^^ . + . + . + . + . + "3022"^^ . + "paiml/aprender#3022@2026-09-10T03:13:40Z"^^ . + "paiml/aprender"^^ . + "CLOSED"^^ . + "apr chat silently loads the toy Demo model for sharded SafeTensors (.safetensors.index.json) instead of the real model"^^ . + "2026-09-10T03:13:40Z"^^ . + "https://github.com/paiml/aprender/issues/3022"^^ . + . + "oxide"^^ . + "8.9"^^ . + "1"^^ . + "lambda"^^ . + "0.0000009537"^^ . + "2.458"^^ . + "2.734"^^ . + "Cuda compilation tools, release 12.8, V12.8.93"^^ . + "21"^^ . + "91969973aac74cf031d14010d0f4a2938c2b1161"^^ . + . + "oxide"^^ . + "8.9"^^ . + "1"^^ . + "lambda"^^ . + "0.0000009537"^^ . + "2.456"^^ . + "2.764"^^ . + "Cuda compilation tools, release 12.8, V12.8.93"^^ . + "21"^^ . + "91969973aac74cf031d14010d0f4a2938c2b1161"^^ . + . + "oxide"^^ . + "8.9"^^ . + "1"^^ . + "lambda"^^ . + "0.0000009537"^^ . + "2.478"^^ . + "2.765"^^ . + "Cuda compilation tools, release 12.8, V12.8.93"^^ . + "21"^^ . + "91969973aac74cf031d14010d0f4a2938c2b1161"^^ . + . + "oxide"^^ . + "8.9"^^ . + "1"^^ . + "lambda"^^ . + "0.0000009537"^^ . + "2.447"^^ . + "2.754"^^ . + "Cuda compilation tools, release 12.8, V12.8.93"^^ . + "21"^^ . + "91969973aac74cf031d14010d0f4a2938c2b1161"^^ . + . + "bash scripts/check_package_includes.sh"^^ . + "APR CLI (`cargo install aprender`)"^^ . + "Build Commands"^^ . + "Debugging: Use apr Tools First (MANDATORY)"^^ . + "bashrs"^^ . + "cargo"^^ . + "gh"^^ . + "pmat"^^ . + "pv"^^ . + "Architecture"^^ . + "CI/CD (`.github/workflows/`)"^^ . + "Key Files"^^ . + "Modules"^^ . + "**Never:** work around `pv` with a shell script. The in-tree tool is the source of truth."^^ . + "Project Overview"^^ . + ".cargo/"^^ . + ".github/workflows/"^^ . + ".github/workflows/ci.yml"^^ . + ".github/workflows/coverage-nightly.yml"^^ . + "ci/explicit-test-commands.d/"^^ . + "contracts/"^^ . + "contracts/apr-cli-commands-v1.yaml"^^ . + "contracts/beat-ollama-decode-throughput-speed-v1.yaml"^^ . + "contracts/claude-md.yaml"^^ . + "contracts/tensor-layout-v1.yaml"^^ . + "crates/"^^ . + "crates/apr-cli/"^^ . + "crates/apr-cli/Cargo.toml"^^ . + "crates/aprender-compute"^^ . + "crates/aprender-compute/"^^ . + "crates/aprender-contracts-cli/"^^ . + "crates/aprender-contracts-macros/"^^ . + "crates/aprender-contracts/"^^ . + "crates/aprender-contracts/src/schema/"^^ . + "crates/aprender-core/"^^ . + "crates/aprender-core/src/format/"^^ . + "crates/aprender-core/src/format/converter/mod.rs"^^ . + "crates/aprender-core/src/format/converter/write.rs"^^ . + "crates/aprender-core/src/format/layout_contract.rs"^^ . + "crates/aprender-core/src/lib.rs"^^ . + "crates/aprender-core/src/primitives/"^^ . + "crates/aprender-core/src/text/chat_template/"^^ . + "crates/aprender-core/src/traits.rs"^^ . + "crates/aprender-core/tests/readme_contract.rs"^^ . + "crates/aprender-serve/"^^ . + "crates/aprender-serve/Cargo.toml"^^ . + "crates/aprender-serve/src/gguf/inference/fused_matmul_into.rs"^^ . + "crates/aprender-serve/src/inference_trace/"^^ . + "crates/aprender-serve/src/inference_trace/mod.rs"^^ . + "crates/aprender-serve/src/quantize/fused_gate_up.rs"^^ . + "crates/aprender-train/"^^ . + "docs/BEATS.md"^^ . + "docs/specifications/aprender-monorepo-consolidation.md"^^ . + "scripts/apr_bin.sh"^^ . + "scripts/bump-version.sh"^^ . + "scripts/check_clippy_current_stable.sh"^^ . + "scripts/check_msrv.sh"^^ . + "scripts/check_sourced_libs_option_neutral.sh"^^ . + "src/"^^ . + "src/bin/"^^ . + "src/bin/apr.rs"^^ . + "src/lib.rs"^^ . + "Autonomous Operation Mode (DEFAULT)"^^ . + "CRITICAL: Code Search Policy"^^ . + "CRITICAL: Realizar-First Architecture"^^ . + "Contract Validation: DOGFOOD `pv`, NEVER bash"^^ . + "Git Workflow (Branch Protection)"^^ . + "LAYOUT-001/002: Tensor Layout Safety"^^ . + "Linting"^^ . + "Publishing Safety (CB-510 Lesson)"^^ . + "Shell Scripts: Use bashrs (NOT shellcheck)"^^ . + "Verification Discipline (MANDATORY — read before reporting any result)"^^ . + "APR CLI (`cargo install aprender`)"^^ . + "Architecture"^^ . + "Autonomous Operation Mode (DEFAULT)"^^ . + "Build Commands"^^ . + "CI/CD (`.github/workflows/`)"^^ . + "CRITICAL: Code Search Policy"^^ . + "CRITICAL: Realizar-First Architecture"^^ . + "Contract Validation: DOGFOOD `pv`, NEVER bash"^^ . + "Debugging: Use apr Tools First (MANDATORY)"^^ . + "Git Workflow (Branch Protection)"^^ . + "Key Files"^^ . + "LAYOUT-001/002: Tensor Layout Safety"^^ . + "Linting"^^ . + "Modules"^^ . + "PMAT Quality Analysis"^^ . + "Project Overview"^^ . + "Publishing Safety (CB-510 Lesson)"^^ . + "SSC Training Infrastructure Status (snapshot 2026-03-22 — STALE, re-verify before acting)"^^ . + "Shell Scripts: Use bashrs (NOT shellcheck)"^^ . + "Stack Documentation Search"^^ . + "Testing"^^ . + "Verification Discipline (MANDATORY — read before reporting any result)"^^ . + . + . + . + "3"^^ . + "paiml/aprender#3@2026-09-24T07:03:22Z"^^ . + "paiml/aprender"^^ . + "CLOSED"^^ . + "0.66.0"^^ . + "2026-09-24T07:03:22Z"^^ . + "https://github.com/paiml/aprender/milestone/3"^^ . + . . . "qwen35"^^ . @@ -9498,6 +21195,7 @@ . "true"^^ . "00fe7986ff5f6b463e62455821146049db6f9313603938a70800d1fb69ef11a4"^^ . + . . . "qwen35"^^ . @@ -9517,6 +21215,15 @@ . "true"^^ . "03b74727a860a56338e042c4420bb3f04b2fec5734175f4cb9fa853daf52b7e8"^^ . + . + . + "2.0"^^ . + . + "crates/aprender-core/tests/fixtures/setfit/slice_model.apr"^^ . + "apr"^^ . + "4a75ee8031b3fbe0b17e1706a248aabbf158420c06f082bc29a89e2103d4057e"^^ . + "37"^^ . + . . . "qwen2"^^ . @@ -9527,6 +21234,7 @@ "gguf"^^ . "gx10"^^ . "lambda"^^ . + "lambda"^^ . "qwen2-1.5b-q4km"^^ . . . @@ -9536,6 +21244,7 @@ . "true"^^ . "6a1a2eb6d15622bf3c96857206351ba97e1af16c30d7a74ee38970e434e9407e"^^ . + . . . "qwen35"^^ . @@ -9555,6 +21264,7 @@ . "true"^^ . "84b5f7f112156d63836a01a69dc3f11a6ba63b10a23b8ca7a7efaf52d5a2d806"^^ . + . . . "qwen35"^^ . @@ -9574,6 +21284,7 @@ . "true"^^ . "aaf42c8b7c3cab2bf3d69c355048d4a0ee9973d48f16c731c0520ee914699223"^^ . + . . . "qwen3"^^ . @@ -9584,6 +21295,7 @@ "gguf"^^ . "gx10"^^ . "lambda"^^ . + "lambda"^^ . "qwen3-1.7b-q4km"^^ . . . @@ -9593,6 +21305,7 @@ . "true"^^ . "b139949c5bd74937ad8ed8c8cf3d9ffb1e99c866c823204dc42c0d91fa181897"^^ . + . . . "qwen35"^^ . @@ -9612,6 +21325,7 @@ . "true"^^ . "bd258782e35f7f458f8aced1adc053e6e92e89bc735ba3be89d38a06121dc517"^^ . + . . . "qwen3"^^ . @@ -9622,6 +21336,7 @@ "gguf"^^ . "gx10"^^ . "lambda"^^ . + "lambda"^^ . "qwen3-8b-q4km"^^ . . . @@ -9738,6 +21453,50 @@ "model_sha256: NOT RECORDED at measurement time and deliberately not back-filled. Hashing the file on the host today would attach a claim about a different world to a receipt about 2026-09-09. Absent means no measurement, never a match (ONT-4c1)."^^ . . . + . + . + . + "main"^^ . + . + "PMAT-3704-ont-4c3-pc-extract"^^ . + "2026-09-21T16:17:54Z"^^ . + "3706"^^ . + "paiml/aprender#3706@2026-09-21T16:17:56Z"^^ . + "paiml/aprender"^^ . + "MERGED"^^ . + "PMAT-3704 / ONT-4c3: pc_extract draws one planted defect per implemented extractor — parity-receipt, json and pv-contract gain their controls, and the set is tied to Σ"^^ . + "2026-09-21T16:17:56Z"^^ . + "https://github.com/paiml/aprender/pull/3706"^^ . + . + "1889"^^ . + "crates/aprender-contracts-cli"^^ . + "library"^^ . + "1.0"^^ . + "A Qwen story"^^ . + "Architecture"^^ . + "At HEAD"^^ . + "Beat 1 — Discover (Registry)"^^ . + "Beat 2 — Trust (QA gates)"^^ . + "Beat 3 — Explore (Inspection)"^^ . + "Beat 4 — Adapt (Model ops)"^^ . + "Beat 5 — Use (Inference)"^^ . + "Beat 6 — Serve (REST API)"^^ . + "Beat 7 — Operate (Profiling)"^^ . + "Beat 8 — Scale (MoE introspection)"^^ . + "Command surface"^^ . + "Contributing"^^ . + "Cookbook"^^ . + "Install"^^ . + "Library usage"^^ . + "License"^^ . + "Migration from old crates"^^ . + "Performance"^^ . + "Provable contracts"^^ . + "Publish (separate flow)"^^ . + "Quick Start"^^ . + "Verified this release"^^ . + "What is Aprender?"^^ . + "bash scripts/check_readme_claims.sh"^^ . . "cpu=ok"^^ . "cuda=ok"^^ . @@ -10372,7 +22131,16 @@ "apr bench refuses qwen35 outright: the subcommand routes through the dense QuantizedGGUFTransformer loader, which does not carry the Gated-DeltaNet hybrid, so the benchmark verb cannot measure the architecture the last release shipped."^^ . "unscheduled"^^ . "bench"^^ . + . + . + . + "main"^^ . + "paiml/aprender"^^ . + "paiml/aprender@aa7c6ef03ee7b7f8d8dc09dc393e97619952d95f"^^ . + "aa7c6ef03ee7b7f8d8dc09dc393e97619952d95f"^^ . + "https://github.com/paiml/aprender"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10387,6 +22155,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10400,13 +22169,29 @@ "run"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "byte_encoder_coverage"^^ . + "crates/apr-cli/src/commands/chat_load_tokenizers.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::chat::realizar_chat"^^ . + "load_tokenizers"^^ . + "true"^^ . + "private"^^ . . + . . "allow"^^ . "doc"^^ . "provable_contracts_macros::contract"^^ . "implemented"^^ . "apr_cli"^^ . + "session_persistence"^^ . "session_state_machine"^^ . "crates/apr-cli/src/commands/chat.rs"^^ . . @@ -10415,27 +22200,8 @@ "run"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "byte_encoder_coverage"^^ . - . - "apr_cli::commands::chat_load_tokenizers"^^ . - "load_tokenizers"^^ . - "false"^^ . - "no `mod chat_load_tokenizers` or `use … chat_load_tokenizers` in `crates/apr-cli/src/commands/mod.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "session_persistence"^^ . - . - "apr_cli::commands::chat_session"^^ . - "run"^^ . - "false"^^ . - "no `mod chat_session` or `use … chat_session` in `crates/apr-cli/src/commands/mod.rs`"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10449,18 +22215,53 @@ "run"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "data_split_determinism"^^ . - "data_validation"^^ . - . - "apr_cli::commands::data"^^ . - "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/data.rs`"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "apr_cli"^^ . + "format_conversion_roundtrip"^^ . + "crates/apr-cli/src/commands/convert.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::convert"^^ . + "run"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "apr_cli"^^ . + "data_validation"^^ . + "crates/apr-cli/src/commands/data.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::data"^^ . + "run_audit"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "data_split_determinism"^^ . + "crates/apr-cli/src/commands/data.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::data"^^ . + "run_split"^^ . + "true"^^ . + "pub(crate)"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10475,17 +22276,8 @@ "run"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "encryption_roundtrip"^^ . - . - "apr_cli::commands::encrypt"^^ . - "run"^^ . - "false"^^ . - "no `mod encrypt` or `use … encrypt` in `crates/apr-cli/src/commands/mod.rs`"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10499,6 +22291,7 @@ "true"^^ . "pub(super)"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10506,16 +22299,19 @@ "implemented"^^ . "apr_cli"^^ . "atomic_write_safety"^^ . + "export_fidelity"^^ . "export_roundtrip"^^ . "crates/apr-cli/src/commands/export.rs"^^ . . . + . "fn"^^ . "apr_cli::commands::export"^^ . "run"^^ . "true"^^ . "pub(crate)"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10530,6 +22326,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10543,6 +22340,7 @@ "true"^^ . "private"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10563,6 +22361,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "provable_contracts_macros::contract"^^ . "implemented"^^ . @@ -10576,6 +22375,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10590,20 +22390,24 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . "implemented"^^ . "apr_cli"^^ . "import_format_detection"^^ . + "import_integrity"^^ . "crates/apr-cli/src/commands/import.rs"^^ . . + . "fn"^^ . "apr_cli::commands::import"^^ . "run"^^ . "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10618,6 +22422,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10633,26 +22438,39 @@ "true"^^ . "pub(crate)"^^ . . + . . + "allow"^^ . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "apr_cli"^^ . "oracle_family_detection"^^ . + "crates/apr-cli/src/commands/oracle_flags.rs"^^ . . + "fn"^^ . "apr_cli::commands::oracle"^^ . "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/oracle.rs`"^^ . + "true"^^ . + "pub(crate)"^^ . . + . . + "cfg"^^ . + "cfg_attr"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "apr_cli"^^ . "gpu_cpu_parity"^^ . + "crates/apr-cli/src/commands/parity_03.rs"^^ . . + "fn"^^ . "apr_cli::commands::parity"^^ . "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/parity.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10667,6 +22485,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10682,6 +22501,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10695,17 +22515,24 @@ "run"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "publish_manifest_integrity"^^ . - . - "apr_cli::commands::publish"^^ . - "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/publish.rs`"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "apr_cli"^^ . + "publish_manifest_integrity"^^ . + "crates/apr-cli/src/commands/publish.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::publish"^^ . + "execute"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -10720,6 +22547,7 @@ "true"^^ . "pub"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10735,6 +22563,7 @@ "true"^^ . "pub"^^ . . + . . "allow"^^ . "doc"^^ . @@ -10749,17 +22578,21 @@ "run"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "rosetta_fingerprint_determinism"^^ . - . - "apr_cli::commands::rosetta"^^ . - "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/rosetta.rs`"^^ . + . + . + . + "implemented"^^ . + "apr_cli"^^ . + "rosetta_fingerprint_determinism"^^ . + "crates/apr-cli/src/commands/rosetta_diff_tensor.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::rosetta"^^ . + "run_fingerprint"^^ . + "true"^^ . + "pub"^^ . . + . . "cfg"^^ . "doc"^^ . @@ -10774,6 +22607,7 @@ "true"^^ . "private"^^ . . + . . "cfg"^^ . "doc"^^ . @@ -10788,6 +22622,7 @@ "true"^^ . "private"^^ . . + . . "allow"^^ . "cfg"^^ . @@ -10803,12 +22638,14 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . "implemented"^^ . "apr_cli"^^ . "concurrent_isolation"^^ . + "concurrent_model_access"^^ . "cors_negotiation"^^ . "graceful_shutdown"^^ . "request_routing"^^ . @@ -10817,6 +22654,7 @@ "session_state_machine"^^ . "crates/apr-cli/src/commands/serve/mod.rs"^^ . . + . . . . @@ -10826,6 +22664,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10839,6 +22678,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10851,85 +22691,104 @@ "format_tools_prompt"^^ . "true"^^ . "pub(super)"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "tokenizer_training_correctness"^^ . - . - "apr_cli::commands::tokenize"^^ . - "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/tokenize.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "training_plan_apply_semantics"^^ . - . - "apr_cli::commands::train"^^ . - "run"^^ . - "false"^^ . - "no `fn run` (free or in an impl) in `crates/apr-cli/src/commands/train.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "dispatch_completeness"^^ . - "output_format_fidelity"^^ . - "resource_cleanup"^^ . - "side_effect_classification"^^ . - . - . - "apr_cli::dispatch"^^ . - "dispatch_core_command"^^ . - "false"^^ . - "no `mod dispatch` or `use … dispatch` in `crates/apr-cli/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "idempotent_inspection"^^ . - "idempotent_output"^^ . - "no_side_effects"^^ . - . - . - "apr_cli::dispatch"^^ . - "dispatch_inspection_commands"^^ . - "false"^^ . - "no `mod dispatch` or `use … dispatch` in `crates/apr-cli/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "output_path_validation"^^ . - "rm_confirmation_gate"^^ . - . - "apr_cli::dispatch"^^ . - "dispatch_model_commands"^^ . - "false"^^ . - "no `mod dispatch` or `use … dispatch` in `crates/apr-cli/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "contract_gate_enforcement"^^ . - . - "apr_cli::dispatch"^^ . - "execute_command"^^ . - "false"^^ . - "no `mod dispatch` or `use … dispatch` in `crates/apr-cli/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "apr_cli"^^ . - "feature_gated_dispatch"^^ . - . - "apr_cli::dispatch_analysis"^^ . - "dispatch_extended_command"^^ . - "false"^^ . - "no `mod dispatch_analysis` or `use … dispatch_analysis` in `crates/apr-cli/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "apr_cli"^^ . + "tokenizer_training_correctness"^^ . + "crates/apr-cli/src/commands/tokenize.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::tokenize"^^ . + "run_apply"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "training_plan_apply_semantics"^^ . + "crates/apr-cli/src/commands/train.rs"^^ . + . + "fn"^^ . + "apr_cli::commands::train"^^ . + "run_apply"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "dispatch_completeness"^^ . + "output_format_fidelity"^^ . + "resource_cleanup"^^ . + "side_effect_classification"^^ . + "crates/apr-cli/src/dispatch.rs"^^ . + . + . + "fn"^^ . + "apr_cli"^^ . + "dispatch_core_command"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "feature_gated_dispatch"^^ . + "crates/apr-cli/src/dispatch_analysis.rs"^^ . + . + "fn"^^ . + "apr_cli"^^ . + "dispatch_extended_command"^^ . + "true"^^ . + "private"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "idempotent_inspection"^^ . + "idempotent_output"^^ . + "no_side_effects"^^ . + "crates/apr-cli/src/dispatch.rs"^^ . + . + . + "fn"^^ . + "apr_cli"^^ . + "dispatch_inspection_commands"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "apr_cli"^^ . + "output_path_validation"^^ . + "rm_confirmation_gate"^^ . + "crates/apr-cli/src/dispatch.rs"^^ . + . + "fn"^^ . + "apr_cli"^^ . + "dispatch_model_commands"^^ . + "true"^^ . + "private"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10950,6 +22809,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10962,7 +22822,22 @@ "resolve_model_path"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "apr_cli"^^ . + "contract_gate_enforcement"^^ . + "crates/apr-cli/src/dispatch_run.rs"^^ . + . + "fn"^^ . + "apr_cli"^^ . + "execute_command"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -10975,7 +22850,130 @@ "with_stdin_support"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "gelu_exact"^^ . + "crates/aprender-core/src/autograd/ops/activation.rs"^^ . + . + "method"^^ . + "aprender::autograd::ops::activation"^^ . + "gelu_exact"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "matmul"^^ . + "crates/aprender-core/src/autograd/ops/activation.rs"^^ . + . + "method"^^ . + "aprender::autograd::ops::activation"^^ . + "matmul"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "embedding_gather"^^ . + "crates/aprender-core/src/autograd/ops/embedding.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::embedding"^^ . + "embedding_gather"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "additive_attention_mask"^^ . + "crates/aprender-core/src/autograd/ops/masking.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::masking"^^ . + "additive_attention_mask"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "l2_normalize_rows"^^ . + "crates/aprender-core/src/autograd/ops/normalize.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::normalize"^^ . + "l2_normalize_rows"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "masked_mean_pool"^^ . + "crates/aprender-core/src/autograd/ops/pooling.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::pooling"^^ . + "masked_mean_pool"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "cosine_similarity_rows"^^ . + "crates/aprender-core/src/autograd/ops/similarity.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::similarity"^^ . + "cosine_similarity_rows"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "mse_loss"^^ . + "crates/aprender-core/src/autograd/ops/similarity.rs"^^ . + . + "fn"^^ . + "aprender::autograd::ops::similarity"^^ . + "mse_loss"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -10990,6 +22988,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11005,6 +23004,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11020,6 +23020,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11034,6 +23035,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11047,6 +23049,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11060,6 +23063,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -11074,6 +23078,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -11088,6 +23093,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11100,7 +23106,38 @@ "predict_proba"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "update"^^ . + "crates/aprender-core/src/cluster/kmeans_impl.rs"^^ . + . + "method"^^ . + "aprender::cluster::kmeans_impl"^^ . + "fit"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "assignment"^^ . + "crates/aprender-core/src/cluster/kmeans_impl.rs"^^ . + . + "method"^^ . + "aprender::cluster::kmeans_impl"^^ . + "predict"^^ . + "true"^^ . + "private"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11115,29 +23152,10 @@ "select_backend"^^ . "true"^^ . "pub"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "format_conversion_roundtrip"^^ . - . - "aprender::convert"^^ . - "run"^^ . - "false"^^ . - "no `mod convert` or `use … convert` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "apr_tokenizer_embedding"^^ . - . - "aprender::convert"^^ . - "save_model_tensors_with_gguf_config_and_tokenizer"^^ . - "false"^^ . - "no `mod convert` or `use … convert` in `crates/aprender-core/src/lib.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "streaming_data_loader"^^ . . @@ -11145,37 +23163,51 @@ "next_batch"^^ . "false"^^ . "no `mod dataloader` or `use … dataloader` in `crates/aprender-core/src/data/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "export_fidelity"^^ . - . - "aprender::export"^^ . - "run"^^ . - "false"^^ . - "no `mod export` or `use … export` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "name_bijection"^^ . - . - "aprender::format::converter"^^ . - "map_tensor_name"^^ . - "false"^^ . - "no `fn map_tensor_name` (free or in an impl) in `crates/aprender-core/src/format/converter/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "identity"^^ . - . - "aprender::format::converter::tokenizer_loader"^^ . - "load_tokenizer_from_explicit_path"^^ . - "false"^^ . - "no `mod tokenizer_loader` or `use … tokenizer_loader` in `crates/aprender-core/src/format/converter/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "identity"^^ . + "crates/aprender-core/src/format/converter/tokenizer_loader.rs"^^ . + . + "fn"^^ . + "aprender::format::converter::import"^^ . + "load_tokenizer_from_explicit_path"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "apr_tokenizer_embedding"^^ . + "crates/aprender-core/src/format/converter/inferred_q4k_config.rs"^^ . + . + "fn"^^ . + "aprender::format::converter"^^ . + "save_model_tensors_with_gguf_config_and_tokenizer"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "name_bijection"^^ . + "crates/aprender-core/src/format/tensor_expectation.rs"^^ . + . + "method"^^ . + "aprender::format::converter_types::Architecture"^^ . + "map_name"^^ . + "true"^^ . + "pub"^^ . . + . . "cfg"^^ . "doc"^^ . @@ -11189,17 +23221,53 @@ "parse_and_validate_header"^^ . "true"^^ . "pub(crate)"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "architecture_config_invariants"^^ . - . - "aprender::format::gguf::api::GgufModelConfig"^^ . - "validate"^^ . - "false"^^ . - "no `mod GgufModelConfig` or `use … GgufModelConfig` in `crates/aprender-core/src/format/gguf/api.rs`"^^ . + . + . + . + "allow"^^ . + "cfg"^^ . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "encryption_roundtrip"^^ . + "crates/aprender-core/src/format/encryption.rs"^^ . + . + "fn"^^ . + "aprender::format::encryption"^^ . + "save_encrypted"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "architecture_config_invariants"^^ . + "crates/aprender-core/src/format/gguf/api.rs"^^ . + . + "method"^^ . + "aprender::format::gguf::api::GgufModelConfig"^^ . + "warn_out_of_bounds"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "f16_to_f32_bias"^^ . + "crates/aprender-core/src/format/gguf/dequant.rs"^^ . + . + "fn"^^ . + "aprender::format::gguf::dequant"^^ . + "f16_to_f32"^^ . + "true"^^ . + "pub(crate)"^^ . . + . . "implemented"^^ . "aprender"^^ . @@ -11212,46 +23280,59 @@ "true"^^ . "pub(crate)"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "transpose_involution"^^ . . "aprender::format::layout"^^ . "swap_axes"^^ . "false"^^ . - "no `mod layout` or `use … layout` in `crates/aprender-core/src/format/mod.rs`"^^ . + "no `mod layout` or `use … layout` in `crates/aprender-core/src/format/encryption.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "element_count"^^ . . "aprender::format::layout"^^ . "validate_element_count"^^ . "false"^^ . - "no `mod layout` or `use … layout` in `crates/aprender-core/src/format/mod.rs`"^^ . + "no `mod layout` or `use … layout` in `crates/aprender-core/src/format/encryption.rs`"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender"^^ . "import_completeness_gate"^^ . + "crates/aprender-core/src/format/layout_contract_enforce.rs"^^ . . + "fn"^^ . "aprender::format::layout_contract"^^ . "enforce_architecture_completeness"^^ . - "false"^^ . - "no `fn enforce_architecture_completeness` (free or in an impl) in `crates/aprender-core/src/format/layout_contract.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "must_use"^^ . "implemented"^^ . "aprender"^^ . "tensor_transpose_reindex"^^ . + "crates/aprender-core/src/format/layout_contract_specs.rs"^^ . . + "method"^^ . "aprender::format::layout_contract"^^ . "should_transpose_gguf"^^ . - "false"^^ . - "no `fn should_transpose_gguf` (free or in an impl) in `crates/aprender-core/src/format/layout_contract.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11266,6 +23347,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11280,6 +23362,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11294,6 +23377,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11308,6 +23392,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11321,9 +23406,23 @@ "throughput_model"^^ . "true"^^ . "pub"^^ . + . + . + . + "implemented"^^ . + "aprender"^^ . + "quantization_bounds"^^ . + "crates/aprender-core/src/format/quantize.rs"^^ . + . + "method"^^ . + "aprender::format"^^ . + "quantize_data"^^ . + "true"^^ . + "private"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "magic_byte_validation"^^ . . @@ -11331,19 +23430,10 @@ "detect_format"^^ . "false"^^ . "no `mod gguf` or `use … gguf` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "metadata_kv_safety"^^ . - . - "aprender::gguf::reader"^^ . - "parse_metadata"^^ . - "false"^^ . - "no `mod gguf` or `use … gguf` in `crates/aprender-core/src/lib.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "header_integrity"^^ . . @@ -11351,17 +23441,8 @@ "validate_header"^^ . "false"^^ . "no `mod gguf` or `use … gguf` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "magic_validation"^^ . - . - "aprender::gguf::reader"^^ . - "validate_magic"^^ . - "false"^^ . - "no `mod gguf` or `use … gguf` in `crates/aprender-core/src/lib.rs`"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11375,6 +23456,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11389,59 +23471,53 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . "implemented"^^ . "aprender"^^ . "global_max_pool"^^ . + "crates/aprender-core/src/gnn/accumulate.rs"^^ . . + "fn"^^ . "aprender::gnn"^^ . "global_max_pool"^^ . - "false"^^ . - "no `fn global_max_pool` (free or in an impl) in `crates/aprender-core/src/gnn/mod.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "must_use"^^ . "implemented"^^ . "aprender"^^ . "global_mean_pool"^^ . + "crates/aprender-core/src/gnn/accumulate.rs"^^ . . + "fn"^^ . "aprender::gnn"^^ . "global_mean_pool"^^ . - "false"^^ . - "no `fn global_mean_pool` (free or in an impl) in `crates/aprender-core/src/gnn/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "pagerank"^^ . - "power_iteration"^^ . - . - "aprender::graph"^^ . - "pagerank"^^ . - "false"^^ . - "no `fn pagerank` (free or in an impl) in `crates/aprender-core/src/graph/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "import_integrity"^^ . - . - "aprender::import"^^ . - "run"^^ . - "false"^^ . - "no `mod import` or `use … import` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "forward_pass_completeness"^^ . - . - "aprender::inference::forward"^^ . - "forward_pass"^^ . - "false"^^ . - "no `mod inference` or `use … inference` in `crates/aprender-core/src/lib.rs`"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "pagerank"^^ . + "power_iteration"^^ . + "crates/aprender-core/src/graph/centrality.rs"^^ . + . + "method"^^ . + "aprender::graph::centrality::Graph"^^ . + "pagerank"^^ . + "true"^^ . + "private"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "generation_temperature_zero"^^ . "inference_determinism"^^ . @@ -11451,7 +23527,38 @@ "generate"^^ . "false"^^ . "no `mod inference` or `use … inference` in `crates/aprender-core/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "sample"^^ . + "crates/aprender-core/src/metaheuristics/cmaes.rs"^^ . + . + "method"^^ . + "aprender::metaheuristics::cmaes"^^ . + "sample"^^ . + "true"^^ . + "private"^^ . + . + . + . + "allow"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "mean_update"^^ . + "crates/aprender-core/src/metaheuristics/cmaes_include_01.rs"^^ . + . + "method"^^ . + "aprender::metaheuristics::cmaes_include_01"^^ . + "optimize"^^ . + "true"^^ . + "private"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11467,16 +23574,23 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender"^^ . "confusion_matrix"^^ . + "crates/aprender-core/src/metrics/classification_include_01.rs"^^ . . + "fn"^^ . "aprender::metrics::classification"^^ . "confusion_matrix"^^ . - "false"^^ . - "no `fn confusion_matrix` (free or in an impl) in `crates/aprender-core/src/metrics/classification.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11492,6 +23606,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11507,6 +23622,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11522,6 +23638,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11537,6 +23654,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11552,6 +23670,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11567,6 +23686,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11582,6 +23702,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11596,6 +23717,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11610,6 +23732,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11624,6 +23747,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11638,6 +23762,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11653,6 +23778,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11666,6 +23792,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11681,46 +23808,80 @@ "true"^^ . "pub"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "cls_pooling"^^ . . "aprender::models::bert"^^ . "cls_embedding"^^ . "false"^^ . - "no `fn cls_embedding` (free or in an impl) in `crates/aprender-core/src/models/bert/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "encoder_layer"^^ . - . - "aprender::models::bert"^^ . - "forward"^^ . - "false"^^ . - "no `fn forward` (free or in an impl) in `crates/aprender-core/src/models/bert/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "swiglu_expansion"^^ . - . - "aprender::models::qwen2::constructors"^^ . - "new"^^ . - "false"^^ . - "no `mod constructors` or `use … constructors` in `crates/aprender-core/src/models/qwen2/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "total_parameters"^^ . - . - "aprender::models::qwen2::constructors"^^ . - "weight_names"^^ . - "false"^^ . - "no `mod constructors` or `use … constructors` in `crates/aprender-core/src/models/qwen2/mod.rs`"^^ . + "no `fn cls_embedding` (free or in an impl) or item `cls_embedding` in `crates/aprender-core/src/models/bert/mod.rs`"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "encoder_layer"^^ . + "crates/aprender-core/src/models/bert/layer.rs"^^ . + . + "method"^^ . + "aprender::models::bert::layer::BertLayer"^^ . + "forward"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "swiglu_expansion"^^ . + "crates/aprender-core/src/models/qwen2/constructors.rs"^^ . + . + "method"^^ . + "aprender::models::qwen2::Qwen2Model"^^ . + "new"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "total_parameters"^^ . + "crates/aprender-core/src/models/qwen2/constructors.rs"^^ . + . + "method"^^ . + "aprender::models::qwen2::Qwen2Model"^^ . + "weight_names"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "embedding_lookup"^^ . + "crates/aprender-core/src/models/qwen2/mod.rs"^^ . + . + "method"^^ . + "aprender::models::qwen2"^^ . + "forward"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11735,7 +23896,22 @@ "weight"^^ . "true"^^ . "pub"^^ . + . + . + . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "conv1d"^^ . + "crates/aprender-core/src/nn/conv/mod.rs"^^ . + . + "method"^^ . + "aprender::nn::conv"^^ . + "forward"^^ . + "true"^^ . + "private"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11750,7 +23926,24 @@ "gelu"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "log_softmax"^^ . + "crates/aprender-core/src/nn/functional.rs"^^ . + . + "fn"^^ . + "aprender::nn::functional"^^ . + "log_softmax"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11765,7 +23958,40 @@ "relu"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "sigmoid"^^ . + "crates/aprender-core/src/nn/functional.rs"^^ . + . + "fn"^^ . + "aprender::nn::functional"^^ . + "sigmoid"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "silu"^^ . + "crates/aprender-core/src/nn/functional.rs"^^ . + . + "fn"^^ . + "aprender::nn::functional"^^ . + "silu"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11783,6 +24009,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11798,6 +24025,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11811,27 +24039,138 @@ "with_top_p"^^ . "true"^^ . "pub"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "rope_position_encoding"^^ . - . - "aprender::nn::rope::RotaryPositionEmbedding"^^ . - "validate_config"^^ . - "false"^^ . - "no `mod rope` or `use … rope` in `crates/aprender-core/src/nn/mod.rs`"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "cross_entropy"^^ . + "crates/aprender-core/src/nn/loss.rs"^^ . + . + "method"^^ . + "aprender::nn::loss"^^ . + "forward"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "batchnorm_train"^^ . + "layernorm"^^ . + "crates/aprender-core/src/nn/normalization/mod.rs"^^ . + . + . + "method"^^ . + "aprender::nn::normalization"^^ . + "forward"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "aprender"^^ . + "rmsnorm"^^ . + "crates/aprender-core/src/nn/normalization/group_norm.rs"^^ . + . + "method"^^ . + "aprender::nn::normalization::group_norm"^^ . + "forward"^^ . + "true"^^ . + "private"^^ . + . + . + . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "weight_update"^^ . + "crates/aprender-core/src/nn/optim/rm_sprop.rs"^^ . + . + "method"^^ . + "aprender::nn::optim::rm_sprop"^^ . + "step"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "aprender"^^ . + "rope_position_encoding"^^ . + "crates/aprender-core/src/nn/transformer/attention_helpers.rs"^^ . + . + "method"^^ . + "aprender::nn::transformer::attention_helpers::RotaryPositionEmbedding"^^ . + "with_base"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "rope"^^ . + "crates/aprender-core/src/nn/transformer/attention_helpers.rs"^^ . + . + "method"^^ . + "aprender::nn::transformer::attention_helpers"^^ . + "apply"^^ . + "true"^^ . + "pub"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "aprender"^^ . "bidirectional_attention"^^ . . "aprender::nn::transformer"^^ . "bidirectional_attention"^^ . "false"^^ . - "no `fn bidirectional_attention` (free or in an impl) in `crates/aprender-core/src/nn/transformer/mod.rs`"^^ . + "no `fn bidirectional_attention` (free or in an impl) or item `bidirectional_attention` in `crates/aprender-core/src/nn/transformer/attention_helpers.rs`"^^ . + . + . + . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "gqa"^^ . + "crates/aprender-core/src/nn/transformer/mod.rs"^^ . + . + "method"^^ . + "aprender::nn::transformer"^^ . + "forward"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "apply_additive_mask"^^ . + "crates/aprender-core/src/nn/transformer/positional_encoding.rs"^^ . + . + "fn"^^ . + "aprender::nn::transformer::positional_encoding"^^ . + "add_mask"^^ . + "true"^^ . + "pub(super)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -11850,6 +24189,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -11864,6 +24204,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -11877,47 +24218,49 @@ "minimize"^^ . "true"^^ . "private"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "quantization_bounds"^^ . - . - "aprender::quantize"^^ . - "quantize_tensor"^^ . - "false"^^ . - "no `mod quantize` or `use … quantize` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "header_size_validation"^^ . - . - "aprender::safetensors::reader"^^ . - "validate_header"^^ . - "false"^^ . - "no `mod safetensors` or `use … safetensors` in `crates/aprender-core/src/lib.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "concurrent_model_access"^^ . - . - "aprender::serve"^^ . - "run"^^ . - "false"^^ . - "no `mod serve` or `use … serve` in `crates/aprender-core/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "aprender"^^ . + "apr_reader"^^ . + . + "aprender::serialization::apr::AprReader"^^ . + "read"^^ . + "false"^^ . + "no `fn read` in an `impl AprReader` or `trait AprReader` in `crates/aprender-core/src/serialization/apr/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "apr_writer"^^ . + "crates/aprender-core/src/serialization/apr/mod.rs"^^ . + . + "method"^^ . + "aprender::serialization::apr::AprWriter"^^ . + "write"^^ . + "true"^^ . + "pub"^^ . . + . . + "derive"^^ . + "doc"^^ . + "serde"^^ . "implemented"^^ . "aprender"^^ . "artifact_doc_schema"^^ . + "crates/aprender-core/src/setfit/artifact.rs"^^ . . + "struct"^^ . "aprender::setfit::artifact"^^ . "SetFitArtifactDoc"^^ . - "false"^^ . - "no `fn SetFitArtifactDoc` (free or in an impl) in `crates/aprender-core/src/setfit/artifact.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11932,6 +24275,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -11946,6 +24290,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11959,6 +24304,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11973,6 +24319,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11986,6 +24333,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -11999,6 +24347,7 @@ "true"^^ . "private"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12012,6 +24361,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12026,38 +24376,98 @@ "true"^^ . "pub"^^ . . + . . + "derive"^^ . + "doc"^^ . + "serde"^^ . "implemented"^^ . "aprender"^^ . "classify_response_schema"^^ . + "crates/aprender-core/src/setfit/classify.rs"^^ . . + "struct"^^ . "aprender::setfit::classify"^^ . "ClassifyResponse"^^ . - "false"^^ . - "no `fn ClassifyResponse` (free or in an impl) in `crates/aprender-core/src/setfit/classify.rs`"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "setfit_encoder_forward"^^ . + "crates/aprender-core/src/setfit/encoder.rs"^^ . + . + "method"^^ . + "aprender::setfit::encoder::BertSentenceEncoder"^^ . + "forward_tokens"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "must_use"^^ . "implemented"^^ . "aprender"^^ . "backend_identity"^^ . + "crates/aprender-core/src/setfit/encoder.rs"^^ . . + "method"^^ . "aprender::setfit::encoder::ExecutionBackend"^^ . "identity"^^ . - "false"^^ . - "no `mod ExecutionBackend` or `use … ExecutionBackend` in `crates/aprender-core/src/setfit/encoder.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "roundtrip_encoding"^^ . - "tokenizer_consistency"^^ . - . - . - "aprender::tokenizer"^^ . - "encode"^^ . - "false"^^ . - "no `mod tokenizer` or `use … tokenizer` in `crates/aprender-core/src/lib.rs`"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "aprender"^^ . + "pair_cosine_mse"^^ . + "crates/aprender-core/src/setfit/loss.rs"^^ . + . + "fn"^^ . + "aprender::setfit::loss"^^ . + "pair_cosine_mse"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "roundtrip_encoding"^^ . + "tokenizer_consistency"^^ . + "crates/aprender-core/src/text/tokenize/bpe_encoding.rs"^^ . + . + . + "method"^^ . + "aprender::text::tokenize::bpe_impl::BpeTokenizer"^^ . + "encode"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "aprender"^^ . + "mse_split"^^ . + "crates/aprender-core/src/tree/regression_helpers.rs"^^ . + . + "fn"^^ . + "aprender::tree::helpers"^^ . + "compute_mse"^^ . + "true"^^ . + "pub(super)"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12071,6 +24481,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12083,37 +24494,23 @@ "gini_split"^^ . "true"^^ . "pub"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "mse_split"^^ . - . - "aprender::tree::helpers_part_02"^^ . - "compute_mse"^^ . - "false"^^ . - "no `mod helpers_part_02` or `use … helpers_part_02` in `crates/aprender-core/src/tree/mod.rs`"^^ . - . - . - "implemented"^^ . - "aprender"^^ . - "mse_split"^^ . - . - "aprender::tree::regression_helpers"^^ . - "compute_mse"^^ . - "false"^^ . - "no `mod regression_helpers` or `use … regression_helpers` in `crates/aprender-core/src/tree/mod.rs`"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "dataset_attestation"^^ . + "crates/aprender-contrastive-data/src/attestation.rs"^^ . . + "method"^^ . "aprender_contrastive_data::attestation::PreparedDataset::"^^ . "from_attested_bytes"^^ . - "false"^^ . - "no `mod PreparedDataset` or `use … PreparedDataset` in `crates/aprender-contrastive-data/src/prepared.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12128,6 +24525,7 @@ "true"^^ . "pub(crate)"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12142,36 +24540,52 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "access_ledger_persistence"^^ . + "crates/aprender-contrastive-data/src/ledger.rs"^^ . . + "method"^^ . "aprender_contrastive_data::ledger::AccessLedger"^^ . "to_canonical_bytes"^^ . - "false"^^ . - "no `mod AccessLedger` or `use … AccessLedger` in `crates/aprender-contrastive-data/src/ledger.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "pair_manifest_replay"^^ . + "crates/aprender-contrastive-data/src/manifest.rs"^^ . . + "method"^^ . "aprender_contrastive_data::manifest::PairReplayRecord"^^ . "to_config"^^ . - "false"^^ . - "no `mod PairReplayRecord` or `use … PairReplayRecord` in `crates/aprender-contrastive-data/src/manifest.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "selection_canonical_payload"^^ . + "crates/aprender-contrastive-data/src/manifest.rs"^^ . . + "method"^^ . "aprender_contrastive_data::manifest::SelectionPayload"^^ . "to_canonical_bytes"^^ . - "false"^^ . - "no `mod SelectionPayload` or `use … SelectionPayload` in `crates/aprender-contrastive-data/src/manifest.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12186,36 +24600,52 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "canonical_pair"^^ . + "crates/aprender-contrastive-data/src/pairs.rs"^^ . . + "method"^^ . "aprender_contrastive_data::pairs::CanonicalPair"^^ . "new"^^ . - "false"^^ . - "no `mod CanonicalPair` or `use … CanonicalPair` in `crates/aprender-contrastive-data/src/pairs.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "pair_stream"^^ . + "crates/aprender-contrastive-data/src/pairs.rs"^^ . . + "method"^^ . "aprender_contrastive_data::pairs::PairLayout"^^ . "raw_pair_at"^^ . - "false"^^ . - "no `mod PairLayout` or `use … PairLayout` in `crates/aprender-contrastive-data/src/pairs.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "singleton_policy"^^ . + "crates/aprender-contrastive-data/src/pairs.rs"^^ . . + "method"^^ . "aprender_contrastive_data::pairs::PairSampler"^^ . "new"^^ . - "false"^^ . - "no `mod PairSampler` or `use … PairSampler` in `crates/aprender-contrastive-data/src/pairs.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12230,6 +24660,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12244,6 +24675,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12258,6 +24690,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12272,6 +24705,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12286,6 +24720,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12301,16 +24736,22 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "prepared_dataset_typestate"^^ . + "crates/aprender-contrastive-data/src/prepared.rs"^^ . . + "method"^^ . "aprender_contrastive_data::prepared::PreparedDataset::"^^ . "from_labeled_rows"^^ . - "false"^^ . - "no `mod PreparedDataset` or `use … PreparedDataset` in `crates/aprender-contrastive-data/src/prepared.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12325,6 +24766,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12339,6 +24781,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12353,26 +24796,37 @@ "true"^^ . "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "few_shot_selection"^^ . + "crates/aprender-contrastive-data/src/select.rs"^^ . . + "method"^^ . "aprender_contrastive_data::select::FewShotSelector"^^ . "select"^^ . - "false"^^ . - "no `mod FewShotSelector` or `use … FewShotSelector` in `crates/aprender-contrastive-data/src/select.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "aprender_contrastive_data"^^ . "selection_replay"^^ . + "crates/aprender-contrastive-data/src/select.rs"^^ . . + "method"^^ . "aprender_contrastive_data::select::Selection"^^ . "replay"^^ . - "false"^^ . - "no `mod Selection` or `use … Selection` in `crates/aprender-contrastive-data/src/select.rs`"^^ . + "true"^^ . + "pub"^^ . . + . . "doc"^^ . "provable_contracts_macros::contract"^^ . @@ -12386,17 +24840,1051 @@ "validate_ingest_ladder"^^ . "true"^^ . "private"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "brick_verification"^^ . + . + "batuta::agent::brick"^^ . + "can_render"^^ . + "false"^^ . + "no `mod brick` or `use … brick` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "brick_budget_enforcement"^^ . + . + "batuta::agent::brick"^^ . + "verify_all"^^ . + "false"^^ . + "no `mod brick` or `use … brick` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "apr_md_compliance"^^ . + . + "batuta::agent::config"^^ . + "load_apr_md"^^ . + "false"^^ . + "no `mod config` or `use … config` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "context_compaction"^^ . + . + "batuta::agent::context"^^ . + "compact"^^ . + "false"^^ . + "no `mod context` or `use … context` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "body_schema_compliance"^^ . + "max_tokens_cap"^^ . + "crates/aprender-orchestrate/src/agent/driver/apr_serve.rs"^^ . + . + "method"^^ . + "batuta::agent::driver::apr_serve"^^ . + "build_openai_body"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "thinking_block_strip"^^ . + "crates/aprender-orchestrate/src/agent/driver/apr_serve.rs"^^ . + . + "fn"^^ . + "batuta::agent::driver::apr_serve"^^ . + "strip_thinking_blocks"^^ . + "true"^^ . + "private"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "backoff_jitter"^^ . + . + "batuta::agent::driver::remote::backoff"^^ . + "delay_for"^^ . + "false"^^ . + "no `mod backoff` or `use … backoff` in `crates/aprender-orchestrate/src/agent/driver/remote.rs`"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "pending"^^ . + "batuta"^^ . + "cost_budget"^^ . + "crates/aprender-orchestrate/src/agent/driver/remote.rs"^^ . + . + "method"^^ . + "batuta::agent::driver::remote"^^ . + "estimate_cost"^^ . + "true"^^ . + "private"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "format_translation"^^ . + . + "batuta::agent::driver::remote::translator"^^ . + "to_openai"^^ . + "false"^^ . + "no `mod translator` or `use … translator` in `crates/aprender-orchestrate/src/agent/driver/remote.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "failover_cascade"^^ . + . + "batuta::agent::driver::routing"^^ . + "failover_next"^^ . + "false"^^ . + "no `mod routing` or `use … routing` in `crates/aprender-orchestrate/src/agent/driver/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "privacy_enforcement"^^ . + . + "batuta::agent::driver::routing"^^ . + "route_to_provider"^^ . + "false"^^ . + "no `mod routing` or `use … routing` in `crates/aprender-orchestrate/src/agent/driver/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "jidoka_validation"^^ . + "crates/aprender-orchestrate/src/agent/driver/validate.rs"^^ . + . + "fn"^^ . + "batuta::agent::driver::validate"^^ . + "is_valid_model_file"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "hook_ordering"^^ . + . + "batuta::agent::hook"^^ . + "execute_with_hooks"^^ . + "false"^^ . + "no `mod hook` or `use … hook` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "search_order"^^ . + "sort_priority"^^ . + "crates/aprender-orchestrate/src/agent/manifest.rs"^^ . + . + "method"^^ . + "batuta::agent::manifest"^^ . + "discover_model"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "startup_latency"^^ . + . + "batuta::agent"^^ . + "run_apr_code"^^ . + "false"^^ . + "no `fn run_apr_code` (free or in an impl) or item `run_apr_code` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "loop_termination"^^ . + . + "batuta::agent"^^ . + "run_loop"^^ . + "false"^^ . + "no `fn run_loop` (free or in an impl) or item `run_loop` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "sandbox_enforcement"^^ . + . + "batuta::agent::sandbox"^^ . + "check_sandbox"^^ . + "false"^^ . + "no `mod sandbox` or `use … sandbox` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "sovereignty_guarantee"^^ . + . + "batuta::agent::sandbox"^^ . + "enforce_offline"^^ . + "false"^^ . + "no `mod sandbox` or `use … sandbox` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "jsonl_roundtrip"^^ . + "crates/aprender-orchestrate/src/agent/session.rs"^^ . + . + "method"^^ . + "batuta::agent::session"^^ . + "append_messages"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "age_filter"^^ . + "crates/aprender-orchestrate/src/agent/session.rs"^^ . + . + "method"^^ . + "batuta::agent::session"^^ . + "find_recent_for_cwd"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "pending"^^ . + "batuta"^^ . + "session_crash_recovery"^^ . + "crates/aprender-orchestrate/src/agent/session.rs"^^ . + . + "method"^^ . + "batuta::agent::session"^^ . + "resume"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "session_integrity"^^ . + . + "batuta::agent::session"^^ . + "resume_session"^^ . + "false"^^ . + "no `fn resume_session` (free or in an impl) or item `resume_session` in `crates/aprender-orchestrate/src/agent/session.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "state_machine"^^ . + "state_machine_validity"^^ . + . + . + "batuta::agent::state"^^ . + "transition"^^ . + "false"^^ . + "no `mod state` or `use … state` in `crates/aprender-orchestrate/src/agent/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "parallel_tool_safety"^^ . + . + "batuta::agent::tool"^^ . + "execute_all"^^ . + "false"^^ . + "no `fn execute_all` (free or in an impl) or item `execute_all` in `crates/aprender-orchestrate/src/agent/tool/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "tool_safety"^^ . + . + "batuta::agent::tool"^^ . + "execute_tool_checked"^^ . + "false"^^ . + "no `fn execute_tool_checked` (free or in an impl) or item `execute_tool_checked` in `crates/aprender-orchestrate/src/agent/tool/mod.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "cost_display_accuracy"^^ . + "cost_display_invariants"^^ . + . + . + "batuta::agent::tui::cost"^^ . + "update_cost"^^ . + "false"^^ . + "no `mod cost` or `use … cost` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "layout_correctness"^^ . + "panel_layout_nonoverlap"^^ . + . + . + "batuta::agent::tui::layout"^^ . + "compute_layout"^^ . + "false"^^ . + "no `mod layout` or `use … layout` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "adaptive_degradation"^^ . + . + "batuta::agent::tui::layout"^^ . + "detail_level"^^ . + "false"^^ . + "no `mod layout` or `use … layout` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "frame_budget"^^ . + . + "batuta::agent::tui::layout"^^ . + "render_frame"^^ . + "false"^^ . + "no `mod layout` or `use … layout` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "sandbox_violation_visibility"^^ . + . + "batuta::agent::tui::sandbox"^^ . + "add_violation"^^ . + "false"^^ . + "no `mod sandbox` or `use … sandbox` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "statusbar_state_display"^^ . + . + "batuta::agent::tui::statusbar"^^ . + "update_statusbar"^^ . + "false"^^ . + "no `mod statusbar` or `use … statusbar` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "streaming_responsiveness"^^ . + "streaming_token_ordering"^^ . + . + . + "batuta::agent::tui::streaming"^^ . + "push_token"^^ . + "false"^^ . + "no `mod streaming` or `use … streaming` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "tool_progress_monotonic"^^ . + . + "batuta::agent::tui::tools"^^ . + "update_tool"^^ . + "false"^^ . + "no `mod tools` or `use … tools` in `crates/aprender-orchestrate/src/agent/tui.rs`"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/analyzer.rs"^^ . + . + "fn"^^ . + "batuta::analyzer"^^ . + "analyze_project"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/bug_hunter/contracts.rs"^^ . + . + "fn"^^ . + "batuta::bug_hunter::contracts"^^ . + "analyze_contract_gaps"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/bug_hunter/model_parity.rs"^^ . + . + "fn"^^ . + "batuta::bug_hunter::model_parity"^^ . + "analyze_model_parity_gaps"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "dispatch_correctness"^^ . + . + "batuta::cli::oracle"^^ . + "dispatch_oracle"^^ . + "false"^^ . + "no `mod cli` or `use … cli` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "rag_query_correctness"^^ . + . + "batuta::cli::oracle::rag"^^ . + "rag_search"^^ . + "false"^^ . + "no `mod cli` or `use … cli` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "pending"^^ . + "batuta"^^ . + "index_freshness"^^ . + . + "batuta::cli::oracle::rag_index"^^ . + "check_freshness"^^ . + "false"^^ . + "no `mod cli` or `use … cli` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/parf.rs"^^ . + . + "method"^^ . + "batuta::parf"^^ . + "analyze_dependencies"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/pipeline/stages/optimization.rs"^^ . + . + "method"^^ . + "batuta::pipeline::stages::optimization"^^ . + "analyze_optimizations"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/pipeline_analysis.rs"^^ . + . + "method"^^ . + "batuta::pipeline_analysis"^^ . + "analyze_numpy_usage"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/pipeline_analysis.rs"^^ . + . + "method"^^ . + "batuta::pipeline_analysis"^^ . + "analyze_pytorch_usage"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/pipeline_analysis.rs"^^ . + . + "method"^^ . + "batuta::pipeline_analysis"^^ . + "analyze_sklearn_usage"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/recipes/benchmark.rs"^^ . + . + "method"^^ . + "batuta::recipes::benchmark"^^ . + "analyze"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "deterministic_encode"^^ . + "vocab_size_bound"^^ . + . + "batuta::src::agent::driver::validate::tokenizer_encode"^^ . + "encode"^^ . + "false"^^ . + "no `mod src` or `use … src` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "manifest_serde"^^ . + . + "batuta::src::agent::session::Session"^^ . + "new"^^ . + "false"^^ . + "no `mod src` or `use … src` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + . + "batuta::src::cli::mod"^^ . + "build_transpiler_args"^^ . + "false"^^ . + "no `mod src` or `use … src` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + . + "batuta::src::cli::pipeline_cmds"^^ . + "cmd_analyze"^^ . + "false"^^ . + "no `mod src` or `use … src` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + . + "batuta::src::cli::pipeline_cmds_transpile"^^ . + "cmd_transpile"^^ . + "false"^^ . + "no `mod src` or `use … src` in `crates/aprender-orchestrate/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "fn"^^ . + "batuta::tools"^^ . + "analyze_quality"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "method"^^ . + "batuta::tools"^^ . + "get_transpiler_for_language"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "method"^^ . + "batuta::tools"^^ . + "has_transpiler"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "fn"^^ . + "batuta::tools"^^ . + "transpile_c_cpp"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "fn"^^ . + "batuta::tools"^^ . + "transpile_python"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/tools.rs"^^ . + . + "fn"^^ . + "batuta::tools"^^ . + "transpile_shell"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "batuta"^^ . + "transpile"^^ . + "crates/aprender-orchestrate/src/types.rs"^^ . + . + "method"^^ . + "batuta::types"^^ . + "recommend_transpiler"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "wasm_bindgen"^^ . + "implemented"^^ . + "batuta"^^ . + "analyze"^^ . + "crates/aprender-orchestrate/src/wasm.rs"^^ . + . + "fn"^^ . + "batuta::wasm"^^ . + "analyze_code"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "accuracy"^^ . + . + "entrenar"^^ . + "accuracy"^^ . + "false"^^ . + "no `fn accuracy` (free or in an impl) or item `accuracy` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "attention"^^ . + . + "entrenar"^^ . + "attention"^^ . + "false"^^ . + "no `fn attention` (free or in an impl) or item `attention` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "device_dispatch"^^ . + . + "entrenar::autograd::cuda_training::ComputeDevice"^^ . + "auto_detect"^^ . + "false"^^ . + "no `mod ComputeDevice` or `use … ComputeDevice` in `crates/aprender-train/src/autograd/cuda_training.rs`"^^ . + . + . + . + "pending"^^ . + "entrenar"^^ . + "weight_roundtrip"^^ . + . + "entrenar::autograd::cuda_training"^^ . + "weight_roundtrip"^^ . + "false"^^ . + "no `fn weight_roundtrip` (free or in an impl) or item `weight_roundtrip` in `crates/aprender-train/src/autograd/cuda_training.rs`"^^ . . + . . + "doc"^^ . + "provable_contracts_macros::contract"^^ . "implemented"^^ . "entrenar"^^ . "matvec_matmul_assoc"^^ . + "crates/aprender-train/src/autograd/ops/matmul.rs"^^ . . + "fn"^^ . "entrenar::autograd"^^ . "matmul"^^ . - "false"^^ . - "no `fn matmul` (free or in an impl) in `crates/aprender-train/src/autograd/mod.rs`"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "transpose_tracked"^^ . + "crates/aprender-train/src/autograd/ops/matmul.rs"^^ . + . + "fn"^^ . + "entrenar::autograd::ops::matmul"^^ . + "transpose_tracked"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "tensor_clone"^^ . + . + "entrenar::autograd::tensor::Tensor"^^ . + "clone"^^ . + "false"^^ . + "no `fn clone` in an `impl Tensor` or `trait Tensor` in `crates/aprender-train/src/autograd/tensor.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "data_read"^^ . + "crates/aprender-train/src/autograd/tensor.rs"^^ . + . + "method"^^ . + "entrenar::autograd::tensor::Tensor"^^ . + "data"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "data_mut"^^ . + "crates/aprender-train/src/autograd/tensor.rs"^^ . + . + "method"^^ . + "entrenar::autograd::tensor::Tensor"^^ . + "data_mut"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "bounds"^^ . + . + "entrenar"^^ . + "bounds"^^ . + "false"^^ . + "no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "confusion_matrix"^^ . + . + "entrenar"^^ . + "confusion_matrix"^^ . + "false"^^ . + "no `fn confusion_matrix` (free or in an impl) or item `confusion_matrix` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "vram_budget"^^ . + . + "entrenar::finetune::classify_pipeline"^^ . + "estimate_wgpu_vram"^^ . + "false"^^ . + "no `fn estimate_wgpu_vram` (free or in an impl) or item `estimate_wgpu_vram` in `crates/aprender-train/src/finetune/classify_pipeline/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "load_checkpoint"^^ . + . + "entrenar::finetune::classify_trainer"^^ . + "load_checkpoint"^^ . + "false"^^ . + "no `fn load_checkpoint` (free or in an impl) or item `load_checkpoint` in `crates/aprender-train/src/finetune/classify_trainer.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "save_checkpoint"^^ . + "crates/aprender-train/src/finetune/classify_trainer.rs"^^ . + . + "method"^^ . + "entrenar::finetune::classify_trainer"^^ . + "save_checkpoint"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "gradient_allreduce"^^ . + . + "entrenar::finetune::data_parallel::DataParallelCoordinator"^^ . + "allreduce"^^ . + "false"^^ . + "no `fn allreduce` in an `impl DataParallelCoordinator` or `trait DataParallelCoordinator` in `crates/aprender-train/src/finetune/data_parallel.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "sharding"^^ . + . + "entrenar::finetune::data_parallel::DataParallelCoordinator"^^ . + "shard_data"^^ . + "false"^^ . + "no `fn shard_data` in an `impl DataParallelCoordinator` or `trait DataParallelCoordinator` in `crates/aprender-train/src/finetune/data_parallel.rs`"^^ . + . + . + . + "pending"^^ . + "entrenar"^^ . + "lora_gradient_size"^^ . + . + "entrenar::finetune::data_parallel"^^ . + "lora_gradient_byte_size"^^ . + "false"^^ . + "no `fn lora_gradient_byte_size` (free or in an impl) or item `lora_gradient_byte_size` in `crates/aprender-train/src/finetune/data_parallel.rs`"^^ . + . + . + . + "pending"^^ . + "entrenar"^^ . + "weighted_loss"^^ . + . + "entrenar::finetune::data_parallel"^^ . + "weighted_loss_aggregate"^^ . + "false"^^ . + "no `fn weighted_loss_aggregate` (free or in an impl) or item `weighted_loss_aggregate` in `crates/aprender-train/src/finetune/data_parallel.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "effective_batch_size"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "effective_batch_size"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "epoch_count_imbalanced"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "epochs"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "gradient_clip_bound"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "gradient_clip_norm"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "learning_rate_scaling"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "learning_rate"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "lora_alpha_ratio"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "lora_alpha"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "seq_len_from_data"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "max_seq_len"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "warmup_fraction"^^ . + . + "entrenar::finetune::qlora_config::QLoraConfig"^^ . + "warmup_steps"^^ . + "false"^^ . + "no `mod qlora_config` or `use … qlora_config` in `crates/aprender-train/src/finetune/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "gelu"^^ . + . + "entrenar"^^ . + "gelu"^^ . + "false"^^ . + "no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "greedy"^^ . + . + "entrenar"^^ . + "greedy"^^ . + "false"^^ . + "no `fn greedy` (free or in an impl) or item `greedy` in `crates/aprender-train/src/lib.rs`"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12410,6 +25898,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12422,9 +25911,153 @@ "merge"^^ . "true"^^ . "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "matmul"^^ . + . + "entrenar"^^ . + "matmul"^^ . + "false"^^ . + "no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "ndcg_at_k"^^ . + . + "entrenar"^^ . + "ndcg_at_k"^^ . + "false"^^ . + "no `fn ndcg_at_k` (free or in an impl) or item `ndcg_at_k` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "output"^^ . + . + "entrenar"^^ . + "output"^^ . + "false"^^ . + "no `fn output` (free or in an impl) or item `output` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "predict"^^ . + . + "entrenar"^^ . + "predict"^^ . + "false"^^ . + "no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "quantize"^^ . + . + "entrenar"^^ . + "quantize"^^ . + "false"^^ . + "no `fn quantize` (free or in an impl) or item `quantize` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "relu"^^ . + . + "entrenar"^^ . + "relu"^^ . + "false"^^ . + "no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "rmse"^^ . + . + "entrenar"^^ . + "rmse"^^ . + "false"^^ . + "no `fn rmse` (free or in an impl) or item `rmse` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "sample"^^ . + . + "entrenar"^^ . + "sample"^^ . + "false"^^ . + "no `fn sample` (free or in an impl) or item `sample` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "sigmoid"^^ . + . + "entrenar"^^ . + "sigmoid"^^ . + "false"^^ . + "no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "statistics"^^ . + . + "entrenar"^^ . + "statistics"^^ . + "false"^^ . + "no `fn statistics` (free or in an impl) or item `statistics` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "temperature"^^ . + . + "entrenar"^^ . + "temperature"^^ . + "false"^^ . + "no `fn temperature` (free or in an impl) or item `temperature` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "top_k"^^ . + . + "entrenar"^^ . + "top_k"^^ . + "false"^^ . + "no `fn top_k` (free or in an impl) or item `top_k` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "total_bytes"^^ . + . + "entrenar"^^ . + "total_bytes"^^ . + "false"^^ . + "no `fn total_bytes` (free or in an impl) or item `total_bytes` in `crates/aprender-train/src/lib.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "entrenar"^^ . "doc_bundle_bijection"^^ . . @@ -12433,8 +26066,9 @@ "false"^^ . "no `mod setfit` or `use … setfit` in `crates/aprender-train/src/train/mod.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "entrenar"^^ . "linear_probe"^^ . . @@ -12443,8 +26077,9 @@ "false"^^ . "no `mod setfit` or `use … setfit` in `crates/aprender-train/src/train/mod.rs`"^^ . . + . . - "implemented"^^ . + "notimplemented"^^ . "entrenar"^^ . "selection_lock_lifecycle"^^ . . @@ -12452,7 +26087,1225 @@ "mint_test_token"^^ . "false"^^ . "no `mod setfit` or `use … setfit` in `crates/aprender-train/src/train/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "C-WGPU-TRAIN-001"^^ . + "crates/aprender-train/src/train/transformer_trainer/wgpu_trainer.rs"^^ . + . + "method"^^ . + "entrenar::train::transformer_trainer::wgpu_trainer"^^ . + "full_train_step"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "C-WGPU-CKPT-002"^^ . + "crates/aprender-train/src/train/transformer_trainer/wgpu_trainer.rs"^^ . + . + "method"^^ . + "entrenar::train::transformer_trainer::wgpu_trainer"^^ . + "load_checkpoint"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "C-WGPU-CKPT-001"^^ . + "crates/aprender-train/src/train/transformer_trainer/wgpu_trainer.rs"^^ . + . + "method"^^ . + "entrenar::train::transformer_trainer::wgpu_trainer"^^ . + "save_checkpoint"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "head_extraction"^^ . + "crates/aprender-train/src/transformer/attention.rs"^^ . + . + "method"^^ . + "entrenar::transformer::attention::MultiHeadAttention"^^ . + "forward"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "forward_with_lora"^^ . + "lora_forward"^^ . + "crates/aprender-train/src/transformer/attention.rs"^^ . + . + . + "method"^^ . + "entrenar::transformer::attention::MultiHeadAttention"^^ . + "forward_with_lora"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "gpu_forward"^^ . + . + "entrenar::transformer::cuda_block::CudaTransformerBlock"^^ . + "gpu_forward"^^ . + "false"^^ . + "no `fn gpu_forward` in an `impl CudaTransformerBlock` or `trait CudaTransformerBlock` in `crates/aprender-train/src/transformer/cuda_block.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "swiglu_ffn"^^ . + . + "entrenar::transformer::cuda_block::CudaTransformerBlock"^^ . + "swiglu_ffn"^^ . + "false"^^ . + "no `fn swiglu_ffn` in an `impl CudaTransformerBlock` or `trait CudaTransformerBlock` in `crates/aprender-train/src/transformer/cuda_block.rs`"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "partial"^^ . + "entrenar"^^ . + "backward"^^ . + "crates/aprender-train/src/transformer/cuda_block.rs"^^ . + . + "method"^^ . + "entrenar::transformer::cuda_block"^^ . + "backward"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "entrenar"^^ . + "with_resident_weights"^^ . + "crates/aprender-train/src/transformer/wgpu_block.rs"^^ . + . + "method"^^ . + "entrenar::transformer::wgpu_block::WgpuForwardPass"^^ . + "with_resident_weights"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "transpose"^^ . + . + "entrenar"^^ . + "transpose"^^ . + "false"^^ . + "no `fn transpose` (free or in an impl) or item `transpose` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "entrenar"^^ . + "update"^^ . + . + "entrenar"^^ . + "update"^^ . + "false"^^ . + "no `fn update` (free or in an impl) or item `update` in `crates/aprender-train/src/lib.rs`"^^ . + . + . + . + . + "oxide"^^ . + "lambda"^^ . + . + . + . + . + . + "21"^^ . + "true"^^ . + "lambda"^^ . + "doc"^^ . + "kernel"^^ . + "implemented"^^ . + "true"^^ . + "gated_rmsnorm_oxide"^^ . + "gated_rmsnorm"^^ . + "experiments/cuda-oxide/gated-rmsnorm/src/lib.rs"^^ . + . + "fn"^^ . + "gated_rmsnorm_oxide::kernels"^^ . + "gated_rmsnorm"^^ . + "true"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-core::src::virtualization"^^ . + "cells_to_render"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-core::src::virtualization"^^ . + "render_count"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-core::src::virtualization"^^ . + "render_range"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-core::src::virtualization"^^ . + "should_render"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-core::src::virtualization"^^ . + "should_render_cell"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-layout::src::compute_block"^^ . + "render_order"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "terminal_restore"^^ . + . + "presentar::crates::presentar-terminal::src::app::TuiApp"^^ . + "drop"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "widget_lifecycle"^^ . + . + "presentar::crates::presentar-terminal::src::app::TuiApp"^^ . + "mount"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "widget_lifecycle"^^ . + . + "presentar::crates::presentar-terminal::src::app::TuiApp"^^ . + "unmount"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "event_dispatch"^^ . + . + "presentar::crates::presentar-terminal::src::app"^^ . + "dispatch_event"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::app"^^ . + "record_render"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "color_mode_fallback"^^ . + . + "presentar::crates::presentar-terminal::src::color"^^ . + "nearest_256"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::compute_block"^^ . + "render"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "unicode_width"^^ . + . + "presentar::crates::presentar-terminal::src::direct::cell_buffer"^^ . + "display_width"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "cellbuffer_bounds"^^ . + . + "presentar::crates::presentar-terminal::src::direct::cell_buffer"^^ . + "get"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "dirty_tracking"^^ . + . + "presentar::crates::presentar-terminal::src::direct::cell_buffer"^^ . + "mark_dirty"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "resize_safety"^^ . + . + "presentar::crates::presentar-terminal::src::direct::cell_buffer"^^ . + "resize"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "cellbuffer_bounds"^^ . + . + "presentar::crates::presentar-terminal::src::direct::cell_buffer"^^ . + "set"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "pending"^^ . + "presentar"^^ . + "diff_renderer_correctness"^^ . + "render_cycle_correctness"^^ . + "zero_alloc_render"^^ . + . + . + "presentar::crates::presentar-terminal::src::direct::diff_renderer"^^ . + "render_diff"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::direct::diff_renderer"^^ . + "render_full"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::perf_trace::trackers"^^ . + "for_render"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::tools::bench"^^ . + "render_frame"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::widgets::micro_heat_bar"^^ . + "render_string"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::widgets::micro_heat_bar"^^ . + "render_text"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-terminal::src::widgets::ux"^^ . + "render_lines"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar-widgets::src::list"^^ . + "render_with"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar::src::browser::app"^^ . + "render_counter"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar::src::browser::app"^^ . + "render_dashboard"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar::src::browser::app"^^ . + "render_json"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar::src::browser::app"^^ . + "render_widget"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "presentar"^^ . + "render"^^ . + . + "presentar::crates::presentar::src::webgpu"^^ . + "render_instances"^^ . + "false"^^ . + "crate `presentar` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "token_budget"^^ . + . + "realizar::api::cuda_batch_scheduler::CudaBatchScheduler"^^ . + "schedule_step"^^ . + "false"^^ . + "no `mod CudaBatchScheduler` or `use … CudaBatchScheduler` in `crates/aprender-serve/src/api/cuda_batch_scheduler.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "residual_stream"^^ . + "crates/aprender-serve/src/apply.rs"^^ . + . + "method"^^ . + "realizar::apply"^^ . + "forward_layer"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "apr_loader_validation"^^ . + . + "realizar::apr::cuda_part_02_part_03::AprV2ModelCuda"^^ . + "upload_quantized_weights_to_gpu"^^ . + "false"^^ . + "no `mod cuda_part_02_part_03` or `use … cuda_part_02_part_03` in `crates/aprender-serve/src/apr/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "attention"^^ . + "gqa"^^ . + "crates/aprender-serve/src/apr/helpers.rs"^^ . + . + . + "fn"^^ . + "realizar::apr::helpers"^^ . + "simple_attention"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "role_mapping"^^ . + . + "realizar::arch_requirements_fallback::WeightRole"^^ . + "field_name"^^ . + "false"^^ . + "no `mod arch_requirements_fallback` or `use … arch_requirements_fallback` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "constraint_matrix_exhaustiveness"^^ . + . + "realizar::arch_requirements_fallback"^^ . + "required_roles"^^ . + "false"^^ . + "no `mod arch_requirements_fallback` or `use … arch_requirements_fallback` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "bounds"^^ . + . + "realizar"^^ . + "bounds"^^ . + "false"^^ . + "no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "realizar"^^ . + "compute_ceiling"^^ . + "crates/aprender-serve/src/brick/fused.rs"^^ . + . + "method"^^ . + "realizar::brick::fused::CoalescedDp4aBrick"^^ . + "arithmetic_intensity"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "realizar"^^ . + "transpose_involution"^^ . + "crates/aprender-serve/src/contract_gate.rs"^^ . + . + "fn"^^ . + "realizar::contract_gate"^^ . + "transpose_f32"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "weight_completeness"^^ . + "crates/aprender-serve/src/contract_gate.rs"^^ . + . + "fn"^^ . + "realizar::contract_gate"^^ . + "validate_completeness"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "paged_contiguous_equivalence"^^ . + . + "realizar::cuda::executor::attention"^^ . + "paged_attention_into"^^ . + "false"^^ . + "no `fn paged_attention_into` (free or in an impl) or item `paged_attention_into` in `crates/aprender-serve/src/cuda/executor/attention.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "jit_compilation_success"^^ . + "crates/aprender-serve/src/cuda/executor/core.rs"^^ . + . + "method"^^ . + "realizar::cuda::executor::core::CudaExecutor"^^ . + "compile_ptx"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "report_denominator"^^ . + "report_fidelity"^^ . + "report_metadata"^^ . + . + "realizar::cuda::executor::executor_api::ExecutorApi"^^ . + "start_brick_id"^^ . + "false"^^ . + "no `mod ExecutorApi` or `use … ExecutorApi` in `crates/aprender-serve/src/cuda/executor/executor_api.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "report_completeness"^^ . + . + "realizar::cuda::executor::executor_api::ExecutorApi"^^ . + "stop_brick_id"^^ . + "false"^^ . + "no `mod ExecutorApi` or `use … ExecutorApi` in `crates/aprender-serve/src/cuda/executor/executor_api.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "block_allocation"^^ . + . + "realizar::cuda::executor::kv_cache::BlockAllocator"^^ . + "allocate"^^ . + "false"^^ . + "no `mod BlockAllocator` or `use … BlockAllocator` in `crates/aprender-serve/src/cuda/executor/kv_cache.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "slot_mapping"^^ . + . + "realizar::cuda::executor::kv_cache::PagedKvCache"^^ . + "slot"^^ . + "false"^^ . + "no `mod PagedKvCache` or `use … PagedKvCache` in `crates/aprender-serve/src/cuda/executor/kv_cache.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "fused_kernel"^^ . + . + "realizar::cuda::executor::kv_cache_part_02_part_03"^^ . + "flash_attention_cached"^^ . + "false"^^ . + "no `mod kv_cache_part_02_part_03` or `use … kv_cache_part_02_part_03` in `crates/aprender-serve/src/cuda/executor/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "qk_norm_score_bound"^^ . + . + "realizar::cuda::executor::layers::apply"^^ . + "apply_qk_norm"^^ . + "false"^^ . + "no `mod apply` or `use … apply` in `crates/aprender-serve/src/cuda/executor/layers/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "context_health"^^ . + "crates/aprender-serve/src/cuda/executor/layers/cublas_prefill/attention.rs"^^ . + . + "method"^^ . + "realizar::cuda::executor::layers::cublas_prefill::attention::CudaExecutor"^^ . + "warmup_fp8_cache"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "cuda_graph_guard"^^ . + "graph_disable"^^ . + . + . + "realizar::cuda::executor::layers::graphed_capture"^^ . + "try_graph_capture"^^ . + "false"^^ . + "no `mod graphed_capture` or `use … graphed_capture` in `crates/aprender-serve/src/cuda/executor/layers/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "chunked_prefill"^^ . + . + "realizar::cuda::executor::layers::prefill::CudaExecutor"^^ . + "chunked_prefill"^^ . + "false"^^ . + "no `fn chunked_prefill` in an `impl CudaExecutor` or `trait CudaExecutor` in `crates/aprender-serve/src/cuda/executor/layers/prefill.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "shared_q8_qkv"^^ . + . + "realizar::cuda::executor::q4k_mwv_gemv::CudaExecutor"^^ . + "batched_qkv_dp4a_q4k_gemv_into"^^ . + "false"^^ . + "no `mod q4k_mwv_gemv` or `use … q4k_mwv_gemv` in `crates/aprender-serve/src/cuda/executor/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "gguf_loader_validation"^^ . + "crates/aprender-serve/src/cuda/executor/weights.rs"^^ . + . + "method"^^ . + "realizar::cuda::executor::weights::CudaExecutor"^^ . + "build_indexed_weights"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "throughput_target"^^ . + "crates/aprender-serve/src/cuda/executor/weights.rs"^^ . + . + "method"^^ . + "realizar::cuda::executor::weights::CudaExecutor"^^ . + "load_weights"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "target_parity"^^ . + "crates/aprender-serve/src/cuda/gpu_profile.rs"^^ . + . + "method"^^ . + "realizar::cuda::gpu_profile::GpuProfile"^^ . + "detect"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "fp8_architecture_guard"^^ . + "crates/aprender-serve/src/cuda/gpu_profile.rs"^^ . + . + "method"^^ . + "realizar::cuda::gpu_profile"^^ . + "detect_fp8_prefill"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "no_hardcoded_targets"^^ . + . + "realizar::cuda::kernel_generator::CudaKernels"^^ . + "generate_ptx"^^ . + "false"^^ . + "no `fn generate_ptx` in an `impl CudaKernels` or `trait CudaKernels` in `crates/aprender-serve/src/cuda/kernel_generator.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "effective_context"^^ . + . + "realizar"^^ . + "effective_context"^^ . + "false"^^ . + "no `fn effective_context` (free or in an impl) or item `effective_context` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "element_count"^^ . + . + "realizar"^^ . + "element_count"^^ . + "false"^^ . + "no `fn element_count` (free or in an impl) or item `element_count` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "flash_attention"^^ . + . + "realizar"^^ . + "flash_attention"^^ . + "false"^^ . + "no `fn flash_attention` (free or in an impl) or item `flash_attention` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "gelu"^^ . + . + "realizar"^^ . + "gelu"^^ . + "false"^^ . + "no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "forward_pass_completeness"^^ . + . + "realizar::gguf::OwnedQuantizedModel"^^ . + "forward"^^ . + "false"^^ . + "no `mod OwnedQuantizedModel` or `use … OwnedQuantizedModel` in `crates/aprender-serve/src/gguf/inference_types.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "o_projection_transpose"^^ . + . + "realizar::gguf::config::ArchConstraints"^^ . + "from_gguf_metadata"^^ . + "false"^^ . + "no `fn from_gguf_metadata` in an `impl ArchConstraints` or `trait ArchConstraints` in `crates/aprender-serve/src/gguf/config.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "correctness_under_batching"^^ . + . + "realizar::gguf::cuda"^^ . + "generate_batched_streaming"^^ . + "false"^^ . + "no `fn generate_batched_streaming` (free or in an impl) or item `generate_batched_streaming` in `crates/aprender-serve/src/gguf/cuda/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "gated_rmsnorm"^^ . + "crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs"^^ . + . + "fn"^^ . + "realizar::gguf::forward_qwen35"^^ . + "gated_rmsnorm"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "flash_attention"^^ . + . + "realizar::gguf::inference::attention_part_02"^^ . + "flash_attention_tiled"^^ . + "false"^^ . + "no `mod attention_part_02` or `use … attention_part_02` in `crates/aprender-serve/src/gguf/inference/mod.rs`"^^ . + . + . + . + "partial"^^ . + "realizar"^^ . + "flattened_batched_gemm"^^ . + . + "realizar::gguf::inference::cached::single_flattened_batched_owned"^^ . + "flattened_batched_gemm"^^ . + "false"^^ . + "no `mod single_flattened_batched_owned` or `use … single_flattened_batched_owned` in `crates/aprender-serve/src/gguf/inference/cached/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "decode_step"^^ . + . + "realizar::gguf::inference::forward::Model"^^ . + "forward"^^ . + "false"^^ . + "no `mod Model` or `use … Model` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "prefill_incremental"^^ . + "prefill_phase"^^ . + . + . + "realizar::gguf::inference::forward::batch_part_02_part_03"^^ . + "prefill_batch"^^ . + "false"^^ . + "no `mod batch_part_02_part_03` or `use … batch_part_02_part_03` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "batched_serial_equivalence"^^ . + . + "realizar::gguf::inference::forward"^^ . + "generate_with_batched_prefill"^^ . + "false"^^ . + "no `fn generate_with_batched_prefill` (free or in an impl) or item `generate_with_batched_prefill` in `crates/aprender-serve/src/gguf/inference/forward/mod.rs`"^^ . + . + . + . + "contract"^^ . + "doc"^^ . + "partial"^^ . + "realizar"^^ . + "rms_norm"^^ . + "crates/aprender-serve/src/gguf/ops.rs"^^ . + . + "fn"^^ . + "realizar::gguf::ops"^^ . + "rms_norm_unit_offset"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "contract"^^ . + "doc"^^ . + "partial"^^ . + "realizar"^^ . + "softmax_inplace"^^ . + "crates/aprender-serve/src/gguf/ops.rs"^^ . + . + "fn"^^ . + "realizar::gguf::ops"^^ . + "softmax"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "merge_rule"^^ . + . + "realizar::gguf::token::GgufTokenizer"^^ . + "merge_rules"^^ . + "false"^^ . + "no `mod GgufTokenizer` or `use … GgufTokenizer` in `crates/aprender-serve/src/gguf/token.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "layer_composition"^^ . + . + "realizar::gpu::adapters::using"^^ . + "forward_layer"^^ . + "false"^^ . + "no `mod using` or `use … using` in `crates/aprender-serve/src/gpu/adapters/mod.rs`"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "partial"^^ . + "realizar"^^ . + "dequant_correctness"^^ . + "crates/aprender-serve/src/gpu/adapters/wgpu_adapter.rs"^^ . + . + "fn"^^ . + "realizar::gpu::adapters::wgpu_adapter"^^ . + "dequant_model_weights"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "attention_sublayer"^^ . + . + "realizar::gpu::scheduler::forward_from_model"^^ . + "forward_block_traced"^^ . + "false"^^ . + "no `mod forward_from_model` or `use … forward_from_model` in `crates/aprender-serve/src/gpu/scheduler/mod.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "decay"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_decay"^^ . + "false"^^ . + "no `fn gated_delta_net_decay` (free or in an impl) or item `gated_delta_net_decay` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "delta"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_delta"^^ . + "false"^^ . + "no `fn gated_delta_net_delta` (free or in an impl) or item `gated_delta_net_delta` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "gdn_sublayer"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_forward"^^ . + "false"^^ . + "no `fn gated_delta_net_forward` (free or in an impl) or item `gated_delta_net_forward` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "output"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_output"^^ . + "false"^^ . + "no `fn gated_delta_net_output` (free or in an impl) or item `gated_delta_net_output` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "read"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_read"^^ . + "false"^^ . + "no `fn gated_delta_net_read` (free or in an impl) or item `gated_delta_net_read` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "write"^^ . + . + "realizar::gpu::scheduler::linear_attn"^^ . + "gated_delta_net_write"^^ . + "false"^^ . + "no `fn gated_delta_net_write` (free or in an impl) or item `gated_delta_net_write` in `crates/aprender-serve/src/gpu/scheduler/linear_attn.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "fused_qkv"^^ . + . + "realizar::gpu::scheduler::model_forward_block_gpu"^^ . + "fused_qkv_projection"^^ . + "false"^^ . + "no `mod model_forward_block_gpu` or `use … model_forward_block_gpu` in `crates/aprender-serve/src/gpu/scheduler/mod.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "temperature"^^ . + "crates/aprender-serve/src/gpu/scheduler/ops.rs"^^ . + . + "fn"^^ . + "realizar::gpu::scheduler::ops"^^ . + "sample_topk"^^ . + "true"^^ . + "pub(super)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "rope_frequency"^^ . + "crates/aprender-serve/src/gpu/simd_ops.rs"^^ . + . + "fn"^^ . + "realizar::gpu::simd_ops"^^ . + "rope_frequency_table"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "gpu_threshold"^^ . + . + "realizar"^^ . + "gpu_threshold"^^ . + "false"^^ . + "no `fn gpu_threshold` (free or in an impl) or item `gpu_threshold` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "gqa"^^ . + . + "realizar"^^ . + "gqa"^^ . + "false"^^ . + "no `fn gqa` (free or in an impl) or item `gqa` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "greedy"^^ . + . + "realizar"^^ . + "greedy"^^ . + "false"^^ . + "no `fn greedy` (free or in an impl) or item `greedy` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12466,6 +27319,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "implemented"^^ . @@ -12479,6 +27333,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -12493,6 +27348,7 @@ "true"^^ . "pub"^^ . . + . . "doc"^^ . "must_use"^^ . @@ -12506,6 +27362,1648 @@ "tool_to_anthropic"^^ . "true"^^ . "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "realizar"^^ . + "multi_backend_parity"^^ . + "crates/aprender-serve/src/infer/batch_wgpu.rs"^^ . + . + "method"^^ . + "realizar::infer::batch_wgpu"^^ . + "generate"^^ . + "true"^^ . + "private"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "implemented"^^ . + "realizar"^^ . + "pcie_overhead"^^ . + "crates/aprender-serve/src/infer/batch_wgpu.rs"^^ . + . + "fn"^^ . + "realizar::infer::batch_wgpu"^^ . + "try_init_wgpu_batch"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "garbage_oracle"^^ . + "crates/aprender-serve/src/inference_trace/mod_get_top_compute.rs"^^ . + . + "fn"^^ . + "realizar::inference_trace::mod_get_top_compute"^^ . + "is_garbage_output"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "realizar"^^ . + "model_bytes"^^ . + "crates/aprender-serve/src/lambda.rs"^^ . + . + "method"^^ . + "realizar::lambda"^^ . + "model_size_bytes"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "base_frequency"^^ . + "crates/aprender-serve/src/layers/position.rs"^^ . + . + "method"^^ . + "realizar::layers::position::RoPE"^^ . + "new"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "linear_interpolation"^^ . + "ntk_scaled_base"^^ . + "yarn_mixed_frequency"^^ . + "yarn_ramp"^^ . + . + "realizar::layers::position::ScaledRope"^^ . + "new"^^ . + "false"^^ . + "no `mod ScaledRope` or `use … ScaledRope` in `crates/aprender-serve/src/layers/position.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "matmul"^^ . + . + "realizar"^^ . + "matmul"^^ . + "false"^^ . + "no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "page_shape"^^ . + "crates/aprender-serve/src/paged_kv/mod_paged.rs"^^ . + . + "method"^^ . + "realizar::paged_kv::mod_paged::PagedKvCache"^^ . + "new"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "position_embedding"^^ . + . + "realizar"^^ . + "position_embedding"^^ . + "false"^^ . + "no `fn position_embedding` (free or in an impl) or item `position_embedding` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "predict"^^ . + . + "realizar"^^ . + "predict"^^ . + "false"^^ . + "no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "quantize"^^ . + . + "realizar"^^ . + "quantize"^^ . + "false"^^ . + "no `fn quantize` (free or in an impl) or item `quantize` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "bsum"^^ . + "crates/aprender-serve/src/quantize/bsum_precompute.rs"^^ . + . + "fn"^^ . + "realizar::quantize::bsum_precompute"^^ . + "precompute_q8k_bsums"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "q4k_superblock"^^ . + "crates/aprender-serve/src/quantize/format_trait.rs"^^ . + . + "struct"^^ . + "realizar::quantize::format_trait"^^ . + "Q4K"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "inline"^^ . + "implemented"^^ . + "realizar"^^ . + "dequantization"^^ . + "crates/aprender-serve/src/quantize/format_trait.rs"^^ . + . + "method"^^ . + "realizar::quantize::format_trait::Q4K"^^ . + "dequant_value"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "q6k_superblock"^^ . + "crates/aprender-serve/src/quantize/format_trait.rs"^^ . + . + "struct"^^ . + "realizar::quantize::format_trait"^^ . + "Q6K"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "size_ordering"^^ . + "crates/aprender-serve/src/quantize/format_trait.rs"^^ . + . + "trait"^^ . + "realizar::quantize::format_trait"^^ . + "QuantBlockFormat"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "quantized_dot"^^ . + "crates/aprender-serve/src/quantize/fused_k.rs"^^ . + . + "fn"^^ . + "realizar::quantize::fused_k"^^ . + "fused_q4k_dot"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "simd_only_threshold"^^ . + "crates/aprender-serve/src/quantize/fused_k.rs"^^ . + . + "fn"^^ . + "realizar::quantize::fused_k"^^ . + "fused_q4k_dot_simd"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "ffn_sublayer"^^ . + "crates/aprender-serve/src/quantize/quantize_rmsnorm_into.rs"^^ . + . + "fn"^^ . + "realizar::quantize::quantize_rmsnorm_into"^^ . + "fused_swiglu_simd"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "inline"^^ . + "implemented"^^ . + "realizar"^^ . + "rotation_matrix"^^ . + "crates/aprender-serve/src/quantize/rope.rs"^^ . + . + "fn"^^ . + "realizar::quantize::rope"^^ . + "apply_rope_rotation_simd"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "relu"^^ . + . + "realizar"^^ . + "relu"^^ . + "false"^^ . + "no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "rmsnorm"^^ . + . + "realizar"^^ . + "rmsnorm"^^ . + "false"^^ . + "no `fn rmsnorm` (free or in an impl) or item `rmsnorm` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "hybrid_block"^^ . + "hybrid_layer_schedule"^^ . + . + . + "realizar::safetensors::safetensors_config::SafeTensorsConfig"^^ . + "is_hybrid_attention"^^ . + "false"^^ . + "no `mod safetensors_config` or `use … safetensors_config` in `crates/aprender-serve/src/safetensors/mod.rs`"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "safetensors_loader_validation"^^ . + "crates/aprender-serve/src/safetensors_cuda_load_safe_tensors.rs"^^ . + . + "method"^^ . + "realizar::safetensors_cuda::SafeTensorsCudaModel"^^ . + "upload_weights"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "sample"^^ . + . + "realizar"^^ . + "sample"^^ . + "false"^^ . + "no `fn sample` (free or in an impl) or item `sample` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "derive"^^ . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "request_state"^^ . + "crates/aprender-serve/src/scheduler/mod_max_scheduler.rs"^^ . + . + "enum"^^ . + "realizar::scheduler::mod_max_scheduler"^^ . + "SlotState"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "scheduling_fairness"^^ . + "crates/aprender-serve/src/scheduler/mod_max_scheduler.rs"^^ . + . + "method"^^ . + "realizar::scheduler::mod_max_scheduler"^^ . + "handle_preemption"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "sigmoid"^^ . + . + "realizar"^^ . + "sigmoid"^^ . + "false"^^ . + "no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "silu"^^ . + . + "realizar"^^ . + "silu"^^ . + "false"^^ . + "no `fn silu` (free or in an impl) or item `silu` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "softmax"^^ . + . + "realizar"^^ . + "softmax"^^ . + "false"^^ . + "no `fn softmax` (free or in an impl) or item `softmax` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "standard_softmax"^^ . + . + "realizar"^^ . + "standard_softmax"^^ . + "false"^^ . + "no `fn standard_softmax` (free or in an impl) or item `standard_softmax` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "tensor_count"^^ . + . + "realizar"^^ . + "tensor_count"^^ . + "false"^^ . + "no `fn tensor_count` (free or in an impl) or item `tensor_count` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "name_bijection"^^ . + "crates/aprender-serve/src/tensor_names.rs"^^ . + . + "fn"^^ . + "realizar::tensor_names"^^ . + "resolve_global"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "realizar"^^ . + "decode"^^ . + "crates/aprender-serve/src/tokenizer_sentence_piece.rs"^^ . + . + "method"^^ . + "realizar::tokenizer::Tokenizer"^^ . + "decode"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "realizar"^^ . + "encode"^^ . + "crates/aprender-serve/src/tokenizer_sentence_piece.rs"^^ . + . + "method"^^ . + "realizar::tokenizer::Tokenizer"^^ . + "encode"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "top_k"^^ . + . + "realizar"^^ . + "top_k"^^ . + "false"^^ . + "no `fn top_k` (free or in an impl) or item `top_k` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "top_p"^^ . + . + "realizar"^^ . + "top_p"^^ . + "false"^^ . + "no `fn top_p` (free or in an impl) or item `top_p` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "total_bytes"^^ . + . + "realizar"^^ . + "total_bytes"^^ . + "false"^^ . + "no `fn total_bytes` (free or in an impl) or item `total_bytes` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "transpose"^^ . + . + "realizar"^^ . + "transpose"^^ . + "false"^^ . + "no `fn transpose` (free or in an impl) or item `transpose` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "update"^^ . + . + "realizar"^^ . + "update"^^ . + "false"^^ . + "no `fn update` (free or in an impl) or item `update` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "implemented"^^ . + "realizar"^^ . + "kv_cache_growth"^^ . + . + "realizar::verification_specs"^^ . + "verify_kv_cache_size"^^ . + "false"^^ . + "no `mod verification_specs` or `use … verification_specs` in `crates/aprender-serve/src/arch_requirements.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/demos/harmonic_oscillator.rs"^^ . + . + "method"^^ . + "simular::demos::harmonic_oscillator"^^ . + "run_periods"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/demos/kepler_orbit.rs"^^ . + . + "method"^^ . + "simular::demos::kepler_orbit"^^ . + "run_orbits"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "simulate"^^ . + "crates/aprender-simulate/src/domains/ml/mod.rs"^^ . + . + "method"^^ . + "simular::domains::ml"^^ . + "simulate"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/audit.rs"^^ . + . + "method"^^ . + "simular::edd::audit"^^ . + "find_by_step_id"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/audit.rs"^^ . + . + "method"^^ . + "simular::edd::audit"^^ . + "new"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/audit.rs"^^ . + . + "method"^^ . + "simular::edd::audit"^^ . + "seek"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/audit.rs"^^ . + . + "method"^^ . + "simular::edd::audit"^^ . + "step_forward"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/gui_coverage.rs"^^ . + . + "method"^^ . + "simular::edd::gui_coverage"^^ . + "register_journey"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/v2.rs"^^ . + . + "method"^^ . + "simular::edd::v2"^^ . + "record_step"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/v2.rs"^^ . + . + "method"^^ . + "simular::edd::v2"^^ . + "record_step_with_equations"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/edd/v2.rs"^^ . + . + "method"^^ . + "simular::edd::v2"^^ . + "step_count"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/engine/clock.rs"^^ . + . + "method"^^ . + "simular::engine::clock"^^ . + "steps_until"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/engine/clock.rs"^^ . + . + "method"^^ . + "simular::engine::clock"^^ . + "timestep_secs"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/orbit/heijunka.rs"^^ . + . + "method"^^ . + "simular::orbit::heijunka"^^ . + "estimate_substeps"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/orbit/heijunka.rs"^^ . + . + "method"^^ . + "simular::orbit::heijunka"^^ . + "substep_multiplier"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "wasm_bindgen"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/orbit/wasm.rs"^^ . + . + "method"^^ . + "simular::orbit::wasm"^^ . + "run_steps"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "wasm_bindgen"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/orbit/wasm.rs"^^ . + . + "method"^^ . + "simular::orbit::wasm"^^ . + "step_days"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "wasm_bindgen"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/orbit/wasm.rs"^^ . + . + "method"^^ . + "simular::orbit::wasm"^^ . + "step_hours"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "simulate"^^ . + "crates/aprender-simulate/src/scenarios/portfolio.rs"^^ . + . + "method"^^ . + "simular::scenarios::portfolio"^^ . + "simulate_path"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "simular"^^ . + "step"^^ . + "crates/aprender-simulate/src/tui/tsp_app.rs"^^ . + . + "method"^^ . + "simular::tui::tsp_app"^^ . + "step"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "import_buffer"^^ . + "crates/aprender-compute/src/backends/gpu/batch/mod.rs"^^ . + . + "method"^^ . + "trueno::backends::gpu::batch::GpuCommandBatch"^^ . + "import_buffer"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "single_encoder"^^ . + "crates/aprender-compute/src/backends/gpu/batch/execute/mod.rs"^^ . + . + "method"^^ . + "trueno::backends::gpu::batch::execute::GpuCommandBatch"^^ . + "execute"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "pipeline_cache"^^ . + "crates/aprender-compute/src/backends/gpu/batch/execute/dispatch.rs"^^ . + . + "type"^^ . + "trueno::backends::gpu::batch::execute::dispatch"^^ . + "PipelineCache"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "allow"^^ . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "execute_matmul_op"^^ . + "crates/aprender-compute/src/backends/gpu/batch/execute/dispatch.rs"^^ . + . + "method"^^ . + "trueno::backends::gpu::batch::execute::dispatch"^^ . + "encode_matmul_op"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "matmul_dispatch"^^ . + "crates/aprender-compute/src/backends/gpu/device/linalg/matmul.rs"^^ . + . + "method"^^ . + "trueno::backends::gpu::device::linalg::matmul"^^ . + "matmul_async"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "provable_contracts_macros::contract"^^ . + "partial"^^ . + "trueno"^^ . + "rmsnorm_correctness"^^ . + "crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs"^^ . + . + "method"^^ . + "trueno::backends::gpu::device::linalg::wgsl_forward"^^ . + "forward_model"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "matmul_shader"^^ . + "crates/aprender-compute/src/backends/gpu/shaders/basic_ops.rs"^^ . + . + "const"^^ . + "trueno::backends::gpu::shaders::basic_ops"^^ . + "MATMUL_SHADER"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "target_feature"^^ . + "implemented"^^ . + "trueno"^^ . + "dequant"^^ . + "crates/aprender-compute/src/backends/q4k/gemv/avx512.rs"^^ . + . + "fn"^^ . + "trueno::backends::q4k::gemv::avx512"^^ . + "compute_chunk_q4k_avx512"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "target_feature"^^ . + "implemented"^^ . + "trueno"^^ . + "dequant"^^ . + "crates/aprender-compute/src/backends/q4k/gemv/avx512.rs"^^ . + . + "fn"^^ . + "trueno::backends::q4k::gemv::avx512"^^ . + "matmul_q4k_f32_avx512"^^ . + "true"^^ . + "pub(crate)"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "target_feature"^^ . + "implemented"^^ . + "trueno"^^ . + "peak_throughput"^^ . + "crates/aprender-compute/src/blis/compute.rs"^^ . + . + "fn"^^ . + "trueno::blis::compute"^^ . + "avx512_microkernel_8x16_rowmajor"^^ . + "true"^^ . + "pub(super)"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "target_feature"^^ . + "implemented"^^ . + "trueno"^^ . + "flops_per_tile"^^ . + "crates/aprender-compute/src/blis/compute.rs"^^ . + . + "fn"^^ . + "trueno::blis::compute"^^ . + "gemm_blis_avx512_large"^^ . + "true"^^ . + "private"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "flops_per_tile"^^ . + "crates/aprender-compute/src/blis/compute.rs"^^ . + . + "fn"^^ . + "trueno::blis::compute"^^ . + "pack_b_block_nr16"^^ . + "true"^^ . + "pub(super)"^^ . + . + . + . + "cfg"^^ . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "amdahl_speedup"^^ . + "crates/aprender-compute/src/blis/parallel.rs"^^ . + . + "fn"^^ . + "trueno::blis::parallel"^^ . + "gemm_blis_parallel"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "bounds"^^ . + . + "trueno"^^ . + "bounds"^^ . + "false"^^ . + "no `fn bounds` (free or in an impl) or item `bounds` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "inline"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno"^^ . + "brick_ordering"^^ . + "crates/aprender-compute/src/brick/profiler/mod.rs"^^ . + . + "method"^^ . + "trueno::brick::profiler::BrickProfiler"^^ . + "brick_stats"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno"^^ . + "sync_verification"^^ . + "crates/aprender-compute/src/brick/profiler/mod.rs"^^ . + . + "method"^^ . + "trueno::brick::profiler::BrickProfiler"^^ . + "set_sync_mode"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno"^^ . + "wall_coverage"^^ . + "crates/aprender-compute/src/brick/profiler/mod.rs"^^ . + . + "method"^^ . + "trueno::brick::profiler::BrickProfiler"^^ . + "total_ns"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno"^^ . + "token_accounting"^^ . + "crates/aprender-compute/src/brick/profiler/mod.rs"^^ . + . + "method"^^ . + "trueno::brick::profiler::BrickProfiler"^^ . + "total_tokens"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "bytes_per_param"^^ . + . + "trueno"^^ . + "bytes_per_param"^^ . + "false"^^ . + "no `fn bytes_per_param` (free or in an impl) or item `bytes_per_param` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "precision"^^ . + . + "trueno::cbtop::adaptive_ml::types::ClassificationMetrics"^^ . + "precision"^^ . + "false"^^ . + "no `mod cbtop` or `use … cbtop` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "recall"^^ . + . + "trueno::cbtop::adaptive_ml::types::ClassificationMetrics"^^ . + "recall"^^ . + "false"^^ . + "no `mod cbtop` or `use … cbtop` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "tensor_count"^^ . + . + "trueno::cbtop::quantize::gguf::GgufReader"^^ . + "tensor_count"^^ . + "false"^^ . + "no `mod cbtop` or `use … cbtop` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "throughput_bound"^^ . + . + "trueno::cbtop::roofline::HardwareProfile"^^ . + "theoretical_peak_at_oi"^^ . + "false"^^ . + "no `mod cbtop` or `use … cbtop` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "quantize"^^ . + . + "trueno::crates::trueno_quant::quantize"^^ . + "quantize_q4_k"^^ . + "false"^^ . + "no `mod crates` or `use … crates` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "eigenvector"^^ . + . + "trueno"^^ . + "eigenvector"^^ . + "false"^^ . + "no `fn eigenvector` (free or in an impl) or item `eigenvector` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "embedding_lookup"^^ . + . + "trueno"^^ . + "embedding_lookup"^^ . + "false"^^ . + "no `fn embedding_lookup` (free or in an impl) or item `embedding_lookup` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "gelu"^^ . + . + "trueno"^^ . + "gelu"^^ . + "false"^^ . + "no `fn gelu` (free or in an impl) or item `gelu` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "log_softmax"^^ . + . + "trueno"^^ . + "log_softmax"^^ . + "false"^^ . + "no `fn log_softmax` (free or in an impl) or item `log_softmax` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "matmul"^^ . + . + "trueno"^^ . + "matmul"^^ . + "false"^^ . + "no `fn matmul` (free or in an impl) or item `matmul` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "bandwidth_ceiling"^^ . + "crates/aprender-compute/src/monitor/compute_device.rs"^^ . + . + "trait-method"^^ . + "trueno::monitor::compute_device::ComputeDevice"^^ . + "memory_bandwidth_gbps"^^ . + "true"^^ . + "private"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "predict"^^ . + . + "trueno"^^ . + "predict"^^ . + "false"^^ . + "no `fn predict` (free or in an impl) or item `predict` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "read"^^ . + . + "trueno"^^ . + "read"^^ . + "false"^^ . + "no `fn read` (free or in an impl) or item `read` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "relu"^^ . + . + "trueno"^^ . + "relu"^^ . + "false"^^ . + "no `fn relu` (free or in an impl) or item `relu` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "sigmoid"^^ . + . + "trueno"^^ . + "sigmoid"^^ . + "false"^^ . + "no `fn sigmoid` (free or in an impl) or item `sigmoid` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "silu"^^ . + . + "trueno"^^ . + "silu"^^ . + "false"^^ . + "no `fn silu` (free or in an impl) or item `silu` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "softmax"^^ . + . + "trueno"^^ . + "softmax"^^ . + "false"^^ . + "no `fn softmax` (free or in an impl) or item `softmax` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "update"^^ . + . + "trueno"^^ . + "update"^^ . + "false"^^ . + "no `fn update` (free or in an impl) or item `update` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "implemented"^^ . + "trueno"^^ . + "write"^^ . + . + "trueno"^^ . + "write"^^ . + "false"^^ . + "no `fn write` (free or in an impl) or item `write` in `crates/aprender-compute/src/lib.rs`"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno_db"^^ . + "insert"^^ . + "crates/aprender-db/src/gpu/jit.rs"^^ . + . + "method"^^ . + "trueno_db::gpu::jit"^^ . + "get_or_insert"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "wasm_bindgen"^^ . + "implemented"^^ . + "trueno_db"^^ . + "query"^^ . + "crates/aprender-db/src/wasm.rs"^^ . + . + "method"^^ . + "trueno_db::wasm"^^ . + "query"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "pcie_overhead"^^ . + "crates/aprender-gpu/src/driver/memory/buffer.rs"^^ . + . + "method"^^ . + "trueno_gpu::driver::memory::buffer::GpuBuffer"^^ . + "from_host_registered"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "culink_skip"^^ . + "crates/aprender-gpu/src/driver/module.rs"^^ . + . + "fn"^^ . + "trueno_gpu::driver::module"^^ . + "compile_ptx_to_cubin"^^ . + "true"^^ . + "private"^^ . + . + . + . + "derive"^^ . + "doc"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "attention"^^ . + "crates/aprender-gpu/src/kernels/attention/flash/mod.rs"^^ . + . + "struct"^^ . + "trueno_gpu::kernels::attention"^^ . + "AttentionKernel"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "backward_a_gemm"^^ . + "shared_memory_per_tile"^^ . + "tiled_gemm_arithmetic_intensity"^^ . + "crates/aprender-gpu/src/kernels/backward/gemm.rs"^^ . + . + "method"^^ . + "trueno_gpu::kernels::backward::gemm::GemmBackwardAKernel"^^ . + "tiled"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "unrolled_instruction_ratio"^^ . + "crates/aprender-gpu/src/kernels/backward/gemm.rs"^^ . + . + "method"^^ . + "trueno_gpu::kernels::backward::gemm::GemmBackwardAKernel"^^ . + "tiled_unrolled"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "doc"^^ . + "must_use"^^ . + "implemented"^^ . + "trueno_gpu"^^ . + "backward_b_gemm"^^ . + "crates/aprender-gpu/src/kernels/backward/gemm.rs"^^ . + . + "method"^^ . + "trueno_gpu::kernels::backward::gemm::GemmBackwardBKernel"^^ . + "tiled"^^ . + "true"^^ . + "pub"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::chunk::mod"^^ . + "set_embedding"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "average_precision"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "compute"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "f1_at_k"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "hit_rate_at_k"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "mean_reciprocal_rank"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "ndcg_at_k"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "precision_at_k"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::metrics"^^ . + "recall_at_k"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::multivector::codec"^^ . + "compress"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::multivector::codec"^^ . + "find_nearest_centroid"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::multivector::codec"^^ . + "train"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::multivector::index"^^ . + "insert"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::multivector::types"^^ . + "new"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "embed"^^ . + . + "trueno_rag::src::retrieve"^^ . + "embedder"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::retrieve"^^ . + "retrieve"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::retrieve"^^ . + "retrieve_dense"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "retrieve"^^ . + . + "trueno_rag::src::retrieve"^^ . + "retrieve_sparse"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "chunk"^^ . + . + "trueno_rag::src::sqlite::mod"^^ . + "chunk_count"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . + . + . + . + "implemented"^^ . + "trueno_rag"^^ . + "chunk"^^ . + . + "trueno_rag::src::sqlite::mod"^^ . + "get_chunk"^^ . + "false"^^ . + "crate `trueno_rag` is not a workspace member"^^ . . . "admitted"^^ . @@ -12830,15 +29328,15 @@ "ProvableContracts.Theorems.Alibi.Slopes"^^ . "alibi_slope_lt_one"^^ . "true"^^ . - . - . - "grounded"^^ . - "Alibi"^^ . - "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Alibi/Slopes.lean"^^ . - . - "ProvableContracts.Theorems.Alibi.Slopes"^^ . - "alibi_slope_pos"^^ . - "true"^^ . + . + . + "grounded"^^ . + "Alibi"^^ . + "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Alibi/Slopes.lean"^^ . + . + "ProvableContracts.Theorems.Alibi.Slopes"^^ . + "alibi_slope_real_pos"^^ . + "true"^^ . . . "grounded"^^ . @@ -14906,23 +31404,6 @@ "ProvableContracts.Theorems.MatMul.Associativity"^^ . "matmul_assoc"^^ . "true"^^ . - . - . - "grounded"^^ . - "MatMul"^^ . - "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/MatMul/CooperativeTiling.lean"^^ . - . - "ProvableContracts.Theorems.MatMul.CooperativeTiling"^^ . - "matmul_block_sum"^^ . - "true"^^ . - . - . - "grounded"^^ . - "MatMul"^^ . - "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/MatMul/CooperativeTiling.lean"^^ . - "ProvableContracts.Theorems.MatMul.CooperativeTiling"^^ . - "tiled_k_sum_eq_full_sum"^^ . - "true"^^ . . . "grounded"^^ . @@ -15235,6 +31716,38 @@ "ProvableContracts.Theorems.Quantization.NF4Dequant"^^ . "gpu_cpu_parity"^^ . "true"^^ . + . + . + "grounded"^^ . + "Quantization"^^ . + "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Quantization/NF4Dequant.lean"^^ . + "ProvableContracts.Theorems.Quantization.NF4Dequant"^^ . + "nf4_lut_bounded"^^ . + "true"^^ . + . + . + "grounded"^^ . + "Quantization"^^ . + "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Quantization/NF4Dequant.lean"^^ . + "ProvableContracts.Theorems.Quantization.NF4Dequant"^^ . + "nf4_lut_monotone"^^ . + "true"^^ . + . + . + "grounded"^^ . + "Quantization"^^ . + "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Quantization/NF4Dequant.lean"^^ . + "ProvableContracts.Theorems.Quantization.NF4Dequant"^^ . + "nf4_lut_q_bounded"^^ . + "true"^^ . + . + . + "grounded"^^ . + "Quantization"^^ . + "crates/aprender-contracts-staging/lean/ProvableContracts/Theorems/Quantization/NF4Dequant.lean"^^ . + "ProvableContracts.Theorems.Quantization.NF4Dequant"^^ . + "nf4_lut_q_monotone"^^ . + "true"^^ . . . "grounded"^^ . diff --git a/contracts/cooperative-matrix-gemm-v1.yaml b/contracts/cooperative-matrix-gemm-v1.yaml deleted file mode 100644 index 3ad3cf8b68..0000000000 --- a/contracts/cooperative-matrix-gemm-v1.yaml +++ /dev/null @@ -1,79 +0,0 @@ -metadata: - version: 1.0.0 - created: '2026-04-02' - author: PAIML Engineering - description: > - Cooperative matrix GEMM — hardware tensor core acceleration via - VK_KHR_cooperative_matrix (wgpu 29.0+). Replaces software tiled GEMM - (375 GFLOPS) with hardware WMMA (expected 1000+ GFLOPS on GB10). - references: - - "VK_KHR_cooperative_matrix Vulkan extension" - - "wgpu v29.0.0 cooperative matrix support (2026-03-19)" - - "NVIDIA Blackwell GB10: BF16+FP8 cooperative matrix, revision 2" - -equations: - cooperative_gemm: - formula: "C[m,n] = α * Σ_k A[m,k] * B[k,n] + β * C[m,n]" - domain: "A ∈ ℝ^{M×K}, B ∈ ℝ^{K×N}, α,β ∈ ℝ" - codomain: "C ∈ ℝ^{M×N}" - invariants: - - "Result matches software tiled GEMM within ε < 1e-3 (f32)" - - "F16 input, F32 accumulation (GB10 config 3: M=16 K=16 N=16)" - lean_theorem: ProvableContracts.CooperativeMatrix.matmul_block_sum - f16_error_bound: - formula: "|C_f32_accum - C_exact| ≤ K * ε_f16 * max|A| * max|B|" - domain: "K reduction dimension, ε_f16 = 2^{-10}" - codomain: "error bound ∈ ℝ≥0" - lean_theorem: ProvableContracts.CooperativeMatrix.f16_accumulation_error_bound - notes: - - "Uses wgpu::Features::SUBGROUP_VERTEX_STAGE or cooperative_matrix feature" - - "Dispatch: ceil(M/coop_M) × ceil(N/coop_N) workgroups" - -invariants: - parity_with_tiled: - statement: "|coop_result - tiled_result| < 1e-3 for all elements" - rationale: "Hardware GEMM must match software reference" - - throughput_improvement: - statement: "coop_gflops >= 2 * tiled_gflops" - rationale: "Tensor cores should at least double throughput" - - fallback: - statement: "If cooperative matrix not available, fall back to tiled GEMM" - rationale: "Not all GPUs support VK_KHR_cooperative_matrix" - -falsification_tests: - - id: FALSIFY-COOP-001 - rule: parity_with_tiled - prediction: "max |coop - tiled| < 1e-3" - if_fails: "Cooperative matrix GEMM diverges from tiled reference" - - id: FALSIFY-COOP-002 - rule: throughput_improvement - prediction: "coop GFLOPS > 2 * tiled GFLOPS" - if_fails: "Cooperative matrix provides no performance benefit" - - id: FALSIFY-COOP-003 - rule: fallback - prediction: "Falls back to tiled GEMM, no crash" - if_fails: "Missing fallback causes crash on unsupported hardware" - -proof_obligations: - - id: CMG-POST-001 - type: postcondition - property: Parity with tiled reference - formal: "|coop - tiled| < 1e-3" - -kani_harnesses: - - id: KANI-COOP-001 - obligation: parity_with_tiled - property: Output parity - bound: 8 - strategy: bounded_int - -qa_gate: - id: F-COOP-GEMM-001 - name: Cooperative Matrix GEMM Contract - description: Quality gate for cooperative matrix GEMM hardware tensor core acceleration - checks: - - validation - - falsification - diff --git a/contracts/crate-hygiene-v1.yaml b/contracts/crate-hygiene-v1.yaml index 3b6a12fbb7..d9d03c24b6 100644 --- a/contracts/crate-hygiene-v1.yaml +++ b/contracts/crate-hygiene-v1.yaml @@ -126,7 +126,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 5 + total_obligations: 3 proven: 0 tested: 5 status: tested diff --git a/contracts/crate-readme-v1.yaml b/contracts/crate-readme-v1.yaml index b37ea810b5..d1bcff05e2 100644 --- a/contracts/crate-readme-v1.yaml +++ b/contracts/crate-readme-v1.yaml @@ -58,7 +58,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 2 + total_obligations: 1 proven: 0 tested: 2 status: tested diff --git a/contracts/csv-train.yaml b/contracts/csv-train.yaml new file mode 100644 index 0000000000..a31e1637dc --- /dev/null +++ b/contracts/csv-train.yaml @@ -0,0 +1,79 @@ +# ───────────────────────────────────────────────────────────────────────────── +# csv-train — one tracked dataset as a graded entity (ONT-001 §5 ONT-4c, Appendix B.8; #3847, PMAT-3847) +# +# WHAT IS GRADED. `extract:csv` reads crates/aprender-train/data/train.csv: the header row, the column names, the +# data-row count, one inferred dtype per column, the sha256 of the bytes, and the producer this contract names. +# `csv:producer` is `resolves: path` (v4.16: measured 0 of 9 tracked CSVs are named by a workflow run: step) — +# prepare_data.sh writes this file's header. A data row whose column count differs from the header's is refused +# naming the file and the row. +# +# KIND: pattern. The edit that makes it fail: rename a header column, or add a fourth. +# ───────────────────────────────────────────────────────────────────────────── +name: csv-train +version: "1.0.0" +scope: > + crates/aprender-train/data/train.csv: its header, shape and dtypes, and the script that produces it. Out of + scope: the values' statistics. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + The aprender-train training CSV becomes a csv:Dataset node with an exact header, three columns, typed + dtypes and a producer that exists. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md v4.16 Appendix B.8' + - 'crates/aprender-contracts/src/ontology/extract/csv.rs' + - 'crates/aprender-train/prepare_data.sh (the producer)' + +entity: + type: csv + ref: crates/aprender-train/data/train.csv + properties: + producer: crates/aprender-train/prepare_data.sh + +relations: + depends_on: [ont-sigma-v1, ont-shapes-v1] + +shape: + targetClass: csv:Dataset + closed: true + properties: + - {path: csv:column, minCount: 3, maxCount: 3} + - {path: csv:header, in: ["feature1,feature2,label"]} + - {path: csv:rows, minCount: 1, datatype: xsd:integer} + - {path: csv:dtype, in: [string, float, int]} + - {path: csv:sha256, minCount: 1, maxCount: 1} + - {path: csv:producer, minCount: 1, maxCount: 1, resolves: path} + +equations: + rectangular: + formula: "∀ row ∈ data(train.csv): |row| = |header(train.csv)| = 3" + domain: every non-empty line after the header + codomain: one csv:Dataset node, or a PV-ONT-012 refusal naming the file and the row + invariants: + - a ragged row is refused by name, never truncated or padded + preconditions: + - the file is UTF-8 + postconditions: + - the header is exactly feature1,feature2,label + lean_theorem: "none — L4 not declared" + +invariants: + - id: CSVT-INV-001 + property: every data row has the header's column count + formal: '∀ r ∈ rows(train.csv): |r| = 3' + prose: false + +falsification_tests: + - id: FALSIFY-CSVT-001 + rule: a ragged row is refused + prediction: > + appending a row with four fields makes extract:csv refuse the file naming the row, and the shapes gate + exits 1 with PV-ONT-012 + test: cargo test -p aprender-contracts --lib ontology::extract::csv + if_fails: a malformed dataset trains a model silently diff --git a/contracts/entrenar/binding.yaml b/contracts/entrenar/binding.yaml new file mode 100644 index 0000000000..7b13fc37d4 --- /dev/null +++ b/contracts/entrenar/binding.yaml @@ -0,0 +1,369 @@ +version: 1.0.0 +target_crate: entrenar +bindings: +- contract: backward-pass-v1.yaml + equation: backward + module_path: entrenar::transformer::cuda_block + function: backward + status: partial + notes: '#4369: #[contract] site at crates/aprender-train/src/transformer/cuda_block.rs:1275 (feature cuda) had no binding row, and contracts/backward-pass-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: apr-checkpoint-v1.yaml + equation: save_checkpoint + module_path: entrenar::finetune::classify_trainer + function: save_checkpoint + signature: 'fn save_checkpoint(&self, path: &Path) -> Result<()>' + status: implemented + notes: APR v1.2.0 format with adapter + training state +- contract: apr-checkpoint-v1.yaml + equation: load_checkpoint + module_path: entrenar::finetune::classify_trainer + function: load_checkpoint + signature: 'fn load_checkpoint(&mut self, path: &Path) -> Result<()>' + status: implemented + notes: Restores optimizer state, epoch, step counters +- contract: apr-checkpoint-v1.yaml + equation: apr_writer + module_path: aprender::serialization::apr + function: AprWriter::write + signature: 'fn write(&self, path: &Path, tensors: &[(&str, &Array1)]) -> Result<()>' + status: implemented + notes: Cross-crate; aprender provides APR format I/O +- contract: apr-checkpoint-v1.yaml + equation: apr_reader + module_path: aprender::serialization::apr + function: AprReader::read + signature: 'fn read(path: &Path) -> Result)>>' + status: implemented + notes: Cross-crate; aprender provides APR format I/O +- contract: attention-head-extraction-v1.yaml + equation: forward_with_lora + module_path: entrenar::transformer::attention + function: MultiHeadAttention::forward_with_lora + signature: 'fn forward_with_lora(&self, x: &Tensor, mask: Option<&Tensor>) -> Tensor' + status: implemented + notes: 'KAIZEN-016: 99.2% reduction in per-head allocations' +- contract: attention-head-extraction-v1.yaml + equation: head_extraction + module_path: entrenar::transformer::attention + function: MultiHeadAttention::forward + signature: 'fn forward(&self, x: &Tensor, mask: Option<&Tensor>) -> Tensor' + status: implemented + notes: Pre-allocated head buffers; zero per-head heap alloc +- contract: cuda-classify-training-v1.yaml + equation: gpu_forward + module_path: entrenar::transformer::cuda_block + function: CudaTransformerBlock::gpu_forward + signature: 'fn gpu_forward(&self, hidden: &CudaBuffer) -> CudaBuffer' + status: implemented + notes: GPU forward pass; parity within 1e-3 of CPU +- contract: cuda-classify-training-v1.yaml + equation: weight_roundtrip + module_path: entrenar::autograd::cuda_training + function: weight_roundtrip + signature: 'fn weight_roundtrip(cpu: &[f32], device: &CudaDevice) -> Vec' + status: pending + notes: function not found in source +- contract: cuda-classify-training-v1.yaml + equation: device_dispatch + module_path: entrenar::autograd::cuda_training + function: ComputeDevice::auto_detect + signature: fn auto_detect() -> ComputeDevice + status: implemented + notes: Auto-detects CUDA, wgpu, or CPU fallback +- contract: distributed-training-v1.yaml + equation: gradient_allreduce + module_path: entrenar::finetune::data_parallel + function: DataParallelCoordinator::allreduce + signature: 'fn allreduce(&self, grads: &mut [Tensor]) -> Result<()>' + status: implemented + notes: Phase 1 complete (multi-GPU); Phase 2 in progress (multi-node) +- contract: distributed-training-v1.yaml + equation: sharding + module_path: entrenar::finetune::data_parallel + function: DataParallelCoordinator::shard_data + signature: 'fn shard_data(&self, dataset: &[Sample]) -> Vec>' + status: implemented + notes: function not found in source +- contract: distributed-training-v1.yaml + equation: weighted_loss + module_path: entrenar::finetune::data_parallel + function: weighted_loss_aggregate + signature: 'fn weighted_loss_aggregate(losses: &[f32], counts: &[usize]) -> f32' + status: pending + notes: function not found in source +- contract: distributed-training-v1.yaml + equation: swiglu_ffn + module_path: entrenar::transformer::cuda_block + function: CudaTransformerBlock::swiglu_ffn + signature: 'fn swiglu_ffn(&self, hidden: &CudaBuffer) -> CudaBuffer' + status: implemented + notes: function not found in source +- contract: distributed-training-v1.yaml + equation: lora_gradient_size + module_path: entrenar::finetune::data_parallel + function: lora_gradient_byte_size + signature: 'fn lora_gradient_byte_size(rank: usize, hidden: usize, layers: usize) + -> usize' + status: pending + notes: function not found in source +- contract: lora-gradient-flow-v1.yaml + equation: transpose_tracked + module_path: entrenar::autograd::ops::matmul + function: transpose_tracked + signature: 'fn transpose_tracked(x: &Tensor) -> Tensor' + status: implemented + notes: 'KAIZEN-018: tracked transpose for LoRA gradient flow' +- contract: lora-gradient-flow-v1.yaml + equation: lora_forward + module_path: entrenar::transformer::attention + function: MultiHeadAttention::forward_with_lora + signature: 'fn forward_with_lora(&self, x: &Tensor, mask: Option<&Tensor>) -> Tensor' + status: implemented + notes: wgpu LoRA gradients restored; 5K to 5.9M trainable params +- contract: qlora-hyperparameters-v1.yaml + equation: learning_rate_scaling + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::learning_rate + signature: fn learning_rate(&self) -> f32 + status: implemented + notes: HP design pending; contract specifies constraints +- contract: qlora-hyperparameters-v1.yaml + equation: effective_batch_size + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::effective_batch_size + signature: fn effective_batch_size(&self) -> usize + status: implemented + notes: function not found in source +- contract: qlora-hyperparameters-v1.yaml + equation: lora_alpha_ratio + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::lora_alpha + signature: fn lora_alpha(&self) -> f32 + status: implemented + notes: function not found in source +- contract: qlora-hyperparameters-v1.yaml + equation: seq_len_from_data + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::max_seq_len + signature: fn max_seq_len(&self) -> usize + status: implemented + notes: Derived from P99 of training data lengths +- contract: qlora-hyperparameters-v1.yaml + equation: warmup_fraction + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::warmup_steps + signature: fn warmup_steps(&self) -> usize + status: implemented + notes: function not found in source +- contract: qlora-hyperparameters-v1.yaml + equation: gradient_clip_bound + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::gradient_clip_norm + signature: fn gradient_clip_norm(&self) -> f32 + status: implemented + notes: function not found in source +- contract: qlora-hyperparameters-v1.yaml + equation: epoch_count_imbalanced + module_path: entrenar::finetune::qlora_config + function: QLoraConfig::epochs + signature: fn epochs(&self) -> usize + status: implemented + notes: 3-10 epochs; more for imbalanced datasets +- contract: tensor-rc-data-v1.yaml + equation: tensor_clone + module_path: entrenar::autograd::tensor + function: Tensor::clone + signature: fn clone(&self) -> Tensor + status: implemented + notes: 'KAIZEN-019: Rc bump (O(1)) instead of 16GB deep copies' +- contract: tensor-rc-data-v1.yaml + equation: data_read + module_path: entrenar::autograd::tensor + function: Tensor::data + signature: fn data(&self) -> &Array1 + status: implemented + notes: Shared read access via Rc +- contract: tensor-rc-data-v1.yaml + equation: data_mut + module_path: entrenar::autograd::tensor + function: Tensor::data_mut + signature: fn data_mut(&mut self) -> &mut Array1 + status: implemented + notes: Copy-on-write via Rc::make_mut +- contract: wgpu-resident-weights-v1.yaml + equation: with_resident_weights + module_path: entrenar::transformer::wgpu_block + function: WgpuForwardPass::with_resident_weights + signature: 'fn with_resident_weights(weights: &GpuWeights) -> WgpuForwardPass' + status: implemented + notes: 'KAIZEN-015: one-time FFN weight upload; frozen during QLoRA' +- contract: wgpu-resident-weights-v1.yaml + equation: import_buffer + module_path: trueno::backends::gpu::batch + function: GpuCommandBatch::import_buffer + signature: 'fn import_buffer(&mut self, buffer: &GpuBuffer)' + status: implemented + notes: function not found in source +- contract: wgpu-resident-weights-v1.yaml + equation: vram_budget + module_path: entrenar::finetune::classify_pipeline + function: estimate_wgpu_vram + signature: 'fn estimate_wgpu_vram(model_params: usize) -> usize' + status: implemented + notes: function not found in source +- contract: cma-es-kernel-v1.yaml + equation: sample + module_path: entrenar + function: sample + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: temperature + module_path: entrenar + function: temperature + status: implemented + notes: Auto-inferred by pv infer +- contract: silu-kernel-v1.yaml + equation: sigmoid + module_path: entrenar + function: sigmoid + status: implemented + notes: Auto-inferred by pv infer +- contract: random-forest-v1.yaml + equation: predict + module_path: entrenar + function: predict + status: implemented + notes: Auto-inferred by pv infer +- contract: metrics-classification-v1.yaml + equation: accuracy + module_path: entrenar + function: accuracy + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: greedy + module_path: entrenar + function: greedy + status: implemented + notes: Auto-inferred by pv infer +- contract: metrics-classification-v1.yaml + equation: confusion_matrix + module_path: entrenar + function: confusion_matrix + status: implemented + notes: Auto-inferred by pv infer +- contract: metrics-ranking-v1.yaml + equation: ndcg_at_k + module_path: entrenar + function: ndcg_at_k + status: implemented + notes: Auto-inferred by pv infer +- contract: layernorm-kernel-v1.yaml + equation: statistics + module_path: entrenar + function: statistics + status: implemented + notes: Auto-inferred by pv infer +- contract: kmeans-kernel-v1.yaml + equation: update + module_path: entrenar + function: update + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: top_k + module_path: entrenar + function: top_k + status: implemented + notes: Auto-inferred by pv infer +- contract: metrics-regression-v1.yaml + equation: rmse + module_path: entrenar + function: rmse + status: implemented + notes: Auto-inferred by pv infer +- contract: int8-symmetric-quant-v1.yaml + equation: quantize + module_path: entrenar + function: quantize + status: implemented + notes: Auto-inferred by pv infer +- contract: model-config-algebra-v1.yaml + equation: bounds + module_path: entrenar + function: bounds + status: implemented + notes: Auto-inferred by pv infer +- contract: gated-delta-net-v1.yaml + equation: output + module_path: entrenar + function: output + status: implemented + notes: Auto-inferred by pv infer +- contract: q4k-q6k-superblock-v1.yaml + equation: total_bytes + module_path: entrenar + function: total_bytes + status: implemented + notes: Auto-inferred by pv infer +- contract: attention-kernel-v1.yaml + equation: attention + module_path: entrenar + function: attention + status: implemented + notes: Auto-inferred by pv infer +- contract: transpose-kernel-v1.yaml + equation: transpose + module_path: entrenar + function: transpose + status: implemented + notes: Auto-inferred by pv infer +- contract: matmul-kernel-v1.yaml + equation: matmul + module_path: entrenar + function: matmul + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: relu + module_path: entrenar + function: relu + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: gelu + module_path: entrenar + function: gelu + status: implemented + notes: Auto-inferred by pv infer +- contract: wgpu-transformer-trainer-v1.yaml + equation: C-WGPU-TRAIN-001 + module_path: entrenar::train::transformer_trainer::wgpu_trainer + function: full_train_step + signature: 'fn full_train_step(&mut self, hidden: &[f32], targets: &[u32], model: + &mut WgpuModelState) -> Result<(f32, f32), String>' + status: implemented + notes: 36-layer Qwen3-4B forward + lm_head + loss + backward + AdamW on WGPU +- contract: wgpu-transformer-trainer-v1.yaml + equation: C-WGPU-CKPT-001 + module_path: entrenar::train::transformer_trainer::wgpu_trainer + function: save_checkpoint + signature: 'fn save_checkpoint(&self, dir: &Path, step: u32, loss: f32, rank: u32, + alpha: f32) -> Result' + status: implemented + notes: Serializes LoRA A/B + optimizer state to JSON +- contract: wgpu-transformer-trainer-v1.yaml + equation: C-WGPU-CKPT-002 + module_path: entrenar::train::transformer_trainer::wgpu_trainer + function: load_checkpoint + signature: 'fn load_checkpoint(&mut self, path: &Path) -> Result<(u32, f32), String>' + status: implemented + notes: Restores LoRA adapters with dimension validation +critical_path: +- forward +- checkpoint +- loss +- gradient +- AprWriter +- CudaTransformerBlock diff --git a/contracts/gated-delta-net-v1.yaml b/contracts/gated-delta-net-v1.yaml index 21b743927a..afcc13fcb1 100644 --- a/contracts/gated-delta-net-v1.yaml +++ b/contracts/gated-delta-net-v1.yaml @@ -62,6 +62,17 @@ equations: - input.len() > 0 - input.iter().all(|v| v.is_finite()) lean_theorem: Theorems.Output + gated_rmsnorm: + formula: out_i = x_i / sqrt(Σ_j x_j² / d + ε) × w_i × silu(z_i), per head chunk of d = head_v_dim + domain: x, z ∈ ℝ^{heads × d}, w ∈ ℝ^{d}, ε > 0 + codomain: out ∈ ℝ^{heads × d} + invariants: + - The input is normalised per head; the gate z is not + - w is shared across heads (no head stride); z is indexed per head + preconditions: + - input.len() > 0 + - input.len() % head_v_dim == 0 + - input.iter().all(|v| v.is_finite()) proof_obligations: - id: GDN-BND-001 type: bound diff --git a/contracts/github-entities-v1.yaml b/contracts/github-entities-v1.yaml new file mode 100644 index 0000000000..fb99676395 --- /dev/null +++ b/contracts/github-entities-v1.yaml @@ -0,0 +1,157 @@ +# ────────────────────────────────────────────── +# github-entities-v1 — GitHub repos, issues, pull requests and milestones as focus nodes (ONT-001 §5 ONT-4f; +# issue aprender#4330) +# +# WHAT THIS IS. Four Σ entity types — `repo`, `issue`, `pull-request`, `milestone` — on the `json` extractor, each +# with its own vocabulary (contracts/ontology.yaml `entity_types[].vocabulary`), and the four shapes below over +# them. Each class is ⊑ Json in Σ `subsumes`, so a shape on Json would reach all four through the R-19 closure. +# +# COMMITTED SNAPSHOTS, NEVER A LIVE API. The extractor reads `evidence/github//.json` and nothing +# else (`ontology/extract/json/github.rs`). A snapshot records ONE moment, and its `ref` says which: +# · repo: /@ — must equal the snapshot's own `sha` +# · issue, pull-request, milestone: /#@ — must equal the snapshot's own `updatedAt` +# A disagreement is refused BY THE EXTRACTOR naming both values (PV-ONT-012, Fail), as is a `state: merged` with +# no `mergedAt` (named). Refreshing a snapshot is committing a new one; the gate never goes to the network, so +# its verdict is a function of the tree. +# +# `resolves:` BETWEEN TRACKED SNAPSHOTS ONLY. `pr:baseRepo resolves: repo` and `issue:milestone resolves: +# milestone`: a value naming a tracked snapshot (by `owner/repo[#n]` or by its full ref) becomes an IRI edge to +# that node, which `class:` then checks; a value naming nothing tracked is materialized on `…Unresolved`, held at +# `maxCount: 0` — FAIL CLOSED, never `Unknown`, never looked up live. The extractor's RESOLVES table is asserted +# equal to these declarations by a unit test, so the two cannot drift apart. +# +# ARMED. The four ids are in `contracts/lint-baseline.json` `armed_shapes[]`: a violation here is a Fail. +# +# KIND: pattern. Vocabulary, an extractor and shapes over a graph; the proof is the gate's own case table. +# ────────────────────────────────────────────── +name: github-entities-v1 +version: "1.0.0" +scope: > + How a committed GitHub snapshot under evidence/github// becomes a focus node typed ont:Repo, ont:Issue, + ont:PullRequest or ont:Milestone, and the four shapes over them. Out of scope: fetching or refreshing the + snapshots (a commit does that), and any GitHub entity type Σ does not declare. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + GitHub repositories, issues, pull requests and milestones are focus nodes read from committed snapshots; a + snapshot's ref names the version it records and must agree with it, a merge carries its time, and a + reference between snapshots resolves to a tracked node or fails closed. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §3.6 (the implemented SHACL subset), §3.7, §5 ONT-4f' + - 'aprender#4330 (this row); aprender#4323 (ONT-4c, the Σ entity_type_target_class map)' + - 'crates/aprender-contracts/src/ontology/extract/json/github.rs — the extractor and its case table' + - 'contracts/ontology.yaml — entity_types[].vocabulary, concepts Json/Repo/Issue/PullRequest/Milestone, subsumes' + +entity: + # The contract governs four entity types; `entity:` names one Σ type, so it anchors on `repo`, the type every + # other one resolves back to. The shapes below target all four by class. + type: repo + +relations: + depends_on: [ont-shapes-v1, ont-sigma-v1] + +shapes: + - id: github-repo + targetClass: ont:Repo + properties: + - {path: repo:ref, minCount: 1, maxCount: 1} + - {path: repo:nameWithOwner, minCount: 1, maxCount: 1} + - {path: repo:sha, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{40}$"} + - {path: repo:url, minCount: 1, maxCount: 1} + + - id: github-issue + targetClass: ont:Issue + properties: + - {path: issue:ref, minCount: 1, maxCount: 1} + - {path: issue:repo, minCount: 1, maxCount: 1} + - {path: issue:number, minCount: 1, maxCount: 1, datatype: xsd:integer} + - {path: issue:title, minCount: 1, maxCount: 1} + - {path: issue:state, minCount: 1, maxCount: 1, in: [OPEN, CLOSED]} + - {path: issue:updatedAt, minCount: 1, maxCount: 1} + - {path: issue:milestone, maxCount: 1, nodeKind: IRI, class: ont:Milestone, resolves: milestone} + - {path: issue:milestoneUnresolved, maxCount: 0} + + - id: github-pull-request + targetClass: ont:PullRequest + properties: + - {path: pr:ref, minCount: 1, maxCount: 1} + - {path: pr:repo, minCount: 1, maxCount: 1} + - {path: pr:number, minCount: 1, maxCount: 1, datatype: xsd:integer} + - {path: pr:title, minCount: 1, maxCount: 1} + - {path: pr:state, minCount: 1, maxCount: 1, in: [OPEN, CLOSED, MERGED]} + - {path: pr:updatedAt, minCount: 1, maxCount: 1} + - {path: pr:mergedAt, maxCount: 1} + - {path: pr:baseRepo, minCount: 1, maxCount: 1, nodeKind: IRI, class: ont:Repo, resolves: repo} + - {path: pr:baseRepoUnresolved, maxCount: 0} + + - id: github-milestone + targetClass: ont:Milestone + properties: + - {path: milestone:ref, minCount: 1, maxCount: 1} + - {path: milestone:repo, minCount: 1, maxCount: 1} + - {path: milestone:number, minCount: 1, maxCount: 1, datatype: xsd:integer} + - {path: milestone:title, minCount: 1, maxCount: 1} + - {path: milestone:state, minCount: 1, maxCount: 1, in: [OPEN, CLOSED]} + - {path: milestone:updatedAt, minCount: 1, maxCount: 1} + +equations: + version_slot: + formula: "accepted(s) ⇒ version(ref(s)) = s[vocabulary(type(s)).version]" + domain: "every *.json under evidence/github// for a Σ snapshot type, walked in byte order" + codomain: "one root node typed the type's root_class, or a refusal naming both values" + invariants: + - "a refused snapshot emits no triple" + - "a file or directory the walk cannot place is refused by name, never skipped" + preconditions: + - "Σ declares the type with a vocabulary on the json extractor" + postconditions: + - "two extractions are byte-identical (R-15)" + lean_theorem: none — L4 not declared + resolves: + formula: "value(s, k) ∈ tracked(target(k)) ⇒ edge(s, k, node(value)) ∧ value(s, k) ∉ tracked(target(k)) ⇒ unresolved(s, k)" + domain: "the RESOLVES table: pr:baseRepo → repo, issue:milestone → milestone" + codomain: "an IRI edge to a tracked node, or a literal on the Unresolved predicate" + invariants: + - "no network lookup: only snapshots in the same walk are candidates" + preconditions: + - "the RESOLVES table equals this contract's resolves: declarations" + postconditions: + - "an unresolved reference fails its shape's maxCount 0" + lean_theorem: none — L4 not declared + +invariants: + - id: GHE-INV-001 + property: a snapshot whose ref version disagrees with its own recorded version is refused naming both + formal: 'version(ref(s)) ≠ s[version] ⇒ s ∈ errors ∧ |nodes(s)| = 0' + prose: false + - id: GHE-INV-002 + property: a merged snapshot with no mergedAt is refused naming the field + formal: 'state(s) = merged ∧ |mergedAt(s)| = 0 ⇒ s ∈ errors' + prose: false + - id: GHE-INV-003 + property: a reference to an untracked snapshot fails closed + formal: 'value(s, k) ∉ tracked(target(k)) ⇒ |unresolved(s, k)| = 1 ⇒ Fail(shape(s), s)' + prose: false + +falsification_tests: + - id: FALSIFY-GHE-001 + rule: the three named mutations + prediction: > + a merged pull-request snapshot with no mergedAt is refused naming `mergedAt`; an issue naming an untracked + milestone materializes milestoneUnresolved and no edge; a repo whose sha disagrees with its ref is refused + naming both shas + test: cargo test -p aprender-contracts --lib ontology::extract::json::github + if_fails: a snapshot that contradicts itself, or points at nothing, reads as a clean corpus + - id: FALSIFY-GHE-002 + rule: the gate discriminates on the CLI + prediction: > + the green fixture passes with every GitHub type counted; each mutation fixture fails with exit 1 naming the + file or the focus node and the property + test: cargo test -p aprender-contracts-cli --test ont4f_github_entities + if_fails: the shapes decorate the corpus instead of grading it diff --git a/contracts/hero-svg-v1.yaml b/contracts/hero-svg-v1.yaml index 6d162f35ff..edabaeeb8d 100644 --- a/contracts/hero-svg-v1.yaml +++ b/contracts/hero-svg-v1.yaml @@ -65,7 +65,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 3 + total_obligations: 1 proven: 0 tested: 3 status: tested diff --git a/contracts/kernel-fusion-v1.yaml b/contracts/kernel-fusion-v1.yaml index a9e87d6f2f..411480f9d4 100644 --- a/contracts/kernel-fusion-v1.yaml +++ b/contracts/kernel-fusion-v1.yaml @@ -130,7 +130,7 @@ fusion_decisions: - Q projection GEMV - K projection GEMV - V projection GEMV - call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:371 + call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:394 saves_per_layer: 2 falsification: Splitting fused Q/K/V into three separate GEMV launches must increase per-token wall. fused_gate_up: @@ -143,7 +143,7 @@ fusion_decisions: - Gate projection GEMV - Up projection GEMV - SwiGLU activation - call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:374 + call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:397 saves_per_layer: 2 falsification: Splitting fused Gate+Up+SwiGLU into separate launches must increase per-token wall. fused_gemm_bias_gelu: @@ -183,7 +183,7 @@ fusion_decisions: replaces: - Gate Q4K GEMV (separate input load) - Up Q4K GEMV (separate input load) - call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:377 + call_site: crates/aprender-serve/src/cuda/kernels_generate_gemm_cuda.rs:400 saves_per_layer: 1 notes: | Distinct from FUSION-007 (FusedGateUpKernel = FP32 input) — this diff --git a/contracts/lint-baseline.json b/contracts/lint-baseline.json index ab4181e755..cf42fb2e7f 100644 --- a/contracts/lint-baseline.json +++ b/contracts/lint-baseline.json @@ -1,18 +1,17 @@ { - "_spec": "APR-RELEASE-001 §11.2 — moves only through `make ont-ratchet` (ONT R-6)", - "armed_gates": ["validate", "audit", "score", "verify", "enforce", "enforcement-level", "duplicate-stems", "composition", "sigma", "relations", "shapes"], - "armed_shapes": ["ont-shapes-v1", "ladder-measured", "ladder-green"], + "_spec": "APR-RELEASE-001 §11.2 counters are MEASURED comparand->head by scripts/check_ont_ratchet.sh (#3569); this file holds decisions only", + "armed_gates": ["validate", "audit", "score", "verify", "enforce", "enforcement-level", "duplicate-stems", "composition", "sigma", "relations", "shapes", "proved-is-derived"], + "armed_shapes": ["ont-shapes-v1", "ladder-measured", "ladder-green", "github-repo", "github-issue", "github-pull-request", "github-milestone", "readme-root", "claude-md", "model-setfit-slice", "csv-train", "capability-cells"], + "readme": {"verified_commands":["bash scripts/check_readme_claims.sh"],"withdrawn":[]}, + "claude_md": {"verified_commands":["bash scripts/check_package_includes.sh"],"withdrawn":[]}, "contracts_without_valid_under": 386, + "command": "make lint-ratchet", + "unpaired_theorem_modules": 129, + "contracts_without_depends_on": 277, + "underived_proved_claims": 95, "ont": { - "consumer_present": true, - "contracts_total": 1889, - "entity_types_registered": 11, - "extractors_implemented": 8, - "contracts_anchored": 8, - "contracts_shaped": 6, - "unanchored_but_bindable": 297, "formal_prose": 1464, "legacy_unresolved_depends_on": 8, - "shapes_unarmed": 15 + "liskov_prose": 0 } } diff --git a/contracts/model-capability-ladder-v1.yaml b/contracts/model-capability-ladder-v1.yaml index f3c13618ce..1852abcb79 100644 --- a/contracts/model-capability-ladder-v1.yaml +++ b/contracts/model-capability-ladder-v1.yaml @@ -225,11 +225,43 @@ ladder: now refuses it by name at pre-load rather than silently falling back to CPU, so a long-rung representative for it would owe cells no code can satisfy and a RefusalCell would be a false arithmetic claim. Restore this key with #3714. + # #4590 (operator 2026-09-28, #3715): an arch the release does NOT claim on one host. Only these cells + # leave the owed set, on that host only; the judge prints the entry on every run, and apr warns at load. + declaimed: + - arch: qwen3 + host: gx10 + issue: 4590 + until: "0.70.1" + why: >- + correct but 27-34x slower than Qwen3.5 on GB10 sm_121 (8k think-on: Qwen3-1.7B 967 s, Qwen3-8B + 2163 s vs Qwen3.5-9B 63-80 s; serial prefill, the sm12x default). 0.70.1 P0 ships native sm_121 + SASS and re-tests batched prefill on GB10. lambda still owes every Qwen3 cell. + # G2 (operator ruling 2026-09-28 16:15Z): R1 was Qwen3 only; this widens it to EVERY dense arch gx10 holds. + # Serial prefill is the default for every arch at cc>=120 (select_prefill_path), not only Qwen3. + - arch: qwen2 + host: gx10 + issue: 4590 + until: "0.70.1" + why: >- + GB10 dense models: prompt processing unbatched (11.4 tok/s); fixed in 0.70.1 (#4590). Correct output, + prefill speed only: sm_12x prefills every dense arch one token at a time (select_prefill_path). Withdrawn, + not waived: lambda still owes every Qwen2 cell, and gx10 still owes every Qwen3.5 (hybrid) cell. + # #3715 H4 (cop 2026-09-28 20:26Z, scope = qwen3moe on gx10 only; STAGED, branch-only, operator rules 07:30Z): + # arch-level DATA is clean here -- no named rung and no long-rung representative is qwen3moe. + - arch: qwen3moe + host: gx10 + issue: 3715 + until: "0.70.1" + why: >- + Qwen3-30B-A3B (and Coder-30B-A3B) on GB10 sm_121: every verb hits the 600 s cell timeout at the + 20k rung (0d H4 19:51Z, 12+12 cells). The 0.69.1 pre-load refusal is gone in 0.70.0-dev, so the + cells run and time out. Withdrawn, not waived: lambda holds both files and still owes every + qwen3moe cell; gx10 still owes every Qwen3.5 (hybrid) cell. Operator D1 APPROVED 2026-09-28 20:10Z. long_rungs_for: families: [qwen35] # every consumer's family: the full rung set, through declared representatives: # one file per other arch the fleet holds - qwen2: qwen2.5-coder-7b-instruct-q4_k_m.gguf # lambda + gx10 - qwen3: Qwen3-8B-Q4_K_M.gguf # lambda + gx10 + qwen2: qwen2.5-coder-7b-instruct-q4_k_m.gguf # lambda (gx10 de-claimed, #4590) + qwen3: Qwen3-8B-Q4_K_M.gguf # lambda (gx10 de-claimed, #4590) # qwen3moe: REMOVED for 0.69.1 (#3817). Not a fit question — this build has no CUDA # forward for the architecture at all (#3714, lands 0.70.0), so `apr run --gpu` refuses # pre-load by name and there is no GPU cell to owe. The CPU path is unchanged and works. @@ -240,6 +272,7 @@ ladder: arch: qwen2 gguf: qwen2.5-1.5b-instruct-q4_k_m.gguf sha256: 6a1a2eb6d15622bf3c96857206351ba97e1af16c30d7a74ee38970e434e9407e + hosts: [lambda] # gx10 DE-CLAIMED for 0.70 (#4590, cells.declaimed, G2); restore to every host in 0.70.1 backends: [cpu, cuda] required: true note: "control rung — the architecture every other gate already exercises; if this is red the host is broken, not the model" @@ -248,6 +281,7 @@ ladder: arch: qwen3 gguf: Qwen3-1.7B-Q4_K_M.gguf sha256: b139949c5bd74937ad8ed8c8cf3d9ffb1e99c866c823204dc42c0d91fa181897 + hosts: [lambda] # gx10 DE-CLAIMED for 0.70 (#4590, cells.declaimed); restore to every host in 0.70.1 backends: [cpu, cuda] required: true note: "dense Qwen3 (per-head QK RMSNorm) — RED on cuda at 4a538ddef on both hosts (#3413)" @@ -256,6 +290,7 @@ ladder: arch: qwen3 gguf: Qwen3-8B-Q4_K_M.gguf sha256: d98cdcbd03e17ce47681435b5150e34c1417f50b5c0019dd560e4882c5745785 + hosts: [lambda] # gx10 DE-CLAIMED for 0.70 (#4590, cells.declaimed); restore to every host in 0.70.1 backends: [cpu, cuda] required: true # flipped 2026-09-22 (#3724): the empty-output defect this key was hiding is FIXED # and MEASURED green 8/8 on BOTH lambda (where it failed) and gx10 at 3ebb1c61e. @@ -458,6 +493,11 @@ falsification_tests: prediction: "check_model_ladder.sh --self-test: green-cells passes (20 owed on lambda, 12 pass, 8 honest refusals); a missing cell, a duplicate row, a fallback, a thinking block that never closed, a prompt under its rung, an empty answer, a co-tenant refusal, an OOM instead of a refusal, a rung that passes nowhere, thinking_modes that disagree with the template, a missing context_length (#3733), no cells, a consumer without a number, a missing context-rungs file, a dropped cells block or verb, a rung id or consumer dropped from context-rungs.json vs origin/main, an arch with no held representative, and a pass where the declared arithmetic says the cell cannot fit (the arithmetic decides what is owed, so it must be right) are each RED naming it; 11 module mutants are each killed by their case" test: "bash scripts/check_model_ladder.sh --self-test" if_fails: "a release dogfoods one verb at one short prompt with thinking off and calls the model family working" + - id: FALSIFY-MCL-017 + rule: "A recorded emergency scope applies to the gate called with no --scope, and is SAID; none recorded is the full ladder (#4086)" + prediction: "check_model_ladder.sh --self-test: the select table (no scope / another release / a prefix of the release -> the full ladder; one scope -> it applies; two scopes or a nameless one -> RED) lands on crux_smoke_scope.recorded_scope, and 4 module mutants are each killed; end to end, NO recorded scope judges the full ladder beside CRUX smoke receipts that would satisfy a scope (RED, no SCOPED: line), a recorded scope prints `SCOPED: ` and returns the SCOPE's verdict (green and red), --scope none and a scope for another release judge the full ladder, two scopes are RED judging neither, and the receipts are found through MODEL_LADDER_CRUX_DIR; 4 dispatch mutants are each killed. check_dogfood_no_defer.sh: a declared gate's `SCOPED:` line is carried into its dogfood row, green or red, and a row with none stays plain (3 mutants)" + test: "bash scripts/check_model_ladder.sh --self-test && bash scripts/check_dogfood_no_defer.sh --self-test" + if_fails: "the dogfood's declared ladder gate judges the full ladder while the release gate judges the recorded scope -- two gates, one release, opposite verdicts (0.69.1, #4086) -- or a scoped pass reads as `exit=0`, indistinguishable from every rung green" qa_gate: id: F-MCL-001 @@ -473,3 +513,14 @@ qa_gate: - every_inventory_model_measured_and_green_on_cuda pass_criteria: "check_model_ladder.sh exits 0 with executed >= 1 and a non-empty measured inventory on every required host" falsification: "plant a receipt with cuda.fallback=true → exit 1" + +# ONT-8: one evidence block — PROV-O names, one L-enum (ProofLevel). The level is MEASURED, not declared: it is what +# the cited command printed for this contract at the time below. Checked by `pv lint --gate evidence`. +evidence: + level: L2 + mark: C + provenance: + wasGeneratedBy: + command: "pv proof-status contracts/ --format json" + wasAttributedTo: pv + generatedAtTime: "2026-09-24T08:22:07Z" diff --git a/contracts/model-setfit-slice.yaml b/contracts/model-setfit-slice.yaml new file mode 100644 index 0000000000..c52357c217 --- /dev/null +++ b/contracts/model-setfit-slice.yaml @@ -0,0 +1,79 @@ +# ───────────────────────────────────────────────────────────────────────────── +# model-setfit-slice — one tracked .apr model file as a graded entity (ONT-001 §5 ONT-4c, Appendix B.6; #3847) +# +# WHY THIS FILE. v4.16's byte-level survey: slice_model.apr is the one tracked v2-magic file that parses under +# extract:apr-model's algorithm (version 2.0, header checksum matches, tensor_count 37), and its sha256 is pinned +# independently by crates/aprender-core/tests/fixtures/setfit/manifest.sha256. golden_v2.apr is the extractor's +# own control and is excluded — it would validate the checker against itself. +# +# TARGET. model:AprModel (v4.16 D-T2, qd4c4): `extract:apr-model` types its node model:AprModel as well as +# model:Model, and the ladder's armed `ladder-measured` shape targets model:LadderRung, so the two do not collide. +# `ignoredProperties` (D1c) declines model:file and model:contract, the extractor's bookkeeping. +# +# KIND: pattern. The edit that makes it fail: change one byte of the file — model:sha256 leaves its `in:`. +# ───────────────────────────────────────────────────────────────────────────── +name: model-setfit-slice +version: "1.0.0" +scope: > + crates/aprender-core/tests/fixtures/setfit/slice_model.apr: format, container version, tensor count and the + exact bytes. Out of scope: what the model computes. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + The SetFit slice model becomes a model:AprModel node; its bytes are pinned by sha256 and its header must + parse as an APR v2 container. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md v4.16 Appendix B.6 (D1, D1c, D-T2)' + - 'crates/aprender-contracts/src/ontology/extract/apr_model.rs' + - 'crates/aprender-core/tests/fixtures/setfit/manifest.sha256' + +entity: + type: apr-model + ref: crates/aprender-core/tests/fixtures/setfit/slice_model.apr + +relations: + depends_on: [ont-sigma-v1, ont-shapes-v1, ont-model-receipts-v1] + +shape: + targetClass: model:AprModel + closed: true + ignoredProperties: [model:file, model:contract] + properties: + - {path: model:format, in: ["apr"]} + - {path: model:aprVersion, minCount: 1, maxCount: 1, pattern: "^[0-9]+\\.[0-9]+$"} + - {path: model:tensorCount, minCount: 1, maxCount: 1, datatype: xsd:integer} + - {path: model:sha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$", in: ["4a75ee8031b3fbe0b17e1706a248aabbf158420c06f082bc29a89e2103d4057e"]} + +equations: + bytes_pinned: + formula: "sha256(slice_model.apr) = 4a75ee80…4057e ∧ parse(slice_model.apr) = Ok" + domain: the tracked file's bytes + codomain: one model:AprModel node, or a PV-ONT-012 refusal naming the file + invariants: + - a file that does not parse is refused, never a node + preconditions: + - the file is tracked + postconditions: + - any byte change moves model:sha256 out of its in-list + lean_theorem: "none — L4 not declared" + +invariants: + - id: MSS-INV-001 + property: the model file's bytes are exactly the pinned ones + formal: 'sha256(ref) ≡ "4a75ee8031b3fbe0b17e1706a248aabbf158420c06f082bc29a89e2103d4057e"' + prose: false + +falsification_tests: + - id: FALSIFY-MSS-001 + rule: a changed byte is refused + prediction: > + flipping one byte of slice_model.apr makes the shapes gate exit 1 on model-setfit-slice (model:sha256 + `in`), and a lying header is refused by extract:apr-model naming the file + test: cargo test -p aprender-contracts --lib ontology::extract::apr_model + if_fails: the model file can change with no contract noticing diff --git a/contracts/online-softmax-v1.yaml b/contracts/online-softmax-v1.yaml index 0414218f83..be0de976f0 100644 --- a/contracts/online-softmax-v1.yaml +++ b/contracts/online-softmax-v1.yaml @@ -8,6 +8,24 @@ metadata: - Rabe & Staats (2022) Self-attention Does Not Need O(n²) Memory depends_on: - softmax-kernel-v1.yaml +# ONT-4e (R-20): online softmax refines the kernel contract. Same precondition (it accepts every input the kernel +# does), the kernel's postcondition plus order preservation (it promises at least as much). +relations: + refines: [softmax-kernel-v1] +requires: +- id: PRE-1 + statement: every input score is finite and the vector is non-empty + formal: '∀i: ¬isNaN(x_i) ∧ ¬isInf(x_i) ∧ len(x) > 0' + formal_status: parsed +ensures: +- id: POST-1 + statement: the output is a probability vector of the input's length + formal: 'len(σ(x)) = len(x) ∧ ∀i: 0 < σ(x)_i < 1 ∧ |Σ σ(x)_i - 1| < ε' + formal_status: parsed +- id: POST-2 + statement: the output preserves the order of the scores + formal: 'x_i > x_j ⟹ σ(x)_i > σ(x)_j' + formal_status: parsed equations: online_normalizer: formula: "Online update rule (streaming max + sum_exp):\n Given running state (m_{i-1}, d_{i-1}) and new score x_i:\n\ diff --git a/contracts/ont-capability-cells-v1.yaml b/contracts/ont-capability-cells-v1.yaml new file mode 100644 index 0000000000..22cd111ef6 --- /dev/null +++ b/contracts/ont-capability-cells-v1.yaml @@ -0,0 +1,156 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-capability-cells-v1 — a required cell of the model-capability ladder is oracle-proven or RED +# (ONT-001 v4.12 §5 ONT-4c5, paiml/infra 948ae923; aprender #3972 + #4047, PMAT-3972) +# +# WHAT IT GOVERNS +# provable_contracts::ontology::capability_cells — D, the cells measured at the current release V*, and +# not_run = D \ {Pass | Fail}; one `apply` for corpus and plant. +# provable_contracts::lint::capability_cells_gate — the validator's half inside `pv lint --gate shapes`: the +# positive control through the ARMED shape, the run-time +# wiring check, a finding naming every NotRun cell, and +# |D| = 0 as a decline. +# +# THE RULE. The domain D is the set of cells ONT-4c1's `ladder-green` quantifies over (receipts::expected_hosts, +# one definition), restricted to `required: true` rungs; a cell id is `@`. A cell is MEASURED only by +# a witness row (sha256 = the rung's) in a receipt of the current release V* (the greatest dotted-numeral +# `version` read) for that host. Its verdict is Pass (ladder-green's row predicate), Fail (its negation), or +# NotRun: a row label that `Verdict::from_label` maps to Unknown(_) — DEFER, MANUAL, NO-VERDICT, … — beats the +# measurement, and a label nobody knows is refused by name. Every D member that is not Pass or Fail is a +# `model:notRunCell`, and the ARMED shape says maxCount 0 on it: Fail, exit 1 — the arming rule. An absent cell is +# NEVER folded into Fail: this row admits Fail (ladder-green refuses it), so a folded absence would pass. +# +# WHY V*, NOT ladder-green's cross-version "∃ witness". Every required rung carries one sha across 0.68.1, +# 0.68.2 and 0.69.1; unioning versions would let a 0.68.2 green row hide a 0.69.1 DEFER or a missing 0.69.1 row +# (plan grill round 1, measured on this tree). ladder-green itself is unchanged. +# +# THE PLANT. A required rung with no receipt (tests/fixtures/ont/capability-cells-plant.yaml, embedded +# byte-identically in the crate) runs every gate run through the same `apply`, then through the ARMED shape; both +# must refuse exactly `capability-cells-plant@plant-host`, or pc_shapes["capability-cells"] is not-fired and the +# gate declines. The corpus wiring is checked at run time too: the rungs the shape flags must be the rungs owning +# a NotRun cell, else the gate declines (Differential). +# +# RESIDUAL, NAMED (the row's own). D takes ONT-4c1's host axis unchanged: `hosts:` absent = every host with a +# receipt read. A host that has never had a receipt is not in D; the probe's literal baseline set B guards the +# 14 named cells. +# +# KIND: pattern. Shapes over a graph; the proof is the gate's own case table. +# ───────────────────────────────────────────────────────────────────────────── +name: ont-capability-cells +version: "1.0.0" +scope: > + Which cells of the model-capability ladder a release owes a verdict on (D), how a cell is measured at the + current release, and the armed shape that turns a required cell without a verdict into RED. Out of scope: + whether a Fail cell may ship (ladder-green, ONT-4c1), per-verb/thinking cells (the v2 cells[] axis, #3712), + the oracle differential (ONT-4b2). +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-23' + last_modified: '2026-09-23' + author: PAIML Engineering + description: > + A required ladder cell is Pass or Fail at the current release V*; NotRun (missing, DEFER, MANUAL, + NO-VERDICT, an unknown label) is a violation of the armed capability-cells shape, exit 1; |D| = 0 declines. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md v4.12 §5 ONT-4c5 (948ae923, infra#950); docs/audits/ont-4c5-plan.md there' + - 'aprender #3972 (extractor + arming rule), #4047 (pv lint capability-cells shape); docs/audits/impl-PMAT-3972-plan.md' + - 'crates/aprender-contracts/src/ontology/{capability_cells.rs, receipts.rs, verdict.rs}, lint/{capability_cells_gate.rs, shapes_gate.rs}' + +entity: + type: gguf + +relations: + depends_on: [ont-shapes-v1, ont-model-receipts-v1, model-capability-ladder-v1] + +shapes: + - id: capability-cells + targetClass: model:RequiredModel + properties: + - {path: model:notRunCell, maxCount: 0} + - {path: model:refusedLabel, maxCount: 0} + +equations: + domain: + formula: "D = { rung@host : rung.required ∧ host ∈ expected_hosts(rung, receipt_hosts(R)) }" + domain: "every rung of the ladder contract; R = every receipt read under evidence/dogfood/models/" + codomain: "a set of cell ids @" + invariants: + - "expected_hosts is ONT-4c1's function, called, not copied: D and ladder-green quantify over one host axis" + - "a rung with required: false contributes no cell" + preconditions: + - "every receipt version is dotted numerals, else the gate refuses by name (exit 3)" + postconditions: + - "|D| = 0 ⇒ the gate declines (exit 2), never Pass" + lean_theorem: none — L4 not declared + not_run: + formula: "not_run = D \\ { c ∈ D : cell(c, V*) ∈ {Pass, Fail} }, V* = max(version(R))" + domain: "D and the witness rows of the V* receipts" + codomain: "a model:notRunCell literal per member, on the rung's model:Model node" + invariants: + - "a row label mapping to Unknown(_) beats the measurement; an unknown label is refused by name and NotRun" + - "several V* rows for one cell combine NotRun > Fail > Pass — not the lattice meet" + - "a row of an older version never measures a cell" + preconditions: + - "a witness row is one whose sha256 equals the rung's; no sha256 or another hex is no measurement" + postconditions: + - "|not_run| > 0 ⇒ the armed capability-cells shape fails ⇒ exit 1" + lean_theorem: none — L4 not declared + +invariants: + - id: CAP-INV-001 + property: a required cell without a verdict fails the armed shape + formal: 'c ∈ D ∧ cell(c, V*) ∉ {Pass, Fail} ⇒ Fail(capability-cells)' + prose: false + - id: CAP-INV-002 + property: an absent cell is never folded into Fail + formal: 'c ∈ D ∧ witness(c, V*) = ∅ ⇒ c ∈ not_run' + prose: false + - id: CAP-INV-003 + property: an older version never masks the current release + formal: 'version(r) < V* ⇒ r ∉ witnesses(c) for every c ∈ D' + prose: false + - id: CAP-INV-004 + property: an empty domain declines + formal: '|D| = 0 ⇒ verdict = Unknown' + prose: false + - id: CAP-INV-005 + property: the plant fires through the armed shape or the gate declines + formal: 'pc_shapes(capability-cells) ≠ fired ⇒ verdict = Unknown' + prose: false + +falsification_tests: + - id: FALSIFY-CAP-001 + rule: cells at V*, label precedence, duplicate rows + prediction: "a missing cell and a DEFER/MANUAL/NO-VERDICT row are not_run; an older green row does not mask either; a stray higher version only adds NotRun; a non-numeral version is refused by name; a green duplicate does not hide a DEFER or a Fail; a Fail cell is admitted" + test: cargo test -p aprender-contracts --lib ontology::capability_cells + if_fails: a required cell without a verdict arms, or a Fail is refused by the wrong shape + - id: FALSIFY-CAP-002 + rule: the plant is the tracked fixture and fires through the shape + prediction: the embedded plant equals tests/fixtures/ont/capability-cells-plant.yaml byte for byte, and refuses exactly capability-cells-plant@plant-host through the armed shape + test: cargo test -p aprender-contracts --lib lint::capability_cells_gate + if_fails: the positive control drifts from the tracked one, or stays fired with the shape gone + - id: FALSIFY-CAP-003 + rule: the gate's answers on the CLI + prediction: "Pass on the real corpus with capability-cells armed, pc_shapes fired, not_run empty and B ⊆ domain; exit 1 naming the cell on a missing, DEFER, MANUAL or NO-VERDICT required cell; exit 2 decline when D is empty or capability-cells is disarmed; a Fail cell exits 0 under this shape" + test: cargo test -p aprender-contracts-cli --test ont4c5_capability_cells + if_fails: a decline, a reject and a pass become indistinguishable to the probe + - id: FALSIFY-CAP-004 + rule: the spec row's mutations + prediction: "fold a missing cell into Fail → the plant passes → decline; drop capability-cells from armed_shapes → decline; flip one required rung to required: false → B ⊄ D → the probe is RED; label a required cell DEFER → exit 1 naming it" + test: cargo test -p aprender-contracts-cli --test ont4c5_capability_cells + if_fails: the row's own probe cannot go RED on the mutations it names + +qa_gate: + id: F-CAP-001 + name: capability-cells + description: 'A required cell of the model-capability ladder is Pass or Fail at the current release; NotRun is RED (ONT-4c5)' + checks: + - domain_is_ladder_greens + - cells_measured_at_v_star + - not_run_never_admitted + - empty_domain_declines + - plant_fires_through_armed_shape + pass_criteria: All 4 falsification tests pass; `pv lint contracts/ --gate shapes --format json` is Pass on the real corpus with capability-cells in armed_shapes, pc_shapes["capability-cells"] fired, capability_cells.not_run empty and the 14 ids of B in capability_cells.domain + falsification: 'label one required 0.69.1 row DEFER → exit 1 naming @; delete one required row from a 0.69.1 receipt → exit 1 naming it' diff --git a/contracts/ont-code-symbols-v1.yaml b/contracts/ont-code-symbols-v1.yaml index 2240e234e2..959f83f03b 100644 --- a/contracts/ont-code-symbols-v1.yaml +++ b/contracts/ont-code-symbols-v1.yaml @@ -45,7 +45,7 @@ metadata: extract:code — the bound symbols by a syn module-tree walk (inline mods, file mods, #[path], one re-export hop, impl methods), fail-closed with the reason in the graph; extract:lean — theorems from the in-tree Theorems tree, sorry-free files only grounding, joined to the contracts that cite them by ONT-2a's name - forms; 16 vendored W3C SHACL-Core cases run every gate run; the oracle differential in the release gate. + forms; 19 vendored W3C SHACL-Core cases run every gate run; the oracle differential in the release gate. references: - 'paiml/infra docs/specifications/paiml-ontology.md §3.6 (the subset), §3.7 (extractors), §5 ONT-4b2, R-13, R-15, R-18' - 'paiml/infra docs/specifications/ont-oracle-ledger.md (ONT-0: the 32-case table and the pinned oracle)' diff --git a/contracts/ont-consistency-v1.yaml b/contracts/ont-consistency-v1.yaml new file mode 100644 index 0000000000..ff3849d484 --- /dev/null +++ b/contracts/ont-consistency-v1.yaml @@ -0,0 +1,165 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-consistency-v1 — the corpus's typed relations are jointly satisfiable, and +# the witness that says so checks (ONT-001 §3.5 + §5 row ONT-5; issue #4074; +# ticket PMAT-4074) +# +# WHAT IT GOVERNS +# provable_contracts::ontology::witness — ClauseSet (the Horn encoding), +# census_id_set_sha256, relations_sha256, Witness, check, pc_checker. +# provable_contracts::lint::consistency_gate — the `ont-consistency` gate: +# PV-ONT-022 (inconsistent, core named), PV-ONT-023 (witness does not check), +# declines NoCheckable / WitnessStale / PositiveControlFailed, exit 3 on a +# malformed Σ. +# `pv-sat` (crates/aprender-contracts-cli/src/bin/pv-sat/) — the UNTRUSTED +# reasoner, private to its bin target (R-1, F-7), and its pc_reasoner plant. +# +# PROOF LADDER, STATED HONESTLY +# * L2 — unit tests over the encoding, the digests and the checker; the gate's +# tests; pv-sat's 500-seed sweep in which every answer it gives checks; +# the CLI test runs the §5 probe verbatim on this repo's corpus. +# * L3 — not declared. There are no Kani harnesses for this checker. +# * L4 — not declared. There is no Lean theorem. +# The gate is COMPUTED and NOT ARMED: it is absent from `armed_gates`, so its +# verdict does not reach the armed meet until an arming PR adds it. +# ───────────────────────────────────────────────────────────────────────────── +metadata: + version: 1.0.0 + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + kind: pattern + description: > + ONT-001 consistency — the typed relation graph as propositional Horn clauses, decided offline by the + untrusted pv-sat, and re-verified in the gate by a linear witness checker with two positive controls + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §3.5 (the checkable subset), §5 ONT-5, R-1 (the reasoner is untrusted), R-2 (zero is a decline), R-3 (positive controls), F-5, F-7' + - 'Dowling & Gallier (1984) Linear-time algorithms for testing the satisfiability of propositional Horn formulae' + - 'crates/aprender-contracts/src/ontology/witness.rs — the encoding, the digests, check, pc_checker' + - 'crates/aprender-contracts/src/lint/consistency_gate.rs — the ont-consistency gate' + - 'crates/aprender-contracts-cli/src/bin/pv-sat/ — the reasoner and its plant' +equations: + encode: + formula: 'clauses(G) = {A : A live} ∪ {¬A ∨ B : A depends_on B ∨ A refines B} ∪ {¬A ∨ ¬B : A contradicts B}' + domain: G the typed relation graph over the census id set; live = not the target of any supersedes + codomain: a propositional Horn clause set over contract ids + invariants: + - every clause names only ids in the census id set + - a superseded id is not asserted, but an implication from a live id still derives it + preconditions: + - G is the relations gate's typed graph with symmetric roles closed + postconditions: + - unencoded_edges(G) lists every edge whose role has no clause, so nothing is dropped silently + lean_theorem: none — L4 not declared + check: + formula: 'check(W, C) = Ok ⇔ (W = model M ∧ ∀c ∈ C: M ⊨ c) ∨ (W = core K ∧ K is a unit-propagation derivation in C ending in a contradicts clause both sides of which K derived)' + domain: W a witness, C = clauses(G) for the current corpus + codomain: '{Ok(Consistent), Ok(Inconsistent(core)), Err(reason)}' + invariants: + - every clause a core names exists in C (a stale or invented core is refused) + - a premise used before it is derived is refused + - the check is linear in |W| + |C| + preconditions: + - W.census_id_set_sha256 and W.relations_sha256 equal the corpus's; otherwise the gate declines WitnessStale before checking + postconditions: + - Ok(Consistent) ⇒ C is satisfiable; Ok(Inconsistent(K)) ⇒ C is unsatisfiable and K names why + lean_theorem: none — L4 not declared + gate: + formula: 'verdict = Unknown(NoCheckable) if |C| = 0; Unknown(WitnessStale) if no witness for the digests; Unknown(PositiveControlFailed) unless pc_checker and pc_reasoner fired; Fail if check errs or yields a core; Pass otherwise' + domain: a contracts/ directory with Σ and a witness directory + codomain: V (the ONT-6 lattice) + invariants: + - zero clauses is a decline, never a Pass (R-2) + - no verdict without both positive controls fired (R-3) + preconditions: + - Σ parses; otherwise exit 3 + postconditions: + - verdict == Pass ⇒ a checked model of the current graph exists + lean_theorem: none — L4 not declared +proof_obligations: +- id: ONT5-INV-001 + type: invariant + property: the encoding types each role and asserts only live contracts + formal: 'units(C) = ids \ targets(supersedes) ∧ roles(C) ⊆ {depends_on, refines, contradicts}' + applies_to: all +- id: ONT5-INV-002 + type: invariant + property: the witness name is order-free and sensitive to every edge + formal: 'relations_sha256(permute(G)) = relations_sha256(G) ∧ ∀e ∈ G: relations_sha256(G \ {e}) ≠ relations_sha256(G)' + applies_to: all +- id: ONT5-INV-003 + type: invariant + property: the checker refuses any core that is not a derivation in the current graph + formal: 'check(K, C) = Ok(Inconsistent) ⇒ every step of K is a unit of C or a clause of C whose premise an earlier step derived, and its final contradicts clause has both sides derived' + applies_to: all +- id: ONT5-INV-004 + type: invariant + property: the checker refuses a model that falsifies any clause + formal: 'check(M, C) = Ok(Consistent) ⇒ ∀c ∈ C: eval(M, c) = true' + applies_to: all +- id: ONT5-POST-005 + type: postcondition + property: the gate's exit is the lattice element, and a stale, absent or uncontrolled witness never passes + formal: 'stale ∨ absent ⇒ exit 2 `decline: WitnessStale` naming make contracts; ¬pc_checker ∨ ¬pc_reasoner ⇒ exit 2; |C| = 0 ⇒ exit 2 `decline: NoCheckable`; malformed Σ ⇒ exit 3' + applies_to: all +- id: ONT5-INV-006 + type: invariant + property: the reasoner is private to its bin target + formal: 'lib.rs contains no `mod sat`; the pv CLI main.rs names no pv_sat (F-7)' + applies_to: all +verification_summary: + total_obligations: 6 + l2_property_tested: 6 + l3_kani_proved: 0 + l4_lean_proved: 0 + l4_sorry_count: 0 + l4_not_applicable: 6 +falsification_tests: +- id: FALSIFY-ONT5-001 + rule: the encoding + prediction: live contracts are units, depends_on/refines are implications, contradicts is a conflict pair, supersedes withdraws a unit + test: cargo test -p aprender-contracts --lib ontology::witness::tests::the_encoding_asserts_live_contracts_and_types_each_role + if_fails: a role is encoded under the wrong clause shape, so the reasoner answers a different question from the corpus +- id: FALSIFY-ONT5-002 + rule: the digests + prediction: permuting the edges leaves both digests unchanged; removing any one edge changes relations_sha256 + test: cargo test -p aprender-contracts --lib ontology::witness::tests::the_digests_are_order_free_and_sensitive_to_every_edge + if_fails: a witness for one graph is accepted for another +- id: FALSIFY-ONT5-003 + rule: the core checker + prediction: a real derivation checks; a premise used early, a clause the graph lacks and an underived conflict side are each refused + test: cargo test -p aprender-contracts --lib ontology::witness::tests::a_premise_used_before_it_is_derived_is_refused + if_fails: the checker accepts a core that proves nothing — pc_checker then fires on the shipped fixture +- id: FALSIFY-ONT5-004 + rule: pc_checker + prediction: the checker refuses fixtures/unsat-core-corrupt.json on every run + test: cargo test -p aprender-contracts --lib ontology::witness::tests::pc_checker_fires_on_the_shipped_fixture + if_fails: a checker that returns Ok for everything goes unnoticed (measured 2026-09-24 — 14 lib tests red) +- id: FALSIFY-ONT5-005 + rule: the gate's lattice + prediction: 'consistent → Pass; inconsistent → PV-ONT-022 with the core named; tampered → PV-ONT-023; missing or foreign → WitnessStale; control not fired → PositiveControlFailed; zero clauses → NoCheckable; malformed Σ → exit 3' + test: cargo test -p aprender-contracts --lib lint::consistency_gate::tests + if_fails: the gate reports a verdict about a graph nobody reasoned over +- id: FALSIFY-ONT5-006 + rule: the reasoner is untrusted but honest here + prediction: over a 500-seed sweep every answer pv-sat gives checks; the plant draws the core [A, B, C]; a rerun leaves the witness byte-identical + test: cargo test -p aprender-contracts-cli --bin pv-sat + if_fails: pv-sat writes witnesses the gate refuses, or its positive control is a constant (measured 2026-09-24 — an all-true reasoner turns 4 tests red) +- id: FALSIFY-ONT5-007 + rule: the §5 probe end to end, and F-7 + prediction: 'pv lint contracts/ --gate ont-consistency on this repo: checkable_n > 0, pc_checker fired, witness.pc_reasoner fired, witness.stale false; exits 0/1/2/3 on witnesses the real pv-sat wrote; lib.rs has no mod sat' + test: cargo test -p aprender-contracts-cli --test ont5_consistency_gate + if_fails: the probe the ledger binds is not the behaviour shipped (measured 2026-09-24 — `pub mod sat` in the lib turns it red) +qa_gate: + id: F-ONT5-001 + name: ONT-001 relation consistency + description: Horn encoding, witness digests, linear checker, two positive controls, pv-sat behind a bin boundary (ONT-5) + checks: + - encoding + - digests + - core_checker + - pc_checker + - gate_lattice + - reasoner_answers_check + - spec_probe + pass_criteria: All 7 falsification tests pass + falsification: 'a checker that returns Ok for everything, `pub mod sat` in the lib, or a reasoner that answers all-true — each turns a named test RED (measured 2026-09-24)' diff --git a/contracts/ont-docs-corpus-v1.yaml b/contracts/ont-docs-corpus-v1.yaml new file mode 100644 index 0000000000..35096b1975 --- /dev/null +++ b/contracts/ont-docs-corpus-v1.yaml @@ -0,0 +1,139 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-docs-corpus-v1 — the docs corpus as ontology focus nodes +# (aprender#3560 "docs, examples and cookbook pv-SHACL validated for Qwen3.5"; row R1) +# +# R1 (this file's first row): extract:example — every cargo example target of a +# workspace member (examples/*.rs, examples/*/main.rs) is an ont:Example with +# its file, crate, target name, and the model families its text names. +# LATER ROWS (not claimed here): R2 book pages (after ONT-4c's readme reader), +# R3 apr-cookbook (its own repo), R5 every example builds and runs. +# R4: the model-currency shape — an example that names a model family must name +# the current one (qwen3.5), or say why not. +# +# ARMING: both shapes are REPORTED, not armed. `examples-well-formed` holds on +# this tree. `examples-model-current` (R4) does not: 430 examples name an +# older model. R4's gate is the shrink-only pin, which CI's --lib run holds: +# a NEW stale example is RED today; the shape arms when the pin reaches 0. +# PIN: an example that names an older model on purpose says so with +# `// ont:model-pinned: `; the reason is kept as example:modelPinned. +# ───────────────────────────────────────────────────────────────────────────── +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + extract:example — the cargo example targets of the workspace members, found by the same [workspace] + members/exclude reading extract:code uses, each an ont:Example carrying example:file, example:crate, + example:name, one example:namesModel per model family a token scan finds, and example:namesQwen35. + Zero examples read at a [workspace] root is an extractor error (PV-ONT-012), never a green. + references: + - 'paiml/aprender#3560 (docs, examples and cookbook pv-SHACL validated for Qwen3.5)' + - 'paiml/infra docs/specifications/paiml-ontology.md §3.7 (extractors), R-3, R-15' + - 'crates/aprender-contracts/src/ontology/extract/example.rs, example_tests.rs' + - 'crates/aprender-contracts/src/ontology/extract/code.rs — workspace_membership, manifest_names (reused)' + +relations: + depends_on: [ont-shapes-v1, ont-sigma-v1] + +equations: + extract_example: + formula: "examples(root) = { e | m ∈ members(root), e ∈ m/examples/*.rs ∪ m/examples/*/main.rs } ; namesModel(e) = families(tokens(lower(text(e))))" + domain: "the repo root the contract dir sits in; members by [workspace] members/exclude, or every manifest when the root has no [workspace]" + codomain: "one ont:Example node per target, plus ExampleStats {packages, examples, non_member_examples, naming_a_model, naming_qwen35, by_family, errors}" + invariants: + - "a target under a manifest the workspace excludes is counted in non_member_examples and not extracted" + - "a helper module below examples// other than main.rs is not a target" + - "the family scan is by token: `graphics` is not phi, `llama.cpp` is a runtime, `qwen3.5` is tried before `qwen3`" + preconditions: + - "the root's Cargo.toml, when it is a [workspace], lists its members" + postconditions: + - "at a [workspace] root, examples = 0 ∧ (members = 0 ∨ non_member_examples > 0) ⇒ one extractor error" + - "two extractions of the same tree are byte-identical (R-15)" + lean_theorem: none — L4 not declared + model_current: + formula: "current(e) ⇔ namesModel(e) = ∅ ∨ qwen3.5 ∈ namesModel(e) ∨ pinned(e) ≠ ⊥ ; stale = |{e | ¬current(e)}| ≤ STALE_EXAMPLES_PINNED" + domain: "every extracted ont:Example" + codomain: "example:modelCurrent true|false, example:modelPinned the stated reason" + invariants: + - "a pin is a `//` comment carrying `ont:model-pinned:` and a non-empty reason; the marker in a string is not a pin" + - "the pin only shrinks: a count below it fails until the constant is lowered to it" + preconditions: + - "CURRENT_FAMILY names the family the project ships against (qwen3.5, #3560)" + postconditions: + - "a new example naming an older model without a pin fails CI's --lib run naming the file" + lean_theorem: none — L4 not declared + +invariants: + - id: DOCS-INV-001 + property: deterministic, no blank nodes + formal: '∀ tree: extract(tree) = extract(tree) ∧ ∀ t ∈ extract(tree): subject(t) ∈ IRI' + prose: false + - id: DOCS-INV-002 + property: a walk that read nothing it could have is RED, never green; a workspace with no examples measures zero + formal: 'workspace(root) ∧ |examples(root)| = 0 ∧ (|members(root)| = 0 ∨ non_member_examples(root) > 0) ⇒ PV-ONT-012 ∈ findings' + prose: false + - id: DOCS-INV-003 + property: only targets cargo builds are extracted + formal: '∀ e ∈ examples(root): admits(membership(root), dir(e)) ∧ (ext(e) = rs ∧ depth(e) = 1 ∨ e = examples/*/main.rs)' + prose: false + +falsification_tests: + - id: FALSIFY-DOCS-001 + rule: the walk extracts exactly the member targets + prediction: a planted workspace yields its two example forms and not the helper module, the excluded crate, a README or target/ + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::a_planted_workspace_yields_exactly_its_member_targets + if_fails: an example cargo never builds is graded, or a real one is missed + - id: FALSIFY-DOCS-002 + rule: vacuity is RED + prediction: "a [workspace] root that admits no member, or whose example targets all sit outside the membership, reports one extractor error; a member with no examples/, or a bare dir, reports none" + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::a_workspace_root_that_read_nothing_is_an_error_and_one_with_no_examples_is_not + if_fails: a walk that read nothing reports a clean corpus + - id: FALSIFY-DOCS-003 + rule: the family scan tells models from look-alikes + prediction: 13 cases — Qwen3.5/3/2.5/2, TinyLlama vs Llama, phi vs graphics and phishing, llama.cpp named as no model — each give the stated family set + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::the_family_scan_separates_models_from_look_alikes + if_fails: a Qwen3 example counts as Qwen3.5, or llama.cpp counts as a Llama example + - id: FALSIFY-DOCS-004 + rule: the repo's own examples + prediction: the workspace yields more than 500 examples across more than 20 packages, with no extractor error + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::the_repo_examples_are_extracted + if_fails: the membership reading or the walk lost the corpus + - id: FALSIFY-DOCS-005 + rule: the extractor is registered and its control fires + prediction: Σ lists example as an implemented entity type, the shapes gate counts it in by_entity_type and its pc_extract control fires + test: cargo test -p aprender-contracts --lib lint::shapes_gate + if_fails: the extractor runs and no gate knows, or its control is silent + - id: FALSIFY-DOCS-006 + rule: currency and the pin marker + prediction: "an example naming only Qwen2.5 is stale; the same with a `// ont:model-pinned: why` line, or naming no model, is current; a marker in a string literal or with no reason is not a pin" + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::a_pin_needs_the_marker_in_a_comment_and_a_reason + if_fails: a stale example passes as current, or a pin without a reason exempts it + - id: FALSIFY-DOCS-007 + rule: the stale count is pinned shrink-only (R4's drift gate) + prediction: the repo's stale examples equal STALE_EXAMPLES_PINNED (430 on 2026-09-24) + test: cargo test -p aprender-contracts --lib ontology::extract::example::tests::the_repo_stale_examples_are_pinned_shrink_only + if_fails: a new example on an older model landed (the message lists the stale files), or examples migrated and the pin was not lowered + +qa_gate: + required_checks: + - "cargo test -p aprender-contracts --lib ontology::extract::example" + - "cargo test -p aprender-contracts --lib lint::shapes_gate" + +shapes: + # REPORTED, not armed (see the header). + - id: examples-well-formed + targetClass: ont:Example + properties: + - {path: example:file, minCount: 1, maxCount: 1} + - {path: example:crate, minCount: 1, maxCount: 1} + - {path: example:name, minCount: 1, maxCount: 1} + - {path: example:namesQwen35, minCount: 1, maxCount: 1, in: [true, false]} + - {path: example:modelCurrent, minCount: 1, maxCount: 1, in: [true, false]} + # R4, REPORTED, not armed: 430 of the 432 examples that name a model name an older one. The drift gate is the + # shrink-only pin STALE_EXAMPLES_PINNED (FALSIFY-DOCS-007), not this shape; arm it when the pin reaches 0. + - id: examples-model-current + targetClass: ont:Example + properties: + - {path: example:modelCurrent, minCount: 1, maxCount: 1, in: [true]} diff --git a/contracts/ont-evidence-v1.yaml b/contracts/ont-evidence-v1.yaml new file mode 100644 index 0000000000..ae3e451d46 --- /dev/null +++ b/contracts/ont-evidence-v1.yaml @@ -0,0 +1,132 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-evidence-v1 — one evidence block, PROV-O names, one L-enum (ONT-8) +# (ONT-001 §4.3 evidence, §5 ONT-8, F-16, R-17; issue #4077; ticket PMAT-4077) +# +# WHAT IT GOVERNS +# provable_contracts::lint::evidence_gate — PV-ONT-017..021; +# `pv lint --gate evidence` and gate 16 of the full run (R-8: computed +# everywhere, armed per repo; not armed here). +# +# THE LEVEL IS proof_status::ProofLevel, NOT A SECOND LIST. `levels_source` +# reports "enum" because the gate deserialises into that one type; a level +# outside it is PV-ONT-018. EV-3 removed L0, so L0 is refused. +# +# ENTITY-BLIND (R-17). `entity.type` is counted into `entity_types_checked` +# and never branched on: a README, a model and a code contract pass or fail +# the same block the same way. +# +# [V] BINDING. census.json `git_sha` is null by operator ruling (2026-09-16), +# so a [V] sha binds to the census when it records one, and otherwise must +# resolve as a commit in the contract directory's repository. A git that +# cannot look (absent, shallow) is Unknown(ToolAbsent), never a finding. +# ───────────────────────────────────────────────────────────────────────────── +name: ont-evidence +version: "1.0.0" +scope: > + The shape of a contract's `evidence` block: its level on the one proof-level enum, its mark, and its PROV-O + provenance (wasGeneratedBy, wasAttributedTo, generatedAtTime), across every entity type. Out of scope: whether + the cited command, re-run today, still yields the level claimed (that is `pv proof-status`), and what pv-sat + does with a level (ONT-5). +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + A contract may state its evidence in one block; the evidence gate refuses undeclared keys (F-16 `author:` + among them), a level outside the one enum, an unknown mark or a cited/verified claim with no command, a + verified sha that does not resolve, and an agent Σ does not declare, on every entity type alike. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §4.3 (evidence), §5 ONT-8, F-16, R-2, R-8, R-17' + - 'crates/aprender-contracts/src/lint/evidence_gate.rs — PV-ONT-017..021' + - 'crates/aprender-contracts/src/proof_status.rs — ProofLevel, the one L-enum' + - 'contracts/ontology.yaml — agents, reader lint/evidence_gate.rs' + +relations: + depends_on: [ont-sigma-v1, ont-verdict-lattice-v1] + +equations: + evidence_admission: + formula: "admits(corpus) ⇔ ∀ c ∈ corpus: evidence(c) = ∅ ∨ (keys(evidence(c)) ⊆ K_e ∧ level(c) ∈ ProofLevel ∧ mark(c) ∈ {V,C,A,U} ∧ (mark(c) ∈ {V,C} ⇒ command(c) ≠ ∅) ∧ (mark(c) = V ⇒ resolves(sha(c))) ∧ agent(c) ∈ Σ.agents)" + domain: "a contract corpus and the Σ that declares its agents" + codomain: "Pass | Fail | Unknown" + invariants: + - "a corpus whose every evidence block is well-formed and resolves is admitted" + - "a corpus with no evidence block is Unknown, never Pass" + preconditions: + - "Σ parses and satisfies its own integrity rules" + postconditions: + - "verdict ∈ {Pass, Fail, Unknown}" + - "the verdict does not depend on entity.type" + lean_theorem: none — L4 not declared + +invariants: + - id: EV-INV-001 + property: evidence, provenance and wasGeneratedBy carry only their declared keys + formal: 'k ∈ keys(evidence) ⇒ k ∈ K_e ∧ k ∈ keys(provenance) ⇒ k ∈ K_p' + prose: false + - id: EV-INV-002 + property: the level is a member of the one proof-level enum + formal: 'level(c) ∈ {L1, L2, L3, L4, L5}' + prose: false + - id: EV-INV-003 + property: a cited or verified claim names the command that produced it + formal: 'mark(c) ∈ {V, C} ⇒ command(c) ≠ ∅' + prose: false + - id: EV-INV-004 + property: a verified claim's sha resolves + formal: 'mark(c) = V ⇒ sha(c) = census.git_sha ∨ (census.git_sha = ⊥ ∧ sha(c) ∈ commits(repo))' + prose: false + - id: EV-INV-005 + property: the attributed agent is declared in Σ + formal: 'agent(c) ∈ Σ.agents' + prose: false + +falsification_tests: + - id: FALSIFY-EV-001 + rule: F-16 + prediction: "`author:` under evidence, provenance or wasGeneratedBy is PV-ONT-017 at each level" + test: cargo test -p aprender-contracts --lib lint::evidence_gate::tests::f16_author_is_pv_ont_017_wherever_it_is_written + if_fails: a non-PROV name enters the provenance record + - id: FALSIFY-EV-002 + rule: one L-enum + prediction: L0, l2, L6 and a missing level are PV-ONT-018; L1..L5 are all admitted + test: cargo test -p aprender-contracts --lib lint::evidence_gate::tests::a_level_outside_the_one_enum_is_pv_ont_018 + if_fails: a second level vocabulary exists beside ProofLevel + - id: FALSIFY-EV-003 + rule: a cited claim names its command + prediction: "mark C with no wasGeneratedBy.command is PV-ONT-019" + test: cargo test -p aprender-contracts --lib lint::evidence_gate::tests::a_cited_claim_without_a_command_is_pv_ont_019 + if_fails: a claim is marked cited with nothing to re-run + - id: FALSIFY-EV-004 + rule: a verified sha resolves + prediction: HEAD of a real repository passes; a well-formed sha that is not a commit is PV-ONT-020 + test: cargo test -p aprender-contracts --lib lint::evidence_gate::tests::a_null_census_binds_verified_claims_to_the_repository + if_fails: a [V] mark can cite a commit that never existed + - id: FALSIFY-EV-005 + rule: R-17 + prediction: the same block passes on readme, apr-model and code; the same defect is rejected on readme, apr-model, code, gguf and csv + test: cargo test -p aprender-contracts --lib lint::evidence_gate::tests::r17_the_same_defect_is_rejected_on_every_entity_type + if_fails: the gate judges an entity type differently + - id: FALSIFY-EV-006 + rule: zero is a decline; R-8 + prediction: no evidence block and no Σ both exit 2; the full `pv lint` run computes evidence, reported and not armed + test: cargo test -p aprender-contracts-cli --test ont8_evidence_gate + if_fails: a decline and a pass become indistinguishable, or armed_gates could name a gate no run computes + +qa_gate: + id: F-EV-001 + name: evidence block + description: One evidence block, PROV-O names, one L-enum, on every entity type (ONT-8) + checks: + - closed_key_set + - level_in_the_one_enum + - cited_claim_names_command + - verified_sha_resolves + - agent_in_sigma + - entity_blind + pass_criteria: All 6 falsification tests pass; `pv lint contracts/ --gate evidence` is Pass on the real corpus with levels_source enum and entity_types_checked ≥ 3 + falsification: 'a level check that accepts anything, and a skip on entity.type == readme, each turn tests red (3 failures, measured 2026-09-24)' diff --git a/contracts/ont-kernel-receipts-v1.yaml b/contracts/ont-kernel-receipts-v1.yaml new file mode 100644 index 0000000000..a65e9f82d1 --- /dev/null +++ b/contracts/ont-kernel-receipts-v1.yaml @@ -0,0 +1,172 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-kernel-receipts-v1 — kernel receipts as focus nodes (ONT-001 §5 ONT-4c4, §3.7; issue #4069, PMAT-4069) +# +# WHAT IT GOVERNS +# provable_contracts::ontology::extract::kernel — extract:kernel over extract:code: a bound #[kernel] symbol +# (cuda-oxide, pure Rust → PTX) gains rdf:type ont:KernelSymbol on the +# SAME IRI (ont:KernelSymbol ⊂ ont:Symbol), a kernel:reference to its +# f64 CPU sibling, and its kernel receipts +# (evidence/kernels//.json, apr-kernel-receipt/v1). +# provable_contracts::ontology::extract::code — sym:unsafeFree / sym:boundsChecked, read off the kernel's body. +# +# THE THREE SHAPES, all targetClass ont:KernelSymbol, all REPORTED and NOT ARMED at this row (absent from armed_shapes in +# lint-baseline.json): `kernel-parity` (every required CUDA host at cos ≥ 0.9999 ∧ max|Δ| < 1e-3 on a witness row of +# the receipt's own build sha; a reference that resolves), `kernel-timing` (oxide_us / ptx_us ≤ 1.2 per required host, +# or a tracked exemption receipt), `kernel-safety` (no unsafe, no get_unchecked, a register budget measured). The tree +# holds no #[kernel] fn and no evidence/kernels/ yet — the OXIDE pilot (#3522) lands the first; arming follows its +# first tracked receipt, and the ratchet keeps it armed. +# +# THE PLANT. pc_extract.kernel, drawn every run: a #[kernel] whose reference does not resolve must be named +# kernel:referenceMissing, one whose reference does must not be, and the ladder schema under evidence/kernels/ must +# be refused by name. +# +# KIND: pattern. Vocabulary, extractor and shapes over a graph; the proof is the extractor's own case table. +# ───────────────────────────────────────────────────────────────────────────── +name: ont-kernel-receipts +version: "1.0.0" +scope: > + How a bound #[kernel] symbol becomes an ont:KernelSymbol focus node, how the tracked kernel receipts are joined to it by + kernel name and build sha, and the three shapes over the join. Out of scope: the kernel-kind contracts (the other + sense of Kernel in Σ), the release kernel diffs (apr-kernel-diff-receipt/v1, aprender#3715), hand-PTX strings + (nothing binds them, so nothing extracts them). +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + Bound #[kernel] symbols are ont:KernelSymbol focus nodes; apr-kernel-receipt/v1 rows witness them when kernel name and + build sha match; kernel-parity, kernel-timing and kernel-safety are reported until the OXIDE pilot's first + tracked receipt arms them. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §3.7, §5 ONT-4c4' + - 'aprender#4069 (this row), aprender#3522 (the OXIDE pilot: the first #[kernel] and its receipt)' + - 'crates/aprender-contracts/src/ontology/extract/{kernel.rs, code.rs}, lint/shapes_gate.rs' + +entity: + type: kernel + +relations: + depends_on: [ont-shapes-v1, ont-relations-v1, ont-sigma-v1, ont-code-symbols-v1] + +shapes: + - id: kernel-parity + targetClass: ont:KernelSymbol + properties: + - {path: kernel:reference, minCount: 1, nodeKind: IRI} + - {path: kernel:referenceMissing, maxCount: 0} + - {path: kernel:parityReceipt, minCount: 1, nodeKind: IRI, class: kernel:Receipt, resolves: kernel-receipt} + - {path: kernel:missingParityHost, maxCount: 0} + - {path: kernel:receiptShaMismatch, maxCount: 0} + - id: kernel-timing + targetClass: ont:KernelSymbol + properties: + - {path: kernel:missingTimingHost, maxCount: 0} + - id: kernel-safety + targetClass: ont:KernelSymbol + properties: + - {path: kernel:safety, minCount: 1, maxCount: 1, in: ["true"]} + - {path: kernel:registerBudget, minCount: 1} + +equations: + witness: + formula: "witness(k, f, row) ⇔ row.kernel ∈ {name(k), module(k)::name(k)} ∧ row.sha = f.sha ∧ f.sha ≠ ⊥" + domain: "every ont:KernelSymbol and every row of every apr-kernel-receipt/v1 file under evidence/kernels/" + codomain: "a kernel:parityReceipt edge to a kernel:Receipt node, or a kernel:receiptShaMismatch literal naming the file" + invariants: + - "a row whose sha is not its receipt's build sha is a mismatch, never a witness" + - "a row naming no bound kernel is counted (orphan_rows), never graded" + preconditions: + - "every *.json under evidence/kernels/ carries schema apr-kernel-receipt/v1; any other schema is exit 3 naming the file" + postconditions: + - "two extractions are byte-identical (R-15)" + lean_theorem: none — L4 not declared + parity: + formula: "parityOn(k, h) ⇔ ∃ witness row on h: cos ≥ 0.9999 ∧ |maxdiff| < 1e-3" + domain: "every ont:KernelSymbol × every required host (evidence/kernels/REQUIRED_HOSTS; absent → the hosts that measured)" + codomain: "a kernel:parityOn literal per host at parity; a kernel:missingParityHost literal per required host that is not" + invariants: + - "a host below parity is named, never averaged against a host above it" + preconditions: + - "the kernel has a kernel:reference: the contract binds a non-#[kernel] symbol to the same equation" + postconditions: + - "kernel-parity fires iff a required host is missing, the reference is missing, or a sha mismatches" + lean_theorem: none — L4 not declared + timing: + formula: "timingOn(k, h) ⇔ (∃ witness row on h: oxide_us / ptx_us ≤ 1.2) ∨ exemption(k)" + domain: "every ont:KernelSymbol × every required host" + codomain: "a kernel:timingOn literal, or a kernel:missingTimingHost literal" + invariants: + - "an exemption is a tracked receipt (evidence/kernels//EXEMPTION), cited as kernel:exemptionReceipt" + preconditions: + - "ptx_us > 0" + postconditions: + - "kernel-timing fires iff a required host is over budget with no exemption" + lean_theorem: none — L4 not declared + safety: + formula: "safety(k) ⇔ unsafeFree(k) ∧ boundsChecked(k) ∧ ∃ registerBudget(k)" + domain: "every ont:KernelSymbol" + codomain: "kernel:safety \"true\" | \"false\"" + invariants: + - "an unsafe fn, an unsafe block, or a get_unchecked call in the body is not safe" + preconditions: + - "the body was resolved by the syn walk" + postconditions: + - "kernel-safety fires iff safety is false" + lean_theorem: none — L4 not declared + +invariants: + - id: KRC-INV-001 + property: a kernel with no reference fails kernel-parity + formal: '|referenceMissing(k)| ≥ 1 ⇒ Fail(kernel-parity, k)' + prose: false + - id: KRC-INV-002 + property: a required host without parity fails kernel-parity naming the host + formal: 'h ∈ required ∧ ¬parityOn(k, h) ⇒ Fail(kernel-parity, k)' + prose: false + - id: KRC-INV-003 + property: a foreign schema under the kernel root is a declaration fault + formal: 'schema(f) ≠ apr-kernel-receipt/v1 ⇒ exit = 3' + prose: false + - id: KRC-INV-004 + property: an unarmed shape never feeds the meet + formal: 'shape ∉ armed_shapes ⇒ verdict(gate) independent of results(shape)' + prose: false + +falsification_tests: + - id: FALSIFY-KRC-001 + rule: witnesses and parity per host + prediction: a two-host all-green fixture has two witnesses and no missing host; one host at cos 0.9990 is named missingParityHost; a wrong-sha row is no witness and is named receiptShaMismatch + test: cargo test -p aprender-contracts --lib ontology::extract::kernel + if_fails: a kernel is called at parity on a build nobody measured, or a red host is averaged away + - id: FALSIFY-KRC-002 + rule: timing and its exemption + prediction: oxide_us / ptx_us = 1.3 is missingTimingHost without an exemption and timingOn with one, which is cited + test: cargo test -p aprender-contracts --lib ontology::extract::kernel + if_fails: a slow kernel passes silently, or an exemption is honoured without being cited + - id: FALSIFY-KRC-003 + rule: safety read off the body + prediction: an unsafe block or get_unchecked in a #[kernel] body sets sym:unsafeFree / sym:boundsChecked false and kernel:safety "false" + test: cargo test -p aprender-contracts --lib ontology::extract + if_fails: an unchecked kernel is graded safe + - id: FALSIFY-KRC-004 + rule: the extractor's positive control + prediction: pc_extract.kernel fires every run — a kernel with no reference is named, one with a reference is typed clean, the ladder schema is refused + test: cargo test -p aprender-contracts --lib lint::shapes_gate::tests + if_fails: a resolver that typed every kernel clean would make every kernel-parity a Pass + +qa_gate: + id: F-KRC-001 + name: kernel-receipts + description: 'Bound #[kernel] symbols are focus nodes, the tracked kernel receipts are their witnesses (ONT-4c4)' + checks: + - witnesses_by_kernel_and_build_sha + - parity_per_required_host + - timing_or_cited_exemption + - safety_from_the_body + - extractor_control_fires + pass_criteria: All 4 falsification tests pass; `pv lint contracts/ --gate shapes` reports by_entity_type.kernel, pc_extract.kernel fired, and by_shape naming kernel-parity, kernel-timing and kernel-safety + falsification: 'set cos to 0.999 in a tracked kernel receipt row → kernel-parity names the host; change its sha → zero witnesses; add an unsafe block to the #[kernel] body → kernel-safety fires' diff --git a/contracts/ont-model-receipts-v1.yaml b/contracts/ont-model-receipts-v1.yaml index 304db4abd1..f6349b06cb 100644 --- a/contracts/ont-model-receipts-v1.yaml +++ b/contracts/ont-model-receipts-v1.yaml @@ -3,7 +3,8 @@ # # WHAT IT GOVERNS # provable_contracts::ontology::extract::gguf — the model-capability ladder's rungs (and any GGUF file a -# contract names) as `model:Model` nodes keyed by sha256. +# contract names) as `model:Model` nodes keyed by sha256; +# ladder rungs are ALSO typed `model:LadderRung` (v4.16 D-T2, ONT-4c). # provable_contracts::ontology::extract::apr_model — an `.apr` v2 container as a `model:Model` node; a header whose # tensor count disagrees with its index is refused, naming the file. # provable_contracts::ontology::receipts — `resolves: receipt`: the tracked ladder receipts @@ -21,7 +22,7 @@ # (`armed_shapes shrank` → exit 3) keeps it armed. Both quantifications live in receipts.rs, which the fixtures # under tests/fixtures/ont/ladder-* falsify; the shapes stay inside §3.6's subset (minCount, maxCount, in, node). # -# THE PLANT. A bare `model:Model` node drawn every run violates `ladder-measured`'s `model:parityReceipt minCount 1` +# THE PLANT. A bare `model:LadderRung` node (the targetClass since ONT-4c) drawn every run violates `ladder-measured`'s `model:parityReceipt minCount 1` # and `model:sha256 minCount 1`: the positive control, drawn from an ARMED shape. Two more every run: # `pc_extract.gguf` (a corrupt magic is refused) and `pc_extract["apr-model"]` (a lying header is refused). # @@ -42,7 +43,7 @@ metadata: last_modified: '2026-09-19' author: PAIML Engineering description: > - The model-capability ladder's rungs are model:Model focus nodes; resolves: receipt joins the tracked ladder + The model-capability ladder's rungs are model:LadderRung focus nodes (ONT-4c retarget); resolves: receipt joins the tracked ladder receipts to them by measured sha256; ladder-measured is armed, ladder-green is reported until the 0.68.2 bump arms it; arming is per shape and monotone. references: @@ -58,7 +59,7 @@ relations: shapes: - id: ladder-measured - targetClass: model:Model + targetClass: model:LadderRung properties: - {path: model:sha256, minCount: 1, maxCount: 1, pattern: "^[0-9a-f]{64}$"} - {path: model:parityReceipt, minCount: 1, nodeKind: IRI, class: model:Receipt, resolves: receipt} diff --git a/contracts/ont-refines-v1.yaml b/contracts/ont-refines-v1.yaml new file mode 100644 index 0000000000..1e9a8ddb5d --- /dev/null +++ b/contracts/ont-refines-v1.yaml @@ -0,0 +1,165 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-refines-v1 — `A refines B` obeys the Liskov rule, and the witness that +# says so checks (ONT-001 §3.5, R-20, §5 row ONT-4e; issue #4075; ticket +# PMAT-4075) +# +# WHAT IT GOVERNS +# provable_contracts::schema::Clause — `requires[]` and `ensures[]` as +# first-class `{id, statement, formal, formal_status}` lists. +# provable_contracts::ontology::liskov — the three obligations, Pair, +# liskov_sha256, LiskovWitness, check, pc_checker. +# provable_contracts::lint::refines_gate — the `refines` gate: PV-ONT-024 +# (Liskov violation, clause named), PV-ONT-025 (witness does not check), +# PV-ONT-026 (malformed clause), PV-ONT-027 (liskov_prose above the baseline); +# declines NoCheckable / WitnessStale / PositiveControlFailed, exit 3 on a +# malformed Σ. +# `pv-sat` liskov.rs — the UNTRUSTED reasoner, with its own direction table and +# its pc_liskov_reasoner plant. +# +# THE OBLIGATIONS (atoms are opaque: `formal`, whitespace collapsed) +# pre — every clause of A.requires is implied by B.requires (else strengthened) +# post — every clause of B.ensures is implied by A.ensures (else weakened) +# inv — every clause of B.invariants is implied by A.invariants (else dropped) +# An invariant is a clause only when it carries `formal_status`; a legacy +# invariants list or mapping is not one, and a pair with no clauses on either +# side is `legacy` — Pass, with liskov_pairs_checked saying nothing was checked. +# +# PROOF LADDER, STATED HONESTLY +# * L2 — unit tests over the obligations, the checker and the digest; the +# gate's tests; pv-sat's reasoner test; the CLI test runs the §5 probe +# verbatim on this repo's corpus. +# * L3 — not declared. There are no Kani harnesses for this checker. +# * L4 — not declared. There is no Lean theorem. +# The gate is COMPUTED and NOT ARMED: it is absent from `armed_gates`, so its +# verdict does not reach the armed meet until an arming PR adds it. +# ───────────────────────────────────────────────────────────────────────────── +metadata: + version: 1.0.0 + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + kind: pattern + description: > + ONT-001 Liskov rule — requires/ensures as first-class clauses; every refines pair's pre, post and invariant + obligations decided offline by the untrusted pv-sat and re-verified in the gate by a witness checker with two + positive controls + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §3.5, §5 ONT-4e, R-20 (Liskov on refines), R-1 (the reasoner is untrusted), R-3 (positive controls)' + - 'Liskov & Wing (1994) A behavioral notion of subtyping' + - 'crates/aprender-contracts/src/ontology/liskov.rs — the obligations, the digest, check, pc_checker' + - 'crates/aprender-contracts/src/lint/refines_gate.rs — the refines gate' + - 'crates/aprender-contracts-cli/src/bin/pv-sat/liskov.rs — the reasoner and its plant' +equations: + obligations: + formula: 'A refines B ⇒ (∀c ∈ A.requires: implies(B.requires, c)) ∧ (∀c ∈ B.ensures: implies(A.ensures, c)) ∧ (∀c ∈ B.invariants: implies(A.invariants, c))' + domain: a refines edge of the relations gate's typed graph, both endpoints in the census + codomain: three obligations, each a chain or a counter-model + invariants: + - implies(P, c) holds exactly when some premise in P has the same atom as c + - a prose clause is never decided; it is reported by name + preconditions: + - every clause is well formed; otherwise PV-ONT-026 names its file and id + postconditions: + - a violation names the pair, the obligation and the clause id + lean_theorem: none — L4 not declared + check: + formula: 'check(W, P) = Ok(V) ⇔ W.liskov_sha256 = liskov_sha256(P) ∧ ∀ obligation o: (o = chain ∧ every step pairs a conclusion clause with a same-atom premise clause) ∨ (o = counter_model ∧ violated = exactly the conclusion clauses with no same-atom premise)' + domain: W a Liskov witness, P the checkable pairs of the current corpus + codomain: '{Ok(violations), Err(reason)}' + invariants: + - a counter-model that hides or invents a violation is refused + - a witness for other pairs or with a missing obligation is refused + preconditions: + - W names the current pairs; otherwise the gate declines WitnessStale before checking + postconditions: + - Ok(V) ⇒ V is exactly the Liskov violations of P + lean_theorem: none — L4 not declared + gate: + formula: 'verdict = Unknown(NoCheckable) if no Σ; Unknown(WitnessStale) if checkable pairs and no witness for them; Unknown(PositiveControlFailed) unless pc_checker and pc_reasoner fired; Fail on any finding; Unknown(Prose) if a prose clause; Pass otherwise' + domain: a contracts/ directory with Σ, lint-baseline.json and a witness directory + codomain: V (the ONT-6 lattice) + invariants: + - no verdict over checkable pairs without both positive controls fired (R-3) + - liskov_prose never rises above the committed baseline + preconditions: + - Σ parses; otherwise exit 3 + postconditions: + - verdict == Pass ⇒ every checkable refines pair keeps the Liskov rule by a checked witness + lean_theorem: none — L4 not declared +proof_obligations: +- id: ONT4E-INV-001 + type: invariant + property: each obligation runs in its own direction + formal: 'premise(pre) = B.requires ∧ conclusion(pre) = A.requires ∧ premise(post) = A.ensures ∧ conclusion(post) = B.ensures ∧ premise(inv) = A.invariants ∧ conclusion(inv) = B.invariants' + applies_to: all +- id: ONT4E-INV-002 + type: invariant + property: the checker refuses a chain step whose premise atom differs from its clause + formal: 'step = (c, p) ∧ atom(p) ≠ atom(c) ⇒ check(W, P) = Err' + applies_to: all +- id: ONT4E-INV-003 + type: invariant + property: the checker refuses a counter-model that hides or invents a violation + formal: 'violated(o) ≠ {c ∈ conclusion(o) : ¬ ∃ p ∈ premise(o): atom(p) = atom(c)} ⇒ check(W, P) = Err' + applies_to: all +- id: ONT4E-INV-004 + type: invariant + property: the witness name is order-free and sensitive to every clause + formal: 'liskov_sha256(permute(P)) = liskov_sha256(P) ∧ ∀c: liskov_sha256(P with c changed) ≠ liskov_sha256(P)' + applies_to: all +- id: ONT4E-POST-005 + type: postcondition + property: the gate's exit is the lattice element, and a stale, absent or uncontrolled witness never passes + formal: 'violation ⇒ exit 1 naming the clause; prose ⇒ Unknown(Prose) naming the clause; stale ∨ absent ⇒ exit 2 naming make contracts; ¬pc_checker ∨ ¬pc_reasoner ⇒ exit 2; legacy pair ⇒ Pass with liskov_pairs_checked = 0' + applies_to: all +verification_summary: + total_obligations: 5 + l2_property_tested: 5 + l3_kani_proved: 0 + l4_lean_proved: 0 + l4_sorry_count: 0 + l4_not_applicable: 5 +falsification_tests: +- id: FALSIFY-ONT4E-001 + rule: the precondition direction + prediction: a strengthened precondition is named; a weakened one is Liskov + test: cargo test -p aprender-contracts --lib ontology::liskov::tests::a_strengthened_precondition_is_named + if_fails: refinement runs backwards for requires (measured 2026-09-24 — swapping the Pre sides turns 2 lib tests and 2 pv-sat tests red) +- id: FALSIFY-ONT4E-002 + rule: the postcondition and invariant directions + prediction: a weakened postcondition and a dropped invariant are named; a strengthened postcondition is Liskov + test: cargo test -p aprender-contracts --lib ontology::liskov::tests::a_dropped_invariant_is_named + if_fails: a refinement that breaks a promise passes +- id: FALSIFY-ONT4E-003 + rule: pc_checker + prediction: the checker refuses fixtures/liskov-corrupt.json on every run + test: cargo test -p aprender-contracts --lib ontology::liskov::tests::pc_checker_fires_on_the_shipped_fixture + if_fails: a checker that accepts any implication goes unnoticed (measured 2026-09-24 — skipping the atom check turns 10 lib tests and the pv-sat reasoner test red) +- id: FALSIFY-ONT4E-004 + rule: the gate's lattice + prediction: 'honest witness → Pass; control not fired → PositiveControlFailed; no witness → WitnessStale naming make contracts; legacy → Pass with nothing checked; prose → Unknown(Prose) naming the clause; prose above the baseline → PV-ONT-027; malformed clause → PV-ONT-026; no Σ → NoCheckable' + test: cargo test -p aprender-contracts --lib lint::refines_gate::tests + if_fails: the gate reports a verdict about pairs nobody reasoned over +- id: FALSIFY-ONT4E-005 + rule: the reasoner is untrusted but honest here + prediction: pv-sat decides each direction so that the library checker proves it, and its self-test fires pc_liskov_reasoner and pc_liskov_checker + test: cargo test -p aprender-contracts-cli --bin pv-sat + if_fails: pv-sat writes Liskov witnesses the gate refuses +- id: FALSIFY-ONT4E-006 + rule: the §5 probe end to end + prediction: 'pv lint contracts/ --gate refines on this repo: liskov_pairs_checked > 0, pc_checker fired, verdict Pass; each fixture exits with the verdict its name states, on witnesses the real pv-sat wrote' + test: cargo test -p aprender-contracts-cli --test ont4e_refines_gate + if_fails: the probe the ledger binds is not the behaviour shipped +qa_gate: + id: F-ONT4E-001 + name: ONT-001 Liskov rule on refines + description: requires/ensures as first-class clauses, three directed obligations, a witness checker with two positive controls, the refines gate (ONT-4e) + checks: + - precondition_direction + - postcondition_and_invariant_directions + - pc_checker + - gate_lattice + - reasoner_answers_check + - spec_probe + pass_criteria: All 6 falsification tests pass + falsification: 'a checker that accepts any implication, or a swapped precondition direction — each turns named tests RED (measured 2026-09-24)' diff --git a/contracts/ont-self-v1.yaml b/contracts/ont-self-v1.yaml new file mode 100644 index 0000000000..851d2e9d7c --- /dev/null +++ b/contracts/ont-self-v1.yaml @@ -0,0 +1,255 @@ +# ───────────────────────────────────────────────────────────────────────────── +# ont-self-v1 — the ontology's contract on itself: the corpus of contracts is +# a well-formed, acyclic, fully-kinded, non-empty graph whose consistency +# witness names the census it was computed over, whose armed gates only grow, +# and whose witness checker accepts nothing that is false (ONT-001 §5 row +# ONT-9, B.16; issue #4078) +# +# WHAT IT GOVERNS +# The corpus itself — entity `pv-contract`, ref `contracts/`. Every invariant +# below is enforced by a gate or refuser that already runs in `make contracts` +# or `pv lint`; this contract names them as ONE claim and binds each to the +# test that turns red when it breaks. +# provable_contracts::lint::relations_gate — cycle_sweep, now including the +# `depends_on ∪ supersedes` union sweep (ONT-9 found it missing: a cycle that +# alternates the two roles is a cycle in neither alone, and passed). +# provable_contracts::ontology::witness — check (the linear witness checker), +# ont_planted, and the KANI-ONT-9-1 harness over ontology::witness_small. +# +# PROOF LADDER, STATED HONESTLY +# * L2 — unit and CLI tests per invariant; `ont_planted` (proptest, 512 seeds, +# graphs of 2-8 ids consistent by construction or carrying a planted +# contradiction; verdict == construction, and every adversarial core +# or model is refused); `kani_ont_9_1_twin` (2048 samples of the +# harness body, over BOTH id instantiations, asserting identical +# verdicts). +# * L3 — PROVED at 3 ids. KANI-ONT-9-1 is four #[kani::proof] fns in +# witness.rs (cores of 1, 2, 3 steps; models) over check_with, the +# one checker implementation, instantiated at u8 ids in fixed arrays. +# Kani 0.67.0, 2026-09-24: all four VERIFICATION:- SUCCESSFUL, and +# all three planted checker mutants FAILED. The bound is 3 ids and +# cores of at most 3 steps; nothing is claimed for 4 ids. The String +# instantiation production runs is tied to it by the L2 twin, not by Kani. +# * L4 — not declared. There is no Lean theorem. +# RESIDUAL — spec-level common mode: the checker, the oracle and the planted +# constructions all read the same Horn reading of the relations (§3.5). A +# mistake in THAT reading is invisible to every rung here. +# ───────────────────────────────────────────────────────────────────────────── +metadata: + version: 1.0.0 + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + kind: kernel + depends_on: [ont-consistency-v1, ont-relations-v1, ont-verdict-lattice-v1] + # ONT-7: every invariant is a fact about the committed tree and the gates run over it. + valid_under: + world: committed + description: > + ONT-001 self — the contract corpus as an entity: unique ids, an acyclic depends_on ∪ supersedes, every + contract kinded, a non-empty census, a witness bound to the census it names, monotone arming, and a witness + checker that accepts only what is true under relation semantics (ONT-9) + references: + - 'paiml/infra docs/specifications/paiml-ontology.md §5 ONT-9, B.16 (the example this contract instantiates), F-12 (planted-solution graphs, verdict == construction)' + - 'crates/aprender-contracts/src/lint/relations_gate.rs — cycle_sweep and the depends_on ∪ supersedes union sweep' + - 'crates/aprender-contracts/src/ontology/witness.rs — check, ont_planted, kani_proofs::kani_ont_9_1' + - 'crates/aprender-contracts/src/ontology/witness_small.rs — the bounded universe and relation-semantics oracle' + - 'crates/aprender-contracts/src/ontology/witness_planted.rs — the planted-solution generators' + - 'crates/aprender-contracts/src/ontology/arming.rs — check_monotone' + +entity: + type: pv-contract + ref: contracts/ + +relations: + depends_on: [ont-consistency-v1, ont-relations-v1, ont-verdict-lattice-v1] + +equations: + corpus: + formula: 'wf(K) = unique(ids(K)) ∧ acyclic(depends_on ∪ supersedes) ∧ ∀c ∈ K: kind(c) ∈ KINDS ∧ n_files(K) > 0 ∧ witness.census_id_set_sha256 = census_sha(K) ∧ armed(K) ⊇ armed(committed)' + domain: K the contracts/ directory at the committed tree + codomain: '{true, false}, each conjunct reported by its own gate' + invariants: + - an id names exactly one contract + - no id reaches itself along depends_on and supersedes edges taken together + - a contract with no metadata.kind is defaulted and named, never silently untyped + - an empty corpus is refused, never a vacuous pass + - a witness computed over another census is stale, never checked + - an armed gate is never silently dropped + preconditions: + - Σ (contracts/ontology.yaml) parses + postconditions: + - each failed conjunct is a named finding, not a count + lean_theorem: none — L4 not declared + checker_soundness: + formula: 'check(G, W) = Ok(Unsat(K)) ⇒ ¬∃σ: σ ⊨ G, and check(G, W) = Ok(Sat) ⇒ ∃σ: σ ⊨ G' + domain: G a Horn clause set over contract ids (units, implications, conflict pairs); W a witness + codomain: '{Ok(Sat), Ok(Unsat(core)), Err(reason)}' + invariants: + - an accepted core names only ids the graph has + - a refusal claims nothing and is always sound + preconditions: + - W was produced by the untrusted reasoner or by anyone else; the checker trusts none of it + postconditions: + - verdict == construction on every planted graph + lean_theorem: none — L4 not declared + +proof_obligations: +- id: ONTSELF-INV-001 + type: invariant + property: contract ids are unique — a stem shared by two contracts is reported, never resolved silently + formal: '∀a, b ∈ K: a ≠ b ⇒ id(a) ≠ id(b)' + applies_to: all +- id: ONTSELF-INV-002 + type: invariant + property: depends_on and supersedes are acyclic taken TOGETHER, not only each alone + formal: '∀x ∈ ids(K): ¬(x reaches x along depends_on ∪ supersedes)' + applies_to: all +- id: ONTSELF-INV-003 + type: invariant + property: kind totality — a contract without metadata.kind is defaulted and named on its first error + formal: '∀c ∈ K: kind(c) ∈ KINDS' + applies_to: all +- id: ONTSELF-INV-004 + type: invariant + property: the corpus is non-empty — zero contracts is a refusal, never a vacuous pass + formal: 'len(K) ≥ 1' + applies_to: all +- id: ONTSELF-INV-005 + type: invariant + property: the witness names the census it was computed over; a missing or foreign witness is stale + formal: 'check_run ⇒ witness.census_id_set_sha256 = census_sha(K) ∧ witness.relations_sha256 = relations_sha(K)' + applies_to: all +- id: ONTSELF-INV-006 + type: invariant + property: armed_gates is monotone against the committed comparand + formal: 'armed(committed) ⊆ armed(K)' + applies_to: all +- id: ONTSELF-INV-007 + type: invariant + property: the witness checker is sound under relation semantics — an accepted core is a real contradiction, an accepted model a real model + formal: 'check(G, W) = Ok(Unsat(core)) ⇒ ¬satisfiable(G) ∧ core ⊆ ids(G) ∧ len(core) ≥ 1' + applies_to: all + +verification_summary: + total_obligations: 7 + l2_property_tested: 7 + l3_kani_proved: 1 + l4_lean_proved: 0 + l4_sorry_count: 0 + l4_not_applicable: 7 + +proof: + status: proved + kani: + harnesses: [KANI-ONT-9-1, KANI-ONT-9-1-LEN1, KANI-ONT-9-1-LEN2, KANI-ONT-9-1-MODEL] + +kani_harnesses: +- id: KANI-ONT-9-1 + obligation: ONTSELF-INV-007 + property: every verdict check accepts over 3 ids is true under relation semantics — an accepted core means no assignment satisfies the graph, an accepted model means one does + bound: 3 + strategy: exhaustive + solver: cadical + harness: kani_ont_9_1 +- id: KANI-ONT-9-1-LEN1 + obligation: ONTSELF-INV-007 + property: every 1-step core check accepts over 3 ids is a real contradiction naming only known ids + bound: 3 + strategy: exhaustive + solver: cadical + harness: kani_ont_9_1_len1 +- id: KANI-ONT-9-1-LEN2 + obligation: ONTSELF-INV-007 + property: every 2-step core check accepts over 3 ids is a real contradiction naming only known ids + bound: 3 + strategy: exhaustive + solver: cadical + harness: kani_ont_9_1_len2 +- id: KANI-ONT-9-1-MODEL + obligation: ONTSELF-INV-007 + property: every model check accepts over 3 ids satisfies the graph + bound: 3 + strategy: exhaustive + solver: cadical + harness: kani_ont_9_1_model +kani_harnesses_note: > + PROVED at the bound, measured 2026-09-24 with Kani 0.67.0 (unwind 9, cadical). Each harness finished + VERIFICATION:- SUCCESSFUL: kani_ont_9_1 (3-step cores) 52 s, _len1 20 s, _len2 278 s, _model 406 s. The four + together are KANI-ONT-9-1: every graph over 3 ids, every core of 1..=CORE_BOUND steps, every conflict pair, + every model. They run check_with, the one checker implementation, at u8 ids over heap-free fixed arrays. Two + earlier runs hit their time caps with no verdict: the BTreeSet instantiation at 2400 s, and u8 over Vec + with a symbolic core length at 1800 s. Checking discriminates: with the premise check off, any conflict accepted, or + the model check skipping conflicts, the harness goes VERIFICATION:- FAILED on its property assertion. The bound is + measured, not chosen: the pv-sat plant's core is 3 steps and the committed corpus witness is a model. Nothing is + claimed for 4+ ids. + +falsification_tests: +- id: FALSIFY-ONTSELF-001 + rule: unique ids + prediction: a stem shared by two contract files is reported as ambiguous, not resolved to either + test: cargo test -p aprender-contracts --lib lint::duplicate_stems::tests::ambiguous_stem_is_reported_not_silently_resolved + if_fails: a relation to a shared id binds to whichever file the walk met first +- id: FALSIFY-ONTSELF-002 + rule: acyclic depends_on ∪ supersedes + prediction: a → b by depends_on and b → a by supersedes is PV-ONT-009 naming `a -> b -> a` (red before ONT-9, measured 2026-09-24) + test: cargo test -p aprender-contracts --lib lint::relations_gate::tests::a_cycle_through_depends_on_and_supersedes_together_is_rejected + if_fails: a cycle that alternates the two roles passes the relations gate +- id: FALSIFY-ONTSELF-003 + rule: a per-role cycle + prediction: a cycle through one acyclic role is PV-ONT-009 naming the closing path + test: cargo test -p aprender-contracts --lib lint::relations_gate::tests::a_cycle_through_an_acyclic_role_is_rejected_naming_the_path + if_fails: introducing a cycle into the corpus stays green +- id: FALSIFY-ONTSELF-004 + rule: kind totality + prediction: a kindless contract that fails names the defaulted kind on its first error + test: cargo test -p aprender-contracts-cli --test ont6b_kind_default kindless_failing_names_the_default_on_its_first_error + if_fails: a contract is linted under a kind nobody declared or saw +- id: FALSIFY-ONTSELF-005 + rule: n_files > 0 + prediction: pv lint over an empty directory is refused + test: cargo test -p aprender-contracts-cli --test pvl_zero_contracts lint_empty_dir_is_refused + if_fails: 0 violations over 0 files reads as a pass +- id: FALSIFY-ONTSELF-006 + rule: witness bound to the census + prediction: a missing or foreign witness declines WitnessStale and is never checked + test: cargo test -p aprender-contracts --lib lint::consistency_gate::tests::a_missing_or_foreign_witness_is_stale_not_a_pass + if_fails: a witness about another corpus passes this one +- id: FALSIFY-ONTSELF-007 + rule: armed_gates monotone + prediction: dropping an armed gate is a shrink that names it + test: cargo test -p aprender-contracts --lib ontology::arming::tests::a_dropped_gate_is_a_shrink_naming_it + if_fails: a gate is disarmed without anyone deciding to +- id: FALSIFY-ONTSELF-008 + rule: checker soundness, planted (F-12) + prediction: on 512 planted graphs verdict == construction and every adversarial certificate is refused; a checker weakened to accept any two ids goes RED (measured 2026-09-24) + test: cargo test -p aprender-contracts --lib ontology::witness::planted::ont_planted + if_fails: the checker accepts a core that proves nothing or a model that falsifies a clause +- id: FALSIFY-ONTSELF-009 + rule: checker soundness, bounded (L2 twin of KANI-ONT-9-1) + prediction: over 3 ids every accepted verdict agrees with the brute-force oracle + test: cargo test -p aprender-contracts --lib ontology::witness_small::tests::kani_ont_9_1_twin + if_fails: the harness body is false and the Kani run would find a counterexample +- id: FALSIFY-ONTSELF-010 + rule: the §5 probe end to end, the L3 rung not claimed early, a corpus cycle RED on the CLI + prediction: the contract is tracked and validates; witness.rs carries ont_planted and KANI-ONT-9-1; declared ⇒ l3_kani_proved = 0; every falsifier names a test fn that exists + test: cargo test -p aprender-contracts-cli --test ont9_self_contract + if_fails: the probe the ledger binds is not the behaviour shipped + +qa_gate: + id: F-ONTSELF-001 + name: ONT-001 ontology self-contract + description: the corpus is a well-formed acyclic kinded non-empty graph, its witness names its census, arming is monotone, and the checker is sound (ONT-9) + checks: + - unique_ids + - union_acyclic + - per_role_acyclic + - kind_totality + - non_empty + - witness_census_bound + - arming_monotone + - checker_planted + - checker_bounded_twin + - spec_probe + pass_criteria: All 10 falsification tests pass + falsification: 'a checker that accepts any two ids, a model check that skips conflicts, or a relations gate without the union sweep — each turns a named test RED (measured 2026-09-24)' diff --git a/contracts/ont-shapes-v1.yaml b/contracts/ont-shapes-v1.yaml index 3441267582..fd5b821820 100644 --- a/contracts/ont-shapes-v1.yaml +++ b/contracts/ont-shapes-v1.yaml @@ -131,3 +131,14 @@ qa_gate: - warnings_never_pass pass_criteria: All 5 falsification tests pass; `pv lint contracts/ --gate shapes` is Pass on the real corpus with shapes_n ≥ 1, focus_nodes_n ≥ 1000, pc_shape fired falsification: 'remove minCount from ont:id → Unknown{PositiveControlFailed} (measured 2026-09-18); add a kind outside the list to one contract → 1 violation naming it' + +# ONT-8: one evidence block — PROV-O names, one L-enum (ProofLevel). The level is MEASURED, not declared: it is what +# the cited command printed for this contract at the time below. Checked by `pv lint --gate evidence`. +evidence: + level: L1 + mark: C + provenance: + wasGeneratedBy: + command: "pv proof-status contracts/ --format json" + wasAttributedTo: pv + generatedAtTime: "2026-09-24T08:22:07Z" diff --git a/contracts/ontology.ofn b/contracts/ontology.ofn new file mode 100644 index 0000000000..1808da0406 --- /dev/null +++ b/contracts/ontology.ofn @@ -0,0 +1,64 @@ +# ontology.ofn — Σ as OWL 2 functional syntax. GENERATED from its ontology.yaml by +# `pv ontology export --owl`; do not edit (ONT-001 ONT-2c, R-18). Not expressed here, by design: +# acyclic: irreflexive ∧ transitive is disallowed in OWL 2 DL; acyclicity is checked in Rust (R-19) +# agents: agents are provenance actors (prov), not classes of the TBox +# entity_types: entity types name extractors; their classes are concepts, which ARE written +# extractors: extractors are readers (code), not ontology +# symbols: the `formal:` token vocabulary is a lexicon, not a TBox +# worlds: worlds scope contracts; OWL 2 EL has no modal or context construct +Ontology( +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +SubClassOf( ) +SubClassOf( ) +SubClassOf( ) +SubClassOf( ) +SubClassOf( ) +SubClassOf( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyDomain( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +ObjectPropertyRange( ) +SymmetricObjectProperty() +) diff --git a/contracts/ontology.yaml b/contracts/ontology.yaml index 8e0fc9d82c..b4b51769dd 100644 --- a/contracts/ontology.yaml +++ b/contracts/ontology.yaml @@ -19,6 +19,10 @@ concepts: Entity: {doc: "anything the ontology can name"} Contract: {doc: "a pv contract file under contracts/"} Kernel: {doc: "a contract whose kind is kernel: it carries equations and proof obligations"} + # ONT-001 v4.15: one IRI, one class. The Rust #[kernel] fn is NOT Kernel (a Contract); it is KernelSymbol, a + # Symbol, on the symbol's own IRI. extract:kernel emits `KernelSymbol rdfs:subClassOf Symbol` and refuses any + # subject typed both Symbol and Contract. + KernelSymbol: {doc: "a Rust #[kernel] fn — cuda-oxide, pure Rust to PTX — graded by its apr-kernel-receipt/v1 receipts; a Symbol, never a Contract"} Code: {doc: "a Rust item — a function, type or module — in this workspace"} Test: {doc: "a test function a contract cites as its falsifier"} Proof: {doc: "a Kani harness or Lean theorem discharging an obligation"} @@ -27,6 +31,19 @@ concepts: Statement: {doc: "a Lean theorem in the in-tree Theorems tree; the focus node of extract:lean (ONT-4b2: ont:Statement with lean:sorryFree, lean:modelOf)"} Model: {doc: "a model file — an .apr container or a GGUF; the ladder's rungs are the ones the release measures (ONT-4c1: model:Model, model:RequiredModel, model:Receipt)"} Dataset: {doc: "a CSV or corpus file"} + Binary: {doc: "a cargo bin target of a workspace member, keyed binary//; the focus node of extract:binary (ONT-4g: bin:package, bin:target, bin:name, bin:exposes)"} + # ONT-4f (aprender#4330): the tracked GitHub snapshots under evidence/github//, read by extract:json + # through the `vocabulary` each entity type below carries. Each is ⊑ Json (see `subsumes`). + Json: {doc: "a JSON document extract:json reads — a tool's --json output, or a committed snapshot"} + Repo: {doc: "a GitHub repository at one commit, snapshotted under evidence/github/repo/ (repo:ref = /@)"} + Issue: {doc: "a GitHub issue at one updatedAt, snapshotted under evidence/github/issue/ (issue:ref = /#@)"} + PullRequest: {doc: "a GitHub pull request at one updatedAt, snapshotted under evidence/github/pull-request/ (pr:ref = /#@)"} + Milestone: {doc: "a GitHub milestone at one updatedAt, snapshotted under evidence/github/milestone/ (milestone:ref = /#@)"} + Example: {doc: "a cargo example target — examples/*.rs or examples/*/main.rs of a workspace member; the focus node of extract:example (#3560 R1: example:file, example:crate, example:name, example:namesModel, example:namesQwen35)"} + AprModel: {doc: "an aprender .apr v2 container, the focus node of extract:apr-model, typed alongside model:Model (v4.16 D-T2; AprModel ⊑ Model is ONT-4d's, not built here)"} + LadderRung: {doc: "a model-capability-ladder rung, typed on extract:gguf's rung path only, disjoint from AprModel (v4.16 D-T2)"} + Readme: {doc: "a README.md under contract, the focus node of extract:readme (ONT-4c)"} + LlmContext: {doc: "an LLM context file (CLAUDE.md) under contract, the focus node of extract:llm-context (ONT-4c)"} roles: binds: @@ -135,11 +152,15 @@ entity_types: - {name: json, extractor: json, implemented: true} - {name: code, extractor: code, implemented: true} - {name: lean, extractor: lean, implemented: true} - - {name: readme, extractor: readme, implemented: false} - - {name: llm-context, extractor: llm_context, implemented: false} + - {name: example, extractor: example, implemented: true} + - {name: readme, extractor: readme, implemented: true} + - {name: llm-context, extractor: llm_context, implemented: true} - {name: apr-model, extractor: apr_model, implemented: true} - {name: gguf, extractor: gguf, implemented: true} - - {name: csv, extractor: csv, implemented: false} + - {name: csv, extractor: csv, implemented: true} + # ONT-4g (aprender#4502): one node per cargo bin target, keyed binary//, read from the tracked + # snapshot scripts/dogfood_surfaces.sh --snapshot writes — never a live binary. Implemented by extract:binary (aprender#4476). + - {name: binary, extractor: binary, implemented: true} # ONT-4c3 (aprender#3577): the logit-parity receipts under evidence/parity/**. Subclasses `json` for now; # it moves under a shared `Receipt` class once quorum and dispatch receipts join, so the three families # inherit common shapes. NOT the throughput parity receipts — a different family with a different validator. @@ -147,19 +168,75 @@ entity_types: # aprender#3715: a release's receipts (per-host inventory + cells, kernel diffs, context rungs, the dogfood # receipt R5 judged), extracted ONLY when a release subject is given — an ordinary PR has no release. - {name: release-evidence, extractor: release_evidence, implemented: true} + # ONT-4f (aprender#4330): GitHub entities as COMMITTED SNAPSHOTS (evidence/github//.json), read by + # the json extractor through the vocabulary here — never a live API call. `version` is the snapshot's own field + # the ref's `@` slot must equal; a disagreement is refused naming both values. + - {name: repo, extractor: json, implemented: true, vocabulary: {prefix: repo, root_class: "ont:Repo", version: sha}} + - {name: issue, extractor: json, implemented: true, vocabulary: {prefix: issue, root_class: "ont:Issue", version: updatedAt}} + - {name: pull-request, extractor: json, implemented: true, vocabulary: {prefix: pr, root_class: "ont:PullRequest", version: updatedAt}} + - {name: milestone, extractor: json, implemented: true, vocabulary: {prefix: milestone, root_class: "ont:Milestone", version: updatedAt}} + # aprender#3745 S2 (#3777): the release candidate's own interface (`apr surface --json`, apr-cli-surface/v1), + # extracted ONLY under a release subject with --surface; the release cells are derived from it. + - {name: cli-surface, extractor: cli_surface, implemented: true} + # ONT-4c4 (aprender#4069): the bound #[kernel] symbols over extract:code, joined to evidence/kernels//.json + # (apr-kernel-receipt/v1 — the ladder schema there, or this one under the ladder root, is refused by name). + - {name: kernel, extractor: kernel, implemented: true} extractors: - {name: pv_contract, reader: ontology/extract/pv_contract.rs, implemented: true} - {name: json, reader: ontology/extract/json.rs, implemented: true} - {name: code, reader: ontology/extract/code.rs, implemented: true} - {name: lean, reader: ontology/extract/lean.rs, implemented: true} - - {name: readme, reader: ontology/extract/readme.rs, implemented: false} - - {name: llm_context, reader: ontology/extract/llm_context.rs, implemented: false} + - {name: example, reader: ontology/extract/example.rs, implemented: true} + - {name: readme, reader: ontology/extract/readme.rs, implemented: true} + - {name: llm_context, reader: ontology/extract/llm_context.rs, implemented: true} - {name: apr_model, reader: ontology/extract/apr_model.rs, implemented: true} - {name: gguf, reader: ontology/extract/gguf.rs, implemented: true} - - {name: csv, reader: ontology/extract/csv.rs, implemented: false} + - {name: csv, reader: ontology/extract/csv.rs, implemented: true} + - {name: binary, reader: ontology/extract/binary.rs, implemented: true} - {name: parity_receipt, reader: ontology/extract/parity_receipt.rs, implemented: true} - {name: release_evidence, reader: ontology/extract/release_evidence.rs, implemented: true} + - {name: cli_surface, reader: ontology/extract/cli_surface.rs, implemented: true} + - {name: kernel, reader: ontology/extract/kernel.rs, implemented: true} + +# v4.16 D-T1 (qd4c4): the class a shape with no `targetClass` targets, by `entity.type` (shapes.rs::default_target). +# A type absent here has no default: its shape is malformed, exit 3, named. The sigma gate cross-checks the map +# against entity_types both ways: a key no entity_type declares is malformed, and so is an implemented type with +# neither a class nor an explicit `~` (no default target: every shape on it names its own targetClass). +entity_type_target_class: + pv-contract: ont:Contract + readme: readme:Readme + llm-context: llm:LlmContext + csv: csv:Dataset + apr-model: model:AprModel + gguf: model:Model + json: ~ + code: ~ + lean: ~ + parity-receipt: ~ + release-evidence: ~ + cli-surface: ~ + kernel: ~ + example: ont:Example + binary: ont:Binary + repo: ont:Repo + issue: ont:Issue + pull-request: ont:PullRequest + milestone: ont:Milestone + +# v4.16 D5b (qd4c3): which `## ` headings of an llm-context file satisfy each role. extract:llm-context emits +# llm:purposeSection / rulesSection / commandsSection / layoutSection per match; an unlisted heading is no error. +llm_context_role_synonyms: + Purpose: ["Project Overview"] + Rules: ["Git Workflow (Branch Protection)", "Autonomous Operation Mode (DEFAULT)", + "LAYOUT-001/002: Tensor Layout Safety", "Publishing Safety (CB-510 Lesson)", + "Shell Scripts: Use bashrs (NOT shellcheck)", + "Verification Discipline (MANDATORY — read before reporting any result)", + "Linting", "Contract Validation: DOGFOOD `pv`, NEVER bash", + "CRITICAL: Code Search Policy", "CRITICAL: Realizar-First Architecture"] + Commands: ["Build Commands", "Debugging: Use apr Tools First (MANDATORY)", + "APR CLI (`cargo install aprender`)"] + Layout: ["Architecture", "Modules", "Key Files", "CI/CD (`.github/workflows/`)"] # What this ontology does NOT express, said out loud so nobody encodes it by accident. Each names the reader that # would have to change first. @@ -167,13 +244,37 @@ not_expressible: - {key: taste, reader: ontology/sigma.rs} - {key: urgency, reader: ontology/sigma.rs} - {key: authorship_intent, reader: ontology/sigma.rs} + # ONT-2c: Σ content the OWL 2 EL writer (ontology/owl.rs) deliberately does not turn into axioms. Each key's + # reason is printed in the header of contracts/ontology.ofn; a populated key missing here refuses the export. + - {key: acyclic, reader: ontology/owl.rs} + - {key: symbols, reader: ontology/owl.rs} + - {key: worlds, reader: ontology/owl.rs} + - {key: agents, reader: ontology/owl.rs} + - {key: entity_types, reader: ontology/owl.rs} + - {key: extractors, reader: ontology/owl.rs} + +# ONT-4d (R-19): the subsumption hierarchy. Each edge is read off the two concepts' own `doc` above, never +# asserted for convenience: `Kernel` is "a contract whose kind is kernel", and `Symbol` is "a Rust symbol … a free +# fn or an impl method", which is a `Code` item ("a Rust item — a function, type or module"). Shapes on the super +# apply to every instance of the sub, through the rdf:type closure `pv extract` materializes into contracts.nt. +subsumes: + - {sub: Kernel, sup: Contract} + - {sub: Symbol, sup: Code} + # ONT-4f: each GitHub snapshot IS a JSON document extract:json reads (the Json doc above names both kinds). + - {sub: Repo, sup: Json} + - {sub: Issue, sup: Json} + - {sub: PullRequest, sup: Json} + - {sub: Milestone, sup: Json} readers: concepts: ontology/sigma.rs + subsumes: ontology/sigma.rs, ontology/extract/mod.rs, lint/subsumption.rs, ontology/owl.rs roles: lint/sigma_gate.rs, lint/relations_gate.rs symbols: lint/sigma_symbols.rs worlds: ontology/sigma.rs, lint/valid_under_gate.rs - agents: ontology/sigma.rs - entity_types: lint/sigma_gate.rs + agents: ontology/sigma.rs, lint/evidence_gate.rs + entity_types: lint/sigma_gate.rs, ontology/extract/json/github.rs extractors: ontology/sigma.rs not_expressible: ontology/sigma.rs + entity_type_target_class: ontology/shapes.rs + llm_context_role_synonyms: ontology/extract/llm_context.rs diff --git a/contracts/presentar/binding.yaml b/contracts/presentar/binding.yaml new file mode 100644 index 0000000000..5875368032 --- /dev/null +++ b/contracts/presentar/binding.yaml @@ -0,0 +1,221 @@ +version: 1.0.0 +target_crate: presentar +critical_path: +- render +- slide +- export +- compile +bindings: +- contract: dag-ordering-v1.yaml + equation: render + function: render_order + module_path: presentar::crates::presentar-layout::src::compute_block::render_order + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render + module_path: presentar::crates::presentar-terminal::src::compute_block::render + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_frame + module_path: presentar::crates::presentar-terminal::src::tools::bench::render_frame + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_full + module_path: presentar::crates::presentar-terminal::src::direct::diff_renderer::render_full + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_lines + module_path: presentar::crates::presentar-terminal::src::widgets::ux::render_lines + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_string + module_path: presentar::crates::presentar-terminal::src::widgets::micro_heat_bar::render_string + status: implemented +- contract: gpu-context-health-v1.yaml + equation: render + function: render_text + module_path: presentar::crates::presentar-terminal::src::widgets::micro_heat_bar::render_text + status: implemented +- contract: display-format-v1.yaml + equation: render + function: for_render + module_path: presentar::crates::presentar-terminal::src::perf_trace::trackers::for_render + status: implemented +- contract: display-format-v1.yaml + equation: render + function: record_render + module_path: presentar::crates::presentar-terminal::src::app::record_render + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_with + module_path: presentar::crates::presentar-widgets::src::list::render_with + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_range + module_path: presentar::crates::presentar-core::src::virtualization::render_range + status: implemented +- contract: display-format-v1.yaml + equation: render + function: should_render + module_path: presentar::crates::presentar-core::src::virtualization::should_render + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_count + module_path: presentar::crates::presentar-core::src::virtualization::render_count + status: implemented +- contract: display-format-v1.yaml + equation: render + function: cells_to_render + module_path: presentar::crates::presentar-core::src::virtualization::cells_to_render + status: implemented +- contract: display-format-v1.yaml + equation: render + function: should_render_cell + module_path: presentar::crates::presentar-core::src::virtualization::should_render_cell + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_instances + module_path: presentar::crates::presentar::src::webgpu::render_instances + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_json + module_path: presentar::crates::presentar::src::browser::app::render_json + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_counter + module_path: presentar::crates::presentar::src::browser::app::render_counter + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_dashboard + module_path: presentar::crates::presentar::src::browser::app::render_dashboard + status: implemented +- contract: display-format-v1.yaml + equation: render + function: render_widget + module_path: presentar::crates::presentar::src::browser::app::render_widget + status: implemented +# --- TUI rendering contracts (2026-04-02) --- +- contract: tui-rendering-v1.yaml + equation: cellbuffer_bounds + function: get + module_path: presentar::crates::presentar-terminal::src::direct::cell_buffer::get + status: pending +- contract: tui-rendering-v1.yaml + equation: cellbuffer_bounds + function: set + module_path: presentar::crates::presentar-terminal::src::direct::cell_buffer::set + status: pending +- contract: tui-rendering-v1.yaml + equation: diff_renderer_correctness + function: render_diff + module_path: presentar::crates::presentar-terminal::src::direct::diff_renderer::render_diff + status: pending +- contract: tui-rendering-v1.yaml + equation: dirty_tracking + function: mark_dirty + module_path: presentar::crates::presentar-terminal::src::direct::cell_buffer::mark_dirty + status: pending +- contract: tui-rendering-v1.yaml + equation: unicode_width + function: display_width + module_path: presentar::crates::presentar-terminal::src::direct::cell_buffer::display_width + status: pending +- contract: tui-rendering-v1.yaml + equation: color_mode_fallback + function: nearest_256 + module_path: presentar::crates::presentar-terminal::src::color::nearest_256 + status: pending +- contract: tui-rendering-v1.yaml + equation: zero_alloc_render + function: render_diff + module_path: presentar::crates::presentar-terminal::src::direct::diff_renderer::render_diff + status: pending +- contract: tui-rendering-v1.yaml + equation: resize_safety + function: resize + module_path: presentar::crates::presentar-terminal::src::direct::cell_buffer::resize + status: pending +# --- Agent TUI panel contracts (2026-04-02) --- +- contract: tui-panels-v1.yaml + equation: panel_layout_nonoverlap + function: compute_layout + module_path: batuta::agent::tui::layout::compute_layout + status: pending +- contract: tui-panels-v1.yaml + equation: adaptive_degradation + function: detail_level + module_path: batuta::agent::tui::layout::detail_level + status: pending +- contract: tui-panels-v1.yaml + equation: streaming_token_ordering + function: push_token + module_path: batuta::agent::tui::streaming::push_token + status: pending +- contract: tui-panels-v1.yaml + equation: tool_progress_monotonic + function: update_tool + module_path: batuta::agent::tui::tools::update_tool + status: pending +- contract: tui-panels-v1.yaml + equation: cost_display_invariants + function: update_cost + module_path: batuta::agent::tui::cost::update_cost + status: pending +- contract: tui-panels-v1.yaml + equation: sandbox_violation_visibility + function: add_violation + module_path: batuta::agent::tui::sandbox::add_violation + status: pending +- contract: tui-panels-v1.yaml + equation: brick_budget_enforcement + function: verify_all + module_path: batuta::agent::brick::verify_all + status: pending +- contract: tui-panels-v1.yaml + equation: statusbar_state_display + function: update_statusbar + module_path: batuta::agent::tui::statusbar::update_statusbar + status: pending +# --- TUI lifecycle contracts (2026-04-03) --- +- contract: tui-lifecycle-v1.yaml + equation: widget_lifecycle + function: mount + module_path: presentar::crates::presentar-terminal::src::app::TuiApp::mount + status: pending + notes: Widget lifecycle state machine — Created→Mounted→Rendering→Unmounted +- contract: tui-lifecycle-v1.yaml + equation: widget_lifecycle + function: unmount + module_path: presentar::crates::presentar-terminal::src::app::TuiApp::unmount + status: pending + notes: Unmounted is terminal state — no transitions out +- contract: tui-lifecycle-v1.yaml + equation: render_cycle_correctness + function: render_diff + module_path: presentar::crates::presentar-terminal::src::direct::diff_renderer::render_diff + status: pending + notes: Only dirty cells written to terminal, zero writes for unchanged buffer +- contract: tui-lifecycle-v1.yaml + equation: event_dispatch + function: dispatch_event + module_path: presentar::crates::presentar-terminal::src::app::dispatch_event + status: pending + notes: Key→focused widget, Mouse→hit-test, Resize→re-layout, non-blocking +- contract: tui-lifecycle-v1.yaml + equation: terminal_restore + function: drop + module_path: presentar::crates::presentar-terminal::src::app::TuiApp::drop + status: pending + notes: Terminal restored on all exit paths — normal, panic, signal diff --git a/contracts/pretokenize-bin-v1.yaml b/contracts/pretokenize-bin-v1.yaml index 275fdfdad0..5fb63268fd 100644 --- a/contracts/pretokenize-bin-v1.yaml +++ b/contracts/pretokenize-bin-v1.yaml @@ -15,7 +15,7 @@ # apr. The stopgap was a Python shim; that stopgap is MUDA per the # 2026-04-19 user feedback "why not fix root cause vs 'hack'". This # contract specifies the native Rust subcommand `apr tokenize -# encode-corpus` (or equivalent `apr-corpus-ingest run`) that +# encode-corpus` (or equivalent `aprender-corpus-ingest run`) that # consumes a trained BPE tokenizer + JSONL corpus and produces the # exact shard format ShardBatchIter expects. # @@ -113,7 +113,7 @@ format: producer: cli_name: "apr tokenize encode-corpus" - alternative_cli_name: "apr-corpus-ingest run" + alternative_cli_name: "aprender-corpus-ingest run" required_inputs: - "--corpus # JSONL file with `content` field per line, or directory of such" - "--tokenizer

# directory containing vocab.json + merges.txt from `apr tokenize train`" diff --git a/contracts/pvl-lint-ratchets-v1.yaml b/contracts/pvl-lint-ratchets-v1.yaml new file mode 100644 index 0000000000..e8160db4af --- /dev/null +++ b/contracts/pvl-lint-ratchets-v1.yaml @@ -0,0 +1,121 @@ +# ───────────────────────────────────────────────────────────────────────────── +# pvl-lint-ratchets-v1 — theorem pairing + depends_on, shrink-only (PVL-001 EV-11) +# (paiml/infra PVL-001 @00553b0b §EV-11; issue #4166; ticket PMAT-4166) +# +# WHAT IT GOVERNS +# provable_contracts::lint::ratchet_gates — PV-RAT-001 (theorem-pairing) and +# PV-RAT-002 (depends-on-present); `pv lint --gate theorem-pairing --gate +# depends-on-present` and gates 14/15 of the full run (R-8: computed +# everywhere, armed per repo); `make lint-ratchet` (scripts/lint_ratchet.sh), +# the only writer of the two baselines, which only turns them down. +# +# THE PAIRING RULE IS THIS ROW'S DESIGN. The row says "module name appears in +# book/"; the full dotted module name on identifier boundaries is the reading +# that cannot be satisfied by accident (the book names 80 of 165 file STEMS, +# mostly as ordinary words; measured 2026-09-24). +# ───────────────────────────────────────────────────────────────────────────── +name: pvl-lint-ratchets +version: "1.0.0" +scope: > + Two debts that may fall and never rise: Lean theorem modules no book page names, and kernel-kind contracts + with an empty `metadata.depends_on`. Out of scope: whether a book page's mention is a real explanation, and + whether a declared dependency is the right one. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + `pv lint --gate theorem-pairing` counts Lean theorem modules whose full module name no book page mentions; + `--gate depends-on-present` counts kernel-kind contracts with no depends_on. Each rejects a count above its + top-level baseline in contracts/lint-baseline.json, reports (never passes) with no baseline, declines when + nothing was measured, and never writes the file. + references: + - 'paiml/infra docs/specifications/pvl-001 @00553b0b §EV-11 (probe + mutation)' + - 'crates/aprender-contracts/src/lint/ratchet_gates.rs — PV-RAT-001, PV-RAT-002' + - 'scripts/lint_ratchet.sh — `make lint-ratchet`, the downward-only writer' + +relations: + depends_on: [ont-verdict-lattice-v1] + +equations: + shrink_only_ratchet: + formula: "admits(corpus) ⟺ baseline ≠ ⊥ ∧ debt(corpus) ≤ baseline" + domain: "a contract corpus, its repo root (lean/, book/) and contracts/lint-baseline.json" + codomain: "Pass | Fail | Unknown" + invariants: + - "no baseline is Unknown(Report) with the count printed, never Pass" + - "nothing measured (no Lean base, no module, no book page, no kernel contract) is a decline" + preconditions: + - "the baseline, when present, is a non-negative integer at the top level" + postconditions: + - "verdict ∈ {Pass, Fail, Unknown}" + - "contracts/lint-baseline.json is byte-identical after the gate" + lean_theorem: none — L4 not declared + +invariants: + - id: RAT-INV-001 + property: the unpaired theorem module debt is shrink-only + formal: 'unpaired(corpus) ≤ baseline(unpaired_theorem_modules)' + prose: false + - id: RAT-INV-002 + property: the kernel contracts without depends_on debt is shrink-only + formal: 'without_depends_on(corpus) ≤ baseline(contracts_without_depends_on)' + prose: false + - id: RAT-INV-003 + property: a module is paired only by its full dotted name on identifier boundaries + formal: 'paired(m) ⟺ ∃ p ∈ book: mentions(p, name(m))' + prose: false + - id: RAT-INV-004 + property: a gate never writes its baseline + formal: '¬(after_gate(baseline) ≠ before_gate(baseline))' + prose: false + +falsification_tests: + - id: FALSIFY-RAT-001 + rule: the spec's mutation + prediction: adding an unpaired Theorem module turns `pv lint --gate theorem-pairing --gate depends-on-present` RED with PV-RAT-001 + test: cargo test -p aprender-contracts-cli --test ev11_lint_ratchets + if_fails: a theorem module can be added that no book page names, unrecorded + - id: FALSIFY-RAT-002 + rule: the depends_on ratchet + prediction: a kernel-kind contract with no depends_on above the baseline is PV-RAT-002; equal or lower passes + test: cargo test -p aprender-contracts --lib lint::ratchet_gates::tests::adding_a_kernel_contract_with_no_depends_on_is_red + if_fails: a kernel contract that names no composition can be added unrecorded + - id: FALSIFY-RAT-003 + rule: the pairing boundary + prediction: a file stem, or a longer module name that starts with it, does not pair a module + test: cargo test -p aprender-contracts --lib lint::ratchet_gates::tests::the_file_stem_or_a_longer_name_does_not_pair_a_module + if_fails: the book's ordinary words pair modules by accident + - id: FALSIFY-RAT-004 + rule: no baseline is reported, never judged + prediction: with no baseline key both gates are Unknown(Report), exit 2, the count printed + test: cargo test -p aprender-contracts --lib lint::ratchet_gates::tests::no_baseline_is_reported_and_never_a_pass + if_fails: deleting the baseline turns the ratchet into a pass + - id: FALSIFY-RAT-005 + rule: the gates never write + prediction: the baseline is byte-identical after both gates, on fixtures and on the real corpus + test: cargo test -p aprender-contracts --lib lint::ratchet_gates::tests::neither_gate_writes_the_baseline + if_fails: a gate can raise its own baseline + - id: FALSIFY-RAT-006 + rule: R-8 + prediction: the full `pv lint` run computes theorem-pairing and depends-on-present, reported and not armed + test: cargo test -p aprender-contracts --lib lint::tests + if_fails: armed_gates could name a gate no run computes + +qa_gate: + id: F-RAT-001 + name: lint ratchets + description: Theorem pairing and depends_on debts may fall, never rise (PVL-001 EV-11) + checks: + - unpaired_theorem_modules_shrink_only + - contracts_without_depends_on_shrink_only + - strict_pairing_boundary + - no_baseline_is_report + - gate_never_writes + - computed_in_every_run + pass_criteria: All 6 falsification tests pass; the EV-11 probe exits 0 on the real corpus and `git diff --exit-code contracts/lint-baseline.json` is clean after it + falsification: 'add lean/ProvableContracts/Theorems/S/B.lean named by no book page → exit 1, PV-RAT-001 (measured on a fixture 2026-09-24)' diff --git a/contracts/ratatui-migration-v1.yaml b/contracts/ratatui-migration-v1.yaml index ebdfa1d982..f5b4442660 100644 --- a/contracts/ratatui-migration-v1.yaml +++ b/contracts/ratatui-migration-v1.yaml @@ -86,7 +86,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 4 + total_obligations: 2 proven: 4 tested: 4 status: COMPLETE diff --git a/contracts/readme-root.yaml b/contracts/readme-root.yaml new file mode 100644 index 0000000000..ed729e3259 --- /dev/null +++ b/contracts/readme-root.yaml @@ -0,0 +1,86 @@ +# ───────────────────────────────────────────────────────────────────────────── +# readme-root — the repository README as a graded document (ONT-001 §5 ONT-4c, Appendix B.4; #3847, PMAT-3847) +# +# WHAT IS GRADED. `extract:readme` reads README.md's frontmatter (emitted by `readme_gen` via +# scripts/readme_sync.sh, never hand-written), its headings and its CLAIM fences. A claim fence is a CommonMark +# backtick or tilde fence whose first info token, lower-cased with `{` and `.` stripped, is bash, sh or shell. +# Every command in one must equal a `run:` line of a merge-path workflow step (push, pull_request, +# pull_request_target, merge_group; not `if: false`) — `resolves: ci-step`. One no workflow runs is refused by +# name, `readme:verifiedCommand unresolved: `. A fence that is illustration, not a claim, says so with +# `text`/`console`, which is a visible, reviewable edit — and dropping a VERIFIED claim that way is the F-34 +# withdrawal ratchet's business (lint-baseline.json `readme.withdrawn[]`, `make ont-ratchet`). +# +# KIND: pattern. One shape over one focus node; the proof is the gate's case table and the fixtures under +# tests/fixtures/ont/readme-*. +# ───────────────────────────────────────────────────────────────────────────── +name: readme-root +version: "1.0.0" +scope: > + README.md at the repository root: its frontmatter, its sections, and every command it presents as runnable. + Out of scope: prose accuracy, link targets, anything in a non-claim fence. +status: active + +metadata: + version: "1.0.0" + kind: pattern + created: '2026-09-24' + last_modified: '2026-09-24' + author: PAIML Engineering + description: > + README.md becomes a readme:Readme node; every claim-fence command must be one a merge-path CI step runs, + every entrypoint must exist, and the contract count it prints must equal census.json n_files. + references: + - 'paiml/infra docs/specifications/paiml-ontology.md v4.16 §5 ONT-4c, Appendix B.4' + - 'crates/aprender-contracts/src/ontology/extract/{readme,claims}.rs, ontology/measured_sets.rs' + - 'scripts/readme_sync.sh (the frontmatter), scripts/check_ont_ratchet.sh (the measured sets)' + +entity: + type: readme + ref: README.md + +relations: + depends_on: [ont-sigma-v1, ont-shapes-v1] + +shape: + targetClass: readme:Readme + closed: true + properties: + - {path: readme:schemaVersion, minCount: 1, maxCount: 1, pattern: "^[0-9]+\\.[0-9]+$"} + - {path: readme:kind, in: [library, cli, service, spec]} + - {path: readme:entrypoint, minCount: 1, resolves: path} + - {path: readme:verifiedCommand, minCount: 1, resolves: ci-step} + - {path: readme:section, minCount: 3} + - {path: readme:contractCount, maxCount: 1, resolves: census} + +equations: + claim_resolves: + formula: "∀ c ∈ claims(README.md): normalise(c) ∈ run_lines(merge_path_workflows)" + domain: every command line of every claim fence in README.md + codomain: readme:verifiedCommand values, or a PV-ONT-012 refusal naming the command + invariants: + - a claim is decided by its fence's info token, never by its content + - an unresolved claim is refused by name, never skipped + preconditions: + - .github/workflows/*.yml parse + postconditions: + - the committed readme.verified_commands[] equals the live set (F-33) + lean_theorem: "none — L4 not declared" + +invariants: + - id: RDM-INV-001 + property: every README claim-fence command is one a merge-path workflow runs + formal: '∀ c ∈ claims(README.md): normalise(c) ∈ run_lines(W_merge)' + prose: false + - id: RDM-INV-002 + property: the printed contract count equals the census + formal: 'frontmatter(README.md).contract_count ≡ census.json.n_files' + prose: false + +falsification_tests: + - id: FALSIFY-RDM-001 + rule: an unrun claim is refused + prediction: > + adding a bash fence to README.md whose command no workflow runs makes `pv lint contracts/ --gate shapes` + exit 1 naming `readme:verifiedCommand unresolved: `; relabelling it `text` makes it pass again + test: cargo test -p aprender-contracts --lib ontology::extract::readme + if_fails: the README can advertise a command nothing has ever run diff --git a/contracts/realizar/binding.yaml b/contracts/realizar/binding.yaml new file mode 100644 index 0000000000..7e1ca8a206 --- /dev/null +++ b/contracts/realizar/binding.yaml @@ -0,0 +1,736 @@ +version: 1.0.0 +target_crate: realizar +bindings: +- contract: forward-pass-v1.yaml + equation: rms_norm + module_path: realizar::gguf::ops + function: rms_norm_unit_offset + status: partial + notes: '#4369: #[contract] site at crates/aprender-serve/src/gguf/ops.rs:128 had no binding row, and contracts/forward-pass-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: gemm-v1.yaml + equation: flattened_batched_gemm + module_path: realizar::gguf::inference::cached::single_flattened_batched_owned + function: flattened_batched_gemm + status: partial + notes: '#4369: #[contract] site at crates/aprender-serve/src/gguf/inference/cached/single_flattened_batched_owned.rs:19 had no binding row, and contracts/gemm-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: gpu-multi-backend-parity-v1.yaml + equation: multi_backend_parity + module_path: realizar::infer::batch_wgpu + function: generate + status: implemented + notes: '#4369: #[contract] site at crates/aprender-serve/src/infer/batch_wgpu.rs:44 had no binding row; row added when the binding moved in-tree' +- contract: gpu-weight-residency-v1.yaml + equation: pcie_overhead + module_path: realizar::infer::batch_wgpu + function: try_init_wgpu_batch + status: implemented + notes: '#4369: #[contract] site at crates/aprender-serve/src/infer/batch_wgpu.rs:129 had no binding row; row added when the binding moved in-tree' +- contract: sampling-v1.yaml + equation: softmax_inplace + module_path: realizar::gguf::ops + function: softmax + status: partial + notes: '#4369: #[contract] site at crates/aprender-serve/src/gguf/ops.rs:347 had no binding row, and contracts/sampling-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: wgpu-forward-pass-v1.yaml + equation: dequant_correctness + module_path: realizar::gpu::adapters::wgpu_adapter + function: dequant_model_weights + status: partial + notes: '#4369: #[contract] site at crates/aprender-serve/src/gpu/adapters/wgpu_adapter.rs:52 had no binding row, and contracts/wgpu-forward-pass-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: matmul-kernel-v1.yaml + equation: quantized_dot + module_path: realizar::quantize::fused_k + function: fused_q4k_dot + signature: 'fn fused_q4k_dot(q4k_data: &[u8], activations: &[f32]) -> Result' + status: implemented + notes: Scalar, AVX2, AVX-512 VNNI variants; also Q6K, Q8_0 +- contract: flash-attention-v1.yaml + equation: flash_attention + module_path: realizar::gguf::inference::attention_part_02 + function: flash_attention_tiled + signature: 'fn flash_attention_tiled(q: &[f32], k: &[f32], v: &[f32], ...) -> Vec' + status: implemented + notes: CPU tiled + CUDA flash attention with causal masking +- contract: attention-kernel-v1.yaml + equation: attention + module_path: realizar::apr::helpers + function: simple_attention + signature: 'fn simple_attention(q: &[f32], k: &[f32], v: &[f32], seq_len: usize, num_heads: usize, num_kv_heads: usize, + head_dim: usize) -> Vec' + status: implemented + notes: Multi-sequence causal attention; GQA-aware with head broadcasting +- contract: gqa-kernel-v1.yaml + equation: gqa + module_path: realizar::apr::helpers + function: simple_attention + signature: 'fn simple_attention(q: &[f32], k: &[f32], v: &[f32], seq_len: usize, num_heads: usize, num_kv_heads: usize, + head_dim: usize) -> Vec' + status: implemented + notes: GQA with head broadcasting (num_heads/num_kv_heads grouping) +- contract: gated-delta-net-v1.yaml + equation: decay + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_decay + signature: 'fn gated_delta_net_decay(a_log: &[f32], dt: &[f32]) -> Vec' + status: implemented + notes: decay = exp(g_h), S *= decay +- contract: gated-delta-net-v1.yaml + equation: read + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_read + signature: 'fn gated_delta_net_read(state: &[f32], q: &[f32]) -> Vec' + status: implemented + notes: 'State read: mem = S^T @ k_norm' +- contract: gated-delta-net-v1.yaml + equation: write + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_write + signature: 'fn gated_delta_net_write(state: &mut [f32], k: &[f32], delta: &[f32], decay: &[f32])' + status: implemented + notes: 'State write: S += k (x) delta' +- contract: gated-delta-net-v1.yaml + equation: delta + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_delta + signature: 'fn gated_delta_net_delta(v: &[f32], read: &[f32], beta: &[f32]) -> Vec' + status: implemented + notes: 'Delta rule: delta = beta * (v - mem)' +- contract: gated-delta-net-v1.yaml + equation: output + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_output + signature: 'fn gated_delta_net_output(y: &[f32], z: &[f32]) -> Vec' + status: implemented + notes: 'Output: o = S^T @ q, then rms_norm_gated' +- contract: rope-extrapolation-v1.yaml + equation: ntk_scaled_base + module_path: realizar::layers::position + function: ScaledRope::new + signature: 'fn new(config: RopeConfig) -> Self' + status: implemented + notes: RopeScalingType::Ntk and DynamicNtk +- contract: rope-extrapolation-v1.yaml + equation: linear_interpolation + module_path: realizar::layers::position + function: ScaledRope::new + signature: 'fn new(config: RopeConfig) -> Self' + status: implemented + notes: RopeScalingType::Linear; context_multiplier tested +- contract: rope-extrapolation-v1.yaml + equation: yarn_ramp + module_path: realizar::layers::position + function: ScaledRope::new + signature: 'fn new(config: RopeConfig) -> Self' + status: implemented + notes: RopeScalingType::Yarn with ramp function +- contract: rope-extrapolation-v1.yaml + equation: yarn_mixed_frequency + module_path: realizar::layers::position + function: ScaledRope::new + signature: 'fn new(config: RopeConfig) -> Self' + status: implemented + notes: YaRN mixed-frequency applied via ScaledRope::apply() +- contract: inference-pipeline-v1.yaml + equation: prefill_phase + module_path: realizar::gguf::inference::forward::batch_part_02_part_03 + function: prefill_batch + signature: 'fn prefill_batch(model: &mut Model, tokens: &[u32], ...) -> Result>' + status: implemented + notes: Batched and chunked prefill; 8.2x speedup over serial +- contract: inference-pipeline-v1.yaml + equation: decode_step + module_path: realizar::gguf::inference::forward + function: Model::forward + signature: 'fn forward(&mut self, token: u32, position: usize) -> Result>' + status: implemented + notes: Single-token forward pass; decode is the default path after prefill +- contract: kv-cache-equivalence-v1.yaml + equation: prefill_incremental + module_path: realizar::gguf::inference::forward::batch_part_02_part_03 + function: prefill_batch + signature: 'fn prefill_batch(model: &mut Model, tokens: &[u32], ...) -> Result>' + status: implemented + notes: KV cache populated incrementally during batched prefill +- contract: kv-cache-equivalence-v1.yaml + equation: batched_serial_equivalence + module_path: realizar::gguf::inference::forward + function: generate_with_batched_prefill + signature: fn generate_with_batched_prefill(...) -> Result> + status: implemented + notes: Batched prefill produces same KV cache as serial +- contract: kv-cache-equivalence-v1.yaml + equation: fused_kernel + module_path: realizar::cuda::executor::kv_cache_part_02_part_03 + function: flash_attention_cached + signature: 'fn flash_attention_cached(q: &CudaSlice, k_cache: &CudaSlice, v_cache: &CudaSlice, ...) -> Result<()>' + status: implemented + notes: CUDA flash attention with fused KV cache read +- contract: qwen35-hybrid-forward-v1.yaml + equation: gdn_sublayer + module_path: realizar::gpu::scheduler::linear_attn + function: gated_delta_net_forward + signature: 'fn gated_delta_net_forward(input: &[f32], weights: &GdnWeights, state: &mut GdnState) -> Vec' + status: implemented + notes: pre-norm -> projections -> causal Conv1D -> SiLU -> gated_delta_rule_step -> RMSNorm_gated -> out_proj -> residual +- contract: qwen35-shapes-v1.yaml + equation: o_projection_transpose + module_path: realizar::gguf::config + function: ArchConstraints::from_gguf_metadata + signature: 'fn from_gguf_metadata(metadata: &GgufMetadata) -> ArchConstraints' + status: implemented + notes: O projection shape [hidden, n_h * d_k] validated by ArchConstraints +- contract: architecture-requirements-v1.yaml + equation: gguf_loader_validation + module_path: realizar::cuda::executor::weights + function: CudaExecutor::build_indexed_weights + signature: 'fn build_indexed_weights(&mut self, num_layers: usize, layer_prefix_fn: F, arch: &ArchConstraints) -> Result<(), + GpuError>' + status: implemented + notes: GGUF loader path; validates via ValidatedLayerWeights::validate() +- contract: architecture-requirements-v1.yaml + equation: apr_loader_validation + module_path: realizar::apr::cuda_part_02_part_03 + function: AprV2ModelCuda::upload_quantized_weights_to_gpu + signature: fn upload_quantized_weights_to_gpu(&mut self) -> Result + status: implemented + notes: APR loader path; derives ArchConstraints from metadata +- contract: architecture-requirements-v1.yaml + equation: safetensors_loader_validation + module_path: realizar::safetensors_cuda + function: SafeTensorsCudaModel::upload_weights + signature: fn upload_weights(...) -> Result<(Vec, ...)> + status: implemented + notes: SafeTensors loader; validates completeness + model_load_basic before GPU init +- contract: ptx-target-parity-v1.yaml + equation: target_parity + module_path: realizar::cuda::gpu_profile + function: GpuProfile::detect + signature: 'fn detect(context: &CudaContext) -> GpuProfile' + status: implemented + notes: sm_target set from context.compute_capability() at executor init +- contract: ptx-target-parity-v1.yaml + equation: no_hardcoded_targets + module_path: realizar::cuda::kernel_generator + function: CudaKernels::generate_ptx + signature: 'fn generate_ptx(&self, kernel_type: &KernelType) -> String' + status: implemented + notes: All PTX generation uses self.sm_target — no emit_ptx() calls in executor/ +- contract: ptx-target-parity-v1.yaml + equation: jit_compilation_success + module_path: realizar::cuda::executor::core + function: CudaExecutor::compile_ptx + signature: 'fn compile_ptx(&self, ptx: &str) -> Result' + status: implemented + notes: PTX JIT with blocklist; target parity ensures CUDA_SUCCESS +- contract: fused-qkv-projection-v1.yaml + equation: shared_q8_qkv + module_path: realizar::cuda::executor::q4k_mwv_gemv + function: CudaExecutor::batched_qkv_dp4a_q4k_gemv_into + signature: 'fn batched_qkv_dp4a_q4k_gemv_into(&mut self, q_w: u64, k_w: u64, v_w: u64, input: &GpuBuffer, q_out: &GpuBuffer, + k_out: &GpuBuffer, v_out: &GpuBuffer, m: u32, q_dim: u32, kv_dim: u32, k: u32) -> Result<(), GpuError>' + status: implemented + notes: 'PMAT-054A: Q8_1 quantize once, 3x GEMV reuse. Saves 56 kernel launches per token.' +- contract: paged-kv-cache-v1.yaml + equation: slot_mapping + module_path: realizar::cuda::executor::kv_cache + function: PagedKvCache::slot + signature: 'fn slot(&self, req: usize, pos: usize) -> usize' + status: implemented + notes: 'PMAT-052: Requires block table allocator. Currently stride-based.' +- contract: paged-kv-cache-v1.yaml + equation: block_allocation + module_path: realizar::cuda::executor::kv_cache + function: BlockAllocator::allocate + signature: 'fn allocate(&mut self, num_blocks: usize) -> Result, GpuError>' + status: implemented + notes: 'PMAT-052: Free list block allocator. Currently contiguous allocation.' +- contract: paged-kv-cache-v1.yaml + equation: paged_contiguous_equivalence + module_path: realizar::cuda::executor::attention + function: paged_attention_into + signature: 'fn paged_attention_into(&self, q: &GpuBuffer, block_table: &GpuBuffer, ...) -> Result<(), GpuError>' + status: implemented + notes: 'PMAT-053: Paged attention kernel. Currently incremental_attention_into.' +- contract: continuous-batching-v1.yaml + equation: token_budget + module_path: realizar::api::cuda_batch_scheduler + function: CudaBatchScheduler::schedule_step + signature: fn schedule_step(&mut self) -> SchedulerOutput + status: implemented + notes: 'PMAT-051: Token budget scheduler. Currently 10ms accumulation window.' +- contract: continuous-batching-v1.yaml + equation: chunked_prefill + module_path: realizar::cuda::executor::layers::prefill + function: CudaExecutor::chunked_prefill + signature: 'fn chunked_prefill(&mut self, tokens: &[u32], chunk_size: usize) -> Result<(), GpuError>' + status: implemented + notes: 'PMAT-051: Chunked prefill to avoid blocking decode. Currently full prefill.' +- contract: continuous-batching-v1.yaml + equation: correctness_under_batching + module_path: realizar::gguf::cuda::generate_batched_streaming + function: generate_batched_streaming + signature: 'fn generate_batched_streaming(&mut self, slots: &[BatchSlot]) -> Result<(), GpuError>' + status: implemented + notes: 'CORRECTNESS-013: FALSIFIED. c=4 slots 1-3 produce constant tokens per step. + + Confirmed in BOTH HGEMM (token 585) and DP4A (tokens 198/88648/198) paths. + + Root cause in common path: prefill_multi_prompt → decode_batched transition. + + PMAT-070: Fix batched decode frozen slots. PMAT-071: Wire FALSIFY-CB-006. + + Instrument: probador llm test c=1 vs c=4, X-Trace-Level: brick, PMAT051_TRACE=1. + + ' +- contract: tensor-inventory-v1.yaml + equation: tensor_count + module_path: realizar + function: tensor_count + status: implemented + notes: Auto-inferred by pv infer +- contract: q4k-q6k-superblock-v1.yaml + equation: total_bytes + module_path: realizar + function: total_bytes + status: implemented + notes: Auto-inferred by pv infer +- contract: int8-symmetric-quant-v1.yaml + equation: quantize + module_path: realizar + function: quantize + status: implemented + notes: Auto-inferred by pv infer +- contract: matmul-kernel-v1.yaml + equation: matmul + module_path: realizar + function: matmul + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: relu + module_path: realizar + function: relu + status: implemented + notes: Auto-inferred by pv infer +- contract: silu-kernel-v1.yaml + equation: sigmoid + module_path: realizar + function: sigmoid + status: implemented + notes: Auto-inferred by pv infer +- contract: backend-dispatch-v1.yaml + equation: gpu_threshold + module_path: realizar + function: gpu_threshold + status: implemented + notes: Auto-inferred by pv infer +- contract: transpose-kernel-v1.yaml + equation: transpose + module_path: realizar + function: transpose + status: implemented + notes: Auto-inferred by pv infer +- contract: softmax-kernel-v1.yaml + equation: softmax + module_path: realizar + function: softmax + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: gelu + module_path: realizar + function: gelu + status: implemented + notes: Auto-inferred by pv infer +- contract: sliding-window-attention-v1.yaml + equation: effective_context + module_path: realizar + function: effective_context + status: implemented + notes: Auto-inferred by pv infer +- contract: kmeans-kernel-v1.yaml + equation: update + module_path: realizar + function: update + status: implemented + notes: Auto-inferred by pv infer +- contract: gqa-kernel-v1.yaml + equation: gqa + module_path: realizar + function: gqa + status: implemented + notes: Auto-inferred by pv infer +- contract: model-config-algebra-v1.yaml + equation: bounds + module_path: realizar + function: bounds + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: silu + module_path: realizar + function: silu + status: implemented + notes: Auto-inferred by pv infer +- contract: learned-position-embedding-v1.yaml + equation: position_embedding + module_path: realizar + function: position_embedding + status: implemented + notes: Auto-inferred by pv infer +- contract: online-softmax-v1.yaml + equation: standard_softmax + module_path: realizar + function: standard_softmax + status: implemented + notes: Auto-inferred by pv infer +- contract: random-forest-v1.yaml + equation: predict + module_path: realizar + function: predict + status: implemented + notes: Auto-inferred by pv infer +- contract: flash-attention-v1.yaml + equation: flash_attention + module_path: realizar + function: flash_attention + status: implemented + notes: Auto-inferred by pv infer +- contract: rmsnorm-kernel-v1.yaml + equation: rmsnorm + module_path: realizar + function: rmsnorm + status: implemented + notes: Auto-inferred by pv infer +- contract: format-parity-v1.yaml + equation: element_count + module_path: realizar + function: element_count + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: greedy + module_path: realizar + function: greedy + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: top_k + module_path: realizar + function: top_k + status: implemented + notes: Auto-inferred by pv infer +- contract: sampling-algorithms-v1.yaml + equation: top_p + module_path: realizar + function: top_p + status: implemented + notes: Auto-inferred by pv infer +- contract: cma-es-kernel-v1.yaml + equation: sample + module_path: realizar + function: sample + status: implemented + notes: Auto-inferred by pv infer +- contract: rope-extrapolation-v1.yaml + equation: base_frequency + module_path: realizar::layers::position + function: RoPE::new + signature: 'pub fn new(dim: usize, base: f32) -> Result' + status: implemented + notes: 'PMAT-414: freq_i = theta^(-2i/d). Computes inv_freq table at init' +- contract: rope-extrapolation-v1.yaml + equation: rotation_matrix + module_path: realizar::quantize::rope + function: apply_rope_rotation_simd + signature: 'pub fn apply_rope_rotation_simd(x1: &mut [f32], x2: &mut [f32], cos_vals: &[f32], sin_vals: &[f32])' + status: implemented + notes: 'PMAT-414: 2x2 rotation [cos,-sin; sin,cos] on paired dimensions. SIMD path' +- contract: architecture-requirements-v1.yaml + equation: weight_completeness + module_path: realizar::contract_gate + function: validate_completeness + signature: 'fn validate_completeness(arch: &ArchConstraints, present: &[WeightRole], arch_name: &str) -> Result<(), ModelLoadError>' + status: implemented + notes: 'PMAT-414: Checks all required roles present for arch. Returns missing list' +- contract: architecture-requirements-v1.yaml + equation: role_mapping + module_path: realizar::arch_requirements_fallback + function: WeightRole::field_name + signature: 'pub const fn field_name(&self) -> &''static str' + status: implemented + notes: 'PMAT-414: Injective mapping WeightRole → IndexedLayerWeights field name' +- contract: architecture-requirements-v1.yaml + equation: constraint_matrix_exhaustiveness + module_path: realizar::arch_requirements_fallback + function: required_roles + signature: 'pub fn required_roles(arch: &ArchConstraints) -> &''static [WeightRole]' + status: implemented + notes: 'PMAT-414: 4 exhaustive arms for (has_qk_norm, has_bias) boolean pairs' +- contract: qwen35-hybrid-forward-v1.yaml + equation: attention_sublayer + module_path: realizar::gpu::scheduler::forward_from_model + function: forward_block_traced + signature: 'fn forward_block_traced(&mut self, input: &[f32], seq_len: usize, block_idx: usize) -> Result<(Vec, LayerActivation)>' + status: implemented + notes: 'PMAT-414: Full attention sublayer: pre-norm, QKV, RoPE, attention, O proj, residual' +- contract: qwen35-hybrid-forward-v1.yaml + equation: ffn_sublayer + module_path: realizar::quantize::quantize_rmsnorm_into + function: fused_swiglu_simd + signature: 'pub fn fused_swiglu_simd(gate: &mut [f32], up: &[f32])' + status: implemented + notes: 'PMAT-414: SwiGLU FFN kernel. gate *= sigmoid(gate) * up' +- contract: qwen35-hybrid-forward-v1.yaml + equation: hybrid_block + module_path: realizar::safetensors::safetensors_config + function: SafeTensorsConfig::is_hybrid_attention + signature: 'pub fn is_hybrid_attention(&self) -> bool' + status: implemented + notes: 'PMAT-414: Dispatches attention vs GDN per layer_types array' +- contract: qwen35-shapes-v1.yaml + equation: rope_frequency + module_path: realizar::gpu::simd_ops + function: rope_frequency_table + signature: 'fn rope_frequency_table(half_head: usize, head_dim: usize, theta: f32) -> Vec' + status: implemented + notes: 'PMAT-414: freq_i = 1/theta^(2i/head_dim). Used by GPU scheduler' +- contract: quantization-ordering-v1.yaml + equation: size_ordering + module_path: realizar::quantize::format_trait + function: QuantBlockFormat + signature: 'trait QuantBlockFormat { const SUPERBLOCK_BYTES: usize; const ELEMENTS_PER_SUPERBLOCK: usize; }' + status: implemented + notes: 'PMAT-414: Q4K 0.5625 < Q6K 0.8203 < Q8_0 1.0625 B/param' +- contract: fused-qkv-projection-v1.yaml + equation: fused_qkv + module_path: realizar::gpu::scheduler::model_forward_block_gpu + function: fused_qkv_projection + signature: 'pub fn fused_qkv_projection(&mut self, input: &[f32]) -> Result<(Vec, Vec, Vec)>' + status: implemented + notes: 'PMAT-408: Fused QKV projection. CUDA path: fused_qkv_into with DP4A shared Q8' +- contract: sampling-algorithms-v1.yaml + equation: temperature + module_path: realizar::gpu::scheduler::ops + function: sample_topk + signature: 'pub(super) fn sample_topk(logits: &[f32], temperature: f32, top_k: usize) -> usize' + status: implemented + notes: 'PMAT-408: Temperature scaling applied before top-k/top-p sampling' +- contract: bpe-tokenization-v1.yaml + equation: merge_rule + module_path: realizar::gguf::token + function: GgufTokenizer::merge_rules + signature: 'pub fn merge_rules(&self) -> Option>' + status: implemented + notes: 'PMAT-408: BPE merge pairs from GGUF metadata' +- contract: bpe-tokenization-v1.yaml + equation: encode + module_path: realizar::tokenizer + function: Tokenizer::encode + signature: 'pub fn encode(&self, text: &str) -> Vec' + status: implemented + notes: 'PMAT-408: BPE tokenization. Also GGUF path at gguf::token' +- contract: bpe-tokenization-v1.yaml + equation: decode + module_path: realizar::tokenizer + function: Tokenizer::decode + signature: 'pub fn decode(&self, token_ids: &[u32]) -> Result' + status: implemented + notes: 'PMAT-408: BPE detokenization. GPT-2 byte-level BPE for WGPU path' +- contract: continuous-batching-v1.yaml + equation: request_state + module_path: realizar::scheduler::mod_max_scheduler + function: SlotState + signature: 'pub enum SlotState { Idle, Processing, Generating }' + status: implemented + notes: 'PMAT-405: Slot state machine. Also CommandSlotState for GPU path' +- contract: continuous-batching-v1.yaml + equation: scheduling_fairness + module_path: realizar::scheduler::mod_max_scheduler + function: handle_preemption + signature: 'fn handle_preemption(&mut self, output: &mut SchedulerOutput, kv_cache: &mut PagedKvCache)' + status: implemented + notes: 'PMAT-405: Preemption logic for fairness under load' +- contract: inference-pipeline-v1.yaml + equation: residual_stream + module_path: realizar::apply + function: forward_layer + signature: 'fn forward_layer(&mut self, layer_idx: usize, hidden: &[f32]) -> Result>' + status: implemented + notes: 'PMAT-405: hidden += attn_proj, residual += ffn_out. Also GPU PTX residualadd kernel' +- contract: inference-pipeline-v1.yaml + equation: layer_composition + module_path: realizar::gpu::adapters::using + function: forward_layer + signature: 'fn forward_layer(&self, executor: &mut CudaExecutor, input: &GpuBuffer, layer_idx: usize, seq_len: usize) -> Result>' + status: implemented + notes: 'PMAT-405: Chained forward through num_layers. CPU and GPU paths' +- contract: inference-pipeline-v1.yaml + equation: hybrid_layer_schedule + module_path: realizar::safetensors::safetensors_config + function: SafeTensorsConfig::is_hybrid_attention + signature: 'pub fn is_hybrid_attention(&self) -> bool' + status: implemented + notes: 'PMAT-405: GDN/attention dispatch per layer_types array (Qwen3.5 MoE)' +- contract: inference-pipeline-v1.yaml + equation: kv_cache_growth + module_path: realizar::verification_specs + function: verify_kv_cache_size + signature: 'fn verify_kv_cache_size(num_layers: u64, num_heads: u64, head_dim: u64, max_seq_len: u64) -> u64' + status: implemented + notes: 'PMAT-405: KV cache = 2 * layers * seq_len * kv_dim * 4 bytes' +- contract: q4k-q6k-superblock-v1.yaml + equation: q4k_superblock + module_path: realizar::quantize::format_trait + function: Q4K + signature: 'impl QuantBlockFormat for Q4K { ELEMENTS_PER_SUPERBLOCK: 256, SUPERBLOCK_BYTES: 144, QUANT_BITS: 4 }' + status: implemented + notes: 'PMAT-405: 144 bytes/256 elements. 12-byte packed scales+mins' +- contract: q4k-q6k-superblock-v1.yaml + equation: q6k_superblock + module_path: realizar::quantize::format_trait + function: Q6K + signature: 'impl QuantBlockFormat for Q6K { ELEMENTS_PER_SUPERBLOCK: 256, SUPERBLOCK_BYTES: 210, QUANT_BITS: 6 }' + status: implemented + notes: 'PMAT-405: 210 bytes/256 elements. Higher precision for LmHead' +- contract: q4k-q6k-superblock-v1.yaml + equation: dequantization + module_path: realizar::quantize::format_trait + function: Q4K::dequant_value + signature: 'fn dequant_value(superblock: &[u8], i: usize) -> f32' + status: implemented + notes: 'PMAT-405: Per-element Q4K→FP32. Also fused_q4k_dot for dot+dequant' +- contract: q4k-q6k-superblock-v1.yaml + equation: bsum + module_path: realizar::quantize::bsum_precompute + function: precompute_q8k_bsums + signature: 'pub fn precompute_q8k_bsums(q8k_quants: &[i8], num_superblocks: usize) -> Result>' + status: implemented + notes: 'PMAT-405: Block sums for DP4A correction term. 8 sub-blocks per superblock' +- contract: roofline-model-v1.yaml + equation: model_bytes + module_path: realizar::lambda + function: model_size_bytes + signature: 'pub fn model_size_bytes(&self) -> usize' + status: implemented + notes: 'PMAT-405: Total weight bytes for roofline BW calculation' +- contract: roofline-model-v1.yaml + equation: compute_ceiling + module_path: realizar::brick::fused + function: CoalescedDp4aBrick::arithmetic_intensity + signature: 'pub fn arithmetic_intensity(&self) -> f64' + status: implemented + notes: 'PMAT-405: FLOP/byte ratio for roofline compute bound' +- contract: format-parity-v1.yaml + equation: transpose_involution + module_path: realizar::contract_gate + function: transpose_f32 + signature: 'pub fn transpose_f32(data: &[f32], rows: usize, cols: usize) -> Vec' + status: implemented + notes: 'PMAT-405: transpose(transpose(x)) == x. Delegates to trueno::blis::transpose' +- contract: format-parity-v1.yaml + equation: name_bijection + module_path: realizar::tensor_names + function: resolve_global + signature: 'pub(crate) fn resolve_global(source: &S, arch: &str, role: GlobalTensorRole) -> Result>' + status: implemented + notes: 'PMAT-405: Weight name mapping across GGUF/SafeTensors/APR formats' +- contract: backend-dispatch-v1.yaml + equation: simd_only_threshold + module_path: realizar::quantize::fused_k + function: fused_q4k_dot_simd + signature: 'pub fn fused_q4k_dot_simd(q4k_data: &[u8], activations: &[f32]) -> Result' + status: implemented + notes: 'PMAT-405: Runtime AVX2 detection + scalar fallback. 5x PCIe rule for CPU vs GPU' +- contract: backend-dispatch-v1.yaml + equation: garbage_oracle + module_path: realizar::inference_trace::mod_get_top_compute + function: is_garbage_output + signature: 'fn is_garbage_output(text: &str) -> bool' + status: implemented + notes: 'PMAT-405: Detects garbled output from quantization errors' +- contract: backend-dispatch-v1.yaml + equation: qk_norm_score_bound + module_path: realizar::cuda::executor::layers::apply + function: apply_qk_norm + signature: 'fn apply_qk_norm(&mut self, q_buf: &GpuBuffer, k_buf: &GpuBuffer, layer_weights: &ValidatedLayerWeights, epsilon: f32) -> Result<(), GpuError>' + status: implemented + notes: 'PMAT-405: QK normalization bounds attention scores. CPU and GPU paths' +- contract: kv-cache-equivalence-v1.yaml + equation: page_shape + module_path: realizar::paged_kv::mod_paged + function: PagedKvCache::new + signature: 'pub fn new(total_pages: usize, block_size: usize, num_heads: usize, head_dim: usize) -> Self' + status: implemented + notes: 'PMAT-405: KvPage shape [block_size, num_heads, head_dim]. tokens_to_pages rounding' +- contract: gpu-context-health-v1.yaml + equation: fp8_architecture_guard + module_path: realizar::cuda::gpu_profile + function: detect_fp8_prefill + signature: 'fn detect_fp8_prefill(cc: u32) -> bool' + status: implemented + notes: 'PMAT-404: cc >= 89 && cc < 100. Blackwell (cc>=121) excluded.' +- contract: gpu-context-health-v1.yaml + equation: context_health + module_path: realizar::cuda::executor::layers::cublas_prefill::attention + function: CudaExecutor::warmup_fp8_cache + signature: 'pub(crate) fn warmup_fp8_cache(&mut self, num_layers: usize, hidden_dim: u32, ...)' + status: implemented + notes: 'PMAT-404: No-op when cc >= 100. Prevents context poisoning on Blackwell.' +- contract: gpu-context-health-v1.yaml + equation: cuda_graph_guard + module_path: realizar::cuda::executor::layers::graphed_capture + function: try_graph_capture + signature: 'fn try_graph_capture(&mut self) -> bool' + status: implemented + notes: 'PMAT-404: Opt-IN via CUDA_GRAPH_ENABLE=1. ThreadLocal capture mode (PMAT-374).' +- contract: gpu-weight-residency-v1.yaml + equation: throughput_target + module_path: realizar::cuda::executor::weights + function: CudaExecutor::load_weights + signature: 'pub fn load_weights(&mut self, name: &str, weights: &[f32]) -> Result' + status: implemented + notes: 'PMAT-404: Pre-uploads all weights at startup. Uses from_host_registered on cc>=120.' +- contract: gpu-decode-profiling-v1.yaml + equation: graph_disable + module_path: realizar::cuda::executor::layers::graphed_capture + function: try_graph_capture + signature: 'fn try_graph_capture(&mut self) -> bool' + status: implemented + notes: 'PMAT-403: CUDA_GRAPH_ENABLE=1 required. Profiling requires graphs OFF (bricks only measured during capture, not replay)' +- contract: gpu-decode-profiling-v1.yaml + equation: report_fidelity + module_path: realizar::cuda::executor::executor_api + function: ExecutorApi::start_brick_id + signature: 'pub(crate) fn start_brick_id(&mut self, brick_id: BrickId) -> Option' + status: implemented + notes: 'PMAT-403: JSON actual_us must equal profiler per-call avg. start/stop_brick_id provide ground truth' +- contract: gpu-decode-profiling-v1.yaml + equation: report_completeness + module_path: realizar::cuda::executor::executor_api + function: ExecutorApi::stop_brick_id + signature: 'pub(crate) fn stop_brick_id(&mut self, timer: Option, elements: u64)' + status: implemented + notes: 'PMAT-403: Every profiler brick must appear in output. No silent truncation' +- contract: gpu-decode-profiling-v1.yaml + equation: report_denominator + module_path: realizar::cuda::executor::executor_api + function: ExecutorApi::start_brick_id + signature: 'pub(crate) fn start_brick_id(&mut self, brick_id: BrickId) -> Option' + status: implemented + notes: 'PMAT-403: decoded_tokens = LmHead.count (exactly 1 LmHead per decoded token). Never use profiler.total_tokens' +- contract: gpu-decode-profiling-v1.yaml + equation: report_metadata + module_path: realizar::cuda::executor::executor_api + function: ExecutorApi::start_brick_id + signature: 'pub(crate) fn start_brick_id(&mut self, brick_id: BrickId) -> Option' + status: implemented + notes: 'PMAT-403: metadata fields must be measured or zero. No hardcoded nonzero constants (137, 173.9, etc.)' +critical_path: +- forward +- flash_attention +- fused_q4k_dot +- sample +- kv_cache +- quantize +- dequantize diff --git a/contracts/refusal-receipt-v1.yaml b/contracts/refusal-receipt-v1.yaml index f0d98bc34c..4e9cbb4746 100644 --- a/contracts/refusal-receipt-v1.yaml +++ b/contracts/refusal-receipt-v1.yaml @@ -171,3 +171,14 @@ falsification_tests: prediction: an entry with no exit_code, or a one-word reason, is refused test: cargo test -p aprender-contracts-cli --test ont_refusal_receipt if_fails: a refusal is indistinguishable from success to anything not reading prose + +# ONT-8: one evidence block — PROV-O names, one L-enum (ProofLevel). The level is MEASURED, not declared: it is what +# the cited command printed for this contract at the time below. Checked by `pv lint --gate evidence`. +evidence: + level: L1 + mark: C + provenance: + wasGeneratedBy: + command: "pv proof-status contracts/ --format json" + wasAttributedTo: pv + generatedAtTime: "2026-09-24T08:22:07Z" diff --git a/contracts/release-readiness-v1.yaml b/contracts/release-readiness-v1.yaml index bf8bc8d8db..a4e1c02beb 100644 --- a/contracts/release-readiness-v1.yaml +++ b/contracts/release-readiness-v1.yaml @@ -25,7 +25,14 @@ # model's measured length. Kernels = every (kernel, quant) on ANY required host's dispatch path, owed on EVERY # required host (each GPU arch). # -# THE FAMILY. `--shape release-readiness-v1` grades these nine shapes, armed whatever `armed_shapes` says, and +# DERIVED CELLS (#3745 S2, #3777). The verb axis is no longer a list: the cells are derived from the release +# candidate's own `apr surface --json` (--surface): per (model command, model, host) a deterministic strength-2 +# covering array over its mode args ∪ {input shape} ∪ (when the surface's typed `generates` says so) {thinking, +# rung}; a model command that does not generate owes `release:ModelCell`s; a command with no model owes one +# `release:ProbeCell` per host; S2.5 adds one-factor-at-a-time `release:EffectCell`s judged by `release:ModeEffect`. +# Receipt rows key onto their cell by `cell_id` (`pv extract --cells-out` lists them). +# +# THE FAMILY. `--shape release-readiness-v1` grades these shapes, armed whatever `armed_shapes` says, and # reports them in this declaration order: release → host → context → model → coverage → tokenizer → kernel → # refusal → cell, so a tokenizer that disagrees with llama.cpp (#3726) or a kernel that diverges across arches is # named before any model cell it breaks (#3715 addenda). @@ -74,6 +81,9 @@ entity: relations: depends_on: [ont-shapes-v1, ont-model-receipts-v1, model-capability-ladder-v1] +# #3610 exemptions, by shape id. Here and not on the shape: the fleet-pinned pv refuses an unknown shape key. +allow_empty: + release-readiness-v1.refusal: "a RefusalCell exists only where a cell does not fit its host; a release where every cell fits has none (#3610)" shapes: - id: release-readiness-v1.release targetClass: release:Release @@ -84,6 +94,12 @@ shapes: - {path: release:dogfoodReceipt, minCount: 1, maxCount: 1, nodeKind: IRI, node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [GO]}, {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # #3745 S2: the cells are DERIVED from the release candidate's own surface — no surface, no cells, no pass + - {path: release:surface, minCount: 1, maxCount: 1, nodeKind: IRI, class: cli:Surface} + - {path: release:generatesUndeclared, maxCount: 0} + # the two shrink-only counts (args no role type claims; stdin forms v1 cannot declare) never grow + - {path: release:ratchetGrew, maxCount: 0} + - {path: release:ratchetBaselineMissing, maxCount: 0} - id: release-readiness-v1.host targetClass: release:Host properties: @@ -140,6 +156,39 @@ shapes: {path: release:withinBound, minCount: 1, maxCount: 1, in: [true]}, {path: release:boundSource, minCount: 1}, {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # S2.4 (#3739): every derived verb has a CRUX entry naming every declared engine — a counterpart or `none` + reason + - id: release-readiness-v1.crux-verb + targetClass: release:CruxVerb + properties: + - {path: release:mappingEntry, minCount: 1, maxCount: 1} + - {path: release:engineMissing, maxCount: 0} + - {path: release:noneWithoutReason, maxCount: 0} + # ... and every (host, model, mapped verb, thinking, rung) was compared: RED, UNJUDGED and absent are violations; + # ALL_WRONG is named, not a violation, pending the cop's ruling + - id: release-readiness-v1.crux + targetClass: release:CruxObligation + properties: + # a verb whose correspondence entry declares request modes owes a :CruxCell in each (#3739 slice 4) + - {path: release:modeMissing, maxCount: 0} + - {path: release:cruxCell, minCount: 1, nodeKind: IRI, + node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [GREEN, ALL_WRONG]}, + {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # S2.5, the flag-effect oracle: every (command, mode arg, level) is OBSERVED to change the output somewhere — + # an effect cell's output digest differs from its base's (#3745 issuecomment-5765674584) + - id: release-readiness-v1.effect + targetClass: release:ModeEffect + properties: + - {path: release:observedIn, minCount: 1, nodeKind: IRI} + # S2.5, a8's sampling controls over typed `sampling` args: sampled ≠ greedy, the same seed ×2 identical, seed A ≠ + # seed B, top-k 1 == T 0; a control the command cannot express (no typed knob) is named, never passed + - id: release-readiness-v1.sampling + targetClass: release:SamplingCheck + properties: + - {path: release:sampledDiffers, maxCount: 1, in: [true]} + - {path: release:seedRepeatable, maxCount: 1, in: [true]} + - {path: release:seedsDiffer, maxCount: 1, in: [true]} + - {path: release:greedyAgrees, maxCount: 1, in: [true]} + - {path: release:underivable, maxCount: 0} # a cell that does not FIT the host is owed as an HONEST PRE-LOAD REFUSAL naming its arithmetic — never an OOM # mid-run, never a silent truncation, never a pass (#3710 rule) - id: release-readiness-v1.refusal @@ -149,6 +198,28 @@ shapes: node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [refused]}, {path: release:arithmeticNamed, minCount: 1, maxCount: 1, in: [true]}, {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # a command with no model arg: it runs, or refuses by name (#3745 design 3, absorbs #3597) + - id: release-readiness-v1.probe + targetClass: release:ProbeCell + properties: + - {path: release:row, minCount: 1, maxCount: 1, nodeKind: IRI, + node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [pass, refused]}, + {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # a model command that does not generate text (inspect-like): Pass, or a typed refusal by name + - id: release-readiness-v1.model-cell + targetClass: release:ModelCell + properties: + - {path: release:row, minCount: 1, maxCount: 1, nodeKind: IRI, + node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [pass, refused]}, + {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}]}} + # S2.5's one-factor-at-a-time cells: each must run, and say what it printed + - id: release-readiness-v1.effect-cell + targetClass: release:EffectCell + properties: + - {path: release:row, minCount: 1, maxCount: 1, nodeKind: IRI, + node: {properties: [{path: release:verdict, minCount: 1, maxCount: 1, in: [pass]}, + {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}, + {path: release:outputSha, minCount: 1, maxCount: 1}]}} - id: release-readiness-v1 targetClass: release:Cell properties: @@ -159,7 +230,9 @@ shapes: {path: release:fresh, minCount: 1, maxCount: 1, in: [true]}, {path: release:contextMet, minCount: 1, maxCount: 1, in: [true]}, {path: release:thinkOk, minCount: 1, maxCount: 1, in: [true]}, - {path: release:answered, minCount: 1, maxCount: 1, in: [true]}]}} + {path: release:answered, minCount: 1, maxCount: 1, in: [true]}, + # #3748: the CPU reference ran fresh, or came from THIS binary's own receipt + {path: release:f2Measured, minCount: 1, maxCount: 1, in: [true]}]}} equations: cell_universe: @@ -260,3 +333,14 @@ qa_gate: - no_subject_declines pass_criteria: All 3 falsification tests pass; `pv lint contracts --gate shapes --shape release-readiness-v1 --release-version V --release-commit MC --receipts DIR --dogfood-receipt FILE` exits 0 only when every cell passes falsification: 'flip one cell row of tests/fixtures/ont/release-green to verdict skip → the gate exits 1 naming that cell' + +# ONT-8: one evidence block — PROV-O names, one L-enum (ProofLevel). The level is MEASURED, not declared: it is what +# the cited command printed for this contract at the time below. Checked by `pv lint --gate evidence`. +evidence: + level: L1 + mark: C + provenance: + wasGeneratedBy: + command: "pv proof-status contracts/ --format json" + wasAttributedTo: pv + generatedAtTime: "2026-09-24T08:22:07Z" diff --git a/contracts/repo-filesystem-v1.yaml b/contracts/repo-filesystem-v1.yaml index 86bbf91a4d..2965703e98 100644 --- a/contracts/repo-filesystem-v1.yaml +++ b/contracts/repo-filesystem-v1.yaml @@ -102,7 +102,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 6 + total_obligations: 1 proven: 0 tested: 0 status: pending diff --git a/contracts/review-corpus-contamination-v1.yaml b/contracts/review-corpus-contamination-v1.yaml new file mode 100644 index 0000000000..790babea85 --- /dev/null +++ b/contracts/review-corpus-contamination-v1.yaml @@ -0,0 +1,38 @@ +metadata: + version: 1.0.0 + kind: schema + description: | + PROMETHEUS (PRM-001) sealed-test contamination check, spec §2.2 and rule R-2. A sealed + test item leaks when a training file carries its diff sha256 literally, or any of + its hunks (header stripped, >= 4 lines, at least one change) — including inside + JSON string values, so a diff stored in a JSONL field is seen unescaped. Training + roots: docs/audits/review-corpus/train/ (contamination::TRAIN_ROOTS). Any hit is + a hard FAIL (S-3). + references: + - "docs/specifications/review-experiment-protocol.md" + - "docs/audits/review-corpus/test-manifest-v1.txt" + - "https://github.com/paiml/aprender/issues/4357" + +equations: + leak: + formula: "leak(f) = exists t in sealed: t.diff_sha256 in text(f) or hunks(t) ∩ hunks(text(f) ∪ json_strings(f)) != {}" + +falsification_tests: + - id: FALSIFY-RCC-001 + name: planted_jsonl_leak_is_caught + prediction: "a sealed diff inside a JSONL training record is a hit" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rcc_001" + expected_output: "exit 0" + if_fails: "Escaped diffs in JSONL pass the check; B2 teacher sets could train on test items." + - id: FALSIFY-RCC-002 + name: rebased_copy_and_sha_literal_leak + prediction: "a copy with other hunk line numbers, and a bare diff sha, are hits; an unrelated diff is not" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rcc_002" + expected_output: "exit 0" + if_fails: "The check keys on line numbers or misses sha references." + - id: FALSIFY-RCC-003 + name: train_roots_are_clean + prediction: "no file under the training roots carries a sealed test item" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rcc_003" + expected_output: "exit 0" + if_fails: "S-3: a sealed test item is in training data. STOP; rotate the leaked items out." diff --git a/contracts/review-corpus-v1.yaml b/contracts/review-corpus-v1.yaml new file mode 100644 index 0000000000..b6bd17367a --- /dev/null +++ b/contracts/review-corpus-v1.yaml @@ -0,0 +1,44 @@ +metadata: + version: 1.0.0 + kind: schema + description: | + PROMETHEUS (PRM-001, was REX-001) review corpus v1, spec §2.2. 150 items: P (planted + cargo-mutants diffs, markers stripped), R (merged fix PRs with a linked issue, + code hunks reverse-applied), G (merged PRs before the 14-day cutoff, green + ci / gate + workspace-test, not reverted, no regression label). Strata on a + bytes/4 token proxy (S < 2000, M 2000–8000, L > 8000). Seeded (4354), stratified + 30/70 dev/test split. Item metadata: docs/audits/review-corpus/corpus-v1.jsonl. + Sealed test manifest: docs/audits/review-corpus/test-manifest-v1.txt (version + review-corpus-v1@787d2026256cc08b). Diffs: items-v1.tar (sha256 70dffe5625e1849661303b82bb1fdf5254ab1d4560e2fe3eacc147e5d2588bba), outside git. + references: + - "docs/specifications/review-experiment-protocol.md" + - "docs/audits/rex-001/rex-02-receipt.md" + - "https://github.com/paiml/aprender/issues/4357" + +equations: + stratum: + formula: "t = ceil(bytes/4); S if t < 2000, M if t <= 8000, else L" + split: + formula: "per (class, stratum): order by SplitMix64(4354) key in id order; first round(0.3 n) are dev" + corpus_version: + formula: "'review-corpus-v1@' || sha256(test-manifest-v1.txt)[0..16]" + +falsification_tests: + - id: FALSIFY-RCV-001 + name: mutation_markers_never_reach_a_prompt + prediction: "a sanitised P diff names neither cargo-mutants nor the replacement" + test_harness: "cargo test -p aprender-review-experiment --lib sanitize_removes_every_mutation_marker" + expected_output: "exit 0" + if_fails: "P items leak their answer; every P verdict is void." + - id: FALSIFY-RCV-002 + name: split_is_seeded_stratified_and_order_free + prediction: "the same seed gives the same split whatever the input order; a different seed moves it; each cell is 30% dev" + test_harness: "cargo test -p aprender-review-experiment --lib split_is_stratified_seeded_and_stable" + expected_output: "exit 0" + if_fails: "The split is not reproducible; the sealed manifest cannot be re-derived." + - id: FALSIFY-RCV-003 + name: comment_only_hunks_are_not_defects + prediction: "R items keep only hunks that change code; defect locations point at code lines" + test_harness: "cargo test -p aprender-review-experiment --lib comment_only_hunks_are_not_the_fix" + expected_output: "exit 0" + if_fails: "R labels name comments; localization is measured against the wrong line." diff --git a/contracts/review-experiment-receipt-v1.yaml b/contracts/review-experiment-receipt-v1.yaml new file mode 100644 index 0000000000..cc576d04b4 --- /dev/null +++ b/contracts/review-experiment-receipt-v1.yaml @@ -0,0 +1,54 @@ +metadata: + version: 1.0.0 + kind: schema + description: | + PROMETHEUS (PRM-001, was REX-001) receipt, spec §2.3/§2.4. One JSONL row per + (item, cell, arm) written by the REX-03 harness (crates/aprender-review-experiment: + harness.rs) against a resident `apr serve`. A row is admissible only if it + deserializes with every required field (no unknown fields), carries no empty or + `unknown` identity field, carries 64-hex shas for the item, prompt, request, prereg, + apr binary and weights (hosted arms say `hosted`), names the locked prereg sha and + the corpus version under analysis, is greedy, and, when it ran, carries token + counts, timings, raw-output path+sha and a load snapshot. The scorer (score.rs) + scores the EXPECTED item set: an item without exactly one admissible row whose raw + output exists with the recorded sha and re-parses to the recorded verdict is + NotRun{Inadmissible}. Unparsed and NotRun are never correct and never a FAIL (R-6). + Signed receipts are verified with minisign; unsigned results are labelled + exploratory. + references: + - "docs/specifications/review-experiment-protocol.md" + - "docs/audits/rex-001/analysis-plan.md" + - "docs/audits/rex-001/rex-03-receipt.md" + - "https://github.com/paiml/aprender/issues/4358" + +equations: + correct: + formula: "correct(item) = (verdict == FAIL and class in {P,R}) or (verdict == PASS and class == G)" + recall: + formula: "|FAIL on P∪R| / |P∪R expected| (NotRun and Unparsed stay in the denominator)" + precision: + formula: "|FAIL on P∪R| / |FAIL|" + false_refute: + formula: "|FAIL on G| / |G expected|" + parse_rate: + formula: "|PASS ∪ FAIL| / |executed|" + +falsification_tests: + - id: FALSIFY-RXR-001 + name: missing_model_sha_is_inadmissible + prediction: "a receipt without weights_sha256 (or with `unknown`, or a sha prefix) is inadmissible" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rxr_001" + expected_output: "exit 0" + if_fails: "Receipts can no longer be tied to the weights that produced them; every cross-cell and cross-model comparison is unanchored." + - id: FALSIFY-RXR-002 + name: not_run_is_never_correct + prediction: "NotRun (any reason, including a missing receipt) is never correct and never a FAIL; it stays in the recall and correctness denominators" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rxr_002" + expected_output: "exit 0" + if_fails: "A cell that cannot run, or a dropped receipt, would raise a score (R-6)." + - id: FALSIFY-RXR-003 + name: unparsed_is_never_a_pass + prediction: "Unparsed is never correct on a G item, never a FAIL on a defect, and never enters a paired rate" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rxr_003" + expected_output: "exit 0" + if_fails: "A model that answers garbage would score as a clean reviewer on good diffs." diff --git a/contracts/rex-cell-admission-v1.yaml b/contracts/rex-cell-admission-v1.yaml new file mode 100644 index 0000000000..a6d1ce2aca --- /dev/null +++ b/contracts/rex-cell-admission-v1.yaml @@ -0,0 +1,45 @@ +metadata: + version: 1.0.0 + kind: schema + description: | + PROMETHEUS (PRM-001, was REX-001) REX-04 per-cell admission, spec §2.1/§7. One JSONL + row per §2.1 device cell (C1 intel-wgpu, C2 intel-cpu, C3 lambda-cpu, C4 gx10-cuda, + C5a mini-cpu, C5b mini-metal) written by `rex admit` (crates/aprender-review-experiment: + admission.rs). A file is admissible only if every cell has exactly one row, each + row names its declared host and backend, carries the apr tag, the apr and weights + sha256, the model id and the locked prereg sha, and resolves to Admitted (a parity + receipt sha, an oracle, a cosine at or above a threshold with a cited basis), + Refused{removed_by}, or NotRun{reason}. A silent cell rejects the whole file. When + no cell is Admitted, the summary raises S-7 and `rex admission-check` exits 10. + references: + - "docs/specifications/review-experiment-protocol.md" + - "docs/audits/rex-001/rex-04-receipt.md" + - "https://github.com/paiml/aprender/issues/4359" + +equations: + resolved: + formula: "∀ c ∈ CELLS: |rows(c)| = 1 ∧ state(c) ∈ {Admitted, Refused, NotRun}" + admitted: + formula: "Admitted(c) ⇒ cosine(c) ≥ threshold(c) ∧ receipt_sha256(c) is 64-hex ∧ threshold_basis(c) ≠ ∅" + s7: + formula: "S-7 ⇔ |{c : Admitted(c)}| = 0" + +falsification_tests: + - id: FALSIFY-RCA-001 + name: a_silent_cell_rejects_the_file + prediction: "dropping any one of the six rows (or duplicating one) makes the admission file inadmissible" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rca_001" + expected_output: "exit 0" + if_fails: "A cell could vanish from the report without a refusal or NotRun on record (0 silent cells, §7 REX-04)." + - id: FALSIFY-RCA-002 + name: admitted_needs_a_passing_parity_receipt + prediction: "Admitted below threshold, with a NaN cosine, or without a receipt, basis or oracle is rejected; Refused without removed_by is rejected" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rca_002" + expected_output: "exit 0" + if_fails: "A cell that diverges from the oracle would be admitted into the hardware ruling." + - id: FALSIFY-RCA-003 + name: no_admitted_cell_raises_s7 + prediction: "a file whose cells are all Refused or NotRun is admissible and raises S-7" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rca_003" + expected_output: "exit 0" + if_fails: "The experiment would proceed to REX-05 with no admissible cell for (A)." diff --git a/contracts/rex-prereg-v1.yaml b/contracts/rex-prereg-v1.yaml new file mode 100644 index 0000000000..830a420991 --- /dev/null +++ b/contracts/rex-prereg-v1.yaml @@ -0,0 +1,39 @@ +metadata: + version: 1.0.0 + kind: schema + description: | + REX-001 pre-registration lock (rule R-1). Spec §2–§5, the analysis code + (crates/aprender-review-experiment/src/stats.rs), the analysis plan and prompt v1 + are frozen by sha256 in docs/audits/rex-001/prereg.lock. The prereg sha is carried + by every review-experiment receipt; a tree that no longer matches the lock is a + new spec version and its data is exploratory. + references: + - "docs/specifications/review-experiment-protocol.md" + - "docs/audits/rex-001/analysis-plan.md" + - "https://github.com/paiml/aprender/issues/4355" + +equations: + prereg_sha: + formula: "sha256('rex-prereg-v1\\n' || 'spec_s2_s5 ' || H(spec[§2..§6)) || '\\nstats_rs ' || H(stats.rs) || '\\nanalysis_plan ' || H(plan) || '\\nprompt_v1 ' || H(prompt) || '\\n')" + locked: + formula: "prereg.lock[k] == H(component_k) for every k, and prereg.lock[prereg_sha] == prereg_sha" + +falsification_tests: + - id: FALSIFY-REX-PREREG-001 + name: lock_matches_tree + prediction: "the committed lock matches every frozen component" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rex_prereg_001" + expected_output: "exit 0" + if_fails: "A frozen component changed after REX-00. That is a new spec version (R-1): label prior data exploratory and cut v2; never edit the lock to match." + - id: FALSIFY-REX-PREREG-002 + name: planted_section3_edit_is_caught + prediction: "an edit to §3 (hypotheses) changes the prereg sha" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rex_prereg_002" + expected_output: "exit 0" + if_fails: "The §2–§5 span no longer covers §3; the lock is vacuous." + - id: FALSIFY-REX-PREREG-003 + name: out_of_scope_edit_is_not_a_version + prediction: "an edit to the status line (outside §2–§5) leaves the prereg sha unchanged" + test_harness: "cargo test -p aprender-review-experiment --lib falsify_rex_prereg_003" + expected_output: "exit 0" + if_fails: "The span leaks past §6; routine status edits would invalidate data." diff --git a/contracts/rmsnorm-kernel-v1.yaml b/contracts/rmsnorm-kernel-v1.yaml index a709b49f19..0988adc3c1 100644 --- a/contracts/rmsnorm-kernel-v1.yaml +++ b/contracts/rmsnorm-kernel-v1.yaml @@ -221,7 +221,9 @@ falsification_tests: kani_harnesses: - id: KANI-RN-001 obligation: RN-INV-001 - property: Output always finite with ε > 0 + property: 'Output always finite with ε > 0, |x_i| ≤ 1e18 and |γ_i| ≤ 1e30. Past + |x| ~ 1e19 Σx² overflows and the output collapses to 0 (Kani flagged it via its sqrtf model, PMAT-3140); + |γ| > f32::MAX/√n overflows the output, and 1e30 is a conservative bound under it' bound: 16 strategy: exhaustive harness: verify_rmsnorm_finiteness diff --git a/contracts/shapes.ttl b/contracts/shapes.ttl index 667b5d4245..3ce0a65c53 100644 --- a/contracts/shapes.ttl +++ b/contracts/shapes.ttl @@ -2,6 +2,1519 @@ @prefix xsd: . @prefix ont: . + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 41 ; + sh:maxCount 41 ; + sh:in ( "DELETE /api/delete" "GET /" "GET /api/tags" "GET /api/version" "GET /health" "GET /health/live" "GET /health/ready" "GET /metrics" "GET /metrics/dispatch" "GET /models" "GET /ready" "GET /realize/model" "GET /tensors" "GET /v1/audit/:request_id" "GET /v1/effective-config" "GET /v1/gpu/status" "GET /v1/metrics" "GET /v1/models" "POST /api/chat" "POST /api/embeddings" "POST /api/generate" "POST /api/pull" "POST /api/show" "POST /batch/generate" "POST /batch/tokenize" "POST /generate" "POST /metrics/dispatch/reset" "POST /realize/batch" "POST /realize/embed" "POST /realize/generate" "POST /realize/reload" "POST /stream/generate" "POST /tokenize" "POST /v1/batch/completions" "POST /v1/chat/completions" "POST /v1/chat/completions/stream" "POST /v1/completions" "POST /v1/embeddings" "POST /v1/explain" "POST /v1/gpu/warmup" "POST /v1/predict" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 9 ; + sh:maxCount 9 ; + sh:in ( "apr.bench" "apr.finetune" "apr.qa" "apr.run" "apr.serve" "apr.tensors" "apr.trace" "apr.validate" "apr.version" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "apr" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "apr-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^apr [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 264 ; + sh:maxCount 264 ; + sh:in ( "attn-parity-lint" "attn-viz-lint" "audio-inspect-lint" "awq-lint" "beat-run" "bench" "canary check" "canary create" "capability" "cbtop" "cgp baseline" "cgp bench" "cgp compete" "cgp contract generate" "cgp contract verify" "cgp diff" "cgp doctor" "cgp explain" "cgp profile binary" "cgp profile compare" "cgp profile cublas" "cgp profile kernel" "cgp profile library" "cgp profile metal" "cgp profile parallel" "cgp profile python" "cgp profile quant" "cgp profile scalar" "cgp profile scaling" "cgp profile simd" "cgp profile wasm" "cgp profile wgpu" "cgp roofline" "cgp trace" "cgp tui" "chat" "check" "check-finite-lint" "code" "compare-hf" "compile" "convert" "data audit" "data balance" "data decontaminate" "data dedup" "data split" "data x convert" "data x dedup" "data x drift compare" "data x drift detect" "data x drift merge" "data x drift report" "data x drift sketch" "data x fed manifest" "data x fed plan" "data x fed split" "data x fed verify" "data x filter-text" "data x fim" "data x head" "data x import local" "data x info" "data x mix" "data x quality check" "data x quality profiles" "data x quality report" "data x quality score" "data x registry delete" "data x registry init" "data x registry list" "data x registry pull" "data x registry push" "data x registry search" "data x registry show-info" "data x repl" "data x schema" "data x view" "dataset audio-inspect" "ddp-metrics-lint" "debug" "debug embed-viz" "decrypt" "devices" "diagnose" "diff" "distill" "dry-sampling-lint" "embed" "embed-viz-lint" "embeddings-lint" "encrypt" "eval" "experiment view" "explain" "explain-token-lint" "export" "finetune" "flow" "fp8-lint" "gbnf-lint" "gptq-lint" "gpu" "gpu-memtrace-lint" "grad-norm" "hang-trace-lint" "hex" "imatrix-lint" "import" "inspect" "kernel parity" "kv-timeline-lint" "lint" "list" "manifest" "mcp" "merge" "modelfile parse" "monitor" "nccl-diag-lint" "nf4-lint" "ollama-chat-lint" "ollama-tools-lint" "oom-lint" "oracle" "otlp-lint" "parity" "pipeline apply" "pipeline plan" "pipeline status" "pipeline validate" "ppl" "pretrain" "profile" "prometheus-lint" "prune" "ptx" "ptx-map" "publish" "pull" "pv audit" "pv book" "pv census" "pv certify" "pv challenge check" "pv challenge gen" "pv check-parity" "pv codegen" "pv coq" "pv coverage" "pv diff" "pv discharge check" "pv discharge gen-axioms" "pv discharge label-ratchet" "pv discharge run" "pv equations" "pv explain" "pv extract" "pv extract-pytorch" "pv flux" "pv fuzz" "pv generate" "pv graph" "pv infer" "pv invariants" "pv kaizen" "pv kani" "pv lean" "pv lean-status" "pv lint" "pv migrate" "pv mirai" "pv obligations" "pv ontology export" "pv ontology tbox" "pv pipeline" "pv probar" "pv proof-status" "pv query" "pv roofline" "pv scaffold" "pv score" "pv status" "pv tla" "pv unlock" "pv validate" "pv verify-bindings" "pv verify-pipeline" "pv verify-structure" "qa" "qualify" "quant-preservation-lint" "quantize" "rag demo" "rag extract-frames" "rag index" "rag info" "rag query" "rag transcribe" "react-trace-lint" "registry aliases" "registry-quota-lint" "rerank" "rm" "rm-gc-lint" "rosetta chain" "rosetta compare-inference" "rosetta convert" "rosetta diff-tensors" "rosetta fingerprint" "rosetta inspect" "rosetta validate-stats" "rosetta verify" "run" "runs diff" "runs ls" "runs show" "serve plan" "serve run" "shard" "shared-cache-lint" "showcase" "sim emc-check" "sim emc-validate" "sim help" "sim list-emc" "sim render" "sim run" "sim validate" "sim verify" "sim version" "stamp" "tensors" "test llm bench" "test tensor" "tokenize apply" "tokenize encode" "tokenize encode-corpus" "tokenize import-hf" "tokenize plan" "tokenize repair-manifest" "tokenize train" "tool-use-lint" "trace" "train apply" "train archive" "train cluster-status" "train halving" "train plan" "train submit" "train sweep" "train watch" "tree" "tui" "tune" "typical-p-lint" "unified-search-lint" "unshard" "validate" "validate-manifest" "zram benchmark" "zram create" "zram remove" "zram status" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-corpus-ingest" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "apr-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-corpus-ingest [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 2 ; + sh:maxCount 2 ; + sh:in ( "plan" "validate-contract" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 41 ; + sh:maxCount 41 ; + sh:in ( "DELETE /api/delete" "GET /" "GET /api/tags" "GET /api/version" "GET /health" "GET /health/live" "GET /health/ready" "GET /metrics" "GET /metrics/dispatch" "GET /models" "GET /ready" "GET /realize/model" "GET /tensors" "GET /v1/audit/:request_id" "GET /v1/effective-config" "GET /v1/gpu/status" "GET /v1/metrics" "GET /v1/models" "POST /api/chat" "POST /api/embeddings" "POST /api/generate" "POST /api/pull" "POST /api/show" "POST /batch/generate" "POST /batch/tokenize" "POST /generate" "POST /metrics/dispatch/reset" "POST /realize/batch" "POST /realize/embed" "POST /realize/generate" "POST /realize/reload" "POST /stream/generate" "POST /tokenize" "POST /v1/batch/completions" "POST /v1/chat/completions" "POST /v1/chat/completions/stream" "POST /v1/completions" "POST /v1/embeddings" "POST /v1/explain" "POST /v1/gpu/warmup" "POST /v1/predict" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 9 ; + sh:maxCount 9 ; + sh:in ( "apr.bench" "apr.finetune" "apr.qa" "apr.run" "apr.serve" "apr.tensors" "apr.trace" "apr.validate" "apr.version" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "apr" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^apr [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 264 ; + sh:maxCount 264 ; + sh:in ( "attn-parity-lint" "attn-viz-lint" "audio-inspect-lint" "awq-lint" "beat-run" "bench" "canary check" "canary create" "capability" "cbtop" "cgp baseline" "cgp bench" "cgp compete" "cgp contract generate" "cgp contract verify" "cgp diff" "cgp doctor" "cgp explain" "cgp profile binary" "cgp profile compare" "cgp profile cublas" "cgp profile kernel" "cgp profile library" "cgp profile metal" "cgp profile parallel" "cgp profile python" "cgp profile quant" "cgp profile scalar" "cgp profile scaling" "cgp profile simd" "cgp profile wasm" "cgp profile wgpu" "cgp roofline" "cgp trace" "cgp tui" "chat" "check" "check-finite-lint" "code" "compare-hf" "compile" "convert" "data audit" "data balance" "data decontaminate" "data dedup" "data split" "data x convert" "data x dedup" "data x drift compare" "data x drift detect" "data x drift merge" "data x drift report" "data x drift sketch" "data x fed manifest" "data x fed plan" "data x fed split" "data x fed verify" "data x filter-text" "data x fim" "data x head" "data x import local" "data x info" "data x mix" "data x quality check" "data x quality profiles" "data x quality report" "data x quality score" "data x registry delete" "data x registry init" "data x registry list" "data x registry pull" "data x registry push" "data x registry search" "data x registry show-info" "data x repl" "data x schema" "data x view" "dataset audio-inspect" "ddp-metrics-lint" "debug" "debug embed-viz" "decrypt" "devices" "diagnose" "diff" "distill" "dry-sampling-lint" "embed" "embed-viz-lint" "embeddings-lint" "encrypt" "eval" "experiment view" "explain" "explain-token-lint" "export" "finetune" "flow" "fp8-lint" "gbnf-lint" "gptq-lint" "gpu" "gpu-memtrace-lint" "grad-norm" "hang-trace-lint" "hex" "imatrix-lint" "import" "inspect" "kernel parity" "kv-timeline-lint" "lint" "list" "manifest" "mcp" "merge" "modelfile parse" "monitor" "nccl-diag-lint" "nf4-lint" "ollama-chat-lint" "ollama-tools-lint" "oom-lint" "oracle" "otlp-lint" "parity" "pipeline apply" "pipeline plan" "pipeline status" "pipeline validate" "ppl" "pretrain" "profile" "prometheus-lint" "prune" "ptx" "ptx-map" "publish" "pull" "pv audit" "pv book" "pv census" "pv certify" "pv challenge check" "pv challenge gen" "pv check-parity" "pv codegen" "pv coq" "pv coverage" "pv diff" "pv discharge check" "pv discharge gen-axioms" "pv discharge label-ratchet" "pv discharge run" "pv equations" "pv explain" "pv extract" "pv extract-pytorch" "pv flux" "pv fuzz" "pv generate" "pv graph" "pv infer" "pv invariants" "pv kaizen" "pv kani" "pv lean" "pv lean-status" "pv lint" "pv migrate" "pv mirai" "pv obligations" "pv ontology export" "pv ontology tbox" "pv pipeline" "pv probar" "pv proof-status" "pv query" "pv roofline" "pv scaffold" "pv score" "pv status" "pv tla" "pv unlock" "pv validate" "pv verify-bindings" "pv verify-pipeline" "pv verify-structure" "qa" "qualify" "quant-preservation-lint" "quantize" "rag demo" "rag extract-frames" "rag index" "rag info" "rag query" "rag transcribe" "react-trace-lint" "registry aliases" "registry-quota-lint" "rerank" "rm" "rm-gc-lint" "rosetta chain" "rosetta compare-inference" "rosetta convert" "rosetta diff-tensors" "rosetta fingerprint" "rosetta inspect" "rosetta validate-stats" "rosetta verify" "run" "runs diff" "runs ls" "runs show" "serve plan" "serve run" "shard" "shared-cache-lint" "showcase" "sim emc-check" "sim emc-validate" "sim help" "sim list-emc" "sim render" "sim run" "sim validate" "sim verify" "sim version" "stamp" "tensors" "test llm bench" "test tensor" "tokenize apply" "tokenize encode" "tokenize encode-corpus" "tokenize import-hf" "tokenize plan" "tokenize repair-manifest" "tokenize train" "tool-use-lint" "trace" "train apply" "train archive" "train cluster-status" "train halving" "train plan" "train submit" "train sweep" "train watch" "tree" "tui" "tune" "typical-p-lint" "unified-search-lint" "unshard" "validate" "validate-manifest" "zram benchmark" "zram create" "zram remove" "zram status" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-cbtop" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-cbtop" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-cbtop [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 4 ; + sh:maxCount 4 ; + sh:in ( "bench" "optimize analyze" "optimize baseline" "optimize check" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-compute-xtask" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-compute-xtask" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-compute-xtask [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 3 ; + sh:maxCount 3 ; + sh:in ( "check-simd" "install-hooks" "validate-examples" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "pv" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-contracts-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^pv [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "\\([0-9a-f]{9}\\)" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 49 ; + sh:maxCount 49 ; + sh:in ( "audit" "book" "census" "certify" "challenge check" "challenge gen" "check-parity" "codegen" "coq" "coverage" "diff" "discharge check" "discharge gen-axioms" "discharge label-ratchet" "discharge run" "equations" "explain" "extract" "extract-pytorch" "flux" "fuzz" "generate" "graph" "infer" "invariants" "kaizen" "kani" "lean" "lean-status" "lint" "migrate" "mirai" "obligations" "ontology export" "ontology tbox" "pipeline" "probar" "proof-status" "query" "roofline" "scaffold" "score" "status" "tla" "unlock" "validate" "verify-bindings" "verify-pipeline" "verify-structure" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-data" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-data" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-data [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 31 ; + sh:maxCount 31 ; + sh:in ( "convert" "dedup" "drift compare" "drift detect" "drift merge" "drift report" "drift sketch" "fed manifest" "fed plan" "fed split" "fed verify" "filter-text" "fim" "head" "import local" "info" "mix" "quality check" "quality profiles" "quality report" "quality score" "registry delete" "registry init" "registry list" "registry pull" "registry push" "registry search" "registry show-info" "repl" "schema" "view" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-db" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-db" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-db [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "--config" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 3 ; + sh:maxCount 3 ; + sh:in ( "GET /health" "GET /status" "POST /query" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-explain" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-explain" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-explain [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 7 ; + sh:maxCount 7 ; + sh:in ( "bugs" "compare" "diff" "ptx" "simd" "tui" "wgpu" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-orchestrate" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-orchestrate" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-orchestrate [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 86 ; + sh:maxCount 86 ; + sh:in ( "agent chat" "agent contracts" "agent pool" "agent run" "agent sign" "agent status" "agent validate" "agent verify-sig" "analyze" "bug-hunter analyze" "bug-hunter deep-hunt" "bug-hunter diff" "bug-hunter ensemble" "bug-hunter falsify" "bug-hunter fuzz" "bug-hunter hunt" "bug-hunter spec" "bug-hunter stack" "bug-hunter ticket" "bug-hunter trend" "bug-hunter triage" "build" "code" "content emit" "content types" "content validate" "data tree" "deploy cloudflare" "deploy docker" "deploy fly" "deploy k8s" "deploy lambda" "experiment tree" "falsify" "hf catalog" "hf course" "hf info" "hf pull" "hf push" "hf search" "hf tree" "init" "mcp" "optimize" "oracle" "pacha alias" "pacha aliases" "pacha decrypt" "pacha encrypt" "pacha keygen" "pacha list" "pacha pin" "pacha prune" "pacha pull" "pacha rm" "pacha run" "pacha search" "pacha show" "pacha sign" "pacha stats" "pacha unpin" "pacha verify" "parf" "playbook lock" "playbook run" "playbook status" "playbook validate" "report" "reset" "serve" "stack check" "stack comply" "stack drift" "stack gate" "stack publish-status" "stack quality" "stack release" "stack status" "stack sync" "stack tree" "stack versions" "status" "transpile" "validate" "viz dashboard" "viz tree" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 4 ; + sh:maxCount 4 ; + sh:in ( "hf_info" "hf_integration" "hf_search" "hf_tree" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 92 ; + sh:maxCount 92 ; + sh:in ( "DELETE /api/delete" "DELETE /api/v1/conversations/:id" "DELETE /api/v1/data/files/:id" "DELETE /api/v1/models/registry/:name" "DELETE /api/v1/prompts/:id" "DELETE /api/v1/rag/index" "DELETE /api/v1/train/runs/:id" "GET /" "GET /api/tags" "GET /api/v1/audio/formats" "GET /api/v1/audit" "GET /api/v1/batch" "GET /api/v1/batch/:id" "GET /api/v1/chat/parameters" "GET /api/v1/config" "GET /api/v1/conversations" "GET /api/v1/conversations/:id" "GET /api/v1/conversations/export" "GET /api/v1/conversations/search" "GET /api/v1/data/datasets" "GET /api/v1/data/datasets/:id/preview" "GET /api/v1/data/files" "GET /api/v1/data/files/:id/info" "GET /api/v1/data/recipes" "GET /api/v1/data/recipes/:id" "GET /api/v1/eval/runs" "GET /api/v1/eval/runs/:id" "GET /api/v1/experiments" "GET /api/v1/experiments/:id/compare" "GET /api/v1/mcp/info" "GET /api/v1/metrics" "GET /api/v1/models" "GET /api/v1/models/merge/strategies" "GET /api/v1/models/registry" "GET /api/v1/models/status" "GET /api/v1/prompts" "GET /api/v1/prompts/:id" "GET /api/v1/rag/search" "GET /api/v1/rag/status" "GET /api/v1/system" "GET /api/v1/tools" "GET /api/v1/train/presets" "GET /api/v1/train/runs" "GET /api/v1/train/runs/:id" "GET /api/v1/train/runs/:id/metrics" "GET /api/v1/ws" "GET /assets/*path" "GET /health" "GET /health/live" "GET /health/ready" "GET /v1/models" "GET /v1/models/:id" "PATCH /api/v1/conversations/:id" "POST /api/chat" "POST /api/generate" "POST /api/pull" "POST /api/show" "POST /api/v1/audio/transcriptions" "POST /api/v1/batch" "POST /api/v1/chat/completions" "POST /api/v1/conversations" "POST /api/v1/conversations/import" "POST /api/v1/data/recipes" "POST /api/v1/data/recipes/:id/run" "POST /api/v1/data/upload" "POST /api/v1/data/upload/json" "POST /api/v1/detokenize" "POST /api/v1/embeddings" "POST /api/v1/eval/perplexity" "POST /api/v1/experiments" "POST /api/v1/experiments/:id/runs" "POST /api/v1/mcp" "POST /api/v1/models/load" "POST /api/v1/models/merge" "POST /api/v1/models/pull" "POST /api/v1/models/unload" "POST /api/v1/prompts" "POST /api/v1/rag/index" "POST /api/v1/tokenize" "POST /api/v1/tools" "POST /api/v1/tools/execute" "POST /api/v1/train/runs/:id/export" "POST /api/v1/train/runs/:id/stop" "POST /api/v1/train/start" "POST /ui/chat" "POST /v1/audio/transcriptions" "POST /v1/chat/completions" "POST /v1/completions" "POST /v1/embeddings" "PUT /api/v1/chat/parameters" "PUT /api/v1/config" "PUT /api/v1/tools/:name/config" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-present" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-present-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-present [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 7 ; + sh:maxCount 7 ; + sh:in ( "bundle" "check" "deploy" "gate" "new" "score" "serve" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-ptop" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-present-terminal" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-ptop [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 10 ; + sh:maxCount 10 ; + sh:in ( "--config" "--deterministic" "--dump-config" "--explode" "--height" "--no-color" "--qa-timing" "--refresh" "--render-once" "--width" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-score" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-present-terminal" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-score [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 7 ; + sh:maxCount 7 ; + sh:in ( "--ci" "--config" "--no-color" "--output" "--quiet" "--threshold" "--verbose" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "PATH" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-profile" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-profile" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-profile [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "validate" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 46 ; + sh:maxCount 46 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "COMMAND" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-ptx-debug" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-ptx-debug" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-ptx-debug [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 3 ; + sh:maxCount 3 ; + sh:in ( "analyze" "gen-fkr" "version" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-qa-readme-sync" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-qa-certify" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-qa-readme-sync [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "apr-qa" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-qa-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^apr-qa [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 15 ; + sh:maxCount 15 ; + sh:in ( "bootstrap" "certify" "export-csv" "export-evidence" "generate" "kernel-coverage" "list" "lock-playbooks" "parity" "report" "run" "score" "tickets" "tools" "validate-contract" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-rag" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-rag-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-rag [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 6 ; + sh:maxCount 6 ; + sh:in ( "demo" "extract-frames" "index" "info" "query" "transcribe" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-simulate" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-simulate" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-simulate [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 9 ; + sh:maxCount 9 ; + sh:in ( "emc-check" "emc-validate" "help" "list-emc" "render" "run" "validate" "verify" "version" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-test-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-test-cli" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-test-cli [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 37 ; + sh:maxCount 37 ; + sh:in ( "animation check" "audio check" "av-sync check" "av-sync report" "build" "comply" "comply check" "comply diff" "comply enforce" "comply migrate" "comply report" "config" "coverage" "init" "llm bench" "llm data-audit" "llm experiment" "llm experiment compare" "llm experiment init" "llm experiment status" "llm gen-dataset" "llm load" "llm report" "llm score" "llm sweep" "llm test" "playbook" "record" "report" "serve" "serve score" "serve tree" "serve viz" "stress" "test" "video check" "watch" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-train-lora" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-train-lora" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-train-lora [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 4 ; + sh:maxCount 4 ; + sh:in ( "compare" "inspect" "merge" "plan" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-train-shell" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-train-shell" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-train-shell [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:minCount 10 ; + sh:maxCount 10 ; + sh:in ( "clear" "distill" "export" "fetch" "help" "history" "inspect" "memory" "quit" "set" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 2 ; + sh:maxCount 2 ; + sh:in ( "-c" "-s" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-zram-generator" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "aprender-zram-generator" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^aprender-zram-generator [0-9]+\\.[0-9]+\\.[0-9]+" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "[0-9a-f]{9}" ; + sh:severity sh:Warning ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 3 ; + sh:maxCount 3 ; + sh:in ( "EARLY_DIR" "LATE_DIR" "NORMAL_DIR" ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{40}$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:minLength 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{64}$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{64}$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{64}$" ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "binary,feature,quality_1_10,verified_hardware,top_competitor,in_dogfood_skill,cluster_id,cluster_label,evidence_path,confidence" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{64}$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:closed true ; + sh:property [ + sh:path ; + sh:minCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + ] ; + sh:property [ + sh:path ; + sh:in ( "pmat" "pv" "forjar" "bashrs" "cargo" "gh" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:closed true ; + sh:property [ + sh:path ; + sh:minCount 3 ; + sh:maxCount 3 ; + ] ; + sh:property [ + sh:path ; + sh:in ( "feature1,feature2,label" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:in ( "string" "float" "int" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9a-f]{40}$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "OPEN" "CLOSED" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + sh:class ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "OPEN" "CLOSED" "MERGED" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:class ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "OPEN" "CLOSED" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:closed true ; + sh:ignoredProperties ( ) ; + sh:property [ + sh:path ; + sh:in ( "apr" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9]+\\.[0-9]+$" ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:datatype ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "4a75ee8031b3fbe0b17e1706a248aabbf158420c06f082bc29a89e2103d4057e" ) ; + sh:pattern "^[0-9a-f]{64}$" ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + a sh:NodeShape ; sh:targetClass ; sh:property [ @@ -59,8 +1572,98 @@ ] ; . + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ "false"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ "false"^^ ) ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:class ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; +. + a sh:NodeShape ; - sh:targetClass ; + sh:targetClass ; sh:property [ sh:path ; sh:minCount 1 ; @@ -218,6 +1821,37 @@ ] ; . + a sh:NodeShape ; + sh:targetClass ; + sh:closed true ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern "^[0-9]+\\.[0-9]+$" ; + ] ; + sh:property [ + sh:path ; + sh:in ( "library" "cli" "service" "spec" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 3 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + ] ; +. + a sh:NodeShape ; sh:targetClass ; sh:closed true ; @@ -269,6 +1903,25 @@ sh:nodeKind sh:IRI ; sh:node ; ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:class ; + sh:nodeKind sh:IRI ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; . a sh:NodeShape ; @@ -475,6 +2128,89 @@ ] ; . + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:nodeKind sh:IRI ; + sh:node ; + ] ; +. + + a sh:NodeShape ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "GREEN" "ALL_WRONG" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:nodeKind sh:IRI ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:maxCount 0 ; + ] ; +. + a sh:NodeShape ; sh:targetClass ; sh:property [ @@ -507,6 +2243,89 @@ ] ; . + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:nodeKind sh:IRI ; + sh:node ; + ] ; +. + + a sh:NodeShape ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "pass" "refused" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:nodeKind sh:IRI ; + sh:node ; + ] ; +. + + a sh:NodeShape ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "pass" "refused" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; +. + + a sh:NodeShape ; + sh:targetClass ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:nodeKind sh:IRI ; + sh:node ; + ] ; +. + + a sh:NodeShape ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "pass" ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + ] ; +. + a sh:NodeShape ; sh:targetClass ; sh:property [ @@ -561,5 +2380,11 @@ sh:maxCount 1 ; sh:in ( "true"^^ ) ; ] ; + sh:property [ + sh:path ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:in ( "true"^^ ) ; + ] ; . diff --git a/contracts/simular/binding.yaml b/contracts/simular/binding.yaml new file mode 100644 index 0000000000..a490474e20 --- /dev/null +++ b/contracts/simular/binding.yaml @@ -0,0 +1,108 @@ +version: 1.0.0 +target_crate: simular +critical_path: +- step +- simulate +- seek +- record_step +bindings: +- contract: iterator-v1.yaml + equation: step + function: step + module_path: simular::tui::tsp_app::step + status: implemented +- contract: iterator-v1.yaml + equation: step + function: new + module_path: simular::edd::audit::new + status: implemented +- contract: iterator-v1.yaml + equation: step + function: seek + module_path: simular::edd::audit::seek + status: implemented +- contract: qwen35-hybrid-forward-v1.yaml + equation: step + function: step_forward + module_path: simular::edd::audit::step_forward + status: implemented +- contract: iterator-v1.yaml + equation: step + function: find_by_step_id + module_path: simular::edd::audit::find_by_step_id + status: implemented +- contract: iterator-v1.yaml + equation: step + function: record_step + module_path: simular::edd::v2::record_step + status: implemented +- contract: iterator-v1.yaml + equation: step + function: record_step_with_equations + module_path: simular::edd::v2::record_step_with_equations + status: implemented +- contract: iterator-v1.yaml + equation: step + function: step_count + module_path: simular::edd::v2::step_count + status: implemented +- contract: iterator-v1.yaml + equation: step + function: register_journey + module_path: simular::edd::gui_coverage::register_journey + status: implemented +- contract: iterator-v1.yaml + equation: step + function: timestep_secs + module_path: simular::engine::clock::timestep_secs + status: implemented +- contract: iterator-v1.yaml + equation: step + function: steps_until + module_path: simular::engine::clock::steps_until + status: implemented +- contract: iterator-v1.yaml + equation: step + function: substep_multiplier + module_path: simular::orbit::heijunka::substep_multiplier + status: implemented +- contract: iterator-v1.yaml + equation: step + function: estimate_substeps + module_path: simular::orbit::heijunka::estimate_substeps + status: implemented +- contract: iterator-v1.yaml + equation: step + function: step_days + module_path: simular::orbit::wasm::step_days + status: implemented +- contract: iterator-v1.yaml + equation: step + function: step_hours + module_path: simular::orbit::wasm::step_hours + status: implemented +- contract: iterator-v1.yaml + equation: step + function: run_steps + module_path: simular::orbit::wasm::run_steps + status: implemented +- contract: iterator-v1.yaml + equation: step + function: run_periods + module_path: simular::demos::harmonic_oscillator::run_periods + status: implemented +- contract: iterator-v1.yaml + equation: step + function: run_orbits + module_path: simular::demos::kepler_orbit::run_orbits + status: implemented +- contract: iterator-v1.yaml + equation: simulate + function: simulate_path + module_path: simular::scenarios::portfolio::simulate_path + status: implemented +- contract: iterator-v1.yaml + equation: simulate + function: simulate + module_path: simular::domains::ml::simulate + status: implemented diff --git a/contracts/softmax-kernel-v1.yaml b/contracts/softmax-kernel-v1.yaml index bb775b0b79..3a27158186 100644 --- a/contracts/softmax-kernel-v1.yaml +++ b/contracts/softmax-kernel-v1.yaml @@ -6,6 +6,17 @@ metadata: references: - Bridle (1990) Training Stochastic Model Recognition Algorithms as Networks - Milakov & Gimelshein (2018) Online normalizer calculation for softmax +# ONT-4e (R-20): the kernel contract every softmax refinement must be Liskov against. +requires: +- id: PRE-1 + statement: every input score is finite and the vector is non-empty + formal: '∀i: ¬isNaN(x_i) ∧ ¬isInf(x_i) ∧ len(x) > 0' + formal_status: parsed +ensures: +- id: POST-1 + statement: the output is a probability vector of the input's length + formal: 'len(σ(x)) = len(x) ∧ ∀i: 0 < σ(x)_i < 1 ∧ |Σ σ(x)_i - 1| < ε' + formal_status: parsed equations: softmax: formula: σ(x)_i = exp(x_i - max(x)) / Σ_j exp(x_j - max(x)) diff --git a/contracts/tbox-report.json b/contracts/tbox-report.json new file mode 100644 index 0000000000..6cfcf8ebf8 --- /dev/null +++ b/contracts/tbox-report.json @@ -0,0 +1,46 @@ +{ + "schema": "ont-tbox-report/v1", + "advisory": true, + "method": "told-closure", + "precondition": { + "holds": true, + "admitted_axiom_kinds": [ + "Declaration(Class)", + "Declaration(ObjectProperty)", + "SubClassOf(atomic, atomic)", + "ObjectPropertyDomain", + "ObjectPropertyRange", + "SymmetricObjectProperty" + ], + "refused": [] + }, + "consistent": true, + "classes": 23, + "entailed_subsumptions": [ + [ + "Issue", + "Json" + ], + [ + "Kernel", + "Contract" + ], + [ + "Milestone", + "Json" + ], + [ + "PullRequest", + "Json" + ], + [ + "Repo", + "Json" + ], + [ + "Symbol", + "Code" + ] + ], + "unintended_subsumptions": [] +} diff --git a/contracts/trueno-db/binding.yaml b/contracts/trueno-db/binding.yaml new file mode 100644 index 0000000000..49a10bf789 --- /dev/null +++ b/contracts/trueno-db/binding.yaml @@ -0,0 +1,16 @@ +version: 1.0.0 +target_crate: trueno-db +critical_path: +- query +- get_or_insert +bindings: +- contract: configuration-v1.yaml + equation: query + function: query + module_path: trueno_db::wasm::query + status: implemented +- contract: configuration-v1.yaml + equation: insert + function: get_or_insert + module_path: trueno_db::gpu::jit::get_or_insert + status: implemented diff --git a/contracts/trueno-rag/binding.yaml b/contracts/trueno-rag/binding.yaml new file mode 100644 index 0000000000..a419e3427c --- /dev/null +++ b/contracts/trueno-rag/binding.yaml @@ -0,0 +1,108 @@ +version: 1.0.0 +target_crate: trueno-rag +critical_path: +- embed +- retrieve +- chunk +- insert +bindings: +- contract: embedding-algebra-v1.yaml + equation: embed + function: insert + module_path: trueno_rag::src::multivector::index::insert + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: find_nearest_centroid + module_path: trueno_rag::src::multivector::codec::find_nearest_centroid + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: compress + module_path: trueno_rag::src::multivector::codec::compress + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: train + module_path: trueno_rag::src::multivector::codec::train + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: new + module_path: trueno_rag::src::multivector::types::new + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: embedder + module_path: trueno_rag::src::retrieve::embedder + status: implemented +- contract: embedding-algebra-v1.yaml + equation: embed + function: set_embedding + module_path: trueno_rag::src::chunk::mod::set_embedding + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: retrieve + module_path: trueno_rag::src::retrieve::retrieve + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: retrieve_dense + module_path: trueno_rag::src::retrieve::retrieve_dense + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: retrieve_sparse + module_path: trueno_rag::src::retrieve::retrieve_sparse + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: compute + module_path: trueno_rag::src::metrics::compute + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: recall_at_k + module_path: trueno_rag::src::metrics::recall_at_k + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: precision_at_k + module_path: trueno_rag::src::metrics::precision_at_k + status: implemented +- contract: pagerank-kernel-v1.yaml + equation: retrieve + function: mean_reciprocal_rank + module_path: trueno_rag::src::metrics::mean_reciprocal_rank + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: ndcg_at_k + module_path: trueno_rag::src::metrics::ndcg_at_k + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: average_precision + module_path: trueno_rag::src::metrics::average_precision + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: f1_at_k + module_path: trueno_rag::src::metrics::f1_at_k + status: implemented +- contract: configuration-v1.yaml + equation: retrieve + function: hit_rate_at_k + module_path: trueno_rag::src::metrics::hit_rate_at_k + status: implemented +- contract: configuration-v1.yaml + equation: chunk + function: chunk_count + module_path: trueno_rag::src::sqlite::mod::chunk_count + status: implemented +- contract: configuration-v1.yaml + equation: chunk + function: get_chunk + module_path: trueno_rag::src::sqlite::mod::get_chunk + status: implemented diff --git a/contracts/trueno/binding.yaml b/contracts/trueno/binding.yaml new file mode 100644 index 0000000000..32a810f4ba --- /dev/null +++ b/contracts/trueno/binding.yaml @@ -0,0 +1,336 @@ +version: 1.0.0 +target_crate: trueno +bindings: +- contract: wgpu-forward-pass-v1.yaml + equation: rmsnorm_correctness + module_path: trueno::backends::gpu::device::linalg::wgsl_forward + function: forward_model + status: partial + notes: '#4369: #[contract] site at crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs:753 had no binding row, and contracts/wgpu-forward-pass-v1.yaml does not exist in-tree: the annotation cites no contract' +- contract: pipeline-cache-v1.yaml + equation: pipeline_cache + module_path: trueno::backends::gpu::batch::execute::dispatch + function: PipelineCache + signature: 'pub type PipelineCache = HashMap' + status: implemented + notes: 'Type alias + cache_key() pattern; get-or-insert via contains_key + conditional + insert in execute_inner()' +- contract: pipeline-cache-v1.yaml + equation: single_encoder + module_path: trueno::backends::gpu::batch::execute + function: GpuCommandBatch::execute + signature: 'pub async fn execute(&mut self) -> Result<(), String>' + status: implemented + notes: 'Single command encoder per execute() call; N ops batched (C-BATCH-EXEC-001)' +- contract: tiled-matmul-shader-v1.yaml + equation: matmul_shader + module_path: trueno::backends::gpu::shaders::basic_ops + function: MATMUL_SHADER + signature: "pub const MATMUL_SHADER: &str" + status: implemented + notes: '16x16 tiled WGSL shader with shared memory (KAIZEN-021, C-TILED-MATMUL-001)' +- contract: tiled-matmul-shader-v1.yaml + equation: matmul_dispatch + module_path: trueno::backends::gpu::device::linalg::matmul + function: matmul_async + signature: 'fn matmul_async(a: &GpuBuffer, b: &GpuBuffer, m: u32, n: u32, k: u32) + -> GpuBuffer' + status: implemented + notes: 'Dispatch: ceil(M/16) x ceil(N/16) x 1 workgroups' +- contract: tiled-matmul-shader-v1.yaml + equation: execute_matmul_op + module_path: trueno::backends::gpu::batch::execute::dispatch + function: encode_matmul_op + signature: 'pub(crate) fn encode_matmul_op(&self, encoder: &mut CommandEncoder, cache: + &mut PipelineCache, shader_source: &str, label: &str, a: &BufferId, b: &BufferId, + output: &BufferId, m: u32, k: u32, n: u32) -> Result<(), String>' + status: implemented + notes: 'Named encode_matmul_op in source (encoder pattern, not direct execute)' +- contract: gemm-backward-tiled-v1.yaml + equation: backward_a_gemm + module_path: trueno_gpu::kernels::backward::gemm + function: GemmBackwardAKernel::tiled + signature: 'pub const fn tiled(m: u32, n: u32, k: u32, tile_size: u32) -> Self' + status: implemented + notes: 'grad_A = grad_C @ B^T; tiled with shared memory (C-TILE-BWD-001). Outputs + gemm_backward_a_tiled kernel.' +- contract: gemm-backward-tiled-v1.yaml + equation: backward_b_gemm + module_path: trueno_gpu::kernels::backward::gemm + function: GemmBackwardBKernel::tiled + signature: 'pub const fn tiled(m: u32, n: u32, k: u32, tile_size: u32) -> Self' + status: implemented + notes: 'grad_B = A^T @ grad_C; tiled with shared memory. Outputs gemm_backward_b_tiled + kernel.' +- contract: gemm-backward-tiled-v1.yaml + equation: tiled_gemm_arithmetic_intensity + module_path: trueno_gpu::kernels::backward::gemm + function: GemmBackwardAKernel::tiled + signature: 'pub const fn tiled(m: u32, n: u32, k: u32, tile_size: u32) -> Self' + status: implemented + notes: 'Arithmetic intensity is a computed property of the tiled kernel variant (naive + ~0.5 vs tiled TILE/4 FLOP/byte). No separate accessor; the tiled constructor + implicitly sets this via tile_size parameter.' +- contract: gemm-backward-tiled-v1.yaml + equation: shared_memory_per_tile + module_path: trueno_gpu::kernels::backward::gemm + function: GemmBackwardAKernel::tiled + signature: 'pub const fn tiled(m: u32, n: u32, k: u32, tile_size: u32) -> Self' + status: implemented + notes: 'Shared memory = 2 * tile_size^2 * sizeof(f32) bytes per tile. Computed from + tile_size parameter, not a separate accessor.' +- contract: gemm-backward-tiled-v1.yaml + equation: unrolled_instruction_ratio + module_path: trueno_gpu::kernels::backward::gemm + function: GemmBackwardAKernel::tiled_unrolled + signature: 'pub const fn tiled_unrolled(m: u32, n: u32, k: u32, tile_size: u32) -> + Self' + status: implemented + notes: '4x unrolled variant; IPC ratio 0.57. Both GemmBackwardAKernel and GemmBackwardBKernel + have tiled_unrolled constructors.' +- contract: activation-kernel-v1.yaml + equation: relu + module_path: trueno + function: relu + status: implemented + notes: Auto-inferred by pv infer +- contract: silu-kernel-v1.yaml + equation: sigmoid + module_path: trueno + function: sigmoid + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: gelu + module_path: trueno + function: gelu + status: implemented + notes: Auto-inferred by pv infer +- contract: softmax-kernel-v1.yaml + equation: softmax + module_path: trueno + function: softmax + status: implemented + notes: Auto-inferred by pv infer +- contract: cross-entropy-kernel-v1.yaml + equation: log_softmax + module_path: trueno + function: log_softmax + status: implemented + notes: Auto-inferred by pv infer +- contract: matmul-kernel-v1.yaml + equation: matmul + module_path: trueno + function: matmul + status: implemented + notes: Auto-inferred by pv infer +- contract: gated-delta-net-v1.yaml + equation: read + module_path: trueno + function: read + status: implemented + notes: Auto-inferred by pv infer +- contract: gated-delta-net-v1.yaml + equation: write + module_path: trueno + function: write + status: implemented + notes: Auto-inferred by pv infer +- contract: model-config-algebra-v1.yaml + equation: bounds + module_path: trueno + function: bounds + status: implemented + notes: Auto-inferred by pv infer +- contract: activation-kernel-v1.yaml + equation: silu + module_path: trueno + function: silu + status: implemented + notes: Auto-inferred by pv infer +- contract: random-forest-v1.yaml + equation: predict + module_path: trueno + function: predict + status: implemented + notes: Auto-inferred by pv infer +- contract: kmeans-kernel-v1.yaml + equation: update + module_path: trueno + function: update + status: implemented + notes: Auto-inferred by pv infer +- contract: quantization-ordering-v1.yaml + equation: bytes_per_param + module_path: trueno + function: bytes_per_param + status: implemented + notes: Auto-inferred by pv infer +- contract: embedding-lookup-v1.yaml + equation: embedding_lookup + module_path: trueno + function: embedding_lookup + status: implemented + notes: Auto-inferred by pv infer +- contract: graph-centrality-v1.yaml + equation: eigenvector + module_path: trueno + function: eigenvector + status: implemented + notes: Auto-inferred by pv infer +- contract: tensor-inventory-v1.yaml + equation: tensor_count + module_path: trueno::cbtop::quantize::gguf + function: GgufReader::tensor_count + signature: 'pub fn tensor_count(&self) -> u64' + status: implemented + notes: 'In crates/cbtop/src/quantize/gguf.rs. Counts tensors in GGUF model files.' +- contract: metrics-classification-v1.yaml + equation: precision + module_path: trueno::cbtop::adaptive_ml::types + function: ClassificationMetrics::precision + signature: 'pub fn precision(&self) -> f64' + status: implemented + notes: 'In crates/cbtop/src/adaptive_ml/types.rs. TP / (TP + FP).' +- contract: metrics-classification-v1.yaml + equation: recall + module_path: trueno::cbtop::adaptive_ml::types + function: ClassificationMetrics::recall + signature: 'pub fn recall(&self) -> f64' + status: implemented + notes: 'In crates/cbtop/src/adaptive_ml/types.rs. TP / (TP + FN).' +- contract: attention-kernel-v1.yaml + equation: attention + module_path: trueno_gpu::kernels::attention + function: AttentionKernel + signature: 'pub struct AttentionKernel (FlashAttention variants)' + status: implemented + notes: 'trueno-gpu/src/kernels/attention/mod.rs. FlashAttention (prefill) + Paged + (incremental) + MultiWarp + FlashDecoding variants.' +- contract: int8-symmetric-quant-v1.yaml + equation: quantize + module_path: trueno::crates::trueno_quant::quantize + function: quantize_q4_k + signature: 'pub fn quantize_q4_k(data: &[f32]) -> Vec' + status: implemented + notes: 'crates/trueno-quant/src/quantize.rs. Q4_K/Q5_K/Q6_K per-block scale-min + quantization. Also NF4 in src/brick/quant_ops/nf4.rs.' +- contract: roofline-model-v1.yaml + equation: bandwidth_ceiling + module_path: trueno::monitor::compute_device + function: ComputeDevice::memory_bandwidth_gbps + signature: 'fn memory_bandwidth_gbps(&self) -> anyhow::Result' + status: implemented + notes: 'Trait method on ComputeDevice (TRUENO-SPEC-020). Also in crates/cbtop/src/roofline.rs + HardwareProfile::peak_bandwidth_gbps.' +- contract: roofline-model-v1.yaml + equation: throughput_bound + module_path: trueno::cbtop::roofline + function: HardwareProfile::theoretical_peak_at_oi + signature: 'pub fn theoretical_peak_at_oi(&self, operational_intensity: f64) -> f64' + status: implemented + notes: 'Roofline model: min(compute_ceiling, bandwidth * operational_intensity). + classify_bottleneck() returns compute-bound vs memory-bound.' +- contract: gpu-context-health-v1.yaml + equation: culink_skip + module_path: trueno_gpu::driver::module + function: compile_ptx_to_cubin + signature: 'fn compile_ptx_to_cubin(driver: &CudaDriver, ptx: &str, jit_target: + c_uint) -> Result, GpuError>' + status: implemented + notes: 'PMAT-404: Always returns Err — cuLinkCreate skipped (PMAT-369). Legacy JIT + used.' +- contract: gpu-weight-residency-v1.yaml + equation: pcie_overhead + module_path: trueno_gpu::driver::memory::buffer + function: GpuBuffer::from_host_registered + signature: 'pub unsafe fn from_host_registered(host_ptr: *mut T, len: usize) -> + Result' + status: implemented + notes: 'PMAT-404: Zero-copy via cuMemHostRegister + cuMemHostGetDevicePointer. Eliminates + PCIe transfer on unified memory (cc>=120). PMAT-396/PMAT-420.' +- contract: gpu-decode-profiling-v1.yaml + equation: wall_coverage + module_path: trueno::brick::profiler + function: BrickProfiler::total_ns + signature: 'pub fn total_ns(&self) -> u64' + status: implemented + notes: 'PMAT-403: Total nanoseconds across all brick profiling. sum(brick_total_ns) + / wall_clock_ns >= 0.85 (PAR-200 fast path).' +- contract: gpu-decode-profiling-v1.yaml + equation: sync_verification + module_path: trueno::brick::profiler + function: BrickProfiler::set_sync_mode + signature: 'pub fn set_sync_mode(&mut self, mode: SyncMode)' + status: implemented + notes: 'PMAT-403: Modes: Immediate, PerLayer, Deferred, None. Deferred is default + for production (PAR-200).' +- contract: gpu-decode-profiling-v1.yaml + equation: token_accounting + module_path: trueno::brick::profiler + function: BrickProfiler::total_tokens + signature: 'pub fn total_tokens(&self) -> u64' + status: implemented + notes: 'PMAT-403: Tracks total elements processed. LmHead.count = decoded tokens.' +- contract: gpu-decode-profiling-v1.yaml + equation: brick_ordering + module_path: trueno::brick::profiler + function: BrickProfiler::brick_stats + signature: 'pub fn brick_stats(&self, brick_id: BrickId) -> &BrickStats' + status: implemented + notes: 'PMAT-403: O(1) per-brick access via BrickId array index (PAR-200). Per-call + avg respects kernel complexity (LmHead > Gate > RmsNorm).' +- contract: avx512-blis-v1.yaml + equation: flops_per_tile + module_path: trueno::blis::compute + function: gemm_blis_avx512_large + signature: 'unsafe fn gemm_blis_avx512_large(m: usize, n: usize, k: usize, a: &[f32], b: &[f32], c: &mut [f32]) -> Result<(), TruenoError>' + status: implemented + notes: 'PMAT-037: AVX-512 8×16 microkernel with zmm registers. MR=8, NR=16. BlisProfiler NOT yet wired (C-AVX512-PROF-001 PENDING).' +- contract: avx512-blis-v1.yaml + equation: peak_throughput + module_path: trueno::blis::compute + function: avx512_microkernel_8x16_rowmajor + signature: 'pub(super) unsafe fn avx512_microkernel_8x16_rowmajor(k: usize, a: *const f32, b: *const f32, c: *mut f32, ldc: usize)' + status: implemented + notes: '8 zmm accumulators, B as zmm load, A as scalar broadcast. 128 GFLOPS measured (98.5% of peak).' +- contract: avx512-blis-v1.yaml + equation: flops_per_tile + module_path: trueno::blis::compute + function: pack_b_block_nr16 + signature: 'pub(super) fn pack_b_block_nr16(b: &[f32], ldb: usize, pc: usize, jc: usize, kc: usize, nc: usize, packed: &mut [f32])' + status: implemented + notes: 'NR=16 B packing for AVX-512 zmm width. Zero-pads when nc < 16.' +- contract: blis-thread-cap-v1.yaml + equation: amdahl_speedup + module_path: trueno::blis::parallel + function: gemm_blis_parallel + signature: 'pub fn gemm_blis_parallel(m: usize, n: usize, k: usize, a: &[f32], b: &[f32], c: &mut [f32]) -> Result<(), TruenoError>' + status: implemented + notes: 'PMAT-037: 4-tier thread cap (2/4/phys÷2/all) based on FLOPs thresholds from cgp scaling measurements.' +- contract: avx512-q4k-v1.yaml + equation: dequant + module_path: trueno::backends::q4k::gemv::avx512 + function: matmul_q4k_f32_avx512 + signature: 'pub(crate) unsafe fn matmul_q4k_f32_avx512(q4k_data: &[u8], input: &[f32], out_dim: usize, in_dim: usize) -> Vec' + status: implemented + notes: '16-wide AVX-512 dequant+FMA. +5-35% vs AVX2. Bottleneck: scalar header parsing per super-block.' +- contract: avx512-q4k-v1.yaml + equation: dequant + module_path: trueno::backends::q4k::gemv::avx512 + function: compute_chunk_q4k_avx512 + signature: 'pub(crate) unsafe fn compute_chunk_q4k_avx512(...)' + status: implemented + notes: 'Parallel dispatch chunk processor for AVX-512 Q4K path.' +critical_path: +- softmax +- matmul +- silu +- gelu +- execute_matmul +- PipelineCache +- GemmBackward +- arithmetic_intensity +- gemm_blis_avx512_large +- gemm_blis_parallel +- matmul_q4k_f32_avx512 diff --git a/contracts/tui-rendering-ux-v1.yaml b/contracts/tui-rendering-ux-v1.yaml index c7e20153d9..51851fd4f9 100644 --- a/contracts/tui-rendering-ux-v1.yaml +++ b/contracts/tui-rendering-ux-v1.yaml @@ -282,7 +282,7 @@ proof_obligations: property: "60 FPS frame budget with smart diffing" verification_summary: - total_obligations: 8 + total_obligations: 6 proven: 0 tested: 0 status: pending diff --git a/contracts/unified-specs-v1.yaml b/contracts/unified-specs-v1.yaml index 628ab11130..cb1396e81c 100644 --- a/contracts/unified-specs-v1.yaml +++ b/contracts/unified-specs-v1.yaml @@ -80,7 +80,7 @@ kani_harnesses: bound: 4 verification_summary: - total_obligations: 3 + total_obligations: 1 proven: 0 tested: 3 status: tested diff --git a/contracts/witness/f4a1686f98d541afc73cc61d63d1a9726ef28aaee70bad35dff472cef083bf67.json b/contracts/witness/f4a1686f98d541afc73cc61d63d1a9726ef28aaee70bad35dff472cef083bf67.json new file mode 100644 index 0000000000..464cb81457 --- /dev/null +++ b/contracts/witness/f4a1686f98d541afc73cc61d63d1a9726ef28aaee70bad35dff472cef083bf67.json @@ -0,0 +1,14 @@ +{ + "census_id_set_sha256": "ce0f9c175ba1345ff18e9e0f68797488dc56f63b968073b707bbb347a5db0eac", + "relations_sha256": "f4a1686f98d541afc73cc61d63d1a9726ef28aaee70bad35dff472cef083bf67", + "reasoner_git_sha": "9f5609568f1fc267287eb8e3edb8883be6c97a7e", + "checkable_n": 84, + "result": { + "kind": "model", + "payload": { + "false": [] + } + }, + "pc_reasoner": "fired", + "cpu_ms": 1 +} diff --git a/contracts/witness/liskov/469bcc39934277e680a45936484c28f6d5f9735bfc3cc1ffd213e97050678d70.json b/contracts/witness/liskov/469bcc39934277e680a45936484c28f6d5f9735bfc3cc1ffd213e97050678d70.json new file mode 100644 index 0000000000..fb08ff3eff --- /dev/null +++ b/contracts/witness/liskov/469bcc39934277e680a45936484c28f6d5f9735bfc3cc1ffd213e97050678d70.json @@ -0,0 +1,36 @@ +{ + "liskov_sha256": "469bcc39934277e680a45936484c28f6d5f9735bfc3cc1ffd213e97050678d70", + "pairs_checked": 1, + "reasoner_git_sha": "c0435711a3bad24afd55b108ee71253e22d079d0", + "pc_reasoner": "fired", + "pairs": [ + { + "a": "online-softmax-v1", + "b": "softmax-kernel-v1", + "obligations": [ + { + "kind": "pre", + "chain": [ + { + "clause": "PRE-1", + "from": "PRE-1" + } + ] + }, + { + "kind": "post", + "chain": [ + { + "clause": "POST-1", + "from": "POST-1" + } + ] + }, + { + "kind": "inv", + "chain": [] + } + ] + } + ] +} diff --git a/contracts/work/PMAT-4080.yaml b/contracts/work/PMAT-4080.yaml new file mode 100644 index 0000000000..1476bb3820 --- /dev/null +++ b/contracts/work/PMAT-4080.yaml @@ -0,0 +1,105 @@ +contract: pvl-2-ghost-binding-reject +metadata: + kind: pattern + version: "1.0.0" + description: > + PVL-2 (PMAT-4080; paiml/infra docs/specifications/PVL-001-pv-lean-gate.md + row EV-2): `pv proof-status --binding` RESOLVES every binding, and a ghost + binding (claimed `implemented`, function absent from source) is a REJECT. + + MEASURED before this contract (origin/main 49fe19c28, 2026-09-23): given + tests/fixtures/pvl/ghost-binding.yaml (status implemented, + aprender::nonexistent::function_that_does_not_exist_xyz), `pv proof-status + --binding` printed its report and exited 0. It counted binding entries and + never resolved them (scripts/dogfood.sh:796 records the same), and + `--verify-bindings` downgraded ghosts silently, still exiting 0. + + Now every `implemented` binding is looked up with the `pv verify-bindings` + resolver (scan_all_sources / derive_src_root, one shared short_name + normalization). A ghost is downgraded, listed under `GHOST BINDINGS (n)` + with its contract, equation and function, and the command exits 1. + `--verify-bindings` is a no-op alias. The resolver now sees every `fn` item + whatever its visibility or qualifiers: the old four-prefix scanner missed + `pub(super) fn compute_mse` (crates/aprender-core/src/tree/regression_helpers.rs:27) + and reported it a ghost, which under a reject gate is a false reject. It also + sees type items (struct/enum/type/trait): setfit-apr-v1 binds two real + `pub struct`s. And the source root no longer assumes the pre-monorepo layout + (`contracts//binding.yaml` -> `../..//`, absent here): it falls back + to the binding's nearest ancestor holding `crates/` or `src/`, so the verdict + no longer depends on the caller's cwd. Measured with the final resolver: + contracts/binding.yaml 1 genuine ghost, contracts/aprender/binding.yaml 5 + genuine ghosts (all filed on #4094). + references: + - "docs/specifications/PVL-001-pv-lean-gate.md (paiml/infra), row EV-2" + - "crates/aprender-contracts-cli/src/commands/proof_status.rs" + - "crates/aprender-contracts-cli/src/commands/verify_bindings.rs" + - "crates/aprender-contracts-cli/tests/pvl_ghost_binding.rs" + - "tests/fixtures/pvl/ghost-binding.yaml" + +equations: + ghost_is_rejected: + formula: > + forall b in bindings(B) . status(b) = implemented and short(function(b)) not in fns(src(B)) + => exit_code(pv proof-status --binding B) = 1 + and stdout contains "GHOST BINDINGS (" ++ |ghosts(B)| ++ ")" + and stdout names function(b) + domain: "B a binding registry file; src(B) the verify-bindings resolver's scan set" + codomain: "{1}" + invariants: + - "a ghost is downgraded before levels are computed, so no L5 is credited on a ghost" + resolved_is_accepted: + formula: > + forall b in bindings(B) . status(b) = implemented => short(function(b)) in fns(src(B)) + => no GHOST BINDINGS line + domain: "B whose every implemented binding resolves" + codomain: "{0}" + invariants: + - "a read error of B is not the ghost verdict: it prints no GHOST BINDINGS line" + +proof_obligations: + - id: P2-INV-001 + type: invariant + property: "An implemented binding whose function the resolver cannot find is rejected, counted and named" + formal: '∀ b ∈ bindings: implemented(b) ∧ short(b) ∉ fns(src) ⟹ exitCode ≡ 1 ∧ b ∈ ghostBlock' + applies_to: all + - id: P2-INV-002 + type: invariant + property: "Every fn item declaration form is visible to the resolver" + formal: '∀ v ∈ visibilities: ∀ q ∈ qualifiers: fnItemName(v · q · fnDecl(n)) ≡ n' + applies_to: all + +falsification_tests: + - id: FALSIFY-PVL-2-001 + rule: "PVL-2-GHOST-REJECTED" + prediction: > + Bypassing the resolver in proof_status::resolve_bindings (`unresolved = + false && …`) turns a_ghost_binding_is_a_reject and + verify_bindings_is_a_noop_alias RED, while the control and the + missing-file case stay GREEN. + test_harness: "cargo test -p aprender-contracts-cli --test pvl_ghost_binding" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: mutation engaged -> FAILED. 2 passed; 2 failed; RED: + verify_bindings_is_a_noop_alias a_ghost_binding_is_a_reject; restored -> ok. + 4 passed; 0 failed. + - id: FALSIFY-PVL-2-002 + rule: "PVL-2-EVERY-FN-FORM" + prediction: > + Dropping the `pub(...)` branch of fn_item_name turns the resolver case table + RED (pub(super) fn compute_mse reads as not-a-declaration). + test_harness: "cargo test -p aprender-contracts-cli --lib -- verify_bindings" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: mutation engaged -> FAILED. 0 passed; 1 failed (left: None); + restored -> ok. 1 passed; 0 failed. + - id: FALSIFY-PVL-2-003 + rule: "PVL-2-GHOST-REJECTED" + prediction: > + The shipped proof-status (origin/main) accepts the ghost fixture: exit 0, no + GHOST BINDINGS line, so the accept test is RED there. + test_harness: "cargo test -p aprender-contracts-cli --test pvl_ghost_binding" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23 on origin/main 49fe19c28 + the test: FAILED. 2 passed; 2 + failed; RED: a_ghost_binding_is_a_reject verify_bindings_is_a_noop_alias (both + exit 0 with the report printed). diff --git a/contracts/work/PMAT-4081.yaml b/contracts/work/PMAT-4081.yaml new file mode 100644 index 0000000000..e69e2dcf7c --- /dev/null +++ b/contracts/work/PMAT-4081.yaml @@ -0,0 +1,122 @@ +contract: pvl-3-one-ladder +metadata: + kind: pattern + version: "1.0.0" + description: > + PVL-3 (PMAT-4081; paiml/infra docs/specifications/PVL-001-pv-lean-gate.md + row EV-3): ONE definition of the proof levels. + + MEASURED before this contract (origin/main 49fe19c28, 2026-09-23): the + `ProofLevel` enum (crates/aprender-contracts/src/proof_status.rs) defines + L3 = Kani bounded model check, L4 = Lean 4 theorem proved, L5 = L4 + every + binding verified. `readme_gen`'s Verification Ladder table printed its OWN + strings one level off (the Kani row one level too high, L5 "Lean 4 theorem", + L1 "Type system"), and all three copies of the ladder doc taught the same + off-by-one ladder plus an L0 the enum does not have. A README reader was told + an L4 contract was Kani-checked when pv had computed it was Lean-proved. + + Now `ProofLevel::method` is the one definition. `readme_gen` prints it, and + each ladder copy carries `levels::ladder_block()` verbatim between + `` and its end marker, with the + statement that L4/L5 are grounded only textually until PVL-001 EV-8b lands + (a claim with no sorry-free in-tree Lean theorem is self-declared and excluded). + references: + - "docs/specifications/PVL-001-pv-lean-gate.md (paiml/infra), row EV-3" + - "crates/aprender-contracts/src/levels.rs" + - "crates/aprender-contracts/src/readme_gen.rs" + - "crates/aprender-contracts/src/proof_status.rs" + +equations: + readme_prints_the_enum: + formula: > + forall l in ProofLevel . row(verification_ladder_table(c), l) + = "| " ++ l ++ " | " ++ c(l) ++ " | " ++ method(l) ++ " |" + domain: "l in {L1..L5}; c the per-level contract counts" + codomain: "the README Verification Ladder table" + invariants: + - "readme_gen holds no level-description string of its own" + every_ladder_copy_carries_the_block: + formula: > + forall d in {the three ladder doc copies PVL-001 EV-3 names} . + ladder_block() is a substring of d + domain: "d a tracked markdown file" + codomain: "{true}" + invariants: + - "the block starts with the marker line and ends with the end marker" + - "the block names each of L5..L1 exactly once, highest first" + +proof_obligations: + - id: P3-INV-001 + type: invariant + property: "The README ladder table's method for every level is ProofLevel::method" + formal: '∀ l ∈ ProofLevel: methodColumn(readmeLadder, l) ≡ method(l)' + applies_to: all + - id: P3-INV-002 + type: invariant + property: "Every ladder doc copy carries the block generated from ProofLevel, byte for byte" + formal: '∀ d ∈ ladderCopies: ladderBlock ⊆ d' + applies_to: all + +falsification_tests: + - id: FALSIFY-PVL-3-001 + rule: "PVL-3-README-IS-ENUM" + prediction: > + Hard-coding one readme_gen string again (L4 -> "Kani BMC" in + verification_ladder_table) turns levels::readme_and_ladder_docs_match_enum RED. + test_harness: "cargo test -p aprender-contracts --lib -- levels::readme_and_ladder_docs_match_enum" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: mutation engaged -> FAILED. 0 passed; 1 failed; 1707 filtered + out ("README ladder row for L4 is not the enum's definition"); restored -> ok. + 1 passed; 0 failed. + - id: FALSIFY-PVL-3-002 + rule: "PVL-3-DOCS-ARE-ENUM" + prediction: > + Editing one row of one ladder copy's generated block (the book copy's L3 + row) turns levels::readme_and_ladder_docs_match_enum RED, naming that file. + test_harness: "cargo test -p aprender-contracts --lib -- levels::readme_and_ladder_docs_match_enum" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: mutation engaged -> FAILED. 0 passed; 1 failed ("…book/src/ + verification-ladder.md does not carry the block generated from ProofLevel"); + restored -> ok. 1 passed; 0 failed. + - id: FALSIFY-PVL-3-003 + rule: "PVL-3-README-IS-ENUM" + prediction: > + The shipped state (readme_gen's own strings, the three docs at origin/main) + fails the accept test. The accept path names the test directly: under + levels::tests:: the spec's accept command ran ZERO tests and passed. + test_harness: "cargo test -p aprender-contracts --lib -- levels::readme_and_ladder_docs_match_enum" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: shipped strings + origin/main docs -> FAILED. 0 passed; 1 + failed ("README ladder row for L5 is not the enum's definition"); this tree -> + ok. 1 passed; 0 failed; 1707 filtered out. + - id: FALSIFY-PVL-3-004 + rule: "PVL-3-DOCS-ARE-ENUM" + prediction: > + Restoring the old "Where Each Tool Lives" header (`Level 4 (Kani) | Level 5 + (Lean)`) in the book copy turns levels::readme_and_ladder_docs_match_enum RED: + the generated block is not enough, prose OUTSIDE it must not pair L4/L5 with + Kani or L5 with Lean alone. Found by quorum lane 2 on PR #4092 round 1; the + enforcement-layer table in copies 1/3 is relabelled E0-E5 for the same reason. + test_harness: "cargo test -p aprender-contracts --lib -- levels::readme_and_ladder_docs_match_enum" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: mutation engaged -> FAILED. 0 passed; 1 failed ("…book/src/ + verification-ladder.md still pairs a level with the wrong tool outside the generated + block"); restored -> ok. 1 passed; 0 failed. + - id: FALSIFY-PVL-3-005 + rule: "PVL-3-DOCS-ARE-ENUM" + prediction: > + The stale-pairing detector pairs each tool with its NEAREST level token in either + direction. Disabling the level-after-tool arm leaves "Falsification is L2 then + Kani at L4." unflagged; disabling the level-before-tool arm leaves "Level 4 (Kani)" + unflagged. Either turns levels::stale_level_pairings_case_table RED. (Quorum + lane 1, PR #4092 round 3, found the forward-only detector blind to reversed order.) + test_harness: "cargo test -p aprender-contracts --lib -- levels::" + expected_output: "exit 0 on an unmutated tree" + if_fails: > + MEASURED 2026-09-23: after-arm disabled -> FAILED 2 passed; 1 failed ("must flag: + Falsification is L2 then Kani at L4."); before-arm disabled -> FAILED 1 passed; 2 + failed; unmutated -> ok 3 passed. diff --git a/crates/apr-cli/Cargo.toml b/crates/apr-cli/Cargo.toml index 110432c9ce..e4a298bd88 100644 --- a/crates/apr-cli/Cargo.toml +++ b/crates/apr-cli/Cargo.toml @@ -66,7 +66,8 @@ path = "src/main.rs" # Task #91: dry-run scaffold for the SHIP-TWO-001 MODEL-2 corpus ingest. # Minimal, network-free. Does not depend on any apr-cli optional feature. [[bin]] -name = "apr-corpus-ingest" +# #4430: every shipped [[bin]] is aprender-* (was `apr-corpus-ingest`) +name = "aprender-corpus-ingest" path = "src/bin/apr-corpus-ingest.rs" [features] @@ -106,7 +107,10 @@ cuda = ["inference", "realizar/cuda", "entrenar/cuda", "trueno/cuda", "aprender- # the binary was built with it (§2.1 INVALID-BUILD reads that entry). cuda-batch = ["cuda"] # PMAT-335: WGPU backend for AMD/Intel/Apple GPU inference via Vulkan/Metal/WebGPU -wgpu = ["inference"] +# #4056: the finetune wgpu path (commands/finetune.rs) uses entrenar items gated +# behind entrenar's own `gpu` feature. Weak dep: enabled only when `training` +# brings entrenar in, which is exactly when that path compiles. +wgpu = ["inference", "entrenar?/gpu"] visualization = ["renacer", "trueno-viz"] zram = ["trueno-zram-core"] training = ["dep:entrenar", "dep:entrenar-lora", "dep:aprender-train-distill", "dep:aprender-train-common", "aprender-train-distill?/shard-batch-source"] @@ -126,23 +130,23 @@ libc = "0.2" # performed. A surface and its capability must not be able to drift. apr-test = { workspace = true } # Core aprender library -aprender = { path = "../aprender-core", version = "0.69.3", package = "aprender-core", default-features = true, features = ["format-compression"] } +aprender = { path = "../aprender-core", version = "0.70.0", package = "aprender-core", default-features = true, features = ["format-compression"] } # Provable-contracts engine — `apr beat-run` parses + evaluates BeatBenchmark contracts (PMAT-741) aprender-contracts = { workspace = true } # MCP (Model Context Protocol) server — `apr mcp` subcommand -aprender-mcp = { path = "../aprender-mcp", version = "0.69.3", features = ["apr-tools"] } # apr mcp serves apr's own tools (PMAT-3954) +aprender-mcp = { path = "../aprender-mcp", version = "0.70.0", features = ["apr-tools"] } # apr mcp serves apr's own tools (PMAT-3954) async-stream = "0.3" provable-contracts-macros = { workspace = true } # Shared formatting and system utilities (Batuta stack) -batuta-common = { workspace = true } +batuta-common = { workspace = true, features = ["cli-roles"] } # Sovereign coding assistant (apr code → batuta agent engine) — PMAT-182. # Non-optional since claude-code-parity-apr M150 (outcome-parity Phase 3 # requires apr code in default builds). GH-344: path dep via # .cargo/config.toml.dev-overrides; CI uses checkout+symlink. -batuta = { version = "0.69.3", path = "../aprender-orchestrate", package = "aprender-orchestrate", default-features = false, features = ["agents", "agents-inference", "agents-mcp", "rag"] } +batuta = { version = "0.70.0", path = "../aprender-orchestrate", package = "aprender-orchestrate", default-features = false, features = ["agents", "agents-inference", "agents-mcp", "rag"] } # Inference engine (workspace path dep — enables cuda/gpu feature forwarding) # GH-573: shim (0.9.1) didn't forward cuda feature → 0.7 tok/s instead of 273 tok/s @@ -238,7 +242,7 @@ pacha = { workspace = true } # published-crate dependency cycle the APR-MONO consolidation removed, and # (b) was never actually linked, so every `trueno_explain::` path in # commands/ptx_explain.rs failed to resolve. -trueno-explain = { path = "../aprender-explain", version = "0.69.3", package = "aprender-explain", optional = true } +trueno-explain = { path = "../aprender-explain", version = "0.70.0", package = "aprender-explain", optional = true } # ML tuning: LoRA/QLoRA configuration and memory planning (GH-176, PMAT-184) entrenar-lora = { workspace = true, optional = true } @@ -259,6 +263,8 @@ alimentar = { workspace = true } # `dispatch` fn those binaries call, so the surfaces cannot drift. aprender-rag-cli = { workspace = true } aprender-zram-cli = { workspace = true } +# `apr ptx-debug` (#4062): pure-Rust PTX falsification analyzer, always built. +aprender-ptx-debug = { workspace = true } # simular: hand-rolled parser, wired as a verbatim passthrough (apr sim ...) simular = { workspace = true } aprender-cgp = { workspace = true } diff --git a/crates/apr-cli/build.rs b/crates/apr-cli/build.rs index c950c1f129..8ee5028d1e 100644 --- a/crates/apr-cli/build.rs +++ b/crates/apr-cli/build.rs @@ -1,9 +1,10 @@ // Contract: apr-version-traceability-v1 F-VERSION-001..004 (paiml/aprender#597, #1862). // Resolve APR_GIT_SHA with a multi-source fallback hierarchy: // 1. APR_GIT_SHA_OVERRIDE env var (CI/release hook) -// 2. git rev-parse --short HEAD (dev builds from worktree or primary checkout) -// 3. committed .git-sha file (crates.io installs) -// 4. "v{CARGO_PKG_VERSION}+no-git" (informative fallback — never bare "unknown") +// 2. .cargo_vcs_info.json `git.sha1` (crates.io / packaged builds, #4110) +// 3. git rev-parse --short HEAD, retried with the checkout marked safe.directory (#4110) +// 4. committed .git-sha file +// 5. "v{CARGO_PKG_VERSION}+no-git" (informative fallback — never bare "unknown") // // The resolution and its rerun-if-changed triggers live in the shared // `aprender-build-sha` crate (#4219) so every workspace [[bin]] stamps the same SHA. diff --git a/crates/apr-cli/src/bin/apr-corpus-ingest.rs b/crates/apr-cli/src/bin/apr-corpus-ingest.rs index 3c53cd9f2b..a80079efe2 100644 --- a/crates/apr-cli/src/bin/apr-corpus-ingest.rs +++ b/crates/apr-cli/src/bin/apr-corpus-ingest.rs @@ -1,4 +1,4 @@ -//! `apr-corpus-ingest` — dry-run scaffolding for the SHIP-TWO-001 MODEL-2 +//! `aprender-corpus-ingest` — dry-run scaffolding for the SHIP-TWO-001 MODEL-2 //! Python-code pretraining corpus ingest pipeline. //! //! Implements task #91: minimal, NETWORK-FREE scaffolding that reads and @@ -51,7 +51,7 @@ const REQUIRED_TOP_KEYS: &[&str] = &[ #[derive(Parser)] #[command( - name = "apr-corpus-ingest", + name = "aprender-corpus-ingest", version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"), about = "Dry-run scaffold for SHIP-TWO-001 MODEL-2 corpus ingest (C-DATA-THESTACK-PYTHON)" )] @@ -375,7 +375,7 @@ fn build_dry_run_manifest(contract: &CorpusContract, contract_path: &Path) -> Dr DryRunManifest { dry_run: true, generated_at_utc: now_utc_iso8601(), - generator: concat!("apr-corpus-ingest ", env!("CARGO_PKG_VERSION")), + generator: concat!("aprender-corpus-ingest ", env!("CARGO_PKG_VERSION")), contract_path: contract_path.display().to_string(), contract_id: contract.contract_id.clone(), contract_version: contract.version.clone(), @@ -436,21 +436,22 @@ fn civil_from_days(z: i64) -> (i32, u32, u32) { mod tests { use super::*; - /// Resolve the real corpus contract path relative to the workspace root. - /// Tests run with `CARGO_MANIFEST_DIR = .../crates/apr-cli`, so we walk - /// up two levels to reach the contracts directory. - fn real_contract_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .and_then(Path::parent) - .expect("workspace root resolvable from CARGO_MANIFEST_DIR") - .join("contracts/dataset-thestack-python-v1.yaml") + /// The real corpus contract, a workspace file: `None` (a named SKIP) out of tree — the + /// published tarball does not carry `contracts/` — and a FAIL in tree when it is missing + /// (#4149). + fn real_contract_path(test: &str) -> Option { + provable_contracts::workspace_path_or_skip!( + test, + "contracts/dataset-thestack-python-v1.yaml" + ) } #[test] fn parses_real_contract_with_structural_invariants() { - let path = real_contract_path(); - assert!(path.exists(), "real contract missing at {}", path.display()); + let Some(path) = real_contract_path("parses_real_contract_with_structural_invariants") + else { + return; + }; let yaml = fs::read_to_string(&path).expect("read real contract"); let contract: CorpusContract = serde_yaml::from_str(&yaml).expect("contract deserializes"); assert_structural_minimums(&contract).expect("structural minimums hold"); @@ -496,7 +497,11 @@ mod tests { #[test] fn validate_contract_passes_on_real_contract_and_reports_top_keys() { - let path = real_contract_path(); + let Some(path) = + real_contract_path("validate_contract_passes_on_real_contract_and_reports_top_keys") + else { + return; + }; let report = validate_contract(&path).expect("validation passes"); assert_eq!(report.contract_id, "C-DATA-THESTACK-PYTHON"); assert_eq!(report.invariants, 7); diff --git a/crates/apr-cli/src/commands/canary.rs b/crates/apr-cli/src/commands/canary.rs index fc34168804..473a92b9d1 100644 --- a/crates/apr-cli/src/commands/canary.rs +++ b/crates/apr-cli/src/commands/canary.rs @@ -16,6 +16,7 @@ //! ``` use crate::error::{CliError, Result}; +use batuta_common::cli_roles::{ConfigPath, InputFile, ModelPath, OutputPath}; use clap::Subcommand; use colored::Colorize; use serde::{Deserialize, Serialize}; @@ -30,25 +31,25 @@ pub enum CanaryCommands { Create { /// Model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Input file (e.g. wav) #[arg(long)] - input: PathBuf, + input: InputFile, /// Output json file #[arg(long)] - output: PathBuf, + output: OutputPath, }, /// Check against a canary test Check { /// Model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Canary json file #[arg(long)] - canary: PathBuf, + canary: ConfigPath, }, } diff --git a/crates/apr-cli/src/commands/canary_tests.rs b/crates/apr-cli/src/commands/canary_tests.rs index 96d11e8d3c..8ce9a737a5 100644 --- a/crates/apr-cli/src/commands/canary_tests.rs +++ b/crates/apr-cli/src/commands/canary_tests.rs @@ -255,9 +255,9 @@ fn test_build_failure_message_std_drift() { #[test] fn test_canary_commands_create() { let cmd = CanaryCommands::Create { - file: PathBuf::from("model.safetensors"), - input: PathBuf::from("input.wav"), - output: PathBuf::from("canary.json"), + file: PathBuf::from("model.safetensors").into(), + input: PathBuf::from("input.wav").into(), + output: PathBuf::from("canary.json").into(), }; match cmd { CanaryCommands::Create { @@ -276,8 +276,8 @@ fn test_canary_commands_create() { #[test] fn test_canary_commands_check() { let cmd = CanaryCommands::Check { - file: PathBuf::from("model.safetensors"), - canary: PathBuf::from("canary.json"), + file: PathBuf::from("model.safetensors").into(), + canary: PathBuf::from("canary.json").into(), }; match cmd { CanaryCommands::Check { file, canary } => { @@ -291,9 +291,9 @@ fn test_canary_commands_check() { #[test] fn test_canary_commands_clone() { let cmd = CanaryCommands::Create { - file: PathBuf::from("model.safetensors"), - input: PathBuf::from("input.wav"), - output: PathBuf::from("canary.json"), + file: PathBuf::from("model.safetensors").into(), + input: PathBuf::from("input.wav").into(), + output: PathBuf::from("canary.json").into(), }; let cloned = cmd.clone(); match cloned { @@ -307,8 +307,8 @@ fn test_canary_commands_clone() { #[test] fn test_canary_commands_debug() { let cmd = CanaryCommands::Check { - file: PathBuf::from("model.safetensors"), - canary: PathBuf::from("canary.json"), + file: PathBuf::from("model.safetensors").into(), + canary: PathBuf::from("canary.json").into(), }; let debug = format!("{cmd:?}"); assert!(debug.contains("Check")); @@ -323,9 +323,9 @@ fn test_run_create_model_not_found() { let output = NamedTempFile::with_suffix(".json").expect("create output"); let input = NamedTempFile::with_suffix(".wav").expect("create input"); let cmd = CanaryCommands::Create { - file: PathBuf::from("/nonexistent/model.safetensors"), - input: input.path().to_path_buf(), - output: output.path().to_path_buf(), + file: PathBuf::from("/nonexistent/model.safetensors").into(), + input: input.path().to_path_buf().into(), + output: output.path().to_path_buf().into(), }; let result = run(cmd); assert!(result.is_err()); @@ -341,9 +341,9 @@ fn test_run_create_invalid_model() { let input = NamedTempFile::with_suffix(".wav").expect("create input"); let cmd = CanaryCommands::Create { - file: model.path().to_path_buf(), - input: input.path().to_path_buf(), - output: output.path().to_path_buf(), + file: model.path().to_path_buf().into(), + input: input.path().to_path_buf().into(), + output: output.path().to_path_buf().into(), }; let result = run(cmd); assert!(result.is_err()); @@ -357,8 +357,8 @@ fn test_run_check_model_not_found() { .expect("write"); let cmd = CanaryCommands::Check { - file: PathBuf::from("/nonexistent/model.safetensors"), - canary: canary.path().to_path_buf(), + file: PathBuf::from("/nonexistent/model.safetensors").into(), + canary: canary.path().to_path_buf().into(), }; let result = run(cmd); assert!(result.is_err()); @@ -370,8 +370,8 @@ fn test_run_check_canary_not_found() { model.write_all(b"fake model").expect("write"); let cmd = CanaryCommands::Check { - file: model.path().to_path_buf(), - canary: PathBuf::from("/nonexistent/canary.json"), + file: model.path().to_path_buf().into(), + canary: PathBuf::from("/nonexistent/canary.json").into(), }; let result = run(cmd); assert!(result.is_err()); @@ -385,8 +385,8 @@ fn test_run_check_invalid_canary() { canary.write_all(b"not valid json").expect("write"); let cmd = CanaryCommands::Check { - file: model.path().to_path_buf(), - canary: canary.path().to_path_buf(), + file: model.path().to_path_buf().into(), + canary: canary.path().to_path_buf().into(), }; let result = run(cmd); assert!(result.is_err()); @@ -1169,9 +1169,9 @@ fn test_run_create_input_not_found() { let output = NamedTempFile::with_suffix(".json").expect("create output"); let cmd = CanaryCommands::Create { - file: model.path().to_path_buf(), - input: PathBuf::from("/nonexistent/input.wav"), - output: output.path().to_path_buf(), + file: model.path().to_path_buf().into(), + input: PathBuf::from("/nonexistent/input.wav").into(), + output: output.path().to_path_buf().into(), }; let result = run(cmd); assert!(result.is_err(), "missing input file should error"); @@ -1185,9 +1185,9 @@ fn test_run_create_empty_input_path() { let output = NamedTempFile::with_suffix(".json").expect("create output"); let cmd = CanaryCommands::Create { - file: model.path().to_path_buf(), - input: PathBuf::from(""), - output: output.path().to_path_buf(), + file: model.path().to_path_buf().into(), + input: PathBuf::from("").into(), + output: output.path().to_path_buf().into(), }; // Will fail on format detection, not input validation let result = run(cmd); diff --git a/crates/apr-cli/src/commands/chat.rs b/crates/apr-cli/src/commands/chat.rs index 585221f3d1..31c72bdc5e 100644 --- a/crates/apr-cli/src/commands/chat.rs +++ b/crates/apr-cli/src/commands/chat.rs @@ -632,8 +632,8 @@ fn clean_chat_response(raw: &str) -> String { trimmed.to_string() } -/// Detect format from magic bytes (more reliable than extension) -#[cfg(feature = "inference")] +/// Detect format from magic bytes (more reliable than extension). Pure, so not feature-gated: its caller +/// `format_from_leading_bytes` is compiled in every build (#4041). fn detect_format_from_bytes(data: &[u8]) -> ModelFormat { if data.len() < 8 { return ModelFormat::Demo; diff --git a/crates/apr-cli/src/commands/diff.rs b/crates/apr-cli/src/commands/diff.rs index e8d77e1148..94b48a057a 100644 --- a/crates/apr-cli/src/commands/diff.rs +++ b/crates/apr-cli/src/commands/diff.rs @@ -245,7 +245,16 @@ pub(crate) fn run( // and run an element-wise diff per `apr-cli-trace-save-tensor-v1` // `apr_diff_values_compat` invariant. if is_aprt_stage_file(path1) && is_aprt_stage_file(path2) { + #[cfg(feature = "inference")] return run_aprt_stage_diff(path1, path2, limit, json_output); + // #4041: the stage-file reader is realizar's. A minimal build names the missing feature + // instead of falling through to the model walker, which would misread an APRT file. + #[cfg(not(feature = "inference"))] + return Err(CliError::ValidationFailed( + "comparing APRT stage tensor files needs the `inference` feature (this apr was built \ + without it)" + .to_string(), + )); } // Run tensor value comparison run_tensor_value_diff(path1, path2, filter, limit, transpose_aware, json_output) diff --git a/crates/apr-cli/src/commands/diff_05_aprt_stage.rs b/crates/apr-cli/src/commands/diff_05_aprt_stage.rs index 7dac722c9e..6cda8dc4c4 100644 --- a/crates/apr-cli/src/commands/diff_05_aprt_stage.rs +++ b/crates/apr-cli/src/commands/diff_05_aprt_stage.rs @@ -91,6 +91,7 @@ fn compute_aprt_stage_stats( // serde_json::json!() macro uses infallible unwrap internally #[allow(clippy::disallowed_methods)] +#[cfg(feature = "inference")] fn run_aprt_stage_diff( path1: &Path, path2: &Path, @@ -223,7 +224,7 @@ fn run_aprt_stage_diff( Ok(()) } -#[cfg(test)] +#[cfg(all(test, feature = "inference"))] mod aprt_stage_diff_tests { use super::*; use std::io::Write; diff --git a/crates/apr-cli/src/commands/explain.rs b/crates/apr-cli/src/commands/explain.rs index ec9d43c293..a1e39bbb44 100644 --- a/crates/apr-cli/src/commands/explain.rs +++ b/crates/apr-cli/src/commands/explain.rs @@ -217,6 +217,20 @@ fn diagnose_missing_model_type(path: &Path, content: &str, json: bool) { } } +/// `name` beside `path`, if it exists. With `inference`, realizar's finder, which also resolves hash-prefixed and +/// symlinked model paths (CB-510). A minimal build (#4041: `--no-default-features` has no realizar) checks the +/// literal sibling only, so a hash-prefixed HF-cache path finds nothing there and reports not-found. +fn sibling_file(path: &Path, name: &str) -> Option { + #[cfg(feature = "inference")] + { + realizar::safetensors::find_sibling_file(path, name) + } + #[cfg(not(feature = "inference"))] + { + path.parent().map(|d| d.join(name)).filter(|p| p.is_file()) + } +} + /// Resolve family from a model file by finding config.json in the same directory. fn resolve_family_from_model_file( path: &Path, @@ -228,7 +242,7 @@ fn resolve_family_from_model_file( // Companion lookup: find_sibling_file handles hash-prefixed / symlinked // model paths robustly (CB-510 / publish-safety) and only returns Some // when the file exists — subsuming the prior with_file_name + .exists(). - if let Some(config_path) = realizar::safetensors::find_sibling_file(&real_path, "config.json") { + if let Some(config_path) = sibling_file(&real_path, "config.json") { resolve_from_config_json(&config_path) } else { emit_kernel_error( @@ -341,7 +355,7 @@ fn resolve_config_mapping( // hash-prefixed / symlinked model paths); fall back to the literal // sibling dir so extract_config_mapping reports not-found as before // (parent().join avoids with_file_name per publish-safety). - realizar::safetensors::find_sibling_file(p, "config.json").unwrap_or_else(|| { + sibling_file(p, "config.json").unwrap_or_else(|| { p.parent().map_or_else( || std::path::PathBuf::from("config.json"), |d| d.join("config.json"), diff --git a/crates/apr-cli/src/commands/finetune.rs b/crates/apr-cli/src/commands/finetune.rs index 48acddf75a..7433cb2332 100644 --- a/crates/apr-cli/src/commands/finetune.rs +++ b/crates/apr-cli/src/commands/finetune.rs @@ -650,6 +650,8 @@ fn execute_training_wgpu( let mut fwd = trueno::backends::gpu::WgslForwardPass::new( gpu.device, gpu.queue, hidden, heads, kv_heads, head_dim, inter, ); + // #4056: the WGSL RMSNorm takes the model's eps (it hardcoded 1e-6). + fwd.set_rms_norm_eps(model_config.rms_norm_eps as f32); // 3. Streaming dequant + upload weights to GPU let lm_head_f32 = dequant_and_upload_weights(&mut fwd, &q_model)?; diff --git a/crates/apr-cli/src/commands/golden_output.rs b/crates/apr-cli/src/commands/golden_output.rs index 9cf72107e5..6833e3352b 100644 --- a/crates/apr-cli/src/commands/golden_output.rs +++ b/crates/apr-cli/src/commands/golden_output.rs @@ -2313,25 +2313,13 @@ include!("throughput.rs"); /// A workspace file read at RUN time for tests (#4129, the #4048 pattern agreed with #4130). /// /// `include_str!` of a path outside this crate cannot compile from the published tarball, and -/// a run-time read that `expect`s the file panics there. In tree (the workspace's `contracts/` -/// beside the crate) a missing file FAILS the test; only out of tree does it print -/// `SKIP : ...` and return `None`. `rel` is relative to the repository root. +/// a run-time read that `expect`s the file panics there. The in-tree decision is the shared +/// rule, `provable_contracts::workspace_file_or_skip!` (#4175): in tree a missing file FAILS the +/// test; only out of tree does it print `SKIP : ...` and return `None`. `rel` is relative +/// to the repository root. #[cfg(test)] fn workspace_file_or_skip(test: &str, rel: &str) -> Option { - let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); - if !root.join("contracts").is_dir() { - eprintln!( - "SKIP {test}: out of tree (no {} beside this crate) - {rel} lives in the workspace, \ - which a published crate does not carry (#4129)", - root.join("contracts").display() - ); - return None; - } - let path = root.join(rel); - Some( - std::fs::read_to_string(&path) - .unwrap_or_else(|e| panic!("in tree, {} must be readable: {e}", path.display())), - ) + provable_contracts::workspace_file_or_skip!(test, rel) } /// #3914: which string the template detector is keyed on. diff --git a/crates/apr-cli/src/commands/gpu_correct_subject.rs b/crates/apr-cli/src/commands/gpu_correct_subject.rs new file mode 100644 index 0000000000..d256d4d020 --- /dev/null +++ b/crates/apr-cli/src/commands/gpu_correct_subject.rs @@ -0,0 +1,528 @@ +// #3821: GPU-correct(model_sha256, host, apr_version) is ONE subject, not a +// per-surface opinion. +// +// Two surfaces decide it, in the same binary, on the same host, for the same +// model, and until now neither recorded the other's answer: +// +// * `apr run --gpu` — the F2 parity guard. REFUSES a diverging GPU +// (`BackendUnavailable`, exit 14). +// * `apr qa` `golden_output` — generates on the GPU and scores the text. +// It calls `generate_gpu_resident` directly, so it never reaches the +// guard (#3804: grepping a failing run's stderr for the guard's own lines +// returns zero occurrences). +// +// On qwen2.5-coder-0.5b-instruct-q4_k_m at `a9502d992` the guard refused at +// cosine 0.4153 while the gate shipped the symptom as ``` ``` ``` repetition. +// Both happened to say "bad", so nothing noticed they were never compared. +// The dangerous ordering is the other one: the guard refusing while the gate +// PASSES, which `APR_F2_PROBE_PATH=serial` produces on demand — serial parity +// is cosine >= 0.9978 at all 16 positions, so the guard accepts, and +// generation still runs the batched prefill that emits the fragment. +// +// REPORT_ONLY for 0.69.1 (#3821's ruling): this reconciles and NAMES, it does +// not yet block. Flipping it to blocking is a one-line change at the caller, +// deliberately deferred until it has been green on a real release commit — +// a gate never green on its own target is not a gate (#3731). + +/// One surface's verdict about the subject, with the evidence it decided on. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct SurfaceVerdict { + /// The surface that decided, named as the user would invoke it. + pub surface: &'static str, + pub verdict: provable_contracts::ontology::verdict::Verdict, + /// What it measured. Printed verbatim in a contradiction. + pub detail: String, +} + +impl SurfaceVerdict { + pub fn new( + surface: &'static str, + verdict: provable_contracts::ontology::verdict::Verdict, + detail: impl Into, + ) -> Self { + Self { + surface, + verdict, + detail: detail.into(), + } + } + + /// A surface that was never evaluated on this subject. Absence is + /// `Unknown(NotRun)`, never agreement (#3821 done_when 3). + pub fn not_run(surface: &'static str) -> Self { + use provable_contracts::ontology::verdict::{Reason, Verdict}; + Self::new( + surface, + Verdict::Unknown(Reason::NotRun), + "not evaluated on this subject", + ) + } +} + +/// The reconciled answer for one subject. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct SubjectReport { + /// The ONT-6 meet over every surface. `Fail < Unknown(_) < Pass`, so this + /// is `min` and a single Fail carries. + pub verdict: provable_contracts::ontology::verdict::Verdict, + /// Set iff at least one surface said Pass AND at least one said Fail. + /// Names every surface and its value — the point of the row is that a + /// disagreement is reported rather than silently minned away. + pub contradiction: Option, + /// Surfaces that never evaluated this subject, named. + pub not_run: Vec<&'static str>, +} + +/// Reconcile every surface's verdict about ONE subject. +/// +/// Pure and GPU-free, so the contradiction logic is testable on a host with no +/// CUDA — which is the only way this row could have been tested at all. +pub(crate) fn reconcile_gpu_correct(subject: &str, surfaces: &[SurfaceVerdict]) -> SubjectReport { + use provable_contracts::ontology::verdict::{Reason, Verdict}; + + if surfaces.is_empty() { + return SubjectReport { + verdict: Verdict::Unknown(Reason::NotRun), + contradiction: None, + not_run: Vec::new(), + }; + } + + let verdict = surfaces + .iter() + .map(|s| s.verdict) + .fold(Verdict::Pass, Verdict::meet); + + let passed: Vec<&SurfaceVerdict> = surfaces + .iter() + .filter(|s| s.verdict == Verdict::Pass) + .collect(); + let failed: Vec<&SurfaceVerdict> = surfaces + .iter() + .filter(|s| s.verdict == Verdict::Fail) + .collect(); + + let contradiction = (!passed.is_empty() && !failed.is_empty()).then(|| { + let mut lines = vec![format!( + "CONTRADICTION on {subject}: GPU-correct is one subject and {} of {} surfaces \ + disagree about it.", + passed.len() + failed.len(), + surfaces.len() + )]; + for s in surfaces { + lines.push(format!(" - {}: {:?} — {}", s.surface, s.verdict, s.detail)); + } + lines.push( + " Both cannot be right about the same model on the same host in the same binary." + .to_string(), + ); + lines.join("\n") + }); + + let not_run = surfaces + .iter() + .filter(|s| s.verdict == Verdict::Unknown(Reason::NotRun)) + .map(|s| s.surface) + .collect(); + + SubjectReport { + verdict, + contradiction, + not_run, + } +} + +/// The subject tuple, rendered. Keyed on the model's CONTENT, not its path — +/// two files with the same name are not the same subject, and the same bytes +/// under two names are. +pub(crate) fn gpu_correct_subject(model_sha256: &str, host: &str, apr_version: &str) -> String { + let short = model_sha256.get(..12).unwrap_or(model_sha256); + format!("GPU-correct(model {short}, host {host}, apr {apr_version})") +} + +/// The two surfaces' names, as a user would invoke them. +pub(crate) const F2_GUARD_SURFACE: &str = "apr run --gpu (F2 parity guard)"; +pub(crate) const GOLDEN_SURFACE: &str = "apr qa golden_output"; + +/// The F2 guard's answer as a surface verdict. The guard is a probe that +/// always runs to a decision, so it is never `Unknown`. +pub(crate) fn f2_guard_verdict(admits: bool) -> SurfaceVerdict { + use provable_contracts::ontology::verdict::Verdict; + if admits { + SurfaceVerdict::new( + F2_GUARD_SURFACE, + Verdict::Pass, + "admitted: GPU first tokens match CPU", + ) + } else { + SurfaceVerdict::new( + F2_GUARD_SURFACE, + Verdict::Fail, + "REFUSED: GPU output diverges from CPU (apr run --gpu would fall back / exit 14)", + ) + } +} + +/// The golden gate's wiring (#3821): reconcile the guard's verdict with the +/// leg's, on the same model and host, and return the contradiction text when +/// they disagree. `subject` is only evaluated then — it hashes the model, which +/// is seconds on a multi-GB GGUF and not worth paying when they agree. +/// +/// REPORT_ONLY: the caller prints this and the gate verdict is unchanged. +pub(crate) fn golden_contradiction( + guard_admits: bool, + leg: &crate::commands::qa::GpuGoldenLeg, + subject: impl FnOnce() -> String, +) -> Option { + use provable_contracts::ontology::verdict::Verdict; + let guard = f2_guard_verdict(guard_admits); + let golden = SurfaceVerdict::new(GOLDEN_SURFACE, leg.subject_verdict(), leg.subject_detail()); + let pair = [guard, golden]; + let says = |v: Verdict| pair.iter().any(|s| s.verdict == v); + if !(says(Verdict::Pass) && says(Verdict::Fail)) { + return None; + } + reconcile_gpu_correct(&subject(), &pair).contradiction +} + +/// The subject for a mapped GGUF on this host and this binary. +pub(crate) fn subject_of_bytes(model_bytes: &[u8]) -> String { + use sha2::{Digest, Sha256}; + let sha = format!("{:x}", Sha256::digest(model_bytes)); + let host = std::fs::read_to_string("/etc/hostname") + .ok() + .map(|h| h.trim().to_string()) + .filter(|h| !h.is_empty()) + .or_else(|| std::env::var("HOSTNAME").ok()) + .unwrap_or_else(|| "unknown-host".to_string()); + gpu_correct_subject(&sha, &host, env!("CARGO_PKG_VERSION")) +} + +// NOTE ON NAMING: `Makefile`'s coverage target passes `--skip gpu_`, and +// libtest matches that substring against the WHOLE test path. Every name below +// therefore avoids the sequence `gpu_` — otherwise these rows would compile, +// pass locally, and be invisible to the coverage gate. The module is named +// `subject_verdict_tests` for the same reason (#3839). +// The golden gate's own leg, expressed in the same lattice as the guard so the +// two can be compared at all. #3821: until this existed the two surfaces spoke +// different languages — one returned bool, the other a five-variant enum — and +// "compare them" had no meaning. +impl crate::commands::qa::GpuGoldenLeg { + /// This leg's verdict ABOUT THE SUBJECT, which is not the same as whether + /// the gate passed. A leg that never ran, errored, or ran out of budget + /// says nothing about `GPU-correct`; only `Passed` and `WrongAnswer` do. + pub(crate) fn subject_verdict(&self) -> provable_contracts::ontology::verdict::Verdict { + use provable_contracts::ontology::verdict::{Reason, Verdict}; + match self { + Self::Passed => Verdict::Pass, + Self::WrongAnswer(_) => Verdict::Fail, + // Attempted, but produced nothing judgeable. Not evidence either way. + Self::Errored(_) | Self::Unclosed { .. } | Self::NotRun(_) => { + Verdict::Unknown(Reason::NotRun) + } + } + } + + /// What this leg measured, for a contradiction message. + pub(crate) fn subject_detail(&self) -> String { + match self { + Self::Passed => "golden patterns matched on the device".to_string(), + Self::WrongAnswer(why) => why.clone(), + Self::Errored(e) => format!("errored: {e}"), + Self::Unclosed { + budget, + generated_chars, + } => format!( + "still reasoning at the {budget}-token budget ({generated_chars} chars, no answer)" + ), + Self::NotRun(why) => (*why).to_string(), + } + } +} + +#[cfg(test)] +mod subject_verdict_tests { + use super::{gpu_correct_subject, reconcile_gpu_correct, SurfaceVerdict}; + use provable_contracts::ontology::verdict::{Reason, Verdict}; + + const RUN: &str = "apr run --gpu (F2 parity guard)"; + const QA: &str = "apr qa golden_output"; + + fn subject() -> String { + gpu_correct_subject("d98cdcbd03e17ce47681435b5150e34c", "lambda", "0.69.1") + } + + /// The row's whole reason for existing. `meet` already yields Fail, so a + /// silent `min` would look like an ordinary failure — indistinguishable + /// from both surfaces agreeing the model is bad. A contradiction is a + /// different fact and must READ differently. + #[test] + fn a_pass_and_a_fail_on_one_subject_are_named_as_a_contradiction() { + let r = reconcile_gpu_correct( + &subject(), + &[ + SurfaceVerdict::new(RUN, Verdict::Fail, "diverges at position 1, cosine 0.4153"), + SurfaceVerdict::new(QA, Verdict::Pass, "3 golden test cases passed"), + ], + ); + assert_eq!(r.verdict, Verdict::Fail, "the meet still carries the Fail"); + let msg = r + .contradiction + .expect("a Pass beside a Fail is a contradiction"); + assert!(msg.contains("CONTRADICTION")); + assert!( + msg.contains(RUN) && msg.contains(QA), + "both surfaces must be named: {msg}" + ); + assert!( + msg.contains("0.4153") && msg.contains("3 golden test cases passed"), + "both VALUES must be named, not just the surfaces: {msg}" + ); + } + + /// Agreement is not a contradiction, in either direction. A row that fired + /// on agreement would be noise and would be turned off. + #[test] + fn surfaces_that_agree_are_not_reported_as_contradicting() { + for v in [Verdict::Pass, Verdict::Fail] { + let r = reconcile_gpu_correct( + &subject(), + &[ + SurfaceVerdict::new(RUN, v, "x"), + SurfaceVerdict::new(QA, v, "y"), + ], + ); + assert_eq!(r.verdict, v); + assert!( + r.contradiction.is_none(), + "agreement on {v:?} is not a contradiction" + ); + } + } + + /// #3821 done_when 3. One surface passing while the other never ran must + /// NOT read as a pass — that is how a gate nobody executed becomes + /// "green". The lattice gives Unknown, and the unrun surface is named. + #[test] + fn absence_is_not_agreement() { + let r = reconcile_gpu_correct( + &subject(), + &[ + SurfaceVerdict::new(RUN, Verdict::Pass, "admitted"), + SurfaceVerdict::not_run(QA), + ], + ); + assert_eq!( + r.verdict, + Verdict::Unknown(Reason::NotRun), + "a Pass beside an unrun surface is Unknown, never Pass" + ); + assert!(!r.verdict.arm(), "Unknown must not arm"); + assert_eq!(r.not_run, vec![QA], "the unrun surface must be named"); + assert!(r.contradiction.is_none(), "not running is not disagreeing"); + } + + /// The meet is the ONT-6 lattice min over all three levels, and a Fail + /// beside an Unknown is still a Fail — the strongest claim wins. + #[test] + fn the_verdict_is_the_lattice_meet() { + let cases: &[(Verdict, Verdict, Verdict)] = &[ + (Verdict::Pass, Verdict::Pass, Verdict::Pass), + (Verdict::Pass, Verdict::Fail, Verdict::Fail), + ( + Verdict::Fail, + Verdict::Unknown(Reason::NotRun), + Verdict::Fail, + ), + ( + Verdict::Pass, + Verdict::Unknown(Reason::NotRun), + Verdict::Unknown(Reason::NotRun), + ), + (Verdict::Fail, Verdict::Fail, Verdict::Fail), + ]; + let wrong: Vec = cases + .iter() + .filter_map(|(a, b, want)| { + let got = reconcile_gpu_correct( + &subject(), + &[ + SurfaceVerdict::new(RUN, *a, ""), + SurfaceVerdict::new(QA, *b, ""), + ], + ) + .verdict; + (got != *want) + .then(|| format!("\n - {a:?} meet {b:?}: expected {want:?}, got {got:?}")) + }) + .collect(); + assert!( + wrong.is_empty(), + "the meet is not the lattice min:{}", + wrong.join("") + ); + } + + /// Nothing measured is Unknown, never Pass. An empty surface set is how a + /// subject nobody looked at would otherwise arrive as green. + #[test] + fn a_subject_no_surface_evaluated_is_unknown_not_pass() { + let r = reconcile_gpu_correct(&subject(), &[]); + assert_eq!(r.verdict, Verdict::Unknown(Reason::NotRun)); + assert!(!r.verdict.arm()); + assert!(r.contradiction.is_none()); + } + + /// The subject is keyed on the model's CONTENT. Two files with the same + /// name are not one subject; the same bytes under two names are. + #[test] + fn the_subject_is_keyed_on_content_and_host_and_version() { + let a = gpu_correct_subject("aaaaaaaaaaaabbbb", "lambda", "0.69.1"); + assert_eq!( + a, + gpu_correct_subject("aaaaaaaaaaaabbbb", "lambda", "0.69.1") + ); + assert_ne!( + a, + gpu_correct_subject("ccccccccccccbbbb", "lambda", "0.69.1") + ); + assert_ne!(a, gpu_correct_subject("aaaaaaaaaaaabbbb", "gx10", "0.69.1")); + assert_ne!( + a, + gpu_correct_subject("aaaaaaaaaaaabbbb", "lambda", "0.69.0") + ); + assert!(a.contains("aaaaaaaaaaaa"), "the sha must appear: {a}"); + assert!( + !a.contains("bbbb"), + "only the first 12 hex are printed: {a}" + ); + } + + /// Only `Passed` and `WrongAnswer` are evidence about the subject. The + /// other three mean the gate produced nothing judgeable, and a gate that + /// produced nothing must not read as agreement with whatever the guard + /// said — that is how an unrun surface becomes a green one. + #[test] + fn only_a_judged_answer_is_evidence_about_the_subject() { + use crate::commands::qa::GpuGoldenLeg; + let cases: Vec<(GpuGoldenLeg, Verdict)> = vec![ + (GpuGoldenLeg::Passed, Verdict::Pass), + (GpuGoldenLeg::WrongAnswer("gibberish".into()), Verdict::Fail), + ( + GpuGoldenLeg::Errored("CUDA init".into()), + Verdict::Unknown(Reason::NotRun), + ), + ( + GpuGoldenLeg::NotRun("no device"), + Verdict::Unknown(Reason::NotRun), + ), + ]; + let wrong: Vec = cases + .iter() + .filter_map(|(leg, want)| { + let got = leg.subject_verdict(); + (got != *want).then(|| format!("\n - {leg:?}: expected {want:?}, got {got:?}")) + }) + .collect(); + assert!( + wrong.is_empty(), + "leg-to-verdict mapping drifted:{}", + wrong.join("") + ); + } + + /// The detail is what a contradiction message prints, so a leg that says + /// nothing useful makes the contradiction unreadable. + #[test] + fn every_leg_states_what_it_measured() { + use crate::commands::qa::GpuGoldenLeg; + for leg in [ + GpuGoldenLeg::Passed, + GpuGoldenLeg::WrongAnswer("fragment repeats".into()), + GpuGoldenLeg::Errored("init".into()), + GpuGoldenLeg::NotRun("no device"), + ] { + let d = leg.subject_detail(); + assert!(!d.trim().is_empty(), "{leg:?} produced an empty detail"); + } + assert!( + GpuGoldenLeg::WrongAnswer("fragment repeats".into()) + .subject_detail() + .contains("fragment repeats"), + "the reason must survive into the detail" + ); + } + + /// The golden wiring: every (guard, leg) pair, and whether it names a + /// contradiction. The dangerous row is guard-refuses + gate-passes, which + /// `meet` alone would report as an ordinary Fail. + #[test] + fn the_golden_wiring_names_exactly_the_disagreeing_pairs() { + use super::golden_contradiction; + use crate::commands::qa::GpuGoldenLeg; + let legs = || { + vec![ + GpuGoldenLeg::Passed, + GpuGoldenLeg::WrongAnswer("fragment repeats".into()), + GpuGoldenLeg::Errored("init".into()), + GpuGoldenLeg::NotRun("no device"), + ] + }; + let mut got = Vec::new(); + for admits in [true, false] { + for leg in legs() { + let c = golden_contradiction(admits, &leg, subject); + got.push((admits, format!("{leg:?}"), c.is_some())); + } + } + let named: Vec<_> = got + .iter() + .filter(|r| r.2) + .map(|r| (r.0, r.1.clone())) + .collect(); + assert_eq!( + named, + vec![ + ( + true, + format!("{:?}", GpuGoldenLeg::WrongAnswer("fragment repeats".into())) + ), + (false, format!("{:?}", GpuGoldenLeg::Passed)), + ], + "only guard-admits+wrong-answer and guard-refuses+passed contradict" + ); + let text = + golden_contradiction(false, &GpuGoldenLeg::Passed, subject).expect("contradiction"); + assert!( + text.contains("REFUSED") && text.contains(QA) && text.contains(RUN), + "{text}" + ); + } + + /// The subject is hashed only when there is something to report. + #[test] + fn agreement_never_hashes_the_model() { + use super::golden_contradiction; + use crate::commands::qa::GpuGoldenLeg; + let c = golden_contradiction(true, &GpuGoldenLeg::Passed, || { + panic!("subject evaluated on agreement") + }); + assert!(c.is_none()); + } + + /// The subject names content, host and version. + #[test] + fn the_subject_of_bytes_is_the_content_digest() { + let s = super::subject_of_bytes(b"abc"); + // sha256("abc") = ba7816bf8f01... + assert!(s.contains("model ba7816bf8f01"), "{s}"); + assert!(s.contains(env!("CARGO_PKG_VERSION")), "{s}"); + } + + /// A short or empty sha must not panic on the 12-char slice. + #[test] + fn a_short_digest_does_not_panic() { + assert!(gpu_correct_subject("abc", "h", "v").contains("abc")); + assert!(gpu_correct_subject("", "h", "v").contains("host h")); + } +} diff --git a/crates/apr-cli/src/commands/mono.rs b/crates/apr-cli/src/commands/mono.rs index f6c4664bcd..f3fe5758c8 100644 --- a/crates/apr-cli/src/commands/mono.rs +++ b/crates/apr-cli/src/commands/mono.rs @@ -9,6 +9,7 @@ //! Contract: contracts/publish-workspace-v1.yaml use crate::error::CliError; +use batuta_common::cli_roles::FreeText; use clap::Subcommand; use std::collections::BTreeMap; use std::io::Write; @@ -28,7 +29,7 @@ pub enum MonoCommands { Shims { /// Output directory for shim crates #[arg(long, default_value = "shims")] - output: String, + output: FreeText, }, /// Verify workspace invariants (FALSIFY-MONO-* and FALSIFY-BUILD-*) diff --git a/crates/apr-cli/src/commands/output_verification.rs b/crates/apr-cli/src/commands/output_verification.rs index e5642dd5e9..783232c6e6 100644 --- a/crates/apr-cli/src/commands/output_verification.rs +++ b/crates/apr-cli/src/commands/output_verification.rs @@ -834,25 +834,37 @@ fn validate_gpu_golden_output( } let model = OwnedQuantizedModel::from_mapped(mapped) .map_err(|e| CliError::ValidationFailed(format!("Model failed: {e}")))?; - let generated = match OwnedQuantizedModelCuda::new(model, 0) { - Ok(cuda_model) => qa_dense_generate( - &mut qa_dense_cuda(cuda_model), - prompt_tokens, - gen_config, - true, - ) - .map(|gpu_tokens| gguf.decode(&gpu_tokens)) - .map_err(|e| format!("GPU generation: {e}")), - Err(e) => Err(format!("CUDA init on device 0: {e}")), + // #3821: consult the SAME F2 parity guard `apr run --gpu` does, on the same + // prompt, before generating — so the two surfaces that decide GPU-correct + // for this model are compared instead of forming separate opinions. + // `None` when the guard measured nothing: absence is never agreement. + let (guard_admits, generated) = match OwnedQuantizedModelCuda::new(model, 0) { + Ok(mut cuda_model) => { + let admits = + realizar::infer::gpu_parity_admits(&mut cuda_model, gen_config, prompt_tokens); + let generated = qa_dense_generate( + &mut qa_dense_cuda(cuda_model), + prompt_tokens, + gen_config, + true, + ) + .map(|gpu_tokens| gguf.decode(&gpu_tokens)) + .map_err(|e| format!("GPU generation: {e}")); + (admits, generated) + }, + Err(e) => (None, Err(format!("CUDA init on device 0: {e}"))), }; // #3711 + #3724: ONE typed leg. The budget is passed so the leg can // distinguish "still reasoning when the budget ran out" from "answered // wrongly" and from "never started" — three outcomes, not two. - Ok(GpuGoldenLeg::judge( - generated, - expected_patterns, - gen_config.max_tokens, - )) + let leg = GpuGoldenLeg::judge(generated, expected_patterns, gen_config.max_tokens); + // REPORT_ONLY (#3821's ruling): name a contradiction, do not change the verdict. + if let Some(admits) = guard_admits { + if let Some(text) = golden_contradiction(admits, &leg, || subject_of_bytes(mapped.data())) { + eprintln!("{}", text.red()); + } + } + Ok(leg) } /// Note, in a verbose human-readable run, that the GPU half of the golden gate diff --git a/crates/apr-cli/src/commands/pretrain.rs b/crates/apr-cli/src/commands/pretrain.rs index f7dac32a45..627087bdb0 100644 --- a/crates/apr-cli/src/commands/pretrain.rs +++ b/crates/apr-cli/src/commands/pretrain.rs @@ -1832,6 +1832,7 @@ mod tests { .expect("spawn parse thread") .join() .expect("parse thread must not panic") + .into() } #[test] @@ -1900,6 +1901,7 @@ mod tests { .expect("spawn parse thread") .join() .expect("parse thread must not panic") + .map(Into::into) } /// FALSIFY-APR-PRETRAIN-INIT-001: --init flag exists in clap surface. diff --git a/crates/apr-cli/src/commands/profile_ollama.rs b/crates/apr-cli/src/commands/profile_ollama.rs index 21f61ef7c2..666d4f22f6 100644 --- a/crates/apr-cli/src/commands/profile_ollama.rs +++ b/crates/apr-cli/src/commands/profile_ollama.rs @@ -404,8 +404,7 @@ fn safe_ratio(num: f64, denom: f64) -> f64 { /// Printed in place of a memory-vs-compute verdict while the arithmetic /// intensity is not measured. A magnitude may be reported; a verdict decided by -/// a number nothing measured may not. -#[cfg(feature = "inference")] +/// a number nothing measured may not. Not feature-gated: `print_roofline_section` reads it in every build (#4041). pub(crate) const ROOFLINE_AI_UNMEASURED: &str = "UNMEASURED"; /// Does the arithmetic intensity actually depend on the workload? diff --git a/crates/apr-cli/src/commands/qa.rs b/crates/apr-cli/src/commands/qa.rs index 8bd0d872ae..341e13303b 100644 --- a/crates/apr-cli/src/commands/qa.rs +++ b/crates/apr-cli/src/commands/qa.rs @@ -771,6 +771,7 @@ fn print_qa_summary(gates: &[GateResult], passed: bool, total_duration: Duration include!("qa_gguf.rs"); include!("output_verification.rs"); +include!("gpu_correct_subject.rs"); include!("golden_output.rs"); include!("speedup.rs"); include!("forward_error.rs"); diff --git a/crates/apr-cli/src/commands/qa_tests_report_pass_fail.rs b/crates/apr-cli/src/commands/qa_tests_report_pass_fail.rs index 1fd5a23701..6626dac1d6 100644 --- a/crates/apr-cli/src/commands/qa_tests_report_pass_fail.rs +++ b/crates/apr-cli/src/commands/qa_tests_report_pass_fail.rs @@ -605,3 +605,27 @@ "Empty temp file should map to 0.5b via file size heuristic" ); } + + /// #3714: the throughput label names what the timed runs REPORTED. A cuda + /// build whose runs all fell back to the CPU must not print "GPU". + #[cfg(feature = "inference")] + #[test] + fn runtime_backend_label_follows_used_gpu_not_the_build() { + assert_eq!( + runtime_backend_label(3, 3), + "runtime entry point, GPU on every timed run" + ); + assert_eq!( + runtime_backend_label(0, 3), + "runtime entry point, CPU on every timed run" + ); + assert_eq!( + runtime_backend_label(1, 3), + "runtime entry point, MIXED GPU/CPU timed runs" + ); + assert_eq!(runtime_backend_label(0, 0), "runtime entry point, no timed run"); + for (g, r) in [(0, 1), (0, 3), (1, 3), (2, 3)] { + assert!(!runtime_backend_label(g, r).contains("GPU on every"), "{g}/{r}"); + } + } + diff --git a/crates/apr-cli/src/commands/rosetta.rs b/crates/apr-cli/src/commands/rosetta.rs index b8797b2934..4c4a46cb9a 100644 --- a/crates/apr-cli/src/commands/rosetta.rs +++ b/crates/apr-cli/src/commands/rosetta.rs @@ -13,6 +13,9 @@ use aprender::format::rosetta::{ ConversionOptions, ConversionPath, ConversionReport, FormatType, InspectionReport, RosettaStone, TensorInfo, VerificationReport, }; +use batuta_common::cli_roles::{ + ConfigPath, DirPath, FreeText, InputFile, ModelPath, OutputPath, PromptText, +}; use clap::Subcommand; use colored::Colorize; use std::fmt::Write; @@ -25,7 +28,7 @@ pub enum RosettaCommands { Inspect { /// Path to model file (GGUF, SafeTensors, or APR) #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Show hex dump of file header #[arg(long)] @@ -40,15 +43,15 @@ pub enum RosettaCommands { Convert { /// Source model file #[arg(value_name = "SOURCE")] - source: PathBuf, + source: ModelPath, /// Target output file (format inferred from extension) #[arg(value_name = "TARGET")] - target: PathBuf, + target: OutputPath, /// Apply quantization during conversion (int8, int4, fp16) #[arg(long)] - quantize: Option, + quantize: Option, /// Verify conversion with round-trip check #[arg(long)] @@ -60,22 +63,22 @@ pub enum RosettaCommands { /// External tokenizer.json for weights-only models (PMAT-232) #[arg(long, value_name = "TOKENIZER")] - tokenizer: Option, + tokenizer: Option, }, /// Execute multi-step conversion chain Chain { /// Source model file #[arg(value_name = "SOURCE")] - source: PathBuf, + source: ModelPath, /// Format chain (e.g., gguf safetensors apr) #[arg(value_name = "FORMATS", num_args = 2..)] - formats: Vec, + formats: Vec, /// Working directory for intermediate files #[arg(long, default_value = "./rosetta-work")] - work_dir: PathBuf, + work_dir: DirPath, /// Output as JSON #[arg(long)] @@ -86,11 +89,11 @@ pub enum RosettaCommands { Verify { /// Source model file #[arg(value_name = "SOURCE")] - source: PathBuf, + source: ModelPath, /// Intermediate format for round-trip (gguf, safetensors, apr) #[arg(long, default_value = "safetensors")] - intermediate: String, + intermediate: FreeText, /// Tolerance for numerical differences (default: 1e-5) #[arg(long, default_value = "1e-5", @@ -103,25 +106,26 @@ pub enum RosettaCommands { }, /// Compare inference outputs between two models (PMAT-114) + #[command(groups(batuta_common::cli_roles::SamplingArg::groups()))] CompareInference { /// Reference model (typically GGUF) #[arg(value_name = "MODEL_A")] - model_a: PathBuf, + model_a: ModelPath, /// Test model (typically APR) #[arg(value_name = "MODEL_B")] - model_b: PathBuf, + model_b: ModelPath, /// Test prompt #[arg(long, default_value = "2+2=")] - prompt: String, + prompt: PromptText, /// Maximum tokens to generate #[arg(long, default_value = "5")] max_tokens: usize, /// Sampling temperature (0 = greedy) - #[arg(long, default_value = "0")] + #[arg(long, default_value = "0", group = batuta_common::cli_roles::SamplingKind::Temperature.id())] temperature: f32, /// Logit difference tolerance @@ -142,11 +146,11 @@ pub enum RosettaCommands { DiffTensors { /// Reference model (typically GGUF - the one that works) #[arg(value_name = "MODEL_A")] - model_a: PathBuf, + model_a: ModelPath, /// Test model (typically APR - the one producing garbage) #[arg(value_name = "MODEL_B")] - model_b: PathBuf, + model_b: ModelPath, /// Only show tensors with dimension mismatches #[arg(long)] @@ -158,7 +162,7 @@ pub enum RosettaCommands { /// Filter tensors by name pattern (e.g., "embed", "lm_head", "layer.0") #[arg(long)] - filter: Option, + filter: Option, /// Output as JSON #[arg(long)] @@ -173,19 +177,19 @@ pub enum RosettaCommands { Fingerprint { /// Model file to fingerprint #[arg(value_name = "MODEL")] - model: PathBuf, + model: ModelPath, /// Second model to compare (optional - enables diff mode) #[arg(value_name = "MODEL_B")] - model_b: Option, + model_b: Option, /// Output fingerprints to JSON file #[arg(long, short)] - output: Option, + output: Option, /// Filter tensors by name pattern #[arg(long)] - filter: Option, + filter: Option, /// Show detailed statistics for each tensor #[arg(long)] @@ -203,15 +207,15 @@ pub enum RosettaCommands { ValidateStats { /// Model to validate #[arg(value_name = "MODEL")] - model: PathBuf, + model: ModelPath, /// Reference model for comparison #[arg(long)] - reference: Option, + reference: Option, /// Fingerprint JSON file for comparison #[arg(long)] - fingerprints: Option, + fingerprints: Option, /// Deviation threshold in standard deviations (default: 3.0) #[arg(long, default_value = "3.0", diff --git a/crates/apr-cli/src/commands/rosetta_fingerprints_load.rs b/crates/apr-cli/src/commands/rosetta_fingerprints_load.rs index 4891db0b1e..bc195aa79d 100644 --- a/crates/apr-cli/src/commands/rosetta_fingerprints_load.rs +++ b/crates/apr-cli/src/commands/rosetta_fingerprints_load.rs @@ -53,9 +53,9 @@ #[test] fn test_rosetta_commands_compare_inference() { let cmd = RosettaCommands::CompareInference { - model_a: PathBuf::from("model_a.gguf"), - model_b: PathBuf::from("model_b.apr"), - prompt: "Hello".to_string(), + model_a: PathBuf::from("model_a.gguf").into(), + model_b: PathBuf::from("model_b.apr").into(), + prompt: "Hello".to_string().into(), max_tokens: 10, temperature: 0.0, tolerance: 0.1, @@ -77,11 +77,11 @@ #[test] fn test_rosetta_commands_diff_tensors() { let cmd = RosettaCommands::DiffTensors { - model_a: PathBuf::from("model_a.gguf"), - model_b: PathBuf::from("model_b.apr"), + model_a: PathBuf::from("model_a.gguf").into(), + model_b: PathBuf::from("model_b.apr").into(), mismatches_only: true, show_values: 5, - filter: Some("attention".to_string()), + filter: Some("attention".to_string().into()), json: false, }; match cmd { @@ -93,7 +93,7 @@ } => { assert!(mismatches_only); assert_eq!(show_values, 5); - assert_eq!(filter, Some("attention".to_string())); + assert_eq!(filter, Some("attention".to_string()).map(Into::into)); } _ => panic!("Wrong command variant"), } @@ -102,9 +102,9 @@ #[test] fn test_rosetta_commands_fingerprint() { let cmd = RosettaCommands::Fingerprint { - model: PathBuf::from("model.gguf"), + model: PathBuf::from("model.gguf").into(), model_b: None, - output: Some(PathBuf::from("fingerprints.json")), + output: Some(PathBuf::from("fingerprints.json").into()), filter: None, verbose: true, json: false, @@ -123,9 +123,9 @@ #[test] fn test_rosetta_commands_validate_stats() { let cmd = RosettaCommands::ValidateStats { - model: PathBuf::from("model.gguf"), + model: PathBuf::from("model.gguf").into(), reference: None, - fingerprints: Some(PathBuf::from("ref.json")), + fingerprints: Some(PathBuf::from("ref.json").into()), threshold: 0.01, strict: true, json: true, diff --git a/crates/apr-cli/src/commands/rosetta_rosetta.rs b/crates/apr-cli/src/commands/rosetta_rosetta.rs index aae267b131..f6107d9718 100644 --- a/crates/apr-cli/src/commands/rosetta_rosetta.rs +++ b/crates/apr-cli/src/commands/rosetta_rosetta.rs @@ -7,7 +7,7 @@ fn test_rosetta_commands_inspect_default() { // Test that the Inspect variant can be created let cmd = RosettaCommands::Inspect { - file: PathBuf::from("model.gguf"), + file: PathBuf::from("model.gguf").into(), hexdump: false, json: false, }; @@ -28,8 +28,8 @@ #[test] fn test_rosetta_commands_convert() { let cmd = RosettaCommands::Convert { - source: PathBuf::from("model.gguf"), - target: PathBuf::from("model.apr"), + source: PathBuf::from("model.gguf").into(), + target: PathBuf::from("model.apr").into(), quantize: None, verify: false, json: false, @@ -53,9 +53,9 @@ #[test] fn test_rosetta_commands_chain() { let cmd = RosettaCommands::Chain { - source: PathBuf::from("model.gguf"), - formats: vec!["safetensors".to_string(), "apr".to_string()], - work_dir: PathBuf::from("./work"), + source: PathBuf::from("model.gguf").into(), + formats: vec!["safetensors".to_string().into(), "apr".to_string().into()], + work_dir: PathBuf::from("./work").into(), json: false, }; match cmd { @@ -71,8 +71,8 @@ #[test] fn test_rosetta_commands_verify() { let cmd = RosettaCommands::Verify { - source: PathBuf::from("model.gguf"), - intermediate: "safetensors".to_string(), + source: PathBuf::from("model.gguf").into(), + intermediate: "safetensors".to_string().into(), tolerance: 1e-5, json: false, }; diff --git a/crates/apr-cli/src/commands/rosetta_tensor_fingerprint_statistical.rs b/crates/apr-cli/src/commands/rosetta_tensor_fingerprint_statistical.rs index f19225200f..0f498c1fbe 100644 --- a/crates/apr-cli/src/commands/rosetta_tensor_fingerprint_statistical.rs +++ b/crates/apr-cli/src/commands/rosetta_tensor_fingerprint_statistical.rs @@ -215,7 +215,7 @@ #[test] fn test_rosetta_commands_inspect_with_hexdump() { let cmd = RosettaCommands::Inspect { - file: PathBuf::from("model.gguf"), + file: PathBuf::from("model.gguf").into(), hexdump: true, json: true, }; @@ -231,9 +231,9 @@ #[test] fn test_rosetta_commands_convert_with_all_options() { let cmd = RosettaCommands::Convert { - source: PathBuf::from("in.safetensors"), - target: PathBuf::from("out.apr"), - quantize: Some("int4".to_string()), + source: PathBuf::from("in.safetensors").into(), + target: PathBuf::from("out.apr").into(), + quantize: Some("int4".to_string().into()), verify: true, json: true, tokenizer: None, @@ -245,7 +245,7 @@ json, .. } => { - assert_eq!(quantize, Some("int4".to_string())); + assert_eq!(quantize, Some("int4".to_string()).map(Into::into)); assert!(verify); assert!(json); } @@ -256,10 +256,10 @@ #[test] fn test_rosetta_commands_fingerprint_with_model_b() { let cmd = RosettaCommands::Fingerprint { - model: PathBuf::from("model_a.gguf"), - model_b: Some(PathBuf::from("model_b.apr")), + model: PathBuf::from("model_a.gguf").into(), + model_b: Some(PathBuf::from("model_b.apr").into()), output: None, - filter: Some("attn".to_string()), + filter: Some("attn".to_string().into()), verbose: false, json: true, }; @@ -271,7 +271,7 @@ .. } => { assert!(model_b.is_some()); - assert_eq!(filter, Some("attn".to_string())); + assert_eq!(filter, Some("attn".to_string()).map(Into::into)); assert!(json); } _ => panic!("Wrong command variant"), diff --git a/crates/apr-cli/src/commands/serve/chat.rs b/crates/apr-cli/src/commands/serve/chat.rs index ef240b175d..ebd2809400 100644 --- a/crates/apr-cli/src/commands/serve/chat.rs +++ b/crates/apr-cli/src/commands/serve/chat.rs @@ -266,6 +266,19 @@ pub(crate) async fn safetensors_chat_completions_handler( .get("temperature") .and_then(|t| t.as_f64()) .unwrap_or(0.0) as f32; + // #4334: stop at the tokenizer's EOS / chat-turn end and honour the request's + // top_p, through the same config builder as the APR CPU path (#4265). + let top_p = request + .get("top_p") + .and_then(serde_json::Value::as_f64) + .map(|p| p as f32); + let stop_tokens = state + .tokenizer_info + .as_ref() + .map(super::handlers::tokenizer_info_stop_tokens) + .unwrap_or_default(); + let gen_config = + super::handlers::apr_cpu_generate_config(max_tokens, temperature, top_p, stop_tokens); let output_ids = { // PMAT-189: Handle transformer lock poisoning gracefully let t = match transformer.lock() { @@ -280,7 +293,7 @@ pub(crate) async fn safetensors_chat_completions_handler( .into_response(); } }; - match st_cpu_generate(&t, &input_ids, max_tokens, temperature) { + match st_cpu_generate(&t, &input_ids, &gen_config) { Ok(ids) => ids, Err(e) => { return ( @@ -294,7 +307,7 @@ pub(crate) async fn safetensors_chat_completions_handler( let elapsed = start.elapsed(); // Decode output using BPE tokenizer (PMAT-093) - let new_tokens = &output_ids[input_ids.len()..]; + let new_tokens = super::handlers::generated_reply_tokens(&output_ids, input_ids.len(), &gen_config); let output_text = if let Some(ref tok_info) = state.tokenizer_info { match tok_info.tokenizer.decode(new_tokens) { Ok(text) => text, diff --git a/crates/apr-cli/src/commands/serve/handler_gpu_completion.rs b/crates/apr-cli/src/commands/serve/handler_gpu_completion.rs index 8e83f6b5b9..a5d45927f4 100644 --- a/crates/apr-cli/src/commands/serve/handler_gpu_completion.rs +++ b/crates/apr-cli/src/commands/serve/handler_gpu_completion.rs @@ -609,7 +609,7 @@ fn start_gguf_server_cuda( "{}", "qwen3moe: serving through the MoE CUDA dispatch (#3714, #3987)".cyan() ); - let model_source = measured_model_source(&quantized_model, config); + let model_source = measured_model_source(&quantized_model, Some(&mapped_model), config); let state = AppState::with_quantized_model_and_vocab(quantized_model, vocab) .map_err(|e| CliError::InferenceFailed(format!("Failed to create state: {e}")))? .with_model_source(model_source) @@ -632,6 +632,10 @@ fn start_gguf_server_cuda( ); println!(" Max sequence length: {max_seq_len}"); + // #4254: measured before the model moves into CUDA. Without it `/v1/effective-config` + // on a CUDA server reported quantization, path and parameter count all null. + let model_source = measured_model_source(&quantized_model, Some(&mapped_model), config); + match OwnedQuantizedModelCuda::with_max_seq_len(quantized_model, 0, max_seq_len) { Ok(mut cuda_model) => { preload_gpu_weights(&mut cuda_model); @@ -649,6 +653,7 @@ fn start_gguf_server_cuda( let state = AppState::with_cuda_model_and_vocab(cuda_model, vocab) .map_err(|e| CliError::InferenceFailed(format!("Failed to create state: {e}")))? .with_mapped_gguf_model(mapped_model.clone()) + .with_model_source(model_source) // PP-14/PP-15/§9 #8: the resolved offload AND this binary's // `cfg!` feature list (including `cuda-batch`) reach the served // process, so a receipt records what the build was rather than diff --git a/crates/apr-cli/src/commands/serve/handlers.rs b/crates/apr-cli/src/commands/serve/handlers.rs index afc052b238..8120dd763b 100644 --- a/crates/apr-cli/src/commands/serve/handlers.rs +++ b/crates/apr-cli/src/commands/serve/handlers.rs @@ -6,7 +6,6 @@ #![allow(unused_imports)] #![allow(unused_variables)] -#![allow(dead_code)] #[cfg(feature = "wgpu")] use axum::response::IntoResponse; @@ -115,22 +114,9 @@ fn wgpu_detokenize_one(id: u32, vocab: &[String]) -> String { String::from_utf8_lossy(&gpt2_token_bytes(token)).into_owned() } -/// PMAT-355: Qwen2 stop conditions for the WGPU greedy decode loop. +/// PMAT-355: Qwen2 stop tokens for the WGPU greedy decode loop (`<|im_end|>`, pad). #[cfg(feature = "wgpu")] -fn wgpu_is_stop_token(token: u32) -> bool { - token == 151645 || token == 0 -} - -/// PMAT-355: Greedy argmax over a logits vector (0 when empty). -#[cfg(feature = "wgpu")] -fn wgpu_argmax(logits: &[f32]) -> u32 { - logits - .iter() - .enumerate() - .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal)) - .map(|(i, _)| i as u32) - .unwrap_or(0) -} +const WGPU_STOP_TOKENS: [u32; 2] = [151645, 0]; /// PMAT-355: Tokens per second, guarding the zero-elapsed case. #[cfg(feature = "wgpu")] @@ -265,8 +251,8 @@ fn wgpu_stream_generate( let mut completion_tokens = 0u32; for step in 0..max_tokens { - let next_token = wgpu_argmax(&last_logits); - if wgpu_is_stop_token(next_token) { + let next_token = realizar::sampling::argmax(&last_logits); + if realizar::sampling::is_stop(next_token, &WGPU_STOP_TOKENS) { break; } let text = wgpu_detokenize_one(next_token, vocab); @@ -328,8 +314,8 @@ fn wgpu_chat_completion_blocking( }; for step in 0..max_tokens { - let next_token = wgpu_argmax(&last_logits); - if wgpu_is_stop_token(next_token) { + let next_token = realizar::sampling::argmax(&last_logits); + if realizar::sampling::is_stop(next_token, &WGPU_STOP_TOKENS) { break; } output_ids.push(next_token); @@ -781,6 +767,8 @@ fn serve_wgpu_backend( dims.head_dim, dims.intermediate_dim, ); + // #4056: the WGSL RMSNorm takes the model's eps (it hardcoded 1e-6). + fwd.set_rms_norm_eps(quantized.config().eps); upload_wgpu_weights(&mut fwd, quantized, weights, num_layers); @@ -1067,6 +1055,9 @@ struct AprServerState { /// always leaves this `None`; only `build_demo_apr_cpu_router_for_test` sets /// it, so the streaming falsifier can observe real multi-chunk NDJSON. demo_scripted_tokens: Option>, + /// #4295: the stop set, derived once from the tokenizers at load + /// ([`derive_apr_cpu_stop_tokens`]), never rescanned per request. + stop_tokens: Arc<[u32]>, } /// Output from a successful APR inference run @@ -1168,13 +1159,23 @@ fn run_apr_cpu_inference( const APR_CPU_TURN_END_TOKENS: &[&str] = &["<|im_end|>", "<|endoftext|>", "<|eot_id|>", ""]; -/// #4265: the token ids that end an APR CPU generation: every EOS the loaded -/// tokenizer declares plus the chat-turn terminators it has an id for. The -/// generation loop already stops at id 0 on its own (`is_eos_token`). +/// #4265: the token ids that end an APR CPU generation, as derived at load. #[cfg(feature = "inference")] fn apr_cpu_stop_tokens(state: &AprServerState) -> Vec { + state.stop_tokens.to_vec() +} + +/// #4265: every EOS the loaded tokenizers declare plus the chat-turn +/// terminators they have an id for. The generation loop already stops at id 0 +/// on its own (`is_eos_token`). It walks the whole tokenizer.json vocab, so it +/// runs once, when the state is built (#4295). +#[cfg(feature = "inference")] +fn derive_apr_cpu_stop_tokens( + embedded_tokenizer: Option<&realizar::apr::BpeTokenizer>, + tokenizer: Option<&SafeTensorsTokenizerInfo>, +) -> Arc<[u32]> { let mut stop = Vec::new(); - if let Some(tok) = &state.embedded_tokenizer { + if let Some(tok) = embedded_tokenizer { stop.extend(tok.eos_id); stop.extend( APR_CPU_TURN_END_TOKENS @@ -1182,19 +1183,44 @@ fn apr_cpu_stop_tokens(state: &AprServerState) -> Vec { .filter_map(|t| tok.special_tokens.get(*t).copied()), ); } - if let Some(tok) = &state.tokenizer { - stop.extend(tok.eos_token_id); - stop.extend(tok.vocab.iter().enumerate().filter_map(|(id, t)| { - APR_CPU_TURN_END_TOKENS - .contains(&t.as_str()) - .then_some(id as u32) - })); + if let Some(tok) = tokenizer { + stop.extend(tokenizer_info_stop_tokens(tok)); } stop.sort_unstable(); stop.dedup(); + stop.into() +} + +/// #4334: the stop ids a tokenizer.json-backed tokenizer declares: its EOS plus +/// the chat-turn terminators its vocab has. Shared by the APR CPU path and the +/// SafeTensors handlers (`chat.rs`, `simple.rs`), which hardcoded an empty set. +#[cfg(feature = "inference")] +pub(crate) fn tokenizer_info_stop_tokens(tok: &SafeTensorsTokenizerInfo) -> Vec { + let mut stop: Vec = tok.eos_token_id.into_iter().collect(); + stop.extend(tok.vocab.iter().enumerate().filter_map(|(id, t)| { + APR_CPU_TURN_END_TOKENS + .contains(&t.as_str()) + .then_some(id as u32) + })); + stop.sort_unstable(); + stop.dedup(); stop } +/// #4334: the reply tokens of an `st_cpu_generate` result: everything past the +/// prompt, minus the stop id the loop ended on (its stop set is +/// `gen_config.stop_tokens` plus token 0). +#[cfg(feature = "inference")] +pub(crate) fn generated_reply_tokens<'a>( + output_ids: &'a [u32], + prompt_len: usize, + gen_config: &realizar::gguf::QuantizedGenerateConfig, +) -> &'a [u32] { + let mut stop_ids = gen_config.stop_tokens.clone(); + stop_ids.push(0); + apr_cpu_reply_tokens(output_ids.get(prompt_len..).unwrap_or(&[]), &stop_ids) +} + /// The reply text's tokens: `new_tokens` without the stop id the loop ended on. /// `stop_ids` must be the loop's whole stop set, token 0 included — the loop /// ends on 0 even when it is not in `stop_tokens` (#4265). @@ -1211,7 +1237,7 @@ fn apr_cpu_reply_tokens<'a>(new_tokens: &'a [u32], stop_ids: &[u32]) -> &'a [u32 /// of its own, so 0 is added to the stop set here to keep the old /// `is_eos_token` contract (token 0 is always EOS). #[cfg(feature = "inference")] -fn apr_cpu_generate_config( +pub(crate) fn apr_cpu_generate_config( max_tokens: usize, temperature: f32, top_p: Option, @@ -1352,6 +1378,8 @@ fn load_apr_model_state(model_path: &Path, config: &ServerConfig) -> Result Result axum::Router { embedded_tokenizer: None, model_name: "apr".to_string(), demo_scripted_tokens: None, + stop_tokens: Arc::from([]), }; build_apr_cpu_router(state, super::auth::AuthGate::disabled()) } @@ -1711,6 +1741,7 @@ pub fn build_demo_streaming_apr_cpu_router_for_test() -> axum::Router { "world".to_string(), "!".to_string(), ]), + stop_tokens: Arc::from([]), }; build_apr_cpu_router(state, super::auth::AuthGate::disabled()) } diff --git a/crates/apr-cli/src/commands/serve/mod.rs b/crates/apr-cli/src/commands/serve/mod.rs index f096601761..232ee7bb6f 100644 --- a/crates/apr-cli/src/commands/serve/mod.rs +++ b/crates/apr-cli/src/commands/serve/mod.rs @@ -21,6 +21,11 @@ pub use types::*; // Test modules #[cfg(test)] mod tests; +// #3809: the apr-serve-v1 / http-api-v1 FALSIFY-SRV/HTTP enforcement suite. It sat +// beside `tests.rs` unwired, so its 25 tests never compiled and the contract's ids +// pointed at code that never ran. +#[cfg(test)] +mod tests_contract_enforcement; #[cfg(all(test, feature = "inference"))] mod tests_route_index_3979; // PP-LLAMA-001 PP-14/PP-15/§9 #8: the offload report the served process diff --git a/crates/apr-cli/src/commands/serve/safetensors.rs b/crates/apr-cli/src/commands/serve/safetensors.rs index e161493507..9aef365c86 100644 --- a/crates/apr-cli/src/commands/serve/safetensors.rs +++ b/crates/apr-cli/src/commands/serve/safetensors.rs @@ -380,39 +380,28 @@ fn classify_bos_eos(content: &str) -> (bool, bool) { include!("chat.rs"); include!("simple.rs"); +#[cfg(all(test, feature = "inference"))] +#[path = "tests_safetensors_stop_4334.rs"] +mod tests_safetensors_stop_4334; /// #4269 (workstream M of #4263): the ONE SafeTensors CPU generate both serve /// handlers (`/v1/chat/completions` in chat.rs, `/generate` in simple.rs) call, /// driven through the engine's `realizar::session::Session` /// instead of `AprTransformer::generate_with_cache`'s own decode loop. /// Returns the prompt followed by the generated tokens, as that loop did. +/// #4334: the stop set and top_p come from the handler's `gen_config` +/// (`handlers::apr_cpu_generate_config`): the tokenizer's EOS / chat-turn end, +/// plus token 0, which `apr_transformer::generation::is_eos_token` (GH-330) +/// stopped on unconditionally and Session has no builtin for. #[cfg(feature = "inference")] fn st_cpu_generate( model: &realizar::apr_transformer::AprTransformer, input_ids: &[u32], - max_tokens: usize, - temperature: f32, + gen_config: &realizar::gguf::QuantizedGenerateConfig, ) -> std::result::Result, String> { use realizar::safetensors_infer::StCpuForward; use realizar::session::Session; - let gen_config = realizar::gguf::QuantizedGenerateConfig { - max_tokens, - temperature, - top_k: 0, - top_p: 0.9, - // #3760: the sampler draws now; no seed is plumbed from this caller. - seed: realizar::apr_transformer::DEFAULT_SEED, - repeat_penalty: 1.0, - repeat_last_n: 0, - // apr_transformer::generation::is_eos_token (GH-330) stopped on token 0 - // unconditionally; Session has no such builtin, so it is an explicit - // stop token here to keep the handlers' stopping behavior identical. - stop_tokens: vec![0], - trace: false, - logprobs: false, - cancel: realizar::generate::CancelToken::never(), - }; Session::new(StCpuForward::new(model)) - .generate(input_ids, &gen_config, &mut |_tok| true) + .generate(input_ids, gen_config, &mut |_tok| true) .map(|turn| turn.tokens) .map_err(|e| e.to_string()) } diff --git a/crates/apr-cli/src/commands/serve/server.rs b/crates/apr-cli/src/commands/serve/server.rs index 480b9ebd94..60aae57c35 100644 --- a/crates/apr-cli/src/commands/serve/server.rs +++ b/crates/apr-cli/src/commands/serve/server.rs @@ -37,6 +37,7 @@ fn dominant_quantization(model: &realizar::gguf::OwnedQuantizedModel) -> Option< #[cfg(feature = "inference")] fn measured_model_source( model: &realizar::gguf::OwnedQuantizedModel, + mapped: Option<&realizar::gguf::MappedGGUFModel>, config: &ServerConfig, ) -> realizar::api::ModelSourceInfo { let base = config @@ -52,9 +53,50 @@ fn measured_model_source( if let Some(quantization) = dominant_quantization(model) { source = source.with_quantization(quantization); } + // #4254: counted from the file's own tensor table, which is the whole model. The + // owned model is not: the Qwen3.5 hybrid reaches the servers as its BASE (#3571). + if let Some(mapped) = mapped { + source = source.with_parameter_count(gguf_parameter_count(&mapped.model)); + } source } +/// #4254: [`dominant_quantization`]'s rule, read off the GGUF's own tensor table: the +/// modal qtype across every layer's attention-output and FFN projections. The Qwen3.5 +/// hybrid has no owned model to ask, so the tensor table is the only measurement it has. +/// The embedding is left out on both paths — on a 0.8B model its Q6_K would outweigh +/// every Q4_K projection, and it is not what "the model's quantization" names. +#[cfg(feature = "inference")] +fn gguf_dominant_quantization(model: &realizar::gguf::GGUFModel) -> Option<&'static str> { + use std::collections::HashMap; + + const PROJECTIONS: [&str; 4] = [ + ".attn_output.weight", + ".ffn_up.weight", + ".ffn_down.weight", + ".ffn_gate.weight", + ]; + let mut counts: HashMap = HashMap::new(); + for t in model + .tensors + .iter() + .filter(|t| t.name.starts_with("blk.") && PROJECTIONS.iter().any(|p| t.name.ends_with(p))) + { + *counts.entry(t.qtype).or_insert(0) += 1; + } + counts + .into_iter() + .filter_map(|(qtype, n)| realizar::api::gguf_qtype_name(qtype).map(|name| (name, n))) + .max_by_key(|&(_, n)| n) + .map(|(name, _)| name) +} + +/// #4254: every element of every tensor the GGUF declares. +#[cfg(feature = "inference")] +fn gguf_parameter_count(model: &realizar::gguf::GGUFModel) -> u64 { + model.tensors.iter().map(|t| t.dims.iter().product::()).sum() +} + /// Run the CPU inference server /// /// `mapped_model` is `None` for non-GGUF formats (APR / SafeTensors). For @@ -76,7 +118,7 @@ fn run_cpu_server( // Measure the model BEFORE it is moved into AppState. Anything not // measurable here stays absent — `/realize/model` no longer substitutes // `size_bytes: 0` / `context_length: 4096` / `quantization: "Q4_K_M"`. - let model_source = measured_model_source(&quantized_model, config); + let model_source = measured_model_source(&quantized_model, mapped_model.as_deref(), config); let mut state = AppState::with_quantized_model_and_vocab(quantized_model, vocab) .map_err(|e| CliError::InferenceFailed(format!("Failed to create app state: {e}")))? @@ -180,7 +222,13 @@ fn build_qwen35_state( .unwrap_or_default() .with_architecture("qwen35") .with_model_max_context_length(context_length) - .with_context_length(config.context_length); + .with_context_length(config.context_length) + .with_parameter_count(gguf_parameter_count(&mapped_model.model)); + // #4254: the v0.69.1 CPU asset served this model with `quantization: null`. + let model_source = match gguf_dominant_quantization(&mapped_model.model) { + Some(q) => model_source.with_quantization(q), + None => model_source, + }; let state = AppState::with_qwen35_session(session, mapped_model, vocab) .map_err(|e| CliError::InferenceFailed(format!("Failed to create app state: {e}")))? .with_model_source(model_source) @@ -247,6 +295,97 @@ mod qwen35_serve_route_tests { assert!(layers > 0, "the session holds the hybrid's layers: {layers}"); } + /// #4254: the v0.69.1 CPU asset served Qwen3.5 with `quantization: null` and + /// `parameter_count: null` in `/v1/effective-config` — the hybrid path built its + /// source from the path alone. Both are now read off the GGUF's own tensor table. + #[test] + fn qwen35_serve_source_carries_quantization_and_parameter_count_4254() { + if !Path::new(MODEL).exists() { + eprintln!("SKIP: {MODEL} is absent"); + return; + } + let mapped = std::sync::Arc::new( + realizar::gguf::MappedGGUFModel::from_path(MODEL).expect("map the GGUF"), + ); + let config = ServerConfig { + no_gpu: true, + ..ServerConfig::default() + }; + let state = build_qwen35_state(mapped, &config).expect("qwen35 serve passes"); + let body = realizar::api::effective_config(&state); + // A Q4_K_M file holds most of its weight elements in Q4_K (some + // projections are Q6_K); "Q4_K_M" is a file label, not a tensor type. + assert_eq!(body.model.quantization.as_deref(), Some("Q4_K")); + // Qwen3.5-0.8B: a count, not a label — anything off by 10x is a wrong sum. + let n = body.model.parameter_count.expect("parameter_count is measured"); + assert!((500_000_000..1_500_000_000).contains(&n), "parameter_count {n}"); + assert_eq!(body.compute_class, "cpu"); + } + + /// #4254: one process, two endpoints, one answer. `/v1/effective-config` on the + /// hybrid named no backend (`backend_loaded: []`, `compute_class: unknown`), no + /// quantization and no parameter count while `/health` said `cpu` — and listed a + /// `gpu` feature this binary cannot dispatch to. + #[test] + fn qwen35_effective_config_agrees_with_health() { + use axum::body::Body; + use axum::http::Request; + use tower::ServiceExt; + + if !Path::new(MODEL).exists() { + eprintln!("SKIP: {MODEL} is absent"); + return; + } + let mapped = std::sync::Arc::new( + realizar::gguf::MappedGGUFModel::from_path(MODEL).expect("map the GGUF"), + ); + let want_params = + realizar::gguf::forward_qwen35::qwen35_known_issue::gguf_param_count(&mapped.model); + let config = ServerConfig { + no_gpu: true, + ..ServerConfig::default() + }; + let state = build_qwen35_state(mapped, &config).expect("qwen35 state"); + let router = realizar::api::create_router_with_config(state, config.router_config()); + let get = |uri: &'static str| { + let router = router.clone(); + async move { + let req = Request::builder().uri(uri).body(Body::empty()).expect("request"); + let resp = router.oneshot(req).await.expect("route"); + let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX) + .await + .expect("body"); + serde_json::from_slice::(&bytes).expect("JSON") + } + }; + let (health, ec) = tokio::runtime::Runtime::new() + .expect("runtime") + .block_on(async { (get("/health").await, get("/v1/effective-config").await) }); + + assert_eq!(health["compute_mode"], "cpu", "{health}"); + assert_eq!(ec["compute_class"], "cpu", "must agree with /health: {ec}"); + assert_eq!(ec["backend_loaded"], serde_json::json!(["cpu"]), "{ec}"); + let q = ec["model"]["quantization"].as_str().expect("quantization named"); + assert!(q.starts_with('Q'), "a GGUF qtype name, got {q:?}"); + assert_eq!(ec["model"]["parameter_count"], want_params, "{ec}"); + assert!( + (600_000_000..1_000_000_000).contains(&want_params), + "fixture: the 0.8B file counts {want_params}" + ); + // The launcher's own set decides which accelerators are dispatchable. + let features: Vec<&str> = ec["build_features"] + .as_array() + .expect("build_features") + .iter() + .filter_map(serde_json::Value::as_str) + .collect(); + assert_eq!(features.contains(&"gpu"), cfg!(feature = "wgpu"), "{features:?}"); + assert_eq!(features.contains(&"cuda"), cfg!(feature = "cuda"), "{features:?}"); + let commit = ec["server"]["build_commit"].as_str().expect("build_commit"); + assert_eq!(commit, env!("APR_GIT_SHA")); + assert!(!commit.ends_with("+no-git"), "built from a checkout: {commit}"); + } + /// The startup line's thinking mode is read off what the template renders. #[test] fn the_thinking_mode_is_read_off_the_rendered_template() { diff --git a/crates/apr-cli/src/commands/serve/simple.rs b/crates/apr-cli/src/commands/serve/simple.rs index 04ee5b7d41..53713a38b6 100644 --- a/crates/apr-cli/src/commands/serve/simple.rs +++ b/crates/apr-cli/src/commands/serve/simple.rs @@ -39,6 +39,19 @@ pub(crate) async fn safetensors_generate_handler( .get("temperature") .and_then(|t| t.as_f64()) .unwrap_or(0.0) as f32; + // #4334: stop at the tokenizer's EOS / chat-turn end and honour the request's + // top_p, through the same config builder as the APR CPU path (#4265). + let top_p = request + .get("top_p") + .and_then(serde_json::Value::as_f64) + .map(|p| p as f32); + let stop_tokens = state + .tokenizer_info + .as_ref() + .map(super::handlers::tokenizer_info_stop_tokens) + .unwrap_or_default(); + let gen_config = + super::handlers::apr_cpu_generate_config(max_tokens, temperature, top_p, stop_tokens); let output_ids = { // PMAT-189: Handle transformer lock poisoning gracefully let t = match transformer.lock() { @@ -53,7 +66,7 @@ pub(crate) async fn safetensors_generate_handler( .into_response(); } }; - match st_cpu_generate(&t, &input_ids, max_tokens, temperature) { + match st_cpu_generate(&t, &input_ids, &gen_config) { Ok(ids) => ids, Err(e) => { return ( @@ -67,7 +80,7 @@ pub(crate) async fn safetensors_generate_handler( let elapsed = start.elapsed(); // Decode using BPE tokenizer (PMAT-093) - let new_tokens = &output_ids[input_ids.len()..]; + let new_tokens = super::handlers::generated_reply_tokens(&output_ids, input_ids.len(), &gen_config); let output_text = if let Some(ref tok_info) = state.tokenizer_info { match tok_info.tokenizer.decode(new_tokens) { Ok(text) => text, diff --git a/crates/apr-cli/src/commands/serve/tests_apr_cpu_gen_config_4265.rs b/crates/apr-cli/src/commands/serve/tests_apr_cpu_gen_config_4265.rs index d3d577885a..f01820e3cb 100644 --- a/crates/apr-cli/src/commands/serve/tests_apr_cpu_gen_config_4265.rs +++ b/crates/apr-cli/src/commands/serve/tests_apr_cpu_gen_config_4265.rs @@ -9,23 +9,31 @@ fn state_with( embedded: Option, eos_from_tokenizer_json: Option, ) -> AprServerState { + let tokenizer = eos_from_tokenizer_json.map(tokenizer_json); + let stop_tokens = derive_apr_cpu_stop_tokens(embedded.as_ref(), tokenizer.as_ref()); AprServerState { transformer: None, model_type: "test".to_string(), architecture: "test".to_string(), is_transformer: false, - tokenizer: eos_from_tokenizer_json.map(|eos| SafeTensorsTokenizerInfo { - tokenizer: std::sync::Arc::new( - realizar::tokenizer::BPETokenizer::new(vec!["".to_string()], vec![], "") - .expect("tiny tokenizer"), - ), - vocab: vec![], - bos_token_id: None, - eos_token_id: Some(eos), - }), + tokenizer, embedded_tokenizer: embedded, model_name: "apr".to_string(), demo_scripted_tokens: None, + stop_tokens, + } +} + +/// A sibling tokenizer.json with `eos` and an empty vocab. +fn tokenizer_json(eos: u32) -> SafeTensorsTokenizerInfo { + SafeTensorsTokenizerInfo { + tokenizer: std::sync::Arc::new( + realizar::tokenizer::BPETokenizer::new(vec!["".to_string()], vec![], "") + .expect("tiny tokenizer"), + ), + vocab: vec![], + bos_token_id: None, + eos_token_id: Some(eos), } } @@ -114,13 +122,24 @@ fn reply_keeps_a_budget_cut_and_an_inner_stop_id() { #[test] fn stop_set_holds_sibling_tokenizer_json_turn_end_by_vocab_index() { + let mut tok = tokenizer_json(2); + tok.vocab = ["a", "<|im_start|>", "b", "<|im_end|>"] + .iter() + .map(|t| (*t).to_string()) + .collect(); + // <|im_end|> sits at index 3; <|im_start|> (index 1) is not a stop. + assert_eq!(&*derive_apr_cpu_stop_tokens(None, Some(&tok)), &[2, 3]); +} + +/// #4295: a request reads the set derived at load; it never walks the vocab +/// again. The tokenizer changes after the state is built and the request's +/// stop set does not. +#[test] +fn a_request_reads_the_stop_set_derived_at_load() { let mut s = state_with(None, Some(2)); if let Some(tok) = s.tokenizer.as_mut() { - tok.vocab = ["a", "<|im_start|>", "b", "<|im_end|>"] - .iter() - .map(|t| (*t).to_string()) - .collect(); + tok.vocab = vec!["<|im_end|>".to_string()]; + tok.eos_token_id = Some(7); } - // <|im_end|> sits at index 3; <|im_start|> (index 1) is not a stop. - assert_eq!(apr_cpu_stop_tokens(&s), vec![2, 3]); + assert_eq!(apr_cpu_stop_tokens(&s), vec![2]); } diff --git a/crates/apr-cli/src/commands/serve/tests_contract_enforcement.rs b/crates/apr-cli/src/commands/serve/tests_contract_enforcement.rs index c3dd427a6b..704836f922 100644 --- a/crates/apr-cli/src/commands/serve/tests_contract_enforcement.rs +++ b/crates/apr-cli/src/commands/serve/tests_contract_enforcement.rs @@ -13,14 +13,16 @@ use std::sync::Arc; fn create_test_model_file() -> tempfile::NamedTempFile { let mut file = tempfile::NamedTempFile::new().expect("tempfile::NamedTempFile::new()"); - file.write_all(b"test model data").expect("write_all(b'test model data'"); + file.write_all(b"test model data") + .expect("write_all(b'test model data'"); file } fn create_test_state() -> Arc { let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); // Keep the temp file alive by leaking the path std::mem::forget(model); Arc::new(state) @@ -34,7 +36,8 @@ fn falsify_srv_001_health_returns_503_when_not_ready() { // ServerState starts with ready=false. health_check() must return Unhealthy. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); // State starts not ready assert!( !state.is_ready(), @@ -53,7 +56,8 @@ fn falsify_srv_001b_health_returns_200_when_ready() { // FALSIFY-SRV-001b: /health returns Healthy once ready=true. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); state.ready.store(true, Ordering::Release); assert!(state.is_ready()); let health = health_check(&state); @@ -182,7 +186,8 @@ fn falsify_srv_006_health_response_fields() { // FALSIFY-SRV-006: HealthResponse has all required fields per contract. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); state.ready.store(true, Ordering::Release); let health = health_check(&state); // All fields must be present and valid @@ -205,14 +210,17 @@ fn falsify_srv_006_health_response_fields() { fn falsify_srv_007_health_status_serde() { // FALSIFY-SRV-007: HealthStatus enum serializes to lowercase strings. // Contract: "healthy", "degraded", "unhealthy" — not "Healthy", etc. - let json = serde_json::to_string(&HealthStatus::Healthy).expect("serde_json::to_string(&HealthS"); + let json = + serde_json::to_string(&HealthStatus::Healthy).expect("serde_json::to_string(&HealthS"); assert_eq!(json, "\"healthy\"", "FALSIFY-SRV-007: Healthy → lowercase"); - let json = serde_json::to_string(&HealthStatus::Degraded).expect("serde_json::to_string(&HealthS"); + let json = + serde_json::to_string(&HealthStatus::Degraded).expect("serde_json::to_string(&HealthS"); assert_eq!( json, "\"degraded\"", "FALSIFY-SRV-007: Degraded → lowercase" ); - let json = serde_json::to_string(&HealthStatus::Unhealthy).expect("serde_json::to_string(&HealthS"); + let json = + serde_json::to_string(&HealthStatus::Unhealthy).expect("serde_json::to_string(&HealthS"); assert_eq!( json, "\"unhealthy\"", "FALSIFY-SRV-007: Unhealthy → lowercase" @@ -380,7 +388,8 @@ fn falsify_srv_010_graceful_shutdown_ready_flag() { // When shutdown is initiated, health endpoint must return 503. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); // Simulate Ready → Draining transition state.ready.store(true, Ordering::Release); @@ -405,7 +414,8 @@ fn falsify_srv_011_degraded_health_on_high_latency() { // Contract: server_lifecycle invariant — Degraded when p99 > 1s. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); state.ready.store(true, Ordering::Release); // Record 10 requests with 2000ms average latency @@ -462,7 +472,8 @@ fn falsify_srv_013_mmap_threshold() { // Contract: ServerState determines mmap based on 50MB threshold. let model = create_test_model_file(); let config = ServerConfig::default(); - let state = ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); + let state = + ServerState::new(model.path().to_path_buf(), config).expect("to_path_buf(), config"); // Test file is small — should NOT use mmap assert!( diff --git a/crates/apr-cli/src/commands/serve/tests_safetensors_stop_4334.rs b/crates/apr-cli/src/commands/serve/tests_safetensors_stop_4334.rs new file mode 100644 index 0000000000..11daccb0b9 --- /dev/null +++ b/crates/apr-cli/src/commands/serve/tests_safetensors_stop_4334.rs @@ -0,0 +1,119 @@ +//! #4334: the SafeTensors serve handlers (`chat.rs`, `simple.rs`) stop at the +//! tokenizer's EOS / chat-turn end, like the APR CPU path does since #4265. +//! They hardcoded `stop_tokens: vec![]`, so every reply ran to `max_tokens`. +#![allow(clippy::unwrap_used, clippy::expect_used)] + +use super::*; +use axum::body::to_bytes; +use realizar::apr_transformer::{AprTransformer, AprTransformerConfig}; + +const VOCAB: usize = 100; +const IM_END: u32 = 7; +const EOS: u32 = 9; +const MAX_TOKENS: u64 = 8; + +/// A vocab with `<|im_end|>` at `IM_END`; every other entry is an ordinary piece. +fn vocab() -> Vec { + (0..VOCAB) + .map(|i| match i { + 0 => "".to_string(), + 7 => "<|im_end|>".to_string(), + _ => format!("t{i}"), + }) + .collect() +} + +fn tokenizer_info(eos: Option) -> SafeTensorsTokenizerInfo { + let vocab = vocab(); + SafeTensorsTokenizerInfo { + tokenizer: std::sync::Arc::new( + realizar::tokenizer::BPETokenizer::new(vocab.clone(), vec![], "") + .expect("tiny tokenizer"), + ), + vocab, + bos_token_id: None, + eos_token_id: eos, + } +} + +/// A tiny zero-weight transformer whose LM-head bias makes greedy decoding emit +/// `always` at every step, so the only way generation ends early is a stop id. +fn state(always: u32, eos: Option) -> SafeTensorsState { + let mut t = AprTransformer::new(AprTransformerConfig { + architecture: "test".to_string(), + hidden_dim: 16, + num_layers: 1, + num_heads: 2, + num_kv_heads: 2, + vocab_size: VOCAB, + intermediate_dim: 32, + context_length: 128, + rope_theta: 10000.0, + eps: 1e-5, + eos_token_id: None, + ..Default::default() + }); + let mut bias = vec![0.0; VOCAB]; + bias[always as usize] = 10.0; + t.lm_head_bias = Some(bias); + SafeTensorsState { + transformer: Some(std::sync::Arc::new(std::sync::Mutex::new(t))), + tokenizer_info: Some(tokenizer_info(eos)), + model_path: "test".to_string(), + } +} + +async fn json(resp: axum::response::Response) -> serde_json::Value { + let bytes = to_bytes(resp.into_body(), 64 * 1024).await.expect("body"); + serde_json::from_slice(&bytes).expect("json") +} + +#[test] +fn stop_set_holds_eos_and_turn_end_from_vocab() { + assert_eq!( + super::super::handlers::tokenizer_info_stop_tokens(&tokenizer_info(Some(EOS))), + vec![IM_END, EOS] + ); + assert_eq!( + super::super::handlers::tokenizer_info_stop_tokens(&tokenizer_info(None)), + vec![IM_END] + ); +} + +#[tokio::test] +async fn generate_handler_stops_at_eos() { + let resp = safetensors_generate_handler( + axum::extract::State(state(EOS, Some(EOS))), + axum::Json(serde_json::json!({"prompt": "t1", "max_tokens": MAX_TOKENS})), + ) + .await; + let v = json(resp).await; + // One step: the model emits EOS, the loop stops, and the stop id is not reply text. + assert_eq!(v["tokens_generated"], 0, "{v}"); +} + +#[tokio::test] +async fn generate_handler_runs_to_max_without_a_stop_id() { + // Positive control: the same model emitting an ordinary token runs to max_tokens, + // so the test above measures the stop set, not a broken generation loop. + let resp = safetensors_generate_handler( + axum::extract::State(state(11, Some(EOS))), + axum::Json(serde_json::json!({"prompt": "t1", "max_tokens": MAX_TOKENS})), + ) + .await; + assert_eq!(json(resp).await["tokens_generated"], MAX_TOKENS); +} + +#[tokio::test] +async fn chat_handler_stops_at_im_end() { + let resp = safetensors_chat_completions_handler( + axum::extract::State(state(IM_END, None)), + axum::Json(serde_json::json!({ + "messages": [{"role": "user", "content": "hi"}], + "max_tokens": MAX_TOKENS, + })), + ) + .await; + let v = json(resp).await; + assert_eq!(v["usage"]["completion_tokens"], 0, "{v}"); +} diff --git a/crates/apr-cli/src/commands/serve/tests_st_serve_session_4269.rs b/crates/apr-cli/src/commands/serve/tests_st_serve_session_4269.rs index 01a4c627fb..599d94a926 100644 --- a/crates/apr-cli/src/commands/serve/tests_st_serve_session_4269.rs +++ b/crates/apr-cli/src/commands/serve/tests_st_serve_session_4269.rs @@ -2,6 +2,7 @@ //! `realizar::session::Session`, which leaves a witness entry per turn. use super::st_cpu_generate; +use crate::commands::serve::handlers::apr_cpu_generate_config; use realizar::apr_transformer::{AprTransformer, AprTransformerConfig, AprTransformerLayer}; use realizar::session::{entries_for, EntryKind}; @@ -40,7 +41,12 @@ fn st_serve_generate_runs_through_session_and_leaves_a_safetensors_witness() { let model = tiny_transformer(); // A prompt no other test in this binary uses, so the witness is this call's. let prompt = [7_u32, 5, 3, 9, 4269 % 12]; - let out = st_cpu_generate(&model, &prompt, 2, 0.0).expect("tiny model generates"); + let out = st_cpu_generate( + &model, + &prompt, + &apr_cpu_generate_config(2, 0.0, None, vec![]), + ) + .expect("tiny model generates"); assert_eq!(&out[..prompt.len()], &prompt, "prompt is echoed first"); assert!(out.len() > prompt.len(), "at least one token was generated"); let entries = entries_for(&prompt); diff --git a/crates/apr-cli/src/commands/speedup.rs b/crates/apr-cli/src/commands/speedup.rs index 8bab4bd801..ce7a3b87ea 100644 --- a/crates/apr-cli/src/commands/speedup.rs +++ b/crates/apr-cli/src/commands/speedup.rs @@ -119,7 +119,7 @@ fn run_throughput_gate(path: &Path, config: &QaConfig) -> Result { /// The threshold is untouched (10 tok/s for unasserted GGUF); what changes is /// that the number the gate compares is decode throughput. #[cfg(feature = "inference")] -fn throughput_runtime(path: &Path, config: &QaConfig) -> Result { +fn throughput_runtime(path: &Path, config: &QaConfig) -> Result<(f64, &'static str)> { use realizar::{run_inference, InferenceConfig}; let infer_config = InferenceConfig::new(path) @@ -139,17 +139,34 @@ fn throughput_runtime(path: &Path, config: &QaConfig) -> Result { let mut generated = 0usize; let mut seconds = 0.0_f64; + let (mut gpu_runs, mut runs) = (0usize, 0usize); for _ in 0..config.iterations.max(1) { let result = run()?; generated += result.generated_token_count; seconds += result.inference_ms / 1000.0; + runs += 1; + gpu_runs += usize::from(result.used_gpu); } - Ok(if seconds > 0.0 { + let tps = if seconds > 0.0 { generated as f64 / seconds } else { 0.0 - }) + }; + Ok((tps, runtime_backend_label(gpu_runs, runs))) +} + +/// #3714: the backend the timed runs REPORTED (`used_gpu`), never the build's +/// features — a cuda build whose device could not serve ran on the CPU, and the +/// gate used to print "GPU" for it (and "hybrid" for a MoE file). +#[cfg(feature = "inference")] +fn runtime_backend_label(gpu_runs: usize, runs: usize) -> &'static str { + match (gpu_runs, runs) { + (_, 0) => "runtime entry point, no timed run", + (g, r) if g == r => "runtime entry point, GPU on every timed run", + (0, _) => "runtime entry point, CPU on every timed run", + _ => "runtime entry point, MIXED GPU/CPU timed runs", + } } /// Gate 2 for an architecture the dense loader refuses: the same falsifiable @@ -168,14 +185,9 @@ fn run_throughput_gate_runtime(path: &Path, config: &QaConfig) -> Result= threshold { ">=" } else { "<" } diff --git a/crates/apr-cli/src/commands/test_llm.rs b/crates/apr-cli/src/commands/test_llm.rs index fb0cc47d36..35e1f21275 100644 --- a/crates/apr-cli/src/commands/test_llm.rs +++ b/crates/apr-cli/src/commands/test_llm.rs @@ -22,6 +22,9 @@ //! `decode_tok_per_sec` = 1000 / `itl_p50_ms`, the inter-token rate, which //! excludes time-to-first-token by construction and so cannot absorb a model //! load. Reading a field beats deriving a number. +// #4041: a `--no-default-features` build has no tokio, so `dispatch` cannot run a benchmark and the benchmark +// code is unreachable there. Dead in that build only; every build with `inference` still lints it. +#![cfg_attr(not(feature = "inference"), allow(dead_code))] use crate::error::{CliError, Result}; use apr_test::llm::{ benchmark::{Benchmark, BenchmarkConfig, BenchmarkReport}, @@ -411,6 +414,7 @@ use crate::LlmSubcommand; /// /// # Errors /// Propagates whichever mode ran. +#[cfg(feature = "inference")] pub fn dispatch(command: &LlmSubcommand) -> Result<()> { let rt = tokio::runtime::Runtime::new() .map_err(|e| CliError::InferenceFailed(format!("tokio runtime: {e}")))?; @@ -420,6 +424,18 @@ pub fn dispatch(command: &LlmSubcommand) -> Result<()> { } } +/// #4041: the benchmark drives its server through tokio, which only the `inference` feature brings. A minimal +/// build (`--no-default-features`) names the missing feature instead of failing to compile. +/// +/// # Errors +/// Always: this build cannot run the benchmark. +#[cfg(not(feature = "inference"))] +pub fn dispatch(_command: &LlmSubcommand) -> Result<()> { + Err(CliError::InferenceFailed( + "`apr test llm` needs the `inference` feature (this apr was built without it)".to_string(), + )) +} + /// TWO MODES, ONE ENTRYPOINT — the §4.4-conformant one. /// /// `--band` selects PERF-024's `run_band`, which until PERF-025 was called by @@ -479,7 +495,7 @@ async fn dispatch_band(command: &LlmSubcommand) -> Result<()> { accelerator, quantization, compute_class, - server_features, + server_features: &batuta_common::cli_roles::strings(server_features), tokenization, tokenizer_sha256: tokenizer_sha256.as_deref(), counts_special_tokens: *counts_special_tokens, diff --git a/crates/apr-cli/src/commands/test_llm_band.rs b/crates/apr-cli/src/commands/test_llm_band.rs index aecf2406a1..5d1ad0fde5 100644 --- a/crates/apr-cli/src/commands/test_llm_band.rs +++ b/crates/apr-cli/src/commands/test_llm_band.rs @@ -51,6 +51,9 @@ //! `receipt.r1.json` … `receipt.rN.json`. The cell's verdict is the conjunction //! over them, which is a decision for the runner, not something this producer //! should pre-collapse. +// #4041: a `--no-default-features` build has no tokio, so `dispatch` cannot run a benchmark and the benchmark +// code is unreachable there. Dead in that build only; every build with `inference` still lints it. +#![cfg_attr(not(feature = "inference"), allow(dead_code))] use crate::error::{CliError, Result}; use apr_test::llm::band::{run_band, BandRun, RequestExtra}; @@ -1770,7 +1773,12 @@ async fn cool(first_lane: &mut bool, cooldown: std::time::Duration, interleaved: *first_lane = false; return; } + #[cfg(feature = "inference")] tokio::time::sleep(cooldown).await; + // #4041: no tokio in a minimal build. Nothing reaches this there (`dispatch` refuses first); if it did, the + // cooldown would still be honoured, blocking. + #[cfg(not(feature = "inference"))] + std::thread::sleep(cooldown); } /// Run the §5.1 protocol over every requested band and write the receipts. diff --git a/crates/apr-cli/src/commands/test_llm_band_workload_binding_tests.rs b/crates/apr-cli/src/commands/test_llm_band_workload_binding_tests.rs index 9b639ec89e..557933f765 100644 --- a/crates/apr-cli/src/commands/test_llm_band_workload_binding_tests.rs +++ b/crates/apr-cli/src/commands/test_llm_band_workload_binding_tests.rs @@ -14,11 +14,13 @@ use super::*; use std::io::Write as _; -/// `crates/apr-cli`'s `CARGO_MANIFEST_DIR` sibling to -/// `crates/aprender-serve/benchmarks/qwen-coder/prompts-w1.jsonl`. -fn w1_corpus_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../aprender-serve/benchmarks/qwen-coder/prompts-w1.jsonl") +/// `crates/aprender-serve/benchmarks/qwen-coder/prompts-w1.jsonl`, a sibling crate's file: in +/// tree it must exist, out of tree (the published tarball) the test skips by name (#4149). +fn w1_corpus_path(test: &str) -> Option { + provable_contracts::workspace_path_or_skip!( + test, + "crates/aprender-serve/benchmarks/qwen-coder/prompts-w1.jsonl", + ) } /// A private, per-process scratch file — never the real corpus, never @@ -35,7 +37,9 @@ fn write_tmp(name: &str, content: &str) -> PathBuf { #[test] fn the_real_w1_corpus_binds_to_the_w1_label() { - let path = w1_corpus_path(); + let Some(path) = w1_corpus_path("the_real_w1_corpus_binds_to_the_w1_label") else { + return; + }; let binding = bind_workload(Workload::W1, &path) .unwrap_or_else(|e| panic!("the real W1 corpus must bind to the W1 label: {e}")); assert_eq!(binding.label, Workload::W1); diff --git a/crates/apr-cli/src/commands/train_tests.rs b/crates/apr-cli/src/commands/train_tests.rs index af22b3c12c..524ce804e8 100644 --- a/crates/apr-cli/src/commands/train_tests.rs +++ b/crates/apr-cli/src/commands/train_tests.rs @@ -320,7 +320,7 @@ mod tests { }) .expect("spawn parse thread") .join() - .expect("parse thread must not panic") + .expect("parse thread must not panic").into() } #[test] diff --git a/crates/apr-cli/src/commands_enum.rs b/crates/apr-cli/src/commands_enum.rs index 2eadea8471..aa3f305237 100644 --- a/crates/apr-cli/src/commands_enum.rs +++ b/crates/apr-cli/src/commands_enum.rs @@ -35,6 +35,29 @@ pub struct BackendArg { pub backend: Option, } +/// Tasks `apr eval --task` accepts (ONT-4g G1.4, #4476). Omitting `--task` +/// runs perplexity on `--dataset`. +/// +/// The flag was a free-form `String` whose help named only `classify`, while the +/// dispatcher matched ten task names and sent ANY other string — a typo, or +/// `classify` in a build without `training` — to perplexity under a `_` arm: the +/// user asked for one evaluation and silently got another (the #2583 class). The +/// list is the `Some("…")` arms of the `ExtendedCommands::Eval` dispatch in +/// `dispatch_analysis.rs`, pinned both ways by +/// `test_eval_task_values_match_dispatch_arms_4476`. +pub const EVAL_TASK_VALUES: [&str; 10] = [ + "classify", + "code", + "humaneval", + "mbpp", + "contamination", + "compare", + "verify", + "correlation", + "human", + "plan", +]; + /// Thinking modes `--thinking` accepts (#3723). pub const THINKING_VALUES: [&str; 2] = ["on", "off"]; @@ -128,19 +151,22 @@ pub enum CodeInputFormat { #[derive(Subcommand, Debug)] pub enum Commands { /// Run model directly (auto-download, cache, execute) + // #3745 S1 v1.1: the sampling controls below join this group, which is how + // `apr surface` reads them as role `sampling`. + #[command(groups(batuta_common::cli_roles::SamplingArg::groups()))] Run { /// Model source: local path, hf://org/repo, or URL #[arg(value_name = "SOURCE")] - source: String, + source: ModelRef, /// Text prompt (positional): `apr run model.gguf "What is 2+2?"` #[arg(value_name = "PROMPT")] - positional_prompt: Option, + positional_prompt: Option, /// Input file (audio, text, etc.) #[arg(short, long)] - input: Option, + input: Option, /// Text prompt for generation (for LLM models) #[arg(short, long)] - prompt: Option, + prompt: Option, /// Maximum tokens to generate (default: 32) #[arg(short = 'n', long, default_value = "32")] max_tokens: usize, @@ -149,10 +175,10 @@ pub enum Commands { stream: bool, /// Language code (for ASR models) #[arg(short, long)] - language: Option, + language: Option, /// Task (transcribe, translate) #[arg(short, long)] - task: Option, + task: Option, /// Output format (text, json, srt, vtt) #[arg(short = 'f', long, default_value = "text", value_parser = RUN_FORMAT_VALUES)] format: String, @@ -177,13 +203,13 @@ pub enum Commands { trace: bool, /// Trace specific steps only (comma-separated) #[arg(long, value_delimiter = ',')] - trace_steps: Option>, + trace_steps: Option>, /// Verbose tracing (show tensor values) #[arg(long)] trace_verbose: bool, /// Save trace output to JSON file #[arg(long, value_name = "FILE")] - trace_output: Option, + trace_output: Option, /// Trace detail level (none, basic, layer, payload, chrome) /// "chrome" outputs chrome://tracing JSON integrating layer trace + brick profile. /// F-CLIPARITY-01 / PMAT-386 / paiml/aprender#574 @@ -202,27 +228,27 @@ pub enum Commands { #[arg(long)] chat: bool, /// Sampling temperature (0.0 = greedy, default: 0.0) - #[arg(long, default_value = "0.0")] + #[arg(long, default_value = "0.0", group = batuta_common::cli_roles::SamplingKind::Temperature.id())] temperature: f32, /// Top-k sampling, used when --temperature > 0. `--temperature 0` (the default) /// or `--top-k 1` decodes greedily; `--top-k 0` disables the filter. - #[arg(long, default_value_t = crate::commands::run::DEFAULT_TOP_K)] + #[arg(long, default_value_t = crate::commands::run::DEFAULT_TOP_K, group = batuta_common::cli_roles::SamplingKind::TopK.id())] top_k: usize, /// Top-p nucleus sampling (0.0 = disabled). When set with --top-k, applies top-k first then top-p. /// F-CLIPARITY-01 / PMAT-381 / paiml/aprender#569 - #[arg(long)] + #[arg(long, group = batuta_common::cli_roles::SamplingKind::TopP.id())] top_p: Option, /// RNG seed for deterministic sampling (default: 299792458, matching Candle) /// F-CLIPARITY-01 / PMAT-382 / paiml/aprender#570 - #[arg(long, default_value = "299792458")] + #[arg(long, default_value = "299792458", group = batuta_common::cli_roles::SamplingKind::Seed.id())] seed: u64, /// Repetition penalty (1.0 = no penalty, >1.0 penalizes repeats) /// F-CLIPARITY-01 / PMAT-383 / paiml/aprender#571 - #[arg(long, default_value = "1.0")] + #[arg(long, default_value = "1.0", group = batuta_common::cli_roles::SamplingKind::RepeatPenalty.id())] repeat_penalty: f32, /// Context window for repetition penalty (number of recent tokens to check) /// F-CLIPARITY-01 / PMAT-384 / paiml/aprender#571 - #[arg(long, default_value = "64")] + #[arg(long, default_value = "64", group = batuta_common::cli_roles::SamplingKind::RepeatLastN.id())] repeat_last_n: usize, /// Process prompt tokens one-by-one instead of batched prefill. /// Useful for debugging prefill correctness (comparing per-token attention). @@ -234,7 +260,7 @@ pub enum Commands { /// Each input line: {"prompt": "...", "task_id": "..."} /// Chat template is applied automatically. #[arg(long, value_name = "FILE")] - batch_jsonl: Option, + batch_jsonl: Option, /// Show verbose output (model loading, backend info) #[arg(short, long)] verbose: bool, @@ -254,7 +280,7 @@ pub enum Commands { Inspect { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Show vocabulary details #[arg(long)] vocab: bool, @@ -289,7 +315,7 @@ pub enum Commands { Debug { /// Path to .apr model file (omit only when using a subcommand) #[arg(value_name = "FILE")] - file: Option, + file: Option, /// Debug subcommand, e.g. `embed-viz` #[command(subcommand)] action: Option, @@ -310,7 +336,7 @@ pub enum Commands { Validate { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Score the structural checks that ran #[arg(long)] quality: bool, @@ -328,10 +354,10 @@ pub enum Commands { ValidateManifest { /// Path to manifest YAML #[arg(value_name = "MANIFEST")] - file: PathBuf, + file: ConfigPath, /// Optional local .apr artifact to discharge FALSIFY-PM-002 (sha256 match) #[arg(long, value_name = "APR_FILE")] - artifact: Option, + artifact: Option, /// Discharge FALSIFY-PM-003 via network: HTTP HEAD + streaming sha256. /// Default is DEFERRED (offline-safe). Ignored when --offline is set. /// Closes F-PUBLISH-EXTRA-001::dogfood_ex05 (no Python in ex-05). @@ -342,10 +368,10 @@ pub enum Commands { Diff { /// First model file #[arg(value_name = "FILE1")] - file1: PathBuf, + file1: ModelPath, /// Second model file #[arg(value_name = "FILE2")] - file2: PathBuf, + file2: ModelPath, /// Show weight-level differences #[arg(long)] weights: bool, @@ -354,7 +380,7 @@ pub enum Commands { values: bool, /// Filter tensors by name pattern (for --values) #[arg(long)] - filter: Option, + filter: Option, /// Maximum number of tensors to compare (for --values) #[arg(long, default_value = "10")] limit: usize, @@ -381,13 +407,13 @@ pub enum Commands { Tensors { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Show tensor statistics (mean, std, min, max) #[arg(long)] stats: bool, /// Filter tensors by name pattern #[arg(long)] - filter: Option, + filter: Option, /// Limit number of tensors shown (0 = unlimited) #[arg(long, default_value = "0")] limit: usize, @@ -399,13 +425,13 @@ pub enum Commands { Trace { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Filter layers by name pattern #[arg(long)] - layer: Option, + layer: Option, /// Compare with reference model #[arg(long)] - reference: Option, + reference: Option, /// Output as JSON #[arg(long)] json: bool, @@ -428,22 +454,22 @@ pub enum Commands { /// stage. Output goes to `--save-tensor-dir` if provided, /// else `/trace-tensors//`. #[arg(long, value_name = "STAGES")] - save_tensor: Option, + save_tensor: Option, /// Output directory for `--save-tensor` (default: sibling /// `trace-tensors//`). #[arg(long, value_name = "DIR")] - save_tensor_dir: Option, + save_tensor_dir: Option, /// Layer-id range for `--save-tensor` (default: 0..1, i.e. /// layer 0 only). Format: `START..END` (Rust range syntax, /// END exclusive). #[arg(long, value_name = "RANGE", default_value = "0..1")] - save_tensor_layers: String, + save_tensor_layers: FreeText, }, /// Check for best practices and conventions Lint { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Fail on warnings as well as errors. /// /// By default only ERROR-level findings fail the run. Every real model @@ -458,7 +484,7 @@ pub enum Commands { BeatRun { /// Path to a beat-benchmark contract YAML (e.g. contracts/beat-sklearn-iris-v1.yaml) #[arg(value_name = "CONTRACT")] - contract: PathBuf, + contract: ConfigPath, /// Measured metric value; when given, emit a WON/REGRESSED verdict and /// exit non-zero on regression. Omit to just report the pinned baseline. #[arg(long, value_name = "VALUE")] @@ -468,22 +494,22 @@ pub enum Commands { Manifest { /// Files to include in the manifest (one entry per file) #[arg(value_name = "FILES", num_args = 1..)] - files: Vec, + files: Vec, /// Output JSON manifest path #[arg(short, long, value_name = "MAN_JSON")] - output: PathBuf, + output: OutputPath, }, /// Explain errors, architecture, tensors, and kernel dispatch Explain { /// Error code, model file path, or family name (auto-detected) #[arg(value_name = "CODE_OR_FILE")] - code_or_file: Option, + code_or_file: Option, /// Path to .apr model file (optional context for --tensor) #[arg(short, long)] - file: Option, + file: Option, /// Explain a specific tensor #[arg(long)] - tensor: Option, + tensor: Option, /// Explain kernel dispatch pipeline for architecture #[arg(long)] kernel: bool, @@ -506,22 +532,22 @@ pub enum Commands { Export { /// Path to .apr model file #[arg(value_name = "FILE", required_unless_present = "list_formats")] - file: Option, + file: Option, /// Output format (safetensors, gguf, mlx, onnx, openvino, coreml) #[arg(long, default_value = "safetensors")] - format: String, + format: FreeText, /// Output file/directory path #[arg(short, long)] - output: Option, + output: Option, /// Apply quantization during export (int8, int4, fp16) #[arg(long)] - quantize: Option, + quantize: Option, /// List all supported export formats #[arg(long)] list_formats: bool, /// Batch export to multiple formats (comma-separated: gguf,mlx,safetensors) #[arg(long)] - batch: Option, + batch: Option, /// Output in JSON format #[arg(long)] json: bool, @@ -536,16 +562,16 @@ pub enum Commands { Import { /// Source: hf://org/repo, local file, or URL #[arg(value_name = "SOURCE")] - source: String, + source: ModelRef, /// Output .apr file path (default: derived from source name) #[arg(short, long)] - output: Option, + output: Option, /// Model architecture (whisper, llama, bert, qwen2, qwen3, gpt2, starcoder, gpt-neox, opt, phi, gemma, falcon, mamba, t5, auto) #[arg(long, default_value = "auto")] - arch: String, + arch: FreeText, /// Quantization (int8, int4, fp16) #[arg(long)] - quantize: Option, + quantize: Option, /// Strict mode: reject unverified architectures and fail on validation errors #[arg(long)] strict: bool, @@ -556,7 +582,7 @@ pub enum Commands { /// PMAT-232: External tokenizer.json for weights-only GGUF files. /// Required if the GGUF has no embedded tokenizer vocabulary. #[arg(long)] - tokenizer: Option, + tokenizer: Option, /// F-GT-001: Enforce provenance chain. Rejects pre-baked GGUF imports /// (only SafeTensors sources allowed). Ensures single-provenance testing. #[arg(long)] @@ -574,11 +600,11 @@ pub enum Commands { /// string "dataset", the next positional `repo` is the /// HuggingFace dataset repo and dataset-pull semantics apply. #[arg(value_name = "MODEL_OR_ASSET_TYPE")] - model_ref: String, + model_ref: ModelRef, /// Dataset repository (used only when model_ref == "dataset"). /// Per `apr-cli-pull-dataset-v1.yaml`. #[arg(value_name = "REPO")] - repo: Option, + repo: Option, /// Force re-download even if cached #[arg(long)] force: bool, @@ -600,7 +626,7 @@ pub enum Commands { /// CRUX-A-03: pin to a specific branch, tag, or git SHA on the remote /// (HuggingFace Hub). Defaults to "main" when omitted. #[arg(long, value_name = "REV")] - revision: Option, + revision: Option, /// CRUX-A-20: offline mode — forbid any outbound network I/O. /// Equivalent to APR_OFFLINE=1 or HF_HUB_OFFLINE=1 in the environment. #[arg(long)] @@ -609,11 +635,11 @@ pub enum Commands { /// multiple times; matches are unioned. fnmatch-compatible /// (`*`, `?`, `[a-z]`). No-match is fail-fast. #[arg(long, value_name = "GLOB")] - include: Vec, + include: Vec, /// (dataset mode) Output directory. Default: /// `~/.cache/aprender/datasets//`. #[arg(short = 'o', long)] - output: Option, + output: Option, }, /// Registry operations (CRUX-A-01): inspect alias map, etc. Registry { @@ -628,22 +654,22 @@ pub enum Commands { Rm { /// Model reference to remove #[arg(value_name = "MODEL")] - model_ref: String, + model_ref: ModelRef, }, /// Convert/optimize model Convert { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Quantize to format (int8, int4, fp16, q4k) #[arg(long)] - quantize: Option, + quantize: Option, /// Compress output (none, zstd, zstd-max, lz4) #[arg(long)] - compress: Option, + compress: Option, /// Output file path #[arg(short, long)] - output: PathBuf, + output: OutputPath, /// Force overwrite existing files #[arg(short, long)] force: bool, @@ -657,16 +683,16 @@ pub enum Commands { Stamp { /// Path to input .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// SPDX license identifier (e.g., Apache-2.0) #[arg(long)] - license: Option, + license: Option, /// Training-data source (e.g., huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct) #[arg(long = "data-source")] - data_source: Option, + data_source: Option, /// SPDX license for data_source (e.g., Apache-2.0) #[arg(long = "data-license")] - data_license: Option, + data_license: Option, /// HuggingFace class name (e.g., Qwen2ForCausalLM, LlamaForCausalLM). /// /// PMAT-690 P0-K extension (SPEC §86): patch the upstream @@ -674,12 +700,12 @@ pub enum Commands { /// consumers (apr inspect --quality, apr pretrain --init, /// apr export → llama-cli) see the correct HF identity. #[arg(long = "hf-architecture")] - hf_architecture: Option, + hf_architecture: Option, /// HuggingFace model_type slug (e.g., qwen2, llama). /// /// PMAT-690 P0-K extension (SPEC §86). #[arg(long = "hf-model-type")] - hf_model_type: Option, + hf_model_type: Option, /// Lowercase architecture family slug (e.g., qwen2, llama). /// /// PMAT-690 P0-K extension (SPEC §86). This is the field @@ -687,7 +713,7 @@ pub enum Commands { /// patching it, pre-P0-K checkpoints with the P0-H "LlamaForCausalLM" /// fallback in this field cannot be loaded as Qwen2 inits. #[arg(long)] - architecture: Option, + architecture: Option, /// Directory containing tokenizer files (vocab.json + merges.txt /// OR tokenizer.json). When provided, embeds the vocabulary + /// BPE merges into the APR's `custom.tokenizer.vocabulary` / @@ -700,10 +726,10 @@ pub enum Commands { /// the tokenizer post-hoc so the artifact is self-contained /// for inference (the apr binary's headline use case). #[arg(long = "tokenizer", value_name = "DIR")] - tokenizer_dir: Option, + tokenizer_dir: Option, /// Output file path #[arg(short, long)] - output: PathBuf, + output: OutputPath, /// Force overwrite existing files #[arg(short, long)] force: bool, @@ -712,16 +738,16 @@ pub enum Commands { Compile { /// Input .apr model file #[arg(value_name = "FILE", required_unless_present = "list_targets")] - file: Option, + file: Option, /// Output binary path (default: derived from model name) #[arg(short, long)] - output: Option, + output: Option, /// Target triple (e.g., x86_64-unknown-linux-musl) #[arg(long)] - target: Option, + target: Option, /// Quantize weights before embedding (int8, int4, fp16) #[arg(long)] - quantize: Option, + quantize: Option, /// Release mode (optimized) #[arg(long)] release: bool, @@ -739,19 +765,19 @@ pub enum Commands { Merge { /// Model files to merge #[arg(value_name = "FILES", num_args = 2..)] - files: Vec, + files: Vec, /// Merge strategy (average, weighted, slerp, ties, dare) #[arg(long, default_value = "average")] - strategy: String, + strategy: FreeText, /// Output file path (optional in --plan mode) #[arg(short, long, required_unless_present = "plan")] - output: Option, + output: Option, /// Weights for weighted merge (comma-separated, e.g., "0.7,0.3") #[arg(long, value_delimiter = ',')] weights: Option>, /// Base model for TIES/DARE (task vectors computed as delta from base) #[arg(long)] - base_model: Option, + base_model: Option, /// DARE drop probability (default: 0.9) #[arg(long, default_value = "0.9")] drop_rate: f32, @@ -772,19 +798,19 @@ pub enum Commands { Quantize { /// Input model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Quantization scheme: int8, int4, fp16, q4k #[arg(long, short = 's', default_value = "int4")] - scheme: String, + scheme: FreeText, /// Output file path (required unless --plan) #[arg(short, long)] - output: Option, + output: Option, /// Output format override (apr, gguf, safetensors) #[arg(long)] - format: Option, + format: Option, /// Batch quantization (comma-separated schemes) #[arg(long)] - batch: Option, + batch: Option, /// Plan mode (estimate only, no execution) #[arg(long)] plan: bool, @@ -799,18 +825,21 @@ pub enum Commands { /// /// Exposes `apr` as MCP tools for Claude Code, Cursor, Cline, and other /// MCP clients. Configure via `.mcp.json` with `{"command":"apr","args":["mcp"]}`. + // #3745 S1 v1.1: prompts arrive over stdio JSON-RPC (the apr.run / apr.serve tools), not through a PromptText arg, + // so the generator is declared by marker (`apr surface` → `generates: true`). + #[command(group(batuta_common::cli_roles::ServesGeneration::group()))] Mcp {}, /// Interactive terminal UI Tui { /// Path to .apr model file #[arg(value_name = "FILE")] - file: Option, + file: Option, }, /// Model self-test: 10-stage pipeline integrity check (APR-TRACE-001) Check { /// Path to model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Disable GPU acceleration #[arg(long)] no_gpu: bool, @@ -830,19 +859,19 @@ pub enum Commands { Code { /// Path to local GGUF/APR model file (prefers .apr format) #[arg(long)] - model: Option, + model: Option, /// Project directory (loads APR.md/CLAUDE.md from this path) #[arg(long, default_value = ".")] - project: PathBuf, + project: DirPath, /// Resume previous session (optionally by ID) #[arg(long)] - resume: Option>, + resume: Option>, /// Agent manifest (advanced — overrides defaults) #[arg(long)] - manifest: Option, + manifest: Option, /// Initial prompt (non-interactive: print response and exit) #[arg(short, long)] @@ -856,7 +885,7 @@ pub enum Commands { /// — a wrong-model execution with no diagnostic — and typo'd flags /// produced no parse error at all. Options are now parsed in any /// position; a prompt that genuinely starts with `-` needs `--`. - prompt: Vec, + prompt: Vec, /// Max turns before stopping #[arg(long, default_value = "50")] @@ -868,7 +897,7 @@ pub enum Commands { /// (`§ trace_schema`). Used by `ccpa measure` to score apr-code /// against canonical Claude Code reference fixtures. #[arg(long)] - emit_trace: Option, + emit_trace: Option, /// Output format for non-interactive (`-p`) mode (PMAT-CODE-OUTPUT-FORMAT-001). /// `text` (default): plain assistant text. @@ -940,6 +969,21 @@ pub enum Commands { #[command(subcommand)] Cgp(cgp::cli::Commands), + /// Print apr's own command surface as JSON (`apr-cli-surface/v1`, #3745). + /// + /// Hidden: it is a gate input, not a user command. Every subcommand path and + /// every argument, with each argument's role read from the type that built + /// it. Release cells are derived from this output rather than from a list + /// someone typed. + #[command(hide = true)] + Surface, + + /// PTX falsification analysis and FKR test generation: analyze, gen-fkr + /// (was the `aprender-ptx-debug` binary; pure Rust, so unlike `apr ptx` + /// it needs no build feature) + #[command(subcommand, name = "ptx-debug")] + PtxDebug(trueno_ptx_debug::cli::Command), + /// Provable-contracts: validate, lint, score, kani, proof-status /// (the `pv` binary keeps shipping under its own name; this is the /// in-apr route to the same commands) @@ -963,10 +1007,10 @@ pub enum DebugCommands { EmbedViz { /// Model file holding the embedding table #[arg(long, value_name = "FILE")] - model: PathBuf, + model: ModelPath, /// Embedding tensor name (default: auto-detect the known names) #[arg(long, value_name = "NAME")] - tensor: Option, + tensor: Option, /// Projection method: exact `pca`, seeded `random`, or `umap` (refused) #[arg(long, value_enum, default_value_t = EmbedProjection::Pca)] projection: EmbedProjection, @@ -979,10 +1023,10 @@ pub enum DebugCommands { /// Token text, one per line, for the `token_str` column. Without it apr /// reads the GGUF vocabulary, or writes `` #[arg(long, value_name = "FILE")] - tokens: Option, + tokens: Option, /// Write the CSV here instead of stdout #[arg(short, long, value_name = "FILE")] - output: Option, + output: Option, /// Overwrite an existing --output file (refused without it) #[arg(short, long)] force: bool, diff --git a/crates/apr-cli/src/data_commands.rs b/crates/apr-cli/src/data_commands.rs index e09cabcb16..e847c9587d 100644 --- a/crates/apr-cli/src/data_commands.rs +++ b/crates/apr-cli/src/data_commands.rs @@ -54,25 +54,25 @@ pub enum DataCommands { Audit { /// Path to JSONL data file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Number of output classes (for label range validation) #[arg(long, default_value = "5")] num_classes: usize, /// Input text column name #[arg(long, default_value = "input")] - input_column: String, + input_column: FreeText, /// Label column name #[arg(long, default_value = "label")] - label_column: String, + label_column: FreeText, /// Preamble prefix to detect (e.g., "#!/") #[arg(long, default_value = "#!/")] - preamble_prefix: Option, + preamble_prefix: Option, }, /// Stratified train/val/test split preserving class proportions Split { /// Path to JSONL data file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Training set fraction #[arg(long, default_value = "0.8")] train: f64, @@ -84,22 +84,22 @@ pub enum DataCommands { test: f64, /// Label column name for stratification #[arg(long, default_value = "label")] - label_column: String, + label_column: FreeText, /// Random seed for deterministic split #[arg(long, default_value = "42")] seed: u64, /// Output directory for split files #[arg(short, long)] - output: PathBuf, + output: OutputPath, }, /// Check training data for benchmark contamination via n-gram overlap Decontaminate { /// Path to training JSONL data file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Reference benchmark JSONL files to check against #[arg(long, required = true, num_args = 1..)] - reference: Vec, + reference: Vec, /// N-gram size for overlap detection (must be >= 1) #[arg(long, default_value = "10", value_parser = parse_ngram_size)] ngram: usize, @@ -114,10 +114,10 @@ pub enum DataCommands { Dedup { /// Path to JSONL data file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Output file path for the deduplicated dataset #[arg(short, long)] - output: PathBuf, + output: OutputPath, /// Output as JSON #[arg(long)] json: bool, @@ -126,13 +126,13 @@ pub enum DataCommands { Balance { /// Path to JSONL data file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Rebalancing strategy: oversample, undersample, sqrt-inverse #[arg(long, default_value = "oversample")] - strategy: String, + strategy: FreeText, /// Label column name #[arg(long, default_value = "label")] - label_column: String, + label_column: FreeText, /// Number of classes (for sqrt-inverse weight computation) #[arg(long)] num_classes: Option, @@ -141,6 +141,6 @@ pub enum DataCommands { seed: u64, /// Output file path (required for oversample/undersample) #[arg(short, long)] - output: Option, + output: Option, }, } diff --git a/crates/apr-cli/src/dispatch.rs b/crates/apr-cli/src/dispatch.rs index 22edb34697..2487a5bad9 100644 --- a/crates/apr-cli/src/dispatch.rs +++ b/crates/apr-cli/src/dispatch.rs @@ -5,6 +5,11 @@ fn dispatch_core_command(cli: &Cli) -> Option> { contract_pre_side_effect_classification!(); contract_pre_dispatch_completeness!(); contract_pre_output_format_fidelity!(); + // #3745 S1: the gate input, answered before any command that loads a model. + if matches!(cli.command.as_ref(), Commands::Surface) { + return Some(crate::surface::run()); + } + // Try runtime commands first (check, run, serve) if let Some(result) = dispatch_runtime_commands(cli) { return Some(result); @@ -53,7 +58,7 @@ fn dispatch_core_command(cli: &Cli) -> Option> { /// library: `apr rag` -> `aprender_rag_cli`, `apr zram` -> `aprender_zram_cli`. /// /// Each arm calls the SAME `dispatch` function the standalone binary calls, so -/// `apr rag index` and `trueno-rag index` cannot drift apart. Before this, both +/// `apr rag index` and `aprender-rag index` cannot drift apart. Before this, both /// command enums lived in a `main.rs`, which is importable by nothing -- the /// separate binary was the only way to reach any of it. fn dispatch_sibling_cli_commands(cli: &Cli) -> Option> { @@ -91,6 +96,9 @@ fn dispatch_sibling_cli_commands(cli: &Cli) -> Option> { cgp::cli::dispatch(command.clone(), cli.json) .map_err(|e| CliError::ValidationFailed(format!("cgp: {e}"))), ), + Commands::PtxDebug(command) => Some(ptx_debug_result( + trueno_ptx_debug::run::run(command.clone()), + )), Commands::Pv(command) => Some( aprender_contracts_cli::dispatch(command.clone()) .map_err(|e| CliError::ValidationFailed(format!("pv: {e}"))), @@ -105,6 +113,24 @@ fn dispatch_sibling_cli_commands(cli: &Cli) -> Option> { /// signatures that #3606 is changing at the same time; the flag is still a flag to the user, and /// the guard prints `--revalidate` as its reason when it fires. (Extracted from /// `dispatch_runtime_commands` unchanged, to keep it under the complexity ratchet.) +/// Map an `apr ptx-debug` outcome onto apr's error type (#4062). +/// +/// The standalone binary exits 1/2/3 for its analysis verdict (score < 90, +/// score < `--min-score`, critical bugs). apr has one exit-code convention, so +/// every non-zero verdict becomes a validation failure (exit 5) whose message +/// names the verdict — a failing analysis must never exit 0. +fn ptx_debug_result(outcome: Result) -> Result<(), CliError> { + let verdict = match outcome { + Ok(0) => return Ok(()), + Ok(1) => "score below 90", + Ok(2) => "score below --min-score", + Ok(3) => "critical bugs present", + Ok(_) => "analysis failed", + Err(e) => return Err(CliError::ValidationFailed(format!("ptx-debug: {e}"))), + }; + Err(CliError::ValidationFailed(format!("ptx-debug: {verdict}"))) +} + fn request_f2_revalidate(revalidate: bool) { if revalidate { // SAFETY-BY-ORDER: set before any inference thread exists; the @@ -175,10 +201,10 @@ fn check_run_backend(backend: Option<&str>) -> Result<(), CliError> { `cuda` feature, so the CUDA backend does not exist in this binary. \ Refusing to silently fall back to wgpu/CPU: that path is ~20x slower \ (~20 tok/s vs ~400) and makes any throughput measurement taken through it \ -meaningless. Rebuild the ROOT facade with CUDA: `cargo build --release \ ---features cuda` (build the root, not `-p apr-cli`: BOTH packages define a \ -binary named `apr`, and only the root's cuda = [\"cli\", \"apr-cli/cuda\"] \ -chain enables this path). To run on this build anyway, pass `--backend cpu` \ +meaningless. Rebuild with CUDA: `cargo build --release --features cuda` \ +(the root facade) or `cargo build --release -p apr-cli --features cuda` \ +(what the nightly CUDA asset builds). Both `apr` binaries run the same \ +`apr_cli::cli_main`. To run on this build anyway, pass `--backend cpu` \ or drop `--backend`." .to_string(), )); @@ -307,11 +333,12 @@ fn dispatch_runtime_commands(cli: &Cli) -> Option> { // GH-240: merge global --json flag into output format let effective_format = if cli.json { "json" } else { format.as_str() }; + let trace_steps = trace_steps.as_deref().map(batuta_common::cli_roles::strings); dispatch_run( source, - positional_prompt.as_ref(), + positional_prompt.as_ref().map(PromptText::as_string), input.as_deref(), - prompt.as_ref(), + prompt.as_ref().map(PromptText::as_string), *max_tokens, *stream, language.as_deref(), @@ -326,7 +353,7 @@ fn dispatch_runtime_commands(cli: &Cli) -> Option> { *trace_payload, trace_steps.as_deref(), *trace_verbose, - trace_output.clone(), + trace_output.as_deref().map(Path::to_path_buf), trace_level.as_str(), *profile, *chat, @@ -361,14 +388,14 @@ fn dispatch_runtime_commands(cli: &Cli) -> Option> { max_tokens, thinking, } => dispatch_code_command(CodeArgs { - model, + model: &model.as_deref().map(Path::to_path_buf), project, - resume, - prompt, + resume: &resume.as_ref().map(|r| r.as_ref().map(FreeText::to_string)), + prompt: &batuta_common::cli_roles::strings(prompt), print: *print, max_turns: *max_turns, - manifest, - emit_trace, + manifest: &manifest.as_deref().map(Path::to_path_buf), + emit_trace: &emit_trace.as_deref().map(Path::to_path_buf), output_format: *output_format, input_format: *input_format, no_gpu: *no_gpu, @@ -514,13 +541,13 @@ fn dispatch_debug( }) = action { return commands::embed_viz::run(&commands::embed_viz::EmbedVizArgs { - model: model.clone(), - tensor: tensor.clone(), + model: model.to_path_buf(), + tensor: tensor.as_deref().map(str::to_string), projection: *projection, seed: *seed, limit: *limit, - tokens: tokens.clone(), - output: output.clone(), + tokens: tokens.as_deref().map(Path::to_path_buf), + output: output.as_deref().map(Path::to_path_buf), force: *force, }); } @@ -600,7 +627,7 @@ fn dispatch_inspection_commands(cli: &Cli) -> Option> { Commands::Manifest { files, output } => { // CRUX-G-05 — SHA-256 manifest of the input file set. - commands::manifest::run(files, output, cli.json) + commands::manifest::run(&batuta_common::cli_roles::path_bufs(files), output, cli.json) } Commands::Explain { code_or_file, @@ -611,8 +638,8 @@ fn dispatch_inspection_commands(cli: &Cli) -> Option> { verbose, proof_status, } => explain::run( - code_or_file.clone(), - file.clone(), + code_or_file.as_deref().map(str::to_string), + file.as_deref().map(Path::to_path_buf), tensor.as_deref(), *kernel, *json || cli.json, @@ -859,7 +886,7 @@ fn dispatch_format_commands(cli: &Cli) -> Option> { quantize.as_deref(), *strict, *preserve_q4k, - tokenizer.as_ref(), + tokenizer.as_deref().map(Path::to_path_buf).as_ref(), *enforce_provenance, *allow_no_config, cli.json, @@ -992,7 +1019,7 @@ fn dispatch_model_commands(cli: &Cli) -> Option> { strategy, output.as_deref(), weights.clone(), - base_model.clone(), + base_model.as_deref().map(Path::to_path_buf), *drop_rate, *density, *seed, @@ -1067,7 +1094,7 @@ fn dispatch_model_commands(cli: &Cli) -> Option> { coordinator.as_deref(), *expect_workers, *wait_gpu, - adapters, + &batuta_common::cli_roles::strings(adapters), adapters_config.as_deref(), cli.json, *experimental_mps, @@ -1157,7 +1184,7 @@ fn dispatch_model_commands(cli: &Cli) -> Option> { // full downloads in violation of the contract. Some(r) => pull::run_dataset( r, - include, + &batuta_common::cli_roles::strings(include), revision.as_deref(), output.as_deref(), *dry_run, @@ -1185,7 +1212,7 @@ fn dispatch_model_commands(cli: &Cli) -> Option> { Commands::Registry { command } => crate::commands::registry::run(command.clone()), Commands::List => pull::list(cli.json, cli.quiet), Commands::Rm { model_ref } => pull::remove(model_ref, cli.json), - Commands::Tui { file } => tui::run(file.clone()), + Commands::Tui { file } => tui::run(file.as_deref().map(Path::to_path_buf)), Commands::Mcp {} => mcp::run(), _ => return None, diff --git a/crates/apr-cli/src/dispatch_analysis.rs b/crates/apr-cli/src/dispatch_analysis.rs index bee51a541f..b87f70c0ad 100644 --- a/crates/apr-cli/src/dispatch_analysis.rs +++ b/crates/apr-cli/src/dispatch_analysis.rs @@ -381,7 +381,7 @@ fn dispatch_analysis_commands(cli: &Cli) -> Option> { /// Run one alimentar command through `alimentar::cli::dispatch` -- the SAME /// function the standalone binary calls, so `apr data x ` and -/// `alimentar ` cannot drift. +/// `aprender-data ` cannot drift. /// /// The command is re-parsed from argv rather than moved out of the parsed value: /// the apr dispatch chain takes `&Cli`, and alimentar's arg types are not all @@ -408,7 +408,7 @@ fn dispatch_alimentar_passthrough( Ok(()) } else { Err(CliError::ValidationFailed( - "alimentar command failed".to_string(), + "aprender-data command failed".to_string(), )) } } @@ -461,7 +461,13 @@ fn dispatch_data_command(command: &DataCommands, json: bool) -> std::result::Res ngram, threshold, json: json_flag, - } => data::run_decontaminate(file, reference, *ngram, *threshold, *json_flag || json), + } => data::run_decontaminate( + file, + &batuta_common::cli_roles::path_bufs(reference), + *ngram, + *threshold, + *json_flag || json, + ), DataCommands::Dedup { file, output, @@ -683,7 +689,9 @@ fn dispatch_analysis_commands_rest(cli: &Cli) -> Option> { *timeout, *json || cli.json, *verbose || cli.verbose, - skip.as_deref(), + skip.as_deref() + .map(batuta_common::cli_roles::strings) + .as_deref(), ) }), @@ -699,9 +707,9 @@ fn dispatch_analysis_commands_rest(cli: &Cli) -> Option> { validate, full, }) => oracle::run( - source.as_ref(), - family.as_ref(), - size.as_ref(), + source.as_ref().map(ModelRef::as_string), + family.as_ref().map(FreeText::as_string), + size.as_ref().map(FreeText::as_string), *compliance, *tensors, cli.json, @@ -897,20 +905,37 @@ fn dispatch_runs_command(command: &RunsCommands, cli: &Cli) -> std::result::Resu status, json, limit, - } => commands::runs::run_ls(dir, *global, status, *json || cli.json, *limit), + } => commands::runs::run_ls( + &dir.as_deref().map(Path::to_path_buf), + *global, + status, + *json || cli.json, + *limit, + ), RunsCommands::Show { run_id, dir, global, json, - } => commands::runs::run_show(run_id, dir, *global, *json || cli.json), + } => commands::runs::run_show( + run_id, + &dir.as_deref().map(Path::to_path_buf), + *global, + *json || cli.json, + ), RunsCommands::Diff { run_a, run_b, dir, global, json, - } => commands::runs::run_diff(run_a, run_b, dir, *global, *json || cli.json), + } => commands::runs::run_diff( + run_a, + run_b, + &dir.as_deref().map(Path::to_path_buf), + *global, + *json || cli.json, + ), } } @@ -921,9 +946,11 @@ fn dispatch_experiment_command( cli: &Cli, ) -> std::result::Result<(), CliError> { match command { - ExperimentCommands::View { db, global, json } => { - commands::experiment::experiment_view(db, *global, *json || cli.json) - } + ExperimentCommands::View { db, global, json } => commands::experiment::experiment_view( + &db.as_deref().map(Path::to_path_buf), + *global, + *json || cli.json, + ), } } @@ -1088,7 +1115,14 @@ fn dispatch_train_command(command: &TrainCommands, cli: &Cli) -> std::result::Re budget_mb, dry_run, } => train::run_submit( - cluster, model, adapters, *rank, *epochs, *budget_mb, *dry_run, cli.json, + cluster, + model, + &batuta_common::cli_roles::strings(adapters), + *rank, + *epochs, + *budget_mb, + *dry_run, + cli.json, ), TrainCommands::ClusterStatus { cluster } => train::run_cluster_status(cluster, cli.json), } @@ -1160,7 +1194,7 @@ fn dispatch_tokenize_command( estimate_only, estimate_sample_docs, } => tokenize::run_encode_corpus( - corpus.as_slice(), + batuta_common::cli_roles::path_bufs(corpus).as_slice(), tokenizer, output, *shard_tokens, @@ -1411,6 +1445,12 @@ fn dispatch_profiling_commands(cli: &Cli) -> Option> { *generate_card, cli.json, ), + // #4476: the parser admits `classify` in every build; without the + // feature it used to fall to the `_` arm and run perplexity instead. + #[cfg(not(feature = "training"))] + Some("classify") => Err(crate::error::CliError::FeatureDisabled( + "--task classify needs apr built with the `training` feature".to_string(), + )), Some("code") => { eval::run_code_eval(&r, data.as_deref(), *max_tokens, *threshold, cli.json) } @@ -1495,14 +1535,14 @@ fn dispatch_profiling_commands(cli: &Cli) -> Option> { *skip_contract, *skip_format_parity, *skip_ptx_parity, - safetensors_path.clone(), + safetensors_path.as_deref().map(Path::to_path_buf), *iterations, *warmup, *max_tokens, *json || cli.json, *verbose || cli.verbose, *min_executed, - previous_report.clone(), + previous_report.as_deref().map(Path::to_path_buf), *regression_threshold, *skip_gpu_state, *skip_metadata, @@ -1694,9 +1734,12 @@ fn dispatch_extended_command(cli: &Cli) -> Result<(), CliError> { effective_no_gpu, run_accelerator_forced(*gpu, *no_gpu, backend.as_deref()), *trace, - trace_steps.as_deref(), + trace_steps + .as_deref() + .map(batuta_common::cli_roles::strings) + .as_deref(), *trace_verbose, - trace_output.clone(), + trace_output.as_deref().map(Path::to_path_buf), trace_level.as_str(), *profile, cli.offline, @@ -1753,7 +1796,10 @@ fn dispatch_extended_command(cli: &Cli) -> Result<(), CliError> { license, pipeline_tag, library_name.as_deref(), - tags.as_ref().map_or(&[], std::vec::Vec::as_slice), + &tags + .as_deref() + .map(batuta_common::cli_roles::strings) + .unwrap_or_default(), message.as_deref(), *dry_run || *plan, cli.verbose, @@ -1805,7 +1851,7 @@ fn dispatch_extended_command(cli: &Cli) -> Result<(), CliError> { token_type_ids.as_deref(), query.as_deref(), passage.as_deref(), - passages, + &batuta_common::cli_roles::strings(passages), *sort, *top_k, vocab.as_deref(), @@ -1839,7 +1885,7 @@ fn dispatch_extended_command(cli: &Cli) -> Result<(), CliError> { json, } => commands::embed::run( model, - text, + &batuta_common::cli_roles::strings(text), text_file.as_deref(), vocab, pool, diff --git a/crates/apr-cli/src/dispatch_run.rs b/crates/apr-cli/src/dispatch_run.rs index 92fc146005..dfa2ccec3a 100644 --- a/crates/apr-cli/src/dispatch_run.rs +++ b/crates/apr-cli/src/dispatch_run.rs @@ -217,14 +217,14 @@ fn dispatch_serve_command(command: &ServeCommands, cli: &Cli) -> Result<(), CliE *no_metrics, *no_gpu, *gpu, - gpu_layers, + &gpu_layers.as_deref().map(str::to_string), *batch, *trace, trace_level, *profile, cli.verbose, backend, - otlp_endpoint, + &otlp_endpoint.as_deref().map(str::to_string), *context_length, *no_fp8_cache, *ollama_compat, @@ -301,7 +301,12 @@ fn dispatch_rosetta(action: &RosettaCommands, global_json: bool) -> Result<(), C formats, work_dir, json, - } => rosetta::run_chain(source, formats, work_dir, *json || global_json), + } => rosetta::run_chain( + source, + &batuta_common::cli_roles::strings(formats), + work_dir, + *json || global_json, + ), RosettaCommands::Verify { source, intermediate, @@ -349,8 +354,8 @@ fn dispatch_rosetta(action: &RosettaCommands, global_json: bool) -> Result<(), C json, } => rosetta::run_fingerprint( model, - model_b.as_ref().map(std::path::PathBuf::as_path), - output.as_ref().map(std::path::PathBuf::as_path), + model_b.as_deref(), + output.as_deref(), filter.as_deref(), *verbose, *json || global_json, @@ -364,8 +369,8 @@ fn dispatch_rosetta(action: &RosettaCommands, global_json: bool) -> Result<(), C json, } => rosetta::run_validate_stats( model, - reference.as_ref().map(std::path::PathBuf::as_path), - fingerprints.as_ref().map(std::path::PathBuf::as_path), + reference.as_deref(), + fingerprints.as_deref(), *threshold, *strict, *json || global_json, diff --git a/crates/apr-cli/src/extended_commands.rs b/crates/apr-cli/src/extended_commands.rs index 3b45813be0..da1a52dc90 100644 --- a/crates/apr-cli/src/extended_commands.rs +++ b/crates/apr-cli/src/extended_commands.rs @@ -37,22 +37,26 @@ pub enum EmbedProjection { #[derive(Subcommand, Debug)] pub enum ExtendedCommands { /// Interactive chat with language model + // #3745 S1 v1.1: prompts arrive over stdin (its only PromptText is --system), not through a PromptText arg, + // so the generator is declared by marker (`apr surface` → `generates: true`). + #[command(group(batuta_common::cli_roles::ServesGeneration::group()))] + #[command(groups(batuta_common::cli_roles::SamplingArg::groups()))] Chat { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Sampling temperature (0 = greedy, higher = more random) - #[arg(long, default_value = "0.7")] + #[arg(long, default_value = "0.7", group = batuta_common::cli_roles::SamplingKind::Temperature.id())] temperature: f32, /// Nucleus sampling threshold - #[arg(long, default_value = "0.9")] + #[arg(long, default_value = "0.9", group = batuta_common::cli_roles::SamplingKind::TopP.id())] top_p: f32, /// Maximum tokens to generate per response #[arg(long, default_value = "512")] max_tokens: usize, /// System prompt to set model behavior #[arg(long)] - system: Option, + system: Option, /// Show inspection info (top-k probs, tokens/sec) #[arg(long)] inspect: bool, @@ -67,13 +71,13 @@ pub enum ExtendedCommands { trace: bool, /// Trace specific steps only (comma-separated) #[arg(long, value_delimiter = ',')] - trace_steps: Option>, + trace_steps: Option>, /// Verbose tracing #[arg(long)] trace_verbose: bool, /// Save trace output to JSON file #[arg(long, value_name = "FILE")] - trace_output: Option, + trace_output: Option, /// Trace detail level (none, basic, layer, payload) #[arg(long, value_name = "LEVEL", default_value = "basic", value_parser = TRACE_LEVEL_VALUES)] trace_level: String, @@ -91,7 +95,7 @@ pub enum ExtendedCommands { Bench { /// Path to model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Number of warmup iterations #[arg(long, default_value = "3")] warmup: usize, @@ -103,13 +107,13 @@ pub enum ExtendedCommands { max_tokens: usize, /// Test prompt #[arg(long)] - prompt: Option, + prompt: Option, /// Use realizar for fast inference (vs aprender baseline) #[arg(long)] fast: bool, /// Benchmark specific brick #[arg(long)] - brick: Option, + brick: Option, /// Comma-separated latency percentile points for JSON output /// (CRUX-E-07). Default: `50,95,99`. Values must be in (0, 100]. #[arg( @@ -124,13 +128,13 @@ pub enum ExtendedCommands { Eval { /// Path to model file or checkpoint directory #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Dataset: wikitext-2, lambada, or custom #[arg(long, default_value = "wikitext-2")] - dataset: String, + dataset: FreeText, /// Custom text (when dataset=custom) #[arg(long)] - text: Option, + text: Option, /// Maximum tokens to evaluate #[arg(long, default_value = "512")] max_tokens: usize, @@ -138,15 +142,17 @@ pub enum ExtendedCommands { #[arg(long, default_value = "20.0", value_parser = commands::threshold_arg::parse_tolerance_f32)] threshold: f32, - /// Task type: omit for perplexity, "classify" for classification eval - #[arg(long)] + /// Task: omit for perplexity on --dataset. classify (needs the + /// `training` feature), code, humaneval, mbpp, contamination, compare, + /// verify, correlation, human, plan + #[arg(long, value_parser = EVAL_TASK_VALUES)] task: Option, /// Test data file (JSONL) for classification evaluation #[arg(long, value_name = "FILE")] - data: Option, + data: Option, /// Model size hint: "0.5B", "tiny" (for classification eval) #[arg(long)] - model_size: Option, + model_size: Option, /// Number of output classes (default: 5) #[arg(long, default_value = "5")] num_classes: usize, @@ -168,16 +174,16 @@ pub enum ExtendedCommands { Profile { /// Path to model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Layer-by-layer granular analysis #[arg(long)] granular: bool, /// Output format (human, json, flamegraph) #[arg(long, default_value = "human")] - format: String, + format: FreeText, /// Focus on specific operation #[arg(long)] - focus: Option, + focus: Option, /// Detect naive implementations #[arg(long)] detect_naive: bool, @@ -187,7 +193,7 @@ pub enum ExtendedCommands { threshold: f64, /// [NOT IMPLEMENTED — accepted and ignored] Compare against HuggingFace baseline #[arg(long)] - compare_hf: Option, + compare_hf: Option, /// [NOT IMPLEMENTED — accepted and ignored] Measure energy consumption (requires RAPL) #[arg(long)] energy: bool, @@ -202,7 +208,7 @@ pub enum ExtendedCommands { fail_on_naive: bool, /// Output file path for flamegraph SVG (GH-174, PMAT-182) #[arg(long, short = 'o')] - output: Option, + output: Option, // PMAT-192: CI Assertion Mode (GH-180) /// Enable CI mode with assertion checks (exits 1 on failure) @@ -235,13 +241,13 @@ pub enum ExtendedCommands { no_gpu: bool, /// Compare against another model format (F-PROFILE-011) #[arg(long, value_name = "FILE")] - compare: Option, + compare: Option, }, /// Falsifiable QA checklist for model releases Qa { /// Path to model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Minimum throughput threshold in tok/s #[arg(long, value_name = "TPS", value_parser = commands::threshold_arg::parse_tolerance)] @@ -277,7 +283,7 @@ pub enum ExtendedCommands { skip_ptx_parity: bool, /// SafeTensors model path for cross-format parity test (F-QUAL-032) #[arg(long, value_name = "PATH")] - safetensors_path: Option, + safetensors_path: Option, /// Number of benchmark iterations #[arg(long, default_value = "10")] iterations: usize, @@ -298,7 +304,7 @@ pub enum ExtendedCommands { min_executed: Option, /// Previous QA report for regression detection #[arg(long, value_name = "FILE")] - previous_report: Option, + previous_report: Option, /// Maximum allowed performance regression ratio (default: 0.10 = 10%) #[arg(long, value_name = "RATIO", value_parser = commands::threshold_arg::parse_fraction)] @@ -320,10 +326,10 @@ pub enum ExtendedCommands { Parity { /// Path to GGUF model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Prompt text (default: "What is 2+2?") #[arg(short, long, default_value = "What is 2+2?")] - prompt: String, + prompt: PromptText, /// Assert parity (exit non-zero on divergence) #[arg(long)] assert: bool, @@ -333,7 +339,7 @@ pub enum ExtendedCommands { per_op: bool, /// Where --per-op writes the two APRT trees (default: a temp dir) #[arg(long, value_name = "DIR")] - out: Option, + out: Option, /// Cosine threshold for --per-op (basis: evidence/parity/thresholds.yaml) #[arg(long, default_value_t = 0.98)] threshold: f32, @@ -343,13 +349,13 @@ pub enum ExtendedCommands { PtxMap { /// Path to GGUF model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Filter to specific kernel (e.g., --kernel Q4KGemv) #[arg(long)] - kernel: Option, + kernel: Option, /// Reverse lookup: kernel name -> which layers/steps use it #[arg(long)] - reverse: Option, + reverse: Option, /// Output as JSON #[arg(long)] json: bool, @@ -369,16 +375,16 @@ pub enum ExtendedCommands { not(feature = "trueno-explain"), command( name = "ptx", - about = "PTX analysis and bug detection [unavailable in this build: cargo install aprender --features ptx]" + about = "PTX analysis and bug detection [unavailable in this build: cargo install aprender --features ptx; `apr ptx-debug` is always built]" ) )] Ptx { /// Path to a PTX source file #[arg(value_name = "FILE")] - file: Option, + file: Option, /// Analyze a named kernel from trueno-gpu #[arg(long, short)] - kernel: Option, + kernel: Option, /// Strict mode (no performance whitelist) #[arg(long)] strict: bool, @@ -397,10 +403,10 @@ pub enum ExtendedCommands { Tune { /// Path to model file (optional if using --model) #[arg(value_name = "FILE")] - file: Option, + file: Option, /// Tuning method: auto, full, lora, qlora #[arg(long, short = 'm', default_value = "auto")] - method: String, + method: FreeText, /// LoRA rank (default: auto-selected) #[arg(long, short = 'r')] rank: Option, @@ -412,56 +418,56 @@ pub enum ExtendedCommands { plan: bool, /// Model size for planning (e.g., "7B", "1.5B") #[arg(long, value_name = "SIZE")] - model: Option, + model: Option, /// Freeze base model weights #[arg(long)] freeze_base: bool, /// Training data file (JSONL format) #[arg(long, value_name = "FILE")] - train_data: Option, + train_data: Option, /// Output as JSON (for CI integration) #[arg(long)] json: bool, /// Task type for HPO: classify (SPEC-TUNE-2026-001) #[arg(long)] - task: Option, + task: Option, /// Number of HPO trials (default: 10) #[arg(long, default_value = "10")] budget: usize, /// HPO search strategy: tpe, grid, random #[arg(long, default_value = "tpe")] - strategy: String, + strategy: FreeText, /// HPO scheduler: asha, median, none #[arg(long, default_value = "asha")] - scheduler: String, + scheduler: FreeText, /// Scout mode: 1 epoch per trial for fast exploration #[arg(long)] scout: bool, /// Training data file for HPO (JSONL format) #[arg(long, value_name = "FILE")] - data: Option, + data: Option, /// Number of output classes for classification #[arg(long, default_value = "5")] num_classes: usize, /// Model size hint for HPO (e.g., "0.5B", "1.5B") #[arg(long)] - model_size: Option, + model_size: Option, /// Warm-start from scout phase results directory #[arg(long, value_name = "DIR")] - from_scout: Option, + from_scout: Option, /// Maximum epochs per trial (full mode, default: 20) #[arg(long, default_value = "20")] max_epochs: usize, /// Maximum wall-clock time (e.g., "8h", "30m") #[arg(long)] - time_limit: Option, + time_limit: Option, }, /// Attach live TUI to a running training session #[cfg(feature = "training")] Monitor { /// Experiment output directory (same as finetune -o) #[arg(value_name = "DIR")] - dir: Option, + dir: Option, /// Refresh interval in milliseconds #[arg(long, default_value = "500")] refresh_ms: u64, @@ -473,7 +479,7 @@ pub enum ExtendedCommands { json: bool, /// Output format: tui (default), json, text #[arg(long, default_value = "tui")] - format: String, + format: FreeText, }, /// List, show, and compare training experiment runs #[cfg(feature = "training")] @@ -491,13 +497,13 @@ pub enum ExtendedCommands { Cbtop { /// Model name (e.g., qwen2.5-coder-1.5b) #[arg(long)] - model: Option, + model: Option, /// Attach to running realizar process #[arg(long)] - attach: Option, + attach: Option, /// Path to GGUF model file for real profiling #[arg(long, value_name = "MODEL")] - model_path: Option, + model_path: Option, /// Run in headless mode (no TUI, for CI/automation) #[arg(long)] headless: bool, @@ -506,7 +512,7 @@ pub enum ExtendedCommands { json: bool, /// Output file path (requires --headless) #[arg(long, value_name = "FILE", requires = "headless")] - output: Option, + output: Option, /// CI mode: exit non-zero if thresholds are not met or the report status is FAIL #[arg(long)] ci: bool, @@ -531,7 +537,7 @@ pub enum ExtendedCommands { speculation_k: usize, /// PAR-099: Path to draft model for speculative decoding #[arg(long, value_name = "DRAFT_MODEL")] - draft_model: Option, + draft_model: Option, /// PAR-102: Number of concurrent requests #[arg(long, default_value = "1")] concurrent: usize, @@ -576,13 +582,13 @@ pub enum ExtendedCommands { CompareHf { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// HuggingFace repo ID (e.g., openai/whisper-tiny) #[arg(long)] - hf: String, + hf: ModelRef, /// Filter tensors by name pattern #[arg(long)] - tensor: Option, + tensor: Option, /// Comparison threshold (default: 1e-5) #[arg(long, default_value = "1e-5", value_parser = commands::threshold_arg::parse_tolerance)] @@ -600,10 +606,10 @@ pub enum ExtendedCommands { Hex { /// Path to model file (APR, GGUF, or SafeTensors) #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Filter tensors by name pattern #[arg(long)] - tensor: Option, + tensor: Option, /// Limit bytes/values to display #[arg(long, default_value = "64")] limit: usize, @@ -636,13 +642,13 @@ pub enum ExtendedCommands { raw: bool, /// Start at byte offset (supports 0x prefix for hex) #[arg(long, default_value = "0")] - offset: String, + offset: FreeText, /// Bytes per row for raw output (default: 16) #[arg(long, default_value = "16")] width: usize, /// Slice range for partial tensor reads (e.g., 0:3 for first 3 elements) #[arg(long)] - slice: Option, + slice: Option, }, /// What this build can and cannot do, and why — read from the capability contract (#3856) Capability { @@ -661,10 +667,10 @@ pub enum ExtendedCommands { Tree { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Filter by component pattern #[arg(long)] - filter: Option, + filter: Option, /// Output format: ascii, dot, mermaid, json /// /// #2394 finding 15: this was a `String` that the dispatcher parsed @@ -686,13 +692,13 @@ pub enum ExtendedCommands { Flow { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Filter by layer pattern #[arg(long)] - layer: Option, + layer: Option, /// Component to visualize: full, encoder, decoder, etc. #[arg(long, default_value = "full")] - component: String, + component: FreeText, /// Verbose output with statistics #[arg(short, long)] verbose: bool, @@ -704,10 +710,10 @@ pub enum ExtendedCommands { Qualify { /// Path to model file (APR, GGUF, or SafeTensors) #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Testing tier: smoke (Phase 1), standard (+contracts), full (+playbook) #[arg(long, default_value = "smoke")] - tier: String, + tier: FreeText, /// Timeout per gate in seconds #[arg(long, default_value = "120")] timeout: u64, @@ -719,7 +725,7 @@ pub enum ExtendedCommands { verbose: bool, /// Skip specific gates (comma-separated) #[arg(long, value_delimiter = ',')] - skip: Option>, + skip: Option>, }, /// Training pipeline (plan/apply) — forjar-style pre-flight validation #[cfg(feature = "training")] @@ -738,13 +744,13 @@ pub enum ExtendedCommands { Pretrain { /// Dataset path (tokenized shard index or raw corpus). #[arg(long, value_name = "PATH")] - dataset: PathBuf, + dataset: InputFile, /// Tokenizer directory (vocab.json + merges.txt). #[arg(long, value_name = "DIR")] - tokenizer: PathBuf, + tokenizer: InputFile, /// Run output directory — checkpoints + metadata go to `{run_dir}/ckpt/`. #[arg(long, value_name = "DIR")] - run_dir: PathBuf, + run_dir: OutputPath, /// Training regime — finetune (MODEL-1) or from-scratch (MODEL-2 cold start). /// Per contract training-loop-pretrain-v1 §hyperparameter_defaults, /// this atomically flips (regime, lr_max, warmup_steps, target_val_loss) @@ -792,7 +798,7 @@ pub enum ExtendedCommands { /// spelling that may fall back silently — all other values /// hard-fail on missing runtime per GATE-GPUTRAIN-002). #[arg(long, default_value = "auto")] - device: String, + device: FreeText, /// Initial weights from a pretrained APR file /// (contract `apr-pretrain-from-init-v1`). Per spec §49's /// MODEL-2 pretrained-init pivot: when present, load weights @@ -802,7 +808,7 @@ pub enum ExtendedCommands { /// corrupted, or arch-mismatched APR files exit non-zero /// before step 1 (no silent random-init fallback). #[arg(long, value_name = "PATH")] - init: Option, + init: Option, /// SPEC §83 P0-J: bypass the Chinchilla compute-optimal hard /// gate (`chinchilla-gate-v1`). Default is fail-fast when /// D/N < 10× (severely under-provisioned per Hoffmann et al. @@ -831,7 +837,7 @@ pub enum ExtendedCommands { /// When omitted, falls back to the historical "first 16 /// batches of --dataset" behaviour for backwards compatibility. #[arg(long, value_name = "DIR")] - val_shard: Option, + val_shard: Option, }, /// Tokenizer training pipeline (plan/apply) — BPE vocabulary learning Tokenize { @@ -852,13 +858,13 @@ pub enum ExtendedCommands { Diagnose { /// Path to checkpoint directory #[arg(value_name = "CHECKPOINT_DIR")] - checkpoint_dir: PathBuf, + checkpoint_dir: ModelPath, /// Test data file (JSONL) for evaluation #[arg(long, value_name = "FILE")] - data: Option, + data: Option, /// Model size hint: "0.5B", "tiny" #[arg(long)] - model_size: Option, + model_size: Option, /// Number of output classes (default: 5) #[arg(long, default_value = "5")] num_classes: usize, @@ -867,7 +873,7 @@ pub enum ExtendedCommands { OllamaChatLint { /// Path to captured /api/chat response (JSON object, or NDJSON if --stream) #[arg(long, value_name = "FILE")] - response_file: PathBuf, + response_file: InputFile, /// Treat input as NDJSON stream (one frame per line) #[arg(long)] stream: bool, @@ -876,12 +882,12 @@ pub enum ExtendedCommands { OllamaToolsLint { /// Path to captured /api/chat response (JSON object, or NDJSON if --stream) #[arg(long, value_name = "FILE")] - response_file: PathBuf, + response_file: InputFile, /// Captured request JSON, required unless --stream — supplies the /// tool-name allowlist (every called tool name must appear in /// request.tools[*].function.name) #[arg(long, value_name = "FILE")] - request_file: Option, + request_file: Option, /// Treat input as NDJSON stream (one frame per line) #[arg(long)] stream: bool, @@ -890,46 +896,46 @@ pub enum ExtendedCommands { DrySamplingLint { /// Path to observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured AWQ quality/compression/flags observation (CRUX-B-08) AwqLint { /// Path to captured AWQ observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured FP8 (E4M3) round-trip + SM-capability observation (CRUX-B-11) Fp8Lint { /// Path to captured observation JSON (frobenius, capability blocks) #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured NF4 codebook/roundtrip/storage/parity observation (CRUX-B-10) Nf4Lint { /// Path to captured NF4 observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured GPTQ compression/cosine/flags observation (CRUX-B-09) GptqLint { /// Path to captured GPTQ observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured CUDA OOM postmortem report (CRUX-F-13) OomLint { /// Path to captured OOM postmortem JSON (e.g. /tmp/apr-oom-.json) #[arg(long, value_name = "FILE")] - report_file: PathBuf, + report_file: InputFile, /// Optional captured stderr log to verify the OOM_REPORT breadcrumb #[arg(long, value_name = "FILE")] - stderr_file: Option, + stderr_file: Option, }, /// Lint a captured NCCL failure-diagnostics JSON from stderr (CRUX-F-15) NcclDiagLint { /// Path to captured stderr JSON diagnostic #[arg(long, value_name = "FILE")] - diag_file: PathBuf, + diag_file: InputFile, /// Optional observed exit code (gate: >= 128 = NCCL class) #[arg(long, value_name = "I32")] exit_code: Option, @@ -941,7 +947,7 @@ pub enum ExtendedCommands { ReactTraceLint { /// Path to captured trace JSON #[arg(long, value_name = "FILE")] - trace_file: PathBuf, + trace_file: InputFile, /// Optional max_iterations budget the trace was produced under #[arg(long, value_name = "N")] max_iterations: Option, @@ -953,10 +959,10 @@ pub enum ExtendedCommands { HangTraceLint { /// Path to the captured trace directory #[arg(long, value_name = "DIR")] - trace_dir: PathBuf, + trace_dir: DirPath, /// Inspection mode: `timeout` (expects per-rank dumps) or `success` (expects empty dir) #[arg(long, value_name = "MODE", default_value = "timeout")] - mode: String, + mode: FreeText, /// Expected world_size when mode=timeout (number of rank{N}.py.txt files) #[arg(long, value_name = "N", default_value_t = 2)] world_size: usize, @@ -971,10 +977,10 @@ pub enum ExtendedCommands { DdpMetricsLint { /// Path to N=1 metrics JSON #[arg(long, value_name = "FILE")] - metrics_1gpu_file: PathBuf, + metrics_1gpu_file: InputFile, /// Path to N=world_size metrics JSON #[arg(long, value_name = "FILE")] - metrics_ngpu_file: PathBuf, + metrics_ngpu_file: InputFile, /// World size used for --metrics-ngpu-file run (>= 2) #[arg(long, value_name = "N")] world_size: i64, @@ -1002,7 +1008,7 @@ pub enum ExtendedCommands { AudioInspectLint { /// Path to the JSON body written by `apr dataset audio-inspect --format json` #[arg(long, value_name = "FILE")] - json_file: PathBuf, + json_file: InputFile, /// Optional expected sample_rate (typically the `--resample-to` arg) #[arg(long, value_name = "U32")] expected_sample_rate: Option, @@ -1016,16 +1022,16 @@ pub enum ExtendedCommands { /// Parity JSON body (`max_abs_diff`, `cosine_sim`), as written by /// `apr kernel parity --json` #[arg(long, value_name = "FILE")] - parity_file: Option, + parity_file: Option, /// Provenance JSON body (`attn_impl`, `kernel_source`, `fallback`). /// `apr kernel parity --json` writes both gates' fields into one body, /// so the same file may be passed here and to --parity-file #[arg(long, value_name = "FILE")] - provenance_file: Option, + provenance_file: Option, /// head_dim refusal JSON, as written by /// `apr kernel parity --impl flash2 --head-dim 96 --json` (which exits non-zero) #[arg(long, value_name = "FILE")] - head_dim_error_file: Option, + head_dim_error_file: Option, /// Max absolute diff tolerance (default 5e-3, FlashAttention-2 bound) #[arg(long, value_name = "F", default_value_t = 5e-3, value_parser = commands::threshold_arg::parse_tolerance)] @@ -1039,10 +1045,10 @@ pub enum ExtendedCommands { AttnVizLint { /// Path to attention dump in JSON form (4-D [layers][heads][rows][cols] floats) #[arg(long, value_name = "FILE")] - attn_file: Option, + attn_file: Option, /// Path to HTML heatmap output #[arg(long, value_name = "FILE")] - html_file: Option, + html_file: Option, /// Minimum , + error_file: Option, /// Externally captured check-finite layer-coverage JSON #[arg(long, value_name = "FILE")] - list_file: Option, + list_file: Option, /// Minimum layer-coverage count when `--list-file` is supplied (default 100) #[arg(long, value_name = "N", default_value_t = 100)] min_layers: usize, @@ -1072,19 +1078,19 @@ pub enum ExtendedCommands { EmbedVizLint { /// Path to the `token_id,token_str,x,y` CSV written by `apr debug embed-viz` #[arg(long, value_name = "FILE")] - csv_file: PathBuf, + csv_file: InputFile, /// Expected row count == vocab_size (optional) #[arg(long, value_name = "N")] expected_vocab_size: Option, /// Second CSV from a rerun at the same --seed, for the determinism gate (optional) #[arg(long, value_name = "FILE")] - csv_file_b: Option, + csv_file_b: Option, }, /// Lint an externally captured token-selection JSONL trace (CRUX-F-19 — no apr producer yet) ExplainTokenLint { /// Path to captured JSONL body (one sampled-token record per line) #[arg(long, value_name = "FILE")] - jsonl_file: PathBuf, + jsonl_file: InputFile, /// Tolerance for `Σ post_prob ≈ 1.0` (default 1e-5) #[arg(long, value_name = "F64", default_value_t = 1e-5, value_parser = commands::threshold_arg::parse_tolerance)] @@ -1097,13 +1103,13 @@ pub enum ExtendedCommands { GpuMemtraceLint { /// Path to an externally captured GPU-memory Chrome Trace JSON (no apr producer yet) #[arg(long, value_name = "FILE")] - trace_file: PathBuf, + trace_file: InputFile, }, /// Lint a captured KV-cache utilization timeline (CRUX-F-06) KvTimelineLint { /// Path to an externally captured KV-cache timeline JSON body (no apr producer yet) #[arg(long, value_name = "FILE")] - timeline_file: PathBuf, + timeline_file: InputFile, /// Preemption threshold (default 0.95, vLLM canonical) #[arg(long, value_name = "FRACTION", default_value_t = 0.95, value_parser = commands::threshold_arg::parse_fraction)] @@ -1116,7 +1122,7 @@ pub enum ExtendedCommands { OtlpLint { /// Path to captured OTLP/JSON export body #[arg(long, value_name = "FILE")] - otlp_file: PathBuf, + otlp_file: InputFile, /// Require at least one `apr.inference` span to be present #[arg(long)] require_apr_span: bool, @@ -1125,16 +1131,16 @@ pub enum ExtendedCommands { require_genai_attrs: bool, /// Verify W3C trace-context propagation: expect this 32-hex traceId #[arg(long, value_name = "HEX32")] - expect_trace_id: Option, + expect_trace_id: Option, }, /// Lint a captured Prometheus /metrics response (CRUX-K-07) PrometheusLint { /// Path to captured /metrics response body (text/plain; version=0.0.4) #[arg(long, value_name = "FILE")] - metrics_file: PathBuf, + metrics_file: InputFile, /// Optional captured Content-Type header to verify against version=0.0.4 #[arg(long, value_name = "HEADER")] - content_type: Option, + content_type: Option, /// Require the K-07 metric set (apr_num_requests_running, ...) to be present #[arg(long)] require_k07_metrics: bool, @@ -1143,26 +1149,26 @@ pub enum ExtendedCommands { ToolUseLint { /// Path to captured OpenAI tool-use response JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a GBNF grammar-constrained observation (CRUX-C-10) GbnfLint { /// Path to captured GBNF observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a typical-p sampling observation (CRUX-C-22) TypicalPLint { /// Path to captured typical-p observation JSON, with any of the /// sections range/identity/mass/sort/renorm #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Gradient-norm telemetry analysis (CRUX-F-09) GradNorm { /// Path to JSON file of per-step grad-norm records #[arg(long, value_name = "FILE")] - history_file: PathBuf, + history_file: InputFile, /// Maximum allowed clipped grad-norm (for cap-violation check) #[arg(long, value_name = "M", value_parser = commands::threshold_arg::parse_tolerance)] @@ -1179,66 +1185,66 @@ pub enum ExtendedCommands { RegistryQuotaLint { /// Path to captured quota/atomic/ceiling observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured imatrix calibration observation (CRUX-B-07) ImatrixLint { /// Path to captured imatrix observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured /v1/embeddings observation (CRUX-C-13) EmbeddingsLint { /// Path to captured /v1/embeddings observation JSON, with any of the /// sections shape/determinism/usage/flag #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured Hub+local unified-search merge observation (CRUX-A-23) UnifiedSearchLint { /// Path to captured unified-search observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured `apr rm` / externally captured gc blob-GC observation (CRUX-A-25) RmGcLint { /// Path to captured rm/gc observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Lint a captured APR_MODELS shared-cache observation (CRUX-A-21) SharedCacheLint { /// Path to captured dedup/permission observation JSON #[arg(long, value_name = "FILE")] - observation_file: PathBuf, + observation_file: InputFile, }, /// Perplexity classifier (CRUX-E-02) Ppl { /// JSON file containing an array of per-token natural-log /// probabilities (e.g. `[-1.2, -0.5, -2.1, ...]`). Required. #[arg(long, value_name = "FILE")] - log_probs_file: PathBuf, + log_probs_file: InputFile, }, /// Validate dequant→requant metadata preservation (CRUX-B-19) QuantPreservationLint { /// Reference GGUF (pre-roundtrip) #[arg(long, value_name = "REF.gguf")] - reference: PathBuf, + reference: ModelPath, /// Requantized GGUF (post-roundtrip) #[arg(long, value_name = "REQ.gguf")] - requant: PathBuf, + requant: ModelPath, }, /// Split a safetensors file into shards + weight-map index (CRUX-B-05) Shard { /// Single-file safetensors model to split #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Maximum size of each shard (e.g. 5GB, 500MB, 1.5GiB) #[arg(long, value_name = "SIZE", default_value = "5GB")] - max_shard_size: String, + max_shard_size: FreeText, /// Output directory for shards + model.safetensors.index.json #[arg(short, long, value_name = "DIR")] - output: PathBuf, + output: OutputPath, /// #2392: Overwrite an existing shard set in the output directory #[arg(short, long)] force: bool, @@ -1247,10 +1253,10 @@ pub enum ExtendedCommands { Unshard { /// Sharded directory containing model.safetensors.index.json #[arg(value_name = "DIR")] - input: PathBuf, + input: ModelPath, /// Output single-file safetensors path #[arg(short, long, value_name = "FILE")] - output: PathBuf, + output: OutputPath, /// #2392: Overwrite an existing output file (refused without it) #[arg(short, long)] force: bool, @@ -1271,26 +1277,26 @@ pub enum ExtendedCommands { Rerank { /// Path to the APR file containing the cross-encoder weights. #[arg(value_name = "MODEL")] - model: PathBuf, + model: ModelPath, /// Pre-tokenised input ids (comma-separated `u32`s). Mutually /// exclusive with `--query`+`--passage`+`--vocab` (Phase 3b). /// Example: `--input-ids 101,2024,102,3456,102` for `[CLS] q [SEP] p [SEP]`. #[arg(long, value_name = "IDS")] - input_ids: Option, + input_ids: Option, /// Pre-tokenised token-type ids (comma-separated `u32`s). /// Same length as `--input-ids`. 0 for query side, 1 for passage. #[arg(long, value_name = "IDS")] - token_type_ids: Option, + token_type_ids: Option, /// Phase 3b — query text. Pair with `--passage` + `--vocab` to enable /// in-process WordPiece tokenisation. The tokeniser builds /// `[CLS] query [SEP] passage [SEP]` with `token_type_ids = 0` for /// the query side and `1` for the passage side. #[arg(long, value_name = "TEXT")] - query: Option, + query: Option, /// Phase 3b — passage text. Required when `--query` is supplied /// in single-pair mode (use `--passages` for batch ranking). #[arg(long, value_name = "TEXT")] - passage: Option, + passage: Option, /// Phase 5 — batch ranking mode (#326). Passage candidates to /// score against `--query`. May be supplied multiple times: /// `apr rerank model.apr --query "..." --passages "p1" --passages "p2"`. @@ -1299,7 +1305,7 @@ pub enum ExtendedCommands { /// of `{passage, logit, score}` objects sorted by descending /// score when `--sort` is set. #[arg(long, value_name = "TEXT")] - passages: Vec, + passages: Vec, /// Phase 5 — sort batch output by descending score (highest /// relevance first). Only meaningful with `--passages` and /// `--json`. Default: preserve input order. @@ -1314,7 +1320,7 @@ pub enum ExtendedCommands { /// Must contain entries for `[CLS]`, `[SEP]`, and `[UNK]`. /// Phase 4 accepts HuggingFace `tokenizer.json` (extension-detected). #[arg(long, value_name = "FILE")] - vocab: Option, + vocab: Option, /// Override hidden_dim (default: 384 / MiniLM-L-6). #[arg(long, default_value_t = 384)] hidden_dim: usize, @@ -1376,10 +1382,10 @@ pub enum ExtendedCommands { Embed { /// Path to the APR file containing the encoder weights (BertModel). #[arg(value_name = "MODEL")] - model: PathBuf, + model: ModelPath, /// Text to encode. Repeatable: `apr embed model.apr --text "a" --text "b" --vocab tok.json`. #[arg(long, value_name = "TEXT")] - text: Vec, + text: Vec, /// Phase 7 (GH-326) — read texts from a file, one per line. /// Concatenated with `--text` inputs in order: `--text` first, /// then `--text-file` rows. Blank lines and lines starting @@ -1387,14 +1393,14 @@ pub enum ExtendedCommands { /// retrieval where the second-stage rerank candidate set /// (50-100 documents) is the embed input. #[arg(long, value_name = "FILE")] - text_file: Option, + text_file: Option, /// Path to a WordPiece `vocab.txt` or HF `tokenizer.json`. #[arg(long, value_name = "FILE")] - vocab: PathBuf, + vocab: InputFile, /// Pooling strategy (`cls` or `mean`). Default: `mean` /// (matches sentence-transformers convention). #[arg(long, default_value = "mean")] - pool: String, + pool: FreeText, /// L2-normalise the output embedding. Default: true (matches /// sentence-transformers convention). Pass `--normalize false` /// to keep raw magnitudes. @@ -1459,14 +1465,14 @@ pub enum DatasetCommands { AudioInspect { /// Path to the .wav file to decode #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Output format: `json` for the lint-readable body, `text` for humans #[arg(long, value_name = "FORMAT", default_value = "text", value_parser = ["json", "text"])] format: String, /// Write the observation here instead of stdout #[arg(short, long, value_name = "FILE")] - output: Option, + output: Option, /// Overwrite an existing --output file (refused without it) #[arg(short, long)] force: bool, @@ -1514,7 +1520,7 @@ pub enum KernelCommands { json: bool, /// Write the observation here instead of stdout #[arg(short, long, value_name = "FILE")] - output: Option, + output: Option, /// Overwrite an existing --output file (refused without it) #[arg(short, long)] force: bool, @@ -1529,13 +1535,13 @@ pub enum RunsCommands { Ls { /// Directory to scan for experiments (default: current dir) #[arg(long, value_name = "DIR")] - dir: Option, + dir: Option, /// Read from global experiment registry (~/.entrenar/experiments.db) #[arg(long)] global: bool, /// Filter by status: all, pending, running, completed, failed, cancelled #[arg(long, default_value = "all")] - status: String, + status: FreeText, /// Output as JSON #[arg(long)] json: bool, @@ -1547,10 +1553,10 @@ pub enum RunsCommands { Show { /// Run ID #[arg(value_name = "RUN_ID")] - run_id: String, + run_id: FreeText, /// Directory containing experiment DB #[arg(long, value_name = "DIR")] - dir: Option, + dir: Option, /// Read from global registry #[arg(long)] global: bool, @@ -1562,13 +1568,13 @@ pub enum RunsCommands { Diff { /// First run ID #[arg(value_name = "RUN_A")] - run_a: String, + run_a: FreeText, /// Second run ID #[arg(value_name = "RUN_B")] - run_b: String, + run_b: FreeText, /// Directory containing experiment DB #[arg(long, value_name = "DIR")] - dir: Option, + dir: Option, /// Read from global registry #[arg(long)] global: bool, @@ -1586,7 +1592,7 @@ pub enum ExperimentCommands { View { /// Path to experiment database file #[arg(long, value_name = "FILE")] - db: Option, + db: Option, /// Read from global experiment registry (~/.entrenar/experiments.db) #[arg(long)] global: bool, @@ -1608,10 +1614,10 @@ pub enum ModelfileSubcommand { Parse { /// Path to the Modelfile #[arg(value_name = "FILE")] - file: PathBuf, + file: ConfigPath, /// Output format: `json` or `human` #[arg(long, default_value = "json")] - format: String, + format: FreeText, }, } @@ -1641,19 +1647,19 @@ pub enum TestSubcommand { Tensor { /// Path to .apr model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Output directory for test artifacts #[arg(short, long, default_value = "./probar-export")] - output: PathBuf, + output: OutputPath, /// Export format: json, png, or both #[arg(long, default_value = "both")] - format: String, + format: FreeText, /// Golden reference directory for comparison #[arg(long)] - golden: Option, + golden: Option, /// Filter layers by name pattern #[arg(long)] - layer: Option, + layer: Option, /// Exit non-zero on golden divergence (CI mode, PMAT-481) #[arg(long)] assert: bool, @@ -1689,15 +1695,15 @@ pub enum LlmSubcommand { Bench { /// Endpoint to measure. #[arg(short, long, default_value = "http://127.0.0.1:8080")] - url: String, + url: FreeText, /// Model name sent in the request body. Most OpenAI-compatible /// servers ignore it; `apr serve` and vLLM do not. #[arg(short, long, default_value = "default")] - model: String, + model: FreeText, /// Command that starts the runtime. Omit to measure something already /// running. #[arg(long)] - start: Option, + start: Option, /// Seconds to wait for the endpoint to become healthy. #[arg(long, default_value = "120")] health_timeout: u64, @@ -1722,16 +1728,16 @@ pub enum LlmSubcommand { cooldown: u64, /// Label recorded in the report, e.g. `apr-cuda` or `llamacpp-39173bcac`. #[arg(long, default_value = "apr")] - runtime_name: String, + runtime_name: FreeText, /// Prior report to compare against. #[arg(long)] - baseline: Option, + baseline: Option, /// Fractional regression that fails the run, e.g. 0.10 for 10%. #[arg(long)] fail_on_regression: Option, /// Write the JSON report here. #[arg(short, long)] - output: Option, + output: Option, /// Use streaming responses (measures TTFT and TPOT). #[arg(long)] stream: bool, @@ -1743,7 +1749,7 @@ pub enum LlmSubcommand { /// knob with a recorded value rather than a constant buried in a /// helper. #[arg(long, default_value = "medium")] - profile: String, + profile: FreeText, /// Prompt corpus, JSONL: one JSON object per line, `{"prompt": "..."}`, /// with optional `max_tokens`, `temperature`, `seed`, `ignore_eos`. /// Overrides `--profile`. @@ -1758,7 +1764,7 @@ pub enum LlmSubcommand { /// so an operator who followed the help produced a file the loader /// refused. #[arg(long)] - prompts: Option, + prompts: Option, // ------------------------------------------------------------------ // PERF-025 — the APR-PERF-GATE-001 v2.2 §4.4 conformant mode. @@ -1804,10 +1810,10 @@ pub enum LlmSubcommand { band: bool, /// Directory receiving `receipt.rN.json` and the gzipped JSONL samples. #[arg(long)] - receipt: Option, + receipt: Option, /// Concurrency levels for --band. Arm A needs `c=1` to be present. #[arg(long, default_value = "1,4,8,16")] - bands: String, + bands: FreeText, /// Full band replicates per cell (PP-LLAMA-001 v3.0 §4.3: n >= 5). /// Each replicate writes its own independently judgeable receipt. /// @@ -1825,13 +1831,13 @@ pub enum LlmSubcommand { workload: String, /// Join key: which machine measured. Required by the receipt schema. #[arg(long, required_if_eq("band", "true"), default_value = "")] - host: String, + host: FreeText, /// Join key: which accelerator served the request. #[arg(long, required_if_eq("band", "true"), default_value = "")] - accelerator: String, + accelerator: FreeText, /// Join key: which quantization the served model uses. #[arg(long, required_if_eq("band", "true"), default_value = "")] - quantization: String, + quantization: FreeText, /// The dispatch path the SERVER took — not the hardware present. #[arg(long, default_value = "unknown", value_parser = ["cpu", "cuda", "metal", "wgpu", "unknown"])] @@ -1843,14 +1849,14 @@ pub enum LlmSubcommand { /// pointing that check at the measuring binary instead of the measured /// one would make it read green while checking nothing. #[arg(long = "server-feature")] - server_features: Vec, + server_features: Vec, /// How tokens were counted (§4.4.6). Absence of a valid value is fatal. #[arg(long, default_value = "server_usage", value_parser = ["server_usage", "client_tokenizer"])] tokenization: String, /// Tokenizer digest, required when --tokenization client_tokenizer. #[arg(long)] - tokenizer_sha256: Option, + tokenizer_sha256: Option, /// Whether the token counts include special tokens (§4.4.6). #[arg(long)] counts_special_tokens: bool, @@ -1866,7 +1872,7 @@ pub enum LlmSubcommand { /// `$(git rev-parse HEAD)`: a full 40-character lowercase object name, /// because an abbreviated sha cannot be tested for ancestry (PP-18). #[arg(long)] - commit: Option, + commit: Option, /// Who owes the comparator measurement this producer refuses to invent. /// /// §4.7.1: without `--comparator-url` every cell is NOT_APPLICABLE or @@ -1874,7 +1880,7 @@ pub enum LlmSubcommand { /// SAME run (PP-3), joined on the PP-22 key and driven by this same /// client binary (PP-25); one lane cannot produce one. #[arg(long, default_value = "perf-gate")] - comparator_owner: String, + comparator_owner: FreeText, // ------------------------------------------------------------------ // PP-LLAMA-001 v3.0 §4.3 / §5.3 — the COMPARATOR LANE. @@ -1897,25 +1903,25 @@ pub enum LlmSubcommand { /// Comparator endpoint. Measured by THIS binary, interleaved with the /// subject lane band by band, and joined on a shared `run_id`. #[arg(long)] - comparator_url: Option, + comparator_url: Option, /// Model name the comparator server expects in the request body. /// Defaults to --model; `llama-server` serves whatever it was /// launched with and ignores the field. #[arg(long)] - comparator_model: Option, + comparator_model: Option, /// PP-20: the comparator build's upstream commit. #[arg(long)] - comparator_commit: Option, + comparator_commit: Option, /// PP-20: the `cmake` line the comparator was configured with. #[arg(long)] - comparator_cmake: Option, + comparator_cmake: Option, /// PP-20: the comparator binary's sha256, 64 lowercase hex. #[arg(long)] - comparator_sha256: Option, + comparator_sha256: Option, /// PP-20: the instant after which every ratio against this pin is /// COMPARATOR_STALE, as `YYYY-MM-DDTHH:MM:SS.mmmZ`. #[arg(long)] - comparator_pin_expiry: Option, + comparator_pin_expiry: Option, // ------------------------------------------------------------------ // PP-22 / §5.3 — the comparator lane's CONFIGURATION, declared. @@ -1947,10 +1953,10 @@ pub enum LlmSubcommand { /// server resolved it to, and a guessed join-key field silently joins /// two different configurations. #[arg(long)] - comparator_fa: Option, + comparator_fa: Option, /// §5.3: `-ctk/-ctv` as launched, e.g. `f16`. #[arg(long)] - comparator_kv_type: Option, + comparator_kv_type: Option, /// PP-26: `witness.json` from /// `scripts/perf041_batched_parity_probe.py`. @@ -1961,12 +1967,12 @@ pub enum LlmSubcommand { /// emitting one token id forever at full speed, and every throughput /// number it produced was arithmetically correct and meaningless. #[arg(long)] - witness_json: Option, + witness_json: Option, /// PP-18: the `apr serve` binary that served the bands, when it is not /// this same `apr`. Hashed into `provenance.subject`. #[arg(long)] - subject_binary: Option, + subject_binary: Option, /// PP-21: sign each receipt with this key id after writing it. /// @@ -1975,11 +1981,11 @@ pub enum LlmSubcommand { /// non-zero exit is fatal: an unsigned receipt reported as signed is /// exactly the document `ArmC-sig` exists to catch. #[arg(long)] - key_id: Option, + key_id: Option, /// PP-21: keyring for --key-id. Defaults to /// `$APR_PERF_RECEIPT_KEYRING` inside the signing script. #[arg(long)] - keyring: Option, + keyring: Option, }, } diff --git a/crates/apr-cli/src/generated_contracts.rs b/crates/apr-cli/src/generated_contracts.rs index cf5fff4f76..04cb165822 100644 --- a/crates/apr-cli/src/generated_contracts.rs +++ b/crates/apr-cli/src/generated_contracts.rs @@ -8937,18 +8937,6 @@ macro_rules! contract_inv_quality_gate { }}; } -// Auto-generated from contracts/cooperative-matrix-gemm-v1.yaml — DO NOT EDIT -// Contract: cooperative-matrix-gemm-v1 - -/// Invariants for equation `cooperative_gemm`. -/// Check after computation: `contract_inv_cooperative_gemm!(result_expr)` -macro_rules! contract_inv_cooperative_gemm { - () => {{}}; - ($result:expr) => {{ - let _contract_result = &$result; - }}; -} - // Auto-generated from contracts/copia-delta-v1.yaml — DO NOT EDIT // Contract: copia-delta-v1 diff --git a/crates/apr-cli/src/lib.rs b/crates/apr-cli/src/lib.rs index 72230fa4c9..fecd2149b5 100644 --- a/crates/apr-cli/src/lib.rs +++ b/crates/apr-cli/src/lib.rs @@ -31,6 +31,13 @@ )] use clap::{Parser, Subcommand, ValueEnum}; +// #3745 S1: every free-form argument of the clap tree below is declared through +// one of these role types (`batuta_common::cli_roles`), which is how `apr +// surface` reads its role. A raw `PathBuf`/`String` there is RED in the marker guard. +pub use batuta_common::cli_roles::{ + ConfigPath, DirPath, EncodeText, FreeText, InputFile, ModelPath, ModelRef, OutputPath, + PromptText, +}; use std::path::{Path, PathBuf}; // Contract assertions from YAML (pv codegen) @@ -48,10 +55,13 @@ pub mod verbosity; /// PERF-021: the accelerator refusal, shared by serve, run and chat. mod accel; + mod commands; pub mod error; mod output; pub mod pipe; +// #3745 S1: the surface the binary actually has, emitted by the binary. +pub mod surface; pub use error::CliError; @@ -69,9 +79,11 @@ pub mod model_pull { // `tests/falsify_auth_*.rs` can construct gates and middleware without // reaching into the private `commands` tree. pub mod serve_auth { + #[cfg(feature = "inference")] + pub use crate::commands::serve::auth::apply; #[cfg(feature = "inference")] pub use crate::commands::serve::auth::layer; - pub use crate::commands::serve::auth::{apply, AuthGate}; + pub use crate::commands::serve::auth::AuthGate; } // PMAT-923: e2e seam so `tests/ollama_api_serve_compat.rs` can build the REAL @@ -180,6 +192,14 @@ include!("dispatch.rs"); include!("dispatch_analysis.rs"); include!("lib_07.rs"); +/// G0.5 (#2582): the heap profiler's allocator lives HERE, next to `cli_main`, +/// so both `apr` bin targets (the root facade's `src/bin/apr.rs` and +/// `crates/apr-cli/src/main.rs`) get it. It used to exist only in apr-cli's +/// `main.rs`, one of the ways the two hand-copied entry points had diverged. +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + /// Full CLI entry point for `cargo install aprender`. /// /// This function encapsulates the complete `apr` binary logic so that @@ -189,6 +209,8 @@ pub fn cli_main() -> std::process::ExitCode { // GH-667: Reset SIGPIPE to default so piping to head/less doesn't panic. #[cfg(unix)] #[allow(unsafe_code)] + // SAFETY: signal(SIGPIPE, SIG_DFL) is async-signal-safe per POSIX. + // Called once at program start before any threads are spawned. unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL); } @@ -196,6 +218,8 @@ pub fn cli_main() -> std::process::ExitCode { // GH-646: Clear FPCR.FZ16 on aarch64 so f16 subnormals work. #[cfg(target_arch = "aarch64")] #[allow(unsafe_code)] + // SAFETY: Reading/writing FPCR only affects floating-point behavior for the + // current thread. Called once at program start before any FP operations. unsafe { let fpcr: u64; core::arch::asm!("mrs {}, fpcr", out(reg) fpcr); @@ -205,6 +229,9 @@ pub fn cli_main() -> std::process::ExitCode { } } + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + // GH-662: Respect NO_COLOR env var and non-TTY output. let no_color = std::env::var("NO_COLOR").is_ok(); let is_tty = std::io::IsTerminal::is_terminal(&std::io::stdout()); @@ -308,3 +335,110 @@ pub fn emit_version_json() { serde_json::to_string_pretty(&body).expect("build version json") ); } + +/// G0.5 (#2582): both `apr` bin targets must BE `cli_main`, not copies of it. +#[cfg(test)] +mod entry_point_is_shared { + /// What a hand-copied prologue re-introduces. Any of these in a bin target + /// means the entry point is being duplicated again instead of shared. + const PROLOGUE_TOKENS: [&str; 7] = [ + "Cli::parse", + "execute_command", + "emit_version_json", + "global_allocator", + "set_override", + "SIGPIPE", + "fpcr", + ]; + + /// The source with `//` comments removed, so a doc comment that NAMES a + /// token (as main.rs's header does) is not read as code. + fn code_of(rel: &str) -> String { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(rel); + let src = + std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display())); + src.lines() + .map(|l| l.split("//").next().unwrap_or("")) + .collect::>() + .join("\n") + } + + fn violations(code: &str) -> Vec<&'static str> { + let mut v: Vec<&'static str> = PROLOGUE_TOKENS + .iter() + .copied() + .filter(|t| code.contains(t)) + .collect(); + if !code.contains("apr_cli::cli_main()") { + v.push("does not call apr_cli::cli_main()"); + } + v + } + + #[test] + fn both_apr_bins_delegate_to_cli_main() { + for rel in ["src/main.rs", "../../src/bin/apr.rs"] { + let v = violations(&code_of(rel)); + assert!(v.is_empty(), "{rel}: {v:?}"); + } + } + + /// Case table: the detector must fire on the pre-#2582 main.rs shape and + /// stay quiet on a comment that only names the tokens. + #[test] + fn detector_case_table() { + let copied = "use apr_cli::{execute_command, Cli};\nfn main() { let cli = Cli::parse(); }"; + assert!(violations(copied).contains(&"Cli::parse")); + assert!(violations(copied).contains(&"does not call apr_cli::cli_main()")); + let alloc = "static ALLOC: dhat::Alloc = dhat::Alloc; #[global_allocator]\nfn main() { apr_cli::cli_main() }"; + assert_eq!(violations(alloc), vec!["global_allocator"]); + let clean = "fn main() -> std::process::ExitCode {\n apr_cli::cli_main()\n}"; + assert!(violations(clean).is_empty()); + } +} + +/// `cli_main` is the whole `apr` binary; judge its return value in a child +/// process, because `Cli::parse` on a bad command line exits the process. +#[cfg(test)] +mod cli_main_exit { + /// Child half: only acts when re-executed by the parent test below. The + /// child's argv is the libtest one, which `Cli` rejects as a usage error, so + /// `cli_main` must exit 2 itself and never return here. + #[test] + fn cli_main_child_entry() { + if std::env::var_os("APR_CLI_MAIN_CHILD").is_none() { + return; + } + // The real `main` runs on the 8 MiB main-thread stack; a libtest worker thread's + // 2 MiB is too small for the full `Cli` parse, so give the child the same room. + let rc = std::thread::Builder::new() + .stack_size(64 * 1024 * 1024) + .spawn(super::cli_main) + .expect("spawn cli_main thread") + .join() + .expect("cli_main thread panicked"); + println!("CLI_MAIN_RETURNED {rc:?}"); + std::process::exit(0); + } + + #[test] + fn cli_main_on_a_usage_error_exits_two_and_does_not_return() { + let out = std::process::Command::new(std::env::current_exe().expect("current_exe")) + .args([ + "--exact", + "cli_main_exit::cli_main_child_entry", + "--nocapture", + "--test-threads=1", + ]) + .env("APR_CLI_MAIN_CHILD", "1") + .env("NO_COLOR", "1") + .output() + .expect("spawn child test binary"); + let stdout = String::from_utf8_lossy(&out.stdout); + assert!( + !stdout.contains("CLI_MAIN_RETURNED"), + "cli_main returned on a usage error: {stdout}" + ); + assert_eq!(out.status.code(), Some(2), "{stdout}"); + } +} diff --git a/crates/apr-cli/src/lib_07.rs b/crates/apr-cli/src/lib_07.rs index 43ba59258c..17c4a96484 100644 --- a/crates/apr-cli/src/lib_07.rs +++ b/crates/apr-cli/src/lib_07.rs @@ -22,4 +22,5 @@ include!("lib_parse_serve.rs"); include!("lib_dispatch_coverage.rs"); include!("lib_dogfood_2392.rs"); include!("lib_falsify_2606_mcp_serve_argv.rs"); +include!("lib_parse_ptx_debug_4062.rs"); } diff --git a/crates/apr-cli/src/lib_dispatch_coverage.rs b/crates/apr-cli/src/lib_dispatch_coverage.rs index 0c73ab61d4..4ed027f7f5 100644 --- a/crates/apr-cli/src/lib_dispatch_coverage.rs +++ b/crates/apr-cli/src/lib_dispatch_coverage.rs @@ -10,7 +10,7 @@ fn test_dispatch_model_commands_returns_none_for_inspect() { // Inspect is an inspection command, not a model management command let cli = make_cli(Commands::Inspect { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), vocab: false, filters: false, weights: false, @@ -32,7 +32,7 @@ #[test] fn test_dispatch_model_commands_pull_nonexistent() { let cli = make_cli(Commands::Pull { - model_ref: "nonexistent-model-that-does-not-exist-xyz123".to_string(), + model_ref: "nonexistent-model-that-does-not-exist-xyz123".to_string().into(), repo: None, force: false, dry_run: false, @@ -50,7 +50,7 @@ #[test] fn test_dispatch_model_commands_rm_nonexistent() { let cli = make_cli(Commands::Rm { - model_ref: "nonexistent-model-xyz789".to_string(), + model_ref: "nonexistent-model-xyz789".to_string().into(), }); let result = dispatch_model_commands(&cli); assert!(result.is_some(), "Rm should be handled by dispatch_model_commands"); @@ -66,7 +66,7 @@ #[test] fn test_dispatch_model_commands_tui_with_file() { let cli = make_cli(Commands::Tui { - file: Some(PathBuf::from("/tmp/nonexistent_tui_model.apr")), + file: Some(PathBuf::from("/tmp/nonexistent_tui_model.apr").into()), }); let result = dispatch_model_commands(&cli); assert!(result.is_some(), "Tui with file should be handled by dispatch_model_commands"); @@ -76,11 +76,11 @@ fn test_dispatch_model_commands_merge_nonexistent() { let cli = make_cli(Commands::Merge { files: vec![ - PathBuf::from("/tmp/nonexistent_merge_a.apr"), - PathBuf::from("/tmp/nonexistent_merge_b.apr"), + PathBuf::from("/tmp/nonexistent_merge_a.apr").into(), + PathBuf::from("/tmp/nonexistent_merge_b.apr").into(), ], - strategy: "average".to_string(), - output: Some(PathBuf::from("/tmp/merged_out.apr")), + strategy: "average".to_string().into(), + output: Some(PathBuf::from("/tmp/merged_out.apr").into()), weights: None, base_model: None, drop_rate: 0.9, @@ -99,10 +99,10 @@ fn test_dispatch_model_commands_merge_plan_mode() { let cli = make_cli(Commands::Merge { files: vec![ - PathBuf::from("/tmp/nonexistent_merge_plan_a.apr"), - PathBuf::from("/tmp/nonexistent_merge_plan_b.apr"), + PathBuf::from("/tmp/nonexistent_merge_plan_a.apr").into(), + PathBuf::from("/tmp/nonexistent_merge_plan_b.apr").into(), ], - strategy: "slerp".to_string(), + strategy: "slerp".to_string().into(), output: None, // plan mode doesn't need output weights: Some(vec![0.5, 0.5]), base_model: None, @@ -119,7 +119,7 @@ #[test] fn test_dispatch_model_commands_returns_none_for_validate() { let cli = make_cli(Commands::Validate { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), quality: false, strict: false, min_score: None, @@ -131,7 +131,7 @@ #[test] fn test_dispatch_model_commands_returns_none_for_debug() { let cli = make_cli(Commands::Debug { - file: Some(PathBuf::from("model.apr")), + file: Some(PathBuf::from("model.apr").into()), action: None, drama: false, hex: false, @@ -145,11 +145,11 @@ #[test] fn test_dispatch_model_commands_prune_nonexistent() { let cli = make_cli(Commands::ModelOps(ModelOpsCommands::Prune { - file: PathBuf::from("/tmp/nonexistent_prune_model.apr"), - method: "magnitude".to_string(), + file: PathBuf::from("/tmp/nonexistent_prune_model.apr").into(), + method: "magnitude".to_string().into(), target_ratio: 0.5, sparsity: 0.0, - output: Some(PathBuf::from("/tmp/pruned_out.apr")), + output: Some(PathBuf::from("/tmp/pruned_out.apr").into()), remove_layers: None, analyze: false, plan: false, @@ -173,7 +173,7 @@ fn test_dispatch_analysis_returns_none_for_chat() { // Chat is Extended but not an analysis command let cli = make_cli(Commands::Extended(ExtendedCommands::Chat { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), temperature: 0.7, top_p: 0.9, max_tokens: 512, @@ -197,7 +197,7 @@ #[test] fn test_dispatch_analysis_tree_nonexistent_file() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tree { - file: PathBuf::from("/tmp/nonexistent_tree_model.apr"), + file: PathBuf::from("/tmp/nonexistent_tree_model.apr").into(), filter: None, format: crate::commands::tree::TreeFormat::Ascii, sizes: false, @@ -211,7 +211,7 @@ #[test] fn test_dispatch_analysis_hex_nonexistent_file() { let cli = make_cli(Commands::Extended(ExtendedCommands::Hex { - file: PathBuf::from("/tmp/nonexistent_hex_model.apr"), + file: PathBuf::from("/tmp/nonexistent_hex_model.apr").into(), tensor: None, limit: 64, stats: false, @@ -223,7 +223,7 @@ contract: false, entropy: false, raw: false, - offset: "0".to_string(), + offset: "0".to_string().into(), width: 16, slice: None, })); @@ -235,9 +235,9 @@ #[test] fn test_dispatch_analysis_flow_nonexistent_file() { let cli = make_cli(Commands::Extended(ExtendedCommands::Flow { - file: PathBuf::from("/tmp/nonexistent_flow_model.apr"), + file: PathBuf::from("/tmp/nonexistent_flow_model.apr").into(), layer: None, - component: "full".to_string(), + component: "full".to_string().into(), verbose: false, json: false, })); @@ -249,8 +249,8 @@ #[test] fn test_dispatch_analysis_qualify_nonexistent_file() { let cli = make_cli(Commands::Extended(ExtendedCommands::Qualify { - file: PathBuf::from("/tmp/nonexistent_qualify_model.apr"), - tier: "basic".to_string(), + file: PathBuf::from("/tmp/nonexistent_qualify_model.apr").into(), + tier: "basic".to_string().into(), timeout: 30, json: false, verbose: false, @@ -264,7 +264,7 @@ #[test] fn test_dispatch_analysis_diagnose_nonexistent() { let cli = make_cli(Commands::Extended(ExtendedCommands::Diagnose { - checkpoint_dir: PathBuf::from("/tmp/nonexistent_diagnose_dir"), + checkpoint_dir: PathBuf::from("/tmp/nonexistent_diagnose_dir").into(), data: None, model_size: None, num_classes: 5, @@ -336,9 +336,9 @@ fn test_dispatch_analysis_probar_rejects_unknown_format() { let cli = make_cli(Commands::Extended(ExtendedCommands::Test { command: TestSubcommand::Tensor { - file: PathBuf::from("/tmp/nonexistent_probar_model.apr"), - output: PathBuf::from("/tmp/nonexistent_probar_out"), - format: "bogus".to_string(), + file: PathBuf::from("/tmp/nonexistent_probar_model.apr").into(), + output: PathBuf::from("/tmp/nonexistent_probar_out").into(), + format: "bogus".to_string().into(), golden: None, layer: None, assert: false, @@ -363,9 +363,9 @@ for format in ["json", "png", "both", "all"] { let cli = make_cli(Commands::Extended(ExtendedCommands::Test { command: TestSubcommand::Tensor { - file: PathBuf::from("/tmp/nonexistent_probar_model.apr"), - output: PathBuf::from("/tmp/nonexistent_probar_out"), - format: format.to_string(), + file: PathBuf::from("/tmp/nonexistent_probar_model.apr").into(), + output: PathBuf::from("/tmp/nonexistent_probar_out").into(), + format: format.to_string().into(), golden: None, layer: None, assert: false, @@ -385,8 +385,8 @@ #[test] fn test_dispatch_analysis_compare_hf_offline_rejected() { let mut cli = make_cli(Commands::Extended(ExtendedCommands::CompareHf { - file: PathBuf::from("/tmp/nonexistent_compare_hf.apr"), - hf: "org/repo".to_string(), + file: PathBuf::from("/tmp/nonexistent_compare_hf.apr").into(), + hf: "org/repo".to_string().into(), tensor: None, threshold: 0.01, json: false, @@ -410,7 +410,7 @@ #[test] fn test_dispatch_profiling_returns_none_for_chat() { let cli = make_cli(Commands::Extended(ExtendedCommands::Chat { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), temperature: 0.7, top_p: 0.9, max_tokens: 512, @@ -443,7 +443,7 @@ #[test] fn ptx_without_analyzer_reports_the_feature_not_the_path() { let cli = make_cli(Commands::Extended(ExtendedCommands::Ptx { - file: Some(PathBuf::from("/nonexistent_ptx_source_xyz.ptx")), + file: Some(PathBuf::from("/nonexistent_ptx_source_xyz.ptx").into()), kernel: None, strict: false, bugs: false, @@ -475,7 +475,7 @@ fn ptx_without_analyzer_reports_remedy_for_kernel_form_too() { let cli = make_cli(Commands::Extended(ExtendedCommands::Ptx { file: None, - kernel: Some("Q4KGemv".to_string()), + kernel: Some("Q4KGemv".to_string().into()), strict: false, bugs: false, json: false, @@ -494,7 +494,7 @@ #[test] fn test_dispatch_profiling_bench_nonexistent() { let cli = make_cli(Commands::Extended(ExtendedCommands::Bench { - file: PathBuf::from("/tmp/nonexistent_bench_model.apr"), + file: PathBuf::from("/tmp/nonexistent_bench_model.apr").into(), warmup: 1, iterations: 1, max_tokens: 10, @@ -511,9 +511,9 @@ #[test] fn test_dispatch_profiling_profile_nonexistent() { let cli = make_cli(Commands::Extended(ExtendedCommands::Profile { - file: PathBuf::from("/tmp/nonexistent_profile_model.apr"), + file: PathBuf::from("/tmp/nonexistent_profile_model.apr").into(), granular: false, - format: "text".to_string(), + format: "text".to_string().into(), focus: None, detect_naive: false, threshold: 0.01, @@ -542,8 +542,8 @@ #[test] fn test_dispatch_profiling_eval_nonexistent() { let cli = make_cli(Commands::Extended(ExtendedCommands::Eval { - file: PathBuf::from("/tmp/nonexistent_eval_model.apr"), - dataset: "test".to_string(), + file: PathBuf::from("/tmp/nonexistent_eval_model.apr").into(), + dataset: "test".to_string().into(), text: None, max_tokens: 32, threshold: 90.0, @@ -564,7 +564,7 @@ #[test] fn test_dispatch_profiling_qa_nonexistent() { let cli = make_cli(Commands::Extended(ExtendedCommands::Qa { - file: PathBuf::from("/tmp/nonexistent_qa_model.apr"), + file: PathBuf::from("/tmp/nonexistent_qa_model.apr").into(), assert_tps: None, assert_speedup: None, assert_gpu_speedup: None, @@ -601,14 +601,14 @@ // Train Plan with nonexistent data — tests train routing path let cli = make_cli(Commands::Extended(ExtendedCommands::Train { command: TrainCommands::Plan { - data: Some(PathBuf::from("/tmp/nonexistent_train_data.jsonl")), - model_size: "tiny".to_string(), + data: Some(PathBuf::from("/tmp/nonexistent_train_data.jsonl").into()), + model_size: "tiny".to_string().into(), model_path: None, num_classes: 5, - task: "classify".to_string(), + task: "classify".to_string().into(), config: None, - output: PathBuf::from("/tmp/train_plan_out"), - strategy: "auto".to_string(), + output: PathBuf::from("/tmp/train_plan_out").into(), + strategy: "auto".to_string().into(), budget: 100, scout: false, max_epochs: 3, @@ -617,7 +617,7 @@ batch_size: Some(4), val_data: None, test_data: None, - format: "text".to_string(), + format: "text".to_string().into(), }, })); let result = dispatch_analysis_commands(&cli); @@ -629,10 +629,10 @@ fn test_dispatch_train_sweep() { let cli = make_cli(Commands::Extended(ExtendedCommands::Train { command: TrainCommands::Sweep { - config: PathBuf::from("/tmp/nonexistent_sweep_config.toml"), - strategy: "grid".to_string(), + config: PathBuf::from("/tmp/nonexistent_sweep_config.toml").into(), + strategy: "grid".to_string().into(), num_configs: 5, - output_dir: PathBuf::from("/tmp/sweep_out"), + output_dir: PathBuf::from("/tmp/sweep_out").into(), seed: 42, }, })); @@ -645,7 +645,7 @@ fn test_dispatch_train_cluster_status() { let cli = make_cli(Commands::Extended(ExtendedCommands::Train { command: TrainCommands::ClusterStatus { - cluster: PathBuf::from("/tmp/nonexistent_cluster_config.yaml"), + cluster: PathBuf::from("/tmp/nonexistent_cluster_config.yaml").into(), }, })); let result = dispatch_analysis_commands(&cli); @@ -659,7 +659,7 @@ #[test] fn test_dispatch_inspection_routes_inspect() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), vocab: false, filters: false, weights: false, @@ -673,7 +673,7 @@ #[test] fn test_dispatch_inspection_routes_debug() { let cli = make_cli(Commands::Debug { - file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr")), + file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr").into()), action: None, drama: false, hex: false, @@ -687,7 +687,7 @@ #[test] fn test_dispatch_inspection_routes_validate() { let cli = make_cli(Commands::Validate { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), strict: false, quality: false, min_score: None, @@ -699,7 +699,7 @@ #[test] fn test_dispatch_inspection_routes_lint() { let cli = make_cli(Commands::Lint { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), strict: false, }); let result = dispatch_inspection_commands(&cli); @@ -709,8 +709,8 @@ #[test] fn test_dispatch_inspection_returns_none_for_export() { let cli = make_cli(Commands::Export { - file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr")), - format: "gguf".to_string(), + file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr").into()), + format: "gguf".to_string().into(), output: None, quantize: None, list_formats: false, @@ -730,7 +730,7 @@ #[test] fn test_dispatch_diagnostic_routes_trace() { let cli = make_cli(Commands::Trace { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), layer: None, reference: None, json: false, @@ -740,7 +740,7 @@ interactive: false, save_tensor: None, save_tensor_dir: None, - save_tensor_layers: "0..1".to_string(), + save_tensor_layers: "0..1".to_string().into(), }); let result = dispatch_diagnostic_commands(&cli); assert!(result.is_some(), "Trace should be handled by diagnostic dispatcher"); @@ -749,7 +749,7 @@ #[test] fn test_dispatch_diagnostic_routes_tensors() { let cli = make_cli(Commands::Tensors { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), stats: false, filter: None, limit: 0, @@ -762,8 +762,8 @@ #[test] fn test_dispatch_diagnostic_routes_diff() { let cli = make_cli(Commands::Diff { - file1: PathBuf::from("/tmp/nonexistent_a.apr"), - file2: PathBuf::from("/tmp/nonexistent_b.apr"), + file1: PathBuf::from("/tmp/nonexistent_a.apr").into(), + file2: PathBuf::from("/tmp/nonexistent_b.apr").into(), weights: false, values: false, filter: None, @@ -781,7 +781,7 @@ #[test] fn test_dispatch_diagnostic_returns_none_for_inspect() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), vocab: false, filters: false, weights: false, @@ -799,9 +799,9 @@ #[test] fn test_dispatch_format_routes_import() { let cli = make_cli(Commands::Import { - source: "/tmp/nonexistent_pmat540.safetensors".to_string(), + source: "/tmp/nonexistent_pmat540.safetensors".to_string().into(), output: None, - arch: "auto".to_string(), + arch: "auto".to_string().into(), quantize: None, strict: false, preserve_q4k: false, @@ -816,10 +816,10 @@ #[test] fn test_dispatch_format_routes_convert() { let cli = make_cli(Commands::Convert { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), quantize: None, compress: None, - output: PathBuf::from("/tmp/nonexistent_pmat540_out.apr"), + output: PathBuf::from("/tmp/nonexistent_pmat540_out.apr").into(), force: false, }); let result = dispatch_format_commands(&cli); @@ -829,8 +829,8 @@ #[test] fn test_dispatch_format_routes_export() { let cli = make_cli(Commands::Export { - file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr")), - format: "gguf".to_string(), + file: Some(PathBuf::from("/tmp/nonexistent_pmat540.apr").into()), + format: "gguf".to_string().into(), output: None, quantize: None, list_formats: false, @@ -846,7 +846,7 @@ #[test] fn test_dispatch_format_returns_none_for_inspect() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), vocab: false, filters: false, weights: false, @@ -864,7 +864,7 @@ #[test] fn test_dispatch_runtime_routes_check() { let cli = make_cli(Commands::Check { - file: PathBuf::from("/tmp/nonexistent_pmat540.gguf"), + file: PathBuf::from("/tmp/nonexistent_pmat540.gguf").into(), no_gpu: true, json: false, }); @@ -875,7 +875,7 @@ #[test] fn test_dispatch_runtime_returns_none_for_inspect() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), vocab: false, filters: false, weights: false, @@ -893,7 +893,7 @@ #[test] fn test_dispatch_core_routes_inspect_via_inspection() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), vocab: false, filters: false, weights: false, @@ -908,7 +908,7 @@ #[test] fn test_dispatch_core_routes_tensors_via_diagnostic() { let cli = make_cli(Commands::Tensors { - file: PathBuf::from("/tmp/nonexistent_pmat540.apr"), + file: PathBuf::from("/tmp/nonexistent_pmat540.apr").into(), stats: false, filter: None, limit: 0, @@ -921,9 +921,9 @@ #[test] fn test_dispatch_core_routes_import_via_format() { let cli = make_cli(Commands::Import { - source: "/tmp/nonexistent_pmat540.safetensors".to_string(), + source: "/tmp/nonexistent_pmat540.safetensors".to_string().into(), output: None, - arch: "auto".to_string(), + arch: "auto".to_string().into(), quantize: None, strict: false, preserve_q4k: false, @@ -938,7 +938,7 @@ #[test] fn test_dispatch_core_returns_none_for_extended() { let cli = make_cli(Commands::Extended(ExtendedCommands::Chat { - file: PathBuf::from("/tmp/nonexistent_pmat540.gguf"), + file: PathBuf::from("/tmp/nonexistent_pmat540.gguf").into(), temperature: 0.7, top_p: 0.9, max_tokens: 512, diff --git a/crates/apr-cli/src/lib_execute_export_convert.rs b/crates/apr-cli/src/lib_execute_export_convert.rs index 9ca5c9e01b..1eafa8a6a2 100644 --- a/crates/apr-cli/src/lib_execute_export_convert.rs +++ b/crates/apr-cli/src/lib_execute_export_convert.rs @@ -3,9 +3,9 @@ #[test] fn test_execute_export_file_not_found() { let cli = make_cli(Commands::Export { - file: Some(PathBuf::from("/tmp/nonexistent_model_export_test.apr")), - format: "safetensors".to_string(), - output: Some(PathBuf::from("/tmp/out.safetensors")), + file: Some(PathBuf::from("/tmp/nonexistent_model_export_test.apr").into()), + format: "safetensors".to_string().into(), + output: Some(PathBuf::from("/tmp/out.safetensors").into()), quantize: None, list_formats: false, batch: None, @@ -21,10 +21,10 @@ #[test] fn test_execute_convert_file_not_found() { let cli = make_cli(Commands::Convert { - file: PathBuf::from("/tmp/nonexistent_model_convert_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_convert_test.apr").into(), quantize: None, compress: None, - output: PathBuf::from("/tmp/out.apr"), + output: PathBuf::from("/tmp/out.apr").into(), force: false, }); let result = execute_command(&cli); @@ -38,7 +38,7 @@ #[test] fn test_execute_hex_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Hex { - file: PathBuf::from("/tmp/nonexistent_model_hex_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_hex_test.apr").into(), tensor: None, limit: 64, stats: false, @@ -50,7 +50,7 @@ contract: false, entropy: false, raw: false, - offset: String::new(), + offset: String::new().into(), width: 16, slice: None, })); @@ -62,7 +62,7 @@ #[test] fn test_execute_tree_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tree { - file: PathBuf::from("/tmp/nonexistent_model_tree_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_tree_test.apr").into(), filter: None, format: crate::commands::tree::TreeFormat::Ascii, sizes: false, @@ -76,9 +76,9 @@ #[test] fn test_execute_flow_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Flow { - file: PathBuf::from("/tmp/nonexistent_model_flow_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_flow_test.apr").into(), layer: None, - component: "full".to_string(), + component: "full".to_string().into(), verbose: false, json: false, })); @@ -93,9 +93,9 @@ use TestSubcommand; let cli = make_cli(Commands::Extended(ExtendedCommands::Test { command: TestSubcommand::Tensor { - file: PathBuf::from("/tmp/nonexistent_model_probar_test.apr"), - output: PathBuf::from("/tmp/probar-out"), - format: "both".to_string(), + file: PathBuf::from("/tmp/nonexistent_model_probar_test.apr").into(), + output: PathBuf::from("/tmp/probar-out").into(), + format: "both".to_string().into(), golden: None, layer: None, assert: false, @@ -110,7 +110,7 @@ #[test] fn test_execute_check_file_not_found() { let cli = make_cli(Commands::Check { - file: PathBuf::from("/tmp/nonexistent_model_check_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_check_test.apr").into(), no_gpu: true, json: false, }); @@ -148,7 +148,7 @@ #[test] fn test_execute_explain_with_code() { let cli = make_cli(Commands::Explain { - code_or_file: Some("E001".to_string()), + code_or_file: Some("E001".to_string().into()), file: None, tensor: None, kernel: false, @@ -166,18 +166,18 @@ fn test_execute_tune_plan_no_file() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tune { file: None, - method: "auto".to_string(), + method: "auto".to_string().into(), rank: None, vram: 16.0, plan: true, - model: Some("7B".to_string()), + model: Some("7B".to_string().into()), freeze_base: false, train_data: None, json: false, task: None, budget: 10, - strategy: "tpe".to_string(), - scheduler: "asha".to_string(), + strategy: "tpe".to_string().into(), + scheduler: "asha".to_string().into(), scout: false, data: None, num_classes: 5, @@ -200,7 +200,7 @@ #[test] fn test_execute_qa_all_skips_succeeds() { let cli = make_cli(Commands::Extended(ExtendedCommands::Qa { - file: PathBuf::from("/tmp/nonexistent_model_qa_test.gguf"), + file: PathBuf::from("/tmp/nonexistent_model_qa_test.gguf").into(), assert_tps: None, assert_speedup: None, assert_gpu_speedup: None, @@ -236,7 +236,7 @@ #[test] fn test_execute_qa_with_gates_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Qa { - file: PathBuf::from("/tmp/nonexistent_model_qa_gates_test.gguf"), + file: PathBuf::from("/tmp/nonexistent_model_qa_gates_test.gguf").into(), assert_tps: None, assert_speedup: None, assert_gpu_speedup: None, @@ -272,9 +272,9 @@ #[test] fn test_execute_import_invalid_source() { let cli = make_cli(Commands::Import { - source: "/tmp/nonexistent_model_import_test.gguf".to_string(), + source: "/tmp/nonexistent_model_import_test.gguf".to_string().into(), output: None, - arch: "auto".to_string(), + arch: "auto".to_string().into(), quantize: None, strict: false, preserve_q4k: false, @@ -382,7 +382,12 @@ .expect("join"); // Exactly what dispatch_run does with those fields. let (run_prompt, chat_template) = - run_prompt_and_chat(prompt.as_ref(), positional.as_ref(), &source, chat); + run_prompt_and_chat( + prompt.as_ref().map(batuta_common::cli_roles::PromptText::as_string), + positional.as_ref().map(batuta_common::cli_roles::PromptText::as_string), + &source, + chat, + ); let options = crate::commands::run::RunOptions { prompt: run_prompt, chat_template, @@ -390,7 +395,7 @@ }; let config = crate::commands::run::realizar_config( std::path::Path::new(&source), - input.as_ref(), + input.as_deref().map(std::path::Path::to_path_buf).as_ref(), &options, ) .expect("config"); diff --git a/crates/apr-cli/src/lib_extract_paths.rs b/crates/apr-cli/src/lib_extract_paths.rs index 51e20478eb..41978db33b 100644 --- a/crates/apr-cli/src/lib_extract_paths.rs +++ b/crates/apr-cli/src/lib_extract_paths.rs @@ -3,7 +3,7 @@ #[test] fn test_extract_paths_tree_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tree { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), filter: None, format: crate::commands::tree::TreeFormat::Ascii, sizes: false, @@ -17,9 +17,9 @@ #[test] fn test_extract_paths_flow_exempt() { let cmd = Commands::Extended(ExtendedCommands::Flow { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), layer: None, - component: "full".to_string(), + component: "full".to_string().into(), verbose: false, json: false, }); @@ -31,11 +31,11 @@ #[test] fn test_extract_paths_publish_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Publish { - directory: PathBuf::from("/tmp/models"), - repo_id: "org/repo".to_string(), + directory: PathBuf::from("/tmp/models").into(), + repo_id: "org/repo".to_string().into(), model_name: None, - license: "mit".to_string(), - pipeline_tag: "text-generation".to_string(), + license: "mit".to_string().into(), + pipeline_tag: "text-generation".to_string().into(), library_name: None, tags: None, message: None, @@ -50,8 +50,8 @@ #[test] fn test_extract_paths_tune_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tune { - file: Some(PathBuf::from("model.apr")), - method: "auto".to_string(), + file: Some(PathBuf::from("model.apr").into()), + method: "auto".to_string().into(), rank: None, vram: 16.0, plan: false, @@ -61,8 +61,8 @@ json: false, task: None, budget: 10, - strategy: "tpe".to_string(), - scheduler: "asha".to_string(), + strategy: "tpe".to_string().into(), + scheduler: "asha".to_string().into(), scout: false, data: None, num_classes: 5, @@ -79,7 +79,7 @@ #[test] fn test_extract_paths_pull_exempt() { let cmd = Commands::Pull { - model_ref: "hf://org/repo".to_string(), + model_ref: "hf://org/repo".to_string().into(), repo: None, force: false, dry_run: false, @@ -97,7 +97,7 @@ #[test] fn test_extract_paths_rm_exempt() { let cmd = Commands::Rm { - model_ref: "model-name".to_string(), + model_ref: "model-name".to_string().into(), }; let paths = extract_model_paths(&cmd); assert!(paths.is_empty(), "Rm is a diagnostic command (exempt)"); @@ -108,8 +108,8 @@ fn test_extract_paths_canary_exempt() { let cmd = Commands::Canary { command: CanaryCommands::Check { - file: PathBuf::from("model.apr"), - canary: PathBuf::from("canary.json"), + file: PathBuf::from("model.apr").into(), + canary: PathBuf::from("canary.json").into(), }, }; let paths = extract_model_paths(&cmd); @@ -120,7 +120,7 @@ #[test] fn test_extract_paths_oracle_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Oracle { - source: Some("model.gguf".to_string()), + source: Some("model.gguf".to_string().into()), family: None, size: None, compliance: false, @@ -141,9 +141,9 @@ let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Showcase { auto_verify: false, step: None, - tier: "small".to_string(), - model_dir: PathBuf::from("./models"), - baseline: "llama-cpp,ollama".to_string(), + tier: "small".to_string().into(), + model_dir: PathBuf::from("./models").into(), + baseline: "llama-cpp,ollama".to_string().into(), zram: false, runs: 30, gpu: false, @@ -163,8 +163,8 @@ fn test_extract_paths_rosetta_convert() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Convert { - source: PathBuf::from("model.gguf"), - target: PathBuf::from("out.safetensors"), + source: PathBuf::from("model.gguf").into(), + target: PathBuf::from("out.safetensors").into(), quantize: None, verify: false, json: false, @@ -180,9 +180,9 @@ fn test_extract_paths_rosetta_chain() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Chain { - source: PathBuf::from("model.gguf"), - formats: vec!["safetensors".to_string(), "apr".to_string()], - work_dir: PathBuf::from("/tmp"), + source: PathBuf::from("model.gguf").into(), + formats: vec!["safetensors".to_string().into(), "apr".to_string().into()], + work_dir: PathBuf::from("/tmp").into(), json: false, }, })); @@ -195,8 +195,8 @@ fn test_extract_paths_rosetta_verify() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Verify { - source: PathBuf::from("model.apr"), - intermediate: "safetensors".to_string(), + source: PathBuf::from("model.apr").into(), + intermediate: "safetensors".to_string().into(), tolerance: 1e-5, json: false, }, @@ -210,9 +210,9 @@ fn test_extract_paths_rosetta_compare_inference() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::CompareInference { - model_a: PathBuf::from("model_a.gguf"), - model_b: PathBuf::from("model_b.apr"), - prompt: "test".to_string(), + model_a: PathBuf::from("model_a.gguf").into(), + model_b: PathBuf::from("model_b.apr").into(), + prompt: "test".to_string().into(), max_tokens: 5, temperature: 0.0, tolerance: 0.1, @@ -231,7 +231,7 @@ fn test_extract_paths_rosetta_inspect_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Inspect { - file: PathBuf::from("model.gguf"), + file: PathBuf::from("model.gguf").into(), hexdump: false, json: false, }, @@ -248,8 +248,8 @@ fn test_extract_paths_rosetta_diff_tensors_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::DiffTensors { - model_a: PathBuf::from("a.gguf"), - model_b: PathBuf::from("b.apr"), + model_a: PathBuf::from("a.gguf").into(), + model_b: PathBuf::from("b.apr").into(), mismatches_only: false, show_values: 0, filter: None, @@ -268,7 +268,7 @@ fn test_extract_paths_rosetta_fingerprint_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Fingerprint { - model: PathBuf::from("model.gguf"), + model: PathBuf::from("model.gguf").into(), model_b: None, output: None, filter: None, @@ -288,7 +288,7 @@ fn test_extract_paths_rosetta_validate_stats_exempt() { let cmd = Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::ValidateStats { - model: PathBuf::from("model.apr"), + model: PathBuf::from("model.apr").into(), reference: None, fingerprints: None, threshold: 3.0, @@ -353,7 +353,7 @@ #[test] fn test_execute_inspect_file_not_found() { let cli = make_cli(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_model_inspect_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_inspect_test.apr").into(), vocab: false, filters: false, weights: false, @@ -371,7 +371,7 @@ #[test] fn test_execute_debug_file_not_found() { let cli = make_cli(Commands::Debug { - file: Some(PathBuf::from("/tmp/nonexistent_model_debug_test.apr")), + file: Some(PathBuf::from("/tmp/nonexistent_model_debug_test.apr").into()), action: None, drama: false, hex: false, @@ -386,7 +386,7 @@ #[test] fn test_execute_validate_file_not_found() { let cli = make_cli(Commands::Validate { - file: PathBuf::from("/tmp/nonexistent_model_validate_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_validate_test.apr").into(), quality: false, strict: false, min_score: None, @@ -402,8 +402,8 @@ #[test] fn test_execute_diff_file_not_found() { let cli = make_cli(Commands::Diff { - file1: PathBuf::from("/tmp/nonexistent_model_diff1.apr"), - file2: PathBuf::from("/tmp/nonexistent_model_diff2.apr"), + file1: PathBuf::from("/tmp/nonexistent_model_diff1.apr").into(), + file2: PathBuf::from("/tmp/nonexistent_model_diff2.apr").into(), weights: false, values: false, filter: None, @@ -422,7 +422,7 @@ #[test] fn test_execute_tensors_file_not_found() { let cli = make_cli(Commands::Tensors { - file: PathBuf::from("/tmp/nonexistent_model_tensors_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_tensors_test.apr").into(), stats: false, filter: None, limit: 0, @@ -439,7 +439,7 @@ #[test] fn test_execute_lint_file_not_found() { let cli = make_cli(Commands::Lint { - file: PathBuf::from("/tmp/nonexistent_model_lint_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_lint_test.apr").into(), strict: false, }); let result = execute_command(&cli); @@ -450,7 +450,7 @@ #[test] fn test_execute_trace_file_not_found() { let cli = make_cli(Commands::Trace { - file: PathBuf::from("/tmp/nonexistent_model_trace_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_trace_test.apr").into(), layer: None, reference: None, json: false, @@ -460,7 +460,7 @@ interactive: false, save_tensor: None, save_tensor_dir: None, - save_tensor_layers: "0..1".to_string(), + save_tensor_layers: "0..1".to_string().into(), }); let result = execute_command(&cli); assert!(result.is_err(), "Trace should fail with non-existent file"); diff --git a/crates/apr-cli/src/lib_parse.rs b/crates/apr-cli/src/lib_parse.rs index cf733773cc..7a1cbe1a50 100644 --- a/crates/apr-cli/src/lib_parse.rs +++ b/crates/apr-cli/src/lib_parse.rs @@ -21,9 +21,9 @@ task, .. } => { - assert_eq!(input, Some(PathBuf::from("audio.wav"))); - assert_eq!(language, Some("en".to_string())); - assert_eq!(task, Some("transcribe".to_string())); + assert_eq!(input, Some(PathBuf::from("audio.wav")).map(Into::into)); + assert_eq!(language, Some("en".to_string()).map(Into::into)); + assert_eq!(task, Some("transcribe".to_string()).map(Into::into)); } _ => panic!("Expected Run command"), } @@ -89,7 +89,7 @@ assert!(skip_gpu_speedup); assert!(skip_contract); assert!(skip_format_parity); - assert_eq!(safetensors_path, Some(PathBuf::from("model.safetensors"))); + assert_eq!(safetensors_path, Some(PathBuf::from("model.safetensors")).map(Into::into)); assert_eq!(iterations, 20); assert_eq!(warmup, 5); assert_eq!(max_tokens, 64); @@ -185,9 +185,9 @@ assert_eq!(warmup, 10); assert_eq!(iterations, 20); assert_eq!(max_tokens, 64); - assert_eq!(prompt, Some("The quick brown fox".to_string())); + assert_eq!(prompt, Some("The quick brown fox".to_string()).map(Into::into)); assert!(fast); - assert_eq!(brick, Some("attention".to_string())); + assert_eq!(brick, Some("attention".to_string()).map(Into::into)); } _ => panic!("Expected Bench command"), } @@ -221,10 +221,10 @@ simulated, .. }) => { - assert_eq!(model_path, Some(PathBuf::from("model.gguf"))); + assert_eq!(model_path, Some(PathBuf::from("model.gguf")).map(Into::into)); assert!(speculative); assert_eq!(speculation_k, 8); - assert_eq!(draft_model, Some(PathBuf::from("draft.gguf"))); + assert_eq!(draft_model, Some(PathBuf::from("draft.gguf")).map(Into::into)); assert_eq!(concurrent, 4); assert!(simulated); } @@ -260,8 +260,8 @@ assert!(energy); assert!(perf_grade); assert!(callgraph); - assert_eq!(compare_hf, Some("openai/whisper-tiny".to_string())); - assert_eq!(output, Some(PathBuf::from("/tmp/flame.svg"))); + assert_eq!(compare_hf, Some("openai/whisper-tiny".to_string()).map(Into::into)); + assert_eq!(output, Some(PathBuf::from("/tmp/flame.svg")).map(Into::into)); } _ => panic!("Expected Profile command"), } @@ -300,15 +300,15 @@ profile, .. }) => { - assert_eq!(system, Some("You are a helpful assistant.".to_string())); + assert_eq!(system, Some("You are a helpful assistant.".to_string()).map(Into::into)); assert!(inspect); assert!(trace); assert_eq!( trace_steps, - Some(vec!["Tokenize".to_string(), "Decode".to_string()]) + Some(vec!["Tokenize".to_string(), "Decode".to_string()]).map(|v| v.into_iter().map(Into::into).collect()) ); assert!(trace_verbose); - assert_eq!(trace_output, Some(PathBuf::from("/tmp/chat-trace.json"))); + assert_eq!(trace_output, Some(PathBuf::from("/tmp/chat-trace.json")).map(Into::into)); assert_eq!(trace_level, "payload"); assert!(profile); } @@ -335,7 +335,7 @@ quiet, .. })) => { - assert_eq!(step, Some("bench".to_string())); + assert_eq!(step, Some("bench".to_string()).map(Into::into)); assert_eq!(tier, "tiny"); assert!(zram); assert_eq!(runs, 50); @@ -423,7 +423,7 @@ assert_eq!(model_b, PathBuf::from("test.apr")); assert!(mismatches_only); assert_eq!(show_values, 5); - assert_eq!(filter, Some("lm_head".to_string())); + assert_eq!(filter, Some("lm_head".to_string()).map(Into::into)); assert!(json); } _ => panic!("Expected DiffTensors subcommand"), diff --git a/crates/apr-cli/src/lib_parse_eval.rs b/crates/apr-cli/src/lib_parse_eval.rs index 3004151ce9..adbf4eecf2 100644 --- a/crates/apr-cli/src/lib_parse_eval.rs +++ b/crates/apr-cli/src/lib_parse_eval.rs @@ -32,7 +32,7 @@ }) => { assert_eq!(file, PathBuf::from("model.gguf")); assert_eq!(dataset, "lambada"); - assert_eq!(text, Some("The quick brown fox".to_string())); + assert_eq!(text, Some("The quick brown fox".to_string()).map(Into::into)); assert_eq!(max_tokens, 256); assert!((threshold - 15.5).abs() < f32::EPSILON); assert!(task.is_none()); @@ -90,7 +90,7 @@ json: _, }) => { assert_eq!(file, PathBuf::from("model.apr")); - assert_eq!(layer, Some("encoder.0".to_string())); + assert_eq!(layer, Some("encoder.0".to_string()).map(Into::into)); assert_eq!(component, "encoder"); assert!(verbose); } @@ -145,7 +145,7 @@ .. }) => { assert_eq!(file, PathBuf::from("model.apr")); - assert_eq!(tensor, Some("embed.weight".to_string())); + assert_eq!(tensor, Some("embed.weight".to_string()).map(Into::into)); assert_eq!(limit, 128); assert!(stats); assert!(list); @@ -204,7 +204,7 @@ depth, }) => { assert_eq!(file, PathBuf::from("model.apr")); - assert_eq!(filter, Some("encoder".to_string())); + assert_eq!(filter, Some("encoder".to_string()).map(Into::into)); assert_eq!(format, crate::commands::tree::TreeFormat::Mermaid); assert!(sizes); assert_eq!(depth, Some(3)); @@ -273,8 +273,8 @@ assert_eq!(file, PathBuf::from("model.apr")); assert_eq!(output, PathBuf::from("/tmp/probar")); assert_eq!(format, "json"); - assert_eq!(golden, Some(PathBuf::from("/refs/golden"))); - assert_eq!(layer, Some("layer.0".to_string())); + assert_eq!(golden, Some(PathBuf::from("/refs/golden")).map(Into::into)); + assert_eq!(layer, Some("layer.0".to_string()).map(Into::into)); assert!(!assert); assert!((tolerance - 0.98).abs() < 0.01); }, @@ -333,7 +333,7 @@ strings, limit, } => { - assert_eq!(file, Some(PathBuf::from("model.apr"))); + assert_eq!(file, Some(PathBuf::from("model.apr")).map(Into::into)); assert!(action.is_none(), "no subcommand was given"); assert!(drama); assert!(hex); @@ -373,7 +373,7 @@ let cli = parse_cli(args).expect("Failed to parse"); match *cli.command { Commands::Tui { file } => { - assert_eq!(file, Some(PathBuf::from("model.apr"))); + assert_eq!(file, Some(PathBuf::from("model.apr")).map(Into::into)); } _ => panic!("Expected Tui command"), } @@ -424,12 +424,12 @@ allow_no_config, } => { assert_eq!(source, "hf://openai/whisper-tiny"); - assert_eq!(output, Some(PathBuf::from("whisper.apr"))); + assert_eq!(output, Some(PathBuf::from("whisper.apr")).map(Into::into)); assert_eq!(arch, "whisper"); - assert_eq!(quantize, Some("int8".to_string())); + assert_eq!(quantize, Some("int8".to_string()).map(Into::into)); assert!(strict); assert!(preserve_q4k); - assert_eq!(tokenizer, Some(PathBuf::from("/path/to/tokenizer.json"))); + assert_eq!(tokenizer, Some(PathBuf::from("/path/to/tokenizer.json")).map(Into::into)); assert!(!enforce_provenance); assert!(!allow_no_config); } @@ -462,3 +462,64 @@ _ => panic!("Expected Import command"), } } + + // ======================================================================== + // ONT-4g G1.4 (#4476): `apr eval --task` must validate its value + // ======================================================================== + + /// `apr eval m.apr --task nonsense` must be REJECTED and the error must name + /// every valid task. Before #4476 `task` was a free `Option` and the + /// dispatch's `_` arm silently ran perplexity for any unknown name. + #[test] + fn test_eval_rejects_unknown_task_4476() { + let err = parse_cli(vec!["apr", "eval", "m.apr", "--task", "nonsense"]) + .expect_err("`apr eval --task nonsense` must NOT parse (#4476)"); + assert_eq!(err.kind(), clap::error::ErrorKind::InvalidValue); + let rendered = err.to_string(); + for value in crate::EVAL_TASK_VALUES { + assert!( + rendered.contains(value), + "error must name the valid task `{value}`; got:\n{rendered}" + ); + } + } + + /// Every value in `EVAL_TASK_VALUES` must still parse. + #[test] + fn test_eval_accepts_known_tasks_4476() { + for value in crate::EVAL_TASK_VALUES { + let cli = parse_cli(vec!["apr", "eval", "m.apr", "--task", value]) + .unwrap_or_else(|e| panic!("`apr eval --task {value}` must parse: {e}")); + match *cli.command { + Commands::Extended(ExtendedCommands::Eval { ref task, .. }) => { + assert_eq!(task.as_deref(), Some(value)); + } + _ => panic!("Expected Eval command"), + } + } + } + + /// The parser's value list and the dispatch's match arms must be the same + /// set: an arm missing from the const is unreachable, a const value with no + /// arm falls to the `_` perplexity arm — the defect #4476 closed. + #[test] + fn test_eval_task_values_match_dispatch_arms_4476() { + let src = include_str!("dispatch_analysis.rs"); + let start = src + .find("ExtendedCommands::Eval {") + .expect("Eval dispatch arm"); + let end = start + + src[start..] + .find("ExtendedCommands::Qa") + .expect("Qa arm follows Eval"); + let mut arms: Vec<&str> = src[start..end] + .split("Some(\"") + .skip(1) + .filter_map(|s| s.split('"').next()) + .collect(); + arms.sort_unstable(); + arms.dedup(); + let mut values: Vec<&str> = crate::EVAL_TASK_VALUES.to_vec(); + values.sort_unstable(); + assert_eq!(arms, values, "EVAL_TASK_VALUES vs dispatch `Some(\"…\")` arms"); + } diff --git a/crates/apr-cli/src/lib_parse_export.rs b/crates/apr-cli/src/lib_parse_export.rs index 2ea477e5a1..353fdcf74d 100644 --- a/crates/apr-cli/src/lib_parse_export.rs +++ b/crates/apr-cli/src/lib_parse_export.rs @@ -22,10 +22,10 @@ quantize, .. } => { - assert_eq!(file, Some(PathBuf::from("model.apr"))); + assert_eq!(file, Some(PathBuf::from("model.apr")).map(Into::into)); assert_eq!(format, "gguf"); - assert_eq!(output, Some(PathBuf::from("model.gguf"))); - assert_eq!(quantize, Some("int4".to_string())); + assert_eq!(output, Some(PathBuf::from("model.gguf")).map(Into::into)); + assert_eq!(quantize, Some("int4".to_string()).map(Into::into)); } _ => panic!("Expected Export command"), } @@ -72,8 +72,8 @@ force, } => { assert_eq!(file, PathBuf::from("model.apr")); - assert_eq!(quantize, Some("q4k".to_string())); - assert_eq!(compress, Some("zstd".to_string())); + assert_eq!(quantize, Some("q4k".to_string()).map(Into::into)); + assert_eq!(compress, Some("zstd".to_string()).map(Into::into)); assert_eq!(output, PathBuf::from("model-q4k.apr")); assert!(force); } @@ -130,7 +130,7 @@ family, size, })) => { - assert_eq!(source, Some("model.gguf".to_string())); + assert_eq!(source, Some("model.gguf".to_string()).map(Into::into)); assert!(compliance); assert!(tensors); assert!(stats); @@ -158,8 +158,8 @@ .. })) => { assert!(source.is_none()); - assert_eq!(family, Some("qwen2".to_string())); - assert_eq!(size, Some("7b".to_string())); + assert_eq!(family, Some("qwen2".to_string()).map(Into::into)); + assert_eq!(size, Some("7b".to_string()).map(Into::into)); } _ => panic!("Expected Oracle command"), } @@ -172,7 +172,7 @@ let cli = parse_cli(args).expect("Failed to parse"); match *cli.command { Commands::Extended(ExtendedCommands::Tools(ToolCommands::Oracle { source, .. })) => { - assert_eq!(source, Some("hf://Qwen/Qwen2.5-Coder-1.5B".to_string())); + assert_eq!(source, Some("hf://Qwen/Qwen2.5-Coder-1.5B".to_string()).map(Into::into)); } _ => panic!("Expected Oracle command"), } @@ -259,7 +259,7 @@ }) => { assert_eq!(file, PathBuf::from("model.apr")); assert_eq!(hf, "openai/whisper-tiny"); - assert_eq!(tensor, Some("encoder.0".to_string())); + assert_eq!(tensor, Some("encoder.0".to_string()).map(Into::into)); assert!((threshold - 1e-3).abs() < f64::EPSILON); assert!(json); } @@ -403,14 +403,14 @@ json, .. }) => { - assert_eq!(file, Some(PathBuf::from("model.apr"))); + assert_eq!(file, Some(PathBuf::from("model.apr")).map(Into::into)); assert_eq!(method, "lora"); assert_eq!(rank, Some(16)); assert!((vram - 24.0).abs() < f64::EPSILON); assert!(plan); - assert_eq!(model, Some("7B".to_string())); + assert_eq!(model, Some("7B".to_string()).map(Into::into)); assert!(freeze_base); - assert_eq!(train_data, Some(PathBuf::from("data.jsonl"))); + assert_eq!(train_data, Some(PathBuf::from("data.jsonl")).map(Into::into)); assert!(json); } _ => panic!("Expected Tune command"), diff --git a/crates/apr-cli/src/lib_parse_ptx_debug_4062.rs b/crates/apr-cli/src/lib_parse_ptx_debug_4062.rs new file mode 100644 index 0000000000..b9542ec101 --- /dev/null +++ b/crates/apr-cli/src/lib_parse_ptx_debug_4062.rs @@ -0,0 +1,64 @@ +// #4062: `apr ptx-debug` absorbs the `aprender-ptx-debug` binary. These pin +// that apr parses its argv, dispatches it to the same library code, and never +// turns a failing analysis verdict into exit 0. + +const PTX_HEADER_ONLY: &str = ".version 8.0\n.target sm_70\n.address_size 64\n"; + +#[test] +fn apr_ptx_debug_parses_the_standalone_analyze_argv() { + let cli = parse_cli(vec![ + "apr", + "ptx-debug", + "analyze", + "k.ptx", + "--min-score", + "80", + "--json", + ]) + .expect("apr ptx-debug analyze must parse"); + match *cli.command { + Commands::PtxDebug(trueno_ptx_debug::cli::Command::Analyze(a)) => { + assert_eq!(a.file, "k.ptx"); + assert!((a.min_score - 80.0).abs() < f64::EPSILON); + assert!(a.json); + } + other => panic!("expected ptx-debug analyze, got {other:?}"), + } + assert!(parse_cli(vec!["apr", "ptx-debug", "bogus"]).is_err()); +} + +#[test] +fn apr_ptx_debug_gen_fkr_dispatches_to_the_library() { + let dir = tempfile::tempdir().expect("tempdir"); + let ptx = dir.path().join("k.ptx"); + let out = dir.path().join("fkr.rs"); + std::fs::write(&ptx, PTX_HEADER_ONLY).expect("write ptx"); + let cli = parse_cli_owned(vec![ + "apr".into(), + "ptx-debug".into(), + "gen-fkr".into(), + ptx.display().to_string(), + "-o".into(), + out.display().to_string(), + ]) + .expect("apr ptx-debug gen-fkr must parse"); + let result = dispatch_sibling_cli_commands(&cli).expect("ptx-debug is a sibling CLI route"); + assert!(result.is_ok(), "gen-fkr failed: {result:?}"); + let written = std::fs::read_to_string(&out).expect("gen-fkr must write -o"); + assert!(!written.trim().is_empty()); +} + +#[test] +fn apr_ptx_debug_a_failing_verdict_is_never_exit_zero() { + assert!(ptx_debug_result(Ok(0)).is_ok()); + let mut messages = Vec::new(); + for code in [1, 2, 3, 42] { + let err = ptx_debug_result(Ok(code)).expect_err("non-zero verdict must fail"); + assert_eq!(err.exit_code_value(), 5, "verdict {code}"); + messages.push(err.to_string()); + } + messages.dedup(); + assert_eq!(messages.len(), 4, "each verdict names itself: {messages:?}"); + let io = ptx_debug_result(Err("Failed to read x.ptx".into())).expect_err("io error"); + assert!(io.to_string().contains("Failed to read x.ptx")); +} diff --git a/crates/apr-cli/src/lib_parse_rosetta.rs b/crates/apr-cli/src/lib_parse_rosetta.rs index d96b2f2fbb..66d5336f35 100644 --- a/crates/apr-cli/src/lib_parse_rosetta.rs +++ b/crates/apr-cli/src/lib_parse_rosetta.rs @@ -136,7 +136,7 @@ // Diagnostic commands should return no paths (exempt from validation) let diagnostic_commands = vec![ Commands::Inspect { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), vocab: false, filters: false, weights: false, @@ -144,7 +144,7 @@ quality: false, }, Commands::Debug { - file: Some(PathBuf::from("m.apr")), + file: Some(PathBuf::from("m.apr").into()), action: None, drama: false, hex: false, @@ -152,24 +152,24 @@ limit: 256, }, Commands::Validate { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), quality: false, strict: false, min_score: None, }, Commands::Tensors { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), stats: false, filter: None, limit: 0, json: false, }, Commands::Lint { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), strict: false, }, Commands::Extended(ExtendedCommands::Qa { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), assert_tps: None, assert_speedup: None, assert_gpu_speedup: None, @@ -195,7 +195,7 @@ assert_classifier_head: false, }), Commands::Extended(ExtendedCommands::Hex { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), tensor: None, limit: 64, stats: false, @@ -207,21 +207,21 @@ contract: false, entropy: false, raw: false, - offset: "0".to_string(), + offset: "0".to_string().into(), width: 16, slice: None, }), Commands::Extended(ExtendedCommands::Tree { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), filter: None, format: crate::commands::tree::TreeFormat::Ascii, sizes: false, depth: None, }), Commands::Extended(ExtendedCommands::Flow { - file: PathBuf::from("m.apr"), + file: PathBuf::from("m.apr").into(), layer: None, - component: "full".to_string(), + component: "full".to_string().into(), verbose: false, json: false, }), @@ -250,9 +250,9 @@ fn test_extract_paths_action_commands() { let serve_cmd = Commands::Serve { command: ServeCommands::Run { - file: Some(PathBuf::from("model.gguf")), + file: Some(PathBuf::from("model.gguf").into()), port: 8080, - host: "127.0.0.1".to_string(), + host: "127.0.0.1".to_string().into(), no_cors: false, no_metrics: false, no_gpu: false, @@ -261,7 +261,7 @@ list_devices: false, batch: false, trace: false, - trace_level: "basic".to_string(), + trace_level: "basic".to_string().into(), profile: false, backend: BackendArg::default(), otlp_endpoint: None, @@ -275,7 +275,7 @@ assert_eq!(paths, Vec::::new()); let bench_cmd = Commands::Extended(ExtendedCommands::Bench { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), warmup: 3, iterations: 5, max_tokens: 32, @@ -292,7 +292,7 @@ #[test] fn test_extract_paths_run_hf_url() { let cmd = Commands::Run { - source: "hf://org/repo".to_string(), + source: "hf://org/repo".to_string().into(), positional_prompt: None, input: None, prompt: None, @@ -338,12 +338,12 @@ fn test_extract_paths_merge_multiple() { let cmd = Commands::Merge { files: vec![ - PathBuf::from("a.apr"), - PathBuf::from("b.apr"), - PathBuf::from("c.apr"), + PathBuf::from("a.apr").into(), + PathBuf::from("b.apr").into(), + PathBuf::from("c.apr").into(), ], - strategy: "average".to_string(), - output: Some(PathBuf::from("merged.apr")), + strategy: "average".to_string().into(), + output: Some(PathBuf::from("merged.apr").into()), weights: None, base_model: None, drop_rate: 0.9, @@ -413,19 +413,19 @@ })) => { assert_eq!(directory, PathBuf::from("/tmp/models")); assert_eq!(repo_id, "paiml/whisper-apr-tiny"); - assert_eq!(model_name, Some("Whisper Tiny".to_string())); + assert_eq!(model_name, Some("Whisper Tiny".to_string()).map(Into::into)); assert_eq!(license, "apache-2.0"); assert_eq!(pipeline_tag, "automatic-speech-recognition"); - assert_eq!(library_name, Some("whisper-apr".to_string())); + assert_eq!(library_name, Some("whisper-apr".to_string()).map(Into::into)); assert_eq!( tags, Some(vec![ "whisper".to_string(), "tiny".to_string(), "asr".to_string() - ]) + ]).map(|v| v.into_iter().map(Into::into).collect()) ); - assert_eq!(message, Some("Initial release".to_string())); + assert_eq!(message, Some("Initial release".to_string()).map(Into::into)); assert!(dry_run); } _ => panic!("Expected Publish command"), diff --git a/crates/apr-cli/src/lib_parse_rosetta_02.rs b/crates/apr-cli/src/lib_parse_rosetta_02.rs index 48880ae5cd..074b975ef1 100644 --- a/crates/apr-cli/src/lib_parse_rosetta_02.rs +++ b/crates/apr-cli/src/lib_parse_rosetta_02.rs @@ -27,9 +27,9 @@ json, } => { assert_eq!(model, PathBuf::from("model.gguf")); - assert_eq!(model_b, Some(PathBuf::from("model2.apr"))); - assert_eq!(output, Some(PathBuf::from("fingerprints.json"))); - assert_eq!(filter, Some("encoder".to_string())); + assert_eq!(model_b, Some(PathBuf::from("model2.apr")).map(Into::into)); + assert_eq!(output, Some(PathBuf::from("fingerprints.json")).map(Into::into)); + assert_eq!(filter, Some("encoder".to_string()).map(Into::into)); assert!(verbose); assert!(json); } @@ -68,8 +68,8 @@ json, } => { assert_eq!(model, PathBuf::from("model.apr")); - assert_eq!(reference, Some(PathBuf::from("ref.gguf"))); - assert_eq!(fingerprints, Some(PathBuf::from("fp.json"))); + assert_eq!(reference, Some(PathBuf::from("ref.gguf")).map(Into::into)); + assert_eq!(fingerprints, Some(PathBuf::from("fp.json")).map(Into::into)); assert!((threshold - 5.0).abs() < f32::EPSILON); assert!(strict); assert!(json); @@ -141,9 +141,9 @@ #[test] fn test_extract_paths_export() { let cmd = Commands::Export { - file: Some(PathBuf::from("model.apr")), - format: "gguf".to_string(), - output: Some(PathBuf::from("out.gguf")), + file: Some(PathBuf::from("model.apr").into()), + format: "gguf".to_string().into(), + output: Some(PathBuf::from("out.gguf").into()), quantize: None, list_formats: false, batch: None, @@ -159,10 +159,10 @@ #[test] fn test_extract_paths_convert() { let cmd = Commands::Convert { - file: PathBuf::from("model.apr"), - quantize: Some("q4k".to_string()), + file: PathBuf::from("model.apr").into(), + quantize: Some("q4k".to_string().into()), compress: None, - output: PathBuf::from("out.apr"), + output: PathBuf::from("out.apr").into(), force: false, }; let paths = extract_model_paths(&cmd); @@ -173,7 +173,7 @@ #[test] fn test_extract_paths_check() { let cmd = Commands::Check { - file: PathBuf::from("model.gguf"), + file: PathBuf::from("model.gguf").into(), no_gpu: false, json: false, }; @@ -185,7 +185,7 @@ #[test] fn test_extract_paths_trace() { let cmd = Commands::Trace { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), layer: None, reference: None, json: false, @@ -195,7 +195,7 @@ interactive: false, save_tensor: None, save_tensor_dir: None, - save_tensor_layers: "0..1".to_string(), + save_tensor_layers: "0..1".to_string().into(), }; let paths = extract_model_paths(&cmd); assert_eq!(paths, vec![PathBuf::from("model.apr")]); @@ -207,9 +207,9 @@ use TestSubcommand; let cmd = Commands::Extended(ExtendedCommands::Test { command: TestSubcommand::Tensor { - file: PathBuf::from("model.apr"), - output: PathBuf::from("./probar-export"), - format: "both".to_string(), + file: PathBuf::from("model.apr").into(), + output: PathBuf::from("./probar-export").into(), + format: "both".to_string().into(), golden: None, layer: None, assert: false, @@ -224,8 +224,8 @@ #[test] fn test_extract_paths_compare_hf() { let cmd = Commands::Extended(ExtendedCommands::CompareHf { - file: PathBuf::from("model.apr"), - hf: "openai/whisper-tiny".to_string(), + file: PathBuf::from("model.apr").into(), + hf: "openai/whisper-tiny".to_string().into(), tensor: None, threshold: 1e-5, json: false, @@ -238,7 +238,7 @@ #[test] fn test_extract_paths_chat() { let cmd = Commands::Extended(ExtendedCommands::Chat { - file: PathBuf::from("model.gguf"), + file: PathBuf::from("model.gguf").into(), temperature: 0.7, top_p: 0.9, max_tokens: 512, @@ -263,8 +263,8 @@ #[test] fn test_extract_paths_eval() { let cmd = Commands::Extended(ExtendedCommands::Eval { - file: PathBuf::from("model.gguf"), - dataset: "wikitext-2".to_string(), + file: PathBuf::from("model.gguf").into(), + dataset: "wikitext-2".to_string().into(), text: None, max_tokens: 512, threshold: 20.0, @@ -285,9 +285,9 @@ #[test] fn test_extract_paths_profile() { let cmd = Commands::Extended(ExtendedCommands::Profile { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), granular: false, - format: "human".to_string(), + format: "human".to_string().into(), focus: None, detect_naive: false, threshold: 10.0, @@ -316,9 +316,9 @@ #[test] fn test_extract_paths_import_hf_url() { let cmd = Commands::Import { - source: "hf://openai/whisper-tiny".to_string(), - output: Some(PathBuf::from("whisper.apr")), - arch: "auto".to_string(), + source: "hf://openai/whisper-tiny".to_string().into(), + output: Some(PathBuf::from("whisper.apr").into()), + arch: "auto".to_string().into(), quantize: None, strict: false, preserve_q4k: false, @@ -337,9 +337,9 @@ #[test] fn test_extract_paths_import_nonexistent_local() { let cmd = Commands::Import { - source: "/tmp/nonexistent_model_abc123.gguf".to_string(), + source: "/tmp/nonexistent_model_abc123.gguf".to_string().into(), output: None, - arch: "auto".to_string(), + arch: "auto".to_string().into(), quantize: None, strict: false, preserve_q4k: false, @@ -358,7 +358,7 @@ #[test] fn test_extract_paths_tui_with_file() { let cmd = Commands::Tui { - file: Some(PathBuf::from("model.apr")), + file: Some(PathBuf::from("model.apr").into()), }; let paths = extract_model_paths(&cmd); assert_eq!(paths, vec![PathBuf::from("model.apr")]); @@ -378,7 +378,7 @@ let cmd = Commands::Extended(ExtendedCommands::Cbtop { model: None, attach: None, - model_path: Some(PathBuf::from("model.gguf")), + model_path: Some(PathBuf::from("model.gguf").into()), headless: false, json: false, output: None, @@ -401,7 +401,7 @@ #[test] fn test_extract_paths_cbtop_no_model_path() { let cmd = Commands::Extended(ExtendedCommands::Cbtop { - model: Some("qwen2.5-coder".to_string()), + model: Some("qwen2.5-coder".to_string().into()), attach: None, model_path: None, headless: false, @@ -426,8 +426,8 @@ #[test] fn test_extract_paths_diff_exempt() { let cmd = Commands::Diff { - file1: PathBuf::from("a.apr"), - file2: PathBuf::from("b.apr"), + file1: PathBuf::from("a.apr").into(), + file2: PathBuf::from("b.apr").into(), weights: false, values: false, filter: None, @@ -446,7 +446,7 @@ #[test] fn test_extract_paths_hex_exempt() { let cmd = Commands::Extended(ExtendedCommands::Hex { - file: PathBuf::from("model.apr"), + file: PathBuf::from("model.apr").into(), tensor: None, limit: 64, stats: false, @@ -458,7 +458,7 @@ contract: false, entropy: false, raw: false, - offset: "0".to_string(), + offset: "0".to_string().into(), width: 16, slice: None, }); diff --git a/crates/apr-cli/src/lib_parse_tensors.rs b/crates/apr-cli/src/lib_parse_tensors.rs index f3d27c4734..09e1a84401 100644 --- a/crates/apr-cli/src/lib_parse_tensors.rs +++ b/crates/apr-cli/src/lib_parse_tensors.rs @@ -24,7 +24,7 @@ } => { assert_eq!(file, PathBuf::from("model.apr")); assert!(stats); - assert_eq!(filter, Some("encoder".to_string())); + assert_eq!(filter, Some("encoder".to_string()).map(Into::into)); assert_eq!(limit, 20); assert!(json); } @@ -44,7 +44,7 @@ tensor, .. } => { - assert_eq!(code_or_file, Some("E001".to_string())); + assert_eq!(code_or_file, Some("E001".to_string()).map(Into::into)); assert!(file.is_none()); assert!(tensor.is_none()); } @@ -72,8 +72,8 @@ .. } => { assert!(code_or_file.is_none()); - assert_eq!(file, Some(PathBuf::from("model.apr"))); - assert_eq!(tensor, Some("embed.weight".to_string())); + assert_eq!(file, Some(PathBuf::from("model.apr")).map(Into::into)); + assert_eq!(tensor, Some("embed.weight".to_string()).map(Into::into)); } _ => panic!("Expected Explain command"), } @@ -112,8 +112,8 @@ save_tensor_layers: _, } => { assert_eq!(file, PathBuf::from("model.apr")); - assert_eq!(layer, Some("layer.0".to_string())); - assert_eq!(reference, Some(PathBuf::from("ref.apr"))); + assert_eq!(layer, Some("layer.0".to_string()).map(Into::into)); + assert_eq!(reference, Some(PathBuf::from("ref.apr")).map(Into::into)); assert!(json); assert!(verbose); assert!(payload); @@ -171,7 +171,7 @@ assert_eq!(file1, PathBuf::from("a.apr")); assert_eq!(file2, PathBuf::from("b.apr")); assert!(values); - assert_eq!(filter, Some("embed".to_string())); + assert_eq!(filter, Some("embed".to_string()).map(Into::into)); assert_eq!(limit, 5); assert!(transpose_aware); assert!(json); @@ -200,7 +200,7 @@ .. } => { assert!(chat); - assert_eq!(prompt, Some("Hello world".to_string())); + assert_eq!(prompt, Some("Hello world".to_string()).map(Into::into)); assert_eq!(source, "model.gguf"); } _ => panic!("Expected Run command"), @@ -248,7 +248,7 @@ .. } => { assert_eq!(source, "model.gguf"); - assert_eq!(positional_prompt, Some("What is 2+2?".to_string())); + assert_eq!(positional_prompt, Some("What is 2+2?".to_string()).map(Into::into)); assert_eq!(prompt, None); } _ => panic!("Expected Run command"), @@ -273,8 +273,8 @@ prompt, .. } => { - assert_eq!(positional_prompt, Some("positional text".to_string())); - assert_eq!(prompt, Some("flag text".to_string())); + assert_eq!(positional_prompt, Some("positional text".to_string()).map(Into::into)); + assert_eq!(prompt, Some("flag text".to_string()).map(Into::into)); } _ => panic!("Expected Run command"), } @@ -293,7 +293,7 @@ .. } => { assert_eq!(source, "model.gguf"); - assert_eq!(positional_prompt, Some("What is 2+2?".to_string())); + assert_eq!(positional_prompt, Some("What is 2+2?".to_string()).map(Into::into)); assert_eq!(max_tokens, 64); } _ => panic!("Expected Run command"), @@ -366,10 +366,10 @@ "Tokenize".to_string(), "Embed".to_string(), "Attention".to_string() - ]) + ]).map(|v| v.into_iter().map(Into::into).collect()) ); assert!(trace_verbose); - assert_eq!(trace_output, Some(PathBuf::from("/tmp/trace.json"))); + assert_eq!(trace_output, Some(PathBuf::from("/tmp/trace.json")).map(Into::into)); assert_eq!(trace_level, "layer"); } _ => panic!("Expected Run command"), diff --git a/crates/apr-cli/src/lib_verbose_inheritance_parse.rs b/crates/apr-cli/src/lib_verbose_inheritance_parse.rs index 16b7bdbf39..2ac1333566 100644 --- a/crates/apr-cli/src/lib_verbose_inheritance_parse.rs +++ b/crates/apr-cli/src/lib_verbose_inheritance_parse.rs @@ -161,7 +161,7 @@ fn test_execute_with_contract_gate_nonexistent() { let cli = Cli { command: Box::new(Commands::Inspect { - file: PathBuf::from("/tmp/nonexistent_contract_test.apr"), + file: PathBuf::from("/tmp/nonexistent_contract_test.apr").into(), vocab: false, filters: false, weights: false, @@ -207,7 +207,7 @@ fn test_execute_rosetta_inspect_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Inspect { - file: PathBuf::from("/tmp/nonexistent_rosetta_inspect.gguf"), + file: PathBuf::from("/tmp/nonexistent_rosetta_inspect.gguf").into(), hexdump: false, json: false, }, @@ -224,8 +224,8 @@ fn test_execute_rosetta_convert_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Convert { - source: PathBuf::from("/tmp/nonexistent_rosetta_convert.gguf"), - target: PathBuf::from("/tmp/out.safetensors"), + source: PathBuf::from("/tmp/nonexistent_rosetta_convert.gguf").into(), + target: PathBuf::from("/tmp/out.safetensors").into(), quantize: None, verify: false, json: false, @@ -244,7 +244,7 @@ fn test_execute_rosetta_fingerprint_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tools(ToolCommands::Rosetta { action: RosettaCommands::Fingerprint { - model: PathBuf::from("/tmp/nonexistent_rosetta_fingerprint.gguf"), + model: PathBuf::from("/tmp/nonexistent_rosetta_fingerprint.gguf").into(), model_b: None, output: None, filter: None, @@ -263,7 +263,7 @@ #[test] fn test_execute_bench_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Bench { - file: PathBuf::from("/tmp/nonexistent_model_bench_test.gguf"), + file: PathBuf::from("/tmp/nonexistent_model_bench_test.gguf").into(), warmup: 1, iterations: 1, max_tokens: 1, @@ -280,8 +280,8 @@ #[test] fn test_execute_eval_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Eval { - file: PathBuf::from("/tmp/nonexistent_model_eval_test.gguf"), - dataset: "wikitext-2".to_string(), + file: PathBuf::from("/tmp/nonexistent_model_eval_test.gguf").into(), + dataset: "wikitext-2".to_string().into(), text: None, max_tokens: 32, threshold: 20.0, @@ -302,9 +302,9 @@ #[test] fn test_execute_profile_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Profile { - file: PathBuf::from("/tmp/nonexistent_model_profile_test.apr"), + file: PathBuf::from("/tmp/nonexistent_model_profile_test.apr").into(), granular: false, - format: "human".to_string(), + format: "human".to_string().into(), focus: None, detect_naive: false, threshold: 10.0, @@ -336,8 +336,8 @@ #[test] fn test_execute_compare_hf_file_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::CompareHf { - file: PathBuf::from("/tmp/nonexistent_model_compare_hf_test.apr"), - hf: "openai/whisper-tiny".to_string(), + file: PathBuf::from("/tmp/nonexistent_model_compare_hf_test.apr").into(), + hf: "openai/whisper-tiny".to_string().into(), tensor: None, threshold: 1e-5, json: false, @@ -354,8 +354,8 @@ fn test_execute_canary_check_file_not_found() { let cli = make_cli(Commands::Canary { command: CanaryCommands::Check { - file: PathBuf::from("/tmp/nonexistent_canary_check.apr"), - canary: PathBuf::from("/tmp/nonexistent_canary.json"), + file: PathBuf::from("/tmp/nonexistent_canary_check.apr").into(), + canary: PathBuf::from("/tmp/nonexistent_canary.json").into(), }, }); let result = execute_command(&cli); @@ -369,11 +369,11 @@ #[test] fn test_execute_publish_dir_not_found() { let cli = make_cli(Commands::Extended(ExtendedCommands::Tools(ToolCommands::Publish { - directory: PathBuf::from("/tmp/nonexistent_publish_dir_test"), - repo_id: "test/test".to_string(), + directory: PathBuf::from("/tmp/nonexistent_publish_dir_test").into(), + repo_id: "test/test".to_string().into(), model_name: None, - license: "mit".to_string(), - pipeline_tag: "text-generation".to_string(), + license: "mit".to_string().into(), + pipeline_tag: "text-generation".to_string().into(), library_name: None, tags: None, message: None, diff --git a/crates/apr-cli/src/main.rs b/crates/apr-cli/src/main.rs index b3b60e2bd7..02891ac9d0 100644 --- a/crates/apr-cli/src/main.rs +++ b/crates/apr-cli/src/main.rs @@ -1,88 +1,11 @@ //! apr - APR Model Operations CLI //! -//! Entry point shim. See lib.rs for implementation. - -use apr_cli::{execute_command, Cli}; -use clap::Parser; -use colored::control; -use std::process::ExitCode; - -#[cfg(feature = "dhat-heap")] -#[global_allocator] -static ALLOC: dhat::Alloc = dhat::Alloc; - -/// GH-667: Reset SIGPIPE to SIG_DFL so piping to head/less exits cleanly. -#[cfg(unix)] -#[allow(unsafe_code)] -fn reset_sigpipe() { - // SAFETY: signal(SIGPIPE, SIG_DFL) is async-signal-safe per POSIX. - // Called once at program start before any threads are spawned. - unsafe { - libc::signal(libc::SIGPIPE, libc::SIG_DFL); - } -} - -#[cfg(not(unix))] -fn reset_sigpipe() {} - -/// GH-646: Clear FPCR.FZ16 on aarch64 so f16 subnormal scales aren't flushed to zero. -/// The ARM `fcvt s, h` instruction (used by the `half` crate) respects FPCR.FZ16. -/// When set (e.g., by NVIDIA's CUDA runtime on Jetson), subnormal f16 scale values -/// in Q6_K super-blocks are flushed to 0.0, causing `d * scale * q = 0` for all elements. -/// This produces all-zero tensors that pass on x86_64 but fail on aarch64. -#[cfg(target_arch = "aarch64")] -#[allow(unsafe_code)] -fn clear_fz16() { - // SAFETY: Reading/writing FPCR is safe — it only affects floating-point behavior - // for the current thread. Called once at program start before any FP operations. - unsafe { - let fpcr: u64; - core::arch::asm!("mrs {}, fpcr", out(reg) fpcr); - if fpcr & (1 << 19) != 0 { - // FZ16 is set — clear it - let new_fpcr = fpcr & !(1 << 19); - core::arch::asm!("msr fpcr, {}", in(reg) new_fpcr); - } - } -} - -#[cfg(not(target_arch = "aarch64"))] -fn clear_fz16() {} - -fn main() -> ExitCode { - // GH-667: Reset SIGPIPE to default so piping to head/less doesn't panic. - reset_sigpipe(); - // GH-646: Clear FPCR.FZ16 on ARM so f16 subnormals dequantize correctly. - clear_fz16(); - - #[cfg(feature = "dhat-heap")] - let _profiler = dhat::Profiler::new_heap(); - // GH-662: Respect NO_COLOR env var and non-TTY output. - // The `colored` crate's auto-detect doesn't reliably work in all environments. - let no_color = std::env::var("NO_COLOR").is_ok(); - let is_tty = std::io::IsTerminal::is_terminal(&std::io::stdout()); - if no_color || !is_tty { - control::set_override(false); - } - // FALSIFY-GPUTRAIN-007 / INV-GPUTRAIN-007: intercept `apr --version - // --json` BEFORE clap exits on the plain --version flag. Emits the - // 3-key CUDA metadata schema required by gputrain_007. Handler lives - // in lib.rs::emit_version_json to keep the schema next to its tests. - let raw: Vec = std::env::args().collect(); - if raw.iter().any(|a| a == "--version") && raw.iter().any(|a| a == "--json") { - apr_cli::emit_version_json(); - return ExitCode::SUCCESS; - } - if let Some(rc) = apr_cli::update_or_check(&raw) { - return rc; - } - - let cli = Cli::parse(); - match execute_command(&cli) { - Ok(()) => ExitCode::SUCCESS, - Err(e) => { - eprintln!("error: {e}"); - e.exit_code() - } - } +//! Entry point shim. The program is `apr_cli::cli_main`, the SAME function the +//! root facade's `src/bin/apr.rs` calls: both packages define a binary named +//! `apr`, and G0.5 (#2582) found the two had been kept equal by hand-copying +//! the prologue, and had already diverged (the dhat allocator existed only +//! here). `entry_point_is_shared` in lib.rs fails if a prologue comes back. + +fn main() -> std::process::ExitCode { + apr_cli::cli_main() } diff --git a/crates/apr-cli/src/model_ops_commands.rs b/crates/apr-cli/src/model_ops_commands.rs index 1319b986b6..f3c50f688e 100644 --- a/crates/apr-cli/src/model_ops_commands.rs +++ b/crates/apr-cli/src/model_ops_commands.rs @@ -6,10 +6,10 @@ pub enum ModelOpsCommands { Finetune { /// Input model file #[arg(value_name = "FILE")] - file: Option, + file: Option, /// Fine-tuning method: auto, full, lora, qlora #[arg(long, short = 'm', default_value = "auto")] - method: String, + method: FreeText, /// LoRA rank (default: auto-selected) #[arg(long, short = 'r')] rank: Option, @@ -21,13 +21,13 @@ pub enum ModelOpsCommands { plan: bool, /// Training data file (JSONL format) #[arg(long, short = 'd', value_name = "FILE")] - data: Option, + data: Option, /// Output path (adapter dir or merged model) #[arg(short, long)] - output: Option, + output: Option, /// Adapter path for merge mode #[arg(long)] - adapter: Option, + adapter: Option, /// Merge adapter into base model #[arg(long)] merge: bool, @@ -42,16 +42,16 @@ pub enum ModelOpsCommands { learning_rate: Option, /// Model size for planning (e.g., "7B", "1.5B") #[arg(long, value_name = "SIZE")] - model_size: Option, + model_size: Option, /// Fine-tuning task: classify (sequence classification) #[arg(long)] - task: Option, + task: Option, /// Number of classes for classification task #[arg(long, default_value = "5")] num_classes: usize, /// Output format for checkpoints: apr, safetensors, or both (comma-separated) #[arg(long, value_name = "FORMAT", default_value = "apr,safetensors")] - checkpoint_format: String, + checkpoint_format: FreeText, /// Oversample minority classes to match majority (for imbalanced datasets) #[arg(long)] oversample: bool, @@ -63,7 +63,7 @@ pub enum ModelOpsCommands { quantize_nf4: bool, /// GPU indices for data-parallel training (e.g., "0,1" for dual GPU) #[arg(long, value_name = "INDICES")] - gpus: Option, + gpus: Option, /// GPU backend selection: auto, cuda, wgpu // #2583: no `value_parser` here meant `--gpu-backend cudaa` parsed and // silently took the `_ =>` ("auto") arm of `gpu_backend_notice`. @@ -72,13 +72,13 @@ pub enum ModelOpsCommands { gpu_backend: String, /// Distributed training role: coordinator or worker #[arg(long, value_name = "ROLE")] - role: Option, + role: Option, /// Address to bind (coordinator) or connect to (worker) #[arg(long, value_name = "ADDR")] - bind: Option, + bind: Option, /// Coordinator address for worker nodes (e.g., "intel:9000") #[arg(long, value_name = "ADDR")] - coordinator: Option, + coordinator: Option, /// Expected number of workers (coordinator only) #[arg(long, value_name = "N")] expect_workers: Option, @@ -89,11 +89,11 @@ pub enum ModelOpsCommands { /// Format: --adapters data/corpus-a.jsonl:checkpoints/adapter-a /// Can be specified multiple times for concurrent adapter training. #[arg(long, value_name = "DATA:CHECKPOINT")] - adapters: Vec, + adapters: Vec, /// Multi-adapter config file: TOML with [[adapter]] entries (GPU-SHARE §2.4) #[arg(long, value_name = "FILE")] - adapters_config: Option, + adapters_config: Option, /// Enable experimental CUDA MPS for concurrent GPU sharing (GPU-SHARE §1.5). /// WARNING: A GPU fault in any MPS client will crash ALL clients on that GPU. @@ -113,10 +113,10 @@ pub enum ModelOpsCommands { Prune { /// Input model file #[arg(value_name = "FILE")] - file: PathBuf, + file: ModelPath, /// Pruning method: magnitude, structured, depth, width, wanda, sparsegpt #[arg(long, short = 'm', default_value = "magnitude")] - method: String, + method: FreeText, /// Target pruning ratio (0-1) #[arg(long, default_value = "0.5")] target_ratio: f32, @@ -125,10 +125,10 @@ pub enum ModelOpsCommands { sparsity: f32, /// Output file path #[arg(short, long)] - output: Option, + output: Option, /// Layers to remove for depth pruning (e.g., "20-24") #[arg(long)] - remove_layers: Option, + remove_layers: Option, /// Analyze mode (identify pruning opportunities) #[arg(long)] analyze: bool, @@ -137,25 +137,25 @@ pub enum ModelOpsCommands { plan: bool, /// Calibration data file #[arg(long, value_name = "FILE")] - calibration: Option, + calibration: Option, }, /// Knowledge distillation (teacher -> student) (GH-247, ALB-011) Distill { /// Teacher model file (positional, for file-based mode) #[arg(value_name = "TEACHER")] - teacher: Option, + teacher: Option, /// Student model file #[arg(long, value_name = "FILE")] - student: Option, + student: Option, /// Training data file #[arg(long, short = 'd', value_name = "FILE")] - data: Option, + data: Option, /// Output file path #[arg(short, long)] - output: Option, + output: Option, /// Distillation strategy: standard, progressive, ensemble #[arg(long, default_value = "standard")] - strategy: String, + strategy: FreeText, /// Temperature for softmax scaling #[arg(long, default_value = "3.0")] temperature: f64, @@ -170,10 +170,10 @@ pub enum ModelOpsCommands { plan: bool, /// YAML config file for two-stage distillation (ALB-011) #[arg(long, value_name = "FILE")] - config: Option, + config: Option, /// Distillation stage: precompute, train (logit KD), or generate (text-based, GH-455) #[arg(long, value_name = "STAGE")] - stage: Option, + stage: Option, /// SPEC-DISTILL-001 Phase 3-prep (PMAT-697): teacher/student backend /// selector. `fixture` (default) uses the in-memory FixtureTeacher /// + FixtureStudent (CPU-only, useful for plumbing tests + CI). @@ -196,6 +196,6 @@ pub enum ModelOpsCommands { /// Phase 4 50K-step dispatch sets this to a tokenized Python /// corpus directory. #[arg(long, value_name = "DIR")] - dataset: Option, + dataset: Option, }, } diff --git a/crates/apr-cli/src/parsing.rs b/crates/apr-cli/src/parsing.rs index cd3e23c326..5e2def1613 100644 --- a/crates/apr-cli/src/parsing.rs +++ b/crates/apr-cli/src/parsing.rs @@ -42,7 +42,7 @@ assert!(print, "-p must still set print"); assert_eq!( model, - Some(PathBuf::from("/tmp/named.gguf")), + Some(PathBuf::from("/tmp/named.gguf")).map(Into::into), "--model written after the prompt must be honoured, not swallowed" ); assert_eq!( @@ -149,7 +149,7 @@ .. } => { assert_eq!(source, "hf://openai/whisper-tiny"); - assert_eq!(prompt, Some("Hello".to_string())); + assert_eq!(prompt, Some("Hello".to_string()).map(Into::into)); assert_eq!(max_tokens, 64); } _ => panic!("Expected Run command"), @@ -481,7 +481,7 @@ } => { assert_eq!(files.len(), 2); assert_eq!(strategy, "weighted"); - assert_eq!(output, Some(PathBuf::from("merged.apr"))); + assert_eq!(output, Some(PathBuf::from("merged.apr")).map(Into::into)); assert_eq!(weights, Some(vec![0.7, 0.3])); } _ => panic!("Expected Merge command"), diff --git a/crates/apr-cli/src/pipeline_commands.rs b/crates/apr-cli/src/pipeline_commands.rs index ee04a38ae8..7a83c5de68 100644 --- a/crates/apr-cli/src/pipeline_commands.rs +++ b/crates/apr-cli/src/pipeline_commands.rs @@ -9,15 +9,15 @@ pub enum PipelineCommands { Plan { /// Path to forjar pipeline manifest (YAML) #[arg(value_name = "MANIFEST")] - manifest: PathBuf, + manifest: ConfigPath, /// Target specific machine #[arg(short, long)] - machine: Option, + machine: Option, /// Filter to resources with this tag #[arg(short, long)] - tag: Option, + tag: Option, /// Show estimated change cost per resource type #[arg(long)] @@ -28,15 +28,15 @@ pub enum PipelineCommands { Apply { /// Path to forjar pipeline manifest (YAML) #[arg(value_name = "MANIFEST")] - manifest: PathBuf, + manifest: ConfigPath, /// Target specific machine #[arg(short, long)] - machine: Option, + machine: Option, /// Filter to resources with this tag #[arg(short, long)] - tag: Option, + tag: Option, /// Number of parallel SSH sessions (default: 5) #[arg(short, long)] @@ -51,13 +51,13 @@ pub enum PipelineCommands { Status { /// Path to forjar pipeline manifest (YAML) #[arg(value_name = "MANIFEST")] - manifest: PathBuf, + manifest: ConfigPath, }, /// Validate pipeline manifest without connecting to machines. Validate { /// Path to forjar pipeline manifest (YAML) #[arg(value_name = "MANIFEST")] - manifest: PathBuf, + manifest: ConfigPath, }, } diff --git a/crates/apr-cli/src/serve_commands.rs b/crates/apr-cli/src/serve_commands.rs index e6f283c435..2332f54f71 100644 --- a/crates/apr-cli/src/serve_commands.rs +++ b/crates/apr-cli/src/serve_commands.rs @@ -17,7 +17,7 @@ pub enum ServeCommands { Plan { /// Model source: local path or HuggingFace repo (hf://org/repo, org/repo) #[arg(value_name = "MODEL")] - model: String, + model: ModelRef, /// Detect GPU via nvidia-smi for VRAM budget #[arg(long)] gpu: bool, @@ -29,25 +29,28 @@ pub enum ServeCommands { seq_len: usize, /// Output format: text, json, yaml #[arg(long, default_value = "text")] - format: String, + format: FreeText, /// Quantization override for HF models (e.g., Q4_K_M, Q6_K, F16) #[arg(long)] - quant: Option, + quant: Option, }, /// Start inference server (REST API, streaming, metrics) + // #3745 S1 v1.1: prompts arrive over HTTP, not through a PromptText arg, + // so the generator is declared by marker (`apr surface` → `generates: true`). + #[command(group(batuta_common::cli_roles::ServesGeneration::group()))] Run { /// Path to model file /// /// Not required with `--list-devices`, which asks what this BUILD can /// dispatch to and needs no model to answer. #[arg(value_name = "FILE", required_unless_present = "list_devices")] - file: Option, + file: Option, /// Port to listen on #[arg(short, long, default_value = "8080")] port: u16, /// Host to bind to #[arg(long, default_value = "127.0.0.1")] - host: String, + host: FreeText, /// Disable CORS #[arg(long)] no_cors: bool, @@ -76,7 +79,7 @@ pub enum ServeCommands { /// Mirrors llama.cpp's `-ngl`, which takes an integer, `auto` or `all` /// and reports what it resolved. Neither comparator has a boolean. #[arg(long, value_name = "N|auto|all|0")] - gpu_layers: Option, + gpu_layers: Option, /// List the accelerators this BUILD can dispatch to, then exit. /// /// Answers "what does this binary actually support" without starting a @@ -91,7 +94,7 @@ pub enum ServeCommands { trace: bool, /// Trace detail level (none, basic, layer) #[arg(long, value_name = "LEVEL", default_value = "basic")] - trace_level: String, + trace_level: FreeText, /// Enable inline Roofline profiling (adds X-Profile headers) #[arg(long)] profile: bool, @@ -107,7 +110,7 @@ pub enum ServeCommands { /// Each request = parent span, each layer = child span with TensorStats. /// Example: --otlp-endpoint http://localhost:4317 #[arg(long, value_name = "URL")] - otlp_endpoint: Option, + otlp_endpoint: Option, /// GH-286: Max context/sequence length for KV cache. Lower = less RSS. #[arg(long, default_value = "4096")] context_length: usize, diff --git a/crates/apr-cli/src/surface.rs b/crates/apr-cli/src/surface.rs new file mode 100644 index 0000000000..931cc9c468 --- /dev/null +++ b/crates/apr-cli/src/surface.rs @@ -0,0 +1,614 @@ +//! `apr surface`: the interface the binary actually has, emitted by the binary +//! (#3745 S1, PMAT-3749). +//! +//! Operator, 2026-09-21, verbatim: "the way to prevent testing leakage is tests +//! are dervived from surface and SHACL enforced...WE ARE NOT ENFORCING our +//! interface AT ALL..it is hand coded not derived". +//! +//! Every release cell used to come from a list someone typed: the ladder's +//! rungs, `{run, chat, serve, code}`, `release_evidence.rs`'s `VERBS`, the +//! hand-maintained `apr-cli-commands-v1.yaml`. A verb, flag or input shape +//! nobody typed was invisible to the gate, and 0.69.0 leaked three that way +//! (`serve` on Qwen3.5 #3571, `code` #3719, `run --prompt P --chat` #3743). +//! +//! This module walks `::command()` and returns every +//! subcommand path and every argument, with the argument's ROLE read from how it +//! is BUILT: +//! +//! * `model` / `prompt` / `input-file` / `other`: the value parser's output type +//! is one of the role types in `batuta_common::cli_roles` (compared by +//! `TypeId`, never by name); +//! * `backend`: the argument belongs to the group the shared [`BackendArg`] +//! flatten creates (`::group_id()`); +//! * `mode`: structural, meaning a flag action or a finite value set; +//! * `unknown`: a free-form value (raw `PathBuf`, or raw `String` without a +//! finite value set) that no role type declares. [`unknown_outside_foreign`] +//! is the marker guard: it must be empty. +//! +//! The JSON (`apr-cli-surface/v1`, agreed on #3745) is produced from the binary +//! under test at gate time and is never committed. [`emit`] is the library entry +//! point that in-process gates call; `apr surface --json` is a thin wrapper +//! over it, and a test pins the two equal. + +use std::any::TypeId; +use std::path::PathBuf; + +use batuta_common::cli_roles::{self, Carrier, Role}; +use clap::{ArgAction, CommandFactory}; +use serde::Serialize; + +use crate::{BackendArg, Cli}; + +/// The schema string consumers refuse to read if it is not one they know. +/// +/// v1.1 (cop ruling on #3745, after S2 found `serve run`'s prompts arrive over +/// HTTP) adds `commands[].generates`. +pub const SCHEMA: &str = "apr-cli-surface/v1.1"; + +/// Stack for the recursive walk. Building and walking the full `apr` clap tree +/// overflows a default test-thread stack (see the note at +/// `commands/tokenize.rs` on FALSIFY-APR-TOK-PAR-004), so [`emit`] always runs +/// it on a thread of its own. +const WALK_STACK: usize = 64 * 1024 * 1024; + +/// The whole surface: one entry per subcommand path. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct Surface { + /// Always [`SCHEMA`]. + pub schema: &'static str, + /// The binary this surface was read from. + pub binary: BinaryId, + /// Every role an argument can carry, in the order the schema lists them. + pub roles: Vec<&'static str>, + /// Arguments the root declares with `global = true`; clap propagates each + /// of them to every command path, and they are not repeated per command. + pub global_args: Vec, + /// Every subcommand path, nested and group nodes included, sorted by path. + pub commands: Vec, +} + +/// Name, version and source revision of the binary under test. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct BinaryId { + pub name: String, + pub version: String, + pub git_sha: String, +} + +/// One subcommand path. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct CommandEntry { + /// The subcommand names from the root, e.g. `["serve", "run"]`. + pub path: Vec, + /// `path` joined with single spaces: the key #3739's CRUX mapping uses. + pub key: String, + /// Other names clap accepts for this subcommand (visible and hidden). + pub aliases: Vec, + /// Absent from `--help`. + pub hidden: bool, + /// Declared by another crate's `Subcommand` type rather than by apr-cli. + pub foreign: bool, + /// True iff the command has no subcommands. + pub leaf: bool, + /// The command generates text from prompts (v1.1): it has a `PromptText` + /// argument, or it carries the `ServesGeneration` marker because its + /// prompts arrive another way (`serve run` over HTTP, `chat` on stdin, + /// `mcp` as JSON-RPC). Read from how the command is built, never its name. + pub generates: bool, + /// Names of the direct subcommands, in declaration order. + pub subcommands: Vec, + /// Arguments declared on this command (not the propagated globals). + pub args: Vec, +} + +/// One argument. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct ArgEntry { + pub id: String, + /// Long name, without dashes. + pub long: Option, + pub short: Option, + /// Every other long name clap accepts (visible and hidden). + pub long_aliases: Vec, + /// Every other short name clap accepts (visible and hidden). + pub short_aliases: Vec, + pub positional: bool, + /// 1-based position of a positional argument. + pub index: Option, + pub required: bool, + pub takes_value: bool, + pub num_args: NumArgs, + /// `flag`, `count`, `enum`, `path`, `text`, `int`, `float` or `other`. + pub value_type: &'static str, + /// The finite set of accepted values (hidden values excluded), else empty. + pub values: Vec, + pub default: Vec, + /// One of [`Surface::roles`]. + pub role: &'static str, + /// The role type that built this argument (`ModelPath`, `BackendArg`, …). + pub marker: Option<&'static str>, + /// For role `sampling` (v1.1): `seed`, `temperature`, `top_k`, `top_p`, + /// `min_p`, `repeat_penalty` or `repeat_last_n`, read from the sampling + /// group the argument joined, never from its name. `null` otherwise. + pub sampling_kind: Option<&'static str>, + pub hidden: bool, + /// Ids of the arguments this one conflicts with. + pub conflicts_with: Vec, +} + +/// How many values one occurrence takes. `max: None` is unbounded. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +pub struct NumArgs { + pub min: usize, + pub max: Option, +} + +impl Surface { + /// The versioned JSON document `apr surface --json` prints. + #[must_use] + pub fn to_json(&self) -> String { + serde_json::to_string_pretty(self).expect("surface is plain data and always serializes") + } + + /// The entry for a subcommand path, e.g. `["serve", "run"]`. + #[must_use] + pub fn command(&self, path: &[&str]) -> Option<&CommandEntry> { + self.commands + .iter() + .find(|c| c.path.iter().map(String::as_str).eq(path.iter().copied())) + } +} + +impl CommandEntry { + /// The argument with this id. + #[must_use] + pub fn arg(&self, id: &str) -> Option<&ArgEntry> { + self.args.iter().find(|a| a.id == id) + } +} + +/// The surface of the `apr` binary this library is linked into. +/// +/// Runs the walk on its own thread with an explicit stack (see [`WALK_STACK`]), +/// so it is safe to call from a test thread or from `main`. +#[must_use] +pub fn emit() -> Surface { + std::thread::Builder::new() + .name("apr-surface".to_string()) + .stack_size(WALK_STACK) + .spawn(|| emit_from(&Cli::command())) + .expect("spawn the surface walk thread") + .join() + .expect("the surface walk panicked") +} + +/// The surface of an arbitrary clap tree rooted at `root`. +/// +/// Membership (which commands and arguments exist) is read from `root` as the +/// code declared it. Finalised attributes (`num_args`, positional indices, +/// conflicts) are read from a BUILT clone. Building also adds clap's generated +/// `help` subcommand and the `--help`/`--version` flags; those are clap's +/// surface, not apr's, so they come from nowhere in the unbuilt tree and are +/// not emitted. +/// +/// Call this on a thread with a large stack when `root` is the full `apr` tree. +#[must_use] +pub fn emit_from(root: &clap::Command) -> Surface { + let mut built = root.clone(); + built.build(); + let foreign = foreign_mounts(root).mounts; + + let global_args = root + .get_arguments() + .filter(|a| a.is_global_set()) + .map(|a| arg_entry(a, &built, root)) + .collect(); + + let mut commands = Vec::new(); + for sub in root.get_subcommands() { + let built_sub = child(&built, sub.get_name()); + walk(sub, built_sub, &mut vec![], &foreign, false, &mut commands); + } + commands.sort_by(|a, b| a.path.cmp(&b.path)); + + Surface { + schema: SCHEMA, + binary: BinaryId { + name: root.get_name().to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + git_sha: env!("APR_GIT_SHA").to_string(), + }, + roles: ROLES.iter().map(|r| r.as_str()).collect(), + global_args, + commands, + } +} + +/// Every role, in schema order. +const ROLES: [Role; 8] = [ + Role::Model, + Role::Prompt, + Role::InputFile, + Role::Backend, + Role::Sampling, + Role::Mode, + Role::Other, + Role::Unknown, +]; + +fn child<'a>(built: &'a clap::Command, name: &str) -> &'a clap::Command { + built + .get_subcommands() + .find(|s| s.get_name() == name) + .expect("a built tree has every subcommand its unbuilt source declares") +} + +fn walk( + cmd: &clap::Command, + built: &clap::Command, + parent: &mut Vec, + foreign: &[Vec], + inherited_foreign: bool, + out: &mut Vec, +) { + let mut path = parent.clone(); + path.push(cmd.get_name().to_string()); + let is_foreign = inherited_foreign || foreign.iter().any(|m| *m == path); + + let subcommands: Vec = cmd + .get_subcommands() + .map(|s| s.get_name().to_string()) + .collect(); + let args: Vec = cmd + .get_arguments() + .filter(|a| !a.is_global_set()) + .map(|a| arg_entry(a, built, cmd)) + .collect(); + out.push(CommandEntry { + key: path.join(" "), + path: path.clone(), + aliases: cmd.get_all_aliases().map(str::to_string).collect(), + hidden: cmd.is_hide_set(), + foreign: is_foreign, + leaf: subcommands.is_empty(), + generates: generates(cmd, &args), + subcommands, + args, + }); + + for sub in cmd.get_subcommands() { + walk( + sub, + child(built, sub.get_name()), + &mut path, + foreign, + is_foreign, + out, + ); + } +} + +fn arg_entry(arg: &clap::Arg, built_cmd: &clap::Command, cmd: &clap::Command) -> ArgEntry { + let built_arg = built_cmd + .get_arguments() + .find(|b| b.get_id() == arg.get_id()) + .expect("a built command keeps every argument its source declares"); + let class = classify(arg, cmd, built_cmd); + let takes_value = built_arg.get_action().takes_values(); + let num_args = built_arg + .get_num_args() + .map(|r| NumArgs { + min: r.min_values(), + max: (r.max_values() != usize::MAX).then_some(r.max_values()), + }) + .unwrap_or(if takes_value { + NumArgs { + min: 1, + max: Some(1), + } + } else { + NumArgs { + min: 0, + max: Some(0), + } + }); + ArgEntry { + id: arg.get_id().to_string(), + long: arg.get_long().map(str::to_string), + short: arg.get_short(), + long_aliases: arg + .get_all_aliases() + .unwrap_or_default() + .into_iter() + .map(str::to_string) + .collect(), + short_aliases: arg.get_all_short_aliases().unwrap_or_default(), + positional: arg.is_positional(), + index: built_arg.get_index(), + required: arg.is_required_set(), + takes_value, + num_args, + value_type: class.value_type, + values: finite_values(arg), + default: arg + .get_default_values() + .iter() + .map(|v| v.to_string_lossy().into_owned()) + .collect(), + role: class.role.as_str(), + marker: class.marker, + sampling_kind: cli_roles::SamplingArg::kind_of(built_cmd, built_arg) + .map(cli_roles::SamplingKind::as_str), + hidden: arg.is_hide_set(), + conflicts_with: built_cmd + .get_arg_conflicts_with(built_arg) + .into_iter() + .map(|c| c.get_id().to_string()) + .collect(), + } +} + +/// True iff the command generates from prompts: a `PromptText` argument, or the +/// command-level `ServesGeneration` marker. Both are read from construction. +fn generates(cmd: &clap::Command, args: &[ArgEntry]) -> bool { + cli_roles::ServesGeneration::is_on(cmd) || args.iter().any(|a| a.role == Role::Prompt.as_str()) +} + +/// An argument's role, the type that declared it, and the shape of its value. +struct Class { + role: Role, + marker: Option<&'static str>, + value_type: &'static str, +} + +/// Decide an argument's role from how it is built. Nothing here reads a name. +fn classify(arg: &clap::Arg, cmd: &clap::Command, built: &clap::Command) -> Class { + let ty = arg.get_value_parser().type_id(); + let finite = !finite_values(arg).is_empty(); + let value_type = match arg.get_action() { + ArgAction::SetTrue | ArgAction::SetFalse => "flag", + ArgAction::Count => "count", + _ if finite => "enum", + _ => scalar_type(arg.get_value_parser()), + }; + + if in_backend_group(arg, cmd) { + return Class { + role: Role::Backend, + marker: Some("BackendArg"), + value_type, + }; + } + // v1.1: membership in the SamplingArg group, which is visible only on the + // BUILT command (clap merges `#[arg(group = …)]` there). + if cli_roles::SamplingArg::kind_of(built, arg).is_some() { + return Class { + role: Role::Sampling, + marker: Some("SamplingArg"), + value_type, + }; + } + if let Some(m) = cli_roles::MARKERS.iter().find(|m| ty == m.type_id()) { + let value_type = match m.carrier { + Carrier::Path => "path", + Carrier::Text => "text", + }; + return Class { + role: m.role, + marker: Some(m.name), + value_type, + }; + } + let role = match value_type { + "flag" | "count" | "enum" => Role::Mode, + "path" | "text" => Role::Unknown, + _ => Role::Other, + }; + Class { + role, + marker: None, + value_type, + } +} + +/// `path`/`text` for the two raw free-form types, `int`/`float` for numbers, +/// `other` for anything else a value parser can yield. +fn scalar_type(parser: &clap::builder::ValueParser) -> &'static str { + let ty = parser.type_id(); + let is = |t: TypeId| ty == t; + if is(TypeId::of::()) { + "path" + } else if is(TypeId::of::()) { + "text" + } else if [ + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + ] + .into_iter() + .any(is) + { + "int" + } else if is(TypeId::of::()) || is(TypeId::of::()) { + "float" + } else { + "other" + } +} + +/// Values the argument's parser declares as its whole domain. clap's bool +/// parser lists `true`/`false`, which is a finite domain and so a mode. +fn finite_values(arg: &clap::Arg) -> Vec { + arg.get_possible_values() + .iter() + .filter(|v| !v.is_hide_set()) + .map(|v| v.get_name().to_string()) + .collect() +} + +/// True iff `arg` is declared through the shared [`BackendArg`] flatten. The +/// group id comes from the type, through `clap::Args`, never from a literal. +fn in_backend_group(arg: &clap::Arg, cmd: &clap::Command) -> bool { + let Some(group) = ::group_id() else { + return false; + }; + cmd.get_groups() + .any(|g| g.get_id() == &group && g.get_args().any(|a| a == arg.get_id())) +} + +/// The subtrees of `root` that another crate's `Subcommand` type declares. +/// +/// apr embeds these CLIs whole (`apr pv …` is `aprender-contracts-cli`'s own +/// enum). Their free-form arguments cannot all carry apr's role types yet, so +/// they are emitted as `foreign: true` and a remaining untyped argument there +/// is role `unknown`, which S2 charges a runs-or-refuses cell and ratchets +/// shrink-only (cop ruling on #3745). +/// +/// A mount is found by TYPE: each listed type renders its own subcommand set, +/// and a subtree of `root` whose children are exactly that set (order ignored) +/// is its mount. +/// The list fails closed both ways. A foreign CLI missing from it has its +/// untyped arguments counted as apr-cli's, which the marker guard refuses. A +/// listed type that is mounted zero times or more than once is reported in +/// [`ForeignScan::problems`], which the marker guard requires to be empty for +/// the real `apr` tree, so a stale entry cannot sit in the list unnoticed. +fn foreign_mounts(root: &clap::Command) -> ForeignScan { + let probes: [(&str, clap::Command); 7] = [ + ( + "aprender_contracts_cli::cli::Commands", + probe::(), + ), + ( + "alimentar::cli::Commands", + probe::(), + ), + ("simular::cli::Commands", probe::()), + ( + "aprender_rag_cli::Commands", + probe::(), + ), + ( + "aprender_zram_cli::Commands", + probe::(), + ), + ("cgp::cli::Commands", probe::()), + // `apr ptx-debug` mounts the former `aprender-ptx-debug` binary's enum (#4062). + ( + "trueno_ptx_debug::cli::Command", + probe::(), + ), + ]; + let mut scan = ForeignScan::default(); + for (ty, p) in &probes { + // A set, not a sequence: declaration order is not identity, and clap's + // own `mut_subcommand` moves the child it mutates to the end. + let mut want: Vec<&str> = p.get_subcommands().map(clap::Command::get_name).collect(); + want.sort_unstable(); + let mut hits = Vec::new(); + find_mounts(root, &mut Vec::new(), &want, &mut hits); + if hits.len() != 1 { + scan.problems.push(format!( + "foreign CLI `{ty}` must be mounted exactly once in the tree, found {} mount(s): {hits:?}", + hits.len() + )); + } + scan.mounts.extend(hits); + } + scan +} + +/// Where the foreign CLIs are mounted, and what is wrong with the list. +#[derive(Debug, Default)] +pub(crate) struct ForeignScan { + /// Path of each mount point. + pub(crate) mounts: Vec>, + /// A listed type mounted zero times, or more than once. + pub(crate) problems: Vec, +} + +/// The foreign-list problems for the real `apr` tree. The marker guard requires +/// this to be empty. +#[must_use] +pub fn foreign_list_problems() -> Vec { + std::thread::Builder::new() + .stack_size(WALK_STACK) + .spawn(|| foreign_mounts(&Cli::command()).problems) + .expect("spawn") + .join() + .expect("join") +} + +fn probe() -> clap::Command { + T::augment_subcommands(clap::Command::new("probe")) +} + +fn find_mounts( + cmd: &clap::Command, + path: &mut Vec, + want: &[&str], + hits: &mut Vec>, +) { + for sub in cmd.get_subcommands() { + path.push(sub.get_name().to_string()); + let mut names: Vec<&str> = sub.get_subcommands().map(clap::Command::get_name).collect(); + names.sort_unstable(); + if !names.is_empty() && names == want { + hits.push(path.clone()); + } + find_mounts(sub, path, want, hits); + path.pop(); + } +} + +/// Every argument outside a foreign subtree whose role is `unknown`, as +/// `" "`. This is the marker guard's reading, and it must be empty: +/// a free-form argument that no role type declares is how a model or prompt +/// argument hides from the cells derived from this surface. +#[must_use] +pub fn unknown_outside_foreign(surface: &Surface) -> Vec { + let mut out: Vec = surface + .global_args + .iter() + .filter(|a| a.role == Role::Unknown.as_str()) + .map(|a| format!(" {}", a.id)) + .collect(); + for c in surface.commands.iter().filter(|c| !c.foreign) { + for a in c.args.iter().filter(|a| a.role == Role::Unknown.as_str()) { + out.push(format!("{} {}", c.key, a.id)); + } + } + out +} + +/// `apr surface`: print [`emit`]'s JSON and nothing else on stdout. +/// +/// Written with `std::io::Write`, not the crate's shadowed `println!`, so +/// `--quiet` cannot suppress the only thing the verb exists to produce. +pub(crate) fn run() -> Result<(), crate::CliError> { + write_surface(&mut std::io::stdout().lock()) + .map_err(|e| crate::CliError::ValidationFailed(format!("apr surface: writing stdout: {e}"))) +} + +/// The bytes `apr surface` writes: [`emit`]'s JSON and one newline. +fn write_surface(out: &mut impl std::io::Write) -> std::io::Result<()> { + out.write_all(emit().to_json().as_bytes())?; + out.write_all(b"\n")?; + out.flush() +} + +#[cfg(test)] +#[path = "surface_tests.rs"] +mod tests; + +#[cfg(test)] +#[path = "surface_guard_tests.rs"] +mod guard; diff --git a/crates/apr-cli/src/surface_guard_tests.rs b/crates/apr-cli/src/surface_guard_tests.rs new file mode 100644 index 0000000000..9e07cb6185 --- /dev/null +++ b/crates/apr-cli/src/surface_guard_tests.rs @@ -0,0 +1,323 @@ +//! #3745 S1.3: the marker guard. +//! +//! Issue #3745 mutant 3, verbatim: "a command that loads a model with a plain +//! `PathBuf` arg instead of `ModelArg` → the marker guard is RED." +//! +//! The guard does not try to detect model loading; apr-cli has no loader funnel +//! to detect it at (about 13 loader APIs across realizar and aprender-core), and +//! a list of loaders would rot like the verb lists #3745 removes. Instead it +//! makes an undeclared role inexpressible: outside a foreign subtree, every +//! free-form argument (raw `PathBuf`, or raw `String` without a finite value +//! set) is RED until it is built through a role type. A model argument can +//! therefore only be written down as a model argument, or as a typed lie that +//! is visible in the diff. + +use super::*; +use clap::Arg; + +fn big_stack(f: impl FnOnce() -> T + Send + 'static) -> T { + std::thread::Builder::new() + .stack_size(WALK_STACK) + .spawn(f) + .expect("spawn") + .join() + .expect("join") +} + +const REMEDY: &str = "declare each through a role type from batuta_common::cli_roles \ + (ModelPath/ModelRef for a model, PromptText for a prompt, InputFile for input data, \ + OutputPath/DirPath/ConfigPath/FreeText otherwise) — a raw PathBuf/String hides the \ + argument's role from the cells derived from `apr surface` (#3745)"; + +/// The guard itself, on the tree this binary ships. +#[test] +fn no_free_form_arg_is_undeclared_outside_foreign_subtrees() { + let unknown = big_stack(|| unknown_outside_foreign(&emit())); + assert!( + unknown.is_empty(), + "{} free-form argument(s) with no declared role:\n {}\n{REMEDY}", + unknown.len(), + unknown.join("\n ") + ); +} + +/// The foreign-CLI list fails closed: every listed type is mounted exactly once. +#[test] +fn the_foreign_list_matches_the_tree() { + let problems = foreign_list_problems(); + assert!(problems.is_empty(), "{}", problems.join("\n")); +} + +/// The verbs whose leaks #3745 names, and the two foreign model arguments the +/// cop ruled into S1, carry the roles their cells are derived from. +#[test] +fn the_leak_verbs_declare_model_prompt_and_input_roles() { + let s = big_stack(emit); + let role = |path: &[&str], id: &str| { + s.command(path) + .and_then(|c| c.arg(id)) + .map(|a| a.role) + .unwrap_or_else(|| panic!("{path:?} {id} is on the surface")) + }; + // #3743: `run --prompt P --chat` vs `-i file --chat`. + assert_eq!(role(&["run"], "source"), "model"); + assert_eq!(role(&["run"], "prompt"), "prompt"); + assert_eq!(role(&["run"], "positional_prompt"), "prompt"); + assert_eq!(role(&["run"], "input"), "input-file"); + assert_eq!(role(&["chat"], "file"), "model"); + // #3571: `apr serve` on Qwen3.5. + assert_eq!(role(&["serve", "run"], "file"), "model"); + // #3719: `apr code`'s backend is `apr serve`. + assert_eq!(role(&["code"], "model"), "model"); + assert_eq!(role(&["code"], "prompt"), "prompt"); + // Cop ruling on #3745: foreign, but typed in S1 itself. + assert_eq!(role(&["pv", "verify-structure"], "model"), "model"); + assert_eq!(role(&["rag", "transcribe"], "model"), "model"); +} + +// ── mutants, planted in the real tree on every run ────────────────────────── + +fn unknown_after( + mutate: impl FnOnce(clap::Command) -> clap::Command + Send + 'static, +) -> Vec { + big_stack(move || unknown_outside_foreign(&emit_from(&mutate(Cli::command())))) +} + +/// Mutant 3a: `inspect`'s model argument rebuilt as a plain `PathBuf`. +#[test] +fn mutant_an_existing_model_arg_reverted_to_plain_pathbuf_is_red() { + let unknown = unknown_after(|root| { + root.mut_subcommand("inspect", |c| { + c.mut_arg("file", |a| a.value_parser(clap::value_parser!(PathBuf))) + }) + }); + assert_eq!( + unknown, + ["inspect file"], + "the guard must name exactly the reverted argument" + ); +} + +/// Mutant 3b: a new command that takes its model through a plain `PathBuf`. +#[test] +fn mutant_a_new_command_with_a_plain_pathbuf_model_is_red() { + let unknown = unknown_after(|root| { + root.subcommand( + clap::Command::new("zz-new-verb").arg( + Arg::new("model") + .long("model") + .value_parser(clap::value_parser!(PathBuf)), + ), + ) + }); + assert_eq!(unknown, ["zz-new-verb model"]); +} + +/// Mutant 3c: a prompt taken as a plain `String` (the #3743 class). +#[test] +fn mutant_a_prompt_as_plain_string_is_red() { + let unknown = unknown_after(|root| { + root.mut_subcommand("run", |c| { + c.mut_arg("prompt", |a| a.value_parser(clap::value_parser!(String))) + }) + }); + assert_eq!(unknown, ["run prompt"]); +} + +/// A raw argument in a nested apr-cli subcommand is caught as well. +#[test] +fn mutant_a_nested_plain_pathbuf_is_red() { + let unknown = unknown_after(|root| { + root.mut_subcommand("serve", |serve| { + serve.mut_subcommand("run", |c| { + c.mut_arg("file", |a| a.value_parser(clap::value_parser!(PathBuf))) + }) + }) + }); + assert_eq!(unknown, ["serve run file"]); +} + +/// A raw global argument is caught: globals reach every command path. +#[test] +fn mutant_a_plain_global_is_red() { + let unknown = unknown_after(|root| { + root.arg( + Arg::new("zz-config") + .long("zz-config") + .global(true) + .value_parser(clap::value_parser!(PathBuf)), + ) + }); + assert_eq!(unknown, [" zz-config"]); +} + +/// Inside a foreign subtree a raw argument is emitted as `unknown` (S2 owes +/// its verb a runs-or-refuses cell and ratchets the count), and it is not the +/// marker guard's to refuse. (Reverting THIS argument is still caught, by +/// `the_leak_verbs_declare_model_prompt_and_input_roles`.) +#[test] +fn a_foreign_raw_arg_is_unknown_not_red() { + let s = big_stack(|| { + emit_from(&Cli::command().mut_subcommand("pv", |pv| { + pv.mut_subcommand("verify-structure", |c| { + c.mut_arg("model", |a| a.value_parser(clap::value_parser!(PathBuf))) + }) + })) + }); + let arg = s + .command(&["pv", "verify-structure"]) + .and_then(|c| c.arg("model")) + .expect("pv verify-structure --model"); + assert_eq!(arg.role, "unknown"); + assert!( + unknown_outside_foreign(&s).is_empty(), + "foreign subtrees are not the marker guard's" + ); +} + +/// v1.1: the generators S2 derives prompt-shape cells for. `serve run`, `chat` +/// and `mcp` take their prompts over HTTP, stdin and JSON-RPC, so only the +/// `ServesGeneration` marker makes them generators; `run` and `code` are by +/// their `PromptText` args. Model tools that never generate are not. +#[test] +fn the_generators_are_declared() { + let s = big_stack(emit); + let generates = |path: &[&str]| { + s.command(path) + .map(|c| c.generates) + .unwrap_or_else(|| panic!("{path:?} is on the surface")) + }; + for path in [ + &["serve", "run"][..], + &["chat"], + &["mcp"], + &["run"], + &["code"], + ] { + assert!(generates(path), "{path:?} must be a generator"); + } + for path in [ + &["inspect"][..], + &["tensors"], + &["serve", "plan"], + &["surface"], + // Encoders and scorers take EncodeText, not PromptText (aprender-97): + // thinking × context-rung cells mean nothing for them. + &["embed"], + &["rerank"], + &["eval"], + // Tokenizes its --prompt and never runs the weights (#3749 rebase). + &["tokenize", "encode"], + ] { + assert!(!generates(path), "{path:?} does not generate"); + } +} + +/// Mutant: `serve run` with its ServesGeneration marker dropped. Its real +/// argument set, rebuilt without the marker, reads `generates: false`. So the +/// marker, not any argument, is what makes it a generator (#3571's verb). +#[test] +fn mutant_serve_run_without_its_marker_does_not_generate() { + let (real, stripped) = big_stack(|| { + let root = Cli::command(); + let run = root + .find_subcommand("serve") + .and_then(|s| s.find_subcommand("run")) + .expect("serve run") + .clone(); + let mut bare = clap::Command::new("run"); + for a in run.get_arguments() { + bare = bare.arg(a.clone()); + } + let real = emit_from(&clap::Command::new("apr").subcommand(run)); + let stripped = emit_from(&clap::Command::new("apr").subcommand(bare)); + (real, stripped) + }); + assert!(real.command(&["run"]).expect("run").generates); + assert!( + !stripped.command(&["run"]).expect("run").generates, + "without the marker serve run must read generates=false — the marker is load-bearing" + ); +} + +/// v1.1 `sampling` (for S2.5's flag-effect sampling controls): the generation +/// sampling controls carry the SamplingArg marker. Numbers with the same names +/// that are not generation sampling (distillation temperature, data seeds, +/// rerank's top-k) are not. +#[test] +fn the_sampling_controls_are_declared() { + let s = big_stack(emit); + let role = |path: &[&str], id: &str| { + s.command(path) + .and_then(|c| c.arg(id)) + .map(|a| a.role) + .unwrap_or_else(|| panic!("{path:?} {id} is on the surface")) + }; + let kind = |path: &[&str], id: &str| { + s.command(path) + .and_then(|c| c.arg(id)) + .and_then(|a| a.sampling_kind) + }; + // `run`'s field ids happen to equal their kinds' spellings; the kind is read + // from the group each field joined, and the case table below the name level + // (`ungrouped_temperature`) shows a name alone yields no kind. + for (id, want) in [ + ("temperature", "temperature"), + ("top_k", "top_k"), + ("top_p", "top_p"), + ("seed", "seed"), + ("repeat_penalty", "repeat_penalty"), + ("repeat_last_n", "repeat_last_n"), + ] { + assert_eq!(role(&["run"], id), "sampling", "run --{id}"); + assert_eq!(kind(&["run"], id), Some(want), "run --{id} sampling_kind"); + } + assert_eq!(kind(&["chat"], "top_p"), Some("top_p")); + assert_eq!( + kind(&["distill"], "temperature"), + None, + "KD temperature has no kind" + ); + assert_eq!(role(&["chat"], "temperature"), "sampling"); + assert_eq!(role(&["chat"], "top_p"), "sampling"); + assert_eq!( + role(&["rosetta", "compare-inference"], "temperature"), + "sampling" + ); + assert_ne!( + role(&["distill"], "temperature"), + "sampling", + "KD temperature" + ); + assert_ne!(role(&["data", "split"], "seed"), "sampling", "a data seed"); + assert_ne!(role(&["rerank"], "top_k"), "sampling", "a ranking cut-off"); +} + +/// Mutant: `run --temperature` with its SamplingArg marker dropped reads as an +/// ordinary number, so `the_sampling_controls_are_declared` would go RED. +#[test] +fn mutant_temperature_without_its_marker_is_not_sampling() { + let s = big_stack(|| { + emit_from(&Cli::command().mut_subcommand("run", |c| { + c.mut_arg("temperature", |a| { + a.group(clap::builder::Resettable::::Reset) + }) + })) + }); + let t = s + .command(&["run"]) + .and_then(|c| c.arg("temperature")) + .expect("run --temperature"); + assert_ne!( + t.role, "sampling", + "without the marker the role must not be sampling" + ); + let k = s + .command(&["run"]) + .and_then(|c| c.arg("top_k")) + .expect("top_k"); + assert_eq!( + k.role, "sampling", + "only the mutated argument loses its role" + ); +} diff --git a/crates/apr-cli/src/surface_tests.rs b/crates/apr-cli/src/surface_tests.rs new file mode 100644 index 0000000000..c4d7e9f5dc --- /dev/null +++ b/crates/apr-cli/src/surface_tests.rs @@ -0,0 +1,390 @@ +//! #3745 S1: the surface emitter, read back. + +use super::*; +use clap::{Arg, ArgAction}; + +/// Run `f` on a thread with the walk's stack. The full `apr` tree does not fit +/// a default test-thread stack. +fn big_stack(f: impl FnOnce() -> T + Send + 'static) -> T { + std::thread::Builder::new() + .stack_size(WALK_STACK) + .spawn(f) + .expect("spawn") + .join() + .expect("join") +} + +/// S1.1: a flag added to the clap tree appears in the JSON, under its command +/// path, with a role. The emitter reads the tree it is given; it keeps no list +/// that a new flag could be missing from. +#[test] +fn the_emitter_sees_a_flag_added_to_the_clap_tree() { + let (before, after) = big_stack(|| { + let root = Cli::command(); + let before = emit_from(&root); + let mutated = root.mut_subcommand("run", |run| { + run.arg( + Arg::new("zz-dummy") + .long("zz-dummy") + .action(ArgAction::SetTrue), + ) + }); + (before, emit_from(&mutated)) + }); + let run_before = before.command(&["run"]).expect("run is on the surface"); + assert!( + run_before.arg("zz-dummy").is_none(), + "the dummy flag must be absent before it is added" + ); + + let run = after.command(&["run"]).expect("run is on the surface"); + let dummy = run + .arg("zz-dummy") + .expect("a flag added to the clap tree must appear in the surface"); + assert_eq!(dummy.long.as_deref(), Some("zz-dummy")); + assert_eq!(dummy.value_type, "flag"); + assert_eq!(dummy.role, "mode"); + let json = after.to_json(); + assert!( + json.contains("\"zz-dummy\""), + "the dummy flag must be in the JSON text too" + ); +} + +#[test] +fn the_surface_is_versioned_and_deterministic() { + let (a, b) = big_stack(|| (emit().to_json(), emit().to_json())); + assert_eq!(a, b, "two emissions from one binary must be byte-identical"); + let v: serde_json::Value = serde_json::from_str(&a).expect("the surface is JSON"); + assert_eq!(v["schema"], SCHEMA); + assert_eq!(v["schema"], "apr-cli-surface/v1.1"); + assert_eq!(v["binary"]["name"], "apr"); + let roles: Vec<&str> = v["roles"] + .as_array() + .expect("roles") + .iter() + .filter_map(|r| r.as_str()) + .collect(); + assert_eq!( + roles, + [ + "model", + "prompt", + "input-file", + "backend", + "sampling", + "mode", + "other", + "unknown" + ] + ); + let paths: Vec> = v["commands"] + .as_array() + .expect("commands") + .iter() + .map(|c| serde_json::from_value(c["path"].clone()).expect("path is a string array")) + .collect(); + let mut sorted = paths.clone(); + sorted.sort(); + assert_eq!(paths, sorted, "commands are sorted by path"); +} + +/// clap's generated `help` subcommand and `--help`/`--version` flags are not +/// apr's declared surface; emitting them would owe a release cell per group. +#[test] +fn clap_generated_help_and_version_are_not_emitted() { + let s = big_stack(emit); + // `help` is never an apr-declared id. `version` can be (`data x registry + // delete --version` names a registry entry's version), so the generated + // `--version` is checked where apr declares no such flag: the root and `run`. + for c in &s.commands { + assert!( + c.args.iter().all(|a| a.id != "help"), + "`{} --help` is clap-generated", + c.key + ); + } + assert!(s + .global_args + .iter() + .all(|a| a.id != "help" && a.id != "version")); + assert!( + s.command(&["run"]).expect("run").arg("version").is_none(), + "propagated --version" + ); + // `serve` has subcommands and does not disable clap's help subcommand. + let serve = s.command(&["serve"]).expect("serve"); + assert!(!serve.leaf); + assert!( + s.command(&["serve", "help"]).is_none(), + "clap's generated `serve help` must not be emitted" + ); +} + +#[test] +fn globals_are_emitted_once_at_the_root() { + let s = big_stack(emit); + let ids: Vec<&str> = s.global_args.iter().map(|a| a.id.as_str()).collect(); + assert!( + ids.contains(&"json") && ids.contains(&"quiet") && ids.contains(&"verbose"), + "{ids:?}" + ); + let run = s.command(&["run"]).expect("run"); + assert!( + run.arg("json").is_none(), + "a propagated global is not repeated per command" + ); +} + +#[test] +fn the_backend_role_comes_from_the_backend_arg_group() { + let s = big_stack(emit); + for path in [&["run"][..], &["chat"][..], &["serve", "run"][..]] { + let c = s + .command(path) + .unwrap_or_else(|| panic!("{path:?} is on the surface")); + let b = c + .arg("backend") + .unwrap_or_else(|| panic!("{path:?} has --backend")); + assert_eq!(b.role, "backend", "{path:?} --backend"); + assert_eq!(b.marker, Some("BackendArg")); + assert_eq!( + b.values, + crate::BACKEND_VALUES.to_vec(), + "{path:?} --backend values" + ); + } +} + +/// The `surface` verb itself is hidden from `--help` but present on the surface, +/// so S3 derives it as a verb and S2 owes it a runs cell. +#[test] +fn the_surface_verb_is_hidden_and_listed() { + let s = big_stack(emit); + let c = s.command(&["surface"]).expect("surface lists itself"); + assert!(c.hidden); + assert!(c.leaf); + assert!(!c.foreign); +} + +/// Every foreign CLI the walker lists is found mounted exactly once, and every +/// command under a mount is marked foreign (the mount point included). +#[test] +fn foreign_mounts_are_found_by_type() { + let s = big_stack(emit); + for mount in [ + &["pv"][..], + &["data", "x"][..], + &["sim"][..], + &["rag"][..], + &["zram"][..], + &["cgp"][..], + ] { + let c = s + .command(mount) + .unwrap_or_else(|| panic!("{mount:?} is on the surface")); + assert!(c.foreign, "{mount:?} is a foreign mount"); + for sub in &c.subcommands { + let mut p: Vec<&str> = mount.to_vec(); + p.push(sub); + assert!( + s.command(&p).is_some_and(|e| e.foreign), + "{p:?} is under a foreign mount" + ); + } + } + let data = s.command(&["data"]).expect("data"); + assert!( + !data.foreign, + "`data` is apr-cli's own; only its `x` arm is alimentar's" + ); + assert!(!s.command(&["run"]).expect("run").foreign); +} + +// ── classification case table ──────────────────────────────────────────────── + +/// One row per way an argument can be built. `expect` is (value_type, role, +/// marker). The table is the specification of `classify`: a class of argument +/// with no row here is a class nobody decided. +#[test] +fn classify_case_table() { + use batuta_common::cli_roles::{ + ConfigPath, DirPath, EncodeText, FreeText, InputFile, ModelPath, ModelRef, OutputPath, + PromptText, + }; + #[derive(clap::Parser)] + #[command(groups(batuta_common::cli_roles::SamplingArg::groups()))] + struct Probe { + // v1.1: a sampling control, by group membership; its twin with no group + // is an ordinary number. + #[arg(long, group = batuta_common::cli_roles::SamplingKind::Temperature.id())] + temperature: Option, + #[arg(long, group = batuta_common::cli_roles::SamplingKind::TopK.id())] + top_k: Option, + #[arg(long)] + ungrouped_temperature: Option, + model_path: ModelPath, + #[arg(long)] + model_ref: Option, + #[arg(long)] + prompt: Option, + #[arg(long)] + input: Option, + #[arg(long)] + output: Option, + #[arg(long)] + dir: Option, + #[arg(long)] + config: Option, + #[arg(long)] + name: Option, + #[arg(long)] + encoded: Option, + #[arg(long)] + raw_path: Option, + #[arg(long)] + raw_text: Option, + #[arg(long, value_parser = ["a", "b"])] + listed: Option, + #[arg(long)] + flag: bool, + #[arg(long, action = ArgAction::Count)] + level: u8, + #[arg(long)] + n: Option, + #[arg(long)] + t: Option, + #[arg(long, value_enum)] + fmt: Option, + #[arg(long)] + c: Option, + #[command(flatten)] + backend: BackendArg, + } + let rows: &[(&str, (&str, &str, Option<&str>))] = &[ + ("model_path", ("path", "model", Some("ModelPath"))), + ("model_ref", ("text", "model", Some("ModelRef"))), + ("prompt", ("text", "prompt", Some("PromptText"))), + ("input", ("path", "input-file", Some("InputFile"))), + ("output", ("path", "other", Some("OutputPath"))), + ("dir", ("path", "other", Some("DirPath"))), + ("config", ("path", "other", Some("ConfigPath"))), + ("name", ("text", "other", Some("FreeText"))), + // Encoder/scorer input: a model input, but not a prompt (v1.1). + ("encoded", ("text", "other", Some("EncodeText"))), + // The two rows the marker guard exists for. + ("raw_path", ("path", "unknown", None)), + ("raw_text", ("text", "unknown", None)), + ("listed", ("enum", "mode", None)), + ("flag", ("flag", "mode", None)), + ("level", ("count", "mode", None)), + ("n", ("int", "other", None)), + ("t", ("float", "other", None)), + ("fmt", ("enum", "mode", None)), + ("c", ("other", "other", None)), + ("backend", ("enum", "backend", Some("BackendArg"))), + ("temperature", ("float", "sampling", Some("SamplingArg"))), + ("top_k", ("int", "sampling", Some("SamplingArg"))), + // The name says "temperature"; without the group it is not sampling. + ("ungrouped_temperature", ("float", "other", None)), + ]; + let cmd = ::command(); + let root = clap::Command::new("apr").subcommand(cmd.name("probe")); + let s = emit_from(&root); + let probe = s.command(&["probe"]).expect("probe"); + assert_eq!( + probe.args.len(), + rows.len(), + "every arg of the probe has a row" + ); + for (id, (value_type, role, marker)) in rows { + let a = probe.arg(id).unwrap_or_else(|| panic!("probe has {id}")); + assert_eq!( + (a.value_type, a.role, a.marker), + (*value_type, *role, *marker), + "row {id}" + ); + } +} + +/// The verb writes [`emit`]'s document and nothing else. The binary-level pin +/// (the spawned `apr surface --json` equals `emit()`) is the integration test +/// `tests/surface_binary_pin.rs`. +#[test] +fn the_verb_writes_exactly_what_emit_returns() { + let (written, emitted) = big_stack(|| { + let mut buf = Vec::new(); + write_surface(&mut buf).expect("write to a Vec"); + (buf, emit().to_json()) + }); + assert_eq!(written, format!("{emitted}\n").into_bytes()); +} + +/// `apr surface` parses to the hidden verb, with and without `--json`/`--quiet`. +#[test] +fn apr_surface_parses_to_the_surface_verb() { + use clap::Parser; + for argv in [ + &["apr", "surface"][..], + &["apr", "surface", "--json"][..], + &["apr", "surface", "--quiet"][..], + ] { + let cli = big_stack({ + let argv: Vec = argv.iter().map(|s| s.to_string()).collect(); + move || { + Cli::try_parse_from(argv).map(|c| matches!(*c.command, crate::Commands::Surface)) + } + }); + assert_eq!( + cli.ok(), + Some(true), + "{argv:?} must parse to Commands::Surface" + ); + } +} + +/// v1.1 `generates`: one row per way a command can be built. A command +/// generates iff it has a `PromptText` argument or carries the +/// `ServesGeneration` marker; a name — the arg's or the group's — never counts. +#[test] +fn generates_case_table() { + use batuta_common::cli_roles::{ModelPath, PromptText, ServesGeneration}; + let model = || Arg::new("model").value_parser(clap::value_parser!(ModelPath)); + let prompt_arg = + |p: clap::builder::ValueParser| Arg::new("prompt").long("prompt").value_parser(p); + let root = clap::Command::new("apr") + .subcommand( + clap::Command::new("with-prompt") + .arg(model()) + .arg(prompt_arg(clap::value_parser!(PromptText).into())), + ) + .subcommand( + clap::Command::new("marked") + .arg(model()) + .group(ServesGeneration::group()), + ) + .subcommand(clap::Command::new("model-only").arg(model())) + .subcommand( + clap::Command::new("lookalike-group") + .arg(model()) + .group(clap::ArgGroup::new("ServesGeneration").multiple(true)), + ) + .subcommand( + clap::Command::new("raw-prompt-name") + .arg(model()) + .arg(prompt_arg(clap::value_parser!(String))), + ); + let s = emit_from(&root); + let rows = [ + ("with-prompt", true), + ("marked", true), + ("model-only", false), + ("lookalike-group", false), + ("raw-prompt-name", false), + ]; + assert_eq!(s.commands.len(), rows.len(), "every command has a row"); + for (name, want) in rows { + let c = s.command(&[name]).unwrap_or_else(|| panic!("{name}")); + assert_eq!(c.generates, want, "row {name}"); + } +} diff --git a/crates/apr-cli/src/tokenize_commands.rs b/crates/apr-cli/src/tokenize_commands.rs index 08cf198b51..587749661c 100644 --- a/crates/apr-cli/src/tokenize_commands.rs +++ b/crates/apr-cli/src/tokenize_commands.rs @@ -14,19 +14,19 @@ pub enum TokenizeCommands { Plan { /// Path to training corpus (text file, one document per line) #[arg(long, value_name = "FILE")] - data: PathBuf, + data: InputFile, /// Target vocabulary size #[arg(long, default_value = "32000")] vocab_size: usize, /// Tokenizer algorithm: bpe, wordpiece, unigram #[arg(long, default_value = "bpe")] - algorithm: String, + algorithm: FreeText, /// Output directory for trained tokenizer #[arg(short, long, default_value = "./tokenizer-output")] - output: PathBuf, + output: OutputPath, /// Output format: text, json, yaml #[arg(long, default_value = "text")] - format: String, + format: FreeText, }, /// Train a tokenizer on the corpus. @@ -38,16 +38,16 @@ pub enum TokenizeCommands { Apply { /// Path to training corpus (text file, one document per line) #[arg(long, value_name = "FILE")] - data: PathBuf, + data: InputFile, /// Target vocabulary size #[arg(long, default_value = "32000")] vocab_size: usize, /// Tokenizer algorithm: bpe, wordpiece, unigram #[arg(long, default_value = "bpe")] - algorithm: String, + algorithm: FreeText, /// Output directory for trained tokenizer #[arg(short, long, default_value = "./tokenizer-output")] - output: PathBuf, + output: OutputPath, /// Maximum number of lines to read from corpus (0 = all) #[arg(long, default_value = "0")] max_lines: usize, @@ -64,7 +64,7 @@ pub enum TokenizeCommands { /// Path to corpus: a `.jsonl` file or a directory containing `.jsonl` files. /// Each line must be a JSON object with a `content` field. #[arg(long, value_name = "PATH")] - corpus: PathBuf, + corpus: InputFile, /// Target vocabulary size. Default 50_257 matches GPT-2 convention /// (50_000 BPE merges + 256 byte-level fallback tokens + 1 sentinel) /// and the MODEL-2 albor tokenizer contract (tokenizer-bpe-v1 v1.2.0). @@ -78,10 +78,10 @@ pub enum TokenizeCommands { min_frequency: usize, /// Output directory; will contain vocab.json and merges.txt. #[arg(long, default_value = "./tokenizer-output")] - output: PathBuf, + output: OutputPath, /// Unicode normalization form applied to each document before training. #[arg(long, default_value = "nfc")] - normalization: String, + normalization: FreeText, }, /// Import a HuggingFace tokenizer.json into aprender's two-file @@ -104,10 +104,10 @@ pub enum TokenizeCommands { ImportHf { /// Path to input HuggingFace tokenizer.json (BPE model required). #[arg(long, value_name = "FILE")] - input: PathBuf, + input: InputFile, /// Output directory; will contain vocab.json + merges.txt + manifest.json. #[arg(long, value_name = "DIR")] - output: PathBuf, + output: OutputPath, /// Include `added_tokens` in vocab.json (default: BPE state machine only). /// Use this when the downstream consumer needs special tokens (e.g., /// `<|im_start|>`, `<|endoftext|>`) materialized in vocab.json itself. @@ -125,13 +125,13 @@ pub enum TokenizeCommands { Encode { /// GGUF model file whose tokenizer to run #[arg(value_name = "MODEL")] - model: PathBuf, + model: ModelPath, /// Text to encode #[arg(short = 'p', long, value_name = "TEXT", conflicts_with = "file")] - prompt: Option, + prompt: Option, /// File whose contents to encode (read as UTF-8, verbatim) #[arg(short = 'f', long, value_name = "FILE")] - file: Option, + file: Option, }, /// Encode a JSONL corpus into `.bin` shards per contracts/pretokenize-bin-v1.yaml. /// @@ -154,25 +154,25 @@ pub enum TokenizeCommands { /// are encoded in command-line order and shard numbering is /// continuous across sources. #[arg(long, value_name = "PATH", required = true)] - corpus: Vec, + corpus: Vec, /// Directory containing vocab.json + merges.txt from `apr tokenize train`. #[arg(long, value_name = "DIR")] - tokenizer: PathBuf, + tokenizer: InputFile, /// Output directory for shard-NNNN.bin + manifest.json. #[arg(long, value_name = "DIR")] - output: PathBuf, + output: OutputPath, /// Target tokens per shard (shard closes once this limit is reached). #[arg(long, default_value = "10000000")] shard_tokens: usize, /// JSONL field to encode (default: `content`). #[arg(long, default_value = "content")] - content_field: String, + content_field: FreeText, /// Unicode normalization (must match tokenizer training). #[arg(long, default_value = "nfc")] - normalization: String, + normalization: FreeText, /// EOS insertion policy: none|between|after. #[arg(long, default_value = "between")] - eos_policy: String, + eos_policy: FreeText, /// Number of rayon workers for per-document BPE encoding. /// /// Defaults to `std::thread::available_parallelism()` (logical CPU count). @@ -240,13 +240,13 @@ pub enum TokenizeCommands { /// Output directory containing shard-NNNN.bin files. /// `manifest.json` will be written into this directory. #[arg(long, value_name = "DIR")] - output: PathBuf, + output: OutputPath, /// Optional tokenizer directory; when provided, `vocab.json` /// is read for the manifest's `vocab_size` field. Without it, /// `vocab_size` is recorded as `null` (provenance-incomplete /// but otherwise valid). #[arg(long, value_name = "DIR")] - tokenizer: Option, + tokenizer: Option, /// Emit the manifest body as JSON to stdout (in addition to /// writing to disk). #[arg(long, default_value_t = false)] diff --git a/crates/apr-cli/src/tool_commands.rs b/crates/apr-cli/src/tool_commands.rs index e18ff68307..11ce2264b9 100644 --- a/crates/apr-cli/src/tool_commands.rs +++ b/crates/apr-cli/src/tool_commands.rs @@ -8,16 +8,16 @@ pub enum ToolCommands { auto_verify: bool, /// Run specific step #[arg(long)] - step: Option, + step: Option, /// Model tier: tiny (0.5B), small (1.5B), medium (7B), large (32B) #[arg(long, default_value = "small")] - tier: String, + tier: FreeText, /// Model directory #[arg(long, default_value = "./models")] - model_dir: PathBuf, + model_dir: DirPath, /// Baselines to compare: llama-cpp,ollama #[arg(long, default_value = "llama-cpp,ollama")] - baseline: String, + baseline: FreeText, /// Enable ZRAM compression #[arg(long)] zram: bool, @@ -46,28 +46,28 @@ pub enum ToolCommands { Publish { /// Directory containing model files to publish #[arg(value_name = "DIRECTORY")] - directory: PathBuf, + directory: DirPath, /// HuggingFace repository ID (e.g., paiml/whisper-apr-tiny) #[arg(value_name = "REPO_ID")] - repo_id: String, + repo_id: FreeText, /// Model display name #[arg(long)] - model_name: Option, + model_name: Option, /// License (SPDX identifier, default: mit) #[arg(long, default_value = "mit")] - license: String, + license: FreeText, /// Pipeline tag (e.g., automatic-speech-recognition, text-generation) #[arg(long, default_value = "text-generation")] - pipeline_tag: String, + pipeline_tag: FreeText, /// Library name (e.g., whisper-apr, aprender) #[arg(long)] - library_name: Option, + library_name: Option, /// Additional tags (comma-separated) #[arg(long, value_delimiter = ',')] - tags: Option>, + tags: Option>, /// Commit message #[arg(long)] - message: Option, + message: Option, /// Dry run (preview without uploading) #[arg(long)] dry_run: bool, @@ -84,13 +84,13 @@ pub enum ToolCommands { Oracle { /// Model file path or hf:// URI #[arg(value_name = "SOURCE")] - source: Option, + source: Option, /// Show contract for a model family (e.g., qwen2, llama, whisper, bert) #[arg(long)] - family: Option, + family: Option, /// Filter to a specific size variant (e.g., 0.5b, 7b) #[arg(long)] - size: Option, + size: Option, /// Run full contract compliance check #[arg(long)] compliance: bool, @@ -121,15 +121,15 @@ pub enum ToolCommands { Encrypt { /// Path to model file (safetensors, GGUF, etc.) #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Output path for encrypted file #[arg(short, long, value_name = "FILE")] - output: PathBuf, + output: OutputPath, /// Encryption key file (32 bytes). If omitted, reads passphrase from stdin. #[arg(long, value_name = "FILE")] - key_file: Option, + key_file: Option, /// Force overwrite if output file exists #[arg(long)] @@ -140,15 +140,15 @@ pub enum ToolCommands { Decrypt { /// Path to encrypted `.enc` file #[arg(value_name = "FILE")] - file: PathBuf, + file: InputFile, /// Output path for decrypted model #[arg(short, long, value_name = "FILE")] - output: PathBuf, + output: OutputPath, /// Decryption key file (32 bytes). If omitted, reads passphrase from stdin. #[arg(long, value_name = "FILE")] - key_file: Option, + key_file: Option, /// Force overwrite if output file exists #[arg(long)] diff --git a/crates/apr-cli/src/train_commands.rs b/crates/apr-cli/src/train_commands.rs index d32063a009..3ffda0bcc6 100644 --- a/crates/apr-cli/src/train_commands.rs +++ b/crates/apr-cli/src/train_commands.rs @@ -14,29 +14,29 @@ pub enum TrainCommands { Plan { /// Path to training data (JSONL). Only read by `apr finetune --task classify`. #[arg(long, value_name = "FILE")] - data: Option, + data: Option, /// Model size: "0.5B", "9B", "7B", "13B" #[arg(long, default_value = "0.5B")] - model_size: String, + model_size: FreeText, /// Path to model weights directory #[arg(long, value_name = "DIR")] - model_path: Option, + model_path: Option, /// Number of output classes #[arg(long, default_value = "5")] num_classes: usize, /// Task type: pretrain (causal LM). Classification fine-tuning is /// `apr finetune --task classify`, not this command. #[arg(long, default_value = "pretrain")] - task: String, + task: FreeText, /// YAML training config (for --task pretrain) #[arg(long, value_name = "FILE")] - config: Option, + config: Option, /// Output directory for checkpoints #[arg(short, long, default_value = "/tmp/training-output")] - output: PathBuf, + output: OutputPath, /// HPO strategy: tpe, grid, random, manual #[arg(long, default_value = "tpe")] - strategy: String, + strategy: FreeText, /// HPO budget (number of trials) #[arg(long, default_value = "20")] budget: usize, @@ -57,13 +57,13 @@ pub enum TrainCommands { batch_size: Option, /// Validation data file (JSONL) #[arg(long, value_name = "FILE")] - val_data: Option, + val_data: Option, /// Test data file (JSONL) #[arg(long, value_name = "FILE")] - test_data: Option, + test_data: Option, /// Output format: text, json, yaml #[arg(long, default_value = "text")] - format: String, + format: FreeText, }, /// Execute a training plan (allocate GPU, run trials). @@ -76,27 +76,27 @@ pub enum TrainCommands { Apply { /// Path to a saved plan file (YAML or JSON from `apr train plan`) #[arg(long, value_name = "FILE")] - plan: Option, + plan: Option, /// YAML training config (for --task pretrain) #[arg(long, value_name = "FILE")] - config: Option, + config: Option, /// Task type: pretrain (causal LM). Classification fine-tuning is /// `apr finetune --task classify`, not this command. #[arg(long, default_value = "pretrain")] - task: String, + task: FreeText, // ── Inline plan params (used when no --plan file is given) ───── /// Path to training data (JSONL) #[arg(long, value_name = "FILE")] - data: Option, + data: Option, /// Model size: "0.5B", "9B", "7B", "13B" #[arg(long, default_value = "0.5B")] - model_size: String, + model_size: FreeText, /// Path to model weights directory #[arg(long, value_name = "DIR")] - model_path: Option, + model_path: Option, /// Number of output classes #[arg(long, default_value = "5")] num_classes: usize, @@ -106,10 +106,10 @@ pub enum TrainCommands { /// When omitted, the config's `training.output_dir` is used (default /// `./checkpoints`). The directory is created if it does not exist. #[arg(short, long, value_name = "DIR")] - output: Option, + output: Option, /// HPO strategy: tpe, grid, random, manual #[arg(long, default_value = "tpe")] - strategy: String, + strategy: FreeText, /// HPO budget (number of trials) #[arg(long, default_value = "20")] budget: usize, @@ -141,7 +141,7 @@ pub enum TrainCommands { rank: Option, /// Coordinator address for distributed training (default: 0.0.0.0:9000) #[arg(long, value_name = "HOST:PORT")] - coordinator_addr: Option, + coordinator_addr: Option, // ── Reproducibility params (R-084 C-DETERM-001) ── /// Enable bitwise deterministic training (CUBLAS_WORKSPACE_CONFIG, cuDNN deterministic) @@ -172,7 +172,7 @@ pub enum TrainCommands { Watch { /// YAML training config to run and watch #[arg(long, value_name = "FILE")] - config: PathBuf, + config: ConfigPath, /// Maximum number of restart attempts #[arg(long, default_value = "5")] @@ -201,11 +201,11 @@ pub enum TrainCommands { Sweep { /// Base YAML training config to sweep from #[arg(long, value_name = "FILE")] - config: PathBuf, + config: ConfigPath, /// Search strategy: grid or random #[arg(long, default_value = "random")] - strategy: String, + strategy: FreeText, /// Number of configs to generate (random) or max combinations (grid) #[arg(long, default_value = "10")] @@ -213,7 +213,7 @@ pub enum TrainCommands { /// Output directory for generated configs #[arg(long, default_value = "sweeps/")] - output_dir: PathBuf, + output_dir: OutputPath, /// Seed for random search reproducibility #[arg(long, default_value = "42")] @@ -232,7 +232,7 @@ pub enum TrainCommands { Halving { /// Directory containing sweep-*.yaml configs (from `apr train sweep`) #[arg(long, value_name = "DIR")] - sweep_dir: PathBuf, + sweep_dir: DirPath, /// Number of halving rounds (default: 3) #[arg(long, default_value = "3")] @@ -252,7 +252,7 @@ pub enum TrainCommands { /// Output JSON file for results #[arg(long, default_value = "sweeps/hpo-results.json")] - output: PathBuf, + output: OutputPath, }, /// Archive a checkpoint into a release bundle. @@ -262,19 +262,19 @@ pub enum TrainCommands { Archive { /// Path to checkpoint directory #[arg(value_name = "CHECKPOINT_DIR")] - checkpoint_dir: PathBuf, + checkpoint_dir: ModelPath, /// Output archive directory #[arg(short, long, value_name = "DIR")] - output: PathBuf, + output: OutputPath, /// Release version tag (e.g., "v1.0") #[arg(long = "release-version")] - release_version: Option, + release_version: Option, /// Release notes #[arg(long)] - notes: Option, + notes: Option, }, /// Submit multi-adapter training jobs to a cluster (GPU-SHARE Phase 3). @@ -284,15 +284,15 @@ pub enum TrainCommands { Submit { /// Path to cluster config YAML #[arg(long, value_name = "FILE")] - cluster: PathBuf, + cluster: ConfigPath, /// Model checkpoint path (.apr) #[arg(long, value_name = "FILE")] - model: PathBuf, + model: ModelPath, /// Adapter specs: DATA:CHECKPOINT pairs (one per adapter) #[arg(long = "adapter", value_name = "DATA:CHECKPOINT")] - adapters: Vec, + adapters: Vec, /// LoRA rank #[arg(long, default_value = "16")] @@ -318,6 +318,6 @@ pub enum TrainCommands { ClusterStatus { /// Path to cluster config YAML #[arg(long, value_name = "FILE")] - cluster: PathBuf, + cluster: ConfigPath, }, } diff --git a/crates/apr-cli/src/validate.rs b/crates/apr-cli/src/validate.rs index 3651c634dd..586a17c28a 100644 --- a/crates/apr-cli/src/validate.rs +++ b/crates/apr-cli/src/validate.rs @@ -11,7 +11,7 @@ fn extract_extended_model_paths(command: &ExtendedCommands) -> Vec { // active subcommand variant. ExtendedCommands::Test { command: probar_sub } => match probar_sub { TestSubcommand::Tensor { file, .. } => { - vec![file.clone()] + vec![file.to_path_buf()] }, // `llm` measures an HTTP endpoint, not a local model file. Listed // explicitly so a future subcommand that DOES name a file has to @@ -22,19 +22,19 @@ fn extract_extended_model_paths(command: &ExtendedCommands) -> Vec { | ExtendedCommands::Chat { file, .. } | ExtendedCommands::Bench { file, .. } | ExtendedCommands::Eval { file, .. } - | ExtendedCommands::Profile { file, .. } => vec![file.clone()], + | ExtendedCommands::Profile { file, .. } => vec![file.to_path_buf()], - ExtendedCommands::Cbtop { model_path, .. } => model_path.iter().cloned().collect(), + ExtendedCommands::Cbtop { model_path, .. } => model_path.iter().map(|p| p.to_path_buf()).collect(), // Rosetta action subcommands ExtendedCommands::Tools(ToolCommands::Rosetta { action }) => match action { RosettaCommands::Convert { source, .. } | RosettaCommands::Chain { source, .. } - | RosettaCommands::Verify { source, .. } => vec![source.clone()], + | RosettaCommands::Verify { source, .. } => vec![source.to_path_buf()], RosettaCommands::CompareInference { model_a, model_b, .. } => { - vec![model_a.clone(), model_b.clone()] + vec![model_a.to_path_buf(), model_b.to_path_buf()] } // Diagnostic rosetta commands — exempt _ => vec![], @@ -64,7 +64,7 @@ fn extract_model_paths(command: &Commands) -> Vec { } } Commands::Export { plan: true, .. } => vec![], - Commands::Export { file, .. } => file.iter().cloned().collect(), + Commands::Export { file, .. } => file.iter().map(|p| p.to_path_buf()).collect(), // GH-471: Serve is exempt from pre-dispatch contract validation. // The model loader itself fails fast on corrupt files. // Contract gate was loading 17 GB+ models just to check tensor counts — @@ -72,22 +72,22 @@ fn extract_model_paths(command: &Commands) -> Vec { Commands::Serve { .. } => vec![], Commands::Trace { file, .. } | Commands::Convert { file, .. } - | Commands::Check { file, .. } => vec![file.clone()], + | Commands::Check { file, .. } => vec![file.to_path_buf()], Commands::Merge { plan: true, .. } => vec![], - Commands::Merge { files, .. } => files.clone(), + Commands::Merge { files, .. } => files.iter().map(|p| p.to_path_buf()).collect(), - Commands::Quantize { file, .. } => vec![file.clone()], - Commands::ModelOps(ModelOpsCommands::Prune { file, .. }) => vec![file.clone()], + Commands::Quantize { file, .. } => vec![file.to_path_buf()], + Commands::ModelOps(ModelOpsCommands::Prune { file, .. }) => vec![file.to_path_buf()], Commands::ModelOps(ModelOpsCommands::Distill { teacher, .. }) => { - teacher.iter().cloned().collect() + teacher.iter().map(|p| p.to_path_buf()).collect() } #[cfg(feature = "training")] Commands::ModelOps(ModelOpsCommands::Finetune { file, .. }) => { - file.iter().cloned().collect() + file.iter().map(|p| p.to_path_buf()).collect() } - Commands::Tui { file, .. } => file.iter().cloned().collect(), + Commands::Tui { file, .. } => file.iter().map(|p| p.to_path_buf()).collect(), Commands::Import { source, .. } => { let path = PathBuf::from(source); if path.exists() { diff --git a/crates/apr-cli/tests/beat_apr_sibling_cli_reach.rs b/crates/apr-cli/tests/beat_apr_sibling_cli_reach.rs index 85cff5d78e..4bbfed7cfc 100644 --- a/crates/apr-cli/tests/beat_apr_sibling_cli_reach.rs +++ b/crates/apr-cli/tests/beat_apr_sibling_cli_reach.rs @@ -223,7 +223,7 @@ fn apr_zram_benchmark_runs_instead_of_panicking() { ); } -// A cross-binary byte-identity check (`apr rag query` vs `trueno-rag query`) +// A cross-binary byte-identity check (`apr rag query` vs `aprender-rag query`) // belongs here in spirit, but `CARGO_BIN_EXE_*` only exposes the binaries of // THIS package, so writing it from apr-cli yields an apr-vs-apr comparison -- // an oracle that agrees with itself by construction. The structural guarantee @@ -263,17 +263,18 @@ fn every_simular_command_is_reachable_through_apr_sim() { #[test] fn apr_sim_validates_a_real_emc_file() { - let emc = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../aprender-simulate/docs/emc/ml/linear_regression.emc.yaml" - ); - assert!( - std::path::Path::new(emc).is_file(), - "fixture moved: {emc} -- fix the path rather than weakening the test" - ); + // A sibling crate's file: in tree a moved fixture FAILS (fix the path rather than weakening + // the test); out of tree (the published tarball) the test skips by name (#4149). + let Some(emc) = provable_contracts::workspace_path_or_skip!( + "apr_sim_validates_a_real_emc_file", + "crates/aprender-simulate/docs/emc/ml/linear_regression.emc.yaml", + ) else { + return; + }; let out = apr() - .args(["sim", "emc-validate", emc]) + .args(["sim", "emc-validate"]) + .arg(&emc) .output() .expect("apr sim emc-validate"); assert!( @@ -411,16 +412,15 @@ fn apr_pv_reaches_the_contract_engine() { assert!(!help.contains("wharrgarbl"), "contains proves nothing here"); // Execution: validate a contract that really is in the tree. - let contract = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../contracts/apr-cli-commands-v1.yaml" - ); - assert!( - std::path::Path::new(contract).is_file(), - "fixture moved: {contract}" - ); + let Some(contract) = provable_contracts::workspace_path_or_skip!( + "apr_pv_reaches_the_contract_engine", + "contracts/apr-cli-commands-v1.yaml", + ) else { + return; + }; let out = apr() - .args(["pv", "validate", contract]) + .args(["pv", "validate"]) + .arg(&contract) .output() .expect("apr pv validate"); assert!( diff --git a/crates/apr-cli/tests/cli_commands.rs b/crates/apr-cli/tests/cli_commands.rs index c8ef00228b..939ab9ade5 100644 --- a/crates/apr-cli/tests/cli_commands.rs +++ b/crates/apr-cli/tests/cli_commands.rs @@ -87,6 +87,7 @@ fn registered_commands() -> Vec<&'static str> { "gpu", "profile", "ptx", + "ptx-debug", "ptx-map", "cbtop", "data", @@ -638,13 +639,11 @@ fn help_subcommands(path: &[&str]) -> Vec { } /// `(parent, [children])` for every parent the CONTRACT declares as having a -/// `subcommands:` list. -fn contract_subcommands() -> Vec<(String, Vec)> { - let path = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../contracts/apr-cli-commands-v1.yaml" - ); - let text = std::fs::read_to_string(path).expect("read the command contract"); +/// `subcommands:` list. The contract is a workspace file: `None` (a named SKIP) out of tree, +/// a FAIL in tree when it is missing (#4149). +fn contract_subcommands(test: &str) -> Option)>> { + let text = + provable_contracts::workspace_file_or_skip!(test, "contracts/apr-cli-commands-v1.yaml")?; let mut out = Vec::new(); let mut current: Option = None; @@ -663,12 +662,15 @@ fn contract_subcommands() -> Vec<(String, Vec)> { } } } - out + Some(out) } #[test] fn every_declared_subcommand_exists_in_the_binary() { - let declared = contract_subcommands(); + let Some(declared) = contract_subcommands("every_declared_subcommand_exists_in_the_binary") + else { + return; + }; // Vacuity: a parse that found nothing would make the loop below pass // trivially -- which is exactly how a depth-2 gate reports green while @@ -703,8 +705,10 @@ fn every_declared_subcommand_exists_in_the_binary() { #[test] fn every_subcommand_in_the_binary_is_declared() { - let declared: std::collections::HashMap> = - contract_subcommands().into_iter().collect(); + let Some(declared) = contract_subcommands("every_subcommand_in_the_binary_is_declared") else { + return; + }; + let declared: std::collections::HashMap> = declared.into_iter().collect(); let mut undeclared: Vec = Vec::new(); let mut seen = 0usize; diff --git a/crates/apr-cli/tests/falsification_crux_a_01.rs b/crates/apr-cli/tests/falsification_crux_a_01.rs index 3bec50e21f..09cd0a4ff0 100644 --- a/crates/apr-cli/tests/falsification_crux_a_01.rs +++ b/crates/apr-cli/tests/falsification_crux_a_01.rs @@ -32,29 +32,24 @@ #![allow(clippy::unwrap_used)] use std::collections::BTreeMap; -use std::path::PathBuf; use std::process::Command; -fn repo_root() -> PathBuf { - let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - manifest - .ancestors() - .nth(2) - .expect("CARGO_MANIFEST_DIR has repo root 2 ancestors up") - .to_path_buf() -} - -fn load_aliases() -> BTreeMap { - let path = repo_root().join("configs").join("aliases.yaml"); - let text = std::fs::read_to_string(&path) - .unwrap_or_else(|e| panic!("FALSIFY-CRUX-A-01-002: cannot read {path:?}: {e}")); - serde_yaml::from_str::>(&text) - .unwrap_or_else(|e| panic!("FALSIFY-CRUX-A-01-002: aliases.yaml not a str→str map: {e}")) +/// `configs/aliases.yaml` is a workspace file: `None` (a named SKIP) out of tree — the published +/// tarball does not carry it — and a FAIL in tree when it is missing or unparseable (#4149). +fn load_aliases(test: &str) -> Option> { + let text = provable_contracts::workspace_file_or_skip!(test, "configs/aliases.yaml")?; + Some( + serde_yaml::from_str::>(&text).unwrap_or_else(|e| { + panic!("FALSIFY-CRUX-A-01-002: aliases.yaml not a str→str map: {e}") + }), + ) } #[test] fn falsify_crux_a_01_002_aliases_yaml_present_and_parseable() { - let map = load_aliases(); + let Some(map) = load_aliases("falsify_crux_a_01_002_aliases_yaml_present_and_parseable") else { + return; + }; assert!( !map.is_empty(), "FALSIFY-CRUX-A-01-002: aliases.yaml parsed empty" @@ -63,7 +58,9 @@ fn falsify_crux_a_01_002_aliases_yaml_present_and_parseable() { #[test] fn falsify_crux_a_01_002_canonical_short_names_present() { - let map = load_aliases(); + let Some(map) = load_aliases("falsify_crux_a_01_002_canonical_short_names_present") else { + return; + }; for canonical in ["llama3", "mistral", "phi3", "qwen2"] { assert!( map.contains_key(canonical), @@ -74,7 +71,9 @@ fn falsify_crux_a_01_002_canonical_short_names_present() { #[test] fn falsify_crux_a_01_002_every_value_has_known_scheme() { - let map = load_aliases(); + let Some(map) = load_aliases("falsify_crux_a_01_002_every_value_has_known_scheme") else { + return; + }; for (name, url) in &map { assert!( url.starts_with("hf://") || url.starts_with("https://"), @@ -85,8 +84,12 @@ fn falsify_crux_a_01_002_every_value_has_known_scheme() { #[test] fn falsify_crux_a_01_002_resolution_deterministic() { - let a = load_aliases(); - let b = load_aliases(); + let (Some(a), Some(b)) = ( + load_aliases("falsify_crux_a_01_002_resolution_deterministic"), + load_aliases("falsify_crux_a_01_002_resolution_deterministic"), + ) else { + return; + }; assert_eq!( a, b, "FALSIFY-CRUX-A-01-002: two reads of aliases.yaml must yield byte-identical maps" diff --git a/crates/apr-cli/tests/pixel_regression.rs b/crates/apr-cli/tests/pixel_regression.rs index bcdfdd81fd..5451d96ccc 100644 --- a/crates/apr-cli/tests/pixel_regression.rs +++ b/crates/apr-cli/tests/pixel_regression.rs @@ -41,6 +41,14 @@ fn snapshots_dir() -> PathBuf { .join("snapshots") } +/// The golden snapshots are `exclude`d from the published package (`Cargo.toml`), so out of +/// tree (the unpacked `.crate`) there is nothing to compare against: the test skips by name. In +/// tree the directory must exist — a missing one FAILS (#4149). +fn snapshots_present(test: &str) -> bool { + provable_contracts::workspace_path_or_skip!(test, "crates/apr-cli/playbooks/snapshots") + .is_some() +} + fn test_apr_file() -> PathBuf { let path = snapshots_dir().join("test.apr"); // Ensure the test file exists and is v2 format @@ -226,6 +234,9 @@ fn create_diff(expected: &str, actual: &str) -> String { #[test] fn test_pixel_hex_dump() { + if !snapshots_present("test_pixel_hex_dump") { + return; + } let output = apr() .args([ "hex", @@ -248,6 +259,9 @@ fn test_pixel_hex_dump() { #[test] fn test_pixel_tree_ascii() { + if !snapshots_present("test_pixel_tree_ascii") { + return; + } let output = apr() .args(["tree", test_apr_file().to_str().unwrap(), "--sizes"]) .output() @@ -259,6 +273,9 @@ fn test_pixel_tree_ascii() { #[test] fn test_pixel_tree_mermaid() { + if !snapshots_present("test_pixel_tree_mermaid") { + return; + } let output = apr() .args([ "tree", @@ -279,6 +296,9 @@ fn test_pixel_tree_mermaid() { #[test] fn test_pixel_flow_cross_attn() { + if !snapshots_present("test_pixel_flow_cross_attn") { + return; + } let output = apr() .args([ "flow", @@ -295,6 +315,9 @@ fn test_pixel_flow_cross_attn() { #[test] fn test_pixel_flow_full() { + if !snapshots_present("test_pixel_flow_full") { + return; + } let output = apr() .args(["flow", test_apr_file().to_str().unwrap()]) .output() diff --git a/crates/apr-cli/tests/registry_failure_catalogue.rs b/crates/apr-cli/tests/registry_failure_catalogue.rs index b6e6cc5e60..faddbacd6a 100644 --- a/crates/apr-cli/tests/registry_failure_catalogue.rs +++ b/crates/apr-cli/tests/registry_failure_catalogue.rs @@ -5,6 +5,7 @@ //! host-dogfood rows recorded as receipts, not CI claims (design quorum //! 2026-09-06). Every hermetic row has its must-RED twin: a defective fixture //! under `tests/fixtures/registry/defective/` that the same check refuses. + use std::path::PathBuf; use std::process::{Command, Output}; @@ -239,11 +240,12 @@ fn reg12_nothing_is_persisted_between_discoveries() { /// `contracts/apr-devices-schema-v1.yaml` binds). #[test] fn json_output_validates_against_the_schema_on_fixtures_and_on_this_machine() { - let schema_text = std::fs::read_to_string( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../contracts/schemas/apr-devices-v1.schema.json"), - ) - .expect("schema file"); + let Some(schema_text) = provable_contracts::workspace_file_or_skip!( + "json_output_validates_against_the_schema_on_fixtures_and_on_this_machine", + "contracts/schemas/apr-devices-v1.schema.json", + ) else { + return; + }; let schema: serde_json::Value = serde_json::from_str(&schema_text).expect("schema json"); let validator = jsonschema::validator_for(&schema).expect("schema compiles"); let mut runs: Vec<(String, Output)> = ["cpu-only.json", "one-cuda.json", "two-vendors.json"] @@ -282,11 +284,12 @@ fn json_output_validates_against_the_schema_on_fixtures_and_on_this_machine() { /// (`minItems: 5`) — the schema discriminates, it is not decoration. #[test] fn schema_twin_the_defective_fixture_is_refused_by_the_schema() { - let schema_text = std::fs::read_to_string( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../contracts/schemas/apr-devices-v1.schema.json"), - ) - .expect("schema file"); + let Some(schema_text) = provable_contracts::workspace_file_or_skip!( + "schema_twin_the_defective_fixture_is_refused_by_the_schema", + "contracts/schemas/apr-devices-v1.schema.json", + ) else { + return; + }; let schema: serde_json::Value = serde_json::from_str(&schema_text).expect("schema json"); let validator = jsonschema::validator_for(&schema).expect("schema compiles"); let out = apr_devices( @@ -320,11 +323,12 @@ fn a_malformed_reserve_override_is_refused_by_name() { /// stray field on an entry are all rejected. #[test] fn schema_twin_documents_serde_refuses_do_not_validate() { - let schema_text = std::fs::read_to_string( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../contracts/schemas/apr-devices-v1.schema.json"), - ) - .expect("schema file"); + let Some(schema_text) = provable_contracts::workspace_file_or_skip!( + "schema_twin_documents_serde_refuses_do_not_validate", + "contracts/schemas/apr-devices-v1.schema.json", + ) else { + return; + }; let schema: serde_json::Value = serde_json::from_str(&schema_text).expect("schema json"); let validator = jsonschema::validator_for(&schema).expect("schema compiles"); let out = apr_devices( diff --git a/crates/apr-cli/tests/surface_binary_pin.rs b/crates/apr-cli/tests/surface_binary_pin.rs new file mode 100644 index 0000000000..335ea07ccc --- /dev/null +++ b/crates/apr-cli/tests/surface_binary_pin.rs @@ -0,0 +1,58 @@ +//! #3745 S1: `apr surface --json` from the built binary is exactly the document +//! the library's `surface::emit()` returns. +//! +//! In-process gates (S3's hand-list guard, S2's cell derivation in lib tests) +//! read `emit()`. The release gate reads the release-candidate binary's +//! `apr surface --json`. If the two could differ, a gate would pass on one +//! surface and ship the other. + +use std::process::Command; + +fn apr(args: &[&str]) -> std::process::Output { + Command::new(env!("CARGO_BIN_EXE_apr")) + .args(args) + .output() + .expect("spawn the apr binary") +} + +#[test] +fn the_binary_prints_the_library_surface() { + let expected = format!("{}\n", apr_cli::surface::emit().to_json()); + for argv in [ + &["surface", "--json"][..], + &["surface"][..], + &["surface", "--quiet"][..], + ] { + let out = apr(argv); + assert!( + out.status.success(), + "`apr {}` exited {:?}; stderr:\n{}", + argv.join(" "), + out.status.code(), + String::from_utf8_lossy(&out.stderr) + ); + let stdout = String::from_utf8(out.stdout).expect("utf-8 JSON"); + assert!( + stdout == expected, + "`apr {}` stdout differs from surface::emit() ({} vs {} bytes)", + argv.join(" "), + stdout.len(), + expected.len() + ); + let v: serde_json::Value = + serde_json::from_str(&stdout).expect("stdout is exactly one JSON document"); + assert_eq!(v["schema"], "apr-cli-surface/v1.1"); + } +} + +/// Hidden means absent from `--help`, not absent from the binary. +#[test] +fn surface_is_hidden_from_help() { + let out = apr(&["--help"]); + assert!(out.status.success()); + let help = String::from_utf8_lossy(&out.stdout); + assert!( + !help.lines().any(|l| l.trim_start().starts_with("surface ")), + "`surface` must not be listed in `apr --help`" + ); +} diff --git a/crates/apr-cli/tests/thinking_budgets_mirror.rs b/crates/apr-cli/tests/thinking_budgets_mirror.rs index 438fb88170..014bd8f7d3 100644 --- a/crates/apr-cli/tests/thinking_budgets_mirror.rs +++ b/crates/apr-cli/tests/thinking_budgets_mirror.rs @@ -24,20 +24,8 @@ const MIRROR: &str = include_str!("../contracts/thinking-budgets-v1.yaml"); /// The linted source, or `None` (after naming the skip) when out of tree. fn source_or_skip(test: &str) -> Option { - let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); - if !root.join("contracts").is_dir() { - eprintln!( - "SKIP {test}: out of tree (no {} beside this crate) - the linted source lives in the \ - workspace, which a published crate does not carry (#4129)", - root.join("contracts").display() - ); - return None; - } - let path = root.join("contracts/thinking-budgets-v1.yaml"); - Some( - std::fs::read_to_string(&path) - .unwrap_or_else(|e| panic!("in tree, {} must be readable: {e}", path.display())), - ) + // The shared in-tree rule (#4175): in tree a missing source FAILS, out of tree it skips by name. + provable_contracts::workspace_file_or_skip!(test, "contracts/thinking-budgets-v1.yaml") } #[test] diff --git a/crates/aprender-bench-compute/Cargo.toml b/crates/aprender-bench-compute/Cargo.toml index f19a38693e..f3a31eadab 100644 --- a/crates/aprender-bench-compute/Cargo.toml +++ b/crates/aprender-bench-compute/Cargo.toml @@ -11,7 +11,7 @@ disallowed_methods = "allow" [dependencies] # The implementation under test -aprender = { path = "../aprender-core", version = "0.69.3", package = "aprender-core", features = ["format-quantize"] } +aprender = { path = "../aprender-core", version = "0.70.0", package = "aprender-core", features = ["format-quantize"] } # Reference implementation for A/B comparison # Pure Rust (no BLAS) to keep comparison fair — both are pure Rust SIMD diff --git a/crates/aprender-bench-tokenizer/Cargo.toml b/crates/aprender-bench-tokenizer/Cargo.toml index cc9cbd4416..158cc95a9f 100644 --- a/crates/aprender-bench-tokenizer/Cargo.toml +++ b/crates/aprender-bench-tokenizer/Cargo.toml @@ -11,7 +11,7 @@ disallowed_methods = "allow" [dependencies] # The implementation under test -aprender = { path = "../aprender-core", version = "0.69.3", package = "aprender-core" } +aprender = { path = "../aprender-core", version = "0.70.0", package = "aprender-core" } # HuggingFace reference for A/B comparison # Regular dep (not dev-dep) because [[bench]] harness=false binaries can't see dev-deps diff --git a/crates/aprender-build-sha/Cargo.toml b/crates/aprender-build-sha/Cargo.toml index fb7a687314..5647c1d3f7 100644 --- a/crates/aprender-build-sha/Cargo.toml +++ b/crates/aprender-build-sha/Cargo.toml @@ -19,3 +19,6 @@ name = "build_sha" [lints] workspace = true + +[dev-dependencies] +tempfile = { workspace = true } diff --git a/crates/aprender-build-sha/src/emit_tests.rs b/crates/aprender-build-sha/src/emit_tests.rs new file mode 100644 index 0000000000..76a08cff52 --- /dev/null +++ b/crates/aprender-build-sha/src/emit_tests.rs @@ -0,0 +1,229 @@ +//! #4110 — a crates.io install of apr names its commit, never `+no-git`. Since #4219 every +//! workspace [[bin]] stamps its SHA through this crate, so the table runs against it. +//! +//! The 0.69.1 post-publish install printed `apr 0.69.1 (v0.69.1+no-git)` (0.68.1 did too) although +//! the published tarball carried the commit: `cargo package` writes `.cargo_vcs_info.json` into +//! every crate. These tests compile the SHIPPED `lib.rs` plus a `fn main() { emit() }` with rustc and run it the way cargo runs a +//! build script (CARGO_MANIFEST_DIR, CARGO_PKG_VERSION, cwd = the package dir) in a temp package +//! dir with no `.git` of ours above it. The vcs file is byte-exact from the real apr-cli-0.69.1 crate. +//! The release binaries printed `+no-git` too: binary-release.yml builds as root in a container over +//! the runner-owned checkout, and git refuses it ("dubious ownership"); row `foreign-owner`. +//! `planted_regressions_turn_red` re-runs the table on three planted copies of `lib.rs` — the vcs +//! rung removed (the pre-#4110 script), the vcs rung behind `git rev-parse`, and the safe.directory +//! retry removed — and requires each to fail, so the table cannot pass vacuously. + +use std::path::{Path, PathBuf}; +use std::process::Command; + +const LIB_RS: &str = include_str!("lib.rs"); + +/// What a package's `build.rs` compiles to: this crate's code, entered through [`super::emit`]. +fn shipped() -> String { + format!("{LIB_RS}\nfn main() {{\n emit();\n}}\n") +} +const VCS_CALL: &str = " if let Some(sha) = vcs_info_sha() {"; + +/// Byte-exact `.cargo_vcs_info.json` of apr-cli-0.69.1 as downloaded from crates.io. +const VCS_REAL: &str = "{\n \"git\": {\n \"sha1\": \"d8a6df53a6c7104ef4eabfd2284908f93f1d6a3f\"\n },\n \"path_in_vcs\": \"crates/apr-cli\"\n}"; +const VCS_DIRTY: &str = "{\n \"git\": {\n \"sha1\": \"d8a6df53a6c7104ef4eabfd2284908f93f1d6a3f\",\n \"dirty\": true\n },\n \"path_in_vcs\": \"crates/apr-cli\"\n}"; +const VCS_BAD: &str = + "{\n \"git\": {\n \"sha1\": \"not-a-sha\"\n },\n \"path_in_vcs\": \"crates/apr-cli\"\n}"; + +fn compile(source: &str, dir: &Path, name: &str) -> PathBuf { + let src = dir.join(format!("{name}.rs")); + let bin = dir.join(name); + std::fs::write(&src, source).expect("write build script copy"); + let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); + let out = Command::new(rustc) + .args([ + "--edition", + "2021", + "--crate-name", + "build_script_build", + "-o", + ]) + .arg(&bin) + .arg(&src) + .output() + .expect("rustc must be runnable: this test compiles the shipped build-sha source"); + assert!( + out.status.success(), + "rustc rejected {name}: {}", + String::from_utf8_lossy(&out.stderr) + ); + bin +} + +fn git(dir: &Path, args: &[&str]) -> String { + let out = Command::new("git") + .args([ + "-c", + "core.hooksPath=/dev/null", + "-c", + "user.email=f@x", + "-c", + "user.name=f", + ]) + .args(args) + .current_dir(dir) + .output() + .expect("git must be runnable"); + assert!( + out.status.success(), + "git {args:?}: {}", + String::from_utf8_lossy(&out.stderr) + ); + String::from_utf8_lossy(&out.stdout).trim().to_string() +} + +/// A fresh package dir under `root`; the vcs file written when given; optionally its own repo. +fn package(root: &Path, name: &str, vcs: Option<&str>) -> PathBuf { + let dir = root.join(name); + std::fs::create_dir_all(&dir).expect("mkdir package"); + if let Some(v) = vcs { + std::fs::write(dir.join(".cargo_vcs_info.json"), v).expect("write vcs file"); + } + dir +} + +/// Runs the compiled build script like cargo does; returns the APR_GIT_SHA it emits. +/// `ceiling` stops git's upward search there (None: search freely, as a real install would). +fn run(bin: &Path, pkg: &Path, ceiling: Option<&Path>, env: &[(&str, &str)]) -> String { + let out_dir = pkg.join("target-out"); + std::fs::create_dir_all(&out_dir).expect("mkdir OUT_DIR"); + let mut cmd = Command::new(bin); + cmd.current_dir(pkg) + .env("CARGO_MANIFEST_DIR", pkg) + .env("OUT_DIR", &out_dir) + .env("CARGO_PKG_VERSION", "9.9.9") + .env_remove("APR_GIT_SHA_OVERRIDE") + .env_remove("GIT_DIR") + .env_remove("GIT_WORK_TREE") + .env( + "GIT_CEILING_DIRECTORIES", + ceiling.unwrap_or_else(|| Path::new("/nonexistent")), + ) + .envs(env.iter().copied()); + let out = cmd.output().expect("run build script"); + String::from_utf8_lossy(&out.stdout) + .lines() + .find_map(|l| l.strip_prefix("cargo:rustc-env=APR_GIT_SHA=")) + .unwrap_or("") + .to_string() +} + +/// The case table. Returns the names of the rows that landed wrong (empty = all as expected). +fn table(build_rs: &str, label: &str) -> Vec { + let tmp = tempfile::tempdir().expect("tempdir"); + let root = tmp.path(); + let bin = compile(build_rs, root, label); + let mut wrong = Vec::new(); + let mut expect = |row: &str, got: String, want: &str| { + if got != want { + wrong.push(format!("{row}: got {got:?}, want {want:?}")); + } + }; + let pkgs = root.join("pkgs"); + let p = package(&pkgs, "packaged", Some(VCS_REAL)); + expect("packaged", run(&bin, &p, Some(&pkgs), &[]), "d8a6df53a"); + // an unrelated repository ABOVE the unpacked crate, with a commit, and no ceiling between them + let foreign = package(&pkgs, "foreign", None); + git(&foreign, &["init", "-q"]); + git( + &foreign, + &["commit", "-q", "--allow-empty", "-m", "foreign"], + ); + let p = package(&foreign, "crate", Some(VCS_REAL)); + expect("foreign-repo", run(&bin, &p, None, &[]), "d8a6df53a"); + let p = package(&pkgs, "dirty", Some(VCS_DIRTY)); + expect("dirty", run(&bin, &p, Some(&pkgs), &[]), "d8a6df53a-dirty"); + let p = package(&pkgs, "malformed", Some(VCS_BAD)); + expect( + "malformed", + run(&bin, &p, Some(&pkgs), &[]), + "v9.9.9+no-git", + ); + let p = package(&pkgs, "no-vcs", None); + expect("no-vcs", run(&bin, &p, Some(&pkgs), &[]), "v9.9.9+no-git"); + let p = package(&pkgs, "override", Some(VCS_REAL)); + expect( + "override", + run( + &bin, + &p, + Some(&pkgs), + &[("APR_GIT_SHA_OVERRIDE", "release123")], + ), + "release123", + ); + // a dev checkout: no vcs file, its own repo -> `git rev-parse --short HEAD` (worktree freshness) + let dev = package(&pkgs, "dev", None); + git(&dev, &["init", "-q"]); + git(&dev, &["commit", "-q", "--allow-empty", "-m", "dev"]); + let head = git(&dev, &["rev-parse", "--short", "HEAD"]); + expect("dev-checkout", run(&bin, &dev, None, &[]), &head); + // the release lane: root in a container over the runner-owned checkout. git's own test knob + // makes it see another owner; the precondition proves plain git really refuses the repo here + let other_owner = [("GIT_TEST_ASSUME_DIFFERENT_OWNER", "1")]; + let refused = Command::new("git") + .args(["rev-parse", "HEAD"]) + .current_dir(&dev) + .envs(other_owner) + .output(); + assert!( + !refused.expect("git").status.success(), + "cannot simulate a checkout owned by another user: this git ignores GIT_TEST_ASSUME_DIFFERENT_OWNER" + ); + expect("foreign-owner", run(&bin, &dev, None, &other_owner), &head); + wrong +} + +#[test] +fn packaged_build_rs_names_its_commit() { + let wrong = table(&shipped(), "shipped"); + assert!( + wrong.is_empty(), + "shipped build-sha rows landed wrong: {wrong:#?}" + ); +} + +#[test] +fn planted_regressions_turn_red() { + let shipped = shipped(); + assert_eq!( + shipped.matches(VCS_CALL).count(), + 1, + "the vcs rung call site moved; re-anchor the plants" + ); + let no_rung = shipped.replace(VCS_CALL, " if let Some(sha) = None:: {"); + let git_first = shipped.replace( + VCS_CALL, + " if let Some(sha) = run_git(&[\"rev-parse\", \"--short\", \"HEAD\"]).or_else(vcs_info_sha) {", + ); + let w1 = table(&no_rung, "plant_no_rung"); + assert!( + w1.iter().any(|r| r.starts_with("packaged:")), + "no-vcs-rung plant survived: {w1:#?}" + ); + // #4254 split the chain across lines and appended the `.git` read after the retry. + let retry = "\n .or_else(|| trusted_git_retry(&head))"; + assert_eq!( + shipped.matches(retry).count(), + 1, + "the git retry call site moved; re-anchor the plant" + ); + let w3 = table(&shipped.replace(retry, ""), "plant_no_retry"); + assert!( + w3.iter().any(|r| r.starts_with("foreign-owner:")), + "no-retry plant survived: {w3:#?}" + ); + let w2 = table(&git_first, "plant_git_first"); + assert!( + w2.iter().any(|r| r.starts_with("foreign-repo:")), + "git-first plant survived: {w2:#?}" + ); + assert!( + !w2.iter().any(|r| r.starts_with("packaged:")), + "git-first plant should still pass packaged: {w2:#?}" + ); +} diff --git a/crates/aprender-build-sha/src/lib.rs b/crates/aprender-build-sha/src/lib.rs index fa491dc461..8406df6b6b 100644 --- a/crates/aprender-build-sha/src/lib.rs +++ b/crates/aprender-build-sha/src/lib.rs @@ -7,13 +7,20 @@ //! Contract: apr-version-traceability-v1 F-VERSION-001..004 (paiml/aprender#597, //! #1862). `APR_GIT_SHA` is resolved with a multi-source fallback hierarchy: //! 1. `APR_GIT_SHA_OVERRIDE` env var (CI/release hook) -//! 2. `git rev-parse --short HEAD` (dev builds from worktree or primary checkout) -//! 3. committed `.git-sha` file (crates.io installs) -//! 4. `v{CARGO_PKG_VERSION}+no-git` (informative fallback — never bare "unknown") +//! 2. `.cargo_vcs_info.json` `git.sha1` — written by `cargo package` into every published +//! tarball, so a crates.io install knows its commit (#4110). Ahead of `git rev-parse` +//! because an unpacked crate may sit inside SOME OTHER repository whose HEAD is not ours +//! 3. `git rev-parse --short HEAD` (dev builds from worktree or primary checkout; retried with +//! the enclosing checkout marked safe.directory when git refuses a checkout owned by another +//! user — binary-release.yml builds as root in a container over the runner's checkout) +//! then, with no git binary that answers, HEAD read straight from `.git` (#4254: the +//! release asset said "v0.69.1+no-git" inside the sibling build container) +//! 4. committed `.git-sha` file +//! 5. `v{CARGO_PKG_VERSION}+no-git` (informative fallback — never bare "unknown") //! -//! This logic was moved out of `crates/apr-cli/build.rs` unchanged; only the -//! pure resolution order was factored into [`resolve`] so it can be tested -//! without running git. +//! This logic was moved out of `crates/apr-cli/build.rs`; the pure resolution +//! order is factored into [`resolve`] so it can be tested without running git, +//! and `emit_tests` compiles this file as a build script and runs it for real. /// Resolve `APR_GIT_SHA`, print it as `cargo:rustc-env`, and register the /// rerun-if-changed triggers. Call once from `build.rs`. @@ -25,12 +32,11 @@ pub fn emit() { // in a worktree `.git` is a file pointer to `/worktrees//`. // Resolve the actual git directory(ies) via `git rev-parse` so both layouts // are watched correctly (#1862). - for path in git_rerun_triggers() { - println!("cargo:rerun-if-changed={path}"); - } + register_git_rerun_triggers(); println!("cargo:rerun-if-env-changed=APR_GIT_SHA_OVERRIDE"); println!("cargo:rerun-if-changed=.git-sha"); + println!("cargo:rerun-if-changed=.cargo_vcs_info.json"); } /// The pure resolution order. Each argument is the raw result of one source, @@ -39,6 +45,7 @@ pub fn emit() { #[must_use] pub fn resolve( override_env: Option<&str>, + vcs_info: Option<&str>, git_head: Option<&str>, sha_file: Option<&str>, version: Option<&str>, @@ -47,15 +54,19 @@ pub fn resolve( if let Some(s) = nonblank(override_env) { return s.to_string(); } - // 2. Live git hash from worktree (works for both primary checkouts and worktrees) + // 2. The commit `cargo package` recorded (crates.io / packaged builds) + if let Some(s) = nonblank(vcs_info) { + return s.to_string(); + } + // 3. Live git hash from worktree (works for both primary checkouts and worktrees) if let Some(s) = nonblank(git_head) { return s.to_string(); } - // 3. Committed .git-sha fallback (updated by release script at publish time) + // 4. Committed .git-sha fallback (updated by release script at publish time) if let Some(s) = nonblank(sha_file) { return s.to_string(); } - // 4. Informative fallback — never bare "unknown" + // 5. Informative fallback — never bare "unknown" format!("v{}+no-git", version.unwrap_or("0.0.0")) } @@ -63,26 +74,22 @@ fn nonblank(s: Option<&str>) -> Option<&str> { s.map(str::trim).filter(|s| !s.is_empty()) } -/// The git paths whose change must rerun the build script. Returned rather -/// than printed so a test can read them. -fn git_rerun_triggers() -> Vec { - let mut paths = Vec::new(); +fn register_git_rerun_triggers() { // Worktree-local HEAD: /HEAD. In a worktree this is the per-worktree // HEAD pointer; in a primary checkout it's the same as /HEAD. if let Some(git_dir) = run_git(&["rev-parse", "--git-dir"]) { let head = format!("{git_dir}/HEAD"); if std::path::Path::new(&head).exists() { - paths.push(head); + println!("cargo:rerun-if-changed={head}"); } } // Shared refs live under /refs/heads/ regardless of layout. if let Some(common_dir) = run_git(&["rev-parse", "--git-common-dir"]) { let refs = format!("{common_dir}/refs/heads"); if std::path::Path::new(&refs).exists() { - paths.push(refs); + println!("cargo:rerun-if-changed={refs}"); } } - paths } fn run_git(args: &[&str]) -> Option { @@ -99,34 +106,31 @@ fn run_git(args: &[&str]) -> Option { } fn resolve_git_sha() -> String { - resolve_git_sha_with( - |k| std::env::var(k).ok(), - run_git, - |p| std::fs::read_to_string(p).ok(), - ) -} - -/// [`resolve_git_sha`] over injected sources, so a test can drive every branch -/// without touching the process environment. -fn resolve_git_sha_with( - env: impl Fn(&str) -> Option, - git: impl Fn(&[&str]) -> Option, - read: impl Fn(&str) -> Option, -) -> String { // Sources are consulted lazily and in order, as the original apr-cli - // build.rs did: git is not run when the override is set, and `.git-sha` - // is not read when git answers. - let override_env = env("APR_GIT_SHA_OVERRIDE"); + // build.rs did: git is not run when an earlier source answers, and + // `.git-sha` is not read when git answers. + let override_env = std::env::var("APR_GIT_SHA_OVERRIDE").ok(); if let Some(s) = nonblank(override_env.as_deref()) { return s.to_string(); } - let git_head = git(&["rev-parse", "--short", "HEAD"]); + if let Some(sha) = vcs_info_sha() { + return sha; + } + // `trusted_git_retry`: the release binaries are built as root in a container over the + // runner-owned checkout, where git refuses the repo as "dubious ownership" (#4110) + let head = ["rev-parse", "--short", "HEAD"]; + // #4254: and when no git binary answers at all (the sibling build container over a + // bind-mounted checkout), read HEAD straight from the checkout's own `.git`. + let git_head = run_git(&head) + .or_else(|| trusted_git_retry(&head)) + .or_else(read_head_sha_from_dot_git); let sha_file = match git_head { Some(_) => None, - None => read(".git-sha"), + None => std::fs::read_to_string(".git-sha").ok(), }; - let version = env("CARGO_PKG_VERSION"); + let version = std::env::var("CARGO_PKG_VERSION").ok(); resolve( + None, None, git_head.as_deref(), sha_file.as_deref(), @@ -134,17 +138,128 @@ fn resolve_git_sha_with( ) } +/// #4254: the commit HEAD names, read from `.git` without the git binary. +/// +/// Walks up from `CARGO_MANIFEST_DIR` to the first `.git`, which is either the git +/// directory or, in a worktree, a file `gitdir: ` (whose `commondir` holds the +/// shared refs). HEAD is a bare SHA when detached (a tag checkout) or `ref: `, +/// resolved as a loose ref, then through `packed-refs`. Short form, 9 hex digits. +fn read_head_sha_from_dot_git() -> Option { + use std::path::{Path, PathBuf}; + let start = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").ok()?); + let dot_git = start + .ancestors() + .map(|d| d.join(".git")) + .find(|p| p.exists())?; + let git_dir: PathBuf = if dot_git.is_file() { + let text = std::fs::read_to_string(&dot_git).ok()?; + let rel = text.trim().strip_prefix("gitdir:")?.trim(); + let p = Path::new(rel); + if p.is_absolute() { + p.to_path_buf() + } else { + dot_git.parent()?.join(p) + } + } else { + dot_git + }; + let common_dir = match std::fs::read_to_string(git_dir.join("commondir")) { + Ok(c) => { + let p = Path::new(c.trim()); + if p.is_absolute() { + p.to_path_buf() + } else { + git_dir.join(p) + } + } + Err(_) => git_dir.clone(), + }; + let head = std::fs::read_to_string(git_dir.join("HEAD")).ok()?; + let head = head.trim(); + let full = match head.strip_prefix("ref:") { + None => head.to_string(), + Some(name) => { + let name = name.trim(); + [&git_dir, &common_dir] + .iter() + .find_map(|d| std::fs::read_to_string(d.join(name)).ok()) + .map(|s| s.trim().to_string()) + .or_else(|| { + let packed = std::fs::read_to_string(common_dir.join("packed-refs")).ok()?; + packed.lines().find_map(|l| { + let (sha, r) = l.split_once(' ')?; + (r.trim() == name).then(|| sha.to_string()) + }) + })? + } + }; + let is_sha = full.len() >= 40 && full.bytes().all(|b| b.is_ascii_hexdigit()); + is_sha.then(|| full[..9].to_string()) +} + +/// `git.sha1` from `.cargo_vcs_info.json`, shortened to 9 hex digits (what `git rev-parse +/// --short` prints in this repository), with `-dirty` when cargo packaged a dirty tree. +/// Parsed by hand: this crate has no dependencies, and the file is cargo's own fixed shape. +fn vcs_info_sha() -> Option { + let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); + let text = std::fs::read_to_string(format!("{dir}/.cargo_vcs_info.json")).ok()?; + let after = &text[text.find("\"sha1\"")? + "\"sha1\"".len()..]; + let open = after.find('"')?; + let value = &after[open + 1..]; + let sha = &value[..value.find('"')?]; + if sha.len() != 40 || !sha.bytes().all(|b| b.is_ascii_hexdigit()) { + return None; + } + let dirty = text.contains("\"dirty\": true") || text.contains("\"dirty\":true"); + Some(format!( + "{}{}", + &sha[..9], + if dirty { "-dirty" } else { "" } + )) +} + +/// `git ` again, with the checkout that encloses this package marked `safe.directory`. +/// +/// git refuses a repository owned by another user ("detected dubious ownership"), and the release +/// lane builds as root in a container over the runner-owned checkout, so every published binary +/// printed `+no-git` (#4110). The exception goes in a private GLOBAL config under OUT_DIR: +/// git ignores `safe.directory` given by `-c` or GIT_CONFIG_* before 2.39 (measured, 2.34.1). +/// Only the checkout found by walking up from CARGO_MANIFEST_DIR is trusted, never `*`, and +/// `rev-parse` runs no hooks. This build is already compiling that checkout's code. +fn trusted_git_retry(args: &[&str]) -> Option { + let manifest = std::env::var("CARGO_MANIFEST_DIR").ok()?; + let top = std::path::Path::new(&manifest) + .ancestors() + .find(|d| d.join(".git").exists())?; + let config = + std::path::Path::new(&std::env::var("OUT_DIR").ok()?).join("apr-safe-directory.gitconfig"); + std::fs::write( + &config, + format!("[safe]\n\tdirectory = {}\n", top.display()), + ) + .ok()?; + let out = std::process::Command::new("git") + .args(args) + .env("GIT_CONFIG_GLOBAL", &config) + .output() + .ok()?; + let s = String::from_utf8_lossy(&out.stdout).trim().to_string(); + (out.status.success() && !s.is_empty()).then_some(s) +} + +#[cfg(test)] +mod emit_tests; + #[cfg(test)] mod tests { - use super::{ - emit, git_rerun_triggers, resolve, resolve_git_sha, resolve_git_sha_with, run_git, - }; + use super::resolve; #[test] fn override_wins_over_everything() { assert_eq!( resolve( Some(" abc123456 \n"), + None, Some("def456789"), Some("0123456789"), Some("1.2.3") @@ -158,214 +273,49 @@ mod tests { assert_eq!( resolve( Some(" "), + None, Some("def456789"), Some("0123456789"), Some("1.2.3") ), "def456789" ); - assert_eq!(resolve(None, Some("def456789"), None, None), "def456789"); - } - - #[test] - fn sha_file_used_only_without_git() { assert_eq!( - resolve(None, None, Some("0123456789\n"), Some("1.2.3")), - "0123456789" + resolve(None, None, Some("def456789"), None, None), + "def456789" ); } #[test] - fn no_git_fallback_names_the_version_never_unknown() { - assert_eq!(resolve(None, None, None, Some("0.69.0")), "v0.69.0+no-git"); + fn packaged_commit_wins_over_git() { assert_eq!( - resolve(Some(""), None, Some(" \n"), Some("0.69.0")), - "v0.69.0+no-git" + resolve(None, Some("d8a6df53a"), Some("def456789"), None, None), + "d8a6df53a" ); - assert_eq!(resolve(None, None, None, None), "v0.0.0+no-git"); - } - - // The git-running half: these kill the mutants that the pure `resolve` - // tests cannot reach (#4315 mutants job, 8 MISSED). - - #[test] - fn run_git_returns_trimmed_stdout_on_success() { - let v = run_git(&["--version"]).expect("git is on PATH in every build env"); - assert!(v.starts_with("git version "), "{v:?}"); - assert_eq!(v, v.trim()); - } - - #[test] - fn run_git_is_none_on_failure_and_on_empty_stdout() { - assert_eq!(run_git(&["definitely-not-a-git-subcommand"]), None); - // `git rev-parse` with no arguments succeeds and prints nothing. - assert_eq!(run_git(&["rev-parse"]), None); - } - - #[test] - fn rerun_triggers_watch_head_and_refs_when_in_a_checkout() { - let triggers = git_rerun_triggers(); - if run_git(&["rev-parse", "--git-dir"]).is_none() { - assert!(triggers.is_empty(), "{triggers:?}"); - return; - } - assert!( - triggers.iter().any(|p| p.ends_with("/HEAD")), - "no HEAD trigger: {triggers:?}" - ); - assert!( - triggers.iter().any(|p| p.ends_with("/refs/heads")), - "no refs/heads trigger: {triggers:?}" - ); - for p in &triggers { - assert!(std::path::Path::new(p).exists(), "{p}"); - } - } - - /// A fake source: records whether it was consulted, answers from a table. - fn fake<'a>( - table: &'a [(&'a str, &'a str)], - hit: &'a std::cell::Cell, - ) -> impl Fn(&str) -> Option + 'a { - move |k| { - hit.set(true); - table - .iter() - .find(|(key, _)| *key == k) - .map(|(_, v)| (*v).to_string()) - } - } - - #[test] - fn override_wins_and_neither_git_nor_the_file_is_consulted() { - let (g, f, e) = Default::default(); - let git_hit: &std::cell::Cell = &g; - let got = resolve_git_sha_with( - fake(&[("APR_GIT_SHA_OVERRIDE", " rel123 \n")], &e), - |_: &[&str]| { - git_hit.set(true); - Some("gitsha".into()) - }, - fake(&[(".git-sha", "filesha")], &f), - ); - assert_eq!(got, "rel123"); - assert!(!g.get(), "git ran although the override was set"); - assert!(!f.get(), ".git-sha was read although the override was set"); - } - - #[test] - fn git_answers_and_the_file_is_not_read() { - let (f, e) = Default::default(); - let got = resolve_git_sha_with( - fake(&[("APR_GIT_SHA_OVERRIDE", " ")], &e), - |a: &[&str]| (a == ["rev-parse", "--short", "HEAD"]).then(|| "abc1234".to_string()), - fake(&[(".git-sha", "filesha")], &f), + assert_eq!( + resolve(Some("rel123"), Some("d8a6df53a"), None, None, None), + "rel123" ); - assert_eq!(got, "abc1234"); - assert!(!f.get(), ".git-sha was read although git answered"); } #[test] - fn without_git_the_file_then_the_version_is_used() { - let (f, e) = Default::default(); - let no_git = |_: &[&str]| None; - let got = resolve_git_sha_with( - fake(&[("CARGO_PKG_VERSION", "1.2.3")], &e), - no_git, - fake(&[(".git-sha", "0123456789\n")], &f), - ); - assert_eq!(got, "0123456789"); - assert!(f.get()); - let (f2, e2) = Default::default(); - let got = resolve_git_sha_with( - fake(&[("CARGO_PKG_VERSION", "1.2.3")], &e2), - no_git, - fake(&[], &f2), + fn sha_file_used_only_without_git() { + assert_eq!( + resolve(None, None, None, Some("0123456789\n"), Some("1.2.3")), + "0123456789" ); - assert_eq!(got, "v1.2.3+no-git"); } #[test] - fn resolve_git_sha_reads_the_live_sources() { - // An oracle independent of `resolve`: in a checkout with no override set, - // the live value is exactly git's short HEAD. - let got = resolve_git_sha(); - assert!(!got.is_empty()); - if let Some(o) = std::env::var("APR_GIT_SHA_OVERRIDE") - .ok() - .filter(|s| !s.trim().is_empty()) - { - assert_eq!(got, o.trim()); - } else if let Some(head) = run_git(&["rev-parse", "--short", "HEAD"]) { - assert_eq!(got, head); - } else { - assert!( - got.ends_with("+no-git") || std::path::Path::new(".git-sha").exists(), - "{got}" - ); - } - } - - /// The child half of `emit_prints_the_sha_and_watches_a_real_checkout`: it - /// only runs `emit` when the parent re-executes this test binary. - #[test] - fn emit_child() { - if std::env::var_os("BUILD_SHA_EMIT_CHILD").is_some() { - emit(); - } - } - - /// `emit` writes to stdout, which a test cannot read in-process, and under - /// cargo-mutants the tree is a copy with no `.git`, so a test that reads the - /// outer checkout passes whether or not the triggers are returned. Re-run this - /// test binary as a child inside a fresh `git init`, so both the sha line and - /// the HEAD / refs/heads triggers must appear on its stdout. - #[test] - fn emit_prints_the_sha_and_watches_a_real_checkout() { - let dir = std::env::temp_dir().join(format!("build-sha-emit-{}", std::process::id())); - std::fs::create_dir_all(&dir).expect("temp dir"); - let init = std::process::Command::new("git") - .args(["init", "-q"]) - .current_dir(&dir) - .status() - .expect("git is required: without a checkout the triggers cannot be observed"); - assert!(init.success(), "git init failed in {}", dir.display()); - let out = std::process::Command::new(std::env::current_exe().expect("test binary")) - .args([ - "--exact", - "tests::emit_child", - "--nocapture", - "--test-threads=1", - ]) - .current_dir(&dir) - .env("BUILD_SHA_EMIT_CHILD", "1") - .env_remove("APR_GIT_SHA_OVERRIDE") - .output() - .expect("re-run the test binary"); - let _ = std::fs::remove_dir_all(&dir); - let stdout = String::from_utf8_lossy(&out.stdout); - assert!(out.status.success(), "{stdout}"); - // libtest prefixes the child's first line with "test tests::emit_child ... ". - let directives: Vec<&str> = stdout - .lines() - .filter_map(|l| l.find("cargo:").map(|at| &l[at..])) - .collect(); - let has = |p: &dyn Fn(&str) -> bool| directives.iter().any(|d| p(d)); - assert!( - has(&|d| d.starts_with("cargo:rustc-env=APR_GIT_SHA=")), - "{stdout}" - ); - assert!( - has(&|d| d.starts_with("cargo:rerun-if-changed=") && d.ends_with("/HEAD")), - "no HEAD trigger: {stdout}" - ); - assert!( - has(&|d| d.starts_with("cargo:rerun-if-changed=") && d.ends_with("/refs/heads")), - "no refs/heads trigger: {stdout}" + fn no_git_fallback_names_the_version_never_unknown() { + assert_eq!( + resolve(None, None, None, None, Some("0.69.0")), + "v0.69.0+no-git" ); - assert!( - has(&|d| d == "cargo:rerun-if-env-changed=APR_GIT_SHA_OVERRIDE"), - "{stdout}" + assert_eq!( + resolve(Some(""), None, None, Some(" \n"), Some("0.69.0")), + "v0.69.0+no-git" ); + assert_eq!(resolve(None, None, None, None, None), "v0.0.0+no-git"); } } diff --git a/crates/aprender-cbtop/Cargo.toml b/crates/aprender-cbtop/Cargo.toml index b9e2c8868c..4b63708e97 100644 --- a/crates/aprender-cbtop/Cargo.toml +++ b/crates/aprender-cbtop/Cargo.toml @@ -32,7 +32,7 @@ presentar-terminal = { workspace = true } # Compute backends trueno = { workspace = true } -trueno-gpu = { version = "0.69.3", path = "../aprender-gpu", package = "aprender-gpu", optional = true } +trueno-gpu = { version = "0.70.0", path = "../aprender-gpu", package = "aprender-gpu", optional = true } # Terminal handling crossterm = "0.28" diff --git a/crates/aprender-cbtop/src/main.rs b/crates/aprender-cbtop/src/main.rs index cd38813b7b..d1481df9be 100644 --- a/crates/aprender-cbtop/src/main.rs +++ b/crates/aprender-cbtop/src/main.rs @@ -16,7 +16,7 @@ use clap::{Parser, Subcommand}; /// Compute Block Top - Real-time load testing and hardware monitoring TUI #[derive(Parser, Debug)] -#[command(name = "cbtop")] +#[command(name = "aprender-cbtop")] #[command(author = "Trueno Engineering")] #[command(version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"))] #[command(about = "Real-time load testing and hardware monitoring TUI", long_about = None)] @@ -33,16 +33,16 @@ struct Cli { #[arg(short, long, default_value = "0")] device: u32, - /// Compute backend: simd, wgpu, cuda, all - #[arg(short, long, default_value = "all")] + /// Compute backend + #[arg(short, long, default_value = "all", value_parser = BACKENDS, ignore_case = true)] backend: String, - /// Load profile: idle, light, medium, heavy, stress - #[arg(short, long, default_value = "idle")] + /// Load profile + #[arg(short, long, default_value = "idle", value_parser = LOADS, ignore_case = true)] load: String, - /// Workload type: gemm, conv, attention, bandwidth, elementwise, reduction, all - #[arg(short, long, default_value = "gemm")] + /// Workload type + #[arg(short, long, default_value = "gemm", value_parser = WORKLOADS, ignore_case = true)] workload: String, /// Problem size in elements @@ -69,8 +69,8 @@ struct Cli { #[arg(long)] headless: bool, - /// Output format for headless mode: json, text - #[arg(long, default_value = "text")] + /// Output format for headless mode + #[arg(long, default_value = "text", value_parser = FORMATS, ignore_case = true)] format: String, /// Benchmark duration in seconds (headless mode) @@ -86,12 +86,12 @@ struct Cli { enum Commands { /// Run benchmark in headless mode Bench { - /// Compute backend: simd, wgpu, cuda, all - #[arg(short, long, default_value = "simd")] + /// Compute backend + #[arg(short, long, default_value = "simd", value_parser = BACKENDS, ignore_case = true)] backend: String, - /// Workload type: gemm, dot, elementwise, reduction - #[arg(short, long, default_value = "gemm")] + /// Workload type + #[arg(short, long, default_value = "gemm", value_parser = WORKLOADS, ignore_case = true)] workload: String, /// Problem size in elements @@ -102,8 +102,8 @@ enum Commands { #[arg(short, long, default_value = "5")] duration: u64, - /// Output format: json, text - #[arg(short, long, default_value = "json")] + /// Output format + #[arg(short, long, default_value = "json", value_parser = FORMATS, ignore_case = true)] format: String, /// Output file path @@ -118,8 +118,8 @@ enum Commands { #[arg(long, default_value = "5.0")] fail_on_regression: f64, - /// Compare multiple backends (comma-separated) - #[arg(long)] + /// Compare multiple backends (comma-separated: simd, wgpu, cuda, all) + #[arg(long, value_parser = parse_backend_list)] compare: Option, }, @@ -153,8 +153,8 @@ pub(crate) enum OptimizeAction { #[arg(short, long, default_value = "benchmarks/baseline.json")] baseline: std::path::PathBuf, - /// Output format: text, json - #[arg(short, long, default_value = "text")] + /// Output format + #[arg(short, long, default_value = "text", value_parser = FORMATS)] format: String, /// Output file (stdout if not specified) @@ -176,12 +176,42 @@ pub(crate) enum OptimizeAction { #[arg(long)] quick: bool, - /// Output format: text, json - #[arg(short, long, default_value = "text")] + /// Output format + #[arg(short, long, default_value = "text", value_parser = FORMATS)] format: String, }, } +// G1.3: every enumerated flag is checked by clap, so a typo is a usage error (exit 2) +// instead of a silent fallback in the parse_* functions below. +const BACKENDS: [&str; 4] = ["simd", "wgpu", "cuda", "all"]; +const LOADS: [&str; 5] = ["idle", "light", "medium", "heavy", "stress"]; +const WORKLOADS: [&str; 8] = [ + "gemm", + "conv", + "conv2d", + "attention", + "bandwidth", + "elementwise", + "reduction", + "all", +]; +const FORMATS: [&str; 2] = ["text", "json"]; + +/// `--compare` takes a comma-separated backend list; each item must be one of [`BACKENDS`]. +fn parse_backend_list(s: &str) -> Result { + for item in s.split(',') { + let item = item.trim().to_lowercase(); + if !BACKENDS.contains(&item.as_str()) { + return Err(format!( + "invalid backend '{item}' [possible values: {}]", + BACKENDS.join(", ") + )); + } + } + Ok(s.to_string()) +} + fn parse_backend(s: &str) -> ComputeBackend { match s.to_lowercase().as_str() { "simd" => ComputeBackend::Simd, @@ -287,3 +317,55 @@ fn main() -> Result<(), CbtopError> { let mut app = CbtopApp::new(config)?; app.run() } + +#[cfg(test)] +mod g13_tests { + use super::Cli; + use clap::{CommandFactory, Parser}; + + fn rejects(args: &[&str]) -> bool { + Cli::try_parse_from(args).is_err() + } + + #[test] + fn g13_name_is_the_target_name() { + assert_eq!(Cli::command().get_name(), "aprender-cbtop"); + } + + #[test] + fn g13_bogus_enumerated_values_are_usage_errors() { + for bad in [ + &["aprender-cbtop", "bench", "--backend", "nosuch"][..], + &["aprender-cbtop", "bench", "--workload", "dot"], + &["aprender-cbtop", "bench", "--format", "nosuch"], + &["aprender-cbtop", "bench", "--compare", "simd,nosuch"], + &["aprender-cbtop", "--load", "nosuch"], + &["aprender-cbtop", "--backend", "nosuch"], + &["aprender-cbtop", "--workload", "nosuch"], + &["aprender-cbtop", "--format", "nosuch"], + &[ + "aprender-cbtop", + "optimize", + "analyze", + "--format", + "nosuch", + ], + &["aprender-cbtop", "optimize", "check", "--format", "nosuch"], + ] { + assert!(rejects(bad), "accepted {bad:?}"); + } + } + + #[test] + fn g13_valid_values_parse_case_insensitively() { + for ok in [ + &["aprender-cbtop", "bench", "--backend", "SIMD"][..], + &["aprender-cbtop", "bench", "--workload", "conv2d"], + &["aprender-cbtop", "bench", "--compare", "simd, all"], + &["aprender-cbtop", "--load", "stress", "--format", "json"], + &["aprender-cbtop", "optimize", "check", "--format", "json"], + ] { + assert!(!rejects(ok), "rejected {ok:?}"); + } + } +} diff --git a/crates/aprender-cgp/Cargo.toml b/crates/aprender-cgp/Cargo.toml index 43ea299cd3..69d47b2732 100644 --- a/crates/aprender-cgp/Cargo.toml +++ b/crates/aprender-cgp/Cargo.toml @@ -36,7 +36,7 @@ num_cpus = "1.17" comfy-table = "7" colored = "3" # GPU profiling: direct cuBLAS measurement (OWN THE STACK) -trueno-gpu = { path = "../aprender-gpu", version = "0.69.3", features = ["cuda"], optional = true, package = "aprender-gpu" } +trueno-gpu = { path = "../aprender-gpu", version = "0.70.0", features = ["cuda"], optional = true, package = "aprender-gpu" } [features] default = [] diff --git a/crates/aprender-cgp/src/analysis/explain.rs b/crates/aprender-cgp/src/analysis/explain.rs index b475e8e3f6..6b4e4560c8 100644 --- a/crates/aprender-cgp/src/analysis/explain.rs +++ b/crates/aprender-cgp/src/analysis/explain.rs @@ -311,8 +311,9 @@ pub fn run_explain(target: &str, kernel: Option<&str>) -> Result<()> { ); } _ => { - println!(" Unknown target: {target}"); - println!(" Supported: ptx, wgsl, simd"); + // An unknown target was once reported on stdout with exit 0, so a script + // could not tell it from a successful analysis (ONT-10 S5 surface probe). + anyhow::bail!("unknown explain target '{target}' (supported: ptx, wgsl, simd)"); } } @@ -496,6 +497,6 @@ fn main(@builtin(global_invocation_id) gid: vec3) { #[test] fn test_run_explain_unknown() { let result = run_explain("unknown_target", None); - assert!(result.is_ok()); + assert!(result.is_err()); } } diff --git a/crates/aprender-cgp/src/cli.rs b/crates/aprender-cgp/src/cli.rs index 3af89083bc..b8e12e72bf 100644 --- a/crates/aprender-cgp/src/cli.rs +++ b/crates/aprender-cgp/src/cli.rs @@ -24,7 +24,7 @@ pub const CGP_BACKEND_VALUES: [&str; 7] = /// Profiles scalar, SIMD (SSE2/AVX2/AVX-512/NEON/WASM SIMD128), /// wgpu (Vulkan/Metal/DX12/WebGPU), and CUDA workloads. #[derive(Parser, Clone, Debug)] -#[command(name = "cgp", version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"), about, long_about = None)] +#[command(name = "aprender-cgp", version = concat!(env!("CARGO_PKG_VERSION"), " (", env!("APR_GIT_SHA"), ")"), about, long_about = None)] pub struct Cli { /// Output JSON instead of human-readable text #[arg(long, global = true)] diff --git a/crates/aprender-cgp/tests/cli_surface_gate.rs b/crates/aprender-cgp/tests/cli_surface_gate.rs new file mode 100644 index 0000000000..040202498d --- /dev/null +++ b/crates/aprender-cgp/tests/cli_surface_gate.rs @@ -0,0 +1,119 @@ +//! ONT-10 S5 surface gate for the `aprender-cgp` binary: `contracts/bin-aprender-cgp--aprender-cgp-v1.yaml`. +//! +//! Every assertion runs the binary built from this tree (`CARGO_BIN_EXE_aprender-cgp`), which +//! cannot fall back to a stale copy on PATH. A test that only asserted success on good +//! input would pass against `fn main() {}`, so each invariant here is one the binary +//! can actually fail: a rejected input must exit non-zero AND must not be a panic. + +use std::path::PathBuf; +use std::process::{Command, Output}; + +const BIN: &str = env!("CARGO_BIN_EXE_aprender-cgp"); + +/// The pinned surface. Adding or removing a subcommand edits this list and the +/// contract's `surface_equals_snapshot` formula together. +const SUBCOMMANDS: &[&str] = &[ + "profile", "bench", "roofline", "diff", "contract", "trace", "explain", "tui", "baseline", + "doctor", "compete", +]; + +fn run(args: &[&str]) -> Output { + Command::new(BIN) + .args(args) + .output() + .expect("spawn the binary under test") +} + +fn advertised() -> Vec { + let out = run(&["--help"]); + assert!(out.status.success(), "--help must exit 0"); + String::from_utf8_lossy(&out.stdout) + .lines() + .skip_while(|l| !l.starts_with("Commands:")) + .skip(1) + .take_while(|l| !l.trim().is_empty()) + .filter_map(|l| l.split_whitespace().next().map(str::to_owned)) + .filter(|c| c != "help") + .collect() +} + +/// Three inputs no subcommand can process: a missing path, an empty file, garbage bytes. +fn unusable_inputs() -> [String; 3] { + let dir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("aprender-cgp-surface-gate"); + std::fs::create_dir_all(&dir).expect("create fixture dir"); + let empty = dir.join("empty.bin"); + let garbage = dir.join("garbage.bin"); + std::fs::write(&empty, b"").expect("write empty fixture"); + std::fs::write(&garbage, b"not a model {{{\x00\x01\xff").expect("write garbage fixture"); + [ + dir.join("does-not-exist").display().to_string(), + empty.display().to_string(), + garbage.display().to_string(), + ] +} + +fn assert_rejected(args: &[&str]) { + let out = run(args); + let code = out.status.code(); + let stderr = String::from_utf8_lossy(&out.stderr); + assert!( + matches!(code, Some(c) if c != 0 && c != 101) && !stderr.contains("panicked"), + "`aprender-cgp {}` must reject its input with a clean non-zero exit, got {code:?}\nstderr: {stderr}", + args.join(" ") + ); +} + +#[test] +fn version_reports_the_crate_version() { + let out = run(&["--version"]); + assert!(out.status.success()); + let stdout = String::from_utf8_lossy(&out.stdout); + assert!( + stdout.contains(env!("CARGO_PKG_VERSION")), + "--version printed {stdout:?}, not {}", + env!("CARGO_PKG_VERSION") + ); +} + +#[test] +fn advertised_subcommands_are_the_pinned_surface() { + assert_eq!(advertised(), SUBCOMMANDS); +} + +#[test] +fn every_advertised_subcommand_is_reachable() { + let cmds = advertised(); + assert!( + !cmds.is_empty(), + "--help advertised no subcommands; the parser is broken" + ); + for cmd in &cmds { + let out = run(&[cmd, "--help"]); + assert!( + out.status.success(), + "`aprender-cgp {cmd} --help` exited {:?}", + out.status.code() + ); + } +} + +#[test] +fn unknown_subcommand_is_a_usage_error() { + assert_eq!(run(&["no-such-subcommand"]).status.code(), Some(2)); +} + +#[test] +fn unknown_flag_is_a_usage_error() { + assert_eq!(run(&["--no-such-flag"]).status.code(), Some(2)); +} + +#[test] +fn unusable_input_is_rejected() { + let bad = unusable_inputs(); + for b in &bad { + assert_rejected(&["diff", "--baseline", b, "--current", b]); + assert_rejected(&["baseline", "--load", b]); + } + // An unknown target printed "Unknown target" and exited 0 until ONT-10 S5. + assert_rejected(&["explain", "no-such-target"]); +} diff --git a/crates/aprender-common/Cargo.toml b/crates/aprender-common/Cargo.toml index 1861e9cd0b..6bd5a61a52 100644 --- a/crates/aprender-common/Cargo.toml +++ b/crates/aprender-common/Cargo.toml @@ -12,7 +12,14 @@ categories = ["command-line-utilities", "text-processing"] [lib] name = "batuta_common" +[features] +default = [] +# #3745 S1: typed CLI argument roles (`batuta_common::cli_roles`). Optional so the +# default build of this crate stays clap-free. +cli-roles = ["dep:clap"] + [dependencies] +clap = { workspace = true, optional = true } lz4_flex = { version = "0.11", default-features = false, features = ["safe-encode", "safe-decode"] } zstd = { version = "0.13", default-features = false } diff --git a/crates/aprender-common/src/cli_roles.rs b/crates/aprender-common/src/cli_roles.rs new file mode 100644 index 0000000000..0cadb60ecb --- /dev/null +++ b/crates/aprender-common/src/cli_roles.rs @@ -0,0 +1,813 @@ +//! Typed CLI argument roles (#3745 S1, PMAT-3749). +//! +//! A release cell can only be derived for an argument whose ROLE the gate can +//! see. Before these types, the role of an `apr` argument was knowable only +//! from its name: `apr run SOURCE`, `--prompt` and `-i FILE` were a `String`, a +//! `String` and a `PathBuf`, the same types as `--name`, `--tag` and +//! `--output`. Every release list was therefore typed by hand, and a verb, flag +//! or input shape missing from that list was invisible to the gate. That is how +//! `apr run --prompt P --chat` shipped double-templated (#3743) while +//! `-i file --chat` was covered. +//! +//! Each type here is a transparent newtype over `PathBuf` or `String` whose clap +//! value parser is the SAME parser clap uses for the raw type +//! (`PathBufValueParser` / `StringValueParser`), mapped into the newtype. So a +//! migrated argument accepts exactly the values it accepted before. The only +//! thing that changes is the parser's output TYPE, and that is what `apr surface` +//! reads, through `ValueParser::type_id()`, to decide the role. The role is +//! declared by how the argument is BUILT; its name never enters into it. +//! +//! The types live here rather than in `apr-cli` because a marker compared by +//! `TypeId` needs ONE defining crate, and some of `apr`'s surface is declared by +//! crates that do not depend on `apr-cli` (`apr pv …` from +//! `aprender-contracts-cli`, `apr rag …` from `aprender-rag-cli`). + +use std::any::TypeId; +use std::ffi::OsStr; +use std::fmt; +use std::ops::Deref; +use std::path::{Path, PathBuf}; + +use clap::builder::{MapValueParser, PathBufValueParser, StringValueParser, TypedValueParser}; + +/// The role an argument plays in a release cell (#3745). +/// +/// `Mode` and `Backend` are not listed in [`MARKERS`]. Mode is structural (a +/// flag action, or a finite value set), and backend is membership in the shared +/// `BackendArg` group; `apr surface` decides both. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Role { + /// A model: a local file, or a reference `apr` resolves to one. + Model, + /// Text the model is prompted with. + Prompt, + /// A file the command consumes as input data (audio, text, a dataset). + InputFile, + /// The compute backend override. + Backend, + /// A generation sampling control: seed, temperature, top-k, top-p, + /// repeat penalty (S1 v1.1, for S2.5's flag-effect sampling controls). + Sampling, + /// A flag, or a value drawn from a finite set. + Mode, + /// Declared, and none of the above: outputs, directories, configs, names. + Other, + /// A free-form value no role type declares. Only a foreign subtree may + /// carry one; the marker guard is RED on it anywhere else. + Unknown, +} + +impl Role { + /// The spelling used in the `apr-cli-surface` JSON. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Model => "model", + Self::Prompt => "prompt", + Self::InputFile => "input-file", + Self::Backend => "backend", + Self::Sampling => "sampling", + Self::Mode => "mode", + Self::Other => "other", + Self::Unknown => "unknown", + } + } +} + +/// What a marker's underlying value is: a filesystem path, or free text. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Carrier { + /// Backed by `PathBuf`. + Path, + /// Backed by `String`. + Text, +} + +/// One role type: the `TypeId` its value parser yields, and what it declares. +#[derive(Debug, Clone, Copy)] +pub struct Marker { + /// The type's name as written in code, e.g. `ModelPath`. + pub name: &'static str, + /// The role this type declares. + pub role: Role, + /// Path- or text-backed. + pub carrier: Carrier, + type_id: fn() -> TypeId, +} + +impl Marker { + /// The `TypeId` of the marker type, which is the `TypeId` its clap value + /// parser reports. + #[must_use] + pub fn type_id(&self) -> TypeId { + (self.type_id)() + } +} + +macro_rules! path_role { + ($(#[$doc:meta])* $name:ident) => { + $(#[$doc])* + #[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct $name(PathBuf); + + // `Debug` is the inner value's, so `{:?}` of a migrated field prints + // `"m.gguf"` exactly as the raw `PathBuf` did, never `ModelPath("m.gguf")`. + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } + } + + impl $name { + /// Wrap a path. + #[must_use] + pub fn new(path: impl Into) -> Self { + Self(path.into()) + } + /// The path, borrowed. + #[must_use] + pub fn as_path(&self) -> &Path { + &self.0 + } + /// The path, owned. + #[must_use] + pub fn into_path_buf(self) -> PathBuf { + self.0 + } + } + + // `Path`, not `PathBuf`: the same target `PathBuf` derefs to, so + // `Option::as_deref()` is `Option<&Path>` exactly as + // `Option::as_deref()` was, and a migrated field reads the same. + impl Deref for $name { + type Target = Path; + fn deref(&self) -> &Path { + &self.0 + } + } + impl AsRef for $name { + fn as_ref(&self) -> &Path { + &self.0 + } + } + impl AsRef for $name { + fn as_ref(&self) -> &OsStr { + self.0.as_os_str() + } + } + impl From for $name { + fn from(p: PathBuf) -> Self { + Self(p) + } + } + impl From<&Path> for $name { + fn from(p: &Path) -> Self { + Self(p.to_path_buf()) + } + } + impl From<&str> for $name { + fn from(p: &str) -> Self { + Self(PathBuf::from(p)) + } + } + impl From for $name { + fn from(p: String) -> Self { + Self(PathBuf::from(p)) + } + } + impl From<$name> for PathBuf { + fn from(p: $name) -> PathBuf { + p.0 + } + } + impl PartialEq for $name { + fn eq(&self, other: &PathBuf) -> bool { + &self.0 == other + } + } + impl PartialEq for $name { + fn eq(&self, other: &Path) -> bool { + self.0 == other + } + } + impl clap::builder::ValueParserFactory for $name { + type Parser = MapValueParser $name>; + fn value_parser() -> Self::Parser { + PathBufValueParser::new().map($name as fn(PathBuf) -> $name) + } + } + }; +} + +macro_rules! text_role { + ($(#[$doc:meta])* $name:ident) => { + $(#[$doc])* + #[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct $name(String); + + // `Debug` is the inner value's: `{:?}` prints `"text"`, as `String` did. + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } + } + + impl $name { + /// Wrap a string. + #[must_use] + pub fn new(s: impl Into) -> Self { + Self(s.into()) + } + /// The text, borrowed. + #[must_use] + pub fn as_str(&self) -> &str { + &self.0 + } + /// The text as a `&String`, for callees that take one. + #[must_use] + pub fn as_string(&self) -> &String { + &self.0 + } + /// The text, owned. + #[must_use] + pub fn into_string(self) -> String { + self.0 + } + } + + // `str`, not `String`: the same target `String` derefs to, so + // `Option::as_deref()` is `Option<&str>` exactly as + // `Option::as_deref()` was. + impl Deref for $name { + type Target = str; + fn deref(&self) -> &str { + &self.0 + } + } + impl AsRef for $name { + fn as_ref(&self) -> &str { + &self.0 + } + } + impl AsRef for $name { + fn as_ref(&self) -> &Path { + Path::new(&self.0) + } + } + impl AsRef for $name { + fn as_ref(&self) -> &OsStr { + OsStr::new(&self.0) + } + } + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } + } + impl From for $name { + fn from(s: String) -> Self { + Self(s) + } + } + impl From<&str> for $name { + fn from(s: &str) -> Self { + Self(s.to_string()) + } + } + impl From<$name> for String { + fn from(s: $name) -> String { + s.0 + } + } + impl PartialEq for $name { + fn eq(&self, other: &str) -> bool { + self.0 == other + } + } + impl PartialEq<&str> for $name { + fn eq(&self, other: &&str) -> bool { + self.0 == *other + } + } + impl PartialEq for $name { + fn eq(&self, other: &String) -> bool { + &self.0 == other + } + } + impl clap::builder::ValueParserFactory for $name { + type Parser = MapValueParser $name>; + fn value_parser() -> Self::Parser { + StringValueParser::new().map($name as fn(String) -> $name) + } + } + }; +} + +path_role!( + /// A local model file (`.apr`, `.gguf`, `.safetensors`, …). + ModelPath +); +path_role!( + /// A file the command reads as input data, not as a model. + InputFile +); +path_role!( + /// A file the command writes. + OutputPath +); +path_role!( + /// A directory: a workspace, a cache, a corpus root, an output tree. + DirPath +); +path_role!( + /// A configuration, manifest, contract, schema or receipt the command + /// reads to decide what to do, as distinct from the data it operates on. + ConfigPath +); +text_role!( + /// A model reference: a local path, `hf://org/repo`, a URL, or a cache + /// name, which `apr` resolves to a model file. + ModelRef +); +text_role!( + /// Text the model is prompted with. + PromptText +); +text_role!( + /// Any other free text: a name, id, tag, URL, pattern or list. + FreeText +); +text_role!( + /// Text a model ENCODES or SCORES rather than generates from: `apr embed + /// --text`, `apr rerank --query/--passage`, `apr eval --text` (perplexity). + /// Role `other`, so the command does not read as a generator: `generates` + /// drives the thinking × context-rung cells, which mean nothing for an + /// encoder or a scorer (aprender-97 on #3745, v1.1). + EncodeText +); + +/// Command-level marker: this command GENERATES text from prompts that do not +/// arrive through a `PromptText` argument (#3745 S1, schema v1.1). +/// +/// `apr serve run` takes its prompts over HTTP, `apr chat` reads them from +/// stdin, and `apr mcp` receives them as JSON-RPC tool calls. None of them has a +/// prompt ARGUMENT, so a rule that reads "generates" off the prompt role alone +/// would derive no prompt-shape cells for the very verb whose 0.69.0 leak +/// (#3571) started this. The marker is attached to the subcommand itself, with +/// no argument and no field: +/// +/// ```ignore +/// #[command(group(batuta_common::cli_roles::ServesGeneration::group()))] +/// Run { /* … */ }, +/// ``` +/// +/// `apr surface` reports `generates: true` for a command that has a +/// `PromptText` argument OR carries this group, and it matches the group by +/// [`ServesGeneration::ID`], never by the command's name. +#[derive(Debug, Clone, Copy)] +pub struct ServesGeneration; + +impl ServesGeneration { + /// The group id the marker is recognised by. + pub const ID: &'static str = "batuta_common::cli_roles::ServesGeneration"; + + /// The argument-less group that marks a command as a generator. It names + /// no argument, so it changes neither parsing nor `--help`. + #[must_use] + pub fn group() -> clap::ArgGroup { + clap::ArgGroup::new(Self::ID).multiple(true) + } + + /// True iff `cmd` carries the marker. + #[must_use] + pub fn is_on(cmd: &clap::Command) -> bool { + cmd.get_groups().any(|g| g.get_id() == Self::ID) + } +} + +/// Which generation sampling control an argument is (#3745 S1, v1.1). S2 +/// derives its sampling cells per kind (sampled ≠ greedy, same seed twice is +/// byte-identical, seed A ≠ seed B, top-k 1 / T 0 is byte-greedy), so the kind +/// is declared, not read off the argument's name. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum SamplingKind { + /// RNG seed of the sampler. + Seed, + /// Softmax temperature. + Temperature, + /// Top-k cut-off. + TopK, + /// Nucleus (top-p) threshold. + TopP, + /// Min-p threshold. + MinP, + /// Repetition penalty. + RepeatPenalty, + /// Window the repetition penalty looks back over. + RepeatLastN, +} + +impl SamplingKind { + /// Every kind. + pub const ALL: [SamplingKind; 7] = [ + Self::Seed, + Self::Temperature, + Self::TopK, + Self::TopP, + Self::MinP, + Self::RepeatPenalty, + Self::RepeatLastN, + ]; + + /// The spelling used in the `apr-cli-surface` JSON (`sampling_kind`). + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Seed => "seed", + Self::Temperature => "temperature", + Self::TopK => "top_k", + Self::TopP => "top_p", + Self::MinP => "min_p", + Self::RepeatPenalty => "repeat_penalty", + Self::RepeatLastN => "repeat_last_n", + } + } + + /// The group id an argument of this kind joins: `#[arg(group = SamplingKind::Temperature.id())]`. + #[must_use] + pub const fn id(self) -> &'static str { + match self { + Self::Seed => "batuta_common::cli_roles::SamplingArg::seed", + Self::Temperature => "batuta_common::cli_roles::SamplingArg::temperature", + Self::TopK => "batuta_common::cli_roles::SamplingArg::top_k", + Self::TopP => "batuta_common::cli_roles::SamplingArg::top_p", + Self::MinP => "batuta_common::cli_roles::SamplingArg::min_p", + Self::RepeatPenalty => "batuta_common::cli_roles::SamplingArg::repeat_penalty", + Self::RepeatLastN => "batuta_common::cli_roles::SamplingArg::repeat_last_n", + } + } +} + +/// Argument-level marker for a generation SAMPLING control (#3745 S1, v1.1). +/// +/// These are numbers, so a newtype would change every field's type and every +/// call site that reads it. Instead the argument joins the argument-less group +/// of its [`SamplingKind`], and the command declares those groups once, each +/// `multiple(true)`, so nothing about parsing changes: +/// +/// ```ignore +/// #[command(groups(batuta_common::cli_roles::SamplingArg::groups()))] +/// Run { +/// #[arg(long, default_value = "0.0", +/// group = batuta_common::cli_roles::SamplingKind::Temperature.id())] +/// temperature: f32, +/// } +/// ``` +/// +/// `apr surface` reports role `sampling` and `sampling_kind` for a member of +/// one of these groups, matched by id, never by the argument's name. +#[derive(Debug, Clone, Copy)] +pub struct SamplingArg; + +impl SamplingArg { + /// One argument-less `multiple(true)` group per [`SamplingKind`], for + /// `#[command(groups(SamplingArg::groups()))]`. + #[must_use] + pub fn groups() -> Vec { + SamplingKind::ALL + .iter() + .map(|k| clap::ArgGroup::new(k.id()).multiple(true)) + .collect() + } + + /// The sampling kind of `arg` on `built`, if it has one. `built` must be a + /// BUILT command: clap merges each argument's `group = …` into the + /// command's groups only when the command is built. + #[must_use] + pub fn kind_of(built: &clap::Command, arg: &clap::Arg) -> Option { + SamplingKind::ALL.into_iter().find(|k| { + built + .get_groups() + .any(|g| g.get_id() == k.id() && g.get_args().any(|a| a == arg.get_id())) + }) + } +} + +/// Owned `String`s from a slice of text role values, for callees that take +/// `&[String]`. +#[must_use] +pub fn strings>(values: &[T]) -> Vec { + values.iter().map(|v| v.as_ref().to_string()).collect() +} + +/// Owned `PathBuf`s from a slice of path role values, for callees that take +/// `&[PathBuf]`. +#[must_use] +pub fn path_bufs>(values: &[T]) -> Vec { + values.iter().map(|v| v.as_ref().to_path_buf()).collect() +} + +/// Every role type, in one table. `apr surface` reads roles through it, and the +/// marker guard refuses a free-form argument whose parser yields none of these. +pub const MARKERS: &[Marker] = &[ + Marker { + name: "ModelPath", + role: Role::Model, + carrier: Carrier::Path, + type_id: TypeId::of::, + }, + Marker { + name: "ModelRef", + role: Role::Model, + carrier: Carrier::Text, + type_id: TypeId::of::, + }, + Marker { + name: "PromptText", + role: Role::Prompt, + carrier: Carrier::Text, + type_id: TypeId::of::, + }, + Marker { + name: "InputFile", + role: Role::InputFile, + carrier: Carrier::Path, + type_id: TypeId::of::, + }, + Marker { + name: "OutputPath", + role: Role::Other, + carrier: Carrier::Path, + type_id: TypeId::of::, + }, + Marker { + name: "DirPath", + role: Role::Other, + carrier: Carrier::Path, + type_id: TypeId::of::, + }, + Marker { + name: "ConfigPath", + role: Role::Other, + carrier: Carrier::Path, + type_id: TypeId::of::, + }, + Marker { + name: "FreeText", + role: Role::Other, + carrier: Carrier::Text, + type_id: TypeId::of::, + }, + Marker { + name: "EncodeText", + role: Role::Other, + carrier: Carrier::Text, + type_id: TypeId::of::, + }, +]; + +/// The marker whose type a value parser yields, if any. +#[must_use] +pub fn marker_for(type_id: TypeId) -> Option<&'static Marker> { + MARKERS.iter().find(|m| m.type_id() == type_id) +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Arg, Command}; + + fn parser_type() -> TypeId + where + T::Parser: TypedValueParser + Send + Sync + 'static, + ::Value: Send + Sync + Clone + 'static, + { + let vp: clap::builder::ValueParser = T::value_parser().into(); + let arg = Arg::new("x").value_parser(vp); + // `AnyValueId` is clap's; it compares equal to a std `TypeId`. + let id = arg.get_value_parser().type_id(); + MARKERS + .iter() + .map(Marker::type_id) + .chain([TypeId::of::(), TypeId::of::()]) + .find(|t| id == *t) + .expect("value parser yields a known type") + } + + #[test] + fn every_marker_parser_reports_its_own_type_id() { + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + assert_eq!(parser_type::(), TypeId::of::()); + } + + #[test] + fn marker_table_is_one_to_one_with_types() { + let mut ids: Vec = MARKERS.iter().map(Marker::type_id).collect(); + ids.sort(); + ids.dedup(); + assert_eq!(ids.len(), MARKERS.len(), "two markers share a TypeId"); + assert!( + marker_for(TypeId::of::()).is_none(), + "raw PathBuf must not be a marker" + ); + assert!( + marker_for(TypeId::of::()).is_none(), + "raw String must not be a marker" + ); + assert_eq!( + marker_for(TypeId::of::()).map(|m| m.role), + Some(Role::Model) + ); + assert_eq!( + marker_for(TypeId::of::()).map(|m| m.role), + Some(Role::Prompt) + ); + } + + /// A migrated argument must accept exactly what the raw type accepted: the + /// marker parser is the raw parser, mapped. An empty path is refused by + /// both, a normal value is accepted by both. + #[test] + fn marker_parsers_accept_what_the_raw_parsers_accept() { + let raw = Command::new("t").arg(Arg::new("p").value_parser(clap::value_parser!(PathBuf))); + let typed = + Command::new("t").arg(Arg::new("p").value_parser(clap::value_parser!(ModelPath))); + for argv in [vec!["t", "m.gguf"], vec!["t", ""], vec!["t", "-"]] { + let a = raw.clone().try_get_matches_from(&argv).is_ok(); + let b = typed.clone().try_get_matches_from(&argv).is_ok(); + assert_eq!(a, b, "PathBuf vs ModelPath disagree on {argv:?}"); + } + let m = typed.try_get_matches_from(["t", "m.gguf"]).expect("parses"); + assert_eq!( + m.get_one::("p").map(ModelPath::as_path), + Some(Path::new("m.gguf")) + ); + + let raw = Command::new("t").arg(Arg::new("s").value_parser(clap::value_parser!(String))); + let typed = + Command::new("t").arg(Arg::new("s").value_parser(clap::value_parser!(PromptText))); + for argv in [vec!["t", "What is 2+2?"], vec!["t", ""]] { + let a = raw.clone().try_get_matches_from(&argv).is_ok(); + let b = typed.clone().try_get_matches_from(&argv).is_ok(); + assert_eq!(a, b, "String vs PromptText disagree on {argv:?}"); + } + } + + /// The generation marker names no argument, so it changes neither what + /// parses nor what `--help` prints, and it is found by id alone. + #[test] + fn serves_generation_marks_a_command_without_changing_it() { + let plain = Command::new("t").arg(Arg::new("port").long("port")); + let marked = plain.clone().group(ServesGeneration::group()); + assert!(!ServesGeneration::is_on(&plain)); + assert!(ServesGeneration::is_on(&marked)); + for argv in [vec!["t"], vec!["t", "--port", "8080"], vec!["t", "--nope"]] { + let a = plain.clone().try_get_matches_from(&argv).is_ok(); + let b = marked.clone().try_get_matches_from(&argv).is_ok(); + assert_eq!(a, b, "the marker changed parsing of {argv:?}"); + } + let help = |c: &Command| c.clone().render_long_help().to_string(); + assert_eq!(help(&plain), help(&marked), "the marker changed --help"); + // A look-alike group spelled with the type's short name is not the marker. + let lookalike = plain.group(clap::ArgGroup::new("ServesGeneration").multiple(true)); + assert!(!ServesGeneration::is_on(&lookalike)); + } + + /// A migrated field must print the same under `{:?}` as the raw type did: + /// error messages and tests format arguments that way. + #[test] + fn debug_output_is_the_raw_types() { + assert_eq!( + format!("{:?}", ModelPath::from("m.gguf")), + format!("{:?}", PathBuf::from("m.gguf")) + ); + assert_eq!( + format!("{:?}", Some(OutputPath::from("o"))), + format!("{:?}", Some(PathBuf::from("o"))) + ); + assert_eq!( + format!("{:?}", PromptText::from("hi")), + format!("{:?}", String::from("hi")) + ); + assert_eq!( + format!("{:?}", vec![FreeText::from("a")]), + format!("{:?}", vec![String::from("a")]) + ); + } + + #[test] + fn derive_picks_the_marker_parser_for_a_marker_field() { + #[derive(clap::Parser)] + struct T { + model: ModelPath, + #[arg(long)] + prompt: Option, + #[arg(long)] + inputs: Vec, + } + use clap::{CommandFactory, Parser}; + let cmd = T::command(); + let id = |name: &str| { + cmd.get_arguments() + .find(|a| a.get_id() == name) + .map(|a| a.get_value_parser().type_id()) + .expect("arg exists") + }; + assert!(id("model") == TypeId::of::()); + assert!(id("prompt") == TypeId::of::()); + assert!(id("inputs") == TypeId::of::()); + let t = T::try_parse_from(["t", "a.apr", "--prompt", "hi", "--inputs", "x.wav"]) + .expect("parses"); + assert_eq!(t.model.as_path(), Path::new("a.apr")); + assert_eq!(t.prompt.as_deref(), Some("hi")); + assert_eq!(t.inputs.len(), 1); + } + + /// The spellings are the `apr-cli-surface` JSON contract: a renamed or + /// blank spelling silently changes what `apr surface` reports. + #[test] + fn role_and_sampling_spellings_are_the_json_contract() { + let roles = [ + (Role::Model, "model"), + (Role::Prompt, "prompt"), + (Role::InputFile, "input-file"), + (Role::Backend, "backend"), + (Role::Sampling, "sampling"), + (Role::Mode, "mode"), + (Role::Other, "other"), + (Role::Unknown, "unknown"), + ]; + for (role, s) in roles { + assert_eq!(role.as_str(), s); + } + let kinds = [ + "seed", + "temperature", + "top_k", + "top_p", + "min_p", + "repeat_penalty", + "repeat_last_n", + ]; + for (k, s) in SamplingKind::ALL.iter().zip(kinds) { + assert_eq!(k.as_str(), s); + assert_eq!( + k.id(), + format!("batuta_common::cli_roles::SamplingArg::{s}") + ); + } + } + + /// One `multiple(true)` group per kind, each named by that kind's id. + #[test] + fn sampling_groups_are_one_per_kind() { + let groups = SamplingArg::groups(); + let ids: Vec<&str> = groups.iter().map(|g| g.get_id().as_str()).collect(); + let want: Vec<&str> = SamplingKind::ALL.iter().map(|k| k.id()).collect(); + assert_eq!(ids, want); + assert!(groups.into_iter().all(|mut g| g.is_multiple())); + } + + /// `kind_of` finds each argument's own kind, and none for an argument + /// outside every sampling group, even when the groups are declared. + #[test] + fn kind_of_reads_the_group_an_argument_joined() { + let mut cmd = Command::new("t").groups(SamplingArg::groups()); + for k in SamplingKind::ALL { + cmd = cmd.arg(Arg::new(k.as_str()).long(k.as_str()).group(k.id())); + } + cmd = cmd.arg(Arg::new("port").long("port")); + cmd.build(); + for k in SamplingKind::ALL { + let arg = cmd + .get_arguments() + .find(|a| a.get_id() == k.as_str()) + .expect("arg exists"); + assert_eq!(SamplingArg::kind_of(&cmd, arg), Some(k), "{k:?}"); + } + let port = cmd + .get_arguments() + .find(|a| a.get_id() == "port") + .expect("arg exists"); + assert_eq!(SamplingArg::kind_of(&cmd, port), None); + } + + #[test] + fn strings_and_path_bufs_keep_every_value_in_order() { + let texts = [FreeText::from("a"), FreeText::from("b")]; + assert_eq!(strings(&texts), vec!["a".to_string(), "b".to_string()]); + assert!(strings::(&[]).is_empty()); + let paths = [InputFile::from("x.wav"), InputFile::from("y.wav")]; + assert_eq!( + path_bufs(&paths), + vec![PathBuf::from("x.wav"), PathBuf::from("y.wav")] + ); + } +} diff --git a/crates/aprender-common/src/lib.rs b/crates/aprender-common/src/lib.rs index 7ee0cb44c6..4128ca843a 100644 --- a/crates/aprender-common/src/lib.rs +++ b/crates/aprender-common/src/lib.rs @@ -8,11 +8,14 @@ //! - [`fmt`] — Byte, percentage, duration, and number formatting //! - [`sys`] — System information (cgroup detection, CPU info) //! - [`display`] — Column alignment, truncation, and builder traits +//! - `cli_roles` — typed CLI argument roles (feature `cli-roles`, #3745) // `.unwrap()` is banned in production via workspace `.clippy.toml`, but is // idiomatic in `#[cfg(test)]` assertions. Allow it only under `cfg(test)`. #![cfg_attr(test, allow(clippy::disallowed_methods))] +#[cfg(feature = "cli-roles")] +pub mod cli_roles; pub mod compression; pub mod display; pub mod fmt; diff --git a/crates/aprender-compute/Cargo.toml b/crates/aprender-compute/Cargo.toml index b514ee73b5..b664d994d3 100644 --- a/crates/aprender-compute/Cargo.toml +++ b/crates/aprender-compute/Cargo.toml @@ -41,11 +41,11 @@ futures-intrusive = { version = "0.5", optional = true } wasm-bindgen-futures = { version = "0.4", optional = true } wasm-bindgen = { version = "0.2", optional = true } # Native CUDA monitoring via trueno-gpu (TRUENO-SPEC-010) -trueno-gpu = { version = "0.69.3", path = "../aprender-gpu", optional = true, package = "aprender-gpu" } +trueno-gpu = { version = "0.70.0", path = "../aprender-gpu", optional = true, package = "aprender-gpu" } # PMAT-336: Provable contracts compile-time enforcement provable-contracts-macros = { workspace = true } # P1a: Sovereign GEMM microkernel codegen (compile-time shape specialization) -trueno-gemm-codegen = { version = "0.69.3", path = "../aprender-gemm-codegen", package = "aprender-gemm-codegen" } +trueno-gemm-codegen = { version = "0.70.0", path = "../aprender-gemm-codegen", package = "aprender-gemm-codegen" } # Tracing/profiling support (renacer integration) tracing = { version = "0.1", optional = true } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"], optional = true } @@ -84,6 +84,7 @@ web-sys = { version = "0.3", optional = true, features = ["console"] } hostname = "0.4" [build-dependencies] +provable-contracts = { workspace = true } # #4369: build_helper::env_key, the one CONTRACT_* key builder serde = { version = "1", features = ["derive"] } serde_yaml_ng = "0.10" diff --git a/crates/aprender-compute/build.rs b/crates/aprender-compute/build.rs index 5c500f8acc..5431c4b79c 100644 --- a/crates/aprender-compute/build.rs +++ b/crates/aprender-compute/build.rs @@ -36,13 +36,10 @@ struct Binding { /// Convert a contract filename + equation into a canonical env var name. fn env_var_name(contract: &str, equation: &str) -> String { - let stem = contract - .trim_end_matches(".yaml") - .trim_end_matches(".yml") - .to_uppercase() - .replace('-', "_"); - let eq = equation.to_uppercase().replace('-', "_"); - format!("CONTRACT_{stem}_{eq}") + provable_contracts::build_helper::env_key( + contract.trim_end_matches(".yaml").trim_end_matches(".yml"), + equation, + ) } /// Rank status values for deduplication. @@ -55,14 +52,8 @@ fn status_rank(s: &str) -> u8 { } fn main() { - let binding_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("..") - .join("provable-contracts") - .join("contracts") - .join("trueno") - .join("binding.yaml"); + let binding_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../contracts/trueno/binding.yaml"); println!("cargo:rerun-if-changed={}", binding_path.display()); diff --git a/crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs b/crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs index 57f7111d77..d4d4cf59ec 100644 --- a/crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs +++ b/crates/aprender-compute/src/backends/gpu/device/linalg/wgsl_forward.rs @@ -108,6 +108,18 @@ pub struct WgslForwardPass { num_kv_heads: u32, head_dim: u32, intermediate_dim: u32, + /// RMSNorm epsilon, passed to the shader as `params.y` (f32 bits). Defaults to + /// [`DEFAULT_RMS_NORM_EPS`]; callers set the model's value with + /// [`WgslForwardPass::set_rms_norm_eps`] (#4056: Llama-family models use 1e-5). + rms_norm_eps: f32, +} + +/// Default RMSNorm epsilon: the constant the shader hardcoded before #4056. +pub const DEFAULT_RMS_NORM_EPS: f32 = 1e-6; + +/// Uniform params for the RMSNorm shader: `(dim, eps as f32 bits, 0, 0)`. +fn rmsnorm_params(dim: u32, eps: f32) -> [u32; 4] { + [dim, eps.to_bits(), 0, 0] } // WGSL shader source for RMSNorm (multi-row via workgroup_id.y) @@ -116,7 +128,7 @@ const RMSNORM_SHADER: &str = r#" @group(0) @binding(0) var input: array; @group(0) @binding(1) var weight: array; @group(0) @binding(2) var output: array; -@group(0) @binding(3) var params: vec4; // (dim, 0, 0, 0) +@group(0) @binding(3) var params: vec4; // (dim, bitcast(eps), 0, 0) var shared_sum: array; @@ -149,7 +161,8 @@ fn main(@builtin(local_invocation_id) lid: vec3, stride >>= 1u; } - let rms = sqrt(shared_sum[0] / f32(dim) + 1e-6); + let eps = bitcast(params.y); + let rms = sqrt(shared_sum[0] / f32(dim) + eps); // Normalize and scale i = tid; @@ -548,9 +561,22 @@ impl WgslForwardPass { num_kv_heads: num_kv_heads as u32, head_dim: head_dim as u32, intermediate_dim: intermediate_dim as u32, + rms_norm_eps: DEFAULT_RMS_NORM_EPS, } } + /// Set the RMSNorm epsilon every norm in this forward pass uses — pass the + /// model's configured `rms_norm_eps`. Default: [`DEFAULT_RMS_NORM_EPS`]. + pub fn set_rms_norm_eps(&mut self, eps: f32) { + self.rms_norm_eps = eps; + } + + /// The RMSNorm epsilon this forward pass uses. + #[must_use] + pub fn rms_norm_eps(&self) -> f32 { + self.rms_norm_eps + } + /// Upload a weight matrix (call once per layer at init). /// PMAT-342: Bias weights (name contains "bias") are stored CPU-side. pub fn upload_weight(&mut self, name: &str, data: &[f32]) { @@ -1954,7 +1980,7 @@ impl WgslForwardPass { output: &wgpu::Buffer, dim: u32, ) { - let params = [dim, 0u32, 0, 0]; + let params = rmsnorm_params(dim, self.rms_norm_eps); let params_buf = self.make_uniform(¶ms); let bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, @@ -2156,3 +2182,35 @@ fn bgl_uniform(binding: u32) -> wgpu::BindGroupLayoutEntry { count: None, } } + +#[cfg(test)] +mod rmsnorm_eps_tests { + use super::{rmsnorm_params, DEFAULT_RMS_NORM_EPS}; + + /// #4056: the shader reads eps from `params.y`; it must carry the configured + /// value bit-exactly, not the old hardcoded 1e-6. + #[test] + fn rmsnorm_params_carry_eps_bits() { + let p = rmsnorm_params(896, 1e-5); + assert_eq!(p[0], 896); + assert_eq!(f32::from_bits(p[1]), 1e-5); + assert_ne!(f32::from_bits(p[1]), DEFAULT_RMS_NORM_EPS); + assert_eq!(f32::from_bits(rmsnorm_params(1, DEFAULT_RMS_NORM_EPS)[1]), 1e-6); + } + + /// #4056: the shader now reads `bitcast(params.y)`; naga must accept it. + /// Skips (returns) when no GPU adapter is present. + #[test] + fn rmsnorm_shader_validates_on_device() { + let Ok(gpu) = crate::backends::gpu::GpuDevice::new() else { + return; + }; + gpu.device.push_error_scope(wgpu::ErrorFilter::Validation); + let _module = gpu.device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("rmsnorm_eps_test"), + source: wgpu::ShaderSource::Wgsl(super::RMSNORM_SHADER.into()), + }); + let err = pollster::block_on(gpu.device.pop_error_scope()); + assert!(err.is_none(), "RMSNORM_SHADER failed validation: {err:?}"); + } +} diff --git a/crates/aprender-compute/src/backends/gpu/shaders/cooperative.rs b/crates/aprender-compute/src/backends/gpu/shaders/cooperative.rs deleted file mode 100644 index 07a57cc920..0000000000 --- a/crates/aprender-compute/src/backends/gpu/shaders/cooperative.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Cooperative matrix GEMM shader — hardware tensor core acceleration -//! -//! Uses VK_KHR_cooperative_matrix via wgpu 29's WGSL extension. -//! GB10 Blackwell: M=16, K=16, N=16, F16 input, F32 accumulation. -//! -//! Contract: cooperative-matrix-gemm-v1 -//! Lean theorem: ProvableContracts.CooperativeMatrix.matmul_block_sum - -/// Cooperative matrix GEMM: C = α * A[M,K] @ B[K,N] -/// -/// Dispatch: ceil(N/16) × ceil(M/16) workgroups, @workgroup_size(32) -/// Fallback: if cooperative matrix not available, use TILED_GEMM_SHADER. -pub const COOPERATIVE_GEMM_SHADER: &str = r#" -enable f16; -enable wgpu_cooperative_matrix; - -@group(0) @binding(0) var a: array; -@group(0) @binding(1) var b: array; -@group(0) @binding(2) var c: array; - -struct Dimensions { - M: u32, - K: u32, - N: u32, - alpha: f32, -} - -@group(0) @binding(3) var dims: Dimensions; - -@compute @workgroup_size(32) -fn main( - @builtin(workgroup_id) wg_id: vec3, -) { - // No early return — cooperative ops require uniform control flow - let tile_row = wg_id.y * 16u; - let tile_col = wg_id.x * 16u; - - var acc = coop_mat16x16(); - let num_k_tiles = (dims.K + 15u) / 16u; - for (var kt = 0u; kt < num_k_tiles; kt++) { - let k_offset = kt * 16u; - let a_tile = coopLoad>( - &a[tile_row * dims.K + k_offset], dims.K - ); - let b_tile = coopLoad>( - &b[k_offset * dims.N + tile_col], dims.N - ); - acc = coopMultiplyAdd(a_tile, b_tile, acc); - } - // Only store if within bounds (uniform control flow maintained above) - if (tile_row < dims.M && tile_col < dims.N) { - coopStore(acc, &c[tile_row * dims.N + tile_col], dims.N); - } -} -"#; diff --git a/crates/aprender-compute/src/generated_contracts.rs b/crates/aprender-compute/src/generated_contracts.rs index 876a7c7eb4..f669762984 100644 --- a/crates/aprender-compute/src/generated_contracts.rs +++ b/crates/aprender-compute/src/generated_contracts.rs @@ -8934,18 +8934,6 @@ macro_rules! contract_inv_quality_gate { }}; } -// Auto-generated from contracts/cooperative-matrix-gemm-v1.yaml — DO NOT EDIT -// Contract: cooperative-matrix-gemm-v1 - -/// Invariants for equation `cooperative_gemm`. -/// Check after computation: `contract_inv_cooperative_gemm!(result_expr)` -macro_rules! contract_inv_cooperative_gemm { - () => {{}}; - ($result:expr) => {{ - let _contract_result = &$result; - }}; -} - // Auto-generated from contracts/copia-delta-v1.yaml — DO NOT EDIT // Contract: copia-delta-v1 diff --git a/crates/aprender-contracts-cli/Cargo.toml b/crates/aprender-contracts-cli/Cargo.toml index 5a43b29a00..563e1467e5 100644 --- a/crates/aprender-contracts-cli/Cargo.toml +++ b/crates/aprender-contracts-cli/Cargo.toml @@ -12,17 +12,32 @@ repository.workspace = true name = "pv" path = "src/main.rs" +# ONT-5: the reasoner. Private to this target (F-7); the library holds only the graph and the checker. +[[bin]] +name = "pv-sat" +path = "src/bin/pv-sat/main.rs" + [dependencies] -aprender-contracts = { path = "../aprender-contracts", version = "0.69.3" } +aprender-contracts = { path = "../aprender-contracts", version = "0.70.0" } +# #3745 S1: `--model` is declared through the shared role type `ModelPath`, so +# `apr surface` reads `apr pv verify-structure --model` as a model argument. +batuta-common = { workspace = true, features = ["cli-roles"] } clap = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } sha2 = { workspace = true } -aprender-update = { workspace = true } +# #4604 C2: optional so the crates.io pv builds from crates that are live there; +# release binaries and in-tree builds pass `--features update-check,build-sha`. +aprender-update = { workspace = true, optional = true } [build-dependencies] -aprender-build-sha = { workspace = true } # #4219: APR_GIT_SHA for --version +aprender-build-sha = { workspace = true, optional = true } # #4219: APR_GIT_SHA for --version + +[features] +# Both off by default (#4604 C2, B2a): pv publishes to crates.io without them. +update-check = ["dep:aprender-update"] +build-sha = ["dep:aprender-build-sha"] [dev-dependencies] tempfile = "3" diff --git a/crates/aprender-contracts-cli/build.rs b/crates/aprender-contracts-cli/build.rs index 961a8293c3..d244d00f5b 100644 --- a/crates/aprender-contracts-cli/build.rs +++ b/crates/aprender-contracts-cli/build.rs @@ -1,6 +1,21 @@ // #4219: stamp APR_GIT_SHA so `--version` prints ` ()`, // the shape `apr --version` has carried since #597. Resolution order and // rerun triggers live in the shared `aprender-build-sha` crate. +#[cfg(feature = "build-sha")] fn main() { build_sha::emit(); } + +// #4604 C2: without `build-sha` (the crates.io default) the stamp is the +// release override when one is set, else the same `v+no-git` fallback +// `build_sha` ends on. +#[cfg(not(feature = "build-sha"))] +fn main() { + let sha = std::env::var("APR_GIT_SHA_OVERRIDE") + .ok() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| format!("v{}+no-git", env!("CARGO_PKG_VERSION"))); + println!("cargo:rustc-env=APR_GIT_SHA={sha}"); + println!("cargo:rerun-if-env-changed=APR_GIT_SHA_OVERRIDE"); +} diff --git a/crates/aprender-contracts-cli/src/bin/pv-sat/liskov.rs b/crates/aprender-contracts-cli/src/bin/pv-sat/liskov.rs new file mode 100644 index 0000000000..bd4b222e94 --- /dev/null +++ b/crates/aprender-contracts-cli/src/bin/pv-sat/liskov.rs @@ -0,0 +1,200 @@ +//! ONT-4e (R-20): the Liskov half of pv-sat. For every checkable `A refines B` it decides the three obligations and +//! writes the certificate the `refines` gate re-checks: `contracts/witness/liskov/.json`. +//! +//! | obligation | premise | conclusion | broken when | +//! |------------|----------------|----------------|--------------------------------------------| +//! | pre | B.requires | A.requires | A demands something B did not (strengthened) | +//! | post | A.ensures | B.ensures | A drops a promise B made (weakened) | +//! | inv | A.invariants | B.invariants | A drops an invariant B kept (dropped) | +//! +//! Atoms are opaque: a conclusion clause is implied only by a premise clause with the same `formal` (whitespace +//! collapsed). The direction is spelled out HERE, not borrowed from the library's `Kind::sides`, so a reasoner and a +//! checker that disagree about which way refinement runs disagree on every pair, and the checker refuses the +//! witness. Before it writes, pv-sat runs `pc_liskov_reasoner`: a planted strengthened precondition must come back as +//! a counter-model naming it, and pass the library checker. + +use std::path::Path; + +use provable_contracts::ontology::liskov::{ + check, liskov_corpus, liskov_sha256, liskov_witness_path, Clauses, CounterModel, Kind, + LiskovWitness, Obligation, Pair, PairClass, PairWitness, Step, +}; +use provable_contracts::ontology::witness::FIRED; +use provable_contracts::schema::{Clause, FormalStatus}; + +/// Decide one obligation: a chain when every conclusion clause has a same-atom premise, else the counter-model +/// making exactly the premises true. +fn decide(kind: Kind, premise: &[Clause], conclusion: &[Clause]) -> Obligation { + let mut chain = Vec::new(); + let mut violated = Vec::new(); + for c in conclusion { + match premise + .iter() + .find(|p| p.atom().is_some() && p.atom() == c.atom()) + { + Some(p) => chain.push(Step { + clause: c.id.clone(), + from: p.id.clone(), + }), + None => violated.push(c.id.clone()), + } + } + if violated.is_empty() { + Obligation { + kind, + chain: Some(chain), + counter_model: None, + } + } else { + Obligation { + kind, + chain: None, + counter_model: Some(CounterModel { + violated, + holds: premise.iter().map(|p| p.id.clone()).collect(), + }), + } + } +} + +/// The three obligations of `a refines b`, each in its own direction. +pub fn reason(p: &Pair) -> PairWitness { + let (a, b) = (&p.a_clauses, &p.b_clauses); + PairWitness { + a: p.a.clone(), + b: p.b.clone(), + obligations: vec![ + decide(Kind::Pre, &b.requires, &a.requires), + decide(Kind::Post, &a.ensures, &b.ensures), + decide(Kind::Inv, &a.invariants, &b.invariants), + ], + } +} + +/// The witness for `pairs`, before `pc_reasoner` and the git sha are stamped on it. +pub fn witness( + pairs: &[Pair], + pc_reasoner: &str, + reasoner_git_sha: Option, +) -> LiskovWitness { + LiskovWitness { + liskov_sha256: liskov_sha256(pairs), + pairs_checked: pairs.len(), + reasoner_git_sha, + pc_reasoner: pc_reasoner.into(), + pairs: pairs.iter().map(reason).collect(), + } +} + +fn parsed(id: &str, formal: &str) -> Clause { + Clause { + id: id.into(), + statement: id.into(), + formal: Some(formal.into()), + formal_status: FormalStatus::Parsed, + } +} + +/// The plant: `a` keeps `b`'s precondition and adds `PRE-2`; its postcondition and invariant are `b`'s. +pub fn plant() -> Pair { + Pair { + a: "a".into(), + b: "b".into(), + a_clauses: Clauses { + requires: vec![parsed("PRE-1", "len(x) > 0"), parsed("PRE-2", "len(x) > 1")], + ensures: vec![parsed("POST-1", "len(y) = len(x)")], + invariants: vec![parsed("INV-1", "y ≥ 0")], + }, + b_clauses: Clauses { + requires: vec![parsed("PRE-1", "len(x) > 0")], + ensures: vec![parsed("POST-1", "len(y) = len(x)")], + invariants: vec![parsed("INV-1", "y ≥ 0")], + }, + } +} + +/// `pc_liskov_reasoner`: `Ok(FIRED)` when the plant's ONLY violation is `a refines b: precondition strengthened +/// (PRE-2)`, and the library checker proves exactly that from the reasoner's witness. +pub fn pc_reasoner() -> Result<&'static str, String> { + let pairs = [plant()]; + let w = witness(&pairs, FIRED, None); + let found = check(&pairs, &w) + .map_err(|e| format!("the checker refused the reasoner's witness for the plant: {e}"))?; + let found: Vec = found.iter().map(ToString::to_string).collect(); + if found == ["a refines b: precondition strengthened (PRE-2)"] { + Ok(FIRED) + } else { + Err(format!( + "the plant strengthens exactly PRE-2, and the reasoner proved {found:?}" + )) + } +} + +/// The checkable pairs of `dir`, read the way the gate reads them. +pub fn checkable_pairs( + dir: &Path, + edges: &std::collections::BTreeSet, +) -> Vec { + let docs = provable_contracts::lint::relations_gate::corpus_documents(dir); + liskov_corpus(&docs, edges) + .pairs + .into_iter() + .filter(|p| p.class() == PairClass::Checkable) + .collect() +} + +/// Write (or confirm) the Liskov witness for `pairs` under `dir`. Nothing is written when there is no checkable +/// pair. Exit-code semantics are the caller's: `Err(1)` a control or the self-check failed, `Err(3)` an I/O error. +pub fn write(dir: &Path, pairs: &[Pair], git_sha: Option) -> Result<(), u8> { + let pc = pc_reasoner().map_err(|e| { + eprintln!("pv-sat: pc_liskov_reasoner did not fire, no Liskov witness written: {e}"); + 1 + })?; + let sha = liskov_sha256(pairs); + let path = liskov_witness_path(dir, &sha); + if pairs.is_empty() { + // Nothing to certify: a witness left from a corpus that had pairs names nothing, so it goes. + let dir_exists = path.parent().is_some_and(Path::is_dir); + return if dir_exists { + super::prune(&path).map_err(|()| 3) + } else { + Ok(()) + }; + } + let fresh = std::fs::read_to_string(&path) + .ok() + .and_then(|t| serde_json::from_str::(&t).ok()) + .filter(|w| w.liskov_sha256 == sha && w.pc_reasoner == FIRED && check(pairs, w).is_ok()); + if fresh.is_some() { + println!( + "pv-sat: {} is fresh ({} pair(s))", + path.display(), + pairs.len() + ); + return super::prune(&path).map_err(|()| 3); + } + let w = witness(pairs, pc, git_sha); + let violations = check(pairs, &w).map_err(|e| { + eprintln!("pv-sat: the reasoner's own Liskov witness does not check, none written: {e}"); + 1 + })?; + let written = path + .parent() + .map_or(Ok(()), std::fs::create_dir_all) + .and_then(|()| { + let mut text = serde_json::to_string_pretty(&w).map_err(std::io::Error::other)?; + text.push('\n'); + std::fs::write(&path, text) + }); + if let Err(e) = written { + eprintln!("pv-sat: cannot write {}: {e}", path.display()); + return Err(3); + } + println!( + "pv-sat: wrote {} ({} pair(s); {} Liskov violation(s))", + path.display(), + pairs.len(), + violations.len() + ); + super::prune(&path).map_err(|()| 3) +} diff --git a/crates/aprender-contracts-cli/src/bin/pv-sat/main.rs b/crates/aprender-contracts-cli/src/bin/pv-sat/main.rs new file mode 100644 index 0000000000..d68cf0762a --- /dev/null +++ b/crates/aprender-contracts-cli/src/bin/pv-sat/main.rs @@ -0,0 +1,243 @@ +//! `pv-sat` — ONT-5's reasoner. Decides whether a corpus's typed relations are jointly satisfiable and writes the +//! certificate the `ont-consistency` gate re-checks: `contracts/witness/.json`. +//! +//! ```text +//! pv-sat [CONTRACT_DIR] write (or confirm) the witness; default `contracts` +//! pv-sat --self-test the reasoner's plant, its satisfiable twin, and the checker's corrupt core +//! (and ONT-4e's: `pc_liskov_reasoner`, `pc_liskov_checker`) +//! ``` +//! +//! Private to this bin target (F-7): the library exports the graph and the checker, and nothing in it can reach +//! the reasoner. Before it writes, pv-sat runs its plant (`pc_reasoner`); a reasoner that cannot find the planted +//! core never writes a witness. A witness already on disk that names the same graph and still checks is left +//! untouched, so `make contracts` leaves a clean tree clean. Other `.json` files in `witness/` are pruned — +//! one graph, one witness. +//! +//! ONT-4e: once the consistency witness stands, pv-sat also certifies every checkable `A refines B` (R-20) in +//! `contracts/witness/liskov/.json` — see `liskov.rs`. +//! +//! Exit: 0 written or confirmed · 1 a control failed · 2 nothing to reason over (no Σ, no typed relation) · 3 Σ +//! malformed or the witness could not be written. + +mod liskov; +mod plant; +mod sat; + +use std::path::{Path, PathBuf}; +use std::process::ExitCode; +use std::time::Instant; + +use provable_contracts::lint::relations_gate::{typed_graph, TypedGraph}; +use provable_contracts::ontology::witness::{ + census_id_set_sha256, check, pc_checker, relations_sha256, witness_path, ClauseSet, Witness, + FIRED, +}; + +fn main() -> ExitCode { + let args: Vec = std::env::args().skip(1).collect(); + match args.as_slice() { + [flag] if flag == "--self-test" => self_test(), + [flag] if flag == "-h" || flag == "--help" => { + println!("usage: pv-sat [CONTRACT_DIR] | pv-sat --self-test | pv-sat --version"); + ExitCode::SUCCESS + } + // G0.1 (#4476): the same ` ()` shape as `pv --version`. + [flag] if flag == "-V" || flag == "--version" => { + println!("{}", version_line()); + ExitCode::SUCCESS + } + [] => write_witness(Path::new("contracts")), + [dir] => write_witness(Path::new(dir)), + _ => { + eprintln!("usage: pv-sat [CONTRACT_DIR] | pv-sat --self-test | pv-sat --version"); + ExitCode::from(3) + } + } +} + +fn version_line() -> String { + format!( + "pv-sat {} ({}) (aprender ONT-5 reasoner)", + env!("CARGO_PKG_VERSION"), + env!("APR_GIT_SHA") + ) +} + +fn self_test() -> ExitCode { + let checks: [(&str, Result<(), String>); 5] = [ + ("pc_reasoner", plant::pc_reasoner().map(|_| ())), + ("pc_model", plant::pc_model()), + ("pc_checker", pc_checker().map(|_| ())), + ("pc_liskov_reasoner", liskov::pc_reasoner().map(|_| ())), + ( + "pc_liskov_checker", + provable_contracts::ontology::liskov::pc_checker().map(|_| ()), + ), + ]; + let mut ok = true; + for (name, r) in &checks { + match r { + Ok(()) => println!("pv-sat self-test: {name} {FIRED}"), + Err(e) => { + ok = false; + eprintln!("pv-sat self-test: {name} FAILED: {e}"); + } + } + } + if ok { + ExitCode::SUCCESS + } else { + ExitCode::from(1) + } +} + +/// The consistency witness, then (ONT-4e) the Liskov witness for every checkable `refines` pair. +fn write_witness(dir: &Path) -> ExitCode { + let code = write_consistency(dir); + if code != ExitCode::SUCCESS { + return code; + } + let TypedGraph::Read { edges, .. } = typed_graph(dir) else { + return code; + }; + let pairs = liskov::checkable_pairs(dir, &edges); + liskov::write(dir, &pairs, git_head(dir)).map_or_else(ExitCode::from, |()| ExitCode::SUCCESS) +} + +fn write_consistency(dir: &Path) -> ExitCode { + let pc = match plant::pc_reasoner() { + Ok(fired) => fired, + Err(e) => { + eprintln!("pv-sat: pc_reasoner did not fire, no witness written: {e}"); + return ExitCode::from(1); + } + }; + let (ids, edges) = match typed_graph(dir) { + TypedGraph::NoSigma => { + eprintln!( + "pv-sat: no {}/ontology.yaml — nothing to reason over", + dir.display() + ); + return ExitCode::from(2); + } + TypedGraph::Malformed(e) => { + eprintln!("pv-sat: Σ is malformed: {e}"); + return ExitCode::from(3); + } + TypedGraph::Read { ids, edges } => (ids, edges), + }; + let cs = ClauseSet::from_graph(&ids, &edges); + if cs.checkable_n() == 0 { + eprintln!( + "pv-sat: no typed relation clause in {} contracts — nothing to reason over", + ids.len() + ); + return ExitCode::from(2); + } + let census_sha = census_id_set_sha256(&ids); + let relations_sha = relations_sha256(&edges); + let path = witness_path(dir, &relations_sha); + + if let Some(w) = fresh_witness(&path, &cs, &census_sha, &relations_sha) { + println!( + "pv-sat: {} is fresh ({}; checkable_n {})", + path.display(), + kind(&w), + w.checkable_n + ); + return prune(&path).map_or(ExitCode::from(3), |()| ExitCode::SUCCESS); + } + + let start = Instant::now(); + let result = sat::solve(&cs); + let cpu_ms = u64::try_from(start.elapsed().as_millis()).unwrap_or(u64::MAX); + if let Err(e) = check(&cs, &result) { + eprintln!("pv-sat: the reasoner's own answer does not check, no witness written: {e}"); + return ExitCode::from(1); + } + let witness = Witness { + census_id_set_sha256: census_sha, + relations_sha256: relations_sha, + reasoner_git_sha: git_head(dir), + checkable_n: cs.checkable_n(), + result, + pc_reasoner: pc.to_string(), + cpu_ms, + }; + let written = path + .parent() + .map_or(Ok(()), std::fs::create_dir_all) + .and_then(|()| { + let mut text = serde_json::to_string_pretty(&witness).map_err(std::io::Error::other)?; + text.push('\n'); + std::fs::write(&path, text) + }); + if let Err(e) = written { + eprintln!("pv-sat: cannot write {}: {e}", path.display()); + return ExitCode::from(3); + } + println!( + "pv-sat: wrote {} ({}; checkable_n {}; {} ms)", + path.display(), + kind(&witness), + witness.checkable_n, + cpu_ms + ); + prune(&path).map_or(ExitCode::from(3), |()| ExitCode::SUCCESS) +} + +fn kind(w: &Witness) -> &'static str { + match w.result { + provable_contracts::ontology::witness::WitnessResult::UnsatCore(_) => "unsat_core", + provable_contracts::ontology::witness::WitnessResult::Model(_) => "model", + } +} + +/// The witness on disk, when it names this graph, its reasoner's plant fired, and it still checks. +fn fresh_witness(path: &Path, cs: &ClauseSet, census: &str, relations: &str) -> Option { + let w: Witness = serde_json::from_str(&std::fs::read_to_string(path).ok()?).ok()?; + let fresh = w.census_id_set_sha256 == census + && w.relations_sha256 == relations + && w.pc_reasoner == FIRED + && w.checkable_n == cs.checkable_n() + && check(cs, &w.result).is_ok(); + fresh.then_some(w) +} + +/// Remove every other `<64 hex>.json` beside `keep`. +fn prune(keep: &Path) -> Result<(), ()> { + let Some(dir) = keep.parent() else { + return Ok(()); + }; + let entries = std::fs::read_dir(dir) + .map_err(|e| eprintln!("pv-sat: cannot list {}: {e}", dir.display()))?; + for entry in entries.flatten() { + let p: PathBuf = entry.path(); + let is_witness = p.extension().is_some_and(|e| e == "json") + && p.file_stem().and_then(|s| s.to_str()).is_some_and(|s| { + s.len() == 64 + && s.bytes() + .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) + }); + if is_witness && p != keep { + std::fs::remove_file(&p) + .map_err(|e| eprintln!("pv-sat: cannot prune {}: {e}", p.display()))?; + println!("pv-sat: pruned {}", p.display()); + } + } + Ok(()) +} + +fn git_head(dir: &Path) -> Option { + let out = std::process::Command::new("git") + .arg("-C") + .arg(dir) + .args(["rev-parse", "HEAD"]) + .output() + .ok()?; + let sha = String::from_utf8(out.stdout).ok()?.trim().to_string(); + (out.status.success() && sha.len() == 40).then_some(sha) +} + +#[cfg(test)] +mod tests; diff --git a/crates/aprender-contracts-cli/src/bin/pv-sat/plant.rs b/crates/aprender-contracts-cli/src/bin/pv-sat/plant.rs new file mode 100644 index 0000000000..7928d14625 --- /dev/null +++ b/crates/aprender-contracts-cli/src/bin/pv-sat/plant.rs @@ -0,0 +1,63 @@ +//! `pc_reasoner`: the plant pv-sat must see through before it may write a witness. +//! +//! `A⇒B, B⇒C, contradicts(A,C)` with only `A` asserted is unsatisfiable, and its only core is `[A, B, C]` — +//! a reasoner that stops one implication early, or that "finds" the conflict without deriving `C`, draws +//! something else. The core must also pass the library's checker: a reasoner and a checker that disagree about +//! the plant cannot both be trusted with the corpus. + +use std::collections::BTreeSet; + +use provable_contracts::ontology::witness::{check, Checked, ClauseSet, WitnessResult, FIRED}; + +use crate::sat::solve; + +/// The planted clause set. +pub fn plant() -> ClauseSet { + let s = |x: &str| x.to_string(); + ClauseSet { + units: [s("A")].into(), + implies: [(s("A"), s("B")), (s("B"), s("C"))].into(), + conflicts: [(s("A"), s("C"))].into(), + } +} + +/// `Ok(FIRED)` when the reasoner draws the core `[A, B, C]` and the checker accepts it. +pub fn pc_reasoner() -> Result<&'static str, String> { + let cs = plant(); + let result = solve(&cs); + let WitnessResult::UnsatCore(_) = &result else { + return Err(format!( + "the plant is unsatisfiable and the reasoner answered {result:?}" + )); + }; + match check(&cs, &result) { + Ok(Checked::Unsat { core }) => { + let want: BTreeSet = ["A", "B", "C"].map(String::from).into(); + if core == want { + Ok(FIRED) + } else { + Err(format!( + "the plant's core is [A, B, C] and the reasoner drew {core:?}" + )) + } + } + Ok(Checked::Sat) => Err("the checker read the plant's core as a model".into()), + Err(e) => Err(format!( + "the checker refused the reasoner's core for the plant: {e}" + )), + } +} + +/// The satisfiable twin of the plant — the conflict removed — must yield a model the checker accepts, so a +/// reasoner that answers "unsat" to everything cannot pass `--self-test`. +pub fn pc_model() -> Result<(), String> { + let mut cs = plant(); + cs.conflicts.clear(); + let result = solve(&cs); + match check(&cs, &result) { + Ok(Checked::Sat) => Ok(()), + other => Err(format!( + "the plant without its conflict is satisfiable, and got {other:?}" + )), + } +} diff --git a/crates/aprender-contracts-cli/src/bin/pv-sat/sat.rs b/crates/aprender-contracts-cli/src/bin/pv-sat/sat.rs new file mode 100644 index 0000000000..279890be4c --- /dev/null +++ b/crates/aprender-contracts-cli/src/bin/pv-sat/sat.rs @@ -0,0 +1,75 @@ +//! The reasoner: Horn-SAT by unit propagation, with a certificate for either answer. +//! +//! Propagation is breadth-first from the units in sorted order, so the derivation — and the witness — is the same +//! bytes on every run for the same clause set. On a conflict the core is the derivation of its two sides and +//! nothing else, in the order propagation reached each variable, which is an order the checker accepts: every +//! premise is derived before the implication that uses it. + +use std::collections::{BTreeMap, VecDeque}; + +use provable_contracts::ontology::witness::{ + implication_adjacency, variables, ClauseSet, Core, Model, Step, WitnessResult, +}; + +/// Each derived variable: (derivation index, the premise that derived it; `None` for a unit). +type Derived<'a> = BTreeMap<&'a str, (usize, Option<&'a str>)>; + +/// Decide `cs`: an `unsat_core` for the first violated conflict (in sorted order), else the least model. +pub fn solve(cs: &ClauseSet) -> WitnessResult { + let derived = propagate(cs); + let violated = cs + .conflicts + .iter() + .find(|(a, b)| derived.contains_key(a.as_str()) && derived.contains_key(b.as_str())); + if let Some((a, b)) = violated { + let mut needed: BTreeMap = BTreeMap::new(); + trace(&derived, a, &mut needed); + trace(&derived, b, &mut needed); + return WitnessResult::UnsatCore(Core { + steps: needed.into_values().collect(), + conflict: (a.clone(), b.clone()), + }); + } + WitnessResult::Model(Model { + false_vars: variables(cs) + .into_iter() + .filter(|v| !derived.contains_key(v.as_str())) + .collect(), + }) +} + +/// Breadth-first unit propagation from the sorted units. +fn propagate(cs: &ClauseSet) -> Derived<'_> { + let adj = implication_adjacency(cs); + let mut derived: Derived<'_> = BTreeMap::new(); + let mut queue: VecDeque<&str> = VecDeque::new(); + for u in &cs.units { + derived.insert(u.as_str(), (derived.len(), None)); + queue.push_back(u.as_str()); + } + while let Some(v) = queue.pop_front() { + for &w in adj.get(v).map_or(&[][..], Vec::as_slice) { + if !derived.contains_key(w) { + derived.insert(w, (derived.len(), Some(v))); + queue.push_back(w); + } + } + } + derived +} + +/// Add the derivation of `side` to `needed`, back to its unit or to a step already there. +fn trace(derived: &Derived<'_>, side: &str, needed: &mut BTreeMap) { + let mut v = side; + while let Some(&(idx, premise)) = derived.get(v) { + if needed.contains_key(&idx) { + return; + } + let Some(p) = premise else { + needed.insert(idx, Step::Unit(v.to_string())); + return; + }; + needed.insert(idx, Step::Implies(p.to_string(), v.to_string())); + v = p; + } +} diff --git a/crates/aprender-contracts-cli/src/bin/pv-sat/tests.rs b/crates/aprender-contracts-cli/src/bin/pv-sat/tests.rs new file mode 100644 index 0000000000..c632147f69 --- /dev/null +++ b/crates/aprender-contracts-cli/src/bin/pv-sat/tests.rs @@ -0,0 +1,234 @@ +//! The reasoner is only trusted through the checker, so the property here is "whatever `solve` says, `check` +//! accepts" — swept over seeded random clause sets (stdlib LCG, no proptest) — plus the controls and the writer's +//! file discipline over a copy of `tests/fixtures/ont/relations-ok`. + +use std::collections::BTreeSet; +use std::path::Path; + +use provable_contracts::lint::consistency_gate::{run_consistency_gate, ConsistencyOutcome}; +use provable_contracts::ontology::witness::{Checked, Core, Step, WitnessResult}; + +use super::*; + +fn s(x: &str) -> String { + x.to_string() +} + +#[test] +fn the_plant_draws_its_core_in_derivation_order() { + assert_eq!( + sat::solve(&plant::plant()), + WitnessResult::UnsatCore(Core { + steps: vec![ + Step::Unit(s("A")), + Step::Implies(s("A"), s("B")), + Step::Implies(s("B"), s("C")), + ], + conflict: (s("A"), s("C")), + }) + ); + assert_eq!(plant::pc_reasoner(), Ok(FIRED)); + assert_eq!(plant::pc_model(), Ok(())); + assert_eq!(pc_checker(), Ok(FIRED)); +} + +/// Deterministic LCG (Knuth MMIX constants). +struct Lcg(u64); +impl Lcg { + fn next(&mut self, n: u64) -> usize { + self.0 = self + .0 + .wrapping_mul(6_364_136_223_846_793_005) + .wrapping_add(1_442_695_040_888_963_407); + usize::try_from((self.0 >> 33) % n).expect("small") + } +} + +#[test] +fn every_answer_the_reasoner_gives_checks() { + let names: Vec = (0..8).map(|i| format!("v{i}")).collect(); + let (mut sat_n, mut unsat_n) = (0, 0); + for seed in 0..500_u64 { + let mut r = Lcg(seed); + let mut cs = ClauseSet { + units: BTreeSet::new(), + implies: BTreeSet::new(), + conflicts: BTreeSet::new(), + }; + for _ in 0..=r.next(3) { + cs.units.insert(names[r.next(8)].clone()); + } + for _ in 0..r.next(10) { + let (a, b) = (r.next(8), r.next(8)); + if a != b { + cs.implies.insert((names[a].clone(), names[b].clone())); + } + } + for _ in 0..r.next(5) { + let (a, b) = (r.next(8), r.next(8)); + if a != b { + cs.conflicts + .insert((names[a.min(b)].clone(), names[a.max(b)].clone())); + } + } + let first = sat::solve(&cs); + assert_eq!(first, sat::solve(&cs), "seed {seed}: not deterministic"); + match check(&cs, &first) { + Ok(Checked::Sat) => sat_n += 1, + Ok(Checked::Unsat { .. }) => unsat_n += 1, + Err(e) => panic!( + "seed {seed}: the reasoner's own answer does not check: {e}\n{cs:?}\n{first:?}" + ), + } + } + // The sweep must visit both answers, or it proves half the claim. + assert!(sat_n > 50 && unsat_n > 50, "sat {sat_n} / unsat {unsat_n}"); +} + +fn corpus() -> tempfile::TempDir { + let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/ont/relations-ok"); + let dir = tempfile::tempdir().expect("tempdir"); + for entry in std::fs::read_dir(src).expect("fixture") { + let entry = entry.expect("entry"); + std::fs::copy(entry.path(), dir.path().join(entry.file_name())).expect("copy"); + } + dir +} + +fn exit(code: ExitCode) -> String { + format!("{code:?}") +} + +#[test] +fn the_written_witness_is_the_one_the_gate_checks_and_a_rerun_leaves_it() { + let dir = corpus(); + let stray = dir + .path() + .join("witness") + .join(format!("{}.json", "a".repeat(64))); + std::fs::create_dir_all(stray.parent().expect("dir")).expect("mkdir"); + std::fs::write(&stray, "{}").expect("stray"); + let notes = dir.path().join("witness").join("README.json"); + std::fs::write(¬es, "{}").expect("notes"); + + assert_eq!(exit(write_witness(dir.path())), exit(ExitCode::SUCCESS)); + assert!(!stray.exists(), "another graph's witness was not pruned"); + assert!(notes.exists(), "a file that is not a witness was pruned"); + + // relations-ok is inconsistent (`a contradicts d`, both live): the gate must reach a verdict, and it is a Fail. + match run_consistency_gate(dir.path()) { + ConsistencyOutcome::Ran { findings, .. } => { + let ids: Vec<&str> = findings.iter().map(|f| f.rule_id.as_str()).collect(); + assert_eq!(ids, ["PV-ONT-022"]); + } + other => panic!("expected a verdict, got {other:?}"), + } + + let written: Vec<_> = std::fs::read_dir(dir.path().join("witness")) + .expect("witness dir") + .flatten() + .map(|e| e.path()) + .filter(|p| p.file_name() != notes.file_name()) + .collect(); + assert_eq!(written.len(), 1); + let before = std::fs::read(&written[0]).expect("witness"); + let mtime = std::fs::metadata(&written[0]) + .and_then(|m| m.modified()) + .expect("mtime"); + assert_eq!(exit(write_witness(dir.path())), exit(ExitCode::SUCCESS)); + assert_eq!(std::fs::read(&written[0]).expect("witness"), before); + assert_eq!( + std::fs::metadata(&written[0]) + .and_then(|m| m.modified()) + .expect("mtime"), + mtime + ); +} + +#[test] +fn nothing_to_reason_over_is_exit_2_and_a_broken_sigma_exit_3() { + let dir = corpus(); + std::fs::remove_file(dir.path().join("ontology.yaml")).expect("rm"); + assert_eq!(exit(write_witness(dir.path())), exit(ExitCode::from(2))); + assert!(!dir.path().join("witness").exists()); + + let dir = corpus(); + std::fs::write(dir.path().join("ontology.yaml"), "roles: [unclosed\n").expect("write"); + assert_eq!(exit(write_witness(dir.path())), exit(ExitCode::from(3))); +} + +/// ONT-4e: the Liskov plant is found, and the reasoner's directions are the ones the checker proves — a strengthened +/// precondition, a weakened postcondition and a dropped invariant each name their clause, and a weakened +/// precondition / strengthened postcondition are Liskov. +#[test] +fn the_liskov_reasoner_decides_each_direction_the_checker_proves() { + use provable_contracts::ontology::liskov::{check, Clauses, Pair}; + use provable_contracts::schema::{Clause, FormalStatus}; + + assert_eq!(liskov::pc_reasoner(), Ok(FIRED)); + assert_eq!( + provable_contracts::ontology::liskov::pc_checker(), + Ok(FIRED) + ); + + let c = |id: &str, f: &str| Clause { + id: s(id), + statement: s(id), + formal: Some(s(f)), + formal_status: FormalStatus::Parsed, + }; + let verdict = |a: Clauses, b: Clauses| -> Vec { + let pairs = [Pair { + a: s("a"), + b: s("b"), + a_clauses: a, + b_clauses: b, + }]; + let w = liskov::witness(&pairs, FIRED, None); + check(&pairs, &w) + .expect("the reasoner's witness checks") + .iter() + .map(ToString::to_string) + .collect() + }; + let req = |v: Vec| Clauses { + requires: v, + ..Clauses::default() + }; + let ens = |v: Vec| Clauses { + ensures: v, + ..Clauses::default() + }; + let inv = |v: Vec| Clauses { + invariants: v, + ..Clauses::default() + }; + assert_eq!( + verdict(req(vec![c("PRE-1", "p")]), Clauses::default()), + ["a refines b: precondition strengthened (PRE-1)"] + ); + assert!(verdict(Clauses::default(), req(vec![c("PRE-1", "p")])).is_empty()); + assert_eq!( + verdict(Clauses::default(), ens(vec![c("POST-1", "q")])), + ["a refines b: postcondition weakened (POST-1)"] + ); + assert!(verdict(ens(vec![c("POST-1", "q")]), Clauses::default()).is_empty()); + assert_eq!( + verdict(Clauses::default(), inv(vec![c("INV-1", "r")])), + ["a refines b: invariant dropped (INV-1)"] + ); +} + +/// G0.1 (#4476): `pv-sat --version` exists and names the semver and the commit. +#[test] +fn g0_1_version_line_names_semver_and_commit() { + let v = version_line(); + let f: Vec<&str> = v.split_whitespace().collect(); + assert_eq!(f.first(), Some(&"pv-sat"), "{v}"); + assert_eq!(f.get(1), Some(&env!("CARGO_PKG_VERSION")), "{v}"); + assert_eq!( + f.get(2).copied(), + Some(format!("({})", env!("APR_GIT_SHA")).as_str()), + "{v}" + ); +} diff --git a/crates/aprender-contracts-cli/src/cli.rs b/crates/aprender-contracts-cli/src/cli.rs index c790ff16f8..487a1247da 100644 --- a/crates/aprender-contracts-cli/src/cli.rs +++ b/crates/aprender-contracts-cli/src/cli.rs @@ -84,6 +84,17 @@ pub enum Commands { /// Path to the new contract YAML file new: PathBuf, }, + /// What the Lean proofs rest on: axiom subset pins and the compiler-escape allowlist (PVL-001 EV-6a, #4139) + Discharge { + #[command(subcommand)] + action: DischargeAction, + }, + /// Pin each contract-bound theorem's STATEMENT apart from its proof: `/Challenge/.lean` + /// (PVL-001 EV-7a, #4200) + Challenge { + #[command(subcommand)] + action: ChallengeAction, + }, /// Census the contract corpus: one cardinality, by_anchoring, by_entity_type (ONT-001 ONT-1) Census { /// Directory containing contract YAML files @@ -109,9 +120,18 @@ pub enum Commands { /// the tracked contracts.nt / shapes.ttl untouched (aprender#3715) #[arg(long)] out: Option, + /// With `--release-*` and `--surface`: write every DERIVED release cell (the producer's work list, keyed + /// by `cell_id`) to this JSON file (aprender#3745 S2) + #[arg(long)] + cells_out: Option, #[command(flatten)] release: Box, }, + /// Σ as OWL and its advisory TBox (ONT-001 §3.8, ONT-2c) + Ontology { + #[command(subcommand)] + command: OntologyCommand, + }, /// Show cross-contract obligation coverage report Coverage { /// Directory containing contract YAML files @@ -182,10 +202,10 @@ pub enum Commands { /// Path to binding registry YAML (adds binding coverage) #[arg(long)] binding: Option, - /// L5 gate: before counting a binding as implemented, verify its - /// `function` actually exists in source (scanned from the given root, - /// default `.`). Phantom "implemented" bindings are downgraded, so L5 - /// means "verified as implemented", not self-declared. + /// No-op alias (PVL-001 EV-2): `--binding` now ALWAYS resolves every + /// `implemented` binding against source with the `pv verify-bindings` + /// resolver, lists ghosts under `GHOST BINDINGS (n)` and exits 1. Kept so + /// existing invocations still parse; its root argument is ignored. #[arg(long, num_args = 0..=1, default_missing_value = ".")] verify_bindings: Option, /// Output format: text (default) or json @@ -209,8 +229,9 @@ pub enum Commands { /// Path to binding registry YAML #[arg(long)] binding: Option, - /// Output format: text (default), json, sarif, github - #[arg(short, long)] + /// Output format: text (default), json, sarif, github, html. + /// G1.3 (#4476): parsed by clap, so an unknown value exits 2 and names these. + #[arg(short, long, value_parser = ["text", "json", "sarif", "github", "html"])] format: Option, /// Minimum severity to report: error, warning, info #[arg(long)] @@ -287,9 +308,11 @@ pub enum Commands { /// merge-base(HEAD, origin/main), else the origin/main tip; with neither, NOT CHECKED is printed. #[arg(long)] armed_baseline_ref: Option, - /// Run ONE named gate and report only it (ONT-001 section 5 ONT-2b): `--gate sigma`. + /// Run ONE named gate and report only it (ONT-001 section 5 ONT-2b): `--gate sigma`. Repeatable + /// (PVL-001 EV-11): every named gate runs and reports, and the exit is their meet — a refusal over a + /// reject over a decline over a pass. #[arg(long)] - gate: Option, + gate: Vec, /// With `--gate shapes`: grade only this shape family (the shape and every `.*` shape), armed /// whatever `armed_shapes` says (aprender#3715: `--shape release-readiness-v1`). #[arg(long)] @@ -374,6 +397,17 @@ pub enum Commands { #[arg(long, default_value = "20")] top: usize, }, + /// Obligation gate (PVL-001 EV-10): every contract under ROOT/contracts validates, hides no + /// test under `falsification:`, and binds each `applies_to` to a `fn` under ROOT/src that + /// mentions the contract's `proved_type`. Replaces pmat's `scripts/pv-obligation-gate.py`. + Obligations { + /// Repository root holding `contracts/` and `src/` + #[arg(default_value = ".")] + root: PathBuf, + /// Exit 1 (`reject:`) when any problem is found; without it the report exits 0 + #[arg(long)] + gate: bool, + }, /// Remove enforcement level lock from a contract (requires --reason) Unlock { /// Path to the contract YAML file @@ -442,8 +476,9 @@ pub enum Commands { contract_dir: PathBuf, #[arg(long)] config: Option, + /// #3745 S1: typed `ModelPath`, which is how `apr surface` knows this is a model. #[arg(long)] - model: Option, + model: Option, }, /// Verify compositional shape flow across contract dependency graph #[command(name = "verify-pipeline")] @@ -473,6 +508,131 @@ pub enum Commands { }, } +/// `pv discharge` actions (PVL-001 EV-6a, #4139). +#[derive(Subcommand, Clone, Debug)] +pub enum DischargeAction { + /// Generate `/Axioms.lean`: a subset axiom pin per contract-bound theorem in the root's import cone + GenAxioms { + /// The Lean dir (holds ProvableContracts.lean) + lean_dir: PathBuf, + /// Directory of the contracts whose `lean_theorem:` references bind the roots + #[arg(long, default_value = "contracts")] + contracts: PathBuf, + /// Do not write: rc 1 when the tracked Axioms.lean differs from its regeneration + #[arg(long)] + check: bool, + }, + /// Judge the tree: escapes vs escape-allowlist.yaml, exact-name roots, the label ratchet, Axioms.lean + /// freshness, then `lake env lean Axioms.lean` (after `build.sh`) unless `--no-lake` + Check { + lean_dir: PathBuf, + #[arg(long, default_value = "contracts")] + contracts: PathBuf, + /// Skip the Lean elaboration of Axioms.lean + #[arg(long)] + no_lake: bool, + /// Allowlist entries still `confirmed_by: pending` are RED + #[arg(long)] + strict: bool, + /// Also judge `/formalization.yaml`: `main_results` listed by discharge-summary.json, + /// `status.axioms` the pinned kernel set, `sorry_count` the measured count; a missing file is RED + /// (PVL-001 EV-8b, #4082) + #[arg(long)] + validate_formalization: bool, + /// Also re-check the BUILT tree's .olean files: `timeout lake env leanchecker ProvableContracts` + /// (non-fresh; `--fresh`, which replays Mathlib, is the nightly's, PVL-F7). rc != 0 rejects; no + /// `leanchecker` in the toolchain declines (PVL-001 EV-6b, #4199) + #[arg(long, conflicts_with = "no_lake")] + leanchecker: bool, + /// `--leanchecker`'s wall-clock limit, seconds + #[arg(long, default_value_t = 3600, requires = "leanchecker")] + leanchecker_timeout: u64, + /// `--leanchecker` under `ulimit -v ` (virtual memory, KiB); unset = no limit + #[arg(long, requires = "leanchecker")] + leanchecker_ulimit_v: Option, + /// `--leanchecker` with at most N Lean worker threads (`LEAN_NUM_THREADS`), 1..=8: the default is also the ceiling. leanchecker replays one full + /// environment per concurrent module task, so memory scales with this: 51 threads took 58-67 GB (#4348) + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_THREADS, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_THREADS)), requires = "leanchecker")] + leanchecker_threads: u32, + /// `--leanchecker` inside `systemd-run --user --scope -p MemoryMax=G -p CPUQuota=%`: this memory cap, + /// GiB, 1..=24: a caller may lower it, never raise it (#4348: the host must stay usable) + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_MEMORY_MAX_GIB, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_MEMORY_MAX_GIB)), requires = "leanchecker")] + leanchecker_memory_max_gib: u32, + /// `--leanchecker`'s scope CPU quota, percent of one core, 1..=800 + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_CPU_QUOTA_PCT, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_CPU_QUOTA_PCT)), requires = "leanchecker")] + leanchecker_cpu_quota_pct: u32, + /// Run `--leanchecker` WITHOUT the systemd scope (a host with no user systemd). The thread cap still applies + #[arg(long, requires = "leanchecker")] + leanchecker_unscoped: bool, + /// Also run the comparator: `lake env lean --run scripts/Comparator.lean Challenge/*.lean` on the BUILT + /// tree. Each EV-7a challenge must be closed by a sorry-free solution of the SAME statement (sha256 of + /// the canonical type). No Challenge file, or zero rows, declines (PVL-001 EV-7b, #4201) + #[arg(long, conflicts_with = "no_lake")] + comparator: bool, + /// Wall-clock limit on each `lake env lean` call (Axioms.lean, the comparator), seconds. A call that + /// exceeds it is killed with its process group and rejects: a hang is RED, not a wait (#4239) + #[arg(long, default_value_t = crate::commands::discharge::LAKE_TIMEOUT_S)] + lake_timeout: u64, + }, + /// `build.sh`, then `check` with every arm (`--strict`, the comparator, `--leanchecker`), then write the + /// untracked full log `/discharge.json` and the TRACKED `/../discharge-summary.json` -- + /// on failure too. The Lean steps run only after `build.sh` exits 0 (PVL-001 EV-8a, #4202) + Run { + lean_dir: PathBuf, + #[arg(long, default_value = "contracts")] + contracts: PathBuf, + /// The leanchecker arm's wall-clock limit, seconds + #[arg(long, default_value_t = 3600)] + leanchecker_timeout: u64, + /// The leanchecker arm under `ulimit -v ` (virtual memory, KiB); unset = no limit + #[arg(long)] + leanchecker_ulimit_v: Option, + /// the leanchecker arm with at most N Lean worker threads (`LEAN_NUM_THREADS`), 1..=8: the default is also the ceiling. leanchecker replays one full + /// environment per concurrent module task, so memory scales with this: 51 threads took 58-67 GB (#4348) + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_THREADS, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_THREADS)))] + leanchecker_threads: u32, + /// the leanchecker arm inside `systemd-run --user --scope -p MemoryMax=G -p CPUQuota=%`: this memory cap, + /// GiB, 1..=24: a caller may lower it, never raise it (#4348: the host must stay usable) + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_MEMORY_MAX_GIB, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_MEMORY_MAX_GIB)))] + leanchecker_memory_max_gib: u32, + /// the leanchecker arm's scope CPU quota, percent of one core, 1..=800 + #[arg(long, default_value_t = crate::commands::discharge::LEANCHECKER_CPU_QUOTA_PCT, value_parser = clap::value_parser!(u32).range(1..=i64::from(crate::commands::discharge::LEANCHECKER_CPU_QUOTA_PCT)))] + leanchecker_cpu_quota_pct: u32, + /// Run the leanchecker arm WITHOUT the systemd scope (a host with no user systemd). The thread cap still applies + #[arg(long)] + leanchecker_unscoped: bool, + /// Wall-clock limit on each `lake env` call outside the leanchecker arm, seconds; a timeout rejects (#4239) + #[arg(long, default_value_t = crate::commands::discharge::LAKE_TIMEOUT_S)] + lake_timeout: u64, + }, + /// `make label-ratchet`: rewrite /unresolved-labels.json DOWNWARD (it never gains a label; a missing + /// file is seeded). `check` never writes it. + LabelRatchet { + lean_dir: PathBuf, + #[arg(long, default_value = "contracts")] + contracts: PathBuf, + }, +} + +/// `pv challenge` actions (PVL-001 EV-7a, #4200). +#[derive(Subcommand, Clone, Debug)] +pub enum ChallengeAction { + /// Write `/Challenge/.lean`: every bound theorem restated as `PvlChallenge.` with + /// its proof replaced by `sorry`. Stale files are removed. + Gen { + /// Directory of the contracts whose `lean_theorem:` references bind the roots + contracts: PathBuf, + /// The Lean dir (holds ProvableContracts.lean) + lean_dir: PathBuf, + }, + /// Regenerate in memory and compare with `/Challenge/`: rc 1 on any difference, rc 2 on zero + /// challenges + Check { + contracts: PathBuf, + lean_dir: PathBuf, + }, +} + /// `pv census` output format (ONT-001 ONT-1). #[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)] pub enum CensusFormat { @@ -508,6 +668,12 @@ pub struct ReleaseArgs { /// The tokenizer-parity receipts, apr vs the pinned llama.cpp (default: evidence/dogfood/tokenizer//) #[arg(long)] pub tokenizer_receipts: Option, + /// The release candidate's `apr surface --json` (#3745): the release cells are DERIVED from it + #[arg(long)] + pub surface: Option, + /// The CRUX receipts, apr vs its comparators (default: evidence/crux//) + #[arg(long)] + pub crux_receipts: Option, } impl ReleaseArgs { @@ -521,6 +687,8 @@ impl ReleaseArgs { || self.kernel_receipts.is_some() || self.dogfood_receipt.is_some() || self.tokenizer_receipts.is_some() + || self.surface.is_some() + || self.crux_receipts.is_some() } /// The subject, or `None` when no flag was passed. A partial set is refused, never completed by a default. @@ -548,6 +716,34 @@ impl ReleaseArgs { s.dogfood_receipt.clone_from(&self.dogfood_receipt); s.tokenizer_receipts_dir .clone_from(&self.tokenizer_receipts); + s.surface.clone_from(&self.surface); + s.crux_receipts_dir.clone_from(&self.crux_receipts); Ok(Some(s)) } } + +/// `pv ontology …` (ONT-001 §3.8, row ONT-2c). +#[derive(Subcommand, Clone, Debug)] +pub enum OntologyCommand { + /// Write Σ as OWL 2 EL functional syntax (the in-house writer; byte-deterministic) + Export { + /// Σ, the ontology declaration + #[arg(default_value = "contracts/ontology.yaml")] + sigma: PathBuf, + /// OWL 2 functional syntax. The only format this command writes; required so the output is named + #[arg(long)] + owl: bool, + /// Write `ontology.ofn` next to Σ instead of printing it + #[arg(long)] + write: bool, + }, + /// The told-closure TBox report (advisory; `tbox-report.json`). Exit 3 if its precondition fails + Tbox { + /// Σ, the ontology declaration + #[arg(default_value = "contracts/ontology.yaml")] + sigma: PathBuf, + /// Write `tbox-report.json` next to Σ instead of printing it + #[arg(long)] + write: bool, + }, +} diff --git a/crates/aprender-contracts-cli/src/commands/census.rs b/crates/aprender-contracts-cli/src/commands/census.rs index ab379a1d48..56e8c29f1f 100644 --- a/crates/aprender-contracts-cli/src/commands/census.rs +++ b/crates/aprender-contracts-cli/src/commands/census.rs @@ -120,6 +120,11 @@ pub struct Census { pub quarantined_n: usize, pub by_kind: BTreeMap, pub by_entity_type: BTreeMap, + /// ONT-4d: instances per Σ concept in the extracted graph AFTER the subsumption closure, so a concept + /// counts its sub-concepts' instances too. Empty when there is no well-formed Σ or the extraction refused + /// (the `sigma` / `shapes` gates report why); never a guess. + #[serde(default)] + pub by_concept: BTreeMap, pub by_anchoring: AnchoringCounts, /// sha256 over the sorted, unique contract ids (file stems), newline /// separated. Two corpora with the same ids hash the same; adding, removing @@ -271,9 +276,23 @@ pub fn census_of(dir: &Path) -> Result> { } census.id_set_sha256 = id_set_sha256(&ids); census.declared_external = declared_external(dir)?; + census.by_concept = by_concept(dir); Ok(census) } +/// ONT-4d: `concept → instances` over the closed graph (see [`Census::by_concept`]). +fn by_concept(dir: &Path) -> BTreeMap { + use provable_contracts::ontology::{extract, rdf::ont}; + let (Some(sigma), Ok(x)) = (extract::sigma_of(dir), extract::all(dir)) else { + return BTreeMap::new(); + }; + sigma + .concepts + .keys() + .map(|c| (c.clone(), x.graph.instances_of(&ont(c)).len())) + .collect() +} + /// Contracts held OUT of the corpus, counted but never parsed. The shared walker /// skips `quarantine/`, so this is its own scan: a number the census reports is a /// number the census measured. @@ -311,6 +330,7 @@ fn empty_census(n_files: usize, quarantined_n: usize) -> Census { quarantined_n, by_kind: BTreeMap::new(), by_entity_type: BTreeMap::new(), + by_concept: BTreeMap::new(), by_anchoring: AnchoringCounts::default(), id_set_sha256: String::new(), declared_external: Vec::new(), diff --git a/crates/aprender-contracts-cli/src/commands/challenge.rs b/crates/aprender-contracts-cli/src/commands/challenge.rs new file mode 100644 index 0000000000..8673cb8ac8 --- /dev/null +++ b/crates/aprender-contracts-cli/src/commands/challenge.rs @@ -0,0 +1,89 @@ +//! `pv challenge gen | check` (PVL-001 EV-7a, #4200). The rendering lives in +//! [`provable_contracts::discharge::challenge`]; this module writes or compares, and prints. +//! +//! Exit: 0 fresh · 1 reject (a statement could not be lifted, or `Challenge/` differs from its regeneration) +//! · 2 decline (the tree does not load, or zero challenges: nothing is pinned, which must not read as fresh). + +use std::path::Path; + +use provable_contracts::discharge::challenge::{self, Rendered, CHALLENGE_DIR}; + +use super::discharge::{DischargeDeclined, DischargeRejected}; +use crate::cli::ChallengeAction; + +type Res = Result<(), Box>; + +pub fn run(action: ChallengeAction) -> Res { + match action { + ChallengeAction::Gen { + contracts, + lean_dir, + } => gen(&contracts, &lean_dir), + ChallengeAction::Check { + contracts, + lean_dir, + } => check(&contracts, &lean_dir), + } +} + +fn load(contracts: &Path, lean_dir: &Path) -> Result { + let r = challenge::render(lean_dir, contracts).map_err(DischargeDeclined)?; + if r.files.is_empty() && r.unrestated.is_empty() { + return Err(DischargeDeclined(format!( + "zero challenges: no contract under {} binds a theorem of {}", + contracts.display(), + lean_dir.display() + ))); + } + Ok(r) +} + +/// One FAIL line per root whose statement could not be lifted; the count. +fn report_unrestated(r: &Rendered) -> usize { + for (stem, fqn, why) in &r.unrestated { + println!("FAIL UNRESTATED {stem}: {fqn}: {why}"); + } + r.unrestated.len() +} + +fn gen(contracts: &Path, lean_dir: &Path) -> Res { + let r = load(contracts, lean_dir)?; + let written = challenge::write(lean_dir, &r) + .map_err(|e| DischargeDeclined(format!("{}/{CHALLENGE_DIR}: {e}", lean_dir.display())))?; + for rel in &written { + println!("wrote {rel}"); + } + println!( + "{} challenge file(s) under {}/{CHALLENGE_DIR} ({} rewritten)", + r.files.len(), + lean_dir.display(), + written.len() + ); + match report_unrestated(&r) { + 0 => Ok(()), + n => Err(DischargeRejected(format!("{n} bound theorem(s) have no challenge")).into()), + } +} + +fn check(contracts: &Path, lean_dir: &Path) -> Res { + let r = load(contracts, lean_dir)?; + let diffs = challenge::diff(lean_dir, &r); + for d in &diffs { + println!("FAIL STALE {d}"); + } + let n = diffs.len() + report_unrestated(&r); + if n > 0 { + return Err(DischargeRejected(format!( + "{n} failure(s); regenerate with `pv challenge gen {} {}`", + contracts.display(), + lean_dir.display() + )) + .into()); + } + println!( + "challenge-fresh: {} file(s) under {}/{CHALLENGE_DIR} match their regeneration", + r.files.len(), + lean_dir.display() + ); + Ok(()) +} diff --git a/crates/aprender-contracts-cli/src/commands/discharge.rs b/crates/aprender-contracts-cli/src/commands/discharge.rs new file mode 100644 index 0000000000..8099de6b57 --- /dev/null +++ b/crates/aprender-contracts-cli/src/commands/discharge.rs @@ -0,0 +1,2025 @@ +//! `pv discharge gen-axioms | check | run` (PVL-001 EV-6a, #4139; `--leanchecker` EV-6b, #4199; `--comparator` +//! EV-7b, #4201; `run` EV-8a, #4202). The judging lives in +//! [`provable_contracts::discharge`]; this module prints the report and runs Lean. +//! +//! Exit: 0 accept · 1 reject (`reject:`) · 2 decline (`decline:` — no root file, zero roots, no `lake`, no +//! `leanchecker` in the toolchain, no `Challenge/*.lean` or zero comparator rows). +//! +//! `--leanchecker` is NON-fresh: it re-checks the tree's own .olean files and trusts the Mathlib .oleans they +//! import. `--fresh` replays Mathlib and is the nightly's (PVL-F7). `formalization.yaml` `scope` says so. + +use std::ffi::OsStr; +use std::fmt; +use std::path::Path; +use std::process::Command; +use std::time::{Duration, Instant}; + +use provable_contracts::discharge::comparator::{self, CHALLENGE_DIR, COMPARATOR}; +use provable_contracts::discharge::summary::{self, LOG_FILE}; +use provable_contracts::discharge::{self, CheckOpts, Report, Tree, AXIOMS_FILE}; + +use crate::cli::DischargeAction; + +/// Nothing could be judged. Exit 2. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DischargeDeclined(pub String); + +impl fmt::Display for DischargeDeclined { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for DischargeDeclined {} + +/// Judged, and failed. Exit 1. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DischargeRejected(pub String); + +impl fmt::Display for DischargeRejected { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for DischargeRejected {} + +type Res = Result<(), Box>; + +pub fn run(action: DischargeAction) -> Res { + match action { + DischargeAction::GenAxioms { + lean_dir, + contracts, + check, + } => gen_axioms(&lean_dir, &contracts, check), + DischargeAction::Check { + lean_dir, + contracts, + no_lake, + strict, + validate_formalization, + leanchecker, + leanchecker_timeout, + leanchecker_ulimit_v, + leanchecker_threads, + leanchecker_memory_max_gib, + leanchecker_cpu_quota_pct, + leanchecker_unscoped, + comparator, + lake_timeout, + } => { + let opts = CheckOpts { + strict, + validate_formalization, + }; + let r = discharge::check(&lean_dir, &contracts, opts); + let lc = leanchecker.then_some(Leanchecker::from_flags( + leanchecker_timeout, + leanchecker_ulimit_v, + leanchecker_threads, + leanchecker_memory_max_gib, + leanchecker_cpu_quota_pct, + leanchecker_unscoped, + )); + finish_with( + Lake::new(lake_timeout), + r, + &lean_dir, + no_lake, + comparator, + lc, + ) + } + DischargeAction::Run { + lean_dir, + contracts, + leanchecker_timeout, + leanchecker_ulimit_v, + leanchecker_threads, + leanchecker_memory_max_gib, + leanchecker_cpu_quota_pct, + leanchecker_unscoped, + lake_timeout, + } => run_all( + Lake::new(lake_timeout), + "build.sh", + &lean_dir, + &contracts, + Leanchecker::from_flags( + leanchecker_timeout, + leanchecker_ulimit_v, + leanchecker_threads, + leanchecker_memory_max_gib, + leanchecker_cpu_quota_pct, + leanchecker_unscoped, + ), + ), + DischargeAction::LabelRatchet { + lean_dir, + contracts, + } => finish_with( + Lake::new(LAKE_TIMEOUT_S), + discharge::ratchet_labels(&lean_dir, &contracts), + &lean_dir, + true, + false, + None, + ), + } +} + +fn gen_axioms(lean_dir: &Path, contracts: &Path, check: bool) -> Res { + let g = discharge::generate(lean_dir, contracts).map_err(DischargeDeclined)?; + let (text, b) = (g.text, g.binding); + let path = lean_dir.join(AXIOMS_FILE); + if check { + return match std::fs::read_to_string(&path) { + Ok(on_disk) if on_disk == text => { + println!( + "ok {} is its regeneration ({} root(s) bound)", + path.display(), + b.roots.len() + ); + Ok(()) + } + Ok(_) => Err(DischargeRejected(format!( + "{} differs from its regeneration", + path.display() + )) + .into()), + Err(e) => Err(DischargeRejected(format!("{}: {e}", path.display())).into()), + }; + } + std::fs::write(&path, &text)?; + println!("wrote {} ({} root(s) bound)", path.display(), b.roots.len()); + Ok(()) +} + +/// How `--leanchecker` runs (PVL-001 EV-6b, #4199; the caps #4348). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Leanchecker { + pub timeout_s: u64, + pub ulimit_v_kib: Option, + /// `LEAN_NUM_THREADS`: leanchecker replays one full environment (Mathlib included) per concurrent module + /// task, so its memory is threads x environment. Uncapped it took 51 threads and 58-67 GB on lambda (#4348). + pub threads: u32, + /// `None` = no systemd scope (`--leanchecker-unscoped`). + pub scope: Option, +} + +impl Leanchecker { + /// The `--leanchecker-*` flags, shared by `check` and `run`. + fn from_flags( + timeout_s: u64, + ulimit_v_kib: Option, + threads: u32, + memory_max_gib: u32, + cpu_quota_pct: u32, + unscoped: bool, + ) -> Self { + Self { + timeout_s, + ulimit_v_kib, + threads, + scope: (!unscoped).then_some(Scope { + memory_max_gib, + cpu_quota_pct, + }), + } + } +} + +/// `systemd-run --user --scope -p MemoryMax=G -p CPUQuota=%` around leanchecker (#4348, operator: "this +/// host must be able to do other work, so never let it get overloaded"). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Scope { + pub memory_max_gib: u32, + pub cpu_quota_pct: u32, +} + +/// The defaults the cop ruled for lambda (#4348): <= 8 Lean threads, 24G, 800%. +pub const LEANCHECKER_THREADS: u32 = 8; +pub const LEANCHECKER_MEMORY_MAX_GIB: u32 = 24; +pub const LEANCHECKER_CPU_QUOTA_PCT: u32 = 800; + +/// The slice the scope is created under. lambda's `agent-slice-sweep` adopts any lean/lake process OUTSIDE it into +/// it -- measured 2026-09-25: a leanchecker in a 24G `run-*.scope` in app.slice was moved to agent.slice (96G) and +/// grew to 71G. A scope nested in agent.slice is left alone and its own, tighter MemoryMax still binds. On a host +/// without that slice systemd creates it as a plain transient slice. +const AGENT_SLICE: &str = "agent.slice"; + +/// The script `sh -c` runs; `$1` ulimit, `$2` timeout, `$3` lake. +const RECHECK_SH: &str = r#"if [ -n "$1" ]; then ulimit -v "$1" || exit 125; fi; exec timeout -k 30 "$2" "$3" env leanchecker ProvableContracts"#; + +/// The argv of the leanchecker arm: `[systemd-run --user --scope -q -p MemoryMax=.. -p CPUQuota=.. --] sh -c ..`. +/// `LEAN_NUM_THREADS` is set on the command, which a `--scope` unit inherits (it runs in the caller's process). +fn recheck_argv(lake_bin: &str, lc: Leanchecker) -> Vec { + let mut v: Vec = Vec::new(); + if let Some(s) = lc.scope { + v.extend( + [ + "systemd-run".to_string(), + "--user".into(), + "--scope".into(), + format!("--slice={AGENT_SLICE}"), + "-q".into(), + "-p".into(), + format!("MemoryMax={}G", s.memory_max_gib), + "-p".into(), + format!("CPUQuota={}%", s.cpu_quota_pct), + "--".into(), + ] + .into_iter(), + ); + } + let limit = lc.ulimit_v_kib.map(|k| k.to_string()).unwrap_or_default(); + v.extend([ + "sh".to_string(), + "-c".into(), + RECHECK_SH.into(), + "pv-leanchecker".into(), + limit, + lc.timeout_s.to_string(), + lake_bin.into(), + ]); + v +} + +/// The Lean steps run only on a tree nothing else has already failed or declined: a 60-minute re-check of a tree +/// that is already RED would only delay the verdict. They record their raw exits in `r.lake_exit` and +/// `r.leanchecker_exit`, and the comparator's closure in `r.challenges` (`None` = never ran), for +/// `discharge-summary.json` (EV-8a). The comparator runs before the (much slower) leanchecker. +pub(crate) fn lean_steps( + lake: Lake<'_>, + r: &mut Report, + lean_dir: &Path, + no_lake: bool, + cmp: bool, + lc: Option, +) { + let open = |r: &Report| !r.reject && r.decline.is_none(); + if open(r) && !no_lake { + elaborate(lake, lean_dir, r); + } + if cmp && open(r) { + compare(lake, lean_dir, r); + } + if let Some(lc) = lc { + if open(r) { + recheck(lake, lean_dir, lc, r); + } + } +} + +fn finish_with( + lake: Lake<'_>, + mut r: Report, + lean_dir: &Path, + no_lake: bool, + cmp: bool, + lc: Option, +) -> Res { + lean_steps(lake, &mut r, lean_dir, no_lake, cmp, lc); + verdict(r, lean_dir) +} + +/// Print the report and turn it into the exit: reject (1) before decline (2) before accept (0). +fn verdict(r: Report, lean_dir: &Path) -> Res { + for l in &r.lines { + println!("{l}"); + } + if r.reject { + let n = r.lines.iter().filter(|l| l.starts_with("FAIL")).count(); + return Err( + DischargeRejected(format!("{n} failure(s) under {}", lean_dir.display())).into(), + ); + } + if let Some(why) = r.decline { + return Err(DischargeDeclined(why).into()); + } + println!("ok discharge {}", lean_dir.display()); + Ok(()) +} + +/// `pv discharge run` (PVL-001 EV-8a, #4202): `build` (run by bash in ``), then `check --strict` and +/// every Lean arm, then the two files -- written whatever the verdict, so a RED run leaves a RED summary, never +/// none. `build.sh` rc 2 is its cache-miss decline and stays a decline; any other non-zero rejects; either way the +/// Lean steps do not run on a tree that did not build. A summary that cannot be written rejects: it is the output. +pub(crate) fn run_all( + lake: Lake<'_>, + build: &str, + lean_dir: &Path, + contracts: &Path, + lc: Leanchecker, +) -> Res { + let (build_exit, build_out) = match Command::new("bash") + .arg(build) + .current_dir(lean_dir) + .output() + { + Ok(o) => ( + Some(raw_exit(o.status)), + format!( + "{}{}", + String::from_utf8_lossy(&o.stdout), + String::from_utf8_lossy(&o.stderr) + ), + ), + Err(e) => (None, e.to_string()), + }; + let mut r = discharge::check( + lean_dir, + contracts, + CheckOpts { + strict: true, + validate_formalization: false, + }, + ); + match build_exit { + Some(0) => { + r.lines.insert(0, format!("ok {build}")); + lean_steps(lake, &mut r, lean_dir, false, true, Some(lc)); + } + Some(2) => { + r.lines.push(format!("{build} declined (rc 2):")); + r.lines.extend(tail(&build_out)); + r.decline.get_or_insert_with(|| { + format!("{build} declined: the Lean steps did not run -- not a verdict") + }); + } + Some(rc) => { + r.lines.push(format!("FAIL {build} exited {rc}")); + r.lines.extend(tail(&build_out)); + r.reject = true; + } + None => { + r.decline + .get_or_insert_with(|| format!("{build} could not be run ({build_out})")); + } + } + let tree = Tree::load(lean_dir).ok(); + let s = summary::summarize( + &r, + tree.as_ref(), + lean_dir, + summary::current_tree_sha(lean_dir), + build_exit, + ); + let spath = summary::summary_path(lean_dir); + // The summary first: the log is built after it, so a summary that cannot be written is in the log's verdict. + write_or_reject(&mut r, &spath, Ok(s.render())); + let log = RunLog { + verdict: if r.reject { + "reject" + } else if r.decline.is_some() { + "decline" + } else { + "accept" + }, + decline: r.decline.as_deref(), + lines: &r.lines, + summary: &s, + }; + let log_text = serde_json::to_string_pretty(&log) + .map(|t| t + "\n") + .map_err(|e| e.to_string()); + write_or_reject(&mut r, &lean_dir.join(LOG_FILE), log_text); + verdict(r, lean_dir) +} + +/// Write `text` to `p`, or record the failure and reject the run. +fn write_or_reject(r: &mut Report, p: &Path, text: Result) { + match text.and_then(|t| std::fs::write(p, t).map_err(|e| e.to_string())) { + Ok(()) => r.lines.push(format!("wrote {}", p.display())), + Err(e) => { + r.lines + .push(format!("FAIL cannot write {}: {e}", p.display())); + r.reject = true; + } + } +} + +/// `/discharge.json`, the untracked full log of one `run`: the verdict, every line, and the summary. +#[derive(serde::Serialize)] +struct RunLog<'a> { + verdict: &'a str, + decline: Option<&'a str>, + lines: &'a [String], + summary: &'a summary::Summary, +} + +/// `lake env lean Axioms.lean`: the subset and capstone pins, elaborated against the BUILT tree (run `build.sh` +/// first; `lake env` builds nothing). +fn elaborate(lake: Lake<'_>, lean_dir: &Path, r: &mut Report) { + let what = format!("lake env lean {AXIOMS_FILE}"); + match lake.run(&["env", "lean", AXIOMS_FILE], lean_dir) { + Ok(Bounded::TimedOut(pgid)) => { + r.lake_exit = Some(TIMED_OUT); + r.lines.push(lake.timed_out(&what, pgid)); + r.reject = true; + } + Err(e) => { + r.decline = Some(format!( + "lake could not be run ({e}): Axioms.lean was not elaborated" + )) + } + Ok(Bounded::Done(o)) if o.status.success() => { + r.lake_exit = Some(raw_exit(o.status)); + r.lines.push(format!("ok {what}")); + } + Ok(Bounded::Done(o)) => { + r.lake_exit = Some(raw_exit(o.status)); + let text = format!( + "{}{}", + String::from_utf8_lossy(&o.stdout), + String::from_utf8_lossy(&o.stderr) + ); + r.lines.push(format!( + "FAIL lake env lean {AXIOMS_FILE} exited {:?}", + o.status.code() + )); + r.lines.extend( + text.lines() + .filter(|l| l.contains("error")) + .take(20) + .map(|l| format!(" {l}")), + ); + r.reject = true; + } + } +} + +/// `lake env lean --run scripts/Comparator.lean Challenge/*.lean` (PVL-001 EV-7b): the script MEASURES, one NDJSON +/// row per challenge, and [`comparator::judge_rows`] judges. No Challenge file, no script or no `lake` declines; +/// a Challenge file that does not elaborate, or output that is not rows, rejects: its rows were never judged. +fn compare(lake: Lake<'_>, lean_dir: &Path, r: &mut Report) { + let files = comparator::challenge_files(lean_dir); + if files.is_empty() { + r.decline = Some(format!( + "comparator: no {CHALLENGE_DIR}/*.lean under {} -- EV-7a writes them; nothing to compare", + lean_dir.display() + )); + return; + } + if !lean_dir.join(COMPARATOR).is_file() { + r.decline = Some(format!( + "comparator: {} does not exist -- not a verdict", + lean_dir.join(COMPARATOR).display() + )); + return; + } + if !self_test(lake, lean_dir, r) { + return; + } + let what = format!("lake env lean --run {COMPARATOR} ({} file(s))", files.len()); + let mut args: Vec<&OsStr> = ["env", "lean", "--run", COMPARATOR] + .map(OsStr::new) + .to_vec(); + args.extend(files.iter().map(|f| f.as_os_str())); + let out = match lake.run(&args, lean_dir) { + Ok(Bounded::Done(o)) => o, + Ok(Bounded::TimedOut(pgid)) => { + r.lines.push(lake.timed_out(&what, pgid)); + r.reject = true; + return; + } + Err(e) => { + r.decline = Some(format!( + "lake could not be run ({e}): the comparator did not run" + )); + return; + } + }; + let stdout = String::from_utf8_lossy(&out.stdout); + if !out.status.success() { + r.lines.push(format!( + "FAIL {what} exited {} -- a Challenge file did not elaborate; its rows were withheld", + raw_exit(out.status) + )); + r.lines.extend(tail(&String::from_utf8_lossy(&out.stderr))); + r.reject = true; + return; + } + match comparator::parse_rows(&stdout) { + Ok(rows) => { + r.lines.push(format!("ok {what}")); + let mut c = comparator::judge_rows(&rows, r); + match comparator::expected_roots(lean_dir, &files) { + Ok(roots) => comparator::cross_check(&rows, &roots, &mut c, r), + Err(e) => { + r.lines.push(format!( + "FAIL comparator: a Challenge file could not be read, its roots were never counted: {e}" + )); + r.reject = true; + } + } + r.challenges = Some(c); + } + Err(e) => { + r.lines.push(format!("FAIL {what}: {e}")); + r.reject = true; + } + } +} + +/// The FIPS 180-4 vectors `Comparator.lean --self-test` checks: "", "abc" and the two-block 448-bit message. +const FIPS_VECTORS: usize = 3; + +/// `lake env lean --run scripts/Comparator.lean --self-test` (#4238), before any row is trusted: the pure-Lean +/// sha256 against the FIPS vectors, then the defeq controls. A regressed sha256 still hashes consistently, so the +/// rows alone cannot catch it. rc != 0, a timeout, or fewer than [`FIPS_VECTORS`] `ok … sha256` lines rejects -- +/// a self-test that checked nothing is not a pass. +fn self_test(lake: Lake<'_>, lean_dir: &Path, r: &mut Report) -> bool { + let what = format!("lake env lean --run {COMPARATOR} --self-test"); + let o = match lake.run( + &["env", "lean", "--run", COMPARATOR, "--self-test"], + lean_dir, + ) { + Ok(Bounded::Done(o)) => o, + Ok(Bounded::TimedOut(pgid)) => { + r.lines.push(lake.timed_out(&what, pgid)); + r.reject = true; + return false; + } + Err(e) => { + r.decline = Some(format!( + "lake could not be run ({e}): the comparator did not run (nor its self-test)" + )); + return false; + } + }; + let stdout = String::from_utf8_lossy(&o.stdout); + let vectors = stdout + .lines() + .filter(|l| l.starts_with("ok") && l.contains(" sha256 ")) + .count(); + if o.status.success() && vectors >= FIPS_VECTORS { + r.lines + .push(format!("ok {what} ({vectors} FIPS vectors)")); + return true; + } + r.lines.push(format!( + "FAIL {what} exited {} with {vectors}/{FIPS_VECTORS} FIPS vectors ok -- the statement hash is not trusted", + raw_exit(o.status) + )); + r.lines.extend(tail(&format!( + "{stdout}{}", + String::from_utf8_lossy(&o.stderr) + ))); + r.reject = true; + false +} + +/// The last 20 lines of `text`, indented. +fn tail(text: &str) -> Vec { + let mut v: Vec = text + .lines() + .rev() + .take(20) + .map(|l| format!(" {l}")) + .collect(); + v.reverse(); + v +} + +/// The process's exit code, or 128+signal when a signal ended it (the shell's convention). +fn raw_exit(s: std::process::ExitStatus) -> i32 { + use std::os::unix::process::ExitStatusExt; + s.code().unwrap_or_else(|| 128 + s.signal().unwrap_or(0)) +} + +/// The default bound on one `lake` call, seconds. `check`/`run` take `--lake-timeout` (#4239). +pub(crate) const LAKE_TIMEOUT_S: u64 = 1800; + +/// The exit recorded for a step the bound killed: `timeout(1)`'s, as the leanchecker arm records. +const TIMED_OUT: i32 = 124; + +/// A `lake` binary and the wall-clock bound on each call to it (#4239: a hang is RED, not a wait). +#[derive(Clone, Copy, Debug)] +pub(crate) struct Lake<'a> { + pub bin: &'a str, + pub timeout_s: u64, +} + +/// How a bounded call ended. `TimedOut` carries the killed process group (the child's own PID). +enum Bounded { + Done(std::process::Output), + TimedOut(u32), +} + +impl Lake<'static> { + fn new(timeout_s: u64) -> Self { + Lake { + bin: "lake", + timeout_s, + } + } +} + +impl Lake<'_> { + /// `lake ` in `dir`, in its own process group, waited on for at most `timeout_s`. At the deadline the + /// group is SIGKILLed by the child's own PID (pgid == pid) -- never by pattern -- so a `lean` under `lake env` + /// dies with it. The deadline also bounds the output drain: a grandchild holding a pipe cannot stall it. + fn run>(self, args: &[S], dir: &Path) -> std::io::Result { + let mut child = self.spawn(args, dir)?; + let pgid = child.id(); + let rx = drain_pipes(&mut child); + let deadline = Instant::now() + Duration::from_secs(self.timeout_s); + let Some(status) = wait_until(&mut child, deadline)? else { + return Ok(kill_group(&mut child, pgid)); + }; + let Some([stdout, stderr]) = collect(&rx, deadline) else { + return Ok(kill_group(&mut child, pgid)); + }; + Ok(Bounded::Done(std::process::Output { + status, + stdout, + stderr, + })) + } + + /// `lake ` spawned in its own process group, piped. ETXTBSY (26): a just-written `lake` whose write fd a + /// concurrent fork still holds until its exec. Transient, so it is retried. + fn spawn>( + self, + args: &[S], + dir: &Path, + ) -> std::io::Result { + use std::os::unix::process::CommandExt; + use std::process::Stdio; + let spawn = || { + Command::new(self.bin) + .args(args) + .current_dir(dir) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .process_group(0) + .spawn() + }; + let mut child = spawn(); + for _ in 0..20 { + match &child { + Err(e) if e.raw_os_error() == Some(26) => { + std::thread::sleep(Duration::from_millis(25)); + child = spawn(); + } + _ => break, + } + } + child + } + + fn timed_out(self, what: &str, pgid: u32) -> String { + format!( + "FAIL {what} timed out after {}s -- killed its process group {pgid}; a hang is RED, not a wait", + self.timeout_s + ) + } +} + +type Drained = std::sync::mpsc::Receiver<(usize, Vec)>; + +/// Read the child's stdout (0) and stderr (1) on their own threads, so a full pipe cannot block its exit. +fn drain_pipes(child: &mut std::process::Child) -> Drained { + use std::io::Read; + let (tx, rx) = std::sync::mpsc::channel(); + let drain = |mut pipe: Box, which: usize| { + let tx = tx.clone(); + std::thread::spawn(move || { + let mut buf = Vec::new(); + let _ = pipe.read_to_end(&mut buf); + let _ = tx.send((which, buf)); + }); + }; + if let Some(p) = child.stdout.take() { + drain(Box::new(p), 0); + } + if let Some(p) = child.stderr.take() { + drain(Box::new(p), 1); + } + rx +} + +/// The child's exit status, or `None` once `deadline` passes with it still running. +fn wait_until( + child: &mut std::process::Child, + deadline: Instant, +) -> std::io::Result> { + loop { + if let Some(status) = child.try_wait()? { + return Ok(Some(status)); + } + if Instant::now() >= deadline { + return Ok(None); + } + std::thread::sleep(Duration::from_millis(20)); + } +} + +/// Both drained pipes, or `None` if `deadline` passes first: a grandchild holding a pipe cannot stall the drain. +fn collect(rx: &Drained, deadline: Instant) -> Option<[Vec; 2]> { + use std::sync::mpsc::RecvTimeoutError; + let mut out = [Vec::new(), Vec::new()]; + for _ in 0..2 { + let left = deadline.saturating_duration_since(Instant::now()); + match rx.recv_timeout(left) { + Ok((which, buf)) => out[which] = buf, + Err(RecvTimeoutError::Disconnected) => break, + Err(RecvTimeoutError::Timeout) => return None, + } + } + Some(out) +} + +/// SIGKILL the whole process group by the child's own PID (pgid == pid) -- never by pattern -- then reap the child. +fn kill_group(child: &mut std::process::Child, pgid: u32) -> Bounded { + let _ = Command::new("kill") + .args(["-s", "KILL", "--", &format!("-{pgid}")]) + .status(); + let _ = child.kill(); + let _ = child.wait(); + Bounded::TimedOut(pgid) +} + +/// The toolchain `lake env` puts on PATH for this tree: `lake env printenv LEAN_SYSROOT`. `Err` is the decline; +/// a timeout rejects in place and yields `Err(None)` (#4239). +fn sysroot( + lake: Lake<'_>, + lean_dir: &Path, + r: &mut Report, +) -> Result> { + let o = match lake.run(&["env", "printenv", "LEAN_SYSROOT"], lean_dir) { + Ok(Bounded::Done(o)) => o, + Ok(Bounded::TimedOut(pgid)) => { + r.lines + .push(lake.timed_out("lake env printenv LEAN_SYSROOT", pgid)); + r.reject = true; + return Err(None); + } + Err(e) => { + return Err(Some(format!( + "lake could not be run ({e}): leanchecker did not run" + ))) + } + }; + let root = String::from_utf8_lossy(&o.stdout).trim().to_string(); + if !o.status.success() || root.is_empty() { + return Err(Some(format!( + "`lake env printenv LEAN_SYSROOT` exited {:?} and named no toolchain: leanchecker did not run", + o.status.code() + ))); + } + Ok(root.into()) +} + +/// `timeout lake env leanchecker ProvableContracts` (PVL-001 EV-6b): rc != 0 rejects, a timeout rejects, and a +/// toolchain without `leanchecker` declines -- measured: elan's `lake env leanchecker` on v4.15.0 exits 1 "does not +/// have the binary", which would otherwise read as a failed check. `timeout` signals the whole process group, so +/// the `leanchecker` under `lake` does not outlive it. +fn recheck(lake: Lake<'_>, lean_dir: &Path, lc: Leanchecker, r: &mut Report) { + let root = match sysroot(lake, lean_dir, r) { + Ok(root) => root, + Err(why) => { + r.decline = why; + return; + } + }; + let bin = root.join("bin").join("leanchecker"); + if !bin.is_file() { + r.decline = Some(format!( + "leanchecker not in toolchain: {} does not exist", + bin.display() + )); + return; + } + // A scope that cannot be created would otherwise read as leanchecker's own non-zero exit: probe it first. + if lc.scope.is_some() { + let probe = Command::new("systemd-run") + .args([ + "--user", + "--scope", + &format!("--slice={AGENT_SLICE}"), + "-q", + "--", + "true", + ]) + .output(); + if !matches!(&probe, Ok(o) if o.status.success()) { + r.decline = Some( + "`systemd-run --user --scope` is unavailable here: leanchecker did not run (pass \ + --leanchecker-unscoped only on a host where an uncapped run cannot starve other work, #4348)" + .to_string(), + ); + return; + } + } + let argv = recheck_argv(lake.bin, lc); + let out = Command::new(&argv[0]) + .args(&argv[1..]) + .env("LEAN_NUM_THREADS", lc.threads.to_string()) + .current_dir(lean_dir) + .output(); + let what = format!( + "lake env leanchecker ProvableContracts (timeout {}s, {} threads, {})", + lc.timeout_s, + lc.threads, + lc.scope.map_or("unscoped".to_string(), |s| format!( + "scope MemoryMax={}G CPUQuota={}%", + s.memory_max_gib, s.cpu_quota_pct + )) + ); + match out { + Err(e) => { + r.decline = Some(format!( + "sh could not be run ({e}): leanchecker did not run" + )) + } + Ok(o) if o.status.success() => { + r.leanchecker_exit = Some(0); + r.lines.push(format!("ok {what}")); + } + Ok(o) => match o.status.code() { + // timeout/ulimit/lake could not start leanchecker: it never ran, so no exit is recorded. + Some(c @ 125..=127) => { + r.decline = Some(format!( + "{what} could not be started (rc {c}: timeout/ulimit/lake): not a verdict" + )); + } + code => { + r.leanchecker_exit = Some(raw_exit(o.status)); + let why = if matches!(code, Some(124 | 137)) { + "timed out".to_string() + } else { + format!("exited {code:?}") + }; + r.lines.push(format!("FAIL {what} {why}")); + let text = format!( + "{}{}", + String::from_utf8_lossy(&o.stdout), + String::from_utf8_lossy(&o.stderr) + ); + r.lines.extend( + text.lines() + .rev() + .take(20) + .collect::>() + .into_iter() + .rev() + .map(|l| format!(" {l}")), + ); + r.reject = true; + } + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::contract_walk::{exit_code_for, verdict_for}; + use std::path::PathBuf; + + /// A one-theorem tree bound by the label `Theorems.Gelu` (the integration fixture, in-process). + fn tree() -> (tempfile::TempDir, PathBuf, PathBuf) { + let d = tempfile::tempdir().expect("tempdir"); + let lean = d.path().join("lean"); + let contracts = d.path().join("contracts"); + std::fs::create_dir_all(lean.join("ProvableContracts/Theorems/Gelu")).expect("mkdir"); + std::fs::create_dir_all(&contracts).expect("mkdir"); + std::fs::write( + lean.join("ProvableContracts.lean"), + "import ProvableContracts.Theorems.Gelu.Bound\n", + ) + .expect("w"); + std::fs::write( + lean.join("ProvableContracts/Theorems/Gelu/Bound.lean"), + "namespace ProvableContracts.Gelu\ntheorem gelu_bound : True := trivial\nend ProvableContracts.Gelu\n", + ) + .expect("w"); + std::fs::write( + contracts.join("gelu-v1.yaml"), + "equations:\n e:\n lean_theorem: Theorems.Gelu\n", + ) + .expect("w"); + (d, lean, contracts) + } + + fn gen(lean: &Path, contracts: &Path, check: bool) -> Res { + run(DischargeAction::GenAxioms { + lean_dir: lean.into(), + contracts: contracts.into(), + check, + }) + } + + fn check(lean: &Path, contracts: &Path, strict: bool) -> Res { + run(DischargeAction::Check { + lean_dir: lean.into(), + contracts: contracts.into(), + no_lake: true, + strict, + validate_formalization: false, + leanchecker: false, + leanchecker_timeout: 3600, + leanchecker_ulimit_v: None, + leanchecker_threads: LEANCHECKER_THREADS, + leanchecker_memory_max_gib: LEANCHECKER_MEMORY_MAX_GIB, + leanchecker_cpu_quota_pct: LEANCHECKER_CPU_QUOTA_PCT, + leanchecker_unscoped: false, + comparator: false, + lake_timeout: LAKE_TIMEOUT_S, + }) + } + + fn is_reject(r: &Res) -> bool { + r.as_ref() + .err() + .is_some_and(|e| e.downcast_ref::().is_some()) + } + + fn is_decline(r: &Res) -> bool { + r.as_ref() + .err() + .is_some_and(|e| e.downcast_ref::().is_some()) + } + + #[test] + fn the_verdict_words_and_exit_codes_are_pvl_1s() { + let d: Box = Box::new(DischargeDeclined("x".into())); + let r: Box = Box::new(DischargeRejected("y".into())); + assert_eq!( + (exit_code_for(d.as_ref()), verdict_for(d.as_ref())), + (2, "decline") + ); + assert_eq!( + (exit_code_for(r.as_ref()), verdict_for(r.as_ref())), + (1, "reject") + ); + assert_eq!( + (d.to_string(), r.to_string()), + ("x".to_string(), "y".to_string()) + ); + } + + #[test] + fn gen_axioms_writes_and_check_mode_judges_freshness() { + let (_d, lean, contracts) = tree(); + assert!( + is_reject(&gen(&lean, &contracts, true)), + "no Axioms.lean yet must reject" + ); + gen(&lean, &contracts, false).expect("write"); + let text = std::fs::read_to_string(lean.join(AXIOMS_FILE)).expect("written"); + assert!( + text.contains("`ProvableContracts.Gelu.gelu_bound"), + "{text}" + ); + gen(&lean, &contracts, true).expect("fresh"); + std::fs::write(lean.join(AXIOMS_FILE), format!("{text}-- edit\n")).expect("w"); + assert!(is_reject(&gen(&lean, &contracts, true))); + std::fs::remove_file(lean.join("ProvableContracts.lean")).expect("rm"); + assert!(is_decline(&gen(&lean, &contracts, false))); + } + + #[test] + fn check_accepts_rejects_and_declines() { + let (_d, lean, contracts) = tree(); + gen(&lean, &contracts, false).expect("write"); + check(&lean, &contracts, false).expect("clean tree"); + let f = lean.join("ProvableContracts/Theorems/Gelu/Bound.lean"); + let src = std::fs::read_to_string(&f).expect("r"); + std::fs::write( + &f, + src.replace( + "end ProvableContracts.Gelu", + "axiom m : False\nend ProvableContracts.Gelu", + ), + ) + .expect("w"); + assert!(is_reject(&check(&lean, &contracts, false))); + std::fs::write( + lean.join("escape-allowlist.yaml"), + "- file: ProvableContracts/Theorems/Gelu/Bound.lean\n decl: ProvableContracts.Gelu.m\n kind: axiom\n reason: r\n ticket: t\n confirmed_by: pending\n", + ) + .expect("w"); + gen(&lean, &contracts, false).expect("regen"); + check(&lean, &contracts, false).expect("pending is accepted"); + assert!( + is_reject(&check(&lean, &contracts, true)), + "--strict rejects pending" + ); + std::fs::write( + contracts.join("gelu-v1.yaml"), + "equations:\n e:\n lean_theorem: none\n", + ) + .expect("w"); + gen(&lean, &contracts, false).expect("regen"); + assert!( + is_decline(&check(&lean, &contracts, false)), + "zero roots declines" + ); + } + + #[test] + fn label_ratchet_writes_the_set() { + let (_d, lean, contracts) = tree(); + run(DischargeAction::LabelRatchet { + lean_dir: lean.clone(), + contracts, + }) + .expect("seed"); + assert!(lean.join(discharge::LABELS).is_file()); + } + + /// A `lake` under a 60 s bound: long enough for any fake, short enough that a hang fails the test. + fn lk(bin: &str) -> Lake<'_> { + Lake { bin, timeout_s: 60 } + } + + /// A fake `lake`: exits `rc` after printing `out`. + fn fake_lake(dir: &Path, rc: i32, out: &str) -> String { + let p = dir.join(format!("lake-{rc}")); + std::fs::write(&p, format!("#!/bin/sh\necho '{out}'\nexit {rc}\n")).expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + p.to_string_lossy().into_owned() + } + + #[test] + fn the_lean_elaboration_decides_the_verdict() { + let (d, lean, _) = tree(); + let ok = fake_lake(d.path(), 0, "fine"); + let bad = fake_lake(d.path(), 1, "Axioms.lean:3:0: error: AXIOMS x"); + finish_with(lk(&ok), Report::default(), &lean, false, false, None).expect("lake ok"); + assert!( + is_reject(&finish_with( + lk(&bad), + Report::default(), + &lean, + false, + false, + None + )), + "a failing elaboration rejects" + ); + assert!( + is_decline(&finish_with( + lk("/nonexistent/lake"), + Report::default(), + &lean, + false, + false, + None + )), + "no lake declines" + ); + finish_with(lk(&bad), Report::default(), &lean, true, false, None) + .expect("--no-lake skips it"); + let rejected = Report { + reject: true, + ..Report::default() + }; + assert!( + is_reject(&finish_with(lk(&ok), rejected, &lean, false, false, None)), + "a prior failure is not cleared by lake" + ); + } + + /// A `lake` for `--leanchecker`: `lake env printenv LEAN_SYSROOT` names `/sysroot` (with `bin/leanchecker` + /// when `has_checker`), `lake env lean …` passes, and `lake env leanchecker …` prints `out` and exits `rc`. + fn checker_lake(dir: &Path, has_checker: bool, rc: i32, out: &str) -> String { + let root = dir.join(format!("sysroot-{has_checker}")); + std::fs::create_dir_all(root.join("bin")).expect("mkdir"); + if has_checker { + std::fs::write(root.join("bin").join("leanchecker"), "").expect("w"); + } + let p = dir.join(format!("checker-lake-{has_checker}-{rc}")); + let script = format!( + "#!/bin/sh\ncase \"$2\" in\n printenv) echo '{}' ;;\n lean) exit 0 ;;\n leanchecker) echo '{out}'; exit {rc} ;;\n *) exit 99 ;;\nesac\n", + root.display() + ); + std::fs::write(&p, script).expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + p.to_string_lossy().into_owned() + } + + const LC: Option = Some(Leanchecker { + timeout_s: 60, + ulimit_v_kib: None, + threads: 8, + scope: None, + }); + + /// PVL-001 EV-6b: leanchecker's rc decides; an ABSENT leanchecker declines and is never read as a failure. + #[test] + fn leanchecker_rc_rejects_and_an_absent_checker_declines() { + let (d, lean, _) = tree(); + let pass = checker_lake(d.path(), true, 0, "ok"); + let fail = checker_lake( + d.path(), + true, + 1, + "error: kernel rejected Theorems.Gelu.bound", + ); + let absent = checker_lake(d.path(), false, 0, "never reached"); + finish_with(lk(&pass), Report::default(), &lean, false, false, LC) + .expect("leanchecker rc 0 accepts"); + assert!( + is_reject(&finish_with( + lk(&fail), + Report::default(), + &lean, + false, + false, + LC + )), + "leanchecker rc 1 rejects" + ); + assert!( + is_decline(&finish_with( + lk(&absent), + Report::default(), + &lean, + false, + false, + LC + )), + "no leanchecker in the toolchain declines" + ); + assert!( + is_decline(&finish_with( + lk("/nonexistent/lake"), + Report::default(), + &lean, + true, + false, + LC + )), + "no lake declines under --leanchecker" + ); + finish_with(lk(&fail), Report::default(), &lean, false, false, None) + .expect("without --leanchecker it never runs"); + } + + #[test] + fn a_timed_out_leanchecker_rejects_and_the_ulimit_reaches_the_checker() { + let (d, lean, _) = tree(); + let slow = checker_lake(d.path(), true, 0, "x"); + let body = std::fs::read_to_string(&slow) + .expect("r") + .replace("echo 'x'; exit 0", "sleep 30"); + std::fs::write(&slow, body).expect("w"); + let t1 = Some(Leanchecker { + timeout_s: 1, + ulimit_v_kib: None, + threads: 8, + scope: None, + }); + assert!( + is_reject(&finish_with( + lk(&slow), + Report::default(), + &lean, + true, + false, + t1 + )), + "a timeout rejects" + ); + // The limit reaches the checker: a stub that prints its own `ulimit -v` and fails shows it in the reject. + let shows = checker_lake(d.path(), true, 1, "x"); + let body = std::fs::read_to_string(&shows) + .expect("r") + .replace("echo 'x'", "echo \"vlimit=$(ulimit -v)\""); + std::fs::write(&shows, body).expect("w"); + let mut r = Report::default(); + recheck( + lk(&shows), + &lean, + Leanchecker { + timeout_s: 60, + ulimit_v_kib: Some(4_194_304), + threads: 8, + scope: None, + }, + &mut r, + ); + assert!(r.reject, "{:?}", r.lines); + assert!( + r.lines.iter().any(|l| l.contains("vlimit=4194304")), + "{:?}", + r.lines + ); + } + + /// #4348: the thread cap reaches the checker -- a stub that prints its own `LEAN_NUM_THREADS` and fails shows it. + #[test] + fn the_thread_cap_reaches_the_checker() { + let (d, lean, _) = tree(); + let shows = checker_lake(d.path(), true, 1, "x"); + let body = std::fs::read_to_string(&shows) + .expect("r") + .replace("echo 'x'", "echo \"threads=$LEAN_NUM_THREADS\""); + std::fs::write(&shows, body).expect("w"); + let mut r = Report::default(); + let lc = Leanchecker { + threads: 3, + ..LC.expect("LC") + }; + recheck(lk(&shows), &lean, lc, &mut r); + assert!(r.reject, "{:?}", r.lines); + assert!( + r.lines.iter().any(|l| l.contains("threads=3")), + "{:?}", + r.lines + ); + } + + /// #4348: scoped, the arm is `systemd-run --user --scope` with the memory and CPU caps around the same `sh -c`; + /// unscoped it is the bare `sh -c`. + #[test] + fn the_scope_wraps_the_checker_with_both_caps() { + let scoped = recheck_argv( + "lake", + Leanchecker { + scope: Some(Scope { + memory_max_gib: 24, + cpu_quota_pct: 800, + }), + ..LC.expect("LC") + }, + ); + assert_eq!( + scoped[..10], + [ + "systemd-run", + "--user", + "--scope", + "--slice=agent.slice", + "-q", + "-p", + "MemoryMax=24G", + "-p", + "CPUQuota=800%", + "--" + ] + ); + let bare = recheck_argv("lake", LC.expect("LC")); + assert_eq!(bare[..2], ["sh", "-c"]); + assert_eq!(scoped[10..], bare[..]); + } + + /// #4348: the DEFAULT `--leanchecker` is capped -- 8 threads inside a 24G/800% scope -- on `check` and `run` + /// alike; only an explicit `--leanchecker-unscoped` drops the scope. + #[test] + fn the_default_leanchecker_is_capped() { + #[derive(clap::Parser)] + struct T { + #[command(subcommand)] + a: crate::cli::DischargeAction, + } + let parse = |args: &[&str]| { + ::try_parse_from(std::iter::once("t").chain(args.iter().copied())) + .expect("parses") + .a + }; + for a in [ + parse(&["check", "L", "--leanchecker"]), + parse(&["run", "L"]), + ] { + let (t, g, q, u) = match a { + DischargeAction::Check { + leanchecker_threads: t, + leanchecker_memory_max_gib: g, + leanchecker_cpu_quota_pct: q, + leanchecker_unscoped: u, + .. + } + | DischargeAction::Run { + leanchecker_threads: t, + leanchecker_memory_max_gib: g, + leanchecker_cpu_quota_pct: q, + leanchecker_unscoped: u, + .. + } => (t, g, q, u), + _ => unreachable!(), + }; + let lc = Leanchecker::from_flags(3600, None, t, g, q, u); + assert_eq!( + (lc.threads, lc.scope), + ( + 8, + Some(Scope { + memory_max_gib: 24, + cpu_quota_pct: 800 + }) + ) + ); + } + match parse(&["check", "L", "--leanchecker", "--leanchecker-unscoped"]) { + DischargeAction::Check { + leanchecker_unscoped, + .. + } => assert!(leanchecker_unscoped), + _ => unreachable!(), + } + } + + /// The defaults are also the ceilings: a caller may lower a cap, never raise it (51 threads took 58-67 GB). + #[test] + fn a_leanchecker_cap_can_be_lowered_never_raised() { + #[derive(clap::Parser)] + struct T { + #[command(subcommand)] + a: crate::cli::DischargeAction, + } + let parses = |args: &[&str]| { + ::try_parse_from(std::iter::once("t").chain(args.iter().copied())) + .is_ok() + }; + for (flag, max) in [ + ("--leanchecker-threads", LEANCHECKER_THREADS), + ("--leanchecker-memory-max-gib", LEANCHECKER_MEMORY_MAX_GIB), + ("--leanchecker-cpu-quota-pct", LEANCHECKER_CPU_QUOTA_PCT), + ] { + for base in [&["check", "L", "--leanchecker"][..], &["run", "L"][..]] { + let with = |v: u32| { + let v = v.to_string(); + let mut a = base.to_vec(); + a.extend([flag, v.as_str()]); + parses(&a) + }; + assert!( + with(1) && with(max), + "{flag} {base:?}: 1 and {max} must parse" + ); + assert!( + !with(max + 1), + "{flag} {base:?}: {} must be refused", + max + 1 + ); + assert!(!with(0), "{flag} {base:?}: 0 must be refused"); + } + } + } + + /// EV-8a reads the raw exits: `Some(n)` for a step that ran (124 on a timeout), `None` for one that never ran. + #[test] + fn the_lean_steps_record_their_raw_exits_and_none_when_they_did_not_run() { + let (d, lean, _) = tree(); + let exits = |lake: &str, no_lake: bool, lc: Option| { + let mut r = Report::default(); + lean_steps(lk(lake), &mut r, &lean, no_lake, false, lc); + (r.lake_exit, r.leanchecker_exit) + }; + let pass = checker_lake(d.path(), true, 0, "ok"); + assert_eq!(exits(&pass, false, LC), (Some(0), Some(0))); + assert_eq!( + exits(&pass, false, None), + (Some(0), None), + "no --leanchecker" + ); + assert_eq!(exits(&pass, true, None), (None, None), "--no-lake"); + let fail = checker_lake(d.path(), true, 3, "kernel error"); + assert_eq!(exits(&fail, true, LC), (None, Some(3))); + let absent = checker_lake(d.path(), false, 0, "never reached"); + assert_eq!( + exits(&absent, true, LC), + (None, None), + "absent checker never ran" + ); + let slow = checker_lake(d.path(), true, 0, "x"); + let body = std::fs::read_to_string(&slow) + .expect("r") + .replace("echo 'x'; exit 0", "sleep 30"); + std::fs::write(&slow, body).expect("w"); + let t1 = Some(Leanchecker { + timeout_s: 1, + ulimit_v_kib: None, + threads: 8, + scope: None, + }); + assert_eq!(exits(&slow, true, t1), (None, Some(124)), "timeout"); + } + + /// A passing `Comparator.lean --self-test`: the three FIPS vector lines, rc 0 (#4238). + const SELF_TEST_OK: &str = "if [ \"$5\" = --self-test ]; then printf 'ok sha256 \"\" = e3\\nok sha256 \"abc\" = ba\\nok sha256 \"abcdbcde\" = 24\\n'; exit 0; fi"; + + /// The root EV-7a's `render` declares for the fixture's one solution. The cross-check (#4240) counts + /// these lines, so an empty Challenge file would make every fixture row an UNEXPECTED-ROW. + const GELU_CHALLENGE: &str = + "theorem _root_.PvlChallenge.ProvableContracts.Gelu.gelu_bound : True := by\n sorry\n"; + + /// A `lake` for `--comparator`: `env lean --run …` writes `rows` to stdout, `stderr` to stderr, and exits + /// `rc`; every other `env lean` passes. The tree gets `Challenge/gelu-v1.lean` and the comparator script. + fn comparator_lake(dir: &Path, lean: &Path, rc: i32, rows: &str, stderr: &str) -> String { + std::fs::create_dir_all(lean.join(CHALLENGE_DIR)).expect("mkdir"); + // The Challenge file declares exactly the roots the canned rows name (#4240 cross-checks the two); + // output that is not rows declares nothing, so those cases still judge the output alone. + let decls: String = comparator::parse_rows(rows) + .map(|rs| { + rs.iter() + .map(|r| { + format!( + "theorem _root_.PvlChallenge.{} : True := by\n sorry\n", + r.name + ) + }) + .collect() + }) + .unwrap_or_default(); + std::fs::write(lean.join(CHALLENGE_DIR).join("gelu-v1.lean"), decls).expect("w"); + std::fs::create_dir_all(lean.join("scripts")).expect("mkdir"); + std::fs::write(lean.join(COMPARATOR), "").expect("w"); + std::fs::write(dir.join(format!("rows-{rc}")), rows).expect("w"); + let p = dir.join(format!("cmp-lake-{rc}")); + let script = format!( + "#!/bin/sh\n{SELF_TEST_OK}\nif [ \"$3\" = --run ]; then\n [ \"$4 $5\" = \"{COMPARATOR} {CHALLENGE_DIR}/gelu-v1.lean\" ] || {{ echo \"bad args: $*\" >&2; exit 98; }}\n cat '{}'; echo '{stderr}' >&2; exit {rc}\nfi\nexit 0\n", + dir.join(format!("rows-{rc}")).display() + ); + std::fs::write(&p, script).expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + p.to_string_lossy().into_owned() + } + + fn cmp_row(name: &str, ch: &str, sol: &str, axioms: &str) -> String { + format!( + "{{\"name\": \"{name}\", \"challenge_type_hash\": \"{ch}\", \"solution_type_hash\": {sol}, \"axioms\": {axioms}}}\n" + ) + } + + fn compared(lake: &str, lean: &Path) -> Report { + let mut r = Report::default(); + lean_steps(lk(lake), &mut r, lean, false, true, None); + r + } + + /// PVL-001 EV-7b: a matching, sorry-free solution closes its challenge; the closure is recorded for EV-8a. + #[test] + fn the_comparator_closes_a_matching_challenge() { + let (d, lean, _) = tree(); + let h = "ab".repeat(32); + let lake = comparator_lake( + d.path(), + &lean, + 0, + &cmp_row( + "ProvableContracts.Gelu.gelu_bound", + &h, + &format!("\"{h}\""), + "[]", + ), + "", + ); + let r = compared(&lake, &lean); + assert!(!r.reject && r.decline.is_none(), "{:?}", r.lines); + assert_eq!( + r.challenges, + Some(comparator::Closure { + closed: 1, + total: 1 + }) + ); + assert!( + r.lines + .iter() + .any(|l| l == "COMPARATOR 1/1 challenge(s) closed"), + "{:?}", + r.lines + ); + finish_with(lk(&lake), Report::default(), &lean, false, true, None).expect("accepts"); + } + + /// The spec's RED: a solution of a WEAKER statement is a mismatch, and a sorry'd one closes nothing. + #[test] + fn a_mismatched_or_sorry_solution_rejects() { + let (d, lean, _) = tree(); + let (c, s) = ("ab".repeat(32), "cd".repeat(32)); + let rows = format!( + "{}{}", + cmp_row("A.weak", &c, &format!("\"{s}\""), "[]"), + cmp_row("A.sorried", &c, &format!("\"{c}\""), "[\"sorryAx\"]") + ); + let lake = comparator_lake(d.path(), &lean, 0, &rows, ""); + let r = compared(&lake, &lean); + assert!(r.reject, "{:?}", r.lines); + assert!(r + .lines + .iter() + .any(|l| l.starts_with("FAIL MISMATCH A.weak"))); + assert!(r + .lines + .iter() + .any(|l| l.starts_with("FAIL SORRY A.sorried"))); + assert_eq!(r.challenges.map(|c| (c.closed, c.total)), Some((0, 2))); + } + + #[test] + fn a_challenge_that_does_not_elaborate_rejects_with_its_errors() { + let (d, lean, _) = tree(); + let lake = comparator_lake( + d.path(), + &lean, + 1, + "", + "Challenge/gelu-v1.lean:5:0: error: unknown g", + ); + let r = compared(&lake, &lean); + assert!(r.reject, "{:?}", r.lines); + assert!( + r.lines.iter().any(|l| l.contains("exited 1")), + "{:?}", + r.lines + ); + assert!( + r.lines.iter().any(|l| l.contains("unknown g")), + "{:?}", + r.lines + ); + assert_eq!(r.challenges, None, "rows withheld: nothing was judged"); + } + + #[test] + fn unreadable_comparator_output_rejects() { + let (d, lean, _) = tree(); + let lake = comparator_lake(d.path(), &lean, 0, "not a row\n", ""); + let r = compared(&lake, &lean); + assert!(r.reject, "{:?}", r.lines); + assert!( + r.lines.iter().any(|l| l.contains("line 1")), + "{:?}", + r.lines + ); + } + + /// Zero measured is a decline, never a pass: no Challenge file, no script, zero rows, no lake. + #[test] + fn the_comparator_declines_when_nothing_was_compared() { + let (d, lean, _) = tree(); + let lake = comparator_lake(d.path(), &lean, 0, "", ""); + let r = compared(&lake, &lean); + assert!(!r.reject, "{:?}", r.lines); + assert!( + r.decline + .as_deref() + .is_some_and(|w| w.contains("0 challenge rows")), + "{r:?}" + ); + assert_eq!(r.challenges.map(|c| c.total), Some(0)); + + std::fs::remove_file(lean.join(COMPARATOR)).expect("rm"); + let r = compared(&lake, &lean); + assert!( + r.decline + .as_deref() + .is_some_and(|w| w.contains("does not exist")), + "{r:?}" + ); + + std::fs::remove_dir_all(lean.join(CHALLENGE_DIR)).expect("rm"); + let r = compared(&lake, &lean); + assert!( + r.decline + .as_deref() + .is_some_and(|w| w.contains("no Challenge/*.lean")), + "{r:?}" + ); + assert!(is_decline(&finish_with( + lk(&lake), + Report::default(), + &lean, + false, + true, + None + ))); + + let (d2, lean2, _) = tree(); + comparator_lake(d2.path(), &lean2, 0, "", ""); + let mut r = Report::default(); + lean_steps(lk("/nonexistent/lake"), &mut r, &lean2, true, true, None); + assert!( + r.decline + .as_deref() + .is_some_and(|w| w.contains("comparator did not run")), + "{r:?}" + ); + } + + #[test] + fn without_the_flag_the_comparator_never_runs() { + let (d, lean, _) = tree(); + let lake = comparator_lake(d.path(), &lean, 1, "", "would reject"); + let mut r = Report::default(); + lean_steps(lk(&lake), &mut r, &lean, false, false, None); + assert!(!r.reject && r.challenges.is_none(), "{:?}", r.lines); + } + + /// PVL-001 EV-8a: a `lake` for `run` — every `env lean` passes, `--run` prints one closing comparator row, + /// `printenv` names a sysroot with a leanchecker, and `env leanchecker` exits `lc_rc`. + fn run_lake(dir: &Path, lean: &Path, lc_rc: i32) -> String { + std::fs::create_dir_all(lean.join(CHALLENGE_DIR)).expect("mkdir"); + std::fs::write( + lean.join(CHALLENGE_DIR).join("gelu-v1.lean"), + GELU_CHALLENGE, + ) + .expect("w"); + std::fs::create_dir_all(lean.join("scripts")).expect("mkdir"); + std::fs::write(lean.join(COMPARATOR), "").expect("w"); + let root = dir.join("run-sysroot"); + std::fs::create_dir_all(root.join("bin")).expect("mkdir"); + std::fs::write(root.join("bin").join("leanchecker"), "").expect("w"); + let h = "ab".repeat(32); + let rows = dir.join("run-rows"); + std::fs::write( + &rows, + cmp_row( + "ProvableContracts.Gelu.gelu_bound", + &h, + &format!("\"{h}\""), + "[]", + ), + ) + .expect("w"); + let p = dir.join(format!("run-lake-{lc_rc}")); + let script = format!( + "#!/bin/sh\n{SELF_TEST_OK}\ncase \"$2 $3\" in\n 'lean --run') cat '{}' ;;\n printenv*) echo '{}' ;;\n leanchecker*) exit {lc_rc} ;;\nesac\nexit 0\n", + rows.display(), + root.display() + ); + std::fs::write(&p, script).expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + p.to_string_lossy().into_owned() + } + + fn build_sh(dir: &Path, rc: i32) -> String { + let p = dir.join(format!("build-{rc}.sh")); + std::fs::write(&p, format!("echo building\nexit {rc}\n")).expect("w"); + p.to_string_lossy().into_owned() + } + + fn summary_of(lean: &Path) -> summary::Summary { + summary::load(&summary::summary_path(lean)).expect("a summary was written") + } + + fn lc() -> Leanchecker { + LC.expect("LC") + } + + /// The accept: every step green → a green summary deriving the tree's theorem, and the untracked log beside it. + #[test] + fn a_green_run_writes_a_green_summary_and_the_log() { + let (d, lean, contracts) = tree(); + gen(&lean, &contracts, false).expect("gen-axioms"); + let lake = run_lake(d.path(), &lean, 0); + let r = run_all(lk(&lake), &build_sh(d.path(), 0), &lean, &contracts, lc()); + let s = summary_of(&lean); + assert!(r.is_ok(), "{r:?} {s:?}"); + assert!(s.is_green(), "{s:?}"); + assert_eq!(s.challenges_closed.as_deref(), Some("1/1")); + assert!(s.derived().contains("ProvableContracts.Gelu.gelu_bound")); + assert!(s.tree_sha.is_none(), "a tempdir is no git checkout"); + let log: serde_json::Value = + serde_json::from_str(&std::fs::read_to_string(lean.join(LOG_FILE)).expect("log")) + .expect("json"); + assert_eq!(log["verdict"], "accept"); + assert_eq!(log["summary"]["leanchecker_exit"], 0); + } + + /// The spec's mutation: a summary hand-edited toward pass differs from its regeneration, byte for byte. + #[test] + fn a_regeneration_reproduces_the_summary_and_catches_a_hand_edit() { + let (d, lean, contracts) = tree(); + gen(&lean, &contracts, false).expect("gen-axioms"); + let lake = run_lake(d.path(), &lean, 1); + let build = build_sh(d.path(), 0); + assert!(is_reject(&run_all( + lk(&lake), + &build, + &lean, + &contracts, + lc() + ))); + let spath = summary::summary_path(&lean); + let first = std::fs::read_to_string(&spath).expect("r"); + assert!(first.contains("\"leanchecker_exit\": 1"), "{first}"); + let edited = first.replace("\"leanchecker_exit\": 1", "\"leanchecker_exit\": 0"); + std::fs::write(&spath, &edited).expect("w"); + assert!(summary_of(&lean).is_green(), "the edit alone would derive"); + assert!(is_reject(&run_all( + lk(&lake), + &build, + &lean, + &contracts, + lc() + ))); + let again = std::fs::read_to_string(&spath).expect("r"); + assert_eq!( + again, first, + "the regeneration is byte-identical to the honest run" + ); + assert_ne!( + again, edited, + "and so differs from the edit: git diff --exit-code fails" + ); + } + + /// A tree that did not build runs no Lean step, and still leaves a summary saying so. + #[test] + fn a_failed_build_rejects_a_cache_miss_declines_and_both_leave_a_red_summary() { + let (d, lean, contracts) = tree(); + gen(&lean, &contracts, false).expect("gen-axioms"); + let lake = run_lake(d.path(), &lean, 0); + for (rc, want_reject) in [(1, true), (2, false)] { + let r = run_all(lk(&lake), &build_sh(d.path(), rc), &lean, &contracts, lc()); + assert_eq!(is_reject(&r), want_reject, "build rc {rc}: {r:?}"); + assert_eq!(is_decline(&r), !want_reject, "build rc {rc}: {r:?}"); + let s = summary_of(&lean); + assert_eq!(s.build_exit, Some(rc)); + assert_eq!( + (s.lake_exit, s.leanchecker_exit), + (None, None), + "no Lean step ran" + ); + assert!(!s.is_green() && s.derived().is_empty()); + assert!(lean.join(LOG_FILE).is_file()); + } + } + + /// A summary that cannot be written is the run's output missing: reject. + #[test] + fn an_unwritable_summary_rejects() { + let (d, lean, contracts) = tree(); + gen(&lean, &contracts, false).expect("gen-axioms"); + std::fs::create_dir_all(summary::summary_path(&lean)).expect("a dir where the file goes"); + let lake = run_lake(d.path(), &lean, 0); + assert!(is_reject(&run_all( + lk(&lake), + &build_sh(d.path(), 0), + &lean, + &contracts, + lc() + ))); + // The log is written after the summary, so it carries the failed write and the reject (EV-8a quorum). + let log = std::fs::read_to_string(lean.join(LOG_FILE)).expect("the log is still written"); + assert!(log.contains("\"verdict\": \"reject\""), "{log}"); + assert!(log.contains("cannot write"), "{log}"); + } + + /// A `lake` whose `env ` call runs `body` (every other call passes); `$PIDF` is a file for a pid. + fn hang_lake(dir: &Path, step: &str, body: &str) -> (String, PathBuf) { + let pidf = dir.join(format!("grandchild-{step}")); + let p = dir.join(format!("hang-lake-{step}")); + let script = format!( + "#!/bin/sh\nPIDF='{}'\nif [ \"$2\" = {step} ]; then\n {body}\nfi\nexit 0\n", + pidf.display() + ); + std::fs::write(&p, script).expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + (p.to_string_lossy().into_owned(), pidf) + } + + /// Whether `pid` is gone (or a zombie awaiting its reaper), polled for up to 5 s. + fn gone(pid: &str) -> bool { + (0..250).any(|_| { + let alive = std::fs::read_to_string(format!("/proc/{pid}/stat")).is_ok_and(|st| { + st.rsplit(')') + .next() + .is_some_and(|t| !t.trim_start().starts_with('Z')) + }); + if alive { + std::thread::sleep(Duration::from_millis(20)); + } + !alive + }) + } + + /// #4239: a hung `lake env lean Axioms.lean` rejects with a named reason inside its bound, records 124, and + /// takes the `lean` under it (a grandchild in its process group) down with it. + #[test] + fn a_hung_elaboration_rejects_within_its_bound_and_kills_its_process_group() { + let (d, lean, _) = tree(); + let (lake, pidf) = hang_lake(d.path(), "lean", "sleep 60 & echo $! > \"$PIDF\"; wait"); + let mut r = Report::default(); + let t0 = Instant::now(); + lean_steps( + Lake { + bin: &lake, + timeout_s: 1, + }, + &mut r, + &lean, + false, + false, + None, + ); + assert!( + t0.elapsed() < Duration::from_secs(20), + "the bound held: {:?}", + t0.elapsed() + ); + assert!( + r.reject && r.decline.is_none(), + "a hang is RED, not a decline: {:?}", + r.lines + ); + assert_eq!(r.lake_exit, Some(TIMED_OUT)); + assert!( + r.lines + .iter() + .any(|l| l.contains("lake env lean Axioms.lean timed out after 1s")), + "{:?}", + r.lines + ); + let pid = std::fs::read_to_string(&pidf).expect("the fake recorded its grandchild"); + assert!( + gone(pid.trim()), + "grandchild {} outlived the kill", + pid.trim() + ); + } + + /// #4239: the comparator and the leanchecker arm's `printenv LEAN_SYSROOT` are bounded too, and a child that + /// exits while a grandchild holds its stdout cannot stall the drain past the bound. + #[test] + fn every_lake_call_is_bounded_and_a_held_pipe_cannot_stall_it() { + let (d, lean, _) = tree(); + std::fs::create_dir_all(lean.join(CHALLENGE_DIR)).expect("mkdir"); + std::fs::write( + lean.join(CHALLENGE_DIR).join("gelu-v1.lean"), + GELU_CHALLENGE, + ) + .expect("w"); + std::fs::create_dir_all(lean.join("scripts")).expect("mkdir"); + std::fs::write(lean.join(COMPARATOR), "").expect("w"); + let bounded = |lake: &str, cmp: bool, lc: Option| { + let mut r = Report::default(); + let t0 = Instant::now(); + lean_steps( + Lake { + bin: lake, + timeout_s: 1, + }, + &mut r, + &lean, + lc.is_some(), + cmp, + lc, + ); + assert!(t0.elapsed() < Duration::from_secs(20), "{:?}", t0.elapsed()); + r + }; + // `env lean --run …`: $2 is `lean` for elaboration too, so hang only on `--run`. + let (cmp, _) = hang_lake( + d.path(), + "lean", + &format!("{SELF_TEST_OK}; [ \"$3\" = --run ] && sleep 60"), + ); + let r = bounded(&cmp, true, None); + assert!(r.reject && r.decline.is_none(), "{:?}", r.lines); + assert!( + r.lines + .iter() + .any(|l| l.contains("file(s)) timed out after 1s")), + "{:?}", + r.lines + ); + let (sys, _) = hang_lake(d.path(), "printenv", "sleep 60"); + let r = bounded(&sys, false, LC); + assert!(r.reject && r.decline.is_none(), "{:?}", r.lines); + assert!( + r.lines + .iter() + .any(|l| l.contains("printenv LEAN_SYSROOT timed out")), + "{:?}", + r.lines + ); + assert_eq!(r.leanchecker_exit, None, "leanchecker never ran"); + // `lake` exits 0 at once, but a background child keeps stdout open: still bounded, still RED. + let (held, pidf) = hang_lake(d.path(), "lean", "sleep 60 & echo $! > \"$PIDF\"; exit 0"); + let r = bounded(&held, false, None); + assert!(r.reject, "{:?}", r.lines); + let pid = std::fs::read_to_string(&pidf).expect("pid"); + assert!( + gone(pid.trim()), + "pipe holder {} outlived the kill", + pid.trim() + ); + } + + /// #4238: the comparator's sha256 is checked against the FIPS vectors before any row is judged. A failing + /// self-test, a hung one, or one that exits 0 having checked fewer than three vectors rejects, and the rows + /// never run; a passing one is recorded first. + #[test] + fn the_comparator_self_test_gates_the_rows() { + let (d, lean, _) = tree(); + let h = "ab".repeat(32); + let lake = comparator_lake( + d.path(), + &lean, + 0, + &cmp_row( + "ProvableContracts.Gelu.gelu_bound", + &h, + &format!("\"{h}\""), + "[]", + ), + "", + ); + let r = compared(&lake, &lean); + let st = r + .lines + .iter() + .position(|l| l.contains("--self-test (3 FIPS vectors)")); + let rows = r.lines.iter().position(|l| l.contains("file(s))")); + assert!(st.is_some() && st < rows, "self-test first: {:?}", r.lines); + let body = std::fs::read_to_string(&lake).expect("r"); + for (name, stub) in [ + ("fails", "printf 'FAIL sha256 \\\"abc\\\" = 00\\n'; exit 1"), + ( + "two-vectors", + "printf 'ok sha256 a\\nok sha256 b\\n'; exit 0", + ), + ("silent", "exit 0"), + ("hangs", "sleep 60"), + ] { + let p = d.path().join(format!("self-test-{name}")); + std::fs::write( + &p, + body.replace( + SELF_TEST_OK, + &format!("if [ \"$5\" = --self-test ]; then {stub}; fi"), + ), + ) + .expect("w"); + let mut perm = std::fs::metadata(&p).expect("meta").permissions(); + std::os::unix::fs::PermissionsExt::set_mode(&mut perm, 0o755); + std::fs::set_permissions(&p, perm).expect("chmod"); + let mut r = Report::default(); + lean_steps( + Lake { + bin: &p.to_string_lossy(), + timeout_s: 2, + }, + &mut r, + &lean, + false, + true, + None, + ); + assert!(r.reject && r.decline.is_none(), "{name}: {:?}", r.lines); + assert!(r.challenges.is_none(), "{name}: rows judged: {:?}", r.lines); + assert!( + r.lines + .iter() + .any(|l| l.starts_with("FAIL") && l.contains("--self-test")), + "{name}: {:?}", + r.lines + ); + } + } + + /// Kills the ETXTBSY guard in `Lake::spawn`: a script whose write fd is held open fails exec with 26 until the + /// fd closes, and is retried until it does (guard `false` never retries); any other error (a missing bin) is not + /// retried (guard `true` would sleep 20 x 25 ms before returning it). + #[test] + fn spawn_retries_etxtbsy_only() { + use std::os::unix::fs::PermissionsExt; + let d = tempfile::tempdir().expect("tempdir"); + let p = d.path().join("lake"); + let mut f = std::fs::File::create(&p).expect("create"); + std::io::Write::write_all(&mut f, b"#!/bin/sh\nexit 0\n").expect("write"); + std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).expect("chmod"); + let closer = std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(100)); + drop(f); + }); + let bin = p.to_string_lossy().to_string(); + let mut child = lk(&bin) + .spawn::<&str>(&[], d.path()) + .expect("ETXTBSY is retried until the writer closes"); + child.wait().expect("wait"); + closer.join().expect("closer"); + + let t0 = std::time::Instant::now(); + let e = lk("/nonexistent/lake-for-spawn-test") + .spawn::<&str>(&[], d.path()) + .expect_err("a missing bin is an error"); + assert_eq!(e.raw_os_error(), Some(2)); + assert!( + t0.elapsed() < Duration::from_millis(400), + "ENOENT must not be retried: {:?}", + t0.elapsed() + ); + } +} diff --git a/crates/aprender-contracts-cli/src/commands/extract_rdf.rs b/crates/aprender-contracts-cli/src/commands/extract_rdf.rs index a962e180df..eecf5f93c1 100644 --- a/crates/aprender-contracts-cli/src/commands/extract_rdf.rs +++ b/crates/aprender-contracts-cli/src/commands/extract_rdf.rs @@ -25,6 +25,27 @@ struct ExtractReport<'a> { check: Option>, } +/// `--out` and `--cells-out` describe a release; without a release subject they are refused. +fn refuse_release_only_args( + out: Option<&Path>, + cells_out: Option<&Path>, +) -> Result<(), Box> { + if cells_out.is_some() { + return Err(ReleaseArgsRefused( + "--cells-out lists a release's derived cells; it needs --release-version, --release-commit and --surface" + .into(), + ) + .into()); + } + if out.is_some() { + return Err(ReleaseArgsRefused( + "--out writes the release evidence graph; it needs --release-version and --release-commit".into(), + ) + .into()); + } + Ok(()) +} + /// `check == true` → compare and report drift, write nothing. With a release `subject` (aprender#3715) the /// release evidence joins the graph, and the result goes ONLY to `out`: the tracked `contracts.nt` is the corpus, /// and a release's receipts written into it would be a release baked into every later PR's baseline. @@ -33,16 +54,12 @@ pub fn run( check: bool, subject: Option<&Subject>, out: Option<&Path>, + cells_out: Option<&Path>, ) -> Result<(), Box> { if let Some(subject) = subject { - return run_release(contract_dir, check, subject, out); - } - if out.is_some() { - return Err(ReleaseArgsRefused( - "--out writes the release evidence graph; it needs --release-version and --release-commit".into(), - ) - .into()); + return run_release(contract_dir, check, subject, out, cells_out); } + refuse_release_only_args(out, cells_out)?; let extraction = match extract::all(contract_dir) { Ok(x) => x, Err(e) => { @@ -125,6 +142,7 @@ fn run_release( check: bool, subject: &Subject, out: Option<&Path>, + cells_out: Option<&Path>, ) -> Result<(), Box> { if check { return Err(ReleaseArgsRefused( @@ -151,6 +169,9 @@ fn run_release( let mut hasher = Sha256::new(); hasher.update(nt.as_bytes()); std::fs::write(out, &nt)?; + if let Some(path) = cells_out { + write_cells(path, subject, extraction.release.as_ref())?; + } let report = ReleaseExtractReport { triples: extraction.graph.len(), sha256: format!("{:x}", hasher.finalize()), @@ -160,3 +181,69 @@ fn run_release( println!("{}", serde_json::to_string_pretty(&report)?); Ok(()) } + +/// `--cells-out`: every derived cell — the producer's work list, keyed by `cell_id` (aprender#3745 S2). +fn write_cells( + path: &Path, + subject: &Subject, + release: Option<&provable_contracts::ontology::extract::release_evidence::ReleaseStats>, +) -> Result<(), Box> { + let derived = release.map(|r| r.derived.as_slice()).unwrap_or_default(); + if derived.is_empty() { + return Err(ReleaseArgsRefused( + "--cells-out: no cell was derived (no --surface, or a surface with no leaf command)" + .into(), + ) + .into()); + } + let mut doc = serde_json::Map::new(); + doc.insert("schema".into(), "apr-release-cells/v1".into()); + doc.insert("version".into(), subject.version.clone().into()); + doc.insert("release_commit".into(), subject.commit.clone().into()); + doc.insert("cells".into(), serde_json::to_value(derived)?); + std::fs::write(path, serde_json::to_string_pretty(&doc)?)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn subject() -> Subject { + Subject::new("0.69.1", "1111111111111111111111111111111111111111").expect("subject") + } + + fn refused(r: Result<(), Box>) -> String { + r.expect_err("refused").to_string() + } + + #[test] + fn release_only_args_are_refused_by_name_without_a_subject() { + let p = Path::new("x"); + assert!(refuse_release_only_args(None, None).is_ok()); + assert!(refused(refuse_release_only_args(None, Some(p))).starts_with("--cells-out")); + assert!(refused(refuse_release_only_args(Some(p), None)).starts_with("--out")); + let dir = tempfile::tempdir().expect("tempdir"); + assert!(refused(run(dir.path(), false, None, Some(p), None)).starts_with("--out")); + } + + #[test] + fn a_release_run_refuses_check_and_a_missing_out() { + let dir = tempfile::tempdir().expect("tempdir"); + let s = subject(); + assert!(refused(run(dir.path(), true, Some(&s), None, None)).starts_with("--check")); + assert!(refused(run_release(dir.path(), false, &s, None, None)).starts_with("--release-*")); + } + + #[test] + fn cells_out_refuses_a_release_with_no_derived_cell() { + let dir = tempfile::tempdir().expect("tempdir"); + let path = dir.path().join("cells.json"); + let msg = refused(write_cells(&path, &subject(), None)); + assert!(msg.starts_with("--cells-out: no cell was derived"), "{msg}"); + let empty = + provable_contracts::ontology::extract::release_evidence::ReleaseStats::default(); + assert!(write_cells(&path, &subject(), Some(&empty)).is_err()); + assert!(!path.exists(), "nothing is written on a refusal"); + } +} diff --git a/crates/aprender-contracts-cli/src/commands/lint.rs b/crates/aprender-contracts-cli/src/commands/lint.rs index 0f590b07a1..4efbe75983 100644 --- a/crates/aprender-contracts-cli/src/commands/lint.rs +++ b/crates/aprender-contracts-cli/src/commands/lint.rs @@ -48,7 +48,7 @@ pub fn run( watch: bool, strict_test_binding: bool, armed_baseline_ref: Option<&str>, - gate: Option<&str>, + gate: &[String], shapes_opts: ShapesOptions, ) -> Result<(), Box> { refuse_missing_corpus(contract_dir)?; @@ -57,8 +57,10 @@ pub fn run( // is asked for, because run_single_gate would otherwise report every ref // missing on exactly the input the refusal exists for. refuse_single_file_strict_binding(contract_dir, strict_test_binding)?; - if let Some(name) = gate { - return run_single_gate(contract_dir, name, &shapes_opts); + match gate { + [] => {} + [name] => return run_single_gate(contract_dir, name, &shapes_opts, armed_baseline_ref), + names => return run_gates(contract_dir, names, &shapes_opts, armed_baseline_ref), } if watch { return run_watch( @@ -215,8 +217,10 @@ fn run_single_gate( contract_dir: &Path, name: &str, shapes_opts: &ShapesOptions, + armed_baseline_ref: Option<&str>, ) -> Result<(), Box> { - let (result, findings) = decide_named_gate(contract_dir, name, shapes_opts)?; + let (mut result, mut findings) = decide_named_gate(contract_dir, name, shapes_opts)?; + apply_measured_ratchet(contract_dir, armed_baseline_ref, &mut result, &mut findings)?; let report = SingleGateReport { gate: &result.name, @@ -237,6 +241,19 @@ fn run_single_gate( }; println!("{}", serde_json::to_string_pretty(&report)?); + // ONT-4e: `refines` says WHICH clause broke Liskov on the reject line, and which prose clause left it Unknown. + if result.name == provable_contracts::lint::refines_gate::GATE { + let lines = provable_contracts::lint::refines_gate::explain(&result, &findings); + if result.verdict == provable_contracts::ontology::verdict::Verdict::Fail + && !lines.is_empty() + { + return Err(crate::contract_walk::GateRejected(lines.join("; ")).into()); + } + for line in &lines { + eprintln!("{line}"); + } + } + // The exit is the gate's VERDICT, not its `passed` bit: a gate that ran and answered `Unknown{Warn}` (ONT-4b, warnings // and no violation) is a decline, exit 2 — `passed` alone would print 0 for a corpus nobody judged clean. match result.verdict { @@ -252,6 +269,106 @@ fn run_single_gate( } } +/// ONT-4c (v4.14) F-34 on the shapes gate: the measured-set withdrawal ratchet needs git, so it is applied here +/// rather than in the library gate. It upgrades `ratchets.measured_sets` from the library's `not-checked` to +/// `checked` when a comparand was asked, and each violation is an Error finding (PV-ONT-014) that turns the +/// verdict to `Fail` — a ratchet that prints and exits 0 is the shape this corpus keeps refusing. +fn apply_measured_ratchet( + contract_dir: &Path, + armed_baseline_ref: Option<&str>, + result: &mut provable_contracts::lint::GateResult, + findings: &mut Vec, +) -> Result<(), Box> { + use provable_contracts::lint::finding::LintFinding; + use provable_contracts::lint::rules::RuleSeverity; + use provable_contracts::lint::GateExtra; + use std::collections::BTreeSet; + + let Some(GateExtra::Shapes { + readme, + claude_md, + ratchets, + .. + }) = result.extra.as_mut() + else { + return Ok(()); + }; + let live_readme: BTreeSet = readme.verified_commands.iter().cloned().collect(); + let live_claude: BTreeSet = claude_md.verified_commands.iter().cloned().collect(); + let answer = lint_arming::measured_ratchet( + contract_dir, + armed_baseline_ref, + [&live_readme, &live_claude], + )?; + ratchets.measured_sets = answer.status.to_string(); + if answer.violations.is_empty() { + return Ok(()); + } + let file = contract_dir + .join("lint-baseline.json") + .display() + .to_string(); + for m in answer.violations { + findings.push(LintFinding::new( + "PV-ONT-014", + RuleSeverity::Error, + m, + file.clone(), + )); + } + result.passed = false; + result.verdict = provable_contracts::ontology::verdict::Verdict::Fail; + Ok(()) +} + +/// PVL-001 EV-11: `--gate a --gate b` runs every named gate, each printing its own report as `--gate a` alone +/// would, and exits with their MEET: any refusal (exit 3) over any reject (1) over any decline (2) over pass (0). +/// A name this build does not compute is refused before any gate runs, so a typo never reports a partial pass. +fn run_gates( + contract_dir: &Path, + names: &[String], + shapes_opts: &ShapesOptions, + armed_baseline_ref: Option<&str>, +) -> Result<(), Box> { + use provable_contracts::lint::NAMED_GATES; + if let Some(bad) = names.iter().find(|n| !NAMED_GATES.contains(&n.as_str())) { + return Err(crate::contract_walk::UnknownGate { + asked: bad.clone(), + known: NAMED_GATES.iter().map(|g| (*g).to_string()).collect(), + } + .into()); + } + let outcomes: Vec<_> = names + .iter() + .map(|n| run_single_gate(contract_dir, n, shapes_opts, armed_baseline_ref)) + .collect(); + meet(outcomes, names.len()) +} + +/// The MEET of the armed gates' outcomes: a refusal over a reject over a decline over a pass. A reject is +/// re-stated with how many of the `armed` gates passed. Split out of `run_gates` so the order is testable +/// without contracts that make real gates decline or reject (#4588). +fn meet( + outcomes: Vec>>, + armed: usize, +) -> Result<(), Box> { + let rank = |r: &Result<(), Box>| match r { + Ok(()) => 0, + Err(e) if e.is::() => 1, + Err(e) if e.is::() => 2, + Err(_) => 3, + }; + let passed = outcomes.iter().filter(|r| rank(r) == 0).count(); + let worst = outcomes + .into_iter() + .max_by_key(|r| rank(r)) + .unwrap_or(Ok(())); + match worst { + Err(e) if e.is::() => Err(LintRejected { passed, armed }.into()), + other => other, + } +} + /// One gate's run, mapped to a report or to the refusal/decline that stands in its place. Every non-verdict /// answer says WHY on stderr before it returns, because an exit code without a reason is the thing this gate /// exists to refuse. @@ -265,10 +382,7 @@ fn decide_named_gate( name: &str, shapes_opts: &ShapesOptions, ) -> Result> { - use provable_contracts::lint::{ - relations_gate::RelationsOutcome, sigma_gate::SigmaOutcome, - valid_under_gate::ValidUnderOutcome, NamedGateOutcome, NAMED_GATES, - }; + use provable_contracts::lint::{NamedGateOutcome, NAMED_GATES}; match provable_contracts::lint::run_named_gate_with(contract_dir, name, shapes_opts) { NamedGateOutcome::UnknownGate => Err(crate::contract_walk::UnknownGate { @@ -276,43 +390,209 @@ fn decide_named_gate( known: NAMED_GATES.iter().map(|g| (*g).to_string()).collect(), } .into()), - NamedGateOutcome::Sigma(SigmaOutcome::NoSigma) => Err(LintDeclined { - reason: provable_contracts::ontology::verdict::Reason::NoCheckable, + NamedGateOutcome::Shapes(outcome) => decide_shapes_gate(outcome), + NamedGateOutcome::Consistency(outcome) => decide_consistency_gate(outcome), + NamedGateOutcome::Refines(outcome) => decide_refines_gate(outcome), + NamedGateOutcome::Tbox(outcome) => decide_tbox_gate(outcome), + other => decide_scored_gate(name, other), + } +} + +/// A gate whose decline is a bare "nothing checkable" reason, with no message of its own. +fn no_checkable() -> Box { + LintDeclined { + reason: provable_contracts::ontology::verdict::Reason::NoCheckable, + } + .into() +} + +/// The remaining named-gate outcomes that share the `Sigma`-derived answer shape (no Σ, malformed Σ, a +/// verdict): `sigma`, `relations`, `evidence`, `valid-under` and any PVL-001 ratchet. +fn decide_scored_gate( + name: &str, + outcome: provable_contracts::lint::NamedGateOutcome, +) -> Result> { + use provable_contracts::lint::NamedGateOutcome; + + match outcome { + NamedGateOutcome::Sigma(o) => decide_sigma_gate(o), + NamedGateOutcome::Relations(o) => decide_relations_gate(o), + NamedGateOutcome::Evidence(o) => decide_evidence_gate(o), + NamedGateOutcome::ValidUnder(o) => decide_valid_under_gate(o), + NamedGateOutcome::Ratchet(o) => decide_ratchet_gate(name, o), + NamedGateOutcome::Ran { result, findings } => Ok((result, findings)), + _ => unreachable!( + "decide_named_gate routes UnknownGate/Shapes/Consistency/Refines/Tbox directly" + ), + } +} + +/// The `sigma` gate's answers: no Σ, malformed Σ, or a verdict. +fn decide_sigma_gate( + outcome: provable_contracts::lint::sigma_gate::SigmaOutcome, +) -> Result> { + use provable_contracts::lint::sigma_gate::SigmaOutcome; + + match outcome { + SigmaOutcome::NoSigma => Err(no_checkable()), + SigmaOutcome::Malformed(e) => { + Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) } - .into()), - NamedGateOutcome::Sigma(SigmaOutcome::Malformed(e)) => { + SigmaOutcome::Ran { result, findings } => Ok((result, findings)), + } +} + +/// The `relations` gate's answers (ONT-4): no Σ, no typed relations, malformed Σ, or a verdict. +fn decide_relations_gate( + outcome: provable_contracts::lint::relations_gate::RelationsOutcome, +) -> Result> { + use provable_contracts::lint::relations_gate::RelationsOutcome; + + match outcome { + RelationsOutcome::NoSigma | RelationsOutcome::NoRelations { .. } => Err(no_checkable()), + RelationsOutcome::Malformed(e) => { Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) } - NamedGateOutcome::Relations( - RelationsOutcome::NoSigma | RelationsOutcome::NoRelations { .. }, - ) => Err(LintDeclined { - reason: provable_contracts::ontology::verdict::Reason::NoCheckable, + RelationsOutcome::Ran { result, findings } => Ok((result, findings)), + } +} + +/// The `evidence` gate's answers (ONT-8): no Σ, no evidence block (prints why first), malformed Σ, or a verdict. +fn decide_evidence_gate( + outcome: provable_contracts::lint::evidence_gate::EvidenceOutcome, +) -> Result> { + use provable_contracts::lint::evidence_gate::EvidenceOutcome; + + match outcome { + EvidenceOutcome::NoSigma => Err(no_checkable()), + EvidenceOutcome::NoEvidence { contracts_checked } => { + eprintln!( + "evidence: no evidence block in {contracts_checked} contract(s) — nothing was measured" + ); + Err(no_checkable()) } - .into()), - NamedGateOutcome::Relations(RelationsOutcome::Malformed(e)) => { + EvidenceOutcome::Malformed(e) => { Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) } - NamedGateOutcome::Shapes(outcome) => decide_shapes_gate(outcome), - NamedGateOutcome::ValidUnder(ValidUnderOutcome::NoSigma) => Err(LintDeclined { - reason: provable_contracts::ontology::verdict::Reason::NoCheckable, + EvidenceOutcome::Ran { result, findings } => Ok((result, findings)), + } +} + +/// The `valid-under` gate's answers (ONT-7): no Σ, no kernels (prints why first), malformed Σ, or a verdict. +fn decide_valid_under_gate( + outcome: provable_contracts::lint::valid_under_gate::ValidUnderOutcome, +) -> Result> { + use provable_contracts::lint::valid_under_gate::ValidUnderOutcome; + + match outcome { + ValidUnderOutcome::NoSigma => Err(no_checkable()), + ValidUnderOutcome::NoKernels { contracts_checked } => { + eprintln!( + "valid-under: no kernel-kind contract and no valid_under in {contracts_checked} contract(s) — nothing was measured" + ); + Err(no_checkable()) + } + ValidUnderOutcome::Malformed(e) => { + Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) + } + ValidUnderOutcome::Ran { result, findings } => Ok((result, findings)), + } +} + +/// A PVL-001 EV-11 ratchet's answers: a decline (prints why first) or a verdict. +fn decide_ratchet_gate( + name: &str, + outcome: provable_contracts::lint::ratchet_gates::RatchetOutcome, +) -> Result> { + use provable_contracts::lint::ratchet_gates::RatchetOutcome; + + match outcome { + RatchetOutcome::Declined(why) => { + eprintln!("{name}: {why}"); + Err(no_checkable()) + } + RatchetOutcome::Ran { result, findings } => Ok((result, findings)), + } +} + +/// The `ont-consistency` gate's answers (ONT-5). Only `Ran` is a verdict; every decline prints WHY first — a stale +/// witness names the command that regenerates it. +fn decide_consistency_gate( + outcome: provable_contracts::lint::consistency_gate::ConsistencyOutcome, +) -> Result> { + use provable_contracts::lint::consistency_gate::{decline_reason, why, ConsistencyOutcome}; + + match outcome { + ConsistencyOutcome::Ran { result, findings } => Ok((result, findings)), + ConsistencyOutcome::Malformed(e) => { + Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) + } + other => { + eprintln!("ont-consistency: {}", why(&other)); + let reason = decline_reason(&other) + .unwrap_or(provable_contracts::ontology::verdict::Reason::NoCheckable); + Err(LintDeclined { reason }.into()) + } + } +} + +/// The `refines` gate's answers (ONT-4e). Only `Ran` is a verdict; every decline prints WHY first — a stale Liskov +/// witness names `make contracts`. +fn decide_refines_gate( + outcome: provable_contracts::lint::refines_gate::RefinesOutcome, +) -> Result> { + use provable_contracts::lint::refines_gate::{decline_reason, why, RefinesOutcome, GATE}; + + match outcome { + RefinesOutcome::Ran { result, findings } => Ok((result, findings)), + RefinesOutcome::Malformed(e) => { + Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) + } + other => { + eprintln!("{GATE}: {}", why(&other)); + let reason = decline_reason(&other) + .unwrap_or(provable_contracts::ontology::verdict::Reason::NoCheckable); + Err(LintDeclined { reason }.into()) + } + } +} + +/// The `tbox` gate's answers (ONT-2c). There is no verdict arm: a clean classification is `Unknown{Advisory}` +/// (R-7, no inferred fact arms a merge); everything else is the declaration's fault (exit 3). +fn decide_tbox_gate( + outcome: provable_contracts::lint::tbox_gate::TboxOutcome, +) -> Result> { + use provable_contracts::lint::tbox_gate::TboxOutcome; + use provable_contracts::ontology::verdict::Reason; + + match outcome { + TboxOutcome::NoSigma => Err(LintDeclined { + reason: Reason::NoCheckable, } .into()), - NamedGateOutcome::ValidUnder(ValidUnderOutcome::NoKernels { contracts_checked }) => { + TboxOutcome::Malformed(e) | TboxOutcome::Stale(e) => { + Err(crate::contract_walk::SigmaMalformed(e).into()) + } + TboxOutcome::PreconditionFailed(refused) => { + Err(crate::contract_walk::SigmaMalformed(format!( + "told-closure precondition fails, so no classification is claimed: {}", + refused.join("; ") + )) + .into()) + } + TboxOutcome::Advisory(report) => { eprintln!( - "valid-under: no kernel-kind contract and no valid_under in {contracts_checked} contract(s) — nothing was measured" + "tbox: {} classes, consistent={}, unintended_subsumptions={} (method {}, advisory; never arms)", + report.classes, + report.consistent, + report.unintended_subsumptions.len(), + report.method ); Err(LintDeclined { - reason: provable_contracts::ontology::verdict::Reason::NoCheckable, + reason: Reason::Advisory, } .into()) } - NamedGateOutcome::ValidUnder(ValidUnderOutcome::Malformed(e)) => { - Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) - } - NamedGateOutcome::Sigma(SigmaOutcome::Ran { result, findings }) - | NamedGateOutcome::Relations(RelationsOutcome::Ran { result, findings }) - | NamedGateOutcome::ValidUnder(ValidUnderOutcome::Ran { result, findings }) - | NamedGateOutcome::Ran { result, findings } => Ok((result, findings)), } } @@ -322,7 +602,6 @@ fn decide_shapes_gate( outcome: provable_contracts::lint::shapes_gate::ShapesOutcome, ) -> Result> { use provable_contracts::lint::shapes_gate::ShapesOutcome; - use provable_contracts::ontology::verdict::Reason; match outcome { ShapesOutcome::Unsupported(e) => { @@ -331,14 +610,24 @@ fn decide_shapes_gate( ShapesOutcome::ExtractFailed(e) => { Err(crate::contract_walk::SigmaMalformed(e.to_string()).into()) } - ShapesOutcome::NoShapes { .. } => Err(LintDeclined { - reason: Reason::NoShapes, - } - .into()), - ShapesOutcome::NoFocus { .. } => Err(LintDeclined { - reason: Reason::NoFocus, + ShapesOutcome::Ran { result, findings } => Ok((result, findings)), + declined => Err(LintDeclined { + reason: shapes_decline_reason(declined), } .into()), + } +} + +/// A `shapes` outcome that is a decline: print what could not be checked, return the decline's reason. +fn shapes_decline_reason( + outcome: provable_contracts::lint::shapes_gate::ShapesOutcome, +) -> provable_contracts::ontology::verdict::Reason { + use provable_contracts::lint::shapes_gate::ShapesOutcome; + use provable_contracts::ontology::verdict::Reason; + + match outcome { + ShapesOutcome::NoShapes { .. } => Reason::NoShapes, + ShapesOutcome::NoFocus { .. } => Reason::NoFocus, ShapesOutcome::WrongCorpus { shapes_n, expected, @@ -351,10 +640,7 @@ fn decide_shapes_gate( for r in &refused { eprintln!("shapes: refused {r}"); } - Err(LintDeclined { - reason: Reason::WrongCorpus, - } - .into()) + Reason::WrongCorpus } ShapesOutcome::NoReceipts { shapes_n, dir } => { // ONT-4c1: the WHY travels with the decline — the lattice has no ReceiptUnmeasured element (ONT-6's @@ -362,17 +648,24 @@ fn decide_shapes_gate( eprintln!( "shapes: {shapes_n} shape(s) resolve receipts and the tree holds none under {dir}/" ); - Err(LintDeclined { - reason: Reason::NoCheckable, + Reason::NoCheckable + } + ShapesOutcome::HarnessBroken { causes } => { + for c in &causes { + eprintln!("shapes: CRUX harness broken — {c}"); } - .into()) + Reason::NoCheckable + } + ShapesOutcome::EmptyDomain { shapes_n } => { + // ONT-4c5 / R-2: |D| = 0 answers nothing about a required cell — a decline, never Pass, never RED + eprintln!( + "shapes: capability-cells domain D is empty ({shapes_n} shape(s)) — no required rung has a host to be measured on" + ); + Reason::NoCheckable } ShapesOutcome::PositiveControlFailed { which, .. } => { eprintln!("shapes: positive control {which} did not fire"); - Err(LintDeclined { - reason: Reason::PositiveControlFailed, - } - .into()) + Reason::PositiveControlFailed } ShapesOutcome::Differential { passed, n, failed, .. @@ -384,12 +677,12 @@ fn decide_shapes_gate( for f in &failed { eprintln!(" {f}"); } - Err(LintDeclined { - reason: Reason::Differential, - } - .into()) + Reason::Differential } - ShapesOutcome::Ran { result, findings } => Ok((result, findings)), + // answered by decide_shapes_gate before it asks for a decline reason + ShapesOutcome::Unsupported(_) + | ShapesOutcome::ExtractFailed(_) + | ShapesOutcome::Ran { .. } => Reason::NoCheckable, } } @@ -865,3 +1158,63 @@ fn collect_yaml_files_lint(dir: &std::path::Path, out: &mut Vec>; + fn pass() -> Outcome { + Ok(()) + } + fn decline() -> Outcome { + Err(LintDeclined { + reason: Reason::NotArmed, + } + .into()) + } + fn reject() -> Outcome { + Err(LintRejected { + passed: 99, + armed: 99, + } + .into()) + } + fn refuse() -> Outcome { + Err(std::io::Error::other("refused").into()) + } + + /// Refusal over reject over decline over pass, whichever order the gates ran in (#4588). + #[test] + fn meet_orders_refusal_reject_decline_pass() { + assert!(meet(vec![pass(), pass()], 2).is_ok()); + for outcomes in [vec![pass(), decline()], vec![decline(), pass()]] { + assert!(meet(outcomes, 2) + .expect_err("a decline") + .is::()); + } + for outcomes in [vec![reject(), decline()], vec![decline(), reject()]] { + assert!(meet(outcomes, 2) + .expect_err("a reject") + .is::()); + } + for outcomes in [ + vec![refuse(), reject()], + vec![reject(), refuse()], + vec![refuse(), decline()], + vec![decline(), refuse()], + ] { + let e = meet(outcomes, 2).expect_err("a refusal"); + assert!(e.is::(), "{e}"); + } + } + + /// A reject is re-stated with the meet's own counts, not the gate's. + #[test] + fn meet_restates_a_reject_with_passed_of_armed() { + let e = meet(vec![pass(), reject(), decline(), pass(), pass()], 5).expect_err("a reject"); + let r = e.downcast_ref::().expect("LintRejected"); + assert_eq!((r.passed, r.armed), (3, 5)); + } +} diff --git a/crates/aprender-contracts-cli/src/commands/lint_arming.rs b/crates/aprender-contracts-cli/src/commands/lint_arming.rs index 0c27c32d97..2ab9bd7af4 100644 --- a/crates/aprender-contracts-cli/src/commands/lint_arming.rs +++ b/crates/aprender-contracts-cli/src/commands/lint_arming.rs @@ -14,6 +14,7 @@ //! A committed `lint-baseline.json` that does not parse is an error on both paths: the committed value //! exists, it just cannot be read, and skipping it would disarm the check for exactly that commit. +use std::collections::BTreeSet; use std::error::Error; use std::path::{Path, PathBuf}; use std::process::Command; @@ -21,6 +22,7 @@ use std::process::Command; use provable_contracts::ontology::arming::{ check_monotone, check_shapes_monotone, ArmedGates, ArmedShapes, }; +use provable_contracts::ontology::measured_sets; const BASELINE: &str = "lint-baseline.json"; const NO_COMPARAND: &str = "NOT CHECKED (no comparand)"; @@ -41,7 +43,7 @@ pub fn resolve(contract_dir: &Path, explicit_ref: Option<&str>) -> Result ( + Comparand::Absent(_, why) => ( format!("{NO_COMPARAND} — {why}"), format!("{NO_COMPARAND} — {why}"), ), @@ -127,6 +129,62 @@ pub fn declared(contract_dir: &Path) -> Result> { )?) } +/// ONT-4c (v4.14) F-34: the measured-set withdrawal ratchet's answer for one run. +pub struct MeasuredRatchet { + /// `checked` when F-34 was computed against a comparand; `not-checked` outside a git work tree. + pub status: &'static str, + /// One RED message per violation (a drop with no `withdrawn[]` entry, an entry with no drop, no comparand + /// ref in a work tree, a set that cannot be read). + pub violations: Vec, +} + +/// F-34 against the comparand `resolve` uses: `withdrawn(HEAD) \ withdrawn(comparand) == set(comparand) \ +/// set(live)` for each key of [`measured_sets::KEYS`], `live` in the same order. The three no-comparand cases +/// are NOT the arming monotone's single `NOT CHECKED`: outside a work tree there is nothing to ask +/// (`not-checked`, which a probe reads as not green); a work tree whose comparand ref does not resolve is RED — +/// on a CI checkout that is a fetch misconfiguration, and answering "not checked" there would disarm the +/// ratchet on exactly the runs that gate; a corpus untracked at the comparand has every set ∅ there. +pub fn measured_ratchet( + contract_dir: &Path, + explicit_ref: Option<&str>, + live: [&BTreeSet; 2], +) -> Result> { + let comparand_text = match comparand(contract_dir, explicit_ref)? { + Comparand::Absent(Missing::WorkTree, _) => { + return Ok(MeasuredRatchet { + status: "not-checked", + violations: Vec::new(), + }) + } + Comparand::Absent(Missing::Ref, why) => { + return Ok(MeasuredRatchet { + status: "checked", + violations: vec![format!( + "F-34: no comparand in a git work tree ({why}) — fetch origin/main; {}", + measured_sets::REMEDY + )], + }) + } + Comparand::Absent(Missing::Untracked, _) => None, + Comparand::At { text, .. } => text, + }; + let head_text = read_baseline(contract_dir)?; + let mut violations = Vec::new(); + for (key, live) in measured_sets::KEYS.iter().zip(live) { + let head = measured_sets::read(head_text.as_deref(), key); + let cmp = measured_sets::read(comparand_text.as_deref(), key); + match (head, cmp) { + (Ok(h), Ok(c)) => violations.extend(measured_sets::withdrawal(key, &h, &c, live)), + (Err(e), _) => violations.push(format!("F-34: {e}")), + (_, Err(e)) => violations.push(format!("F-34: comparand {e}")), + } + } + Ok(MeasuredRatchet { + status: "checked", + violations, + }) +} + /// `/lint-baseline.json`, or `None` when the corpus has none (a single-file corpus never does). fn read_baseline(contract_dir: &Path) -> Result, Box> { if !contract_dir.is_dir() { @@ -140,9 +198,21 @@ fn read_baseline(contract_dir: &Path) -> Result, Box> } } +/// Why the default path has no comparand. ONT-4c (v4.14) splits the three: the measured-set ratchet (F-34) +/// answers each differently, where the arming monotone prints `NOT CHECKED` for all of them. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Missing { + /// The corpus is not in a git work tree: nothing to ask. + WorkTree, + /// A work tree with neither merge-base(HEAD, origin/main) nor origin/main. + Ref, + /// A comparand commit at which the corpus is not tracked: every committed set there is ∅. + Untracked, +} + enum Comparand { - /// Default path only: nothing committed to compare against, and why. - Absent(String), + /// Default path only: nothing committed to compare against, which case, and why. + Absent(Missing, String), /// The committed baseline text at `label` (`None`: the corpus is tracked there without a baseline, /// which arms the default set). At { @@ -164,7 +234,7 @@ fn comparand(contract_dir: &Path, explicit_ref: Option<&str>) -> Result Err(format!("--armed-baseline-ref {r}: {why}").into()), - None => Ok(Comparand::Absent(why)), + None => Ok(Comparand::Absent(Missing::WorkTree, why)), }; }; let (commit, label) = match explicit_ref { @@ -184,6 +254,7 @@ fn comparand(contract_dir: &Path, explicit_ref: Option<&str>) -> Result found, None => { return Ok(Comparand::Absent( + Missing::Ref, "neither merge-base(HEAD, origin/main) nor origin/main resolves".into(), )) } @@ -193,7 +264,7 @@ fn comparand(contract_dir: &Path, explicit_ref: Option<&str>) -> Result Err(format!("--armed-baseline-ref {r}: {why}").into()), - None => Ok(Comparand::Absent(why)), + None => Ok(Comparand::Absent(Missing::Untracked, why)), }; } let path = if rel.is_empty() { diff --git a/crates/aprender-contracts-cli/src/commands/mod.rs b/crates/aprender-contracts-cli/src/commands/mod.rs index c9734eccc5..0d2d4a9e82 100644 --- a/crates/aprender-contracts-cli/src/commands/mod.rs +++ b/crates/aprender-contracts-cli/src/commands/mod.rs @@ -2,11 +2,13 @@ pub mod audit; pub mod book; pub mod census; pub mod certify; +pub mod challenge; pub mod check_parity; pub mod codegen; pub mod coq; pub mod coverage; pub mod diff; +pub mod discharge; pub mod equations; pub mod explain; pub mod extract; @@ -24,6 +26,8 @@ pub mod lean_status; pub mod lint; pub mod migrate; pub mod mirai; +pub mod obligations; +pub mod ontology; pub mod pipeline; pub mod probar; pub mod proof_status; diff --git a/crates/aprender-contracts-cli/src/commands/obligations.rs b/crates/aprender-contracts-cli/src/commands/obligations.rs new file mode 100644 index 0000000000..5f2edf2655 --- /dev/null +++ b/crates/aprender-contracts-cli/src/commands/obligations.rs @@ -0,0 +1,506 @@ +//! `pv obligations [ROOT] [--gate]` — PVL-001 EV-10 (paiml/aprender#4198). +//! +//! A native replacement for pmat's `scripts/pv-obligation-gate.py` (sha256 `e4fa9719…`, pmat +//! `7c93535e`), which pmat's `Makefile:227` and `quality-gate.yml:246` run as a blocking gate. +//! Every contract must be readable by pv, and its obligations must bind to code. Three checks, +//! run per contract in the script's order, over `ROOT/contracts/*.yaml` (not recursive; a name +//! ending in `binding.yaml` is skipped) with functions searched under `ROOT/src/`: +//! +//! 1. `pv validate` passes — here in-process ([`validate_artifact`], the same decision +//! `pv validate ` makes: any error-severity violation, or no verdict at all, fails). +//! 2. No test-bearing entry (a mapping with a `test` key) hides under `falsification:`, the key +//! pv does not read. Entries without `test` are alert thresholds and are left alone. +//! 3. Every `applies_to` other than `all`, a falsy value, or an equation of the contract names a +//! `fn` under `src/`; where the contract declares `metadata.proved_type`, at least one file +//! defining that `fn` mentions the type as a word. +//! +//! Output is the script's, byte for byte: one `::error::` line per problem, then +//! `pv obligation gate: N problem(s) over M contracts`. Without `--gate` the exit is 0; with it, +//! any problem is `reject:` at exit 1. `tests/pvl_obligations_golden.rs` holds the two against +//! outputs recorded from the script. +//! +//! **Where it deliberately differs from the script** (each is an input the script does not +//! survive, or PVL-1): +//! - zero contracts is `decline:` at exit 2 (PVL-1), where the script reports 0 over 0 at exit 0; +//! - an input the script dies on with a Python traceback is a named problem here: a file that +//! cannot be read or is not YAML; a truthy document that is not a mapping; a truthy +//! `equations`/`metadata` that is not a mapping, or `proof_obligations`/`falsification` that +//! is not a list (a `falsification` mapping or string counts 0, as the script's loop does); +//! a `proof_obligations` entry that is not a mapping; a truthy `applies_to` that is not a +//! string; a truthy `proved_type` that is not a string, where a bound `fn` would be searched +//! for it; +//! - YAML is read by `serde_yaml` (YAML 1.2), the script's by PyYAML (YAML 1.1): a plain +//! `yes`/`no`/`on`/`off` is a string here and a boolean there; +//! - a document `serde_yaml` cannot read that PyYAML can (a duplicate key, an integer beyond +//! 64 bits, …) gets no checks 2 and 3 here. `pv validate` reads with the same parser, so +//! check 1 fails on it on both sides, and pv adds no parse line of its own: the report is +//! the script's unless PyYAML also finds a check-2 or check-3 problem in it, which pv then +//! does not name (the verdict still agrees); +//! - `src/` is walked on disk (symlinks not followed), where the script's `git grep` searches +//! tracked files only: an untracked file under `src/` counts here; +//! - a word character is `char::is_alphanumeric` or `_`; next to non-ASCII text this can differ +//! from Python's `re` and from `git grep`'s locale. +//! +//! **Where it deliberately agrees** (each held by `tests/fixtures/pvl/obligations/edge/`): merge +//! keys (`<<`) are resolved as PyYAML resolves them, and falsy values follow Python truthiness — +//! a falsy document is `{}`, a falsy `applies_to` is skipped, a falsy `proved_type` is none. +//! +//! Line-by-line mapping (script → here): `CONTRACTS` → [`contract_files`]; `_fn_files` → +//! [`SrcTree::fn_files`]; `check_validate` → [`check_validate`]; `check_visible` → +//! [`check_visible`]; `check_bindings` → [`check_bindings`]; `main` → [`run`]; `{x!r}` → +//! [`py_repr`]; `{files}` → [`py_list`]; `re` `\b` → [`boundary`]. + +use std::path::{Path, PathBuf}; + +use provable_contracts::error::Severity; +use provable_contracts::schema::validate_artifact; +use serde_yaml::{Mapping, Value}; + +use crate::contract_walk::{ObligationsRejected, ZeroContracts}; + +/// Run the three checks over `root`. With `gate`, any problem is an error (exit 1). +/// +/// # Errors +/// [`ZeroContracts`] when `root/contracts` holds no contract; [`ObligationsRejected`] under +/// `gate` when any problem was found. +pub fn run(root: &Path, gate: bool) -> Result<(), Box> { + let contracts = contract_files(root); + if contracts.is_empty() { + return Err(ZeroContracts { + path: root.join("contracts"), + filter: None, + } + .into()); + } + let mut src = SrcTree::new(root.join("src")); + let mut problems = Vec::new(); + for rel in &contracts { + problems.extend(check_contract(root, rel, &mut src)); + } + for p in &problems { + println!("::error::{p}"); + } + println!( + "pv obligation gate: {} problem(s) over {} contracts", + problems.len(), + contracts.len() + ); + if gate && !problems.is_empty() { + return Err(ObligationsRejected { + problems: problems.len(), + contracts: contracts.len(), + } + .into()); + } + Ok(()) +} + +/// `contracts/*.yaml` under `root`, relative and `/`-joined, sorted, `*binding.yaml` excluded. +/// Hidden names are excluded as `glob`'s `*` excludes them. Like `glob`, the name alone decides: +/// a symlink is followed when read, and an entry that cannot be read is a named problem. +fn contract_files(root: &Path) -> Vec { + let Ok(entries) = std::fs::read_dir(root.join("contracts")) else { + return Vec::new(); + }; + let mut out: Vec = entries + .filter_map(Result::ok) + .map(|e| e.file_name().to_string_lossy().into_owned()) + .filter(|n| !n.starts_with('.') && n.ends_with(".yaml") && !n.ends_with("binding.yaml")) + .map(|n| format!("contracts/{n}")) + .collect(); + out.sort(); + out +} + +fn check_contract(root: &Path, rel: &str, src: &mut SrcTree) -> Vec { + let mut problems = check_validate(root, rel); + let doc = match load(&root.join(rel)) { + Ok(doc) => doc, + // A document `serde_yaml` refuses also fails `pv validate`, the script's only + // report on it when PyYAML finds nothing else; a second line would miscount it. + // An `if`, not a match guard: every validate path parses with the same serde_yaml, so + // `problems` is never empty here and a guard's `-> true` mutant was equivalent (#4588). + Err(Load::Parse(why)) => { + if problems.is_empty() { + problems.push(format!("{rel}: {why}")); + } + return problems; + } + Err(Load::Other(why)) => { + problems.push(format!("{rel}: {why}")); + return problems; + } + }; + problems.extend(check_visible(rel, &doc)); + problems.extend(check_bindings(rel, &doc, src)); + problems +} + +/// Why a contract could not be loaded. `Parse` is `serde_yaml` refusing the text. +enum Load { + Parse(String), + Other(String), +} + +/// `yaml.safe_load(...) or {}`: merge keys (`<<`) resolved as PyYAML resolves them, and a +/// falsy document (empty, `false`, `0`, `[]`) is an empty mapping. +fn load(path: &Path) -> Result { + let text = + std::fs::read_to_string(path).map_err(|e| Load::Other(format!("cannot be read: {e}")))?; + let mut doc: Value = serde_yaml::from_str(&text) + .map_err(|e| Load::Parse(format!("does not parse as YAML: {e}")))?; + // One pass resolves one level; a merged mapping that itself merges needs another. + loop { + let before = doc.clone(); + doc.apply_merge() + .map_err(|e| Load::Other(format!("has a merge key PyYAML refuses: {e}")))?; + if doc == before { + break; + } + } + match doc { + Value::Mapping(m) => Ok(m), + v if !truthy(&v) => Ok(Mapping::new()), + _ => Err(Load::Other("is not a YAML mapping".into())), + } +} + +/// Python's `bool(x)` over a YAML value — what the script's `x or {}`, `if not target` and +/// `if proved` decide on. +fn truthy(v: &Value) -> bool { + match v { + Value::Null => false, + Value::Bool(b) => *b, + Value::Number(n) => n.as_f64().is_none_or(|f| f != 0.0), + Value::String(s) => !s.is_empty(), + Value::Sequence(s) => !s.is_empty(), + Value::Mapping(m) => !m.is_empty(), + Value::Tagged(_) => true, + } +} + +/// `m.get(key) or `: `None` when the key is absent or its value is falsy. +fn get_truthy<'a>(m: &'a Mapping, key: &str) -> Option<&'a Value> { + m.get(key).filter(|v| truthy(v)) +} + +fn check_validate(root: &Path, rel: &str) -> Vec { + let passes = validate_artifact(&root.join(rel)) + .is_ok_and(|(_, v)| v.iter().all(|v| v.severity != Severity::Error)); + if passes { + Vec::new() + } else { + vec![format!("{rel}: pv validate failed")] + } +} + +fn check_visible(rel: &str, doc: &Mapping) -> Vec { + let hidden = match get_truthy(doc, "falsification") { + None => 0, + Some(Value::Sequence(entries)) => entries + .iter() + .filter(|e| e.as_mapping().is_some_and(|m| m.contains_key("test"))) + .count(), + // Iterating a dict or a str yields keys or characters, never a dict. + Some(Value::Mapping(_) | Value::String(_)) => 0, + Some(_) => return vec![format!("{rel}: falsification is not a list")], + }; + if hidden == 0 { + return Vec::new(); + } + vec![format!( + "{rel}: {hidden} test-bearing obligation(s) under `falsification:`, \ + which pv cannot read — use `falsification_tests:`" + )] +} + +/// `metadata.proved_type` as the script reads it: `(doc.get("metadata") or {}).get(...)`, then +/// `if proved`. A truthy non-string is only an error where the script would `re.escape` it. +#[derive(Clone, Copy)] +enum Proved<'a> { + Absent, + Type(&'a str), + NotAString, +} + +fn check_bindings(rel: &str, doc: &Mapping, src: &mut SrcTree) -> Vec { + let (equations, proved, obligations) = match bindings_of(doc) { + Ok(parts) => parts, + Err(why) => return vec![format!("{rel}: {why}")], + }; + let mut problems = Vec::new(); + for ob in obligations { + let Some(ob) = ob.as_mapping() else { + problems.push(format!("{rel}: a proof_obligations entry is not a mapping")); + continue; + }; + // `if not target or target == "all" or target in equations: continue` + let Some(target) = get_truthy(ob, "applies_to") else { + continue; + }; + if equations.contains(&target) { + continue; + } + let Some(target) = target.as_str() else { + problems.push(format!("{rel}: an applies_to is not a string")); + continue; + }; + if target != "all" { + problems.extend(check_target(rel, target, proved, src)); + } + } + problems +} + +/// The equation names, the proved type and the obligations, each read as the script reads +/// them; a shape the script would die on is an error naming it. +fn bindings_of(doc: &Mapping) -> Result<(Vec<&Value>, Proved<'_>, &[Value]), String> { + let equations = match get_truthy(doc, "equations") { + None => Vec::new(), + Some(Value::Mapping(m)) => m.keys().collect(), + Some(_) => return Err("equations is not a mapping".into()), + }; + let proved = match get_truthy(doc, "metadata") { + None => Proved::Absent, + Some(Value::Mapping(m)) => match get_truthy(m, "proved_type") { + None => Proved::Absent, + Some(Value::String(s)) => Proved::Type(s), + Some(_) => Proved::NotAString, + }, + Some(_) => return Err("metadata is not a mapping".into()), + }; + let obligations = match get_truthy(doc, "proof_obligations") { + None => &[][..], + Some(Value::Sequence(obs)) => obs.as_slice(), + Some(_) => return Err("proof_obligations is not a list".into()), + }; + Ok((equations, proved, obligations)) +} + +fn check_target(rel: &str, target: &str, proved: Proved<'_>, src: &mut SrcTree) -> Option { + let files = src.fn_files(target); + if files.is_empty() { + return Some(format!( + "{rel}: applies_to {} names neither an equation of this contract nor any `fn` under src/", + py_repr(target) + )); + } + let proved = match proved { + Proved::Absent => return None, + Proved::Type(t) => t, + Proved::NotAString => { + return Some(format!( + "{rel}: applies_to {} is proved against a metadata.proved_type that is not a string", + py_repr(target) + )) + } + }; + if files.iter().any(|f| src.mentions(f, proved)) { + return None; + } + Some(format!( + "{rel}: applies_to {} is proved against {}, but none of {} mentions it — the proof does \ + not reach the code it names", + py_repr(target), + py_repr(proved), + py_list(&files) + )) +} + +/// Every file under `src/`, read once, lossily, on first use. Keys are `src/…`, `/`-joined, +/// in byte order (`git grep -l`'s order). +struct SrcTree { + dir: PathBuf, + files: Option>, +} + +impl SrcTree { + fn new(dir: PathBuf) -> Self { + Self { dir, files: None } + } + + fn loaded(&mut self) -> &[(String, String)] { + let dir = &self.dir; + self.files.get_or_insert_with(|| { + let mut paths = Vec::new(); + walk(dir, &mut paths); + let mut files: Vec<(String, String)> = paths + .into_iter() + .filter_map(|p| { + let bytes = std::fs::read(&p).ok()?; + let rel = p.strip_prefix(dir).ok()?; + let key = std::iter::once("src".to_owned()) + .chain( + rel.components() + .map(|c| c.as_os_str().to_string_lossy().into_owned()), + ) + .collect::>() + .join("/"); + Some((key, String::from_utf8_lossy(&bytes).into_owned())) + }) + .collect(); + files.sort(); + files + }) + } + + /// `git grep -l -E "fn \b" -- src/`. + fn fn_files(&mut self, name: &str) -> Vec { + let needle = format!("fn {name}"); + self.loaded() + .iter() + .filter(|(_, text)| { + text.match_indices(&needle) + .any(|(i, m)| boundary(&text[..i + m.len()], &text[i + m.len()..])) + }) + .map(|(k, _)| k.clone()) + .collect() + } + + /// `re.search(rf"\b{re.escape(word)}\b", text)`. + fn mentions(&mut self, key: &str, word: &str) -> bool { + self.loaded().iter().any(|(k, text)| { + k == key + && text.match_indices(word).any(|(i, m)| { + boundary(&text[..i], &text[i..]) + && boundary(&text[..i + m.len()], &text[i + m.len()..]) + }) + }) + } +} + +fn walk(dir: &Path, out: &mut Vec) { + let Ok(entries) = std::fs::read_dir(dir) else { + return; + }; + for entry in entries.filter_map(Result::ok) { + let Ok(kind) = entry.file_type() else { + continue; + }; + if kind.is_dir() { + walk(&entry.path(), out); + } else if kind.is_file() { + out.push(entry.path()); + } + } +} + +/// A regex `\b` between `before` and `after`: exactly one side is a word character. +fn boundary(before: &str, after: &str) -> bool { + let word = |c: Option| c.is_some_and(|c| c.is_alphanumeric() || c == '_'); + word(before.chars().next_back()) != word(after.chars().next()) +} + +/// Python's `repr` of a `str`: single quotes unless the text holds `'` and no `"`. +fn py_repr(s: &str) -> String { + let quote = if s.contains('\'') && !s.contains('"') { + '"' + } else { + '\'' + }; + let mut out = String::from(quote); + for c in s.chars() { + match c { + '\\' => out.push_str("\\\\"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + c if c == quote => { + out.push('\\'); + out.push(c); + } + c => out.push(c), + } + } + out.push(quote); + out +} + +/// Python's `repr` of a `list[str]`. +fn py_list(items: &[String]) -> String { + let inner: Vec = items.iter().map(|s| py_repr(s)).collect(); + format!("[{}]", inner.join(", ")) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Unparseable YAML is reported ONCE, by the validate line (#4588). + #[test] + fn unparseable_yaml_is_one_problem() { + let d = tempfile::tempdir().expect("tempdir"); + std::fs::write(d.path().join("bad.yaml"), "a: [\n").expect("write"); + let mut src = SrcTree::new(d.path().join("src")); + assert_eq!( + check_contract(d.path(), "bad.yaml", &mut src), + ["bad.yaml: pv validate failed"] + ); + } + + /// `yaml.safe_load(...) or {}`: only a FALSY non-mapping becomes `{}`; a truthy one is refused (#4588). + #[test] + fn load_refuses_a_truthy_non_mapping() { + let d = tempfile::tempdir().expect("tempdir"); + for (text, mapping) in [ + ("- 1\n", false), + ("x\n", false), + ("[]\n", true), + ("a: 1\n", true), + ] { + let p = d.path().join("c.yaml"); + std::fs::write(&p, text).expect("write"); + match load(&p) { + Ok(_) => assert!(mapping, "{text:?} must be refused"), + Err(Load::Other(why)) => { + assert!(!mapping, "{text:?}: {why}"); + assert_eq!(why, "is not a YAML mapping"); + } + Err(Load::Parse(why)) => panic!("{text:?} parses: {why}"), + } + } + } + + #[test] + fn repr_matches_python() { + assert_eq!(py_repr("from_score"), "'from_score'"); + assert_eq!(py_repr("it's"), "\"it's\""); + assert_eq!(py_repr("a'b\"c"), "'a\\'b\"c'"); + assert_eq!(py_repr("a\\b"), "'a\\\\b'"); + assert_eq!( + py_list(&["src/a.rs".into(), "src/b.rs".into()]), + "['src/a.rs', 'src/b.rs']" + ); + assert_eq!(py_list(&[]), "[]"); + } + + /// `\bword\b` needs a boundary at BOTH ends, each tested where the word actually starts and ends (#4588). + #[test] + fn mentions_needs_a_boundary_at_both_ends() { + let d = tempfile::tempdir().expect("tempdir"); + std::fs::create_dir(d.path().join("src")).expect("mkdir"); + std::fs::write(d.path().join("src/a.rs"), " abc\n").expect("write"); + std::fs::write(d.path().join("src/b.rs"), " ab c\n").expect("write"); + let mut src = SrcTree::new(d.path().join("src")); + assert!( + !src.mentions("src/a.rs", "ab"), + "`abc` does not mention `ab`" + ); + assert!(src.mentions("src/b.rs", "ab")); + assert!(!src.mentions("src/a.rs", "bc"), "no boundary before `bc`"); + assert!( + !src.mentions("src/c.rs", "ab"), + "only the named file counts" + ); + } + + #[test] + fn boundary_is_regex_backslash_b() { + assert!(boundary("fn score", "(x)")); + assert!(boundary("fn score", "")); + assert!(!boundary("fn score", "_v2")); + assert!(!boundary("fn score", "2")); + assert!(boundary(" ", "Grade")); + assert!(!boundary("Tdg", "Grade")); + } +} diff --git a/crates/aprender-contracts-cli/src/commands/ontology.rs b/crates/aprender-contracts-cli/src/commands/ontology.rs new file mode 100644 index 0000000000..471556b112 --- /dev/null +++ b/crates/aprender-contracts-cli/src/commands/ontology.rs @@ -0,0 +1,81 @@ +//! `pv ontology export --owl` / `pv ontology tbox` — ONT-001 §3.8, row ONT-2c. +//! +//! `export --owl` writes Σ as OWL 2 functional syntax with the in-house writer, byte-deterministic, so CI can +//! `cmp` a fresh export against the tracked `contracts/ontology.ofn` (R-18). `tbox` writes the told-closure +//! classification, `contracts/tbox-report.json`, which is ADVISORY: the `tbox` lint gate maps it to +//! `Unknown{Advisory}` and it never arms (R-7: no inferred fact arms a merge). +//! +//! Exit codes follow `pv lint`: 3 `error:` when Σ is malformed, cannot be written as OWL, or the TBox +//! precondition fails. In that last case the report is still printed, so the refusal names its axiom. + +use std::path::Path; + +use provable_contracts::ontology::owl; +use provable_contracts::ontology::sigma::Sigma; + +use crate::cli::OntologyCommand; + +pub fn run(command: &OntologyCommand) -> Result<(), Box> { + match command { + OntologyCommand::Export { + sigma, + owl: as_owl, + write, + } => { + if !as_owl { + eprintln!("error: `pv ontology export` writes one format today; pass --owl"); + std::process::exit(2); + } + let export = load_export(sigma); + emit(sigma, "ontology.ofn", &owl::to_ofn(&export), *write) + } + OntologyCommand::Tbox { sigma, write } => { + let export = load_export(sigma); + let report = owl::tbox(&export); + emit( + sigma, + "tbox-report.json", + &owl::report_json(&report), + *write, + )?; + if !report.precondition.holds { + for r in &report.precondition.refused { + eprintln!("error: told-closure precondition fails: {r}"); + } + std::process::exit(3); + } + Ok(()) + } + } +} + +fn load_export(sigma_path: &Path) -> owl::OwlExport { + let fail = |msg: String| -> ! { + eprintln!("error: {msg}"); + std::process::exit(3); + }; + let text = std::fs::read_to_string(sigma_path) + .unwrap_or_else(|e| fail(format!("cannot read Σ {}: {e}", sigma_path.display()))); + let sigma = Sigma::from_yaml(&text).unwrap_or_else(|e| fail(format!("Σ: {e}"))); + if let Err(e) = sigma.check_integrity() { + fail(format!("Σ: {e}")); + } + owl::export(&sigma).unwrap_or_else(|e| fail(e.to_string())) +} + +fn emit( + sigma_path: &Path, + name: &str, + body: &str, + write: bool, +) -> Result<(), Box> { + if write { + let dir = sigma_path.parent().unwrap_or_else(|| Path::new(".")); + let out = dir.join(name); + std::fs::write(&out, body)?; + eprintln!("wrote {}", out.display()); + } else { + print!("{body}"); + } + Ok(()) +} diff --git a/crates/aprender-contracts-cli/src/commands/proof_status.rs b/crates/aprender-contracts-cli/src/commands/proof_status.rs index f3fa582ffc..edf8671823 100644 --- a/crates/aprender-contracts-cli/src/commands/proof_status.rs +++ b/crates/aprender-contracts-cli/src/commands/proof_status.rs @@ -1,6 +1,6 @@ use std::path::Path; -use provable_contracts::binding::parse_binding; +use provable_contracts::binding::{parse_binding, BindingRegistry, ImplStatus}; use provable_contracts::obligation_matrix::{format_obligation_table, obligation_matrix}; use provable_contracts::proof_status::{format_text, proof_status_report}; use provable_contracts::schema::ContractKind; @@ -15,15 +15,20 @@ pub fn run( table: bool, kind_filter: Option<&str>, ) -> Result<(), Box> { - let binding = match binding_path { - // L5 gate: when --verify-bindings is set, downgrade any `implemented` - // binding whose function is absent from source, so L5 requires bindings - // that are VERIFIED as implemented rather than merely self-declared. - Some(bp) => Some(match verify_root { - Some(root) => parse_binding(bp)?.verified(root), - None => parse_binding(bp)?, - }), - None => None, + // PVL-001 EV-2 (PVL-2): a binding is RESOLVED, always. `--binding` used to count + // entries without resolving them (a binding naming a function that exists + // nowhere printed its report and exited 0), and `--verify-bindings` downgraded + // silently. Now every `implemented` binding is looked up with the + // `pv verify-bindings` resolver; a ghost is downgraded, listed under + // `GHOST BINDINGS (n)`, and the command exits 1. `--verify-bindings` is a no-op + // alias, kept so existing invocations still parse. + let _ = verify_root; + let (binding, ghosts) = match binding_path { + Some(bp) => { + let (reg, ghosts) = resolve_bindings(bp, parse_binding(bp)?); + (Some(reg), ghosts) + } + None => (None, Vec::new()), }; let kind = kind_filter.map(parse_kind).transpose()?; @@ -61,7 +66,72 @@ pub fn run( print!("{}", format_obligation_table(&matrices)); } - Ok(()) + if ghosts.is_empty() { + return Ok(()); + } + // Text mode prints the block on stdout with the report; JSON mode keeps stdout a + // single JSON document and prints the block on stderr. + let block = ghost_block(&ghosts); + if format == "json" { + eprint!("{block}"); + } else { + print!("{block}"); + } + Err(format!( + "{} ghost binding(s): claimed implemented, not found in source", + ghosts.len() + ) + .into()) +} + +/// One binding that claims `implemented` for a function the resolver cannot find. +struct Ghost { + contract: String, + equation: String, + function: String, +} + +/// Resolve every `implemented` binding against source with the `pv verify-bindings` +/// resolver (`scan_all_sources`: the binding's derived source root, its `crates/`, +/// and the local `src/`). A ghost is downgraded to `not_implemented` so the report's +/// levels are honest, and returned so the caller can name it and reject. +fn resolve_bindings(binding_path: &Path, reg: BindingRegistry) -> (BindingRegistry, Vec) { + use crate::commands::verify_bindings::{scan_all_sources, short_name}; + let found = scan_all_sources(binding_path, ®.target_crate); + let mut ghosts = Vec::new(); + let bindings = reg + .bindings + .into_iter() + .map(|mut b| { + let unresolved = b.status == ImplStatus::Implemented + && b.function + .as_deref() + .and_then(short_name) + .is_some_and(|s| !found.contains(&s)); + if unresolved { + ghosts.push(Ghost { + contract: b.contract.clone(), + equation: b.equation.clone(), + function: b.function.clone().unwrap_or_default(), + }); + b.status = ImplStatus::NotImplemented; + } + b + }) + .collect(); + (BindingRegistry { bindings, ..reg }, ghosts) +} + +/// `GHOST BINDINGS (n)`, then one line per ghost: the line PVL-001 EV-2's probe reads. +fn ghost_block(ghosts: &[Ghost]) -> String { + let mut out = format!("\nGHOST BINDINGS ({})\n", ghosts.len()); + for g in ghosts { + out.push_str(&format!( + " {} {}: {}\n", + g.contract, g.equation, g.function + )); + } + out } fn print_kind_breakdown(contracts: &[(String, provable_contracts::schema::Contract)]) { diff --git a/crates/aprender-contracts-cli/src/commands/verify_bindings.rs b/crates/aprender-contracts-cli/src/commands/verify_bindings.rs index 60b76cb115..b33a79c36e 100644 --- a/crates/aprender-contracts-cli/src/commands/verify_bindings.rs +++ b/crates/aprender-contracts-cli/src/commands/verify_bindings.rs @@ -50,21 +50,28 @@ fn parse_expected_functions(content: &str) -> HashSet { let Some(rest) = line.trim().strip_prefix("function:") else { continue; }; - let func = rest.trim().trim_matches('"').trim_matches('\'').trim(); - if func.is_empty() || func == "N/A" { - continue; - } - let short = func.rsplit("::").next().unwrap_or(func).to_lowercase(); - if !short.is_empty() { + if let Some(short) = short_name(rest) { expected.insert(short); } } expected } +/// The name a `function:` value is resolved by: its last `::` segment, lowercased. +/// `None` for an empty value or `N/A`. ONE normalization, shared with +/// `pv proof-status --binding` (PVL-001 EV-2), so the two commands cannot disagree. +pub(crate) fn short_name(function: &str) -> Option { + let func = function.trim().trim_matches('"').trim_matches('\'').trim(); + if func.is_empty() || func == "N/A" { + return None; + } + let short = func.rsplit("::").next().unwrap_or(func).to_lowercase(); + (!short.is_empty()).then_some(short) +} + /// Scan the crate's `src/`, `crates/`, and the current-dir `src/` (if different) /// for `fn` declarations. -fn scan_all_sources(binding_path: &Path, label: &str) -> HashSet { +pub(crate) fn scan_all_sources(binding_path: &Path, label: &str) -> HashSet { let src_dir = derive_src_root(binding_path, label); let mut found: HashSet = HashSet::new(); let src = src_dir.join("src"); @@ -82,16 +89,30 @@ fn scan_all_sources(binding_path: &Path, label: &str) -> HashSet { found } -/// binding.yaml lives in `contracts//` — source is `../..//`. -/// Falls back to `.` when the path has no usable parent chain. +/// Where a binding's source lives. +/// +/// The multi-repo layout (`contracts//binding.yaml`, source at `../..//`) +/// is used when that directory exists. In this monorepo it does not +/// (`contracts/aprender/binding.yaml` -> `./aprender/`, absent), and scanning an +/// absent root made nearly every binding a ghost: under PVL-001 EV-2's reject that +/// is a false reject, and one that depended on the caller's cwd. So otherwise the +/// root is the nearest ancestor of the binding file holding a `crates/` or `src/` +/// tree (the workspace root), and `.` only when there is none. fn derive_src_root(binding_path: &Path, label: &str) -> std::path::PathBuf { - let Some(parent) = binding_path.parent() else { - return Path::new(".").to_path_buf(); - }; - parent + let has_tree = |d: &Path| d.join("src").is_dir() || d.join("crates").is_dir(); + let legacy = binding_path .parent() - .and_then(|p| p.parent()) - .map_or_else(|| Path::new(".").to_path_buf(), |p| p.join(label)) + .and_then(Path::parent) + .and_then(Path::parent) + .map(|p| p.join(label)); + if let Some(l) = legacy.filter(|l| has_tree(l)) { + return l; + } + let abs = std::fs::canonicalize(binding_path).unwrap_or_else(|_| binding_path.to_path_buf()); + abs.ancestors() + .skip(1) + .find(|d| has_tree(d)) + .map_or_else(|| Path::new(".").to_path_buf(), Path::to_path_buf) } /// Sort the expected names missing from `found` for stable reporting. @@ -159,33 +180,170 @@ fn scan_fns(dir: &Path, found: &mut HashSet) { } } -/// Extract lowercased `fn`/`pub fn`/`pub async fn`/`pub(crate) fn` names from source. +/// Extract lowercased function names from source: every `fn` item, whatever its +/// visibility (`pub`, `pub(crate)`, `pub(super)`, `pub(in path)`) and qualifiers +/// (`const`, `async`, `unsafe`, `extern "ABI"`). PVL-001 EV-2: `pv proof-status` +/// now REJECTS on a ghost, so a real function the scanner cannot see is a false +/// reject. Measured on aprender's contracts/binding.yaml: `compute_mse` is +/// `pub(super) fn` (crates/aprender-core/src/tree/regression_helpers.rs:27) and was +/// reported a ghost by the old four-prefix scanner (scripts/dogfood.sh records the +/// same defect for rmedia's `apply_loudnorm`). fn extract_fn_names(content: &str, found: &mut HashSet) { for line in content.lines() { - let t = line.trim(); - if !(t.starts_with("pub fn ") - || t.starts_with("pub async fn ") - || t.starts_with("pub(crate) fn ") - || t.starts_with("fn ")) - { - continue; - } - let part = t - .trim_start_matches("pub async fn ") - .trim_start_matches("pub(crate) fn ") - .trim_start_matches("pub fn ") - .trim_start_matches("fn "); - let name = part - .split('(') - .next() - .unwrap_or("") - .split('<') - .next() - .unwrap_or("") - .trim() - .to_lowercase(); - if !name.is_empty() { + if let Some(name) = fn_item_name(line) { found.insert(name); } } } + +/// The lowercased name of the item (`fn`, `struct`, `enum`, `type`, `trait`) a source +/// line declares, if it declares one. +fn fn_item_name(line: &str) -> Option { + let mut t = line.trim_start(); + // visibility: `pub` or `pub(...)` + if let Some(rest) = t.strip_prefix("pub") { + let rest_trim = rest.trim_start(); + if let Some(inner) = rest_trim.strip_prefix('(') { + t = inner.split_once(')')?.1.trim_start(); + } else if rest.starts_with(char::is_whitespace) { + t = rest_trim; + } else { + return None; // `pubfoo`, `pub_x`: an identifier, not a visibility + } + } + // qualifiers, in any order the grammar allows them to appear + loop { + let before = t; + for q in ["const ", "async ", "unsafe ", "default "] { + if let Some(rest) = t.strip_prefix(q) { + t = rest.trim_start(); + } + } + if let Some(rest) = t.strip_prefix("extern ") { + let rest = rest.trim_start(); + t = match rest.strip_prefix('"') { + Some(abi) => abi.split_once('"')?.1.trim_start(), + None => rest, + }; + } + if t == before { + break; + } + } + // A binding may name a function or a type (setfit-apr-v1 binds + // `SetFitArtifactDoc` and `ClassifyResponse`, both `pub struct`): the resolver + // sees every item kind a binding can name. + let part = ["fn ", "struct ", "enum ", "type ", "trait "] + .iter() + .find_map(|kw| t.strip_prefix(kw))?; + let name = part + .split(|c: char| matches!(c, '(' | '<' | ';' | '{' | ':' | '=') || c.is_whitespace()) + .next() + .unwrap_or("") + .trim() + .to_lowercase(); + (!name.is_empty()).then_some(name) +} + +#[cfg(test)] +mod tests { + use super::{ + derive_src_root, extract_fn_names, fn_item_name, parse_expected_functions, short_name, + }; + use std::collections::HashSet; + + /// `function:` values normalize to their last `::` segment, lowercased; empty and + /// `N/A` values name nothing (#4588: these helpers had no test at all). + #[test] + fn short_name_normalizes_or_declines() { + assert_eq!(short_name(" a::b::Foo ").as_deref(), Some("foo")); + assert_eq!(short_name("\"Bar\"").as_deref(), Some("bar")); + assert_eq!(short_name("'q::Baz'").as_deref(), Some("baz")); + assert_eq!(short_name(""), None); + assert_eq!(short_name(" "), None); + assert_eq!(short_name("N/A"), None); + assert_eq!(short_name("\"N/A\""), None); + } + + #[test] + fn expected_functions_come_only_from_function_lines() { + let yaml = "bindings:\n - contract: c\n function: crate::m::Alpha\n module: x::Beta\n - contract: d\n function: \"N/A\"\n - contract: e\n function: ''\n - contract: f\n function: gamma\n"; + let want: HashSet = ["alpha", "gamma"].map(String::from).into(); + assert_eq!(parse_expected_functions(yaml), want); + assert!(parse_expected_functions("module: m\n").is_empty()); + } + + #[test] + fn extract_fn_names_collects_every_declared_item() { + let mut found = HashSet::new(); + extract_fn_names( + "pub fn a() {}\n// fn hidden() {}\nstruct B;\nlet c = 1;\n", + &mut found, + ); + let want: HashSet = ["a", "b"].map(String::from).into(); + assert_eq!(found, want); + } + + /// Without the legacy `../../