2026-05-19 06:37:27 +03:00
import { describe , it , expect } from 'vitest' ;
2026-05-27 06:46:55 +03:00
import { renderStatus , computeCostBlock , computeAnomalyBlock , computeSelfRetrospectBlock , computeReviewerBlock , computeReviewerFindingsBlock , computeSessionLengthBlock } from './status-md-generator.mjs' ;
2026-05-19 06:37:27 +03:00
2026-05-28 10:19:24 +03:00
// brain-retro #9 candidate 5 (2026-05-28): surface long-running system processes in STATUS.md.
// Block is passed as pre-computed string `systemHealthBlock` in inputs — same wiring pattern
// as sessionLengthBlock, costBlock, etc. (no I/O in renderStatus).
describe ( 'renderStatus — systemHealthBlock (brain-retro #9 candidate 5)' , ( ) => {
const minInputs = {
now : '2026-05-28T10:00:00Z' ,
c1 : { status : 'ok' , detail : 'OK' } ,
c2 : { status : 'ok' , detail : 'OK' } ,
c3 : { status : 'ok' , detail : 'OK' } ,
c5 : { status : 'ok' , detail : 'OK' } ,
c6 : { status : 'ok' , detail : 'OK' } ,
observer : { episodeCount : 5 , observerErrors : 0 , piiMatches : 0 , v1Episodes : 0 } ,
missed : { totalMissed : 0 , byNode : { } , byClassification : { } } ,
} ;
it ( 'renders systemHealthBlock when provided as string' , ( ) => {
2026-05-28 10:29:13 +03:00
const block = '## System Health\n\nТоп-3 процессов с CPU > 1ч:\n\n| PID | Имя | CPU-время | Возраст |\n|---|---|---|---|\n| 6444 | python | 7.07ч | 13.6ч |\n\n⚠️ Проверь, не «осиротевшие» ли это процессы.\n' ;
2026-05-28 10:19:24 +03:00
const md = renderStatus ( { ... minInputs , systemHealthBlock : block } ) ;
2026-05-28 10:29:13 +03:00
expect ( md ) . toContain ( '## System Health' ) ;
2026-05-28 10:19:24 +03:00
expect ( md ) . toContain ( '6444' ) ;
expect ( md ) . toContain ( 'python' ) ;
} ) ;
it ( 'omits systemHealthBlock when absent (backward compat)' , ( ) => {
const md = renderStatus ( minInputs ) ;
2026-05-28 10:29:13 +03:00
// Should not contain System Health section
expect ( md ) . not . toContain ( '## System Health' ) ;
2026-05-28 10:19:24 +03:00
} ) ;
it ( 'renders "Долго работающих процессов нет" variant correctly' , ( ) => {
2026-05-28 10:29:13 +03:00
const block = '## System Health\n\nДолго работающих процессов нет (порог CPU > 1ч).\n' ;
2026-05-28 10:19:24 +03:00
const md = renderStatus ( { ... minInputs , systemHealthBlock : block } ) ;
expect ( md ) . toContain ( 'Долго работающих процессов нет' ) ;
} ) ;
} ) ;
2026-05-19 10:41:17 +03:00
const baseInputs = ( overrides = { } ) => ( {
now : '2026-05-19T10:00:00+03:00' ,
c1 : { status : 'ok' , detail : 'no drift' } ,
c2 : { status : 'ok' , detail : '0 version drift' } ,
c3 : { status : 'ok' , detail : 'last read today' } ,
c5 : { status : 'ok' , detail : 'coverage OK · registration OK' } ,
2026-05-21 04:54:00 +03:00
c6 : { status : 'ok' , detail : '14 chains in sync' } ,
2026-05-19 10:41:17 +03:00
observer : { episodeCount : 12 , observerErrors : 0 , piiMatches : 0 } ,
2026-05-21 08:38:21 +03:00
missed : { totalMissed : 0 , byNode : { } , byClassification : { } } ,
2026-05-19 10:41:17 +03:00
... overrides ,
} ) ;
2026-05-19 06:37:27 +03:00
describe ( 'renderStatus' , ( ) => {
2026-05-19 10:41:17 +03:00
it ( 'renders all 5 controllers + metrics' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
2026-05-19 06:37:27 +03:00
expect ( md ) . toContain ( '# Brain Status' ) ;
expect ( md ) . toContain ( '| C1 L1-watcher | ✅' ) ;
expect ( md ) . toContain ( '| C2 Cross-ref consistency | ✅' ) ;
expect ( md ) . toContain ( '| C3 Observer-of-observer | ✅' ) ;
expect ( md ) . toContain ( '| C4 Сигнальный статус | ✅' ) ;
2026-05-19 10:41:17 +03:00
expect ( md ) . toContain ( '| C5 Observer-coverage | ✅' ) ;
2026-05-19 06:37:27 +03:00
expect ( md ) . toContain ( '12 episodes' ) ;
} ) ;
2026-05-21 04:54:00 +03:00
it ( 'includes a C6 chain-map row' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
expect ( md ) . toContain ( '| C6 Chain map sync | ✅' ) ;
} ) ;
2026-05-19 10:41:17 +03:00
it ( 'shows a warn status for the coverage controller' , ( ) => {
const md = renderStatus ( baseInputs ( { c5 : { status : 'warn' , detail : '3 commits, 0 episodes' } } ) ) ;
expect ( md ) . toContain ( '| C5 Observer-coverage | ⚠️' ) ;
} ) ;
it ( 'shows the observer_error count in the metrics block' , ( ) => {
const md = renderStatus ( baseInputs ( { observer : { episodeCount : 4 , observerErrors : 2 , piiMatches : 0 } } ) ) ;
expect ( md ) . toContain ( '2 observer_error markers' ) ;
} ) ;
it ( 'shows a red status for failing controllers' , ( ) => {
const md = renderStatus ( baseInputs ( { c1 : { status : 'fail' , detail : '2 plugins not formalized' } } ) ) ;
expect ( md ) . toContain ( '| C1 L1-watcher | 🔴' ) ;
2026-05-19 06:37:27 +03:00
} ) ;
2026-05-21 08:38:21 +03:00
it ( 'mentions the conditional capability-readiness behavioral rule (§16.4 v1.36)' , ( ) => {
2026-05-19 10:41:17 +03:00
const md = renderStatus ( baseInputs ( ) ) ;
2026-05-21 08:38:21 +03:00
expect ( md ) . toContain ( 'Неиспользованные узлы — не алерт' ) ;
expect ( md ) . toContain ( 'если профильной задачи не было' ) ;
2026-05-19 06:37:27 +03:00
expect ( md ) . toContain ( 'feedback_brain_unused_tools_not_problem' ) ;
} ) ;
2026-05-20 12:51:28 +03:00
it ( 'shows piiMatches > 0 when counter file has data (Task 3)' , ( ) => {
const md = renderStatus ( baseInputs ( { observer : { episodeCount : 24 , observerErrors : 0 , piiMatches : 7 } } ) ) ;
expect ( md ) . toMatch ( /7 PII matches before filter/ ) ;
} ) ;
2026-05-19 06:37:27 +03:00
} ) ;
2026-05-20 13:19:22 +03:00
describe ( 'renderStatus — last /brain-retro (Task 10)' , ( ) => {
it ( 'shows last /brain-retro days-ago when counter has data' , ( ) => {
const md = renderStatus ( baseInputs ( { lastRetroDaysAgo : 3 } ) ) ;
expect ( md ) . toMatch ( /Last \/brain-retro:\s*3 day\(s\) ago/ ) ;
} ) ;
it ( 'shows "never" when lastRetroDaysAgo is null' , ( ) => {
const md = renderStatus ( baseInputs ( { lastRetroDaysAgo : null } ) ) ;
expect ( md ) . toMatch ( /Last \/brain-retro:\s*never/ ) ;
} ) ;
it ( 'shows "never" when lastRetroDaysAgo is undefined' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
expect ( md ) . toMatch ( /Last \/brain-retro:\s*never/ ) ;
} ) ;
} ) ;
2026-05-20 13:26:29 +03:00
describe ( 'renderStatus — v1 episodes count surface (Task 18)' , ( ) => {
it ( 'shows v1 count when present' , ( ) => {
const md = renderStatus ( baseInputs ( { observer : { episodeCount : 22 , observerErrors : 0 , piiMatches : 0 , v1Episodes : 5 } } ) ) ;
expect ( md ) . toMatch ( /Legacy v1 episodes \(not in factor analysis\):\s*5/ ) ;
} ) ;
it ( 'shows 0 when v1Episodes undefined' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
expect ( md ) . toMatch ( /Legacy v1 episodes \(not in factor analysis\):\s*0/ ) ;
} ) ;
} ) ;
2026-05-21 08:38:21 +03:00
describe ( 'renderStatus — missed activations (Task 7, Pravila §16.4 v1.36)' , ( ) => {
it ( 'renders missed_activations: 0 when there are no misses' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
expect ( md ) . toContain ( 'missed_activations: 0' ) ;
} ) ;
it ( 'renders missed_activations: N when misses occur' , ( ) => {
const md = renderStatus ( baseInputs ( {
missed : { totalMissed : 3 , byNode : { '#11' : 2 , '#12' : 1 } , byClassification : { refactor : 3 } } ,
} ) ) ;
expect ( md ) . toContain ( 'missed_activations: 3' ) ;
} ) ;
it ( 'keeps C5 ✅ when controller is ok and no misses' , ( ) => {
const md = renderStatus ( baseInputs ( ) ) ;
expect ( md ) . toContain ( '| C5 Observer-coverage | ✅' ) ;
} ) ;
it ( 'honors the c5 status override (warn) regardless of missed count' , ( ) => {
const md = renderStatus ( baseInputs ( {
c5 : { status : 'warn' , detail : '16 missed activation(s)' } ,
} ) ) ;
expect ( md ) . toContain ( '| C5 Observer-coverage | ⚠️' ) ;
} ) ;
} ) ;
2026-05-24 07:03:41 +03:00
describe ( 'renderStatus — discipline block (stage 2)' , ( ) => {
const baseInputs = {
now : '2026-05-24T10:00:00Z' ,
c1 : { status : 'ok' , detail : 'OK' } ,
c2 : { status : 'ok' , detail : 'OK' } ,
c3 : { status : 'ok' , detail : 'OK' } ,
c5 : { status : 'ok' , detail : 'OK' } ,
c6 : { status : 'ok' , detail : 'OK' } ,
observer : { episodeCount : 10 , observerErrors : 0 , piiMatches : 0 , v1Episodes : 0 } ,
missed : { totalMissed : 0 , byNode : { } , byClassification : { } } ,
lastRetroDaysAgo : 0 ,
} ;
it ( 'renders discipline table when discipline data is provided' , ( ) => {
const md = renderStatus ( {
... baseInputs ,
discipline : {
byClassification : {
feature : { episodes : 5 , withTriggerMatch : 0 , viaSkill : 0 , pctTriggerMatch : 0 , pctViaSkill : 0 } ,
bugfix : { episodes : 6 , withTriggerMatch : 2 , viaSkill : 2 , pctTriggerMatch : 0.333 , pctViaSkill : 0.333 } ,
} ,
routerStep : { distribution : { '1' : 10 , '3' : 1 } , total : 11 , suspicious : true } ,
boundariesRate : { total : 11 , withBoundaries : 3 , rate : 0.273 , byPathType : { } } ,
} ,
} ) ;
expect ( md ) . toMatch ( /## Метрики дисциплины/ ) ;
expect ( md ) . toMatch ( /feature/ ) ;
expect ( md ) . toMatch ( /bugfix/ ) ;
expect ( md ) . toMatch ( /33\.3%/ ) ;
expect ( md ) . toMatch ( /router step distribution/i ) ;
expect ( md ) . toMatch ( /⚠️.*suspicious/i ) ;
expect ( md ) . toMatch ( /boundaries applied/i ) ;
expect ( md ) . toMatch ( /27\.3%/ ) ;
} ) ;
it ( 'omits the discipline block when discipline is absent (backward compat)' , ( ) => {
const md = renderStatus ( baseInputs ) ;
expect ( md ) . not . toMatch ( /## Метрики дисциплины/ ) ;
} ) ;
2026-05-26 11:48:16 +03:00
it ( 'coexists: both sessionLengthBlock (brain-retro candidate B) and overrideUsageBlock (enforce hole 8) appear together in template after merge' , ( ) => {
const md = renderStatus ( {
... baseInputs ,
sessionLengthBlock : '## Длинные сессии\n\nflagged content' ,
overrideUsageBlock : '## Использование override-фраз\n\nflagged content' ,
} ) ;
expect ( md ) . toContain ( '## Длинные сессии' ) ;
expect ( md ) . toContain ( '## Использование override-фраз' ) ;
} ) ;
2026-05-24 07:03:41 +03:00
} ) ;
2026-05-25 13:59:40 +03:00
// ── Phase 3 deferred #3: 4 new helper blocks ─────────────────────────────────
const PRICING _TEST = {
sonnet46 : { input _per _mtok : 3.0 , output _per _mtok : 15.0 } ,
opus47 : { input _per _mtok : 15.0 , output _per _mtok : 75.0 } ,
} ;
const makeEp = ( overrides = { } ) => ( {
schema _version : 2 ,
task _cost : {
classifier _input _tokens : 0 ,
classifier _output _tokens : 0 ,
self _assessment _input _tokens : 0 ,
self _assessment _output _tokens : 0 ,
reviewer _subagent _usd : null ,
reviewer _input _tokens : 0 ,
reviewer _output _tokens : 0 ,
reviewer _direct _fallback _usd : null ,
} ,
review : { reviewed _at : null , reviewer : null , reviewer _error : false } ,
... overrides ,
} ) ;
describe ( 'computeCostBlock' , ( ) => {
it ( 'sums token costs and formats USD for 3 episodes' , ( ) => {
const eps = [
makeEp ( { task _cost : { classifier _input _tokens : 1000 , classifier _output _tokens : 200 , self _assessment _input _tokens : 500 , self _assessment _output _tokens : 100 , reviewer _subagent _usd : null , reviewer _input _tokens : 0 , reviewer _output _tokens : 0 , reviewer _direct _fallback _usd : null } } ) ,
makeEp ( { task _cost : { classifier _input _tokens : 2000 , classifier _output _tokens : 400 , self _assessment _input _tokens : 1000 , self _assessment _output _tokens : 200 , reviewer _subagent _usd : 0.01 , reviewer _input _tokens : 500 , reviewer _output _tokens : 100 , reviewer _direct _fallback _usd : null } } ) ,
makeEp ( { task _cost : { classifier _input _tokens : 500 , classifier _output _tokens : 100 , self _assessment _input _tokens : 250 , self _assessment _output _tokens : 50 , reviewer _subagent _usd : null , reviewer _input _tokens : 0 , reviewer _output _tokens : 0 , reviewer _direct _fallback _usd : 0.005 } } ) ,
] ;
const block = computeCostBlock ( eps , PRICING _TEST ) ;
expect ( block ) . toMatch ( /## Стоимость месяца/ ) ;
expect ( block ) . toMatch ( /Classifier/ ) ;
expect ( block ) . toMatch ( /Self-assessment/ ) ;
expect ( block ) . toMatch ( /Reviewer/ ) ;
expect ( block ) . toMatch ( /\$\d+\.\d{2}/ ) ;
} ) ;
it ( 'returns a block with zeros when episodes array is empty' , ( ) => {
const block = computeCostBlock ( [ ] , PRICING _TEST ) ;
expect ( block ) . toMatch ( /## Стоимость месяца/ ) ;
expect ( block ) . toMatch ( /\$0\.00/ ) ;
} ) ;
} ) ;
describe ( 'computeAnomalyBlock' , ( ) => {
it ( 'returns "Аномалий нет." when all outputs are within threshold' , ( ) => {
const eps = [
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 100 } } ) ,
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 120 } } ) ,
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 110 } } ) ,
] ;
const block = computeAnomalyBlock ( eps ) ;
expect ( block ) . toMatch ( /## Аномалии классификатора/ ) ;
expect ( block ) . toMatch ( /Аномалий нет\./ ) ;
} ) ;
it ( 'lists the outlier episode when one exceeds threshold' , ( ) => {
const eps = [
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 100 } } ) ,
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 100 } } ) ,
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 100 } } ) ,
makeEp ( { task _cost : { ... makeEp ( ) . task _cost , classifier _output _tokens : 50000 } } ) ,
] ;
const block = computeAnomalyBlock ( eps ) ;
expect ( block ) . toMatch ( /## Аномалии классификатора/ ) ;
expect ( block ) . toMatch ( /50000/ ) ;
} ) ;
} ) ;
describe ( 'computeSelfRetrospectBlock' , ( ) => {
it ( 'returns block with days-ago and no warning when under threshold' , ( ) => {
const fakeFs = {
existsSync : ( ) => true ,
readFileSync : ( ) => JSON . stringify ( { last _run _at : new Date ( Date . now ( ) - 2 * 86400000 ) . toISOString ( ) , episodes _since _last : 3 , threshold : 10 } ) ,
} ;
const block = computeSelfRetrospectBlock ( 'fake/path.json' , fakeFs ) ;
expect ( block ) . toMatch ( /## Авто-ретроспектива/ ) ;
expect ( block ) . toMatch ( /2 day\(s\) ago/ ) ;
expect ( block ) . not . toMatch ( /⚠️/ ) ;
} ) ;
it ( 'adds warning when episodes_since_last >= threshold' , ( ) => {
const fakeFs = {
existsSync : ( ) => true ,
readFileSync : ( ) => JSON . stringify ( { last _run _at : new Date ( Date . now ( ) - 5 * 86400000 ) . toISOString ( ) , episodes _since _last : 15 , threshold : 10 } ) ,
} ;
const block = computeSelfRetrospectBlock ( 'fake/path.json' , fakeFs ) ;
expect ( block ) . toMatch ( /⚠️/ ) ;
expect ( block ) . toMatch ( /15/ ) ;
} ) ;
it ( 'returns "never" when counter file is missing' , ( ) => {
const fakeFs = { existsSync : ( ) => false } ;
const block = computeSelfRetrospectBlock ( 'fake/path.json' , fakeFs ) ;
expect ( block ) . toMatch ( /## Авто-ретроспектива/ ) ;
expect ( block ) . toMatch ( /never/ ) ;
} ) ;
} ) ;
describe ( 'computeReviewerBlock' , ( ) => {
it ( 'shows subagent and fallback counts with percentages' , ( ) => {
const eps = [
makeEp ( { review : { reviewed _at : '2026-05-01T00:00:00Z' , reviewer : 'subagent-opus-4-7' , reviewer _error : false } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-02T00:00:00Z' , reviewer : 'subagent-opus-4-7' , reviewer _error : false } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-03T00:00:00Z' , reviewer : 'direct-opus-fallback' , reviewer _error : false } } ) ,
makeEp ( { review : { reviewed _at : null , reviewer : null , reviewer _error : false } } ) ,
] ;
const block = computeReviewerBlock ( eps ) ;
expect ( block ) . toMatch ( /## Reviewer: субагент vs fallback/ ) ;
expect ( block ) . toMatch ( /subagent-opus-4-7/ ) ;
expect ( block ) . toMatch ( /direct-opus-fallback/ ) ;
expect ( block ) . toMatch ( /\d+%/ ) ;
} ) ;
it ( 'shows fallback message when no episodes were reviewed' , ( ) => {
const eps = [
makeEp ( ) ,
makeEp ( ) ,
] ;
const block = computeReviewerBlock ( eps ) ;
expect ( block ) . toMatch ( /## Reviewer: субагент vs fallback/ ) ;
expect ( block ) . toMatch ( /0 эпизодов проверено/ ) ;
} ) ;
} ) ;
2026-05-27 06:46:55 +03:00
// brain-retro #7 C4 (2026-05-27): surface reviewer findings (wrong_skill,
// wrong_chain_order, alternative suggestions) directly in STATUS.md so the
// owner doesn't have to run /brain-retro to see them.
describe ( 'computeReviewerFindingsBlock (C4 — brain-retro #7)' , ( ) => {
it ( 'counts error_root_cause and alternative_better across reviewed episodes' , ( ) => {
const eps = [
makeEp ( { review : { reviewed _at : '2026-05-26T17:00:00Z' , error _root _cause : 'wrong_skill' , alternative _better : '#3' , node _quality : 'wrong_node' } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-26T17:10:00Z' , error _root _cause : 'wrong_skill' , alternative _better : '#3' , node _quality : 'disputable' } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-26T17:20:00Z' , error _root _cause : 'wrong_chain_order' , alternative _better : '#8' , node _quality : 'disputable' } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-26T17:30:00Z' , error _root _cause : 'external_failure' , alternative _better : null , node _quality : 'correct' } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-26T17:40:00Z' , error _root _cause : 'n/a' , alternative _better : null , node _quality : 'correct' } } ) ,
makeEp ( { review : { reviewed _at : null } } ) , // not reviewed — excluded
] ;
const block = computeReviewerFindingsBlock ( eps ) ;
expect ( block ) . toMatch ( /Reviewer findings/ ) ;
expect ( block ) . toMatch ( /wrong_skill.*2/ ) ;
expect ( block ) . toMatch ( /wrong_chain_order.*1/ ) ;
expect ( block ) . toMatch ( /external_failure.*1/ ) ;
// Top alternatives: #3 (x2), #8 (x1)
expect ( block ) . toMatch ( /#3.*2/ ) ;
expect ( block ) . toMatch ( /#8.*1/ ) ;
} ) ;
it ( 'emits "никаких находок" when 0 reviewed episodes with actionable findings' , ( ) => {
const eps = [ makeEp ( ) , makeEp ( ) ] ;
const block = computeReviewerFindingsBlock ( eps ) ;
expect ( block ) . toMatch ( /Reviewer findings/ ) ;
expect ( block ) . toMatch ( /нет проверенных/i ) ;
} ) ;
// batch-reviewer (tools/brain-retro-batch-reviewer.mjs) writes
// `outcome_reviewed_source: "direct_api_batch"` at episode level and the
// review object lacks `reviewed_at`. Detect via either signal.
it ( 'detects batch-reviewed episodes via outcome_reviewed_source (no reviewed_at)' , ( ) => {
const eps = [
{ ... makeEp ( ) , outcome _reviewed _source : 'direct_api_batch' , review : { error _root _cause : 'wrong_skill' , alternative _better : '#3' , node _quality : 'wrong_node' } } ,
{ ... makeEp ( ) , outcome _reviewed _source : 'direct_api_batch' , review : { error _root _cause : 'n/a' , alternative _better : null , node _quality : 'correct' } } ,
] ;
const block = computeReviewerFindingsBlock ( eps ) ;
expect ( block ) . toMatch ( /Reviewer findings/ ) ;
expect ( block ) . toMatch ( /wrong_skill.*1/ ) ;
} ) ;
it ( 'emits "всё чисто" when reviewed but no wrong_skill / wrong_chain_order' , ( ) => {
const eps = [
makeEp ( { review : { reviewed _at : '2026-05-26T17:00:00Z' , error _root _cause : 'n/a' , alternative _better : null , node _quality : 'correct' } } ) ,
makeEp ( { review : { reviewed _at : '2026-05-26T17:10:00Z' , error _root _cause : 'n/a' , alternative _better : null , node _quality : 'correct' } } ) ,
] ;
const block = computeReviewerFindingsBlock ( eps ) ;
expect ( block ) . toMatch ( /Reviewer findings/ ) ;
expect ( block ) . toMatch ( /всё чисто|без проблем/i ) ;
} ) ;
} ) ;
// brain-retro #7 C4 follow-up: wiring extra blocks risked introducing
// markdownlint MD012 (multiple consecutive blank lines) by chaining
// `\n${block}\n` with blocks whose templates end with `\n`. Guard.
describe ( 'renderStatus — no double-blank-lines (MD012 lint guard)' , ( ) => {
it ( 'does not emit "\\n\\n\\n" anywhere when all blocks chained' , ( ) => {
const inputs = {
now : '2026-05-27T04:00:00Z' ,
c1 : { status : 'ok' , detail : 'OK' } ,
c2 : { status : 'ok' , detail : 'OK' } ,
c3 : { status : 'ok' , detail : 'OK' } ,
c5 : { status : 'ok' , detail : 'OK' } ,
c6 : { status : 'ok' , detail : 'OK' } ,
observer : { episodeCount : 1 , observerErrors : 0 , piiMatches : 0 } ,
missed : { totalMissed : 0 , byNode : { } , byClassification : { } } ,
reviewerBlock : '## Reviewer: субагент vs fallback\n\n0 эпизодов проверено.\n' ,
reviewerFindingsBlock : '## Reviewer findings\n\n(нет данных)\n' ,
overrideUsageBlock : '## Использование override-фраз\n\n| ph | n |\n|---|---|\n| x | 0 |\n' ,
selfRetrospectBlock : '## Авто-ретроспектива\n\nLast: never\n' ,
costBlock : '## Cost\n\n$0\n' ,
} ;
const md = renderStatus ( inputs ) ;
expect ( md ) . not . toMatch ( /\n\n\n/ ) ;
} ) ;
} ) ;
2026-05-25 13:59:40 +03:00
describe ( 'renderStatus — 4 new optional blocks integration' , ( ) => {
const minInputs = {
now : '2026-05-25T10:00:00Z' ,
c1 : { status : 'ok' , detail : 'OK' } ,
c2 : { status : 'ok' , detail : 'OK' } ,
c3 : { status : 'ok' , detail : 'OK' } ,
c5 : { status : 'ok' , detail : 'OK' } ,
c6 : { status : 'ok' , detail : 'OK' } ,
observer : { episodeCount : 5 , observerErrors : 0 , piiMatches : 0 , v1Episodes : 0 } ,
missed : { totalMissed : 0 , byNode : { } , byClassification : { } } ,
} ;
it ( 'renders all 4 blocks when provided as strings' , ( ) => {
const md = renderStatus ( {
... minInputs ,
costBlock : '## Стоимость месяца\ncost content' ,
anomalyBlock : '## Аномалии классификатора\nanomaly content' ,
selfRetrospectBlock : '## Авто-ретроспектива\nretro content' ,
reviewerBlock : '## Reviewer: субагент vs fallback\nreviewer content' ,
} ) ;
expect ( md ) . toContain ( '## Стоимость месяца' ) ;
expect ( md ) . toContain ( '## Аномалии классификатора' ) ;
expect ( md ) . toContain ( '## Авто-ретроспектива' ) ;
expect ( md ) . toContain ( '## Reviewer: субагент vs fallback' ) ;
expect ( md ) . toContain ( '## Алерт-индикаторы' ) ;
} ) ;
it ( 'omits all 4 blocks when absent (backward compat)' , ( ) => {
const md = renderStatus ( minInputs ) ;
expect ( md ) . not . toContain ( '## Стоимость месяца' ) ;
expect ( md ) . not . toContain ( '## Аномалии классификатора' ) ;
expect ( md ) . not . toContain ( '## Авто-ретроспектива' ) ;
expect ( md ) . not . toContain ( '## Reviewer: субагент vs fallback' ) ;
} ) ;
} ) ;
2026-05-26 10:52:35 +03:00
// -----------------------------------------------------------------------------
// computeSessionLengthBlock — brain-retro #5 candidate B (2026-05-26)
// Long sessions correlate with discipline drift; surface a warning when any
// session today (UTC) has ≥50 turns.
// -----------------------------------------------------------------------------
describe ( 'computeSessionLengthBlock' , ( ) => {
const day = '2026-05-26' ;
const ep = ( turn , opts = { } ) => ( {
task _id : opts . id ? ? 'sess-1' ,
timestamps : { started _at : ` ${ opts . day ? ? day } T01:00:0 ${ turn % 10 } Z ` , ended _at : ` ${ opts . day ? ? day } T01:00:0 ${ turn % 10 } Z ` } ,
environment : { session _turn : turn } ,
path _type : opts . regulated ? 'regulated' : 'improvised' ,
} ) ;
it ( 'returns "no data" placeholder when episodes empty' , ( ) => {
expect ( computeSessionLengthBlock ( [ ] ) ) . toContain ( '(нет данных)' ) ;
} ) ;
it ( 'returns OK (✅) when no session reaches threshold' , ( ) => {
const out = computeSessionLengthBlock ( [ ep ( 1 ) , ep ( 2 ) , ep ( 10 ) ] , { now : ` ${ day } T05:00:00Z ` } ) ;
expect ( out ) . toContain ( '✅' ) ;
expect ( out ) . toContain ( 'Ни одной сессии' ) ;
} ) ;
it ( 'flags a session that crossed threshold' , ( ) => {
const eps = Array . from ( { length : 55 } , ( _ , i ) => ep ( i + 1 ) ) ;
const out = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` } ) ;
expect ( out ) . toContain ( '⚠️' ) ;
expect ( out ) . toContain ( '`sess-1' ) ;
expect ( out ) . toContain ( '55' ) ; // max turn
} ) ;
it ( 'respects custom threshold' , ( ) => {
const eps = Array . from ( { length : 15 } , ( _ , i ) => ep ( i + 1 ) ) ;
const flagged = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` , threshold : 10 } ) ;
const notFlagged = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` , threshold : 20 } ) ;
expect ( flagged ) . toContain ( '⚠️' ) ;
expect ( notFlagged ) . toContain ( '✅' ) ;
} ) ;
it ( 'ignores episodes from other UTC days' , ( ) => {
const eps = Array . from ( { length : 55 } , ( _ , i ) => ep ( i + 1 , { day : '2026-05-25' } ) ) ;
const out = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` } ) ;
expect ( out ) . toContain ( '✅' ) ; // yesterday's session not counted
} ) ;
it ( 'computes regulated % per long session' , ( ) => {
const eps = Array . from ( { length : 50 } , ( _ , i ) => ep ( i + 1 , { regulated : i < 10 } ) ) ;
const out = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` , threshold : 40 } ) ;
expect ( out ) . toContain ( '⚠️' ) ;
expect ( out ) . toContain ( '20%' ) ; // 10 regulated out of 50 = 20%
} ) ;
it ( 'handles missing session_turn / task_id gracefully' , ( ) => {
const eps = [
{ task _id : 'x' , timestamps : { started _at : ` ${ day } T01:00:00Z ` } } , // no session_turn
{ timestamps : { started _at : ` ${ day } T01:00:00Z ` } , environment : { session _turn : 60 } } , // no task_id
ep ( 70 , { id : 'real' } ) ,
] ;
const out = computeSessionLengthBlock ( eps , { now : ` ${ day } T05:00:00Z ` } ) ;
expect ( out ) . toContain ( '⚠️' ) ;
expect ( out ) . toContain ( '`real' ) ;
expect ( out ) . toContain ( '70' ) ;
} ) ;
} ) ;