Skip to content

fix: health ring never fakes a learning backslide across projects - #53

Merged
pacphi merged 1 commit into
mainfrom
fix/health-ring-per-project-learning
Jul 24, 2026
Merged

pacphi merged 1 commit into
mainfrom
fix/health-ring-per-project-learning

Conversation

@pacphi

@pacphi pacphi commented Jul 24, 2026

Copy link
Copy Markdown
Owner

The defect

ak status alarmed regression: learning rows shrank 75 → 0 after a routine ak sync — but no learning data was lost (ak x verify learning passes; the 75-pattern store is intact).

Root cause: the health-history ring is machine-global (kit.json), but learningRows is project-local — read from whatever cwd sync ran in, with an absent store recorded as a literal 0. A sync run from a project with no learning store (claude-autopilot) recorded 0, and detectRegression() compared it against the previous entry — recorded from a different project (agentic-kit, 75) — and cried backslide.

The fix

Two parts, mirroring the admin page's honesty rule (unknown is never a fabricated zero):

  1. Record honestly — sync stamps each snapshot with its project (cwd) and records learningRows: null when the store is absent.
  2. Compare honestly — detectRegression() pairs learningRows only with the most recent prior entry from the same project with a known count; other projects, legacy unstamped entries, and unknown readings are never baselines. Machine-global metrics (nativeSlots, drift, security) keep the existing last-two comparison. The alarm message now names the project.

Legacy rings need no migration: pre-fix entries have no project stamp, so they simply never serve as baselines.

Tests

6 new cases in tests/health-history.test.cjs (31 total in the file), including a direct repro of the false positive, cross-project isolation, unknown-skipping baseline search, legacy-entry handling, and Windows path labels. Full suite: 314 kit tests + cjs suites pass; eslint clean.

🤖 Generated with Claude Code

The health-history ring is machine-global (kit.json) but learningRows is
project-local (the sync cwd's .claude-flow/neural/stats.json), and an
absent store was recorded as a literal 0. A sync run from a project with
no learning store therefore compared 0 against another project's real
count and fired a false 'learning rows shrank 75 -> 0' alarm.

Two-part fix, mirroring the admin page's honesty rule (unknown is never
a fabricated zero):

- sync stamps each snapshot with its project (cwd) and records
  learningRows: null when the store is absent
- detectRegression compares learningRows only against the most recent
  prior entry from the SAME project with a KNOWN count; other projects,
  legacy unstamped entries, and unknown readings are never baselines.
  Machine-global metrics (nativeSlots, drift, security) still compare
  the last two entries as before. The alarm message now names the
  project so a real backslide says where it happened.
@pacphi
pacphi merged commit 9bbbc28 into main Jul 24, 2026
11 checks passed
@pacphi
pacphi deleted the fix/health-ring-per-project-learning branch July 24, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant