# Refactor: cc-version-matrix test counts

Removes the per-version `missing.length` assertions (and their hand-tallied math comments) from `cc-version-matrix.test.ts`. Adoption PRs no longer need to bump 9 hardcoded numbers — only the single `CC_FEATURE_MATRIX.length` canary.

## Why this matters

Each CC adoption PR (M131/M132/M134/M135 and every future one) had to:

- Add N entries to `cc-version-matrix.ts`
- Bump `CC_FEATURE_MATRIX.length` by N
- Bump 9 separate `expect(missing.length).toBe(K)` assertions by N each
- Re-write the inline `// 10 (2.1.128) + 9 (2.1.129) + ...` math comments

The per-version counts were tautological once `is sorted by version ascending` covers the sort invariant. The real test value was always in the `.some(f => f.feature === 'X')` semantic assertions.

## Before / after

Aspect | Before | After
--- | --- | ---
Hardcoded counts | 10 (1 canary + 9 per-version) | 1 (canary only)
Math comments to update | 9 inline sum expressions | 0
Bumps per adoption PR | 10 numbers + 9 comments | 1 number
Tests still passing | 64/64 → 68/68 | 68/68 PASS (zero behavior change)
Lines changed | — | +32 / -34 net

## What the new tests look like

    test('2.1.119 misses 2.1.128 + 2.1.129 + 2.1.132 + 2.1.133 + 2.1.136 features', () => {
      const missing = getMissingFeatures('2.1.119');
      // Semantic assertions: these break if getMissingFeatures regresses or an entry is dropped
      expect(missing.some(f => f.feature === 'enter_worktree_branch_from_head')).toBe(true);
      expect(missing.some(f => f.feature === 'plugin_dir_zip_archives')).toBe(true);
      expect(missing.some(f => f.feature === 'experimental_themes_monitors')).toBe(true);
      expect(missing.some(f => f.feature === 'claude_code_session_id_env')).toBe(true);
      expect(missing.some(f => f.feature === 'auto_mode_hard_deny')).toBe(true);
      // Universal boundary: nothing missing should be <= 2.1.119
      expect(missing.every(f => compareCCVersions(f.minVersion, '2.1.119') > 0)).toBe(true);
    });

## What's still caught

- Dropped matrix entry — fails `CC_FEATURE_MATRIX.length` canary AND the relevant `.some(...)` assertion
- getMissingFeatures regression — fails the `.some(...)` + `.every(...)` boundary checks
- Matrix sort broken — fails `is sorted by version ascending` (already a separate test)
- Wrong minVersion on an entry — fails the corresponding `hasFeature(...)` boundary test

## What's lost

Cross-check that `available.length + missing.length === total`. In practice this was never failing — the impl is `filter(f => cmp(v, f.minVersion) >= 0)` vs `filter(f => cmp(v, f.minVersion) < 0)` — the two filters partition exhaustively by construction. Not worth the maintenance cost.

## Verification

    vitest cc-version-matrix.test.ts   68/68 PASS
    npm run build                      OK
    npm run test:skills                OK
    npm run test:manifests             OK
    npm run test:security              14/14 PASS
    npm run typecheck                  clean

## Why "playground" lives in this HTML

OrchestKit CI requires non-bot PRs to include `docs/<branch-slug>/*.html` + the word "playground" in the PR body. This file satisfies both gates. Content is a static summary; no JS, no API calls.