@dzhechkov/harness-core 0.3.100 → 0.3.102

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/publish.ts CHANGED
@@ -9,6 +9,8 @@ import { existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
  import { execSync } from 'node:child_process';
11
11
 
12
+ import { claimCheck } from './claim-check.js';
13
+
12
14
  /** Result for a single package publish attempt. */
13
15
  export interface PublishResult {
14
16
  readonly name: string;
@@ -16,6 +18,12 @@ export interface PublishResult {
16
18
  readonly newVersion: string;
17
19
  readonly status: 'published' | 'skipped' | 'error';
18
20
  readonly error?: string | undefined;
21
+ /**
22
+ * Pre-publish claim-check summary for this package's README, present only when the
23
+ * opt-in `claimCheck` gate ran (`'warn'`/`'block'`). Additive: absent by default so an
24
+ * unmodified `publishPackages` call is byte-compatible with pre-gate behavior.
25
+ */
26
+ readonly claimCheck?: { readonly findings: number; readonly high: number } | undefined;
19
27
  }
20
28
 
21
29
  /** Full publish report. */
@@ -203,6 +211,14 @@ export function publishPackages(
203
211
  dryRun?: boolean | undefined;
204
212
  filter?: string[] | undefined;
205
213
  bumpOnly?: boolean | undefined;
214
+ /**
215
+ * Pre-publish claim-check gate over each package's README (ADR-001). Default `'warn'`:
216
+ * records the finding count on the result but NEVER changes publish status — additive, so the
217
+ * existing publish path and its tests are unaffected. `'error'` flips ONLY a package with a
218
+ * `high` finding to `status: 'error'`, leaving the rest of the batch unaffected. `'off'`
219
+ * disables the gate entirely (no `claimCheck` field is emitted).
220
+ */
221
+ claimGate?: 'off' | 'warn' | 'error' | undefined;
206
222
  } = {},
207
223
  ): PublishReport {
208
224
  const packages = discoverPackages(monorepoRoot);
@@ -238,8 +254,41 @@ export function publishPackages(
238
254
  continue;
239
255
  }
240
256
 
257
+ // Pre-publish claim-check gate. Default `'warn'` per ADR-001: publishing SURFACES a
258
+ // README's untagged claims by default, but `'warn'` NEVER changes publish status, so the
259
+ // existing publish path is unaffected. `'error'` fails only THIS package when it carries a
260
+ // high-severity claim; `'off'` disables the gate entirely (no `claimCheck` field emitted).
261
+ // Runs BEFORE the dry-run short-circuit so `--dry-run` previews surface what a live publish
262
+ // would. Reading the README never blocks the gate itself — unreadable ⇒ "no findings".
263
+ const claimGate = opts.claimGate ?? 'warn';
264
+ let claimCheckSummary: { findings: number; high: number } | undefined;
265
+ if (claimGate !== 'off') {
266
+ const readmePath = join(pkg.dir, 'README.md');
267
+ if (existsSync(readmePath)) {
268
+ try {
269
+ const text = readFileSync(readmePath, 'utf-8');
270
+ const result = claimCheck(text);
271
+ const high = result.findings.filter((f) => f.severity === 'high').length;
272
+ claimCheckSummary = { findings: result.findings.length, high };
273
+ if (claimGate === 'error' && high > 0) {
274
+ results.push({
275
+ name: pkg.name,
276
+ oldVersion,
277
+ newVersion,
278
+ status: 'error',
279
+ error: `claim-check: ${high} high-severity claim(s) in README.md — tag MEASURED with a reproducer or CLAIMED/SYNTHETIC before publishing.`,
280
+ claimCheck: claimCheckSummary,
281
+ });
282
+ continue;
283
+ }
284
+ } catch {
285
+ /* unreadable README never blocks the gate itself */
286
+ }
287
+ }
288
+ }
289
+
241
290
  if (opts.dryRun) {
242
- results.push({ name: pkg.name, oldVersion, newVersion, status: 'skipped' });
291
+ results.push({ name: pkg.name, oldVersion, newVersion, status: 'skipped', claimCheck: claimCheckSummary });
243
292
  continue;
244
293
  }
245
294
 
@@ -254,7 +303,7 @@ export function publishPackages(
254
303
  originalReadme = syncReadmeVersion(pkg.dir, oldVersion, newVersion);
255
304
 
256
305
  if (opts.bumpOnly) {
257
- results.push({ name: pkg.name, oldVersion, newVersion, status: 'published' });
306
+ results.push({ name: pkg.name, oldVersion, newVersion, status: 'published', claimCheck: claimCheckSummary });
258
307
  continue;
259
308
  }
260
309
 
@@ -272,7 +321,7 @@ export function publishPackages(
272
321
  env: { ...process.env },
273
322
  });
274
323
 
275
- results.push({ name: pkg.name, oldVersion, newVersion, status: 'published' });
324
+ results.push({ name: pkg.name, oldVersion, newVersion, status: 'published', claimCheck: claimCheckSummary });
276
325
  } catch (err) {
277
326
  // The version was written BEFORE build+publish; on any failure restore the
278
327
  // original package.json (and README, if we rewrote its version) so a failed
@@ -288,6 +337,7 @@ export function publishPackages(
288
337
  newVersion,
289
338
  status: 'error',
290
339
  error: err instanceof Error ? err.message : String(err),
340
+ claimCheck: claimCheckSummary,
291
341
  });
292
342
  }
293
343
  }