From a6bf06af1276cd17112660b9999e78aa90d9ba3b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 14:22:43 +0000 Subject: [PATCH 1/4] chore(kiban): first-time konjo-gates onboarding, fix decorative lint job squish's first connection to kiban's konjo-gates orchestrator (Track A2). Pins .konjo/kiban.ref at v1.9.0, adds .konjo/profile.yml (re-verified field by field against the real tree), and wires .github/workflows/konjo-gates.yml as a new, real, blocking CI job. Fixes the actual decorative-lint-job defect: ci.yml's lint-only job ran `ruff check ... --exit-zero` and `mypy ... --no-error-summary || true`, both structurally unable to fail regardless of findings. ruff is now real (0 standing violations); mypy is ratcheted against its measured 215-error baseline via a new generic ratchet gate (.konjo/scripts/ratchet_check.py, same shape as lopi's coverage-floor gate). konjo-gate.yml's 10 continue-on-error steps are all triaged: 7 promoted/ratcheted against measured baselines (ruff-format 363, vulture 108, bandit 67, complexity 146, DRY 99, docstrings 31.4%), 2 kept soft with a named owner and revisit date, 0 deleted. Also: gate_polarity full-tree scan (17 standing findings, all triaged, 1 fixed, 1 flagged and deferred); CLAUDE.md converted to the six-section contract; a stale single-figure PyPI performance claim ("5.4x faster") corrected to the honest measured range ("1.15-14.7x depending on prompt repetition"); version bumped to 9.34.15. KT-A2.1 kill-test: confirmed konjo-gates genuinely runs and can fail (a deliberate scratch-file violation triggered real ruff/vulture/bandit FAILs, then was reverted). Also caught, live, a real defect before it shipped: the profile's initial `mutation: mutmut` value would have made konjo-gates shell out to an unbounded, un-timed-out `mutmut run` on every future PR -- fixed to a "none-with-reason" value pointing at konjo-gate.yml's own properly-capped mutation job. Full reasoning, dispositions, and two kiban-side false positives found (and reported upstream rather than worked around) are in LEDGER.md's "Track A2" entry. Konjo-Threat-Model: f666caf4bcbd Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01NLdTtZgWyLWu2gE9CohWwa --- .github/workflows/ci.yml | 34 +++- .github/workflows/konjo-gate.yml | 140 ++++++++++++--- .github/workflows/konjo-gates.yml | 59 ++++++ .konjo/bandit-ceiling.txt | 10 ++ .konjo/complexity-ceiling.txt | 4 + .konjo/docstring-floor.txt | 4 + .konjo/dry-ceiling.txt | 5 + .konjo/kiban.ref | 1 + .konjo/mypy-ceiling.txt | 8 + .konjo/profile.yml | 228 ++++++++++++++++++++++++ .konjo/ruff-format-ceiling.txt | 9 + .konjo/scripts/ratchet_check.py | 105 +++++++++++ .konjo/scripts/test_ratchet_killtest.sh | 57 ++++++ .konjo/vulture-ceiling.txt | 4 + CHANGELOG.md | 77 ++++++++ CLAUDE.md | 84 ++++++--- LEDGER.md | 166 +++++++++++++++++ NEXT_SESSION_PROMPT.md | 136 ++++++++------ pyproject.toml | 16 +- scripts/check_release_sync.py | 14 +- scripts/compress_and_upload.py | 2 +- squish/__init__.py | 2 +- squish/catalog.py | 14 +- squish/daemon/squishd.py | 4 +- squish/server.py | 5 +- 25 files changed, 1068 insertions(+), 120 deletions(-) create mode 100644 .github/workflows/konjo-gates.yml create mode 100644 .konjo/bandit-ceiling.txt create mode 100644 .konjo/complexity-ceiling.txt create mode 100644 .konjo/docstring-floor.txt create mode 100644 .konjo/dry-ceiling.txt create mode 100644 .konjo/kiban.ref create mode 100644 .konjo/mypy-ceiling.txt create mode 100644 .konjo/profile.yml create mode 100644 .konjo/ruff-format-ceiling.txt create mode 100755 .konjo/scripts/ratchet_check.py create mode 100755 .konjo/scripts/test_ratchet_killtest.sh create mode 100644 .konjo/vulture-ceiling.txt create mode 100644 LEDGER.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c9523c7..96c73fa1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,8 +48,10 @@ jobs: pip install pytest-timeout - name: Lint (ruff) + # --exit-zero removed Track A2 (0 standing violations measured 2026-07-29, + # see LEDGER.md's Squish-Lint-Job-Fix-1) — this step now genuinely blocks. run: | - ruff check squish/ tests/ --output-format github --exit-zero + ruff check squish/ tests/ --output-format github - name: Verify version consistency run: | @@ -138,6 +140,19 @@ jobs: lint-only: # Runs on Linux for fast feedback on PRs from forks (no Apple Silicon needed) + # + # Both steps below used to be structurally unable to fail: ruff check carried + # --exit-zero (forces exit 0 regardless of findings) and mypy check was wrapped in + # `|| true` (swallows its exit code). Neither shape contains the string + # "continue-on-error", so a naive audit of konjo-gate.yml alone missed both — this + # job was the repo's actual decorative lint gate. Fixed Track A2 (squish's first + # kiban connection): ruff check is real and blocking now (0 standing violations + # measured 2026-07-29, so flipping it costs nothing); mypy check is ratcheted + # against its measured 215-error baseline (.konjo/mypy-ceiling.txt) via the same + # generic ratchet gate lopi's coverage-floor gate pioneered + # (.konjo/scripts/ratchet_check.py) rather than flipped hard on day one, which + # would just red every PR against a 215-error backlog no single PR caused. See + # LEDGER.md's Squish-Lint-Job-Fix-1. name: Lint (ubuntu) runs-on: ubuntu-latest steps: @@ -152,10 +167,17 @@ jobs: run: pip install ruff mypy - name: ruff check - run: ruff check squish/ tests/ --output-format github --exit-zero + run: ruff check squish/ tests/ --output-format github - - name: mypy check - run: mypy squish/ --ignore-missing-imports --no-error-summary || true + - name: mypy check (ratcheted against the measured baseline) + run: | + set +e + mypy squish/ --ignore-missing-imports --no-error-summary 2>&1 | tee mypy-out.log + COUNT=$(grep -c ": error:" mypy-out.log || true) + set -e + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name mypy-errors --measured "$COUNT" \ + --file .konjo/mypy-ceiling.txt test-linux: name: Test Linux (Python ${{ matrix.python-version }}) @@ -181,8 +203,10 @@ jobs: pip install pytest-timeout - name: Lint (ruff) + # --exit-zero removed Track A2 (0 standing violations measured 2026-07-29, + # see LEDGER.md's Squish-Lint-Job-Fix-1) — this step now genuinely blocks. run: | - ruff check squish/ tests/ --output-format github --exit-zero + ruff check squish/ tests/ --output-format github - name: Run tests (Linux — MLX tests auto-skipped) # MLX-dependent tests skip automatically on Linux via pytest.importorskip. diff --git a/.github/workflows/konjo-gate.yml b/.github/workflows/konjo-gate.yml index 5331dd0c..10a52f93 100644 --- a/.github/workflows/konjo-gate.yml +++ b/.github/workflows/konjo-gate.yml @@ -14,6 +14,28 @@ jobs: # ══════════════════════════════════════════════════════════════════════════ # Gate 1 — Static Analysis + # + # Track A2 triage (2026-07-29, see LEDGER.md's Squish-Gate-Triage-1 for the full + # per-step disposition table): every step below used to carry + # `continue-on-error: true`, so this whole job always reported "success" to the + # final konjo-gate job regardless of findings. + # - ruff lint: PROMOTED to real blocking. 0 standing violations measured + # 2026-07-29 (10 pre-existing findings fixed/exempted this same sprint — + # 4 auto-fixed, 6 covered by a new per-file-ignore extending the existing + # tests/**-BLE001 precedent to benchmarks/**, demo/**, scripts/**). + # - ruff format, vulture, bandit: large pre-existing backlogs (332 files / 108 + # / 67 findings respectively — mass-reformatting or clearing all of them is + # its own sprint, out of this one's non-goals). RATCHETED instead of left + # soft: each now runs for real and fails only on regression above its + # measured baseline (.konjo/*-ceiling.txt), via the same generic ratchet + # gate lopi's coverage-floor gate pioneered + # (.konjo/scripts/ratchet_check.py). A step that could never fail before now + # genuinely can. + # - bandit's --exclude also had a real, separate bug fixed here: `.venv,venv, + # tests` (no leading `./`) never matched bandit's own `./`-prefixed walk + # paths, so it was silently scanning tests/ too (134 findings, not the real + # 67 in squish/ + benchmarks/ + scripts/ + demo/). Fixed to `./.venv, + # ./venv,./tests`, confirmed locally to actually exclude tests/ now. # ══════════════════════════════════════════════════════════════════════════ static: name: "G1 · Static Analysis" @@ -26,20 +48,57 @@ jobs: - name: Install tools run: pip install ruff mypy vulture bandit --quiet - name: ruff lint - continue-on-error: true run: ruff check . - - name: ruff format - continue-on-error: true - run: ruff format --check . - - name: vulture — dead code - continue-on-error: true - run: vulture . --min-confidence 80 - - name: bandit — security - continue-on-error: true - run: bandit -r . -ll -q --exclude .venv,venv,tests + - name: ruff format (ratcheted against the measured baseline) + run: | + set +e + COUNT=$(ruff format --check . 2>&1 | grep -c "^Would reformat") + set -e + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name ruff-format-files --measured "$COUNT" \ + --file .konjo/ruff-format-ceiling.txt + - name: vulture — dead code (ratcheted against the measured baseline) + run: | + set +e + COUNT=$(vulture . --min-confidence 80 | wc -l) + set -e + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name vulture-findings --measured "$COUNT" \ + --file .konjo/vulture-ceiling.txt + - name: bandit — security (ratcheted against the measured baseline) + # --exclude paths must be `./`-prefixed to match bandit's own walk paths + # (see the job-level comment above — plain "tests" never matched). + run: | + set +e + COUNT=$(bandit -r . -ll -q --exclude ./.venv,./venv,./tests 2>&1 | grep -c "^>> Issue") + set -e + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name bandit-findings --measured "$COUNT" \ + --file .konjo/bandit-ceiling.txt # ══════════════════════════════════════════════════════════════════════════ # Gate 2 — Tests + Coverage (≥ 80%) + # + # Track A2 triage (2026-07-29): KEPT SOFT, not promoted — owner: squish + # maintainers, revisit-by 2026-09-30. This job is a duplicate of ci.yml's real + # `coverage` job (macos-14, MLX-aware, publishes the badge) with two concrete + # bugs that would make promoting it today just red every PR on a false signal, + # not a real one: + # 1. It runs on ubuntu-latest with no mlx install and none of the + # Metal-unguarded-import exclusions ci.yml's own `test`/`test-linux`/ + # `coverage` jobs carry (tests/test_sqint2_linear.py etc.) — confirmed by + # reading tests/conftest.py's own Layer-2 comment: those files import + # mlx.core at module level with no guard, which collection-errors the + # instant GITHUB_ACTIONS is set and mlx isn't installed. + # 2. `--cov=.` measures the whole repo (benchmarks/, demo/, scripts/, tests/ + # themselves) instead of `--cov=squish` the way ci.yml's real coverage job + # correctly scopes it, so even a clean run would report a materially + # different, misleadingly low percentage against the same 80% bar. + # Fixing both is real, scoped work for a maintainer to decide deliberately + # (rewrite the ignore list, add an mlx install, rescope --cov) or to delete this + # duplicate outright in favor of ci.yml's real one — not a call this sprint + # makes unilaterally per its own non-goal ("connect what exists, don't rebuild + # CI"). Recorded here instead of silently left implying it blocks. # ══════════════════════════════════════════════════════════════════════════ coverage: name: "G2 · Tests + Coverage" @@ -56,6 +115,8 @@ jobs: pip install -e ".[test]" --quiet 2>/dev/null || \ pip install -e . --quiet 2>/dev/null || true - name: Run tests with coverage + # KEEP SOFT — see the job-level comment above (owner: squish maintainers, + # revisit-by 2026-09-30). Known-broken as configured on ubuntu-latest. continue-on-error: true run: | python -m pytest tests/ \ @@ -65,6 +126,7 @@ jobs: -x -q - name: Coverage gate if: always() + # KEEP SOFT — tied 1:1 to "Run tests with coverage" above; same reason. continue-on-error: true run: | python3 -c " @@ -84,6 +146,19 @@ jobs: # ══════════════════════════════════════════════════════════════════════════ # Gate 3 — Mutation Testing (PRs only) + # + # Track A2 triage (2026-07-29): KEPT SOFT — owner: squish maintainers, + # revisit-by 2026-09-30. Unlike the other soft steps in this file, this one's + # `continue-on-error` was already load-bearing for a documented, sound reason + # (see the step's own comment below): a full mutmut run on this codebase's size + # routinely exceeds the GitHub Actions 8-minute step timeout, and a SIGKILL'd + # step fails the job even under continue-on-error, so the 420s internal cap + + # partial-results tail is the real mitigation — continue-on-error just covers + # the remaining risk of a genuine mutation survival past the cap. Promoting + # this to hard-blocking needs a real mutation-survival threshold measured on a + # completed run first, which this soft step's own timeout risk prevents + # getting today; that measurement is real, scoped follow-up work, not + # something to guess at here. # ══════════════════════════════════════════════════════════════════════════ mutation: name: "G3 · Mutation Testing" @@ -99,6 +174,7 @@ jobs: pip install mutmut pytest --quiet pip install -e . --quiet 2>/dev/null || true - name: Run mutation testing + # KEEP SOFT — see the job-level comment above. continue-on-error: true timeout-minutes: 8 run: | @@ -111,6 +187,12 @@ jobs: # ══════════════════════════════════════════════════════════════════════════ # Gate 4 — Complexity + Size + DRY + # + # Track A2 triage (2026-07-29): Complexity gate, DRY check, and Documentation + # gate all RATCHETED against their measured baselines (146 / 99 / 31.4% + # respectively) instead of left soft — same reasoning and mechanism as G1 + # above (.konjo/scripts/ratchet_check.py). File size gate was already the one + # step in this whole file with no continue-on-error and needed no change. # ══════════════════════════════════════════════════════════════════════════ complexity: name: "G4 · Complexity + Size + DRY" @@ -122,9 +204,9 @@ jobs: python-version: "3.11" - name: Install tools run: pip install radon interrogate --quiet - - name: Complexity gate - continue-on-error: true + - name: Complexity gate (ratcheted against the measured baseline) run: | + set +e COUNT=$(radon cc . -n C --json 2>/dev/null \ | python3 -c " import json, sys @@ -132,13 +214,11 @@ jobs: total = sum(len(v) for v in d.values()) print(total) " 2>/dev/null || echo 0) + set -e echo "Functions with cyclomatic complexity > 10: $COUNT" - if [ "$COUNT" -gt 0 ]; then - radon cc . -n C -s - echo "::error::$COUNT function(s) above complexity grade C." - exit 1 - fi - echo "Complexity: all functions grade C or better ✓" + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name complexity-grade-c-plus --measured "$COUNT" \ + --file .konjo/complexity-ceiling.txt - name: File size gate run: | # BLOCKING for new files; existing oversized files are grandfathered @@ -161,22 +241,28 @@ jobs: exit 1 fi echo "File sizes: all new files within 500-line limit ✓ (allowlisted legacy files exempt)" - - name: DRY check - continue-on-error: true + - name: DRY check (ratcheted against the measured baseline) run: | python3 .konjo/scripts/dry_check.py \ --threshold 0.85 \ --min-lines 20 \ --report dry_report.json 2>&1 COUNT=$(python3 -c "import json; d=json.load(open('dry_report.json')); print(d['count'])") - if [ "$COUNT" -gt 0 ]; then - echo "::error::$COUNT DRY violation(s). Abstract into shared functions." - exit 1 + python3 .konjo/scripts/ratchet_check.py \ + --mode ceiling --name dry-violations --measured "$COUNT" \ + --file .konjo/dry-ceiling.txt + - name: Documentation gate (ratcheted against the measured baseline) + run: | + set +e + PCT=$(interrogate . 2>&1 | grep -oE "actual: [0-9.]+" | grep -oE "[0-9.]+") + set -e + if [ -z "$PCT" ]; then + echo "::error::Could not parse interrogate's actual coverage percent from its output." + exit 2 fi - echo "DRY check: no violations ✓" - - name: Documentation gate - continue-on-error: true - run: interrogate . --fail-under 80 -q 2>/dev/null || true + python3 .konjo/scripts/ratchet_check.py \ + --mode floor --name docstring-coverage --measured "$PCT" \ + --file .konjo/docstring-floor.txt # Gate 5 — Adversarial Review — DISABLED in CI # Run locally: git diff HEAD~1 | python3 .konjo/scripts/konjo_review.py diff --git a/.github/workflows/konjo-gates.yml b/.github/workflows/konjo-gates.yml new file mode 100644 index 00000000..a74c6877 --- /dev/null +++ b/.github/workflows/konjo-gates.yml @@ -0,0 +1,59 @@ +# .github/workflows/konjo-gates.yml +# squish's first connection to kiban's own `konjo-gates` orchestrator (Track A2, +# 2026-07-29). Follows kiban's templates/repo-ci.yml pattern (the same shape vectro's +# real, genuinely-blocking konjo-gates.yml uses) rather than konjo-gate.yml's own +# repo-native G1-G4 jobs, which stay repo-native by design — see LEDGER.md's +# Squish-Gate-Triage-1. This is net-new: adds a job, deletes nothing that already +# existed in squish's CI. +# +# CI never reads ~/.konjo; the gate logic and eval cassettes come from the installed, +# version-pinned kiban package. Pin KIBAN_REF to a tag so a kiban change rolls out to +# squish on a deliberate schedule, not the instant a new kiban tag ships — bump this +# together with .konjo/kiban.ref, never one without the other (see CLAUDE.md's Pinning +# section, mirroring lopi's convention). + +name: konjo-gates + +on: + pull_request: + push: + branches: [main] + +env: + KIBAN_REF: "v1.9.0" # matches .konjo/kiban.ref — bump both together. + +jobs: + gates: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # konjo-gates diffs against the base ref + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + # The repo:ruff / repo:ruff-format / repo:mypy / repo:vulture / repo:bandit gates + # shell out to these tools directly (confirmed: konjo_gates_py's _TOOL_SCOPE / + # _TOOL_BIN tables dispatch all five for real under SCOPE_PYTHON) — install them + # on PATH first, the same working pattern konjo-gate.yml's own G1 job already + # uses, so a PR touching squish/** or tests/** doesn't just error "tool not + # installed" on its first run. + - name: Install Python gate tools + run: pip install ruff mypy vulture bandit --quiet + + # Install the whole kiban distribution at the pin. It ships the real engine (lib, + # evals) with the konjo-gates entry point and the eval cassettes, so the + # orchestrator imports one source of truth and the replay self-test needs no + # model. + - name: Install pinned kiban + run: pip install "kiban @ git+https://github.com/konjoai/kiban.git@${KIBAN_REF}" + + # konjo-gates writes a per-gate progress heartbeat to stderr, so this step is + # never silent even while a slower gate (mutation, prove) runs. Add --verbose to + # also stream the exact scanner argv and each HEAD/base scan pass with its + # duration. + - name: Run gates against the repo profile + run: konjo-gates --profile .konjo/profile.yml --base "origin/${{ github.base_ref || 'main' }}" diff --git a/.konjo/bandit-ceiling.txt b/.konjo/bandit-ceiling.txt new file mode 100644 index 00000000..0245d9b6 --- /dev/null +++ b/.konjo/bandit-ceiling.txt @@ -0,0 +1,10 @@ +# Konjo ratchet ceiling — bandit medium+high findings (-ll, tests/ excluded). +# Never regress above this count; ratchet it down as findings are resolved. +# Seeded 2026-07-29 (Track A2): measured via +# `bandit -r . -ll -q --exclude ./.venv,./venv,./tests`, AFTER fixing the 2 +# real High-severity findings this same sprint found (squishd.py _model_key's +# SHA1 and server.py's _system_fingerprint MD5 — both non-cryptographic ID +# hashes, both cleared with usedforsecurity=False, zero behavior change). +# Pre-fix baseline was 69 (3 High before the tests-exclude bug was fixed in +# this sprint's own measurement pass; 2 High in production code after). +67 diff --git a/.konjo/complexity-ceiling.txt b/.konjo/complexity-ceiling.txt new file mode 100644 index 00000000..28ae15af --- /dev/null +++ b/.konjo/complexity-ceiling.txt @@ -0,0 +1,4 @@ +# Konjo ratchet ceiling — radon cyclomatic-complexity grade C+ function count. +# Never regress above this count; ratchet it down as functions are simplified. +# Seeded 2026-07-29 (Track A2): measured via `radon cc . -n C --json`. +146 diff --git a/.konjo/docstring-floor.txt b/.konjo/docstring-floor.txt new file mode 100644 index 00000000..2493447f --- /dev/null +++ b/.konjo/docstring-floor.txt @@ -0,0 +1,4 @@ +# Konjo ratchet floor — interrogate docstring-coverage percent. +# Never regress below this value; ratchet it up as docstrings are added. +# Seeded 2026-07-29 (Track A2): measured via `interrogate .` (target: 80%). +31.4 diff --git a/.konjo/dry-ceiling.txt b/.konjo/dry-ceiling.txt new file mode 100644 index 00000000..80d6e693 --- /dev/null +++ b/.konjo/dry-ceiling.txt @@ -0,0 +1,5 @@ +# Konjo ratchet ceiling — dry_check.py duplicate-block violation count. +# Never regress above this count; ratchet it down as duplication is removed. +# Seeded 2026-07-29 (Track A2): measured via +# `dry_check.py --threshold 0.85 --min-lines 20`. +99 diff --git a/.konjo/kiban.ref b/.konjo/kiban.ref new file mode 100644 index 00000000..295e37c0 --- /dev/null +++ b/.konjo/kiban.ref @@ -0,0 +1 @@ +v1.9.0 diff --git a/.konjo/mypy-ceiling.txt b/.konjo/mypy-ceiling.txt new file mode 100644 index 00000000..bd8d8757 --- /dev/null +++ b/.konjo/mypy-ceiling.txt @@ -0,0 +1,8 @@ +# Konjo ratchet ceiling — mypy error count (squish/, --ignore-missing-imports). +# Never regress above this count; ratchet it down as errors are fixed. +# Seeded 2026-07-29 (Track A2): measured via +# `mypy squish/ --ignore-missing-imports --no-error-summary`. +# This is the backlog that made ci.yml's lint-only job's mypy step +# structurally unable to fail (`|| true`) before this sprint. See +# LEDGER.md's Squish-Lint-Job-Fix-1. +215 diff --git a/.konjo/profile.yml b/.konjo/profile.yml new file mode 100644 index 00000000..091c9edd --- /dev/null +++ b/.konjo/profile.yml @@ -0,0 +1,228 @@ +# .konjo/profile.yml +# squish's Konjo profile. The CI plane installs the pinned kiban distribution (see +# .konjo/kiban.ref and .github/workflows/konjo-gates.yml) and runs `konjo-gates` against +# this profile. +# +# This is squish's FIRST connection to kiban (Track A2). Started from kiban's own +# profiles/squish.yml starting point (itself reconciled read-only against this repo in an +# earlier kiban-side sprint), then re-verified field by field against squish's real tree +# as it stands today: requirements.txt/pyproject.toml (mlx + mlx-lm, Apple-Silicon gated), +# .konjo/hooks/pre-commit, .github/workflows/ci.yml and konjo-gate.yml (read in full, +# every job and every step), and real local runs of ruff/ruff-format/mypy/vulture/bandit/ +# radon/interrogate/dry_check.py against this exact checkout (commit 4dd6f62 + this +# sprint's own changes). squish is Python 3.11+/FastAPI/MLX -- no Rust, no TypeScript, no +# Swift build surface in the profile sense (SquishBar is a separate Swift app, not part of +# any Python-tooling gate). + +repo: squish + +# Confirmed: requirements.txt and pyproject.toml declare mlx/mlx-lm gated to +# `sys_platform == 'darwin' and platform_machine == 'arm64'`; there is no Cargo.toml at +# the top level driving these gates (squish_quant_rs is a separate Rust extension, built +# by ci.yml's own test-rust job, not part of the Python quality-gate surface below). +stack: + - python + - mlx + +# lang/python: ruff/ruff-format/mypy/vulture/bandit/radon/interrogate/mutmut, the exact +# tool set already wired in konjo-gate.yml G1/G3/G4 and ci.yml's lint jobs. +# lang/mlx: numerics + memory-bandwidth specialist lanes for the MLX inference core. +packs: + - lang/python + - lang/mlx + +# Confirmed from .konjo/hooks/pre-commit (ruff lint + ruff format + mypy) and +# .github/workflows/konjo-gate.yml G1 (ruff, ruff-format, vulture, bandit) plus +# ci.yml's lint-only job (ruff, mypy). mypy is real -- it runs in ci.yml, just not in +# konjo-gate.yml's own G1 (which installs it but never calls it, a small dead-install +# left as-is; not this sprint's to fix). +format_lint: + - ruff + - ruff-format + - mypy + - vulture + - bandit + +# Confirmed from konjo-gate.yml G2 and G4, this sprint's own measurement (2026-07-29): +# coverage-80 -- G2 pytest coverage gate, currently soft (see LEDGER.md, +# Squish-Lint-Job-Fix-1 / Squish-Gate-Triage-1) +# complexity-radon -- G4 radon gate; 146 grade-C+ functions measured standing, +# ratcheted (kept repo-native, see below) +# file-size-500 -- G4 + pre-commit; the one hard-blocking check in G1-G4 today +# dry -- G4 dry_check.py; 99 violations measured standing, ratcheted +# docs-interrogate-80 -- G4 interrogate; 31.4% measured standing, floor-ratcheted +# ruff-format-ratchet -- NEW this sprint: 363 files measured standing, whole- +# repo scope matching G1's real command (kept +# repo-native, .konjo/scripts/ratchet_check.py) +# vulture-ratchet -- NEW this sprint: 108 findings measured standing (repo-native) +# bandit-ratchet -- NEW this sprint: 69 medium/high findings measured standing +# (2 real High findings fixed this sprint -- see CHANGELOG; +# repo-native ratchet on the remainder) +# mypy-ratchet -- NEW this sprint: 215 errors measured standing in ci.yml's +# lint-only job, ratcheted so the job can genuinely fail on +# regression instead of being structurally unable to fail +contract_gates: + - coverage-80 + - complexity-radon + - file-size-500 + - dry + - docs-interrogate-80 + - ruff-format-ratchet + - vulture-ratchet + - bandit-ratchet + - mypy-ratchet + +# NOT a bare "mutmut" -- caught live by this sprint's own KT-A2.1 kill-test run: a bare +# tool name here (`mutation.get("mutation")` in konjo_gates_py's cli.py) adds "mutmut" +# to the generic net-new-diff tool dispatch table, which shells out to plain `mutmut +# run` with NO timeout wrapper at all (unlike the cargo-mutants path, which gets +# --jobs/--timeout flags). That hung this sprint's own local konjo-gates run +# indefinitely (killed after several minutes, still climbing in RSS) -- confirmed the +# exact trap lopi's own profiles/lopi.yml comment already documents avoiding for +# cargo-mutants, now confirmed live for squish's mutmut too. Real mutation testing for +# squish lives in konjo-gate.yml G3 (`mutmut run`, capped at an internal 420s with a +# partial-results tail specifically to dodge the GitHub Actions 8-minute step SIGKILL, +# see Squish-Gate-Triage-1 in LEDGER.md) -- konjo-gates' own generic dispatch would +# just re-run the same tool uncapped and unbounded, redundant AND dangerous. The +# "none"-prefix here is what konjo_gates_py's dispatcher checks for to skip adding the +# tool to repo_tools at all (`mutation.startswith("none")`). +mutation: "none-with-reason: real mutation testing runs in konjo-gate.yml G3 (7-minute internal cap); konjo-gates' generic mutmut dispatch has no timeout and hung this sprint's own kill-test run" + +# kiban-native review lanes. numerics and memory-bandwidth are the lang/mlx lanes (the +# inference core: quant/, kv/, context/, serving/ hot paths); concurrency and api-surface +# are the shared _base lanes -- concurrency because squishd/serving run a request +# scheduler, api-surface because squish/api + squish/server.py are the public HTTP +# surface. squish's own repo-native review is the single 10-question adversarial critic +# (.konjo/scripts/konjo_review.py, Wall 3, disabled in CI) -- these specialists are the +# kiban-side lanes, not a replacement for it. +specialists: + - numerics + - memory-bandwidth + - concurrency + - api-surface + +# Confirmed from BENCHMARKS.md and benchmarks/ollama_vs_squish/: bench_v5_1.py / +# bench_thermal_h2h.py are the maintained head-to-head harnesses; results/ carries dated +# JSON artifacts. min_effect_pct is left unset (PENDING) rather than guessed -- the same +# discipline vectro's and lopi's profiles use: a real noise-floor measurement on the bench +# hardware (Apple Silicon) is required before this gate can pass a real verdict, and this +# session has no such hardware. Until activated, konjo-prove refuses a verdict (exit 3, +# NOT ACTIVATED) rather than silently passing a perf change. +# +# perf_globs corrected this sprint from a bare "squish/**" (kiban's own starting-point +# profile) to the genuine hot-path directories: KT-A2.1's own kill-test run caught this +# live -- "squish/**" matches literally every file in the package (cli.py's UX code, +# daemon/ lifecycle management, api/'s thin FastAPI adapters, integrations/'s HF +# metadata calls), so a docstring-only edit or a bandit-hygiene one-liner in catalog.py +# was tripping gate_prove and demanding a bench-hardware MERGE verdict for a change with +# no measurable perf effect. Narrowed to the directories BENCHMARKS.md's own numbers +# actually exercise: the quant/serving/kv/context/loaders/io/speculative/hardware +# inference core plus the backend dispatcher, not the whole package. +prove: + enabled: true + baseline: "benchmarks_v5_1_1" + metric: "e2e_200tok_s" + unit: "s" + lower_is_better: true + run_floor: 30 + perf_globs: + - "benchmarks/**" + - "squish/quant/**" + - "squish/serving/**" + - "squish/kv/**" + - "squish/context/**" + - "squish/loaders/**" + - "squish/io/**" + - "squish/speculative/**" + - "squish/hardware/**" + - "squish/backend.py" + bench_cmd: "python benchmarks/ollama_vs_squish/bench_thermal_h2h.py" + bench_adapter: "konjo-prove adapt (lib/bench_squish.py)" + min_effect_pct: null # PENDING: measure run-to-run jitter on real Apple Silicon first + activation_checklist: + - "On M-series bench hardware, run bench_cmd repeatedly under thermal control so >= 30 paired runs accumulate." + - "Use the bench's own DRIFT CHECK (ollama first vs last) to confirm thermal fairness." + - "Adapt the runs: konjo-prove adapt --bench thermal/*.json --config --phase p4000 --out baseline.json" + - "Measure run-to-run jitter (CoV or MAD as percent of median total_s)." + - "Set min_effect_pct above that noise floor, then confirm the number here." + - "Capture the golden: konjo-prove baseline capture --tag benchmarks_v5_1_1 --results baseline.json" + +killtest: true + +# Default set, declared explicitly rather than left implicit: squish's benchmark scripts +# (benchmarks/**, matching **/bench_*.py) are the repo's only long-running, checkpoint- +# worthy scripts. No scripts/train_*.py exists in this repo. None of squish's benchmark +# scripts currently wire a resume contract (checked this sprint: no +# --resume/--fresh/Checkpoint hits under benchmarks/ or scripts/) -- that is real, +# pre-existing debt, not something this sprint introduces or silently papers over. +longrun_globs: + - "benchmarks/**" + - "**/bench_*.py" + +# G-POLARITY (K1). Full-tree scan this sprint (2026-07-29, first connection -- see +# LEDGER.md's Squish-Polarity-First-Scan-1) found 17 standing findings across the whole +# tree: 2 real (one fixed this sprint -- catalog.py's has_prebuilt comment clarified, zero +# behavior change since the property only feeds a CLI display label, not the server or +# model-loading path; one deferred, scripts/compress_and_upload.py's smoke-test-not- +# installed-returns-True, quant-adjacent so not changed without its own review), 10 false +# positives in squish/ production code (numeric API defaults, idempotent completion +# bookkeeping -- none on the daemon's network-listening or model-loading trust boundary), +# and 5 in tests/**/benchmarks/** (test fixtures and a documented, logged thermal-sensor +# fallback). advisory: true is the deliberate ramp default for a repo with standing +# findings, same shape as gate_claude_contract's own ramp -- not silently inherited from +# the code default, chosen here because 15 of 17 findings need a human read before this +# can safely block. +polarity: + enabled: true + advisory: true + exempt_globs: + - "tests/**" # test fixtures intentionally simulate absence/fallback branches + - "benchmarks/**" # offline perf-harness readiness polls and a documented, + # explicitly-logged thermal-sensor-absent fallback (see above) + +# Phase 4/13-style ramp default, same shape as lopi's and vectro's own profiles. This +# sprint applied docs/pilots/squish-claude-md.proposed.md to squish's real CLAUDE.md (see +# CHANGELOG.md and LEDGER.md's Squish-Claude-Contract-1); re-measured after the apply: +# every invariant bullet now names its enforcing gate or says ADVISORY, all six required +# sections present. advisory stays true this sprint anyway -- one measurement of a +# freshly-converted document is not the same bar as lopi's PR #184 (converted, then run +# clean across real subsequent PRs before flipping to blocking); the next squish sprint +# that finds it still clean can flip advisory: false with that evidence. +claude_contract: + enabled: true + advisory: true + +# Phase 13, Phase 3 field. squish is a daemon with a network listener (server.py, api/**) +# and a model-loading path (catalog.py, quant/**, loaders/**) -- the trust boundaries the +# brief for this sprint explicitly called out. subprocess/exec risk lives in agent/** +# (tool execution) and daemon/** (launchagent, squishd); network ingress in server.py and +# api/**; path handling in io/** (split/loader paths); catalog.py resolves model URIs and +# performs the network fetch/verify logic gate_polarity's own full-tree scan flagged. +security_globs: + - "squish/server.py" + - "squish/api/**" + - "squish/serving/**" + - "squish/agent/**" + - "squish/daemon/**" + - "squish/integrations/**" + - "squish/catalog.py" + - "squish/io/**" + +# Confirmed: this sprint ran the exact command and confirmed it is squish's real, +# canonical verify command (CLAUDE.md's own Commands section, tests/conftest.py's +# CI-detection logic exercised for real this sprint -- see KT-A2.1 in LEDGER.md). +verify_cmd: "python -m pytest tests/ -x" + +# Confirmed: ruff format is squish's real formatter (pyproject.toml's [tool.ruff], +# .konjo/hooks/pre-commit). +format_cmd: "ruff format squish/ tests/" + +# The eval corpus for this repo, matching the existing kiban-side reconciliation +# (profiles/squish.yml): scopes the self-test to squish's own Python + MLX fixtures so the +# meta-gate never reviews a Rust/TypeScript/Mojo fixture this repo has no cassette for. +eval_corpus: + - squish + - _clean_control + - _clean_control_mlx + +overrides: {} diff --git a/.konjo/ruff-format-ceiling.txt b/.konjo/ruff-format-ceiling.txt new file mode 100644 index 00000000..49d0cea8 --- /dev/null +++ b/.konjo/ruff-format-ceiling.txt @@ -0,0 +1,9 @@ +# Konjo ratchet ceiling — files needing `ruff format` reformatting (whole-repo +# scope, matching konjo-gate.yml G1's real `ruff format --check .` command). +# Never regress above this count; ratchet it down as files get reformatted. +# Seeded 2026-07-29 (Track A2, squish's first kiban connection): measured via +# `ruff format --check . | grep -c '^Would reformat'`. +# 363 files is real, pre-existing debt — a bulk reformat is its own sprint +# (non-goal here: "Feature work of any kind" excludes a drive-by mass reformat +# touching hundreds of files in the same PR that connects the quality gates). +363 diff --git a/.konjo/scripts/ratchet_check.py b/.konjo/scripts/ratchet_check.py new file mode 100755 index 00000000..731998da --- /dev/null +++ b/.konjo/scripts/ratchet_check.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Konjo generic ratchet gate — a cleared bar never moves backward. + +Same shape as lopi's `.konjo/scripts/coverage_floor_check.py`, generalized to cover +both directions a metric can be locked: + + --mode ceiling a count that must never GROW (dead code, security findings, + complexity violations, DRY violations, files needing reformat). + FAIL if measured > locked value. + --mode floor a percentage/score that must never SHRINK (docstring coverage, + test coverage). FAIL if measured < locked value. + +Introduced this sprint (Track A2, squish's first kiban connection) to convert several +`continue-on-error: true` steps in konjo-gate.yml from "cannot ever fail" to "fails +only on regression against squish's own measured baseline" — see LEDGER.md's +Squish-Gate-Triage-1 for the per-step disposition table and the baseline counts each +`.konjo/*-ceiling.txt` / `.konjo/*-floor.txt` file was seeded with. + +This script does not run the underlying tool (ruff/vulture/bandit/radon/interrogate). +The caller runs the tool, extracts a single numeric measurement, and passes it via +--measured — kept this way (rather than each gate shelling out through this script) +so the tool invocation stays visible and greppable in the workflow YAML itself, the +same transparency lopi's coverage_floor_check.py's own workflow step already has. + +Exit codes: + 0 — measured value at or better than the locked value + 1 — regression (ceiling exceeded, or floor undershot) + 2 — locked-value file missing or malformed, or --measured unparseable +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + + +def read_locked_value(path: Path) -> float: + """Read the locked value: the first non-comment, non-blank line.""" + for raw_line in path.read_text(encoding="utf-8").splitlines(): + line = raw_line.strip() + if not line or line.startswith("#"): + continue + return float(line) + raise ValueError(f"{path} has no value (only comments/blank lines)") + + +def main(argv: list[str]) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--mode", required=True, choices=["ceiling", "floor"]) + parser.add_argument("--name", required=True, help="metric name, for messages") + parser.add_argument("--measured", required=True, help="the value the caller measured") + parser.add_argument("--file", required=True, type=Path, help="the locked-value file") + args = parser.parse_args(argv) + + try: + measured = float(args.measured) + except ValueError: + print(f"::error::--measured {args.measured!r} is not a number.") + return 2 + + try: + locked = read_locked_value(args.file) + except (OSError, ValueError) as exc: + print(f"::error::Cannot read {args.name} ratchet value from {args.file}: {exc}") + return 2 + + print(f"Measured {args.name}: {measured:g}") + print(f"Locked {args.mode}: {locked:g}") + + if args.mode == "ceiling": + if round(measured, 4) > round(locked, 4): + print( + f"::error::{args.name} rose to {measured:g}, above the locked ceiling " + f"{locked:g} ({args.file}). Fix the regression, or if this is a genuine " + "measurement-method change, say why in the commit message and update the " + "ceiling — never raise it silently to make a real regression pass." + ) + return 1 + if measured < locked: + print( + f"{args.name} improved to {measured:g}, below the {locked:g} ceiling. " + f"Consider ratcheting {args.file} down to {measured:g} in this PR." + ) + else: # floor + if round(measured, 4) < round(locked, 4): + print( + f"::error::{args.name} dropped to {measured:g}, below the locked floor " + f"{locked:g} ({args.file}). Fix the regression, or if this is a genuine " + "measurement-method change, say why in the commit message and update the " + "floor — never lower it silently to make a real regression pass." + ) + return 1 + if measured > locked: + print( + f"{args.name} rose to {measured:g}, above the {locked:g} floor. Consider " + f"ratcheting {args.file} up to {measured:g} in this PR." + ) + + print(f"{args.name} ratchet gate: OK") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/.konjo/scripts/test_ratchet_killtest.sh b/.konjo/scripts/test_ratchet_killtest.sh new file mode 100755 index 00000000..99bc75b4 --- /dev/null +++ b/.konjo/scripts/test_ratchet_killtest.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Kill-test for ratchet_check.py — Track A2 verification. +# +# Proves, against synthetic locked-value files (never squish's real ceiling/floor +# files — this must never depend on the current tree's actual counts): +# 1. A ceiling metric that regresses UP (more findings) fails. +# 2. A ceiling metric that holds or improves passes. +# 3. A floor metric that regresses DOWN (less coverage) fails. +# 4. A floor metric that holds or improves passes. +# 5. A missing/malformed locked-value file fails with exit 2, not a silent pass. +# 6. A non-numeric --measured fails with exit 2, not a silent pass. +# +# Usage: bash .konjo/scripts/test_ratchet_killtest.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CHECK="$SCRIPT_DIR/ratchet_check.py" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +PASS=0 +FAIL=0 + +assert_exit() { + local desc="$1" expected="$2" + shift 2 + set +e + python3 "$CHECK" "$@" >"$TMP/out.log" 2>&1 + local actual=$? + set -e + if [ "$actual" -eq "$expected" ]; then + echo "PASS: $desc (exit $actual)" + PASS=$((PASS + 1)) + else + echo "FAIL: $desc (expected exit $expected, got $actual)" + cat "$TMP/out.log" + FAIL=$((FAIL + 1)) + fi +} + +echo "108" > "$TMP/ceiling_108.txt" +echo "31.4" > "$TMP/floor_31_4.txt" + +echo "── ratchet_check.py kill-test ──" +assert_exit "ceiling holds exactly (108 == 108) passes" 0 --mode ceiling --name vulture --measured 108 --file "$TMP/ceiling_108.txt" +assert_exit "ceiling improves (90 < 108) passes" 0 --mode ceiling --name vulture --measured 90 --file "$TMP/ceiling_108.txt" +assert_exit "ceiling regresses (120 > 108) fails" 1 --mode ceiling --name vulture --measured 120 --file "$TMP/ceiling_108.txt" +assert_exit "floor holds exactly (31.4 == 31.4) passes" 0 --mode floor --name docstrings --measured 31.4 --file "$TMP/floor_31_4.txt" +assert_exit "floor improves (40 > 31.4) passes" 0 --mode floor --name docstrings --measured 40 --file "$TMP/floor_31_4.txt" +assert_exit "floor regresses (20 < 31.4) fails" 1 --mode floor --name docstrings --measured 20 --file "$TMP/floor_31_4.txt" +assert_exit "missing locked-value file fails closed (exit 2)" 2 --mode ceiling --name vulture --measured 10 --file "$TMP/does_not_exist.txt" +assert_exit "non-numeric --measured fails closed (exit 2)" 2 --mode ceiling --name vulture --measured "not-a-number" --file "$TMP/ceiling_108.txt" + +echo +echo "Results: $PASS passed, $FAIL failed" +[ "$FAIL" -eq 0 ] diff --git a/.konjo/vulture-ceiling.txt b/.konjo/vulture-ceiling.txt new file mode 100644 index 00000000..b801db59 --- /dev/null +++ b/.konjo/vulture-ceiling.txt @@ -0,0 +1,4 @@ +# Konjo ratchet ceiling — vulture dead-code findings (--min-confidence 80). +# Never regress above this count; ratchet it down as dead code is removed. +# Seeded 2026-07-29 (Track A2): measured via `vulture . --min-confidence 80 | wc -l`. +108 diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f6cec7..3ef413a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,83 @@ This project adheres to [Semantic Versioning](https://semver.org/). --- +## [9.34.15] — Konjo quality-gate onboarding (Track A2): first kiban connection, decorative-lint-job fix, honest performance range + +squish's first connection to the org's kiban quality-gate substrate. No feature work; +this release is a CI/quality-gate and documentation retrofit, following the same +protocol lopi's Sprint S13R and vectro's Track A1 used. + +### Added +- `.konjo/kiban.ref` (pinned `v1.9.0`) and `.konjo/profile.yml` — squish's first + `konjo-gates` profile, re-verified field by field against the real repo (real module + paths, real test/lint commands, `lang/python` + `lang/mlx` packs, `verify_cmd`, + `format_cmd`, `longrun_globs`, `security_globs` declared explicitly, no placeholders). +- `.github/workflows/konjo-gates.yml` — runs kiban's pinned `konjo-gates` orchestrator + against `.konjo/profile.yml` on every PR and push to main. Net-new addition; nothing + existing was removed. +- `.konjo/scripts/ratchet_check.py` (+ `test_ratchet_killtest.sh`) — a generic + ceiling/floor ratchet gate, the same shape as lopi's `coverage_floor_check.py` + generalized to cover both directions a metric can be locked. Seeds ratchets for + ruff-format (363 files), vulture (108), bandit (67), radon complexity (146), DRY (99), + and mypy (215), each measured against this sprint's real tree. + +### Fixed +- **`ci.yml`'s `lint-only` job was decorative through a mechanism a `continue-on-error` + audit alone would miss**: `ruff check ... --exit-zero` and + `mypy ... --no-error-summary || true` structurally cannot fail regardless of + findings. `--exit-zero` removed (0 standing violations); mypy ratcheted against its + measured 215-error baseline instead of flipped hard on day one. Same `--exit-zero` + fixed in `ci.yml`'s `test` and `test-linux` jobs' own `Lint (ruff)` steps. +- `konjo-gate.yml`'s G1 `ruff lint` step promoted to real blocking (10 whole-repo + findings fixed: 4 auto-fixed, 6 covered by extending the existing tests/**-BLE001 + per-file-ignore to benchmarks/**, demo/**, scripts/**). G1's `ruff format`, `vulture`, + `bandit` and G4's `Complexity gate`, `DRY check`, `Documentation gate` — all six + previously `continue-on-error: true` — now genuinely run and fail on regression above + (or, for docstrings, below) their measured baseline via `ratchet_check.py`, instead of + being structurally unable to fail. +- `konjo-gate.yml` G1's `bandit --exclude` flag had a real, separate bug: `.venv,venv, + tests` (no leading `./`) never matched bandit's own `./`-prefixed walk paths, so it + was silently scanning `tests/` too (134 findings measured, not the real 67). Fixed to + `./.venv,./venv,./tests`. +- Two real bandit **High**-severity findings fixed: `squish/daemon/squishd.py`'s + `_model_key` (SHA1) and `squish/server.py`'s `_system_fingerprint` (MD5) both use a + weak hash for a non-cryptographic display/dedup key, not a security token — both + cleared with `usedforsecurity=False` (zero behavior change, same hash output). +- `pyproject.toml`'s PyPI `description` stated a single stale figure ("5.4× faster + end-to-end on 4K-token prompts vs Ollama") traced to a superseded v5.1.1-era + benchmark run. The current, thermally-controlled benchmark (`docs/paper.md`, + `BENCHMARKS.md`, the linked blog post) reports the honest range for the same + claim — up to 9.8× on exact prompt repetition, 1.15–1.32× on completely unique + prompts, up to 14.7× in the isolated prompt-reuse-percentage ablation — and + `BENCHMARKS.md` already states its own documentation rule ("quote the range, not a + single number"). Corrected to: "1.15-14.7x faster than Ollama depending on prompt + repetition." `README.md` already stated the honest range; no change needed there. +- `squish/catalog.py`'s `has_prebuilt` docstring clarified (no behavior change): the + network-unavailable fallback returns `True` on the strength of the static catalog + field, an intentional "trust the last-known entry" choice for a display-only + property (its only callers are `cli.py`'s model-listing table), not an + unconfigured/failed-evaluation fallback on the daemon's network or model-loading + path — `gate_polarity`'s full-tree scan surfaced this shape; see LEDGER.md. +- `CLAUDE.md` converted to the Phase 13 six-section contract + (`docs/pilots/squish-claude-md.proposed.md`, applied from kiban): every invariant + bullet now names its enforcing gate or says `ADVISORY`; added the missing `Org + rules`, `Invariants`, `Repo map`, `Repo-specific rules` sections and the org import + line. Also updated the stale `**v9.34.2**` header and the Konjo Quality Framework + section to describe the real, post-fix Wall 2 state (ratcheted gates, `konjo-gates` + as Wall 2b) instead of the pre-existing "blocks the merge" claim four of five + clauses didn't back. + +### Quality Gates Now Active +- `ruff check` (whole repo and `squish/`+`tests/`): blocking, 0 standing violations. +- `ruff format`, `vulture`, `bandit`, radon complexity, DRY, docstring coverage, mypy: + ratcheted — blocking on regression against a measured baseline, not the full backlog. +- File size ≤ 500L: blocking for new files (unchanged, already correct). +- Coverage, mutation testing: still soft, each with a named reason and owner (see + LEDGER.md's `Squish-Gate-Triage-1`). +- `gate_polarity`, `gate_claude_contract`: advisory (first-connection ramp, matching + lopi's and vectro's own adoption path). +- `konjo-gates` (`.github/workflows/konjo-gates.yml`): blocking, real. + ## [9.34.14] — `squish quantize-remote`: quantize models bigger than local RAM/disk (Waves 139–147b) Wave 131 bounded peak disk during quantization to one raw shard in flight, diff --git a/CLAUDE.md b/CLAUDE.md index 0526f2a9..efee279e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,30 @@ Local LLM inference server — MLX-accelerated on Apple Silicon, with speculative decoding, quantization (INT4/INT3/SQINT2), agent tool execution, Ollama/OpenAI-compatible API, and the macOS SquishBar. -**v9.34.2** +**v9.34.15** + +## Org rules + +@~/.konjo/kiban/plugins/konjo/skills/konjo/SKILL.md + +The org ethos applies here: ship over optimize, kill-test first, statistical rigor, +honest negative results, evidence first, token-efficient context. + +Editorial rules: no em dashes, no AI-tell vocabulary. The prose lint enforces it; run +`konjo-prose` on docs before pushing. + +Log durable decisions with `konjo-decision decide` at `repo:squish` scope. Search with +`konjo-decision search` before reopening a settled call. + +When you catch a mistake worth not repeating, invoke `correct`: it records a learning +with `konjo-learn` and proposes the smallest durable fix. A learning must name where +its rule lives (a CLAUDE.md line, a prose-lint word, a lane, or a gate), or it is +refused. + +Build the Konjo way: the `craft` skill carries the four behaviors (think before coding, +simplicity first, surgical changes, goal-driven execution) plus the verify-loop and the +pre-implementation trust-boundary contract. `verify_cmd` is declared in +`.konjo/profile.yml`. ## Stack Python 3.10+ · MLX + mlx-lm (Apple Silicon) · FastAPI · transformers · HuggingFace Hub · Swift (macOS SquishBar) @@ -17,18 +40,17 @@ squish trace # observability report squish compat # backend compatibility check ``` -## Critical Constraints -- No `unwrap()`/`expect()` in Python — raise with a clear message or log + re-raise -- No silent failures — `logging.warning` if a fallback swallows an error -- MLX imports must be gated behind platform check — never imported on Linux paths -- `squish.squash` is now an **optional** import — never hard-depend on `squash-ai` -- Quantization accuracy gates are hard stops: INT4 AWQ g=32 ≥ 70.6% arc_easy (Qwen2.5-1.5B); INT2 naive is **NEVER SHIP** -- Pre-scan HF models **before** loading weights — `HFFileSummary` scan runs at `squish pull hf:` time -- Prompt injection: system prompt content must never be controllable by request payload -- Never log raw user prompt content at INFO level or above — log a hash or truncated prefix -- Version bumps touch `pyproject.toml` + `squish/__init__.py` - -## Module Map +## Invariants +- No `unwrap()`/`expect()` in Python and no silent failures — `repo:pre-commit "silent error swallowing scan"` (blocks the commit on bare/`Exception`-wide `except`) +- Quantization accuracy gates are hard stops: INT4 AWQ g=32 ≥ 70.6% arc_easy (Qwen2.5-1.5B); INT2 naive is **NEVER SHIP** — `repo:model_pipeline.yml` "Compress and validate — accuracy gate" +- MLX imports must be gated behind platform check — never imported on Linux paths — ADVISORY +- `squish.squash` is an **optional** import — never hard-depend on `squash-ai` — ADVISORY +- Pre-scan HF models **before** loading weights — `HFFileSummary` scan runs at `squish pull hf:` time — ADVISORY +- Prompt injection: system prompt content must never be controllable by request payload — ADVISORY (only checked by Wall 3, which is disabled in CI) +- Never log raw user prompt content at INFO level or above — log a hash or truncated prefix — ADVISORY (same reason) +- Version bumps touch `pyproject.toml` + `squish/__init__.py` — ADVISORY + +## Repo map | Module | Role | |--------|------| | `squish/server.py` | FastAPI app entry point, startup profiler, backend routing | @@ -44,27 +66,45 @@ squish compat # backend compatibility check | `squish/platform/` | Cross-platform router and detector | | `apps/macos/SquishBar/` | Swift macOS menu bar app (model picker, progress, hotkey) | -## Planning Docs +## Repo-specific rules + +### Planning Docs - `MODULES.md` — per-wave module reference (Waves 1–99+) - `CHANGELOG.md` — all notable changes -## Konjo Quality Framework - -Three walls against AI slop — all enforced by CI. +### Konjo Quality Framework **Wall 1 — Pre-commit** (`bash .konjo/scripts/install-hooks.sh`): ruff lint, ruff format, bare-except scan, DRY check, TODO scan. Blocks the commit. -**Wall 2 — CI gate** (`.github/workflows/konjo-gate.yml`): -Coverage ≥ 80% · mutation survival ≤ 10% · complexity ≤ 15 · file ≤ 500L · zero DRY violations. Blocks the merge. -The 500L gate is blocking for **new** files; legacy oversized files are grandfathered in -`.konjo/oversized-allowlist.txt` (split them to remove, don't grow the list). +**Wall 2 — CI gate** (`.github/workflows/konjo-gate.yml`, `.github/workflows/ci.yml`): +- `ruff check` blocks for real on every job that runs it (0 standing violations). +- `ruff format`, `vulture`, `bandit`, `radon` complexity, `dry_check.py`, `interrogate` + docstrings, and `mypy` are **ratcheted**: each blocks only on regression above (or, for + docstrings, below) its measured baseline, not the full pre-existing backlog. Baselines + live in `.konjo/*-ceiling.txt` / `*-floor.txt`; see `.konjo/scripts/ratchet_check.py`. +- File size ≤ 500L blocks for **new** files; legacy oversized files are grandfathered in + `.konjo/oversized-allowlist.txt` (split them to remove, don't grow the list). +- Coverage and mutation testing stay soft — both are duplicated by real enforcement + elsewhere (`ci.yml`'s own macOS coverage job; mutation's own documented CI-timeout + constraint) — see `LEDGER.md`'s `Squish-Gate-Triage-1` for the full per-step table. + +**Wall 2b — kiban `konjo-gates`** (`.github/workflows/konjo-gates.yml`): +Runs kiban's pinned gate orchestrator (`.konjo/kiban.ref`) against `.konjo/profile.yml` +— `gate_polarity` and `gate_claude_contract` (both advisory during this repo's adoption +ramp), plus the same format/lint tools above, net-new-diff scoped. Blocks for real. **Wall 3 — Adversarial review** (local only — disabled in CI): `git diff HEAD~1 | python3 .konjo/scripts/konjo_review.py` See the `konjo-quality` skill (`.claude/skills/konjo-quality/`) for the full specification. -## Skills +### Skills See `.claude/skills/` — auto-loaded when relevant. Run `/konjo` to boot a full session (Brief + Discovery + Plan). + +## Pinning + +This repo pins a kiban ref in `.konjo/kiban.ref` (currently `v1.9.0`) and `KIBAN_REF` in +`.github/workflows/konjo-gates.yml` — bump both together; a kiban change should not +silently reach the gate. diff --git a/LEDGER.md b/LEDGER.md new file mode 100644 index 00000000..1d51263a --- /dev/null +++ b/LEDGER.md @@ -0,0 +1,166 @@ +# Ledger + +A running log of load-bearing design decisions — the ones that would be +expensive to silently re-litigate in a later sprint. One entry per sprint, +newest first. Not a changelog (that's `CHANGELOG.md`) — this is *why*, not +*what*. + +## Track A2 — squish's first kiban connection: decorative-lint-job fix, gate triage, honest performance range + +**KT-A2.1 (required kill-test, run before any triage): `konjo-gates` genuinely runs +against squish's new profile and CAN fail.** Ran the real, pinned engine (installed +from the local kiban checkout, since no `v1.9.0` tag exists yet in this sandboxed +clone — the real CI job resolves the tag once kiban itself cuts and pushes it) +against `.konjo/profile.yml` with `--base origin/main`. First finding, before any +deliberate test: the run **hung** — `mutation: mutmut` in the profile (inherited +verbatim from kiban's own `profiles/squish.yml` starting point) causes +`konjo_gates_py.cli`'s generic net-new dispatcher to shell out to a bare `mutmut run` +with **no timeout wrapper at all** (unlike the `cargo-mutants` path, which gets +`--jobs`/`--timeout` flags). Killed after several minutes with RSS still climbing. +This is the exact trap `profiles/lopi.yml`'s own comment documents avoiding for +`cargo-mutants` — confirmed live here for `mutmut` too. Fixed: `mutation: +"none-with-reason: ..."` (the `"none"`-prefix konjo_gates_py's dispatcher checks for +to skip the tool entirely); real mutation testing stays in `konjo-gate.yml` G3, which +already has the correct 7-minute internal cap. **This is exactly the kind of defect a +kill-test exists to catch before it reaches CI, not after** — recorded here instead of +quietly fixed with no trace. + +With that fixed, the deliberate half of KT-A2.1: a scratch file +(`squish/_kt_a21_scratch.py`, never committed) with a genuine, non-globally-ignored +violation (`except Exception:` — BLE001, enforced on `squish/` — plus a bare `1 / 0` +expression, B018) was scanned via `--changed squish/_kt_a21_scratch.py`. Result: **RED** +— `repo:ruff` FAILed on the exact BLE001/B018 text, `repo:vulture` FAILed (unused +function), `repo:bandit` FAILed (B110 try-except-pass). `polarity` correctly PASSed (no +permissive-value shape here) and `threat_model` correctly SKIPped (file not in +`security_globs`). Scratch file deleted immediately after. **The mechanism is live and +correctly discriminating real violations from clean code, not just returning green by +default.** + +**Real count and shapes of "soft" CI, vs the brief's stated 11.** `konjo-gate.yml` +carries exactly **10** literal `continue-on-error: true` steps (16 named steps total, +matching the brief's "16"), not 11 — a small but real discrepancy from the brief's own +count, reported rather than silently reconciled to match. Separately, and *not* caught +by any `continue-on-error` grep, `ci.yml`'s `lint-only` job's two steps +(`ruff check ... --exit-zero`, `mypy ... --no-error-summary || true`) plus the same +`--exit-zero` in the `test` and `test-linux` jobs' own `Lint (ruff)` steps — 4 more +structurally-cannot-fail shapes. **Total: 14 decorative quality-gate steps found across +both workflow files, not 11 and not 10** — the brief's "11" undercounts because it was +counting only `konjo-gate.yml`'s literal string, and this sprint's own instruction to +check for all three shapes (`continue-on-error`, `--exit-zero`, `|| true`) is exactly +what surfaced the other 4. + +**Every one of the 14 got a real disposition** (promote / keep-soft-with-owner-and-date +/ delete-with-reason; see `.github/workflows/konjo-gate.yml` and `ci.yml`'s own inline +comments for the full per-step reasoning, not duplicated here): + +| Step | Baseline measured | Disposition | +|---|---:|---| +| `ci.yml` `lint-only` ruff check | 0 | **PROMOTED** — `--exit-zero` removed, real block | +| `ci.yml` `lint-only` mypy check | 215 errors | **RATCHETED** — `.konjo/mypy-ceiling.txt`, real block on regression | +| `ci.yml` `test` job's `Lint (ruff)` | 0 | **PROMOTED** — `--exit-zero` removed | +| `ci.yml` `test-linux` job's `Lint (ruff)` | 0 | **PROMOTED** — `--exit-zero` removed | +| G1 `ruff lint` | 0 (10 fixed this sprint) | **PROMOTED** — real block | +| G1 `ruff format` | 363 files | **RATCHETED** — `.konjo/ruff-format-ceiling.txt` | +| G1 `vulture` | 108 | **RATCHETED** — `.konjo/vulture-ceiling.txt` | +| G1 `bandit` | 67 (2 real High fixed) | **RATCHETED** — `.konjo/bandit-ceiling.txt`; also fixed a real `--exclude` path-prefix bug that was silently scanning `tests/` too (134 findings measured with the old flag, not the real 67) | +| G2 `Run tests with coverage` + `Coverage gate` | n/a | **KEPT SOFT** — owner: squish maintainers, revisit-by 2026-09-30. Duplicate of `ci.yml`'s real macOS coverage job, broken as configured on `ubuntu-latest` (no mlx install, missing the Metal-unguarded-import ignore list, `--cov=.` instead of `--cov=squish`) — promoting today would red every PR on a false signal, not a real one. | +| G3 `Run mutation testing` | n/a | **KEPT SOFT** — owner: squish maintainers, revisit-by 2026-09-30. Already load-bearing for a documented, sound reason (7-min internal cap dodges the 8-min GH Actions SIGKILL); promoting needs a real mutation-survival threshold this same timeout risk prevents measuring today. | +| G4 `Complexity gate` | 146 | **RATCHETED** — `.konjo/complexity-ceiling.txt` | +| G4 `DRY check` | 99 | **RATCHETED** — `.konjo/dry-ceiling.txt` | +| G4 `Documentation gate` | 31.4% | **RATCHETED** (floor) — `.konjo/docstring-floor.txt` | +| G4 `File size gate` | n/a | Already the one hard-blocking step; no change needed. | + +**`gate_polarity` full-tree scan (first connection — see `konjo-retrofit`'s baseline- +before-gating protocol): 17 standing findings, all triaged, none waved through.** +2 real (1 fixed this sprint, 1 flagged and deferred), 10 false positives in `squish/` +production code, 5 in `tests/**`/`benchmarks/**` (added to `polarity.exempt_globs`). +Given the brief's explicit instruction to scrutinize fail-open shapes extra carefully +on a daemon with a network listener and a model-loading path, every finding was traced +to its actual callers before being dismissed, not pattern-matched and dropped: + +| Finding | Disposition | +|---|---| +| `squish/catalog.py`'s `has_prebuilt`, network-unavailable → `True` | **FIXED (docs only, zero behavior change).** Traced every caller (`cli.py`'s model-listing table, `catalog.py`'s own `__str__`) — display-only, not on the request-handling or model-loading path. Docstring now states the intentional "trust the last-known catalog entry" reasoning instead of leaving the comment/code relationship ambiguous. | +| `scripts/compress_and_upload.py`'s `_smoke_test`, `mlx_lm` not installed → `True` (coherence check treated as passed) | **REAL DEFECT, FLAGGED, DEFERRED.** Gates whether a quantized model's coherence was actually verified before upload; "couldn't run the check" silently becoming "check passed" is backwards, though it does warn to stderr. Quant-adjacent — CLAUDE.md's hard constraint on quantization accuracy gates means changing this script's return-value semantics needs its own review, not a drive-by fix in a CI-connection sprint. Named for the next squish sprint. | +| `squish/cli.py` ×2 (ASTC-loader `ImportError` → int4 fallback; default compression format selection) | False positive — visible warning printed, legitimate UX default, not a trust-boundary bypass. | +| `squish/catalog.py`'s hash-verify, no expected hash recorded → `(True, "")` | False positive — "nothing recorded to compare against" is a data-completeness case, not a failed evaluation. | +| `squish/server.py` ×3, `repetition_penalty` default `1.0` | False positive — the semantically-neutral value for that parameter (no penalty), a normal API default. | +| `squish/serving/ollama_compat.py` ×2, `stream` default `True` | False positive — matches upstream Ollama's own default; a response-format choice, not a trust-boundary bypass. | +| `squish/serving/scheduler.py` ×2, `req.done = True` after emitting completion | False positive — idempotent completion bookkeeping after real work (emitting the finish signal), not an unconfigured-fallback shape. | +| `tests/test_wave82_autoload_eagle3.py` | Exempt — test fixture, added `tests/**` to `polarity.exempt_globs`. | +| `benchmarks/**` ×3 (two readiness-poll `wait_ready` functions treating any HTTP response including errors as "server up"; one thermal-sensor-absent fallback with its own inline justification comment already in the code) | Exempt — offline perf-harness code, not the daemon's request path; the thermal one was already self-documenting before this sprint touched it. Added `benchmarks/**` to `polarity.exempt_globs`. | + +**A live kiban-side finding, not a squish defect, reported upstream rather than worked +around by gaming squish's own code:** `repo:ruff`'s net-new dispatch (`konjo_gates_py. +cli`) diffs raw tool **stdout text** between the HEAD and base worktree scans, not +per-finding identity. When this sprint's own `pyproject.toml` per-file-ignore addition +(the fix for the whole-repo `ruff lint` promotion above) made a file's ruff output go +from several real findings (at base, old config) to `All checks passed!` (at HEAD, new +config), the dispatcher counted `All checks passed!` itself as a "1 net-new finding" — +a config-driven *cleanup* registering as a regression. Confirmed by isolating the exact +file and reproducing twice. Left as a known, upstream-reportable limitation rather than +suppressed; this PR's own `konjo-gates` run will show it. + +A second, related false trigger caught and fixed rather than worked around dishonestly: +`gate_one_way_door`'s `_REMOVED_DEF` regex flags *any* diff line starting with `-def`/ +`-class`, with no semantic understanding of "same function, modernized type annotation" +vs "function actually removed." A `ruff --fix`-applied `Optional[str]` → `str | None` +rewrite on a single-line function signature (`scripts/check_release_sync.py`) tripped +it. Rather than fabricate a `Konjo-Acknowledged-Oneway` trailer for a change that isn't +genuinely one-way, the line was reverted to its original spelling with a whole-file +`# ruff: noqa: UP045` directive (not a per-line one, which would touch the same `def` +line and retrigger the detector) — confirmed clean on both `one_way_door` and `ruff +lint` afterward. + +**`perf_globs` corrected from a bare `squish/**` (kiban's own starting-point profile, +carried forward unquestioned) to the genuine hot-path directories** — +`quant/serving/kv/context/loaders/io/speculative/hardware/backend.py`, not the whole +package. Caught live by KT-A2.1's own kill-test run: `squish/**` matched literally +every file including `cli.py`'s UX code and `catalog.py`'s docstring-only edit, +demanding a bench-hardware `konjo-prove` MERGE verdict for changes with no measurable +perf effect. This is exactly the kind of kiban-inherited field the brief asked to be +re-verified against the real tree rather than left as a placeholder. + +**`threat_model` exercised for real, not stubbed.** This sprint's own diff touches +`squish/catalog.py` and `squish/server.py`, both correctly in `security_globs` +(`network_ingress` boundary). Ran `konjo-threat classify` then `record` for real +(not simulated): mitigation states the diff is a docstring clarification plus a +`usedforsecurity=False` annotation on two non-authentication hashes, zero behavior +change to the request-handling path; `hmac.compare_digest` (the real API-key check) +is untouched. Trailer: `Konjo-Threat-Model: f666caf4bcbd` (carried on this sprint's +commit). + +**`claude_contract` applied for real** (`docs/pilots/squish-claude-md.proposed.md`, +copied from kiban read-only, applied here): squish's `CLAUDE.md` had 4 of 6 required +sections missing before this sprint (org rules, invariants, repo map, repo-specific +rules) and no org import line — same finding kiban's own read-only reconciliation +recorded for both squish and vectro. Applied verbatim plus one necessary update the +static proposal couldn't have known about: the Konjo Quality Framework section's Wall 2 +description no longer claims "blocks the merge" for gates this same sprint just +converted from decorative to real-but-ratcheted — re-verified against +`lib.claude_contract.check_contract` (`ok=True`) after editing, not just assumed clean. +`profile.yml`'s `claude_contract.advisory` stays `true` this sprint regardless (one +measurement of a freshly-converted document isn't the same bar as a document that's +run clean across several real subsequent PRs) — the next sprint that confirms it holds +can flip it. + +**Performance-claim correction.** `pyproject.toml`'s PyPI description stated "5.4× +faster end-to-end on 4K-token prompts vs Ollama" as a single figure — traced to +`CHANGELOG.md`'s v5.1.1-era entry (12.78s vs 69.63s, an old benchmark run), superseded +by the current thermally-controlled benchmark that measures the same claim (4K-token +prompt, end-to-end vs Ollama) at 9.8× on exact repetition and 1.19× on a completely +unique prompt — a materially different number for the identical claim, from newer, +more rigorous data already in the repo (`docs/paper.md`, `BENCHMARKS.md`, the linked +blog post `docs/blog/posts/local-llm-fast-enough.md`, which independently states "1.15 +to 14.7× faster than Ollama depending on how much your prompts repeat" in its own +description and TL;DR). `BENCHMARKS.md` already states its own rule for exactly this +situation — "quote the range, not a single number" — which the stale PyPI description +violated. Corrected to: "1.15-14.7x faster than Ollama depending on prompt +repetition." `README.md` already stated the honest range and needed no change. +`Formula/*.rb`'s Homebrew `desc` carries no numeric claim; nothing to fix there. + +**Non-goal boundary held**: this sprint did not touch quantization algorithms, the +Homebrew formula, or PyPI packaging mechanics (build-system, publish workflow, +classifiers) — the one `pyproject.toml` edit beyond the version bump and the +per-file-ignore addition is the `description` string, which step 7 of this sprint's own +brief explicitly named as one of three places to check for performance claims. diff --git a/NEXT_SESSION_PROMPT.md b/NEXT_SESSION_PROMPT.md index 1c831b42..f05ae9a3 100644 --- a/NEXT_SESSION_PROMPT.md +++ b/NEXT_SESSION_PROMPT.md @@ -1,61 +1,85 @@ -# Memory governor eviction sprint — complete (v9.34.9 - v9.34.12) +# Konjo quality-gate onboarding (Track A2) — complete, follow-ups flagged -All 5 phases from the original sprint brief are landed. `MemoryGovernor` -now actually drives eviction, context sizing, and request shedding across -all four pressure levels (NORMAL/WARNING/URGENT/CRITICAL), matching its own -docstring intent instead of being read only by `/health`. +squish's first connection to kiban's `konjo-gates` orchestrator landed this sprint. No +feature work touched. See `LEDGER.md`'s "Track A2" entry for the full reasoning behind +every decision below; this file is the actionable follow-up list, not a re-derivation. ## What's built and verified -- **Phase 1** (v9.34.9): `BlockKVCache.set_hot_max_bytes(n)` / - `PromptKVStore.set_max_bytes(n)` — thread-safe, live-adjustable cache - budgets that evict immediately when shrunk. -- **Phase 2** (v9.34.9-10): `squish/server.py::_on_memory_pressure_change` - — WARNING shrinks caches to 50%, URGENT to 20%, both always shrinking - from the same originally-captured baseline regardless of escalation - direction. NORMAL restores exactly. CRITICAL doesn't shrink caches - further (that's Phase 4's job). -- **Phase 3** (v9.34.10): `squish/server.py::_effective_max_kv_size()` — - per-request `max_kv_size` ceiling, capped at `governor.budget_tokens()` - whenever pressure isn't NORMAL (including CRITICAL). Never raises the - configured ceiling. -- **Phase 4** (v9.34.11): `squish/server.py::_MemoryPressureShedMiddleware` - — rejects new requests with HTTP 503 at CRITICAL (reject-only, no - queueing), exempting `/health`/`/v1/metrics`. In-flight requests are - never aborted. -- **Phase 5** (v9.34.12): concurrency safety review found and fixed one - real (if not-yet-reachable) TOCTOU race in Phase 2's baseline-capture - logic (`_pressure_callback_lock`). Stress tests prove cache/budget - invariants and response integrity hold under concurrent pressure storms - racing concurrent request traffic. +- `.konjo/kiban.ref` (`v1.9.0`) + `.konjo/profile.yml` — squish's first real profile, + re-verified field by field against this repo's actual tree, not copied blind from + kiban's starting point. +- `.github/workflows/konjo-gates.yml` — new, real, blocking job. Confirmed live + (KT-A2.1): runs 18 gates, correctly PASSes clean code and FAILs a deliberately + planted violation (ruff/vulture/bandit all fired on a scratch file with a real + `except Exception:` + bare expression, then the scratch file was deleted). +- `ci.yml`'s `lint-only` job's `--exit-zero`/`|| true` fixed — this was the actual + decorative-lint-job defect the sprint brief named. `ruff check` is real now (0 + standing violations); `mypy` is ratcheted against its measured 215-error baseline. +- `konjo-gate.yml`'s 10 `continue-on-error: true` steps: all triaged (promote / keep- + soft-with-owner-date / none deleted). 6 promoted to real ratcheted gates + (`.konjo/scripts/ratchet_check.py` + `.konjo/*-ceiling.txt`/`*-floor.txt`), 2 kept + soft with a named owner and 2026-09-30 revisit date, `ruff lint` fully promoted, file + size gate already correct. +- `CLAUDE.md` converted to the six-section contract + (`docs/pilots/squish-claude-md.proposed.md`), re-verified clean against + `lib.claude_contract.check_contract` after editing. +- `pyproject.toml`'s stale single-figure performance claim ("5.4× faster") corrected to + the honest measured range ("1.15-14.7x ... depending on prompt repetition"). +- Version bumped to `9.34.15` (`pyproject.toml` + `squish/__init__.py`, consistency + check passes). -## Test coverage (all in `tests/serving/`) -- `test_memory_governor_wiring.py` (17 cases) — WARNING/URGENT/NORMAL - cache-shrink and restore, real cache instances + mocked-call precision. -- `test_effective_max_kv_size.py` (10 cases) — ceiling computation across - all pressure levels, never-raise guarantee, real-governor end-to-end case. -- `test_critical_request_shedding.py` (13 cases) — shedding, exemptions, - CORS-header survival, in-flight-request survival (single transition). -- `test_phase5_concurrency_stress.py` (2 cases) — the same invariants - under real concurrent load and rapid pressure storms. +## Follow-ups flagged for a future sprint (named here, not silently dropped) +- **G2 coverage job (`konjo-gate.yml`) is a broken duplicate of `ci.yml`'s real + macOS coverage job.** Owner: squish maintainers, revisit-by 2026-09-30. Either fix + its ignore-list + `--cov=squish` scoping + mlx install, or delete it outright in + favor of the real one. Not this sprint's call (non-goal: connect what exists, don't + rebuild CI). +- **G3 mutation testing (`konjo-gate.yml`) stays soft.** Owner: squish maintainers, + revisit-by 2026-09-30. Needs a completed, non-timed-out mutation run to set a real + survival-rate threshold before it can safely go hard. +- **`scripts/compress_and_upload.py`'s `_smoke_test`** returns `True` ("coherence + check passed") when `mlx_lm` isn't installed to actually run it — a real, if modest, + fail-open shape on a quant-adjacent upload gate. Flagged by this sprint's + `gate_polarity` full-tree scan, deliberately not fixed here (quantization-adjacent + behavior change needs its own review per CLAUDE.md's hard constraint, not a drive-by + fix in a CI-connection sprint). +- **Two kiban-side (not squish-side) false positives found and reported, not worked + around by gaming squish's code**: (1) `repo:ruff`'s net-new dispatch diffs raw tool + stdout text rather than per-finding identity, so a config-driven cleanup (this + sprint's own `pyproject.toml` per-file-ignore addition) registers as a "net-new + finding" — reproducible, see LEDGER.md. (2) `gate_one_way_door`'s `_REMOVED_DEF` + regex flags any `-def`/`-class` diff line with no semantic understanding of a type- + annotation modernization vs. an actual removal — worked around in + `scripts/check_release_sync.py` (reverted to the original spelling with a whole-file + `# ruff: noqa` directive) rather than fabricating a one-way-door acknowledgement. + Both worth filing against `konjoai/kiban`. +- **363 files need `ruff format`, 108 vulture findings, 67 bandit findings, 146 + radon grade-C+ functions, 99 DRY violations, 31.4% docstring coverage** — all real, + all now ratcheted (can't regress), none fixed outright this sprint (non-goal: + no drive-by mass reformatting/refactoring in the same PR that connects the gates). + Whoever picks up the next quality sprint: work the ratchets down incrementally, one + category at a time, per the `konjo-retrofit` protocol. +- **`konjo-prose`** (the org's em-dash/AI-tell-vocabulary lint) is not wired into any + squish CI job and was run this sprint only in `--warn` (non-blocking) mode against + this sprint's own new prose (`LEDGER.md`, the new `CHANGELOG.md` entry) — consistent + with how the tool's own module docstring describes its intended use ("docs run non- + blocking while article branches stay strict") and with the em-dash-heavy style + already present throughout squish's, lopi's, and kiban's own existing `CHANGELOG.md`/ + `LEDGER.md` files. Not treated as a blocking requirement for internal engineering + logs; flagged here rather than silently ignored. +- **`.claude/rules/python-conventions.md`** claims `mypy --strict` clean, zero vulture + findings, and zero radon grade-C+ functions — none of which hold today (215 mypy + errors under plain `--ignore-missing-imports`, let alone `--strict`; 108 vulture + findings; 146 grade-C+ functions). Found while cross-checking rule files for this + sprint's CLAUDE.md work; out of this sprint's explicit scope (only `CLAUDE.md` itself + was in scope for the section-contract conversion), flagged for whoever next touches + that rules file. -## Explicitly out of scope for this sprint (flag for a future one if needed) -- **Request queueing/backpressure at CRITICAL.** The sprint brief scoped - this out explicitly as "a separate, larger design question" — CRITICAL - currently only rejects, never queues. -- **10 benchmark-matrix cells** (`r*_c16000`, `r*_c32000` in - `benchmarks/ollama_vs_squish/matrix`) measure stale behavior on - memory-constrained hosts now that live eviction exists — see CHANGELOG - 9.34.9 for the full list. Not re-run per this sprint's explicit non-goal. -- **`ruff format --check` pre-existing drift** on `squish/server.py`, - `squish/kv/block_kv_cache.py`, `squish/kv/prompt_kv_cache.py` — confirmed - via `git stash` to predate this sprint (local ruff 0.15.20 vs whatever - version last formatted the repo's hand-aligned dataclass style). Not this - sprint's regression. -- **`/v1/models`, `/v1/tokenize`, and other cheap-but-unlisted endpoints** - are shed (503) at CRITICAL along with the generation routes, since - Phase 4 used a small observability allowlist (`/health`, `/v1/metrics`) - rather than a generation-route denylist. Intentional simplicity - tradeoff — revisit only if it proves operationally annoying. -- **Fraction values (WARNING=50%, URGENT=20%) are starting points**, not - derived from fleet telemetry. Revisit with real production pressure data - if/when available. +## Explicitly out of scope for this sprint (per the brief's own non-goals) +- Feature work of any kind. +- Quantization algorithm changes. +- The Homebrew formula or PyPI packaging mechanics (build-system, publish workflow, + classifiers) — only the `description` string was touched, and only because it + contained a stated performance claim, which the brief explicitly named in scope. +- Re-running benchmarks — the performance-claim fix is an audit of existing evidence + already in the repo, not new measurement. diff --git a/pyproject.toml b/pyproject.toml index d0f99ec5..416c5015 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta" [project] name = "squish-ai" -version = "9.34.14" -description = "Local LLM inference server for Apple Silicon. Block-level paged KV cache for long-context workloads. 5.4× faster end-to-end on 4K-token prompts vs Ollama, less RAM, INT3 support for Qwen3. OpenAI-compatible API." +version = "9.34.15" +description = "Local LLM inference server for Apple Silicon. Block-level paged KV cache for long-context workloads. 1.15-14.7x faster than Ollama depending on prompt repetition, less RAM, INT3 support for Qwen3. OpenAI-compatible API." readme = "README.md" requires-python = ">=3.11,<3.15" license = {text = "BUSL-1.1"} @@ -150,6 +150,18 @@ ignore = [ # Tests legitimately use broad `except Exception` for failure-path assertions and # best-effort probes; BLE001 is enforced only on the squish/ package. "tests/**" = ["BLE001"] +# benchmarks/, demo/, and scripts/ are offline tooling: best-effort probes (a +# thermal/health-check wait loop, a demo server's top-level request handler, a +# packaging script's HF-repo-creation step) that intentionally catch broadly and +# print a clear message rather than crash the whole run over one bad probe. Same +# reasoning as tests/** above, applied Track A2 (2026-07-29) when konjo-gate.yml's +# G1 `ruff lint` step (previously `continue-on-error: true`, whole-repo `ruff check +# .`) was promoted to real blocking status — these six real, pre-existing findings +# needed a disposition, not a silent ratchet, since BLE001 in a best-effort tooling +# script is squish's own established exception, not a defect. +"benchmarks/**" = ["BLE001"] +"demo/**" = ["BLE001"] +"scripts/**" = ["BLE001"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/scripts/check_release_sync.py b/scripts/check_release_sync.py index 94ae5a5d..dac268f9 100644 --- a/scripts/check_release_sync.py +++ b/scripts/check_release_sync.py @@ -7,6 +7,16 @@ Exits 0 if all checks pass, 1 if any fail. """ +# ruff: noqa: UP045 +# `run_checks`'s signature below is deliberately left on the legacy `Optional[str]` +# spelling rather than modernized to `str | None`: touching that exact line trips +# kiban's own one_way_door detector (`_REMOVED_DEF` matches any diff line starting +# with `-def ...`, with no semantic understanding of "same function, modernized +# annotation" vs "function actually removed") -- see LEDGER.md's +# Squish-Gate-Triage-1/KT-A2.1 for the full finding, reported upstream rather than +# worked around by faking a one-way-door acknowledgement for a change that isn't +# genuinely one-way. A whole-file ignore comment (above, not attached to the def +# line itself) keeps that line byte-for-byte unchanged from its committed form. from __future__ import annotations import argparse @@ -112,9 +122,9 @@ def _check_bottle_sha(expected_version: str) -> str: def _run_check( label: str, fn, - expected: Optional[str], + expected: str | None, results: list[tuple[str, bool, str]], -) -> Optional[str]: +) -> str | None: try: value = fn() ok = expected is None or value == expected diff --git a/scripts/compress_and_upload.py b/scripts/compress_and_upload.py index af07e4d3..61640956 100644 --- a/scripts/compress_and_upload.py +++ b/scripts/compress_and_upload.py @@ -285,7 +285,7 @@ def main() -> int: # ── Smoke test ──────────────────────────────────────────────────── if not args.skip_smoke_test: if not args.quiet: - print(f" Running coherence smoke test …") + print(" Running coherence smoke test …") passed = _smoke_test(output_dir, verbose=verbose) if not passed: print( diff --git a/squish/__init__.py b/squish/__init__.py index 11a6498d..248fbb31 100644 --- a/squish/__init__.py +++ b/squish/__init__.py @@ -14,7 +14,7 @@ from __future__ import annotations -__version__ = "9.34.14" +__version__ = "9.34.15" def _install_vendored_squish_quant() -> None: diff --git a/squish/catalog.py b/squish/catalog.py index 28944840..d7e892f5 100644 --- a/squish/catalog.py +++ b/squish/catalog.py @@ -260,12 +260,22 @@ def dir_name(self) -> str: @property def has_prebuilt(self) -> bool: - """True when a pre-compressed Squish repo exists on HuggingFace.""" + """True when a pre-compressed Squish repo exists on HuggingFace. + + Display-only: the sole callers (cli.py's model-listing table) use this to + pick a "prebuilt"/"compress" label, not to gate a fetch or a load. When the + live HF lookup can't run (network unavailable, e.g. Zscaler), this returns + True on the strength of the catalog's own static `squish_repo` field rather + than the live index -- an intentional "trust the last-known catalog entry" + choice, not an unconfigured/failed-evaluation fallback (gate_polarity + surfaces this shape; see .konjo/profile.yml's polarity section and + LEDGER.md's Squish-Polarity-First-Scan-1 for the full reasoning). + """ if not self.squish_repo: return False live = _fetch_squishai_model_ids() if not live: - # Network unavailable (Zscaler etc.) — fall back to hardcoded field. + # Network unavailable: trust the static catalog field (see docstring). return True return self.squish_repo in live diff --git a/squish/daemon/squishd.py b/squish/daemon/squishd.py index c64413cf..ab653926 100644 --- a/squish/daemon/squishd.py +++ b/squish/daemon/squishd.py @@ -488,7 +488,9 @@ def _load_model( def _model_key(model_dir: str) -> str: """Stable short key for a model directory (basename + hash prefix).""" name = Path(model_dir).name - h = hashlib.sha1(model_dir.encode()).hexdigest()[:8] + # Non-cryptographic use (a short display/dedup key, not a security token) — + # usedforsecurity=False documents that intent and clears bandit's B324. + h = hashlib.sha1(model_dir.encode(), usedforsecurity=False).hexdigest()[:8] return f"{name}:{h}" diff --git a/squish/server.py b/squish/server.py index eff68a55..577cc5f5 100644 --- a/squish/server.py +++ b/squish/server.py @@ -1145,9 +1145,12 @@ def _system_fingerprint(model_name: str | None, loaded_at: float) -> str: Cached with lru_cache so the MD5 is only computed once per unique (model_name, loaded_at) pair — not on every streamed token. + + Non-cryptographic use (a display fingerprint, not a security token) — + usedforsecurity=False documents that intent and clears bandit's B324. """ return "sq-" + hashlib.md5( - f"{model_name}{loaded_at}".encode() + f"{model_name}{loaded_at}".encode(), usedforsecurity=False ).hexdigest()[:8] From 486101391f978bed45178cbc6caf2763a60f1ddc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 14:26:20 +0000 Subject: [PATCH 2/4] docs(ledger): record real one-way-door and threat-model trailers The previous commit's threat_model trailer was generated against a partial 5-file changed-set before every file was staged; the fingerprint changes once the full 25-file diff is considered. Regenerated both trailers for real (konjo-oneway confirm, konjo-threat record) against the actual full changed-file set this PR carries, and recorded why each gate fired (destructive-shell matched test_ratchet_killtest.sh's standard mktemp cleanup trap; authn_authz matched a docstring mentioning the existing, unmodified API-key check). Konjo-Acknowledged-Oneway: b94be0e76761 Konjo-Threat-Model: b94be0e76761 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01NLdTtZgWyLWu2gE9CohWwa --- LEDGER.md | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/LEDGER.md b/LEDGER.md index 1d51263a..ede648b0 100644 --- a/LEDGER.md +++ b/LEDGER.md @@ -123,12 +123,26 @@ re-verified against the real tree rather than left as a placeholder. **`threat_model` exercised for real, not stubbed.** This sprint's own diff touches `squish/catalog.py` and `squish/server.py`, both correctly in `security_globs` -(`network_ingress` boundary). Ran `konjo-threat classify` then `record` for real -(not simulated): mitigation states the diff is a docstring clarification plus a -`usedforsecurity=False` annotation on two non-authentication hashes, zero behavior -change to the request-handling path; `hmac.compare_digest` (the real API-key check) -is untouched. Trailer: `Konjo-Threat-Model: f666caf4bcbd` (carried on this sprint's -commit). +(`network_ingress` boundary); the full 25-file changed-file set also matched +`authn_authz` on a diff-content scan (the word "auth"/"api" appears in a nearby +docstring, not in any changed authentication logic). Ran `konjo-threat classify` then +`record` for real (not simulated) against the full, real changed-file set — not the +partial 5-file set an earlier mid-sprint check used before every file was staged, +which produced a different (now-superseded) fingerprint. Mitigation for both +boundaries states the diff is a docstring clarification plus a `usedforsecurity=False` +annotation on two non-authentication hashes, zero behavior change to the request- +handling or auth-check path; `hmac.compare_digest` (the real API-key check) is +untouched. Trailer: `Konjo-Threat-Model: b94be0e76761`. + +**`one_way_door` also fired for real on the full changed-file set, on a fourth kiban +false-positive class**: `_DIFF_RULES`'s `destructive-shell` pattern matches any diff +line containing `rm -rf` with no scope awareness of "inside a `mktemp -d` test-fixture +cleanup trap" vs. an actual destructive repo action — the same `rm -rf "$TMP"` idiom +lopi's own `.konjo/scripts/test_coverage_floor_killtest.sh` already uses, here in this +sprint's new `.konjo/scripts/test_ratchet_killtest.sh`. Rather than rewrite a safe, +standard test-cleanup idiom to dodge a detector, ran `konjo-oneway confirm` for real +(not simulated) and recorded the acknowledgement: `Konjo-Acknowledged-Oneway: +b94be0e76761` (same fingerprint — both trailers key on the identical sorted file set). **`claude_contract` applied for real** (`docs/pilots/squish-claude-md.proposed.md`, copied from kiban read-only, applied here): squish's `CLAUDE.md` had 4 of 6 required @@ -164,3 +178,17 @@ Homebrew formula, or PyPI packaging mechanics (build-system, publish workflow, classifiers) — the one `pyproject.toml` edit beyond the version bump and the per-file-ignore addition is the `description` string, which step 7 of this sprint's own brief explicitly named as one of three places to check for performance claims. + +**Honest caveat on this session's own final re-verification, not glossed over.** This +sandboxed environment's root filesystem ran critically low on disk mid-sprint (a +pre-existing condition, not caused by this sprint's own file additions, which total a +few hundred KB) forcing deletion of the isolated venv this sprint used for its clean, +authoritative measurements (the 215-error mypy baseline, the 0-violation ruff baseline, +all ratchet seeds). The final post-commit `konjo-gates` re-run used bare system Python +without squish's own runtime dependencies installed, which inflated `repo:mypy`'s +apparent finding count with spurious "not defined"/"attr-defined" noise from +unresolved imports (numpy, mlx, fastapi) — an environment artifact, not a real +regression. The pre-commit run, with the properly-installed venv, is the authoritative +measurement and is what every ratchet file and the CHANGELOG/LEDGER numbers above +report. Recorded here rather than silently reported as if the degraded re-run's larger +numbers were real. From 0ea471ae998b220f33cc4c06cd55e85127b121e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 14:28:36 +0000 Subject: [PATCH 3/4] docs(ledger): record a fifth kiban-side finding from re-verification repo:mypy/repo:vulture's net-new dispatch reports far more findings on this PR's real diff than any direct single-pass measurement of HEAD ever found. Confirmed with a properly-installed venv (ruling out the earlier environment-degradation explanation) -- this is kiban's own worktree/ import-resolution mechanics, not a squish defect. Disclosed here so the PR description doesn't need to hide that this PR's own first konjo-gates run will show red on these two checks. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01NLdTtZgWyLWu2gE9CohWwa --- LEDGER.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/LEDGER.md b/LEDGER.md index ede648b0..22e7dd28 100644 --- a/LEDGER.md +++ b/LEDGER.md @@ -192,3 +192,24 @@ regression. The pre-commit run, with the properly-installed venv, is the authori measurement and is what every ratchet file and the CHANGELOG/LEDGER numbers above report. Recorded here rather than silently reported as if the degraded re-run's larger numbers were real. + +**A fifth kiban-side limitation, found re-verifying with a properly-installed venv +(ruling out the environment-degradation explanation above): `repo:mypy` and +`repo:vulture`'s net-new dispatch reports far more "net-new" findings against this +PR's real 25-file diff (110 and 59) than any direct, single-pass measurement of HEAD +ever found (215 total mypy errors repo-wide, 108 total vulture findings repo-wide — +this diff touches 5 Python files, so 110/59 "new" findings on a docstring-and-two- +one-liners diff cannot be real). The two-pass mechanism scans HEAD and a separate +`git worktree` checkout of the base commit; the leading hypothesis (not fully root- +caused — this is kiban's own internal worktree/import-resolution mechanics, out of +scope for a "connect squish to kiban" sprint to debug) is that mypy resolves the +editable-installed `squish` package from its real site-packages redirect rather than +from the base worktree's own checkout, so files in the base pass get analyzed against +the WRONG (HEAD's) module contents, manufacturing a large, spurious diff. Because +`repo:mypy`/`repo:vulture`/`repo:ruff`/`repo:ruff-format` have no advisory/soft +setting in `konjo-gates` (unlike `polarity`/`claude_contract`), this PR's own first +`konjo-gates.yml` CI run will show these as real FAILs — disclosed here and in the PR +description rather than hidden, worked around, or silently re-ordered to dodge them. +This, the `repo:ruff` stdout-diffing issue, and the `one_way_door` regex false +positive are three upstream `konjoai/kiban` findings this connection sprint surfaced, +all real, none squish-side defects. From 2817148ccdb013f64262cdd3060fa2fde9035c8d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 14:32:54 +0000 Subject: [PATCH 4/4] fix(ci): unblock DRY ratchet and update stale hardcoded-version tests dry_check.py exits 1 on any violation by default, so the G4 DRY step never reached ratchet_check.py's comparison against the seeded 99-violation ceiling. Add --warn-only so the ratchet actually gates on regression instead of the full pre-existing backlog, as designed. tests/test_version.py and tests/test_wave79_startup_inference.py pin a literal version string; this PR's 9.34.14 -> 9.34.15 bump left both at the old value. --- .github/workflows/konjo-gate.yml | 1 + tests/test_version.py | 2 +- tests/test_wave79_startup_inference.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/konjo-gate.yml b/.github/workflows/konjo-gate.yml index 10a52f93..72d32134 100644 --- a/.github/workflows/konjo-gate.yml +++ b/.github/workflows/konjo-gate.yml @@ -246,6 +246,7 @@ jobs: python3 .konjo/scripts/dry_check.py \ --threshold 0.85 \ --min-lines 20 \ + --warn-only \ --report dry_report.json 2>&1 COUNT=$(python3 -c "import json; d=json.load(open('dry_report.json')); print(d['count'])") python3 .konjo/scripts/ratchet_check.py \ diff --git a/tests/test_version.py b/tests/test_version.py index f712bb66..15b79736 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -18,7 +18,7 @@ import pytest # Pinned expected version — update this whenever pyproject.toml version changes. -EXPECTED_VERSION = "9.34.14" +EXPECTED_VERSION = "9.34.15" class TestVersionConsistency: diff --git a/tests/test_wave79_startup_inference.py b/tests/test_wave79_startup_inference.py index f601675e..0425a5b7 100644 --- a/tests/test_wave79_startup_inference.py +++ b/tests/test_wave79_startup_inference.py @@ -33,7 +33,7 @@ class TestVersionConsistency(unittest.TestCase): def test_version_is_9_34_8(self): import squish - self.assertEqual(squish.__version__, "9.34.14") + self.assertEqual(squish.__version__, "9.34.15") def test_version_is_string(self): import squish