@zq-silk/yui 0.16.2 → 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +6 -4
  2. package/dist/agent/agent.js +4 -9
  3. package/dist/agent/executionComponents.js +4 -4
  4. package/dist/agentRun/agentRun.js +8 -8
  5. package/dist/artifacts/managedGit.js +3 -56
  6. package/dist/brief/taskBrief.js +3 -3
  7. package/dist/cli/updateOrchestrator.js +2 -13
  8. package/dist/cli/updatePorts.js +36 -69
  9. package/dist/cli/upgradeCommand.js +4 -8
  10. package/dist/cli.js +4 -7
  11. package/dist/commands/controllerCommands.js +1 -1
  12. package/dist/commands/taskCommands.js +8 -1
  13. package/dist/commands/taskRoleRuntimeStatus.js +1 -1
  14. package/dist/context/runInputContract.js +1 -1
  15. package/dist/controller/agentRuntimeObserver.js +4 -4
  16. package/dist/controller/clientRuntime.js +19 -44
  17. package/dist/controller/controller.js +16 -4
  18. package/dist/controller/fileSchedulerStoreAdapter.js +6 -6
  19. package/dist/controller/globalInputDelivery.js +3 -1
  20. package/dist/controller/jobSupervisor.js +3 -3
  21. package/dist/controller/providerRetryDelivery.js +128 -118
  22. package/dist/controller/runtime.js +1 -1
  23. package/dist/controller/structuredProviderObservation.js +1 -1
  24. package/dist/coordination/workMailbox.js +4 -4
  25. package/dist/core/controllerIdentity.js +25 -0
  26. package/dist/core/controllerProcessIdentity.js +2 -2
  27. package/dist/core/controllerServer.js +4 -2
  28. package/dist/core/protocol.js +1 -1
  29. package/dist/domain/validation.js +6 -0
  30. package/dist/event/taskEvent.js +3 -3
  31. package/dist/execution/workItemExecution.js +2 -2
  32. package/dist/executor/agentExecutor.js +6 -6
  33. package/dist/executor/effectiveLaunch.js +3 -3
  34. package/dist/grant/capabilityGrant.js +2 -2
  35. package/dist/input/inputRequest.js +4 -4
  36. package/dist/integration/changeSet.js +3 -3
  37. package/dist/integration/integrationAttempt.js +3 -3
  38. package/dist/integration/integrationSourceApplication.js +1 -1
  39. package/dist/job/durableJob.js +1 -1
  40. package/dist/job/jobRunner.js +2 -2
  41. package/dist/message/message.js +8 -6
  42. package/dist/milestone/milestone.js +3 -3
  43. package/dist/profile/agentProfile.js +3 -3
  44. package/dist/release/runtimeRelease.js +9 -7
  45. package/dist/repository/project.js +3 -3
  46. package/dist/resources/liveReferences.js +1 -1
  47. package/dist/resources/sqliteResourceRegistry.js +2 -2
  48. package/dist/review/reviewRound.js +5 -5
  49. package/dist/role/role.js +7 -8
  50. package/dist/runtime/acpProtocol.js +2 -3
  51. package/dist/runtime/agentDriverObservation.js +1 -1
  52. package/dist/runtime/agentHost.js +3 -3
  53. package/dist/runtime/agentHostProtocol.js +2 -2
  54. package/dist/runtime/codexInteractiveHost.js +1 -1
  55. package/dist/runtime/launchBroker.js +1 -1
  56. package/dist/runtime/processExitObservation.js +1 -1
  57. package/dist/runtime/providerContinuationReconciliationService.js +84 -75
  58. package/dist/runtime/providerRuntimeIdentity.js +3 -3
  59. package/dist/runtime/runtimeCoherence.js +7 -3
  60. package/dist/runtime/runtimeObservation.js +3 -3
  61. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  62. package/dist/runtime/taskRuntimeIsolation.js +3 -3
  63. package/dist/runtime/tmuxAdapters.js +3 -3
  64. package/dist/scheduler/taskWake.js +1 -1
  65. package/dist/storage/baselineSchema.js +606 -0
  66. package/dist/storage/homeLayout.js +5 -16
  67. package/dist/storage/recordValidation.js +16 -4
  68. package/dist/storage/sqliteSchema.js +106 -1741
  69. package/dist/storage/sqliteStore.js +16 -14
  70. package/dist/storage/storageSchema.js +8 -7
  71. package/dist/storage/storageVersions.js +23 -16
  72. package/dist/storage/taskStore.js +3 -29
  73. package/dist/storage/upgrade/upgradeOrchestrator.js +14 -102
  74. package/dist/task/task.js +15 -7
  75. package/dist/task/taskActivation.js +5 -4
  76. package/dist/telemetry/sqliteTelemetryStore.js +2 -2
  77. package/dist/verification/gateArtifact.js +5 -3
  78. package/dist/verification/verificationPlan.js +6 -7
  79. package/dist/workItem/workItem.js +6 -6
  80. package/dist/workspace/cleanupInspection.js +1 -9
  81. package/dist/worktree/managedWorkspace.js +3 -3
  82. package/docs/managed-turn-and-session-runtime.md +26 -14
  83. package/docs/managed-turn-and-session-runtime.zh-CN.md +18 -10
  84. package/docs/release-workflow.md +60 -300
  85. package/docs/release-workflow.zh-CN.md +46 -234
  86. package/docs/sqlite-control-plane-design.md +48 -289
  87. package/docs/sqlite-control-plane-design.zh-CN.md +37 -53
  88. package/docs/storage-baseline.md +139 -0
  89. package/docs/storage-baseline.zh-CN.md +111 -0
  90. package/docs/task-delivery.md +3 -4
  91. package/docs/task-delivery.zh-CN.md +3 -3
  92. package/docs/testing/verification-levels.md +40 -180
  93. package/docs/testing/verification-levels.zh-CN.md +27 -131
  94. package/i18n/README.zh-CN.md +5 -4
  95. package/package.json +1 -1
  96. package/dist/storage/migrations/agentFailureContext.js +0 -22
  97. package/dist/storage/migrations/agentRunContract.js +0 -159
  98. package/dist/storage/migrations/artifactsToGit.js +0 -338
  99. package/dist/storage/migrations/collapseWorktreeLayout.js +0 -963
  100. package/dist/storage/migrations/currentInputContract.js +0 -86
  101. package/dist/storage/migrations/currentRuntimeContract.js +0 -228
  102. package/dist/storage/migrations/historicalVerificationPlan.js +0 -35
  103. package/dist/storage/migrations/integrationContinuation.js +0 -105
  104. package/dist/storage/migrations/narrowAgentFailureContext.js +0 -65
  105. package/dist/storage/migrations/notificationOnlyWakes.js +0 -74
  106. package/dist/storage/migrations/removeRuntimeGeneration.js +0 -207
  107. package/dist/storage/migrations/submitIntent.js +0 -126
  108. package/dist/storage/migrations/unifyHomeLayout.js +0 -925
  109. package/dist/storage/migrations/verificationPlanV1.js +0 -162
  110. package/dist/storage/migrations/verificationPolicy.js +0 -74
  111. package/dist/storage/migrations/workItemHistory.js +0 -46
