@holdyourvoice/hyv 3.6.1 → 4.0.1

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.
Files changed (87) hide show
  1. package/Readme.md +91 -96
  2. package/dist/ai-editor-rules.js +1 -0
  3. package/dist/ai-editor.js +19 -13
  4. package/dist/analysis.js +33 -0
  5. package/dist/cli/agents.js +15 -35
  6. package/dist/cli/io.js +41 -37
  7. package/dist/copy-spec.js +6 -6
  8. package/dist/editorial-packs.js +4 -3
  9. package/dist/fact-linter.js +87 -37
  10. package/dist/hidden-text.js +6 -6
  11. package/dist/hold-your-voice.mcpb +0 -0
  12. package/dist/internal.js +8 -0
  13. package/dist/judgment-task.js +10 -14
  14. package/dist/learning.js +66 -67
  15. package/dist/lifecycle-adapter.js +2 -5
  16. package/dist/local-eval.js +16 -31
  17. package/dist/mcp-server.js +272 -0
  18. package/dist/mcp-tools.js +2 -2
  19. package/dist/mcp.js +2 -261
  20. package/dist/pipeline.js +9 -85
  21. package/dist/profile-compose.js +18 -11
  22. package/dist/profile-score.js +6 -6
  23. package/dist/profile.js +17 -19
  24. package/dist/provenance-status.js +1 -3
  25. package/dist/rebuild-task.js +14 -25
  26. package/dist/recomposition.js +18 -13
  27. package/dist/rewrite-prompt.js +125 -0
  28. package/dist/rewrite-task.js +117 -70
  29. package/dist/rule-allowances.js +6 -10
  30. package/dist/semantic-review.js +71 -47
  31. package/dist/stage1-evaluation.js +29 -14
  32. package/dist/strict-quality.js +10 -18
  33. package/dist/version.js +1 -1
  34. package/dist/voice-dna.js +34 -21
  35. package/dist/writing-examples.js +2 -7
  36. package/package.json +6 -3
  37. package/skills/hyv-prepare-judgment/SKILL.md +4 -0
  38. package/dist/agents/catalog.test.js +0 -60
  39. package/dist/agents/emit.test.js +0 -64
  40. package/dist/agents/load.test.js +0 -149
  41. package/dist/ai-editor.test.js +0 -265
  42. package/dist/approval-capability.test.js +0 -52
  43. package/dist/approval-context.test.js +0 -38
  44. package/dist/backtest.test.js +0 -20
  45. package/dist/benchmark.test.js +0 -328
  46. package/dist/canonical-json.test.js +0 -24
  47. package/dist/cli/context.test.js +0 -55
  48. package/dist/cli.test.js +0 -731
  49. package/dist/editorial-packs.test.js +0 -94
  50. package/dist/fact-linter.test.js +0 -85
  51. package/dist/hidden-text.test.js +0 -26
  52. package/dist/hygiene.test.js +0 -83
  53. package/dist/judgment-task.test.js +0 -162
  54. package/dist/learning.test.js +0 -325
  55. package/dist/lifecycle-adapter.test.js +0 -56
  56. package/dist/local-eval.test.js +0 -20
  57. package/dist/logic-linter-corpus.test.js +0 -22
  58. package/dist/logic-linter.test.js +0 -39
  59. package/dist/mcp-tools.test.js +0 -286
  60. package/dist/mcp.test.js +0 -312
  61. package/dist/mirror-refs.test.js +0 -63
  62. package/dist/pipeline.test.js +0 -247
  63. package/dist/preservation.test.js +0 -22
  64. package/dist/production-gates.test.js +0 -34
  65. package/dist/profile-compose.test.js +0 -32
  66. package/dist/profile-score.test.js +0 -22
  67. package/dist/profile-watch.test.js +0 -23
  68. package/dist/profile.test.js +0 -141
  69. package/dist/provenance-status.test.js +0 -22
  70. package/dist/rebuild-task.test.js +0 -206
  71. package/dist/recomposition.test.js +0 -34
  72. package/dist/release-audit.test.js +0 -292
  73. package/dist/rewrite-task.test.js +0 -166
  74. package/dist/rule-allowances.test.js +0 -17
  75. package/dist/rule-reconciliation.test.js +0 -50
  76. package/dist/sample-ingest.test.js +0 -52
  77. package/dist/semantic-review.test.js +0 -101
  78. package/dist/stage1-dry-run.test.js +0 -39
  79. package/dist/stage1-evaluation.test.js +0 -184
  80. package/dist/stage1-human-packet.test.js +0 -102
  81. package/dist/stage1-schema-contract.test.js +0 -95
  82. package/dist/stage2-human-packet.test.js +0 -81
  83. package/dist/strict-quality.test.js +0 -62
  84. package/dist/text-provenance.feature.test.js +0 -45
  85. package/dist/text.test.js +0 -16
  86. package/dist/voice-dna.test.js +0 -121
  87. package/dist/writing-examples.test.js +0 -35
