@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.
- package/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/bridge/mcp-server.cjs +14171 -0
- package/package.json +98 -0
- package/src/adapters/capabilities.ts +178 -0
- package/src/adapters/claude-code.ts +110 -0
- package/src/adapters/cline.ts +98 -0
- package/src/adapters/codex.ts +173 -0
- package/src/adapters/copilot.ts +106 -0
- package/src/adapters/cursor.ts +97 -0
- package/src/adapters/degradation.ts +95 -0
- package/src/adapters/index.ts +324 -0
- package/src/adapters/kiro-frontmatter.ts +139 -0
- package/src/adapters/kiro-inclusion.ts +86 -0
- package/src/adapters/kiro.ts +412 -0
- package/src/adapters/qdeveloper.ts +115 -0
- package/src/adapters/types.ts +81 -0
- package/src/adapters/windsurf.ts +96 -0
- package/src/admin.ts +283 -0
- package/src/asset-conventions.ts +118 -0
- package/src/attribution-backfill.ts +319 -0
- package/src/attribution-report.ts +95 -0
- package/src/attribution.ts +239 -0
- package/src/backends/github.ts +194 -0
- package/src/backends/http.ts +122 -0
- package/src/backends/index.ts +39 -0
- package/src/backends/local.ts +47 -0
- package/src/backends/s3.ts +157 -0
- package/src/backends/types.ts +59 -0
- package/src/base-cache.ts +270 -0
- package/src/browse-ui.ts +3754 -0
- package/src/browse.ts +1038 -0
- package/src/build.ts +1108 -0
- package/src/catalog.ts +204 -0
- package/src/cli-deprecated.ts +29 -0
- package/src/cli.ts +773 -0
- package/src/collection-admin.ts +287 -0
- package/src/collection-builder.ts +464 -0
- package/src/collections.ts +116 -0
- package/src/compatibility.ts +105 -0
- package/src/config.ts +743 -0
- package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
- package/src/eval.ts +1169 -0
- package/src/file-writer.ts +61 -0
- package/src/format-registry.ts +141 -0
- package/src/guild/auto-updater.ts +163 -0
- package/src/guild/backend-resolver.ts +49 -0
- package/src/guild/cli.ts +592 -0
- package/src/guild/collection-expander.ts +47 -0
- package/src/guild/global-cache.ts +247 -0
- package/src/guild/hook-generator.ts +100 -0
- package/src/guild/manifest.ts +154 -0
- package/src/guild/path-utils.ts +12 -0
- package/src/guild/sync.ts +622 -0
- package/src/guild/version-resolver.ts +42 -0
- package/src/help/metadata.ts +445 -0
- package/src/help/renderer.ts +265 -0
- package/src/help/typo-suggester.ts +25 -0
- package/src/hooks/expression.ts +493 -0
- package/src/hooks/pipeline.ts +141 -0
- package/src/import.ts +773 -0
- package/src/importers/claude-code.ts +134 -0
- package/src/importers/cline.ts +103 -0
- package/src/importers/codex.ts +140 -0
- package/src/importers/copilot.ts +103 -0
- package/src/importers/cursor.ts +105 -0
- package/src/importers/index.ts +390 -0
- package/src/importers/kiro.ts +110 -0
- package/src/importers/qdeveloper.ts +103 -0
- package/src/importers/types.ts +54 -0
- package/src/importers/windsurf.ts +104 -0
- package/src/install.ts +1005 -0
- package/src/manifest-admin.ts +306 -0
- package/src/mcp-bridge.ts +240 -0
- package/src/mutation/delta.ts +50 -0
- package/src/mutation/history.ts +66 -0
- package/src/mutation/operators.ts +524 -0
- package/src/mutation/runner.ts +332 -0
- package/src/new.ts +106 -0
- package/src/outcomes/collision.ts +127 -0
- package/src/outcomes/normalize.ts +208 -0
- package/src/outcomes/registry.ts +173 -0
- package/src/parser.ts +446 -0
- package/src/provenance-backfill-cli.ts +319 -0
- package/src/provenance-backfill.ts +520 -0
- package/src/publish.ts +354 -0
- package/src/reconcile-orchestrator.ts +502 -0
- package/src/reconcile-report-renderer.ts +176 -0
- package/src/resolve-body.ts +15 -0
- package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
- package/src/rosetta/builtins/contracts.ts +1033 -0
- package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
- package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
- package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
- package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
- package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
- package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
- package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
- package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
- package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
- package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
- package/src/rosetta/builtins/sources/cline-native.ts +176 -0
- package/src/rosetta/builtins/sources/codex-native.ts +343 -0
- package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
- package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
- package/src/rosetta/builtins/sources/index.ts +95 -0
- package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
- package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
- package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
- package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
- package/src/rosetta/builtins/sources/superpowers.ts +240 -0
- package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
- package/src/rosetta/builtins/targets/claude-code.ts +181 -0
- package/src/rosetta/builtins/targets/cline.ts +87 -0
- package/src/rosetta/builtins/targets/codex.ts +226 -0
- package/src/rosetta/builtins/targets/copilot.ts +103 -0
- package/src/rosetta/builtins/targets/cursor.ts +87 -0
- package/src/rosetta/builtins/targets/index.ts +60 -0
- package/src/rosetta/builtins/targets/kiro.ts +278 -0
- package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
- package/src/rosetta/builtins/targets/windsurf.ts +87 -0
- package/src/rosetta/canonical.ts +729 -0
- package/src/rosetta/compatibility.ts +432 -0
- package/src/rosetta/contracts.ts +329 -0
- package/src/rosetta/detector.ts +724 -0
- package/src/rosetta/diagnostics.ts +630 -0
- package/src/rosetta/engine-bootstrap.ts +103 -0
- package/src/rosetta/engine.ts +744 -0
- package/src/rosetta/index.ts +381 -0
- package/src/rosetta/inspection.ts +530 -0
- package/src/rosetta/plan.ts +448 -0
- package/src/rosetta/provenance-digest.ts +369 -0
- package/src/rosetta/reconcile.ts +812 -0
- package/src/rosetta/redaction.ts +467 -0
- package/src/rosetta/registry.ts +712 -0
- package/src/rosetta/renderers.ts +571 -0
- package/src/rosetta/request-guard.ts +335 -0
- package/src/rosetta/resolution.ts +419 -0
- package/src/rosetta/source-accounting.ts +233 -0
- package/src/rosetta/templates.ts +129 -0
- package/src/rosetta-cli.ts +717 -0
- package/src/rosetta-docs-generator.ts +793 -0
- package/src/rosetta-profiles-cli.ts +367 -0
- package/src/schemas.ts +1712 -0
- package/src/spec-coordination.ts +1141 -0
- package/src/temper.ts +747 -0
- package/src/template-bundle-loader.ts +312 -0
- package/src/template-engine.ts +53 -0
- package/src/translation-application-policy.ts +496 -0
- package/src/translation-orchestrator.ts +1013 -0
- package/src/translation-plan-applier.ts +473 -0
- package/src/tutorial.ts +305 -0
- package/src/validate.ts +1093 -0
- package/src/versioning.ts +553 -0
- package/src/wizard.ts +660 -0
- package/src/workspace.ts +237 -0
- package/templates/eval-contexts/claude-code.md.njk +6 -0
- package/templates/eval-contexts/cline.md.njk +6 -0
- package/templates/eval-contexts/copilot.md.njk +6 -0
- package/templates/eval-contexts/cursor.md.njk +6 -0
- package/templates/eval-contexts/kiro.md.njk +10 -0
- package/templates/eval-contexts/qdeveloper.md.njk +6 -0
- package/templates/eval-contexts/windsurf.md.njk +6 -0
- package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
- package/templates/harness-adapters/_base/base.md.njk +16 -0
- package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
- package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
- package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
- package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
- package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
- package/templates/harness-adapters/cline/hook.sh.njk +4 -0
- package/templates/harness-adapters/cline/mcp.json.njk +1 -0
- package/templates/harness-adapters/cline/rule.md.njk +1 -0
- package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
- package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
- package/templates/harness-adapters/codex/skill.md.njk +27 -0
- package/templates/harness-adapters/copilot/agents.md.njk +1 -0
- package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
- package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
- package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
- package/templates/harness-adapters/cursor/rule.md.njk +6 -0
- package/templates/harness-adapters/kiro/hook.json.njk +1 -0
- package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
- package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
- package/templates/harness-adapters/kiro/power.md.njk +12 -0
- package/templates/harness-adapters/kiro/steering.md.njk +16 -0
- package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
- package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
- package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
- package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
- package/templates/knowledge/hooks.yaml.njk +4 -0
- package/templates/knowledge/knowledge.md.njk +53 -0
- package/templates/knowledge/mcp-servers.yaml.njk +2 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rosetta Stone — Deterministic Inspection Models
|
|
3
|
+
*
|
|
4
|
+
* Builds a deterministic InspectionReport from resolved translation data.
|
|
5
|
+
* The report projects formats/variants, detection evidence, versions,
|
|
6
|
+
* canonical summaries, defaults, normalizations, option origins,
|
|
7
|
+
* compatibility/degradation counts, diagnostics, plan paths, collision data,
|
|
8
|
+
* and preview status.
|
|
9
|
+
*
|
|
10
|
+
* CONSTRAINTS:
|
|
11
|
+
* - NO filesystem, process, clock, random, Git, or network imports
|
|
12
|
+
* - Pure functions only
|
|
13
|
+
* - All output is deterministic — same input always produces same report
|
|
14
|
+
* - JSON-serializable (no functions, symbols, circular refs)
|
|
15
|
+
*
|
|
16
|
+
* Requirements: 7.8, 9.3, 9.6, 9.7, 9.8, 9.9
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
DegradationRecord,
|
|
21
|
+
DetectionCandidate,
|
|
22
|
+
Direction,
|
|
23
|
+
InspectionReportEnvelope,
|
|
24
|
+
JsonValue,
|
|
25
|
+
LifecycleStatus,
|
|
26
|
+
TranslationDiagnostic,
|
|
27
|
+
TranslationPlan,
|
|
28
|
+
} from "../schemas";
|
|
29
|
+
import { InspectionReportEnvelopeSchema } from "../schemas";
|
|
30
|
+
import { codePointCompare, compareDiagnostics, deepFreeze } from "./contracts";
|
|
31
|
+
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
33
|
+
// Sub-types
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
35
|
+
|
|
36
|
+
/** Summarized request details */
|
|
37
|
+
export interface RequestSummary {
|
|
38
|
+
readonly direction: Direction;
|
|
39
|
+
readonly sourceFormat?: string;
|
|
40
|
+
readonly targetFormat?: string;
|
|
41
|
+
readonly strict: boolean;
|
|
42
|
+
readonly dryRun: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Format detection results */
|
|
46
|
+
export interface DetectionSummary {
|
|
47
|
+
readonly candidates: readonly DetectionCandidateSummary[];
|
|
48
|
+
readonly selectedFormatId?: string;
|
|
49
|
+
readonly ambiguous: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Condensed detection candidate for the report */
|
|
53
|
+
export interface DetectionCandidateSummary {
|
|
54
|
+
readonly formatId: string;
|
|
55
|
+
readonly confidence: number;
|
|
56
|
+
readonly evidenceCount: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Selected format, variant, contract version, canonical version range, lifecycle */
|
|
60
|
+
export interface FormatSummary {
|
|
61
|
+
readonly formatId: string;
|
|
62
|
+
readonly variant?: string;
|
|
63
|
+
readonly contractVersion: string;
|
|
64
|
+
readonly canonicalVersionRange?: string;
|
|
65
|
+
readonly lifecycle: LifecycleStatus;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Canonical schema version, artifact summary */
|
|
69
|
+
export interface CanonicalSummary {
|
|
70
|
+
readonly schemaVersion: string;
|
|
71
|
+
readonly artifactName?: string;
|
|
72
|
+
readonly type?: string;
|
|
73
|
+
readonly harnesses: readonly string[];
|
|
74
|
+
readonly hookCount: number;
|
|
75
|
+
readonly mcpServerCount: number;
|
|
76
|
+
readonly workflowCount: number;
|
|
77
|
+
readonly bodyOverrideCount: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Resolved options with origins */
|
|
81
|
+
export interface OptionsSummary {
|
|
82
|
+
readonly effective: Readonly<Record<string, JsonValue>>;
|
|
83
|
+
readonly origins: Readonly<
|
|
84
|
+
Record<string, "explicit" | "profile" | "canonical" | "contract-default">
|
|
85
|
+
>;
|
|
86
|
+
readonly defaults: Readonly<Record<string, JsonValue>>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Compatibility and degradation summary */
|
|
90
|
+
export interface CompatibilitySummary {
|
|
91
|
+
readonly fullCount: number;
|
|
92
|
+
readonly partialCount: number;
|
|
93
|
+
readonly noneCount: number;
|
|
94
|
+
readonly degradations: Readonly<Record<string, string>>;
|
|
95
|
+
readonly strictPromoted: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Diagnostics grouped by severity, sorted deterministically */
|
|
99
|
+
export interface DiagnosticsSummary {
|
|
100
|
+
readonly errorCount: number;
|
|
101
|
+
readonly warningCount: number;
|
|
102
|
+
readonly infoCount: number;
|
|
103
|
+
readonly blocking: readonly string[];
|
|
104
|
+
readonly diagnostics: readonly TranslationDiagnostic[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Output file paths, operation counts, application state */
|
|
108
|
+
export interface PlanSummary {
|
|
109
|
+
readonly outputFilePaths: readonly string[];
|
|
110
|
+
readonly operationCount: number;
|
|
111
|
+
readonly applicationState: "eligible" | "policy-required" | "withheld";
|
|
112
|
+
readonly policyDiagnosticCodes: readonly string[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Collision policy and outcomes */
|
|
116
|
+
export interface CollisionSummary {
|
|
117
|
+
readonly policy: string;
|
|
118
|
+
readonly collisionCount: number;
|
|
119
|
+
readonly outcomes: Readonly<Record<string, string>>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Content preview availability */
|
|
123
|
+
export interface PreviewStatus {
|
|
124
|
+
readonly available: boolean;
|
|
125
|
+
readonly reason?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
129
|
+
// InspectionReport — the deterministic report model
|
|
130
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
131
|
+
|
|
132
|
+
/** The deterministic inspection report model */
|
|
133
|
+
export interface InspectionReport {
|
|
134
|
+
readonly schemaVersion: "1.0";
|
|
135
|
+
readonly timestamp?: string;
|
|
136
|
+
readonly request: RequestSummary;
|
|
137
|
+
readonly detection?: DetectionSummary;
|
|
138
|
+
readonly format: FormatSummary;
|
|
139
|
+
readonly canonical: CanonicalSummary;
|
|
140
|
+
readonly options: OptionsSummary;
|
|
141
|
+
readonly compatibility: CompatibilitySummary;
|
|
142
|
+
readonly diagnostics: DiagnosticsSummary;
|
|
143
|
+
readonly plan: PlanSummary;
|
|
144
|
+
readonly collision: CollisionSummary;
|
|
145
|
+
readonly preview: PreviewStatus;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
149
|
+
// InspectionContext — inputs needed to build the report
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
/** All resolved translation data needed to build an InspectionReport */
|
|
153
|
+
export interface InspectionContext {
|
|
154
|
+
/** Resolved translation request info */
|
|
155
|
+
readonly request: {
|
|
156
|
+
readonly direction: Direction;
|
|
157
|
+
readonly sourceFormat?: string;
|
|
158
|
+
readonly targetFormat?: string;
|
|
159
|
+
readonly strict: boolean;
|
|
160
|
+
readonly dryRun: boolean;
|
|
161
|
+
};
|
|
162
|
+
/** Detection result (candidates, selection) */
|
|
163
|
+
readonly detection?: {
|
|
164
|
+
readonly candidates: readonly DetectionCandidate[];
|
|
165
|
+
readonly selectedFormatId?: string;
|
|
166
|
+
readonly ambiguous: boolean;
|
|
167
|
+
};
|
|
168
|
+
/** Resolved format contract */
|
|
169
|
+
readonly format: {
|
|
170
|
+
readonly formatId: string;
|
|
171
|
+
readonly contractVersion: string;
|
|
172
|
+
readonly canonicalVersionRange?: string;
|
|
173
|
+
readonly lifecycle: LifecycleStatus;
|
|
174
|
+
};
|
|
175
|
+
/** Resolved variant */
|
|
176
|
+
readonly variant?: {
|
|
177
|
+
readonly id: string;
|
|
178
|
+
};
|
|
179
|
+
/** Canonical artifact (if available) */
|
|
180
|
+
readonly artifact?: {
|
|
181
|
+
readonly schemaVersion: string;
|
|
182
|
+
readonly name?: string;
|
|
183
|
+
readonly type?: string;
|
|
184
|
+
readonly harnesses?: readonly string[];
|
|
185
|
+
readonly hookCount?: number;
|
|
186
|
+
readonly mcpServerCount?: number;
|
|
187
|
+
readonly workflowCount?: number;
|
|
188
|
+
readonly bodyOverrideCount?: number;
|
|
189
|
+
};
|
|
190
|
+
/** Resolved effective options with origins */
|
|
191
|
+
readonly options: {
|
|
192
|
+
readonly effective: Readonly<Record<string, JsonValue>>;
|
|
193
|
+
readonly origins: Readonly<
|
|
194
|
+
Record<string, "explicit" | "profile" | "canonical" | "contract-default">
|
|
195
|
+
>;
|
|
196
|
+
readonly defaults: Readonly<Record<string, JsonValue>>;
|
|
197
|
+
};
|
|
198
|
+
/** Compatibility evaluation result */
|
|
199
|
+
readonly compatibility?: {
|
|
200
|
+
readonly fullCount: number;
|
|
201
|
+
readonly partialCount: number;
|
|
202
|
+
readonly noneCount: number;
|
|
203
|
+
readonly degradations: readonly DegradationRecord[];
|
|
204
|
+
readonly strictPromoted: boolean;
|
|
205
|
+
};
|
|
206
|
+
/** All collected diagnostics */
|
|
207
|
+
readonly diagnostics: readonly TranslationDiagnostic[];
|
|
208
|
+
/** Translation plan (if produced) */
|
|
209
|
+
readonly plan?: TranslationPlan;
|
|
210
|
+
/** The collision policy used */
|
|
211
|
+
readonly collisionPolicy?: string;
|
|
212
|
+
/** Path→outcome map for collisions */
|
|
213
|
+
readonly collisionOutcomes?: Readonly<Record<string, string>>;
|
|
214
|
+
/** Whether content preview is safe */
|
|
215
|
+
readonly previewAvailable: boolean;
|
|
216
|
+
/** Reason string for preview unavailability */
|
|
217
|
+
readonly previewUnavailableReason?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
221
|
+
// buildInspectionReport — constructs a report from translation results
|
|
222
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Constructs an InspectionReport from resolved translation data.
|
|
226
|
+
*
|
|
227
|
+
* All sections are projected deterministically: sorted keys use
|
|
228
|
+
* code-point ordering, arrays are sorted deterministically, and the
|
|
229
|
+
* returned report is deeply frozen.
|
|
230
|
+
*/
|
|
231
|
+
export function buildInspectionReport(
|
|
232
|
+
context: InspectionContext,
|
|
233
|
+
): Readonly<InspectionReport> {
|
|
234
|
+
const request: RequestSummary = {
|
|
235
|
+
direction: context.request.direction,
|
|
236
|
+
...(context.request.sourceFormat !== undefined && {
|
|
237
|
+
sourceFormat: context.request.sourceFormat,
|
|
238
|
+
}),
|
|
239
|
+
...(context.request.targetFormat !== undefined && {
|
|
240
|
+
targetFormat: context.request.targetFormat,
|
|
241
|
+
}),
|
|
242
|
+
strict: context.request.strict,
|
|
243
|
+
dryRun: context.request.dryRun,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const detection = buildDetectionSummary(context.detection);
|
|
247
|
+
const format = buildFormatSummary(context.format, context.variant);
|
|
248
|
+
const canonical = buildCanonicalSummary(context.artifact);
|
|
249
|
+
const options = buildOptionsSummary(context.options);
|
|
250
|
+
const compatibility = buildCompatibilitySummary(context.compatibility);
|
|
251
|
+
const diagnostics = buildDiagnosticsSummary(context.diagnostics);
|
|
252
|
+
const plan = buildPlanSummary(context.plan);
|
|
253
|
+
const collision = buildCollisionSummary(
|
|
254
|
+
context.collisionPolicy,
|
|
255
|
+
context.collisionOutcomes,
|
|
256
|
+
);
|
|
257
|
+
const preview: PreviewStatus = {
|
|
258
|
+
available: context.previewAvailable,
|
|
259
|
+
...(context.previewUnavailableReason !== undefined && {
|
|
260
|
+
reason: context.previewUnavailableReason,
|
|
261
|
+
}),
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const report: InspectionReport = {
|
|
265
|
+
schemaVersion: "1.0",
|
|
266
|
+
request,
|
|
267
|
+
...(detection !== undefined && { detection }),
|
|
268
|
+
format,
|
|
269
|
+
canonical,
|
|
270
|
+
options,
|
|
271
|
+
compatibility,
|
|
272
|
+
diagnostics,
|
|
273
|
+
plan,
|
|
274
|
+
collision,
|
|
275
|
+
preview,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return deepFreeze(report) as Readonly<InspectionReport>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
282
|
+
// Internal projection helpers
|
|
283
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
284
|
+
|
|
285
|
+
function buildDetectionSummary(
|
|
286
|
+
detection: InspectionContext["detection"],
|
|
287
|
+
): DetectionSummary | undefined {
|
|
288
|
+
if (!detection) return undefined;
|
|
289
|
+
|
|
290
|
+
// Sort candidates by confidence descending, then formatId ascending (code-point)
|
|
291
|
+
const sortedCandidates = [...detection.candidates].sort((a, b) => {
|
|
292
|
+
const confDiff = b.confidence - a.confidence;
|
|
293
|
+
if (confDiff !== 0) return confDiff;
|
|
294
|
+
return codePointCompare(a.formatId, b.formatId);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const candidates: DetectionCandidateSummary[] = sortedCandidates.map((c) => ({
|
|
298
|
+
formatId: c.formatId,
|
|
299
|
+
confidence: c.confidence,
|
|
300
|
+
evidenceCount: c.evidence.length,
|
|
301
|
+
}));
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
candidates,
|
|
305
|
+
...(detection.selectedFormatId !== undefined && {
|
|
306
|
+
selectedFormatId: detection.selectedFormatId,
|
|
307
|
+
}),
|
|
308
|
+
ambiguous: detection.ambiguous,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function buildFormatSummary(
|
|
313
|
+
format: InspectionContext["format"],
|
|
314
|
+
variant: InspectionContext["variant"],
|
|
315
|
+
): FormatSummary {
|
|
316
|
+
return {
|
|
317
|
+
formatId: format.formatId,
|
|
318
|
+
...(variant?.id !== undefined && { variant: variant.id }),
|
|
319
|
+
contractVersion: format.contractVersion,
|
|
320
|
+
...(format.canonicalVersionRange !== undefined && {
|
|
321
|
+
canonicalVersionRange: format.canonicalVersionRange,
|
|
322
|
+
}),
|
|
323
|
+
lifecycle: format.lifecycle,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function buildCanonicalSummary(
|
|
328
|
+
artifact: InspectionContext["artifact"],
|
|
329
|
+
): CanonicalSummary {
|
|
330
|
+
if (!artifact) {
|
|
331
|
+
return {
|
|
332
|
+
schemaVersion: "1.0",
|
|
333
|
+
harnesses: [],
|
|
334
|
+
hookCount: 0,
|
|
335
|
+
mcpServerCount: 0,
|
|
336
|
+
workflowCount: 0,
|
|
337
|
+
bodyOverrideCount: 0,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Sort harnesses deterministically
|
|
342
|
+
const harnesses = [...(artifact.harnesses ?? [])].sort(codePointCompare);
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
schemaVersion: artifact.schemaVersion,
|
|
346
|
+
...(artifact.name !== undefined && { artifactName: artifact.name }),
|
|
347
|
+
...(artifact.type !== undefined && { type: artifact.type }),
|
|
348
|
+
harnesses,
|
|
349
|
+
hookCount: artifact.hookCount ?? 0,
|
|
350
|
+
mcpServerCount: artifact.mcpServerCount ?? 0,
|
|
351
|
+
workflowCount: artifact.workflowCount ?? 0,
|
|
352
|
+
bodyOverrideCount: artifact.bodyOverrideCount ?? 0,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function buildOptionsSummary(
|
|
357
|
+
options: InspectionContext["options"],
|
|
358
|
+
): OptionsSummary {
|
|
359
|
+
// Sort effective options keys by code-point order
|
|
360
|
+
const effectiveKeys = Object.keys(options.effective).sort(codePointCompare);
|
|
361
|
+
const effective: Record<string, JsonValue> = {};
|
|
362
|
+
for (const key of effectiveKeys) {
|
|
363
|
+
effective[key] = options.effective[key];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Sort origins keys by code-point order
|
|
367
|
+
const originsKeys = Object.keys(options.origins).sort(codePointCompare);
|
|
368
|
+
const origins: Record<
|
|
369
|
+
string,
|
|
370
|
+
"explicit" | "profile" | "canonical" | "contract-default"
|
|
371
|
+
> = {};
|
|
372
|
+
for (const key of originsKeys) {
|
|
373
|
+
origins[key] = options.origins[key];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Sort defaults keys by code-point order
|
|
377
|
+
const defaultsKeys = Object.keys(options.defaults).sort(codePointCompare);
|
|
378
|
+
const defaults: Record<string, JsonValue> = {};
|
|
379
|
+
for (const key of defaultsKeys) {
|
|
380
|
+
defaults[key] = options.defaults[key];
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return { effective, origins, defaults };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function buildCompatibilitySummary(
|
|
387
|
+
compat: InspectionContext["compatibility"],
|
|
388
|
+
): CompatibilitySummary {
|
|
389
|
+
if (!compat) {
|
|
390
|
+
return {
|
|
391
|
+
fullCount: 0,
|
|
392
|
+
partialCount: 0,
|
|
393
|
+
noneCount: 0,
|
|
394
|
+
degradations: {},
|
|
395
|
+
strictPromoted: false,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Build degradations map: capability → action, sorted by capability (code-point)
|
|
400
|
+
const degradationEntries = [...compat.degradations]
|
|
401
|
+
.map((d) => [d.capability, d.action] as const)
|
|
402
|
+
.sort((a, b) => codePointCompare(a[0], b[0]));
|
|
403
|
+
|
|
404
|
+
const degradations: Record<string, string> = {};
|
|
405
|
+
for (const [capability, action] of degradationEntries) {
|
|
406
|
+
degradations[capability] = action;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
fullCount: compat.fullCount,
|
|
411
|
+
partialCount: compat.partialCount,
|
|
412
|
+
noneCount: compat.noneCount,
|
|
413
|
+
degradations,
|
|
414
|
+
strictPromoted: compat.strictPromoted,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function buildDiagnosticsSummary(
|
|
419
|
+
diagnostics: readonly TranslationDiagnostic[],
|
|
420
|
+
): DiagnosticsSummary {
|
|
421
|
+
// Sort diagnostics deterministically
|
|
422
|
+
const sorted = [...diagnostics].sort(compareDiagnostics);
|
|
423
|
+
|
|
424
|
+
let errorCount = 0;
|
|
425
|
+
let warningCount = 0;
|
|
426
|
+
let infoCount = 0;
|
|
427
|
+
const blocking: string[] = [];
|
|
428
|
+
|
|
429
|
+
for (const d of sorted) {
|
|
430
|
+
switch (d.severity) {
|
|
431
|
+
case "error":
|
|
432
|
+
errorCount++;
|
|
433
|
+
break;
|
|
434
|
+
case "warning":
|
|
435
|
+
warningCount++;
|
|
436
|
+
break;
|
|
437
|
+
case "info":
|
|
438
|
+
infoCount++;
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
if (d.blocking && !blocking.includes(d.code)) {
|
|
442
|
+
blocking.push(d.code);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Sort blocking codes by code-point order
|
|
447
|
+
blocking.sort(codePointCompare);
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
errorCount,
|
|
451
|
+
warningCount,
|
|
452
|
+
infoCount,
|
|
453
|
+
blocking,
|
|
454
|
+
diagnostics: sorted,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function buildPlanSummary(plan: TranslationPlan | undefined): PlanSummary {
|
|
459
|
+
if (!plan) {
|
|
460
|
+
return {
|
|
461
|
+
outputFilePaths: [],
|
|
462
|
+
operationCount: 0,
|
|
463
|
+
applicationState: "withheld",
|
|
464
|
+
policyDiagnosticCodes: [],
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Sort output file paths deterministically by code-point order
|
|
469
|
+
const outputFilePaths = plan.outputFiles
|
|
470
|
+
.map((f) => f.relativePath)
|
|
471
|
+
.sort(codePointCompare);
|
|
472
|
+
|
|
473
|
+
// Sort policy diagnostic codes deterministically
|
|
474
|
+
const policyDiagnosticCodes = [...plan.policyDiagnosticCodes].sort(
|
|
475
|
+
codePointCompare,
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
return {
|
|
479
|
+
outputFilePaths,
|
|
480
|
+
operationCount: plan.operations.length,
|
|
481
|
+
applicationState: plan.applicationState,
|
|
482
|
+
policyDiagnosticCodes,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function buildCollisionSummary(
|
|
487
|
+
policy: string | undefined,
|
|
488
|
+
outcomes: Readonly<Record<string, string>> | undefined,
|
|
489
|
+
): CollisionSummary {
|
|
490
|
+
const resolvedPolicy = policy ?? "none";
|
|
491
|
+
const resolvedOutcomes = outcomes ?? {};
|
|
492
|
+
|
|
493
|
+
// Sort outcome keys by code-point order
|
|
494
|
+
const sortedKeys = Object.keys(resolvedOutcomes).sort(codePointCompare);
|
|
495
|
+
const sortedOutcomes: Record<string, string> = {};
|
|
496
|
+
for (const key of sortedKeys) {
|
|
497
|
+
sortedOutcomes[key] = resolvedOutcomes[key];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
policy: resolvedPolicy,
|
|
502
|
+
collisionCount: sortedKeys.length,
|
|
503
|
+
outcomes: sortedOutcomes,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
508
|
+
// validateInspectionReport — validates against InspectionReportEnvelopeSchema
|
|
509
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Validates an inspection report envelope against the InspectionReportEnvelopeSchema.
|
|
513
|
+
*
|
|
514
|
+
* Returns a result object with success status and either the validated data
|
|
515
|
+
* or an array of error messages.
|
|
516
|
+
*/
|
|
517
|
+
export function validateInspectionReport(
|
|
518
|
+
report: unknown,
|
|
519
|
+
):
|
|
520
|
+
| { success: true; data: InspectionReportEnvelope }
|
|
521
|
+
| { success: false; errors: string[] } {
|
|
522
|
+
const result = InspectionReportEnvelopeSchema.safeParse(report);
|
|
523
|
+
if (result.success) {
|
|
524
|
+
return { success: true, data: result.data };
|
|
525
|
+
}
|
|
526
|
+
const errors = result.error.issues.map(
|
|
527
|
+
(issue) => `${issue.path.join(".")}: ${issue.message}`,
|
|
528
|
+
);
|
|
529
|
+
return { success: false, errors };
|
|
530
|
+
}
|