principles-disciple 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADVANCED_CONFIG_ZH.md +97 -0
- package/AGENT_INSTALL.md +173 -0
- package/AGENT_INSTALL_EN.md +173 -0
- package/INSTALL.md +256 -0
- package/SKILL.md +63 -0
- package/docs/COMMAND_REFERENCE.md +76 -0
- package/docs/COMMAND_REFERENCE_EN.md +79 -0
- package/esbuild.config.js +75 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +13 -15
- package/scripts/build-web.mjs +46 -0
- package/scripts/install-dependencies.cjs +47 -0
- package/scripts/sync-plugin.mjs +802 -0
- package/scripts/verify-build.mjs +109 -0
- package/src/agents/nocturnal-dreamer.md +152 -0
- package/src/agents/nocturnal-philosopher.md +138 -0
- package/src/agents/nocturnal-reflector.md +126 -0
- package/src/agents/nocturnal-scribe.md +164 -0
- package/src/commands/capabilities.ts +85 -0
- package/{dist/commands/context.js → src/commands/context.ts} +78 -38
- package/src/commands/evolution-status.ts +146 -0
- package/src/commands/export.ts +111 -0
- package/src/commands/focus.ts +533 -0
- package/src/commands/nocturnal-review.ts +311 -0
- package/src/commands/nocturnal-rollout.ts +763 -0
- package/src/commands/nocturnal-train.ts +1002 -0
- package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
- package/src/commands/principle-rollback.ts +27 -0
- package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
- package/src/commands/samples.ts +60 -0
- package/src/commands/strategy.ts +38 -0
- package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
- package/src/commands/workflow-debug.ts +128 -0
- package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
- package/src/config/errors.ts +163 -0
- package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
- package/src/constants/diagnostician.ts +66 -0
- package/src/constants/tools.ts +62 -0
- package/src/core/adaptive-thresholds.ts +476 -0
- package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
- package/{dist/core/config.js → src/core/config.ts} +158 -46
- package/src/core/control-ui-db.ts +435 -0
- package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
- package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
- package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
- package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
- package/src/core/empathy-keyword-matcher.ts +327 -0
- package/src/core/empathy-types.ts +218 -0
- package/src/core/event-log.ts +544 -0
- package/src/core/evolution-engine.ts +612 -0
- package/src/core/evolution-logger.ts +353 -0
- package/src/core/evolution-migration.ts +77 -0
- package/src/core/evolution-reducer.ts +731 -0
- package/src/core/evolution-types.ts +456 -0
- package/src/core/external-training-contract.ts +527 -0
- package/src/core/focus-history.ts +1458 -0
- package/src/core/hygiene/tracker.ts +117 -0
- package/{dist/core/init.js → src/core/init.ts} +39 -26
- package/src/core/local-worker-routing.ts +617 -0
- package/{dist/core/migration.js → src/core/migration.ts} +18 -11
- package/src/core/model-deployment-registry.ts +722 -0
- package/src/core/model-training-registry.ts +813 -0
- package/src/core/nocturnal-arbiter.ts +706 -0
- package/src/core/nocturnal-candidate-scoring.ts +392 -0
- package/src/core/nocturnal-compliance.ts +1075 -0
- package/src/core/nocturnal-dataset.ts +668 -0
- package/src/core/nocturnal-executability.ts +428 -0
- package/src/core/nocturnal-export.ts +390 -0
- package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
- package/src/core/nocturnal-trajectory-extractor.ts +484 -0
- package/src/core/nocturnal-trinity.ts +1384 -0
- package/src/core/pain.ts +122 -0
- package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
- package/{dist/core/paths.js → src/core/paths.ts} +13 -4
- package/src/core/principle-training-state.ts +450 -0
- package/src/core/profile.ts +226 -0
- package/src/core/promotion-gate.ts +822 -0
- package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
- package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +185 -63
- package/src/core/shadow-observation-registry.ts +534 -0
- package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
- package/src/core/thinking-models.ts +217 -0
- package/src/core/training-program.ts +630 -0
- package/src/core/trajectory-types.ts +243 -0
- package/src/core/trajectory.ts +1673 -0
- package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
- package/src/hooks/bash-risk.ts +171 -0
- package/src/hooks/edit-verification.ts +295 -0
- package/src/hooks/gate-block-helper.ts +160 -0
- package/src/hooks/gate.ts +210 -0
- package/src/hooks/gfi-gate.ts +177 -0
- package/src/hooks/lifecycle.ts +326 -0
- package/{dist/hooks/llm.js → src/hooks/llm.ts} +166 -139
- package/src/hooks/message-sanitize.ts +45 -0
- package/src/hooks/pain.ts +384 -0
- package/src/hooks/progressive-trust-gate.ts +174 -0
- package/src/hooks/prompt.ts +920 -0
- package/src/hooks/subagent.ts +207 -0
- package/src/hooks/thinking-checkpoint.ts +73 -0
- package/src/hooks/trajectory-collector.ts +290 -0
- package/src/http/principles-console-route.ts +716 -0
- package/src/i18n/commands.ts +117 -0
- package/src/index.ts +694 -0
- package/src/service/central-database.ts +831 -0
- package/src/service/control-ui-query-service.ts +888 -0
- package/src/service/evolution-query-service.ts +405 -0
- package/src/service/evolution-worker.ts +1646 -0
- package/src/service/health-query-service.ts +836 -0
- package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +263 -36
- package/src/service/nocturnal-service.ts +1015 -0
- package/src/service/nocturnal-target-selector.ts +532 -0
- package/src/service/phase3-input-filter.ts +237 -0
- package/src/service/runtime-summary-service.ts +757 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
- package/src/service/subagent-workflow/index.ts +51 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
- package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
- package/src/service/subagent-workflow/types.ts +378 -0
- package/src/service/subagent-workflow/workflow-store.ts +328 -0
- package/src/service/trajectory-service.ts +15 -0
- package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
- package/src/tools/deep-reflect.ts +349 -0
- package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
- package/src/types/event-types.ts +453 -0
- package/src/types/hygiene-types.ts +31 -0
- package/src/types/principle-tree-schema.ts +244 -0
- package/src/types/runtime-summary.ts +49 -0
- package/src/types.ts +74 -0
- package/src/utils/file-lock.ts +391 -0
- package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
- package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
- package/src/utils/io.ts +110 -0
- package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
- package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
- package/src/utils/subagent-probe.ts +94 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
- package/templates/pain_settings.json +2 -1
- package/tests/README.md +120 -0
- package/tests/build-artifacts.test.ts +111 -0
- package/tests/commands/evolution-status.test.ts +222 -0
- package/tests/commands/evolver.test.ts +22 -0
- package/tests/commands/export.test.ts +78 -0
- package/tests/commands/nocturnal-review.test.ts +448 -0
- package/tests/commands/nocturnal-train.test.ts +97 -0
- package/tests/commands/pain.test.ts +108 -0
- package/tests/commands/samples.test.ts +65 -0
- package/tests/commands/strategy.test.ts +34 -0
- package/tests/commands/thinking-os.test.ts +88 -0
- package/tests/core/adaptive-thresholds.test.ts +261 -0
- package/tests/core/config-service.test.ts +89 -0
- package/tests/core/config.test.ts +90 -0
- package/tests/core/control-ui-db.test.ts +75 -0
- package/tests/core/core-template-guidance.test.ts +21 -0
- package/tests/core/detection-funnel.test.ts +63 -0
- package/tests/core/detection-service.test.ts +50 -0
- package/tests/core/dictionary-service.test.ts +116 -0
- package/tests/core/dictionary.test.ts +168 -0
- package/tests/core/empathy-keyword-matcher.test.ts +209 -0
- package/tests/core/event-log.test.ts +181 -0
- package/tests/core/evolution-e2e.test.ts +58 -0
- package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
- package/tests/core/evolution-engine.test.ts +562 -0
- package/tests/core/evolution-logger.test.ts +148 -0
- package/tests/core/evolution-migration.test.ts +50 -0
- package/tests/core/evolution-paths.test.ts +21 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
- package/tests/core/evolution-reducer.test.ts +180 -0
- package/tests/core/evolution-types-loop.test.ts +48 -0
- package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
- package/tests/core/external-training-contract.test.ts +463 -0
- package/tests/core/focus-history.test.ts +682 -0
- package/tests/core/init-flatten.test.ts +69 -0
- package/tests/core/init-refactor.test.ts +87 -0
- package/tests/core/init-v1.3.test.ts +46 -0
- package/tests/core/init.test.ts +190 -0
- package/tests/core/local-worker-routing.test.ts +757 -0
- package/tests/core/migration.test.ts +84 -0
- package/tests/core/model-deployment-registry.test.ts +845 -0
- package/tests/core/model-training-registry.test.ts +889 -0
- package/tests/core/nocturnal-arbiter.test.ts +494 -0
- package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
- package/tests/core/nocturnal-compliance.test.ts +646 -0
- package/tests/core/nocturnal-dataset.test.ts +892 -0
- package/tests/core/nocturnal-executability.test.ts +357 -0
- package/tests/core/nocturnal-export.test.ts +462 -0
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
- package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
- package/tests/core/nocturnal-trinity.test.ts +953 -0
- package/tests/core/pain.test.ts +33 -0
- package/tests/core/path-resolver.test.ts +57 -0
- package/tests/core/paths-refactor.test.ts +42 -0
- package/tests/core/phase7-rollout-integration.test.ts +477 -0
- package/tests/core/principle-training-state.test.ts +712 -0
- package/tests/core/profile.test.ts +56 -0
- package/tests/core/promotion-gate.test.ts +556 -0
- package/tests/core/risk-calculator.test.ts +168 -0
- package/tests/core/session-tracker.test.ts +191 -0
- package/tests/core/training-program.test.ts +472 -0
- package/tests/core/trajectory.test.ts +265 -0
- package/tests/core/workspace-context-factory.test.ts +18 -0
- package/tests/core/workspace-context.test.ts +134 -0
- package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
- package/tests/fixtures/production-compatibility.test.ts +147 -0
- package/tests/fixtures/production-mock-generator.ts +282 -0
- package/tests/hooks/bash-risk-integration.test.ts +137 -0
- package/tests/hooks/bash-risk.test.ts +81 -0
- package/tests/hooks/edit-verification.test.ts +678 -0
- package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
- package/tests/hooks/gate-edit-verification.test.ts +435 -0
- package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
- package/tests/hooks/gate.test.ts +271 -0
- package/tests/hooks/gfi-gate-unit.test.ts +422 -0
- package/tests/hooks/gfi-gate.test.ts +669 -0
- package/tests/hooks/lifecycle.test.ts +248 -0
- package/tests/hooks/llm.test.ts +308 -0
- package/tests/hooks/message-sanitize.test.ts +36 -0
- package/tests/hooks/pain.test.ts +141 -0
- package/tests/hooks/progressive-trust-gate.test.ts +277 -0
- package/tests/hooks/prompt.test.ts +1411 -0
- package/tests/hooks/subagent.test.ts +467 -0
- package/tests/hooks/thinking-gate.test.ts +313 -0
- package/tests/http/principles-console-route.test.ts +140 -0
- package/tests/hygiene-tracker.test.ts +77 -0
- package/tests/index.integration.test.ts +179 -0
- package/tests/index.shadow-routing.integration.test.ts +140 -0
- package/tests/index.test.ts +9 -0
- package/tests/integration/empathy-workflow-integration.test.ts +627 -0
- package/tests/service/control-ui-query-service.test.ts +121 -0
- package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
- package/tests/service/evolution-worker.test.ts +585 -0
- package/tests/service/nocturnal-runtime.test.ts +470 -0
- package/tests/service/nocturnal-service.test.ts +577 -0
- package/tests/service/nocturnal-target-selector.test.ts +615 -0
- package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
- package/tests/service/phase3-input-filter.test.ts +289 -0
- package/tests/service/runtime-summary-service.test.ts +919 -0
- package/tests/task-compliance.test.ts +166 -0
- package/tests/test-utils.ts +48 -0
- package/tests/tools/critique-prompt.test.ts +260 -0
- package/tests/tools/deep-reflect.test.ts +232 -0
- package/tests/tools/model-index.test.ts +246 -0
- package/tests/ui/app.test.tsx +114 -0
- package/tests/utils/file-lock.test.ts +407 -0
- package/tests/utils/hashing.test.ts +32 -0
- package/tests/utils/io.test.ts +39 -0
- package/tests/utils/nlp.test.ts +53 -0
- package/tests/utils/plugin-logger.test.ts +156 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/src/App.tsx +45 -0
- package/ui/src/api.ts +216 -0
- package/ui/src/charts.tsx +586 -0
- package/ui/src/components/ErrorState.tsx +6 -0
- package/ui/src/components/Loading.tsx +13 -0
- package/ui/src/components/ProtectedRoute.tsx +12 -0
- package/ui/src/components/Shell.tsx +91 -0
- package/ui/src/components/WorkspaceConfig.tsx +146 -0
- package/ui/src/components/index.ts +5 -0
- package/ui/src/context/auth.tsx +80 -0
- package/ui/src/context/theme.tsx +66 -0
- package/ui/src/hooks/useAutoRefresh.ts +39 -0
- package/ui/src/i18n/ui.ts +363 -0
- package/ui/src/main.tsx +16 -0
- package/ui/src/pages/EvolutionPage.tsx +352 -0
- package/ui/src/pages/FeedbackPage.tsx +140 -0
- package/ui/src/pages/GateMonitorPage.tsx +136 -0
- package/ui/src/pages/LoginPage.tsx +88 -0
- package/ui/src/pages/OverviewPage.tsx +238 -0
- package/ui/src/pages/SamplesPage.tsx +174 -0
- package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
- package/ui/src/styles.css +1661 -0
- package/ui/src/types.ts +368 -0
- package/ui/src/utils/format.ts +15 -0
- package/vitest.config.ts +23 -0
- package/dist/commands/capabilities.d.ts +0 -3
- package/dist/commands/capabilities.js +0 -73
- package/dist/commands/context.d.ts +0 -5
- package/dist/commands/evolution-status.d.ts +0 -4
- package/dist/commands/evolution-status.js +0 -117
- package/dist/commands/evolver.d.ts +0 -9
- package/dist/commands/evolver.js +0 -26
- package/dist/commands/export.d.ts +0 -2
- package/dist/commands/export.js +0 -98
- package/dist/commands/focus.d.ts +0 -14
- package/dist/commands/focus.js +0 -457
- package/dist/commands/nocturnal-review.d.ts +0 -24
- package/dist/commands/nocturnal-review.js +0 -265
- package/dist/commands/nocturnal-rollout.d.ts +0 -27
- package/dist/commands/nocturnal-rollout.js +0 -671
- package/dist/commands/nocturnal-train.d.ts +0 -25
- package/dist/commands/nocturnal-train.js +0 -919
- package/dist/commands/pain.d.ts +0 -5
- package/dist/commands/principle-rollback.d.ts +0 -4
- package/dist/commands/principle-rollback.js +0 -22
- package/dist/commands/rollback.d.ts +0 -19
- package/dist/commands/samples.d.ts +0 -2
- package/dist/commands/samples.js +0 -55
- package/dist/commands/strategy.d.ts +0 -3
- package/dist/commands/strategy.js +0 -29
- package/dist/commands/thinking-os.d.ts +0 -2
- package/dist/config/defaults/runtime.d.ts +0 -40
- package/dist/config/errors.d.ts +0 -84
- package/dist/config/errors.js +0 -94
- package/dist/config/index.js +0 -7
- package/dist/constants/diagnostician.d.ts +0 -12
- package/dist/constants/diagnostician.js +0 -56
- package/dist/constants/tools.d.ts +0 -17
- package/dist/constants/tools.js +0 -54
- package/dist/core/adaptive-thresholds.d.ts +0 -186
- package/dist/core/adaptive-thresholds.js +0 -300
- package/dist/core/config-service.d.ts +0 -15
- package/dist/core/config.d.ts +0 -127
- package/dist/core/control-ui-db.d.ts +0 -95
- package/dist/core/control-ui-db.js +0 -292
- package/dist/core/detection-funnel.d.ts +0 -33
- package/dist/core/detection-service.d.ts +0 -15
- package/dist/core/dictionary-service.d.ts +0 -15
- package/dist/core/dictionary.d.ts +0 -38
- package/dist/core/event-log.d.ts +0 -82
- package/dist/core/event-log.js +0 -463
- package/dist/core/evolution-engine.d.ts +0 -118
- package/dist/core/evolution-engine.js +0 -464
- package/dist/core/evolution-logger.d.ts +0 -137
- package/dist/core/evolution-logger.js +0 -256
- package/dist/core/evolution-migration.d.ts +0 -5
- package/dist/core/evolution-migration.js +0 -65
- package/dist/core/evolution-reducer.d.ts +0 -98
- package/dist/core/evolution-reducer.js +0 -465
- package/dist/core/evolution-types.d.ts +0 -287
- package/dist/core/evolution-types.js +0 -78
- package/dist/core/external-training-contract.d.ts +0 -276
- package/dist/core/external-training-contract.js +0 -269
- package/dist/core/focus-history.d.ts +0 -210
- package/dist/core/focus-history.js +0 -1185
- package/dist/core/hygiene/tracker.d.ts +0 -22
- package/dist/core/hygiene/tracker.js +0 -106
- package/dist/core/init.d.ts +0 -12
- package/dist/core/local-worker-routing.d.ts +0 -175
- package/dist/core/local-worker-routing.js +0 -525
- package/dist/core/migration.d.ts +0 -6
- package/dist/core/model-deployment-registry.d.ts +0 -218
- package/dist/core/model-deployment-registry.js +0 -503
- package/dist/core/model-training-registry.d.ts +0 -295
- package/dist/core/model-training-registry.js +0 -475
- package/dist/core/nocturnal-arbiter.d.ts +0 -159
- package/dist/core/nocturnal-arbiter.js +0 -534
- package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
- package/dist/core/nocturnal-candidate-scoring.js +0 -266
- package/dist/core/nocturnal-compliance.d.ts +0 -175
- package/dist/core/nocturnal-compliance.js +0 -824
- package/dist/core/nocturnal-dataset.d.ts +0 -224
- package/dist/core/nocturnal-dataset.js +0 -443
- package/dist/core/nocturnal-executability.d.ts +0 -85
- package/dist/core/nocturnal-executability.js +0 -331
- package/dist/core/nocturnal-export.d.ts +0 -124
- package/dist/core/nocturnal-export.js +0 -275
- package/dist/core/nocturnal-paths.d.ts +0 -124
- package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
- package/dist/core/nocturnal-trajectory-extractor.js +0 -307
- package/dist/core/nocturnal-trinity.d.ts +0 -311
- package/dist/core/nocturnal-trinity.js +0 -880
- package/dist/core/pain.d.ts +0 -4
- package/dist/core/pain.js +0 -70
- package/dist/core/path-resolver.d.ts +0 -46
- package/dist/core/paths.d.ts +0 -65
- package/dist/core/principle-training-state.d.ts +0 -121
- package/dist/core/principle-training-state.js +0 -321
- package/dist/core/profile.d.ts +0 -62
- package/dist/core/profile.js +0 -210
- package/dist/core/promotion-gate.d.ts +0 -238
- package/dist/core/promotion-gate.js +0 -529
- package/dist/core/risk-calculator.d.ts +0 -22
- package/dist/core/session-tracker.d.ts +0 -99
- package/dist/core/shadow-observation-registry.d.ts +0 -217
- package/dist/core/shadow-observation-registry.js +0 -308
- package/dist/core/system-logger.d.ts +0 -8
- package/dist/core/thinking-models.d.ts +0 -38
- package/dist/core/thinking-models.js +0 -170
- package/dist/core/training-program.d.ts +0 -233
- package/dist/core/training-program.js +0 -433
- package/dist/core/trajectory.d.ts +0 -411
- package/dist/core/trajectory.js +0 -1307
- package/dist/core/workspace-context.d.ts +0 -71
- package/dist/hooks/bash-risk.d.ts +0 -57
- package/dist/hooks/bash-risk.js +0 -137
- package/dist/hooks/edit-verification.d.ts +0 -62
- package/dist/hooks/edit-verification.js +0 -256
- package/dist/hooks/gate-block-helper.d.ts +0 -44
- package/dist/hooks/gate-block-helper.js +0 -119
- package/dist/hooks/gate.d.ts +0 -24
- package/dist/hooks/gate.js +0 -173
- package/dist/hooks/gfi-gate.d.ts +0 -40
- package/dist/hooks/gfi-gate.js +0 -113
- package/dist/hooks/lifecycle.d.ts +0 -5
- package/dist/hooks/lifecycle.js +0 -284
- package/dist/hooks/llm.d.ts +0 -12
- package/dist/hooks/message-sanitize.d.ts +0 -3
- package/dist/hooks/message-sanitize.js +0 -37
- package/dist/hooks/pain.d.ts +0 -5
- package/dist/hooks/pain.js +0 -301
- package/dist/hooks/progressive-trust-gate.d.ts +0 -51
- package/dist/hooks/progressive-trust-gate.js +0 -89
- package/dist/hooks/prompt.d.ts +0 -47
- package/dist/hooks/prompt.js +0 -884
- package/dist/hooks/subagent.d.ts +0 -10
- package/dist/hooks/subagent.js +0 -387
- package/dist/hooks/thinking-checkpoint.d.ts +0 -37
- package/dist/hooks/thinking-checkpoint.js +0 -51
- package/dist/hooks/trajectory-collector.d.ts +0 -32
- package/dist/hooks/trajectory-collector.js +0 -256
- package/dist/http/principles-console-route.d.ts +0 -9
- package/dist/http/principles-console-route.js +0 -567
- package/dist/i18n/commands.d.ts +0 -26
- package/dist/i18n/commands.js +0 -116
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -581
- package/dist/service/central-database.d.ts +0 -104
- package/dist/service/central-database.js +0 -649
- package/dist/service/control-ui-query-service.d.ts +0 -221
- package/dist/service/control-ui-query-service.js +0 -543
- package/dist/service/empathy-observer-manager.d.ts +0 -52
- package/dist/service/empathy-observer-manager.js +0 -229
- package/dist/service/evolution-query-service.d.ts +0 -155
- package/dist/service/evolution-query-service.js +0 -258
- package/dist/service/evolution-worker.d.ts +0 -101
- package/dist/service/evolution-worker.js +0 -974
- package/dist/service/nocturnal-runtime.d.ts +0 -183
- package/dist/service/nocturnal-service.d.ts +0 -163
- package/dist/service/nocturnal-service.js +0 -787
- package/dist/service/nocturnal-target-selector.d.ts +0 -145
- package/dist/service/nocturnal-target-selector.js +0 -315
- package/dist/service/phase3-input-filter.d.ts +0 -73
- package/dist/service/phase3-input-filter.js +0 -172
- package/dist/service/runtime-summary-service.d.ts +0 -122
- package/dist/service/runtime-summary-service.js +0 -485
- package/dist/service/trajectory-service.d.ts +0 -2
- package/dist/service/trajectory-service.js +0 -15
- package/dist/tools/critique-prompt.d.ts +0 -14
- package/dist/tools/deep-reflect.d.ts +0 -39
- package/dist/tools/deep-reflect.js +0 -350
- package/dist/tools/model-index.d.ts +0 -9
- package/dist/types/event-types.d.ts +0 -306
- package/dist/types/event-types.js +0 -106
- package/dist/types/hygiene-types.d.ts +0 -20
- package/dist/types/hygiene-types.js +0 -12
- package/dist/types/runtime-summary.d.ts +0 -47
- package/dist/types/runtime-summary.js +0 -1
- package/dist/types.d.ts +0 -50
- package/dist/types.js +0 -22
- package/dist/utils/file-lock.d.ts +0 -71
- package/dist/utils/file-lock.js +0 -309
- package/dist/utils/glob-match.d.ts +0 -28
- package/dist/utils/hashing.d.ts +0 -9
- package/dist/utils/io.d.ts +0 -6
- package/dist/utils/io.js +0 -106
- package/dist/utils/nlp.d.ts +0 -9
- package/dist/utils/plugin-logger.d.ts +0 -39
- package/dist/utils/subagent-probe.d.ts +0 -34
- package/dist/utils/subagent-probe.js +0 -81
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { EventLogService } from '../../src/core/event-log.js';
|
|
6
|
+
import { clearSession, trackFriction } from '../../src/core/session-tracker.js';
|
|
7
|
+
import { WorkspaceContext } from '../../src/core/workspace-context.js';
|
|
8
|
+
import { serializeKvLines } from '../../src/utils/io.js';
|
|
9
|
+
import { RuntimeSummaryService } from '../../src/service/runtime-summary-service.js';
|
|
10
|
+
|
|
11
|
+
const tempDirs: string[] = [];
|
|
12
|
+
|
|
13
|
+
function makeWorkspace(): string {
|
|
14
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-runtime-summary-'));
|
|
15
|
+
tempDirs.push(dir);
|
|
16
|
+
fs.mkdirSync(path.join(dir, '.state', 'sessions'), { recursive: true });
|
|
17
|
+
fs.mkdirSync(path.join(dir, '.state', 'logs'), { recursive: true });
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeJson(filePath: string, value: unknown): void {
|
|
22
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
23
|
+
fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeSession(workspace: string, sessionId: string, payload: Record<string, unknown>): void {
|
|
27
|
+
writeJson(path.join(workspace, '.state', 'sessions', `${sessionId}.json`), {
|
|
28
|
+
sessionId,
|
|
29
|
+
...payload,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeEvents(workspace: string, entries: unknown[]): void {
|
|
34
|
+
const filePath = path.join(workspace, '.state', 'logs', 'events.jsonl');
|
|
35
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
36
|
+
const content = entries.map((entry) => JSON.stringify(entry)).join('\n');
|
|
37
|
+
fs.writeFileSync(filePath, content ? `${content}\n` : '', 'utf8');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
for (const dir of tempDirs.splice(0)) {
|
|
42
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
WorkspaceContext.clearCache();
|
|
45
|
+
clearSession('live-session');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('RuntimeSummaryService', () => {
|
|
49
|
+
it('builds an active workspace summary from canonical state files', () => {
|
|
50
|
+
const workspace = makeWorkspace();
|
|
51
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
52
|
+
trust_score: 85,
|
|
53
|
+
success_streak: 50,
|
|
54
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
55
|
+
});
|
|
56
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
57
|
+
{ id: '1', status: 'pending', score: 50 },
|
|
58
|
+
{ id: '2', status: 'completed', score: 10 },
|
|
59
|
+
]);
|
|
60
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
61
|
+
active: true,
|
|
62
|
+
task: 'fix something important',
|
|
63
|
+
timestamp: '2026-03-20T10:00:00Z',
|
|
64
|
+
});
|
|
65
|
+
writeJson(path.join(workspace, '.state', 'pain_candidates.json'), {
|
|
66
|
+
candidates: {
|
|
67
|
+
a: {},
|
|
68
|
+
b: {},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
fs.writeFileSync(
|
|
72
|
+
path.join(workspace, '.state', '.pain_flag'),
|
|
73
|
+
serializeKvLines({
|
|
74
|
+
source: 'tool_failure',
|
|
75
|
+
score: '50',
|
|
76
|
+
time: '2026-03-20T10:00:00Z',
|
|
77
|
+
}),
|
|
78
|
+
'utf8'
|
|
79
|
+
);
|
|
80
|
+
writeSession(workspace, 's1', {
|
|
81
|
+
currentGfi: 45,
|
|
82
|
+
dailyGfiPeak: 78,
|
|
83
|
+
lastActivityAt: 2,
|
|
84
|
+
});
|
|
85
|
+
writeSession(workspace, 's0', {
|
|
86
|
+
currentGfi: 20,
|
|
87
|
+
dailyGfiPeak: 30,
|
|
88
|
+
lastActivityAt: 1,
|
|
89
|
+
});
|
|
90
|
+
writeEvents(workspace, [
|
|
91
|
+
{
|
|
92
|
+
ts: '2026-03-20T10:00:01Z',
|
|
93
|
+
type: 'pain_signal',
|
|
94
|
+
category: 'detected',
|
|
95
|
+
sessionId: 's1',
|
|
96
|
+
data: { source: 'tool_failure', score: 50, reason: 'write failed' },
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
ts: '2026-03-20T10:00:02Z',
|
|
100
|
+
type: 'gate_bypass',
|
|
101
|
+
category: 'bypassed',
|
|
102
|
+
sessionId: 's1',
|
|
103
|
+
data: { toolName: 'write' },
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
108
|
+
|
|
109
|
+
expect(summary.gfi.current).toBe(45);
|
|
110
|
+
expect(summary.gfi.peak).toBe(78);
|
|
111
|
+
expect(summary.evolution.queue.pending).toBe(1);
|
|
112
|
+
expect(summary.evolution.queue.completed).toBe(1);
|
|
113
|
+
expect(summary.evolution.directive.exists).toBe(false);
|
|
114
|
+
expect(summary.evolution.directive.active).toBeNull();
|
|
115
|
+
expect(summary.evolution.dataQuality).toBe('authoritative');
|
|
116
|
+
expect(summary.pain.activeFlag).toBe(true);
|
|
117
|
+
expect(summary.pain.activeFlagSource).toBe('tool_failure');
|
|
118
|
+
expect(summary.pain.candidates).toBe(2);
|
|
119
|
+
expect(summary.pain.lastSignal?.source).toBe('tool_failure');
|
|
120
|
+
expect(summary.gate.recentBypasses).toBe(1);
|
|
121
|
+
expect(summary.metadata.sessionId).toBe('s1');
|
|
122
|
+
expect(summary.metadata.selectedSessionReason).toBe('latest_active');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('returns partial warnings when canonical files are missing', () => {
|
|
126
|
+
const workspace = makeWorkspace();
|
|
127
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
128
|
+
trust_score: 100,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
132
|
+
|
|
133
|
+
expect(summary.gfi.current).toBeNull();
|
|
134
|
+
expect(summary.evolution.dataQuality).toBe('partial');
|
|
135
|
+
expect(summary.evolution.directive.exists).toBe(false);
|
|
136
|
+
expect(summary.pain.candidates).toBeNull();
|
|
137
|
+
expect(summary.metadata.warnings.join('\n')).toContain('No persisted session state was found');
|
|
138
|
+
expect(summary.metadata.warnings.join('\n')).toContain('partial');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('derives compact phase3 readiness flags from queue inputs', () => {
|
|
142
|
+
const workspace = makeWorkspace();
|
|
143
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
144
|
+
{ id: 'task-1', status: 'in_progress' },
|
|
145
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-20T10:01:00Z' },
|
|
146
|
+
{ id: 'task-2', status: 'completed' },
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
150
|
+
|
|
151
|
+
expect(summary.phase3.queueTruthReady).toBe(false);
|
|
152
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(false);
|
|
153
|
+
expect(summary.phase3.evolutionRejectedReasons).toEqual(
|
|
154
|
+
expect.arrayContaining([
|
|
155
|
+
'reused_task_id',
|
|
156
|
+
'missing_started_at',
|
|
157
|
+
'missing_completed_at',
|
|
158
|
+
])
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('derives phase3 readiness from valid queue entries', () => {
|
|
163
|
+
const workspace = makeWorkspace();
|
|
164
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
165
|
+
{ id: 'task-1', status: 'pending' },
|
|
166
|
+
{ id: 'task-2', status: 'in_progress', started_at: '2026-03-20T10:00:00Z' },
|
|
167
|
+
{ id: 'task-3', status: 'completed', completed_at: '2026-03-20T10:01:00Z' },
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
171
|
+
|
|
172
|
+
expect(summary.phase3.queueTruthReady).toBe(true);
|
|
173
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('prefers the explicit session when provided', () => {
|
|
177
|
+
const workspace = makeWorkspace();
|
|
178
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
179
|
+
trust_score: 30,
|
|
180
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
181
|
+
});
|
|
182
|
+
writeSession(workspace, 's1', {
|
|
183
|
+
currentGfi: 10,
|
|
184
|
+
dailyGfiPeak: 12,
|
|
185
|
+
lastActivityAt: 10,
|
|
186
|
+
});
|
|
187
|
+
writeSession(workspace, 's2', {
|
|
188
|
+
currentGfi: 70,
|
|
189
|
+
dailyGfiPeak: 80,
|
|
190
|
+
lastActivityAt: 20,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's1' });
|
|
194
|
+
|
|
195
|
+
expect(summary.metadata.sessionId).toBe('s1');
|
|
196
|
+
expect(summary.metadata.selectedSessionReason).toBe('explicit');
|
|
197
|
+
expect(summary.gfi.current).toBe(10);
|
|
198
|
+
expect(summary.gfi.peak).toBe(12);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('prefers the session with the newest control activity timestamp over plain activity', () => {
|
|
202
|
+
const workspace = makeWorkspace();
|
|
203
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
204
|
+
trust_score: 42,
|
|
205
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
206
|
+
});
|
|
207
|
+
writeSession(workspace, 's1', {
|
|
208
|
+
currentGfi: 11,
|
|
209
|
+
dailyGfiPeak: 20,
|
|
210
|
+
lastActivityAt: 50,
|
|
211
|
+
lastControlActivityAt: 200,
|
|
212
|
+
});
|
|
213
|
+
writeSession(workspace, 's2', {
|
|
214
|
+
currentGfi: 99,
|
|
215
|
+
dailyGfiPeak: 99,
|
|
216
|
+
lastActivityAt: 100,
|
|
217
|
+
lastControlActivityAt: 100,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
221
|
+
|
|
222
|
+
expect(summary.metadata.sessionId).toBe('s1');
|
|
223
|
+
expect(summary.gfi.current).toBe(11);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('warns when a legacy directive is stale relative to an empty queue', () => {
|
|
227
|
+
const workspace = makeWorkspace();
|
|
228
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
229
|
+
trust_score: 100,
|
|
230
|
+
});
|
|
231
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), []);
|
|
232
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
233
|
+
active: true,
|
|
234
|
+
task: 'old task',
|
|
235
|
+
timestamp: '2026-03-18T00:00:00Z',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
239
|
+
|
|
240
|
+
expect(summary.evolution.directive.exists).toBe(false);
|
|
241
|
+
expect(summary.evolution.directive.active).toBeNull();
|
|
242
|
+
expect(summary.evolution.dataQuality).toBe('authoritative');
|
|
243
|
+
expect(summary.evolution.directive.ageSeconds).toBeNull();
|
|
244
|
+
expect(summary.metadata.warnings.join('\n')).toContain('Legacy directive file disagrees with queue-derived evolution state');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('derives directive view from queue-only in-progress work and treats it as authoritative without a directive file', () => {
|
|
248
|
+
const workspace = makeWorkspace();
|
|
249
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
250
|
+
trust_score: 55,
|
|
251
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
252
|
+
});
|
|
253
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
254
|
+
{ id: 'task-1', status: 'in_progress', score: 60, task: 'Diagnose systemic pain [ID: task-1]' },
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
258
|
+
|
|
259
|
+
expect(summary.evolution.queue.inProgress).toBe(1);
|
|
260
|
+
expect(summary.evolution.directive.exists).toBe(true);
|
|
261
|
+
expect(summary.evolution.directive.active).toBe(true);
|
|
262
|
+
expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-1]');
|
|
263
|
+
expect(summary.evolution.dataQuality).toBe('authoritative');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('uses trigger_text_preview when an in-progress queue item is missing task text', () => {
|
|
267
|
+
const workspace = makeWorkspace();
|
|
268
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
269
|
+
trust_score: 55,
|
|
270
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
271
|
+
});
|
|
272
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
273
|
+
{
|
|
274
|
+
id: 'task-2',
|
|
275
|
+
status: 'in_progress',
|
|
276
|
+
score: 80,
|
|
277
|
+
source: 'tool_failure',
|
|
278
|
+
reason: 'write failed',
|
|
279
|
+
trigger_text_preview: 'Could not find the file to patch',
|
|
280
|
+
},
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
284
|
+
|
|
285
|
+
expect(summary.evolution.directive.exists).toBe(true);
|
|
286
|
+
expect(summary.evolution.directive.active).toBe(true);
|
|
287
|
+
expect(summary.evolution.directive.taskPreview).toContain('Could not find the file to patch');
|
|
288
|
+
expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-2]');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('skips a malformed top-ranked in-progress task and falls back to the next resolvable task', () => {
|
|
292
|
+
const workspace = makeWorkspace();
|
|
293
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
294
|
+
trust_score: 55,
|
|
295
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
296
|
+
});
|
|
297
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
298
|
+
{
|
|
299
|
+
status: 'in_progress',
|
|
300
|
+
score: 100,
|
|
301
|
+
task: 'undefined',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: 'task-3',
|
|
305
|
+
status: 'in_progress',
|
|
306
|
+
score: 20,
|
|
307
|
+
task: 'Diagnose systemic pain [ID: task-3]',
|
|
308
|
+
},
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
312
|
+
|
|
313
|
+
expect(summary.evolution.directive.exists).toBe(true);
|
|
314
|
+
expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-3]');
|
|
315
|
+
expect(summary.evolution.directive.taskPreview).not.toContain('undefined');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('warns when a legacy directive disagrees with queue in-progress state', () => {
|
|
319
|
+
const workspace = makeWorkspace();
|
|
320
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
321
|
+
trust_score: 55,
|
|
322
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
323
|
+
});
|
|
324
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
325
|
+
{ id: 'task-1', status: 'in_progress', score: 60, task: 'Diagnose systemic pain [ID: task-1]' },
|
|
326
|
+
]);
|
|
327
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
328
|
+
active: false,
|
|
329
|
+
task: 'legacy mismatch task',
|
|
330
|
+
timestamp: '2026-03-20T10:00:00Z',
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
334
|
+
|
|
335
|
+
expect(summary.evolution.directive.exists).toBe(true);
|
|
336
|
+
expect(summary.evolution.directive.active).toBe(true);
|
|
337
|
+
expect(summary.metadata.warnings.join('\n')).toContain('Legacy directive file disagrees');
|
|
338
|
+
expect(summary.evolution.dataQuality).toBe('authoritative');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('surfaces observer empathy events as authoritative gfi sources', () => {
|
|
342
|
+
const workspace = makeWorkspace();
|
|
343
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
344
|
+
trust_score: 59,
|
|
345
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
346
|
+
});
|
|
347
|
+
writeSession(workspace, 's-observer', {
|
|
348
|
+
currentGfi: 25,
|
|
349
|
+
dailyGfiPeak: 25,
|
|
350
|
+
lastActivityAt: 5,
|
|
351
|
+
});
|
|
352
|
+
writeEvents(workspace, [
|
|
353
|
+
{
|
|
354
|
+
ts: '2026-03-20T10:00:03Z',
|
|
355
|
+
type: 'pain_signal',
|
|
356
|
+
category: 'detected',
|
|
357
|
+
sessionId: 's-observer',
|
|
358
|
+
data: {
|
|
359
|
+
source: 'user_empathy',
|
|
360
|
+
origin: 'system_infer',
|
|
361
|
+
severity: 'moderate',
|
|
362
|
+
confidence: 0.8,
|
|
363
|
+
score: 25,
|
|
364
|
+
reason: 'observer caught frustration',
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's-observer' });
|
|
370
|
+
|
|
371
|
+
expect(summary.gfi.sources).toEqual([
|
|
372
|
+
expect.objectContaining({
|
|
373
|
+
source: 'user_empathy',
|
|
374
|
+
origin: 'system_infer',
|
|
375
|
+
score: 25,
|
|
376
|
+
confidence: 0.8,
|
|
377
|
+
}),
|
|
378
|
+
]);
|
|
379
|
+
expect(summary.pain.lastSignal?.source).toBe('user_empathy');
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('includes in-memory session state and buffered events before flush', () => {
|
|
383
|
+
const workspace = makeWorkspace();
|
|
384
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
385
|
+
trust_score: 59,
|
|
386
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
trackFriction('live-session', 18, 'user_empathy_mild', workspace, { source: 'user_empathy' });
|
|
390
|
+
const eventLog = EventLogService.get(path.join(workspace, '.state'));
|
|
391
|
+
eventLog.recordPainSignal('live-session', {
|
|
392
|
+
score: 18,
|
|
393
|
+
source: 'user_empathy',
|
|
394
|
+
reason: 'buffered empathy event',
|
|
395
|
+
origin: 'assistant_self_report',
|
|
396
|
+
severity: 'mild',
|
|
397
|
+
confidence: 1,
|
|
398
|
+
detection_mode: 'structured',
|
|
399
|
+
deduped: false,
|
|
400
|
+
eventId: 'live-emp-1',
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 'live-session' });
|
|
404
|
+
|
|
405
|
+
expect(summary.metadata.sessionId).toBe('live-session');
|
|
406
|
+
expect(summary.metadata.selectedSessionReason).toBe('explicit');
|
|
407
|
+
expect(summary.gfi.current).toBe(18);
|
|
408
|
+
expect(summary.pain.lastSignal?.reason).toBe('buffered empathy event');
|
|
409
|
+
expect(summary.gfi.sources).toEqual([
|
|
410
|
+
expect.objectContaining({
|
|
411
|
+
source: 'user_empathy',
|
|
412
|
+
score: 18,
|
|
413
|
+
origin: 'assistant_self_report',
|
|
414
|
+
}),
|
|
415
|
+
]);
|
|
416
|
+
expect(summary.metadata.warnings.join('\n')).not.toContain('Live event buffer is unavailable');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('deduplicates persisted and buffered copies of the same event by eventId', () => {
|
|
420
|
+
const workspace = makeWorkspace();
|
|
421
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
422
|
+
trust_score: 59,
|
|
423
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
424
|
+
});
|
|
425
|
+
writeSession(workspace, 's-dedupe', {
|
|
426
|
+
currentGfi: 18,
|
|
427
|
+
dailyGfiPeak: 18,
|
|
428
|
+
lastActivityAt: 5,
|
|
429
|
+
lastControlActivityAt: 5,
|
|
430
|
+
});
|
|
431
|
+
writeEvents(workspace, [
|
|
432
|
+
{
|
|
433
|
+
ts: '2026-03-20T10:00:03Z',
|
|
434
|
+
type: 'pain_signal',
|
|
435
|
+
category: 'detected',
|
|
436
|
+
sessionId: 's-dedupe',
|
|
437
|
+
data: {
|
|
438
|
+
source: 'user_empathy',
|
|
439
|
+
origin: 'assistant_self_report',
|
|
440
|
+
severity: 'mild',
|
|
441
|
+
confidence: 1,
|
|
442
|
+
score: 18,
|
|
443
|
+
reason: 'same event',
|
|
444
|
+
eventId: 'dup-1',
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
]);
|
|
448
|
+
|
|
449
|
+
const eventLog = EventLogService.get(path.join(workspace, '.state'));
|
|
450
|
+
eventLog.recordPainSignal('s-dedupe', {
|
|
451
|
+
source: 'user_empathy',
|
|
452
|
+
origin: 'assistant_self_report',
|
|
453
|
+
severity: 'mild',
|
|
454
|
+
confidence: 1,
|
|
455
|
+
score: 18,
|
|
456
|
+
reason: 'same event',
|
|
457
|
+
eventId: 'dup-1',
|
|
458
|
+
detection_mode: 'structured',
|
|
459
|
+
deduped: false,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's-dedupe' });
|
|
463
|
+
|
|
464
|
+
expect(summary.gfi.sources).toHaveLength(1);
|
|
465
|
+
expect(summary.pain.lastSignal?.reason).toBe('same event');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('warns when malformed event lines are skipped', () => {
|
|
469
|
+
const workspace = makeWorkspace();
|
|
470
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
471
|
+
trust_score: 59,
|
|
472
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
473
|
+
});
|
|
474
|
+
fs.writeFileSync(
|
|
475
|
+
path.join(workspace, '.state', 'logs', 'events.jsonl'),
|
|
476
|
+
[
|
|
477
|
+
JSON.stringify({
|
|
478
|
+
ts: '2026-03-20T10:00:01Z',
|
|
479
|
+
type: 'pain_signal',
|
|
480
|
+
category: 'detected',
|
|
481
|
+
sessionId: 's1',
|
|
482
|
+
data: { source: 'tool_failure', score: 10, reason: 'write failed' },
|
|
483
|
+
}),
|
|
484
|
+
'{not-json}',
|
|
485
|
+
].join('\n'),
|
|
486
|
+
'utf8'
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
490
|
+
|
|
491
|
+
expect(summary.metadata.warnings.join('\n')).toContain('Skipped 1 malformed event line');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Task 8: Updated phase3 integration tests
|
|
495
|
+
describe('Phase 3 Input Quarantine Integration', () => {
|
|
496
|
+
it('reports legacy queue status as rejection reason in phase3 section', () => {
|
|
497
|
+
const workspace = makeWorkspace();
|
|
498
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
499
|
+
trust_score: 85,
|
|
500
|
+
frozen: true,
|
|
501
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
502
|
+
});
|
|
503
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
504
|
+
{ id: '1afdd4bb', status: 'resolved', started_at: '2026-03-24T15:29:39.710Z', completed_at: '2026-03-24T15:29:39.710Z' }
|
|
505
|
+
]);
|
|
506
|
+
|
|
507
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
508
|
+
|
|
509
|
+
expect(summary.phase3.queueTruthReady).toBe(false);
|
|
510
|
+
expect(summary.phase3.evolutionRejectedReasons).toContain('legacy_queue_status');
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('reports null status as rejection reason in phase3 section', () => {
|
|
514
|
+
const workspace = makeWorkspace();
|
|
515
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
516
|
+
trust_score: 85,
|
|
517
|
+
frozen: true,
|
|
518
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
519
|
+
});
|
|
520
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
521
|
+
{ id: '6a7c7c48', status: null, started_at: '2026-03-24T15:29:39.710Z' }
|
|
522
|
+
]);
|
|
523
|
+
|
|
524
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
525
|
+
|
|
526
|
+
expect(summary.phase3.queueTruthReady).toBe(false);
|
|
527
|
+
expect(summary.phase3.evolutionRejectedReasons).toContain('missing_status');
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('reports timeout-only outcomes as referenceOnly (not rejected)', () => {
|
|
531
|
+
const workspace = makeWorkspace();
|
|
532
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
533
|
+
{ id: 'e5da4f5c', status: 'completed', resolution: 'auto_completed_timeout', completed_at: '2026-03-24T15:29:39.710Z' }
|
|
534
|
+
]);
|
|
535
|
+
|
|
536
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
537
|
+
|
|
538
|
+
// Timeout-only outcomes are valid data (queue is ready)
|
|
539
|
+
// They go to referenceOnly, not rejected
|
|
540
|
+
expect(summary.phase3.queueTruthReady).toBe(true);
|
|
541
|
+
// No rejection reasons from timeout
|
|
542
|
+
expect(summary.phase3.evolutionRejectedReasons).not.toContain('timeout_only_outcome');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('labels directive status as compatibility-only when directive file exists', () => {
|
|
546
|
+
const workspace = makeWorkspace();
|
|
547
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
548
|
+
trust_score: 85,
|
|
549
|
+
frozen: true,
|
|
550
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
551
|
+
});
|
|
552
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
553
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' },
|
|
554
|
+
{ id: 'task-2', status: 'in_progress', started_at: '2026-03-25T11:00:00.000Z' }
|
|
555
|
+
]);
|
|
556
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
557
|
+
active: true,
|
|
558
|
+
task: 'some task',
|
|
559
|
+
timestamp: '2026-03-20T10:00:00Z',
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
563
|
+
|
|
564
|
+
expect(summary.phase3.directiveStatus).toBe('compatibility-only');
|
|
565
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('reports directive status as missing when directive file does not exist', () => {
|
|
569
|
+
const workspace = makeWorkspace();
|
|
570
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
571
|
+
trust_score: 85,
|
|
572
|
+
frozen: true,
|
|
573
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
574
|
+
});
|
|
575
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
576
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
|
|
577
|
+
]);
|
|
578
|
+
// No evolution_directive.json file
|
|
579
|
+
|
|
580
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
581
|
+
|
|
582
|
+
expect(summary.phase3.directiveStatus).toBe('missing');
|
|
583
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('reports directive as compatibility-only even when stale', () => {
|
|
587
|
+
// Production scenario: directive stopped updating on 2026-03-22
|
|
588
|
+
// Should still be labeled as compatibility-only, not 'stale'
|
|
589
|
+
const workspace = makeWorkspace();
|
|
590
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
591
|
+
trust_score: 85,
|
|
592
|
+
frozen: true,
|
|
593
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
594
|
+
});
|
|
595
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
596
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
|
|
597
|
+
]);
|
|
598
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
599
|
+
active: true,
|
|
600
|
+
task: 'old task',
|
|
601
|
+
timestamp: '2026-03-22T00:00:00Z', // Stale timestamp
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
605
|
+
|
|
606
|
+
expect(summary.phase3.directiveStatus).toBe('compatibility-only');
|
|
607
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('labels directive as compatibility-only even when active flag is false', () => {
|
|
611
|
+
// Directive presence matters for labeling, not the active flag value
|
|
612
|
+
const workspace = makeWorkspace();
|
|
613
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
614
|
+
trust_score: 85,
|
|
615
|
+
frozen: true,
|
|
616
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
617
|
+
});
|
|
618
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
619
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
|
|
620
|
+
]);
|
|
621
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
622
|
+
active: false, // inactive directive
|
|
623
|
+
task: 'inactive task',
|
|
624
|
+
timestamp: '2026-03-20T10:00:00Z',
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
628
|
+
|
|
629
|
+
expect(summary.phase3.directiveStatus).toBe('compatibility-only');
|
|
630
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('labels directive as compatibility-only even with minimal/empty fields', () => {
|
|
634
|
+
// Directive file exists with minimal content should still be labeled
|
|
635
|
+
const workspace = makeWorkspace();
|
|
636
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
637
|
+
trust_score: 85,
|
|
638
|
+
frozen: true,
|
|
639
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
640
|
+
});
|
|
641
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
642
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
|
|
643
|
+
]);
|
|
644
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
645
|
+
// Minimal directive with no task, timestamp, or active fields
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
649
|
+
|
|
650
|
+
expect(summary.phase3.directiveStatus).toBe('compatibility-only');
|
|
651
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// Task 8: TDD tests for Runtime vs Analytics Separation (RED phase)
|
|
656
|
+
describe('Runtime vs Analytics Separation', () => {
|
|
657
|
+
it('populates runtime truth section with queue and sessions', () => {
|
|
658
|
+
const workspace = makeWorkspace();
|
|
659
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
660
|
+
{ id: 'task-1', status: 'pending' },
|
|
661
|
+
{ id: 'task-2', status: 'in_progress' },
|
|
662
|
+
{ id: 'task-3', status: 'completed' },
|
|
663
|
+
]);
|
|
664
|
+
|
|
665
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
666
|
+
|
|
667
|
+
expect(summary.runtime.queueState.total).toBe(3);
|
|
668
|
+
expect(summary.runtime.queueState.pending).toBe(1);
|
|
669
|
+
expect(summary.runtime.queueState.inProgress).toBe(1);
|
|
670
|
+
expect(summary.runtime.queueState.completed).toBe(1);
|
|
671
|
+
expect(summary.runtime.activeSessions).toEqual(expect.any(Array));
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('populates analytics truth section with trajectory, daily stats, trends', () => {
|
|
675
|
+
const workspace = makeWorkspace();
|
|
676
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
677
|
+
trust_score: 75,
|
|
678
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
679
|
+
});
|
|
680
|
+
writeJson(path.join(workspace, '.state', 'logs', 'daily-stats.json'), {
|
|
681
|
+
'2026-03-20': {
|
|
682
|
+
toolCalls: 120,
|
|
683
|
+
painSignals: 15,
|
|
684
|
+
evolutionTasks: 5,
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
689
|
+
|
|
690
|
+
// These properties don't exist yet - tests will FAIL (RED phase)
|
|
691
|
+
expect(summary.analytics.trajectoryData).toBeDefined();
|
|
692
|
+
expect(summary.analytics.dailyStats.toolCalls).toBe(120);
|
|
693
|
+
expect(summary.analytics.dailyStats.painSignals).toBe(15);
|
|
694
|
+
expect(summary.analytics.trends).toBeDefined();
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('calculates Phase 3 readiness from runtime truth only', () => {
|
|
698
|
+
const workspace = makeWorkspace();
|
|
699
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
700
|
+
{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' },
|
|
701
|
+
{ id: 'task-2', status: 'pending' },
|
|
702
|
+
]);
|
|
703
|
+
|
|
704
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
705
|
+
|
|
706
|
+
// Runtime truth drives eligibility
|
|
707
|
+
expect(summary.phase3.queueTruthReady).toBe(true);
|
|
708
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(true);
|
|
709
|
+
|
|
710
|
+
// Phase 3 eligibility source should be runtime truth
|
|
711
|
+
expect(summary.phase3.eligibilitySource).toBe('runtime_truth');
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('does not use analytics for Phase 3 eligibility', () => {
|
|
715
|
+
const workspace = makeWorkspace();
|
|
716
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
717
|
+
trust_score: 85,
|
|
718
|
+
frozen: true,
|
|
719
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
720
|
+
});
|
|
721
|
+
// Invalid runtime queue - no valid entries
|
|
722
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
723
|
+
{ id: 'task-1', status: 'resolved' }, // legacy status - invalid
|
|
724
|
+
]);
|
|
725
|
+
|
|
726
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
727
|
+
|
|
728
|
+
// Runtime truth invalid → not eligible, regardless of analytics
|
|
729
|
+
expect(summary.phase3.queueTruthReady).toBe(false);
|
|
730
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(false);
|
|
731
|
+
|
|
732
|
+
// Analytics data exists but is ignored for eligibility
|
|
733
|
+
expect(summary.analytics).toBeDefined();
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it('separates queue into runtime truth section only', () => {
|
|
737
|
+
const workspace = makeWorkspace();
|
|
738
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
739
|
+
trust_score: 75,
|
|
740
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
741
|
+
});
|
|
742
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
743
|
+
{ id: 'task-1', status: 'pending', score: 50 },
|
|
744
|
+
{ id: 'task-2', status: 'in_progress', score: 60 },
|
|
745
|
+
{ id: 'task-3', status: 'completed', score: 10 },
|
|
746
|
+
]);
|
|
747
|
+
|
|
748
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
749
|
+
|
|
750
|
+
// Queue data belongs to runtime truth
|
|
751
|
+
expect(summary.runtime.queueState.total).toBe(3);
|
|
752
|
+
expect(summary.runtime.queueState.pending).toBe(1);
|
|
753
|
+
expect(summary.runtime.queueState.inProgress).toBe(1);
|
|
754
|
+
expect(summary.runtime.queueState.completed).toBe(1);
|
|
755
|
+
expect(summary.runtime.queueState.lastUpdated).toBeDefined();
|
|
756
|
+
|
|
757
|
+
// Analytics section should NOT have queue data
|
|
758
|
+
expect(summary.analytics.trajectoryData).not.toHaveProperty('queue');
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it('surfaces reference-only Phase 3 evidence in the runtime summary', () => {
|
|
762
|
+
const workspace = makeWorkspace();
|
|
763
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
764
|
+
trust_score: 85,
|
|
765
|
+
frozen: true,
|
|
766
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
767
|
+
});
|
|
768
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
769
|
+
{ id: 'timeout-1', status: 'completed', resolution: 'auto_completed_timeout', completed_at: '2026-03-24T15:29:39.710Z' },
|
|
770
|
+
]);
|
|
771
|
+
|
|
772
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
773
|
+
|
|
774
|
+
expect(summary.phase3.queueTruthReady).toBe(true);
|
|
775
|
+
expect(summary.phase3.evolutionReferenceOnly).toBe(1);
|
|
776
|
+
expect(summary.phase3.evolutionReferenceOnlyReasons).toContain('timeout_only');
|
|
777
|
+
expect(summary.phase3.evolutionRejected).toBe(0);
|
|
778
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(false);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('includes lastUpdated timestamps in runtime truth section', () => {
|
|
782
|
+
const workspace = makeWorkspace();
|
|
783
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
784
|
+
{ id: 'task-1', status: 'pending' },
|
|
785
|
+
]);
|
|
786
|
+
|
|
787
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
788
|
+
|
|
789
|
+
// Runtime truth section includes timing metadata
|
|
790
|
+
expect(summary.runtime.queueState.lastUpdated).toBeDefined();
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it('populates activeSessions from session tracker in runtime truth', () => {
|
|
794
|
+
const workspace = makeWorkspace();
|
|
795
|
+
writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
|
|
796
|
+
trust_score: 75,
|
|
797
|
+
last_updated: '2026-03-20T10:00:00Z',
|
|
798
|
+
});
|
|
799
|
+
writeSession(workspace, 'session-1', {
|
|
800
|
+
currentGfi: 10,
|
|
801
|
+
lastActivityAt: 100,
|
|
802
|
+
});
|
|
803
|
+
writeSession(workspace, 'session-2', {
|
|
804
|
+
currentGfi: 20,
|
|
805
|
+
lastActivityAt: 200,
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
809
|
+
|
|
810
|
+
// Active sessions belong to runtime truth
|
|
811
|
+
expect(summary.runtime.activeSessions).toEqual(expect.any(Array));
|
|
812
|
+
expect(summary.runtime.activeSessions).toContain('session-1');
|
|
813
|
+
expect(summary.runtime.activeSessions).toContain('session-2');
|
|
814
|
+
|
|
815
|
+
// Analytics section should NOT have sessions
|
|
816
|
+
expect(summary.analytics).not.toHaveProperty('sessions');
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
821
|
+
// Task 3: Directive File Does Not Affect Phase 3 Eligibility
|
|
822
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
823
|
+
describe('Directive File Is Compatibility-Only Display Artifact', () => {
|
|
824
|
+
/**
|
|
825
|
+
* PURPOSE: Prove that EVOLUTION_DIRECTIVE.json does NOT affect Phase 3 eligibility.
|
|
826
|
+
* Directive is a compatibility-only display artifact, not a truth source.
|
|
827
|
+
* Phase 3 eligibility depends ONLY on queue.
|
|
828
|
+
*/
|
|
829
|
+
|
|
830
|
+
it('Phase 3 eligibility is same whether directive exists or not', () => {
|
|
831
|
+
const workspace = makeWorkspace();
|
|
832
|
+
|
|
833
|
+
// Valid queue for Phase 3
|
|
834
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
835
|
+
{ id: 'task-1', status: 'pending' },
|
|
836
|
+
]);
|
|
837
|
+
|
|
838
|
+
// Get summary WITHOUT directive file
|
|
839
|
+
const summaryWithoutDirective = RuntimeSummaryService.getSummary(workspace);
|
|
840
|
+
|
|
841
|
+
// Now add directive file with conflicting content
|
|
842
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
843
|
+
active: false, // Contradicts queue
|
|
844
|
+
task: 'completely different task',
|
|
845
|
+
taskId: 'different-id',
|
|
846
|
+
timestamp: '2026-03-15T10:00:00Z', // Old timestamp
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
const summaryWithDirective = RuntimeSummaryService.getSummary(workspace);
|
|
850
|
+
|
|
851
|
+
// Phase 3 eligibility should be IDENTICAL regardless of directive
|
|
852
|
+
expect(summaryWithDirective.phase3.phase3ShadowEligible).toBe(
|
|
853
|
+
summaryWithoutDirective.phase3.phase3ShadowEligible
|
|
854
|
+
);
|
|
855
|
+
expect(summaryWithDirective.phase3.queueTruthReady).toBe(
|
|
856
|
+
summaryWithoutDirective.phase3.queueTruthReady
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
// Both should be eligible because queue is valid
|
|
860
|
+
expect(summaryWithDirective.phase3.phase3ShadowEligible).toBe(true);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('Phase 3 eligibility is false when queue invalid, regardless of directive', () => {
|
|
864
|
+
const workspace = makeWorkspace();
|
|
865
|
+
|
|
866
|
+
// Invalid queue (legacy status)
|
|
867
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
868
|
+
{ id: 'task-1', status: 'resolved' }, // Legacy status
|
|
869
|
+
]);
|
|
870
|
+
|
|
871
|
+
// Add directive claiming everything is fine
|
|
872
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
873
|
+
active: true,
|
|
874
|
+
task: 'valid active task',
|
|
875
|
+
taskId: 'task-1',
|
|
876
|
+
timestamp: new Date().toISOString(),
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
880
|
+
|
|
881
|
+
// Phase 3 eligibility should be false despite directive claiming active
|
|
882
|
+
expect(summary.phase3.phase3ShadowEligible).toBe(false);
|
|
883
|
+
expect(summary.phase3.queueTruthReady).toBe(false);
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
it('directive summary shows queue-derived values, not file content', () => {
|
|
887
|
+
const workspace = makeWorkspace();
|
|
888
|
+
|
|
889
|
+
// Queue has in_progress task
|
|
890
|
+
writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
|
|
891
|
+
{
|
|
892
|
+
id: 'queue-task-123',
|
|
893
|
+
status: 'in_progress',
|
|
894
|
+
task: 'task from queue',
|
|
895
|
+
started_at: '2026-03-24T10:00:00Z',
|
|
896
|
+
},
|
|
897
|
+
]);
|
|
898
|
+
|
|
899
|
+
// Directive has completely different content (stale/mismatch)
|
|
900
|
+
writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
|
|
901
|
+
active: true,
|
|
902
|
+
task: 'stale task from directive file',
|
|
903
|
+
taskId: 'directive-task-999',
|
|
904
|
+
timestamp: '2026-03-15T10:00:00Z',
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
const summary = RuntimeSummaryService.getSummary(workspace);
|
|
908
|
+
|
|
909
|
+
// Directive summary should show queue-derived values
|
|
910
|
+
// The directive taskPreview should come from queue, not directive file
|
|
911
|
+
expect(summary.evolution.directive.exists).toBe(true);
|
|
912
|
+
expect(summary.evolution.directive.active).toBe(true);
|
|
913
|
+
|
|
914
|
+
// The directiveStatus should indicate it's compatibility-only
|
|
915
|
+
expect(summary.phase3.directiveStatus).toBe('compatibility-only');
|
|
916
|
+
expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
});
|