@cleocode/contracts 2026.9.8 → 2026.9.10
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/dist/__tests__/acceptance-gate.test.js +242 -1
- package/dist/__tests__/acceptance-gate.test.js.map +1 -1
- package/dist/__tests__/evidence-atom-schema.test.js +17 -14
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
- package/dist/__tests__/memory-wire-shapes.test.js.map +1 -1
- package/dist/__tests__/operations-registry.test.js +18 -0
- package/dist/__tests__/operations-registry.test.js.map +1 -1
- package/dist/acceptance-gate-schema.d.ts +60 -2
- package/dist/acceptance-gate-schema.d.ts.map +1 -1
- package/dist/acceptance-gate-schema.js +273 -1
- package/dist/acceptance-gate-schema.js.map +1 -1
- package/dist/acceptance-gate.d.ts +133 -11
- package/dist/acceptance-gate.d.ts.map +1 -1
- package/dist/acceptance-gate.js +3 -3
- package/dist/audit.d.ts +71 -2
- package/dist/audit.d.ts.map +1 -1
- package/dist/backup-manifest.d.ts +80 -0
- package/dist/backup-manifest.d.ts.map +1 -1
- package/dist/caamp-markers.d.ts +20 -0
- package/dist/caamp-markers.d.ts.map +1 -1
- package/dist/capabilities.d.ts +282 -0
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/changesets.d.ts +4 -4
- package/dist/code-symbol.d.ts +33 -0
- package/dist/code-symbol.d.ts.map +1 -1
- package/dist/daemon/health.d.ts +3 -3
- package/dist/data-accessor.d.ts +15 -0
- package/dist/data-accessor.d.ts.map +1 -1
- package/dist/data-accessor.js.map +1 -1
- package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js +19 -1
- package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +713 -3
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/docs-taxonomy.d.ts +25 -0
- package/dist/docs-taxonomy.d.ts.map +1 -1
- package/dist/docs-taxonomy.js.map +1 -1
- package/dist/evidence-atom-schema.d.ts +7 -6
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +10 -9
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/graph.d.ts +283 -2
- package/dist/graph.d.ts.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/adr-070-orchestration.d.ts.map +1 -1
- package/dist/invariants/adr-070-orchestration.js +1 -1
- package/dist/invariants/adr-070-orchestration.js.map +1 -1
- package/dist/invariants/adr-073-saga.js +2 -2
- package/dist/invariants/adr-073-saga.js.map +1 -1
- package/dist/jobs.d.ts +329 -3
- package/dist/jobs.d.ts.map +1 -1
- package/dist/knowledge-health.d.ts +695 -0
- package/dist/knowledge-health.d.ts.map +1 -0
- package/dist/knowledge-health.js +8 -0
- package/dist/knowledge-health.js.map +1 -0
- package/dist/llm/catalog-schema.d.ts +4 -4
- package/dist/memory/observe.d.ts +4 -2
- package/dist/memory/observe.d.ts.map +1 -1
- package/dist/memory/search.d.ts +4 -0
- package/dist/memory/search.d.ts.map +1 -1
- package/dist/nexus-living-brain-ops.d.ts +28 -3
- package/dist/nexus-living-brain-ops.d.ts.map +1 -1
- package/dist/nexus-tasks-bridge-ops.d.ts +11 -0
- package/dist/nexus-tasks-bridge-ops.d.ts.map +1 -1
- package/dist/operations/admin.d.ts +10 -4
- package/dist/operations/admin.d.ts.map +1 -1
- package/dist/operations/docs.d.ts +138 -0
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +47 -0
- package/dist/operations/docs.js.map +1 -1
- package/dist/operations/focus.d.ts +7 -0
- package/dist/operations/focus.d.ts.map +1 -1
- package/dist/operations/nexus.d.ts +7 -2
- package/dist/operations/nexus.d.ts.map +1 -1
- package/dist/operations/output-contracts-data.d.ts.map +1 -1
- package/dist/operations/output-contracts-data.js +31 -3
- package/dist/operations/output-contracts-data.js.map +1 -1
- package/dist/operations/research.d.ts +33 -0
- package/dist/operations/research.d.ts.map +1 -1
- package/dist/operations/tasks.d.ts +107 -4
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js +5 -0
- package/dist/operations/tasks.js.map +1 -1
- package/dist/operations/validate.d.ts +23 -0
- package/dist/operations/validate.d.ts.map +1 -1
- package/dist/package-artifact.d.ts +112 -0
- package/dist/package-artifact.d.ts.map +1 -0
- package/dist/package-artifact.js +8 -0
- package/dist/package-artifact.js.map +1 -0
- package/dist/project-context.d.ts +18 -0
- package/dist/project-context.d.ts.map +1 -1
- package/dist/release/evidence-atoms.d.ts +10 -0
- package/dist/release/evidence-atoms.d.ts.map +1 -1
- package/dist/release/evidence-atoms.js +6 -0
- package/dist/release/evidence-atoms.js.map +1 -1
- package/dist/release/plan.d.ts +6 -6
- package/dist/resource-governor.d.ts +114 -0
- package/dist/resource-governor.d.ts.map +1 -1
- package/dist/resource-governor.js.map +1 -1
- package/dist/supervisor-ipc/messages.d.ts +6 -6
- package/dist/task-record.d.ts +25 -4
- package/dist/task-record.d.ts.map +1 -1
- package/dist/task.d.ts +55 -36
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js.map +1 -1
- package/dist/tasks/archive.d.ts +3 -3
- package/dist/templates/manifest.d.ts +1 -1
- package/dist/workgraph.d.ts +5 -3
- package/dist/workgraph.d.ts.map +1 -1
- package/dist/workgraph.js +19 -0
- package/dist/workgraph.js.map +1 -1
- package/package.json +2 -2
- package/schemas/gate-result.schema.json +350 -1
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/acceptance-gate.test.ts +282 -1
- package/src/__tests__/evidence-atom-schema.test.ts +17 -14
- package/src/__tests__/memory-wire-shapes.test.ts +2 -0
- package/src/__tests__/operations-registry.test.ts +26 -0
- package/src/acceptance-gate-schema.ts +320 -14
- package/src/acceptance-gate.ts +144 -11
- package/src/audit.ts +72 -2
- package/src/backup-manifest.ts +88 -0
- package/src/caamp-markers.ts +22 -0
- package/src/capabilities.ts +311 -0
- package/src/code-symbol.ts +37 -0
- package/src/data-accessor.ts +16 -0
- package/src/dispatch/__tests__/tasks-gateway-crud-ops.test.ts +31 -1
- package/src/dispatch/operations-registry.ts +719 -3
- package/src/docs-taxonomy.ts +26 -0
- package/src/evidence-atom-schema.ts +10 -9
- package/src/graph.ts +326 -2
- package/src/index.ts +45 -1
- package/src/invariants/adr-070-orchestration.ts +2 -1
- package/src/invariants/adr-073-saga.ts +2 -2
- package/src/jobs.ts +382 -3
- package/src/knowledge-health.ts +748 -0
- package/src/memory/observe.ts +4 -2
- package/src/memory/search.ts +4 -0
- package/src/nexus-living-brain-ops.ts +34 -3
- package/src/nexus-tasks-bridge-ops.ts +12 -0
- package/src/operations/admin.ts +11 -4
- package/src/operations/docs.ts +153 -0
- package/src/operations/focus.ts +11 -0
- package/src/operations/nexus.ts +7 -1
- package/src/operations/output-contracts-data.ts +33 -3
- package/src/operations/research.ts +36 -0
- package/src/operations/tasks.ts +116 -4
- package/src/operations/validate.ts +24 -0
- package/src/package-artifact.ts +127 -0
- package/src/project-context.ts +19 -0
- package/src/release/evidence-atoms.ts +6 -0
- package/src/resource-governor.ts +132 -0
- package/src/task-record.ts +27 -4
- package/src/task.ts +58 -36
- package/src/workgraph.ts +19 -0
|
@@ -11,8 +11,14 @@
|
|
|
11
11
|
* @task T780
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
14
15
|
import { describe, expect, it } from 'vitest';
|
|
15
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
AcceptanceGate,
|
|
18
|
+
AcceptanceGateBinding,
|
|
19
|
+
AcceptanceGateResult,
|
|
20
|
+
FileAssertion,
|
|
21
|
+
} from '../acceptance-gate.js';
|
|
16
22
|
import {
|
|
17
23
|
acceptanceArraySchema,
|
|
18
24
|
acceptanceGateResultSchema,
|
|
@@ -23,9 +29,11 @@ import {
|
|
|
23
29
|
httpGateSchema,
|
|
24
30
|
lintGateSchema,
|
|
25
31
|
manualGateSchema,
|
|
32
|
+
testCountReportSchema,
|
|
26
33
|
testGateSchema,
|
|
27
34
|
} from '../acceptance-gate-schema.js';
|
|
28
35
|
import type { AcceptanceItem } from '../index.js';
|
|
36
|
+
import type { ProcessCaptureResult } from '../resource-governor.js';
|
|
29
37
|
|
|
30
38
|
// ─── TestGate ────────────────────────────────────────────────────────────────
|
|
31
39
|
|
|
@@ -734,3 +742,276 @@ describe('acceptanceArraySchema — mixed (string | AcceptanceGate)[] (T780)', (
|
|
|
734
742
|
expect(typed).toHaveLength(2);
|
|
735
743
|
});
|
|
736
744
|
});
|
|
745
|
+
|
|
746
|
+
describe('acceptance result captured execution preservation (T12292)', () => {
|
|
747
|
+
const execution: ProcessCaptureResult = {
|
|
748
|
+
started: true,
|
|
749
|
+
targetPid: 1234,
|
|
750
|
+
exitCode: 0,
|
|
751
|
+
signal: null,
|
|
752
|
+
error: null,
|
|
753
|
+
stopped: null,
|
|
754
|
+
stdout: 'literal ü | output',
|
|
755
|
+
stderr: '',
|
|
756
|
+
outputTruncated: false,
|
|
757
|
+
durationMs: 12,
|
|
758
|
+
mode: 'systemd',
|
|
759
|
+
unitName: 'cleo-tool-fixture.scope',
|
|
760
|
+
nativeMemory: 'observed-cgroup',
|
|
761
|
+
resourceLimits: {
|
|
762
|
+
cgroup: '/user.slice/cleo-tool-fixture.scope',
|
|
763
|
+
memoryMaxBytes: 4294967296,
|
|
764
|
+
tasksMax: 256,
|
|
765
|
+
},
|
|
766
|
+
cleanupScope: 'process-group',
|
|
767
|
+
transportClosed: true,
|
|
768
|
+
targetCloseObserved: true,
|
|
769
|
+
cleanupObservation: 'scope-terminal',
|
|
770
|
+
cleanupErrors: [],
|
|
771
|
+
};
|
|
772
|
+
const result: AcceptanceGateResult = {
|
|
773
|
+
index: 3,
|
|
774
|
+
req: 'PARTNER-EXACT',
|
|
775
|
+
kind: 'test',
|
|
776
|
+
result: 'pass',
|
|
777
|
+
durationMs: 12,
|
|
778
|
+
checkedAt: '2026-09-20T17:00:00.000Z',
|
|
779
|
+
checkedBy: 'captured-agent',
|
|
780
|
+
execution,
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
it('preserves every execution field through JSON and runtime validation', () => {
|
|
784
|
+
expect(acceptanceGateResultSchema.parse(JSON.parse(JSON.stringify(result)))).toEqual(result);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it.each([
|
|
788
|
+
'deadline',
|
|
789
|
+
'cancelled',
|
|
790
|
+
'resource-limit',
|
|
791
|
+
'output-limit',
|
|
792
|
+
'transport-error',
|
|
793
|
+
] as const)('retains %s as an incomplete observation without inventing a verdict', (stopped) => {
|
|
794
|
+
const interrupted: AcceptanceGateResult = {
|
|
795
|
+
...result,
|
|
796
|
+
result: 'error',
|
|
797
|
+
execution: { ...execution, stopped, exitCode: null, targetCloseObserved: false },
|
|
798
|
+
};
|
|
799
|
+
expect(acceptanceGateResultSchema.parse(interrupted)).toEqual(interrupted);
|
|
800
|
+
expect(acceptanceGateResultSchema.safeParse({ ...interrupted, result: 'pass' }).success).toBe(
|
|
801
|
+
false,
|
|
802
|
+
);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('rejects a completed verdict for an unstarted target', () => {
|
|
806
|
+
expect(
|
|
807
|
+
acceptanceGateResultSchema.safeParse({
|
|
808
|
+
...result,
|
|
809
|
+
execution: { ...execution, started: false, targetPid: null, exitCode: null },
|
|
810
|
+
}).success,
|
|
811
|
+
).toBe(false);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
it('rejects malformed or contradictory process evidence instead of stripping it', () => {
|
|
815
|
+
for (const corrupt of [
|
|
816
|
+
{ ...execution, unexpectedAuthority: true },
|
|
817
|
+
{ ...execution, targetPid: 0 },
|
|
818
|
+
{ ...execution, signal: 'SIGTERM' },
|
|
819
|
+
{ ...execution, nativeMemory: 'observed-cgroup', resourceLimits: undefined },
|
|
820
|
+
{ ...execution, resourceLimits: { ...execution.resourceLimits, cgroup: '/wrong-owner' } },
|
|
821
|
+
{ ...execution, durationMs: Number.POSITIVE_INFINITY },
|
|
822
|
+
])
|
|
823
|
+
expect(acceptanceGateResultSchema.safeParse({ ...result, execution: corrupt }).success).toBe(
|
|
824
|
+
false,
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
describe('typed requirement verification binding (T12292)', () => {
|
|
830
|
+
const binding: AcceptanceGateBinding = {
|
|
831
|
+
version: 1,
|
|
832
|
+
verificationId: '9f4a8d70-6325-4bf8-a30c-b719ade689da',
|
|
833
|
+
identity: {
|
|
834
|
+
projectId: 'stable-project',
|
|
835
|
+
projectRoot: '/project',
|
|
836
|
+
actor: 'agent-test',
|
|
837
|
+
operation: 'check.gate.verify',
|
|
838
|
+
idempotencyKey: 'verification-attempt',
|
|
839
|
+
},
|
|
840
|
+
taskId: 'T122',
|
|
841
|
+
criterionId: 'e1cf21de-92db-46e4-8e3d-156ab78a0b32',
|
|
842
|
+
criterionHash: 'a'.repeat(64),
|
|
843
|
+
gateHash: 'b'.repeat(64),
|
|
844
|
+
capturedAt: '2026-09-20T17:00:00.000Z',
|
|
845
|
+
deadlineAt: 1790000000000,
|
|
846
|
+
invocation: {
|
|
847
|
+
command: 'node',
|
|
848
|
+
args: ['scripts/verify.mjs', '--task', 'T122'],
|
|
849
|
+
cwd: '/project',
|
|
850
|
+
environmentHash: 'c'.repeat(64),
|
|
851
|
+
},
|
|
852
|
+
artifacts: [{ path: '/project/scripts/verify.mjs', sha256: 'd'.repeat(64), bytes: 81 }],
|
|
853
|
+
};
|
|
854
|
+
const result: AcceptanceGateResult = {
|
|
855
|
+
index: 3,
|
|
856
|
+
req: 'PARTNER-122',
|
|
857
|
+
kind: 'test',
|
|
858
|
+
result: 'error',
|
|
859
|
+
durationMs: 5,
|
|
860
|
+
checkedAt: '2026-09-20T17:00:00.005Z',
|
|
861
|
+
checkedBy: 'agent-test',
|
|
862
|
+
binding,
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
it('round-trips bound identity, invocation hashes and untracked input snapshots losslessly', () => {
|
|
866
|
+
expect(acceptanceGateResultSchema.parse(JSON.parse(JSON.stringify(result)))).toEqual(result);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
it('preserves an explicitly observed absent input without fabricating a hash', () => {
|
|
870
|
+
const absent = {
|
|
871
|
+
...result,
|
|
872
|
+
binding: {
|
|
873
|
+
...binding,
|
|
874
|
+
artifacts: [{ path: '/project/scripts/missing.mjs', sha256: null, bytes: null }],
|
|
875
|
+
},
|
|
876
|
+
};
|
|
877
|
+
expect(acceptanceGateResultSchema.parse(absent)).toEqual(absent);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it('does not turn a bound process result into a verdict without an actual execution observation', () => {
|
|
881
|
+
expect(acceptanceGateResultSchema.safeParse({ ...result, result: 'pass' }).success).toBe(false);
|
|
882
|
+
const legacy = { ...result, binding: undefined, result: 'pass' };
|
|
883
|
+
expect(acceptanceGateResultSchema.parse(legacy)).toEqual(legacy);
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
it.each([
|
|
887
|
+
{ ...binding, gateHash: 'not-a-hash' },
|
|
888
|
+
{ ...binding, identity: { ...binding.identity, projectRoot: 'relative-root' } },
|
|
889
|
+
{ ...binding, invocation: { ...binding.invocation, cwd: 'relative-cwd' } },
|
|
890
|
+
{ ...binding, artifacts: [{ ...binding.artifacts[0], path: 'relative-input' }] },
|
|
891
|
+
{ ...binding, verificationId: 'not-a-uuid' },
|
|
892
|
+
{ ...binding, identity: { ...binding.identity, actor: 'different-agent' } },
|
|
893
|
+
{ ...binding, capturedAt: '2026-09-20T17:00:01.000Z' },
|
|
894
|
+
{ ...binding, deadlineAt: 1 },
|
|
895
|
+
{ ...binding, artifacts: [binding.artifacts[0], binding.artifacts[0]] },
|
|
896
|
+
{ ...binding, artifacts: [{ ...binding.artifacts[0], sha256: null }] },
|
|
897
|
+
{ ...binding, artifacts: [{ ...binding.artifacts[0], bytes: -1 }] },
|
|
898
|
+
{
|
|
899
|
+
...binding,
|
|
900
|
+
invocation: { ...binding.invocation, environmentHash: '', env: { SECRET: 'must-not-store' } },
|
|
901
|
+
},
|
|
902
|
+
{ ...binding, authority: 'implicit' },
|
|
903
|
+
])('rejects inconsistent or malformed persisted binding %#', (invalid) => {
|
|
904
|
+
expect(acceptanceGateResultSchema.safeParse({ ...result, binding: invalid }).success).toBe(
|
|
905
|
+
false,
|
|
906
|
+
);
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
describe('published acceptance result schema (T12292)', () => {
|
|
911
|
+
it('retains binding and execution shapes while disclosing runtime-only invariants', () => {
|
|
912
|
+
const emitted = JSON.parse(
|
|
913
|
+
readFileSync(new URL('../../schemas/gate-result.schema.json', import.meta.url), 'utf8'),
|
|
914
|
+
);
|
|
915
|
+
expect(emitted.properties.binding.properties.gateHash.pattern).toBe('^[a-f0-9]{64}$');
|
|
916
|
+
expect(emitted.properties.binding.properties.artifacts.items.properties.sha256).toBeDefined();
|
|
917
|
+
expect(emitted.properties.execution.properties.cleanupObservation).toBeDefined();
|
|
918
|
+
expect(emitted.properties.binding.additionalProperties).toBe(false);
|
|
919
|
+
expect(emitted.$comment).toContain('acceptanceGateResultSchema');
|
|
920
|
+
expect(emitted.$comment).toContain('cross-field');
|
|
921
|
+
expect(emitted.$comment).toContain('not proof');
|
|
922
|
+
expect(emitted.required).not.toContain('binding');
|
|
923
|
+
expect(emitted.required).not.toContain('execution');
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
describe('structured minimum-count report schema', () => {
|
|
928
|
+
// Captured Vitest 4.1.4 JSON: one nested suite, pass + skip + todo.
|
|
929
|
+
const report = {
|
|
930
|
+
numTotalTestSuites: 2,
|
|
931
|
+
numPassedTestSuites: 2,
|
|
932
|
+
numFailedTestSuites: 0,
|
|
933
|
+
numPendingTestSuites: 0,
|
|
934
|
+
numTotalTests: 3,
|
|
935
|
+
numPassedTests: 1,
|
|
936
|
+
numFailedTests: 0,
|
|
937
|
+
numPendingTests: 1,
|
|
938
|
+
numTodoTests: 1,
|
|
939
|
+
success: true,
|
|
940
|
+
testResults: [
|
|
941
|
+
{
|
|
942
|
+
name: '/fixture/reporter.test.ts',
|
|
943
|
+
status: 'passed',
|
|
944
|
+
assertionResults: [
|
|
945
|
+
{ fullName: 'actual reporter passes', status: 'passed' },
|
|
946
|
+
{ fullName: 'actual reporter skipped', status: 'skipped' },
|
|
947
|
+
{ fullName: 'actual reporter later', status: 'todo' },
|
|
948
|
+
],
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
it('recognizes passed tests without counting skipped or todo assertions', () => {
|
|
954
|
+
const parsed = testCountReportSchema.parse(report);
|
|
955
|
+
expect(parsed.numPassedTests).toBe(1);
|
|
956
|
+
expect(parsed.numTotalTests).toBe(3);
|
|
957
|
+
expect(parsed.numTotalTestSuites).toBe(2);
|
|
958
|
+
expect(parsed.testResults).toHaveLength(1);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it.each([
|
|
962
|
+
{ numPassedTests: 3 },
|
|
963
|
+
{ numTotalTests: 99 },
|
|
964
|
+
{ numPendingTests: 0 },
|
|
965
|
+
{ numTodoTests: 0 },
|
|
966
|
+
{ numPassedTests: 1.5 },
|
|
967
|
+
{ numPassedTests: -1 },
|
|
968
|
+
{ numTotalTests: Number.MAX_SAFE_INTEGER + 1 },
|
|
969
|
+
{ numFailedTestSuites: 1 },
|
|
970
|
+
])('rejects malformed or inconsistent counters: %j', (change) => {
|
|
971
|
+
expect(testCountReportSchema.safeParse({ ...report, ...change }).success).toBe(false);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('rejects summary-only, missing counters, and unsupported assertion states', () => {
|
|
975
|
+
expect(testCountReportSchema.safeParse({ numPassedTests: 100, success: true }).success).toBe(
|
|
976
|
+
false,
|
|
977
|
+
);
|
|
978
|
+
expect(testCountReportSchema.safeParse({ ...report, numTodoTests: undefined }).success).toBe(
|
|
979
|
+
false,
|
|
980
|
+
);
|
|
981
|
+
expect(
|
|
982
|
+
testCountReportSchema.safeParse({
|
|
983
|
+
...report,
|
|
984
|
+
testResults: [
|
|
985
|
+
{
|
|
986
|
+
...report.testResults[0],
|
|
987
|
+
assertionResults: [{ fullName: 'test', status: 'invented' }],
|
|
988
|
+
},
|
|
989
|
+
],
|
|
990
|
+
}).success,
|
|
991
|
+
).toBe(false);
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it('retains real failed suite reports without converting them into success', () => {
|
|
995
|
+
const failed = {
|
|
996
|
+
...report,
|
|
997
|
+
success: false,
|
|
998
|
+
numPassedTestSuites: 1,
|
|
999
|
+
numFailedTestSuites: 1,
|
|
1000
|
+
testResults: [{ ...report.testResults[0], status: 'failed' }],
|
|
1001
|
+
};
|
|
1002
|
+
expect(testCountReportSchema.parse(failed).success).toBe(false);
|
|
1003
|
+
expect(testCountReportSchema.safeParse({ ...failed, success: true }).success).toBe(false);
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it('accepts zero-count structure without claiming that any test ran', () => {
|
|
1007
|
+
const empty = {
|
|
1008
|
+
...report,
|
|
1009
|
+
numTotalTests: 0,
|
|
1010
|
+
numPassedTests: 0,
|
|
1011
|
+
numPendingTests: 0,
|
|
1012
|
+
numTodoTests: 0,
|
|
1013
|
+
testResults: [],
|
|
1014
|
+
};
|
|
1015
|
+
expect(testCountReportSchema.parse(empty).numPassedTests).toBe(0);
|
|
1016
|
+
});
|
|
1017
|
+
});
|
|
@@ -540,7 +540,10 @@ describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
|
540
540
|
];
|
|
541
541
|
expect(validateEvidenceForGate('implemented', d).ok).toBe(true);
|
|
542
542
|
|
|
543
|
-
const e: EvidenceAtomInput[] = [
|
|
543
|
+
const e: EvidenceAtomInput[] = [
|
|
544
|
+
{ kind: 'pr', prNumber: 357 },
|
|
545
|
+
{ kind: 'files', paths: ['src/fix.ts'] },
|
|
546
|
+
];
|
|
544
547
|
expect(validateEvidenceForGate('implemented', e).ok).toBe(true);
|
|
545
548
|
});
|
|
546
549
|
|
|
@@ -552,11 +555,11 @@ describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
|
552
555
|
if (!r.ok) {
|
|
553
556
|
expect(r.message).toMatch(/^Gate 'implemented' requires evidence: /);
|
|
554
557
|
expect(r.message).toContain('[commit AND files]');
|
|
555
|
-
expect(r.message).toContain('[pr]');
|
|
558
|
+
expect(r.message).toContain('[pr AND files]');
|
|
556
559
|
}
|
|
557
560
|
});
|
|
558
561
|
|
|
559
|
-
it('testsPassed accepts
|
|
562
|
+
it('testsPassed accepts actual runs and tools but rejects PR provenance', () => {
|
|
560
563
|
expect(
|
|
561
564
|
validateEvidenceForGate('testsPassed', [
|
|
562
565
|
{ kind: 'test-run', path: '/tmp/v.json' },
|
|
@@ -570,7 +573,7 @@ describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
|
570
573
|
expect(
|
|
571
574
|
validateEvidenceForGate('testsPassed', [{ kind: 'pr', prNumber: 357 }] as EvidenceAtomInput[])
|
|
572
575
|
.ok,
|
|
573
|
-
).toBe(
|
|
576
|
+
).toBe(false);
|
|
574
577
|
});
|
|
575
578
|
|
|
576
579
|
it('testsPassed rejects empty evidence', () => {
|
|
@@ -579,7 +582,7 @@ describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
|
579
582
|
|
|
580
583
|
it('qaPassed accepts a single tool atom (behavior parity with legacy)', () => {
|
|
581
584
|
// Critical parity check: GATE_EVIDENCE_REQUIREMENTS.qaPassed.oneOf is
|
|
582
|
-
// [['tool']
|
|
585
|
+
// [['tool']] — not [['tool', 'tool']]. The legacy runtime did NOT
|
|
583
586
|
// count distinct tool names, so one tool atom passes.
|
|
584
587
|
expect(
|
|
585
588
|
validateEvidenceForGate('qaPassed', [{ kind: 'tool', tool: 'lint' }] as EvidenceAtomInput[])
|
|
@@ -587,11 +590,11 @@ describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
|
587
590
|
).toBe(true);
|
|
588
591
|
});
|
|
589
592
|
|
|
590
|
-
it('qaPassed
|
|
593
|
+
it('qaPassed rejects PR provenance without a review tool result', () => {
|
|
591
594
|
expect(
|
|
592
595
|
validateEvidenceForGate('qaPassed', [{ kind: 'pr', prNumber: 357 }] as EvidenceAtomInput[])
|
|
593
596
|
.ok,
|
|
594
|
-
).toBe(
|
|
597
|
+
).toBe(false);
|
|
595
598
|
});
|
|
596
599
|
|
|
597
600
|
it('documented accepts files or url', () => {
|
|
@@ -656,7 +659,7 @@ describe('formatGateRequirement (T10337)', () => {
|
|
|
656
659
|
expect(s).toContain('[commit AND note]');
|
|
657
660
|
expect(s).toContain('[decision AND files]');
|
|
658
661
|
expect(s).toContain('[decision AND note]');
|
|
659
|
-
expect(s).toContain('[pr]');
|
|
662
|
+
expect(s).toContain('[pr AND files]');
|
|
660
663
|
// Joiner between combinations is ' OR '.
|
|
661
664
|
expect(s.split(' OR ').length).toBe(5);
|
|
662
665
|
});
|
|
@@ -678,7 +681,7 @@ describe('formatGateRequirementHint (T9949)', () => {
|
|
|
678
681
|
expect(h).toContain("--evidence 'commit:<sha>;note:<short description>'");
|
|
679
682
|
expect(h).toContain("--evidence 'decision:D-arch-001;files:path/a.ts,path/b.ts'");
|
|
680
683
|
expect(h).toContain("--evidence 'decision:D-arch-001;note:<short description>'");
|
|
681
|
-
expect(h).toContain("--evidence 'pr:357'");
|
|
684
|
+
expect(h).toContain("--evidence 'pr:357;files:path/a.ts,path/b.ts'");
|
|
682
685
|
// Routes a `note:`-only caller at the partner atom they need.
|
|
683
686
|
expect(h).toContain("'note:' alone is NOT accepted for this gate");
|
|
684
687
|
expect(h).toContain("'commit:'");
|
|
@@ -686,11 +689,11 @@ describe('formatGateRequirementHint (T9949)', () => {
|
|
|
686
689
|
});
|
|
687
690
|
|
|
688
691
|
it('routes note-only callers for testsPassed at the right alternatives', () => {
|
|
689
|
-
// testsPassed accepts test-run
|
|
692
|
+
// testsPassed accepts test-run or tool — neither involves note.
|
|
690
693
|
const h = formatGateRequirementHint('testsPassed');
|
|
691
694
|
expect(h).toContain("--evidence 'test-run:/tmp/vitest-out.json'");
|
|
692
695
|
expect(h).toContain("--evidence 'tool:test'");
|
|
693
|
-
expect(h).toContain(
|
|
696
|
+
expect(h).not.toContain('pr:357');
|
|
694
697
|
// When no combination includes note at all, the helper falls through to a
|
|
695
698
|
// generic "not accepted, see ADR-051" clarifier rather than fabricating a
|
|
696
699
|
// partner suggestion.
|
|
@@ -731,9 +734,9 @@ describe('validateEvidenceForGate failure surface (T9949)', () => {
|
|
|
731
734
|
] as EvidenceAtomInput[]);
|
|
732
735
|
expect(r.ok).toBe(false);
|
|
733
736
|
if (!r.ok) {
|
|
734
|
-
//
|
|
737
|
+
// Error includes the same artifact requirement as the validator.
|
|
735
738
|
expect(r.message).toBe(
|
|
736
|
-
"Gate 'implemented' requires evidence: [commit AND files] OR [commit AND note] OR [decision AND files] OR [decision AND note] OR [pr]",
|
|
739
|
+
"Gate 'implemented' requires evidence: [commit AND files] OR [commit AND note] OR [decision AND files] OR [decision AND note] OR [pr AND files]",
|
|
737
740
|
);
|
|
738
741
|
// New rich hint includes the same content as formatGateRequirementHint.
|
|
739
742
|
expect(r.hint).toBe(formatGateRequirementHint('implemented'));
|
|
@@ -747,7 +750,7 @@ describe('validateEvidenceForGate failure surface (T9949)', () => {
|
|
|
747
750
|
expect(r.ok).toBe(false);
|
|
748
751
|
if (!r.ok) {
|
|
749
752
|
expect(r.hint).toContain("--evidence 'tool:test'");
|
|
750
|
-
expect(r.hint).toContain(
|
|
753
|
+
expect(r.hint).not.toContain('pr:357');
|
|
751
754
|
expect(r.hint).toContain("'note:' is NOT accepted for this gate");
|
|
752
755
|
}
|
|
753
756
|
});
|
|
@@ -58,6 +58,7 @@ type AssertEquals1<T extends 1> = T;
|
|
|
58
58
|
// ─── Search: BrainCompactHit shape pin ──────────────────────────────
|
|
59
59
|
|
|
60
60
|
type _BrainCompactHitShape = {
|
|
61
|
+
matchedHistoricalIds?: string[];
|
|
61
62
|
id: string;
|
|
62
63
|
type: 'decision' | 'pattern' | 'learning' | 'observation';
|
|
63
64
|
title: string;
|
|
@@ -73,6 +74,7 @@ type _AssertBrainCompactHitPinned = AssertEquals1<Equals<BrainCompactHit, _Brain
|
|
|
73
74
|
// ─── Search: SearchBrainCompactParams shape pin ─────────────────────
|
|
74
75
|
|
|
75
76
|
type _SearchBrainCompactParamsShape = {
|
|
77
|
+
includeHistory?: boolean;
|
|
76
78
|
query: string;
|
|
77
79
|
limit?: number;
|
|
78
80
|
tables?: Array<'decisions' | 'patterns' | 'learnings' | 'observations'>;
|
|
@@ -20,6 +20,19 @@ describe('operations-registry', () => {
|
|
|
20
20
|
expect(JSON.stringify(OPERATIONS)).toMatchSnapshot();
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
it('declares deletion force and cascade as distinct optional boolean controls', () => {
|
|
24
|
+
const deletion = OPERATIONS.find(
|
|
25
|
+
(operation) => operation.domain === 'tasks' && operation.operation === 'delete',
|
|
26
|
+
);
|
|
27
|
+
expect(deletion?.requiredParams).toEqual(['taskId']);
|
|
28
|
+
expect(deletion?.params).toEqual(
|
|
29
|
+
expect.arrayContaining([
|
|
30
|
+
expect.objectContaining({ name: 'force', type: 'boolean', required: false }),
|
|
31
|
+
expect.objectContaining({ name: 'cascade', type: 'boolean', required: false }),
|
|
32
|
+
]),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
23
36
|
it('OPERATIONS count is consistent', () => {
|
|
24
37
|
expect(OPERATIONS.length).toBeGreaterThan(0);
|
|
25
38
|
const queryCount = OPERATIONS.filter((o) => o.gateway === 'query').length;
|
|
@@ -40,6 +53,19 @@ describe('operations-registry', () => {
|
|
|
40
53
|
}
|
|
41
54
|
});
|
|
42
55
|
|
|
56
|
+
it('registers docs.status as a read-only, session-independent query', () => {
|
|
57
|
+
const matches = OPERATIONS.filter(
|
|
58
|
+
(operation) => operation.domain === 'docs' && operation.operation === 'status',
|
|
59
|
+
);
|
|
60
|
+
expect(matches).toHaveLength(1);
|
|
61
|
+
expect(matches[0]).toMatchObject({
|
|
62
|
+
gateway: 'query',
|
|
63
|
+
idempotent: true,
|
|
64
|
+
sessionRequired: false,
|
|
65
|
+
requiredParams: [],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
43
69
|
it('defineOp is an identity function', () => {
|
|
44
70
|
const def = OPERATIONS[0];
|
|
45
71
|
expect(defineOp(def)).toBe(def);
|