principles-disciple 1.71.0 → 1.73.0
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/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/confirm-first-gate.ts +255 -0
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +38 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +46 -44
- package/src/hooks/gate.ts +207 -7
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +469 -339
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +115 -18
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +39 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +10 -0
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +2 -2
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +2 -2
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +192 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-gate.test.ts +333 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +329 -0
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +184 -3
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { JsonFileStore } from '../../src/core/file-store.js';
|
|
6
|
+
import { safeRmDir } from '../test-utils.js';
|
|
7
|
+
|
|
8
|
+
function tmpDir(): string {
|
|
9
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'pd-filestore-test-'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TestData {
|
|
13
|
+
count: number;
|
|
14
|
+
items: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('JsonFileStore', () => {
|
|
18
|
+
let dir: string;
|
|
19
|
+
let filePath: string;
|
|
20
|
+
let store: JsonFileStore<TestData>;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
dir = tmpDir();
|
|
24
|
+
filePath = path.join(dir, 'test.json');
|
|
25
|
+
store = new JsonFileStore<TestData>(filePath, () => ({ count: 0, items: [] }));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
safeRmDir(dir);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('load', () => {
|
|
33
|
+
it('returns default data when file does not exist', () => {
|
|
34
|
+
const data = store.load();
|
|
35
|
+
expect(data).toEqual({ count: 0, items: [] });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns parsed JSON when file exists', () => {
|
|
39
|
+
fs.writeFileSync(filePath, JSON.stringify({ count: 42, items: ['a', 'b'] }), 'utf8');
|
|
40
|
+
const data = store.load();
|
|
41
|
+
expect(data).toEqual({ count: 42, items: ['a', 'b'] });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns default data when file is empty', () => {
|
|
45
|
+
fs.writeFileSync(filePath, '', 'utf8');
|
|
46
|
+
const data = store.load();
|
|
47
|
+
expect(data).toEqual({ count: 0, items: [] });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('returns default data when file has malformed JSON', () => {
|
|
51
|
+
fs.writeFileSync(filePath, '{broken json', 'utf8');
|
|
52
|
+
const data = store.load();
|
|
53
|
+
expect(data).toEqual({ count: 0, items: [] });
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('save', () => {
|
|
58
|
+
it('writes data to file atomically', () => {
|
|
59
|
+
store.save({ count: 10, items: ['x'] });
|
|
60
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
61
|
+
expect(JSON.parse(raw)).toEqual({ count: 10, items: ['x'] });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('overwrites existing file', () => {
|
|
65
|
+
store.save({ count: 1, items: [] });
|
|
66
|
+
store.save({ count: 2, items: ['y'] });
|
|
67
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
68
|
+
expect(JSON.parse(raw)).toEqual({ count: 2, items: ['y'] });
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('mutate', () => {
|
|
73
|
+
it('performs read-modify-write and returns result', () => {
|
|
74
|
+
const result = store.mutate((d) => {
|
|
75
|
+
d.count += 5;
|
|
76
|
+
d.items.push('new');
|
|
77
|
+
return d.count;
|
|
78
|
+
});
|
|
79
|
+
expect(result).toBe(5);
|
|
80
|
+
const loaded = store.load();
|
|
81
|
+
expect(loaded.count).toBe(5);
|
|
82
|
+
expect(loaded.items).toEqual(['new']);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('does not write when mutate function throws', () => {
|
|
86
|
+
expect(() => {
|
|
87
|
+
store.mutate(() => {
|
|
88
|
+
throw new Error('boom');
|
|
89
|
+
});
|
|
90
|
+
}).toThrow('boom');
|
|
91
|
+
const loaded = store.load();
|
|
92
|
+
expect(loaded).toEqual({ count: 0, items: [] });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('preserves existing data across mutate calls', () => {
|
|
96
|
+
store.mutate((d) => { d.count = 1; });
|
|
97
|
+
store.mutate((d) => { d.count += 1; });
|
|
98
|
+
const loaded = store.load();
|
|
99
|
+
expect(loaded.count).toBe(2);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
cleanupHistory,
|
|
8
8
|
getHistoryVersions,
|
|
9
9
|
extractSummary,
|
|
10
|
-
|
|
10
|
+
cleanupStaleInfo,
|
|
11
11
|
extractWorkingMemory,
|
|
12
12
|
parseWorkingMemorySection,
|
|
13
13
|
mergeWorkingMemory,
|
|
@@ -25,6 +25,11 @@ vi.mock('fs', () => ({
|
|
|
25
25
|
writeFileSync: vi.fn(),
|
|
26
26
|
statSync: vi.fn(),
|
|
27
27
|
unlinkSync: vi.fn(),
|
|
28
|
+
promises: {
|
|
29
|
+
readdir: vi.fn(),
|
|
30
|
+
stat: vi.fn(),
|
|
31
|
+
readFile: vi.fn(),
|
|
32
|
+
}
|
|
28
33
|
}));
|
|
29
34
|
|
|
30
35
|
describe('focus-history', () => {
|
|
@@ -164,30 +169,83 @@ describe('focus-history', () => {
|
|
|
164
169
|
});
|
|
165
170
|
|
|
166
171
|
describe('getHistoryVersions', () => {
|
|
167
|
-
it('should return empty array if no history', () => {
|
|
168
|
-
|
|
172
|
+
it('should return empty array if no history', async () => {
|
|
173
|
+
const err = new Error('ENOENT: no such file or directory') as NodeJS.ErrnoException;
|
|
174
|
+
err.code = 'ENOENT';
|
|
175
|
+
vi.mocked(fs.promises.readdir).mockRejectedValue(err);
|
|
169
176
|
|
|
170
|
-
const result = getHistoryVersions(mockFocusPath, 3);
|
|
177
|
+
const result = await getHistoryVersions(mockFocusPath, 3);
|
|
171
178
|
|
|
172
179
|
expect(result).toEqual([]);
|
|
173
180
|
});
|
|
174
181
|
|
|
175
|
-
it('should return history versions sorted by mtime', () => {
|
|
182
|
+
it('should return history versions sorted by mtime', async () => {
|
|
176
183
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
177
|
-
|
|
184
|
+
// fs.promises.readdir mock
|
|
185
|
+
vi.mocked(fs.promises.readdir).mockResolvedValue([
|
|
178
186
|
'CURRENT_FOCUS.v1.2026-03-10.md',
|
|
179
187
|
'CURRENT_FOCUS.v2.2026-03-11.md',
|
|
180
188
|
] as any);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.
|
|
184
|
-
|
|
189
|
+
// fs.promises.stat mock
|
|
190
|
+
vi.mocked(fs.promises.stat)
|
|
191
|
+
.mockResolvedValueOnce({ mtime: new Date('2026-03-10') } as any)
|
|
192
|
+
.mockResolvedValueOnce({ mtime: new Date('2026-03-11') } as any);
|
|
193
|
+
// fs.promises.readFile mock
|
|
194
|
+
vi.mocked(fs.promises.readFile).mockResolvedValue('history content');
|
|
185
195
|
|
|
186
|
-
const result = getHistoryVersions(mockFocusPath, 3);
|
|
196
|
+
const result = await getHistoryVersions(mockFocusPath, 3);
|
|
187
197
|
|
|
188
198
|
expect(result.length).toBe(2);
|
|
189
199
|
expect(result[0]).toBe('history content');
|
|
190
200
|
});
|
|
201
|
+
|
|
202
|
+
it('should return empty array if readdir throws ENOENT', async () => {
|
|
203
|
+
const err = new Error('ENOENT') as NodeJS.ErrnoException;
|
|
204
|
+
err.code = 'ENOENT';
|
|
205
|
+
vi.mocked(fs.promises.readdir).mockRejectedValue(err);
|
|
206
|
+
|
|
207
|
+
const result = await getHistoryVersions(mockFocusPath, 3);
|
|
208
|
+
|
|
209
|
+
expect(result).toEqual([]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle partial stat failures', async () => {
|
|
213
|
+
vi.mocked(fs.promises.readdir).mockResolvedValue([
|
|
214
|
+
'CURRENT_FOCUS.v1.md',
|
|
215
|
+
'CURRENT_FOCUS.v2.md',
|
|
216
|
+
] as any);
|
|
217
|
+
|
|
218
|
+
// First fails, second succeeds
|
|
219
|
+
vi.mocked(fs.promises.stat)
|
|
220
|
+
.mockRejectedValueOnce(new Error('Deleted'))
|
|
221
|
+
.mockResolvedValueOnce({ mtime: new Date() } as any);
|
|
222
|
+
|
|
223
|
+
vi.mocked(fs.promises.readFile).mockResolvedValue('content');
|
|
224
|
+
|
|
225
|
+
const result = await getHistoryVersions(mockFocusPath, 3);
|
|
226
|
+
|
|
227
|
+
expect(result.length).toBe(1);
|
|
228
|
+
expect(result[0]).toBe('content');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should handle partial readFile failures', async () => {
|
|
232
|
+
vi.mocked(fs.promises.readdir).mockResolvedValue([
|
|
233
|
+
'CURRENT_FOCUS.v1.md',
|
|
234
|
+
'CURRENT_FOCUS.v2.md',
|
|
235
|
+
] as any);
|
|
236
|
+
|
|
237
|
+
vi.mocked(fs.promises.stat).mockResolvedValue({ mtime: new Date() } as any);
|
|
238
|
+
|
|
239
|
+
// First fails, second succeeds
|
|
240
|
+
vi.mocked(fs.promises.readFile)
|
|
241
|
+
.mockRejectedValueOnce(new Error('Read error'))
|
|
242
|
+
.mockResolvedValueOnce('content v2');
|
|
243
|
+
|
|
244
|
+
const result = await getHistoryVersions(mockFocusPath, 3);
|
|
245
|
+
|
|
246
|
+
expect(result.length).toBe(1);
|
|
247
|
+
expect(result[0]).toBe('content v2');
|
|
248
|
+
});
|
|
191
249
|
});
|
|
192
250
|
|
|
193
251
|
describe('extractSummary', () => {
|
|
@@ -567,6 +625,140 @@ ${Array.from({ length: 40 }, (_, i) => `| Item ${i + 1} | Value ${i + 1} |`).joi
|
|
|
567
625
|
});
|
|
568
626
|
});
|
|
569
627
|
|
|
628
|
+
describe('cleanupStaleInfo', () => {
|
|
629
|
+
it('should filter stale file artifacts when workspaceDir is provided', () => {
|
|
630
|
+
const content = `# 🎯 CURRENT_FOCUS
|
|
631
|
+
|
|
632
|
+
## 🧠 Working Memory
|
|
633
|
+
|
|
634
|
+
| 文件路径 | 操作 | 描述 |
|
|
635
|
+
|----------|------|------|
|
|
636
|
+
| \`src/exists.ts\` | modified | exists |
|
|
637
|
+
| \`src/missing.ts\` | modified | missing |
|
|
638
|
+
| \`src/also-missing.ts\` | modified | also missing |
|
|
639
|
+
|
|
640
|
+
### ➡️ 下一步行动
|
|
641
|
+
|
|
642
|
+
1. Next action`;
|
|
643
|
+
|
|
644
|
+
vi.mocked(fs.existsSync).mockImplementation((p: any) => {
|
|
645
|
+
const path = String(p);
|
|
646
|
+
return path.includes('exists');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
const result = cleanupStaleInfo(content, '/workspace');
|
|
650
|
+
|
|
651
|
+
expect(result).toContain('exists.ts');
|
|
652
|
+
expect(result).not.toContain('missing.ts');
|
|
653
|
+
expect(result).not.toContain('also-missing.ts');
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('should keep table state after skipping a missing file row', () => {
|
|
657
|
+
const content = `# 🎯 CURRENT_FOCUS
|
|
658
|
+
|
|
659
|
+
## 🧠 Working Memory
|
|
660
|
+
|
|
661
|
+
| 文件路径 | 操作 | 描述 |
|
|
662
|
+
|----------|------|------|
|
|
663
|
+
| \`src/stale-a.ts\` | modified | stale |
|
|
664
|
+
| \`src/stale-b.ts\` | modified | stale |
|
|
665
|
+
| \`src/valid.ts\` | modified | valid |
|
|
666
|
+
|
|
667
|
+
### ➡️ 下一步行动
|
|
668
|
+
|
|
669
|
+
1. Action`;
|
|
670
|
+
|
|
671
|
+
vi.mocked(fs.existsSync).mockImplementation((p: any) => {
|
|
672
|
+
const path = String(p);
|
|
673
|
+
return path.includes('valid');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
const result = cleanupStaleInfo(content, '/workspace');
|
|
677
|
+
|
|
678
|
+
expect(result).toContain('valid.ts');
|
|
679
|
+
expect(result).not.toContain('stale-a.ts');
|
|
680
|
+
expect(result).not.toContain('stale-b.ts');
|
|
681
|
+
expect(result).toContain('### ➡️ 下一步行动');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('should preserve table header when filtering artifacts', () => {
|
|
685
|
+
const content = `# 🎯 CURRENT_FOCUS
|
|
686
|
+
|
|
687
|
+
## 🧠 Working Memory
|
|
688
|
+
|
|
689
|
+
| 文件路径 | 操作 | 描述 |
|
|
690
|
+
|----------|------|------|
|
|
691
|
+
| \`src/stale.ts\` | modified | stale |
|
|
692
|
+
|
|
693
|
+
### ➡️ 下一步行动
|
|
694
|
+
|
|
695
|
+
1. Action`;
|
|
696
|
+
|
|
697
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
698
|
+
|
|
699
|
+
const result = cleanupStaleInfo(content, '/workspace');
|
|
700
|
+
|
|
701
|
+
expect(result).toContain('| 文件路径 | 操作 | 描述 |');
|
|
702
|
+
expect(result).not.toContain('stale.ts');
|
|
703
|
+
expect(result).toContain('### ➡️ 下一步行动');
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
describe('cleanupStaleInfo I/O integration (autoCompressFocus prefilter path)', () => {
|
|
708
|
+
it('should filter stale artifacts when workspaceDir is provided', () => {
|
|
709
|
+
const focusContent = `# 🎯 CURRENT_FOCUS
|
|
710
|
+
|
|
711
|
+
**版本**: v3
|
|
712
|
+
**更新**: 2026-01-01
|
|
713
|
+
|
|
714
|
+
## 🔄 当前任务
|
|
715
|
+
|
|
716
|
+
${Array.from({ length: 150 }, (_, i) => `- [x] Task ${i + 1}`).join('\n')}
|
|
717
|
+
|
|
718
|
+
## 🧠 Working Memory
|
|
719
|
+
|
|
720
|
+
| 文件路径 | 操作 | 描述 |
|
|
721
|
+
|----------|------|------|
|
|
722
|
+
| \`src/missing-a.ts\` | modified | stale |
|
|
723
|
+
| \`src/existing.ts\` | modified | valid |
|
|
724
|
+
| \`src/missing-b.ts\` | modified | stale |
|
|
725
|
+
|
|
726
|
+
### ➡️ 下一步行动
|
|
727
|
+
|
|
728
|
+
1. Next action`;
|
|
729
|
+
|
|
730
|
+
vi.spyOn(fs, 'existsSync').mockImplementation((p: unknown) => {
|
|
731
|
+
const s = String(p);
|
|
732
|
+
if (s.includes('existing')) return true;
|
|
733
|
+
return false;
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const result = cleanupStaleInfo(focusContent, '/workspace');
|
|
737
|
+
|
|
738
|
+
expect(result).toContain('existing.ts');
|
|
739
|
+
expect(result).not.toContain('missing-a.ts');
|
|
740
|
+
expect(result).not.toContain('missing-b.ts');
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it('should not filter when workspaceDir is omitted', () => {
|
|
744
|
+
const focusContent = `# 🎯 CURRENT_FOCUS
|
|
745
|
+
|
|
746
|
+
## 🧠 Working Memory
|
|
747
|
+
|
|
748
|
+
| 文件路径 | 操作 | 描述 |
|
|
749
|
+
|----------|------|------|
|
|
750
|
+
| \`src/stale.ts\` | modified | stale |
|
|
751
|
+
|
|
752
|
+
### ➡️ 下一步行动
|
|
753
|
+
|
|
754
|
+
1. Action`;
|
|
755
|
+
|
|
756
|
+
const result = cleanupStaleInfo(focusContent);
|
|
757
|
+
|
|
758
|
+
expect(result).toContain('stale.ts');
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
|
|
570
762
|
// ============================================================================
|
|
571
763
|
// 端到端测试:压缩时文件路径落盘
|
|
572
764
|
// ============================================================================
|
|
@@ -6,30 +6,9 @@ import {
|
|
|
6
6
|
formatMergeGateAuditReport,
|
|
7
7
|
runMergeGateAudit,
|
|
8
8
|
} from '../../src/core/merge-gate-audit.js';
|
|
9
|
-
import type { NocturnalArtifact } from '../../src/core/nocturnal-arbiter.js';
|
|
10
|
-
import {
|
|
11
|
-
registerSample,
|
|
12
|
-
updateReviewStatus,
|
|
13
|
-
} from '../../src/core/nocturnal-dataset.js';
|
|
14
|
-
import { appendArtifactLineageRecord } from '../../src/core/nocturnal-artifact-lineage.js';
|
|
15
|
-
import { exportORPOSamples } from '../../src/core/nocturnal-export.js';
|
|
16
9
|
import { createImplementationAssetDir, getImplementationAssetRoot } from '../../src/core/code-implementation-storage.js';
|
|
17
10
|
import { safeRmDir } from '../test-utils.js';
|
|
18
11
|
|
|
19
|
-
function makeArtifact(overrides: Partial<NocturnalArtifact> = {}): NocturnalArtifact {
|
|
20
|
-
return {
|
|
21
|
-
artifactId: 'artifact-1',
|
|
22
|
-
sessionId: 'session-1',
|
|
23
|
-
principleId: 'T-08',
|
|
24
|
-
sourceSnapshotRef: 'snapshot-1',
|
|
25
|
-
badDecision: 'Retried without checking state',
|
|
26
|
-
betterDecision: 'Inspect state before retrying',
|
|
27
|
-
rationale: 'Evidence first.',
|
|
28
|
-
createdAt: '2026-04-12T09:00:00.000Z',
|
|
29
|
-
...overrides,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
12
|
describe('merge-gate-audit', () => {
|
|
34
13
|
let tempDir: string;
|
|
35
14
|
let workspaceDir: string;
|
|
@@ -47,50 +26,13 @@ describe('merge-gate-audit', () => {
|
|
|
47
26
|
safeRmDir(tempDir);
|
|
48
27
|
});
|
|
49
28
|
|
|
50
|
-
function registerApprovedArtifact(artifactId = 'artifact-1'): string {
|
|
51
|
-
const artifact = makeArtifact({ artifactId });
|
|
52
|
-
const artifactPath = path.join(
|
|
53
|
-
workspaceDir,
|
|
54
|
-
'.state',
|
|
55
|
-
'nocturnal',
|
|
56
|
-
'samples',
|
|
57
|
-
`${artifactId}.json`,
|
|
58
|
-
);
|
|
59
|
-
fs.mkdirSync(path.dirname(artifactPath), { recursive: true });
|
|
60
|
-
fs.writeFileSync(artifactPath, JSON.stringify(artifact, null, 2), 'utf-8');
|
|
61
|
-
|
|
62
|
-
const record = registerSample(workspaceDir, artifact, artifactPath, 'gpt-4').record;
|
|
63
|
-
updateReviewStatus(
|
|
64
|
-
workspaceDir,
|
|
65
|
-
record.sampleFingerprint,
|
|
66
|
-
'approved_for_training',
|
|
67
|
-
'approved for merge gate audit',
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
appendArtifactLineageRecord(workspaceDir, {
|
|
71
|
-
artifactKind: 'behavioral-sample',
|
|
72
|
-
artifactId: record.artifactId,
|
|
73
|
-
principleId: record.principleId,
|
|
74
|
-
ruleId: null,
|
|
75
|
-
sessionId: record.sessionId,
|
|
76
|
-
sourceSnapshotRef: record.sourceSnapshotRef,
|
|
77
|
-
sourcePainIds: ['pain-1'],
|
|
78
|
-
sourceGateBlockIds: ['gate-1'],
|
|
79
|
-
storagePath: artifactPath,
|
|
80
|
-
implementationId: null,
|
|
81
|
-
createdAt: record.createdAt,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return record.sampleFingerprint;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
29
|
it('returns defer when audit surfaces are not populated yet', () => {
|
|
88
30
|
const report = runMergeGateAudit(workspaceDir, stateDir);
|
|
89
31
|
|
|
90
32
|
expect(report.overallStatus).toBe('defer');
|
|
91
33
|
expect(report.checks.find((check) => check.id === 'pain_flag_path_contract')?.status).toBe('pass');
|
|
92
34
|
expect(report.checks.find((check) => check.id === 'queue_path_contract')?.status).toBe('pass');
|
|
93
|
-
expect(report.checks.find((check) => check.id === '
|
|
35
|
+
expect(report.checks.find((check) => check.id === 'replay_evidence_integrity')?.status).toBe('defer');
|
|
94
36
|
expect(report.counts.defer).toBeGreaterThan(0);
|
|
95
37
|
});
|
|
96
38
|
|
|
@@ -129,115 +71,6 @@ describe('merge-gate-audit', () => {
|
|
|
129
71
|
expect(replayCheck?.status).toBe('block');
|
|
130
72
|
});
|
|
131
73
|
|
|
132
|
-
it('passes populated dataset, lineage, export, and replay evidence surfaces', () => {
|
|
133
|
-
registerApprovedArtifact('artifact-pass');
|
|
134
|
-
const exportResult = exportORPOSamples(workspaceDir, 'gpt-4');
|
|
135
|
-
expect(exportResult.success).toBe(true);
|
|
136
|
-
|
|
137
|
-
createImplementationAssetDir(stateDir, 'IMPL-1', '1.0.0');
|
|
138
|
-
const replayDir = path.join(getImplementationAssetRoot(stateDir, 'IMPL-1'), 'replays');
|
|
139
|
-
fs.mkdirSync(replayDir, { recursive: true });
|
|
140
|
-
fs.writeFileSync(
|
|
141
|
-
path.join(replayDir, 'good-report.json'),
|
|
142
|
-
JSON.stringify(
|
|
143
|
-
{
|
|
144
|
-
overallDecision: 'pass',
|
|
145
|
-
replayResults: {
|
|
146
|
-
painNegative: { total: 1, passed: 1, failed: 0, details: [] },
|
|
147
|
-
successPositive: { total: 0, passed: 0, failed: 0, details: [] },
|
|
148
|
-
principleAnchor: { total: 0, passed: 0, failed: 0, details: [] },
|
|
149
|
-
},
|
|
150
|
-
blockers: [],
|
|
151
|
-
generatedAt: '2026-04-12T09:00:00.000Z',
|
|
152
|
-
implementationId: 'IMPL-1',
|
|
153
|
-
sampleFingerprints: ['sample-1'],
|
|
154
|
-
evidenceSummary: {
|
|
155
|
-
evidenceStatus: 'observed',
|
|
156
|
-
totalSamples: 1,
|
|
157
|
-
classifiedCounts: {
|
|
158
|
-
painNegative: 1,
|
|
159
|
-
successPositive: 0,
|
|
160
|
-
principleAnchor: 0,
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
null,
|
|
165
|
-
2,
|
|
166
|
-
),
|
|
167
|
-
'utf-8',
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
const report = runMergeGateAudit(workspaceDir, stateDir);
|
|
171
|
-
|
|
172
|
-
expect(report.overallStatus).toBe('pass');
|
|
173
|
-
expect(report.counts.block).toBe(0);
|
|
174
|
-
expect(report.counts.defer).toBe(0);
|
|
175
|
-
expect(formatMergeGateAuditReport(report)).toContain('Overall Status: PASS');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('blocks when dataset artifacts are missing', () => {
|
|
179
|
-
const artifactPath = path.join(
|
|
180
|
-
workspaceDir,
|
|
181
|
-
'.state',
|
|
182
|
-
'nocturnal',
|
|
183
|
-
'samples',
|
|
184
|
-
'artifact-missing.json',
|
|
185
|
-
);
|
|
186
|
-
fs.mkdirSync(path.dirname(artifactPath), { recursive: true });
|
|
187
|
-
const artifact = makeArtifact({ artifactId: 'artifact-missing' });
|
|
188
|
-
fs.writeFileSync(artifactPath, JSON.stringify(artifact, null, 2), 'utf-8');
|
|
189
|
-
|
|
190
|
-
const record = registerSample(workspaceDir, artifact, artifactPath, 'gpt-4').record;
|
|
191
|
-
updateReviewStatus(workspaceDir, record.sampleFingerprint, 'approved_for_training', 'approved');
|
|
192
|
-
|
|
193
|
-
// Append lineage pointing to a real file (so lineage passes)
|
|
194
|
-
appendArtifactLineageRecord(workspaceDir, {
|
|
195
|
-
artifactKind: 'behavioral-sample',
|
|
196
|
-
artifactId: record.artifactId,
|
|
197
|
-
principleId: record.principleId,
|
|
198
|
-
ruleId: null,
|
|
199
|
-
sessionId: record.sessionId,
|
|
200
|
-
sourceSnapshotRef: record.sourceSnapshotRef,
|
|
201
|
-
sourcePainIds: [],
|
|
202
|
-
sourceGateBlockIds: [],
|
|
203
|
-
storagePath: artifactPath,
|
|
204
|
-
implementationId: null,
|
|
205
|
-
createdAt: record.createdAt,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Delete the artifact to simulate a missing file
|
|
209
|
-
fs.unlinkSync(artifactPath);
|
|
210
|
-
|
|
211
|
-
const report = runMergeGateAudit(workspaceDir, stateDir);
|
|
212
|
-
const datasetCheck = report.checks.find((c) => c.id === 'dataset_artifact_integrity');
|
|
213
|
-
|
|
214
|
-
expect(report.overallStatus).toBe('block');
|
|
215
|
-
expect(datasetCheck?.status).toBe('block');
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('blocks when artifact lineage storage paths are missing', () => {
|
|
219
|
-
const badPath = path.join(workspaceDir, '.state', 'nocturnal', 'samples', 'nonexistent.json');
|
|
220
|
-
appendArtifactLineageRecord(workspaceDir, {
|
|
221
|
-
artifactKind: 'behavioral-sample',
|
|
222
|
-
artifactId: 'lineage-missing',
|
|
223
|
-
principleId: 'T-08',
|
|
224
|
-
ruleId: null,
|
|
225
|
-
sessionId: 'session-1',
|
|
226
|
-
sourceSnapshotRef: 'snap-1',
|
|
227
|
-
sourcePainIds: [],
|
|
228
|
-
sourceGateBlockIds: [],
|
|
229
|
-
storagePath: badPath,
|
|
230
|
-
implementationId: null,
|
|
231
|
-
createdAt: new Date().toISOString(),
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
const report = runMergeGateAudit(workspaceDir, stateDir);
|
|
235
|
-
const lineageCheck = report.checks.find((c) => c.id === 'artifact_lineage_integrity');
|
|
236
|
-
|
|
237
|
-
expect(report.overallStatus).toBe('block');
|
|
238
|
-
expect(lineageCheck?.status).toBe('block');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
74
|
it('blocks when replay reports are malformed', () => {
|
|
242
75
|
createImplementationAssetDir(stateDir, 'IMPL-BAD', '1.0.0');
|
|
243
76
|
const replayDir = path.join(getImplementationAssetRoot(stateDir, 'IMPL-BAD'), 'replays');
|
|
@@ -268,7 +101,7 @@ describe('merge-gate-audit', () => {
|
|
|
268
101
|
blockers: [],
|
|
269
102
|
generatedAt: '2026-04-12T09:00:00.000Z',
|
|
270
103
|
implementationId: 'IMPL-NOEVID',
|
|
271
|
-
evidenceSummary: { evidenceStatus: 'observed' },
|
|
104
|
+
evidenceSummary: { evidenceStatus: 'observed' },
|
|
272
105
|
}),
|
|
273
106
|
'utf-8',
|
|
274
107
|
);
|
|
@@ -66,6 +66,7 @@ function rmdir(dir: string): void {
|
|
|
66
66
|
*/
|
|
67
67
|
function setupDeployableReaderCheckpoint(tmpDir: string): { runId: string; checkpointId: string } {
|
|
68
68
|
const run = registerTrainingRun(tmpDir, {
|
|
69
|
+
experimentId: 'mock-experiment-id',
|
|
69
70
|
targetModelFamily: 'claude-reader-latest',
|
|
70
71
|
datasetFingerprint: 'sha256-reader-001',
|
|
71
72
|
exportId: 'export-reader',
|
|
@@ -108,6 +109,7 @@ function setupDeployableReaderCheckpoint(tmpDir: string): { runId: string; check
|
|
|
108
109
|
*/
|
|
109
110
|
function setupDeployableEditorCheckpoint(tmpDir: string): { runId: string; checkpointId: string } {
|
|
110
111
|
const run = registerTrainingRun(tmpDir, {
|
|
112
|
+
experimentId: 'mock-experiment-id',
|
|
111
113
|
targetModelFamily: 'gpt-editor-v4',
|
|
112
114
|
datasetFingerprint: 'sha256-editor-001',
|
|
113
115
|
exportId: 'export-editor',
|
|
@@ -149,6 +151,7 @@ function setupDeployableEditorCheckpoint(tmpDir: string): { runId: string; check
|
|
|
149
151
|
*/
|
|
150
152
|
function setupNonDeployableCheckpoint(tmpDir: string): { runId: string; checkpointId: string } {
|
|
151
153
|
const run = registerTrainingRun(tmpDir, {
|
|
154
|
+
experimentId: 'mock-experiment-id',
|
|
152
155
|
targetModelFamily: 'claude-reader-latest',
|
|
153
156
|
datasetFingerprint: 'sha256-abc',
|
|
154
157
|
exportId: 'export-abc',
|
|
@@ -280,6 +283,7 @@ describe('ModelDeploymentRegistry bindCheckpointToWorkerProfile', () => {
|
|
|
280
283
|
|
|
281
284
|
// Set up a second reader checkpoint
|
|
282
285
|
const run2 = registerTrainingRun(tmpDir, {
|
|
286
|
+
experimentId: 'mock-experiment-id',
|
|
283
287
|
targetModelFamily: 'claude-reader-latest',
|
|
284
288
|
datasetFingerprint: 'sha256-reader-002',
|
|
285
289
|
exportId: 'export-reader-2',
|
|
@@ -564,6 +568,7 @@ describe('ModelDeploymentRegistry rollbackDeployment', () => {
|
|
|
564
568
|
|
|
565
569
|
// Set up ck2
|
|
566
570
|
const run2 = registerTrainingRun(tmpDir, {
|
|
571
|
+
experimentId: 'mock-experiment-id',
|
|
567
572
|
targetModelFamily: 'claude-reader-latest',
|
|
568
573
|
datasetFingerprint: 'sha256-reader-002',
|
|
569
574
|
exportId: 'export-reader-2',
|
|
@@ -619,8 +624,8 @@ describe('ModelDeploymentRegistry rollbackDeployment', () => {
|
|
|
619
624
|
|
|
620
625
|
it('rollback fails when previous checkpoint no longer exists', () => {
|
|
621
626
|
const { checkpointId: ck1 } = setupDeployableReaderCheckpoint(tmpDir);
|
|
622
|
-
|
|
623
627
|
const run2 = registerTrainingRun(tmpDir, {
|
|
628
|
+
experimentId: 'mock-experiment-id',
|
|
624
629
|
targetModelFamily: 'claude-reader-latest',
|
|
625
630
|
datasetFingerprint: 'sha256-reader-002',
|
|
626
631
|
exportId: 'export-reader-2',
|
|
@@ -677,6 +682,7 @@ describe('ModelDeploymentRegistry rollbackDeployment', () => {
|
|
|
677
682
|
const { checkpointId: ck1 } = setupDeployableReaderCheckpoint(tmpDir);
|
|
678
683
|
|
|
679
684
|
const run2 = registerTrainingRun(tmpDir, {
|
|
685
|
+
experimentId: 'mock-experiment-id',
|
|
680
686
|
targetModelFamily: 'claude-reader-latest',
|
|
681
687
|
datasetFingerprint: 'sha256-reader-002',
|
|
682
688
|
exportId: 'export-reader-2',
|