@thinkingsage/kanon 0.8.0

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 (199) hide show
  1. package/CHANGELOG.md +410 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -0
  4. package/bridge/mcp-server.cjs +14171 -0
  5. package/package.json +98 -0
  6. package/src/adapters/capabilities.ts +178 -0
  7. package/src/adapters/claude-code.ts +110 -0
  8. package/src/adapters/cline.ts +98 -0
  9. package/src/adapters/codex.ts +173 -0
  10. package/src/adapters/copilot.ts +106 -0
  11. package/src/adapters/cursor.ts +97 -0
  12. package/src/adapters/degradation.ts +95 -0
  13. package/src/adapters/index.ts +324 -0
  14. package/src/adapters/kiro-frontmatter.ts +139 -0
  15. package/src/adapters/kiro-inclusion.ts +86 -0
  16. package/src/adapters/kiro.ts +412 -0
  17. package/src/adapters/qdeveloper.ts +115 -0
  18. package/src/adapters/types.ts +81 -0
  19. package/src/adapters/windsurf.ts +96 -0
  20. package/src/admin.ts +283 -0
  21. package/src/asset-conventions.ts +118 -0
  22. package/src/attribution-backfill.ts +319 -0
  23. package/src/attribution-report.ts +95 -0
  24. package/src/attribution.ts +239 -0
  25. package/src/backends/github.ts +194 -0
  26. package/src/backends/http.ts +122 -0
  27. package/src/backends/index.ts +39 -0
  28. package/src/backends/local.ts +47 -0
  29. package/src/backends/s3.ts +157 -0
  30. package/src/backends/types.ts +59 -0
  31. package/src/base-cache.ts +270 -0
  32. package/src/browse-ui.ts +3754 -0
  33. package/src/browse.ts +1038 -0
  34. package/src/build.ts +1108 -0
  35. package/src/catalog.ts +204 -0
  36. package/src/cli-deprecated.ts +29 -0
  37. package/src/cli.ts +773 -0
  38. package/src/collection-admin.ts +287 -0
  39. package/src/collection-builder.ts +464 -0
  40. package/src/collections.ts +116 -0
  41. package/src/compatibility.ts +105 -0
  42. package/src/config.ts +743 -0
  43. package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
  44. package/src/eval.ts +1169 -0
  45. package/src/file-writer.ts +61 -0
  46. package/src/format-registry.ts +141 -0
  47. package/src/guild/auto-updater.ts +163 -0
  48. package/src/guild/backend-resolver.ts +49 -0
  49. package/src/guild/cli.ts +592 -0
  50. package/src/guild/collection-expander.ts +47 -0
  51. package/src/guild/global-cache.ts +247 -0
  52. package/src/guild/hook-generator.ts +100 -0
  53. package/src/guild/manifest.ts +154 -0
  54. package/src/guild/path-utils.ts +12 -0
  55. package/src/guild/sync.ts +622 -0
  56. package/src/guild/version-resolver.ts +42 -0
  57. package/src/help/metadata.ts +445 -0
  58. package/src/help/renderer.ts +265 -0
  59. package/src/help/typo-suggester.ts +25 -0
  60. package/src/hooks/expression.ts +493 -0
  61. package/src/hooks/pipeline.ts +141 -0
  62. package/src/import.ts +773 -0
  63. package/src/importers/claude-code.ts +134 -0
  64. package/src/importers/cline.ts +103 -0
  65. package/src/importers/codex.ts +140 -0
  66. package/src/importers/copilot.ts +103 -0
  67. package/src/importers/cursor.ts +105 -0
  68. package/src/importers/index.ts +390 -0
  69. package/src/importers/kiro.ts +110 -0
  70. package/src/importers/qdeveloper.ts +103 -0
  71. package/src/importers/types.ts +54 -0
  72. package/src/importers/windsurf.ts +104 -0
  73. package/src/install.ts +1005 -0
  74. package/src/manifest-admin.ts +306 -0
  75. package/src/mcp-bridge.ts +240 -0
  76. package/src/mutation/delta.ts +50 -0
  77. package/src/mutation/history.ts +66 -0
  78. package/src/mutation/operators.ts +524 -0
  79. package/src/mutation/runner.ts +332 -0
  80. package/src/new.ts +106 -0
  81. package/src/outcomes/collision.ts +127 -0
  82. package/src/outcomes/normalize.ts +208 -0
  83. package/src/outcomes/registry.ts +173 -0
  84. package/src/parser.ts +446 -0
  85. package/src/provenance-backfill-cli.ts +319 -0
  86. package/src/provenance-backfill.ts +520 -0
  87. package/src/publish.ts +354 -0
  88. package/src/reconcile-orchestrator.ts +502 -0
  89. package/src/reconcile-report-renderer.ts +176 -0
  90. package/src/resolve-body.ts +15 -0
  91. package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
  92. package/src/rosetta/builtins/contracts.ts +1033 -0
  93. package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
  94. package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
  95. package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
  96. package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
  97. package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
  98. package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
  99. package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
  100. package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
  101. package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
  102. package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
  103. package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
  104. package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
  105. package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
  106. package/src/rosetta/builtins/sources/cline-native.ts +176 -0
  107. package/src/rosetta/builtins/sources/codex-native.ts +343 -0
  108. package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
  109. package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
  110. package/src/rosetta/builtins/sources/index.ts +95 -0
  111. package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
  112. package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
  113. package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
  114. package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
  115. package/src/rosetta/builtins/sources/superpowers.ts +240 -0
  116. package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
  117. package/src/rosetta/builtins/targets/claude-code.ts +181 -0
  118. package/src/rosetta/builtins/targets/cline.ts +87 -0
  119. package/src/rosetta/builtins/targets/codex.ts +226 -0
  120. package/src/rosetta/builtins/targets/copilot.ts +103 -0
  121. package/src/rosetta/builtins/targets/cursor.ts +87 -0
  122. package/src/rosetta/builtins/targets/index.ts +60 -0
  123. package/src/rosetta/builtins/targets/kiro.ts +278 -0
  124. package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
  125. package/src/rosetta/builtins/targets/windsurf.ts +87 -0
  126. package/src/rosetta/canonical.ts +729 -0
  127. package/src/rosetta/compatibility.ts +432 -0
  128. package/src/rosetta/contracts.ts +329 -0
  129. package/src/rosetta/detector.ts +724 -0
  130. package/src/rosetta/diagnostics.ts +630 -0
  131. package/src/rosetta/engine-bootstrap.ts +103 -0
  132. package/src/rosetta/engine.ts +744 -0
  133. package/src/rosetta/index.ts +381 -0
  134. package/src/rosetta/inspection.ts +530 -0
  135. package/src/rosetta/plan.ts +448 -0
  136. package/src/rosetta/provenance-digest.ts +369 -0
  137. package/src/rosetta/reconcile.ts +812 -0
  138. package/src/rosetta/redaction.ts +467 -0
  139. package/src/rosetta/registry.ts +712 -0
  140. package/src/rosetta/renderers.ts +571 -0
  141. package/src/rosetta/request-guard.ts +335 -0
  142. package/src/rosetta/resolution.ts +419 -0
  143. package/src/rosetta/source-accounting.ts +233 -0
  144. package/src/rosetta/templates.ts +129 -0
  145. package/src/rosetta-cli.ts +717 -0
  146. package/src/rosetta-docs-generator.ts +793 -0
  147. package/src/rosetta-profiles-cli.ts +367 -0
  148. package/src/schemas.ts +1712 -0
  149. package/src/spec-coordination.ts +1141 -0
  150. package/src/temper.ts +747 -0
  151. package/src/template-bundle-loader.ts +312 -0
  152. package/src/template-engine.ts +53 -0
  153. package/src/translation-application-policy.ts +496 -0
  154. package/src/translation-orchestrator.ts +1013 -0
  155. package/src/translation-plan-applier.ts +473 -0
  156. package/src/tutorial.ts +305 -0
  157. package/src/validate.ts +1093 -0
  158. package/src/versioning.ts +553 -0
  159. package/src/wizard.ts +660 -0
  160. package/src/workspace.ts +237 -0
  161. package/templates/eval-contexts/claude-code.md.njk +6 -0
  162. package/templates/eval-contexts/cline.md.njk +6 -0
  163. package/templates/eval-contexts/copilot.md.njk +6 -0
  164. package/templates/eval-contexts/cursor.md.njk +6 -0
  165. package/templates/eval-contexts/kiro.md.njk +10 -0
  166. package/templates/eval-contexts/qdeveloper.md.njk +6 -0
  167. package/templates/eval-contexts/windsurf.md.njk +6 -0
  168. package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
  169. package/templates/harness-adapters/_base/base.md.njk +16 -0
  170. package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
  171. package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
  172. package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
  173. package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
  174. package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
  175. package/templates/harness-adapters/cline/hook.sh.njk +4 -0
  176. package/templates/harness-adapters/cline/mcp.json.njk +1 -0
  177. package/templates/harness-adapters/cline/rule.md.njk +1 -0
  178. package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
  179. package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
  180. package/templates/harness-adapters/codex/skill.md.njk +27 -0
  181. package/templates/harness-adapters/copilot/agents.md.njk +1 -0
  182. package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
  183. package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
  184. package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
  185. package/templates/harness-adapters/cursor/rule.md.njk +6 -0
  186. package/templates/harness-adapters/kiro/hook.json.njk +1 -0
  187. package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
  188. package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
  189. package/templates/harness-adapters/kiro/power.md.njk +12 -0
  190. package/templates/harness-adapters/kiro/steering.md.njk +16 -0
  191. package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
  192. package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
  193. package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
  194. package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
  195. package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
  196. package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
  197. package/templates/knowledge/hooks.yaml.njk +4 -0
  198. package/templates/knowledge/knowledge.md.njk +53 -0
  199. package/templates/knowledge/mcp-servers.yaml.njk +2 -0