@@ -1,184 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { createHash } from 'node:crypto';
3
- import test from 'node:test';
4
- import { EvaluationContractError, commitProtocol, freezeBlind, recordCheckpointDisposition, recordRating, reduceEvaluation, sealRatings, sha256Canonical, validateRuns, } from './stage1-evaluation.js';
5
- const hex = (character) => character.repeat(64);
6
- function protocol(mode = 'development-calibration') {
7
- const providerDisclosureDigest = hex('f');
8
- const reviewerDisclosureDigest = hex('8');
9
- const derivedRetentionDigest = hex('9');
10
- const caseIdentity = { caseId: 'case-1', caseDigest: hex('3'), provenanceDigest: hex('6'), providerDisclosureDigest, reviewerDisclosureDigest, derivedRetentionDigest };
11
- const nonce = 'AAAAAAAAAAAAAAAAAAAAAA';
12
- return {
13
- kind: 'hyv-stage1-preregistration', version: '1', mode,
14
- baseline: { packageVersion: '3.2.0', sourceCommit: '4e6269121d551c008a34db73077e1e4fea41b3f9' },
15
- stage1: { sourceCommit: '550ea24f652291dca13757fdbd2f0fa0b5e3f621' },
16
- benchmark: { manifestDigest: hex('1'), partitionDigest: hex('2'), partition: mode === 'locked-human' ? 'locked-test' : 'calibration', synthetic: mode !== 'locked-human' },
17
- cases: [{ ...caseIdentity, rightsDigest: sha256Canonical(caseIdentity) }],
18
- intentToTreat: { expectedAssignments: 2, expectedReviewers: 1 },
19
- execution: { provider: 'provider-neutral', model: 'pinned-model', modelRevision: 'immutable-revision-1', settingsDigest: hex('a'), taskContractDigest: hex('b'), rulesetDigest: hex('c'), rubricDigest: hex('d') },
20
- randomization: { algorithm: 'sha256-counter-v1', commitment: sha256Canonical({ algorithm: 'sha256-counter-v1', nonce, labels: { A: 'stage1', B: 'baseline' } }) },
21
- reviewerRosterDigest: hex('5'),
22
- analysis: { statistic: 'paired-preference-rate', decisionRule: 'preference-or-correction-with-workflow-non-regression', margin: 0.05, minimumCases: 1, minimumRatings: 1, missingRatingPolicy: 'count-as-non-preference', tiePolicy: 'count-as-half', retryPolicy: 'no-retry', routingPolicy: 'precommitted-blind-routing' },
23
- rights: { providerDisclosureDigest, reviewerDisclosureDigest, derivedRetentionDigest },
24
- releaseAuditContractDigest: hex('7'),
25
- registeredAt: '2026-08-13T00:00:00Z',
26
- measures: ['writer_preference', 'correction_versus_confirm', 'workflow_completion', 'workflow_abandonment'],
27
- gates: ['semantic', 'copy_spec', 'hygiene', 'preservation', 'cli_mcp_parity', 'backward_compatibility'],
28
- };
29
- }
30
- function rating(state, evidenceClass = 'synthetic-dry-run', previous = []) {
31
- const reviewerId = 'reviewer-1';
32
- const identityKey = sha256Canonical({ reviewerId });
33
- const core = {
34
- kind: 'hyv-stage1-reviewer-record', version: '1', evidenceClass,
35
- protocolDigest: state.committed.protocolDigest, candidateDigest: state.candidateDigest, packetDigest: state.packet.packetDigest,
36
- reviewerId, identityKey, recordId: sha256Canonical({ protocolDigest: state.committed.protocolDigest, packetDigest: state.packet.packetDigest, identityKey, caseId: 'case-1' }),
37
- sequence: previous.length + 1, previousRecordDigest: previous.length ? previous.at(-1).recordDigest : null,
38
- caseId: 'case-1', preferredLabel: 'A', correctionVersusConfirm: { A: 'confirm', B: 'correction' }, workflow: 'completed',
39
- };
40
- return { ...core, recordDigest: sha256Canonical(core), attestation: null };
41
- }
42
- function setup(mode = 'development-calibration') {
43
- const committed = commitProtocol(protocol(mode));
44
- const candidateDigest = sha256Canonical({ baseline: committed.baseline, stage1: committed.stage1 });
45
- const runs = ['baseline', 'stage1'].map((arm, ordinal) => ({
46
- kind: 'hyv-stage1-run-record', version: '1', evidenceClass: mode === 'locked-human' ? 'human' : 'synthetic-dry-run',
47
- protocolDigest: committed.protocolDigest, candidateDigest, assignmentId: `assignment-${arm}`,
48
- participantId: 'participant-1', caseId: 'case-1', caseDigest: hex('3'), rightsDigest: committed.cases[0].rightsDigest, arm,
49
- ordinal: ordinal + 1, latencyMs: 10, inputTokens: 5, outputTokens: 5, costMicrousd: 1,
50
- occurredAt: `2026-08-13T00:00:0${ordinal}Z`,
51
- outcome: 'completed', outputDigest: createHash('sha256').update(arm === 'baseline' ? 'candidate B text' : 'candidate A text').digest('hex'),
52
- hardGates: { semantic: true, copySpec: true, hygiene: true, preservation: true, cliMcpParity: true, backwardCompatibility: true },
53
- }));
54
- const mapping = { kind: 'hyv-stage1-blind-mapping', version: '1', nonce: 'AAAAAAAAAAAAAAAAAAAAAA', labels: { A: 'stage1', B: 'baseline' }, custodianId: 'dry-run-custodian', unblindingAccess: ['dry-run-custodian'], attestation: null };
55
- const frozen = freezeBlind(committed, runs, mapping, [{ caseId: 'case-1', A: 'candidate A text', B: 'candidate B text' }]);
56
- return { committed, candidateDigest, runs, mapping, ...frozen };
57
- }
58
- test('canonical preregistration digest is stable and identities are immutable', () => {
59
- assert.equal(sha256Canonical({ b: 2, a: 1 }), sha256Canonical({ a: 1, b: 2 }));
60
- const committed = commitProtocol(protocol());
61
- assert.match(committed.protocolDigest, /^[a-f0-9]{64}$/);
62
- assert.throws(() => commitProtocol({ ...protocol(), stage1: { sourceCommit: hex('a') } }), (error) => error instanceof EvaluationContractError && error.code === 'stage1_identity_mismatch');
63
- assert.throws(() => commitProtocol({ ...protocol(), extra: true }), (error) => error instanceof EvaluationContractError && error.code === 'unexpected_field');
64
- assert.throws(() => commitProtocol({ ...protocol('locked-human'), benchmark: { ...protocol('locked-human').benchmark, partition: 'calibration' } }), (error) => error instanceof EvaluationContractError && error.code === 'benchmark_partition_mismatch');
65
- });
66
- test('run validation binds every digest and reconciles the intent-to-treat denominator', () => {
67
- const state = setup();
68
- assert.equal(validateRuns(state.committed, state.runs).denominator, 2);
69
- assert.throws(() => validateRuns(state.committed, state.runs.slice(1)), (error) => error instanceof EvaluationContractError && error.code === 'itt_denominator_mismatch');
70
- assert.throws(() => validateRuns(state.committed, [{ ...state.runs[0], rightsDigest: hex('9') }, state.runs[1]]), (error) => error instanceof EvaluationContractError && error.code === 'rights_digest_mismatch');
71
- const { outputDigest: _outputDigest, hardGates: _hardGates, ...failed } = state.runs[0];
72
- assert.equal(validateRuns(state.committed, [{ ...failed, outcome: 'timeout' }, state.runs[1]]).timeouts, 1);
73
- });
74
- test('preregistration and paired assignments reject the reviewed integrity attacks', () => {
75
- const base = protocol();
76
- assert.throws(() => commitProtocol({ ...base, registeredAt: '1' }), (error) => error instanceof EvaluationContractError && error.code === 'registered_at_invalid');
77
- assert.throws(() => commitProtocol({ ...base, registeredAt: '2026-02-31T00:00:00Z' }), (error) => error instanceof EvaluationContractError && error.code === 'registered_at_invalid');
78
- assert.throws(() => commitProtocol({ ...base, cases: [{ ...base.cases[0], rightsDigest: hex('4') }] }), (error) => error instanceof EvaluationContractError && error.code === 'case_rights_digest_mismatch');
79
- const state = setup();
80
- assert.throws(() => validateRuns(state.committed, [state.runs[0], { ...state.runs[1], ordinal: 1 }]), (error) => error instanceof EvaluationContractError && error.code === 'ordinal_sequence_invalid');
81
- assert.throws(() => validateRuns(state.committed, [state.runs[0], { ...state.runs[1], participantId: 'participant-2' }]), (error) => error instanceof EvaluationContractError && error.code === 'paired_participant_mismatch');
82
- assert.throws(() => freezeBlind(state.committed, state.runs, { ...state.mapping, nonce: 'short' }, [{ caseId: 'case-1', A: 'candidate A text', B: 'candidate B text' }]), (error) => error instanceof EvaluationContractError && error.code === 'randomization_nonce_invalid');
83
- assert.throws(() => freezeBlind(state.committed, state.runs, { ...state.mapping, unblindingAccess: [] }, [{ caseId: 'case-1', A: 'candidate A text', B: 'candidate B text' }]), (error) => error instanceof EvaluationContractError && error.code === 'unblinding_access_invalid');
84
- });
85
- test('blind packet rejects label leaks and mapping swaps', () => {
86
- const state = setup();
87
- const { attestation: _attestation, ...mappingCore } = state.mapping;
88
- assert.equal(state.packet.mappingDigest, sha256Canonical(mappingCore));
89
- assert.throws(() => freezeBlind(state.committed, state.runs, state.mapping, [{ caseId: 'case-1', A: 'stage1 output', B: 'safe' }]), (error) => error instanceof EvaluationContractError && error.code === 'blind_label_leak');
90
- assert.throws(() => sealRatings(state.packet, { ...state.mapping, labels: { A: 'baseline', B: 'stage1' } }, []), (error) => error instanceof EvaluationContractError && error.code === 'mapping_digest_mismatch');
91
- });
92
- test('reviewer log is append-only and rejects duplicate or conflicting ratings', () => {
93
- const state = setup();
94
- const record = rating(state);
95
- const records = recordRating(state.packet, [], record);
96
- assert.equal(records.length, 1);
97
- assert.throws(() => recordRating(state.packet, records, record), (error) => error instanceof EvaluationContractError && error.code === 'reviewer_chain_invalid' || error instanceof EvaluationContractError && error.code === 'duplicate_reviewer_record');
98
- const changedCore = { ...record, preferredLabel: 'B', sequence: 2, previousRecordDigest: record.recordDigest, recordDigest: undefined, attestation: undefined };
99
- delete changedCore.recordDigest;
100
- delete changedCore.attestation;
101
- assert.throws(() => recordRating(state.packet, records, { ...changedCore, recordDigest: sha256Canonical(changedCore), attestation: null }), (error) => error instanceof EvaluationContractError && error.code === 'conflicting_reviewer_record');
102
- });
103
- test('development/calibration reduction is always blocked and non-promotable', () => {
104
- const state = setup();
105
- const records = recordRating(state.packet, [], rating(state));
106
- const seal = sealRatings(state.packet, state.mapping, records);
107
- const auditCore = { kind: 'hyv-release-audit', version: '1', candidateCommit: state.committed.stage1.sourceCommit, protocolDigest: state.committed.protocolDigest, contractDigest: state.committed.releaseAuditContractDigest, passed: true };
108
- const releaseAudit = { ...auditCore, digest: sha256Canonical(auditCore) };
109
- const report = reduceEvaluation(state.committed, state.runs, state.packet, state.mapping, records, seal, releaseAudit);
110
- assert.equal(report.decision, 'BLOCKED');
111
- assert.equal(report.promotable, false);
112
- assert.ok(report.blockers.includes('synthetic_fixture_evidence'));
113
- assert.ok(report.blockers.includes('locked_human_evidence_required'));
114
- assert.ok(report.blockers.includes('human_writer_evidence_deferred'));
115
- });
116
- test('locked-human evidence rejects missing external signatures and release-audit drift', () => {
117
- const state = setup('locked-human');
118
- const unsigned = rating(state, 'human');
119
- assert.throws(() => recordRating(state.packet, [], unsigned), (error) => error instanceof EvaluationContractError && error.code === 'human_attestation_required');
120
- const signed = { ...unsigned, attestation: { kind: 'external-verification-receipt', version: '1', verified: true, artifactDigest: unsigned.recordDigest, trustStoreDigest: hex('a'), keyId: 'human-key-1', purpose: 'stage1-blind-review', protocolDigest: state.committed.protocolDigest, packetDigest: state.packet.packetDigest, nonce: 'nonce-1', issuedAt: '2026-08-13T00:00:00Z', expiresAt: '2026-08-14T00:00:00Z', verifiedAt: '2026-08-13T00:01:00Z' } };
121
- const records = recordRating(state.packet, [], signed);
122
- const seal = sealRatings(state.packet, state.mapping, records);
123
- assert.throws(() => reduceEvaluation(state.committed, state.runs, state.packet, state.mapping, records, seal, { kind: 'hyv-release-audit', version: '1', candidateCommit: hex('8'), protocolDigest: state.committed.protocolDigest, contractDigest: state.committed.releaseAuditContractDigest, passed: true, digest: hex('7') }), (error) => error instanceof EvaluationContractError && error.code === 'release_audit_digest_mismatch');
124
- });
125
- test('forged locked-human JSON cannot pass or proceed while human evidence is deferred', () => {
126
- const state = setup();
127
- const records = recordRating(state.packet, [], rating(state));
128
- const seal = sealRatings(state.packet, state.mapping, records);
129
- const auditCore = { kind: 'hyv-release-audit', version: '1', candidateCommit: state.committed.stage1.sourceCommit, protocolDigest: state.committed.protocolDigest, contractDigest: state.committed.releaseAuditContractDigest, passed: true };
130
- const report = reduceEvaluation(state.committed, state.runs, state.packet, state.mapping, records, seal, { ...auditCore, digest: sha256Canonical(auditCore) });
131
- const fake = { ...report, decision: 'PASS', promotable: true, blockers: [] };
132
- const { reportDigest: _old, ...core } = fake;
133
- const forged = { ...core, reportDigest: sha256Canonical(core) };
134
- assert.throws(() => recordCheckpointDisposition(forged, 'PROCEED_TO_MAR_363', {}), (error) => error instanceof EvaluationContractError && error.code === 'report_must_be_blocked');
135
- assert.throws(() => recordCheckpointDisposition(forged, 'STOP', {}), (error) => error instanceof EvaluationContractError && error.code === 'report_must_be_blocked');
136
- assert.throws(() => recordCheckpointDisposition(forged, 'REPEAT_PROTOCOL', {}), (error) => error instanceof EvaluationContractError && error.code === 'report_must_be_blocked');
137
- const emptyNestedCore = { ...report, intentToTreat: {}, metrics: {}, reportDigest: undefined };
138
- delete emptyNestedCore.reportDigest;
139
- const emptyNested = { ...emptyNestedCore, reportDigest: sha256Canonical(emptyNestedCore) };
140
- assert.throws(() => recordCheckpointDisposition(emptyNested, 'STOP', {}), (error) => error instanceof EvaluationContractError && error.code === 'missing_field');
141
- });
142
- test('a failed arm is committed as non-reviewable and remains in the ITT report', () => {
143
- const state = setup();
144
- const { outputDigest: _output, hardGates: _gates, ...stage1Failure } = state.runs[1];
145
- const runs = [state.runs[0], { ...stage1Failure, outcome: 'timeout' }];
146
- const { packet } = freezeBlind(state.committed, runs, state.mapping, [], [{ caseId: 'case-1', baselineOutcome: 'completed', stage1Outcome: 'timeout' }]);
147
- assert.equal(packet.reviewableCount, 0);
148
- assert.equal(packet.nonReviewableCount, 1);
149
- const abandonedCore = { ...rating({ ...state, packet }), workflow: 'abandoned', preferredLabel: undefined, correctionVersusConfirm: undefined, recordDigest: undefined, attestation: undefined };
150
- delete abandonedCore.preferredLabel;
151
- delete abandonedCore.correctionVersusConfirm;
152
- delete abandonedCore.recordDigest;
153
- delete abandonedCore.attestation;
154
- const abandoned = { ...abandonedCore, recordDigest: sha256Canonical(abandonedCore), attestation: null };
155
- const ratings = recordRating(packet, [], abandoned);
156
- const seal = sealRatings(packet, state.mapping, ratings);
157
- const auditCore = { kind: 'hyv-release-audit', version: '1', candidateCommit: state.committed.stage1.sourceCommit, protocolDigest: state.committed.protocolDigest, contractDigest: state.committed.releaseAuditContractDigest, passed: true };
158
- const report = reduceEvaluation(state.committed, runs, packet, state.mapping, ratings, seal, { ...auditCore, digest: sha256Canonical(auditCore) });
159
- assert.equal(report.decision, 'BLOCKED');
160
- assert.equal(report.metrics.preference.denominator, 1);
161
- assert.equal(report.metrics.preference.numerator, 0);
162
- assert.equal(report.metrics.abandonment.numerator, 1);
163
- assert.equal(report.metrics.correctionVersusConfirm.stage1.corrections, 0);
164
- assert.equal(report.metrics.correctionVersusConfirm.baseline.corrections, 0);
165
- assert.equal(report.metrics.providerRuns.timeouts, 1);
166
- });
167
- test('failed hard gates never enter blind review and packet counts reconcile at reduction', () => {
168
- const state = setup();
169
- const runs = [state.runs[0], { ...state.runs[1], hardGates: { ...state.runs[1].hardGates, semantic: false } }];
170
- const { packet } = freezeBlind(state.committed, runs, state.mapping, [], [{ caseId: 'case-1', baselineOutcome: 'completed', stage1Outcome: 'hard-gate-failed' }]);
171
- assert.equal(packet.reviewableCount, 0);
172
- const abandonedCore = { ...rating({ ...state, packet }), workflow: 'abandoned', preferredLabel: undefined, correctionVersusConfirm: undefined, recordDigest: undefined, attestation: undefined };
173
- delete abandonedCore.preferredLabel;
174
- delete abandonedCore.correctionVersusConfirm;
175
- delete abandonedCore.recordDigest;
176
- delete abandonedCore.attestation;
177
- const ratings = recordRating(packet, [], { ...abandonedCore, recordDigest: sha256Canonical(abandonedCore), attestation: null });
178
- const seal = sealRatings(packet, state.mapping, ratings);
179
- const auditCore = { kind: 'hyv-release-audit', version: '1', candidateCommit: state.committed.stage1.sourceCommit, protocolDigest: state.committed.protocolDigest, contractDigest: state.committed.releaseAuditContractDigest, passed: true };
180
- const forgedPacketCore = { ...packet, reviewableCount: 1, nonReviewableCount: 0, packetDigest: undefined };
181
- delete forgedPacketCore.packetDigest;
182
- const forgedPacket = { ...forgedPacketCore, packetDigest: sha256Canonical(forgedPacketCore) };
183
- assert.throws(() => reduceEvaluation(state.committed, runs, forgedPacket, state.mapping, ratings, seal, { ...auditCore, digest: sha256Canonical(auditCore) }), (error) => error instanceof EvaluationContractError && error.code === 'blind_packet_count_mismatch');
184
- });
@@ -1,102 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { execFileSync } from 'node:child_process';
3
- import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, symlinkSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { dirname, join } from 'node:path';
6
- import test from 'node:test';
7
- import { BASELINE_COMMIT, STAGE1_COMMIT } from './stage1-evaluation.js';
8
- function gitObjectExists(commit) {
9
- try {
10
- execFileSync('git', ['cat-file', '-t', commit], { stdio: 'pipe' });
11
- return true;
12
- }
13
- catch {
14
- return false;
15
- }
16
- }
17
- function packetFiles(root) {
18
- return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
19
- const path = join(root, entry.name);
20
- return entry.isDirectory() ? packetFiles(path) : [path];
21
- });
22
- }
23
- test('Stage 1 human packet executes outside the repository and remains blocked without ratings', () => {
24
- const parent = mkdtempSync(join(tmpdir(), 'hyv-stage1-human-packet-test-'));
25
- const outputRoot = join(parent, 'packet');
26
- try {
27
- const output = JSON.parse(execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', outputRoot], { encoding: 'utf8' }));
28
- assert.equal(output.kind, 'hyv-stage1-operator-kit');
29
- assert.equal(output.writerEvidence, false);
30
- assert.equal(output.kitEmitted, true);
31
- assert.equal(output.decision, 'BLOCKED');
32
- assert.equal(output.promotable, false);
33
- assert.ok(output.blockers.includes('human_writer_evidence_deferred'));
34
- assert.ok(output.blockers.includes('locked_human_evidence_required'));
35
- assert.equal(output.blockers.includes('stage1_commit_uncheckoutable'), !gitObjectExists(STAGE1_COMMIT));
36
- assert.ok(output.blockers.includes('receipts_unverified'));
37
- assert.equal(existsSync(join(outputRoot, 'ratings.ndjson')), false);
38
- for (const file of packetFiles(outputRoot)) {
39
- assert.doesNotMatch(readFileSync(file, 'utf8'), /"evidenceClass"\s*:\s*"human"/);
40
- }
41
- const status = JSON.parse(readFileSync(join(outputRoot, 'status.json'), 'utf8'));
42
- assert.equal(status.kind, 'hyv-stage1-operator-kit');
43
- assert.equal(status.writerEvidence, false);
44
- assert.equal(status.decision, output.decision);
45
- assert.equal(status.promotable, output.promotable);
46
- assert.deepEqual(status.blockers, output.blockers);
47
- }
48
- finally {
49
- rmSync(parent, { recursive: true, force: true });
50
- }
51
- });
52
- test('Stage 1 human packet reports locked commit checkoutability', () => {
53
- const parent = mkdtempSync(join(tmpdir(), 'hyv-stage1-human-identities-test-'));
54
- const outputRoot = join(parent, 'packet');
55
- try {
56
- execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', outputRoot], { stdio: 'pipe' });
57
- const identities = JSON.parse(readFileSync(join(outputRoot, 'IDENTITIES.json'), 'utf8'));
58
- assert.equal(identities.baselineCheckoutable, gitObjectExists(BASELINE_COMMIT));
59
- assert.equal(identities.stage1CommitCheckoutable, gitObjectExists(STAGE1_COMMIT));
60
- assert.equal(identities.exactHeadBindingSatisfied, identities.stage1CommitCheckoutable);
61
- assert.equal(identities.captureAuthorized, false);
62
- const operator = readFileSync(join(outputRoot, 'OPERATOR.md'), 'utf8');
63
- assert.match(operator, /Do not capture paired Stage 1 arm output until/);
64
- assert.match(operator, /verificationStatus: unverified/);
65
- const rating = JSON.parse(readFileSync(join(outputRoot, 'examples', 'rating.json'), 'utf8'));
66
- assert.equal(rating.verificationStatus, 'unverified');
67
- }
68
- finally {
69
- rmSync(parent, { recursive: true, force: true });
70
- }
71
- });
72
- test('Stage 1 human packet refuses a relative output path', () => {
73
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', 'stage1-human-output'], { stdio: 'pipe' }), /Human packet output must use an absolute path/);
74
- });
75
- test('Stage 1 human packet refuses repository output', () => {
76
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', join(process.cwd(), 'stage1-human-output')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
77
- });
78
- test('Stage 1 human packet refuses every linked worktree and the primary clone', () => {
79
- const porcelain = execFileSync('git', ['worktree', 'list', '--porcelain'], { encoding: 'utf8' });
80
- const trees = porcelain.split('\n').flatMap((line) => (line.startsWith('worktree ') ? [realpathSync(line.slice('worktree '.length))] : []));
81
- const gitCommonDir = realpathSync(execFileSync('git', ['rev-parse', '--git-common-dir'], { encoding: 'utf8' }).trim());
82
- trees.push(dirname(gitCommonDir));
83
- for (const root of new Set(trees)) {
84
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', join(root, 'stage1-human-output')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
85
- }
86
- });
87
- test('Stage 1 human packet refuses a symlinked ancestor into the repository', () => {
88
- const parent = mkdtempSync(join(tmpdir(), 'hyv-stage1-human-symlink-test-'));
89
- const repositoryTarget = join(process.cwd(), 'stage1-human-symlink-target');
90
- mkdirSync(repositoryTarget);
91
- symlinkSync(repositoryTarget, join(parent, 'redirect'));
92
- try {
93
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--out', join(parent, 'redirect', 'packet')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
94
- }
95
- finally {
96
- rmSync(parent, { recursive: true, force: true });
97
- rmSync(repositoryTarget, { recursive: true, force: true });
98
- }
99
- });
100
- test('Stage 1 human packet refuses fabricated human ratings', () => {
101
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage1-human-packet.mjs', '--fabricate-human-ratings'], { stdio: 'pipe' }), /Human evidence cannot be fabricated/);
102
- });
@@ -1,95 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { readFileSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import test from 'node:test';
5
- const schemaRoot = join(process.cwd(), 'benchmarks/schema');
6
- const schemaNames = [
7
- 'protocol-manifest.v1',
8
- 'run-event.v1',
9
- 'blind-packet.v1',
10
- 'blind-mapping.v1',
11
- 'reviewer-record.v1',
12
- 'ratings-seal.v1',
13
- 'aggregate-report.v1',
14
- 'checkpoint-disposition.v1',
15
- ];
16
- function loadSchema(name) {
17
- return JSON.parse(readFileSync(join(schemaRoot, `${name}.schema.json`), 'utf8'));
18
- }
19
- function assertWireKeys(name, keys) {
20
- const schema = loadSchema(name);
21
- const optionalKeys = name === 'run-event.v1'
22
- ? ['outputDigest', 'hardGates']
23
- : name === 'reviewer-record.v1' ? ['preferredLabel', 'correctionVersusConfirm'] : [];
24
- assert.deepEqual(Object.keys(schema.properties).sort(), [...keys].sort(), `${name} wire keys drifted from the evaluator`);
25
- assert.deepEqual([...schema.required].sort(), [...keys.filter((key) => !optionalKeys.includes(key))].sort(), `${name} required keys drifted from the evaluator`);
26
- }
27
- test('Stage 1 evidence schemas are strict Draft 2020-12 contracts', () => {
28
- for (const name of schemaNames) {
29
- const schema = loadSchema(name);
30
- assert.equal(schema.$schema, 'https://json-schema.org/draft/2020-12/schema');
31
- assert.equal(schema.$id, `https://holdyourvoice.dev/schemas/${name}.schema.json`);
32
- assert.equal(schema.type, 'object');
33
- assert.equal(schema.additionalProperties, false);
34
- assert.ok(Array.isArray(schema.required) && schema.required.length > 0);
35
- }
36
- });
37
- test('protocol and evidence schemas bind immutable identities and the complete digest chain', () => {
38
- const protocol = loadSchema('protocol-manifest.v1');
39
- assert.equal(protocol.properties.baseline.properties.sourceCommit.const, '4e6269121d551c008a34db73077e1e4fea41b3f9');
40
- assert.equal(protocol.properties.stage1.properties.sourceCommit.const, '550ea24f652291dca13757fdbd2f0fa0b5e3f621');
41
- for (const field of ['kind', 'version', 'mode', 'benchmark', 'cases', 'execution', 'reviewerRosterDigest', 'analysis', 'rights', 'releaseAuditContractDigest', 'protocolDigest']) {
42
- assert.ok(protocol.required.includes(field), `protocol must require ${field}`);
43
- }
44
- const run = loadSchema('run-event.v1');
45
- for (const field of ['protocolDigest', 'candidateDigest', 'caseDigest', 'rightsDigest', 'ordinal', 'latencyMs', 'inputTokens', 'outputTokens', 'costMicrousd'])
46
- assert.ok(run.required.includes(field));
47
- assert.ok(run.oneOf[0].required.includes('outputDigest'), 'completed runs must bind the output digest');
48
- assert.match(JSON.stringify(run.oneOf[1].not), /outputDigest/, 'failed runs must not fabricate an output digest');
49
- const packet = loadSchema('blind-packet.v1');
50
- for (const field of ['protocolDigest', 'candidateDigest', 'runsDigest', 'packetDigest', 'mappingDigest', 'contentDigest', 'nonReviewableDigest', 'reviewableCount', 'nonReviewableCount'])
51
- assert.ok(packet.required.includes(field));
52
- const reviewer = loadSchema('reviewer-record.v1');
53
- for (const field of ['protocolDigest', 'candidateDigest', 'packetDigest', 'reviewerId', 'identityKey', 'recordId', 'sequence', 'previousRecordDigest', 'recordDigest', 'caseId', 'attestation'])
54
- assert.ok(reviewer.required.includes(field));
55
- assert.equal(reviewer.properties.sourceText, undefined);
56
- assert.equal(reviewer.properties.correctionText, undefined);
57
- const seal = loadSchema('ratings-seal.v1');
58
- for (const field of ['protocolDigest', 'candidateDigest', 'packetDigest', 'mappingDigest', 'ratingsDigest', 'sealDigest'])
59
- assert.ok(seal.required.includes(field));
60
- const report = loadSchema('aggregate-report.v1');
61
- for (const field of ['protocolDigest', 'candidateDigest', 'runsDigest', 'packetDigest', 'mappingDigest', 'sealDigest', 'releaseAuditDigest', 'intentToTreat', 'metrics'])
62
- assert.ok(report.required.includes(field));
63
- });
64
- test('deferred-human artifacts cannot claim pass, promotion, or MAR-363 progression', () => {
65
- const report = loadSchema('aggregate-report.v1');
66
- assert.equal(report.properties.decision.const, 'BLOCKED');
67
- assert.equal(report.properties.promotable.const, false);
68
- assert.equal(report.properties.blockers.contains.const, 'human_writer_evidence_deferred');
69
- const disposition = loadSchema('checkpoint-disposition.v1');
70
- assert.deepEqual(disposition.properties.disposition.enum, ['STOP', 'REPEAT_PROTOCOL']);
71
- assert.equal(disposition.properties.releaseApproved, undefined, 'checkpoint disposition is not a release approval');
72
- });
73
- test('schema wire keys match evaluator artifacts exactly', () => {
74
- assertWireKeys('protocol-manifest.v1', ['kind', 'version', 'mode', 'baseline', 'stage1', 'benchmark', 'cases', 'intentToTreat', 'execution', 'randomization', 'reviewerRosterDigest', 'analysis', 'rights', 'releaseAuditContractDigest', 'registeredAt', 'measures', 'gates', 'protocolDigest']);
75
- assertWireKeys('run-event.v1', ['kind', 'version', 'evidenceClass', 'protocolDigest', 'candidateDigest', 'assignmentId', 'participantId', 'caseId', 'caseDigest', 'rightsDigest', 'arm', 'ordinal', 'latencyMs', 'inputTokens', 'outputTokens', 'costMicrousd', 'occurredAt', 'outcome', 'outputDigest', 'hardGates']);
76
- assertWireKeys('blind-packet.v1', ['kind', 'version', 'protocolDigest', 'candidateDigest', 'runsDigest', 'mappingDigest', 'contentDigest', 'nonReviewableDigest', 'reviewableCount', 'nonReviewableCount', 'encryptedAtRest', 'approvedStorage', 'packetDigest']);
77
- assertWireKeys('blind-mapping.v1', ['kind', 'version', 'nonce', 'labels', 'custodianId', 'unblindingAccess', 'attestation']);
78
- assertWireKeys('reviewer-record.v1', ['kind', 'version', 'evidenceClass', 'protocolDigest', 'candidateDigest', 'packetDigest', 'reviewerId', 'identityKey', 'recordId', 'sequence', 'previousRecordDigest', 'recordDigest', 'caseId', 'preferredLabel', 'correctionVersusConfirm', 'workflow', 'attestation']);
79
- assertWireKeys('ratings-seal.v1', ['kind', 'version', 'protocolDigest', 'candidateDigest', 'packetDigest', 'mappingDigest', 'ratingsDigest', 'recordCount', 'sealDigest']);
80
- assertWireKeys('aggregate-report.v1', ['kind', 'version', 'protocolDigest', 'candidateDigest', 'runsDigest', 'packetDigest', 'mappingDigest', 'sealDigest', 'releaseAuditDigest', 'intentToTreat', 'metrics', 'decision', 'promotable', 'blockers', 'reportDigest']);
81
- assertWireKeys('checkpoint-disposition.v1', ['kind', 'version', 'protocolDigest', 'packetDigest', 'releaseAuditDigest', 'reportDigest', 'disposition', 'attestation', 'dispositionDigest']);
82
- });
83
- test('nested protocol and report contracts match the repaired runtime', () => {
84
- const protocol = loadSchema('protocol-manifest.v1');
85
- assert.deepEqual([...protocol.properties.cases.items.required].sort(), ['caseId', 'caseDigest', 'provenanceDigest', 'rightsDigest', 'providerDisclosureDigest', 'reviewerDisclosureDigest', 'derivedRetentionDigest'].sort());
86
- assert.deepEqual([...protocol.properties.execution.required].sort(), ['provider', 'model', 'modelRevision', 'settingsDigest', 'taskContractDigest', 'rulesetDigest', 'rubricDigest'].sort());
87
- const reviewer = loadSchema('reviewer-record.v1');
88
- assert.deepEqual([...reviewer.properties.correctionVersusConfirm.required].sort(), ['A', 'B']);
89
- assert.ok(!reviewer.required.includes('preferredLabel'));
90
- assert.ok(!reviewer.required.includes('correctionVersusConfirm'));
91
- assert.match(JSON.stringify(reviewer.allOf), /abandoned.*preferredLabel.*correctionVersusConfirm/);
92
- const report = loadSchema('aggregate-report.v1');
93
- assert.deepEqual([...report.properties.intentToTreat.required].sort(), ['denominator', 'completed', 'hardFailures', 'timeouts', 'abandonments', 'runsDigest', 'expectedRatings', 'observedRatings', 'missingRatings', 'reconciled'].sort());
94
- assert.deepEqual([...report.properties.metrics.required].sort(), ['preference', 'correctionVersusConfirm', 'completion', 'abandonment', 'providerRuns'].sort());
95
- });
@@ -1,81 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { execFileSync } from 'node:child_process';
3
- import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, symlinkSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { dirname, join } from 'node:path';
6
- import test from 'node:test';
7
- function packetFiles(root) {
8
- return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
9
- const path = join(root, entry.name);
10
- return entry.isDirectory() ? packetFiles(path) : [path];
11
- });
12
- }
13
- test('Stage 2 human packet executes outside the repository and remains blocked without ratings', () => {
14
- const parent = mkdtempSync(join(tmpdir(), 'hyv-stage2-human-packet-test-'));
15
- const outputRoot = join(parent, 'packet');
16
- try {
17
- const output = JSON.parse(execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--out', outputRoot], { encoding: 'utf8' }));
18
- assert.equal(output.kind, 'hyv-stage2-operator-kit');
19
- assert.equal(output.writerEvidence, false);
20
- assert.equal(output.kitEmitted, true);
21
- assert.equal(output.decision, 'BLOCKED');
22
- assert.equal(output.promotable, false);
23
- assert.ok(output.blockers.includes('stage2_adoption_evidence_deferred'));
24
- assert.ok(output.blockers.includes('stage1_checkpoint_unpassed'));
25
- assert.ok(output.blockers.includes('rebuild_release_claim_blocked'));
26
- assert.equal(existsSync(join(outputRoot, 'ratings.ndjson')), false);
27
- for (const file of packetFiles(outputRoot)) {
28
- assert.doesNotMatch(readFileSync(file, 'utf8'), /"evidenceClass"\s*:\s*"human"/);
29
- }
30
- const status = JSON.parse(readFileSync(join(outputRoot, 'status.json'), 'utf8'));
31
- assert.equal(status.kind, 'hyv-stage2-operator-kit');
32
- assert.equal(status.decision, 'BLOCKED');
33
- assert.equal(status.promotable, false);
34
- assert.deepEqual(status.blockers, output.blockers);
35
- const identities = JSON.parse(readFileSync(join(outputRoot, 'IDENTITIES.json'), 'utf8'));
36
- assert.equal(identities.rebuildCommit, '387de69eae9ec176f32bfb0f3a7b769a4e0b6686');
37
- const protocolSkeleton = JSON.parse(readFileSync(join(outputRoot, 'protocol-skeleton.json'), 'utf8'));
38
- assert.equal(protocolSkeleton.rebuild.sourceCommit, identities.rebuildCommit);
39
- assert.equal(protocolSkeleton.baseline.packageVersion, '3.2.0');
40
- const operator = readFileSync(join(outputRoot, 'OPERATOR.md'), 'utf8');
41
- assert.match(operator, /four separate reports/);
42
- assert.match(operator, /Product publish does not wait on this kit/);
43
- assert.match(operator, /Never record PROCEED_TO_MAR_365/);
44
- assert.match(operator, /product npm publish may proceed without this kit/);
45
- assert.match(operator, /do not claim writer checkpoints passed/);
46
- }
47
- finally {
48
- rmSync(parent, { recursive: true, force: true });
49
- }
50
- });
51
- test('Stage 2 human packet refuses fabricated human ratings', () => {
52
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--fabricate-human-ratings'], { stdio: 'pipe' }), /Human evidence cannot be fabricated/);
53
- });
54
- test('Stage 2 human packet refuses a relative output path', () => {
55
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--out', 'stage2-human-output'], { stdio: 'pipe' }), /Human packet output must use an absolute path/);
56
- });
57
- test('Stage 2 human packet refuses repository output', () => {
58
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--out', join(process.cwd(), 'stage2-human-output')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
59
- });
60
- test('Stage 2 human packet refuses every linked worktree and the primary clone', () => {
61
- const porcelain = execFileSync('git', ['worktree', 'list', '--porcelain'], { encoding: 'utf8' });
62
- const trees = porcelain.split('\n').flatMap((line) => (line.startsWith('worktree ') ? [realpathSync(line.slice('worktree '.length))] : []));
63
- const gitCommonDir = realpathSync(execFileSync('git', ['rev-parse', '--git-common-dir'], { encoding: 'utf8' }).trim());
64
- trees.push(dirname(gitCommonDir));
65
- for (const root of new Set(trees)) {
66
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--out', join(root, 'stage2-human-output')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
67
- }
68
- });
69
- test('Stage 2 human packet refuses a symlinked ancestor into the repository', () => {
70
- const parent = mkdtempSync(join(tmpdir(), 'hyv-stage2-human-symlink-test-'));
71
- const repositoryTarget = join(process.cwd(), 'stage2-human-symlink-target');
72
- mkdirSync(repositoryTarget);
73
- symlinkSync(repositoryTarget, join(parent, 'redirect'));
74
- try {
75
- assert.throws(() => execFileSync(process.execPath, ['scripts/run-stage2-human-packet.mjs', '--out', join(parent, 'redirect', 'packet')], { stdio: 'pipe' }), /Human packet output must stay outside the repository and every worktree/);
76
- }
77
- finally {
78
- rmSync(parent, { recursive: true, force: true });
79
- rmSync(repositoryTarget, { recursive: true, force: true });
80
- }
81
- });
@@ -1,62 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { createHash } from 'node:crypto';
3
- import test from 'node:test';
4
- import { evaluateStrictQuality } from './strict-quality.js';
5
- function canonical(value) {
6
- if (Array.isArray(value))
7
- return `[${value.map(canonical).join(',')}]`;
8
- if (value !== null && typeof value === 'object')
9
- return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(',')}}`;
10
- return JSON.stringify(value);
11
- }
12
- function profile() {
13
- const unsigned = {
14
- version: '3', id: 'strict.author', revision: 1, sampleCount: 5,
15
- metrics: { sentenceLength: 6, sentenceVariation: 2, sentenceStructure: [], rhythm: 1, paragraphLength: 1, openingMoves: [], vocabulary: [], lexicalDensity: 0.5, pointOfView: 'mixed', punctuation: { '!': 0, '?': 0, ';': 0, ':': 0, '—': 0 }, caseStyle: 'standard', questionRate: 0, transitions: [] },
16
- avoid: [], provenance: { source: 'local', rights: 'author-owned', createdAt: '2026-08-25T00:00:00.000Z' }, rulePolicy: {},
17
- fingerprint: { contractionRate: 0, sentenceLengthDistribution: { short: 1, medium: 0, long: 0 }, bulletRate: 0, enDashRate: 0 },
18
- tolerances: { contractionRate: { absolute: 1, calibrated: true }, sentenceLengthDistribution: { absolute: 1, calibrated: true }, bulletRate: { absolute: 1, calibrated: true }, enDashRate: { absolute: 1, calibrated: true } },
19
- metricFixtures: { contractionRate: ['fixture.a'], sentenceLengthDistribution: ['fixture.b'], bulletRate: ['fixture.c'], enDashRate: ['fixture.d'] },
20
- };
21
- return { ...unsigned, revisionDigest: createHash('sha256').update(canonical(unsigned)).digest('hex') };
22
- }
23
- function samples() { return Array.from({ length: 5 }, (_, index) => Array.from({ length: 100 }, () => `sample${index} records one concrete detail.`).join(' ')); }
24
- test('returns strict-ready only for a calibrated, adequately sampled, clean v3 profile', () => {
25
- const report = evaluateStrictQuality('The launch starts Tuesday. The owner signed the checklist.', profile(), samples());
26
- assert.equal(report.disposition, 'strict-ready');
27
- assert.deepEqual(report.findings, []);
28
- });
29
- test('blocks an underpowered or uncalibrated voice profile before trusting voice matching', () => {
30
- const underpowered = evaluateStrictQuality('The launch starts Tuesday.', profile(), samples().slice(0, 2));
31
- assert.equal(underpowered.disposition, 'blocked');
32
- assert.ok(underpowered.findings.some((item) => item.id === 'strict.profile.sample-count'));
33
- const sparse = evaluateStrictQuality('The launch starts Tuesday.', profile(), Array.from({ length: 5 }, (_, index) => `sample ${index}.`));
34
- assert.ok(sparse.findings.some((item) => item.id === 'strict.profile.sample-coverage'));
35
- const uncalibrated = profile();
36
- uncalibrated.tolerances.bulletRate.calibrated = false;
37
- const report = evaluateStrictQuality('The launch starts Tuesday.', uncalibrated, samples());
38
- assert.ok(report.findings.some((item) => item.id === 'strict.profile.uncalibrated.bulletRate'));
39
- const thinProfile = profile();
40
- thinProfile.sampleCount = 2;
41
- const thinProfileReport = evaluateStrictQuality('The launch starts Tuesday.', thinProfile, samples());
42
- assert.ok(thinProfileReport.findings.some((item) => item.id === 'strict.profile.profile-sample-count'));
43
- const mixedFormats = samples();
44
- mixedFormats[0] = Array.from({ length: 100 }, () => 'subject: a concise update for the release list.').join(' ');
45
- const mixedFormatReport = evaluateStrictQuality('The launch starts Tuesday.', profile(), mixedFormats);
46
- assert.ok(mixedFormatReport.findings.some((item) => item.id === 'strict.profile.format-spread'));
47
- });
48
- test('blocks every active AI Editor pattern under strict quality', () => {
49
- const review = evaluateStrictQuality('We leverage the existing checklist.', profile(), samples());
50
- assert.equal(review.disposition, 'blocked');
51
- assert.ok(review.findings.some((item) => item.id === 'strict.ai_editor.ai.leverage' && item.disposition === 'block'));
52
- const blockedProfile = profile();
53
- blockedProfile.rulePolicy['ai.leverage'] = 'blocking';
54
- const blocked = evaluateStrictQuality('We leverage the existing checklist.', blockedProfile, samples());
55
- assert.equal(blocked.disposition, 'blocked');
56
- assert.ok(blocked.findings.some((item) => item.id === 'strict.ai_editor.ai.leverage' && item.disposition === 'block'));
57
- });
58
- test('blocks unresolved final-output hygiene', () => {
59
- const report = evaluateStrictQuality('The launch starts\u200b Tuesday.', profile(), samples());
60
- assert.equal(report.disposition, 'blocked');
61
- assert.ok(report.findings.some((item) => item.id === 'strict.final-output'));
62
- });
@@ -1,45 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { readFileSync } from 'node:fs';
3
- import test from 'node:test';
4
- import { applyHiddenTextPolicy, inspectHiddenText } from './hidden-text.js';
5
- import { provenanceStatusForRebuild, writerRequestForRebuild } from './provenance-status.js';
6
- const feature = readFileSync(new URL('../features/text-provenance.feature', import.meta.url), 'utf8');
7
- const policy = { version: '1', mode: 'meaning-first', lexicalResidual: { ngramSize: 5, maxSharedNgramFraction: 0.1, maxLongestSharedRunTokens: 8 }, acknowledgement: 'Measures shared wording only; does not detect or prove removal of a watermark.' };
8
- const task = {
9
- version: '1', fingerprint: 'a'.repeat(64), draft: 'private source phrase', prompt: 'structured task prompt',
10
- copySpec: { version: '1', audience: 'operators', intent: 'explain', channel: 'email', claims: [{ id: 'date', text: 'The date is fixed.', evidence: 'calendar' }] },
11
- recommendationFingerprint: 'b'.repeat(64), authorizationFingerprint: 'c'.repeat(64), profileId: 'profile', profileRevisionDigest: 'd'.repeat(64), recompositionPolicy: policy,
12
- };
13
- function scenario(name, verify) {
14
- test(`Feature: bounded text provenance sanitation — Scenario: ${name}`, () => {
15
- assert.match(feature, new RegExp(`Scenario: ${name}`));
16
- verify();
17
- });
18
- }
19
- scenario('explicitly approved non-semantic controls are removed with evidence', () => {
20
- const receipt = applyHiddenTextPolicy('one\u0007two\uFEFFthree');
21
- assert.deepEqual(receipt.proposedChanges.map((item) => item.codepoint), ['U+0007', 'U+FEFF']);
22
- assert.match(receipt.inputHash, /^[a-f0-9]{64}$/);
23
- assert.match(receipt.outputHash, /^[a-f0-9]{64}$/);
24
- assert.equal(receipt.idempotent, true);
25
- });
26
- scenario('multilingual and structured text is review-only by default', () => {
27
- const source = '```md\nالعربية\u202E ไทย\u200B 👩\u200D💻\t\n```';
28
- const receipt = applyHiddenTextPolicy(source);
29
- assert.equal(receipt.output, source);
30
- assert.equal(receipt.proposedChanges.length, 0);
31
- assert.ok(inspectHiddenText(source).findings.every((item) => item.action === 'review'));
32
- });
33
- scenario('an external writer receives no source prose', () => {
34
- const request = writerRequestForRebuild(task);
35
- assert.match(request.prompt, /structured task prompt/);
36
- assert.doesNotMatch(JSON.stringify(request), /private source phrase|authorizationFingerprint|profileRevisionDigest|draft/);
37
- });
38
- scenario('unknown provider status remains explicit', () => {
39
- assert.deepEqual(provenanceStatusForRebuild(task), { version: '1', state: 'unknown', reason: 'private_or_unavailable_verifier' });
40
- assert.match(feature, /does not claim watermark removal or absence/);
41
- });
42
- scenario('a controlled verifier is not configured', () => {
43
- assert.deepEqual(provenanceStatusForRebuild({ ...task, recompositionPolicy: undefined }), { version: '1', state: 'not_configured' });
44
- assert.match(feature, /lexical residual is not used as a provider verifier/);
45
- });
package/dist/text.test.js DELETED
@@ -1,16 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
- import { sentences, words } from './text.js';
4
- test('keeps decimals and common abbreviations inside a sentence', () => {
5
- assert.deepEqual(sentences('Dr. Shah raised $3.5m. It funded the work.').map((sentence) => sentence.text), ['Dr. Shah raised $3.5m.', 'It funded the work.']);
6
- });
7
- test('records exact sentence offsets across newlines', () => {
8
- const text = 'First line\nSecond line.';
9
- assert.deepEqual(sentences(text), [
10
- { index: 1, start: 0, end: 10, text: 'First line' },
11
- { index: 2, start: 11, end: 23, text: 'Second line.' },
12
- ]);
13
- });
14
- test('counts Unicode writing as words', () => {
15
- assert.deepEqual(words('café नमस्ते दुनिया'), ['café', 'नमस्ते', 'दुनिया']);
16
- });