@clossys/writer 0.3.6 → 0.3.9

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/CHANGELOG.md CHANGED
@@ -3,6 +3,95 @@
3
3
  All notable changes to this package are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.3.9] - 2026-09-18
7
+
8
+ ### Added
9
+
10
+ - Documented installation against the public npm registry
11
+ (`https://registry.npmjs.org`) and that installing needs no authentication.
12
+ - Stated the charter close condition in the README: independent consumer
13
+ evidence of `approved copy coverage rate`, computed by
14
+ `assessApprovedCopyCoverageRate()`. An empty evaluated set is
15
+ indeterminate, never a perfect rate of 1. `checkCopyRecord` and
16
+ `checkCopyTraceability` remain the gates they are; neither is this rate.
17
+ - Declared `foundry.assessment` against a new mapped `writer-rate-check`
18
+ bin with `invocation: "single-json-input"`. `writer-check` remains the
19
+ multi-mode CLI and is not the assessment surface. Advisor remains the
20
+ only required first-day role.
21
+ - `writer-rate-check assessment.json`: prints the `approved copy coverage
22
+ rate` report and exits on the `0` / `1` / `2` ternary.
23
+
24
+ ### Notes
25
+
26
+ - This does not claim the position is closed. Qualification of `0.3.9` is
27
+ deferred under #833.
28
+
29
+ ## [0.3.8] - 2026-09-16
30
+
31
+ ### Added
32
+
33
+ - README now documents the `writer-check --format json` contract added in
34
+ 0.3.7: the exact object shape, and the guarantee that `verdict`, `findings`
35
+ and `unchecked` are present together on every path — clean, findings, and
36
+ total failure alike. The flag was documented in `--help` and in this
37
+ changelog but not in the README, where this package documents its other
38
+ CLI exit-code contracts (#880).
39
+
40
+ ### Changed
41
+
42
+ - Version bumped from 0.3.7 to 0.3.8 without a functional change to the
43
+ shipped code. 0.3.7 was never published: its retained qualification record
44
+ bound a tarball that could not be reproduced from the tree, because the
45
+ record was generated against an incrementally-built `dist/` that carried
46
+ stale artifacts. Qualification records are immutable — one introduction per
47
+ path, hash-pinned — so the 0.3.7 record could not be corrected and 0.3.7
48
+ could never be published. It is skipped deliberately rather than reused.
49
+
50
+ The mechanism is filed as #893; this release's own record was generated
51
+ from a clean build on the pinned release runtime specifically because of it.
52
+
53
+ ## [0.3.7] - 2026-09-15
54
+
55
+ ### Fixed
56
+
57
+ - The default `writer-check` command's scanner could not classify a `//`
58
+ line comment or a `/* */` block comment placed BETWEEN a JSX element's
59
+ attributes — legal TSX every mainstream formatter produces for a
60
+ commented-out or explained prop. The attribute-parsing loop inside
61
+ `tryScanJsxElement` had no case for either comment form, so it fell
62
+ through to the loop's own "not committed, silently not-JSX" backtrack;
63
+ for a NESTED element (the realistic shape in a real component tree)
64
+ that backtrack surfaced as an `"unrecognized-jsx-child"` `unchecked`
65
+ entry, and this package's own indeterminate-fold ("one unreadable
66
+ region must not let a scan report itself clean") turned that ONE
67
+ construct into exit code 2 for the WHOLE run — discarding every real
68
+ finding the same scan had already produced (issue #753: a 270-entry
69
+ `CopyRecord` scan against a real application tree extracted 294
70
+ candidates, produced 292 findings, and still exited 2 over this one
71
+ construct). Comment trivia between attributes is now skipped exactly
72
+ like the whitespace surrounding it, before any commit decision is
73
+ made — the same "silent, pre-commit trivia" treatment this loop
74
+ already gives TSX's generic-arrow-function syntax.
75
+
76
+ ### Added
77
+
78
+ - `writer-check --format json`: the default command's own structured,
79
+ machine-readable report (`CopyTraceabilityReport`), matching the
80
+ `--format json` shape `inspector`'s CLI already publishes. Exit code 2
81
+ (indeterminate) is unchanged and still wins whenever any JSX construct
82
+ is `unchecked` — an indeterminate result must never read as clean, and
83
+ this does not touch that rule. What changes is that a consumer no
84
+ longer has to choose between reading the exit code and discarding
85
+ everything the run DID measure: `--format json` prints exactly one
86
+ object to stdout, always carrying `verdict` (the same
87
+ `"clean" | "findings" | "indeterminate"` states this package uses
88
+ everywhere), `findings`, and `unchecked` together, regardless of which
89
+ verdict resulted — so a real finding produced alongside a genuinely
90
+ unclassifiable construct in the same run is still reachable by a
91
+ consumer that reads past the exit code, exactly as #753 asked for,
92
+ while a consumer that only checks `exitCode`/`verdict` still, correctly,
93
+ never sees an indeterminate run reported as a pass.
94
+
6
95
  ## [0.3.6] - 2026-09-14
7
96
 
8
97
  ### Changed
package/README.md CHANGED
@@ -16,6 +16,45 @@ template; each consumer supplies its own language and facts.
16
16
  npm install @clossys/writer
17
17
  ```
18
18
 
19
+ This package is published to the public npm registry, `https://registry.npmjs.org`.
20
+ Installing it needs no authentication: no npm token, no `.npmrc` registry
21
+ override, and no GitHub credential of any kind.
22
+
23
+ ## Approved copy coverage rate
24
+
25
+ Independent consumer evidence shows the position's owned metric meets its
26
+ setpoint over the declared review cadence. The owned metric is `approved
27
+ copy coverage rate`, computed by `assessApprovedCopyCoverageRate()`. An
28
+ empty evaluated set is `indeterminate`, never a perfect rate of 1.
29
+ `checkCopyRecord` and `checkCopyTraceability` remain the gates they are;
30
+ neither is this rate. This package does not measure consumer evidence and
31
+ does not close the loop. A green run of this package's tests is not a close.
32
+
33
+ ```ts
34
+ import { assessApprovedCopyCoverageRate } from "@clossys/writer";
35
+
36
+ const report = assessApprovedCopyCoverageRate(input);
37
+ ```
38
+
39
+ ```bash
40
+ writer-rate-check assessment.json
41
+ ```
42
+
43
+ The command prints JSON and exits `0` for satisfied, `1` for violated, and
44
+ `2` for indeterminate, unreadable, or invalid input.
45
+
46
+ This package declares that command as its first-day assessment surface in
47
+ its own manifest:
48
+
49
+ ```json
50
+ "foundry": { "assessment": { "bin": "writer-rate-check", "invocation": "single-json-input" } }
51
+ ```
52
+
53
+ Onboarding discovers that declaration from the installed manifest and never
54
+ infers a surface. `writer-check` remains the multi-mode CLI and is not the
55
+ assessment surface. Writer is not a required first-day role; Advisor
56
+ remains the only required first-day assessment.
57
+
19
58
  ## Public entry points
20
59
 
21
60
  - `@clossys/writer` exposes both voice and copy-record APIs.
@@ -388,6 +427,52 @@ this run is reported too, as a `"warning"`, since a stale exclusion (the
388
427
  file it named was renamed or deleted) is otherwise indistinguishable from
389
428
  one still doing real work.
390
429
 
430
+ ## Machine-readable output: `writer-check --format json`
431
+
432
+ The default `writer-check` command takes `--format <text|json>`, defaulting to
433
+ `text`. Under `json` it prints exactly one object to stdout and nothing else —
434
+ every diagnostic that would normally go to stdout is suppressed, so the output
435
+ is always parseable — and writes nothing to stderr on a successful run.
436
+
437
+ The contract that matters is what the object carries, and when:
438
+
439
+ ```
440
+ {
441
+ recordFile, scanDir,
442
+ verdict: "clean" | "findings" | "indeterminate",
443
+ exitCode: 0 | 1 | 2,
444
+ reason?, // present when the verdict needs explaining
445
+ filesScanned, candidatesScanned, matched,
446
+ findings: CopyGateFinding[],
447
+ ignored: CopyGateIgnored[],
448
+ unchecked: UncheckedItem[],
449
+ parseFailures: { file, detail }[]
450
+ }
451
+ ```
452
+
453
+ **`verdict`, `findings` and `unchecked` are always present together, on every
454
+ path** — the clean path, the findings path, and the total-failure path alike.
455
+ That is the whole point of the shape. A single construct the scanner cannot
456
+ classify makes the run `indeterminate` and exits `2`, but the findings it did
457
+ produce are still in `findings`, recoverable by any caller that reads past the
458
+ exit code.
459
+
460
+ That mattered concretely: before this shape existed, one unclassifiable JSX
461
+ comment between attributes forced exit `2` and discarded **292 real findings**
462
+ from the same scan (issue #753). The exit code and the verdict still refuse to
463
+ read as clean — an indeterminate must never look like a pass — but "I could
464
+ not evaluate THIS ITEM" is no longer collapsed into "I could not evaluate
465
+ ANYTHING."
466
+
467
+ A caller keying only on `exitCode` or `verdict` therefore never mistakes an
468
+ indeterminate run for a clean one; a caller that reads `findings` recovers
469
+ everything the run measured regardless of how it ended.
470
+
471
+ This mirrors `@clossys/inspector`'s `VerifyStandardsReport` (per-row verdict
472
+ plus one overall verdict and exit code) and `@clossys/observer`'s
473
+ `CoverageCellState` three-state union, rather than inventing a fourth
474
+ convention.
475
+
391
476
  ## Copy addressability — is prose resolved from the registry, or typed inline?
392
477
 
393
478
  `checkCopyTraceability` above answers "does this literal match a registered
@@ -567,6 +652,10 @@ The root entry point exports the copy registry and traceability surface:
567
652
  `PathExclusionFindingRule`, and `PathExclusionValidation`.
568
653
  - Traceability: `checkCopyTraceability`, `CopyGateFinding`,
569
654
  `CopyGateIgnored`, `CopyGateResult`, and `CopyGateRule`.
655
+ - Charter close metric: `assessApprovedCopyCoverageRate`,
656
+ `ApprovedCopyCoverageRateAssessment`, `ApprovedCopyCoverageRateFinding`,
657
+ and `ApprovedCopyCoverageRateState`. Not `checkCopyRecord` or
658
+ `checkCopyTraceability`.
570
659
  - Addressability (see above): `scanAddressabilitySources`,
571
660
  `extractAddressabilityCandidates`, `checkAddressability`,
572
661
  `AddressabilityGateResult`, `AddressabilityPosition`,
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ export declare class WriterRateCliInputError extends Error {
3
+ }
4
+ /** Reads caller-owned assessment evidence without treating it as validated. */
5
+ export declare function readWriterAssessmentJson(path: string): unknown;
6
+ /** Testable CLI dispatcher. Invalid arguments throw; the executable maps them to exit 2. */
7
+ export declare function main(argv: readonly string[]): number;
8
+ /** Resolves an npm/POSIX bin symlink before deciding whether this module is the entrypoint. */
9
+ export declare function isDirectInvocation(moduleUrl: string, argvPath: string | undefined): boolean;
10
+ //# sourceMappingURL=approved-copy-coverage-rate-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approved-copy-coverage-rate-cli.d.ts","sourceRoot":"","sources":["../src/approved-copy-coverage-rate-cli.ts"],"names":[],"mappings":";AAQA,qBAAa,uBAAwB,SAAQ,KAAK;CAAG;AAErD,+EAA+E;AAC/E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAU9D;AAED,4FAA4F;AAC5F,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CASpD;AAWD,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAO3F"}
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { assessApprovedCopyCoverageRate } from "./approved-copy-coverage-rate.js";
6
+ const USAGE = `Usage: writer-rate-check <assessment.json>\n\nAssess approved copy coverage rate from consumer-supplied independent observations.\nExit codes: 0 = satisfied, 1 = violated, 2 = indeterminate or unreadable.\nwriter-check remains the multi-mode CLI and is not this assessment.`;
7
+ export class WriterRateCliInputError extends Error {
8
+ }
9
+ /** Reads caller-owned assessment evidence without treating it as validated. */
10
+ export function readWriterAssessmentJson(path) {
11
+ const resolved = resolve(path);
12
+ if (!existsSync(resolved))
13
+ throw new WriterRateCliInputError(`assessment file "${path}" does not exist`);
14
+ try {
15
+ if (!statSync(resolved).isFile())
16
+ throw new WriterRateCliInputError(`assessment file "${path}" is not a file`);
17
+ return JSON.parse(readFileSync(resolved, "utf8"));
18
+ }
19
+ catch (cause) {
20
+ if (cause instanceof WriterRateCliInputError)
21
+ throw cause;
22
+ throw new WriterRateCliInputError(`assessment file "${path}" is unreadable JSON: ${cause instanceof Error ? cause.message : String(cause)}`);
23
+ }
24
+ }
25
+ /** Testable CLI dispatcher. Invalid arguments throw; the executable maps them to exit 2. */
26
+ export function main(argv) {
27
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
28
+ console.log(USAGE);
29
+ return 0;
30
+ }
31
+ if (argv.length !== 1)
32
+ throw new WriterRateCliInputError("exactly one assessment.json file is required");
33
+ const report = assessApprovedCopyCoverageRate(readWriterAssessmentJson(argv[0]));
34
+ console.log(JSON.stringify(report, null, 2));
35
+ return report.state === "satisfied" ? 0 : report.state === "violated" ? 1 : 2;
36
+ }
37
+ function run() {
38
+ try {
39
+ process.exitCode = main(process.argv.slice(2));
40
+ }
41
+ catch (cause) {
42
+ console.error(`writer-rate-check: ${cause instanceof Error ? cause.message : String(cause)}`);
43
+ process.exitCode = 2;
44
+ }
45
+ }
46
+ /** Resolves an npm/POSIX bin symlink before deciding whether this module is the entrypoint. */
47
+ export function isDirectInvocation(moduleUrl, argvPath) {
48
+ if (argvPath === undefined)
49
+ return false;
50
+ try {
51
+ return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(resolve(argvPath));
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ if (isDirectInvocation(import.meta.url, process.argv[1]))
58
+ run();
59
+ //# sourceMappingURL=approved-copy-coverage-rate-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approved-copy-coverage-rate-cli.js","sourceRoot":"","sources":["../src/approved-copy-coverage-rate-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAElF,MAAM,KAAK,GAAG,mRAAmR,CAAC;AAElS,MAAM,OAAO,uBAAwB,SAAQ,KAAK;CAAG;AAErD,+EAA+E;AAC/E,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,IAAI,kBAAkB,CAAC,CAAC;IACzG,IAAI,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE;YAAE,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,IAAI,iBAAiB,CAAC,CAAC;QAC/G,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,uBAAuB;YAAE,MAAM,KAAK,CAAC;QAC1D,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,IAAI,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/I,CAAC;AACH,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,IAAI,CAAC,IAAuB;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,8CAA8C,CAAC,CAAC;IACzG,MAAM,MAAM,GAAG,8BAA8B,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC,CAAC,CAAC;IAC3F,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,GAAG;IACV,IAAI,CAAC;QACH,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9F,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,kBAAkB,CAAC,SAAiB,EAAE,QAA4B;IAChF,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAAE,GAAG,EAAE,CAAC"}
@@ -0,0 +1,23 @@
1
+ export type ApprovedCopyCoverageRateState = "satisfied" | "violated" | "indeterminate";
2
+ export interface ApprovedCopyCoverageRateFinding {
3
+ readonly rule: string;
4
+ readonly severity: "error";
5
+ readonly message: string;
6
+ readonly path?: string;
7
+ }
8
+ export interface ApprovedCopyCoverageRateAssessment {
9
+ readonly metric: "approved copy coverage rate";
10
+ readonly state: ApprovedCopyCoverageRateState;
11
+ readonly rate: number | null;
12
+ readonly evaluatedCopy: number;
13
+ readonly approvedCoveredCopy: number;
14
+ readonly findings: readonly ApprovedCopyCoverageRateFinding[];
15
+ readonly proposedPositions: readonly unknown[];
16
+ }
17
+ /**
18
+ * Computes `approved copy coverage rate`: shipped audience-facing copy
19
+ * resolved from approved traceable registry entries / all shipped
20
+ * audience-facing copy evaluated.
21
+ */
22
+ export declare function assessApprovedCopyCoverageRate(input: unknown): ApprovedCopyCoverageRateAssessment;
23
+ //# sourceMappingURL=approved-copy-coverage-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approved-copy-coverage-rate.d.ts","sourceRoot":"","sources":["../src/approved-copy-coverage-rate.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,6BAA6B,GAAG,WAAW,GAAG,UAAU,GAAG,eAAe,CAAC;AAEvF,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,MAAM,EAAE,6BAA6B,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,6BAA6B,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAC9D,QAAQ,CAAC,iBAAiB,EAAE,SAAS,OAAO,EAAE,CAAC;CAChD;AAyDD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,kCAAkC,CA4GjG"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Computes the Writer charter metric `approved copy coverage rate` from
3
+ * consumer-supplied independent observations.
4
+ *
5
+ * This module does not invent observations and does not relabel
6
+ * `checkCopyRecord` or `checkCopyTraceability`. An empty evaluated set is
7
+ * indeterminate, never a perfect rate of 1. `writer-check` remains the
8
+ * multi-mode CLI.
9
+ */
10
+ const METRIC = "approved copy coverage rate";
11
+ const RESERVED_OBSERVERS = new Set(["writer", "@clossys/writer"]);
12
+ const PROPOSED_POSITIONS = [];
13
+ function record(value) {
14
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15
+ }
16
+ function text(value) {
17
+ return typeof value === "string" && value.trim().length > 0;
18
+ }
19
+ function timestamp(value) {
20
+ return text(value) && !Number.isNaN(Date.parse(value));
21
+ }
22
+ function finding(rule, message, path) {
23
+ return path === undefined ? { rule, severity: "error", message } : { rule, severity: "error", message, path };
24
+ }
25
+ function evidenceCounts(value) {
26
+ if (!Array.isArray(value) || value.length === 0)
27
+ return false;
28
+ return value.every((item) => record(item) && text(item.id) && text(item.description));
29
+ }
30
+ function reservedObserver(value) {
31
+ return RESERVED_OBSERVERS.has(value.trim().toLowerCase());
32
+ }
33
+ function report(state, evaluatedCopy, approvedCoveredCopy, rate, findings) {
34
+ return {
35
+ metric: METRIC,
36
+ state,
37
+ rate,
38
+ evaluatedCopy,
39
+ approvedCoveredCopy,
40
+ findings,
41
+ proposedPositions: PROPOSED_POSITIONS,
42
+ };
43
+ }
44
+ function isCovered(entry) {
45
+ return entry.approved && entry.traceable && entry.resolvedFromRegistry;
46
+ }
47
+ /**
48
+ * Computes `approved copy coverage rate`: shipped audience-facing copy
49
+ * resolved from approved traceable registry entries / all shipped
50
+ * audience-facing copy evaluated.
51
+ */
52
+ export function assessApprovedCopyCoverageRate(input) {
53
+ if (!record(input)) {
54
+ return report("indeterminate", 0, 0, null, [finding("assessment-shape", "Assessment input must be an object.", "$")]);
55
+ }
56
+ const findings = [];
57
+ if (!timestamp(input.asOf)) {
58
+ findings.push(finding("assessment-as-of", "asOf must be an interpretable timestamp.", "asOf"));
59
+ }
60
+ const declaredCopy = input.declaredCopy;
61
+ if (!Array.isArray(declaredCopy)) {
62
+ findings.push(finding("declared-copy-required", "declaredCopy must be an array of { id } objects.", "declaredCopy"));
63
+ return report("indeterminate", 0, 0, null, findings);
64
+ }
65
+ const declaredIds = [];
66
+ const declaredSet = new Set();
67
+ declaredCopy.forEach((item, index) => {
68
+ const path = `declaredCopy[${index}]`;
69
+ if (!record(item) || !text(item.id)) {
70
+ findings.push(finding("copy-id-required", "id must be a non-empty string.", `${path}.id`));
71
+ return;
72
+ }
73
+ if (declaredSet.has(item.id)) {
74
+ findings.push(finding("duplicate-copy-id", `Duplicate id "${item.id}".`, `${path}.id`));
75
+ return;
76
+ }
77
+ declaredSet.add(item.id);
78
+ declaredIds.push(item.id);
79
+ });
80
+ if (declaredIds.length === 0) {
81
+ findings.push(finding("declared-copy-empty", "No declared copy is available for the approved-copy-coverage metric; the rate is indeterminate, never 1.", "declaredCopy"));
82
+ }
83
+ const observations = input.observations;
84
+ if (observations !== undefined && !Array.isArray(observations)) {
85
+ findings.push(finding("observations-shape", "observations must be an array.", "observations"));
86
+ }
87
+ const counting = new Map();
88
+ if (Array.isArray(observations)) {
89
+ observations.forEach((item, index) => {
90
+ const path = `observations[${index}]`;
91
+ if (!record(item)) {
92
+ findings.push(finding("observation-shape", "An observation must be an object.", path));
93
+ return;
94
+ }
95
+ if (text(item.observerRef) && reservedObserver(item.observerRef)) {
96
+ findings.push(finding("self-observation", "Writer cannot observe itself; reserved observerRef values are not independent.", `${path}.observerRef`));
97
+ return;
98
+ }
99
+ if (text(item.copyId) && !declaredSet.has(item.copyId)) {
100
+ findings.push(finding("unknown-copy", `Observation copyId "${item.copyId}" is not declared copy.`, `${path}.copyId`));
101
+ return;
102
+ }
103
+ if (item.independent !== true)
104
+ return;
105
+ if (typeof item.approved !== "boolean" ||
106
+ typeof item.traceable !== "boolean" ||
107
+ typeof item.resolvedFromRegistry !== "boolean")
108
+ return;
109
+ if (!text(item.observerRef) || !evidenceCounts(item.evidence) || !text(item.copyId) || !declaredSet.has(item.copyId))
110
+ return;
111
+ const bucket = counting.get(item.copyId) ?? [];
112
+ bucket.push({
113
+ approved: item.approved,
114
+ traceable: item.traceable,
115
+ resolvedFromRegistry: item.resolvedFromRegistry,
116
+ });
117
+ counting.set(item.copyId, bucket);
118
+ });
119
+ }
120
+ let evaluatedCopy = 0;
121
+ let approvedCoveredCopy = 0;
122
+ for (const id of declaredIds) {
123
+ const observed = counting.get(id) ?? [];
124
+ if (observed.length === 0) {
125
+ findings.push(finding("copy-unevaluated", `Declared copy "${id}" has no counting independent observation.`, `declaredCopy.${id}`));
126
+ continue;
127
+ }
128
+ evaluatedCopy += 1;
129
+ const allCovered = observed.every((entry) => isCovered(entry));
130
+ if (allCovered)
131
+ approvedCoveredCopy += 1;
132
+ else {
133
+ findings.push(finding("copy-not-covered", `Declared copy "${id}" was independently observed not resolved from an approved traceable registry entry.`, `declaredCopy.${id}`));
134
+ }
135
+ if (observed.length > 1) {
136
+ const first = observed[0];
137
+ const disagree = observed.some((entry) => entry.approved !== first.approved ||
138
+ entry.traceable !== first.traceable ||
139
+ entry.resolvedFromRegistry !== first.resolvedFromRegistry);
140
+ if (disagree) {
141
+ findings.push(finding("observation-disagreement", `Counting observations for "${id}" disagree.`, `observations.${id}`));
142
+ }
143
+ }
144
+ }
145
+ const rate = evaluatedCopy === 0 ? null : approvedCoveredCopy / evaluatedCopy;
146
+ if (evaluatedCopy === 0)
147
+ return report("indeterminate", 0, 0, null, findings);
148
+ const coverageComplete = evaluatedCopy === declaredIds.length;
149
+ const noErrorFindings = findings.length === 0;
150
+ const state = coverageComplete && approvedCoveredCopy === evaluatedCopy && noErrorFindings ? "satisfied" : "violated";
151
+ return report(state, evaluatedCopy, approvedCoveredCopy, rate, findings);
152
+ }
153
+ //# sourceMappingURL=approved-copy-coverage-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approved-copy-coverage-rate.js","sourceRoot":"","sources":["../src/approved-copy-coverage-rate.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -120,6 +120,8 @@
120
120
  * anyway so the accounting/exit-code contract stays correct for a future
121
121
  * CLI flag without a second pass over this logic.
122
122
  */
123
+ import { type CopyGateFinding, type CopyGateIgnored } from "./copy-gate.js";
124
+ import { type UncheckedItem } from "./scan.js";
123
125
  /** Exported for `cli.test.ts` — anything wrong with the arguments themselves always maps to exit code 2, never 1. */
124
126
  export declare class CliInputError extends Error {
125
127
  }
@@ -131,6 +133,59 @@ export declare class CliInputError extends Error {
131
133
  * that reason — `run()` below is the only caller that reads the real
132
134
  * `process.argv`.
133
135
  */
136
+ /**
137
+ * The default command's own structured, machine-readable report — issue
138
+ * #753's second half. `printGateReport`/`printScanAccounting` already put
139
+ * every finding a run DID produce ahead of any exit-code decision in the
140
+ * text output; the gap #753 found is that a consumer keying on the exit
141
+ * code alone (2 = indeterminate) cannot reach those findings at all,
142
+ * because nothing besides that human-readable text carries them. This
143
+ * type is that carrier: `--format json` prints EXACTLY ONE of these,
144
+ * nothing else, to stdout, whatever `exitCode` turns out to be — the same
145
+ * "one parseable object regardless of verdict" contract `inspector`'s own
146
+ * `VerifyStandardsReport`/`--format json` already publishes (see
147
+ * `packages/inspector/src/cli.ts`), and the same shape `observer`'s
148
+ * fleet-coverage grader and `addressability.ts`'s own `AddressabilityGateResult`
149
+ * use: a per-item verdict (here, each `CopyGateFinding`/`UncheckedItem`) is
150
+ * a DIFFERENT question from the run's one overall verdict, and collapsing
151
+ * the two into a bare exit code is exactly what discarded 292 real
152
+ * findings alongside one unclassifiable construct in #753's own repro.
153
+ *
154
+ * `verdict` is the SAME three states this whole package uses everywhere
155
+ * else (`"clean" | "findings" | "indeterminate"`), computed with the
156
+ * identical precedence `main()`'s own exit-code decision already applies
157
+ * below — `unchecked.length > 0` wins over `findings.length > 0`, which
158
+ * wins over a clean pass. Never a fourth state, and never inferred
159
+ * separately from `exitCode` by a consumer — read `verdict` (or
160
+ * `exitCode`) and treat `findings`/`unchecked` as what was actually
161
+ * measured, present or empty independent of which verdict resulted.
162
+ *
163
+ * `reason` is populated ONLY for the "could not run at all" shape of
164
+ * indeterminate — a missing/invalid copy record, an untrustworthy
165
+ * exclusion list, or a scan that matched/parsed nothing — where `findings`
166
+ * and `unchecked` are necessarily both `[]` because `checkCopyTraceability`
167
+ * never ran. It is `undefined` whenever a real scan DID run, including the
168
+ * `"indeterminate"` case #753 is about (real `findings`, plus one or more
169
+ * `unchecked` entries) — that case needs no extra prose beyond the data
170
+ * itself, which is the entire point of shipping it as data.
171
+ */
172
+ export interface CopyTraceabilityReport {
173
+ recordFile: string;
174
+ scanDir: string;
175
+ verdict: "clean" | "findings" | "indeterminate";
176
+ exitCode: 0 | 1 | 2;
177
+ reason?: string;
178
+ filesScanned: number;
179
+ candidatesScanned: number;
180
+ matched: number;
181
+ findings: CopyGateFinding[];
182
+ ignored: CopyGateIgnored[];
183
+ unchecked: UncheckedItem[];
184
+ parseFailures: {
185
+ file: string;
186
+ detail: string;
187
+ }[];
188
+ }
134
189
  export declare function main(argv: string[]): number;
135
190
  /**
136
191
  * Exported (unlike a typical CLI `main`) for the identical reason `main`
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AAuEH,qHAAqH;AACrH,qBAAa,aAAc,SAAQ,KAAK;CAAG;AA2f3C;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAyH3C;AAsGD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAqCxD;AAkFD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B9D"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AAWH,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,eAAe,EAAuB,MAAM,gBAAgB,CAAC;AAIxH,OAAO,EAAuC,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAkEpF,qHAAqH;AACrH,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAsgB3C;;;;;;;GAOG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,CAAC;IAChD,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnD;AAmBD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAyL3C;AAsGD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAqCxD;AAkFD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B9D"}