@treeseed/sdk 0.13.0-rc.1 → 0.13.0-rc.3
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/dist/agent-capacity/contracts/index.d.ts +0 -1
- package/dist/agent-capacity/contracts/index.js +0 -1
- package/dist/capacity/agents/agent-capacity.d.ts +35 -1
- package/dist/capacity/agents/agent-capacity.js +41 -1
- package/dist/market-client/projects/agents/contracts/agent-deployments.d.ts +1 -1
- package/dist/operator-contracts/canonical-command-tree.d.ts +3 -0
- package/dist/operator-contracts/canonical-command-tree.js +81 -0
- package/dist/operator-contracts/command-tree.d.ts +79 -0
- package/dist/operator-contracts/command-tree.js +53 -0
- package/dist/operator-contracts/index.d.ts +4 -0
- package/dist/operator-contracts/index.js +4 -0
- package/dist/operator-contracts/workday-lifecycle.d.ts +157 -0
- package/dist/operator-contracts/workday-lifecycle.js +65 -0
- package/dist/operator-contracts/workday-profile.d.ts +66 -0
- package/dist/operator-contracts/workday-profile.js +89 -0
- package/dist/scripts/packages/publish-package.js +37 -1
- package/dist/scripts/packages/published-package-readback.js +61 -0
- package/dist/scripts/packages/verify-published-package.js +3 -26
- package/dist/scripts/standards/package-acceptance.js +2 -1
- package/dist/scripts/standards/verify-api-consumer.js +7 -2
- package/package.json +5 -1
|
@@ -14,7 +14,6 @@ export * from './support/failure-records.js';
|
|
|
14
14
|
export * from './support/financial-records.js';
|
|
15
15
|
export * from './support/time-capacity.js';
|
|
16
16
|
export * from './support/authority/execution-mode.js';
|
|
17
|
-
export * from './support/operator-surface.js';
|
|
18
17
|
export * from './support/planning-records.js';
|
|
19
18
|
export * from './support/research-citation.js';
|
|
20
19
|
export * from './support/research-source-policy.js';
|
|
@@ -14,7 +14,6 @@ export * from "./support/failure-records.js";
|
|
|
14
14
|
export * from "./support/financial-records.js";
|
|
15
15
|
export * from "./support/time-capacity.js";
|
|
16
16
|
export * from "./support/authority/execution-mode.js";
|
|
17
|
-
export * from "./support/operator-surface.js";
|
|
18
17
|
export * from "./support/planning-records.js";
|
|
19
18
|
export * from "./support/research-citation.js";
|
|
20
19
|
export * from "./support/research-source-policy.js";
|
|
@@ -1,2 +1,36 @@
|
|
|
1
1
|
export type { AgentKernelPolicy, AgentKernelProfile, ProjectAgentClass, ProjectAgentClassStatus } from '../../agent-capacity/contracts/projects/agents/project-agent-class.js';
|
|
2
|
-
export * from '../../agent-capacity/
|
|
2
|
+
export * from '../../agent-capacity/allocation.js';
|
|
3
|
+
export * from '../../agent-capacity/artifacts.js';
|
|
4
|
+
export { deriveAgentRuntimeStatus } from '../../agent-capacity/authoring/agent-definition-authoring.js';
|
|
5
|
+
export { compileDefaultChatActivityProfile } from '../../agent-capacity/authoring/chat-activity-profile.js';
|
|
6
|
+
export { validateGroupDefinition, validateGroupEdgeDefinition } from '../../agent-capacity/authoring/group-definition-authoring.js';
|
|
7
|
+
export * from '../../agent-capacity/authority/agent-authority-presets.js';
|
|
8
|
+
export * from '../../agent-capacity/authority/assignment-authority-probe.js';
|
|
9
|
+
export * from '../../agent-capacity/artifact-mutation-receipt.js';
|
|
10
|
+
export * from '../../agent-capacity/cooperative-planning.js';
|
|
11
|
+
export * from '../../agent-capacity/planning-graph.js';
|
|
12
|
+
export * from '../../agent-capacity/policy/index.js';
|
|
13
|
+
export * from '../../agent-capacity/validation/index.js';
|
|
14
|
+
export * from '../../agent-capacity/workday.js';
|
|
15
|
+
export * from '../../agent-capacity/contracts/capacity/assignments/assignment-records.js';
|
|
16
|
+
export * from '../../agent-capacity/contracts/capacity/communication/communication-records.js';
|
|
17
|
+
export * from '../../agent-capacity/contracts/capacity/providers/supply-policy.js';
|
|
18
|
+
export * from '../../agent-capacity/contracts/capacity/workdays/workday-records.js';
|
|
19
|
+
export * from '../../agent-capacity/contracts/configuration/configuration.js';
|
|
20
|
+
export * from '../../agent-capacity/contracts/operations/research-workflow.js';
|
|
21
|
+
export * from '../../agent-capacity/contracts/operations/workflow-promotion.js';
|
|
22
|
+
export * from '../../agent-capacity/contracts/projects/agents/project-agent-class.js';
|
|
23
|
+
export * from '../../agent-capacity/contracts/runtime/runtime-observability.js';
|
|
24
|
+
export * from '../../agent-capacity/contracts/support/decision-work.js';
|
|
25
|
+
export * from '../../agent-capacity/contracts/support/agent-lab-monitoring.js';
|
|
26
|
+
export * from '../../agent-capacity/contracts/support/agent-atlas.js';
|
|
27
|
+
export * from '../../agent-capacity/contracts/support/failure-records.js';
|
|
28
|
+
export * from '../../agent-capacity/contracts/support/financial-records.js';
|
|
29
|
+
export * from '../../agent-capacity/contracts/support/time-capacity.js';
|
|
30
|
+
export * from '../../agent-capacity/contracts/support/authority/execution-mode.js';
|
|
31
|
+
export * from '../../agent-capacity/contracts/support/planning-records.js';
|
|
32
|
+
export * from '../../agent-capacity/contracts/support/research-citation.js';
|
|
33
|
+
export * from '../../agent-capacity/contracts/support/research-source-policy.js';
|
|
34
|
+
export * from '../../governance/groups/contracts.js';
|
|
35
|
+
export * from '../../governance/groups/membership.js';
|
|
36
|
+
export * from '../../governance/groups/signal-scope.js';
|
|
@@ -1 +1,41 @@
|
|
|
1
|
-
export * from "../../agent-capacity/
|
|
1
|
+
export * from "../../agent-capacity/allocation.js";
|
|
2
|
+
export * from "../../agent-capacity/artifacts.js";
|
|
3
|
+
import { deriveAgentRuntimeStatus } from "../../agent-capacity/authoring/agent-definition-authoring.js";
|
|
4
|
+
import { compileDefaultChatActivityProfile } from "../../agent-capacity/authoring/chat-activity-profile.js";
|
|
5
|
+
import { validateGroupDefinition, validateGroupEdgeDefinition } from "../../agent-capacity/authoring/group-definition-authoring.js";
|
|
6
|
+
export * from "../../agent-capacity/authority/agent-authority-presets.js";
|
|
7
|
+
export * from "../../agent-capacity/authority/assignment-authority-probe.js";
|
|
8
|
+
export * from "../../agent-capacity/artifact-mutation-receipt.js";
|
|
9
|
+
export * from "../../agent-capacity/cooperative-planning.js";
|
|
10
|
+
export * from "../../agent-capacity/planning-graph.js";
|
|
11
|
+
export * from "../../agent-capacity/policy/index.js";
|
|
12
|
+
export * from "../../agent-capacity/validation/index.js";
|
|
13
|
+
export * from "../../agent-capacity/workday.js";
|
|
14
|
+
export * from "../../agent-capacity/contracts/capacity/assignments/assignment-records.js";
|
|
15
|
+
export * from "../../agent-capacity/contracts/capacity/communication/communication-records.js";
|
|
16
|
+
export * from "../../agent-capacity/contracts/capacity/providers/supply-policy.js";
|
|
17
|
+
export * from "../../agent-capacity/contracts/capacity/workdays/workday-records.js";
|
|
18
|
+
export * from "../../agent-capacity/contracts/configuration/configuration.js";
|
|
19
|
+
export * from "../../agent-capacity/contracts/operations/research-workflow.js";
|
|
20
|
+
export * from "../../agent-capacity/contracts/operations/workflow-promotion.js";
|
|
21
|
+
export * from "../../agent-capacity/contracts/projects/agents/project-agent-class.js";
|
|
22
|
+
export * from "../../agent-capacity/contracts/runtime/runtime-observability.js";
|
|
23
|
+
export * from "../../agent-capacity/contracts/support/decision-work.js";
|
|
24
|
+
export * from "../../agent-capacity/contracts/support/agent-lab-monitoring.js";
|
|
25
|
+
export * from "../../agent-capacity/contracts/support/agent-atlas.js";
|
|
26
|
+
export * from "../../agent-capacity/contracts/support/failure-records.js";
|
|
27
|
+
export * from "../../agent-capacity/contracts/support/financial-records.js";
|
|
28
|
+
export * from "../../agent-capacity/contracts/support/time-capacity.js";
|
|
29
|
+
export * from "../../agent-capacity/contracts/support/authority/execution-mode.js";
|
|
30
|
+
export * from "../../agent-capacity/contracts/support/planning-records.js";
|
|
31
|
+
export * from "../../agent-capacity/contracts/support/research-citation.js";
|
|
32
|
+
export * from "../../agent-capacity/contracts/support/research-source-policy.js";
|
|
33
|
+
export * from "../../governance/groups/contracts.js";
|
|
34
|
+
export * from "../../governance/groups/membership.js";
|
|
35
|
+
export * from "../../governance/groups/signal-scope.js";
|
|
36
|
+
export {
|
|
37
|
+
compileDefaultChatActivityProfile,
|
|
38
|
+
deriveAgentRuntimeStatus,
|
|
39
|
+
validateGroupDefinition,
|
|
40
|
+
validateGroupEdgeDefinition
|
|
41
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MarketClient } from '../../../../entrypoints/clients/market-client.js';
|
|
2
|
-
import type { AgentDeploymentBindings } from '../../../../agent-capacity/
|
|
2
|
+
import type { AgentDeploymentBindings } from '../../../../agent-capacity/authoring/agent-deployment.js';
|
|
3
3
|
export type AgentDeploymentRequest = {
|
|
4
4
|
sourceProjectId: string;
|
|
5
5
|
targetProjectId: string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const planOption = { name: "--plan", description: "Return the exact proposed outcome without mutation.", type: "boolean" };
|
|
2
|
+
function leaf(segment, kind = "read", argument, confirmation = "never") {
|
|
3
|
+
const value = {
|
|
4
|
+
segment,
|
|
5
|
+
description: `${segment[0].toUpperCase()}${segment.slice(1)} the selected resource.`,
|
|
6
|
+
kind,
|
|
7
|
+
arguments: argument ? [{ name: argument, description: `${argument} identity or path.`, required: true }] : void 0,
|
|
8
|
+
options: kind === "mutation" ? [planOption] : void 0,
|
|
9
|
+
authorization: kind === "mutation" ? { capability: `command.${segment}`, confirmation } : void 0,
|
|
10
|
+
resultSchemaId: `treeseed.command.${segment}/v1`
|
|
11
|
+
};
|
|
12
|
+
return { nodeType: "leaf", ...value };
|
|
13
|
+
}
|
|
14
|
+
function branch(segment, children) {
|
|
15
|
+
return { nodeType: "branch", segment, description: `${segment[0].toUpperCase()}${segment.slice(1)} operations.`, children };
|
|
16
|
+
}
|
|
17
|
+
const TREESEED_COMMAND_TREE_V1 = {
|
|
18
|
+
schemaVersion: "treeseed.command-tree/v1",
|
|
19
|
+
executable: "trsd",
|
|
20
|
+
commands: [
|
|
21
|
+
branch("auth", [leaf("login", "mutation", void 0, "credential"), leaf("logout", "mutation"), leaf("status")]),
|
|
22
|
+
branch("secrets", [leaf("list"), leaf("status"), leaf("unlock", "mutation", void 0, "credential"), leaf("lock", "mutation"), leaf("rotate", "mutation", void 0, "credential")]),
|
|
23
|
+
branch("agents", [
|
|
24
|
+
leaf("list"),
|
|
25
|
+
leaf("show", "read", "agent"),
|
|
26
|
+
leaf("validate"),
|
|
27
|
+
leaf("diff"),
|
|
28
|
+
leaf("diagnose"),
|
|
29
|
+
branch("classes", [leaf("list"), leaf("show", "read", "class")]),
|
|
30
|
+
branch("bindings", [leaf("list"), leaf("show", "read", "binding"), leaf("explain", "read", "binding")])
|
|
31
|
+
]),
|
|
32
|
+
branch("providers", [
|
|
33
|
+
leaf("list"),
|
|
34
|
+
leaf("show", "read", "provider"),
|
|
35
|
+
leaf("status", "read", "provider"),
|
|
36
|
+
leaf("diagnose", "read", "provider"),
|
|
37
|
+
leaf("connect", "mutation", void 0, "credential"),
|
|
38
|
+
leaf("disconnect", "mutation", "connection", "destructive"),
|
|
39
|
+
branch("requests", [leaf("list"), leaf("show", "read", "request"), leaf("approve", "mutation", "request", "authority"), leaf("reject", "mutation", "request", "authority")]),
|
|
40
|
+
branch("credentials", [leaf("status", "read", "connection"), leaf("rotate", "mutation", "connection", "credential"), leaf("revoke", "mutation", "connection", "irreversible")]),
|
|
41
|
+
branch("offers", [leaf("show", "read", "connection"), leaf("validate", "read", "file"), leaf("plan", "read", "file"), leaf("apply", "mutation", "file", "authority")])
|
|
42
|
+
]),
|
|
43
|
+
branch("capacity", [leaf("status"), leaf("explain"), leaf("usage"), leaf("ledger"), leaf("audit")]),
|
|
44
|
+
branch("plans", [leaf("list"), leaf("show", "read", "plan"), leaf("explain", "read", "plan"), { nodeType: "leaf", segment: "diff", description: "Compare two API-derived plans.", kind: "read", arguments: [{ name: "left", description: "Left plan identity.", required: true }, { name: "right", description: "Right plan identity.", required: true }], resultSchemaId: "treeseed.command.plans.diff/v1" }]),
|
|
45
|
+
branch("workdays", [
|
|
46
|
+
branch("profiles", [leaf("list"), leaf("show", "read", "profile"), leaf("validate", "read", "file")]),
|
|
47
|
+
leaf("plan"),
|
|
48
|
+
leaf("start", "mutation", void 0, "authority"),
|
|
49
|
+
leaf("list"),
|
|
50
|
+
leaf("show", "read", "workday"),
|
|
51
|
+
leaf("watch", "read", "workday"),
|
|
52
|
+
leaf("pause", "mutation", "workday", "authority"),
|
|
53
|
+
leaf("resume", "mutation", "workday", "authority"),
|
|
54
|
+
leaf("stop", "mutation", "workday", "destructive"),
|
|
55
|
+
leaf("cancel", "mutation", "workday", "destructive"),
|
|
56
|
+
branch("schedules", [leaf("list"), leaf("show", "read", "schedule"), leaf("plan"), leaf("start", "mutation", void 0, "authority"), leaf("pause", "mutation", "schedule", "authority"), leaf("resume", "mutation", "schedule", "authority"), leaf("retire", "mutation", "schedule", "destructive")])
|
|
57
|
+
]),
|
|
58
|
+
branch("assignments", [leaf("list"), leaf("show", "read", "assignment"), leaf("explain", "read", "assignment"), leaf("watch", "read", "assignment"), leaf("retry", "mutation", "assignment", "authority"), leaf("cancel", "mutation", "assignment", "destructive"), leaf("artifacts", "read", "assignment")]),
|
|
59
|
+
leaf("save", "mutation"),
|
|
60
|
+
leaf("stage", "mutation", void 0, "authority"),
|
|
61
|
+
leaf("release", "mutation", void 0, "production"),
|
|
62
|
+
leaf("status"),
|
|
63
|
+
leaf("diagnose")
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
function listCommandPaths(tree = TREESEED_COMMAND_TREE_V1) {
|
|
67
|
+
const paths = [];
|
|
68
|
+
const visit = (nodes, parent) => {
|
|
69
|
+
for (const node of nodes) {
|
|
70
|
+
const path = [...parent, node.segment];
|
|
71
|
+
if (node.nodeType === "leaf") paths.push(path.join(" "));
|
|
72
|
+
else visit(node.children, path);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
visit(tree.commands, []);
|
|
76
|
+
return paths;
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
TREESEED_COMMAND_TREE_V1,
|
|
80
|
+
listCommandPaths
|
|
81
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type CommandOperationKind = 'read' | 'mutation';
|
|
2
|
+
export type CommandExecutionMode = 'execute' | 'plan';
|
|
3
|
+
export type CommandErrorCategory = 'unknown_command' | 'invalid_input' | 'ambiguous_context' | 'authentication_required' | 'authorization_denied' | 'confirmation_required' | 'policy_blocked' | 'stale_preflight' | 'conflict' | 'not_found' | 'provider_unavailable' | 'rate_limited' | 'internal_error';
|
|
4
|
+
export interface CommandArgumentDescriptor {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
required: boolean;
|
|
8
|
+
multiple?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CommandOptionDescriptor {
|
|
11
|
+
name: `--${string}`;
|
|
12
|
+
description: string;
|
|
13
|
+
type: 'boolean' | 'string' | 'number' | 'string[]';
|
|
14
|
+
required?: boolean;
|
|
15
|
+
defaultValue?: boolean | string | number | string[];
|
|
16
|
+
sensitive?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface CommandAuthorizationDescriptor {
|
|
19
|
+
capability: string;
|
|
20
|
+
confirmation: 'never' | 'destructive' | 'credential' | 'authority' | 'production' | 'irreversible';
|
|
21
|
+
}
|
|
22
|
+
export interface CommandLeafDescriptor {
|
|
23
|
+
segment: string;
|
|
24
|
+
description: string;
|
|
25
|
+
kind: CommandOperationKind;
|
|
26
|
+
arguments?: CommandArgumentDescriptor[];
|
|
27
|
+
options?: CommandOptionDescriptor[];
|
|
28
|
+
authorization?: CommandAuthorizationDescriptor;
|
|
29
|
+
resultSchemaId: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CommandBranchDescriptor {
|
|
32
|
+
segment: string;
|
|
33
|
+
description: string;
|
|
34
|
+
children: CommandNodeDescriptor[];
|
|
35
|
+
}
|
|
36
|
+
export type CommandNodeDescriptor = ({
|
|
37
|
+
nodeType: 'branch';
|
|
38
|
+
} & CommandBranchDescriptor) | ({
|
|
39
|
+
nodeType: 'leaf';
|
|
40
|
+
} & CommandLeafDescriptor);
|
|
41
|
+
export interface CommandTreeDescriptor {
|
|
42
|
+
schemaVersion: 'treeseed.command-tree/v1';
|
|
43
|
+
executable: 'trsd';
|
|
44
|
+
commands: CommandNodeDescriptor[];
|
|
45
|
+
}
|
|
46
|
+
export interface CommandBlocker {
|
|
47
|
+
code: string;
|
|
48
|
+
message: string;
|
|
49
|
+
path?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface CommandReceiptReference {
|
|
52
|
+
kind: string;
|
|
53
|
+
id: string;
|
|
54
|
+
digest?: string;
|
|
55
|
+
uri?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface CommandResultEnvelope<TResult = unknown> {
|
|
58
|
+
schemaVersion: 'treeseed.command-result/v1';
|
|
59
|
+
commandPath: string[];
|
|
60
|
+
mode: CommandExecutionMode;
|
|
61
|
+
ok: boolean;
|
|
62
|
+
result: TResult | null;
|
|
63
|
+
error: {
|
|
64
|
+
category: CommandErrorCategory;
|
|
65
|
+
code: string;
|
|
66
|
+
message: string;
|
|
67
|
+
} | null;
|
|
68
|
+
warnings: string[];
|
|
69
|
+
blockers: CommandBlocker[];
|
|
70
|
+
receipts: CommandReceiptReference[];
|
|
71
|
+
nextActions: string[];
|
|
72
|
+
}
|
|
73
|
+
export interface CommandTreeDiagnostic {
|
|
74
|
+
code: string;
|
|
75
|
+
path: string;
|
|
76
|
+
message: string;
|
|
77
|
+
}
|
|
78
|
+
export declare function validateCommandTree(tree: CommandTreeDescriptor): CommandTreeDiagnostic[];
|
|
79
|
+
export declare function createCommandResult<TResult>(input: Omit<CommandResultEnvelope<TResult>, 'schemaVersion'>): CommandResultEnvelope<TResult>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const SEGMENT = /^[a-z][a-z0-9]*$/;
|
|
2
|
+
const FORBIDDEN_AGENT_MANAGEMENT_OPTIONS = /* @__PURE__ */ new Set([
|
|
3
|
+
"--commit",
|
|
4
|
+
"--commit-sha",
|
|
5
|
+
"--source-ref",
|
|
6
|
+
"--expected-base",
|
|
7
|
+
"--expected-commit",
|
|
8
|
+
"--branch",
|
|
9
|
+
"--checkpoint",
|
|
10
|
+
"--integration"
|
|
11
|
+
]);
|
|
12
|
+
function validateCommandTree(tree) {
|
|
13
|
+
const diagnostics = [];
|
|
14
|
+
if (tree.schemaVersion !== "treeseed.command-tree/v1") diagnostics.push({ code: "schema_version_invalid", path: "schemaVersion", message: "Command tree schemaVersion must be treeseed.command-tree/v1." });
|
|
15
|
+
if (tree.executable !== "trsd") diagnostics.push({ code: "executable_invalid", path: "executable", message: "trsd is the only public executable." });
|
|
16
|
+
const visit = (nodes, parent) => {
|
|
17
|
+
const segments = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const [index, node] of nodes.entries()) {
|
|
19
|
+
const path = [...parent, node.segment];
|
|
20
|
+
const diagnosticPath = `commands.${path.join(".")}`;
|
|
21
|
+
if (!SEGMENT.test(node.segment)) diagnostics.push({ code: "command_segment_invalid", path: diagnosticPath, message: "Command path segments must be lowercase alphanumeric words without colons or hyphens." });
|
|
22
|
+
if (segments.has(node.segment)) diagnostics.push({ code: "command_path_duplicate", path: diagnosticPath, message: `Duplicate command segment ${node.segment}.` });
|
|
23
|
+
segments.add(node.segment);
|
|
24
|
+
const raw = node;
|
|
25
|
+
if ("alias" in raw || "aliases" in raw) diagnostics.push({ code: "command_alias_forbidden", path: diagnosticPath, message: "Public command aliases are forbidden." });
|
|
26
|
+
if (node.nodeType === "branch") {
|
|
27
|
+
if ("kind" in raw || "resultSchemaId" in raw || "options" in raw || "arguments" in raw) diagnostics.push({ code: "command_node_ambiguous", path: diagnosticPath, message: "Intermediate command nodes cannot also declare leaf behavior." });
|
|
28
|
+
if (!Array.isArray(node.children) || node.children.length === 0) diagnostics.push({ code: "command_branch_empty", path: diagnosticPath, message: "Intermediate command nodes must contain at least one child." });
|
|
29
|
+
visit(node.children ?? [], path);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if ("children" in raw) diagnostics.push({ code: "command_node_ambiguous", path: diagnosticPath, message: "Leaf command nodes cannot contain child commands." });
|
|
33
|
+
if (!node.resultSchemaId.trim()) diagnostics.push({ code: "result_schema_required", path: `${diagnosticPath}.resultSchemaId`, message: "Leaf commands require a stable result schema." });
|
|
34
|
+
const optionNames = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const option of node.options ?? []) {
|
|
36
|
+
if (optionNames.has(option.name)) diagnostics.push({ code: "command_option_duplicate", path: `${diagnosticPath}.options.${index}`, message: `Duplicate option ${option.name}.` });
|
|
37
|
+
optionNames.add(option.name);
|
|
38
|
+
if (option.name === "--execute") diagnostics.push({ code: "execute_option_forbidden", path: `${diagnosticPath}.options.${option.name}`, message: "Mutations execute by default; --plan is the only dry-run switch." });
|
|
39
|
+
if (parent[0] === "agents" && FORBIDDEN_AGENT_MANAGEMENT_OPTIONS.has(option.name)) diagnostics.push({ code: "agent_internal_option_forbidden", path: `${diagnosticPath}.options.${option.name}`, message: "Agent management commands cannot expose repository integration mechanics." });
|
|
40
|
+
}
|
|
41
|
+
if (node.kind === "mutation" && !optionNames.has("--plan")) diagnostics.push({ code: "mutation_plan_option_required", path: `${diagnosticPath}.options`, message: "Every mutation must expose universal zero-mutation --plan mode." });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
visit(tree.commands, []);
|
|
45
|
+
return diagnostics;
|
|
46
|
+
}
|
|
47
|
+
function createCommandResult(input) {
|
|
48
|
+
return { schemaVersion: "treeseed.command-result/v1", ...input };
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
createCommandResult,
|
|
52
|
+
validateCommandTree
|
|
53
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { type WorkdayBorrowingEvidence } from './workday-profile.js';
|
|
2
|
+
export type WorkdayDemandMode = 'planning' | 'acting';
|
|
3
|
+
export interface WorkdayIntent {
|
|
4
|
+
schemaVersion: 'treeseed.workday-intent/v1';
|
|
5
|
+
teamId: string;
|
|
6
|
+
profileId: string;
|
|
7
|
+
projects: 'all' | string[];
|
|
8
|
+
startsAt: string;
|
|
9
|
+
endsAt?: string;
|
|
10
|
+
durationSeconds?: number;
|
|
11
|
+
objectiveFilters?: string[];
|
|
12
|
+
operatorConstraints?: {
|
|
13
|
+
providerIds?: string[];
|
|
14
|
+
maxConcurrency?: number;
|
|
15
|
+
reservePercent?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface WorkdayActingAuthorityEvidence {
|
|
19
|
+
decisionId: string;
|
|
20
|
+
decisionStatus: 'approved';
|
|
21
|
+
executionInputId: string;
|
|
22
|
+
executionInputStatus: 'accepted';
|
|
23
|
+
estimateId: string;
|
|
24
|
+
capacityPlanId: string;
|
|
25
|
+
capacityPlanDigest: string;
|
|
26
|
+
}
|
|
27
|
+
export interface WorkdaySelectedDemand {
|
|
28
|
+
id: string;
|
|
29
|
+
projectId: string;
|
|
30
|
+
sourceType: string;
|
|
31
|
+
sourceId: string;
|
|
32
|
+
mode: WorkdayDemandMode;
|
|
33
|
+
classSlug: string;
|
|
34
|
+
requestedSeconds: number;
|
|
35
|
+
priority: number;
|
|
36
|
+
actingAuthority?: WorkdayActingAuthorityEvidence;
|
|
37
|
+
}
|
|
38
|
+
export interface WorkdayClassAccounting {
|
|
39
|
+
classSlug: string;
|
|
40
|
+
allocatedSeconds: number;
|
|
41
|
+
borrowedSeconds: number;
|
|
42
|
+
lentSeconds: number;
|
|
43
|
+
idleSeconds: number;
|
|
44
|
+
reservedSeconds: number;
|
|
45
|
+
activeSeconds: number;
|
|
46
|
+
releasedSeconds: number;
|
|
47
|
+
overrunSeconds: number;
|
|
48
|
+
}
|
|
49
|
+
export interface WorkdayPreflightReceipt {
|
|
50
|
+
schemaVersion: 'treeseed.workday-preflight/v1';
|
|
51
|
+
id: string;
|
|
52
|
+
teamId: string;
|
|
53
|
+
intentDigest: string;
|
|
54
|
+
profileId: string;
|
|
55
|
+
profileVersion: string;
|
|
56
|
+
profileGeneration: number;
|
|
57
|
+
profileDigest: string;
|
|
58
|
+
demandSetDigest: string;
|
|
59
|
+
providerCapacityDigest: string;
|
|
60
|
+
authorizationDigest: string;
|
|
61
|
+
reservationDigest: string;
|
|
62
|
+
selectedDemands: WorkdaySelectedDemand[];
|
|
63
|
+
classAccounting: WorkdayClassAccounting[];
|
|
64
|
+
borrowing: WorkdayBorrowingEvidence[];
|
|
65
|
+
startsAt: string;
|
|
66
|
+
endsAt: string;
|
|
67
|
+
maxConcurrency: number;
|
|
68
|
+
reserveSeconds: number;
|
|
69
|
+
preflightDigest: string;
|
|
70
|
+
expiresAt: string;
|
|
71
|
+
}
|
|
72
|
+
export interface WorkdayPreflightObservation {
|
|
73
|
+
profileGeneration: number;
|
|
74
|
+
profileDigest: string;
|
|
75
|
+
demandSetDigest: string;
|
|
76
|
+
providerCapacityDigest: string;
|
|
77
|
+
authorizationDigest: string;
|
|
78
|
+
reservationDigest: string;
|
|
79
|
+
}
|
|
80
|
+
export interface WorkdayStartRequest {
|
|
81
|
+
preflightId: string;
|
|
82
|
+
preflightDigest: string;
|
|
83
|
+
idempotencyKey: string;
|
|
84
|
+
}
|
|
85
|
+
export interface WorkdayStartReceipt {
|
|
86
|
+
schemaVersion: 'treeseed.workday-start-receipt/v1';
|
|
87
|
+
workdayId: string;
|
|
88
|
+
preflightId: string;
|
|
89
|
+
preflightDigest: string;
|
|
90
|
+
acceptedCapacityPlanIds: string[];
|
|
91
|
+
assignmentIds: string[];
|
|
92
|
+
reservationIds: string[];
|
|
93
|
+
startedAt: string;
|
|
94
|
+
providerReceiptRefs: string[];
|
|
95
|
+
transactionReceiptId: string;
|
|
96
|
+
}
|
|
97
|
+
export interface WorkdaySchedule {
|
|
98
|
+
schemaVersion: 'treeseed.workday-schedule/v1';
|
|
99
|
+
id: string;
|
|
100
|
+
teamId: string;
|
|
101
|
+
profileId: string;
|
|
102
|
+
profileVersion: string;
|
|
103
|
+
projectScope: 'all' | string[];
|
|
104
|
+
cadence: {
|
|
105
|
+
timezone: string;
|
|
106
|
+
expression: string;
|
|
107
|
+
};
|
|
108
|
+
durationSeconds: number;
|
|
109
|
+
status: 'active' | 'paused' | 'retired';
|
|
110
|
+
nextStartAt: string | null;
|
|
111
|
+
}
|
|
112
|
+
export interface WorkdaySettlement {
|
|
113
|
+
schemaVersion: 'treeseed.workday-settlement/v1';
|
|
114
|
+
workdayId: string;
|
|
115
|
+
status: 'completed' | 'cancelled' | 'failed' | 'degraded';
|
|
116
|
+
preflightDigest: string;
|
|
117
|
+
classAccounting: WorkdayClassAccounting[];
|
|
118
|
+
assignmentIds: string[];
|
|
119
|
+
releasedReservationIds: string[];
|
|
120
|
+
artifactRefs: string[];
|
|
121
|
+
startedAt: string;
|
|
122
|
+
completedAt: string;
|
|
123
|
+
settlementDigest: string;
|
|
124
|
+
}
|
|
125
|
+
export interface RepositoryProfileGenerationReceipt {
|
|
126
|
+
schemaVersion: 'treeseed.repository-profile-generation/v1';
|
|
127
|
+
repository: string;
|
|
128
|
+
ref: string;
|
|
129
|
+
commit: string;
|
|
130
|
+
path: string;
|
|
131
|
+
profileId: string;
|
|
132
|
+
profileVersion: string;
|
|
133
|
+
profileDigest: string;
|
|
134
|
+
generation: number;
|
|
135
|
+
indexedAt: string;
|
|
136
|
+
}
|
|
137
|
+
export interface RepositoryProfileReconciliationReceipt {
|
|
138
|
+
schemaVersion: 'treeseed.repository-profile-reconciliation/v1';
|
|
139
|
+
repository: string;
|
|
140
|
+
observedCommit: string;
|
|
141
|
+
previousGeneration: number | null;
|
|
142
|
+
acceptedGeneration: number;
|
|
143
|
+
profileDigests: string[];
|
|
144
|
+
status: 'created' | 'updated' | 'unchanged' | 'rejected';
|
|
145
|
+
diagnostics: string[];
|
|
146
|
+
receiptDigest: string;
|
|
147
|
+
}
|
|
148
|
+
export interface WorkdayLifecycleDiagnostic {
|
|
149
|
+
code: string;
|
|
150
|
+
path: string;
|
|
151
|
+
message: string;
|
|
152
|
+
}
|
|
153
|
+
export declare function validateWorkdayIntent(intent: WorkdayIntent): WorkdayLifecycleDiagnostic[];
|
|
154
|
+
export declare function validateSelectedDemand(demand: WorkdaySelectedDemand): WorkdayLifecycleDiagnostic[];
|
|
155
|
+
export declare function validateWorkdayPreflight(receipt: WorkdayPreflightReceipt, now?: Date): WorkdayLifecycleDiagnostic[];
|
|
156
|
+
export declare function validateWorkdayPreflightFreshness(receipt: WorkdayPreflightReceipt, observed: WorkdayPreflightObservation): WorkdayLifecycleDiagnostic[];
|
|
157
|
+
export declare function validateWorkdaySettlement(settlement: WorkdaySettlement): WorkdayLifecycleDiagnostic[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { validateWorkdayBorrowingEvidence } from "./workday-profile.js";
|
|
2
|
+
function validateWorkdayIntent(intent) {
|
|
3
|
+
const diagnostics = [];
|
|
4
|
+
if (intent.schemaVersion !== "treeseed.workday-intent/v1") diagnostics.push({ code: "schema_version_invalid", path: "schemaVersion", message: "Unsupported workday intent schema." });
|
|
5
|
+
if (!intent.teamId.trim()) diagnostics.push({ code: "team_required", path: "teamId", message: "Team identity is required." });
|
|
6
|
+
if (!intent.profileId.trim()) diagnostics.push({ code: "profile_required", path: "profileId", message: "Allocation profile identity is required." });
|
|
7
|
+
if (intent.endsAt === void 0 === (intent.durationSeconds === void 0)) diagnostics.push({ code: "time_range_ambiguous", path: "endsAt", message: "Specify exactly one of endsAt or durationSeconds." });
|
|
8
|
+
const start = Date.parse(intent.startsAt);
|
|
9
|
+
if (!Number.isFinite(start)) diagnostics.push({ code: "start_invalid", path: "startsAt", message: "startsAt must be an ISO timestamp." });
|
|
10
|
+
if (intent.endsAt !== void 0 && (!Number.isFinite(Date.parse(intent.endsAt)) || Date.parse(intent.endsAt) <= start)) diagnostics.push({ code: "end_invalid", path: "endsAt", message: "endsAt must be a valid timestamp after startsAt." });
|
|
11
|
+
if (intent.durationSeconds !== void 0 && (!Number.isInteger(intent.durationSeconds) || intent.durationSeconds <= 0)) diagnostics.push({ code: "duration_invalid", path: "durationSeconds", message: "durationSeconds must be a positive integer." });
|
|
12
|
+
return diagnostics;
|
|
13
|
+
}
|
|
14
|
+
function validateSelectedDemand(demand) {
|
|
15
|
+
const diagnostics = [];
|
|
16
|
+
if (demand.requestedSeconds <= 0 || !Number.isInteger(demand.requestedSeconds)) diagnostics.push({ code: "requested_seconds_invalid", path: "requestedSeconds", message: "Demand duration must be a positive integer." });
|
|
17
|
+
if (demand.mode === "acting" && !demand.actingAuthority) diagnostics.push({ code: "acting_authority_required", path: "actingAuthority", message: "Acting demand requires approved decision, accepted execution input, estimate, and API-derived capacity plan evidence." });
|
|
18
|
+
if (demand.mode === "acting" && demand.actingAuthority && (demand.actingAuthority.decisionStatus !== "approved" || demand.actingAuthority.executionInputStatus !== "accepted")) diagnostics.push({ code: "acting_authority_invalid", path: "actingAuthority", message: "Acting authority must bind an approved decision and accepted execution input." });
|
|
19
|
+
if (demand.mode === "acting" && demand.actingAuthority && [demand.actingAuthority.decisionId, demand.actingAuthority.executionInputId, demand.actingAuthority.estimateId, demand.actingAuthority.capacityPlanId, demand.actingAuthority.capacityPlanDigest].some((value) => !value.trim())) diagnostics.push({ code: "acting_authority_identity_missing", path: "actingAuthority", message: "Acting authority must bind non-empty decision, execution, estimate, capacity-plan, and plan-digest identities." });
|
|
20
|
+
return diagnostics;
|
|
21
|
+
}
|
|
22
|
+
function validateWorkdayPreflight(receipt, now = /* @__PURE__ */ new Date()) {
|
|
23
|
+
const diagnostics = receipt.selectedDemands.flatMap((demand, index) => validateSelectedDemand(demand).map((diagnostic) => ({ ...diagnostic, path: `selectedDemands.${index}.${diagnostic.path}` })));
|
|
24
|
+
if (receipt.schemaVersion !== "treeseed.workday-preflight/v1") diagnostics.push({ code: "schema_version_invalid", path: "schemaVersion", message: "Unsupported workday preflight schema." });
|
|
25
|
+
if (!Number.isInteger(receipt.profileGeneration) || receipt.profileGeneration <= 0) diagnostics.push({ code: "profile_generation_invalid", path: "profileGeneration", message: "Profile generation must be a positive integer." });
|
|
26
|
+
for (const field of ["intentDigest", "profileDigest", "demandSetDigest", "providerCapacityDigest", "authorizationDigest", "reservationDigest", "preflightDigest"]) {
|
|
27
|
+
if (!receipt[field].trim()) diagnostics.push({ code: "digest_required", path: field, message: `${field} is required.` });
|
|
28
|
+
}
|
|
29
|
+
const expiry = Date.parse(receipt.expiresAt);
|
|
30
|
+
if (!Number.isFinite(expiry)) diagnostics.push({ code: "preflight_expiry_invalid", path: "expiresAt", message: "Preflight expiry must be a valid timestamp." });
|
|
31
|
+
else if (expiry <= now.getTime()) diagnostics.push({ code: "preflight_expired", path: "expiresAt", message: "Workday preflight has expired and must be regenerated." });
|
|
32
|
+
if (!Number.isFinite(Date.parse(receipt.startsAt)) || !Number.isFinite(Date.parse(receipt.endsAt)) || Date.parse(receipt.endsAt) <= Date.parse(receipt.startsAt)) diagnostics.push({ code: "preflight_time_range_invalid", path: "endsAt", message: "Preflight must bind a valid time range." });
|
|
33
|
+
if (!Number.isInteger(receipt.maxConcurrency) || receipt.maxConcurrency <= 0) diagnostics.push({ code: "preflight_concurrency_invalid", path: "maxConcurrency", message: "Preflight concurrency must be a positive integer." });
|
|
34
|
+
if (!Number.isFinite(receipt.reserveSeconds) || receipt.reserveSeconds < 0) diagnostics.push({ code: "preflight_reserve_invalid", path: "reserveSeconds", message: "Preflight reserve seconds must be finite and non-negative." });
|
|
35
|
+
for (const [index, evidence] of receipt.borrowing.entries()) diagnostics.push(...validateWorkdayBorrowingEvidence(evidence).map((diagnostic) => ({ ...diagnostic, path: `borrowing.${index}.${diagnostic.path}` })));
|
|
36
|
+
if (new Set(receipt.selectedDemands.map((demand) => demand.id)).size !== receipt.selectedDemands.length) diagnostics.push({ code: "preflight_demand_duplicate", path: "selectedDemands", message: "Preflight demand identities must be unique." });
|
|
37
|
+
return diagnostics;
|
|
38
|
+
}
|
|
39
|
+
function validateWorkdayPreflightFreshness(receipt, observed) {
|
|
40
|
+
const diagnostics = [];
|
|
41
|
+
for (const field of ["profileGeneration", "profileDigest", "demandSetDigest", "providerCapacityDigest", "authorizationDigest", "reservationDigest"]) {
|
|
42
|
+
if (receipt[field] !== observed[field]) diagnostics.push({ code: "preflight_state_changed", path: field, message: `${field} changed after preflight; generate a fresh plan.` });
|
|
43
|
+
}
|
|
44
|
+
return diagnostics;
|
|
45
|
+
}
|
|
46
|
+
function validateWorkdaySettlement(settlement) {
|
|
47
|
+
const diagnostics = [];
|
|
48
|
+
if (settlement.schemaVersion !== "treeseed.workday-settlement/v1") diagnostics.push({ code: "schema_version_invalid", path: "schemaVersion", message: "Unsupported workday settlement schema." });
|
|
49
|
+
for (const [index, accounting] of settlement.classAccounting.entries()) {
|
|
50
|
+
for (const [field, value] of Object.entries(accounting).filter(([field2]) => field2 !== "classSlug")) {
|
|
51
|
+
if (!Number.isFinite(value) || value < 0) diagnostics.push({ code: "settlement_accounting_invalid", path: `classAccounting.${index}.${field}`, message: "Settlement seconds must be finite and non-negative." });
|
|
52
|
+
}
|
|
53
|
+
if (accounting.lentSeconds > accounting.allocatedSeconds + accounting.borrowedSeconds) diagnostics.push({ code: "settlement_lending_invalid", path: `classAccounting.${index}.lentSeconds`, message: "A class cannot lend more capacity than it held." });
|
|
54
|
+
}
|
|
55
|
+
if (!settlement.preflightDigest.trim() || !settlement.settlementDigest.trim()) diagnostics.push({ code: "settlement_digest_required", path: "settlementDigest", message: "Settlement must bind its preflight and final accounting digests." });
|
|
56
|
+
if (!Number.isFinite(Date.parse(settlement.startedAt)) || !Number.isFinite(Date.parse(settlement.completedAt)) || Date.parse(settlement.completedAt) < Date.parse(settlement.startedAt)) diagnostics.push({ code: "settlement_time_range_invalid", path: "completedAt", message: "Settlement must bind a valid completion time at or after workday start." });
|
|
57
|
+
return diagnostics;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
validateSelectedDemand,
|
|
61
|
+
validateWorkdayIntent,
|
|
62
|
+
validateWorkdayPreflight,
|
|
63
|
+
validateWorkdayPreflightFreshness,
|
|
64
|
+
validateWorkdaySettlement
|
|
65
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type WorkdayDemandSourcePolicy = 'approved-decisions' | 'planning-inputs' | 'questions' | 'proposals' | 'knowledge-gaps' | 'reliability-campaigns';
|
|
2
|
+
export interface WorkdayClassAllocation {
|
|
3
|
+
classSlug: string;
|
|
4
|
+
minimumPercent: number;
|
|
5
|
+
targetPercent: number;
|
|
6
|
+
maximumPercent: number;
|
|
7
|
+
mayLend: boolean;
|
|
8
|
+
mayBorrow: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkdayAllocationProfile {
|
|
11
|
+
schemaVersion: 'treeseed.workday-allocation-profile/v1';
|
|
12
|
+
id: string;
|
|
13
|
+
version: string;
|
|
14
|
+
projects: 'all' | string[];
|
|
15
|
+
excludedClassSlugs?: Record<string, string[]>;
|
|
16
|
+
classes: WorkdayClassAllocation[];
|
|
17
|
+
demandSources: WorkdayDemandSourcePolicy[];
|
|
18
|
+
actingDecisionRequired: true;
|
|
19
|
+
defaultDurationSeconds: number;
|
|
20
|
+
maxConcurrency: number;
|
|
21
|
+
reservePercent: number;
|
|
22
|
+
prioritization: {
|
|
23
|
+
strategy: 'priority-then-age' | 'age-then-priority' | 'weighted-fair';
|
|
24
|
+
starvationLimitSeconds: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface ProjectAgentClassMembership {
|
|
28
|
+
projectId: string;
|
|
29
|
+
agentId: string;
|
|
30
|
+
classSlug: string;
|
|
31
|
+
status: 'active' | 'paused' | 'archived';
|
|
32
|
+
}
|
|
33
|
+
export interface ProjectAgentIdentity {
|
|
34
|
+
projectId: string;
|
|
35
|
+
agentId: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ProjectClassCatalog {
|
|
38
|
+
projectId: string;
|
|
39
|
+
classSlugs: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface WorkdayProfileDiagnostic {
|
|
42
|
+
code: string;
|
|
43
|
+
path: string;
|
|
44
|
+
message: string;
|
|
45
|
+
}
|
|
46
|
+
export interface WorkdayBorrowingEvidence {
|
|
47
|
+
workdayId: string;
|
|
48
|
+
borrowerClassSlug: string;
|
|
49
|
+
lenderClassSlug: string;
|
|
50
|
+
borrowedSeconds: number;
|
|
51
|
+
lendingPermitted: boolean;
|
|
52
|
+
borrowingPermitted: boolean;
|
|
53
|
+
lenderAllocatedSecondsBefore: number;
|
|
54
|
+
lenderAllocatedSecondsAfter: number;
|
|
55
|
+
lenderMinimumSeconds: number;
|
|
56
|
+
borrowerAllocatedSecondsBefore: number;
|
|
57
|
+
borrowerAllocatedSecondsAfter: number;
|
|
58
|
+
borrowerMaximumSeconds: number;
|
|
59
|
+
profileId: string;
|
|
60
|
+
profileVersion: string;
|
|
61
|
+
profileDigest: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function validateOneClassPerProjectAgent(memberships: ProjectAgentClassMembership[], expectedAgents?: ProjectAgentIdentity[]): WorkdayProfileDiagnostic[];
|
|
64
|
+
export declare function validateWorkdayAllocationProfile(profile: WorkdayAllocationProfile, classCatalogs?: ProjectClassCatalog[]): WorkdayProfileDiagnostic[];
|
|
65
|
+
export declare function normalizeWorkdayAllocationProfile(profile: WorkdayAllocationProfile): WorkdayAllocationProfile;
|
|
66
|
+
export declare function validateWorkdayBorrowingEvidence(evidence: WorkdayBorrowingEvidence): WorkdayProfileDiagnostic[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const PERCENT_TOLERANCE = 1e-6;
|
|
2
|
+
const PROFILE_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3
|
+
function validateOneClassPerProjectAgent(memberships, expectedAgents = []) {
|
|
4
|
+
const diagnostics = [];
|
|
5
|
+
const byAgent = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const membership of memberships) {
|
|
7
|
+
const key = `${membership.projectId}:${membership.agentId}`;
|
|
8
|
+
byAgent.set(key, [...byAgent.get(key) ?? [], membership]);
|
|
9
|
+
}
|
|
10
|
+
for (const [key, values] of byAgent) {
|
|
11
|
+
if (values.length !== 1) diagnostics.push({ code: "agent_class_membership_multiple", path: `memberships.${key}`, message: "Each project agent must have exactly one allocation-class membership." });
|
|
12
|
+
if (values.some((value) => value.status !== "active")) diagnostics.push({ code: "agent_class_membership_inactive", path: `memberships.${key}`, message: "The single allocation-class membership must be active." });
|
|
13
|
+
}
|
|
14
|
+
for (const agent of expectedAgents) {
|
|
15
|
+
const key = `${agent.projectId}:${agent.agentId}`;
|
|
16
|
+
if (!byAgent.has(key)) diagnostics.push({ code: "agent_class_membership_missing", path: `memberships.${key}`, message: "Each project agent must have exactly one allocation-class membership." });
|
|
17
|
+
}
|
|
18
|
+
return diagnostics;
|
|
19
|
+
}
|
|
20
|
+
function validateWorkdayAllocationProfile(profile, classCatalogs) {
|
|
21
|
+
const diagnostics = [];
|
|
22
|
+
if (profile.schemaVersion !== "treeseed.workday-allocation-profile/v1") diagnostics.push({ code: "schema_version_invalid", path: "schemaVersion", message: "Unsupported workday allocation profile schema." });
|
|
23
|
+
if (!profile.id.trim()) diagnostics.push({ code: "profile_id_required", path: "id", message: "Profile id is required." });
|
|
24
|
+
if (!PROFILE_VERSION.test(profile.version)) diagnostics.push({ code: "profile_version_invalid", path: "version", message: "Profile version must be a semantic version." });
|
|
25
|
+
if (profile.actingDecisionRequired !== true) diagnostics.push({ code: "acting_decision_requirement_invalid", path: "actingDecisionRequired", message: "Acting work must require an approved decision." });
|
|
26
|
+
if (profile.projects !== "all" && profile.projects.length === 0) diagnostics.push({ code: "project_scope_empty", path: "projects", message: "Profile project scope must select at least one project or all." });
|
|
27
|
+
if (profile.demandSources.length === 0) diagnostics.push({ code: "demand_sources_empty", path: "demandSources", message: "At least one governed demand source is required." });
|
|
28
|
+
if (new Set(profile.demandSources).size !== profile.demandSources.length) diagnostics.push({ code: "demand_source_duplicate", path: "demandSources", message: "Demand sources must be unique." });
|
|
29
|
+
if (!Number.isInteger(profile.defaultDurationSeconds) || profile.defaultDurationSeconds <= 0) diagnostics.push({ code: "duration_invalid", path: "defaultDurationSeconds", message: "Default duration must be a positive integer number of seconds." });
|
|
30
|
+
if (!Number.isInteger(profile.maxConcurrency) || profile.maxConcurrency <= 0) diagnostics.push({ code: "concurrency_invalid", path: "maxConcurrency", message: "Maximum concurrency must be a positive integer." });
|
|
31
|
+
if (!Number.isFinite(profile.reservePercent) || profile.reservePercent < 0 || profile.reservePercent > 100) diagnostics.push({ code: "reserve_invalid", path: "reservePercent", message: "Reserve percent must be between 0 and 100." });
|
|
32
|
+
if (!Number.isFinite(profile.prioritization.starvationLimitSeconds) || profile.prioritization.starvationLimitSeconds <= 0) diagnostics.push({ code: "starvation_limit_invalid", path: "prioritization.starvationLimitSeconds", message: "Starvation limit must be positive." });
|
|
33
|
+
const classSlugs = /* @__PURE__ */ new Set();
|
|
34
|
+
let minimumTotal = 0;
|
|
35
|
+
let targetTotal = 0;
|
|
36
|
+
let maximumTotal = 0;
|
|
37
|
+
for (const [index, allocation] of profile.classes.entries()) {
|
|
38
|
+
const path = `classes.${index}`;
|
|
39
|
+
if (!allocation.classSlug.trim()) diagnostics.push({ code: "class_slug_required", path: `${path}.classSlug`, message: "Class slug is required." });
|
|
40
|
+
if (classSlugs.has(allocation.classSlug)) diagnostics.push({ code: "class_slug_duplicate", path: `${path}.classSlug`, message: `Class ${allocation.classSlug} is declared more than once.` });
|
|
41
|
+
classSlugs.add(allocation.classSlug);
|
|
42
|
+
const values = [allocation.minimumPercent, allocation.targetPercent, allocation.maximumPercent];
|
|
43
|
+
if (values.some((value) => !Number.isFinite(value) || value < 0 || value > 100) || allocation.minimumPercent > allocation.targetPercent || allocation.targetPercent > allocation.maximumPercent) diagnostics.push({ code: "class_allocation_invalid", path, message: "Class allocation must satisfy 0 <= minimum <= target <= maximum <= 100." });
|
|
44
|
+
minimumTotal += allocation.minimumPercent;
|
|
45
|
+
targetTotal += allocation.targetPercent;
|
|
46
|
+
maximumTotal += allocation.maximumPercent;
|
|
47
|
+
}
|
|
48
|
+
if (minimumTotal > 100 + PERCENT_TOLERANCE) diagnostics.push({ code: "minimum_total_invalid", path: "classes", message: "Class minimum percentages cannot total more than 100." });
|
|
49
|
+
if (Math.abs(targetTotal - 100) > PERCENT_TOLERANCE) diagnostics.push({ code: "target_total_invalid", path: "classes", message: "Class target percentages must total exactly 100." });
|
|
50
|
+
if (maximumTotal < 100 - PERCENT_TOLERANCE) diagnostics.push({ code: "maximum_total_invalid", path: "classes", message: "Class maximum percentages must total at least 100." });
|
|
51
|
+
const selectedProjects = classCatalogs === void 0 ? [] : profile.projects === "all" ? classCatalogs.map((catalog) => catalog.projectId) : profile.projects;
|
|
52
|
+
for (const projectId of selectedProjects) {
|
|
53
|
+
const catalog = classCatalogs?.find((candidate) => candidate.projectId === projectId);
|
|
54
|
+
if (!catalog) {
|
|
55
|
+
diagnostics.push({ code: "project_class_catalog_missing", path: `projects.${projectId}`, message: `No class catalog exists for selected project ${projectId}.` });
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const exclusions = new Set(profile.excludedClassSlugs?.[projectId] ?? []);
|
|
59
|
+
for (const classSlug of classSlugs) {
|
|
60
|
+
if (!catalog.classSlugs.includes(classSlug) && !exclusions.has(classSlug)) diagnostics.push({ code: "class_missing_from_project", path: `projects.${projectId}.${classSlug}`, message: `Class ${classSlug} is missing from project ${projectId} and is not explicitly excluded.` });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return diagnostics;
|
|
64
|
+
}
|
|
65
|
+
function normalizeWorkdayAllocationProfile(profile) {
|
|
66
|
+
return {
|
|
67
|
+
...profile,
|
|
68
|
+
projects: profile.projects === "all" ? "all" : [...new Set(profile.projects)].sort(),
|
|
69
|
+
excludedClassSlugs: profile.excludedClassSlugs ? Object.fromEntries(Object.entries(profile.excludedClassSlugs).sort(([left], [right]) => left.localeCompare(right)).map(([projectId, slugs]) => [projectId, [...new Set(slugs)].sort()])) : void 0,
|
|
70
|
+
classes: [...profile.classes].sort((left, right) => left.classSlug.localeCompare(right.classSlug)),
|
|
71
|
+
demandSources: [...new Set(profile.demandSources)].sort()
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function validateWorkdayBorrowingEvidence(evidence) {
|
|
75
|
+
const diagnostics = [];
|
|
76
|
+
if (!evidence.lendingPermitted) diagnostics.push({ code: "lending_not_permitted", path: "lendingPermitted", message: "The profile does not permit the lender to lend capacity." });
|
|
77
|
+
if (!evidence.borrowingPermitted) diagnostics.push({ code: "borrowing_not_permitted", path: "borrowingPermitted", message: "The profile does not permit the borrower to borrow capacity." });
|
|
78
|
+
if (evidence.borrowedSeconds <= 0) diagnostics.push({ code: "borrowed_seconds_invalid", path: "borrowedSeconds", message: "Borrowed seconds must be positive." });
|
|
79
|
+
if (evidence.lenderAllocatedSecondsAfter < evidence.lenderMinimumSeconds) diagnostics.push({ code: "lender_minimum_violated", path: "lenderAllocatedSecondsAfter", message: "Borrowing cannot reduce the lender below its minimum." });
|
|
80
|
+
if (evidence.borrowerAllocatedSecondsAfter > evidence.borrowerMaximumSeconds) diagnostics.push({ code: "borrower_maximum_violated", path: "borrowerAllocatedSecondsAfter", message: "Borrowing cannot raise the borrower above its maximum." });
|
|
81
|
+
if (evidence.lenderAllocatedSecondsBefore - evidence.lenderAllocatedSecondsAfter !== evidence.borrowedSeconds || evidence.borrowerAllocatedSecondsAfter - evidence.borrowerAllocatedSecondsBefore !== evidence.borrowedSeconds) diagnostics.push({ code: "borrowing_accounting_mismatch", path: "borrowedSeconds", message: "Lender and borrower deltas must exactly equal the borrowed seconds." });
|
|
82
|
+
return diagnostics;
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
normalizeWorkdayAllocationProfile,
|
|
86
|
+
validateOneClassPerProjectAgent,
|
|
87
|
+
validateWorkdayAllocationProfile,
|
|
88
|
+
validateWorkdayBorrowingEvidence
|
|
89
|
+
};
|
|
@@ -2,7 +2,8 @@ import { execFileSync, spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
6
7
|
import { assertPackageReleaseTag, packageReleaseVersion } from './release-version.js';
|
|
7
8
|
const packageRoot = resolve(fileURLToPath(new URL('../..', import.meta.url)));
|
|
8
9
|
const extraArgs = process.argv.slice(2);
|
|
@@ -37,6 +38,41 @@ catch (error) {
|
|
|
37
38
|
process.exit(1);
|
|
38
39
|
}
|
|
39
40
|
const npmArgs = ['publish', packageArtifact, '--access', 'public', '--tag', release.npmDistTag];
|
|
41
|
+
const dryRun = extraArgs.includes('--dry-run');
|
|
42
|
+
if (!dryRun) {
|
|
43
|
+
let existingVersion = false;
|
|
44
|
+
try {
|
|
45
|
+
execFileSync('npm', ['view', `${evidence.packageName}@${evidence.packageVersion}`, 'version', '--json', '--prefer-online'], {
|
|
46
|
+
cwd: packageRoot,
|
|
47
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
48
|
+
});
|
|
49
|
+
existingVersion = true;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const output = `${String(error.stdout ?? '')}\n${String(error.stderr ?? '')}`;
|
|
53
|
+
if (!output.includes('E404') && !output.includes('ETARGET'))
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
if (existingVersion) {
|
|
57
|
+
const destination = mkdtempSync(resolve(tmpdir(), 'treeseed-sdk-existing-'));
|
|
58
|
+
let existingDigest = '';
|
|
59
|
+
try {
|
|
60
|
+
const packed = JSON.parse(execFileSync('npm', [
|
|
61
|
+
'pack', `${evidence.packageName}@${evidence.packageVersion}`, '--json', '--ignore-scripts', '--prefer-online',
|
|
62
|
+
'--pack-destination', destination,
|
|
63
|
+
], { cwd: packageRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] }));
|
|
64
|
+
existingDigest = `sha256:${createHash('sha256').update(readFileSync(resolve(destination, packed[0].filename))).digest('hex')}`;
|
|
65
|
+
if (existingDigest !== evidence.packageDigest) {
|
|
66
|
+
throw new Error(`Published SDK digest ${existingDigest} does not match sealed evidence ${evidence.packageDigest}.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
rmSync(destination, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
console.log(JSON.stringify({ ok: true, adoptedExisting: true, packageVersion: evidence.packageVersion, packageDigest: existingDigest }));
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
40
76
|
if (process.env.GITHUB_ACTIONS === 'true') {
|
|
41
77
|
npmArgs.push('--provenance');
|
|
42
78
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
export class RegistryPropagationError extends Error {
|
|
6
|
+
}
|
|
7
|
+
const retryableRegistryFailure = (error) => {
|
|
8
|
+
if (error instanceof RegistryPropagationError)
|
|
9
|
+
return true;
|
|
10
|
+
const output = `${String(error.stdout ?? '')}\n${String(error.stderr ?? '')}\n${error instanceof Error ? error.message : String(error)}`;
|
|
11
|
+
return ['E404', 'ETARGET', 'EAI_AGAIN', 'ECONNRESET', 'ETIMEDOUT', 'E502', 'E503', 'E504', 'FETCH_ERROR']
|
|
12
|
+
.some((marker) => output.includes(marker));
|
|
13
|
+
};
|
|
14
|
+
export async function readBackPublishedPackage(input) {
|
|
15
|
+
const now = input.now ?? Date.now;
|
|
16
|
+
const delay = input.delay ?? ((milliseconds) => new Promise((resolveDelay) => setTimeout(resolveDelay, milliseconds)));
|
|
17
|
+
const deadline = now() + (input.deadlineMs ?? 120_000);
|
|
18
|
+
const perCallTimeout = input.perCallTimeoutMs ?? 15_000;
|
|
19
|
+
const execNpm = input.execNpm ?? ((args, timeout) => execFileSync('npm', args, {
|
|
20
|
+
cwd: input.cwd,
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
23
|
+
timeout,
|
|
24
|
+
}));
|
|
25
|
+
const readArtifact = input.readArtifact ?? readFileSync;
|
|
26
|
+
const remainingTimeout = () => Math.max(1, Math.min(perCallTimeout, deadline - now()));
|
|
27
|
+
const waitForPropagation = async (error) => {
|
|
28
|
+
if (!retryableRegistryFailure(error) || now() >= deadline)
|
|
29
|
+
throw error;
|
|
30
|
+
await delay(Math.min(3000, Math.max(1, deadline - now())));
|
|
31
|
+
};
|
|
32
|
+
let observedDigest = '';
|
|
33
|
+
let tags = {};
|
|
34
|
+
while (!observedDigest) {
|
|
35
|
+
try {
|
|
36
|
+
const packed = JSON.parse(execNpm([
|
|
37
|
+
'pack', `${input.packageName}@${input.packageVersion}`, '--json', '--ignore-scripts', '--prefer-online',
|
|
38
|
+
'--pack-destination', input.destination,
|
|
39
|
+
], remainingTimeout()));
|
|
40
|
+
observedDigest = `sha256:${createHash('sha256').update(readArtifact(resolve(input.destination, packed[0].filename))).digest('hex')}`;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
await waitForPropagation(error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (observedDigest !== input.packageDigest)
|
|
47
|
+
throw new Error(`Published SDK digest ${observedDigest} does not match ${input.packageDigest}.`);
|
|
48
|
+
while (tags.rc !== input.packageVersion) {
|
|
49
|
+
try {
|
|
50
|
+
tags = JSON.parse(execNpm(['view', input.packageName, 'dist-tags', '--json', '--prefer-online'], remainingTimeout()));
|
|
51
|
+
if (tags.latest !== input.latestDistTag)
|
|
52
|
+
throw new Error(`npm latest changed from ${input.latestDistTag} to ${tags.latest ?? 'nothing'}.`);
|
|
53
|
+
if (tags.rc !== input.packageVersion)
|
|
54
|
+
throw new RegistryPropagationError(`npm rc points to ${tags.rc ?? 'nothing'}, expected ${input.packageVersion}.`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
await waitForPropagation(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { packageVersion: input.packageVersion, packageDigest: observedDigest, rc: tags.rc, latest: tags.latest };
|
|
61
|
+
}
|
|
@@ -1,37 +1,14 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
1
|
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
4
2
|
import { tmpdir } from 'node:os';
|
|
5
3
|
import { resolve } from 'node:path';
|
|
4
|
+
import { readBackPublishedPackage } from './published-package-readback.js';
|
|
6
5
|
const root = resolve(import.meta.dirname, '../..');
|
|
7
6
|
const evidence = JSON.parse(readFileSync(resolve(root, '.treeseed/standards/release-evidence.json'), 'utf8'));
|
|
8
7
|
const baseline = JSON.parse(readFileSync(resolve(root, '.treeseed/standards/baseline/receipt.json'), 'utf8'));
|
|
9
8
|
const destination = mkdtempSync(resolve(tmpdir(), 'treeseed-sdk-published-'));
|
|
10
9
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14
|
-
try {
|
|
15
|
-
const packed = JSON.parse(execFileSync('npm', [
|
|
16
|
-
'pack', `${evidence.packageName}@${evidence.packageVersion}`, '--json', '--ignore-scripts', '--pack-destination', destination,
|
|
17
|
-
], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] }));
|
|
18
|
-
observedDigest = `sha256:${createHash('sha256').update(readFileSync(resolve(destination, packed[0].filename))).digest('hex')}`;
|
|
19
|
-
tags = JSON.parse(execFileSync('npm', ['view', evidence.packageName, 'dist-tags', '--json'], { cwd: root, encoding: 'utf8' }));
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
if (attempt === 5)
|
|
24
|
-
throw error;
|
|
25
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, 3000));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (observedDigest !== evidence.packageDigest)
|
|
29
|
-
throw new Error(`Published SDK digest ${observedDigest} does not match ${evidence.packageDigest}.`);
|
|
30
|
-
if (tags.rc !== evidence.packageVersion)
|
|
31
|
-
throw new Error(`npm rc points to ${tags.rc ?? 'nothing'}, expected ${evidence.packageVersion}.`);
|
|
32
|
-
if (tags.latest !== baseline.latestDistTag)
|
|
33
|
-
throw new Error(`npm latest changed from ${baseline.latestDistTag} to ${tags.latest ?? 'nothing'}.`);
|
|
34
|
-
console.log(JSON.stringify({ ok: true, packageVersion: evidence.packageVersion, packageDigest: observedDigest, rc: tags.rc, latest: tags.latest }));
|
|
10
|
+
const result = await readBackPublishedPackage({ ...evidence, latestDistTag: baseline.latestDistTag, destination, cwd: root });
|
|
11
|
+
console.log(JSON.stringify({ ok: true, ...result }));
|
|
35
12
|
}
|
|
36
13
|
finally {
|
|
37
14
|
rmSync(destination, { recursive: true, force: true });
|
|
@@ -6,6 +6,7 @@ const required = [
|
|
|
6
6
|
'dist/standards/index.js', 'dist/standards/index.d.ts',
|
|
7
7
|
'dist/standards/typescript/index.js', 'dist/standards/typescript/index.d.ts',
|
|
8
8
|
'dist/standards/openapi/index.js', 'dist/standards/openapi/index.d.ts',
|
|
9
|
+
'dist/operator-contracts/index.js', 'dist/operator-contracts/index.d.ts',
|
|
9
10
|
'.treeseed/standards/contract-models.json', '.treeseed/standards/contract-bundle.json',
|
|
10
11
|
'.treeseed/standards/typescript-public-api.json', '.treeseed/standards/openapi.json',
|
|
11
12
|
];
|
|
@@ -13,7 +14,7 @@ const missing = required.filter((path) => !existsSync(resolve(root, path)));
|
|
|
13
14
|
if (missing.length)
|
|
14
15
|
throw new Error(`Missing standards package outputs: ${missing.join(', ')}.`);
|
|
15
16
|
const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
16
|
-
for (const specifier of ['./standards', './standards/typescript', './standards/openapi']) {
|
|
17
|
+
for (const specifier of ['./standards', './standards/typescript', './standards/openapi', './operator-contracts']) {
|
|
17
18
|
if (!packageJson.exports[specifier])
|
|
18
19
|
throw new Error(`Missing package export ${specifier}.`);
|
|
19
20
|
}
|
|
@@ -28,8 +28,12 @@ try {
|
|
|
28
28
|
const observed = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: worktree, encoding: 'utf8' }).trim();
|
|
29
29
|
if (observed !== sourceCommit)
|
|
30
30
|
throw new Error(`API consumer checkout resolved ${observed}, expected ${sourceCommit}.`);
|
|
31
|
-
execFileSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--save
|
|
31
|
+
execFileSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--no-save', tarball], { cwd: worktree, stdio: 'inherit' });
|
|
32
32
|
execFileSync('npm', ['run', 'build'], { cwd: worktree, stdio: 'inherit' });
|
|
33
|
+
// The complete API release gate imports every runtime module and executes the
|
|
34
|
+
// local HTTP acceptance campaign. Focused contract tests alone cannot prove
|
|
35
|
+
// that the packed SDK satisfies the consumer's full runtime boundary.
|
|
36
|
+
execFileSync('npm', ['run', 'verify:direct'], { cwd: worktree, stdio: 'inherit' });
|
|
33
37
|
const testResultPath = resolve(worktree, '.treeseed-sdk-consumer-results.json');
|
|
34
38
|
execFileSync('npx', [
|
|
35
39
|
'vitest', 'run', '--config', './vitest.config.js',
|
|
@@ -37,6 +41,7 @@ try {
|
|
|
37
41
|
'tests/contract/api/provider-assignment-capability-handles.test.ts',
|
|
38
42
|
'tests/contract/api/api-route-descriptors.test.ts',
|
|
39
43
|
'tests/unit/governance/content-validation.test.ts',
|
|
44
|
+
'tests/unit/services/capacity/workdays/planning-profile-input-source.test.ts',
|
|
40
45
|
], { cwd: worktree, stdio: 'inherit' });
|
|
41
46
|
const testResult = JSON.parse(readFileSync(testResultPath, 'utf8'));
|
|
42
47
|
if (!testResult.success || testResult.numPassedTestSuites !== testResult.numTotalTestSuites
|
|
@@ -48,7 +53,7 @@ try {
|
|
|
48
53
|
.trim().split('\n').filter(Boolean).sort();
|
|
49
54
|
if (finalHead !== sourceCommit || consumerCommits !== 0)
|
|
50
55
|
throw new Error('API consumer verification created or adopted a source commit.');
|
|
51
|
-
if (
|
|
56
|
+
if (changedPaths.length !== 0) {
|
|
52
57
|
throw new Error(`API consumer verification changed unexpected tracked paths: ${changedPaths.join(', ')}.`);
|
|
53
58
|
}
|
|
54
59
|
execFileSync('git', ['diff', '--cached', '--quiet'], { cwd: worktree, stdio: 'ignore' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/sdk",
|
|
3
|
-
"version": "0.13.0-rc.
|
|
3
|
+
"version": "0.13.0-rc.3",
|
|
4
4
|
"description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -135,6 +135,10 @@
|
|
|
135
135
|
"types": "./dist/standards/openapi/index.d.ts",
|
|
136
136
|
"default": "./dist/standards/openapi/index.js"
|
|
137
137
|
},
|
|
138
|
+
"./operator-contracts": {
|
|
139
|
+
"types": "./dist/operator-contracts/index.d.ts",
|
|
140
|
+
"default": "./dist/operator-contracts/index.js"
|
|
141
|
+
},
|
|
138
142
|
"./sdk": {
|
|
139
143
|
"types": "./dist/entrypoints/models/sdk.d.ts",
|
|
140
144
|
"default": "./dist/entrypoints/models/sdk.js"
|