12f88f32c1
Phase 3 Task 19 partial — coverage announcement §4.9 deferred to a
separate commit (touches Pravila §17, requires §15.2 pre-flight sync).
- tools/brain-retro-sanity-generator.mjs (NEW, pure):
generateCandidateQuestions(episodes) returns ≤5 sanity questions
derived from per-classification volume (>10 episodes per task type
triggers a themed question: bugfix/feature/planning/refactor/security/
marketing) plus 2 meta questions about missed activations / direct
bypass. Reads task_type from classifier_output (v4) with fallback
to primary_rationale.task_classification (v2/v3). Spec §4.7.
- tools/brain-retro-sanity-generator.test.mjs (NEW): 6 tests
(bugfix >10 / feature >10 / max 5 / empty / legacy v2/v3 / strings).
- .claude/skills/brain-retro/SKILL.md:
+ description rewritten — "раз в 1-2 недели OR sanity-check threshold"
(cadence change per spec §4.7).
+ procedure +steps 5a (sanity questions via AskUserQuestion +
PII filter + sanity-checks/YYYY-MM-DD.json), 5b (reviewer-agent
Task() spawn + fallback to brain-retro-opus-reviewer.mjs), 9
(self-retrospect threshold check), 10 (cost report from
~/.claude/runtime/cost-daily.json), 11 (richer summary).
- .claude/skills/self-retrospect/SKILL.md (NEW) — stub skill;
full procedure wired in Task 20 (analyzer + STATUS.md surface the
threshold).
- docs/observer/.self-retrospect-counter.json (NEW): initial state
{last_run_at: null, episodes_since_last: 0}.
- docs/observer/sanity-checks/.gitkeep (NEW): directory placeholder
for sanity-answers JSON files.
Tests: 608 passed / 0 failed (+15 from Task 19 + prior). 4 pre-existing
file fails unchanged. Coverage announcement §4.9 (economy-mode.py +
Pravila §17 subsection + feedback memory + coverage-annotation-mode
flag) — deferred: touches Pravila which is in the §15.2 8-file SoT
list and needs pre-flight `git fetch origin && git log HEAD..origin/main`
before edit; flagging as Phase 3 follow-up commit.
41 lines
1.8 KiB
JavaScript
41 lines
1.8 KiB
JavaScript
// tools/brain-retro-sanity-generator.test.mjs — Phase 3 Task 19 (spec §4.7)
|
|
import { describe, it, expect } from 'vitest';
|
|
import { generateCandidateQuestions } from './brain-retro-sanity-generator.mjs';
|
|
|
|
describe('generateCandidateQuestions — sanity-check candidates (spec §4.7)', () => {
|
|
it('emits a bugfix-themed question when bugfix volume > 10', () => {
|
|
const eps = Array(11).fill({ classifier_output: { task_type: 'bugfix' } });
|
|
const qs = generateCandidateQuestions(eps);
|
|
expect(qs.some((q) => /баг|debug/i.test(q))).toBe(true);
|
|
});
|
|
|
|
it('emits a feature-themed question when feature volume > 10', () => {
|
|
const eps = Array(12).fill({ classifier_output: { task_type: 'feature' } });
|
|
const qs = generateCandidateQuestions(eps);
|
|
expect(qs.some((q) => /фич|feature/i.test(q))).toBe(true);
|
|
});
|
|
|
|
it('never returns more than 5 candidate questions', () => {
|
|
const eps = Array(50).fill({ classifier_output: { task_type: 'bugfix' } });
|
|
expect(generateCandidateQuestions(eps).length).toBeLessThanOrEqual(5);
|
|
});
|
|
|
|
it('returns at most 5 even on empty input (defensive default)', () => {
|
|
expect(generateCandidateQuestions([]).length).toBeLessThanOrEqual(5);
|
|
});
|
|
|
|
it('handles legacy v2/v3 episodes (primary_rationale.task_classification fallback)', () => {
|
|
const eps = Array(11).fill({ schema_version: 3, primary_rationale: { task_classification: 'bugfix' } });
|
|
const qs = generateCandidateQuestions(eps);
|
|
expect(qs.some((q) => /баг|debug/i.test(q))).toBe(true);
|
|
});
|
|
|
|
it('always returns strings', () => {
|
|
const eps = Array(5).fill({ classifier_output: { task_type: 'feature' } });
|
|
for (const q of generateCandidateQuestions(eps)) {
|
|
expect(typeof q).toBe('string');
|
|
expect(q.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|