@treeseed/sdk 0.13.0-rc.34 → 0.13.0-rc.36

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.
@@ -425,12 +425,26 @@ export declare const hostConfigurationSchema: z.ZodObject<{
425
425
  role: string;
426
426
  architecture: "amd64";
427
427
  }>;
428
- runtime: z.ZodObject<{
428
+ runtime: z.ZodEffects<z.ZodObject<{
429
429
  management: z.ZodEnum<["managed", "external"]>;
430
+ environment: z.ZodDefault<z.ZodEnum<["production", "development"]>>;
431
+ dataRoot: z.ZodOptional<z.ZodString>;
430
432
  }, "strict", z.ZodTypeAny, {
431
433
  management: "managed" | "external";
434
+ environment: "development" | "production";
435
+ dataRoot?: string | undefined;
436
+ }, {
437
+ management: "managed" | "external";
438
+ environment?: "development" | "production" | undefined;
439
+ dataRoot?: string | undefined;
440
+ }>, {
441
+ management: "managed" | "external";
442
+ environment: "development" | "production";
443
+ dataRoot?: string | undefined;
432
444
  }, {
433
445
  management: "managed" | "external";
446
+ environment?: "development" | "production" | undefined;
447
+ dataRoot?: string | undefined;
434
448
  }>;
435
449
  updates: z.ZodObject<{
436
450
  defaultTrack: z.ZodEnum<["stable", "development"]>;
@@ -1145,6 +1159,8 @@ export declare const hostConfigurationSchema: z.ZodObject<{
1145
1159
  };
1146
1160
  runtime: {
1147
1161
  management: "managed" | "external";
1162
+ environment: "development" | "production";
1163
+ dataRoot?: string | undefined;
1148
1164
  };
1149
1165
  updates: {
1150
1166
  stable: {
@@ -1253,6 +1269,8 @@ export declare const hostConfigurationSchema: z.ZodObject<{
1253
1269
  };
1254
1270
  runtime: {
1255
1271
  management: "managed" | "external";
1272
+ environment?: "development" | "production" | undefined;
1273
+ dataRoot?: string | undefined;
1256
1274
  };
1257
1275
  updates: {
1258
1276
  stable: {
@@ -69,7 +69,14 @@ const hostConfigurationSchema = z.object({
69
69
  role: hostRoleSchema,
70
70
  architecture: z.literal("amd64")
71
71
  }).strict(),
72
- runtime: z.object({ management: z.enum(["managed", "external"]) }).strict(),
72
+ runtime: z.object({
73
+ management: z.enum(["managed", "external"]),
74
+ environment: z.enum(["production", "development"]).default("production"),
75
+ dataRoot: z.string().startsWith("/").regex(/\/\.treeseed\/data$/u, "Development dataRoot must end with /.treeseed/data.").optional()
76
+ }).strict().superRefine((runtime, context) => {
77
+ if (runtime.environment === "development" && !runtime.dataRoot) context.addIssue({ code: z.ZodIssueCode.custom, path: ["dataRoot"], message: "Development hosts require an explicit workspace-visible dataRoot." });
78
+ if (runtime.environment === "production" && runtime.dataRoot) context.addIssue({ code: z.ZodIssueCode.custom, path: ["dataRoot"], message: "Production hosts use manager-owned /var/lib state and cannot override dataRoot." });
79
+ }),
73
80
  updates: z.object({
74
81
  defaultTrack: deploymentTrackSchema,
75
82
  stable: z.object({
@@ -98,6 +98,23 @@ const operationBindings = {
98
98
  "projects treedx workspaces list": operation("treedx.workspaces.list", [field("path", "projectId", "argument", "project", true), ...page()]),
99
99
  "projects treedx workspaces show": operation("treedx.workspaces.show", [field("path", "projectId", "context", "project", true), field("path", "workspaceId", "argument", "workspace", true)]),
100
100
  "projects treedx workspaces abandon": operation("treedx.workspaces.abandon", [field("path", "projectId", "context", "project", true), field("path", "workspaceId", "argument", "workspace", true)]),
101
+ "library show": local("local.library.show"),
102
+ "library status": local("local.library.status"),
103
+ "library paths": local("local.library.paths"),
104
+ "library read": local("local.library.read"),
105
+ "library search": local("local.library.search"),
106
+ "library query": local("local.library.query"),
107
+ "library context": local("local.library.context"),
108
+ "library workspace create": operation("knowledge.workspaces.create", [field("path", "projectId", "argument", "project", true), field("body", "requestId", "option", "request")]),
109
+ "library workspace show": operation("knowledge.workspaces.show", [field("path", "workspaceId", "argument", "workspace", true)]),
110
+ "library workspace read": operation("knowledge.workspaces.content.show", [field("path", "workspaceId", "argument", "workspace", true), field("query", "path", "argument", "path", true)]),
111
+ "library workspace diff": operation("knowledge.workspaces.diff", [field("path", "workspaceId", "argument", "workspace", true)]),
112
+ "library workspace write": operation("knowledge.workspaces.content.update", [field("path", "workspaceId", "argument", "workspace", true), field("body", "file", "option", "input", true)]),
113
+ "library workspace submit": operation("knowledge.workspaces.submit", [field("path", "workspaceId", "argument", "workspace", true), field("body", "version", "option", "version", true, "integer"), field("body", "message", "option"), field("body", "notes", "option"), field("body", "contextDigest", "option", "contextDigest")]),
114
+ "library workspace abandon": operation("knowledge.workspaces.abandon", [field("path", "workspaceId", "argument", "workspace", true), field("body", "version", "option", "version", true, "integer")]),
115
+ "library reviews list": operation("knowledge.reviews.list", [field("path", "teamId", "context", "team", true), ...page()]),
116
+ "library reviews decide": operation("knowledge.reviews.decide", [field("path", "reviewId", "argument", "review", true), field("body", "file", "option", "input", true)]),
117
+ "library reviews publish": operation("knowledge.reviews.publish", [field("path", "reviewId", "argument", "review", true), field("body", "file", "option", "input")]),
101
118
  "status": operation("status.show"),
102
119
  "save": unavailable("Unified GitHub-backed save is intentionally fail-closed during this cutover."),
103
120
  "stage": unavailable("Unified GitHub-backed stage is intentionally fail-closed during this cutover."),
@@ -134,23 +151,49 @@ function hostReset() {
134
151
  return value;
135
152
  }
136
153
  function userCreate() {
137
- const value = leaf("create", "mutation", void 0, "credential");
154
+ const value = leaf("create", "mutation");
138
155
  if (value.nodeType !== "leaf") throw new Error("User creation must be a leaf command.");
139
156
  value.description = "Create a local TreeSeed user with a securely prompted password.";
140
157
  value.options = [
141
158
  ...value.options ?? [],
142
159
  { name: "--email", description: "Email address for the new user.", type: "string" },
143
160
  { name: "--username", description: "Unique username for the new user.", type: "string" },
144
- { name: "--display-name", description: "Human-readable display name.", type: "string" }
161
+ { name: "--display-name", description: "Human-readable display name.", type: "string" },
162
+ { name: "--timeout", description: "Maximum seconds to wait for registration.", type: "number" }
145
163
  ];
146
164
  return value;
147
165
  }
166
+ function authLogin() {
167
+ const value = leaf("login", "mutation");
168
+ if (value.nodeType !== "leaf") throw new Error("Authentication login must be a leaf command.");
169
+ value.options = [
170
+ ...value.options ?? [],
171
+ { name: "--timeout", description: "Maximum seconds to wait for device authorization.", type: "number" }
172
+ ];
173
+ return value;
174
+ }
175
+ function libraryRead(segment, extraArguments = [], extraOptions = []) {
176
+ return {
177
+ nodeType: "leaf",
178
+ segment,
179
+ description: `${segment[0].toUpperCase()}${segment.slice(1)} project library knowledge.`,
180
+ kind: "read",
181
+ arguments: ["project", ...extraArguments].map((name) => ({ name, description: `${name} value.`, required: true })),
182
+ options: [{ name: "--ref", description: "Exact commit or protected library ref.", type: "string" }, ...extraOptions],
183
+ resultSchemaId: `treeseed.command.library.${segment}/v1`,
184
+ execution: local(`local.library.${segment}`)
185
+ };
186
+ }
187
+ function addOptions(node, options) {
188
+ if (node.nodeType === "leaf") node.options = [...node.options ?? [], ...options];
189
+ return node;
190
+ }
148
191
  const commandTree = {
149
192
  schemaVersion: "treeseed.command-tree/v1",
150
193
  executable: "trsd",
151
194
  commands: [
152
195
  { nodeType: "leaf", segment: "send", description: "Send a message to project agents through a team discussion channel.", kind: "mutation", arguments: [{ name: "channel", description: "Team discussion channel.", required: true }, { name: "message", description: "Markdown message to send.", required: true }], options: [planOption, { name: "--team", description: "Team identity or slug.", type: "string" }, { name: "--project", description: "Default project for unqualified agent names.", type: "string" }, { name: "--to", description: "Agent target; use project/agent when names may be ambiguous.", type: "string[]" }, { name: "--topic", description: "Optional discussion topic.", type: "string" }, { name: "--wait", description: "Seconds to wait for durable responses.", type: "number", defaultValue: 0 }], authorization: { capability: "agents.execute", confirmation: "never" }, resultSchemaId: "treeseed.communication-send-receipt/v1", execution: unavailable() },
153
- branch("auth", [leaf("login", "mutation", void 0, "credential"), leaf("logout", "mutation"), leaf("status")]),
196
+ branch("auth", [authLogin(), leaf("logout", "mutation"), leaf("status")]),
154
197
  branch("users", [userCreate()]),
155
198
  branch("secrets", [leaf("list"), leaf("status"), leaf("unlock", "mutation", void 0, "credential"), leaf("lock", "mutation"), leaf("rotate", "mutation", void 0, "credential")]),
156
199
  branch("host", [
@@ -217,6 +260,25 @@ const commandTree = {
217
260
  leaf("capabilities", "read", "project"),
218
261
  branch("workspaces", [leaf("list", "read", "project"), leaf("show", "read", "workspace"), leaf("abandon", "mutation", "workspace", "destructive")])
219
262
  ])]),
263
+ branch("library", [
264
+ libraryRead("show"),
265
+ libraryRead("status"),
266
+ libraryRead("paths", [], [{ name: "--prefix", description: "Repository-relative path prefix.", type: "string" }, { name: "--limit", description: "Page size.", type: "number" }, { name: "--cursor", description: "Opaque page cursor.", type: "string" }]),
267
+ libraryRead("read", ["path"]),
268
+ libraryRead("search", ["query"], [{ name: "--path", description: "Restrict search to a repository-relative path.", type: "string" }, { name: "--limit", description: "Page size.", type: "number" }, { name: "--cursor", description: "Opaque page cursor.", type: "string" }]),
269
+ libraryRead("query", ["query"], [{ name: "--model", description: "TreeDX content model.", type: "string" }, { name: "--input", description: "YAML or JSON query body.", type: "string" }]),
270
+ libraryRead("context", ["query"], [{ name: "--max-items", description: "Maximum context items.", type: "number" }, { name: "--max-tokens", description: "Maximum context tokens.", type: "number" }]),
271
+ branch("workspace", [
272
+ addOptions(leaf("create", "mutation", "project"), [{ name: "--request", description: "Replay-safe UUID request identity.", type: "string" }]),
273
+ leaf("show", "read", "workspace"),
274
+ { nodeType: "leaf", segment: "read", description: "Read a file from a governed library workspace.", kind: "read", arguments: [{ name: "workspace", description: "Workspace identity.", required: true }, { name: "path", description: "Repository-relative path.", required: true }], resultSchemaId: "treeseed.command.library.workspace.read/v1", execution: unavailable() },
275
+ leaf("diff", "read", "workspace"),
276
+ addOptions(leaf("write", "mutation", "workspace"), [{ name: "--input", description: "YAML or JSON draft body.", type: "string", required: true }]),
277
+ addOptions(leaf("submit", "mutation", "workspace"), [{ name: "--version", description: "Expected workspace version.", type: "number", required: true }, { name: "--message", description: "Commit message.", type: "string" }, { name: "--notes", description: "Review notes.", type: "string" }, { name: "--context-digest", description: "Verified editorial context digest.", type: "string" }]),
278
+ addOptions(leaf("abandon", "mutation", "workspace", "destructive"), [{ name: "--version", description: "Expected workspace version.", type: "number", required: true }])
279
+ ]),
280
+ branch("reviews", [leaf("list"), addOptions(leaf("decide", "mutation", "review"), [{ name: "--input", description: "YAML or JSON review decision.", type: "string", required: true }]), addOptions(leaf("publish", "mutation", "review"), [{ name: "--input", description: "Optional YAML or JSON publication body.", type: "string" }])])
281
+ ]),
220
282
  leaf("save", "mutation"),
221
283
  leaf("stage", "mutation", void 0, "authority"),
222
284
  leaf("release", "mutation", void 0, "production"),
@@ -184,17 +184,17 @@ const CONTROL_PLANE_OPERATIONS = {
184
184
  search: resource("knowledge.search", "GET", "/v1/knowledge/search", {}, { capability: "knowledge.read", surfaces: ["rest", "mcp_tool"], pagination: "cursor" }),
185
185
  teamCatalog: resource("knowledge.catalog.team", "GET", "/v1/teams/{teamId}/knowledge/catalog", { teamId: z.string().min(1) }, { capability: "knowledge.read", pagination: "cursor" }),
186
186
  projectCatalog: resource("knowledge.catalog.project", "GET", "/v1/projects/{projectId}/knowledge/catalog", { projectId: z.string().min(1) }, { capability: "knowledge.read", pagination: "cursor" }),
187
- createWorkspace: resource("knowledge.workspaces.create", "POST", "/v1/projects/{projectId}/knowledge/workspaces", { projectId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"] }),
188
- workspace: resource("knowledge.workspaces.show", "GET", "/v1/knowledge/workspaces/{workspaceId}", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
189
- workspaceContent: resource("knowledge.workspaces.content.show", "GET", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
190
- updateWorkspaceContent: resource("knowledge.workspaces.content.update", "PUT", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], concurrency: true }),
191
- workspaceDiff: resource("knowledge.workspaces.diff", "GET", "/v1/knowledge/workspaces/{workspaceId}/diff", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
192
- abandonWorkspace: resource("knowledge.workspaces.abandon", "POST", "/v1/knowledge/workspaces/{workspaceId}/abandon", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], risk: "destructive", concurrency: true }),
193
- submitWorkspace: resource("knowledge.workspaces.submit", "POST", "/v1/knowledge/workspaces/{workspaceId}/submit", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], concurrency: true }),
194
- reviews: resource("knowledge.reviews.list", "GET", "/v1/teams/{teamId}/knowledge/reviews", { teamId: z.string().min(1) }, { capability: "knowledge.review", pagination: "cursor" }),
187
+ createWorkspace: resource("knowledge.workspaces.create", "POST", "/v1/projects/{projectId}/knowledge/workspaces", { projectId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"] }),
188
+ workspace: resource("knowledge.workspaces.show", "GET", "/v1/knowledge/workspaces/{workspaceId}", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
189
+ workspaceContent: resource("knowledge.workspaces.content.show", "GET", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
190
+ updateWorkspaceContent: resource("knowledge.workspaces.content.update", "PUT", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
191
+ workspaceDiff: resource("knowledge.workspaces.diff", "GET", "/v1/knowledge/workspaces/{workspaceId}/diff", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
192
+ abandonWorkspace: resource("knowledge.workspaces.abandon", "POST", "/v1/knowledge/workspaces/{workspaceId}/abandon", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], risk: "destructive", concurrency: true }),
193
+ submitWorkspace: resource("knowledge.workspaces.submit", "POST", "/v1/knowledge/workspaces/{workspaceId}/submit", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
194
+ reviews: resource("knowledge.reviews.list", "GET", "/v1/teams/{teamId}/knowledge/reviews", { teamId: z.string().min(1) }, { capability: "knowledge.review", surfaces: ["rest", "cli"], pagination: "cursor" }),
195
195
  commentReview: resource("knowledge.reviews.comments.create", "POST", "/v1/knowledge/reviews/{reviewId}/comments", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"] }),
196
- decideReview: resource("knowledge.reviews.decide", "POST", "/v1/knowledge/reviews/{reviewId}/decision", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"], concurrency: true }),
197
- publishReview: resource("knowledge.reviews.publish", "POST", "/v1/knowledge/reviews/{reviewId}/publish", { reviewId: z.string().min(1) }, { capability: "knowledge.publish", scopes: ["treeseed:knowledge:write"], concurrency: true })
196
+ decideReview: resource("knowledge.reviews.decide", "POST", "/v1/knowledge/reviews/{reviewId}/decision", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
197
+ publishReview: resource("knowledge.reviews.publish", "POST", "/v1/knowledge/reviews/{reviewId}/publish", { reviewId: z.string().min(1) }, { capability: "knowledge.publish", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true })
198
198
  },
199
199
  governance: {
200
200
  proposals: resource("governance.proposals.list", "GET", "/v1/projects/{projectId}/proposals", { projectId: z.string().min(1) }, { capability: "governance.read", surfaces: ["rest", "mcp_tool", "mcp_resource"], pagination: "cursor" }),
@@ -1,5 +1,6 @@
1
1
  import type { ProviderLanePurpose } from '../../agent-capacity/contracts/capacity/communication/communication-records.js';
2
- export declare const SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/v2";
2
+ export declare const SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/v3";
3
+ export declare const HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/v2";
3
4
  export type SeedEnvironment = 'local' | 'staging' | 'prod' | string;
4
5
  export interface SeedTeamResource {
5
6
  key: string;
@@ -43,7 +44,7 @@ export interface SeedRepositoryPolicy {
43
44
  export interface SeedRepositoryResource {
44
45
  key: string;
45
46
  project: string;
46
- role: 'primary' | 'fixture' | 'support' | 'template';
47
+ role: 'primary' | 'library' | 'fixture' | 'support' | 'template';
47
48
  provider: 'github';
48
49
  owner: string;
49
50
  name: string;
@@ -62,6 +63,7 @@ export interface SeedProjectResource {
62
63
  description: string;
63
64
  kind: string;
64
65
  primaryRepository?: string;
66
+ libraryRepository: string;
65
67
  metadata?: Record<string, unknown>;
66
68
  }
67
69
  export interface SeedCapacityProviderPrerequisite {
@@ -75,8 +77,7 @@ export interface SeedCapacityProviderPrerequisite {
75
77
  allowedModes: Array<'planning' | 'acting'>;
76
78
  requiredLanePurposes: ProviderLanePurpose[];
77
79
  }
78
- export interface SeedBundleV2 {
79
- schemaVersion: typeof SEED_BUNDLE_SCHEMA_VERSION;
80
+ interface SeedBundleBody {
80
81
  name: string;
81
82
  version: number;
82
83
  description: string;
@@ -92,6 +93,19 @@ export interface SeedBundleV2 {
92
93
  capacityProviders: SeedCapacityProviderPrerequisite[];
93
94
  };
94
95
  }
96
+ export interface SeedBundleV3 extends SeedBundleBody {
97
+ schemaVersion: typeof SEED_BUNDLE_SCHEMA_VERSION;
98
+ }
99
+ /** Historical receipt shape. It is decodable, but cannot be planned or applied. */
100
+ export interface SeedBundleV2 extends Omit<SeedBundleBody, 'resources'> {
101
+ schemaVersion: typeof HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION;
102
+ resources: Omit<SeedBundleBody['resources'], 'projects'> & {
103
+ projects: Array<Omit<SeedProjectResource, 'libraryRepository'> & {
104
+ libraryRepository?: string;
105
+ }>;
106
+ };
107
+ }
108
+ export type SeedBundle = SeedBundleV3 | SeedBundleV2;
95
109
  export interface SeedResourceReceipt {
96
110
  resourceKey: string;
97
111
  action: 'create' | 'adopt' | 'update' | 'noop' | 'blocked';
@@ -118,6 +132,7 @@ export interface SeedBundleDiagnostic {
118
132
  path: string;
119
133
  message: string;
120
134
  }
121
- export declare function canonicalSeedBundlePayload(bundle: Omit<SeedBundleV2, 'digest'> | SeedBundleV2): string;
122
- export declare function digestSeedBundle(bundle: Omit<SeedBundleV2, 'digest'> | SeedBundleV2): Promise<`sha256:${string}`>;
123
- export declare function validateSeedBundle(bundle: SeedBundleV2): SeedBundleDiagnostic[];
135
+ export declare function canonicalSeedBundlePayload(bundle: Omit<SeedBundle, 'digest'> | SeedBundle): string;
136
+ export declare function digestSeedBundle(bundle: Omit<SeedBundle, 'digest'> | SeedBundle): Promise<`sha256:${string}`>;
137
+ export declare function validateSeedBundle(bundle: SeedBundle): SeedBundleDiagnostic[];
138
+ export {};
@@ -1,4 +1,5 @@
1
- const SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/v2";
1
+ const SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/v3";
2
+ const HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/v2";
2
3
  function text(value) {
3
4
  return typeof value === "string" && value.trim().length > 0;
4
5
  }
@@ -19,7 +20,11 @@ async function digestSeedBundle(bundle) {
19
20
  function validateSeedBundle(bundle) {
20
21
  const diagnostics = [];
21
22
  const add = (code, path, message) => diagnostics.push({ code, path, message });
22
- if (bundle?.schemaVersion !== SEED_BUNDLE_SCHEMA_VERSION) add("seed_bundle_schema_invalid", "schemaVersion", `schemaVersion must be ${SEED_BUNDLE_SCHEMA_VERSION}.`);
23
+ if (bundle?.schemaVersion === HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION) {
24
+ add("seed_bundle_v2_migration_required", "schemaVersion", `Historical v2 bundles remain readable but must be migrated to ${SEED_BUNDLE_SCHEMA_VERSION} before planning or apply.`);
25
+ } else if (bundle?.schemaVersion !== SEED_BUNDLE_SCHEMA_VERSION) {
26
+ add("seed_bundle_schema_invalid", "schemaVersion", `schemaVersion must be ${SEED_BUNDLE_SCHEMA_VERSION}.`);
27
+ }
23
28
  if (!text(bundle?.name) || !Number.isInteger(bundle?.version) || bundle.version < 1) add("seed_bundle_identity_invalid", "name", "Seed name and positive version are required.");
24
29
  if (!/^sha256:[a-f0-9]{64}$/u.test(bundle?.digest ?? "")) add("seed_bundle_digest_invalid", "digest", "Seed digest must be an exact SHA-256 digest.");
25
30
  const teams = new Set((bundle?.resources?.teams ?? []).map((entry) => entry.key));
@@ -35,9 +40,17 @@ function validateSeedBundle(bundle) {
35
40
  if (!teams.has(project.team)) add("seed_bundle_project_team_unknown", `resources.projects[${index}].team`, "Project references an unknown team.");
36
41
  if (!project.primaryRepository && project.kind !== "content") add("seed_bundle_primary_repository_required", `resources.projects[${index}].primaryRepository`, "Non-content projects require a primary source repository.");
37
42
  if (project.primaryRepository && !repositories.has(project.primaryRepository)) add("seed_bundle_primary_repository_unknown", `resources.projects[${index}].primaryRepository`, "Project primary repository is missing.");
43
+ if (!project.libraryRepository) add("seed_bundle_library_repository_required", `resources.projects[${index}].libraryRepository`, "Every project requires one library repository.");
44
+ if (project.libraryRepository && !repositories.has(project.libraryRepository)) add("seed_bundle_library_repository_unknown", `resources.projects[${index}].libraryRepository`, "Project library repository is missing.");
38
45
  }
39
46
  for (const [index, repository] of (bundle?.resources?.repositories ?? []).entries()) {
40
- if (repository.role === "content") add("seed_bundle_content_repository_forbidden", `resources.repositories[${index}].role`, "Virtual knowledge repositories are derived by TreeDX and must not be declared as Git repositories.");
47
+ if (repository.role === "content") add("seed_bundle_content_repository_renamed", `resources.repositories[${index}].role`, "Repository-backed knowledge uses role library.");
48
+ }
49
+ for (const [index, project] of (bundle?.resources?.projects ?? []).entries()) {
50
+ const library = (bundle?.resources?.repositories ?? []).find((entry) => entry.key === project.libraryRepository);
51
+ if (library && (library.role !== "library" || library.project !== project.key)) add("seed_bundle_library_repository_mismatch", `resources.projects[${index}].libraryRepository`, "libraryRepository must reference a library-role repository owned by this project.");
52
+ const projectLibraries = (bundle?.resources?.repositories ?? []).filter((entry) => entry.project === project.key && entry.role === "library");
53
+ if (projectLibraries.length !== 1) add("seed_bundle_library_repository_count_invalid", `resources.projects[${index}].libraryRepository`, "Every project must declare exactly one library-role repository.");
41
54
  }
42
55
  for (const [index, provider] of (bundle?.runtime?.capacityProviders ?? []).entries()) {
43
56
  if (!teams.has(provider.team)) add("seed_bundle_provider_team_unknown", `runtime.capacityProviders[${index}].team`, "Provider references an unknown team.");
@@ -47,6 +60,7 @@ function validateSeedBundle(bundle) {
47
60
  return diagnostics;
48
61
  }
49
62
  export {
63
+ HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION,
50
64
  SEED_BUNDLE_SCHEMA_VERSION,
51
65
  canonicalSeedBundlePayload,
52
66
  digestSeedBundle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.13.0-rc.34",
3
+ "version": "0.13.0-rc.36",
4
4
  "description": "Portable TreeSeed contracts, standards, and typed remote control-plane clients.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {