@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
package/src/schemas.ts
ADDED
|
@@ -0,0 +1,1712 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { HARNESS_FORMAT_REGISTRY } from "./format-registry";
|
|
3
|
+
|
|
4
|
+
// --- Harness & Inclusion ---
|
|
5
|
+
|
|
6
|
+
export const SUPPORTED_HARNESSES = [
|
|
7
|
+
"kiro",
|
|
8
|
+
"claude-code",
|
|
9
|
+
"codex",
|
|
10
|
+
"copilot",
|
|
11
|
+
"cursor",
|
|
12
|
+
"windsurf",
|
|
13
|
+
"cline",
|
|
14
|
+
"qdeveloper",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
export const HarnessNameSchema = z.enum(SUPPORTED_HARNESSES);
|
|
18
|
+
export type HarnessName = z.infer<typeof HarnessNameSchema>;
|
|
19
|
+
|
|
20
|
+
export const InclusionModeSchema = z.enum([
|
|
21
|
+
"always",
|
|
22
|
+
"auto",
|
|
23
|
+
"fileMatch",
|
|
24
|
+
"manual",
|
|
25
|
+
]);
|
|
26
|
+
export type InclusionMode = z.infer<typeof InclusionModeSchema>;
|
|
27
|
+
|
|
28
|
+
// --- Kiro Progressive Inclusion ---
|
|
29
|
+
|
|
30
|
+
export const KiroProgressiveInclusionSchema = z.enum([
|
|
31
|
+
"always",
|
|
32
|
+
"fileMatch",
|
|
33
|
+
"manual",
|
|
34
|
+
]);
|
|
35
|
+
export type KiroProgressiveInclusion = z.infer<
|
|
36
|
+
typeof KiroProgressiveInclusionSchema
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
export const KiroHarnessConfigSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
format: z.enum(["steering", "power"]).optional(),
|
|
42
|
+
power: z.boolean().optional(),
|
|
43
|
+
inclusion: KiroProgressiveInclusionSchema.optional(),
|
|
44
|
+
fileMatchPattern: z.string().min(1).optional(),
|
|
45
|
+
progressiveWorkflowsStrict: z.boolean().optional(),
|
|
46
|
+
"spec-hooks": z.array(z.record(z.string(), z.unknown())).optional(),
|
|
47
|
+
})
|
|
48
|
+
.passthrough();
|
|
49
|
+
|
|
50
|
+
export const AssetTypeSchema = z.enum([
|
|
51
|
+
"skill",
|
|
52
|
+
// Deprecated alias for "skill" — "power" is Kiro's own output-format
|
|
53
|
+
// concept (harness-config.kiro.format: "power"), not a taxonomy value.
|
|
54
|
+
// Kept valid for backward compat only; canonical going forward is
|
|
55
|
+
// type: "skill" + an explicit harness-config.kiro.format. See ADR-0051.
|
|
56
|
+
"power",
|
|
57
|
+
"rule",
|
|
58
|
+
"workflow",
|
|
59
|
+
"agent",
|
|
60
|
+
"prompt",
|
|
61
|
+
"template",
|
|
62
|
+
"reference-pack",
|
|
63
|
+
]);
|
|
64
|
+
export type AssetType = z.infer<typeof AssetTypeSchema>;
|
|
65
|
+
|
|
66
|
+
// Backward-compat alias — existing imports of ArtifactTypeSchema continue to compile
|
|
67
|
+
export const ArtifactTypeSchema = AssetTypeSchema;
|
|
68
|
+
export type ArtifactType = AssetType;
|
|
69
|
+
|
|
70
|
+
// --- Bazaar Governance Enums ---
|
|
71
|
+
|
|
72
|
+
export const MaturitySchema = z.enum([
|
|
73
|
+
"experimental",
|
|
74
|
+
"beta",
|
|
75
|
+
"stable",
|
|
76
|
+
"deprecated",
|
|
77
|
+
]);
|
|
78
|
+
export type Maturity = z.infer<typeof MaturitySchema>;
|
|
79
|
+
|
|
80
|
+
export const TrustLaneSchema = z.enum([
|
|
81
|
+
"official",
|
|
82
|
+
"partner",
|
|
83
|
+
"community",
|
|
84
|
+
"experimental",
|
|
85
|
+
]);
|
|
86
|
+
export type TrustLane = z.infer<typeof TrustLaneSchema>;
|
|
87
|
+
|
|
88
|
+
export const RiskLevelSchema = z.enum(["low", "medium", "high"]);
|
|
89
|
+
export type RiskLevel = z.infer<typeof RiskLevelSchema>;
|
|
90
|
+
|
|
91
|
+
export const AudienceSchema = z.enum(["beginner", "intermediate", "advanced"]);
|
|
92
|
+
export type Audience = z.infer<typeof AudienceSchema>;
|
|
93
|
+
|
|
94
|
+
// --- Catalog Visibility & Priority (Req 4) ---
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Catalog visibility for an artifact or collection (Req 4.1).
|
|
98
|
+
* - public: listed everywhere (default)
|
|
99
|
+
* - private: excluded entirely from generated catalog.json
|
|
100
|
+
* - unlisted: included in catalog.json but hidden from default browse listings
|
|
101
|
+
*/
|
|
102
|
+
export const VisibilitySchema = z
|
|
103
|
+
.enum(["public", "private", "unlisted"])
|
|
104
|
+
.default("public");
|
|
105
|
+
export type Visibility = z.infer<typeof VisibilitySchema>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Catalog ordering priority (Req 4.2). Integer 1–100 inclusive, default 50.
|
|
109
|
+
* Higher values sort first in catalog listings.
|
|
110
|
+
*/
|
|
111
|
+
export const PrioritySchema = z.number().int().min(1).max(100).default(50);
|
|
112
|
+
export type Priority = z.infer<typeof PrioritySchema>;
|
|
113
|
+
|
|
114
|
+
// --- Collection Manifest ---
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Collection manifests define metadata only — no member list.
|
|
118
|
+
* Membership is declared by artifacts in their own frontmatter via `collections: [...]`.
|
|
119
|
+
*/
|
|
120
|
+
export const CollectionSchema = z.object({
|
|
121
|
+
name: z
|
|
122
|
+
.string()
|
|
123
|
+
.min(1)
|
|
124
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "Collection name must be kebab-case"),
|
|
125
|
+
displayName: z.string().min(1),
|
|
126
|
+
description: z.string().default(""),
|
|
127
|
+
version: z.string().default("0.1.0"),
|
|
128
|
+
author: z.string().default(""),
|
|
129
|
+
trust: TrustLaneSchema.optional(),
|
|
130
|
+
tags: z.array(z.string()).default([]),
|
|
131
|
+
harnesses: z.array(HarnessNameSchema).optional(),
|
|
132
|
+
visibility: VisibilitySchema.optional(),
|
|
133
|
+
priority: PrioritySchema.optional(),
|
|
134
|
+
});
|
|
135
|
+
export type Collection = z.infer<typeof CollectionSchema>;
|
|
136
|
+
|
|
137
|
+
// --- Canonical Events & Actions ---
|
|
138
|
+
|
|
139
|
+
export const CanonicalEventSchema = z.enum([
|
|
140
|
+
"file_edited",
|
|
141
|
+
"file_created",
|
|
142
|
+
"file_deleted",
|
|
143
|
+
"agent_stop",
|
|
144
|
+
"prompt_submit",
|
|
145
|
+
"pre_tool_use",
|
|
146
|
+
"post_tool_use",
|
|
147
|
+
"pre_task",
|
|
148
|
+
"post_task",
|
|
149
|
+
"user_triggered",
|
|
150
|
+
]);
|
|
151
|
+
export type CanonicalEvent = z.infer<typeof CanonicalEventSchema>;
|
|
152
|
+
|
|
153
|
+
export const CanonicalActionSchema = z.discriminatedUnion("type", [
|
|
154
|
+
z.object({ type: z.literal("ask_agent"), prompt: z.string().min(1) }),
|
|
155
|
+
z.object({ type: z.literal("run_command"), command: z.string().min(1) }),
|
|
156
|
+
]);
|
|
157
|
+
export type CanonicalAction = z.infer<typeof CanonicalActionSchema>;
|
|
158
|
+
|
|
159
|
+
// --- Canonical Hook ---
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A value that a DES-style hook may write into shared state (Req 3.5).
|
|
163
|
+
* Restricted to string or boolean so gate/postcondition expressions can
|
|
164
|
+
* compare against string/boolean literals deterministically.
|
|
165
|
+
*/
|
|
166
|
+
export const HookStateValueSchema = z.union([z.string(), z.boolean()]);
|
|
167
|
+
export type HookStateValue = z.infer<typeof HookStateValueSchema>;
|
|
168
|
+
|
|
169
|
+
export const CanonicalHookSchema = z.object({
|
|
170
|
+
name: z.string().min(1),
|
|
171
|
+
description: z.string().optional(),
|
|
172
|
+
event: CanonicalEventSchema,
|
|
173
|
+
condition: z
|
|
174
|
+
.object({
|
|
175
|
+
file_patterns: z.array(z.string()).optional(),
|
|
176
|
+
tool_types: z.array(z.string()).optional(),
|
|
177
|
+
})
|
|
178
|
+
.optional(),
|
|
179
|
+
action: CanonicalActionSchema,
|
|
180
|
+
// DES-style hook execution (Req 3). All optional — hooks without these
|
|
181
|
+
// fields behave exactly as before.
|
|
182
|
+
/** Boolean precondition expression; the action runs only when it holds (Req 3.1). */
|
|
183
|
+
gate: z.string().optional(),
|
|
184
|
+
/** Boolean expression checked after the action; failure halts the run (Req 3.3). */
|
|
185
|
+
postcondition: z.string().optional(),
|
|
186
|
+
/** State keys this hook writes, visible to later hooks' expressions (Req 3.5). */
|
|
187
|
+
state: z.record(z.string(), HookStateValueSchema).optional(),
|
|
188
|
+
});
|
|
189
|
+
export type CanonicalHook = z.infer<typeof CanonicalHookSchema>;
|
|
190
|
+
|
|
191
|
+
export const HooksFileSchema = z.array(CanonicalHookSchema);
|
|
192
|
+
|
|
193
|
+
// --- MCP Server Definition ---
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Stdio-based MCP server (command + args).
|
|
197
|
+
*/
|
|
198
|
+
export const StdioMcpServerSchema = z.object({
|
|
199
|
+
name: z.string().min(1),
|
|
200
|
+
transport: z.literal("stdio").default("stdio"),
|
|
201
|
+
command: z.string().min(1),
|
|
202
|
+
args: z.array(z.string()).default([]),
|
|
203
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
204
|
+
timeout: z.number().optional(),
|
|
205
|
+
autoApprove: z.array(z.string()).optional(),
|
|
206
|
+
disabled: z.boolean().optional(),
|
|
207
|
+
});
|
|
208
|
+
export type StdioMcpServer = z.infer<typeof StdioMcpServerSchema>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* URL-based MCP server (SSE or HTTP streamable).
|
|
212
|
+
*/
|
|
213
|
+
export const UrlMcpServerSchema = z.object({
|
|
214
|
+
name: z.string().min(1),
|
|
215
|
+
transport: z.enum(["sse", "http"]),
|
|
216
|
+
url: z.string().url(),
|
|
217
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
218
|
+
timeout: z.number().optional(),
|
|
219
|
+
autoApprove: z.array(z.string()).optional(),
|
|
220
|
+
disabled: z.boolean().optional(),
|
|
221
|
+
});
|
|
222
|
+
export type UrlMcpServer = z.infer<typeof UrlMcpServerSchema>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Preprocessor that infers transport from shape:
|
|
226
|
+
* - Has `url` → URL-based (default to "sse" if transport not specified)
|
|
227
|
+
* - Has `command` → stdio (default to "stdio" if transport not specified)
|
|
228
|
+
*/
|
|
229
|
+
const McpServerPreprocess = z.preprocess(
|
|
230
|
+
(val) => {
|
|
231
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
232
|
+
const obj = val as Record<string, unknown>;
|
|
233
|
+
if (!obj.transport) {
|
|
234
|
+
if ("url" in obj) {
|
|
235
|
+
return { ...obj, transport: "sse" };
|
|
236
|
+
}
|
|
237
|
+
return { ...obj, transport: "stdio" };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return val;
|
|
241
|
+
},
|
|
242
|
+
z.union([StdioMcpServerSchema, UrlMcpServerSchema]),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Union of stdio and URL-based MCP server definitions.
|
|
247
|
+
* Accepts objects without `transport` — infers from shape.
|
|
248
|
+
*/
|
|
249
|
+
export const McpServerDefinitionSchema = McpServerPreprocess;
|
|
250
|
+
export type McpServerDefinition = StdioMcpServer | UrlMcpServer;
|
|
251
|
+
|
|
252
|
+
/** Type guard: is this a stdio-based server? */
|
|
253
|
+
export function isStdioServer(
|
|
254
|
+
server: McpServerDefinition,
|
|
255
|
+
): server is StdioMcpServer {
|
|
256
|
+
// Handle objects that bypass Zod parsing (e.g. test fixtures without transport)
|
|
257
|
+
const s = server as Record<string, unknown>;
|
|
258
|
+
if (!s.transport || s.transport === "stdio") return "command" in s;
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Type guard: is this a URL-based server? */
|
|
263
|
+
export function isUrlServer(
|
|
264
|
+
server: McpServerDefinition,
|
|
265
|
+
): server is UrlMcpServer {
|
|
266
|
+
return server.transport === "sse" || server.transport === "http";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export const McpServersFileSchema = z.array(McpServerDefinitionSchema);
|
|
270
|
+
|
|
271
|
+
// --- Category Taxonomy ---
|
|
272
|
+
|
|
273
|
+
export const CATEGORIES = [
|
|
274
|
+
"testing",
|
|
275
|
+
"security",
|
|
276
|
+
"code-style",
|
|
277
|
+
"devops",
|
|
278
|
+
"documentation",
|
|
279
|
+
"architecture",
|
|
280
|
+
"debugging",
|
|
281
|
+
"performance",
|
|
282
|
+
"accessibility",
|
|
283
|
+
"writing",
|
|
284
|
+
] as const;
|
|
285
|
+
|
|
286
|
+
export const CategoryEnum = z.enum(CATEGORIES);
|
|
287
|
+
export type Category = z.infer<typeof CategoryEnum>;
|
|
288
|
+
|
|
289
|
+
// --- Outcomes Registry ---
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The kind of an outcome declaration.
|
|
293
|
+
* - specification: a declarative description of expected behavior
|
|
294
|
+
* - operation: an action/transformation with input → output shapes
|
|
295
|
+
* - invariant: a property that must hold
|
|
296
|
+
*/
|
|
297
|
+
export const OutcomeKindSchema = z.enum([
|
|
298
|
+
"specification",
|
|
299
|
+
"operation",
|
|
300
|
+
"invariant",
|
|
301
|
+
]);
|
|
302
|
+
export type OutcomeKind = z.infer<typeof OutcomeKindSchema>;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* A formal outcome declaration (Req 2B). Outcomes capture an artifact's
|
|
306
|
+
* intended input/output shapes plus keywords for two-tier collision detection.
|
|
307
|
+
* IDs must be globally unique kebab-case identifiers prefixed with `out-`.
|
|
308
|
+
*/
|
|
309
|
+
export const OutcomeSchema = z.object({
|
|
310
|
+
id: z
|
|
311
|
+
.string()
|
|
312
|
+
.regex(
|
|
313
|
+
/^out-[a-z0-9]+(-[a-z0-9]+)*$/,
|
|
314
|
+
"Outcome id must match out-kebab-case",
|
|
315
|
+
)
|
|
316
|
+
.max(64),
|
|
317
|
+
kind: OutcomeKindSchema,
|
|
318
|
+
inputShape: z.string().min(1),
|
|
319
|
+
outputShape: z.string().min(1),
|
|
320
|
+
summary: z.string().max(120),
|
|
321
|
+
keywords: z.array(z.string().max(24)).max(6).default([]),
|
|
322
|
+
related: z.array(z.string()).default([]),
|
|
323
|
+
});
|
|
324
|
+
export type Outcome = z.infer<typeof OutcomeSchema>;
|
|
325
|
+
|
|
326
|
+
// --- Frontmatter ---
|
|
327
|
+
|
|
328
|
+
export const FrontmatterSchema = z
|
|
329
|
+
.object({
|
|
330
|
+
name: z.string().min(1),
|
|
331
|
+
displayName: z.string().optional(),
|
|
332
|
+
description: z.string().default(""),
|
|
333
|
+
keywords: z.array(z.string()).default([]),
|
|
334
|
+
author: z.string().default(""),
|
|
335
|
+
version: z
|
|
336
|
+
.string()
|
|
337
|
+
.regex(
|
|
338
|
+
/^\d+\.\d+\.\d+$/,
|
|
339
|
+
"Version must be a valid semver string (e.g. 1.2.3)",
|
|
340
|
+
)
|
|
341
|
+
.default("0.1.0"),
|
|
342
|
+
migrations: z.boolean().optional(),
|
|
343
|
+
harnesses: z.array(HarnessNameSchema).default([...SUPPORTED_HARNESSES]),
|
|
344
|
+
type: ArtifactTypeSchema.default("skill"),
|
|
345
|
+
inclusion: InclusionModeSchema.default("always"),
|
|
346
|
+
file_patterns: z.array(z.string()).optional(),
|
|
347
|
+
categories: z.array(CategoryEnum).default([]),
|
|
348
|
+
ecosystem: z
|
|
349
|
+
.array(
|
|
350
|
+
z
|
|
351
|
+
.string()
|
|
352
|
+
.min(1)
|
|
353
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
354
|
+
)
|
|
355
|
+
.default([]),
|
|
356
|
+
depends: z
|
|
357
|
+
.array(
|
|
358
|
+
z
|
|
359
|
+
.string()
|
|
360
|
+
.min(1)
|
|
361
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
362
|
+
)
|
|
363
|
+
.default([]),
|
|
364
|
+
enhances: z
|
|
365
|
+
.array(
|
|
366
|
+
z
|
|
367
|
+
.string()
|
|
368
|
+
.min(1)
|
|
369
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
370
|
+
)
|
|
371
|
+
.default([]),
|
|
372
|
+
// Bazaar manifest fields
|
|
373
|
+
id: z
|
|
374
|
+
.string()
|
|
375
|
+
.regex(/^@[a-z0-9-]+\/[a-z0-9-]+$/)
|
|
376
|
+
.optional(),
|
|
377
|
+
license: z.string().optional(),
|
|
378
|
+
maturity: MaturitySchema.default("experimental"),
|
|
379
|
+
trust: TrustLaneSchema.optional(),
|
|
380
|
+
"risk-level": RiskLevelSchema.optional(),
|
|
381
|
+
audience: AudienceSchema.optional(),
|
|
382
|
+
"model-assumptions": z.array(z.string()).default([]),
|
|
383
|
+
successor: z.string().optional(),
|
|
384
|
+
replaces: z.string().optional(),
|
|
385
|
+
collections: z
|
|
386
|
+
.array(
|
|
387
|
+
z
|
|
388
|
+
.string()
|
|
389
|
+
.min(1)
|
|
390
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
391
|
+
)
|
|
392
|
+
.default([]),
|
|
393
|
+
"inherit-hooks": z.boolean().default(false),
|
|
394
|
+
visibility: VisibilitySchema.optional(),
|
|
395
|
+
priority: PrioritySchema.optional(),
|
|
396
|
+
outcomes: z.array(OutcomeSchema).default([]),
|
|
397
|
+
// Machine-managed distillation provenance (see ProvenanceRecordSchema).
|
|
398
|
+
// Written by the import/acquisition path, never hand-edited. Absent for
|
|
399
|
+
// artifacts authored from scratch. Defined later in this file, so we
|
|
400
|
+
// reference it lazily to avoid a temporal-dead-zone error.
|
|
401
|
+
provenance: z.lazy(() => ProvenanceRecordSchema).optional(),
|
|
402
|
+
// Curation-owned human/legal attribution (see AttributionRecordSchema and
|
|
403
|
+
// ADR-0064). Additive to `author` (which stays as the display string);
|
|
404
|
+
// captured by the import Attribution_Wizard, preserved across re-sync.
|
|
405
|
+
// Referenced lazily since AttributionRecordSchema is defined later.
|
|
406
|
+
attribution: z.lazy(() => AttributionRecordSchema).optional(),
|
|
407
|
+
})
|
|
408
|
+
.passthrough()
|
|
409
|
+
.superRefine((data, ctx) => {
|
|
410
|
+
const harnessConfig = data["harness-config"] as
|
|
411
|
+
| Record<string, Record<string, unknown>>
|
|
412
|
+
| undefined;
|
|
413
|
+
if (!harnessConfig || typeof harnessConfig !== "object") return;
|
|
414
|
+
|
|
415
|
+
for (const [harness, config] of Object.entries(harnessConfig)) {
|
|
416
|
+
if (!config || typeof config !== "object" || !("format" in config))
|
|
417
|
+
continue;
|
|
418
|
+
|
|
419
|
+
const registryEntry = HARNESS_FORMAT_REGISTRY[harness as HarnessName];
|
|
420
|
+
if (!registryEntry) continue;
|
|
421
|
+
|
|
422
|
+
const formatValue = config.format as string;
|
|
423
|
+
if (!registryEntry.formats.includes(formatValue)) {
|
|
424
|
+
ctx.addIssue({
|
|
425
|
+
code: z.ZodIssueCode.custom,
|
|
426
|
+
path: ["harness-config", harness, "format"],
|
|
427
|
+
message: `Invalid format "${formatValue}" for harness "${harness}". Valid values: ${registryEntry.formats.join(", ")}`,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Validate kiro-specific harness-config through KiroHarnessConfigSchema
|
|
433
|
+
const kiroConfig = harnessConfig.kiro;
|
|
434
|
+
if (kiroConfig && typeof kiroConfig === "object") {
|
|
435
|
+
const result = KiroHarnessConfigSchema.safeParse(kiroConfig);
|
|
436
|
+
if (!result.success) {
|
|
437
|
+
for (const issue of result.error.issues) {
|
|
438
|
+
ctx.addIssue({
|
|
439
|
+
code: z.ZodIssueCode.custom,
|
|
440
|
+
path: ["harness-config", "kiro", ...issue.path.map(String)],
|
|
441
|
+
message: issue.message,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
export type Frontmatter = z.infer<typeof FrontmatterSchema>;
|
|
448
|
+
|
|
449
|
+
// --- Workflow File ---
|
|
450
|
+
|
|
451
|
+
export const WorkflowFileSchema = z.object({
|
|
452
|
+
name: z.string(),
|
|
453
|
+
filename: z.string(),
|
|
454
|
+
content: z.string(),
|
|
455
|
+
});
|
|
456
|
+
export type WorkflowFile = z.infer<typeof WorkflowFileSchema>;
|
|
457
|
+
|
|
458
|
+
// --- Knowledge Artifact ---
|
|
459
|
+
|
|
460
|
+
export const KnowledgeArtifactSchema = z.object({
|
|
461
|
+
name: z.string().min(1),
|
|
462
|
+
frontmatter: FrontmatterSchema,
|
|
463
|
+
body: z.string(),
|
|
464
|
+
hooks: z.array(CanonicalHookSchema).default([]),
|
|
465
|
+
mcpServers: z.array(McpServerDefinitionSchema).default([]),
|
|
466
|
+
workflows: z.array(WorkflowFileSchema).default([]),
|
|
467
|
+
sourcePath: z.string(),
|
|
468
|
+
extraFields: z.record(z.string(), z.unknown()).default({}),
|
|
469
|
+
// Per-harness body overrides, keyed by harness name. Loaded from optional
|
|
470
|
+
// `body.<harness>.md` sibling files. Empty when no override files exist.
|
|
471
|
+
bodyOverrides: z.record(z.string(), z.string()).default({}),
|
|
472
|
+
});
|
|
473
|
+
export type KnowledgeArtifact = z.infer<typeof KnowledgeArtifactSchema>;
|
|
474
|
+
|
|
475
|
+
// --- Catalog ---
|
|
476
|
+
|
|
477
|
+
export const CatalogEntrySchema = z.object({
|
|
478
|
+
name: z.string(),
|
|
479
|
+
displayName: z.string(),
|
|
480
|
+
description: z.string(),
|
|
481
|
+
keywords: z.array(z.string()),
|
|
482
|
+
author: z.string(),
|
|
483
|
+
version: z.string(),
|
|
484
|
+
harnesses: z.array(HarnessNameSchema),
|
|
485
|
+
type: AssetTypeSchema,
|
|
486
|
+
path: z.string(),
|
|
487
|
+
evals: z.boolean().default(false),
|
|
488
|
+
categories: z.array(CategoryEnum),
|
|
489
|
+
ecosystem: z.array(z.string()),
|
|
490
|
+
depends: z.array(z.string()),
|
|
491
|
+
enhances: z.array(z.string()),
|
|
492
|
+
formatByHarness: z.record(z.string(), z.string()).optional(),
|
|
493
|
+
changelog: z.boolean().default(false),
|
|
494
|
+
migrations: z.boolean().default(false),
|
|
495
|
+
// Feature flags — derived from artifact content at catalog generation time
|
|
496
|
+
features: z
|
|
497
|
+
.object({
|
|
498
|
+
hooks: z.boolean().default(false),
|
|
499
|
+
mcp: z.boolean().default(false),
|
|
500
|
+
workflows: z.boolean().default(false),
|
|
501
|
+
conditionalInclusion: z.boolean().default(false),
|
|
502
|
+
})
|
|
503
|
+
.default(() => ({
|
|
504
|
+
hooks: false,
|
|
505
|
+
mcp: false,
|
|
506
|
+
workflows: false,
|
|
507
|
+
conditionalInclusion: false,
|
|
508
|
+
})),
|
|
509
|
+
// Bazaar manifest fields
|
|
510
|
+
id: z.string().optional(),
|
|
511
|
+
license: z.string().optional(),
|
|
512
|
+
maturity: MaturitySchema,
|
|
513
|
+
trust: TrustLaneSchema.optional(),
|
|
514
|
+
"risk-level": RiskLevelSchema.optional(),
|
|
515
|
+
audience: AudienceSchema.optional(),
|
|
516
|
+
"model-assumptions": z.array(z.string()),
|
|
517
|
+
successor: z.string().optional(),
|
|
518
|
+
replaces: z.string().optional(),
|
|
519
|
+
collections: z.array(z.string()).default([]),
|
|
520
|
+
// Projected human/legal attribution (ADR-0064). Copied verbatim from the
|
|
521
|
+
// artifact's curation-owned `attribution` block during catalog generation;
|
|
522
|
+
// powers the gallery relationship chip and the detail "Sources & credits".
|
|
523
|
+
attribution: z.lazy(() => AttributionRecordSchema).optional(),
|
|
524
|
+
// Catalog visibility & ordering (Req 4.4, 4.6)
|
|
525
|
+
visibility: VisibilitySchema,
|
|
526
|
+
priority: PrioritySchema,
|
|
527
|
+
// Outcomes registry — projected subset for external discovery (Req 2H.2)
|
|
528
|
+
outcomes: z
|
|
529
|
+
.array(
|
|
530
|
+
z.object({
|
|
531
|
+
id: z.string(),
|
|
532
|
+
kind: OutcomeKindSchema,
|
|
533
|
+
inputShape: z.string(),
|
|
534
|
+
outputShape: z.string(),
|
|
535
|
+
keywords: z.array(z.string()),
|
|
536
|
+
}),
|
|
537
|
+
)
|
|
538
|
+
.default([]),
|
|
539
|
+
});
|
|
540
|
+
export type CatalogEntry = z.infer<typeof CatalogEntrySchema>;
|
|
541
|
+
|
|
542
|
+
export const CatalogSchema = z.array(CatalogEntrySchema);
|
|
543
|
+
|
|
544
|
+
// --- Capability Matrix ---
|
|
545
|
+
|
|
546
|
+
export const SupportLevelSchema = z.enum(["full", "partial", "none"]);
|
|
547
|
+
export type SupportLevel = z.infer<typeof SupportLevelSchema>;
|
|
548
|
+
|
|
549
|
+
export const DegradationStrategySchema = z.enum(["inline", "comment", "omit"]);
|
|
550
|
+
export type DegradationStrategy = z.infer<typeof DegradationStrategySchema>;
|
|
551
|
+
|
|
552
|
+
export const CapabilityEntrySchema = z
|
|
553
|
+
.object({
|
|
554
|
+
support: SupportLevelSchema,
|
|
555
|
+
degradation: DegradationStrategySchema.optional(),
|
|
556
|
+
})
|
|
557
|
+
.refine(
|
|
558
|
+
(entry) => entry.support === "full" || entry.degradation !== undefined,
|
|
559
|
+
{ message: "Degradation strategy required when support is not 'full'" },
|
|
560
|
+
);
|
|
561
|
+
export type CapabilityEntry = z.infer<typeof CapabilityEntrySchema>;
|
|
562
|
+
|
|
563
|
+
// --- Validation ---
|
|
564
|
+
|
|
565
|
+
export const ValidationErrorSchema = z.object({
|
|
566
|
+
field: z.string(),
|
|
567
|
+
message: z.string(),
|
|
568
|
+
filePath: z.string(),
|
|
569
|
+
line: z.number().optional(),
|
|
570
|
+
});
|
|
571
|
+
export type ValidationError = z.infer<typeof ValidationErrorSchema>;
|
|
572
|
+
|
|
573
|
+
export const ValidationWarningSchema = z.object({
|
|
574
|
+
field: z.string(),
|
|
575
|
+
message: z.string(),
|
|
576
|
+
filePath: z.string(),
|
|
577
|
+
});
|
|
578
|
+
export type ValidationWarning = z.infer<typeof ValidationWarningSchema>;
|
|
579
|
+
|
|
580
|
+
export const ValidationResultSchema = z.object({
|
|
581
|
+
artifactName: z.string(),
|
|
582
|
+
valid: z.boolean(),
|
|
583
|
+
errors: z.array(ValidationErrorSchema),
|
|
584
|
+
warnings: z.array(ValidationWarningSchema).optional(),
|
|
585
|
+
});
|
|
586
|
+
export type ValidationResult = z.infer<typeof ValidationResultSchema>;
|
|
587
|
+
|
|
588
|
+
// --- Workspace Config ---
|
|
589
|
+
|
|
590
|
+
export const WorkspaceProjectSchema = z.object({
|
|
591
|
+
name: z.string().min(1),
|
|
592
|
+
root: z.string().min(1),
|
|
593
|
+
harnesses: z.array(HarnessNameSchema).min(1),
|
|
594
|
+
artifacts: z
|
|
595
|
+
.object({
|
|
596
|
+
include: z.array(z.string()).optional(),
|
|
597
|
+
exclude: z.array(z.string()).optional(),
|
|
598
|
+
})
|
|
599
|
+
.optional(),
|
|
600
|
+
overrides: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
601
|
+
});
|
|
602
|
+
export type WorkspaceProject = z.infer<typeof WorkspaceProjectSchema>;
|
|
603
|
+
|
|
604
|
+
export const WorkspaceConfigSchema = z.object({
|
|
605
|
+
knowledgeSources: z.array(z.string()).min(1),
|
|
606
|
+
sharedMcpServers: z.string().optional(),
|
|
607
|
+
defaults: z
|
|
608
|
+
.object({
|
|
609
|
+
harnesses: z.array(HarnessNameSchema).optional(),
|
|
610
|
+
buildOptions: z.record(z.string(), z.unknown()).optional(),
|
|
611
|
+
})
|
|
612
|
+
.optional(),
|
|
613
|
+
projects: z.array(WorkspaceProjectSchema).min(1),
|
|
614
|
+
});
|
|
615
|
+
export type WorkspaceConfig = z.infer<typeof WorkspaceConfigSchema>;
|
|
616
|
+
|
|
617
|
+
// --- Temper Output ---
|
|
618
|
+
|
|
619
|
+
export const TemperSectionSchema = z.object({
|
|
620
|
+
title: z.string(),
|
|
621
|
+
content: z.string(),
|
|
622
|
+
type: z.enum([
|
|
623
|
+
"system-prompt",
|
|
624
|
+
"steering",
|
|
625
|
+
"hooks",
|
|
626
|
+
"mcp-servers",
|
|
627
|
+
"degradation-report",
|
|
628
|
+
]),
|
|
629
|
+
});
|
|
630
|
+
export type TemperSection = z.infer<typeof TemperSectionSchema>;
|
|
631
|
+
|
|
632
|
+
export const TemperOutputSchema = z.object({
|
|
633
|
+
artifactName: z.string(),
|
|
634
|
+
harnessName: z.string(),
|
|
635
|
+
sections: z.array(TemperSectionSchema),
|
|
636
|
+
degradations: z.array(z.string()),
|
|
637
|
+
fileCount: z.number(),
|
|
638
|
+
hooksTranslated: z.number(),
|
|
639
|
+
hooksDegraded: z.number(),
|
|
640
|
+
mcpServers: z.array(z.string()),
|
|
641
|
+
});
|
|
642
|
+
export type TemperOutput = z.infer<typeof TemperOutputSchema>;
|
|
643
|
+
|
|
644
|
+
// --- Version Manifest ---
|
|
645
|
+
|
|
646
|
+
export const VersionManifestSchema = z.object({
|
|
647
|
+
artifactName: z.string().min(1),
|
|
648
|
+
version: z.string().regex(/^\d+\.\d+\.\d+$/),
|
|
649
|
+
harnessName: z.string().min(1),
|
|
650
|
+
sourcePath: z.string().min(1),
|
|
651
|
+
installedAt: z.string().datetime(),
|
|
652
|
+
files: z.array(z.string()),
|
|
653
|
+
});
|
|
654
|
+
export type VersionManifest = z.infer<typeof VersionManifestSchema>;
|
|
655
|
+
|
|
656
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
657
|
+
// Rosetta Stone — Public Schemas and Types
|
|
658
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
659
|
+
//
|
|
660
|
+
// All Rosetta Stone data shapes are defined here with Zod 4 and exported with
|
|
661
|
+
// inferred TypeScript types. Rosetta modules compose but never redefine these
|
|
662
|
+
// public schemas. Schemas use .strict() unless an explicit extension map exists.
|
|
663
|
+
//
|
|
664
|
+
// Requirements: 1.2, 2.4, 8.1, 8.2, 8.6, 13.1, 15.3
|
|
665
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
666
|
+
|
|
667
|
+
// --- Rosetta Primitives and Version Schemas ---
|
|
668
|
+
|
|
669
|
+
/** SemVer pattern for canonical schema versions */
|
|
670
|
+
const SEMVER_PATTERN =
|
|
671
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Validates a normalized relative path.
|
|
675
|
+
* Rules:
|
|
676
|
+
* - Uses `/` separator only
|
|
677
|
+
* - Unicode NFC normalized
|
|
678
|
+
* - No empty or `.` segments
|
|
679
|
+
* - No `..` (traversal)
|
|
680
|
+
* - No absolute/root/drive/UNC prefix
|
|
681
|
+
* - No NUL character
|
|
682
|
+
*/
|
|
683
|
+
function validateRelativePath(val: string, ctx: z.RefinementCtx): void {
|
|
684
|
+
if (val.length === 0) {
|
|
685
|
+
ctx.addIssue({
|
|
686
|
+
code: z.ZodIssueCode.custom,
|
|
687
|
+
message: "Path must not be empty",
|
|
688
|
+
});
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (val.includes("\0")) {
|
|
692
|
+
ctx.addIssue({
|
|
693
|
+
code: z.ZodIssueCode.custom,
|
|
694
|
+
message: "Path must not contain NUL character",
|
|
695
|
+
});
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
// Reject absolute/drive/UNC prefixes
|
|
699
|
+
if (val.startsWith("/") || val.startsWith("\\")) {
|
|
700
|
+
ctx.addIssue({
|
|
701
|
+
code: z.ZodIssueCode.custom,
|
|
702
|
+
message: "Path must not be absolute",
|
|
703
|
+
});
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (/^[A-Za-z]:/.test(val)) {
|
|
707
|
+
ctx.addIssue({
|
|
708
|
+
code: z.ZodIssueCode.custom,
|
|
709
|
+
message: "Path must not contain a drive prefix",
|
|
710
|
+
});
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (val.startsWith("\\\\") || val.startsWith("//")) {
|
|
714
|
+
ctx.addIssue({
|
|
715
|
+
code: z.ZodIssueCode.custom,
|
|
716
|
+
message: "Path must not be a UNC path",
|
|
717
|
+
});
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
// Check NFC normalization
|
|
721
|
+
if (val !== val.normalize("NFC")) {
|
|
722
|
+
ctx.addIssue({
|
|
723
|
+
code: z.ZodIssueCode.custom,
|
|
724
|
+
message: "Path must be Unicode NFC normalized",
|
|
725
|
+
});
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
// Only forward slashes
|
|
729
|
+
if (val.includes("\\")) {
|
|
730
|
+
ctx.addIssue({
|
|
731
|
+
code: z.ZodIssueCode.custom,
|
|
732
|
+
message: "Path must use '/' separator only",
|
|
733
|
+
});
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
// Check segments
|
|
737
|
+
const segments = val.split("/");
|
|
738
|
+
for (const seg of segments) {
|
|
739
|
+
if (seg === "") {
|
|
740
|
+
ctx.addIssue({
|
|
741
|
+
code: z.ZodIssueCode.custom,
|
|
742
|
+
message: "Path must not contain empty segments",
|
|
743
|
+
});
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (seg === ".") {
|
|
747
|
+
ctx.addIssue({
|
|
748
|
+
code: z.ZodIssueCode.custom,
|
|
749
|
+
message: "Path must not contain '.' segments",
|
|
750
|
+
});
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (seg === "..") {
|
|
754
|
+
ctx.addIssue({
|
|
755
|
+
code: z.ZodIssueCode.custom,
|
|
756
|
+
message: "Path must not contain '..' traversal",
|
|
757
|
+
});
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export const FormatIdentifierSchema = z
|
|
764
|
+
.string()
|
|
765
|
+
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
|
766
|
+
export type FormatIdentifier = z.infer<typeof FormatIdentifierSchema>;
|
|
767
|
+
|
|
768
|
+
export const NormalizedRelativePathSchema = z
|
|
769
|
+
.string()
|
|
770
|
+
.superRefine(validateRelativePath);
|
|
771
|
+
export type NormalizedRelativePath = z.infer<
|
|
772
|
+
typeof NormalizedRelativePathSchema
|
|
773
|
+
>;
|
|
774
|
+
|
|
775
|
+
export const ContractVersionSchema = z.literal("1.0");
|
|
776
|
+
export type ContractVersion = z.infer<typeof ContractVersionSchema>;
|
|
777
|
+
|
|
778
|
+
export const CanonicalSchemaVersionSchema = z.string().regex(SEMVER_PATTERN);
|
|
779
|
+
export type CanonicalSchemaVersion = z.infer<
|
|
780
|
+
typeof CanonicalSchemaVersionSchema
|
|
781
|
+
>;
|
|
782
|
+
|
|
783
|
+
export const LifecycleStatusSchema = z.enum([
|
|
784
|
+
"experimental",
|
|
785
|
+
"active",
|
|
786
|
+
"deprecated",
|
|
787
|
+
"retired",
|
|
788
|
+
]);
|
|
789
|
+
export type LifecycleStatus = z.infer<typeof LifecycleStatusSchema>;
|
|
790
|
+
|
|
791
|
+
export const DirectionSchema = z.enum(["source", "target", "bidirectional"]);
|
|
792
|
+
export type Direction = z.infer<typeof DirectionSchema>;
|
|
793
|
+
|
|
794
|
+
export const RosettaSeveritySchema = z.enum(["info", "warning", "error"]);
|
|
795
|
+
export type RosettaSeverity = z.infer<typeof RosettaSeveritySchema>;
|
|
796
|
+
|
|
797
|
+
// --- Recursive JSON Value ---
|
|
798
|
+
|
|
799
|
+
export type JsonValue =
|
|
800
|
+
| null
|
|
801
|
+
| boolean
|
|
802
|
+
| number
|
|
803
|
+
| string
|
|
804
|
+
| JsonValue[]
|
|
805
|
+
| { [key: string]: JsonValue };
|
|
806
|
+
|
|
807
|
+
export const JsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
|
|
808
|
+
z.union([
|
|
809
|
+
z.null(),
|
|
810
|
+
z.boolean(),
|
|
811
|
+
z.number().finite(),
|
|
812
|
+
z.string(),
|
|
813
|
+
z.array(JsonValueSchema),
|
|
814
|
+
z.record(z.string(), JsonValueSchema),
|
|
815
|
+
]),
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
// --- Source Documents ---
|
|
819
|
+
|
|
820
|
+
export const SourceDocumentSchema = z
|
|
821
|
+
.object({
|
|
822
|
+
path: NormalizedRelativePathSchema,
|
|
823
|
+
content: z.union([z.string(), z.instanceof(Uint8Array)]),
|
|
824
|
+
mediaType: z.string().optional(),
|
|
825
|
+
executable: z.boolean().default(false),
|
|
826
|
+
})
|
|
827
|
+
.strict();
|
|
828
|
+
export type SourceDocument = z.infer<typeof SourceDocumentSchema>;
|
|
829
|
+
export type SourceDocumentInput = z.input<typeof SourceDocumentSchema>;
|
|
830
|
+
|
|
831
|
+
// --- Lifecycle Metadata ---
|
|
832
|
+
|
|
833
|
+
export const LifecycleMetadataSchema = z
|
|
834
|
+
.object({
|
|
835
|
+
status: LifecycleStatusSchema,
|
|
836
|
+
introducedIn: CanonicalSchemaVersionSchema,
|
|
837
|
+
deprecatedIn: CanonicalSchemaVersionSchema.optional(),
|
|
838
|
+
retiredIn: CanonicalSchemaVersionSchema.optional(),
|
|
839
|
+
replacement: FormatIdentifierSchema.optional(),
|
|
840
|
+
})
|
|
841
|
+
.strict();
|
|
842
|
+
export type LifecycleMetadata = z.infer<typeof LifecycleMetadataSchema>;
|
|
843
|
+
|
|
844
|
+
// --- Canonical Version Range ---
|
|
845
|
+
|
|
846
|
+
export const CanonicalVersionRangeSchema = z
|
|
847
|
+
.object({
|
|
848
|
+
minInclusive: CanonicalSchemaVersionSchema,
|
|
849
|
+
maxExclusive: CanonicalSchemaVersionSchema,
|
|
850
|
+
})
|
|
851
|
+
.strict();
|
|
852
|
+
export type CanonicalVersionRange = z.infer<typeof CanonicalVersionRangeSchema>;
|
|
853
|
+
|
|
854
|
+
// --- Schema Reference ---
|
|
855
|
+
|
|
856
|
+
export const SchemaReferenceSchema = z
|
|
857
|
+
.object({
|
|
858
|
+
type: z.enum(["zod", "json-schema", "grammar", "none"]),
|
|
859
|
+
location: z.string().optional(),
|
|
860
|
+
description: z.string().optional(),
|
|
861
|
+
})
|
|
862
|
+
.strict();
|
|
863
|
+
export type SchemaReference = z.infer<typeof SchemaReferenceSchema>;
|
|
864
|
+
|
|
865
|
+
// --- Path Convention ---
|
|
866
|
+
|
|
867
|
+
export const PathConventionSchema = z
|
|
868
|
+
.object({
|
|
869
|
+
pattern: z.string().min(1),
|
|
870
|
+
required: z.boolean().default(false),
|
|
871
|
+
description: z.string().optional(),
|
|
872
|
+
})
|
|
873
|
+
.strict();
|
|
874
|
+
export type PathConvention = z.infer<typeof PathConventionSchema>;
|
|
875
|
+
|
|
876
|
+
// --- Detection Rule Kinds and Contract ---
|
|
877
|
+
|
|
878
|
+
export const DetectionRuleKindSchema = z.enum([
|
|
879
|
+
"path-glob",
|
|
880
|
+
"basename",
|
|
881
|
+
"extension",
|
|
882
|
+
"content-marker",
|
|
883
|
+
"frontmatter-key",
|
|
884
|
+
"json-pointer",
|
|
885
|
+
"yaml-key",
|
|
886
|
+
]);
|
|
887
|
+
export type DetectionRuleKind = z.infer<typeof DetectionRuleKindSchema>;
|
|
888
|
+
|
|
889
|
+
export const DetectionRuleSchema = z
|
|
890
|
+
.object({
|
|
891
|
+
id: z.string().min(1),
|
|
892
|
+
kind: DetectionRuleKindSchema,
|
|
893
|
+
pattern: z.string().min(1),
|
|
894
|
+
weight: z.number().int(),
|
|
895
|
+
required: z.boolean().default(false),
|
|
896
|
+
evidenceLabel: z.string().min(1),
|
|
897
|
+
maxParseBytes: z.number().int().positive().optional(),
|
|
898
|
+
})
|
|
899
|
+
.strict();
|
|
900
|
+
export type DetectionRule = z.infer<typeof DetectionRuleSchema>;
|
|
901
|
+
|
|
902
|
+
export const DetectionContractSchema = z
|
|
903
|
+
.object({
|
|
904
|
+
threshold: z.number().min(0).max(1),
|
|
905
|
+
rules: z.array(DetectionRuleSchema),
|
|
906
|
+
})
|
|
907
|
+
.strict();
|
|
908
|
+
export type DetectionContract = z.infer<typeof DetectionContractSchema>;
|
|
909
|
+
|
|
910
|
+
// --- Variant Contract ---
|
|
911
|
+
|
|
912
|
+
export const VariantContractSchema = z
|
|
913
|
+
.object({
|
|
914
|
+
id: FormatIdentifierSchema,
|
|
915
|
+
description: z.string().optional(),
|
|
916
|
+
pathConventions: z.array(PathConventionSchema).default([]),
|
|
917
|
+
defaults: z.record(z.string(), JsonValueSchema).default({}),
|
|
918
|
+
optionOverrides: z.record(z.string(), JsonValueSchema).default({}),
|
|
919
|
+
})
|
|
920
|
+
.strict();
|
|
921
|
+
export type VariantContract = z.infer<typeof VariantContractSchema>;
|
|
922
|
+
|
|
923
|
+
// --- Format Option Definition ---
|
|
924
|
+
|
|
925
|
+
export const FormatOptionDefinitionSchema = z
|
|
926
|
+
.object({
|
|
927
|
+
type: z.enum(["string", "boolean", "number", "enum"]),
|
|
928
|
+
description: z.string().min(1),
|
|
929
|
+
required: z.boolean().default(false),
|
|
930
|
+
defaultValue: JsonValueSchema.optional(),
|
|
931
|
+
enumValues: z.array(z.string()).optional(),
|
|
932
|
+
effective: z.boolean().default(true),
|
|
933
|
+
})
|
|
934
|
+
.strict();
|
|
935
|
+
export type FormatOptionDefinition = z.infer<
|
|
936
|
+
typeof FormatOptionDefinitionSchema
|
|
937
|
+
>;
|
|
938
|
+
|
|
939
|
+
// --- Normalization Rule ---
|
|
940
|
+
|
|
941
|
+
export const NormalizationRuleSchema = z
|
|
942
|
+
.object({
|
|
943
|
+
id: z.string().min(1),
|
|
944
|
+
description: z.string().min(1),
|
|
945
|
+
scope: z.enum(["source", "canonical", "both"]),
|
|
946
|
+
})
|
|
947
|
+
.strict();
|
|
948
|
+
export type NormalizationRule = z.infer<typeof NormalizationRuleSchema>;
|
|
949
|
+
|
|
950
|
+
// --- Format Security Policy ---
|
|
951
|
+
|
|
952
|
+
export const FormatSecurityPolicySchema = z
|
|
953
|
+
.object({
|
|
954
|
+
sensitiveValuePolicy: z.enum(["reject", "preserve", "reference-only"]),
|
|
955
|
+
allowedReferencePatterns: z.array(z.string()).default([]),
|
|
956
|
+
})
|
|
957
|
+
.strict();
|
|
958
|
+
export type FormatSecurityPolicy = z.infer<typeof FormatSecurityPolicySchema>;
|
|
959
|
+
|
|
960
|
+
// --- Canonical Capability ---
|
|
961
|
+
|
|
962
|
+
/** Closed enum of all translatable KnowledgeArtifact capabilities */
|
|
963
|
+
export const CanonicalCapabilitySchema = z.enum([
|
|
964
|
+
"frontmatter",
|
|
965
|
+
"body",
|
|
966
|
+
"hooks",
|
|
967
|
+
"mcp-servers",
|
|
968
|
+
"workflows",
|
|
969
|
+
"body-overrides",
|
|
970
|
+
"extra-fields",
|
|
971
|
+
"path-scoping",
|
|
972
|
+
"toggleable-rules",
|
|
973
|
+
"file-match-inclusion",
|
|
974
|
+
"system-prompt-merging",
|
|
975
|
+
// Asset-type capabilities (one per AssetTypeSchema value)
|
|
976
|
+
"skill",
|
|
977
|
+
"power",
|
|
978
|
+
"rule",
|
|
979
|
+
"workflow",
|
|
980
|
+
"agent",
|
|
981
|
+
"prompt",
|
|
982
|
+
"template",
|
|
983
|
+
"reference-pack",
|
|
984
|
+
]);
|
|
985
|
+
export type CanonicalCapability = z.infer<typeof CanonicalCapabilitySchema>;
|
|
986
|
+
|
|
987
|
+
// --- Rosetta Compatibility Profile ---
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Compatibility entry for the Rosetta Stone. Reuses the same support/degradation
|
|
991
|
+
* semantics as the existing CapabilityEntrySchema but is independent to allow
|
|
992
|
+
* future divergence and keeps the Rosetta boundary self-contained.
|
|
993
|
+
*/
|
|
994
|
+
export const RosettaCompatibilityEntrySchema = z
|
|
995
|
+
.object({
|
|
996
|
+
support: SupportLevelSchema,
|
|
997
|
+
degradation: DegradationStrategySchema.optional(),
|
|
998
|
+
})
|
|
999
|
+
.strict()
|
|
1000
|
+
.refine(
|
|
1001
|
+
(entry) =>
|
|
1002
|
+
entry.support === "full"
|
|
1003
|
+
? entry.degradation === undefined
|
|
1004
|
+
: entry.degradation !== undefined,
|
|
1005
|
+
{
|
|
1006
|
+
message:
|
|
1007
|
+
"Degradation action is required for 'partial'/'none' and forbidden for 'full'",
|
|
1008
|
+
},
|
|
1009
|
+
);
|
|
1010
|
+
export type RosettaCompatibilityEntry = z.infer<
|
|
1011
|
+
typeof RosettaCompatibilityEntrySchema
|
|
1012
|
+
>;
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* A complete compatibility profile: every canonical capability must have an entry.
|
|
1016
|
+
* Enforced by Zod refinement checking completeness against CanonicalCapabilitySchema values.
|
|
1017
|
+
*/
|
|
1018
|
+
export const RosettaCompatibilityProfileSchema = z
|
|
1019
|
+
.record(CanonicalCapabilitySchema, RosettaCompatibilityEntrySchema)
|
|
1020
|
+
.refine(
|
|
1021
|
+
(profile) => {
|
|
1022
|
+
const required = CanonicalCapabilitySchema.options;
|
|
1023
|
+
return required.every((cap) => cap in profile);
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
message:
|
|
1027
|
+
"Compatibility profile must include an entry for every canonical capability",
|
|
1028
|
+
},
|
|
1029
|
+
);
|
|
1030
|
+
export type RosettaCompatibilityProfile = z.infer<
|
|
1031
|
+
typeof RosettaCompatibilityProfileSchema
|
|
1032
|
+
>;
|
|
1033
|
+
|
|
1034
|
+
// --- Format Contract ---
|
|
1035
|
+
|
|
1036
|
+
export const FormatContractSchema = z
|
|
1037
|
+
.object({
|
|
1038
|
+
id: FormatIdentifierSchema,
|
|
1039
|
+
contractVersion: ContractVersionSchema,
|
|
1040
|
+
direction: DirectionSchema,
|
|
1041
|
+
harness: HarnessNameSchema.nullable(),
|
|
1042
|
+
aliases: z.array(FormatIdentifierSchema),
|
|
1043
|
+
lifecycle: LifecycleMetadataSchema,
|
|
1044
|
+
canonicalVersions: CanonicalVersionRangeSchema,
|
|
1045
|
+
schemaReference: SchemaReferenceSchema,
|
|
1046
|
+
pathConventions: z.array(PathConventionSchema),
|
|
1047
|
+
detection: DetectionContractSchema,
|
|
1048
|
+
variants: z
|
|
1049
|
+
.record(FormatIdentifierSchema, VariantContractSchema)
|
|
1050
|
+
.default({}),
|
|
1051
|
+
defaultVariant: FormatIdentifierSchema.optional(),
|
|
1052
|
+
optionDefinitions: z
|
|
1053
|
+
.record(z.string(), FormatOptionDefinitionSchema)
|
|
1054
|
+
.default({}),
|
|
1055
|
+
defaults: z.record(z.string(), JsonValueSchema).default({}),
|
|
1056
|
+
normalizationRules: z.array(NormalizationRuleSchema),
|
|
1057
|
+
compatibility: RosettaCompatibilityProfileSchema,
|
|
1058
|
+
security: FormatSecurityPolicySchema,
|
|
1059
|
+
})
|
|
1060
|
+
.strict();
|
|
1061
|
+
export type FormatContract = z.infer<typeof FormatContractSchema>;
|
|
1062
|
+
|
|
1063
|
+
// --- Translation Phase ---
|
|
1064
|
+
|
|
1065
|
+
export const TranslationPhaseSchema = z.enum([
|
|
1066
|
+
"request",
|
|
1067
|
+
"registry",
|
|
1068
|
+
"detection",
|
|
1069
|
+
"source-validation",
|
|
1070
|
+
"source-translation",
|
|
1071
|
+
"canonical-validation",
|
|
1072
|
+
"compatibility",
|
|
1073
|
+
"target-translation",
|
|
1074
|
+
"plan-validation",
|
|
1075
|
+
"redaction",
|
|
1076
|
+
]);
|
|
1077
|
+
export type TranslationPhase = z.infer<typeof TranslationPhaseSchema>;
|
|
1078
|
+
|
|
1079
|
+
// --- Source and Canonical Diagnostic Locations ---
|
|
1080
|
+
|
|
1081
|
+
export const SourceLocationSchema = z
|
|
1082
|
+
.object({
|
|
1083
|
+
path: NormalizedRelativePathSchema,
|
|
1084
|
+
field: z.string().optional(),
|
|
1085
|
+
line: z.number().int().positive().optional(),
|
|
1086
|
+
column: z.number().int().nonnegative().optional(),
|
|
1087
|
+
offset: z.number().int().nonnegative().optional(),
|
|
1088
|
+
})
|
|
1089
|
+
.strict();
|
|
1090
|
+
export type SourceLocation = z.infer<typeof SourceLocationSchema>;
|
|
1091
|
+
|
|
1092
|
+
export const SourceDiagnosticLocationSchema = SourceLocationSchema;
|
|
1093
|
+
export type SourceDiagnosticLocation = SourceLocation;
|
|
1094
|
+
|
|
1095
|
+
export const CanonicalDiagnosticLocationSchema = z
|
|
1096
|
+
.object({
|
|
1097
|
+
artifactName: z.string().min(1),
|
|
1098
|
+
fieldPath: z.string().min(1),
|
|
1099
|
+
})
|
|
1100
|
+
.strict();
|
|
1101
|
+
export type CanonicalDiagnosticLocation = z.infer<
|
|
1102
|
+
typeof CanonicalDiagnosticLocationSchema
|
|
1103
|
+
>;
|
|
1104
|
+
|
|
1105
|
+
// --- Degradation Detail ---
|
|
1106
|
+
|
|
1107
|
+
export const DegradationDetailSchema = z
|
|
1108
|
+
.object({
|
|
1109
|
+
capability: CanonicalCapabilitySchema,
|
|
1110
|
+
action: DegradationStrategySchema,
|
|
1111
|
+
affectedValueCount: z.number().int().nonnegative(),
|
|
1112
|
+
expectedSemanticChange: z.string().optional(),
|
|
1113
|
+
})
|
|
1114
|
+
.strict();
|
|
1115
|
+
export type DegradationDetail = z.infer<typeof DegradationDetailSchema>;
|
|
1116
|
+
|
|
1117
|
+
// --- Translation Diagnostic ---
|
|
1118
|
+
|
|
1119
|
+
export const TranslationDiagnosticSchema = z
|
|
1120
|
+
.object({
|
|
1121
|
+
code: z.string().regex(/^RS_[A-Z0-9_]+$/),
|
|
1122
|
+
severity: RosettaSeveritySchema,
|
|
1123
|
+
phase: TranslationPhaseSchema,
|
|
1124
|
+
formatId: FormatIdentifierSchema.optional(),
|
|
1125
|
+
message: z.string().min(1),
|
|
1126
|
+
remediation: z.string().min(1),
|
|
1127
|
+
source: SourceDiagnosticLocationSchema.optional(),
|
|
1128
|
+
canonical: CanonicalDiagnosticLocationSchema.optional(),
|
|
1129
|
+
degradation: DegradationDetailSchema.optional(),
|
|
1130
|
+
unavailableDetails: z.array(z.string()).default([]),
|
|
1131
|
+
blocking: z.boolean(),
|
|
1132
|
+
})
|
|
1133
|
+
.strict();
|
|
1134
|
+
export type TranslationDiagnostic = z.infer<typeof TranslationDiagnosticSchema>;
|
|
1135
|
+
|
|
1136
|
+
// --- Translation Plan ---
|
|
1137
|
+
|
|
1138
|
+
export const OutputFileSchema = z
|
|
1139
|
+
.object({
|
|
1140
|
+
relativePath: NormalizedRelativePathSchema,
|
|
1141
|
+
content: z.union([z.string(), z.instanceof(Uint8Array)]),
|
|
1142
|
+
executable: z.boolean().default(false),
|
|
1143
|
+
mediaType: z.string().optional(),
|
|
1144
|
+
})
|
|
1145
|
+
.strict();
|
|
1146
|
+
export type OutputFile = z.infer<typeof OutputFileSchema>;
|
|
1147
|
+
|
|
1148
|
+
export const PlanOperationSchema = z
|
|
1149
|
+
.object({
|
|
1150
|
+
kind: z.literal("write-file"),
|
|
1151
|
+
relativePath: NormalizedRelativePathSchema,
|
|
1152
|
+
outputFileIndex: z.number().int().nonnegative(),
|
|
1153
|
+
})
|
|
1154
|
+
.strict();
|
|
1155
|
+
export type PlanOperation = z.infer<typeof PlanOperationSchema>;
|
|
1156
|
+
|
|
1157
|
+
export const TranslationPlanSchema = z
|
|
1158
|
+
.object({
|
|
1159
|
+
schemaVersion: z.literal("1.0"),
|
|
1160
|
+
formatId: FormatIdentifierSchema,
|
|
1161
|
+
variant: FormatIdentifierSchema.optional(),
|
|
1162
|
+
canonicalSchemaVersion: CanonicalSchemaVersionSchema,
|
|
1163
|
+
outputFiles: z.array(OutputFileSchema),
|
|
1164
|
+
operations: z.array(PlanOperationSchema),
|
|
1165
|
+
applicationState: z.enum(["eligible", "policy-required", "withheld"]),
|
|
1166
|
+
policyDiagnosticCodes: z.array(z.string()),
|
|
1167
|
+
})
|
|
1168
|
+
.strict();
|
|
1169
|
+
export type TranslationPlan = z.infer<typeof TranslationPlanSchema>;
|
|
1170
|
+
|
|
1171
|
+
// --- Format Selection ---
|
|
1172
|
+
|
|
1173
|
+
export const FormatSelectionSchema = z
|
|
1174
|
+
.object({
|
|
1175
|
+
formatId: FormatIdentifierSchema.optional(),
|
|
1176
|
+
variant: FormatIdentifierSchema.optional(),
|
|
1177
|
+
options: z.record(z.string(), JsonValueSchema).default({}),
|
|
1178
|
+
})
|
|
1179
|
+
.strict();
|
|
1180
|
+
export type FormatSelection = z.infer<typeof FormatSelectionSchema>;
|
|
1181
|
+
|
|
1182
|
+
// --- Canonical Output Options ---
|
|
1183
|
+
|
|
1184
|
+
export const CanonicalOutputOptionsSchema = z
|
|
1185
|
+
.object({
|
|
1186
|
+
emitEmptyAuxiliaryFiles: z.boolean().default(false),
|
|
1187
|
+
destinationName: z.string().min(1).optional(),
|
|
1188
|
+
})
|
|
1189
|
+
.strict();
|
|
1190
|
+
export type CanonicalOutputOptions = z.infer<
|
|
1191
|
+
typeof CanonicalOutputOptionsSchema
|
|
1192
|
+
>;
|
|
1193
|
+
|
|
1194
|
+
// --- Translation Requests (Discriminated Union) ---
|
|
1195
|
+
|
|
1196
|
+
export const InboundTranslationRequestSchema = z
|
|
1197
|
+
.object({
|
|
1198
|
+
mode: z.literal("inbound"),
|
|
1199
|
+
sourceDocuments: z.array(SourceDocumentSchema),
|
|
1200
|
+
source: FormatSelectionSchema,
|
|
1201
|
+
canonical: CanonicalOutputOptionsSchema,
|
|
1202
|
+
canonicalSchemaVersion: CanonicalSchemaVersionSchema,
|
|
1203
|
+
strict: z.boolean(),
|
|
1204
|
+
callerContext: z.record(z.string(), JsonValueSchema),
|
|
1205
|
+
})
|
|
1206
|
+
.strict();
|
|
1207
|
+
export type InboundTranslationRequest = z.infer<
|
|
1208
|
+
typeof InboundTranslationRequestSchema
|
|
1209
|
+
>;
|
|
1210
|
+
|
|
1211
|
+
export const OutboundTranslationRequestSchema = z
|
|
1212
|
+
.object({
|
|
1213
|
+
mode: z.literal("outbound"),
|
|
1214
|
+
artifact: KnowledgeArtifactSchema,
|
|
1215
|
+
target: FormatSelectionSchema.extend({
|
|
1216
|
+
formatId: FormatIdentifierSchema,
|
|
1217
|
+
}).strict(),
|
|
1218
|
+
canonicalSchemaVersion: CanonicalSchemaVersionSchema,
|
|
1219
|
+
strict: z.boolean(),
|
|
1220
|
+
callerContext: z.record(z.string(), JsonValueSchema),
|
|
1221
|
+
})
|
|
1222
|
+
.strict();
|
|
1223
|
+
export type OutboundTranslationRequest = z.infer<
|
|
1224
|
+
typeof OutboundTranslationRequestSchema
|
|
1225
|
+
>;
|
|
1226
|
+
|
|
1227
|
+
export const TranscodeTranslationRequestSchema = z
|
|
1228
|
+
.object({
|
|
1229
|
+
mode: z.literal("transcode"),
|
|
1230
|
+
sourceDocuments: z.array(SourceDocumentSchema),
|
|
1231
|
+
source: FormatSelectionSchema,
|
|
1232
|
+
target: FormatSelectionSchema.extend({
|
|
1233
|
+
formatId: FormatIdentifierSchema,
|
|
1234
|
+
}).strict(),
|
|
1235
|
+
canonicalSchemaVersion: CanonicalSchemaVersionSchema,
|
|
1236
|
+
strict: z.boolean(),
|
|
1237
|
+
callerContext: z.record(z.string(), JsonValueSchema),
|
|
1238
|
+
})
|
|
1239
|
+
.strict();
|
|
1240
|
+
export type TranscodeTranslationRequest = z.infer<
|
|
1241
|
+
typeof TranscodeTranslationRequestSchema
|
|
1242
|
+
>;
|
|
1243
|
+
|
|
1244
|
+
export const TranslationRequestSchema = z.discriminatedUnion("mode", [
|
|
1245
|
+
InboundTranslationRequestSchema,
|
|
1246
|
+
OutboundTranslationRequestSchema,
|
|
1247
|
+
TranscodeTranslationRequestSchema,
|
|
1248
|
+
]);
|
|
1249
|
+
export type TranslationRequest = z.infer<typeof TranslationRequestSchema>;
|
|
1250
|
+
|
|
1251
|
+
// --- Resolved Format Summary ---
|
|
1252
|
+
|
|
1253
|
+
export const ResolvedFormatSummarySchema = z
|
|
1254
|
+
.object({
|
|
1255
|
+
formatId: FormatIdentifierSchema,
|
|
1256
|
+
variant: FormatIdentifierSchema.optional(),
|
|
1257
|
+
contractVersion: ContractVersionSchema,
|
|
1258
|
+
lifecycle: LifecycleStatusSchema,
|
|
1259
|
+
})
|
|
1260
|
+
.strict();
|
|
1261
|
+
export type ResolvedFormatSummary = z.infer<typeof ResolvedFormatSummarySchema>;
|
|
1262
|
+
|
|
1263
|
+
// --- Applied Default ---
|
|
1264
|
+
|
|
1265
|
+
export const AppliedDefaultSchema = z
|
|
1266
|
+
.object({
|
|
1267
|
+
field: z.string().min(1),
|
|
1268
|
+
value: JsonValueSchema,
|
|
1269
|
+
rule: z.string().min(1),
|
|
1270
|
+
})
|
|
1271
|
+
.strict();
|
|
1272
|
+
export type AppliedDefault = z.infer<typeof AppliedDefaultSchema>;
|
|
1273
|
+
|
|
1274
|
+
// --- Applied Normalization ---
|
|
1275
|
+
|
|
1276
|
+
export const AppliedNormalizationSchema = z
|
|
1277
|
+
.object({
|
|
1278
|
+
ruleId: z.string().min(1),
|
|
1279
|
+
field: z.string().min(1),
|
|
1280
|
+
description: z.string().min(1),
|
|
1281
|
+
})
|
|
1282
|
+
.strict();
|
|
1283
|
+
export type AppliedNormalization = z.infer<typeof AppliedNormalizationSchema>;
|
|
1284
|
+
|
|
1285
|
+
// --- Degradation Record ---
|
|
1286
|
+
|
|
1287
|
+
export const DegradationRecordSchema = z
|
|
1288
|
+
.object({
|
|
1289
|
+
capability: CanonicalCapabilitySchema,
|
|
1290
|
+
canonicalPaths: z.array(z.string()),
|
|
1291
|
+
action: DegradationStrategySchema,
|
|
1292
|
+
affectedValueCount: z.number().int().nonnegative(),
|
|
1293
|
+
expectedSemanticChange: z.string().optional(),
|
|
1294
|
+
})
|
|
1295
|
+
.strict();
|
|
1296
|
+
export type DegradationRecord = z.infer<typeof DegradationRecordSchema>;
|
|
1297
|
+
|
|
1298
|
+
// --- Translation Result ---
|
|
1299
|
+
|
|
1300
|
+
export const TranslationResultSchema = z
|
|
1301
|
+
.object({
|
|
1302
|
+
schemaVersion: z.literal("1.0"),
|
|
1303
|
+
status: z.enum(["success", "partial", "failure"]),
|
|
1304
|
+
registryVersion: z.string().min(1),
|
|
1305
|
+
sourceFormat: ResolvedFormatSummarySchema.optional(),
|
|
1306
|
+
targetFormat: ResolvedFormatSummarySchema.optional(),
|
|
1307
|
+
canonical: KnowledgeArtifactSchema.optional(),
|
|
1308
|
+
plan: TranslationPlanSchema.optional(),
|
|
1309
|
+
diagnostics: z.array(TranslationDiagnosticSchema),
|
|
1310
|
+
defaults: z.array(AppliedDefaultSchema),
|
|
1311
|
+
normalizations: z.array(AppliedNormalizationSchema),
|
|
1312
|
+
degradations: z.array(DegradationRecordSchema),
|
|
1313
|
+
})
|
|
1314
|
+
.strict();
|
|
1315
|
+
export type TranslationResult = z.infer<typeof TranslationResultSchema>;
|
|
1316
|
+
|
|
1317
|
+
// --- Detection Models ---
|
|
1318
|
+
|
|
1319
|
+
export const DetectionEvidenceSchema = z
|
|
1320
|
+
.object({
|
|
1321
|
+
ruleId: z.string().min(1),
|
|
1322
|
+
kind: DetectionRuleKindSchema,
|
|
1323
|
+
outcome: z.enum([
|
|
1324
|
+
"matched",
|
|
1325
|
+
"missing-required",
|
|
1326
|
+
"conflicting",
|
|
1327
|
+
"not-matched",
|
|
1328
|
+
]),
|
|
1329
|
+
paths: z.array(NormalizedRelativePathSchema),
|
|
1330
|
+
marker: z.string().optional(),
|
|
1331
|
+
metadataLocation: SourceLocationSchema.optional(),
|
|
1332
|
+
})
|
|
1333
|
+
.strict();
|
|
1334
|
+
export type DetectionEvidence = z.infer<typeof DetectionEvidenceSchema>;
|
|
1335
|
+
|
|
1336
|
+
export const DetectionCandidateSchema = z
|
|
1337
|
+
.object({
|
|
1338
|
+
formatId: FormatIdentifierSchema,
|
|
1339
|
+
confidence: z.number().min(0).max(1),
|
|
1340
|
+
threshold: z.number().min(0).max(1),
|
|
1341
|
+
qualifies: z.boolean(),
|
|
1342
|
+
evidence: z.array(DetectionEvidenceSchema),
|
|
1343
|
+
})
|
|
1344
|
+
.strict();
|
|
1345
|
+
export type DetectionCandidate = z.infer<typeof DetectionCandidateSchema>;
|
|
1346
|
+
|
|
1347
|
+
// --- Registry Failure ---
|
|
1348
|
+
|
|
1349
|
+
export const RegistryFailureSchema = z
|
|
1350
|
+
.object({
|
|
1351
|
+
code: z.literal("RS_REGISTRY_FAILURE"),
|
|
1352
|
+
message: z.string().min(1),
|
|
1353
|
+
})
|
|
1354
|
+
.strict();
|
|
1355
|
+
export type RegistryFailure = z.infer<typeof RegistryFailureSchema>;
|
|
1356
|
+
|
|
1357
|
+
// --- Profiles ---
|
|
1358
|
+
|
|
1359
|
+
export const AcquisitionProfileSchema = z.object({
|
|
1360
|
+
repo: z.string().min(1),
|
|
1361
|
+
branch: z.string().min(1).default("main"),
|
|
1362
|
+
remote: z.string().min(1).default("origin"),
|
|
1363
|
+
checkoutPrefix: z.string().optional(),
|
|
1364
|
+
credentialReference: z.string().optional(),
|
|
1365
|
+
});
|
|
1366
|
+
export type AcquisitionProfile = z.infer<typeof AcquisitionProfileSchema>;
|
|
1367
|
+
|
|
1368
|
+
export const TranslationProfileSchema = z.object({
|
|
1369
|
+
sourceFormat: FormatIdentifierSchema.optional(),
|
|
1370
|
+
sourceSubpath: z.string().optional(),
|
|
1371
|
+
targetFormat: FormatIdentifierSchema.optional(),
|
|
1372
|
+
targetVariant: FormatIdentifierSchema.optional(),
|
|
1373
|
+
canonicalDestination: z.string().optional(),
|
|
1374
|
+
collections: z.array(z.string()).default([]),
|
|
1375
|
+
strict: z.boolean().default(false),
|
|
1376
|
+
canonicalSchemaVersion: CanonicalSchemaVersionSchema.optional(),
|
|
1377
|
+
options: z.record(z.string(), JsonValueSchema).default({}),
|
|
1378
|
+
});
|
|
1379
|
+
export type TranslationProfile = z.infer<typeof TranslationProfileSchema>;
|
|
1380
|
+
|
|
1381
|
+
// --- Machine-Output Envelopes ---
|
|
1382
|
+
|
|
1383
|
+
export const InspectionReportEnvelopeSchema = z
|
|
1384
|
+
.object({
|
|
1385
|
+
machineSchemaVersion: z.literal("1.0"),
|
|
1386
|
+
generatedAt: z.string().datetime(),
|
|
1387
|
+
registryVersion: z.string().min(1),
|
|
1388
|
+
request: TranslationRequestSchema,
|
|
1389
|
+
sourceFormat: ResolvedFormatSummarySchema.optional(),
|
|
1390
|
+
targetFormat: ResolvedFormatSummarySchema.optional(),
|
|
1391
|
+
detection: z
|
|
1392
|
+
.object({
|
|
1393
|
+
candidates: z.array(DetectionCandidateSchema),
|
|
1394
|
+
selected: FormatIdentifierSchema.optional(),
|
|
1395
|
+
})
|
|
1396
|
+
.strict()
|
|
1397
|
+
.optional(),
|
|
1398
|
+
canonical: z
|
|
1399
|
+
.object({
|
|
1400
|
+
artifactName: z.string().optional(),
|
|
1401
|
+
fieldCount: z.number().int().nonnegative(),
|
|
1402
|
+
})
|
|
1403
|
+
.strict()
|
|
1404
|
+
.optional(),
|
|
1405
|
+
compatibility: z
|
|
1406
|
+
.object({
|
|
1407
|
+
counts: z.record(
|
|
1408
|
+
CanonicalCapabilitySchema,
|
|
1409
|
+
z
|
|
1410
|
+
.object({
|
|
1411
|
+
support: SupportLevelSchema,
|
|
1412
|
+
affectedValues: z.number().int().nonnegative(),
|
|
1413
|
+
})
|
|
1414
|
+
.strict(),
|
|
1415
|
+
),
|
|
1416
|
+
})
|
|
1417
|
+
.strict()
|
|
1418
|
+
.optional(),
|
|
1419
|
+
plan: z
|
|
1420
|
+
.object({
|
|
1421
|
+
fileCount: z.number().int().nonnegative(),
|
|
1422
|
+
paths: z.array(z.string()),
|
|
1423
|
+
})
|
|
1424
|
+
.strict()
|
|
1425
|
+
.optional(),
|
|
1426
|
+
defaults: z.array(AppliedDefaultSchema),
|
|
1427
|
+
normalizations: z.array(AppliedNormalizationSchema),
|
|
1428
|
+
diagnostics: z.array(TranslationDiagnosticSchema),
|
|
1429
|
+
degradations: z.array(DegradationRecordSchema),
|
|
1430
|
+
})
|
|
1431
|
+
.strict();
|
|
1432
|
+
export type InspectionReportEnvelope = z.infer<
|
|
1433
|
+
typeof InspectionReportEnvelopeSchema
|
|
1434
|
+
>;
|
|
1435
|
+
|
|
1436
|
+
export const DiagnosticsEnvelopeSchema = z
|
|
1437
|
+
.object({
|
|
1438
|
+
machineSchemaVersion: z.literal("1.0"),
|
|
1439
|
+
generatedAt: z.string().datetime(),
|
|
1440
|
+
registryVersion: z.string().min(1),
|
|
1441
|
+
diagnostics: z.array(TranslationDiagnosticSchema),
|
|
1442
|
+
status: z.enum(["success", "partial", "failure"]),
|
|
1443
|
+
})
|
|
1444
|
+
.strict();
|
|
1445
|
+
export type DiagnosticsEnvelope = z.infer<typeof DiagnosticsEnvelopeSchema>;
|
|
1446
|
+
|
|
1447
|
+
// --- Provenance Record ---
|
|
1448
|
+
|
|
1449
|
+
export const ProvenanceRecordSchema = z
|
|
1450
|
+
.object({
|
|
1451
|
+
upstream: z.string().min(1),
|
|
1452
|
+
sourcePath: z.string().min(1),
|
|
1453
|
+
sourceFormat: FormatIdentifierSchema,
|
|
1454
|
+
sourceRevision: z.string().min(1),
|
|
1455
|
+
contract: z.string().min(1),
|
|
1456
|
+
baseDigest: z.string().min(1),
|
|
1457
|
+
importedAt: z.string().datetime(),
|
|
1458
|
+
})
|
|
1459
|
+
.strict();
|
|
1460
|
+
export type ProvenanceRecord = z.infer<typeof ProvenanceRecordSchema>;
|
|
1461
|
+
|
|
1462
|
+
// --- Attribution Record ---
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* The copyright-relevant relationship between this artifact and an upstream
|
|
1466
|
+
* work. Captured (not inferred) at import time by the Attribution_Wizard.
|
|
1467
|
+
*
|
|
1468
|
+
* - `verbatim`: vendored unchanged; the body is upstream's.
|
|
1469
|
+
* - `adapted`: materially edited from upstream.
|
|
1470
|
+
* - `inspired-by`: original expression here, only the idea is upstream's.
|
|
1471
|
+
* - `packaged`: repackaged/reformatted, authorship unchanged.
|
|
1472
|
+
*/
|
|
1473
|
+
export const RelationshipSchema = z.enum([
|
|
1474
|
+
"verbatim",
|
|
1475
|
+
"adapted",
|
|
1476
|
+
"inspired-by",
|
|
1477
|
+
"packaged",
|
|
1478
|
+
]);
|
|
1479
|
+
export type Relationship = z.infer<typeof RelationshipSchema>;
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* One upstream work this artifact derives from. `source-repo`/`source-commit`
|
|
1483
|
+
* may overlap with the machine-owned ProvenanceRecord intentionally; that
|
|
1484
|
+
* overlap is not deduplicated. `license` reuses the SPDX conventions of the
|
|
1485
|
+
* frontmatter `license` field.
|
|
1486
|
+
*/
|
|
1487
|
+
export const UpstreamWorkSchema = z.object({
|
|
1488
|
+
work: z.string().min(1),
|
|
1489
|
+
authors: z.array(z.string().min(1)).min(1),
|
|
1490
|
+
url: z.string().url().optional(),
|
|
1491
|
+
license: z.string().optional(),
|
|
1492
|
+
"source-repo": z.string().min(1).optional(),
|
|
1493
|
+
"source-commit": z.string().min(1).optional(),
|
|
1494
|
+
relationship: RelationshipSchema.default("verbatim"),
|
|
1495
|
+
});
|
|
1496
|
+
export type UpstreamWork = z.infer<typeof UpstreamWorkSchema>;
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Curation-owned, human/legal attribution for an artifact (see ADR-0064). The
|
|
1500
|
+
* counterpart to the machine-owned ProvenanceRecord: `provenance` answers
|
|
1501
|
+
* "where do I re-sync from" and is overwritten on every import; `attribution`
|
|
1502
|
+
* answers "who do I credit and under what license" and is preserved across
|
|
1503
|
+
* every re-sync (classified `curation-owned` in the Field_Ownership_Policy).
|
|
1504
|
+
*/
|
|
1505
|
+
export const AttributionRecordSchema = z.object({
|
|
1506
|
+
upstream: z.array(UpstreamWorkSchema).min(1),
|
|
1507
|
+
"curated-by": z.string().min(1).optional(),
|
|
1508
|
+
notice: z.string().optional(),
|
|
1509
|
+
});
|
|
1510
|
+
export type AttributionRecord = z.infer<typeof AttributionRecordSchema>;
|
|
1511
|
+
|
|
1512
|
+
// --- Reconciliation: field ownership and three-way merge ---
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* The four ownership classes that determine which merge rule applies to a
|
|
1516
|
+
* reconcilable field during Three_Way_Reconciliation (Requirement 18).
|
|
1517
|
+
*
|
|
1518
|
+
* - `curation-owned`: always keep Ours; never overwritten from upstream.
|
|
1519
|
+
* - `upstream-owned`: fast-forward to Theirs when Base == Ours; conflict when
|
|
1520
|
+
* both sides diverged.
|
|
1521
|
+
* - `merge-by-union`: deterministic union of Ours and Theirs additions minus
|
|
1522
|
+
* members removed between Base and Theirs.
|
|
1523
|
+
* - `machine-owned`: recomputed from the merged result (never merged directly).
|
|
1524
|
+
*/
|
|
1525
|
+
export const FieldOwnershipClassSchema = z.enum([
|
|
1526
|
+
"curation-owned",
|
|
1527
|
+
"upstream-owned",
|
|
1528
|
+
"merge-by-union",
|
|
1529
|
+
"machine-owned",
|
|
1530
|
+
]);
|
|
1531
|
+
export type FieldOwnershipClass = z.infer<typeof FieldOwnershipClassSchema>;
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* The closed set of canonical fields and capabilities that participate in
|
|
1535
|
+
* reconciliation. A complete Field_Ownership_Policy assigns exactly one
|
|
1536
|
+
* FieldOwnershipClass to every member of this set (Requirement 18.14). The
|
|
1537
|
+
* Configuration_Validator rejects any policy that references a field outside
|
|
1538
|
+
* this set or omits a classification for one of its members.
|
|
1539
|
+
*/
|
|
1540
|
+
export const ReconcilableFieldSchema = z.enum([
|
|
1541
|
+
// Curation-owned frontmatter fields
|
|
1542
|
+
"categories",
|
|
1543
|
+
"trust",
|
|
1544
|
+
"collections",
|
|
1545
|
+
"audience",
|
|
1546
|
+
"priority",
|
|
1547
|
+
"visibility",
|
|
1548
|
+
"hooks",
|
|
1549
|
+
"attribution",
|
|
1550
|
+
// Upstream-owned capabilities
|
|
1551
|
+
"body",
|
|
1552
|
+
"workflows",
|
|
1553
|
+
"mcpServers",
|
|
1554
|
+
// Merge-by-union frontmatter fields
|
|
1555
|
+
"keywords",
|
|
1556
|
+
"enhances",
|
|
1557
|
+
"depends",
|
|
1558
|
+
// Machine-owned fields
|
|
1559
|
+
"provenance",
|
|
1560
|
+
"version",
|
|
1561
|
+
]);
|
|
1562
|
+
export type ReconcilableField = z.infer<typeof ReconcilableFieldSchema>;
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* A Field_Ownership_Policy maps each reconcilable canonical field to an
|
|
1566
|
+
* ownership class. It is overridable per upstream in configuration; a complete
|
|
1567
|
+
* policy classifies every member of ReconcilableFieldSchema. This base schema
|
|
1568
|
+
* accepts a partial record so that per-upstream overrides can specify only the
|
|
1569
|
+
* fields they change; the Configuration_Validator enforces completeness against
|
|
1570
|
+
* DEFAULT_FIELD_OWNERSHIP_POLICY before use (Requirement 18.14).
|
|
1571
|
+
*/
|
|
1572
|
+
export const FieldOwnershipPolicySchema = z
|
|
1573
|
+
.record(ReconcilableFieldSchema, FieldOwnershipClassSchema)
|
|
1574
|
+
.refine(
|
|
1575
|
+
(policy) =>
|
|
1576
|
+
Object.keys(policy).every((field) =>
|
|
1577
|
+
(ReconcilableFieldSchema.options as readonly string[]).includes(field),
|
|
1578
|
+
),
|
|
1579
|
+
{
|
|
1580
|
+
message:
|
|
1581
|
+
"Field_Ownership_Policy references a field outside ReconcilableFieldSchema",
|
|
1582
|
+
},
|
|
1583
|
+
);
|
|
1584
|
+
export type FieldOwnershipPolicy = z.infer<typeof FieldOwnershipPolicySchema>;
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* The documented default Field_Ownership_Policy. Every reconcilable field is
|
|
1588
|
+
* classified so the default is complete (Requirement 18.14). Per ADR-0049 and
|
|
1589
|
+
* the Rosetta Stone design:
|
|
1590
|
+
*
|
|
1591
|
+
* - Curation-owned fields (`categories`, `trust`, `collections`, `audience`,
|
|
1592
|
+
* `priority`, `visibility`, `hooks`, `attribution`) always keep the curated
|
|
1593
|
+
* (Ours) value. `hooks` is curation-owned because maintainers routinely tune
|
|
1594
|
+
* hooks locally; `attribution` is curation-owned so an upstream re-sync never
|
|
1595
|
+
* clobbers hand-confirmed credit (ADR-0064).
|
|
1596
|
+
* - Upstream-owned fields (`body`, `workflows`, `mcpServers`) fast-forward to
|
|
1597
|
+
* the upstream (Theirs) value only when the maintainer never edited them.
|
|
1598
|
+
* - Merge-by-union fields (`keywords`, `enhances`, `depends`) take the
|
|
1599
|
+
* deterministic union of both sides minus upstream removals.
|
|
1600
|
+
* - Machine-owned fields (`provenance`, `version`) are recomputed from the
|
|
1601
|
+
* merged result and are never merged directly.
|
|
1602
|
+
*/
|
|
1603
|
+
export const DEFAULT_FIELD_OWNERSHIP_POLICY: Readonly<
|
|
1604
|
+
Record<ReconcilableField, FieldOwnershipClass>
|
|
1605
|
+
> = Object.freeze({
|
|
1606
|
+
categories: "curation-owned",
|
|
1607
|
+
trust: "curation-owned",
|
|
1608
|
+
collections: "curation-owned",
|
|
1609
|
+
audience: "curation-owned",
|
|
1610
|
+
priority: "curation-owned",
|
|
1611
|
+
visibility: "curation-owned",
|
|
1612
|
+
hooks: "curation-owned",
|
|
1613
|
+
attribution: "curation-owned",
|
|
1614
|
+
body: "upstream-owned",
|
|
1615
|
+
workflows: "upstream-owned",
|
|
1616
|
+
mcpServers: "upstream-owned",
|
|
1617
|
+
keywords: "merge-by-union",
|
|
1618
|
+
enhances: "merge-by-union",
|
|
1619
|
+
depends: "merge-by-union",
|
|
1620
|
+
provenance: "machine-owned",
|
|
1621
|
+
version: "machine-owned",
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* The per-field and per-artifact classification produced by a
|
|
1626
|
+
* Three_Way_Reconciliation (Requirement 18).
|
|
1627
|
+
*/
|
|
1628
|
+
export const ReconciliationOutcomeSchema = z.enum([
|
|
1629
|
+
"clean",
|
|
1630
|
+
"fast-forward",
|
|
1631
|
+
"merged",
|
|
1632
|
+
"conflict",
|
|
1633
|
+
"orphaned",
|
|
1634
|
+
"new",
|
|
1635
|
+
]);
|
|
1636
|
+
export type ReconciliationOutcome = z.infer<typeof ReconciliationOutcomeSchema>;
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* A Reconciliation_Request. `base` is optional to express the reduced-confidence
|
|
1640
|
+
* two-way path used when the Base_Artifact cannot be reconstructed or its
|
|
1641
|
+
* provenance digest fails self-verification (Requirements 18.11, 18.16).
|
|
1642
|
+
*/
|
|
1643
|
+
export const ReconciliationRequestSchema = z
|
|
1644
|
+
.object({
|
|
1645
|
+
base: KnowledgeArtifactSchema.optional(),
|
|
1646
|
+
ours: KnowledgeArtifactSchema,
|
|
1647
|
+
theirs: KnowledgeArtifactSchema,
|
|
1648
|
+
policy: FieldOwnershipPolicySchema,
|
|
1649
|
+
})
|
|
1650
|
+
.strict();
|
|
1651
|
+
export type ReconciliationRequest = z.infer<typeof ReconciliationRequestSchema>;
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* A Reconciliation_Diagnostic extends the shared TranslationDiagnostic shape
|
|
1655
|
+
* with reconciliation-specific fields identifying the affected field, its
|
|
1656
|
+
* ownership class, the per-field outcome, whether a Base value was available,
|
|
1657
|
+
* and the confidence of the merge (reduced when Base is absent).
|
|
1658
|
+
*/
|
|
1659
|
+
export const ReconciliationDiagnosticSchema =
|
|
1660
|
+
TranslationDiagnosticSchema.extend({
|
|
1661
|
+
field: ReconcilableFieldSchema,
|
|
1662
|
+
fieldClass: FieldOwnershipClassSchema,
|
|
1663
|
+
outcome: ReconciliationOutcomeSchema,
|
|
1664
|
+
baseValuePresent: z.boolean(),
|
|
1665
|
+
confidence: z.enum(["full", "reduced"]),
|
|
1666
|
+
}).strict();
|
|
1667
|
+
export type ReconciliationDiagnostic = z.infer<
|
|
1668
|
+
typeof ReconciliationDiagnosticSchema
|
|
1669
|
+
>;
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* The result of reconciling a single artifact: the merged KnowledgeArtifact,
|
|
1673
|
+
* its overall outcome, and the ordered reconciliation diagnostics.
|
|
1674
|
+
*/
|
|
1675
|
+
export const ReconciliationResultSchema = z
|
|
1676
|
+
.object({
|
|
1677
|
+
artifact: KnowledgeArtifactSchema,
|
|
1678
|
+
outcome: ReconciliationOutcomeSchema,
|
|
1679
|
+
diagnostics: z.array(ReconciliationDiagnosticSchema).default([]),
|
|
1680
|
+
})
|
|
1681
|
+
.strict();
|
|
1682
|
+
export type ReconciliationResult = z.infer<typeof ReconciliationResultSchema>;
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* A single entry in a Reconciliation_Report, pairing an artifact's identity
|
|
1686
|
+
* (upstream and name, used for stable ordering) with its reconciliation result.
|
|
1687
|
+
*/
|
|
1688
|
+
export const ReconciliationReportEntrySchema = z
|
|
1689
|
+
.object({
|
|
1690
|
+
upstream: z.string().min(1),
|
|
1691
|
+
artifactName: z.string().min(1),
|
|
1692
|
+
result: ReconciliationResultSchema,
|
|
1693
|
+
})
|
|
1694
|
+
.strict();
|
|
1695
|
+
export type ReconciliationReportEntry = z.infer<
|
|
1696
|
+
typeof ReconciliationReportEntrySchema
|
|
1697
|
+
>;
|
|
1698
|
+
|
|
1699
|
+
/**
|
|
1700
|
+
* A deterministic Reconciliation_Report aggregating results across every
|
|
1701
|
+
* Provenance_Record-bearing artifact for one or more upstreams. Entries are
|
|
1702
|
+
* ordered by outcome, then upstream identifier, then artifact name
|
|
1703
|
+
* (Requirement 18.15). Carries its own machineSchemaVersion, independent of
|
|
1704
|
+
* InspectionReportEnvelopeSchema and DiagnosticsEnvelopeSchema.
|
|
1705
|
+
*/
|
|
1706
|
+
export const ReconciliationReportSchema = z
|
|
1707
|
+
.object({
|
|
1708
|
+
machineSchemaVersion: z.literal("1.0"),
|
|
1709
|
+
entries: z.array(ReconciliationReportEntrySchema).default([]),
|
|
1710
|
+
})
|
|
1711
|
+
.strict();
|
|
1712
|
+
export type ReconciliationReport = z.infer<typeof ReconciliationReportSchema>;
|