@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,744 @@
1
+ /**
2
+ * Rosetta Stone — Translation Engine
3
+ *
4
+ * Coordinates request guard, registry resolution, format detection, source
5
+ * translation, canonical parsing/validation, compatibility evaluation, target
6
+ * translation, and plan validation across inbound, outbound, and transcode modes.
7
+ *
8
+ * Derives success/partial/failure status and eligible/policy-required/withheld
9
+ * application states. Converts unexpected implementation failures to redacted
10
+ * RS_TRANSLATOR_INTERNAL diagnostics — no stack or source content enters output.
11
+ *
12
+ * CONSTRAINTS:
13
+ * - NO filesystem, process, clock, random, Git, or network imports
14
+ * - Pure module — never applies effects
15
+ *
16
+ * Requirements: 1.1, 1.2, 4.6, 4.7, 4.8, 8.1, 8.7, 12.4, 12.5
17
+ */
18
+
19
+ import {
20
+ type AppliedDefault,
21
+ type AppliedNormalization,
22
+ type DegradationRecord,
23
+ type FormatContract,
24
+ type FormatIdentifier,
25
+ type KnowledgeArtifact,
26
+ KnowledgeArtifactSchema,
27
+ type TranslationDiagnostic,
28
+ type TranslationPlan,
29
+ type TranslationRequest,
30
+ type TranslationResult,
31
+ } from "../schemas";
32
+
33
+ import {
34
+ evaluateCompatibility,
35
+ identifyUsedCapabilities,
36
+ promoteInStrictMode,
37
+ resolveEffectiveProfile,
38
+ } from "./compatibility";
39
+ import type { DetectionRequest, DetectionResult } from "./detector";
40
+ import { detect } from "./detector";
41
+ import {
42
+ convertInternalError,
43
+ createDiagnostic,
44
+ getBlockingDiagnostics,
45
+ hasBlockingDiagnostics,
46
+ sortDiagnostics,
47
+ } from "./diagnostics";
48
+ import type { InspectionContext, InspectionReport } from "./inspection";
49
+ import { buildInspectionReport } from "./inspection";
50
+ import { validatePlan } from "./plan";
51
+ import type {
52
+ SourceTranslatorContext,
53
+ TargetTranslationOutput,
54
+ TargetTranslatorContext,
55
+ TranslationRegistrySnapshot,
56
+ } from "./registry";
57
+ import { guardRequest } from "./request-guard";
58
+ import { resolveVariant } from "./resolution";
59
+ import type { ImmutableTemplateBundle } from "./templates";
60
+
61
+ // ═══════════════════════════════════════════════════════════════════════════════
62
+ // RosettaStone Interface
63
+ // ═══════════════════════════════════════════════════════════════════════════════
64
+
65
+ /**
66
+ * The public RosettaStone translation interface.
67
+ */
68
+ export interface RosettaStone {
69
+ detect(request: DetectionRequest): DetectionResult;
70
+ translate(request: TranslationRequest): TranslationResult;
71
+ inspect(request: TranslationRequest): InspectionReport;
72
+ }
73
+
74
+ // ═══════════════════════════════════════════════════════════════════════════════
75
+ // RosettaEngine Class
76
+ // ═══════════════════════════════════════════════════════════════════════════════
77
+
78
+ /**
79
+ * The main Rosetta Stone translation engine.
80
+ *
81
+ * Coordinates all phases of translation without applying any effects.
82
+ * Returns diagnostics and plans; never writes, prompts, or performs I/O.
83
+ */
84
+ export class RosettaEngine implements RosettaStone {
85
+ private readonly registry: TranslationRegistrySnapshot;
86
+ private readonly templates: ImmutableTemplateBundle;
87
+
88
+ constructor(
89
+ registry: TranslationRegistrySnapshot,
90
+ templates: ImmutableTemplateBundle,
91
+ ) {
92
+ this.registry = registry;
93
+ this.templates = templates;
94
+ }
95
+
96
+ // ═══════════════════════════════════════════════════════════════════════════
97
+ // detect — format detection only
98
+ // ═══════════════════════════════════════════════════════════════════════════
99
+
100
+ detect(request: DetectionRequest): DetectionResult {
101
+ try {
102
+ return detect(request);
103
+ } catch (error: unknown) {
104
+ const diagnostic = convertInternalError(error, "detection");
105
+ return {
106
+ ok: false,
107
+ candidates: [],
108
+ diagnostics: [diagnostic],
109
+ };
110
+ }
111
+ }
112
+
113
+ // ═══════════════════════════════════════════════════════════════════════════
114
+ // translate — core translation pipeline
115
+ // ═══════════════════════════════════════════════════════════════════════════
116
+
117
+ translate(request: TranslationRequest): TranslationResult {
118
+ const diagnostics: TranslationDiagnostic[] = [];
119
+ const defaults: AppliedDefault[] = [];
120
+ const normalizations: AppliedNormalization[] = [];
121
+ const degradations: DegradationRecord[] = [];
122
+
123
+ let plan: TranslationPlan | undefined;
124
+ let canonical: KnowledgeArtifact | undefined;
125
+ let sourceFormatContract: FormatContract | undefined;
126
+ let targetFormatContract: FormatContract | undefined;
127
+ let _detectionResult: DetectionResult | undefined;
128
+
129
+ try {
130
+ // ─── Phase 1: Guard the request ───────────────────────────────
131
+ const guardResult = guardRequest(request);
132
+ if (!guardResult.ok) {
133
+ diagnostics.push(...guardResult.diagnostics);
134
+ return this.buildResult(
135
+ diagnostics,
136
+ defaults,
137
+ normalizations,
138
+ degradations,
139
+ plan,
140
+ canonical,
141
+ sourceFormatContract,
142
+ targetFormatContract,
143
+ );
144
+ }
145
+ const guarded = guardResult.request;
146
+
147
+ // ─── Phase 2: Resolve format from registry ────────────────────
148
+ if (guarded.mode === "inbound" || guarded.mode === "transcode") {
149
+ const sourceResolution = this.resolveSourceFormat(guarded);
150
+ diagnostics.push(...sourceResolution.diagnostics);
151
+ if (sourceResolution.detection) {
152
+ _detectionResult = sourceResolution.detection;
153
+ }
154
+ if (!sourceResolution.contract) {
155
+ return this.buildResult(
156
+ diagnostics,
157
+ defaults,
158
+ normalizations,
159
+ degradations,
160
+ plan,
161
+ canonical,
162
+ sourceFormatContract,
163
+ targetFormatContract,
164
+ );
165
+ }
166
+ sourceFormatContract = sourceResolution.contract;
167
+ }
168
+
169
+ if (guarded.mode === "outbound" || guarded.mode === "transcode") {
170
+ const targetResolution = this.registry.resolve(
171
+ guarded.target.formatId,
172
+ "target",
173
+ );
174
+ if (!targetResolution.ok) {
175
+ diagnostics.push(...targetResolution.diagnostics);
176
+ return this.buildResult(
177
+ diagnostics,
178
+ defaults,
179
+ normalizations,
180
+ degradations,
181
+ plan,
182
+ canonical,
183
+ sourceFormatContract,
184
+ targetFormatContract,
185
+ );
186
+ }
187
+ diagnostics.push(...targetResolution.diagnostics);
188
+ targetFormatContract = targetResolution.contract;
189
+ }
190
+
191
+ // ─── Phase 3: Inbound — source translate + canonical validate ─
192
+ if (
193
+ (guarded.mode === "inbound" || guarded.mode === "transcode") &&
194
+ sourceFormatContract
195
+ ) {
196
+ const sourceResult = this.runSourceTranslation(
197
+ guarded,
198
+ sourceFormatContract,
199
+ );
200
+ diagnostics.push(...sourceResult.diagnostics);
201
+
202
+ if (hasBlockingDiagnostics(sourceResult.diagnostics)) {
203
+ return this.buildResult(
204
+ diagnostics,
205
+ defaults,
206
+ normalizations,
207
+ degradations,
208
+ plan,
209
+ canonical,
210
+ sourceFormatContract,
211
+ targetFormatContract,
212
+ );
213
+ }
214
+
215
+ // Parse and validate canonical
216
+ if (sourceResult.candidate) {
217
+ const canonicalResult = this.validateCanonical(
218
+ sourceResult.candidate,
219
+ );
220
+ diagnostics.push(...canonicalResult.diagnostics);
221
+
222
+ if (hasBlockingDiagnostics(canonicalResult.diagnostics)) {
223
+ return this.buildResult(
224
+ diagnostics,
225
+ defaults,
226
+ normalizations,
227
+ degradations,
228
+ plan,
229
+ canonical,
230
+ sourceFormatContract,
231
+ targetFormatContract,
232
+ );
233
+ }
234
+ canonical = canonicalResult.artifact;
235
+ }
236
+ }
237
+
238
+ // For outbound, the canonical artifact comes from the request
239
+ if (guarded.mode === "outbound") {
240
+ canonical = guarded.artifact as KnowledgeArtifact;
241
+ }
242
+
243
+ // ─── Phase 4: Outbound — compatibility + target translation ────
244
+ if (
245
+ (guarded.mode === "outbound" || guarded.mode === "transcode") &&
246
+ targetFormatContract &&
247
+ canonical
248
+ ) {
249
+ // Evaluate compatibility
250
+ const compatResult = this.runCompatibility(
251
+ canonical,
252
+ targetFormatContract,
253
+ guarded.strict,
254
+ );
255
+ diagnostics.push(...compatResult.diagnostics);
256
+ degradations.push(...compatResult.degradations);
257
+
258
+ if (hasBlockingDiagnostics(compatResult.diagnostics)) {
259
+ return this.buildResult(
260
+ diagnostics,
261
+ defaults,
262
+ normalizations,
263
+ degradations,
264
+ plan,
265
+ canonical,
266
+ sourceFormatContract,
267
+ targetFormatContract,
268
+ );
269
+ }
270
+
271
+ // Run target translation
272
+ const targetResult = this.runTargetTranslation(
273
+ canonical,
274
+ guarded,
275
+ targetFormatContract,
276
+ );
277
+ diagnostics.push(...targetResult.diagnostics);
278
+
279
+ if (hasBlockingDiagnostics(targetResult.diagnostics)) {
280
+ return this.buildResult(
281
+ diagnostics,
282
+ defaults,
283
+ normalizations,
284
+ degradations,
285
+ plan,
286
+ canonical,
287
+ sourceFormatContract,
288
+ targetFormatContract,
289
+ );
290
+ }
291
+
292
+ plan = targetResult.plan;
293
+ }
294
+
295
+ // ─── Phase 5: Validate the plan ───────────────────────────────
296
+ if (plan) {
297
+ const planResult = validatePlan(plan);
298
+ diagnostics.push(...planResult.diagnostics);
299
+
300
+ if (!planResult.valid) {
301
+ plan = undefined;
302
+ } else if (planResult.plan) {
303
+ plan = planResult.plan;
304
+ }
305
+ }
306
+
307
+ // ─── Phase 6: Determine application state ─────────────────────
308
+ return this.buildResult(
309
+ diagnostics,
310
+ defaults,
311
+ normalizations,
312
+ degradations,
313
+ plan,
314
+ canonical,
315
+ sourceFormatContract,
316
+ targetFormatContract,
317
+ );
318
+ } catch (error: unknown) {
319
+ // Unexpected implementation failure — convert to redacted diagnostic
320
+ diagnostics.push(convertInternalError(error, "request"));
321
+ return this.buildResult(
322
+ diagnostics,
323
+ defaults,
324
+ normalizations,
325
+ degradations,
326
+ plan,
327
+ canonical,
328
+ sourceFormatContract,
329
+ targetFormatContract,
330
+ );
331
+ }
332
+ }
333
+
334
+ // ═══════════════════════════════════════════════════════════════════════════
335
+ // inspect — builds inspection report from translation results
336
+ // ═══════════════════════════════════════════════════════════════════════════
337
+
338
+ inspect(request: TranslationRequest): InspectionReport {
339
+ try {
340
+ const result = this.translate(request);
341
+ const context = this.buildInspectionContext(request, result);
342
+ return buildInspectionReport(context);
343
+ } catch (error: unknown) {
344
+ // Even inspection failures are redacted
345
+ const diagnostic = convertInternalError(error, "request");
346
+ const fallbackContext: InspectionContext = {
347
+ request: {
348
+ direction: request.mode === "outbound" ? "target" : "source",
349
+ strict: request.strict,
350
+ dryRun: true,
351
+ },
352
+ diagnostics: [diagnostic],
353
+ options: { effective: {}, origins: {}, defaults: {} },
354
+ previewAvailable: false,
355
+ previewUnavailableReason: "Internal error during inspection",
356
+ format: {
357
+ formatId: "unknown",
358
+ contractVersion: "1.0",
359
+ lifecycle: "active",
360
+ },
361
+ };
362
+ return buildInspectionReport(fallbackContext);
363
+ }
364
+ }
365
+
366
+ // ═══════════════════════════════════════════════════════════════════════════
367
+ // Private Helpers
368
+ // ═══════════════════════════════════════════════════════════════════════════
369
+
370
+ /**
371
+ * Resolve source format: explicit or auto-detect.
372
+ */
373
+ private resolveSourceFormat(request: TranslationRequest): {
374
+ contract: FormatContract | undefined;
375
+ diagnostics: TranslationDiagnostic[];
376
+ detection?: DetectionResult;
377
+ } {
378
+ if (request.mode !== "inbound" && request.mode !== "transcode") {
379
+ return { contract: undefined, diagnostics: [] };
380
+ }
381
+
382
+ const source = request.source;
383
+ const explicitId = source.formatId as FormatIdentifier | undefined;
384
+
385
+ const detectionRequest: DetectionRequest = {
386
+ documents: request.sourceDocuments,
387
+ registrySnapshot: this.registry,
388
+ explicitFormatId: explicitId,
389
+ direction: "source",
390
+ };
391
+
392
+ const detectionResult = detect(detectionRequest);
393
+
394
+ if (!detectionResult.ok) {
395
+ return {
396
+ contract: undefined,
397
+ diagnostics: detectionResult.diagnostics,
398
+ detection: detectionResult,
399
+ };
400
+ }
401
+
402
+ // Resolve the selected format from the registry
403
+ const resolution = this.registry.resolve(
404
+ detectionResult.selected,
405
+ "source",
406
+ );
407
+ if (!resolution.ok) {
408
+ return {
409
+ contract: undefined,
410
+ diagnostics: [
411
+ ...detectionResult.diagnostics,
412
+ ...resolution.diagnostics,
413
+ ],
414
+ detection: detectionResult,
415
+ };
416
+ }
417
+
418
+ return {
419
+ contract: resolution.contract,
420
+ diagnostics: [...detectionResult.diagnostics, ...resolution.diagnostics],
421
+ detection: detectionResult,
422
+ };
423
+ }
424
+
425
+ /**
426
+ * Run source translation phase — delegates to the registered translator.
427
+ */
428
+ private runSourceTranslation(
429
+ request: TranslationRequest,
430
+ contract: FormatContract,
431
+ ): {
432
+ candidate?: Record<string, unknown>;
433
+ diagnostics: TranslationDiagnostic[];
434
+ } {
435
+ if (request.mode !== "inbound" && request.mode !== "transcode") {
436
+ return { diagnostics: [] };
437
+ }
438
+
439
+ const translator = this.registry.getSourceTranslator(contract.id);
440
+ if (!translator) {
441
+ return {
442
+ diagnostics: [
443
+ createDiagnostic("RS_TRANSLATOR_INTERNAL", {
444
+ formatId: contract.id,
445
+ message: `No source translator registered for format "${contract.id}".`,
446
+ }),
447
+ ],
448
+ };
449
+ }
450
+
451
+ const context: SourceTranslatorContext = {
452
+ format: contract,
453
+ canonicalSchemaVersion: request.canonicalSchemaVersion,
454
+ options: request.source.options ?? {},
455
+ callerContext: request.callerContext,
456
+ };
457
+
458
+ try {
459
+ const output = translator(request.sourceDocuments, context);
460
+ return {
461
+ candidate: output.candidate,
462
+ diagnostics: [...output.diagnostics],
463
+ };
464
+ } catch (error: unknown) {
465
+ return {
466
+ diagnostics: [convertInternalError(error, "source-translation")],
467
+ };
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Validate a candidate against the canonical schema using the canonical parser.
473
+ */
474
+ private validateCanonical(candidate: Record<string, unknown>): {
475
+ artifact: KnowledgeArtifact | undefined;
476
+ diagnostics: TranslationDiagnostic[];
477
+ } {
478
+ try {
479
+ const result = KnowledgeArtifactSchema.safeParse(candidate);
480
+ if (!result.success) {
481
+ const artifactName =
482
+ typeof candidate.name === "string" && candidate.name.length > 0
483
+ ? candidate.name
484
+ : "unknown";
485
+ const diagnostics: TranslationDiagnostic[] = result.error.issues.map(
486
+ (issue) =>
487
+ createDiagnostic("RS_CANONICAL_INVALID", {
488
+ message: `Canonical validation failed at "${issue.path.join(".")}": ${issue.message}`,
489
+ canonical: {
490
+ artifactName,
491
+ fieldPath: issue.path.join(".") || "root",
492
+ },
493
+ }),
494
+ );
495
+ return { artifact: undefined, diagnostics };
496
+ }
497
+ return { artifact: result.data as KnowledgeArtifact, diagnostics: [] };
498
+ } catch (error: unknown) {
499
+ return {
500
+ artifact: undefined,
501
+ diagnostics: [convertInternalError(error, "canonical-validation")],
502
+ };
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Run compatibility evaluation against the target format.
508
+ */
509
+ private runCompatibility(
510
+ artifact: KnowledgeArtifact,
511
+ contract: FormatContract,
512
+ strict: boolean,
513
+ ): {
514
+ diagnostics: TranslationDiagnostic[];
515
+ degradations: DegradationRecord[];
516
+ } {
517
+ try {
518
+ const profile = resolveEffectiveProfile(contract);
519
+ const usedCapabilities = identifyUsedCapabilities(artifact);
520
+ let evaluation = evaluateCompatibility(
521
+ profile,
522
+ usedCapabilities,
523
+ artifact,
524
+ );
525
+
526
+ if (strict) {
527
+ evaluation = promoteInStrictMode(evaluation);
528
+ }
529
+
530
+ return {
531
+ diagnostics: [...evaluation.diagnostics],
532
+ degradations: [...evaluation.degradations],
533
+ };
534
+ } catch (error: unknown) {
535
+ return {
536
+ diagnostics: [convertInternalError(error, "compatibility")],
537
+ degradations: [],
538
+ };
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Run target translation — delegates to the registered translator.
544
+ */
545
+ private runTargetTranslation(
546
+ artifact: KnowledgeArtifact,
547
+ request: TranslationRequest,
548
+ contract: FormatContract,
549
+ ): { plan?: TranslationPlan; diagnostics: TranslationDiagnostic[] } {
550
+ if (request.mode !== "outbound" && request.mode !== "transcode") {
551
+ return { diagnostics: [] };
552
+ }
553
+
554
+ const translator = this.registry.getTargetTranslator(contract.id);
555
+ if (!translator) {
556
+ return {
557
+ diagnostics: [
558
+ createDiagnostic("RS_TRANSLATOR_INTERNAL", {
559
+ formatId: contract.id,
560
+ message: `No target translator registered for format "${contract.id}".`,
561
+ }),
562
+ ],
563
+ };
564
+ }
565
+
566
+ // Resolve variant
567
+ const variantResolution = resolveVariant(contract, {
568
+ explicitVariant: request.target.variant,
569
+ contractDefault: contract.defaultVariant,
570
+ });
571
+
572
+ const context: TargetTranslatorContext = {
573
+ format: contract,
574
+ variant: variantResolution.variant ?? contract.defaultVariant ?? "",
575
+ canonicalSchemaVersion: request.canonicalSchemaVersion,
576
+ options: request.target.options ?? {},
577
+ callerContext: request.callerContext,
578
+ templates: this.templates,
579
+ };
580
+
581
+ try {
582
+ const output: TargetTranslationOutput = translator(
583
+ artifact as unknown as Record<string, unknown>,
584
+ context,
585
+ );
586
+ return {
587
+ plan: output.plan as TranslationPlan | undefined,
588
+ diagnostics: [...output.diagnostics],
589
+ };
590
+ } catch (error: unknown) {
591
+ return {
592
+ diagnostics: [convertInternalError(error, "target-translation")],
593
+ };
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Build the final TranslationResult with status and application state.
599
+ */
600
+ private buildResult(
601
+ diagnostics: TranslationDiagnostic[],
602
+ defaults: AppliedDefault[],
603
+ normalizations: AppliedNormalization[],
604
+ degradations: DegradationRecord[],
605
+ plan: TranslationPlan | undefined,
606
+ canonical: KnowledgeArtifact | undefined,
607
+ sourceFormat: FormatContract | undefined,
608
+ targetFormat: FormatContract | undefined,
609
+ ): TranslationResult {
610
+ const sorted = sortDiagnostics(diagnostics);
611
+ const blocking = getBlockingDiagnostics(sorted);
612
+ const hasBlocking = blocking.length > 0;
613
+
614
+ // Derive status
615
+ const status = hasBlocking
616
+ ? "failure"
617
+ : sorted.some((d) => d.severity === "warning")
618
+ ? "partial"
619
+ : "success";
620
+
621
+ // Derive application state
622
+ let applicationState: "eligible" | "policy-required" | "withheld";
623
+ if (hasBlocking || !plan) {
624
+ applicationState = "withheld";
625
+ } else if (sorted.some((d) => d.severity === "warning")) {
626
+ applicationState = "policy-required";
627
+ } else {
628
+ applicationState = "eligible";
629
+ }
630
+
631
+ // Update plan's applicationState if present
632
+ if (plan) {
633
+ plan = { ...plan, applicationState };
634
+ }
635
+
636
+ return {
637
+ schemaVersion: "1.0",
638
+ status,
639
+ registryVersion: this.registry.version,
640
+ ...(sourceFormat && {
641
+ sourceFormat: {
642
+ formatId: sourceFormat.id,
643
+ contractVersion: sourceFormat.contractVersion,
644
+ lifecycle: sourceFormat.lifecycle.status,
645
+ },
646
+ }),
647
+ ...(targetFormat && {
648
+ targetFormat: {
649
+ formatId: targetFormat.id,
650
+ contractVersion: targetFormat.contractVersion,
651
+ lifecycle: targetFormat.lifecycle.status,
652
+ },
653
+ }),
654
+ ...(canonical && { canonical }),
655
+ ...(plan && { plan }),
656
+ diagnostics: sorted,
657
+ defaults,
658
+ normalizations,
659
+ degradations,
660
+ };
661
+ }
662
+
663
+ /**
664
+ * Build InspectionContext from a request and its translation result.
665
+ */
666
+ private buildInspectionContext(
667
+ request: TranslationRequest,
668
+ result: TranslationResult,
669
+ ): InspectionContext {
670
+ const direction = request.mode === "outbound" ? "target" : "source";
671
+
672
+ return {
673
+ request: {
674
+ direction,
675
+ sourceFormat: result.sourceFormat?.formatId,
676
+ targetFormat: result.targetFormat?.formatId,
677
+ strict: request.strict,
678
+ dryRun: true,
679
+ },
680
+ format: {
681
+ formatId:
682
+ result.targetFormat?.formatId ??
683
+ result.sourceFormat?.formatId ??
684
+ "unknown",
685
+ contractVersion:
686
+ result.targetFormat?.contractVersion ??
687
+ result.sourceFormat?.contractVersion ??
688
+ "1.0",
689
+ lifecycle:
690
+ result.targetFormat?.lifecycle ??
691
+ result.sourceFormat?.lifecycle ??
692
+ "active",
693
+ },
694
+ artifact: result.canonical
695
+ ? {
696
+ schemaVersion: "1.0",
697
+ name: result.canonical.name,
698
+ type: result.canonical.frontmatter.type,
699
+ harnesses: result.canonical.frontmatter.harnesses,
700
+ hookCount: result.canonical.hooks.length,
701
+ mcpServerCount: result.canonical.mcpServers.length,
702
+ workflowCount: result.canonical.workflows.length,
703
+ bodyOverrideCount: Object.keys(result.canonical.bodyOverrides)
704
+ .length,
705
+ }
706
+ : undefined,
707
+ options: { effective: {}, origins: {}, defaults: {} },
708
+ compatibility:
709
+ result.degradations.length > 0
710
+ ? {
711
+ fullCount: 0,
712
+ partialCount: result.degradations.filter(
713
+ (d) => d.action !== "omit",
714
+ ).length,
715
+ noneCount: result.degradations.filter((d) => d.action === "omit")
716
+ .length,
717
+ degradations: result.degradations,
718
+ strictPromoted: request.strict,
719
+ }
720
+ : undefined,
721
+ diagnostics: result.diagnostics,
722
+ plan: result.plan,
723
+ previewAvailable: result.plan !== undefined,
724
+ previewUnavailableReason: result.plan ? undefined : "No plan produced",
725
+ };
726
+ }
727
+ }
728
+
729
+ // ═══════════════════════════════════════════════════════════════════════════════
730
+ // Factory Function
731
+ // ═══════════════════════════════════════════════════════════════════════════════
732
+
733
+ /**
734
+ * Create a new RosettaEngine instance.
735
+ *
736
+ * @param registry - Immutable registry snapshot with registered format contracts
737
+ * @param templates - Immutable template bundle for target translations
738
+ */
739
+ export function createEngine(
740
+ registry: TranslationRegistrySnapshot,
741
+ templates: ImmutableTemplateBundle,
742
+ ): RosettaEngine {
743
+ return new RosettaEngine(registry, templates);
744
+ }