@clossys/writer 0.3.2

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 (115) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/LICENSE +21 -0
  3. package/README.md +593 -0
  4. package/dist/addressability.d.ts +245 -0
  5. package/dist/addressability.d.ts.map +1 -0
  6. package/dist/addressability.js +567 -0
  7. package/dist/addressability.js.map +1 -0
  8. package/dist/checker.d.ts +159 -0
  9. package/dist/checker.d.ts.map +1 -0
  10. package/dist/checker.js +177 -0
  11. package/dist/checker.js.map +1 -0
  12. package/dist/cli.d.ts +149 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +1049 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/copy-gate.d.ts +107 -0
  17. package/dist/copy-gate.d.ts.map +1 -0
  18. package/dist/copy-gate.js +132 -0
  19. package/dist/copy-gate.js.map +1 -0
  20. package/dist/fingerprint.d.ts +61 -0
  21. package/dist/fingerprint.d.ts.map +1 -0
  22. package/dist/fingerprint.js +64 -0
  23. package/dist/fingerprint.js.map +1 -0
  24. package/dist/index.d.ts +47 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +63 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/locale-coverage.d.ts +227 -0
  29. package/dist/locale-coverage.d.ts.map +1 -0
  30. package/dist/locale-coverage.js +414 -0
  31. package/dist/locale-coverage.js.map +1 -0
  32. package/dist/passage.d.ts +294 -0
  33. package/dist/passage.d.ts.map +1 -0
  34. package/dist/passage.js +463 -0
  35. package/dist/passage.js.map +1 -0
  36. package/dist/path-exclusions.d.ts +114 -0
  37. package/dist/path-exclusions.d.ts.map +1 -0
  38. package/dist/path-exclusions.js +213 -0
  39. package/dist/path-exclusions.js.map +1 -0
  40. package/dist/registry.d.ts +61 -0
  41. package/dist/registry.d.ts.map +1 -0
  42. package/dist/registry.js +64 -0
  43. package/dist/registry.js.map +1 -0
  44. package/dist/resolve.d.ts +33 -0
  45. package/dist/resolve.d.ts.map +1 -0
  46. package/dist/resolve.js +133 -0
  47. package/dist/resolve.js.map +1 -0
  48. package/dist/scan.d.ts +457 -0
  49. package/dist/scan.d.ts.map +1 -0
  50. package/dist/scan.js +1698 -0
  51. package/dist/scan.js.map +1 -0
  52. package/dist/schema.d.ts +81 -0
  53. package/dist/schema.d.ts.map +1 -0
  54. package/dist/schema.js +482 -0
  55. package/dist/schema.js.map +1 -0
  56. package/dist/types.d.ts +268 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +40 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/voice/checker.d.ts +186 -0
  61. package/dist/voice/checker.d.ts.map +1 -0
  62. package/dist/voice/checker.js +483 -0
  63. package/dist/voice/checker.js.map +1 -0
  64. package/dist/voice/derivation-coverage.d.ts +168 -0
  65. package/dist/voice/derivation-coverage.d.ts.map +1 -0
  66. package/dist/voice/derivation-coverage.js +181 -0
  67. package/dist/voice/derivation-coverage.js.map +1 -0
  68. package/dist/voice/fields.d.ts +107 -0
  69. package/dist/voice/fields.d.ts.map +1 -0
  70. package/dist/voice/fields.js +144 -0
  71. package/dist/voice/fields.js.map +1 -0
  72. package/dist/voice/index.d.ts +19 -0
  73. package/dist/voice/index.d.ts.map +1 -0
  74. package/dist/voice/index.js +24 -0
  75. package/dist/voice/index.js.map +1 -0
  76. package/dist/voice/internal/parse-template.d.ts +27 -0
  77. package/dist/voice/internal/parse-template.d.ts.map +1 -0
  78. package/dist/voice/internal/parse-template.js +111 -0
  79. package/dist/voice/internal/parse-template.js.map +1 -0
  80. package/dist/voice/internal/pattern-safety.d.ts +111 -0
  81. package/dist/voice/internal/pattern-safety.d.ts.map +1 -0
  82. package/dist/voice/internal/pattern-safety.js +278 -0
  83. package/dist/voice/internal/pattern-safety.js.map +1 -0
  84. package/dist/voice/schema.d.ts +65 -0
  85. package/dist/voice/schema.d.ts.map +1 -0
  86. package/dist/voice/schema.js +535 -0
  87. package/dist/voice/schema.js.map +1 -0
  88. package/dist/voice/types.d.ts +282 -0
  89. package/dist/voice/types.d.ts.map +1 -0
  90. package/dist/voice/types.js +37 -0
  91. package/dist/voice/types.js.map +1 -0
  92. package/package.json +72 -0
  93. package/src/addressability.ts +664 -0
  94. package/src/checker.ts +293 -0
  95. package/src/cli.ts +1166 -0
  96. package/src/copy-gate.ts +197 -0
  97. package/src/fingerprint.ts +66 -0
  98. package/src/index.ts +163 -0
  99. package/src/locale-coverage.ts +512 -0
  100. package/src/passage.ts +644 -0
  101. package/src/path-exclusions.ts +265 -0
  102. package/src/registry.ts +103 -0
  103. package/src/resolve.ts +172 -0
  104. package/src/scan.ts +1959 -0
  105. package/src/schema.ts +528 -0
  106. package/src/types.ts +301 -0
  107. package/src/voice/checker.ts +590 -0
  108. package/src/voice/derivation-coverage.ts +217 -0
  109. package/src/voice/fields.ts +160 -0
  110. package/src/voice/index.ts +55 -0
  111. package/src/voice/internal/parse-template.ts +114 -0
  112. package/src/voice/internal/pattern-safety.ts +336 -0
  113. package/src/voice/schema.ts +585 -0
  114. package/src/voice/types.ts +329 -0
  115. package/templates/voice-record.template.jsonc +135 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * `checkVoiceDerivationCoverage` — the voice half of `@clossys/
3
+ * strategy`'s `checkBrandCoverage`, which this file mirrors closely on
4
+ * purpose. Read `packages/strategy/src/brand-derivation.ts`'s header
5
+ * comment in full before touching this one; the design below is that
6
+ * file's, ported to the other side of the seam it documents.
7
+ *
8
+ * THE GAP THIS CLOSES
9
+ * ---------------------------------------------------------------------------
10
+ * A `BrandDerivation` (`strategy`) names two kinds of obligation by PLAIN
11
+ * STRING: `tokenSlots` and `voiceRules` — see that file's "THE NAME-ONLY
12
+ * SEAM, AND WHY". `checkBrandCoverage` verifies the `tokenSlots` half
13
+ * against a caller-supplied `brandableSlots: string[]`, in both directions.
14
+ * It does NOT, and by its own package's constraints CANNOT, verify the
15
+ * `voiceRules` half — `strategy` ships zero runtime dependencies and does
16
+ * not import `@clossys/writer/voice`, so it has no way to know which
17
+ * voice rule ids a real `VoiceRecord` actually declares. `strategy`'s own
18
+ * README says this plainly: "whether a `voiceRules` id resolves to a real
19
+ * `@clossys/writer/voice` glossary entry... [is] a later,
20
+ * cross-package gate's job — one with visibility into... `voice` that this
21
+ * package deliberately does not have." A brand attribute can oblige a voice
22
+ * rule that no `VoiceRecord` anywhere declares, and nothing catches it: the
23
+ * token half of the gate passes cleanly, because it only ever compares
24
+ * against a token slot list, and the voice half is never compared against
25
+ * anything at all. This file is that later gate — built here, in `copy`,
26
+ * because this is the one package that actually holds a `VoiceRecord`.
27
+ *
28
+ * THE NAME-ONLY SEAM, AND WHY
29
+ * ---------------------------------------------------------------------------
30
+ * This function takes two PLAIN STRING LISTS, `obligations` and
31
+ * `brandDerivedRuleIds` — the exact same discipline `BrandDerivation.
32
+ * voiceRules` documents for itself, carried through to this side of the
33
+ * seam, and the exact same shape `checkBrandCoverage` takes
34
+ * `brandableSlots` in. This file does not import `@clossys/
35
+ * strategy` or know anything about `BrandDerivation`, and it does not read
36
+ * a `VoiceRecord` either. Earlier revisions of this function DID take a
37
+ * `VoiceRecord` and scanned it for every glossary term, claim id, and
38
+ * pattern id, treating that whole scan as the set direction 2 had to cover.
39
+ * That was wrong, and it was wrong for the identical reason `strategy`
40
+ * cannot look up `tokens`' `brandable` flag itself: EVERY field in a
41
+ * `VoiceRecord` is consumer-authored, but authored is not the same claim as
42
+ * brand-derived. `@example/ui` ships 154 tokens and marks a curated
43
+ * 42 of them `brandable: true` — a deliberate subset a `BrandDerivation`'s
44
+ * `tokenSlots` is checked against, never the full 154. `voice` has no
45
+ * `brandable`-equivalent flag on a glossary entry or a claim, so scanning
46
+ * the whole record and calling that "the brand-derived set" silently
47
+ * substituted "everything a consumer happened to author" for "everything a
48
+ * brand attribute actually obliges" — a consumer with 200 glossary terms
49
+ * and one real brand obligation would need 200 `BrandDerivation.voiceRules`
50
+ * entries just to pass direction 2, a false-positive machine that gets
51
+ * disabled the first time it fires on a real record. Nothing inside a
52
+ * `VoiceRecord`, and nothing inside this package, can tell a brand-derived
53
+ * glossary term from an editorially-authored one — that judgment lives only
54
+ * where a `BrandDerivation` and a `VoiceRecord` are both in scope, i.e. the
55
+ * caller.
56
+ *
57
+ * THE CHECKER'S SEAM — READ THIS BEFORE CALLING `checkVoiceDerivationCoverage`
58
+ * ---------------------------------------------------------------------------
59
+ * Because this package cannot know which of a `VoiceRecord`'s ids a brand
60
+ * attribute actually derived, `checkVoiceDerivationCoverage` cannot look
61
+ * that set up itself. It takes it as an argument (`brandDerivedRuleIds:
62
+ * readonly string[]`) instead. This is deliberate, not a missing feature:
63
+ * the CALLER — a consumer repo that depends on both `strategy` and `copy`,
64
+ * or a later cross-package gate built with visibility into both — is the
65
+ * one place that seam can be closed for real, e.g. by flattening every
66
+ * `BrandDerivation.voiceRules` entry (across every derivation) into the
67
+ * flat `brandDerivedRuleIds` list this function takes, the same way a
68
+ * caller closes `checkBrandCoverage`'s seam by passing
69
+ * `Object.values(TOKENS).filter(t => t.brandable).map(t => t.property)`
70
+ * from `@example/ui/tokens`. `checkVoiceDerivationCoverage` only
71
+ * ever sees the two plain lists it's handed; it has no way to tell a real,
72
+ * current, brand-derived rule-id list from a stale or empty one, which is
73
+ * exactly why the degenerate cases below (an empty list either way) are
74
+ * treated as failures rather than silent passes — see "Fails closed",
75
+ * below.
76
+ *
77
+ * WHAT COUNTS AS A "VOICE RULE ID"
78
+ * ---------------------------------------------------------------------------
79
+ * This function no longer inspects a `VoiceRecord` at all, so it does not
80
+ * decide what counts as a rule id — the caller does, by what it puts in
81
+ * `brandDerivedRuleIds`. In practice that id space is still the one
82
+ * `strategy`'s own README names for `voiceRules`: "a real
83
+ * `@clossys/writer/voice` glossary entry" — i.e. a glossary term's
84
+ * `term` or a claim's `id` (`types.ts`'s `GlossaryEntry`/`Claim`). Earlier
85
+ * revisions of this file additionally counted `patterns` ids as voice rule
86
+ * ids by auto-deriving the whole set from a record; that widened direction
87
+ * 2 beyond what `strategy`'s documented `voiceRules` contract actually
88
+ * names, purely as a side effect of this package doing the scanning itself.
89
+ * With no record to scan and a caller-supplied list instead, this package
90
+ * no longer makes that decision — the caller decides what belongs in
91
+ * `brandDerivedRuleIds`, exactly as the caller decides what belongs in
92
+ * `checkBrandCoverage`'s `brandableSlots`.
93
+ *
94
+ * THE CHECK ITSELF — BOTH DIRECTIONS, LIKE `checkBrandCoverage`
95
+ * ---------------------------------------------------------------------------
96
+ * 1. Every id in `obligations` is named by `brandDerivedRuleIds` — an
97
+ * obligation naming an id nothing derived is a stale or typo'd
98
+ * reference (`obligationsMissingFromRecord`).
99
+ * 2. Every id in `brandDerivedRuleIds` is reached by at least one
100
+ * obligation — a brand-derived rule id with no obligation behind it
101
+ * is undocumented brand intent, exactly `checkBrandCoverage`'s
102
+ * `slotsMissingDerivation` mirrored onto the other list
103
+ * (`recordRulesNotObliged`).
104
+ *
105
+ * A ONE-DIRECTIONAL CHECKER PASSES CASE 2 AND HIDES IT — this file exists
106
+ * specifically because a checker that only implements direction 1 (every
107
+ * obligation resolves) looks complete, passes every obligation-side test,
108
+ * and still never notices a whole brand-derived rule id nobody obliged. See
109
+ * `derivation-coverage.test.ts` for the fixture built to prove this file
110
+ * does not make that mistake.
111
+ *
112
+ * FAILS CLOSED, IDENTICAL PRECEDENT TO `checkBrandCoverage`
113
+ * ---------------------------------------------------------------------------
114
+ * A checker given nothing to check must never report the same shape as a
115
+ * checker that checked everything and found it clean. Both degenerate
116
+ * inputs are an explicit failure, never a vacuous pass:
117
+ *
118
+ * - `obligations.length === 0` — nothing was obliged at all. `ok: false`,
119
+ * `reason: "no-obligations-provided"`.
120
+ * - `brandDerivedRuleIds.length === 0` — nothing to check obligations
121
+ * against. Maybe the caller forgot to pass the real list, maybe a
122
+ * consumer genuinely derived zero voice rules from its brand; either
123
+ * way this function cannot tell the difference, so it cannot report
124
+ * success. `ok: false`, `reason: "no-brand-derived-rules-provided"`.
125
+ *
126
+ * `obligationsChecked` and `rulesChecked` are always present, in every
127
+ * branch, for the identical reason `checkBrandCoverage`'s own header states
128
+ * for `slotsChecked`/`derivationsChecked`: so "zero things were checked"
129
+ * and "everything checked out fine" can never be told apart by a caller who
130
+ * only glances at `ok`.
131
+ *
132
+ * Pure — no I/O, never throws.
133
+ */
134
+ /**
135
+ * Why a `VoiceDerivationCoverageResult` is not `ok`, when it isn't.
136
+ * `"coverage-gap"` is a REAL finding — some obligation names an id nothing
137
+ * derived, or some brand-derived rule id is reached by no obligation —
138
+ * over a genuine, non-empty comparison. `"no-obligations-provided"` and
139
+ * `"no-brand-derived-rules-provided"` are the OTHER kind of failure:
140
+ * nothing meaningful was compared at all. See this file's top-of-file doc
141
+ * comment, "Fails closed".
142
+ */
143
+ export type VoiceDerivationCoverageFailureReason = "no-obligations-provided" | "no-brand-derived-rules-provided" | "coverage-gap";
144
+ export interface VoiceDerivationCoverageResult {
145
+ ok: boolean;
146
+ /** `obligations.length`, always present — see this file's header comment, "Fails closed". */
147
+ obligationsChecked: number;
148
+ /** `brandDerivedRuleIds.length`, always present, for the same reason. */
149
+ rulesChecked: number;
150
+ /** Direction 1: every obligation naming an id `brandDerivedRuleIds` does not declare. */
151
+ obligationsMissingFromRecord: string[];
152
+ /** Direction 2: every id in `brandDerivedRuleIds` that no obligation names. */
153
+ recordRulesNotObliged: string[];
154
+ /** Present exactly when `ok` is `false`. See `VoiceDerivationCoverageFailureReason`. */
155
+ reason?: VoiceDerivationCoverageFailureReason;
156
+ }
157
+ /**
158
+ * Checks whether `obligations` fully accounts for `brandDerivedRuleIds`, in
159
+ * both directions, and fails closed on either empty input. Pure — no I/O,
160
+ * never throws. See this file's top-of-file doc comment for the seam
161
+ * (`brandDerivedRuleIds` is caller-supplied, since this package has no way
162
+ * to tell a brand-derived voice rule id from any other consumer-authored
163
+ * one) and for why the two degenerate inputs are failures rather than
164
+ * vacuous passes. See `strategy`'s `checkBrandCoverage` (which this
165
+ * mirrors) for the precedent.
166
+ */
167
+ export declare function checkVoiceDerivationCoverage(obligations: readonly string[], brandDerivedRuleIds: readonly string[]): VoiceDerivationCoverageResult;
168
+ //# sourceMappingURL=derivation-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derivation-coverage.d.ts","sourceRoot":"","sources":["../../src/voice/derivation-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoIG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,oCAAoC,GAC5C,yBAAyB,GACzB,iCAAiC,GACjC,cAAc,CAAC;AAEnB,MAAM,WAAW,6BAA6B;IAC5C,EAAE,EAAE,OAAO,CAAC;IACZ,6FAA6F;IAC7F,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,4BAA4B,EAAE,MAAM,EAAE,CAAC;IACvC,+EAA+E;IAC/E,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,wFAAwF;IACxF,MAAM,CAAC,EAAE,oCAAoC,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,mBAAmB,EAAE,SAAS,MAAM,EAAE,GACrC,6BAA6B,CAyC/B"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `checkVoiceDerivationCoverage` — the voice half of `@clossys/
3
+ * strategy`'s `checkBrandCoverage`, which this file mirrors closely on
4
+ * purpose. Read `packages/strategy/src/brand-derivation.ts`'s header
5
+ * comment in full before touching this one; the design below is that
6
+ * file's, ported to the other side of the seam it documents.
7
+ *
8
+ * THE GAP THIS CLOSES
9
+ * ---------------------------------------------------------------------------
10
+ * A `BrandDerivation` (`strategy`) names two kinds of obligation by PLAIN
11
+ * STRING: `tokenSlots` and `voiceRules` — see that file's "THE NAME-ONLY
12
+ * SEAM, AND WHY". `checkBrandCoverage` verifies the `tokenSlots` half
13
+ * against a caller-supplied `brandableSlots: string[]`, in both directions.
14
+ * It does NOT, and by its own package's constraints CANNOT, verify the
15
+ * `voiceRules` half — `strategy` ships zero runtime dependencies and does
16
+ * not import `@clossys/writer/voice`, so it has no way to know which
17
+ * voice rule ids a real `VoiceRecord` actually declares. `strategy`'s own
18
+ * README says this plainly: "whether a `voiceRules` id resolves to a real
19
+ * `@clossys/writer/voice` glossary entry... [is] a later,
20
+ * cross-package gate's job — one with visibility into... `voice` that this
21
+ * package deliberately does not have." A brand attribute can oblige a voice
22
+ * rule that no `VoiceRecord` anywhere declares, and nothing catches it: the
23
+ * token half of the gate passes cleanly, because it only ever compares
24
+ * against a token slot list, and the voice half is never compared against
25
+ * anything at all. This file is that later gate — built here, in `copy`,
26
+ * because this is the one package that actually holds a `VoiceRecord`.
27
+ *
28
+ * THE NAME-ONLY SEAM, AND WHY
29
+ * ---------------------------------------------------------------------------
30
+ * This function takes two PLAIN STRING LISTS, `obligations` and
31
+ * `brandDerivedRuleIds` — the exact same discipline `BrandDerivation.
32
+ * voiceRules` documents for itself, carried through to this side of the
33
+ * seam, and the exact same shape `checkBrandCoverage` takes
34
+ * `brandableSlots` in. This file does not import `@clossys/
35
+ * strategy` or know anything about `BrandDerivation`, and it does not read
36
+ * a `VoiceRecord` either. Earlier revisions of this function DID take a
37
+ * `VoiceRecord` and scanned it for every glossary term, claim id, and
38
+ * pattern id, treating that whole scan as the set direction 2 had to cover.
39
+ * That was wrong, and it was wrong for the identical reason `strategy`
40
+ * cannot look up `tokens`' `brandable` flag itself: EVERY field in a
41
+ * `VoiceRecord` is consumer-authored, but authored is not the same claim as
42
+ * brand-derived. `@example/ui` ships 154 tokens and marks a curated
43
+ * 42 of them `brandable: true` — a deliberate subset a `BrandDerivation`'s
44
+ * `tokenSlots` is checked against, never the full 154. `voice` has no
45
+ * `brandable`-equivalent flag on a glossary entry or a claim, so scanning
46
+ * the whole record and calling that "the brand-derived set" silently
47
+ * substituted "everything a consumer happened to author" for "everything a
48
+ * brand attribute actually obliges" — a consumer with 200 glossary terms
49
+ * and one real brand obligation would need 200 `BrandDerivation.voiceRules`
50
+ * entries just to pass direction 2, a false-positive machine that gets
51
+ * disabled the first time it fires on a real record. Nothing inside a
52
+ * `VoiceRecord`, and nothing inside this package, can tell a brand-derived
53
+ * glossary term from an editorially-authored one — that judgment lives only
54
+ * where a `BrandDerivation` and a `VoiceRecord` are both in scope, i.e. the
55
+ * caller.
56
+ *
57
+ * THE CHECKER'S SEAM — READ THIS BEFORE CALLING `checkVoiceDerivationCoverage`
58
+ * ---------------------------------------------------------------------------
59
+ * Because this package cannot know which of a `VoiceRecord`'s ids a brand
60
+ * attribute actually derived, `checkVoiceDerivationCoverage` cannot look
61
+ * that set up itself. It takes it as an argument (`brandDerivedRuleIds:
62
+ * readonly string[]`) instead. This is deliberate, not a missing feature:
63
+ * the CALLER — a consumer repo that depends on both `strategy` and `copy`,
64
+ * or a later cross-package gate built with visibility into both — is the
65
+ * one place that seam can be closed for real, e.g. by flattening every
66
+ * `BrandDerivation.voiceRules` entry (across every derivation) into the
67
+ * flat `brandDerivedRuleIds` list this function takes, the same way a
68
+ * caller closes `checkBrandCoverage`'s seam by passing
69
+ * `Object.values(TOKENS).filter(t => t.brandable).map(t => t.property)`
70
+ * from `@example/ui/tokens`. `checkVoiceDerivationCoverage` only
71
+ * ever sees the two plain lists it's handed; it has no way to tell a real,
72
+ * current, brand-derived rule-id list from a stale or empty one, which is
73
+ * exactly why the degenerate cases below (an empty list either way) are
74
+ * treated as failures rather than silent passes — see "Fails closed",
75
+ * below.
76
+ *
77
+ * WHAT COUNTS AS A "VOICE RULE ID"
78
+ * ---------------------------------------------------------------------------
79
+ * This function no longer inspects a `VoiceRecord` at all, so it does not
80
+ * decide what counts as a rule id — the caller does, by what it puts in
81
+ * `brandDerivedRuleIds`. In practice that id space is still the one
82
+ * `strategy`'s own README names for `voiceRules`: "a real
83
+ * `@clossys/writer/voice` glossary entry" — i.e. a glossary term's
84
+ * `term` or a claim's `id` (`types.ts`'s `GlossaryEntry`/`Claim`). Earlier
85
+ * revisions of this file additionally counted `patterns` ids as voice rule
86
+ * ids by auto-deriving the whole set from a record; that widened direction
87
+ * 2 beyond what `strategy`'s documented `voiceRules` contract actually
88
+ * names, purely as a side effect of this package doing the scanning itself.
89
+ * With no record to scan and a caller-supplied list instead, this package
90
+ * no longer makes that decision — the caller decides what belongs in
91
+ * `brandDerivedRuleIds`, exactly as the caller decides what belongs in
92
+ * `checkBrandCoverage`'s `brandableSlots`.
93
+ *
94
+ * THE CHECK ITSELF — BOTH DIRECTIONS, LIKE `checkBrandCoverage`
95
+ * ---------------------------------------------------------------------------
96
+ * 1. Every id in `obligations` is named by `brandDerivedRuleIds` — an
97
+ * obligation naming an id nothing derived is a stale or typo'd
98
+ * reference (`obligationsMissingFromRecord`).
99
+ * 2. Every id in `brandDerivedRuleIds` is reached by at least one
100
+ * obligation — a brand-derived rule id with no obligation behind it
101
+ * is undocumented brand intent, exactly `checkBrandCoverage`'s
102
+ * `slotsMissingDerivation` mirrored onto the other list
103
+ * (`recordRulesNotObliged`).
104
+ *
105
+ * A ONE-DIRECTIONAL CHECKER PASSES CASE 2 AND HIDES IT — this file exists
106
+ * specifically because a checker that only implements direction 1 (every
107
+ * obligation resolves) looks complete, passes every obligation-side test,
108
+ * and still never notices a whole brand-derived rule id nobody obliged. See
109
+ * `derivation-coverage.test.ts` for the fixture built to prove this file
110
+ * does not make that mistake.
111
+ *
112
+ * FAILS CLOSED, IDENTICAL PRECEDENT TO `checkBrandCoverage`
113
+ * ---------------------------------------------------------------------------
114
+ * A checker given nothing to check must never report the same shape as a
115
+ * checker that checked everything and found it clean. Both degenerate
116
+ * inputs are an explicit failure, never a vacuous pass:
117
+ *
118
+ * - `obligations.length === 0` — nothing was obliged at all. `ok: false`,
119
+ * `reason: "no-obligations-provided"`.
120
+ * - `brandDerivedRuleIds.length === 0` — nothing to check obligations
121
+ * against. Maybe the caller forgot to pass the real list, maybe a
122
+ * consumer genuinely derived zero voice rules from its brand; either
123
+ * way this function cannot tell the difference, so it cannot report
124
+ * success. `ok: false`, `reason: "no-brand-derived-rules-provided"`.
125
+ *
126
+ * `obligationsChecked` and `rulesChecked` are always present, in every
127
+ * branch, for the identical reason `checkBrandCoverage`'s own header states
128
+ * for `slotsChecked`/`derivationsChecked`: so "zero things were checked"
129
+ * and "everything checked out fine" can never be told apart by a caller who
130
+ * only glances at `ok`.
131
+ *
132
+ * Pure — no I/O, never throws.
133
+ */
134
+ /**
135
+ * Checks whether `obligations` fully accounts for `brandDerivedRuleIds`, in
136
+ * both directions, and fails closed on either empty input. Pure — no I/O,
137
+ * never throws. See this file's top-of-file doc comment for the seam
138
+ * (`brandDerivedRuleIds` is caller-supplied, since this package has no way
139
+ * to tell a brand-derived voice rule id from any other consumer-authored
140
+ * one) and for why the two degenerate inputs are failures rather than
141
+ * vacuous passes. See `strategy`'s `checkBrandCoverage` (which this
142
+ * mirrors) for the precedent.
143
+ */
144
+ export function checkVoiceDerivationCoverage(obligations, brandDerivedRuleIds) {
145
+ const obligationsChecked = obligations.length;
146
+ const rulesChecked = brandDerivedRuleIds.length;
147
+ const ruleIdSet = new Set(brandDerivedRuleIds);
148
+ const obligedSet = new Set(obligations);
149
+ const obligationsMissingFromRecord = obligations.filter((id) => !ruleIdSet.has(id));
150
+ const recordRulesNotObliged = brandDerivedRuleIds.filter((id) => !obligedSet.has(id));
151
+ if (obligationsChecked === 0) {
152
+ return {
153
+ ok: false,
154
+ obligationsChecked,
155
+ rulesChecked,
156
+ obligationsMissingFromRecord,
157
+ recordRulesNotObliged,
158
+ reason: "no-obligations-provided",
159
+ };
160
+ }
161
+ if (rulesChecked === 0) {
162
+ return {
163
+ ok: false,
164
+ obligationsChecked,
165
+ rulesChecked,
166
+ obligationsMissingFromRecord,
167
+ recordRulesNotObliged,
168
+ reason: "no-brand-derived-rules-provided",
169
+ };
170
+ }
171
+ const ok = obligationsMissingFromRecord.length === 0 && recordRulesNotObliged.length === 0;
172
+ return {
173
+ ok,
174
+ obligationsChecked,
175
+ rulesChecked,
176
+ obligationsMissingFromRecord,
177
+ recordRulesNotObliged,
178
+ reason: ok ? undefined : "coverage-gap",
179
+ };
180
+ }
181
+ //# sourceMappingURL=derivation-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derivation-coverage.js","sourceRoot":"","sources":["../../src/voice/derivation-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoIG;AA8BH;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,WAA8B,EAC9B,mBAAsC;IAEtC,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAAC;IAC9C,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC;IAEhD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAExC,MAAM,4BAA4B,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtF,IAAI,kBAAkB,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,kBAAkB;YAClB,YAAY;YACZ,4BAA4B;YAC5B,qBAAqB;YACrB,MAAM,EAAE,yBAAyB;SAClC,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,kBAAkB;YAClB,YAAY;YACZ,4BAA4B;YAC5B,qBAAqB;YACrB,MAAM,EAAE,iCAAiC;SAC1C,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,GAAG,4BAA4B,CAAC,MAAM,KAAK,CAAC,IAAI,qBAAqB,CAAC,MAAM,KAAK,CAAC,CAAC;IAC3F,OAAO;QACL,EAAE;QACF,kBAAkB;QAClB,YAAY;QACZ,4BAA4B;QAC5B,qBAAqB;QACrB,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc;KACxC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The bindable/fixed split for `VoiceRecord` — the `TokenDefinition.brandable`
3
+ * analog for this package. See `@example/ui/tokens`' `src/tokens.ts` for
4
+ * the model this file ports.
5
+ *
6
+ * WHERE THE LINE ACTUALLY FALLS (a finding, not an assumption)
7
+ * ---------------------------------------------------------------------------
8
+ * In `tokens`, the split runs WITHIN one flat namespace: `styles/tokens.css`
9
+ * declares 154 real CSS custom properties, and only 42 of them are
10
+ * `brandable: true` — the other 112 (spacing, the type ramp, z-index,
11
+ * duration, easing, breakpoints, the neutral ramp, ...) are declared with a
12
+ * real value and STAY that value for every brand, forever. Both the
13
+ * brandable and the non-brandable tokens live in the same file, the same
14
+ * `TOKENS` record, distinguished only by the boolean.
15
+ *
16
+ * `VoiceRecord` does not have that shape, and pretending it does would be
17
+ * dishonest. Every field a `VoiceRecord` instance actually carries — `id`,
18
+ * both rules' `description`s, `forbiddenPronouns`, `forbiddenMarkers`,
19
+ * `formality`, `tone`, `glossary`, `claims`, `patterns` — is content one specific
20
+ * consumer authors: there is no field here that ships a working default
21
+ * value and stays that value for every voice the way `--spacing-lg: 16px`
22
+ * does for every brand. `VoiceRecord` IS the consumer-owned binding layer in
23
+ * its entirety, the same way `brand.css` is: nothing in `brand-template.css`
24
+ * is "shipped, leave it alone" either — every slot IN that file is
25
+ * `bindable`, by construction, because only the bindable slots were ever
26
+ * put in the template in the first place. The non-brandable 112 in `tokens`
27
+ * live in `tokens.css`, a different file `brand-template.css` never touches;
28
+ * `VoiceRecord`'s analog of that other file is `types.ts` and `schema.ts` —
29
+ * TypeScript interfaces and string-literal unions, not runtime record
30
+ * fields at all.
31
+ *
32
+ * So the split for voice runs BETWEEN two files, not within one:
33
+ *
34
+ * FIXED (rule KINDS — never appear below, never appear in the template,
35
+ * shipped once in `types.ts`, identical for every consumer forever):
36
+ * - the shape of `VoiceRules` itself — that a voice has exactly a
37
+ * `person` rule, a `tense` rule, a `formality` axis, and a `tone`
38
+ * axis, no more, no fewer;
39
+ * - `FormalityLevel`'s three possible values (`"casual"`, `"neutral"`,
40
+ * `"formal"`) and `GlossaryStatus`'s two (`"forbidden"`,
41
+ * `"preferred"`) — the ENUM, not which member a given entry picks;
42
+ * - the field set of `PersonRule`, `TenseRule`, `GlossaryEntry`, and
43
+ * `Claim` — that a glossary entry has a `term`/`status`/`reason`/
44
+ * `alternative`/`caseSensitive` and nothing else, for instance.
45
+ *
46
+ * BINDABLE (rule VALUES — every entry below, every one `true`, all
47
+ * consumer-owned, all present in `voice-record.template.jsonc`):
48
+ * - which pronouns are forbidden, which tense markers, which specific
49
+ * terms are forbidden or preferred and why, which claims are
50
+ * registered and whether each needs a `factRef` — the actual data
51
+ * that fills the fixed shape above, one consumer's own.
52
+ *
53
+ * This file catalogs only the second half — the runtime fields of a
54
+ * `VoiceRecord` instance — because those are the only things a template can
55
+ * meaningfully hold a fill-in-the-blank for. It is `Readonly<Record<...>>`,
56
+ * every entry `bindable: true`, deliberately: `src/field-coverage.test.ts`
57
+ * still runs a two-way check against it (and a third, currently-vacuous but
58
+ * future-guarding check that no `bindable: false` entry is ever live in the
59
+ * template), the same shape as `tokens`' `brand-coverage.test.ts`, so that
60
+ * if a future field is ever added here as `bindable: false`, the existing
61
+ * tests immediately start meaning something rather than needing to be
62
+ * written from scratch.
63
+ *
64
+ * A LEAF, NOT A TREE — WHY ARRAYS STOP HERE
65
+ * ---------------------------------------------------------------------------
66
+ * `glossary` and `claims` are each catalogued as ONE path, not expanded
67
+ * into their per-entry fields (`glossary.term`, `glossary.reason`, ...).
68
+ * Both are variable-length collections of consumer-authored records — the
69
+ * PER-ENTRY shape (`GlossaryEntry`, `Claim`) is the FIXED half described
70
+ * above, already enumerated once in `types.ts`; re-stating it here as
71
+ * catalog entries would just be a second, driftable copy of the same
72
+ * interface. What's bindable about `glossary` and `claims` is that the
73
+ * ARRAY exists and a consumer populates it — exactly what one path each,
74
+ * marked `bindable: true`, records.
75
+ */
76
+ /** One runtime field path of a `VoiceRecord` instance, and its bindable/fixed classification. */
77
+ export interface VoiceFieldDefinition {
78
+ /** Dot-path from the record root, e.g. `"rules.person.forbiddenPronouns"`. Array-valued fields stop here — see this file's header comment, "A leaf, not a tree". */
79
+ readonly path: string;
80
+ /**
81
+ * `true` for every entry in this catalog: a rule VALUE, consumer-owned,
82
+ * expected to appear in `voice-record.template.jsonc`. See this file's
83
+ * header comment for why no `VoiceRecord` field is ever `false` here, and
84
+ * where the fixed half of this package's contract actually lives instead.
85
+ */
86
+ readonly bindable: boolean;
87
+ /** Human-readable note on what this slot holds. */
88
+ readonly description: string;
89
+ }
90
+ /**
91
+ * Every runtime field path a `VoiceRecord` instance carries, keyed by that
92
+ * same path. `src/field-coverage.test.ts` asserts, in both directions, that
93
+ * this set and `templates/voice-record.template.jsonc`'s own set of paths
94
+ * are identical.
95
+ */
96
+ export declare const VOICE_FIELDS: Readonly<Record<string, VoiceFieldDefinition>>;
97
+ /**
98
+ * The literal placeholder value `voice-record.template.jsonc` uses for every
99
+ * bindable string slot. Loud and specific on purpose — see
100
+ * `checker.ts`'s `findPlaceholderPaths` for why an unmissable, near-zero-
101
+ * collision sentinel is exactly what makes the unbound signal trustworthy:
102
+ * a real voice's copy of the template that forgot to fill in even one slot
103
+ * still trips it, and nothing a real consumer would plausibly type by
104
+ * coincidence ever does.
105
+ */
106
+ export declare const TEMPLATE_PLACEHOLDER = "REPLACE_ME__VOICE_TEMPLATE_PLACEHOLDER";
107
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../src/voice/fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AAEH,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACnC,oKAAoK;IACpK,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAmDvE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,2CAA2C,CAAC"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * The bindable/fixed split for `VoiceRecord` — the `TokenDefinition.brandable`
3
+ * analog for this package. See `@example/ui/tokens`' `src/tokens.ts` for
4
+ * the model this file ports.
5
+ *
6
+ * WHERE THE LINE ACTUALLY FALLS (a finding, not an assumption)
7
+ * ---------------------------------------------------------------------------
8
+ * In `tokens`, the split runs WITHIN one flat namespace: `styles/tokens.css`
9
+ * declares 154 real CSS custom properties, and only 42 of them are
10
+ * `brandable: true` — the other 112 (spacing, the type ramp, z-index,
11
+ * duration, easing, breakpoints, the neutral ramp, ...) are declared with a
12
+ * real value and STAY that value for every brand, forever. Both the
13
+ * brandable and the non-brandable tokens live in the same file, the same
14
+ * `TOKENS` record, distinguished only by the boolean.
15
+ *
16
+ * `VoiceRecord` does not have that shape, and pretending it does would be
17
+ * dishonest. Every field a `VoiceRecord` instance actually carries — `id`,
18
+ * both rules' `description`s, `forbiddenPronouns`, `forbiddenMarkers`,
19
+ * `formality`, `tone`, `glossary`, `claims`, `patterns` — is content one specific
20
+ * consumer authors: there is no field here that ships a working default
21
+ * value and stays that value for every voice the way `--spacing-lg: 16px`
22
+ * does for every brand. `VoiceRecord` IS the consumer-owned binding layer in
23
+ * its entirety, the same way `brand.css` is: nothing in `brand-template.css`
24
+ * is "shipped, leave it alone" either — every slot IN that file is
25
+ * `bindable`, by construction, because only the bindable slots were ever
26
+ * put in the template in the first place. The non-brandable 112 in `tokens`
27
+ * live in `tokens.css`, a different file `brand-template.css` never touches;
28
+ * `VoiceRecord`'s analog of that other file is `types.ts` and `schema.ts` —
29
+ * TypeScript interfaces and string-literal unions, not runtime record
30
+ * fields at all.
31
+ *
32
+ * So the split for voice runs BETWEEN two files, not within one:
33
+ *
34
+ * FIXED (rule KINDS — never appear below, never appear in the template,
35
+ * shipped once in `types.ts`, identical for every consumer forever):
36
+ * - the shape of `VoiceRules` itself — that a voice has exactly a
37
+ * `person` rule, a `tense` rule, a `formality` axis, and a `tone`
38
+ * axis, no more, no fewer;
39
+ * - `FormalityLevel`'s three possible values (`"casual"`, `"neutral"`,
40
+ * `"formal"`) and `GlossaryStatus`'s two (`"forbidden"`,
41
+ * `"preferred"`) — the ENUM, not which member a given entry picks;
42
+ * - the field set of `PersonRule`, `TenseRule`, `GlossaryEntry`, and
43
+ * `Claim` — that a glossary entry has a `term`/`status`/`reason`/
44
+ * `alternative`/`caseSensitive` and nothing else, for instance.
45
+ *
46
+ * BINDABLE (rule VALUES — every entry below, every one `true`, all
47
+ * consumer-owned, all present in `voice-record.template.jsonc`):
48
+ * - which pronouns are forbidden, which tense markers, which specific
49
+ * terms are forbidden or preferred and why, which claims are
50
+ * registered and whether each needs a `factRef` — the actual data
51
+ * that fills the fixed shape above, one consumer's own.
52
+ *
53
+ * This file catalogs only the second half — the runtime fields of a
54
+ * `VoiceRecord` instance — because those are the only things a template can
55
+ * meaningfully hold a fill-in-the-blank for. It is `Readonly<Record<...>>`,
56
+ * every entry `bindable: true`, deliberately: `src/field-coverage.test.ts`
57
+ * still runs a two-way check against it (and a third, currently-vacuous but
58
+ * future-guarding check that no `bindable: false` entry is ever live in the
59
+ * template), the same shape as `tokens`' `brand-coverage.test.ts`, so that
60
+ * if a future field is ever added here as `bindable: false`, the existing
61
+ * tests immediately start meaning something rather than needing to be
62
+ * written from scratch.
63
+ *
64
+ * A LEAF, NOT A TREE — WHY ARRAYS STOP HERE
65
+ * ---------------------------------------------------------------------------
66
+ * `glossary` and `claims` are each catalogued as ONE path, not expanded
67
+ * into their per-entry fields (`glossary.term`, `glossary.reason`, ...).
68
+ * Both are variable-length collections of consumer-authored records — the
69
+ * PER-ENTRY shape (`GlossaryEntry`, `Claim`) is the FIXED half described
70
+ * above, already enumerated once in `types.ts`; re-stating it here as
71
+ * catalog entries would just be a second, driftable copy of the same
72
+ * interface. What's bindable about `glossary` and `claims` is that the
73
+ * ARRAY exists and a consumer populates it — exactly what one path each,
74
+ * marked `bindable: true`, records.
75
+ */
76
+ /**
77
+ * Every runtime field path a `VoiceRecord` instance carries, keyed by that
78
+ * same path. `src/field-coverage.test.ts` asserts, in both directions, that
79
+ * this set and `templates/voice-record.template.jsonc`'s own set of paths
80
+ * are identical.
81
+ */
82
+ export const VOICE_FIELDS = {
83
+ id: {
84
+ path: "id",
85
+ bindable: true,
86
+ description: "This voice's own identifier, e.g. the product or repo it belongs to.",
87
+ },
88
+ "rules.person.description": {
89
+ path: "rules.person.description",
90
+ bindable: true,
91
+ description: "Human-readable statement of the person rule, e.g. \"second-person, you-voice\".",
92
+ },
93
+ "rules.person.forbiddenPronouns": {
94
+ path: "rules.person.forbiddenPronouns",
95
+ bindable: true,
96
+ description: "The word list `checkCopy` actually scans for — pronouns that contradict the person rule.",
97
+ },
98
+ "rules.tense.description": {
99
+ path: "rules.tense.description",
100
+ bindable: true,
101
+ description: "Human-readable statement of the tense rule, e.g. \"present tense, no future promises\".",
102
+ },
103
+ "rules.tense.forbiddenMarkers": {
104
+ path: "rules.tense.forbiddenMarkers",
105
+ bindable: true,
106
+ description: "The word list `checkCopy` actually scans for — tense markers that contradict the tense rule.",
107
+ },
108
+ "rules.formality": {
109
+ path: "rules.formality",
110
+ bindable: true,
111
+ description: "Which `FormalityLevel` this voice picks. Descriptive only — never read by `checkCopy`.",
112
+ },
113
+ "rules.tone": {
114
+ path: "rules.tone",
115
+ bindable: true,
116
+ description: "Free-text tone descriptors, e.g. [\"warm\", \"direct\", \"no jargon\"]. Descriptive only.",
117
+ },
118
+ glossary: {
119
+ path: "glossary",
120
+ bindable: true,
121
+ description: "This voice's own forbidden/preferred term list. Per-entry shape (`GlossaryEntry`) is fixed; the entries themselves are this consumer's own.",
122
+ },
123
+ claims: {
124
+ path: "claims",
125
+ bindable: true,
126
+ description: "This voice's own claims register. Per-entry shape (`Claim`) is fixed; the entries themselves are this consumer's own.",
127
+ },
128
+ patterns: {
129
+ path: "patterns",
130
+ bindable: true,
131
+ description: "This voice's own regex pattern rules (alternation, optional apostrophes, hard character bans). Per-entry shape (`PatternRule`) is fixed; the entries themselves are this consumer's own. Optional — a voice with no pattern rules simply omits this array (`patterns` is the one field in this catalog that is also optional at the `VoiceRecord` TYPE level, not merely defaulted at validation time — see `types.ts`'s `VoiceRecord` doc comment for why).",
132
+ },
133
+ };
134
+ /**
135
+ * The literal placeholder value `voice-record.template.jsonc` uses for every
136
+ * bindable string slot. Loud and specific on purpose — see
137
+ * `checker.ts`'s `findPlaceholderPaths` for why an unmissable, near-zero-
138
+ * collision sentinel is exactly what makes the unbound signal trustworthy:
139
+ * a real voice's copy of the template that forgot to fill in even one slot
140
+ * still trips it, and nothing a real consumer would plausibly type by
141
+ * coincidence ever does.
142
+ */
143
+ export const TEMPLATE_PLACEHOLDER = "REPLACE_ME__VOICE_TEMPLATE_PLACEHOLDER";
144
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/voice/fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AAiBH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAmD;IAC1E,EAAE,EAAE;QACF,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,sEAAsE;KACpF;IACD,0BAA0B,EAAE;QAC1B,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,iFAAiF;KAC/F;IACD,gCAAgC,EAAE;QAChC,IAAI,EAAE,gCAAgC;QACtC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,0FAA0F;KACxG;IACD,yBAAyB,EAAE;QACzB,IAAI,EAAE,yBAAyB;QAC/B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,yFAAyF;KACvG;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,8BAA8B;QACpC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8FAA8F;KAC5G;IACD,iBAAiB,EAAE;QACjB,IAAI,EAAE,iBAAiB;QACvB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,wFAAwF;KACtG;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,2FAA2F;KACzG;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,6IAA6I;KAC3J;IACD,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,uHAAuH;KACrI;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8bAA8b;KAC5c;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,wCAAwC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @clossys/writer/voice — public entry point. See README.md for the full
3
+ * picture: the machinery-vs-values split this package mirrors from
4
+ * @example/ui/tokens, exactly what `checkCopy` does and does not
5
+ * attempt, and the `factRef` seam into a separate `strategy` package this
6
+ * package deliberately does not import from.
7
+ */
8
+ export { FORMALITY_LEVELS, GLOSSARY_STATUSES, VOICE_SEVERITIES } from "./types.js";
9
+ export type { PersonRule, TenseRule, FormalityLevel, VoiceRules, GlossaryStatus, GlossaryEntry, Claim, VoiceChannel, VoicePattern, PatternRule, VoiceSeverity, VoiceRecord, VoiceFinding, } from "./types.js";
10
+ export { VOICE_FIELDS, TEMPLATE_PLACEHOLDER } from "./fields.js";
11
+ export type { VoiceFieldDefinition } from "./fields.js";
12
+ export { validateVoiceRecordShape, parseVoiceRecord } from "./schema.js";
13
+ export { checkCopy, auditClaimsRegister, isCiBlockingSeverity } from "./checker.js";
14
+ export type { VoiceCheckDimension, VoiceDimensionSkip, VoiceCheckWaiver, WaivedVoiceFinding, VoiceCheckOptions, VoiceCheckReport, } from "./checker.js";
15
+ export { checkVoiceDerivationCoverage } from "./derivation-coverage.js";
16
+ export type { VoiceDerivationCoverageFailureReason, VoiceDerivationCoverageResult } from "./derivation-coverage.js";
17
+ export { checkPatternSafety } from "./internal/pattern-safety.js";
18
+ export type { PatternSafetyIssue, PatternSafetyResult } from "./internal/pattern-safety.js";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnF,YAAY,EACV,UAAU,EACV,SAAS,EACT,cAAc,EACd,UAAU,EACV,cAAc,EACd,aAAa,EACb,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpF,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAMtB,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,YAAY,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAQpH,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC"}