principles-disciple 1.8.1 → 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 +4 -4
- package/package.json +11 -13
- 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} +175 -62
- 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} +160 -80
- 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} +235 -79
- 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/{dist/service/subagent-workflow/empathy-observer-workflow-manager.js → src/service/subagent-workflow/empathy-observer-workflow-manager.ts} +240 -117
- 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/{dist/service/subagent-workflow/types.d.ts → src/service/subagent-workflow/types.ts} +137 -18
- 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 -129
- 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 -101
- 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 -13
- 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 -52
- package/dist/hooks/progressive-trust-gate.js +0 -134
- package/dist/hooks/prompt.d.ts +0 -49
- package/dist/hooks/prompt.js +0 -905
- 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 -681
- 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 -88
- package/dist/service/empathy-observer-manager.js +0 -414
- 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 -975
- package/dist/service/health-query-service.d.ts +0 -170
- package/dist/service/health-query-service.js +0 -662
- 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/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
- package/dist/service/subagent-workflow/index.d.ts +0 -4
- package/dist/service/subagent-workflow/index.js +0 -3
- package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
- package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
- package/dist/service/subagent-workflow/types.js +0 -11
- package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
- package/dist/service/subagent-workflow/workflow-store.js +0 -165
- 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,668 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nocturnal Dataset — Sample Lineage Store and Review State Registry
|
|
3
|
+
* =================================================================
|
|
4
|
+
*
|
|
5
|
+
* PURPOSE: Establish each approved nocturnal sample as a first-class auditable
|
|
6
|
+
* data asset with fingerprint, lineage, review state, and model family binding.
|
|
7
|
+
*
|
|
8
|
+
* ARCHITECTURE:
|
|
9
|
+
* - Registry file: {stateDir}/.state/nocturnal/dataset-registry.json
|
|
10
|
+
* - One JSON array of NocturnalDatasetRecord
|
|
11
|
+
* - Each record is immutable except for reviewStatus and reviewReason
|
|
12
|
+
* - sampleFingerprint is the primary key (deterministic: SHA-256 of artifactId+principleId+sessionId)
|
|
13
|
+
*
|
|
14
|
+
* RELATIONSHIP TO NOCTURNAL ARTIFACTS:
|
|
15
|
+
* - Artifacts live in: .state/nocturnal/samples/{artifactId}.json
|
|
16
|
+
* - Dataset records reference artifacts via artifactId and artifactPath
|
|
17
|
+
* - Artifacts are NOT modified by dataset operations
|
|
18
|
+
*
|
|
19
|
+
* DESIGN CONSTRAINTS:
|
|
20
|
+
* - No training run registry (Phase 4)
|
|
21
|
+
* - No checkpoint registry (Phase 4)
|
|
22
|
+
* - No worker routing changes
|
|
23
|
+
* - No JSONL export (that's Task 3.2)
|
|
24
|
+
* - Lineage is append-only for approved records
|
|
25
|
+
* - reviewStatus transitions are the only state mutations allowed
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import * as fs from 'fs';
|
|
29
|
+
import * as path from 'path';
|
|
30
|
+
import * as crypto from 'crypto';
|
|
31
|
+
import { NocturnalPathResolver, resolveNocturnalDir } from './nocturnal-paths.js';
|
|
32
|
+
import type { NocturnalArtifact } from './nocturnal-arbiter.js';
|
|
33
|
+
import { withLock } from '../utils/file-lock.js';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Types
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Review status for a nocturnal dataset sample.
|
|
41
|
+
* Follows the lifecycle: pending_review → approved_for_training | rejected | superseded
|
|
42
|
+
*/
|
|
43
|
+
export type NocturnalReviewStatus =
|
|
44
|
+
| 'pending_review'
|
|
45
|
+
| 'approved_for_training'
|
|
46
|
+
| 'rejected'
|
|
47
|
+
| 'superseded';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A nocturnal dataset record — the immutable lineage entry for one sample.
|
|
51
|
+
*
|
|
52
|
+
* PRIMARY KEY: sampleFingerprint (deterministic SHA-256)
|
|
53
|
+
* MUTABLE FIELDS: reviewStatus, reviewReason only
|
|
54
|
+
* IMMUTABLE FIELDS: all others
|
|
55
|
+
*/
|
|
56
|
+
export interface NocturnalDatasetRecord {
|
|
57
|
+
/**
|
|
58
|
+
* Deterministic fingerprint: SHA-256(artifactId + principleId + sessionId).
|
|
59
|
+
* Primary key for dataset operations.
|
|
60
|
+
*/
|
|
61
|
+
sampleFingerprint: string;
|
|
62
|
+
|
|
63
|
+
/** Reference to the original artifact */
|
|
64
|
+
artifactId: string;
|
|
65
|
+
|
|
66
|
+
/** Source session */
|
|
67
|
+
sessionId: string;
|
|
68
|
+
|
|
69
|
+
/** Target principle that generated this sample */
|
|
70
|
+
principleId: string;
|
|
71
|
+
|
|
72
|
+
/** Reference to the trajectory snapshot used */
|
|
73
|
+
sourceSnapshotRef: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Current review state.
|
|
77
|
+
* Only transitions allowed: pending_review → approved_for_training | rejected | superseded
|
|
78
|
+
*/
|
|
79
|
+
reviewStatus: NocturnalReviewStatus;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Human-provided reason for the review decision.
|
|
83
|
+
* Required for approved_for_training and rejected; optional for superseded.
|
|
84
|
+
*/
|
|
85
|
+
reviewReason?: string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Target model family this sample is bound to.
|
|
89
|
+
* REQUIRED for export-ready samples.
|
|
90
|
+
* NULL means "not yet assigned" (pending_review defaults to null).
|
|
91
|
+
*/
|
|
92
|
+
targetModelFamily: string | null;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* When this sample was first registered in the dataset.
|
|
96
|
+
*/
|
|
97
|
+
createdAt: string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Last time reviewStatus or reviewReason was updated.
|
|
101
|
+
*/
|
|
102
|
+
updatedAt: string;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Absolute path to the artifact file.
|
|
106
|
+
*/
|
|
107
|
+
artifactPath: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Filter options for listing dataset records.
|
|
112
|
+
*/
|
|
113
|
+
export interface DatasetFilterOptions {
|
|
114
|
+
/**
|
|
115
|
+
* Filter by review status.
|
|
116
|
+
*/
|
|
117
|
+
reviewStatus?: NocturnalReviewStatus | NocturnalReviewStatus[];
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Filter by target model family.
|
|
121
|
+
* NULL means "any" (including null/unassigned).
|
|
122
|
+
*/
|
|
123
|
+
targetModelFamily?: string | null;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Include only export-ready records.
|
|
127
|
+
* An export-ready record must have:
|
|
128
|
+
* - reviewStatus === 'approved_for_training'
|
|
129
|
+
* - targetModelFamily !== null
|
|
130
|
+
* - artifactPath points to an existing file
|
|
131
|
+
*/
|
|
132
|
+
exportReadyOnly?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Result of registering a sample.
|
|
137
|
+
*/
|
|
138
|
+
export interface RegisterSampleResult {
|
|
139
|
+
/** The registered record */
|
|
140
|
+
record: NocturnalDatasetRecord;
|
|
141
|
+
/** Whether this was a new registration (true) or duplicate link (false) */
|
|
142
|
+
isNew: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* If isNew === false, this points to the existing record.
|
|
145
|
+
*/
|
|
146
|
+
existingRecord?: NocturnalDatasetRecord;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Fingerprint Generation
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate a deterministic sample fingerprint from an artifact.
|
|
155
|
+
*
|
|
156
|
+
* FINGERPRINT = SHA-256(artifactId || principleId || sessionId)
|
|
157
|
+
*
|
|
158
|
+
* The fingerprint is deterministic so the same sample always produces
|
|
159
|
+
* the same fingerprint, enabling duplicate detection.
|
|
160
|
+
*/
|
|
161
|
+
export function generateSampleFingerprint(
|
|
162
|
+
artifactId: string,
|
|
163
|
+
principleId: string,
|
|
164
|
+
sessionId: string
|
|
165
|
+
): string {
|
|
166
|
+
const input = `${artifactId}|${principleId}|${sessionId}`;
|
|
167
|
+
return crypto.createHash('sha256').update(input, 'utf8').digest('hex');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generate a fingerprint from an existing NocturnalArtifact.
|
|
172
|
+
*/
|
|
173
|
+
export function generateFingerprintFromArtifact(artifact: NocturnalArtifact): string {
|
|
174
|
+
return generateSampleFingerprint(
|
|
175
|
+
artifact.artifactId,
|
|
176
|
+
artifact.principleId,
|
|
177
|
+
artifact.sessionId
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Registry Path
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Path to the dataset registry file.
|
|
187
|
+
*/
|
|
188
|
+
function getRegistryPath(workspaceDir: string): string {
|
|
189
|
+
// Registry lives in .state/nocturnal/dataset-registry.json
|
|
190
|
+
const nocturnalRoot = resolveNocturnalDir(workspaceDir, 'ROOT');
|
|
191
|
+
return path.join(nocturnalRoot, 'dataset-registry.json');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Ensure the registry directory exists.
|
|
196
|
+
*/
|
|
197
|
+
function ensureRegistryDir(workspaceDir: string): void {
|
|
198
|
+
const registryPath = getRegistryPath(workspaceDir);
|
|
199
|
+
const dir = path.dirname(registryPath);
|
|
200
|
+
if (!fs.existsSync(dir)) {
|
|
201
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Read the registry file. Returns empty array if missing.
|
|
207
|
+
*/
|
|
208
|
+
function readRegistry(workspaceDir: string): NocturnalDatasetRecord[] {
|
|
209
|
+
const registryPath = getRegistryPath(workspaceDir);
|
|
210
|
+
if (!fs.existsSync(registryPath)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const content = fs.readFileSync(registryPath, 'utf-8');
|
|
215
|
+
return JSON.parse(content) as NocturnalDatasetRecord[];
|
|
216
|
+
} catch (err) {
|
|
217
|
+
// Corrupted registry — fail-safe to empty array, but log the problem
|
|
218
|
+
console.warn(`[nocturnal-dataset] Registry corrupted at ${registryPath}, recovering with empty state: ${String(err)}`);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Write the registry file atomically (write-then-rename for atomicity).
|
|
225
|
+
* Caller must hold the registry lock (via withRegistryLock).
|
|
226
|
+
*/
|
|
227
|
+
function writeRegistry(workspaceDir: string, records: NocturnalDatasetRecord[]): void {
|
|
228
|
+
ensureRegistryDir(workspaceDir);
|
|
229
|
+
const registryPath = getRegistryPath(workspaceDir);
|
|
230
|
+
const tmpPath = `${registryPath}.tmp`;
|
|
231
|
+
fs.writeFileSync(tmpPath, JSON.stringify(records, null, 2), 'utf-8');
|
|
232
|
+
fs.renameSync(tmpPath, registryPath);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// Core Operations
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Execute a read-modify-write on the registry under an exclusive lock.
|
|
241
|
+
* This prevents concurrent writers from racing on the same file.
|
|
242
|
+
*/
|
|
243
|
+
function withRegistryLock<T>(workspaceDir: string, fn: (records: NocturnalDatasetRecord[]) => T): T {
|
|
244
|
+
const registryPath = getRegistryPath(workspaceDir);
|
|
245
|
+
return withLock(registryPath, () => {
|
|
246
|
+
const records = readRegistry(workspaceDir);
|
|
247
|
+
return fn(records);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Register an approved nocturnal artifact in the dataset registry.
|
|
253
|
+
*
|
|
254
|
+
* DUPLICATE HANDLING:
|
|
255
|
+
* - If a record with the same sampleFingerprint already exists, returns
|
|
256
|
+
* existingRecord (isNew === false) instead of creating a duplicate.
|
|
257
|
+
* - The original artifact file is never modified.
|
|
258
|
+
*
|
|
259
|
+
* @param workspaceDir - Workspace directory
|
|
260
|
+
* @param artifact - The approved NocturnalArtifact
|
|
261
|
+
* @param artifactPath - Absolute path where the artifact file is stored
|
|
262
|
+
* @param targetModelFamily - Model family binding (required for export-ready)
|
|
263
|
+
* @returns RegisterSampleResult
|
|
264
|
+
*/
|
|
265
|
+
export function registerSample(
|
|
266
|
+
workspaceDir: string,
|
|
267
|
+
artifact: NocturnalArtifact,
|
|
268
|
+
artifactPath: string,
|
|
269
|
+
targetModelFamily: string | null = null
|
|
270
|
+
): RegisterSampleResult {
|
|
271
|
+
const fingerprint = generateFingerprintFromArtifact(artifact);
|
|
272
|
+
const now = new Date().toISOString();
|
|
273
|
+
|
|
274
|
+
return withRegistryLock(workspaceDir, (records) => {
|
|
275
|
+
const existing = records.find((r) => r.sampleFingerprint === fingerprint);
|
|
276
|
+
if (existing) {
|
|
277
|
+
return {
|
|
278
|
+
record: existing,
|
|
279
|
+
isNew: false,
|
|
280
|
+
existingRecord: existing,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const record: NocturnalDatasetRecord = {
|
|
285
|
+
sampleFingerprint: fingerprint,
|
|
286
|
+
artifactId: artifact.artifactId,
|
|
287
|
+
sessionId: artifact.sessionId,
|
|
288
|
+
principleId: artifact.principleId,
|
|
289
|
+
sourceSnapshotRef: artifact.sourceSnapshotRef,
|
|
290
|
+
reviewStatus: 'pending_review',
|
|
291
|
+
reviewReason: undefined,
|
|
292
|
+
targetModelFamily,
|
|
293
|
+
createdAt: now,
|
|
294
|
+
updatedAt: now,
|
|
295
|
+
artifactPath: path.normalize(artifactPath),
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
records.push(record);
|
|
299
|
+
writeRegistry(workspaceDir, records);
|
|
300
|
+
|
|
301
|
+
return { record, isNew: true };
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get a dataset record by fingerprint.
|
|
307
|
+
*/
|
|
308
|
+
export function getDatasetRecord(
|
|
309
|
+
workspaceDir: string,
|
|
310
|
+
sampleFingerprint: string
|
|
311
|
+
): NocturnalDatasetRecord | null {
|
|
312
|
+
const records = readRegistry(workspaceDir);
|
|
313
|
+
return records.find((r) => r.sampleFingerprint === sampleFingerprint) ?? null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get a dataset record by artifactId.
|
|
318
|
+
*/
|
|
319
|
+
export function getDatasetRecordByArtifactId(
|
|
320
|
+
workspaceDir: string,
|
|
321
|
+
artifactId: string
|
|
322
|
+
): NocturnalDatasetRecord | null {
|
|
323
|
+
const records = readRegistry(workspaceDir);
|
|
324
|
+
return records.find((r) => r.artifactId === artifactId) ?? null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* List dataset records with optional filtering.
|
|
329
|
+
*
|
|
330
|
+
* @param workspaceDir - Workspace directory
|
|
331
|
+
* @param filter - Optional filter criteria
|
|
332
|
+
* @returns Filtered records sorted by createdAt descending
|
|
333
|
+
*/
|
|
334
|
+
export function listDatasetRecords(
|
|
335
|
+
workspaceDir: string,
|
|
336
|
+
filter?: DatasetFilterOptions
|
|
337
|
+
): NocturnalDatasetRecord[] {
|
|
338
|
+
let records = readRegistry(workspaceDir);
|
|
339
|
+
|
|
340
|
+
if (!filter) {
|
|
341
|
+
return records.sort(
|
|
342
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Filter by reviewStatus
|
|
347
|
+
if (filter.reviewStatus !== undefined) {
|
|
348
|
+
const statuses = Array.isArray(filter.reviewStatus)
|
|
349
|
+
? filter.reviewStatus
|
|
350
|
+
: [filter.reviewStatus];
|
|
351
|
+
records = records.filter((r) => statuses.includes(r.reviewStatus));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Filter by targetModelFamily
|
|
355
|
+
if (filter.targetModelFamily !== undefined) {
|
|
356
|
+
if (filter.targetModelFamily === null) {
|
|
357
|
+
// Include only null/unassigned
|
|
358
|
+
records = records.filter((r) => r.targetModelFamily === null);
|
|
359
|
+
} else {
|
|
360
|
+
records = records.filter((r) => r.targetModelFamily === filter.targetModelFamily);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Filter export-ready only
|
|
365
|
+
if (filter.exportReadyOnly === true) {
|
|
366
|
+
records = records.filter((r) => {
|
|
367
|
+
if (r.reviewStatus !== 'approved_for_training') return false;
|
|
368
|
+
if (r.targetModelFamily === null) return false;
|
|
369
|
+
// Verify artifact file exists
|
|
370
|
+
if (!fs.existsSync(r.artifactPath)) return false;
|
|
371
|
+
return true;
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return records.sort(
|
|
376
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Valid review status transitions.
|
|
382
|
+
*pending_review → approved_for_training | rejected | superseded
|
|
383
|
+
* approved_for_training → superseded (if a better sample replaces it)
|
|
384
|
+
* rejected → pending_review (if re-review is requested)
|
|
385
|
+
* superseded → (terminal state, no transitions)
|
|
386
|
+
*/
|
|
387
|
+
const VALID_TRANSITIONS: Record<NocturnalReviewStatus, NocturnalReviewStatus[]> = {
|
|
388
|
+
pending_review: ['approved_for_training', 'rejected', 'superseded'],
|
|
389
|
+
approved_for_training: ['superseded'],
|
|
390
|
+
rejected: ['pending_review', 'superseded'],
|
|
391
|
+
superseded: [], // terminal
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Update the review status of a dataset record.
|
|
396
|
+
*
|
|
397
|
+
* @param workspaceDir - Workspace directory
|
|
398
|
+
* @param sampleFingerprint - The fingerprint of the record to update
|
|
399
|
+
* @param newStatus - The new review status
|
|
400
|
+
* @param reason - Optional reason (required for approved/rejected per spec)
|
|
401
|
+
* @returns Updated record, or null if not found
|
|
402
|
+
* @throws Error if transition is invalid
|
|
403
|
+
*/
|
|
404
|
+
export function updateReviewStatus(
|
|
405
|
+
workspaceDir: string,
|
|
406
|
+
sampleFingerprint: string,
|
|
407
|
+
newStatus: NocturnalReviewStatus,
|
|
408
|
+
reason?: string
|
|
409
|
+
): NocturnalDatasetRecord {
|
|
410
|
+
return withRegistryLock(workspaceDir, (records) => {
|
|
411
|
+
const idx = records.findIndex((r) => r.sampleFingerprint === sampleFingerprint);
|
|
412
|
+
|
|
413
|
+
if (idx === -1) {
|
|
414
|
+
throw new Error(`Dataset record not found: ${sampleFingerprint}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const record = records[idx];
|
|
418
|
+
|
|
419
|
+
// Validate transition
|
|
420
|
+
const allowed = VALID_TRANSITIONS[record.reviewStatus];
|
|
421
|
+
if (!allowed.includes(newStatus)) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`Invalid review status transition: ${record.reviewStatus} → ${newStatus}. ` +
|
|
424
|
+
`Allowed transitions from ${record.reviewStatus}: ${allowed.join(', ') || 'none'}`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Enforce reason requirement for approved/rejected
|
|
429
|
+
if (
|
|
430
|
+
(newStatus === 'approved_for_training' || newStatus === 'rejected') &&
|
|
431
|
+
!reason
|
|
432
|
+
) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
`reviewReason is required when transitioning to ${newStatus}`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Apply update
|
|
439
|
+
records[idx] = {
|
|
440
|
+
...record,
|
|
441
|
+
reviewStatus: newStatus,
|
|
442
|
+
reviewReason: reason ?? record.reviewReason,
|
|
443
|
+
updatedAt: new Date().toISOString(),
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
writeRegistry(workspaceDir, records);
|
|
447
|
+
return records[idx];
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Update the target model family binding.
|
|
453
|
+
*/
|
|
454
|
+
export function updateTargetModelFamily(
|
|
455
|
+
workspaceDir: string,
|
|
456
|
+
sampleFingerprint: string,
|
|
457
|
+
targetModelFamily: string | null
|
|
458
|
+
): NocturnalDatasetRecord {
|
|
459
|
+
return withRegistryLock(workspaceDir, (records) => {
|
|
460
|
+
const idx = records.findIndex((r) => r.sampleFingerprint === sampleFingerprint);
|
|
461
|
+
|
|
462
|
+
if (idx === -1) {
|
|
463
|
+
throw new Error(`Dataset record not found: ${sampleFingerprint}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
records[idx] = {
|
|
467
|
+
...records[idx],
|
|
468
|
+
targetModelFamily,
|
|
469
|
+
updatedAt: new Date().toISOString(),
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
writeRegistry(workspaceDir, records);
|
|
473
|
+
return records[idx];
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Check if a sample is export-ready.
|
|
479
|
+
*
|
|
480
|
+
* EXPORT-READY means:
|
|
481
|
+
* - reviewStatus === 'approved_for_training'
|
|
482
|
+
* - targetModelFamily !== null
|
|
483
|
+
* - artifact file exists
|
|
484
|
+
* - lineage fields are complete
|
|
485
|
+
*/
|
|
486
|
+
export function isExportReady(
|
|
487
|
+
workspaceDir: string,
|
|
488
|
+
sampleFingerprint: string
|
|
489
|
+
): boolean {
|
|
490
|
+
const record = getDatasetRecord(workspaceDir, sampleFingerprint);
|
|
491
|
+
if (!record) return false;
|
|
492
|
+
if (record.reviewStatus !== 'approved_for_training') return false;
|
|
493
|
+
if (record.targetModelFamily === null) return false;
|
|
494
|
+
if (!fs.existsSync(record.artifactPath)) return false;
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* List all export-ready records for a specific target model family.
|
|
500
|
+
*/
|
|
501
|
+
export function listExportReadyRecords(
|
|
502
|
+
workspaceDir: string,
|
|
503
|
+
targetModelFamily?: string | null
|
|
504
|
+
): NocturnalDatasetRecord[] {
|
|
505
|
+
return listDatasetRecords(workspaceDir, {
|
|
506
|
+
exportReadyOnly: true,
|
|
507
|
+
targetModelFamily: targetModelFamily ?? undefined,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Get the artifact path for a dataset record.
|
|
513
|
+
* Verifies the file exists before returning.
|
|
514
|
+
*/
|
|
515
|
+
export function getArtifactPath(
|
|
516
|
+
workspaceDir: string,
|
|
517
|
+
sampleFingerprint: string
|
|
518
|
+
): string | null {
|
|
519
|
+
const record = getDatasetRecord(workspaceDir, sampleFingerprint);
|
|
520
|
+
if (!record) return null;
|
|
521
|
+
if (!fs.existsSync(record.artifactPath)) return null;
|
|
522
|
+
return record.artifactPath;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Read the artifact file for a dataset record.
|
|
527
|
+
* @throws Error if record not found, artifact file missing, or unreadable
|
|
528
|
+
*/
|
|
529
|
+
export function readDatasetArtifact(
|
|
530
|
+
workspaceDir: string,
|
|
531
|
+
sampleFingerprint: string
|
|
532
|
+
): NocturnalArtifact {
|
|
533
|
+
const artifactPath = getArtifactPath(workspaceDir, sampleFingerprint);
|
|
534
|
+
if (!artifactPath) {
|
|
535
|
+
throw new Error(`Artifact file not found for sample ${sampleFingerprint}`);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const content = fs.readFileSync(artifactPath, 'utf-8');
|
|
539
|
+
const parsed = JSON.parse(content);
|
|
540
|
+
// Return only the NocturnalArtifact fields (not the extended sample record)
|
|
541
|
+
return {
|
|
542
|
+
artifactId: parsed.artifactId,
|
|
543
|
+
sessionId: parsed.sessionId,
|
|
544
|
+
principleId: parsed.principleId,
|
|
545
|
+
sourceSnapshotRef: parsed.sourceSnapshotRef,
|
|
546
|
+
badDecision: parsed.badDecision,
|
|
547
|
+
betterDecision: parsed.betterDecision,
|
|
548
|
+
rationale: parsed.rationale,
|
|
549
|
+
createdAt: parsed.createdAt,
|
|
550
|
+
} as NocturnalArtifact;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Count records by status for dashboard purposes.
|
|
555
|
+
*/
|
|
556
|
+
export function getDatasetStats(
|
|
557
|
+
workspaceDir: string
|
|
558
|
+
): {
|
|
559
|
+
total: number;
|
|
560
|
+
pendingReview: number;
|
|
561
|
+
approvedForTraining: number;
|
|
562
|
+
rejected: number;
|
|
563
|
+
superseded: number;
|
|
564
|
+
exportReadyByFamily: Record<string, number>;
|
|
565
|
+
} {
|
|
566
|
+
const records = readRegistry(workspaceDir);
|
|
567
|
+
|
|
568
|
+
const counts = {
|
|
569
|
+
total: records.length,
|
|
570
|
+
pendingReview: 0,
|
|
571
|
+
approvedForTraining: 0,
|
|
572
|
+
rejected: 0,
|
|
573
|
+
superseded: 0,
|
|
574
|
+
exportReadyByFamily: {} as Record<string, number>,
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
for (const record of records) {
|
|
578
|
+
switch (record.reviewStatus) {
|
|
579
|
+
case 'pending_review':
|
|
580
|
+
counts.pendingReview++;
|
|
581
|
+
break;
|
|
582
|
+
case 'approved_for_training':
|
|
583
|
+
counts.approvedForTraining++;
|
|
584
|
+
break;
|
|
585
|
+
case 'rejected':
|
|
586
|
+
counts.rejected++;
|
|
587
|
+
break;
|
|
588
|
+
case 'superseded':
|
|
589
|
+
counts.superseded++;
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Count export-ready by family
|
|
594
|
+
if (
|
|
595
|
+
record.reviewStatus === 'approved_for_training' &&
|
|
596
|
+
record.targetModelFamily !== null &&
|
|
597
|
+
fs.existsSync(record.artifactPath)
|
|
598
|
+
) {
|
|
599
|
+
const family = record.targetModelFamily;
|
|
600
|
+
counts.exportReadyByFamily[family] = (counts.exportReadyByFamily[family] || 0) + 1;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return counts;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ---------------------------------------------------------------------------
|
|
608
|
+
// Auto-registration from persisted samples
|
|
609
|
+
// ---------------------------------------------------------------------------
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Scan the samples directory and register any approved artifacts
|
|
613
|
+
* that are not yet in the dataset registry.
|
|
614
|
+
*
|
|
615
|
+
* This is used for:
|
|
616
|
+
* 1. Initial migration of Phase 2 artifacts to Phase 3 dataset
|
|
617
|
+
* 2. Recovering from registry corruption
|
|
618
|
+
*
|
|
619
|
+
* @param workspaceDir - Workspace directory
|
|
620
|
+
* @param targetModelFamily - Default target family for migrated samples
|
|
621
|
+
* @returns Number of newly registered samples
|
|
622
|
+
*/
|
|
623
|
+
export function migrateSampleArtifacts(
|
|
624
|
+
workspaceDir: string,
|
|
625
|
+
targetModelFamily: string | null = null
|
|
626
|
+
): number {
|
|
627
|
+
const samplePaths = NocturnalPathResolver.listSamples(workspaceDir);
|
|
628
|
+
let newCount = 0;
|
|
629
|
+
|
|
630
|
+
for (const samplePath of samplePaths) {
|
|
631
|
+
try {
|
|
632
|
+
const content = fs.readFileSync(samplePath, 'utf-8');
|
|
633
|
+
const sample = JSON.parse(content);
|
|
634
|
+
|
|
635
|
+
// Only process approved samples
|
|
636
|
+
if (sample.status !== 'approved') continue;
|
|
637
|
+
if (!sample.artifactId || !sample.sessionId || !sample.principleId) continue;
|
|
638
|
+
|
|
639
|
+
// Skip if already in registry
|
|
640
|
+
const fingerprint = generateSampleFingerprint(
|
|
641
|
+
sample.artifactId,
|
|
642
|
+
sample.principleId,
|
|
643
|
+
sample.sessionId
|
|
644
|
+
);
|
|
645
|
+
const existing = getDatasetRecord(workspaceDir, fingerprint);
|
|
646
|
+
if (existing) continue;
|
|
647
|
+
|
|
648
|
+
// Register the artifact
|
|
649
|
+
const artifact: NocturnalArtifact = {
|
|
650
|
+
artifactId: sample.artifactId,
|
|
651
|
+
sessionId: sample.sessionId,
|
|
652
|
+
principleId: sample.principleId,
|
|
653
|
+
sourceSnapshotRef: sample.sourceSnapshotRef || '',
|
|
654
|
+
badDecision: sample.badDecision || '',
|
|
655
|
+
betterDecision: sample.betterDecision || '',
|
|
656
|
+
rationale: sample.rationale || '',
|
|
657
|
+
createdAt: sample.createdAt || new Date().toISOString(),
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
registerSample(workspaceDir, artifact, samplePath, targetModelFamily);
|
|
661
|
+
newCount++;
|
|
662
|
+
} catch {
|
|
663
|
+
// Skip malformed files
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return newCount;
|
|
668
|
+
}
|