@holdyourvoice/hyv 3.5.1 → 3.6.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/Readme.md +4 -2
- package/dist/cli.test.js +1 -1
- package/dist/hold-your-voice.mcpb +0 -0
- package/dist/mcp.js +7 -7
- package/dist/pipeline.js +19 -9
- package/dist/pipeline.test.js +5 -3
- package/dist/rebuild-task.test.js +1 -1
- package/dist/rewrite-task.js +1 -1
- package/dist/rewrite-task.test.js +10 -9
- package/dist/strict-quality.js +1 -2
- package/dist/strict-quality.test.js +3 -3
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/hyv-analyze/SKILL.md +1 -1
- package/skills/hyv-analyze/agents/openai.yaml +1 -1
- package/skills/hyv-apply-rebuild/SKILL.md +1 -0
- package/skills/hyv-apply-rebuild/agents/openai.yaml +1 -1
- package/skills/hyv-apply-rewrite/SKILL.md +1 -0
- package/skills/hyv-apply-rewrite/agents/openai.yaml +1 -1
- package/skills/hyv-prepare-rebuild/SKILL.md +1 -0
- package/skills/hyv-prepare-rebuild/agents/openai.yaml +1 -1
- package/skills/hyv-prepare-rewrite/SKILL.md +1 -0
- package/skills/hyv-prepare-rewrite/agents/openai.yaml +1 -1
- package/skills/hyv-rebuild-writer-request/SKILL.md +1 -0
- package/skills/hyv-rebuild-writer-request/agents/openai.yaml +1 -1
- package/skills/hyv-rewrite-prompt/SKILL.md +4 -3
- package/skills/hyv-rewrite-prompt/agent.json +1 -1
- package/skills/hyv-rewrite-prompt/agents/openai.yaml +2 -2
- package/skills/hyv-strict-check/SKILL.md +3 -3
- package/skills/hyv-verify/SKILL.md +2 -1
- package/skills/hyv-verify/agents/openai.yaml +1 -1
- package/skills/hyv-verify-spec/SKILL.md +3 -2
- package/skills/hyv-verify-spec/agent.json +1 -1
- package/skills/hyv-verify-spec/agents/openai.yaml +2 -2
package/Readme.md
CHANGED
|
@@ -131,7 +131,7 @@ producer | hyv final-check -
|
|
|
131
131
|
| `hyv score <draft> <profile-v3.json> <heldout...>` | Measure the draft against a separate held-out local writing range; may abstain. |
|
|
132
132
|
| `hyv ingest <source> <export> --owner=owner --output=/absolute/dir` | Create redacted, owner-authorized Gmail or Telegram samples and a text-free receipt. |
|
|
133
133
|
| `hyv analyze <draft> <profile.json>` | Run VoiceDNA, AI Editor, and hygiene checks. |
|
|
134
|
-
| `hyv strict-check <draft> <profile-v3.json> <sample...>` | Run the
|
|
134
|
+
| `hyv strict-check <draft> <profile-v3.json> <sample...>` | Run the calibrated local strict-quality gate. It requires a V3 profile built from five or more samples, five non-duplicate validation samples with a consistent visible format and 1,500 words total, and returns `strict-ready`, `needs-human-review`, or `blocked`. |
|
|
135
135
|
| `hyv hygiene <draft> [--fix]` | Inspect hidden Unicode or write a conservative cleaned copy. |
|
|
136
136
|
| `hyv inspect-hidden-text <draft> [policy.json]` | Inspect hidden text with an optional policy. |
|
|
137
137
|
| `hyv apply-hidden-text-policy <draft> <policy.json> <output>` | Apply approved hidden-text removals. |
|
|
@@ -143,7 +143,7 @@ producer | hyv final-check -
|
|
|
143
143
|
| `hyv fact-lint <draft\|-> --source=id:path` | Check claims against local source files. |
|
|
144
144
|
| `hyv logic-lint <draft\|-> [brief.json]` | Check deterministic document logic. |
|
|
145
145
|
| `hyv batch-analyze <draft...>` | Find repeated openings and endings across drafts. |
|
|
146
|
-
| `hyv rewrite-prompt <draft> <profile.json>` | Create a constrained editing brief. |
|
|
146
|
+
| `hyv rewrite-prompt <draft> <profile.json>` | Create a strict constrained editing brief. |
|
|
147
147
|
| `hyv prepare-rewrite ...` | Create a fingerprint-bound edit task. |
|
|
148
148
|
| `hyv apply-rewrite ...` | Apply and verify a response to an edit task. |
|
|
149
149
|
| `hyv prepare-judgment ...` | Create a pre-edit or post-candidate judgment task. |
|
|
@@ -159,6 +159,8 @@ producer | hyv final-check -
|
|
|
159
159
|
| `hyv agent list\|validate\|describe\|emit` | Inspect or emit portable agent contracts. |
|
|
160
160
|
| `hyv mcp` | Start the local MCP server on standard input/output. |
|
|
161
161
|
|
|
162
|
+
`rewrite-prompt`, `prepare-rewrite`, `apply-rewrite`, and `verify` are strict by default: every active AI Editor finding must be repaired, and verification rejects a candidate that leaves one unresolved. A Profile v3 `disabled` policy is the only deliberate exception. `strict-check` adds the separate calibrated voice-evidence decision.
|
|
163
|
+
|
|
162
164
|
Most commands return JSON. Exit code `0` means the command completed, `2` means a content or policy gate failed, and `1` means the command or input was invalid.
|
|
163
165
|
|
|
164
166
|
Run `hyv <command>` without enough arguments to see its exact usage. Read the [CLI reference](docs/wiki/CLI-Reference.md) for every option.
|
package/dist/cli.test.js
CHANGED
|
@@ -272,7 +272,7 @@ test('uses exit code 2 for a failed candidate gate and 1 for misuse', () => {
|
|
|
272
272
|
assert.equal(run(['profile', profile, first, second, '--avoid=unlock']).status, 0);
|
|
273
273
|
const verification = run(['verify', original, candidate, profile]);
|
|
274
274
|
assert.equal(verification.status, 2);
|
|
275
|
-
assert.deepEqual(Object.keys(JSON.parse(verification.stdout)).sort(), ['candidate', 'finalOutput', 'logicLint', 'original', 'passed', 'preservationScore', 'regressions', 'version']);
|
|
275
|
+
assert.deepEqual(Object.keys(JSON.parse(verification.stdout)).sort(), ['candidate', 'finalOutput', 'logicLint', 'original', 'passed', 'preservationScore', 'regressions', 'strictFindings', 'version']);
|
|
276
276
|
assert.equal(run(['unknown-command']).status, 1);
|
|
277
277
|
assert.equal(run(['mcp', 'unexpected']).status, 1);
|
|
278
278
|
}
|
|
Binary file
|
package/dist/mcp.js
CHANGED
|
@@ -74,7 +74,7 @@ server.registerTool('hyv_analyze', {
|
|
|
74
74
|
annotations: { readOnlyHint: true },
|
|
75
75
|
}, async ({ draft, profile_json, writing_brief_json }) => guardedJson(() => analyzeForMcp(draft, profile_json, writing_brief_json)));
|
|
76
76
|
server.registerTool('hyv_strict_check', {
|
|
77
|
-
description: 'Run the
|
|
77
|
+
description: 'Run the calibrated local strict-quality gate. It requires a Profile v3 and local writing samples; it returns strict-ready, needs-human-review, or blocked without changing text or learning state.',
|
|
78
78
|
inputSchema: { draft: writing, profile_json: profileJson, samples: strictSamples, writing_brief_json: writingBriefJson.optional() },
|
|
79
79
|
annotations: { readOnlyHint: true },
|
|
80
80
|
}, async ({ draft, profile_json, samples: localSamples, writing_brief_json }) => guardedJson(() => strictCheckForMcp(draft, profile_json, localSamples, writing_brief_json)));
|
|
@@ -128,7 +128,7 @@ server.registerTool('hyv_logic_lint', {
|
|
|
128
128
|
annotations: { readOnlyHint: true },
|
|
129
129
|
}, async ({ draft, writing_brief_json }) => guardedJson(() => logicLintForMcp(draft, writing_brief_json)));
|
|
130
130
|
server.registerTool('hyv_rewrite_prompt', {
|
|
131
|
-
description: 'Create a constrained editing brief.
|
|
131
|
+
description: 'Create a strict constrained editing brief. Every active AI Editor finding is a required repair; explicit local examples remain advisory cadence evidence. It does not rewrite the draft or call a model.',
|
|
132
132
|
inputSchema: { draft: writing, profile_json: profileJson, writing_brief_json: writingBriefJson.optional(), examples: writingExamples.optional() },
|
|
133
133
|
annotations: { readOnlyHint: true },
|
|
134
134
|
}, async ({ draft, profile_json, writing_brief_json, examples }) => guardedJson(() => rewritePromptForMcp(draft, profile_json, {}, writing_brief_json, examples)));
|
|
@@ -138,12 +138,12 @@ server.registerTool('hyv_find_writing_examples', {
|
|
|
138
138
|
annotations: { readOnlyHint: true },
|
|
139
139
|
}, async ({ query, examples }) => guardedJson(() => findWritingExamplesForMcp(query, examples)));
|
|
140
140
|
server.registerTool('hyv_prepare_rewrite', {
|
|
141
|
-
description: 'Prepare a local, versioned rewrite task.
|
|
141
|
+
description: 'Prepare a strict local, versioned rewrite task. Every active AI Editor finding is eligible for source-faithful repair; the caller may forward the task to a provider only by explicit choice.',
|
|
142
142
|
inputSchema: { draft: writing, profile_json: profileJson, copy_spec_json: copySpecJson.optional(), writing_brief_json: writingBriefJson.optional() },
|
|
143
143
|
annotations: { readOnlyHint: true },
|
|
144
144
|
}, async ({ draft, profile_json, copy_spec_json, writing_brief_json }) => guardedJson(() => prepareRewriteForMcp(draft, profile_json, copy_spec_json, writing_brief_json)));
|
|
145
145
|
server.registerTool('hyv_apply_rewrite', {
|
|
146
|
-
description: 'Validate
|
|
146
|
+
description: 'Validate a model response to a prepared task, then reject any candidate with an unresolved active AI Editor finding. It never calls a provider or stores source or candidate text.',
|
|
147
147
|
inputSchema: { task_json: z.string().min(1).max(250_000), response_json: z.string().min(1).max(100_000), profile_json: profileJson },
|
|
148
148
|
annotations: { readOnlyHint: true },
|
|
149
149
|
}, async ({ task_json, response_json, profile_json }) => guardedJson(() => applyRewriteForMcp(task_json, response_json, profile_json)));
|
|
@@ -164,12 +164,12 @@ server.registerTool('hyv_reduce_judgment', {
|
|
|
164
164
|
annotations: { readOnlyHint: true },
|
|
165
165
|
}, async ({ envelopes_json }) => guardedJson(() => reduceJudgmentForMcp(envelopes_json)));
|
|
166
166
|
server.registerTool('hyv_verify', {
|
|
167
|
-
description: 'Verify a revised candidate against an original draft and portable profile
|
|
167
|
+
description: 'Verify a revised candidate against an original draft and portable profile. Every active AI Editor finding fails default verification; learning state remains unchanged.',
|
|
168
168
|
inputSchema: { original: writing, candidate: writing, profile_json: profileJson, writing_brief_json: writingBriefJson.optional() },
|
|
169
169
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
170
170
|
}, async ({ original, candidate, profile_json, writing_brief_json }) => guardedJson(() => verifyForMcp(original, candidate, profile_json, writing_brief_json)));
|
|
171
171
|
server.registerTool('hyv_verify_copy_spec', {
|
|
172
|
-
description: 'Verify a candidate against
|
|
172
|
+
description: 'Verify a candidate against strict default voice gates and a local CopySpec. Every active AI Editor finding fails verification; immutable claims remain verbatim unless atoms are supplied, and prohibited claims fail closed.',
|
|
173
173
|
inputSchema: { original: writing, candidate: writing, profile_json: profileJson, copy_spec_json: copySpecJson, writing_brief_json: writingBriefJson.optional() },
|
|
174
174
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
175
175
|
}, async ({ original, candidate, profile_json, copy_spec_json, writing_brief_json }) => guardedJson(() => verifyCopySpecForMcp(original, candidate, profile_json, copy_spec_json, writing_brief_json)));
|
|
@@ -250,7 +250,7 @@ if (redactsSensitiveInputs) {
|
|
|
250
250
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
251
251
|
}, async (args) => guardedJson(() => prepareRebuildForMcp(args.draft, args.profile_json, args.reduction_json, args.copy_spec_json, args.capability_json, loadApprovalContext(), args.writing_brief_json, args.recomposition_policy_json), 'Rebuild preparation failed.'));
|
|
252
252
|
server.registerTool('hyv_apply_rebuild', {
|
|
253
|
-
description: 'Validate
|
|
253
|
+
description: 'Validate a whole-document rebuild response against a prepared authorized rebuild task and reject unresolved active AI Editor findings. Capability input requires host-guaranteed sensitive-input redaction. It never calls a provider.',
|
|
254
254
|
inputSchema: { task_json: lifecycleJson, response_json: z.string().min(1).max(100_000), profile_json: profileJson, capability_json: lifecycleJson },
|
|
255
255
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
256
256
|
}, async (args) => guardedJson(() => applyRebuildForMcp(args.task_json, args.response_json, args.profile_json, args.capability_json, loadApprovalContext()), 'Rebuild application failed.'));
|
package/dist/pipeline.js
CHANGED
|
@@ -29,10 +29,16 @@ function formatFindings(findings) {
|
|
|
29
29
|
export function isBlockingFinding(finding) {
|
|
30
30
|
return finding.engine === 'ai_editor' ? finding.appliedPolicy === 'blocking' : finding.severity === 'red';
|
|
31
31
|
}
|
|
32
|
-
export function
|
|
32
|
+
export function isStrictFinding(finding) {
|
|
33
|
+
return finding.engine === 'ai_editor' || isBlockingFinding(finding);
|
|
34
|
+
}
|
|
35
|
+
export function strictFindings(result) {
|
|
36
|
+
return [...result.voiceDna.findings, ...result.aiEditor.findings, ...(result.editorial?.findings ?? [])].filter(isStrictFinding);
|
|
37
|
+
}
|
|
38
|
+
export function deriveEditScope(result, strict = false) {
|
|
33
39
|
const findings = [...result.voiceDna.findings, ...result.aiEditor.findings, ...(result.editorial?.findings ?? [])];
|
|
34
|
-
const blocking = findings.filter(isBlockingFinding);
|
|
35
|
-
const pendingJudgment = findings.filter((finding) => finding.appliedPolicy === 'judgment-required');
|
|
40
|
+
const blocking = findings.filter(strict ? isStrictFinding : isBlockingFinding);
|
|
41
|
+
const pendingJudgment = strict ? [] : findings.filter((finding) => finding.appliedPolicy === 'judgment-required');
|
|
36
42
|
return {
|
|
37
43
|
eligibleSentenceIds: [...new Set(blocking.map((finding) => finding.sentence))].sort((left, right) => left - right),
|
|
38
44
|
blocking,
|
|
@@ -41,16 +47,16 @@ export function deriveEditScope(result) {
|
|
|
41
47
|
}
|
|
42
48
|
export function renderRewritePrompt(draft, profile, result, learning = [], brief, examples = []) {
|
|
43
49
|
const allFindings = [...result.voiceDna.findings, ...result.aiEditor.findings, ...(result.editorial?.findings ?? [])];
|
|
44
|
-
const scope = deriveEditScope(result);
|
|
50
|
+
const scope = deriveEditScope(result, true);
|
|
45
51
|
const redFindings = scope.blocking;
|
|
46
|
-
const yellowFindings = allFindings.filter((finding) => !
|
|
52
|
+
const yellowFindings = allFindings.filter((finding) => !isStrictFinding(finding) && finding.appliedPolicy !== 'judgment-required');
|
|
47
53
|
const metrics = profile.metrics;
|
|
48
54
|
return [
|
|
49
55
|
'# Tier 0 — non-negotiable preservation',
|
|
50
56
|
'Preserve facts, names, numbers, claims, and every unflagged sentence exactly. Do not add claims, examples, sections, hooks, or CTAs.',
|
|
51
57
|
'',
|
|
52
|
-
'# Tier 1 —
|
|
53
|
-
'
|
|
58
|
+
'# Tier 1 — strict repair requirements',
|
|
59
|
+
'Every active AI Editor finding is a required repair. Replace each flagged sentence with a stronger, source-faithful sentence; do not merely swap one stock phrase for another.',
|
|
54
60
|
'Use only facts already present in the draft, CopySpec, WritingBrief, or supplied source context. Do not invent a source, metric, date, quotation, mechanism, example, CTA, or opinion.',
|
|
55
61
|
`VoiceDNA: ${result.voiceDna.score}/100 (${result.voiceDna.passed ? 'pass' : 'fail'}).`,
|
|
56
62
|
`AI Editor: ${result.aiEditor.score}/100 (${result.aiEditor.passed ? 'pass' : 'fail'}).`,
|
|
@@ -120,16 +126,18 @@ export function verify(original, candidate, profile, brief) {
|
|
|
120
126
|
const logicLint = lintLogic(candidate, brief);
|
|
121
127
|
const factLint = brief?.factSources?.length ? lintFacts({ sources: brief.factSources, draft: candidate, metadata: brief.factMetadata }) : undefined;
|
|
122
128
|
const requiredFacts = verifyRequiredFacts(candidate, brief);
|
|
129
|
+
const unresolvedStrictFindings = strictFindings(checked);
|
|
123
130
|
return {
|
|
124
131
|
version: '2',
|
|
125
132
|
original: baseline,
|
|
126
133
|
candidate: checked,
|
|
127
134
|
preservationScore: preservation,
|
|
128
135
|
regressions,
|
|
136
|
+
strictFindings: unresolvedStrictFindings,
|
|
129
137
|
finalOutput,
|
|
130
138
|
logicLint,
|
|
131
139
|
...(factLint ? { factLint } : {}), ...(requiredFacts ? { requiredFacts } : {}),
|
|
132
|
-
passed: checked.passed && !regressions.some(isBlockingFinding) && preservation >= 70 && finalOutput.accepted && logicLint.passed && !factLint?.findings.some((finding) => finding.severity === 'error') && (requiredFacts?.passed ?? true),
|
|
140
|
+
passed: checked.passed && unresolvedStrictFindings.length === 0 && !regressions.some(isBlockingFinding) && preservation >= 70 && finalOutput.accepted && logicLint.passed && !factLint?.findings.some((finding) => finding.severity === 'error') && (requiredFacts?.passed ?? true),
|
|
133
141
|
};
|
|
134
142
|
}
|
|
135
143
|
export function verifyWithCopySpec(original, candidate, profile, spec, brief) {
|
|
@@ -144,17 +152,19 @@ export function verifyRebuildWithCopySpec(original, candidate, profile, spec, br
|
|
|
144
152
|
const logicLint = lintLogic(candidate, brief);
|
|
145
153
|
const factLint = brief?.factSources?.length ? lintFacts({ sources: brief.factSources, draft: candidate, metadata: brief.factMetadata }) : undefined;
|
|
146
154
|
const requiredFacts = verifyRequiredFacts(candidate, brief);
|
|
155
|
+
const unresolvedStrictFindings = strictFindings(checked);
|
|
147
156
|
return {
|
|
148
157
|
version: '2',
|
|
149
158
|
original: baseline,
|
|
150
159
|
candidate: checked,
|
|
151
160
|
preservationScore: preservation,
|
|
152
161
|
regressions,
|
|
162
|
+
strictFindings: unresolvedStrictFindings,
|
|
153
163
|
claims,
|
|
154
164
|
finalOutput: finalCheck,
|
|
155
165
|
logicLint,
|
|
156
166
|
...(factLint ? { factLint } : {}), ...(requiredFacts ? { requiredFacts } : {}),
|
|
157
|
-
passed: checked.passed && !regressions.some(isBlockingFinding) && claims.passed && finalCheck.accepted && logicLint.passed && !factLint?.findings.some((finding) => finding.severity === 'error') && (requiredFacts?.passed ?? true),
|
|
167
|
+
passed: checked.passed && unresolvedStrictFindings.length === 0 && !regressions.some(isBlockingFinding) && claims.passed && finalCheck.accepted && logicLint.passed && !factLint?.findings.some((finding) => finding.severity === 'error') && (requiredFacts?.passed ?? true),
|
|
158
168
|
};
|
|
159
169
|
}
|
|
160
170
|
function projectDeterministicVerificationArtifact(source, candidate, profile, verification, copySpec, writingBrief, verificationKind = 'claims' in verification ? 'copy_spec' : 'standard') {
|
package/dist/pipeline.test.js
CHANGED
|
@@ -83,7 +83,7 @@ test('orders rewrite instructions by importance tier', () => {
|
|
|
83
83
|
});
|
|
84
84
|
test('gives strong, source-faithful repair feedback without authorizing a broader rewrite', () => {
|
|
85
85
|
const prompt = rewritePrompt('The scheduler failed — twice. Experts say this changes everything. The launch is on 14 August.', profile);
|
|
86
|
-
assert.match(prompt, /
|
|
86
|
+
assert.match(prompt, /Every active AI Editor finding is a required repair/);
|
|
87
87
|
assert.match(prompt, /Do not invent a source, metric, date, quotation, mechanism, example, CTA, or opinion/);
|
|
88
88
|
assert.match(prompt, /otherwise remove the unsupported framing without widening the claim/);
|
|
89
89
|
assert.match(prompt, /Before responding, check every Tier 1 finding against its replacement/);
|
|
@@ -119,9 +119,11 @@ test('advisory and pending-judgment findings pass while blocking findings fail',
|
|
|
119
119
|
const blocking = analyze('The scheduler failed — twice.', profile);
|
|
120
120
|
assert.equal(blocking.passed, false);
|
|
121
121
|
});
|
|
122
|
-
test('verify rejects
|
|
122
|
+
test('verify rejects unresolved active AI Editor findings under the default strict policy', () => {
|
|
123
123
|
const neutralProfile = buildProfile(['I write plainly.', 'I name the mechanism.']);
|
|
124
|
-
|
|
124
|
+
const unresolved = verify('The scheduler failed twice.', 'The scheduler failed twice. We leverage logs.', neutralProfile);
|
|
125
|
+
assert.equal(unresolved.passed, false);
|
|
126
|
+
assert.ok(unresolved.strictFindings.some((finding) => finding.id === 'ai.leverage'));
|
|
125
127
|
assert.equal(verify('The scheduler failed twice.', 'The scheduler failed — twice.', profile).passed, false);
|
|
126
128
|
});
|
|
127
129
|
test('keeps verify pass/fail on the legacy metric while calibration reports both metrics', () => {
|
|
@@ -174,7 +174,7 @@ test('CLI and MCP rebuild helpers share fingerprints', () => {
|
|
|
174
174
|
version: '1', audience: 'operators', intent: 'explain', format: 'outreach',
|
|
175
175
|
});
|
|
176
176
|
assert.match(briefTask.prompt, /# WritingBrief/);
|
|
177
|
-
assert.equal(HYV_VERSION, '3.
|
|
177
|
+
assert.equal(HYV_VERSION, '3.6.0');
|
|
178
178
|
});
|
|
179
179
|
test('apply rejects forged tasks, missing capability, and substituted profiles', () => {
|
|
180
180
|
const reduction = rebuildRecommendation();
|
package/dist/rewrite-task.js
CHANGED
|
@@ -106,7 +106,7 @@ export function prepareRewriteTask(draft, profile, copySpec, writingBrief, autho
|
|
|
106
106
|
const result = analyze(draft, profile, writingBrief);
|
|
107
107
|
const prompt = renderRewritePrompt(draft, profile, result, [], writingBrief);
|
|
108
108
|
const mapped = sentences(draft);
|
|
109
|
-
const eligibleSentenceIds = new Set([...deriveEditScope(result).eligibleSentenceIds, ...authorizedSentenceIds]);
|
|
109
|
+
const eligibleSentenceIds = new Set([...deriveEditScope(result, true).eligibleSentenceIds, ...authorizedSentenceIds]);
|
|
110
110
|
const taskBase = {
|
|
111
111
|
version: '1',
|
|
112
112
|
draft,
|
|
@@ -21,18 +21,19 @@ test('applies only eligible numbered replacements and preserves all clean bytes'
|
|
|
21
21
|
assert.equal(result.candidate, 'I use the answer. The launch is on 14 August.');
|
|
22
22
|
assert.deepEqual(result.receipt.adapterIds, []);
|
|
23
23
|
});
|
|
24
|
-
test('
|
|
24
|
+
test('makes every active AI Editor finding eligible under the default strict rewrite policy', () => {
|
|
25
25
|
const task = prepareRewriteTask('I write clear notes. This work is meaningful. I keep the mechanism visible.', profile);
|
|
26
|
-
assert.deepEqual(task.eligibleSentenceIds, []);
|
|
27
|
-
assert.deepEqual(task.sentences.map((sentence) => sentence.eligible), [false,
|
|
26
|
+
assert.deepEqual(task.eligibleSentenceIds, [2]);
|
|
27
|
+
assert.deepEqual(task.sentences.map((sentence) => sentence.eligible), [false, true, false]);
|
|
28
|
+
assert.match(task.prompt, /Every active AI Editor finding is a required repair/);
|
|
28
29
|
});
|
|
29
|
-
test('
|
|
30
|
+
test('makes advisory and judgment-required findings required repairs', () => {
|
|
30
31
|
const advisory = prepareRewriteTask('Firstly, check the invoice.', profile);
|
|
31
|
-
assert.deepEqual(advisory.eligibleSentenceIds, []);
|
|
32
|
+
assert.deepEqual(advisory.eligibleSentenceIds, [1]);
|
|
32
33
|
const neutralProfile = buildProfile(['I write plainly.', 'I name the mechanism.']);
|
|
33
34
|
const pending = prepareRewriteTask('We leverage the scheduler.', neutralProfile);
|
|
34
|
-
assert.deepEqual(pending.eligibleSentenceIds, []);
|
|
35
|
-
assert.match(pending.prompt, /
|
|
35
|
+
assert.deepEqual(pending.eligibleSentenceIds, [1]);
|
|
36
|
+
assert.match(pending.prompt, /Every active AI Editor finding is a required repair/);
|
|
36
37
|
const blocking = prepareRewriteTask('The scheduler failed — twice. The owner checked it.', profile);
|
|
37
38
|
assert.deepEqual(blocking.eligibleSentenceIds, [1]);
|
|
38
39
|
});
|
|
@@ -40,9 +41,9 @@ test('keeps blocking VoiceDNA avoid findings eligible', () => {
|
|
|
40
41
|
const task = prepareRewriteTask('The plan uses leverage.', profile);
|
|
41
42
|
assert.deepEqual(task.eligibleSentenceIds, [1]);
|
|
42
43
|
});
|
|
43
|
-
test('
|
|
44
|
+
test('makes reconciled active founder reframes eligible under the default strict policy', () => {
|
|
44
45
|
const task = prepareRewriteTask("This isn't positioning. This is proof.", profile);
|
|
45
|
-
assert.deepEqual(task.eligibleSentenceIds, []);
|
|
46
|
+
assert.deepEqual(task.eligibleSentenceIds, [1]);
|
|
46
47
|
});
|
|
47
48
|
test('preserves a clean draft byte-for-byte when the rewrite response is empty', () => {
|
|
48
49
|
const draft = 'I write clear notes.\n\nI keep the mechanism visible.\n';
|
package/dist/strict-quality.js
CHANGED
|
@@ -8,8 +8,7 @@ function finding(id, disposition, reason, suggestion, sentence) {
|
|
|
8
8
|
}
|
|
9
9
|
function strictFinding(source, profile) {
|
|
10
10
|
if (source.engine === 'ai_editor') {
|
|
11
|
-
|
|
12
|
-
return finding(`strict.${source.engine}.${source.id}`, disposition, source.reason, source.suggestion, source.sentence);
|
|
11
|
+
return finding(`strict.${source.engine}.${source.id}`, 'block', source.reason, source.suggestion, source.sentence);
|
|
13
12
|
}
|
|
14
13
|
if (source.engine === 'voice_dna') {
|
|
15
14
|
if (source.severity === 'red')
|
|
@@ -45,10 +45,10 @@ test('blocks an underpowered or uncalibrated voice profile before trusting voice
|
|
|
45
45
|
const mixedFormatReport = evaluateStrictQuality('The launch starts Tuesday.', profile(), mixedFormats);
|
|
46
46
|
assert.ok(mixedFormatReport.findings.some((item) => item.id === 'strict.profile.format-spread'));
|
|
47
47
|
});
|
|
48
|
-
test('
|
|
48
|
+
test('blocks every active AI Editor pattern under strict quality', () => {
|
|
49
49
|
const review = evaluateStrictQuality('We leverage the existing checklist.', profile(), samples());
|
|
50
|
-
assert.equal(review.disposition, '
|
|
51
|
-
assert.ok(review.findings.some((item) => item.id === 'strict.ai_editor.ai.leverage' && item.disposition === '
|
|
50
|
+
assert.equal(review.disposition, 'blocked');
|
|
51
|
+
assert.ok(review.findings.some((item) => item.id === 'strict.ai_editor.ai.leverage' && item.disposition === 'block'));
|
|
52
52
|
const blockedProfile = profile();
|
|
53
53
|
blockedProfile.rulePolicy['ai.leverage'] = 'blocking';
|
|
54
54
|
const blocked = evaluateStrictQuality('We leverage the existing checklist.', blockedProfile, samples());
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const HYV_VERSION = '3.
|
|
1
|
+
export const HYV_VERSION = '3.6.0';
|
package/package.json
CHANGED
|
@@ -23,4 +23,4 @@ hyv analyze draft.md profile.json [writing-brief.json]
|
|
|
23
23
|
|
|
24
24
|
## Handoff
|
|
25
25
|
|
|
26
|
-
Run `hyv analyze` directly to execute the operation.
|
|
26
|
+
Run `hyv analyze` directly to execute the operation. Follow with hyv-rewrite-prompt or hyv-prepare-rewrite: both use strict-by-default repair scope, so every active AI Editor finding becomes a required repair. For calibrated voice evidence, hand off to hyv-strict-check with a Profile v3 and the required local samples; analysis alone is not a strict-ready result.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Analyze"
|
|
3
3
|
short_description: "Run separate VoiceDNA and AI Editor checks plus a non-scoring Unicode hygiene inspection against a draft."
|
|
4
|
-
default_prompt: "Use $hyv-analyze to
|
|
4
|
+
default_prompt: "Use $hyv-analyze to inspect this draft, then use the strict-by-default rewrite workflow so every active AI Editor finding is repaired."
|
|
@@ -16,6 +16,7 @@ hyv apply-rebuild task.json response.json profile.json (--capability-stdin|--cap
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: evaluation rejects a rebuild candidate with any unresolved active AI Editor finding.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Apply Rebuild"
|
|
3
3
|
short_description: "Validate and evaluate a whole-document rebuild response against a prepared authorized rebuild task."
|
|
4
|
-
default_prompt: "Use $hyv-apply-rebuild to
|
|
4
|
+
default_prompt: "Use $hyv-apply-rebuild to reject a whole-document rebuild response with any unresolved active AI Editor finding."
|
|
@@ -16,6 +16,7 @@ hyv apply-rewrite task.json response.json profile.json
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- The task's default strict policy rejects the candidate at deterministic verification when an active AI Editor finding remains. A successful deterministic result is not calibrated `strict-ready` voice evidence.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Apply Rewrite"
|
|
3
3
|
short_description: "Validate and apply a model response to a prepared rewrite task, then run local gates."
|
|
4
|
-
default_prompt: "Use $hyv-apply-rewrite to validate
|
|
4
|
+
default_prompt: "Use $hyv-apply-rewrite to validate a response through the strict default local gates."
|
|
@@ -16,6 +16,7 @@ hyv prepare-rebuild draft.md profile.json reduction.json copy-spec.json task.jso
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: the later rebuild evaluation rejects a candidate with any unresolved active AI Editor finding.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Prepare Rebuild"
|
|
3
3
|
short_description: "Prepare a rebuild task only after an upstream REBUILD recommendation, a CopySpec, and a signed rebuild-authorization capability."
|
|
4
|
-
default_prompt: "Use $hyv-prepare-rebuild to prepare a rebuild task only after an upstream rebuild recommendation, a copyspec, and a signed rebuild-authorization capability."
|
|
4
|
+
default_prompt: "Use $hyv-prepare-rebuild to prepare a strict rebuild task only after an upstream rebuild recommendation, a copyspec, and a signed rebuild-authorization capability."
|
|
@@ -16,6 +16,7 @@ hyv prepare-rewrite draft.md profile.json task.json [copy-spec.json] [writing-br
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: the task authorizes repairs for every active AI Editor finding and keeps unrelated sentences locked.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Prepare Rewrite"
|
|
3
3
|
short_description: "Prepare a local, versioned, fingerprint-bound rewrite task."
|
|
4
|
-
default_prompt: "Use $hyv-prepare-rewrite to prepare a local, versioned, fingerprint-bound rewrite task."
|
|
4
|
+
default_prompt: "Use $hyv-prepare-rewrite to prepare a strict local, versioned, fingerprint-bound rewrite task."
|
|
@@ -16,6 +16,7 @@ hyv rebuild-writer-request task.json writer-request.json
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- The resulting whole-document candidate remains subject to strict default verification: every active AI Editor finding must be repaired before it can proceed.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Rebuild Writer Request"
|
|
3
3
|
short_description: "Create the writer-only payload for a prepared rebuild task."
|
|
4
|
-
default_prompt: "Use $hyv-rebuild-writer-request to create the writer-only payload for a prepared rebuild task."
|
|
4
|
+
default_prompt: "Use $hyv-rebuild-writer-request to create the writer-only payload for a prepared rebuild task that will receive strict default verification."
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hyv-rewrite-prompt
|
|
3
|
-
description: Create a constrained editing brief without rewriting the draft.
|
|
3
|
+
description: Create a strict constrained editing brief without rewriting the draft.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hyv-rewrite-prompt
|
|
7
7
|
|
|
8
|
-
Create a constrained editing brief. It does not rewrite the draft or call a model. The brief is printed to stdout for redirect into a markdown file.
|
|
8
|
+
Create a strict constrained editing brief. It does not rewrite the draft or call a model. The brief is printed to stdout for redirect into a markdown file.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -16,9 +16,10 @@ hyv rewrite-prompt draft.md profile.json [writing-brief.json] > rewrite-brief.md
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: every active AI Editor finding is a required, source-faithful repair. Do not return a candidate with an active finding unresolved; a separate calibrated `hyv-strict-check` is required only when the caller needs a `strict-ready` voice-evidence result.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
22
23
|
## Handoff
|
|
23
24
|
|
|
24
|
-
Run `hyv rewrite-prompt` directly to execute the operation. Follow-on agents: hyv-prepare-rewrite.
|
|
25
|
+
Run `hyv rewrite-prompt` directly to execute the operation. Follow-on agents: hyv-prepare-rewrite, hyv-verify.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": "1.0.0",
|
|
3
3
|
"id": "hyv-rewrite-prompt",
|
|
4
4
|
"title": "hyv-rewrite-prompt",
|
|
5
|
-
"description": "Create a constrained editing brief without rewriting the draft.",
|
|
5
|
+
"description": "Create a strict constrained editing brief without rewriting the draft.",
|
|
6
6
|
"instruction_file": "SKILL.md",
|
|
7
7
|
"role": "Brief author",
|
|
8
8
|
"workflow_phase": "plan",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Rewrite Prompt"
|
|
3
|
-
short_description: "Create a
|
|
4
|
-
default_prompt: "Use $hyv-rewrite-prompt to create a constrained editing brief
|
|
3
|
+
short_description: "Create a strict editing brief without rewriting the draft."
|
|
4
|
+
default_prompt: "Use $hyv-rewrite-prompt to create a strict constrained editing brief that requires repair of every active AI Editor finding."
|
|
@@ -5,7 +5,7 @@ description: Run the local strict AI-pattern and calibrated voice-match gate.
|
|
|
5
5
|
|
|
6
6
|
# hyv-strict-check
|
|
7
7
|
|
|
8
|
-
Run the
|
|
8
|
+
Run the calibrated strict local quality gate without changing the draft, profile, or learning state.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -17,8 +17,8 @@ hyv strict-check draft.md profile-v3.json sample-a.md sample-b.md sample-c.md sa
|
|
|
17
17
|
|
|
18
18
|
- Requires a Profile v3 built from at least five samples, five or more local validation samples, 1,500 validation-sample words, no duplicate samples, a consistent visible format baseline, and calibrated fingerprint tolerances.
|
|
19
19
|
- Returns `strict-ready`, `needs-human-review`, or `blocked` as JSON and exits `0` only for `strict-ready`.
|
|
20
|
-
- `blocked` covers missing strict evidence,
|
|
21
|
-
- `needs-human-review`
|
|
20
|
+
- `blocked` covers missing strict evidence, every active AI Editor pattern, hard calibrated fingerprint drift, red VoiceDNA findings, and unresolved hidden text.
|
|
21
|
+
- `needs-human-review` covers remaining non-blocking VoiceDNA or editorial review findings. It is not a clean result.
|
|
22
22
|
- It remains local-first and never sends samples or drafts to a provider.
|
|
23
23
|
|
|
24
24
|
## Handoff
|
|
@@ -16,9 +16,10 @@ hyv verify original.md candidate.md profile.json [writing-brief.json]
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: verification fails when the candidate has any active AI Editor finding, including an advisory or judgment-required match. An explicit Profile v3 `disabled` policy remains a deliberate user exception.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
22
23
|
## Handoff
|
|
23
24
|
|
|
24
|
-
Run `hyv verify` directly to execute the operation.
|
|
25
|
+
Run `hyv verify` directly to execute the operation. For calibrated voice evidence, run hyv-strict-check before any lifecycle handoff. A passing verify report does not substitute for strict-ready. Follow-on agents: hyv-strict-check, hyv-lifecycle.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Verify"
|
|
3
3
|
short_description: "Verify a revised candidate against an original draft and portable profile."
|
|
4
|
-
default_prompt: "Use $hyv-verify to
|
|
4
|
+
default_prompt: "Use $hyv-verify to reject any revised candidate with an unresolved active AI Editor finding."
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hyv-verify-spec
|
|
3
|
-
description: Verify a candidate against
|
|
3
|
+
description: Verify a candidate against strict default voice gates and a local CopySpec.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hyv-verify-spec
|
|
7
7
|
|
|
8
|
-
Verify a candidate against
|
|
8
|
+
Verify a candidate against strict default voice gates and a local CopySpec. Immutable claims remain verbatim unless atoms are supplied; then each declared atom must remain. Prohibited claims fail closed.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -16,6 +16,7 @@ hyv verify-spec original.md candidate.md profile.json copy-spec.json [writing-br
|
|
|
16
16
|
## Behavior
|
|
17
17
|
|
|
18
18
|
- Deterministic and local-first: this command never calls a provider and never sends drafts, samples, profiles, or telemetry to a service.
|
|
19
|
+
- Strict by default: verification fails when the candidate has any active AI Editor finding, in addition to CopySpec failures.
|
|
19
20
|
- Commands that write use an explicit output path. Confirm that path before running the command.
|
|
20
21
|
- This agent describes how to invoke the command and what it returns. It does not change command behavior or exit codes.
|
|
21
22
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": "1.0.0",
|
|
3
3
|
"id": "hyv-verify-spec",
|
|
4
4
|
"title": "hyv-verify-spec",
|
|
5
|
-
"description": "Verify a candidate against
|
|
5
|
+
"description": "Verify a candidate against strict default voice gates and a local CopySpec.",
|
|
6
6
|
"instruction_file": "SKILL.md",
|
|
7
7
|
"role": "CopySpec verifier",
|
|
8
8
|
"workflow_phase": "verify",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Hyv Verify Spec"
|
|
3
|
-
short_description: "Verify a candidate against
|
|
4
|
-
default_prompt: "Use $hyv-verify-spec to
|
|
3
|
+
short_description: "Verify a candidate against strict default voice gates and a local CopySpec."
|
|
4
|
+
default_prompt: "Use $hyv-verify-spec to reject a candidate with any active AI Editor finding or CopySpec failure."
|