@@ -171,7 +171,7 @@ async function startControllerServerLocked(home, dispatcher, beforeDiscoveryRemo
171
171
  }
172
172
  }
173
173
  /**
174
- * Fence an older Controller whose socket name was derived by another protocol.
174
+ * Prevent a second Controller from claiming a Home with existing discovery.
175
175
  * A dead or PID-reused owner is stale and may be replaced; an unverifiable
176
176
  * live owner fails closed so an endpoint migration can never create dual
177
177
  * writers for one durable Home.
@@ -362,6 +362,8 @@ async function routeRequest(socket, line, token, dispatcher, stop, status, telem
362
362
  cliRealpath: controllerCliRealpath(),
363
363
  controllerRealpath: realpathSync(fileURLToPath(import.meta.url)),
364
364
  controllerProtocolVersion: FILE_TASK_CONTROLLER_PROTOCOL_VERSION,
365
+ storageVersion: yuiVersionIdentity().storageVersion,
366
+ minimumStorageVersion: yuiVersionIdentity().minimumStorageVersion,
365
367
  homeId,
366
368
  homeFilesystemId,
367
369
  controllerInstanceId,
@@ -676,7 +678,7 @@ export function buildRuntimeIdentityReceipt(input) {
676
678
  }
677
679
  })();
678
680
  return Object.freeze({
679
- schemaVersion: 2,
681
+ schemaVersion: 1,
680
682
  version: YUI_VERSION,
681
683
  executablePath: process.execPath,
682
684
  args: process.argv.slice(1),
@@ -3,7 +3,7 @@ import { validateHomeFilesystemId } from "./homeFilesystemIdentity.js";
3
3
  export const MAX_CONTROLLER_MESSAGE_BYTES = 1_048_576;
4
4
  export const CONTROLLER_DISCOVERY_PATH = "runtime/controller.json";
5
5
  /** Bump when a running Controller cannot safely share one YUI_HOME with this CLI. */
