@dzhechkov/harness-core 0.3.125 → 0.3.127

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/src/parity.ts ADDED
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Target-parity model (`dz parity`, feature target-parity-matrix, ADR-001).
3
+ *
4
+ * The honest map of "which harness feature works on which target, and HOW". The matrix does NOT
5
+ * exist as a hand-written document anywhere (the sitedoc lesson, b57e686: a hand-maintained
6
+ * duplicate list is a second drift surface) — this declarative model is the ONLY source, and the
7
+ * table is always COMPUTED from it.
8
+ *
9
+ * Honesty contract (FR-6): a capability flag states only what has been VERIFIED, each with its
10
+ * source in a comment. Unknown support is NOT flagged — a feature falsely promised on a target
11
+ * is worse than one conservatively marked absent.
12
+ *
13
+ * Derivation anchor (FR-4): `TARGET_CAPABILITIES` is typed `Record<TargetName, …>`, so ADDING an
14
+ * 11th target to the registry refuses to compile until it is classified here; the model test
15
+ * re-asserts coverage at runtime and validates every capability reference.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+
20
+ import type { TargetName } from './targets.js';
21
+
22
+ /** Runtime capabilities a target platform can provide to harness features. */
23
+ export type RuntimeCapability =
24
+ | 'shell' // can run the dz CLI, i.e. a shell WITH Node.js (what `npm i -g` implies on a dev machine)
25
+ | 'skills' // consumes compiled skills (all adapters emit them; agents-md as one merged file)
26
+ | 'hooks' // pre/post tool-call hooks (auto claim-check, auto recall apply-leg)
27
+ | 'mcp' // Model Context Protocol client exists on the platform (servers still need configuring)
28
+ | 'mcp-configured' // our MCP servers (AgentDB / agentic-qe) are wired up out of the box (.mcp.json)
29
+ | 'workflows' // deterministic multi-agent Workflow runtime (ultracode feature-adr, delivery gate)
30
+ | 'statusline'; // live status line (🚦 gates)
31
+
32
+ /** All declared capabilities — the closed vocabulary the model test validates references against. */
33
+ export const RUNTIME_CAPABILITIES: readonly RuntimeCapability[] = [
34
+ 'shell',
35
+ 'skills',
36
+ 'hooks',
37
+ 'mcp',
38
+ 'mcp-configured',
39
+ 'workflows',
40
+ 'statusline',
41
+ ];
42
+
43
+ /**
44
+ * What each target has been VERIFIED to provide (FR-6: source per line; conservative — an
45
+ * unverified capability stays absent until proven, never assumed).
46
+ */
47
+ export const TARGET_CAPABILITIES: Record<TargetName, readonly RuntimeCapability[]> = {
48
+ // Daily-driven harness: hooks (claim-check PreToolUse, recall UserPromptSubmit), MCP servers,
49
+ // Workflow runtime and the statusline are all exercised in this repo every session.
50
+ 'claude-code': ['shell', 'skills', 'hooks', 'mcp', 'mcp-configured', 'workflows', 'statusline'],
51
+ // MCP subsystem probed live: `codex mcp --help` + `codex mcp list` answer (2026-07-19).
52
+ codex: ['shell', 'skills', 'mcp'],
53
+ // Conservative v1: skills emission verified by the adapters; richer runtimes unproven.
54
+ opencode: ['shell', 'skills'],
55
+ hermes: ['shell', 'skills'],
56
+ openclaude: ['shell', 'skills'],
57
+ copilot: ['shell', 'skills'],
58
+ // Lowest common denominator: one merged AGENTS.md — skills arrive as a single managed file.
59
+ 'agents-md': ['shell', 'skills'],
60
+ cursor: ['shell', 'skills'],
61
+ gemini: ['shell', 'skills'],
62
+ windsurf: ['shell', 'skills'],
63
+ };
64
+
65
+ /** How a feature manifests on a platform: a concrete FORM with its runtime requirements. */
66
+ export interface FeatureForm {
67
+ /** Human-readable name of the form, shown as the `via` of a parity cell (AM-2). */
68
+ readonly form: string;
69
+ readonly requires: readonly RuntimeCapability[];
70
+ /** `full` = the complete experience; `manual` = works, but the user drives it by hand. */
71
+ readonly level: 'full' | 'manual';
72
+ }
73
+
74
+ export interface ParityFeature {
75
+ readonly id: string;
76
+ readonly title: string;
77
+ readonly forms: readonly FeatureForm[];
78
+ }
79
+
80
+ /**
81
+ * The harness feature inventory for the parity map. A feature may carry SEVERAL forms — parity
82
+ * for a target is the BEST form whose requirements the target provides (e.g. claim-check: the
83
+ * hook form is automatic on every Write/Edit; the CLI form works anywhere but must be invoked).
84
+ */
85
+ export const PARITY_FEATURES: readonly ParityFeature[] = [
86
+ {
87
+ id: 'dz-cli',
88
+ title: 'dz CLI (all commands)',
89
+ forms: [{ form: 'shell command', requires: ['shell'], level: 'full' }],
90
+ },
91
+ {
92
+ id: 'skills-packs',
93
+ title: 'Skill packs (compiled per target)',
94
+ forms: [{ form: 'compiled skills via adapter', requires: ['skills'], level: 'full' }],
95
+ },
96
+ {
97
+ id: 'feature-adr',
98
+ title: 'feature-adr pipeline',
99
+ forms: [
100
+ { form: 'ultracode deterministic workflow', requires: ['workflows'], level: 'full' },
101
+ { form: 'interactive skill (plain /feature-adr)', requires: ['skills'], level: 'manual' },
102
+ ],
103
+ },
104
+ {
105
+ id: 'delivery-gate',
106
+ title: 'Step-10 Delivery Gate',
107
+ forms: [{ form: 'workflow delivery block', requires: ['workflows'], level: 'full' }],
108
+ },
109
+ {
110
+ id: 'challenge-panel',
111
+ title: 'Adversarial challenge-panel (plan gate)',
112
+ forms: [
113
+ { form: 'workflow plan gate', requires: ['workflows'], level: 'full' },
114
+ { form: 'dz challenge (CLI)', requires: ['shell'], level: 'manual' },
115
+ ],
116
+ },
117
+ {
118
+ id: 'claim-check',
119
+ title: 'Integrity claim-check',
120
+ forms: [
121
+ { form: 'PreToolUse hook (automatic on Write/Edit)', requires: ['hooks'], level: 'full' },
122
+ { form: 'dz claim-check (CLI) + publish gate', requires: ['shell'], level: 'manual' },
123
+ ],
124
+ },
125
+ {
126
+ id: 'learning-collect',
127
+ title: 'Self-learning: collect + rank (dz teach/recall)',
128
+ forms: [{ form: 'dz teach / consolidate / recall', requires: ['shell'], level: 'full' }],
129
+ },
130
+ {
131
+ id: 'learning-apply',
132
+ title: 'Self-learning: automatic apply-leg',
133
+ forms: [
134
+ { form: 'UserPromptSubmit hook (auto recall)', requires: ['hooks'], level: 'full' },
135
+ { form: 'manual dz recall before a task', requires: ['shell'], level: 'manual' },
136
+ ],
137
+ },
138
+ {
139
+ id: 'statusline-gates',
140
+ title: 'Live 🚦 gates status line',
141
+ forms: [{ form: 'statusline integration', requires: ['statusline'], level: 'full' }],
142
+ },
143
+ {
144
+ id: 'project-guards',
145
+ title: 'Deterministic project guards (setup --guards)',
146
+ forms: [{ form: 'zero-dependency check.mjs (runs anywhere node runs)', requires: ['shell'], level: 'full' }],
147
+ },
148
+ {
149
+ id: 'release-signing',
150
+ title: 'Verified release + signing (dz release/sign/sbom)',
151
+ forms: [{ form: 'dz release / sign / sbom / publish', requires: ['shell'], level: 'full' }],
152
+ },
153
+ {
154
+ id: 'mcp-memory',
155
+ title: 'MCP memory (AgentDB, agentic-qe)',
156
+ forms: [
157
+ { form: 'preconfigured MCP servers (.mcp.json ships in-repo)', requires: ['mcp-configured'], level: 'full' },
158
+ { form: 'manual MCP server config (e.g. `codex mcp add`)', requires: ['mcp'], level: 'manual' },
159
+ ],
160
+ },
161
+ ];
162
+
163
+ /**
164
+ * Short column labels for the grid renderer — kept NEXT TO the model and covered by the same
165
+ * coverage test (a hand map in the CLI would be a second, unchecked target registry).
166
+ */
167
+ export const TARGET_SHORT_LABELS: Record<TargetName, string> = {
168
+ 'claude-code': 'cc',
169
+ codex: 'cdx',
170
+ opencode: 'ocd',
171
+ hermes: 'hrm',
172
+ openclaude: 'ocl',
173
+ copilot: 'cop',
174
+ 'agents-md': 'amd',
175
+ cursor: 'cur',
176
+ gemini: 'gem',
177
+ windsurf: 'wsf',
178
+ };
179
+
180
+ /** One computed parity cell: the best available form for a feature on a target. */
181
+ export interface ParityCell {
182
+ readonly level: 'full' | 'manual' | 'none';
183
+ /** Which form delivers it (absent only when level is `none`) — AM-2: "partial" must name its path. */
184
+ readonly via?: string | undefined;
185
+ }
186
+
187
+ /** Compute the parity cell for one feature on one target (pure; best form wins, `full` first). */
188
+ export function computeParity(feature: ParityFeature, capabilities: readonly RuntimeCapability[]): ParityCell {
189
+ const caps = new Set(capabilities);
190
+ const usable = feature.forms.filter((f) => f.requires.every((r) => caps.has(r)));
191
+ const full = usable.find((f) => f.level === 'full');
192
+ if (full !== undefined) return { level: 'full', via: full.form };
193
+ const manual = usable.find((f) => f.level === 'manual');
194
+ if (manual !== undefined) return { level: 'manual', via: manual.form };
195
+ return { level: 'none' };
196
+ }
197
+
198
+ export interface ParityMatrixRow {
199
+ readonly feature: ParityFeature;
200
+ readonly cells: Readonly<Record<TargetName, ParityCell>>;
201
+ }
202
+
203
+ /** The full computed matrix over every declared feature × every registered target. */
204
+ export function buildParityMatrix(): ParityMatrixRow[] {
205
+ const targets = Object.keys(TARGET_CAPABILITIES) as TargetName[];
206
+ return PARITY_FEATURES.map((feature) => {
207
+ const cells = {} as Record<TargetName, ParityCell>;
208
+ for (const t of targets) cells[t] = computeParity(feature, TARGET_CAPABILITIES[t]);
209
+ return { feature, cells };
210
+ });
211
+ }