@@ -0,0 +1,630 @@
1
+ /**
2
+ * Rosetta Stone structured diagnostics.
3
+ *
4
+ * Provides trusted diagnostic factories, blocking-code metadata,
5
+ * source/canonical locations, deterministic sorting, safe internal-error
6
+ * conversion, and the RegistryFailure fallback.
7
+ *
8
+ * All functions are pure. No filesystem, process, clock, random, Git,
9
+ * or network imports.
10
+ */
11
+
12
+ import type {
13
+ CanonicalDiagnosticLocation,
14
+ DegradationDetail,
15
+ RegistryFailure,
16
+ RosettaSeverity,
17
+ SourceDiagnosticLocation,
18
+ TranslationDiagnostic,
19
+ TranslationPhase,
20
+ } from "../schemas";
21
+
22
+ // --- Phase ordering (used for deterministic sorting) ---
23
+
24
+ /**
25
+ * Canonical ordering of translation phases. Lower index = earlier in pipeline.
26
+ */
27
+ export const TRANSLATION_PHASE_ORDER: readonly TranslationPhase[] = [
28
+ "request",
29
+ "registry",
30
+ "detection",
31
+ "source-validation",
32
+ "source-translation",
33
+ "canonical-validation",
34
+ "compatibility",
35
+ "target-translation",
36
+ "plan-validation",
37
+ "redaction",
38
+ ] as const;
39
+
40
+ const PHASE_INDEX: ReadonlyMap<TranslationPhase, number> = new Map(
41
+ TRANSLATION_PHASE_ORDER.map((phase, idx) => [phase, idx]),
42
+ );
43
+
44
+ // --- Severity ordering ---
45
+
46
+ const SEVERITY_ORDER: Record<RosettaSeverity, number> = {
47
+ error: 0,
48
+ warning: 1,
49
+ info: 2,
50
+ };
51
+
52
+ // --- Diagnostic Code Metadata ---
53
+
54
+ export interface DiagnosticCodeMetadata {
55
+ readonly code: string;
56
+ readonly phase: TranslationPhase;
57
+ readonly defaultSeverity: RosettaSeverity;
58
+ readonly blocking: boolean;
59
+ readonly messageTemplate: string;
60
+ readonly remediationTemplate: string;
61
+ }
62
+
63
+ /**
64
+ * Const registry of known RS_ diagnostic codes with metadata.
65
+ */
66
+ export const DIAGNOSTIC_CODE_REGISTRY: Readonly<
67
+ Record<string, DiagnosticCodeMetadata>
68
+ > = {
69
+ RS_INVALID_REQUEST: {
70
+ code: "RS_INVALID_REQUEST",
71
+ phase: "request",
72
+ defaultSeverity: "error",
73
+ blocking: true,
74
+ messageTemplate: "The translation request is invalid or malformed.",
75
+ remediationTemplate:
76
+ "Check the request structure against the TranslationRequest schema.",
77
+ },
78
+ RS_INVALID_CONTRACT: {
79
+ code: "RS_INVALID_CONTRACT",
80
+ phase: "registry",
81
+ defaultSeverity: "error",
82
+ blocking: true,
83
+ messageTemplate:
84
+ "The format contract is invalid or does not satisfy registration requirements.",
85
+ remediationTemplate:
86
+ "Review the format contract fields against FormatContractSchema.",
87
+ },
88
+ RS_REGISTRATION_FAILED: {
89
+ code: "RS_REGISTRATION_FAILED",
90
+ phase: "registry",
91
+ defaultSeverity: "error",
92
+ blocking: true,
93
+ messageTemplate:
94
+ "Format registration failed due to a conflict or constraint violation.",
95
+ remediationTemplate:
96
+ "Resolve duplicate identifiers, aliases, or unsupported contract versions.",
97
+ },
98
+ RS_REGISTRY_FAILURE: {
99
+ code: "RS_REGISTRY_FAILURE",
100
+ phase: "registry",
101
+ defaultSeverity: "error",
102
+ blocking: true,
103
+ messageTemplate:
104
+ "A registry operation failed and diagnostic construction was unavailable.",
105
+ remediationTemplate:
106
+ "Inspect the registration inputs for structural issues.",
107
+ },
108
+ RS_NO_MATCH: {
109
+ code: "RS_NO_MATCH",
110
+ phase: "detection",
111
+ defaultSeverity: "warning",
112
+ blocking: false,
113
+ messageTemplate:
114
+ "No registered format met the selection threshold for the provided documents.",
115
+ remediationTemplate:
116
+ "Supply an explicit format selection or verify the source documents match a registered format.",
117
+ },
118
+ RS_AMBIGUOUS_MATCH: {
119
+ code: "RS_AMBIGUOUS_MATCH",
120
+ phase: "detection",
121
+ defaultSeverity: "error",
122
+ blocking: true,
123
+ messageTemplate:
124
+ "Multiple formats share the highest qualifying confidence score.",
125
+ remediationTemplate:
126
+ "Supply an explicit format selection to resolve the ambiguity.",
127
+ },
128
+ RS_SOURCE_UNACCOUNTED: {
129
+ code: "RS_SOURCE_UNACCOUNTED",
130
+ phase: "source-translation",
131
+ defaultSeverity: "warning",
132
+ blocking: false,
133
+ messageTemplate:
134
+ "A source document was neither consumed nor preserved during translation.",
135
+ remediationTemplate:
136
+ "Consume the document by parsing its content or preserve it by carrying it into the canonical artifact.",
137
+ },
138
+ RS_SOURCE_LOSS: {
139
+ code: "RS_SOURCE_LOSS",
140
+ phase: "source-translation",
141
+ defaultSeverity: "warning",
142
+ blocking: false,
143
+ messageTemplate:
144
+ "Source data has no declared canonical mapping and will not be preserved.",
145
+ remediationTemplate:
146
+ "Verify the source field is not needed or declare an extraFields mapping.",
147
+ },
148
+ RS_SOURCE_LOSS_STRICT: {
149
+ code: "RS_SOURCE_LOSS_STRICT",
150
+ phase: "source-translation",
151
+ defaultSeverity: "error",
152
+ blocking: true,
153
+ messageTemplate: "Source data loss is not permitted in strict mode.",
154
+ remediationTemplate:
155
+ "Declare an explicit canonical mapping for the source field or disable strict mode.",
156
+ },
157
+ RS_CANONICAL_INVALID: {
158
+ code: "RS_CANONICAL_INVALID",
159
+ phase: "canonical-validation",
160
+ defaultSeverity: "error",
161
+ blocking: true,
162
+ messageTemplate: "The canonical artifact fails schema validation.",
163
+ remediationTemplate:
164
+ "Review canonical field paths against KnowledgeArtifactSchema.",
165
+ },
166
+ RS_COMPATIBILITY_PARTIAL: {
167
+ code: "RS_COMPATIBILITY_PARTIAL",
168
+ phase: "compatibility",
169
+ defaultSeverity: "warning",
170
+ blocking: false,
171
+ messageTemplate:
172
+ "A canonical capability is only partially supported by the target format.",
173
+ remediationTemplate:
174
+ "Review the degradation action and expected semantic change for the affected capability.",
175
+ },
176
+ RS_COMPATIBILITY_NONE: {
177
+ code: "RS_COMPATIBILITY_NONE",
178
+ phase: "compatibility",
179
+ defaultSeverity: "warning",
180
+ blocking: false,
181
+ messageTemplate:
182
+ "A canonical capability is not supported by the target format.",
183
+ remediationTemplate:
184
+ "The affected canonical data will be omitted from the target output.",
185
+ },
186
+ RS_COMPATIBILITY_INCOMPLETE_PROFILE: {
187
+ code: "RS_COMPATIBILITY_INCOMPLETE_PROFILE",
188
+ phase: "compatibility",
189
+ defaultSeverity: "error",
190
+ blocking: true,
191
+ messageTemplate:
192
+ "The compatibility profile is incomplete and does not cover all canonical capabilities.",
193
+ remediationTemplate:
194
+ "Add entries for every canonical capability to the format contract's compatibility profile.",
195
+ },
196
+ RS_UNSAFE_PATH: {
197
+ code: "RS_UNSAFE_PATH",
198
+ phase: "plan-validation",
199
+ defaultSeverity: "error",
200
+ blocking: true,
201
+ messageTemplate:
202
+ "An output path is unsafe or violates path normalization rules.",
203
+ remediationTemplate:
204
+ "Ensure all output paths use forward slashes, no traversal, and are NFC-normalized.",
205
+ },
206
+ RS_PATH_COLLISION: {
207
+ code: "RS_PATH_COLLISION",
208
+ phase: "plan-validation",
209
+ defaultSeverity: "error",
210
+ blocking: true,
211
+ messageTemplate:
212
+ "Multiple output operations target the same normalized path.",
213
+ remediationTemplate:
214
+ "Resolve duplicate output paths in the translation plan.",
215
+ },
216
+ RS_TRANSLATOR_INTERNAL: {
217
+ code: "RS_TRANSLATOR_INTERNAL",
218
+ phase: "request",
219
+ defaultSeverity: "error",
220
+ blocking: true,
221
+ messageTemplate: "An internal translator error occurred.",
222
+ remediationTemplate: "Report this issue to the translator maintainer.",
223
+ },
224
+ RS_REDACTION_UNSAFE: {
225
+ code: "RS_REDACTION_UNSAFE",
226
+ phase: "redaction",
227
+ defaultSeverity: "error",
228
+ blocking: true,
229
+ messageTemplate:
230
+ "Diagnostic redaction cannot prove safe output for sensitive values.",
231
+ remediationTemplate:
232
+ "Ensure all sensitive locations are covered by a structured redactor.",
233
+ },
234
+ RS_PLAN_INVALID_PATH: {
235
+ code: "RS_PLAN_INVALID_PATH",
236
+ phase: "plan-validation",
237
+ defaultSeverity: "error",
238
+ blocking: true,
239
+ messageTemplate:
240
+ "An output path contains traversal, absolute prefix, NUL, or empty segment.",
241
+ remediationTemplate:
242
+ "Ensure all output paths are normalized: forward slashes, no traversal, no absolute prefix, NFC, no NUL.",
243
+ },
244
+ RS_PLAN_DUPLICATE_PATH: {
245
+ code: "RS_PLAN_DUPLICATE_PATH",
246
+ phase: "plan-validation",
247
+ defaultSeverity: "error",
248
+ blocking: true,
249
+ messageTemplate: "Duplicate normalized output paths detected in the plan.",
250
+ remediationTemplate:
251
+ "Remove or rename output files so each normalized path is unique.",
252
+ },
253
+ RS_PLAN_ORPHAN_OPERATION: {
254
+ code: "RS_PLAN_ORPHAN_OPERATION",
255
+ phase: "plan-validation",
256
+ defaultSeverity: "error",
257
+ blocking: true,
258
+ messageTemplate:
259
+ "An operation references a non-existent output file index.",
260
+ remediationTemplate:
261
+ "Ensure every operation's outputFileIndex points to a valid entry in outputFiles.",
262
+ },
263
+ RS_PLAN_ORPHAN_FILE: {
264
+ code: "RS_PLAN_ORPHAN_FILE",
265
+ phase: "plan-validation",
266
+ defaultSeverity: "error",
267
+ blocking: true,
268
+ messageTemplate: "An output file has no corresponding write operation.",
269
+ remediationTemplate:
270
+ "Add a write operation for every output file or remove the unused file entry.",
271
+ },
272
+ RS_PLAN_SCHEMA_INVALID: {
273
+ code: "RS_PLAN_SCHEMA_INVALID",
274
+ phase: "plan-validation",
275
+ defaultSeverity: "error",
276
+ blocking: true,
277
+ messageTemplate: "The translation plan fails Zod schema validation.",
278
+ remediationTemplate:
279
+ "Check the plan structure against TranslationPlanSchema.",
280
+ },
281
+ RS_SENSITIVE_REJECTED: {
282
+ code: "RS_SENSITIVE_REJECTED",
283
+ phase: "redaction",
284
+ defaultSeverity: "error",
285
+ blocking: true,
286
+ messageTemplate:
287
+ "A literal secret was found in source content under a reject security policy.",
288
+ remediationTemplate:
289
+ "Remove the literal secret or switch to an approved reference pattern.",
290
+ },
291
+ RS_SENSITIVE_REFERENCE_INVALID: {
292
+ code: "RS_SENSITIVE_REFERENCE_INVALID",
293
+ phase: "redaction",
294
+ defaultSeverity: "error",
295
+ blocking: true,
296
+ messageTemplate:
297
+ "A sensitive-value reference does not match any approved reference pattern.",
298
+ remediationTemplate:
299
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: literal ${ENV_VAR} in user-facing message
300
+ "Use an approved reference syntax such as ${ENV_VAR} declared in the format contract.",
301
+ },
302
+ RS_PLAN_WITHHELD: {
303
+ code: "RS_PLAN_WITHHELD",
304
+ phase: "plan-validation",
305
+ defaultSeverity: "info",
306
+ blocking: false,
307
+ messageTemplate:
308
+ "The translation plan is withheld pending application policy approval.",
309
+ remediationTemplate:
310
+ "Supply an application policy that authorizes the reported diagnostic codes.",
311
+ },
312
+ RS_CANONICAL_MISSING_KNOWLEDGE_MD: {
313
+ code: "RS_CANONICAL_MISSING_KNOWLEDGE_MD",
314
+ phase: "source-validation",
315
+ defaultSeverity: "error",
316
+ blocking: true,
317
+ messageTemplate: "knowledge.md not found in the provided document set.",
318
+ remediationTemplate:
319
+ "Ensure the source documents include a file with path 'knowledge.md'.",
320
+ },
321
+ RS_CANONICAL_INVALID_FRONTMATTER: {
322
+ code: "RS_CANONICAL_INVALID_FRONTMATTER",
323
+ phase: "source-validation",
324
+ defaultSeverity: "error",
325
+ blocking: true,
326
+ messageTemplate:
327
+ "The frontmatter YAML in knowledge.md could not be parsed.",
328
+ remediationTemplate:
329
+ "Check the YAML frontmatter syntax in knowledge.md for grammar errors.",
330
+ },
331
+ RS_CANONICAL_INVALID_YAML: {
332
+ code: "RS_CANONICAL_INVALID_YAML",
333
+ phase: "source-validation",
334
+ defaultSeverity: "error",
335
+ blocking: true,
336
+ messageTemplate: "An auxiliary YAML file could not be parsed.",
337
+ remediationTemplate:
338
+ "Check the YAML syntax in the indicated auxiliary file.",
339
+ },
340
+ RS_CANONICAL_INVALID_BODY_OVERRIDE: {
341
+ code: "RS_CANONICAL_INVALID_BODY_OVERRIDE",
342
+ phase: "source-validation",
343
+ defaultSeverity: "warning",
344
+ blocking: false,
345
+ messageTemplate: "A body override file has an invalid harness name.",
346
+ remediationTemplate:
347
+ "Rename the file to use a supported harness identifier: body.<harness>.md.",
348
+ },
349
+ RS_CANONICAL_WORKFLOW_TRAVERSAL: {
350
+ code: "RS_CANONICAL_WORKFLOW_TRAVERSAL",
351
+ phase: "source-validation",
352
+ defaultSeverity: "error",
353
+ blocking: true,
354
+ messageTemplate: "A workflow path contains directory traversal.",
355
+ remediationTemplate:
356
+ "Remove '..' segments from workflow paths. Only forward relative paths are permitted.",
357
+ },
358
+ RS_CANONICAL_DUPLICATE_WORKFLOW: {
359
+ code: "RS_CANONICAL_DUPLICATE_WORKFLOW",
360
+ phase: "source-validation",
361
+ defaultSeverity: "error",
362
+ blocking: true,
363
+ messageTemplate: "Duplicate normalized workflow paths detected.",
364
+ remediationTemplate:
365
+ "Ensure each workflow file has a unique normalized path within workflows/.",
366
+ },
367
+ RS_EXTRA_FIELD_COLLISION: {
368
+ code: "RS_EXTRA_FIELD_COLLISION",
369
+ phase: "source-translation",
370
+ defaultSeverity: "error",
371
+ blocking: true,
372
+ messageTemplate:
373
+ "An extra field would collide with a canonical frontmatter key.",
374
+ remediationTemplate:
375
+ "Rename the extra field to avoid conflicting with known canonical frontmatter keys.",
376
+ },
377
+ RS_DEFAULT_APPLIED: {
378
+ code: "RS_DEFAULT_APPLIED",
379
+ phase: "source-translation",
380
+ defaultSeverity: "info",
381
+ blocking: false,
382
+ messageTemplate:
383
+ "A default canonical value was applied during source translation.",
384
+ remediationTemplate:
385
+ "Review the applied default and override if the source provides a value.",
386
+ },
387
+ RS_NORMALIZATION_APPLIED: {
388
+ code: "RS_NORMALIZATION_APPLIED",
389
+ phase: "source-translation",
390
+ defaultSeverity: "info",
391
+ blocking: false,
392
+ messageTemplate:
393
+ "A normalization rule was applied during source translation.",
394
+ remediationTemplate:
395
+ "Review the normalization to confirm the canonical representation is correct.",
396
+ },
397
+ RS_LIFECYCLE_DEPRECATED: {
398
+ code: "RS_LIFECYCLE_DEPRECATED",
399
+ phase: "registry",
400
+ defaultSeverity: "warning",
401
+ blocking: false,
402
+ messageTemplate: "The selected format has a deprecated lifecycle status.",
403
+ remediationTemplate:
404
+ "Migrate to the declared replacement format before the retired date.",
405
+ },
406
+ RS_DIRECTION_MISMATCH: {
407
+ code: "RS_DIRECTION_MISMATCH",
408
+ phase: "registry",
409
+ defaultSeverity: "error",
410
+ blocking: true,
411
+ messageTemplate:
412
+ "The requested direction is not supported by the selected format contract.",
413
+ remediationTemplate:
414
+ "Select a format that declares the required direction or use an alternative.",
415
+ },
416
+ } as const;
417
+
418
+ // --- Factory Options ---
419
+
420
+ export interface CreateDiagnosticOptions {
421
+ readonly formatId?: string;
422
+ /** Override the default message template */
423
+ readonly message?: string;
424
+ /** Override the default remediation template */
425
+ readonly remediation?: string;
426
+ readonly source?: SourceDiagnosticLocation;
427
+ readonly canonical?: CanonicalDiagnosticLocation;
428
+ readonly degradation?: DegradationDetail;
429
+ readonly unavailableDetails?: readonly string[];
430
+ /** Override the default severity from code metadata */
431
+ readonly severityOverride?: RosettaSeverity;
432
+ }
433
+
434
+ // --- Factory Functions ---
435
+
436
+ /**
437
+ * Create a valid TranslationDiagnostic from a known RS_ code.
438
+ *
439
+ * Messages and remediation come from trusted templates in the code registry.
440
+ * Callers may override with their own trusted strings but MUST NOT
441
+ * interpolate raw user/source payloads.
442
+ */
443
+ export function createDiagnostic(
444
+ code: string,
445
+ options: CreateDiagnosticOptions = {},
446
+ ): TranslationDiagnostic {
447
+ const metadata = DIAGNOSTIC_CODE_REGISTRY[code];
448
+ if (!metadata) {
449
+ // Fallback: produce a valid diagnostic with the unknown code
450
+ return {
451
+ code,
452
+ severity: options.severityOverride ?? "error",
453
+ phase: "request",
454
+ message: options.message ?? "Unknown diagnostic code.",
455
+ remediation:
456
+ options.remediation ?? "Report this unknown code to the maintainer.",
457
+ blocking: true,
458
+ unavailableDetails: options.unavailableDetails
459
+ ? [...options.unavailableDetails]
460
+ : [],
461
+ ...(options.formatId !== undefined && { formatId: options.formatId }),
462
+ ...(options.source !== undefined && { source: options.source }),
463
+ ...(options.canonical !== undefined && { canonical: options.canonical }),
464
+ ...(options.degradation !== undefined && {
465
+ degradation: options.degradation,
466
+ }),
467
+ };
468
+ }
469
+
470
+ return {
471
+ code: metadata.code,
472
+ severity: options.severityOverride ?? metadata.defaultSeverity,
473
+ phase: metadata.phase,
474
+ message: options.message ?? metadata.messageTemplate,
475
+ remediation: options.remediation ?? metadata.remediationTemplate,
476
+ blocking: metadata.blocking,
477
+ unavailableDetails: options.unavailableDetails
478
+ ? [...options.unavailableDetails]
479
+ : [],
480
+ ...(options.formatId !== undefined && { formatId: options.formatId }),
481
+ ...(options.source !== undefined && { source: options.source }),
482
+ ...(options.canonical !== undefined && { canonical: options.canonical }),
483
+ ...(options.degradation !== undefined && {
484
+ degradation: options.degradation,
485
+ }),
486
+ };
487
+ }
488
+
489
+ // --- Blocking Check ---
490
+
491
+ /**
492
+ * Returns whether a diagnostic code is classified as blocking.
493
+ * Unknown codes are treated as blocking by default.
494
+ */
495
+ export function isBlockingCode(code: string): boolean {
496
+ const metadata = DIAGNOSTIC_CODE_REGISTRY[code];
497
+ if (!metadata) {
498
+ return true;
499
+ }
500
+ return metadata.blocking;
501
+ }
502
+
503
+ // --- Safe Internal Error Conversion ---
504
+
505
+ /**
506
+ * Converts an unknown error to an RS_TRANSLATOR_INTERNAL diagnostic
507
+ * without leaking stack traces, error messages, or source content.
508
+ *
509
+ * Records only the error type name if it is a standard Error subclass.
510
+ */
511
+ export function convertInternalError(
512
+ error: unknown,
513
+ phase: TranslationPhase,
514
+ ): TranslationDiagnostic {
515
+ // Determine safe error type name without leaking content
516
+ let errorTypeName: string | undefined;
517
+ if (error instanceof Error) {
518
+ const name = error.constructor?.name;
519
+ if (name && name !== "Error") {
520
+ errorTypeName = name;
521
+ }
522
+ }
523
+
524
+ const unavailableDetails: string[] = [];
525
+ if (errorTypeName) {
526
+ unavailableDetails.push(`errorType: ${errorTypeName}`);
527
+ }
528
+
529
+ return {
530
+ code: "RS_TRANSLATOR_INTERNAL",
531
+ severity: "error",
532
+ phase,
533
+ message: "An internal translator error occurred.",
534
+ remediation: "Report this issue to the translator maintainer.",
535
+ blocking: true,
536
+ unavailableDetails,
537
+ };
538
+ }
539
+
540
+ // --- Registry Failure Factory ---
541
+
542
+ /**
543
+ * Creates a typed RegistryFailure when TranslationDiagnostic construction
544
+ * is unavailable during registration.
545
+ */
546
+ export function createRegistryFailure(message: string): RegistryFailure {
547
+ return {
548
+ code: "RS_REGISTRY_FAILURE",
549
+ message,
550
+ };
551
+ }
552
+
553
+ // --- Deterministic Diagnostic Sorting ---
554
+
555
+ /**
556
+ * Sort diagnostics deterministically by:
557
+ * 1. Severity (error first, then warning, then info)
558
+ * 2. Phase order (from TRANSLATION_PHASE_ORDER)
559
+ * 3. Source path (Unicode code-point order)
560
+ * 4. Source location (line, then column)
561
+ * 5. Code (Unicode code-point order)
562
+ * 6. Format identifier (Unicode code-point order)
563
+ */
564
+ export function sortDiagnostics(
565
+ diagnostics: readonly TranslationDiagnostic[],
566
+ ): TranslationDiagnostic[] {
567
+ return [...diagnostics].sort(compareDiagnostics);
568
+ }
569
+
570
+ function compareDiagnostics(
571
+ a: TranslationDiagnostic,
572
+ b: TranslationDiagnostic,
573
+ ): number {
574
+ // 1. Severity order (error < warning < info)
575
+ const sevDiff = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
576
+ if (sevDiff !== 0) return sevDiff;
577
+
578
+ // 2. Phase order
579
+ const phaseA = PHASE_INDEX.get(a.phase) ?? TRANSLATION_PHASE_ORDER.length;
580
+ const phaseB = PHASE_INDEX.get(b.phase) ?? TRANSLATION_PHASE_ORDER.length;
581
+ const phaseDiff = phaseA - phaseB;
582
+ if (phaseDiff !== 0) return phaseDiff;
583
+
584
+ // 3. Source path (code-point comparison)
585
+ const pathA = a.source?.path ?? "";
586
+ const pathB = b.source?.path ?? "";
587
+ if (pathA < pathB) return -1;
588
+ if (pathA > pathB) return 1;
589
+
590
+ // 4. Source location (line, then column)
591
+ const lineA = a.source?.line ?? 0;
592
+ const lineB = b.source?.line ?? 0;
593
+ if (lineA !== lineB) return lineA - lineB;
594
+
595
+ const colA = a.source?.column ?? 0;
596
+ const colB = b.source?.column ?? 0;
597
+ if (colA !== colB) return colA - colB;
598
+
599
+ // 5. Code (code-point comparison)
600
+ if (a.code < b.code) return -1;
601
+ if (a.code > b.code) return 1;
602
+
603
+ // 6. Format identifier (code-point comparison)
604
+ const fmtA = a.formatId ?? "";
605
+ const fmtB = b.formatId ?? "";
606
+ if (fmtA < fmtB) return -1;
607
+ if (fmtA > fmtB) return 1;
608
+
609
+ return 0;
610
+ }
611
+
612
+ // --- Blocking Diagnostic Filters ---
613
+
614
+ /**
615
+ * Returns only the diagnostics that are classified as blocking.
616
+ */
617
+ export function getBlockingDiagnostics(
618
+ diagnostics: readonly TranslationDiagnostic[],
619
+ ): TranslationDiagnostic[] {
620
+ return diagnostics.filter((d) => d.blocking);
621
+ }
622
+
623
+ /**
624
+ * Returns true if the diagnostic set contains at least one blocking diagnostic.
625
+ */
626
+ export function hasBlockingDiagnostics(
627
+ diagnostics: readonly TranslationDiagnostic[],
628
+ ): boolean {
629
+ return diagnostics.some((d) => d.blocking);
630
+ }