@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,196 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [0.3.2] - 2026-08-30
7
+
8
+ ### Changed
9
+
10
+ - Updated the package's public repository, issue-tracker, and homepage metadata to the canonical Foundry repository. This change is not a publication or qualification claim.
11
+
12
+ ## [0.3.1] - 2026-08-24
13
+
14
+ ### Fixed
15
+
16
+ - Added a current lifecycle note to the historical donor availability record.
17
+
18
+ ## [0.3.0] - 2026-08-21
19
+
20
+ ### Added — the passage layer (issue #373)
21
+
22
+ **A `Passage` composes `CopyEntry`/glossary-term REFERENCES the way a UI
23
+ block composes atoms.** `@vespeneventures/writer` already had terms (a
24
+ glossary) and entries (single addressable strings) and nothing between
25
+ them — in practice nobody reuses one string; they reuse a whole
26
+ empty-state (title + body + action), a whole FAQ item (question + answer),
27
+ a whole error (message + recovery). This release adds that missing
28
+ middle: terms ≈ tokens, entries ≈ atoms, passages ≈ blocks. Documents
29
+ (the composition layer, mirroring how a view composes blocks) remain out
30
+ of scope for this release — see the issue.
31
+
32
+ - **`PassageRecord`/`Passage`** (`passage.ts`): a `Passage` has a stable
33
+ dot-separated `id`, a required `context` (an unlocatable passage is not
34
+ reviewable, mirroring `CopyEntry.context`'s own rule), and `fields` — a
35
+ set of named slots (`title`, `body`, `action`, ...) each of which should
36
+ hold a `PassageReference`: `{ ref: "entry", id }` or `{ ref: "term",
37
+ term }`.
38
+ - **`validatePassageRecordShape`/`parsePassageRecord`** — structural
39
+ validation, in the same dependency-free, accumulate-and-keep-going style
40
+ every other schema in this package uses. Deliberately does NOT validate
41
+ a field's own value shape (whether it's a literal or a reference) — see
42
+ below.
43
+ - **`readPassageRecord`** — the one place in `passage.ts` that touches a
44
+ filesystem; never throws, mirrors `registry.ts`'s `readCopyRecord`
45
+ exactly.
46
+ - **`classifyPassageField`/`checkPassageComposition`** — the passage
47
+ COMPOSITION gate, wired to a new CLI subcommand, `writer-check passages
48
+ <registry-file>`, dispatched the identical way `writer-check
49
+ addressability` already is (a fully separate top-level branch, its own
50
+ exported `mainPassagesCheck`, never folded through `main()`). THE
51
+ TERNARY:
52
+ - **`0` (satisfied)** — every passage references only entries and
53
+ terms, at least one passage evaluated.
54
+ - **`1` (violated)** — a passage inlines a literal string instead of
55
+ referencing an entry (the verbal equivalent of a hardcoded value
56
+ instead of a token), or references another passage's own internals
57
+ (`{ ref: "passage", ... }`) rather than composing entries/terms the
58
+ way a block composes atoms. Wins over an incomplete picture in the
59
+ same run — the identical "a real violation must outrank an incomplete
60
+ scan" precedence this package's own `addressability` gate settled on
61
+ for issue #407/#433, applied here from the start.
62
+ - **`2` (indeterminate)** — the registry could not be
63
+ read/parsed/validated, zero passages were registered, or a field's
64
+ value could not be confidently classified, with zero violations found.
65
+ Fails CLOSED with a machine-readable reason; never a silent pass.
66
+ - **What this gate deliberately does not do**: verify a referenced entry
67
+ id or term actually exists in a real `CopyRecord`/glossary. That is a
68
+ different, weaker question than the one this gate answers (composition
69
+ purity: is this field a reference at all, never mind whether it
70
+ resolves) — the same split `addressability.ts` already draws from
71
+ `copy-gate.ts`'s traceability check. See
72
+ `passage.adversarial.test.ts` for the proof: a weaker
73
+ "every referenced entry id exists" tool passes a passage built entirely
74
+ from inline literals (it has zero references to check), while
75
+ `writer-check passages`, spawned as the compiled CLI, correctly exits 1
76
+ on the identical fixture.
77
+ - Not ported from `@vespeneventures/designer/tokens`: the `brandable` boolean.
78
+ See `passage.ts`'s own top doc comment, "WHERE THE MIRROR STOPS", for
79
+ why forcing that field into this layer would be false symmetry.
80
+
81
+ `checkPassageComposition`, `classifyPassageField`, `parsePassageRecord`,
82
+ `readPassageRecord`, `validatePassageRecordShape`, and every associated
83
+ type are exported from this package's root entry point.
84
+
85
+ ## [0.2.0] - 2026-08-21
86
+
87
+ ### Changed — BEHAVIOURAL, read this before upgrading (issue #407)
88
+
89
+ **`writer-check addressability`'s exit-code precedence flipped.** A run that
90
+ finds at least one violation now exits `1` REGARDLESS of how many string
91
+ positions are unclassified. Previously, any unclassified ("unchecked")
92
+ position forced exit `2` ("indeterminate") even when the same run had
93
+ already found and named real violations — and on any real tree, hundreds of
94
+ positions are token data this gate cannot classify by design, so the old
95
+ precedence made `1` unreachable outside a fixture: every real run had *some*
96
+ unclassified positions and therefore always read `2`, silently discarding
97
+ every violation it had actually found.
98
+
99
+ - **`violated` (exit `1`)** — at least one violation, regardless of
100
+ unclassified count. The coverage gap is not hidden by this: `unchecked`
101
+ and `reasons` are still populated and `writer-check addressability`
102
+ still prints them unconditionally; only the verdict changed.
103
+ - **`indeterminate` (exit `2`)** — zero violations AND at least one
104
+ unclassified position (or the tree could not be read, or zero components
105
+ were scanned). The honest "found nothing, but did not see everything"
106
+ case — unchanged, and still never a pass.
107
+ - **`satisfied` (exit `0`)** — zero violations, zero unclassified, over at
108
+ least one scanned file. Unchanged.
109
+
110
+ **If your CI treats `writer-check addressability`'s exit `2` as
111
+ non-blocking or "flaky, coverage is never complete, ignore it": stop.** A
112
+ tree that used to report `2` will now correctly report `1` whenever it
113
+ contains a real violation, and that is the point of this release, not a
114
+ regression — those trees always had the violation, this exit code was
115
+ previously the only thing hiding it.
116
+
117
+ `checkAddressability`'s `AddressabilityGateResult.verdict`/`.reasons` and
118
+ `mainAddressabilityCheck`'s exit code both changed together; no other
119
+ export's shape changed. See `addressability.ts`'s top doc comment ("THE
120
+ TERNARY") and `README.md`'s "Copy addressability" section for the full
121
+ precedence.
122
+
123
+ ## [0.1.0] - 2026-08-21
124
+
125
+ First release. This package is the writer role, recut from
126
+ `@vespeneventures/copy` per
127
+ [decision 10](../../docs/DECISIONS.md#10-recutting-the-expression-surface-into-role-shaped-packages).
128
+
129
+ This changelog starts here rather than carrying the donor's history, which
130
+ cites decisions and issues that would mean nothing — or the wrong thing — to
131
+ a reader who arrives at this package first.
132
+
133
+ ### Added
134
+
135
+ - A consumer-owned language system: voice rules (glossary terms and
136
+ regex-safe pattern rules), claims validation, addressable and
137
+ locale-aware copy records (`CopyRegistry`, `CopyRef`), source
138
+ traceability scanning, and computed content fingerprints.
139
+ - `readCopyRecord` and the `checkCopy` voice checker, unchanged from the
140
+ donor.
141
+ - Four gates, all reachable from the single `writer-check` bin: the
142
+ default traceability command, `addressability`, `voice-derivation-coverage`,
143
+ and `locale-coverage`. Each dispatches on `argv[0]` matching exactly —
144
+ never on `basename(process.argv[1])`, which would see `cli.js` and
145
+ silently run the wrong command wherever a gate is invoked by compiled
146
+ path.
147
+ - Zero runtime dependencies, unchanged from the donor.
148
+ - The `voice-record.template.jsonc` template, still reachable at the
149
+ `@vespeneventures/writer/voice-record.template.jsonc` export subpath.
150
+ - **The published tarball carries this changelog.** `files` includes
151
+ `CHANGELOG.md`, following the convention the operation packages adopted in
152
+ #417. A consumer reading the installed package should not have to leave it
153
+ to find out what changed; a new package should be born with the current
154
+ convention rather than inheriting its donor's gap.
155
+
156
+ ### Changed from `@vespeneventures/copy`
157
+
158
+ - **The package is named for the job, not the artifact.** The role's
159
+ exclusive question is *is it well said?* A name that describes a thing
160
+ rather than a doer is an artifact, and an artifact belongs inside a role.
161
+ - **The bin is `writer-check`, not `copy-check`.** Same program, same four
162
+ subcommands, renamed to match the package.
163
+ - **Nothing else was renamed.** `CopyRegistry`, `CopyRef`, `checkCopyRecord`,
164
+ `CopyEntry`, `checkCopyTraceability`, the `copy-record` vocabulary, and
165
+ every voice/glossary/claim term keep their names. A role owns artifacts;
166
+ renaming the role does not rename what it reasons about. A sweep that also
167
+ renamed the vocabulary would have made the diff unreviewable while
168
+ changing no behaviour.
169
+ - Self-referential `@vespeneventures/copy` package-name mentions in doc
170
+ comments — including the `/voice` subpath — were updated to
171
+ `@vespeneventures/writer`, the same treatment `strategist` gave its own
172
+ self-references to `@vespeneventures/strategy`.
173
+
174
+ ### On the donor, and why it is not deprecated yet
175
+
176
+ > **Current lifecycle note:** `@vespeneventures/copy` and
177
+ > `@vespeneventures/surface` are now retired. This release note records their
178
+ > state at 0.1.0; the lifecycle contract is the authority for current
179
+ > availability.
180
+
181
+ `@vespeneventures/copy` stays `published` for now. It cannot be marked
182
+ deprecated while `@vespeneventures/surface` still declares it as a runtime
183
+ dependency — `check:package-governance` reports a lifecycle finding for a live
184
+ package depending on a deprecated one, and it is right to. The donors are
185
+ deprecated once `publisher` replaces `surface` and depends on this package
186
+ instead.
187
+
188
+ This is a deferral with a trigger, not an omission.
189
+
190
+ ### Not included
191
+
192
+ - **No forwarding stub in the donor.** `@vespeneventures/copy` is
193
+ deprecated-and-retained: still installable for a consumer already pinned
194
+ to it, with no re-export pointing here. A stub would keep the old name
195
+ importable, and a supersession check could then never reach zero — the
196
+ forwarding layer would defeat the gate built to prove the swap completed.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Calvin Hung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.