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,494 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
validateArtifact,
|
|
4
|
+
parseAndValidateArtifact,
|
|
5
|
+
type ArbiterResult,
|
|
6
|
+
} from '../../src/core/nocturnal-arbiter.js';
|
|
7
|
+
|
|
8
|
+
describe('Nocturnal Arbiter', () => {
|
|
9
|
+
// -------------------------------------------------------------------------
|
|
10
|
+
// Valid artifact factory
|
|
11
|
+
// -------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
function makeValidArtifact(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
14
|
+
return {
|
|
15
|
+
artifactId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
16
|
+
sessionId: 'session-abc123',
|
|
17
|
+
principleId: 'T-08',
|
|
18
|
+
sourceSnapshotRef: 'snapshot-2026-03-27-001',
|
|
19
|
+
badDecision: 'After bash command failed, immediately retried without diagnosing the root cause',
|
|
20
|
+
betterDecision: 'Check the error message and verify preconditions before retrying a failed bash command',
|
|
21
|
+
rationale: 'Treating each failure as a signal to diagnose rather than blindly retry prevents repeated failures',
|
|
22
|
+
createdAt: '2026-03-27T12:00:00.000Z',
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// -------------------------------------------------------------------------
|
|
28
|
+
// Tests: validateArtifact — valid inputs
|
|
29
|
+
// -------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
describe('validateArtifact', () => {
|
|
32
|
+
it('passes a valid artifact with all required fields', () => {
|
|
33
|
+
const artifact = makeValidArtifact();
|
|
34
|
+
const result = validateArtifact(artifact);
|
|
35
|
+
expect(result.passed).toBe(true);
|
|
36
|
+
expect(result.artifact).toBeDefined();
|
|
37
|
+
expect(result.failures).toHaveLength(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('passes with optional sourceSnapshotRef present', () => {
|
|
41
|
+
const artifact = makeValidArtifact({ sourceSnapshotRef: 'snapshot-custom-001' });
|
|
42
|
+
const result = validateArtifact(artifact);
|
|
43
|
+
expect(result.passed).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('passes when principleId and sessionId cross-validate against expected values', () => {
|
|
47
|
+
const artifact = makeValidArtifact({ principleId: 'T-08', sessionId: 'session-abc123' });
|
|
48
|
+
const result = validateArtifact(artifact, {
|
|
49
|
+
expectedPrincipleId: 'T-08',
|
|
50
|
+
expectedSessionId: 'session-abc123',
|
|
51
|
+
});
|
|
52
|
+
expect(result.passed).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('passes a minimal but complete artifact', () => {
|
|
56
|
+
// Only required fields, no optional
|
|
57
|
+
const artifact = {
|
|
58
|
+
artifactId: '11111111-2222-4333-aaaa-555555555555',
|
|
59
|
+
sessionId: 'session-minimal',
|
|
60
|
+
principleId: 'T-01',
|
|
61
|
+
badDecision: 'Edited a file without reading it first',
|
|
62
|
+
betterDecision: 'Read the file before editing to understand its current state',
|
|
63
|
+
rationale: 'Surveying the existing territory before making changes prevents conflicts',
|
|
64
|
+
createdAt: '2026-03-27T12:00:00.000Z',
|
|
65
|
+
};
|
|
66
|
+
const result = validateArtifact(artifact);
|
|
67
|
+
expect(result.passed).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// -------------------------------------------------------------------------
|
|
72
|
+
// Tests: validateArtifact — invalid JSON structure
|
|
73
|
+
// -------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
describe('validateArtifact — invalid JSON structure', () => {
|
|
76
|
+
it('rejects null', () => {
|
|
77
|
+
const result = validateArtifact(null);
|
|
78
|
+
expect(result.passed).toBe(false);
|
|
79
|
+
expect(result.failures[0].reason).toContain('must be a JSON object');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('rejects undefined', () => {
|
|
83
|
+
const result = validateArtifact(undefined);
|
|
84
|
+
expect(result.passed).toBe(false);
|
|
85
|
+
expect(result.failures[0].reason).toContain('must be a JSON object');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('rejects an array', () => {
|
|
89
|
+
const result = validateArtifact([{ artifactId: 'xxx' }]);
|
|
90
|
+
expect(result.passed).toBe(false);
|
|
91
|
+
expect(result.failures[0].reason).toContain('must be a JSON object');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('rejects a string', () => {
|
|
95
|
+
const result = validateArtifact('not an object');
|
|
96
|
+
expect(result.passed).toBe(false);
|
|
97
|
+
expect(result.failures[0].reason).toContain('must be a JSON object');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('rejects a number', () => {
|
|
101
|
+
const result = validateArtifact(42);
|
|
102
|
+
expect(result.passed).toBe(false);
|
|
103
|
+
expect(result.failures[0].reason).toContain('must be a JSON object');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// -------------------------------------------------------------------------
|
|
108
|
+
// Tests: validateArtifact — invalid flag from reflector
|
|
109
|
+
// -------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
describe('validateArtifact — invalid flag', () => {
|
|
112
|
+
it('rejects artifact with invalid: true', () => {
|
|
113
|
+
const artifact = makeValidArtifact({ invalid: true, reason: 'no clear violation found' });
|
|
114
|
+
const result = validateArtifact(artifact);
|
|
115
|
+
expect(result.passed).toBe(false);
|
|
116
|
+
expect(result.failures[0].reason).toContain('invalid');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('rejects artifact with invalid: "true" (string)', () => {
|
|
120
|
+
const artifact = makeValidArtifact({ invalid: 'true', reason: 'no violation' });
|
|
121
|
+
const result = validateArtifact(artifact);
|
|
122
|
+
expect(result.passed).toBe(false);
|
|
123
|
+
expect(result.failures[0].reason).toContain('invalid');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// Tests: validateArtifact — missing required fields
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
describe('validateArtifact — missing required fields', () => {
|
|
132
|
+
it('rejects missing artifactId', () => {
|
|
133
|
+
const artifact = makeValidArtifact();
|
|
134
|
+
delete artifact.artifactId;
|
|
135
|
+
const result = validateArtifact(artifact);
|
|
136
|
+
expect(result.passed).toBe(false);
|
|
137
|
+
expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('rejects empty artifactId', () => {
|
|
141
|
+
const artifact = makeValidArtifact({ artifactId: '' });
|
|
142
|
+
const result = validateArtifact(artifact);
|
|
143
|
+
expect(result.passed).toBe(false);
|
|
144
|
+
expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('rejects whitespace-only artifactId', () => {
|
|
148
|
+
const artifact = makeValidArtifact({ artifactId: ' ' });
|
|
149
|
+
const result = validateArtifact(artifact);
|
|
150
|
+
expect(result.passed).toBe(false);
|
|
151
|
+
expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('rejects missing sessionId', () => {
|
|
155
|
+
const artifact = makeValidArtifact();
|
|
156
|
+
delete artifact.sessionId;
|
|
157
|
+
const result = validateArtifact(artifact);
|
|
158
|
+
expect(result.passed).toBe(false);
|
|
159
|
+
expect(result.failures.some(f => f.field === 'sessionId')).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('rejects missing principleId', () => {
|
|
163
|
+
const artifact = makeValidArtifact();
|
|
164
|
+
delete artifact.principleId;
|
|
165
|
+
const result = validateArtifact(artifact);
|
|
166
|
+
expect(result.passed).toBe(false);
|
|
167
|
+
expect(result.failures.some(f => f.field === 'principleId')).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('rejects missing badDecision', () => {
|
|
171
|
+
const artifact = makeValidArtifact();
|
|
172
|
+
delete artifact.badDecision;
|
|
173
|
+
const result = validateArtifact(artifact);
|
|
174
|
+
expect(result.passed).toBe(false);
|
|
175
|
+
expect(result.failures.some(f => f.field === 'badDecision')).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('rejects missing betterDecision', () => {
|
|
179
|
+
const artifact = makeValidArtifact();
|
|
180
|
+
delete artifact.betterDecision;
|
|
181
|
+
const result = validateArtifact(artifact);
|
|
182
|
+
expect(result.passed).toBe(false);
|
|
183
|
+
expect(result.failures.some(f => f.field === 'betterDecision')).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('rejects missing rationale', () => {
|
|
187
|
+
const artifact = makeValidArtifact();
|
|
188
|
+
delete artifact.rationale;
|
|
189
|
+
const result = validateArtifact(artifact);
|
|
190
|
+
expect(result.passed).toBe(false);
|
|
191
|
+
expect(result.failures.some(f => f.field === 'rationale')).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('rejects missing createdAt', () => {
|
|
195
|
+
const artifact = makeValidArtifact();
|
|
196
|
+
delete artifact.createdAt;
|
|
197
|
+
const result = validateArtifact(artifact);
|
|
198
|
+
expect(result.passed).toBe(false);
|
|
199
|
+
expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('rejects empty createdAt', () => {
|
|
203
|
+
const artifact = makeValidArtifact({ createdAt: '' });
|
|
204
|
+
const result = validateArtifact(artifact);
|
|
205
|
+
expect(result.passed).toBe(false);
|
|
206
|
+
expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// -------------------------------------------------------------------------
|
|
211
|
+
// Tests: validateArtifact — invalid field formats
|
|
212
|
+
// -------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
describe('validateArtifact — invalid field formats', () => {
|
|
215
|
+
it('rejects artifactId that is not a valid UUID', () => {
|
|
216
|
+
const artifact = makeValidArtifact({ artifactId: 'not-a-uuid' });
|
|
217
|
+
const result = validateArtifact(artifact);
|
|
218
|
+
expect(result.passed).toBe(false);
|
|
219
|
+
expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('rejects createdAt that is not ISO 8601', () => {
|
|
223
|
+
const artifact = makeValidArtifact({ createdAt: '2026-03-27' });
|
|
224
|
+
const result = validateArtifact(artifact);
|
|
225
|
+
expect(result.passed).toBe(false);
|
|
226
|
+
expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('rejects createdAt with time but no seconds', () => {
|
|
230
|
+
const artifact = makeValidArtifact({ createdAt: '2026-03-27T12:00Z' });
|
|
231
|
+
const result = validateArtifact(artifact);
|
|
232
|
+
expect(result.passed).toBe(false);
|
|
233
|
+
expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('rejects createdAt with milliseconds (valid ISO but our format is different)', () => {
|
|
237
|
+
// Our regex requires .SSS after T
|
|
238
|
+
const artifact = makeValidArtifact({ createdAt: '2026-03-27T12:00:00Z' });
|
|
239
|
+
const result = validateArtifact(artifact);
|
|
240
|
+
// Actually this should pass — our regex allows optional .SSS
|
|
241
|
+
// Let's test the other direction
|
|
242
|
+
const artifact2 = makeValidArtifact({ createdAt: '2026-03-27T12:00:00.123Z' });
|
|
243
|
+
const result2 = validateArtifact(artifact2);
|
|
244
|
+
expect(result2.passed).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// -------------------------------------------------------------------------
|
|
249
|
+
// Tests: validateArtifact — cross-validation
|
|
250
|
+
// -------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
describe('validateArtifact — cross-validation', () => {
|
|
253
|
+
it('rejects principleId mismatch', () => {
|
|
254
|
+
const artifact = makeValidArtifact({ principleId: 'T-08' });
|
|
255
|
+
const result = validateArtifact(artifact, { expectedPrincipleId: 'T-01' });
|
|
256
|
+
expect(result.passed).toBe(false);
|
|
257
|
+
expect(result.failures.some(f => f.reason.includes('principleId mismatch'))).toBe(true);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('rejects sessionId mismatch', () => {
|
|
261
|
+
const artifact = makeValidArtifact({ sessionId: 'session-abc' });
|
|
262
|
+
const result = validateArtifact(artifact, { expectedSessionId: 'session-xyz' });
|
|
263
|
+
expect(result.passed).toBe(false);
|
|
264
|
+
expect(result.failures.some(f => f.reason.includes('sessionId mismatch'))).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('passes when principleId matches expected', () => {
|
|
268
|
+
const artifact = makeValidArtifact({ principleId: 'T-08' });
|
|
269
|
+
const result = validateArtifact(artifact, { expectedPrincipleId: 'T-08' });
|
|
270
|
+
expect(result.passed).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('passes when sessionId matches expected', () => {
|
|
274
|
+
const artifact = makeValidArtifact({ sessionId: 'session-abc' });
|
|
275
|
+
const result = validateArtifact(artifact, { expectedSessionId: 'session-abc' });
|
|
276
|
+
expect(result.passed).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('passes when no expected values provided', () => {
|
|
280
|
+
const artifact = makeValidArtifact({ principleId: 'T-99', sessionId: 'session-xyz' });
|
|
281
|
+
const result = validateArtifact(artifact);
|
|
282
|
+
expect(result.passed).toBe(true);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// -------------------------------------------------------------------------
|
|
287
|
+
// Tests: validateArtifact — placeholder detection
|
|
288
|
+
// -------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
describe('validateArtifact — placeholder detection', () => {
|
|
291
|
+
const placeholderTests = [
|
|
292
|
+
{ value: '<placeholder>', expected: true },
|
|
293
|
+
{ value: '<uuid>', expected: true },
|
|
294
|
+
{ value: '<session-id>', expected: true },
|
|
295
|
+
{ value: '<principle-id>', expected: true },
|
|
296
|
+
{ value: 'undefined', expected: true },
|
|
297
|
+
{ value: 'null', expected: true },
|
|
298
|
+
{ value: 'N/A', expected: true },
|
|
299
|
+
{ value: 'TODO', expected: true },
|
|
300
|
+
{ value: 'FIXME', expected: true },
|
|
301
|
+
{ value: 'valid decision text', expected: false },
|
|
302
|
+
{ value: 'Read the file before editing', expected: false },
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
placeholderTests.forEach(({ value, expected }) => {
|
|
306
|
+
it(`badDecision: '${value}' → ${expected ? 'rejected' : 'accepted'}`, () => {
|
|
307
|
+
const artifact = makeValidArtifact({ badDecision: value });
|
|
308
|
+
const result = validateArtifact(artifact);
|
|
309
|
+
if (expected) {
|
|
310
|
+
expect(result.passed).toBe(false);
|
|
311
|
+
expect(result.failures.some(f => f.reason.includes('placeholder'))).toBe(true);
|
|
312
|
+
} else {
|
|
313
|
+
expect(result.passed).toBe(true);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('rejects betterDecision containing placeholder', () => {
|
|
319
|
+
const artifact = makeValidArtifact({ betterDecision: '<action>' });
|
|
320
|
+
const result = validateArtifact(artifact);
|
|
321
|
+
expect(result.passed).toBe(false);
|
|
322
|
+
expect(result.failures.some(f => f.field === 'betterDecision')).toBe(true);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('rejects rationale containing placeholder', () => {
|
|
326
|
+
const artifact = makeValidArtifact({ rationale: 'TODO: add rationale' });
|
|
327
|
+
const result = validateArtifact(artifact);
|
|
328
|
+
expect(result.passed).toBe(false);
|
|
329
|
+
expect(result.failures.some(f => f.field === 'rationale')).toBe(true);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// -------------------------------------------------------------------------
|
|
334
|
+
// Tests: validateArtifact — raw content detection
|
|
335
|
+
// -------------------------------------------------------------------------
|
|
336
|
+
|
|
337
|
+
describe('validateArtifact — raw content detection', () => {
|
|
338
|
+
it('rejects badDecision containing function definition', () => {
|
|
339
|
+
const artifact = makeValidArtifact({ badDecision: 'function handleError() { return 1; }' });
|
|
340
|
+
const result = validateArtifact(artifact);
|
|
341
|
+
expect(result.passed).toBe(false);
|
|
342
|
+
expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('rejects badDecision containing import statement', () => {
|
|
346
|
+
const artifact = makeValidArtifact({ badDecision: 'import { foo } from "./bar"; doSomething();' });
|
|
347
|
+
const result = validateArtifact(artifact);
|
|
348
|
+
expect(result.passed).toBe(false);
|
|
349
|
+
expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('rejects betterDecision containing credential pattern', () => {
|
|
353
|
+
const artifact = makeValidArtifact({ betterDecision: 'Check the api_key before proceeding' });
|
|
354
|
+
const result = validateArtifact(artifact);
|
|
355
|
+
expect(result.passed).toBe(false);
|
|
356
|
+
expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('accepts text without raw content patterns', () => {
|
|
360
|
+
const artifact = makeValidArtifact({
|
|
361
|
+
badDecision: 'Proceeded with editing without reading the file first',
|
|
362
|
+
betterDecision: 'Read the file to understand its current structure before making edits',
|
|
363
|
+
});
|
|
364
|
+
const result = validateArtifact(artifact);
|
|
365
|
+
expect(result.passed).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// -------------------------------------------------------------------------
|
|
370
|
+
// Tests: validateArtifact — semantic rules
|
|
371
|
+
// -------------------------------------------------------------------------
|
|
372
|
+
|
|
373
|
+
describe('validateArtifact — semantic rules', () => {
|
|
374
|
+
it('rejects identical badDecision and betterDecision', () => {
|
|
375
|
+
const artifact = makeValidArtifact({
|
|
376
|
+
badDecision: 'Read the file first',
|
|
377
|
+
betterDecision: 'Read the file first',
|
|
378
|
+
});
|
|
379
|
+
const result = validateArtifact(artifact);
|
|
380
|
+
expect(result.passed).toBe(false);
|
|
381
|
+
expect(result.failures.some(f => f.reason.includes('identical'))).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('accepts different badDecision and betterDecision', () => {
|
|
385
|
+
const artifact = makeValidArtifact({
|
|
386
|
+
badDecision: 'Edited without reading',
|
|
387
|
+
betterDecision: 'Read the file before editing',
|
|
388
|
+
});
|
|
389
|
+
const result = validateArtifact(artifact);
|
|
390
|
+
expect(result.passed).toBe(true);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('rejects rationale that is too short', () => {
|
|
394
|
+
const artifact = makeValidArtifact({ rationale: 'Because.' });
|
|
395
|
+
const result = validateArtifact(artifact);
|
|
396
|
+
expect(result.passed).toBe(false);
|
|
397
|
+
expect(result.failures.some(f => f.reason.includes('too short'))).toBe(true);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('accepts rationale at minimum length (20 chars)', () => {
|
|
401
|
+
const artifact = makeValidArtifact({ rationale: '12345678901234567890' }); // exactly 20
|
|
402
|
+
const result = validateArtifact(artifact);
|
|
403
|
+
expect(result.passed).toBe(true);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('rejects sourceSnapshotRef that is present but empty', () => {
|
|
407
|
+
const artifact = makeValidArtifact({ sourceSnapshotRef: '' });
|
|
408
|
+
const result = validateArtifact(artifact);
|
|
409
|
+
expect(result.passed).toBe(false);
|
|
410
|
+
expect(result.failures.some(f => f.field === 'sourceSnapshotRef')).toBe(true);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// -------------------------------------------------------------------------
|
|
415
|
+
// Tests: parseAndValidateArtifact
|
|
416
|
+
// -------------------------------------------------------------------------
|
|
417
|
+
|
|
418
|
+
describe('parseAndValidateArtifact', () => {
|
|
419
|
+
it('parses a valid JSON string and validates successfully', () => {
|
|
420
|
+
const json = JSON.stringify(makeValidArtifact());
|
|
421
|
+
const result = parseAndValidateArtifact(json);
|
|
422
|
+
expect(result.passed).toBe(true);
|
|
423
|
+
expect(result.artifact).toBeDefined();
|
|
424
|
+
expect(result.artifact?.principleId).toBe('T-08');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('fails on invalid JSON', () => {
|
|
428
|
+
const result = parseAndValidateArtifact('not valid json {');
|
|
429
|
+
expect(result.passed).toBe(false);
|
|
430
|
+
expect(result.failures[0].reason).toContain('Failed to parse JSON');
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('fails on empty string', () => {
|
|
434
|
+
const result = parseAndValidateArtifact('');
|
|
435
|
+
expect(result.passed).toBe(false);
|
|
436
|
+
expect(result.failures[0].reason).toContain('Failed to parse JSON');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('passes cross-validation from options', () => {
|
|
440
|
+
const artifact = makeValidArtifact({ principleId: 'T-01', sessionId: 'session-xyz' });
|
|
441
|
+
const json = JSON.stringify(artifact);
|
|
442
|
+
const result = parseAndValidateArtifact(json, {
|
|
443
|
+
expectedPrincipleId: 'T-01',
|
|
444
|
+
expectedSessionId: 'session-xyz',
|
|
445
|
+
});
|
|
446
|
+
expect(result.passed).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('fails cross-validation from options on mismatch', () => {
|
|
450
|
+
const artifact = makeValidArtifact({ principleId: 'T-01' });
|
|
451
|
+
const json = JSON.stringify(artifact);
|
|
452
|
+
const result = parseAndValidateArtifact(json, { expectedPrincipleId: 'T-99' });
|
|
453
|
+
expect(result.passed).toBe(false);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// -------------------------------------------------------------------------
|
|
458
|
+
// Tests: constructed artifact output shape
|
|
459
|
+
// -------------------------------------------------------------------------
|
|
460
|
+
|
|
461
|
+
describe('constructed artifact shape', () => {
|
|
462
|
+
it('returns correct NocturnalArtifact shape when passed', () => {
|
|
463
|
+
const artifact = makeValidArtifact();
|
|
464
|
+
const result = validateArtifact(artifact);
|
|
465
|
+
expect(result.passed).toBe(true);
|
|
466
|
+
expect(result.artifact).toEqual({
|
|
467
|
+
artifactId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
468
|
+
sessionId: 'session-abc123',
|
|
469
|
+
principleId: 'T-08',
|
|
470
|
+
sourceSnapshotRef: 'snapshot-2026-03-27-001',
|
|
471
|
+
badDecision: 'After bash command failed, immediately retried without diagnosing the root cause',
|
|
472
|
+
betterDecision: 'Check the error message and verify preconditions before retrying a failed bash command',
|
|
473
|
+
rationale: 'Treating each failure as a signal to diagnose rather than blindly retry prevents repeated failures',
|
|
474
|
+
createdAt: '2026-03-27T12:00:00.000Z',
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('converts all fields to strings even if passed as numbers', () => {
|
|
479
|
+
// artifactId is normally string, but test robustness
|
|
480
|
+
const artifact = makeValidArtifact() as Record<string, unknown>;
|
|
481
|
+
const result = validateArtifact(artifact);
|
|
482
|
+
expect(result.passed).toBe(true);
|
|
483
|
+
expect(typeof result.artifact?.artifactId).toBe('string');
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('uses empty string for missing sourceSnapshotRef', () => {
|
|
487
|
+
const artifact = makeValidArtifact();
|
|
488
|
+
delete artifact.sourceSnapshotRef;
|
|
489
|
+
const result = validateArtifact(artifact);
|
|
490
|
+
expect(result.passed).toBe(true);
|
|
491
|
+
expect(result.artifact?.sourceSnapshotRef).toBe('');
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
});
|