6
- export const FILE_TASK_CONTROLLER_PROTOCOL_VERSION = 4;
6
+ export const FILE_TASK_CONTROLLER_PROTOCOL_VERSION = 1;
7
7
  export class ControllerProtocolError extends Error {
8
8
  code;
9
9
  id;
@@ -51,6 +51,12 @@ export function normalizedUniqueIdentities(values, label) {
51
51
  export function cloneJson(value) {
52
52
  return JSON.parse(JSON.stringify(value));
53
53
  }
54
+ /** Current envelopes accept only declared fields; no historical-field denylist. */
55
+ export function requireKnownFields(value, fields, label) {
56
+ const unknown = Object.keys(value).find(key => !fields.includes(key));
57
+ if (unknown !== undefined)
58
+ throw new Error(`${label} has unsupported field: ${unknown}.`);
59
+ }
54
60
  /**
55
61
  * True for a concrete, pinnable package version — a semver-shaped `X.Y.Z` with
56
62
  * an optional pre-release/build suffix. Rejects dist-tag sentinels (`latest`,
@@ -2,7 +2,7 @@ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
2
2
  import { requireTimestamp } from "../domain/validation.js";
3
3
  export function createTaskEvent(id, taskId, type, payload, now) {
4
4
  const event = {
5
- schemaVersion: 2,
5
+ schemaVersion: 1,
6
6
  id: requireText(id, "Task event id"),
7
7
  taskId: requireText(taskId, "Task event Task id"),
8
8
  type: requireText(type, "Task event type"),
@@ -12,8 +12,8 @@ export function createTaskEvent(id, taskId, type, payload, now) {
12
12
  return validateTaskEvent(event);
13
13
  }
14
14
  export function validateTaskEvent(event) {
15
- if (event.schemaVersion !== 2)
16
- throw new Error("Task Event must use schemaVersion 2.");
15
+ if (event.schemaVersion !== 1)
16
+ throw new Error("Task Event must use schemaVersion 1.");
17
17
  validateTaskRecordReference({ taskId: event.taskId, localId: event.id }, "event");
18
18
  requireText(event.type, "Task event type");
19
19
  if (event.payload === null || typeof event.payload !== "object" || Array.isArray(event.payload)) {
@@ -3,8 +3,8 @@ import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity, requ
3
3
  import { validateContextSnapshotRef } from "../context/contextSnapshot.js";
4
4
  import { validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
5
5
  import { MAX_SYNTHESIS_SOURCE_RUNS } from "../context/sourceRunContext.js";
6
- export const WORK_ITEM_EXECUTION_GROUP_SCHEMA_VERSION = 2;
7
- export const WORK_ITEM_EXECUTION_LANE_SCHEMA_VERSION = 2;
6
+ export const WORK_ITEM_EXECUTION_GROUP_SCHEMA_VERSION = 1;
7
+ export const WORK_ITEM_EXECUTION_LANE_SCHEMA_VERSION = 1;
8
8
  export const WORK_ITEM_EXECUTION_ASSIGNMENT_SCHEMA_VERSION = 1;
9
9
  export const MINIMUM_EXECUTION_GROUP_LANES = 2;
10
10
  export const EXECUTION_GROUP_SCHEMA_VERSION = WORK_ITEM_EXECUTION_GROUP_SCHEMA_VERSION;
@@ -36,10 +36,10 @@ export function createRoleSessionSet(owner, activeAgentId, now) {
36
36
  updatedAt: now.toISOString()
37
37
  };
38
38
  return owner.scope === "global"
39
- ? { ...base, schemaVersion: 5, providerBinding: null }
39
+ ? { ...base, schemaVersion: 1, providerBinding: null }
40
40
  : {
41
41
  ...base,
42
- schemaVersion: 12,
42
+ schemaVersion: 1,
43
43
  providerBinding: null
44
44
  };
45
45
  }
@@ -106,7 +106,7 @@ export function recordRoleAgentSession(set, input, now) {
106
106
  throw new Error("A native Session cannot change its Endpoint implementation in place.");
107
107
  }
108
108
  const session = {
109
- schemaVersion: 6,
109
+ schemaVersion: 1,
110
110
  agentId,
111
111
  adapterId,
112
112
  nativeSessionId,
@@ -464,7 +464,7 @@ export function validateRoleSessionSet(set) {
464
464
  validateRoleAgentSession(session, agentId);
465
465
  }
466
466
  if (set.owner.scope === "global") {
467
- if (set.schemaVersion !== 5) {
467
+ if (set.schemaVersion !== 1) {
468
468
  throw new Error("Global Role session set schema version is invalid.");
469
469
  }
470
470
  const globalSet = set;
@@ -503,7 +503,7 @@ export function validateRoleSessionSet(set) {
503
503
  }
504
504
  }
505
505
  else {
506
- if (set.schemaVersion !== 12) {
506
+ if (set.schemaVersion !== 1) {
507
507
  throw new Error("Task Role session set schema version is invalid.");
508
508
  }
509
509
  if (!Object.hasOwn(set, "providerBinding")) {
@@ -550,7 +550,7 @@ export function validateRoleSessionSet(set) {
550
550
  return set;
551
551
  }
552
552
  export function validateRoleAgentSession(session, expectedAgentId = session.agentId) {
553
- if (session.schemaVersion !== 6) {
553
+ if (session.schemaVersion !== 1) {
554
554
  throw new Error(`Role Agent session schema version is invalid: ${expectedAgentId}.`);
555
555
  }
556
556
  const agentId = requireSafeIdentity(session.agentId, "Agent id");
@@ -172,8 +172,8 @@ function sessionContinuitySnapshot(snapshot) {
172
172
  };
173
173
  }
174
174
  export function validateEffectiveLaunchSnapshot(snapshot) {
175
- if (snapshot.schemaVersion !== 4) {
176
- throw new Error("Effective launch snapshot must use schemaVersion 4.");
175
+ if (snapshot.schemaVersion !== 1) {
176
+ throw new Error("Effective launch snapshot must use schemaVersion 1.");
177
177
  }
178
178
  if (snapshot.executionAuthority !== "planning" && snapshot.executionAuthority !== "delivery") {
179
179
  throw new Error("Effective launch requires its captured execution authority.");
@@ -278,7 +278,7 @@ function snapshotFromConfig(input) {
278
278
  reviewBaseCommit: commit(input.reviewBaseCommit ?? "", "Review base commit")
279
279
  };
280
280
  const common = {
281
- schemaVersion: 4,
281
+ schemaVersion: 1,
282
282
  executionAuthority: input.executionAuthority,
283
283
  sourceDesiredRevision: positiveInteger(input.sourceDesiredRevision, "Source desired revision"),
284
284
  agentId: identity(input.agentId, "Effective Agent id"),
@@ -1,6 +1,6 @@
1
1
  import { requireIdentity, requireText, requireTimestamp, requirePositiveInteger } from "../domain/validation.js";
2
2
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
- export const CAPABILITY_GRANT_SCHEMA_VERSION = 2;
3
+ export const CAPABILITY_GRANT_SCHEMA_VERSION = 1;
4
4
  const IRREVERSIBILITY_RANK = {
5
5
  none: 0,
6
6
  reversible: 1,
@@ -105,7 +105,7 @@ export function revokeGrant(grant, revokedBy, now) {
105
105
  }
106
106
  export function validateCapabilityGrant(grant) {
107
107
  if (grant.schemaVersion !== CAPABILITY_GRANT_SCHEMA_VERSION) {
108
- throw new Error("Capability grant must use schemaVersion 2.");
108
+ throw new Error("Capability grant must use schemaVersion 1.");
109
109
  }
110
110
  validateTaskRecordReference({ taskId: grant.taskId, localId: grant.id }, "capabilityGrant");
111
111
  requireIdentity(grant.taskId, "Task id");
@@ -14,7 +14,7 @@ export function createInputRequest(id, taskId, requester, input, now) {
14
14
  const timestamp = requireDate(now, "Input request creation time");
15
15
  const choices = normalizeChoices(input.choices);
16
16
  return validateInputRequest({
17
- schemaVersion: 3,
17
+ schemaVersion: 1,
18
18
  id: requireIdentity(id, "Input request id"),
19
19
  taskId: requireIdentity(taskId, "Input request Task id"),
20
20
  requester: normalizeRequester(requester),
@@ -72,12 +72,12 @@ export function validateInputRequest(value) {
72
72
  "schemaVersion", "id", "taskId", "requester", "question", "choices",
73
73
  "blockedRefs", "policy", "status", "createdAt", "updatedAt", ...terminalField
74
74
  ], "Input request");
75
- if (request.schemaVersion !== 3)
76
- throw new Error("Input request must use schemaVersion 3.");
75
+ if (request.schemaVersion !== 1)
76
+ throw new Error("Input request must use schemaVersion 1.");
77
77
  const choices = validateChoices(request.choices);
78
78
  const createdAt = requireTimestamp(request.createdAt, "Input request createdAt");
79
79
  const base = {
80
- schemaVersion: 3,
80
+ schemaVersion: 1,
81
81
  id: requireIdentity(request.id, "Input request id"),
82
82
  taskId: requireIdentity(request.taskId, "Input request Task id"),
83
83
  requester: normalizeRequester(request.requester),
@@ -3,7 +3,7 @@ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
3
  import { validateChangeSetManifest } from "./changeSetManifest.js";
4
4
  export function createWorkItemChangeSet(input, now) {
5
5
  return validateChangeSet({
6
- schemaVersion: 4,
6
+ schemaVersion: 1,
7
7
  ...input,
8
8
  changedPaths: [...input.changedPaths],
9
9
  manifest: validateChangeSetManifest(input.manifest),
@@ -11,8 +11,8 @@ export function createWorkItemChangeSet(input, now) {
11
11
  });
12
12
  }
13
13
  export function validateChangeSet(changeSet) {
14
- if (changeSet.schemaVersion !== 4)
15
- throw new Error("ChangeSet must use schemaVersion 4.");
14
+ if (changeSet.schemaVersion !== 1)
15
+ throw new Error("ChangeSet must use schemaVersion 1.");
16
16
  validateTaskRecordReference({ taskId: changeSet.taskId, localId: changeSet.id }, "changeSet");
17
17
  validateTaskRecordReference({
18
18
  taskId: changeSet.taskId,
@@ -4,7 +4,7 @@ import { normalizeCheckResult } from "./checkResult.js";
4
4
  export function createIntegrationAttempt(input, now) {
5
5
  const timestamp = now.toISOString();
6
6
  return validateIntegrationAttempt({
7
- schemaVersion: 6,
7
+ schemaVersion: 1,
8
8
  id: input.id,
9
9
  taskId: input.taskId,
10
10
  projectId: input.projectId,
@@ -132,8 +132,8 @@ export function supersedeIntegration(attempt, reason, now) {
132
132
  });
133
133
  }
134
134
  export function validateIntegrationAttempt(attempt) {
135
- if (attempt.schemaVersion !== 6) {
136
- throw new Error("IntegrationAttempt must use schemaVersion 6.");
135
+ if (attempt.schemaVersion !== 1) {
136
+ throw new Error("IntegrationAttempt must use schemaVersion 1.");
137
137
  }
138
138
  validateTaskRecordReference({
139
139
  taskId: attempt.taskId,
@@ -233,7 +233,7 @@ async function assertOperation(path, kind, commit, before, branch, source) {
233
233
  throw new Error(`Integration expected ${kind}, found ${active ?? "no active Git operation"}.`);
234
234
  if (kind === "rebase") {
235
235
  const directory = await gitPath(path, "rebase-merge");
236
- // Yui invokes the merge backend explicitly. A legacy apply-backend
236
+ // Yui invokes the merge backend explicitly. An apply-backend
237
237
  // operation cannot be silently treated as the same execution.
238
238
  const [onto, original, headName] = await Promise.all(["onto", "orig-head", "head-name"].map(name => readFile(resolve(directory, name), "utf8")));
239
239
  if (source.kind !== "upstream" || onto.trim() !== commit
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
3
3
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
4
4
  import { recordOperationEvidence, validateOperationFacts } from "../kernel/operationFacts.js";
5
- export const CURRENT_DURABLE_JOB_SCHEMA_VERSION = 2;
5
+ export const CURRENT_DURABLE_JOB_SCHEMA_VERSION = 1;
6
6
  export const JOB_RUNNER_IMPLEMENTATION = Object.freeze({ id: "yui:job-runner", generation: "1" });
7
7
  export const DURABLE_JOB_TERMINAL_STATUSES = [
8
8
  "succeeded",
@@ -128,8 +128,8 @@ export async function runDurableJobRunner(specPath) {
128
128
  let child;
129
129
  try {
130
130
  // Issue 08: a structured argv step executes without a shell so its
131
- // tokens can never be reinterpreted; a legacy command step keeps the
132
- // shell form. A per-step cwd/env override applies to either form.
131
+ // tokens can never be reinterpreted; an explicit command step uses
132
+ // the shell. A per-step cwd/env override applies to either form.
133
133
  const stepCwd = jobStepDirectory(spec.workspace, step.cwd);
134
134
  const stepEnv = step.env === undefined
135
135
  ? spec.env
@@ -1,4 +1,5 @@
1
1
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
2
+ import { requireKnownFields } from "../domain/validation.js";
2
3
  export const TASK_MESSAGE_KINDS = ["user", "operator", "role-result", "system"];
3
4
  /**
4
5
  * How a user/operator submission asks its Task to react (task-32 Requirement A).
@@ -24,7 +25,7 @@ export const TASK_MESSAGE_INPUT_CONTROL_OUTCOMES = ["pending", "accepted", "reje
24
25
  export function createTaskMessage(id, taskId, body, kind, author, now, context = {}) {
25
26
  validateKindAndAuthor(kind, author);
26
27
  const message = {
27
- schemaVersion: 3,
28
+ schemaVersion: 1,
28
29
  id: requireSafeIdentity(id, "Message id"),
29
30
  taskId: requireSafeIdentity(taskId, "Message Task id"),
30
31
  kind,
@@ -199,15 +200,16 @@ export function withSubmissionReceipt(message, receipt) {
199
200
  return updated;
200
201
  }
201
202
  export function validateTaskMessage(message) {
202
- if (message.schemaVersion !== 3)
203
- throw new Error("Task Message must use schemaVersion 3.");
203
+ if (message.schemaVersion !== 1)
204
+ throw new Error("Task Message must use schemaVersion 1.");
205
+ requireKnownFields(message, [
206
+ "schemaVersion", "id", "taskId", "kind", "author", "body", "intent", "submissionKey", "submissionReceipt",
207
+ "inputControl", "interruptThen", "control", "runId", "resultRef", "workItemId", "recipient", "continuation", "handovers", "createdAt"
208
+ ], "Task Message");
204
209
  validateTaskRecordReference({ taskId: message.taskId, localId: message.id }, "message");
205
210
  requireText(message.body, "Message body");
206
211
  validateKindAndAuthor(message.kind, message.author);
207
212
  normalizeAuthor(message.author);
208
- if (Object.hasOwn(message, "wakePolicy")) {
209
- throw new Error("Message contains a retired wake policy; use the storage upgrade boundary.");
210
- }
211
213
  if ((message.kind === "user" || message.kind === "operator") && message.intent === undefined) {
212
214
  throw new Error("User/operator Message intent is required.");
213
215
  }
@@ -1,8 +1,8 @@
1
1
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
2
2
  import { requireTimestamp } from "../domain/validation.js";
3
3
  export function validateMilestone(milestone) {
4
- if (milestone.schemaVersion !== 2)
5
- throw new Error("Milestone must use schemaVersion 2.");
4
+ if (milestone.schemaVersion !== 1)
5
+ throw new Error("Milestone must use schemaVersion 1.");
6
6
  validateTaskRecordReference({ taskId: milestone.taskId, localId: milestone.id }, "milestone");
7
7
  requireText(milestone.title, "Milestone title");
8
8
  requireText(milestone.summary, "Milestone summary");
@@ -12,7 +12,7 @@ export function validateMilestone(milestone) {
12
12
  }
13
13
  export function createMilestone(id, taskId, title, summary, createdBy, now) {
14
14
  return {
15
- schemaVersion: 2,
15
+ schemaVersion: 1,
16
16
  id: validateTaskRecordReference({ taskId, localId: id }, "milestone").localId,
17
17
  taskId: requireSafeIdentity(taskId, "Task id"),
18
18
  title: requireText(title, "Milestone title"),
@@ -8,7 +8,7 @@ export const BUILTIN_PROFILE_IDS = Object.freeze([
8
8
  export function createAgentProfile(input, now) {
9
9
  const timestamp = now.toISOString();
10
10
  return validateAgentProfile({
11
- schemaVersion: 3,
11
+ schemaVersion: 1,
12
12
  id: requireIdentity(input.id, "Agent Profile id"),
13
13
  revision: 1,
14
14
  ...normalizeInput(input),
@@ -31,8 +31,8 @@ export function updateAgentProfile(profile, patch, now) {
31
31
  return validateAgentProfile(candidate);
32
32
  }
33
33
  export function validateAgentProfile(profile) {
34
- if (profile.schemaVersion !== 3) {
35
- throw new Error("AgentProfile must use schemaVersion 3.");
34
+ if (profile.schemaVersion !== 1) {
35
+ throw new Error("AgentProfile must use schemaVersion 1.");
36
36
  }
37
37
  requireIdentity(profile.id, "Agent Profile id");
38
38
  requirePositiveInteger(profile.revision, "Agent Profile revision");
@@ -15,6 +15,7 @@
15
15
  * deterministic and recoverable.
16
16
  */
17
17
  import { createHash, randomBytes, randomUUID } from "node:crypto";
18
+ import { isStorageVersion, compareStorageVersions } from "../storage/storageVersions.js";
18
19
  import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, rmSync, writeFileSync } from "node:fs";
19
20
  import { dirname, join, resolve } from "node:path";
20
21
  import { readLinuxProcessStartIdentity } from "../controller/domainIdentity.js";
@@ -133,7 +134,7 @@ export function writeActiveReleasePointer(home, pointer) {
133
134
  }
134
135
  /**
135
136
  * Resolves the Home's active release. Returns null when no pointer exists
136
- * (legacy/dev Home). Fails closed when the pointer names a release that is
137
+ * (no immutable release selected). Fails closed when the pointer names a release that is
137
138
  * missing or drifted.
138
139
  */
139
140
  export function resolveActiveRelease(home) {
@@ -478,7 +479,7 @@ function validatePointer(value) {
478
479
  function validateRuntimeIdentity(value) {
479
480
  if (value === null
480
481
  || typeof value !== "object"
481
- || value.schemaVersion !== 2
482
+ || value.schemaVersion !== 1
482
483
  || typeof value.version !== "string"
483
484
  || typeof value.executablePath !== "string"
484
485
  || value.executablePath === ""
@@ -487,15 +488,16 @@ function validateRuntimeIdentity(value) {
487
488
  || typeof value.cliRealpath !== "string"
488
489
  || typeof value.controllerRealpath !== "string"
489
490
  || !isPositiveInteger(value.controllerProtocolVersion)
490
- || !isPositiveInteger(value.storageVersion)
491
- || !isPositiveInteger(value.minimumStorageVersion)
492
- || value.minimumStorageVersion
493
- > value.storageVersion
491
+ || !isStorageVersion(value.storageVersion)
492
+ || !isStorageVersion(value.minimumStorageVersion)
493
+ || compareStorageVersions(value.minimumStorageVersion, value.storageVersion) > 0
494
494
  || typeof value.pid !== "number"
495
495
  || typeof value.processStartIdentity !== "string"
496
496
  || (value.mode !== "primary"
497
497
  && value.mode !== "candidate")
498
- || typeof value.dualOwner !== "boolean") {
498
+ || typeof value.dualOwner !== "boolean"
499
+ || value.storageBackend !== "sqlite"
500
+ || typeof value.workerEnabled !== "boolean") {
499
501
  throw new Error("Runtime identity receipt is invalid.");
500
502
  }
501
503
  return value;
@@ -8,7 +8,7 @@ export function managedProjectPath(home, projectId) {
8
8
  export function createProject(id, name, path, branches, now, metadata = {}) {
9
9
  const timestamp = now.toISOString();
10
10
  return validateProject({
11
- schemaVersion: 6,
11
+ schemaVersion: 1,
12
12
  id: requireIdentity(id, "Project id"),
13
13
  name: validateProjectName(name),
14
14
  aliases: normalizeAliases(metadata.aliases ?? [], name),
@@ -278,8 +278,8 @@ export function assertProjectCatalog(projects) {
278
278
  }
279
279
  }
280
280
  export function validateProject(project) {
281
- if (project.schemaVersion !== 6) {
282
- throw new Error("Project must use schemaVersion 6.");
281
+ if (project.schemaVersion !== 1) {
282
+ throw new Error("Project must use schemaVersion 1.");
283
283
  }
284
284
  if (!Array.isArray(project.resourceRefs))
285
285
  throw new Error("Project resourceRefs must be an array.");
@@ -91,7 +91,7 @@ export async function scanLiveReferences(input) {
91
91
  diagnostics.push(...tmuxResult.diagnostics);
92
92
  // 3. Controller discovery: a running Controller protects only its own
93
93
  // discovery record. Its cwd and open files are covered by the /proc
94
- // scan above, so a live Controller does not blanket-protect legacy
94
+ // scan above, so a live Controller does not blanket-protect unattributed
95
95
  // deployments and worktrees that no descriptor references — automatic
96
96
  // GC (which runs inside the Controller) would otherwise be a no-op.
97
97
  const discovery = readControllerDiscovery(home);
@@ -14,7 +14,7 @@ import { existsSync } from "node:fs";
14
14
  import { join } from "node:path";
15
15
  import { isDeepStrictEqual } from "node:util";
16
16
  import Database from "better-sqlite3";
17
- import { migrateSqliteSchema } from "../storage/sqliteSchema.js";
17
+ import { validateSqliteSchema } from "../storage/sqliteSchema.js";
18
18
  import { emptyResourceRegistry, parseResourceRegistryState } from "./resourceRegistry.js";
19
19
  import { RESOURCE_REGISTRY_SCHEMA_VERSION } from "./resourceTypes.js";
20
20
  export const SQLITE_RESOURCE_REGISTRY_TABLE = "resource_registry";
@@ -31,10 +31,10 @@ export class SqliteResourceRegistry {
31
31
  }
32
32
  this.#db = new Database(dbPath);
33
33
  try {
34
+ validateSqliteSchema(this.#db);
34
35
  this.#db.pragma("journal_mode = WAL");
35
36
  this.#db.pragma("foreign_keys = ON");
36
37
  this.#db.pragma("busy_timeout = 5000");
37
- migrateSqliteSchema(this.#db, { mode: "validate" });
38
38
  }
39
39
  catch (error) {
40
40
  this.#db.close();
@@ -5,7 +5,7 @@ import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
5
5
  import { assertExecutionGroupTransition, retryFailedExecutionLanes, validateExecutionGroup, validateReviewExecutionAssignment } from "../execution/workItemExecution.js";
6
6
  export function createReviewRound(id, taskId, workItemId, candidateId, reviewerRoleName, requestedBy, reviewBaseCommit, now, executionGroup) {
7
7
  return validateReviewRound({
8
- schemaVersion: 8,
8
+ schemaVersion: 1,
9
9
  id: requireIdentity(id, "ReviewRound id"),
10
10
  taskId: requireIdentity(taskId, "Task id"),
11
11
  workItemId: requireIdentity(workItemId, "Work Item id"),
@@ -22,7 +22,7 @@ export function createReviewRound(id, taskId, workItemId, candidateId, reviewerR
22
22
  export function createTaskReviewRound(id, taskId, reviewerRoleName, requestedBy, taskCandidate, now, taskFinalReviewContract, executionGroup) {
23
23
  const candidate = validateTaskReviewCandidate(taskCandidate);
24
24
  return validateReviewRound({
25
- schemaVersion: 8,
25
+ schemaVersion: 1,
26
26
  id: requireIdentity(id, "ReviewRound id"),
27
27
  taskId: requireIdentity(taskId, "Task id"),
28
28
  reviewerRoleName: requireIdentity(reviewerRoleName, "Reviewer Role"),
@@ -48,7 +48,7 @@ export function createTaskReviewRound(id, taskId, reviewerRoleName, requestedBy,
48
48
  export function createTaskDeltaReviewRound(id, taskId, reviewerRoleName, requestedBy, taskCandidate, deltaRecheck, now, taskFinalReviewContract, executionGroup) {
49
49
  const candidate = validateTaskReviewCandidate(taskCandidate);
50
50
  return validateReviewRound({
51
- schemaVersion: 8,
51
+ schemaVersion: 1,
52
52
  id: requireIdentity(id, "ReviewRound id"),
53
53
  taskId: requireIdentity(taskId, "Task id"),
54
54
  reviewerRoleName: requireIdentity(reviewerRoleName, "Reviewer Role"),
@@ -285,8 +285,8 @@ export function validateReviewRound(round) {
285
285
  "createdAt",
286
286
  "endedAt"
287
287
  ], "ReviewRound");
288
- if (round.schemaVersion !== 8)
289
- throw new Error("ReviewRound must use schemaVersion 8.");
288
+ if (round.schemaVersion !== 1)
289
+ throw new Error("ReviewRound must use schemaVersion 1.");
290
290
  validateTaskRecordReference({ taskId: round.taskId, localId: round.id }, "reviewRound");
291
291
  requireIdentity(round.reviewerRoleName, "Reviewer Role");
292
292
  requireCommit(round.reviewBaseCommit, "Review base commit");
package/dist/role/role.js CHANGED
@@ -30,14 +30,14 @@ export function createRole(taskId, name, bindings, activeAgentId, workspace, now
30
30
  const owner = createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess);
31
31
  return validateTaskRole({
32
32
  ...owner,
33
- schemaVersion: 4,
33
+ schemaVersion: 1,
34
34
  taskId: requireSafeIdentity(taskId, "Task id")
35
35
  });
36
36
  }
37
37
  export function createGlobalRole(name, bindings, activeAgentId, workspace, now, profile = {}, defaultAccess = "write") {
38
38
  return validateGlobalRole({
39
39
  ...createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess),
40
- schemaVersion: 3
40
+ schemaVersion: 1
41
41
  });
42
42
  }
43
43
  export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalRole.name) {
@@ -45,7 +45,7 @@ export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalR
45
45
  const timestamp = now.toISOString();
46
46
  return validateTaskRole({
47
47
  ...cloneProfile(globalRole),
48
- schemaVersion: 4,
48
+ schemaVersion: 1,
49
49
  launchRevision: 1,
50
50
  defaultAccess: globalRole.defaultAccess,
51
51
  taskId: requireSafeIdentity(taskId, "Task id"),
@@ -189,6 +189,8 @@ export function unbindRoleAgent(role, sessions, agentId, now) {
189
189
  return { role: updatedRole, sessions: updatedSessions };
190
190
  }
191
191
  export function validateGlobalRole(role) {
192
+ if (Object.hasOwn(role, "taskId"))
193
+ throw new Error("Global Role cannot carry a Task identity.");
192
194
  return validateRoleOwner(role);
193
195
  }
194
196
  export function validateTaskRole(role) {
@@ -218,8 +220,7 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
218
220
  };
219
221
  }
220
222
  function validateRoleOwner(role) {
221
- const expectedSchemaVersion = "taskId" in role ? 4 : 3;
222
- if (role.schemaVersion !== expectedSchemaVersion) {
223
+ if (role.schemaVersion !== 1) {
223
224
  throw new Error("Role schema version is invalid.");
224
225
  }
225
226
  if (!Number.isSafeInteger(role.launchRevision) || role.launchRevision < 1) {
@@ -256,9 +257,7 @@ function validateRoleOwner(role) {
256
257
  function validateRoleAgentBinding(binding) {
257
258
  const agentId = requireSafeIdentity(binding.agentId, "Role Agent id");
258
259
  const adapterId = requireSupportedAdapterId(binding.adapterId);
259
- // Storage 10 backfilled every stored binding, so a missing component here is
260
- // a corrupt record rather than an old one. Defaulting it would invent a
261
- // product identity for data that never lost one.
260
+ // The current contract requires an explicit execution component.
262
261
  if (binding.component === undefined) {
263
262
  throw new Error(`Role Agent binding is missing its execution component: ${agentId}.`);
264
263
  }
@@ -126,9 +126,8 @@ export function acpPromptRequest(sessionId, text) {
126
126
  /**
127
127
  * Read a `configOptions` array from a session setup or set-option result.
128
128
  *
129
- * Returns undefined when the field is absent, which is how an Agent that
130
- * predates config options answers; that is a different fact from an Agent
131
- * answering with an empty list, which offers nothing configurable. Callers must
129
+ * Returns undefined when the field is absent: configuration support is then
130
+ * unobserved. An explicit empty list instead offers nothing configurable. Callers must
132
131
  * be able to tell those apart, so the absence is not flattened to `[]`.
133
132
  */
134
133
  export function readAcpConfigOptions(value) {
@@ -40,7 +40,7 @@ function observation(input, mapped) {
40
40
  const eventId = hookEventId(input, mapped, requestOccurrenceId);
41
41
  const fence = { ...input.fence, ...mapped.fence };
42
42
  return createRuntimeObservation({
43
- schemaVersion: 4,
43
+ schemaVersion: 1,
44
44
  eventId,
45
45
  semanticKey: requestOccurrenceId === undefined
46
46
  ? runtimeObservationSemanticKey({
@@ -487,7 +487,7 @@ export async function runAgentHost(input) {
487
487
  const failures = [];
488
488
  try {
489
489
  await persistAndSubmitExit(input.home, validateRuntimeProcessExitObservation({
490
- schemaVersion: 2,
490
+ schemaVersion: 1,
491
491
  observationId: `${hostInstanceId}-${hostSequence}`,
492
492
  hostSequence,
493
493
  hostInstanceId,
@@ -1684,7 +1684,7 @@ function isProviderDeliveryFailure(value) {
1684
1684
  && ["accepted", "not-accepted", "unknown"].includes(failure.inputDisposition ?? "");
1685
1685
  }
1686
1686
  function validateSnapshot(snapshot) {
1687
- if (snapshot.schemaVersion !== 2
1687
+ if (snapshot.schemaVersion !== 1
1688
1688
  || !["idle", "starting", "ready", "settling", "delivery-unknown", "busy", "rejected", "failed", "exited"]
1689
1689
  .includes(snapshot.state)) {
1690
1690
  throw new Error("Agent Host snapshot is invalid.");
@@ -1727,7 +1727,7 @@ function validateSnapshot(snapshot) {
1727
1727
  }
1728
1728
  function hostSnapshot(state, fields = {}) {
1729
1729
  return validateSnapshot({
1730
- schemaVersion: 2,
1730
+ schemaVersion: 1,
1731
1731
  state,
1732
1732
  ...definedFields(fields),
1733
1733
  updatedAt: new Date().toISOString()
@@ -1,7 +1,7 @@
1
1
  import { createSessionOwnerIdentity } from "./sessionOwnerIdentity.js";
2
2
  /** Current Host event source contract, independent of Home storage access. */
3
3
  export const AGENT_HOST_EVENT_PROTOCOL = "yui-agent-host-events/v1";
4
- export const AGENT_HOST_CONTROL_PROTOCOL = "yui-agent-host/v5";
4
+ export const AGENT_HOST_CONTROL_PROTOCOL = "yui-agent-host-control/v1";
5
5
  export function readAgentHostObservationSource(value) {
6
6
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
7
7
  throw new Error("Agent Host observation source is invalid.");
@@ -15,7 +15,7 @@ export function readAgentHostObservationSource(value) {
15
15
  throw new Error("Agent Host connection evidence is invalid.");
16
16
  }
17
17
  if (source.connection?.processOwner !== undefined
18
- && (source.connection.processOwner.schemaVersion !== 2
18
+ && (source.connection.processOwner.schemaVersion !== 1
19
19
  || source.connection.processOwner.kind !== "yui-session-owner")) {
20
20
  throw new Error("Agent Host process custody wire contract is invalid.");
21
21
  }
@@ -29,7 +29,7 @@ export async function runCodexInteractiveHost(home, payload, openAgentHostContro
29
29
  }
30
30
  const expectedId = environment.YUI_NATIVE_SESSION_ID;
31
31
  let snapshot = {
32
- schemaVersion: 2, state: "starting", adapterId: "codex", updatedAt: new Date().toISOString()
32
+ schemaVersion: 1, state: "starting", adapterId: "codex", updatedAt: new Date().toISOString()
33
33
  };
34
34
  const control = await openAgentHostControl(home, payload, () => snapshot, async (request) => {
35
35
  if (request.type !== "status") {
@@ -56,7 +56,7 @@ export function validateAgentHostLaunchPayload(value) {
56
56
  return validatePayload(value);
57
57
  }
58
58
  function validatePayload(payload) {
59
- if (payload.schemaVersion !== 2)
59
+ if (payload.schemaVersion !== 1)
60
60
  throw new Error("Agent Host launch payload version is invalid.");
61
61
  text(payload.command, "command");
62
62
  text(payload.cwd, "cwd");