@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.
- package/README.md +6 -4
- package/dist/agent/agent.js +4 -9
- package/dist/agent/executionComponents.js +4 -4
- package/dist/agentRun/agentRun.js +8 -8
- package/dist/artifacts/managedGit.js +3 -56
- package/dist/brief/taskBrief.js +3 -3
- package/dist/cli/updateOrchestrator.js +2 -13
- package/dist/cli/updatePorts.js +36 -69
- package/dist/cli/upgradeCommand.js +4 -8
- package/dist/cli.js +4 -7
- package/dist/commands/controllerCommands.js +1 -1
- package/dist/commands/taskCommands.js +8 -1
- package/dist/commands/taskRoleRuntimeStatus.js +1 -1
- package/dist/context/runInputContract.js +1 -1
- package/dist/controller/agentRuntimeObserver.js +4 -4
- package/dist/controller/clientRuntime.js +19 -44
- package/dist/controller/controller.js +16 -4
- package/dist/controller/fileSchedulerStoreAdapter.js +6 -6
- package/dist/controller/globalInputDelivery.js +3 -1
- package/dist/controller/jobSupervisor.js +3 -3
- package/dist/controller/providerRetryDelivery.js +128 -118
- package/dist/controller/runtime.js +1 -1
- package/dist/controller/structuredProviderObservation.js +1 -1
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/core/controllerIdentity.js +25 -0
- package/dist/core/controllerProcessIdentity.js +2 -2
- package/dist/core/controllerServer.js +4 -2
- package/dist/core/protocol.js +1 -1
- package/dist/domain/validation.js +6 -0
- package/dist/event/taskEvent.js +3 -3
- package/dist/execution/workItemExecution.js +2 -2
- package/dist/executor/agentExecutor.js +6 -6
- package/dist/executor/effectiveLaunch.js +3 -3
- package/dist/grant/capabilityGrant.js +2 -2
- package/dist/input/inputRequest.js +4 -4
- package/dist/integration/changeSet.js +3 -3
- package/dist/integration/integrationAttempt.js +3 -3
- package/dist/integration/integrationSourceApplication.js +1 -1
- package/dist/job/durableJob.js +1 -1
- package/dist/job/jobRunner.js +2 -2
- package/dist/message/message.js +8 -6
- package/dist/milestone/milestone.js +3 -3
- package/dist/profile/agentProfile.js +3 -3
- package/dist/release/runtimeRelease.js +9 -7
- package/dist/repository/project.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +2 -2
- package/dist/review/reviewRound.js +5 -5
- package/dist/role/role.js +7 -8
- package/dist/runtime/acpProtocol.js +2 -3
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +3 -3
- package/dist/runtime/agentHostProtocol.js +2 -2
- package/dist/runtime/codexInteractiveHost.js +1 -1
- package/dist/runtime/launchBroker.js +1 -1
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/providerContinuationReconciliationService.js +84 -75
- package/dist/runtime/providerRuntimeIdentity.js +3 -3
- package/dist/runtime/runtimeCoherence.js +7 -3
- package/dist/runtime/runtimeObservation.js +3 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/taskRuntimeIsolation.js +3 -3
- package/dist/runtime/tmuxAdapters.js +3 -3
- package/dist/scheduler/taskWake.js +1 -1
- package/dist/storage/baselineSchema.js +606 -0
- package/dist/storage/homeLayout.js +5 -16
- package/dist/storage/recordValidation.js +16 -4
- package/dist/storage/sqliteSchema.js +106 -1741
- package/dist/storage/sqliteStore.js +16 -14
- package/dist/storage/storageSchema.js +8 -7
- package/dist/storage/storageVersions.js +23 -16
- package/dist/storage/taskStore.js +3 -29
- package/dist/storage/upgrade/upgradeOrchestrator.js +14 -102
- package/dist/task/task.js +15 -7
- package/dist/task/taskActivation.js +5 -4
- package/dist/telemetry/sqliteTelemetryStore.js +2 -2
- package/dist/verification/gateArtifact.js +5 -3
- package/dist/verification/verificationPlan.js +6 -7
- package/dist/workItem/workItem.js +6 -6
- package/dist/workspace/cleanupInspection.js +1 -9
- package/dist/worktree/managedWorkspace.js +3 -3
- package/docs/managed-turn-and-session-runtime.md +26 -14
- package/docs/managed-turn-and-session-runtime.zh-CN.md +18 -10
- package/docs/release-workflow.md +60 -300
- package/docs/release-workflow.zh-CN.md +46 -234
- package/docs/sqlite-control-plane-design.md +48 -289
- package/docs/sqlite-control-plane-design.zh-CN.md +37 -53
- package/docs/storage-baseline.md +139 -0
- package/docs/storage-baseline.zh-CN.md +111 -0
- package/docs/task-delivery.md +3 -4
- package/docs/task-delivery.zh-CN.md +3 -3
- package/docs/testing/verification-levels.md +40 -180
- package/docs/testing/verification-levels.zh-CN.md +27 -131
- package/i18n/README.zh-CN.md +5 -4
- package/package.json +1 -1
- package/dist/storage/migrations/agentFailureContext.js +0 -22
- package/dist/storage/migrations/agentRunContract.js +0 -159
- package/dist/storage/migrations/artifactsToGit.js +0 -338
- package/dist/storage/migrations/collapseWorktreeLayout.js +0 -963
- package/dist/storage/migrations/currentInputContract.js +0 -86
- package/dist/storage/migrations/currentRuntimeContract.js +0 -228
- package/dist/storage/migrations/historicalVerificationPlan.js +0 -35
- package/dist/storage/migrations/integrationContinuation.js +0 -105
- package/dist/storage/migrations/narrowAgentFailureContext.js +0 -65
- package/dist/storage/migrations/notificationOnlyWakes.js +0 -74
- package/dist/storage/migrations/removeRuntimeGeneration.js +0 -207
- package/dist/storage/migrations/submitIntent.js +0 -126
- package/dist/storage/migrations/unifyHomeLayout.js +0 -925
- package/dist/storage/migrations/verificationPlanV1.js +0 -162
- package/dist/storage/migrations/verificationPolicy.js +0 -74
- package/dist/storage/migrations/workItemHistory.js +0 -46
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* the write transaction; conflict ->
|
|
13
13
|
* StorageConflictError (transactionWithRevisionCas).
|
|
14
14
|
* - Atomic durable write ........ WAL + synchronous=FULL; COMMIT == fsync.
|
|
15
|
-
* - Mailbox per-target ordering . WorkMailbox
|
|
15
|
+
* - Mailbox per-target ordering . WorkMailbox sequence/cursors in one row.
|
|
16
16
|
* - Exactly-once terminal state . conditional updates + UNIQUE(request_id)
|
|
17
17
|
* on the durable outbox.
|
|
18
18
|
* - Crash recovery .............. WAL rollback of uncommitted transactions;
|
|
@@ -62,8 +62,9 @@ import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtif
|
|
|
62
62
|
import { validateWorkItem } from "../workItem/workItem.js";
|
|
63
63
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
64
64
|
import { contextInputReferences, queryContextRecords } from "./contextRecords.js";
|
|
65
|
-
import {
|
|
65
|
+
import { initializeSqliteSchema, validateSqliteSchema, SqliteSchemaError, TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
|
|
66
66
|
import { StorageSchemaError } from "./storageSchema.js";
|
|
67
|
+
import { STORAGE_FORMAT } from "./storageVersions.js";
|
|
67
68
|
import { queryTaskCatalog } from "./taskCatalog.js";
|
|
68
69
|
import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, pendingWakeupProjection, StorageCancelledError, StorageConflictError, StorageRecordError, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, validateYuiConfig } from "./taskStore.js";
|
|
69
70
|
/** Read the immutable Home identity without opening a writable Store connection. */
|
|
@@ -139,7 +140,8 @@ export class SqliteTaskStore {
|
|
|
139
140
|
#dirty = false;
|
|
140
141
|
constructor(rootDir, _options = {}) {
|
|
141
142
|
this.#rootDir = rootDir;
|
|
142
|
-
|
|
143
|
+
if (!_options.readonly)
|
|
144
|
+
mkdirSync(rootDir, { recursive: true, mode: 0o700 });
|
|
143
145
|
const filename = _options.databaseFilename ?? "yui.db";
|
|
144
146
|
const databasePath = join(rootDir, filename);
|
|
145
147
|
// Remember whether the database file existed before this connection opened
|
|
@@ -147,18 +149,17 @@ export class SqliteTaskStore {
|
|
|
147
149
|
// may seed its singleton rows; a store that opens an existing file missing
|
|
148
150
|
// those rows has found corruption, not a bootstrap opportunity.
|
|
149
151
|
const databaseExisted = existsSync(databasePath);
|
|
150
|
-
this.#db = new Database(databasePath);
|
|
152
|
+
this.#db = new Database(databasePath, _options.readonly ? { readonly: true, fileMustExist: true } : {});
|
|
151
153
|
try {
|
|
154
|
+
const existingSchema = databaseExisted ? validateSqliteSchema(this.#db) : undefined;
|
|
152
155
|
// §4.1 / §9: WAL, no fsync weakening, FKs on, busy timeout for CLI contention.
|
|
153
|
-
|
|
156
|
+
if (!_options.readonly)
|
|
157
|
+
this.#db.pragma("journal_mode = WAL");
|
|
154
158
|
this.#db.pragma("synchronous = FULL");
|
|
155
159
|
this.#db.pragma("foreign_keys = ON");
|
|
156
160
|
this.#db.pragma("busy_timeout = 5000");
|
|
157
161
|
this.#db.pragma("wal_autocheckpoint = 1000");
|
|
158
|
-
|
|
159
|
-
mode: !databaseExisted ? "apply" : "validate"
|
|
160
|
-
});
|
|
161
|
-
const schema = inspectSqliteSchemaMigrations(this.#db);
|
|
162
|
+
const schema = existingSchema ?? initializeSqliteSchema(this.#db);
|
|
162
163
|
this.#openedSchemaHead = {
|
|
163
164
|
version: schema.currentVersion,
|
|
164
165
|
checksum: schema.currentChecksum
|
|
@@ -309,12 +310,13 @@ export class SqliteTaskStore {
|
|
|
309
310
|
}
|
|
310
311
|
/** Verify the SQLite schema head at the single write boundary. */
|
|
311
312
|
#prepareWrite() {
|
|
312
|
-
const current = this.#db.prepare("SELECT
|
|
313
|
-
if (current?.
|
|
313
|
+
const current = this.#db.prepare("SELECT format, major || '.' || minor AS version, checksum FROM storage_schema WHERE id=1").get();
|
|
314
|
+
if (current?.format !== STORAGE_FORMAT
|
|
315
|
+
|| current.version !== this.#openedSchemaHead.version
|
|
314
316
|
|| current.checksum !== this.#openedSchemaHead.checksum) {
|
|
315
|
-
throw new
|
|
317
|
+
throw new SqliteSchemaError(`open Store format ${STORAGE_FORMAT} found ${String(current?.format)}; schema head ${this.#openedSchemaHead.version}/${this.#openedSchemaHead.checksum} `
|
|
316
318
|
+ `changed to ${String(current?.version)}/${String(current?.checksum)}; `
|
|
317
|
-
+ "reopen the Store with the active Yui version before writing"
|
|
319
|
+
+ "reopen the Store with the active Yui version before writing");
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
#bumpRevision() {
|
|
@@ -1754,7 +1756,7 @@ export class SqliteTaskStore {
|
|
|
1754
1756
|
// -- active turns ----------------------------------------------------------
|
|
1755
1757
|
#saveActiveRun(taskId, pointer, runId) {
|
|
1756
1758
|
this.#mutate(() => {
|
|
1757
|
-
const payload = this.#json({ schemaVersion:
|
|
1759
|
+
const payload = this.#json({ schemaVersion: 1, runId });
|
|
1758
1760
|
this.#db.prepare(`INSERT INTO active_turns (task_id, pointer, turn_id, payload, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
1759
1761
|
ON CONFLICT(task_id, pointer) DO UPDATE SET turn_id = excluded.turn_id, payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, pointer, runId, payload, this.#now());
|
|
1760
1762
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
|
-
import {
|
|
5
|
-
import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "./storageVersions.js";
|
|
4
|
+
import { inspectSqliteSchema, storageMinorUpgradePlan, SqliteSchemaError } from "./sqliteSchema.js";
|
|
5
|
+
import { compareStorageVersions, CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "./storageVersions.js";
|
|
6
6
|
export { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "./storageVersions.js";
|
|
7
7
|
export const CURRENT_DATABASE_FILENAME = "yui.db";
|
|
8
8
|
export class StorageSchemaError extends Error {
|
|
@@ -47,7 +47,7 @@ export function inspectStorageSchema(rootDir) {
|
|
|
47
47
|
return invalid(databasePath, error);
|
|
48
48
|
}
|
|
49
49
|
try {
|
|
50
|
-
const migration =
|
|
50
|
+
const migration = inspectSqliteSchema(database);
|
|
51
51
|
const fields = {
|
|
52
52
|
currentVersion: migration.currentVersion,
|
|
53
53
|
latestVersion: CURRENT_STORAGE_VERSION,
|
|
@@ -57,13 +57,13 @@ export function inspectStorageSchema(rootDir) {
|
|
|
57
57
|
if (migration.currentVersion === CURRENT_STORAGE_VERSION) {
|
|
58
58
|
return { status: "current", ...fields };
|
|
59
59
|
}
|
|
60
|
-
if (migration.currentVersion >
|
|
60
|
+
if (compareStorageVersions(migration.currentVersion, CURRENT_STORAGE_VERSION) > 0) {
|
|
61
61
|
return { status: "unsupported", direction: "newer", ...fields };
|
|
62
62
|
}
|
|
63
|
-
if (migration.currentVersion <
|
|
63
|
+
if (compareStorageVersions(migration.currentVersion, MIN_SUPPORTED_STORAGE_VERSION) < 0) {
|
|
64
64
|
return { status: "unsupported", direction: "older", ...fields };
|
|
65
65
|
}
|
|
66
|
-
const plan =
|
|
66
|
+
const plan = storageMinorUpgradePlan(migration.currentVersion);
|
|
67
67
|
if (plan === null) {
|
|
68
68
|
return {
|
|
69
69
|
status: "invalid",
|
|
@@ -114,6 +114,7 @@ function invalid(databasePath, error) {
|
|
|
114
114
|
latestVersion: CURRENT_STORAGE_VERSION,
|
|
115
115
|
minimumSupportedVersion: MIN_SUPPORTED_STORAGE_VERSION,
|
|
116
116
|
databasePath,
|
|
117
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
117
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
118
|
+
reason: error instanceof SqliteSchemaError && error.code === "STORAGE_FORMAT_UNSUPPORTED" ? "format" : "corruption"
|
|
118
119
|
};
|
|
119
120
|
}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export
|
|
16
|
-
|
|
1
|
+
export const CURRENT_STORAGE_VERSION = "1.0";
|
|
2
|
+
export const MIN_SUPPORTED_STORAGE_VERSION = "1.0";
|
|
3
|
+
export const STORAGE_FORMAT = "yui-home";
|
|
4
|
+
export function isStorageVersion(value) {
|
|
5
|
+
if (typeof value !== "string" || !/^[1-9]\d*\.(0|[1-9]\d*)$/u.test(value))
|
|
6
|
+
return false;
|
|
7
|
+
return value.split(".").every(part => Number.isSafeInteger(Number(part)));
|
|
8
|
+
}
|
|
9
|
+
export function storageVersionParts(value) {
|
|
10
|
+
if (!isStorageVersion(value))
|
|
11
|
+
throw new Error(`Invalid storage version: ${String(value)}.`);
|
|
12
|
+
const [major, minor] = value.split(".").map(Number);
|
|
13
|
+
return { major: major, minor: minor };
|
|
14
|
+
}
|
|
15
|
+
export function compareStorageVersions(left, right) {
|
|
16
|
+
const a = storageVersionParts(left), b = storageVersionParts(right);
|
|
17
|
+
return Math.sign(a.major - b.major || a.minor - b.minor);
|
|
18
|
+
}
|
|
19
|
+
/** Cross-major conversion is never an ordinary update, even if explicitly pinned. */
|
|
20
|
+
export function isMinorStorageUpgrade(from, to) {
|
|
21
|
+
const a = storageVersionParts(from), b = storageVersionParts(to);
|
|
22
|
+
return a.major === b.major && b.minor > a.minor;
|
|
23
|
+
}
|
|
@@ -9,30 +9,10 @@ import { validateReleaseWorkflow } from "../release/releaseWorkflow.js";
|
|
|
9
9
|
import { validateReviewConfig } from "../review/reviewConfig.js";
|
|
10
10
|
import { validatePublicationReference } from "../task/publicationReference.js";
|
|
11
11
|
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
12
|
-
export const CURRENT_CONFIG_SCHEMA_VERSION =
|
|
13
|
-
|
|
14
|
-
export const CURRENT_CONFIGURED_AGENT_SCHEMA_VERSION = 2;
|
|
15
|
-
export const CURRENT_PROJECT_SCHEMA_VERSION = 6;
|
|
16
|
-
export const CURRENT_AGENT_PROFILE_SCHEMA_VERSION = 3;
|
|
17
|
-
export const CURRENT_GLOBAL_ROLE_SCHEMA_VERSION = 3;
|
|
18
|
-
export const CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION = 5;
|
|
19
|
-
export const CURRENT_TASK_SCHEMA_VERSION = 7;
|
|
20
|
-
export const CURRENT_TASK_BRIEF_SCHEMA_VERSION = 2;
|
|
21
|
-
export const CURRENT_CONTEXT_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
22
|
-
export const CURRENT_TASK_ROLE_SCHEMA_VERSION = 4;
|
|
23
|
-
export const CURRENT_MANAGED_WORKSPACE_SCHEMA_VERSION = 2;
|
|
24
|
-
export const CURRENT_WORK_ITEM_SCHEMA_VERSION = 15;
|
|
25
|
-
export const CURRENT_REVIEW_ROUND_SCHEMA_VERSION = 8;
|
|
26
|
-
export const CURRENT_CHANGE_SET_SCHEMA_VERSION = 4;
|
|
27
|
-
export const CURRENT_INTEGRATION_ATTEMPT_SCHEMA_VERSION = 6;
|
|
28
|
-
export const CURRENT_MESSAGE_SCHEMA_VERSION = 3;
|
|
29
|
-
export const CURRENT_INPUT_REQUEST_SCHEMA_VERSION = 3;
|
|
30
|
-
export const CURRENT_DECISION_SCHEMA_VERSION = 1;
|
|
31
|
-
export const CURRENT_MILESTONE_SCHEMA_VERSION = 2;
|
|
32
|
-
export const CURRENT_EVENT_SCHEMA_VERSION = 2;
|
|
33
|
-
export const CURRENT_CAPABILITY_GRANT_SCHEMA_VERSION = 2;
|
|
12
|
+
export const CURRENT_CONFIG_SCHEMA_VERSION = 1;
|
|
13
|
+
export const CURRENT_CAPABILITY_GRANT_SCHEMA_VERSION = 1;
|
|
34
14
|
export const CURRENT_RELEASE_WORKFLOW_SCHEMA_VERSION = 1;
|
|
35
|
-
export const CURRENT_WORK_MAILBOX_SCHEMA_VERSION =
|
|
15
|
+
export const CURRENT_WORK_MAILBOX_SCHEMA_VERSION = 1;
|
|
36
16
|
export const CURRENT_PUBLICATION_REFERENCE_SCHEMA_VERSION = 1;
|
|
37
17
|
export const CURRENT_PENDING_WAKEUP_SCHEMA_VERSION = 1;
|
|
38
18
|
export const COMPLETION_SHELLS = ["bash", "zsh", "fish"];
|
|
@@ -62,12 +42,6 @@ export function executionLaneActiveRunKeyParts(key) {
|
|
|
62
42
|
return null;
|
|
63
43
|
}
|
|
64
44
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Nested-record versions consumed by the current SQLite payload validators.
|
|
67
|
-
*/
|
|
68
|
-
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 12;
|
|
69
|
-
export const CURRENT_RUN_SCHEMA_VERSION = 5;
|
|
70
|
-
export const CURRENT_INTEGRATION_QUEUE_SCHEMA_VERSION = 1;
|
|
71
45
|
export class StorageRecordError extends Error {
|
|
72
46
|
constructor(message) { super(message); this.name = "StorageRecordError"; }
|
|
73
47
|
}
|
|
@@ -5,17 +5,11 @@ import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation } from "../../
|
|
|
5
5
|
import { RUNTIME_PROCESS_EXIT_TASK_EVENT, validateRuntimeProcessExitObservation } from "../../runtime/processExitObservation.js";
|
|
6
6
|
import { SqliteTaskStore } from "../sqliteStore.js";
|
|
7
7
|
import { storageBackupRoot } from "../homeLayout.js";
|
|
8
|
-
import {
|
|
9
|
-
import { preflightCollapseWorktreeLayout } from "../migrations/collapseWorktreeLayout.js";
|
|
10
|
-
import { preflightNotificationOnlyWakes } from "../migrations/notificationOnlyWakes.js";
|
|
11
|
-
import { preflightVerificationPolicy } from "../migrations/verificationPolicy.js";
|
|
12
|
-
import { preflightCurrentInputContract } from "../migrations/currentInputContract.js";
|
|
13
|
-
import { preflightCurrentRuntimeContract } from "../migrations/currentRuntimeContract.js";
|
|
14
|
-
import { migrateSqliteSchema, storageMigrationPlan } from "../sqliteSchema.js";
|
|
8
|
+
import { applySqliteMinorUpgrades, storageMinorUpgradePlan } from "../sqliteSchema.js";
|
|
15
9
|
import { CURRENT_DATABASE_FILENAME, inspectStorageSchema } from "../storageSchema.js";
|
|
16
10
|
import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "../storageVersions.js";
|
|
17
11
|
/**
|
|
18
|
-
* Upgrade
|
|
12
|
+
* Upgrade only recognized minor versions within the current Home major.
|
|
19
13
|
*
|
|
20
14
|
* Supported earlier versions are readable only here. Ordinary stores still
|
|
21
15
|
* admit exactly {@link CURRENT_STORAGE_VERSION}; no old-shape normalizer or
|
|
@@ -29,6 +23,13 @@ export async function runStorageUpgrade(options) {
|
|
|
29
23
|
return blocked({ ...corruptedClassification("Storage is not initialized."), uninitialized: true }, "uninitialized", "Yui storage is not initialized for this Home.", "Run `yui setup` with a new Home.");
|
|
30
24
|
}
|
|
31
25
|
if (state.status === "invalid") {
|
|
26
|
+
if (state.reason === "format") {
|
|
27
|
+
return blocked({
|
|
28
|
+
classification: { verdict: "UNSUPPORTED_FORMAT", status: "unsupported", detail: state.detail },
|
|
29
|
+
currentStorageVersion: CURRENT_STORAGE_VERSION,
|
|
30
|
+
minimumSupportedStorageVersion: MIN_SUPPORTED_STORAGE_VERSION
|
|
31
|
+
}, "unsupported", state.detail, "Keep this Home unchanged and use its matching release or the independent explicit conversion tool. Ordinary upgrades never cross storage formats.");
|
|
32
|
+
}
|
|
32
33
|
return blocked(corruptedClassification(state.detail), "corruption", `Storage is invalid: ${state.detail}`, "Preserve this Home for diagnosis and restore it from a known-good backup.");
|
|
33
34
|
}
|
|
34
35
|
if (state.status === "unsupported") {
|
|
@@ -68,27 +69,12 @@ export async function runStorageUpgrade(options) {
|
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
|
-
const plan =
|
|
72
|
+
const plan = storageMinorUpgradePlan(state.currentVersion);
|
|
72
73
|
if (plan === null) {
|
|
73
74
|
return blocked(corruptedClassification(`No complete migration path exists from ${state.currentVersion} `
|
|
74
|
-
+ `to ${CURRENT_STORAGE_VERSION}.`), "corruption", "The storage migration registry is incomplete.", "
|
|
75
|
+
+ `to ${CURRENT_STORAGE_VERSION}.`), "corruption", "The storage migration registry is incomplete.", "Use a release with an explicit same-major minor upgrade path; cross-major conversion is separate.");
|
|
75
76
|
}
|
|
76
77
|
const classification = migratableClassification(state.currentVersion);
|
|
77
|
-
// Genuine pre-migration safety check, shared by ALL migrating modes. The
|
|
78
|
-
// unify-home data migration (18->19) physically relocates on-disk trees and
|
|
79
|
-
// rewrites live launch pointers, so before any mode proceeds we run its READ-
|
|
80
|
-
// ONLY preflight and refuse up front on the same conditions the migration would
|
|
81
|
-
// throw on inside the apply transaction: a queued/running Job bound under a
|
|
82
|
-
// relocating root, or a conflicting relocation target. The migration is applied
|
|
83
|
-
// offline (after the operator has stopped this Home's writers), so the preflight
|
|
84
|
-
// does not scan for live processes. Surfacing these here — before the Controller
|
|
85
|
-
// is stopped or a backup is taken — is what makes `--dry-run` and the updater's
|
|
86
|
-
// `--update-preflight` a genuine readiness signal rather than a mere list of
|
|
87
|
-
// schema steps, while the in-transaction checks remain the authoritative guard
|
|
88
|
-
// against anything that starts after this point.
|
|
89
|
-
const migrationBlocked = preflightMigrationBlockers(options.home, plan, classification);
|
|
90
|
-
if (migrationBlocked !== null)
|
|
91
|
-
return migrationBlocked;
|
|
92
78
|
if (options.mode === "update-preflight") {
|
|
93
79
|
return {
|
|
94
80
|
outcome: "update-preflight",
|
|
@@ -134,7 +120,7 @@ export async function runStorageUpgrade(options) {
|
|
|
134
120
|
database.pragma("synchronous = FULL");
|
|
135
121
|
database.pragma("foreign_keys = ON");
|
|
136
122
|
database.pragma("busy_timeout = 5000");
|
|
137
|
-
|
|
123
|
+
applySqliteMinorUpgrades(database);
|
|
138
124
|
migrationCommitted = true;
|
|
139
125
|
}
|
|
140
126
|
finally {
|
|
@@ -191,7 +177,7 @@ export async function runStorageUpgrade(options) {
|
|
|
191
177
|
};
|
|
192
178
|
}
|
|
193
179
|
function validateCurrentStore(home) {
|
|
194
|
-
const store = new SqliteTaskStore(home);
|
|
180
|
+
const store = new SqliteTaskStore(home, { readonly: true });
|
|
195
181
|
try {
|
|
196
182
|
store.validateCurrentRecords();
|
|
197
183
|
for (const taskId of store.listTasks().map(({ id }) => id)) {
|
|
@@ -274,7 +260,7 @@ function unsupportedClassification(state) {
|
|
|
274
260
|
const action = future
|
|
275
261
|
? "Use a newer Yui release."
|
|
276
262
|
: "Preserve this Home for use with its matching historical Yui release, "
|
|
277
|
-
+ "or
|
|
263
|
+
+ "or use the independent explicit converter for a different storage major.";
|
|
278
264
|
return {
|
|
279
265
|
classification: {
|
|
280
266
|
verdict: "NEEDS_NEW_VERSION",
|
|
@@ -310,80 +296,6 @@ function blocked(classification, stage, message, action) {
|
|
|
310
296
|
sceneUnchanged: true
|
|
311
297
|
};
|
|
312
298
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Run each data migration's registered READ-ONLY preflight against the
|
|
315
|
-
* current (pre-upgrade) database and, when any finds a blocker, return a
|
|
316
|
-
* `blocked` result the caller surfaces before touching the Controller or the
|
|
317
|
-
* Home. Returns `null` when the plan carries no registered readiness check,
|
|
318
|
-
* or when every preflight is clear.
|
|
319
|
-
*
|
|
320
|
-
* Both the 23->24 unify and the 24->25 collapse migrations physically relocate
|
|
321
|
-
* managed worktrees and share the same blocker shape (in-flight Job, conflicting
|
|
322
|
-
* relocation target). When a Home is upgraded across both in one run, their
|
|
323
|
-
* blockers are aggregated so the operator sees their readiness problems together.
|
|
324
|
-
* Notification retirement separately rejects unsettled execution references.
|
|
325
|
-
*
|
|
326
|
-
* The database is opened read-only so the checks cannot mutate the authoritative
|
|
327
|
-
* store, and the handle is always closed. An unexpected failure to evaluate a
|
|
328
|
-
* preflight is itself a fail-closed blocker: we must not advance to an
|
|
329
|
-
* irreversible migration on an unverifiable readiness signal.
|
|
330
|
-
*/
|
|
331
|
-
function preflightMigrationBlockers(home, plan, classification) {
|
|
332
|
-
const relocatesUnify = plan.some((step) => step.name === "unify-home-layout");
|
|
333
|
-
const relocatesCollapse = plan.some((step) => step.name === "collapse-worktree-layout");
|
|
334
|
-
const retiresRunWakes = plan.some((step) => step.name === "notification-only-wakes");
|
|
335
|
-
const changesVerification = plan.some(step => step.name === "advisory-and-verification-policy");
|
|
336
|
-
const changesInput = plan.some(step => step.name === "current-input-contract");
|
|
337
|
-
const retiresRuntime = plan.some(step => step.name === "current-verification-and-owner-contract");
|
|
338
|
-
if (!relocatesUnify && !relocatesCollapse && !retiresRunWakes && !changesVerification && !changesInput && !retiresRuntime)
|
|
339
|
-
return null;
|
|
340
|
-
let blockers;
|
|
341
|
-
try {
|
|
342
|
-
const database = new Database(join(home, CURRENT_DATABASE_FILENAME), {
|
|
343
|
-
readonly: true,
|
|
344
|
-
fileMustExist: true
|
|
345
|
-
});
|
|
346
|
-
try {
|
|
347
|
-
blockers = [];
|
|
348
|
-
if (relocatesUnify)
|
|
349
|
-
blockers.push(...preflightUnifyHomeLayout(database).blockers);
|
|
350
|
-
if (relocatesCollapse)
|
|
351
|
-
blockers.push(...preflightCollapseWorktreeLayout(database).blockers);
|
|
352
|
-
if (retiresRunWakes)
|
|
353
|
-
preflightNotificationOnlyWakes(database);
|
|
354
|
-
if (changesVerification)
|
|
355
|
-
preflightVerificationPolicy(database);
|
|
356
|
-
if (changesInput)
|
|
357
|
-
preflightCurrentInputContract(database);
|
|
358
|
-
if (retiresRuntime)
|
|
359
|
-
preflightCurrentRuntimeContract(database);
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
database.close();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
catch (error) {
|
|
366
|
-
return {
|
|
367
|
-
...blocked(classification, "in-flight", `The storage migration readiness check could not be completed: ${messageOf(error)}`, "Resolve the named execution or resource boundary, preserving original input and results. "
|
|
368
|
-
+ "Confirm the relevant Runs and Jobs are settled, then rerun the upgrade.")
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
if (blockers.length === 0)
|
|
372
|
-
return null;
|
|
373
|
-
return {
|
|
374
|
-
outcome: "blocked",
|
|
375
|
-
stage: "in-flight",
|
|
376
|
-
message: "Refusing to migrate: the Home is not safe to relocate under the current layout yet. "
|
|
377
|
-
+ blockers.map((entry) => entry.detail).join("; ") + ".",
|
|
378
|
-
action: "Resolve the reported condition(s) — let a queued/running Job finish or cancel it, and "
|
|
379
|
-
+ "clear any conflicting relocation target — then rerun the upgrade. The migration is applied "
|
|
380
|
-
+ "offline, so first confirm the Controller and any execution writers for this Home are "
|
|
381
|
-
+ "stopped. The authoritative Home is unchanged.",
|
|
382
|
-
blockers: blockers.map((entry) => ({ reason: entry.reason, detail: entry.detail })),
|
|
383
|
-
classification,
|
|
384
|
-
sceneUnchanged: true
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
299
|
function messageOf(error) {
|
|
388
300
|
return error instanceof Error ? error.message : String(error);
|
|
389
301
|
}
|
package/dist/task/task.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cancelTaskActivationRequest, recordTaskActivationEvidence, validateTask
|
|
|
3
3
|
export function createTask(id, title, now, metadata = {}) {
|
|
4
4
|
const timestamp = now.toISOString();
|
|
5
5
|
return {
|
|
6
|
-
schemaVersion:
|
|
6
|
+
schemaVersion: 1,
|
|
7
7
|
id: requireSafeIdentity(id, "Task id"),
|
|
8
8
|
title: requireText(title, "Task title"),
|
|
9
9
|
...cloneMetadata(metadata),
|
|
@@ -16,7 +16,7 @@ export function createTask(id, title, now, metadata = {}) {
|
|
|
16
16
|
/**
|
|
17
17
|
* Persist the Task's durable workspace identity. The identity is immutable on
|
|
18
18
|
* the Task: a second binding is rejected so restart/reconcile/attach can never
|
|
19
|
-
* silently adopt a different
|
|
19
|
+
* silently adopt a different workspace ref namespace.
|
|
20
20
|
*/
|
|
21
21
|
export function bindTaskWorkspaceIdentity(task, identity, now) {
|
|
22
22
|
validateTask(task);
|
|
@@ -409,8 +409,19 @@ export function startTaskExecution(task, now) {
|
|
|
409
409
|
});
|
|
410
410
|
}
|
|
411
411
|
export function validateTask(task) {
|
|
412
|
-
if (task.schemaVersion !==
|
|
413
|
-
throw new Error("Task must use schemaVersion
|
|
412
|
+
if (task.schemaVersion !== 1)
|
|
413
|
+
throw new Error("Task must use schemaVersion 1.");
|
|
414
|
+
const fields = [
|
|
415
|
+
"schemaVersion", "id", "title", "type", "description", "priority", "tags", "dueAt",
|
|
416
|
+
"projectBindings", "cwd", "workspaceIdentity", "status", "executionGate", "activationRequest",
|
|
417
|
+
"settledActivationRequests", "completedAt", "completedBy", "completionSummary", "completionArtifactRefs",
|
|
418
|
+
"retiredAt", "retiredBy", "retirementSummary", "retirementIsolation", "replacementTaskId",
|
|
419
|
+
"archivedAt", "archivedBy", "archiveReason", "archiveSummary", "createdAt", "updatedAt"
|
|
420
|
+
];
|
|
421
|
+
for (const field of Object.keys(task)) {
|
|
422
|
+
if (!fields.includes(field))
|
|
423
|
+
throw new Error(`Task contains an unsupported field: ${field}.`);
|
|
424
|
+
}
|
|
414
425
|
requireSafeIdentity(task.id, "Task id");
|
|
415
426
|
requireText(task.title, "Task title");
|
|
416
427
|
if (task.type !== undefined)
|
|
@@ -492,9 +503,6 @@ export function validateTask(task) {
|
|
|
492
503
|
normalizeProjectBindings(task.projectBindings);
|
|
493
504
|
if (task.cwd !== undefined)
|
|
494
505
|
requireText(task.cwd, "Task workspace");
|
|
495
|
-
if (Object.hasOwn(task, "legacyDeliveryPath")) {
|
|
496
|
-
throw new Error("Task contains the removed delivery-path field.");
|
|
497
|
-
}
|
|
498
506
|
const completionFields = [task.completedAt, task.completedBy, task.completionSummary];
|
|
499
507
|
const hasAnyCompletion = completionFields.some((value) => value !== undefined);
|
|
500
508
|
const hasAllCompletion = completionFields.every((value) => value !== undefined);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
-
import { requireIdentity, requireTimestamp } from "../domain/validation.js";
|
|
3
|
+
import { requireIdentity, requireTimestamp, requireKnownFields } from "../domain/validation.js";
|
|
4
4
|
import { recordOperationEvidence, validateOperationFacts } from "../kernel/operationFacts.js";
|
|
5
5
|
export const TASK_ACTIVATION_IMPLEMENTATION = Object.freeze({
|
|
6
6
|
id: "yui:task-activation",
|
|
@@ -229,6 +229,10 @@ export function validateTaskActivationRequest(request) {
|
|
|
229
229
|
if (request.schemaVersion !== 1) {
|
|
230
230
|
throw new Error("Activation request must use schemaVersion 1.");
|
|
231
231
|
}
|
|
232
|
+
requireKnownFields(request, [
|
|
233
|
+
"schemaVersion", "operation", "startMode", "afterPlanningRun", "environmentPlan", "disposition",
|
|
234
|
+
"preparationId", "outcome", "requestedAt", "updatedAt"
|
|
235
|
+
], "Activation request");
|
|
232
236
|
validateOperationFacts(request.operation);
|
|
233
237
|
if (request.operation.capability !== TASK_ACTIVATION_CAPABILITY) {
|
|
234
238
|
throw new Error("Activation request capability is invalid.");
|
|
@@ -236,9 +240,6 @@ export function validateTaskActivationRequest(request) {
|
|
|
236
240
|
if (!["immediate", "after-planning-turn"].includes(request.startMode)) {
|
|
237
241
|
throw new Error(`Activation start mode is invalid: ${String(request.startMode)}.`);
|
|
238
242
|
}
|
|
239
|
-
if (Object.hasOwn(request, "origin")) {
|
|
240
|
-
throw new Error("Activation contains retired origin metadata; use the storage upgrade boundary.");
|
|
241
|
-
}
|
|
242
243
|
if ((request.startMode === "after-planning-turn")
|
|
243
244
|
!== (request.afterPlanningRun !== undefined)) {
|
|
244
245
|
throw new Error("Activation deferral must name exactly the planning Turn it waits for.");
|
|
@@ -2,7 +2,7 @@ import Database from "better-sqlite3";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
5
|
-
import {
|
|
5
|
+
import { validateSqliteSchema } from "../storage/sqliteSchema.js";
|
|
6
6
|
import { AsyncTaskStoreClient } from "../storage/storeRpc.js";
|
|
7
7
|
import { DEFAULT_RUN_CAP, DEFAULT_TERMINAL_KEEP } from "./telemetryConfig.js";
|
|
8
8
|
/**
|
|
@@ -251,13 +251,13 @@ export class SqliteTelemetryStore {
|
|
|
251
251
|
throw new Error(`Telemetry database not found: ${this.#path}`);
|
|
252
252
|
}
|
|
253
253
|
const db = opening = new Database(this.#path);
|
|
254
|
+
validateSqliteSchema(db);
|
|
254
255
|
db.pragma("journal_mode = WAL");
|
|
255
256
|
db.pragma("synchronous = FULL");
|
|
256
257
|
db.pragma("foreign_keys = ON");
|
|
257
258
|
// Cold diagnostic reads/retention must not wait behind a semantic writer.
|
|
258
259
|
db.pragma("busy_timeout = 0");
|
|
259
260
|
db.pragma("wal_autocheckpoint = 1000");
|
|
260
|
-
migrateSqliteSchema(db, { mode: "validate" });
|
|
261
261
|
this.#db = db;
|
|
262
262
|
return db;
|
|
263
263
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
|
-
import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
|
|
3
|
+
import { requireIdentity, requireKnownFields, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
|
|
4
4
|
/**
|
|
5
5
|
* Issue 08: the reusable exact-SHA gate evidence record.
|
|
6
6
|
*
|
|
@@ -80,6 +80,10 @@ export function recordGateArtifactReuse(artifact, now) {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
export function validateGateArtifact(artifact) {
|
|
83
|
+
requireKnownFields(artifact, [
|
|
84
|
+
"schemaVersion", "key", "projectId", "level", "commit", "planId", "planVersion", "planDigest", "toolchainDigest",
|
|
85
|
+
"boundary", "steps", "generator", "status", "outcome", "reuseCount", "createdAt", "completedAt", "lastUsedAt"
|
|
86
|
+
], "GateArtifact");
|
|
83
87
|
if (artifact.schemaVersion !== GATE_ARTIFACT_SCHEMA_VERSION) {
|
|
84
88
|
throw new Error(`GateArtifact must use schemaVersion ${GATE_ARTIFACT_SCHEMA_VERSION}.`);
|
|
85
89
|
}
|
|
@@ -139,8 +143,6 @@ export function validateGateArtifact(artifact) {
|
|
|
139
143
|
throw new Error(`GateArtifact step ${step.name} logBytes is invalid.`);
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
|
-
if (Object.hasOwn(artifact, "potentialReuseCount"))
|
|
143
|
-
throw new Error("GateArtifact contains retired shadow metrics.");
|
|
144
146
|
requirePositiveInteger(artifact.reuseCount + 1, "GateArtifact reuseCount");
|
|
145
147
|
requireTimestamp(artifact.createdAt, "GateArtifact createdAt");
|
|
146
148
|
requireTimestamp(artifact.lastUsedAt, "GateArtifact lastUsedAt");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { requireIdentity, requirePositiveInteger, requireText } from "../domain/validation.js";
|
|
3
|
+
import { requireIdentity, requireKnownFields, requirePositiveInteger, requireText } from "../domain/validation.js";
|
|
4
4
|
/**
|
|
5
5
|
* Issue 08: the per-Project VerificationPlan contract.
|
|
6
6
|
*
|
|
@@ -11,7 +11,7 @@ import { requireIdentity, requirePositiveInteger, requireText } from "../domain/
|
|
|
11
11
|
* explicit `--check <shell>` path, which is marked unstructured and is not
|
|
12
12
|
* reusable across stages.
|
|
13
13
|
*/
|
|
14
|
-
export const VERIFICATION_PLAN_SCHEMA_VERSION =
|
|
14
|
+
export const VERIFICATION_PLAN_SCHEMA_VERSION = 1;
|
|
15
15
|
export const VERIFICATION_PLAN_KIND = "verification-plan";
|
|
16
16
|
/** The reserved knowledge marker that carries a Project's VerificationPlan. */
|
|
17
17
|
export const VERIFICATION_PLAN_KNOWLEDGE_KIND = VERIFICATION_PLAN_KIND;
|
|
@@ -27,10 +27,9 @@ export function normalizeVerificationPlan(raw) {
|
|
|
27
27
|
if (schemaVersion !== VERIFICATION_PLAN_SCHEMA_VERSION) {
|
|
28
28
|
throw new Error(`VerificationPlan schemaVersion must be ${VERIFICATION_PLAN_SCHEMA_VERSION}.`);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
throw new Error("VerificationPlan l1 is retired; declare current checks in l2.");
|
|
30
|
+
requireKnownFields(record, [
|
|
31
|
+
"schemaVersion", "kind", "id", "version", "toolchain", "bootstrap", "l2", "l3", "excludedRealResourceChecks", "artifactTtlDays"
|
|
32
|
+
], "VerificationPlan");
|
|
34
33
|
const plan = {
|
|
35
34
|
schemaVersion: VERIFICATION_PLAN_SCHEMA_VERSION,
|
|
36
35
|
kind: VERIFICATION_PLAN_KIND,
|
|
@@ -145,7 +144,7 @@ export function verificationPlanDigest(plan) {
|
|
|
145
144
|
const canonical = canonicalJson({
|
|
146
145
|
// Old artifacts may have skipped shell commands or run in the wrong cwd.
|
|
147
146
|
// Keep that history, but never reuse it as proof under corrected semantics.
|
|
148
|
-
executionContract: "
|
|
147
|
+
executionContract: "yui-baseline/clean-candidate/l2-only/v1",
|
|
149
148
|
id: plan.id,
|
|
150
149
|
version: plan.version,
|
|
151
150
|
toolchain: plan.toolchain,
|
|
@@ -12,7 +12,7 @@ const TERMINAL_STATUSES = [
|
|
|
12
12
|
export function createWorkItem(id, taskId, input, now) {
|
|
13
13
|
const timestamp = now.toISOString();
|
|
14
14
|
return validateWorkItem({
|
|
15
|
-
schemaVersion:
|
|
15
|
+
schemaVersion: 1,
|
|
16
16
|
id: requireIdentity(id, "Work Item id"),
|
|
17
17
|
taskId: requireIdentity(taskId, "Task id"),
|
|
18
18
|
title: requireText(input.title, "Work item title"),
|
|
@@ -90,7 +90,7 @@ export function submitWorkItemCandidate(workItem, input, now) {
|
|
|
90
90
|
const revision = workItem.revision + 1;
|
|
91
91
|
const sequence = workItem.candidates.length + 1;
|
|
92
92
|
const candidate = validateWorkItemCandidate({
|
|
93
|
-
schemaVersion:
|
|
93
|
+
schemaVersion: 1,
|
|
94
94
|
id: `candidate-${sequence}`,
|
|
95
95
|
taskId: workItem.taskId,
|
|
96
96
|
workItemId: workItem.id,
|
|
@@ -321,8 +321,8 @@ export function validateWorkItem(workItem) {
|
|
|
321
321
|
"acceptedCandidateId",
|
|
322
322
|
"currentCandidateId"
|
|
323
323
|
], "WorkItem");
|
|
324
|
-
if (workItem.schemaVersion !==
|
|
325
|
-
throw new Error("WorkItem must use schemaVersion
|
|
324
|
+
if (workItem.schemaVersion !== 1)
|
|
325
|
+
throw new Error("WorkItem must use schemaVersion 1.");
|
|
326
326
|
validateTaskRecordReference({ taskId: workItem.taskId, localId: workItem.id }, "workItem");
|
|
327
327
|
requireIdentity(workItem.taskId, "Task id");
|
|
328
328
|
requireText(workItem.title, "Work item title");
|
|
@@ -470,8 +470,8 @@ export function validateWorkItemCandidate(candidate) {
|
|
|
470
470
|
if (typeof candidate !== "object" || candidate === null) {
|
|
471
471
|
throw new Error("Work Item candidate is required.");
|
|
472
472
|
}
|
|
473
|
-
if (candidate.schemaVersion !==
|
|
474
|
-
throw new Error("Work Item candidate must use schemaVersion
|
|
473
|
+
if (candidate.schemaVersion !== 1) {
|
|
474
|
+
throw new Error("Work Item candidate must use schemaVersion 1.");
|
|
475
475
|
}
|
|
476
476
|
requireIdentity(candidate.taskId, "Work Item candidate Task id");
|
|
477
477
|
validateTaskRecordReference({
|
|
@@ -36,10 +36,7 @@ export function cleanupFailure(reason, detail, expected, observed, status = "blo
|
|
|
36
36
|
throw new CleanupInspectionError([{ resource: "git-workspace", reason, status,
|
|
37
37
|
detail, expected, observed, sources: [], actions: [] }]);
|
|
38
38
|
}
|
|
39
|
-
/** Describe exact differences without disclosing paths outside this Task.
|
|
40
|
-
* Legacy locations are displayed only for this Task's exact branch identity;
|
|
41
|
-
* displaying a historical location is not accepting it as migration proof.
|
|
42
|
-
*/
|
|
39
|
+
/** Describe exact differences without disclosing paths outside this Task. */
|
|
43
40
|
export function workspacePathValue(workspace, path) {
|
|
44
41
|
const inside = (root) => {
|
|
45
42
|
const value = relative(root, path);
|
|
@@ -52,11 +49,6 @@ export function workspacePathValue(workspace, path) {
|
|
|
52
49
|
const taskPath = inside(join(home, "workspaces", "tasks", workspace.owner.taskId));
|
|
53
50
|
if (taskPath !== undefined)
|
|
54
51
|
return `<task>/${taskPath}`;
|
|
55
|
-
const legacyPath = inside(join(home, "workspaces", "worktree"));
|
|
56
|
-
const parts = legacyPath?.split("/");
|
|
57
|
-
const taskSegment = workspace.entries.find(e => e.access === "write")?.branch.split("/")[1];
|
|
58
|
-
if (parts?.length === 3 && parts[1] === taskSegment)
|
|
59
|
-
return `<legacy-worktree>/${legacyPath}`;
|
|
60
52
|
}
|
|
61
53
|
const local = inside(workspace.root);
|
|
62
54
|
return local === undefined ? "[outside authorized Task paths]" : `<owner>/${local}`;
|