@h-rig/task-sources-plugin 0.0.6-alpha.157 → 0.0.6-alpha.158
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/src/control-plane/native/github-token-env.d.ts +3 -0
- package/dist/src/control-plane/native/github-token-env.js +26 -0
- package/dist/src/control-plane/native/native-git.d.ts +18 -0
- package/dist/src/control-plane/native/native-git.js +291 -0
- package/dist/src/control-plane/native/runtime-binary-build.d.ts +9 -0
- package/dist/src/control-plane/native/runtime-binary-build.js +107 -0
- package/dist/src/control-plane/native/task-ops.d.ts +52 -0
- package/dist/src/control-plane/native/task-ops.js +3192 -0
- package/dist/src/control-plane/native/task-state.d.ts +30 -0
- package/dist/src/control-plane/native/task-state.js +944 -0
- package/dist/src/control-plane/native/utils.d.ts +7 -0
- package/dist/src/control-plane/native/utils.js +110 -0
- package/dist/src/control-plane/native/validator-binaries.d.ts +3 -0
- package/dist/src/control-plane/native/validator-binaries.js +175 -0
- package/dist/src/control-plane/native/validator.d.ts +44 -0
- package/dist/src/control-plane/native/validator.js +979 -0
- package/dist/src/control-plane/state-sync/index.d.ts +4 -0
- package/dist/src/control-plane/state-sync/index.js +1205 -0
- package/dist/src/control-plane/state-sync/native-git.d.ts +1 -0
- package/dist/src/control-plane/state-sync/native-git.js +281 -0
- package/dist/src/control-plane/state-sync/read.d.ts +46 -0
- package/dist/src/control-plane/state-sync/read.js +564 -0
- package/dist/src/control-plane/state-sync/reconcile.d.ts +28 -0
- package/dist/src/control-plane/state-sync/reconcile.js +260 -0
- package/dist/src/control-plane/state-sync/repo.d.ts +1 -0
- package/dist/src/control-plane/state-sync/repo.js +42 -0
- package/dist/src/control-plane/state-sync/types.d.ts +28 -0
- package/dist/src/control-plane/state-sync/types.js +111 -0
- package/dist/src/control-plane/state-sync/write.d.ts +83 -0
- package/dist/src/control-plane/state-sync/write.js +1165 -0
- package/dist/src/control-plane/task-data-service.d.ts +17 -0
- package/dist/src/control-plane/task-data-service.js +3653 -0
- package/dist/src/control-plane/task-fields.d.ts +1 -0
- package/dist/src/control-plane/task-fields.js +6 -0
- package/dist/src/control-plane/task-io-service.d.ts +6 -0
- package/dist/src/control-plane/task-io-service.js +108 -0
- package/dist/src/control-plane/task-source-bootstrap.d.ts +1 -0
- package/dist/src/control-plane/task-source-bootstrap.js +6 -0
- package/dist/src/control-plane/task-source.d.ts +2 -0
- package/dist/src/control-plane/task-source.js +6 -0
- package/dist/src/control-plane/tasks/legacy-task-config-source.d.ts +19 -0
- package/dist/src/control-plane/tasks/legacy-task-config-source.js +124 -0
- package/dist/src/control-plane/tasks/plugin-task-source.d.ts +30 -0
- package/dist/src/control-plane/tasks/plugin-task-source.js +99 -0
- package/dist/src/control-plane/tasks/source-aware-task-config-source.d.ts +28 -0
- package/dist/src/control-plane/tasks/source-aware-task-config-source.js +642 -0
- package/dist/src/control-plane/tasks/source-lifecycle.d.ts +56 -0
- package/dist/src/control-plane/tasks/source-lifecycle.js +834 -0
- package/dist/src/plugin.d.ts +1 -1
- package/dist/src/plugin.js +3927 -64
- package/package.json +57 -4
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/task-sources-plugin/src/control-plane/state-sync/reconcile.ts
|
|
3
|
+
var STALE_CLAIM_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
function parseTimestamp(value) {
|
|
5
|
+
if (!value) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const parsed = Date.parse(value);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
10
|
+
}
|
|
11
|
+
function inProgressOwnershipMetadataIssue(metadata) {
|
|
12
|
+
if (!metadata?.claimId || !metadata.ownerId || !metadata.claimedAt || !metadata.lastEvidenceAt) {
|
|
13
|
+
return "missing-ownership-metadata";
|
|
14
|
+
}
|
|
15
|
+
if (parseTimestamp(metadata.claimedAt) == null || parseTimestamp(metadata.lastEvidenceAt) == null) {
|
|
16
|
+
return "invalid-ownership-timestamps";
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function hasExplicitPullRequestEvidence(evidence) {
|
|
21
|
+
return evidence != null && Object.prototype.hasOwnProperty.call(evidence, "pr");
|
|
22
|
+
}
|
|
23
|
+
function hasPrCloseoutCompletionEvidence(evidence) {
|
|
24
|
+
const pr = evidence?.pr;
|
|
25
|
+
return evidence?.closeoutPassed === true && (pr?.isOpen === true || pr?.isMerged === true);
|
|
26
|
+
}
|
|
27
|
+
function isTaskClaimStale(metadata, nowIso) {
|
|
28
|
+
if (!metadata) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const now = parseTimestamp(nowIso);
|
|
32
|
+
if (now == null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const reference = parseTimestamp(metadata.lastEvidenceAt) ?? parseTimestamp(metadata.claimedAt);
|
|
36
|
+
if (reference == null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return now - reference > STALE_CLAIM_MS;
|
|
40
|
+
}
|
|
41
|
+
function projectTaskReconciliation(input) {
|
|
42
|
+
const reasons = [];
|
|
43
|
+
const nowIso = input.evidence?.now ?? new Date().toISOString();
|
|
44
|
+
const isStale = isTaskClaimStale(input.metadata, nowIso);
|
|
45
|
+
const pr = input.evidence?.pr ?? null;
|
|
46
|
+
const hasExplicitPrEvidence = hasExplicitPullRequestEvidence(input.evidence);
|
|
47
|
+
const activeOwnerIds = (input.evidence?.activeOwnerIds ?? []).filter(Boolean);
|
|
48
|
+
if (new Set(activeOwnerIds).size > 1) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
reasons: ["multiple-active-owners"],
|
|
52
|
+
isStale,
|
|
53
|
+
wouldReconcileTo: null,
|
|
54
|
+
nextMetadata: input.metadata
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (input.lifecycleStatus === "blocked" || input.lifecycleStatus === "cancelled") {
|
|
58
|
+
return {
|
|
59
|
+
valid: true,
|
|
60
|
+
reasons,
|
|
61
|
+
isStale,
|
|
62
|
+
wouldReconcileTo: null,
|
|
63
|
+
nextMetadata: input.metadata
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (input.lifecycleStatus === "in_progress") {
|
|
67
|
+
const ownershipMetadataIssue = inProgressOwnershipMetadataIssue(input.metadata);
|
|
68
|
+
const hasOwnershipMetadata = ownershipMetadataIssue == null;
|
|
69
|
+
if (hasPrCloseoutCompletionEvidence(input.evidence)) {
|
|
70
|
+
return {
|
|
71
|
+
valid: hasOwnershipMetadata,
|
|
72
|
+
reasons: hasOwnershipMetadata ? reasons : [ownershipMetadataIssue],
|
|
73
|
+
isStale,
|
|
74
|
+
wouldReconcileTo: "completed",
|
|
75
|
+
nextMetadata: input.metadata ? {
|
|
76
|
+
...input.metadata,
|
|
77
|
+
status: "completed",
|
|
78
|
+
lastEvidenceAt: nowIso,
|
|
79
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
80
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
81
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
82
|
+
} : {
|
|
83
|
+
status: "completed",
|
|
84
|
+
lastEvidenceAt: nowIso,
|
|
85
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
86
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
87
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (ownershipMetadataIssue) {
|
|
92
|
+
return {
|
|
93
|
+
valid: false,
|
|
94
|
+
reasons: [ownershipMetadataIssue],
|
|
95
|
+
isStale,
|
|
96
|
+
wouldReconcileTo: pr?.isOpen ? "under_review" : "ready",
|
|
97
|
+
nextMetadata: pr?.isOpen ? {
|
|
98
|
+
...input.metadata ?? { status: "under_review" },
|
|
99
|
+
status: "under_review",
|
|
100
|
+
lastEvidenceAt: nowIso,
|
|
101
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
102
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
103
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
104
|
+
} : null
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (isStale) {
|
|
108
|
+
if (pr?.isOpen) {
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
reasons: ["stale-claim"],
|
|
112
|
+
isStale,
|
|
113
|
+
wouldReconcileTo: "under_review",
|
|
114
|
+
nextMetadata: {
|
|
115
|
+
...input.metadata,
|
|
116
|
+
status: "under_review",
|
|
117
|
+
lastEvidenceAt: nowIso,
|
|
118
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
119
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
120
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
valid: false,
|
|
126
|
+
reasons: ["stale-claim"],
|
|
127
|
+
isStale,
|
|
128
|
+
wouldReconcileTo: "ready",
|
|
129
|
+
nextMetadata: null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (hasPrCloseoutCompletionEvidence(input.evidence) && (input.lifecycleStatus === "open" || input.lifecycleStatus === "ready" || input.lifecycleStatus === "queued")) {
|
|
134
|
+
return {
|
|
135
|
+
valid: false,
|
|
136
|
+
reasons: ["pr-closeout-before-completion"],
|
|
137
|
+
isStale,
|
|
138
|
+
wouldReconcileTo: "completed",
|
|
139
|
+
nextMetadata: {
|
|
140
|
+
...input.metadata ?? { status: "completed" },
|
|
141
|
+
status: "completed",
|
|
142
|
+
lastEvidenceAt: nowIso,
|
|
143
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
144
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
145
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (input.lifecycleStatus === "under_review") {
|
|
150
|
+
const hasPrLink = Boolean(input.metadata?.prNumber || input.metadata?.prUrl || pr?.prNumber || pr?.prUrl);
|
|
151
|
+
if (!hasPrLink) {
|
|
152
|
+
return {
|
|
153
|
+
valid: false,
|
|
154
|
+
reasons: ["missing-pr-metadata"],
|
|
155
|
+
isStale,
|
|
156
|
+
wouldReconcileTo: "ready",
|
|
157
|
+
nextMetadata: null
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (hasExplicitPrEvidence && !pr) {
|
|
161
|
+
return {
|
|
162
|
+
valid: false,
|
|
163
|
+
reasons: ["missing-pr-evidence"],
|
|
164
|
+
isStale,
|
|
165
|
+
wouldReconcileTo: "ready",
|
|
166
|
+
nextMetadata: null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (pr?.reviewState === "changes_requested") {
|
|
170
|
+
const ownershipMetadataIssue = inProgressOwnershipMetadataIssue(input.metadata);
|
|
171
|
+
if (ownershipMetadataIssue) {
|
|
172
|
+
return {
|
|
173
|
+
valid: false,
|
|
174
|
+
reasons: [ownershipMetadataIssue],
|
|
175
|
+
isStale,
|
|
176
|
+
wouldReconcileTo: "ready",
|
|
177
|
+
nextMetadata: null
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
valid: true,
|
|
182
|
+
reasons,
|
|
183
|
+
isStale,
|
|
184
|
+
wouldReconcileTo: "in_progress",
|
|
185
|
+
nextMetadata: input.metadata ? {
|
|
186
|
+
...input.metadata,
|
|
187
|
+
status: "in_progress",
|
|
188
|
+
lastEvidenceAt: nowIso,
|
|
189
|
+
reviewState: "changes_requested"
|
|
190
|
+
} : null
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (hasPrCloseoutCompletionEvidence(input.evidence)) {
|
|
194
|
+
return {
|
|
195
|
+
valid: true,
|
|
196
|
+
reasons,
|
|
197
|
+
isStale,
|
|
198
|
+
wouldReconcileTo: "completed",
|
|
199
|
+
nextMetadata: input.metadata ? {
|
|
200
|
+
...input.metadata,
|
|
201
|
+
status: "completed",
|
|
202
|
+
lastEvidenceAt: nowIso,
|
|
203
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
204
|
+
} : null
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (pr && !pr.isOpen && !pr.isMerged) {
|
|
208
|
+
return {
|
|
209
|
+
valid: false,
|
|
210
|
+
reasons: ["closed-pr-without-merge"],
|
|
211
|
+
isStale,
|
|
212
|
+
wouldReconcileTo: "ready",
|
|
213
|
+
nextMetadata: null
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (input.lifecycleStatus === "completed" && pr?.isOpen && !hasPrCloseoutCompletionEvidence(input.evidence)) {
|
|
218
|
+
return {
|
|
219
|
+
valid: false,
|
|
220
|
+
reasons: ["completed-with-open-pr"],
|
|
221
|
+
isStale,
|
|
222
|
+
wouldReconcileTo: "under_review",
|
|
223
|
+
nextMetadata: {
|
|
224
|
+
...input.metadata ?? { status: "under_review" },
|
|
225
|
+
status: "under_review",
|
|
226
|
+
lastEvidenceAt: nowIso,
|
|
227
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
228
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
229
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (pr?.isOpen && (input.lifecycleStatus === "draft" || input.lifecycleStatus === "open" || input.lifecycleStatus === "ready" || input.lifecycleStatus === "queued" || input.lifecycleStatus === "in_progress")) {
|
|
234
|
+
return {
|
|
235
|
+
valid: true,
|
|
236
|
+
reasons,
|
|
237
|
+
isStale,
|
|
238
|
+
wouldReconcileTo: "under_review",
|
|
239
|
+
nextMetadata: {
|
|
240
|
+
...input.metadata ?? { status: "under_review" },
|
|
241
|
+
status: "under_review",
|
|
242
|
+
lastEvidenceAt: nowIso,
|
|
243
|
+
...pr?.prNumber != null ? { prNumber: pr?.prNumber } : {},
|
|
244
|
+
...pr?.prUrl ? { prUrl: pr?.prUrl } : {},
|
|
245
|
+
...pr?.reviewState ? { reviewState: pr?.reviewState } : {}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
valid: true,
|
|
251
|
+
reasons,
|
|
252
|
+
isStale,
|
|
253
|
+
wouldReconcileTo: null,
|
|
254
|
+
nextMetadata: input.metadata
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
export {
|
|
258
|
+
projectTaskReconciliation,
|
|
259
|
+
isTaskClaimStale
|
|
260
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveTrackerRepoPath(projectRoot: string): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/task-sources-plugin/src/control-plane/state-sync/repo.ts
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/task-sources-plugin/src/control-plane/native/utils.ts
|
|
7
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
8
|
+
import { unique } from "@rig/core/exec";
|
|
9
|
+
import {
|
|
10
|
+
runCapture,
|
|
11
|
+
runCaptureAsync,
|
|
12
|
+
runInherit,
|
|
13
|
+
getValidationTimeoutMs,
|
|
14
|
+
readJsonFile,
|
|
15
|
+
nowIso,
|
|
16
|
+
unique as unique2,
|
|
17
|
+
fileLines
|
|
18
|
+
} from "@rig/core/exec";
|
|
19
|
+
import { resolveCheckoutRoot } from "@rig/core/checkout-root";
|
|
20
|
+
import { resolveHarnessPaths } from "@rig/core/harness-paths";
|
|
21
|
+
var scopeRegexCache = new Map;
|
|
22
|
+
|
|
23
|
+
// packages/task-sources-plugin/src/control-plane/state-sync/repo.ts
|
|
24
|
+
import { MANAGED_REPO_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
25
|
+
import { defineCapability } from "@rig/core/capability";
|
|
26
|
+
import { getInstalledCapability } from "@rig/core/capability-loaders";
|
|
27
|
+
function resolveTrackerRepoPath(projectRoot) {
|
|
28
|
+
const monorepoRoot = resolveCheckoutRoot(projectRoot);
|
|
29
|
+
const managedRepos = getInstalledCapability(defineCapability(MANAGED_REPO_SERVICE_CAPABILITY));
|
|
30
|
+
if (managedRepos) {
|
|
31
|
+
try {
|
|
32
|
+
const layout = managedRepos.resolveMonorepoRepoLayout(projectRoot);
|
|
33
|
+
if (existsSync(resolve(layout.mirrorRoot, "HEAD"))) {
|
|
34
|
+
return layout.mirrorRoot;
|
|
35
|
+
}
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
return monorepoRoot;
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
resolveTrackerRepoPath
|
|
42
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CanonicalTaskLifecycleStatus = "draft" | "open" | "ready" | "queued" | "in_progress" | "under_review" | "blocked" | "completed" | "cancelled";
|
|
2
|
+
export type TaskStateMetadata = {
|
|
3
|
+
claimId?: string;
|
|
4
|
+
status: CanonicalTaskLifecycleStatus;
|
|
5
|
+
ownerId?: string;
|
|
6
|
+
claimedAt?: string;
|
|
7
|
+
lastEvidenceAt?: string;
|
|
8
|
+
runId?: string;
|
|
9
|
+
branchName?: string;
|
|
10
|
+
prNumber?: number;
|
|
11
|
+
prUrl?: string;
|
|
12
|
+
reviewState?: string;
|
|
13
|
+
blockerReason?: string;
|
|
14
|
+
sourceCommit?: string;
|
|
15
|
+
};
|
|
16
|
+
export type TaskStateMetadataEnvelope = {
|
|
17
|
+
schemaVersion: number;
|
|
18
|
+
supported: boolean;
|
|
19
|
+
baseTrackerCommit: string | null;
|
|
20
|
+
tasks: Record<string, TaskStateMetadata>;
|
|
21
|
+
};
|
|
22
|
+
export declare function normalizeTaskLifecycleStatus(status: unknown): CanonicalTaskLifecycleStatus | null;
|
|
23
|
+
export declare function discardMismatchedTaskStateMetadata(input: {
|
|
24
|
+
taskId: string;
|
|
25
|
+
lifecycleStatus: CanonicalTaskLifecycleStatus | null;
|
|
26
|
+
metadata: unknown;
|
|
27
|
+
}): TaskStateMetadata | null;
|
|
28
|
+
export declare function readTaskStateMetadataEnvelope(raw: unknown): TaskStateMetadataEnvelope;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/task-sources-plugin/src/control-plane/state-sync/types.ts
|
|
3
|
+
var SUPPORTED_TASK_STATE_SCHEMA_VERSION = 1;
|
|
4
|
+
var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
|
|
5
|
+
"draft",
|
|
6
|
+
"open",
|
|
7
|
+
"ready",
|
|
8
|
+
"queued",
|
|
9
|
+
"in_progress",
|
|
10
|
+
"under_review",
|
|
11
|
+
"blocked",
|
|
12
|
+
"completed",
|
|
13
|
+
"cancelled"
|
|
14
|
+
]);
|
|
15
|
+
function normalizeTaskLifecycleStatus(status) {
|
|
16
|
+
switch (status) {
|
|
17
|
+
case "draft":
|
|
18
|
+
case "open":
|
|
19
|
+
case "ready":
|
|
20
|
+
case "queued":
|
|
21
|
+
case "in_progress":
|
|
22
|
+
case "under_review":
|
|
23
|
+
case "blocked":
|
|
24
|
+
case "completed":
|
|
25
|
+
case "cancelled":
|
|
26
|
+
return status;
|
|
27
|
+
case "closed":
|
|
28
|
+
return "completed";
|
|
29
|
+
case "running":
|
|
30
|
+
return "in_progress";
|
|
31
|
+
case "failed":
|
|
32
|
+
return "ready";
|
|
33
|
+
default:
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function normalizeTaskStateMetadataStatus(status) {
|
|
38
|
+
if (CANONICAL_TASK_LIFECYCLE_STATUSES.has(status)) {
|
|
39
|
+
return status;
|
|
40
|
+
}
|
|
41
|
+
switch (status) {
|
|
42
|
+
case "closed":
|
|
43
|
+
return "completed";
|
|
44
|
+
case "running":
|
|
45
|
+
return "in_progress";
|
|
46
|
+
case "failed":
|
|
47
|
+
return "ready";
|
|
48
|
+
default:
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function canonicalizeTaskStateMetadata(raw) {
|
|
53
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const metadata = raw;
|
|
57
|
+
const claimId = typeof metadata.claimId === "string" && metadata.claimId.length > 0 ? metadata.claimId : undefined;
|
|
58
|
+
const status = normalizeTaskStateMetadataStatus(metadata.status);
|
|
59
|
+
if (!status) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...claimId ? { claimId } : {},
|
|
64
|
+
status,
|
|
65
|
+
...typeof metadata.ownerId === "string" && metadata.ownerId.length > 0 ? { ownerId: metadata.ownerId } : {},
|
|
66
|
+
...typeof metadata.claimedAt === "string" && metadata.claimedAt.length > 0 ? { claimedAt: metadata.claimedAt } : {},
|
|
67
|
+
...typeof metadata.lastEvidenceAt === "string" && metadata.lastEvidenceAt.length > 0 ? { lastEvidenceAt: metadata.lastEvidenceAt } : {},
|
|
68
|
+
...typeof metadata.runId === "string" && metadata.runId.length > 0 ? { runId: metadata.runId } : {},
|
|
69
|
+
...typeof metadata.branchName === "string" && metadata.branchName.length > 0 ? { branchName: metadata.branchName } : {},
|
|
70
|
+
...typeof metadata.prNumber === "number" ? { prNumber: metadata.prNumber } : {},
|
|
71
|
+
...typeof metadata.prUrl === "string" && metadata.prUrl.length > 0 ? { prUrl: metadata.prUrl } : {},
|
|
72
|
+
...typeof metadata.reviewState === "string" && metadata.reviewState.length > 0 ? { reviewState: metadata.reviewState } : {},
|
|
73
|
+
...typeof metadata.blockerReason === "string" && metadata.blockerReason.length > 0 ? { blockerReason: metadata.blockerReason } : {},
|
|
74
|
+
...typeof metadata.sourceCommit === "string" && metadata.sourceCommit.length > 0 ? { sourceCommit: metadata.sourceCommit } : {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function discardMismatchedTaskStateMetadata(input) {
|
|
78
|
+
input.taskId;
|
|
79
|
+
const canonicalMetadata = canonicalizeTaskStateMetadata(input.metadata);
|
|
80
|
+
if (!canonicalMetadata || !input.lifecycleStatus) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const metadataStatus = canonicalMetadata.status ?? null;
|
|
84
|
+
if (metadataStatus && metadataStatus !== input.lifecycleStatus) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return canonicalMetadata;
|
|
88
|
+
}
|
|
89
|
+
function readTaskStateMetadataEnvelope(raw) {
|
|
90
|
+
if (!raw || typeof raw !== "object") {
|
|
91
|
+
return { schemaVersion: SUPPORTED_TASK_STATE_SCHEMA_VERSION, supported: true, baseTrackerCommit: null, tasks: {} };
|
|
92
|
+
}
|
|
93
|
+
const envelope = raw;
|
|
94
|
+
const schemaVersion = typeof envelope.schemaVersion === "number" ? envelope.schemaVersion : SUPPORTED_TASK_STATE_SCHEMA_VERSION;
|
|
95
|
+
if (schemaVersion !== SUPPORTED_TASK_STATE_SCHEMA_VERSION) {
|
|
96
|
+
return { schemaVersion, supported: false, baseTrackerCommit: null, tasks: {} };
|
|
97
|
+
}
|
|
98
|
+
const rawTasks = envelope.tasks && typeof envelope.tasks === "object" && !Array.isArray(envelope.tasks) ? envelope.tasks : {};
|
|
99
|
+
const tasks = Object.fromEntries(Object.entries(rawTasks).map(([taskId, metadata]) => [taskId, canonicalizeTaskStateMetadata(metadata)]).filter((entry) => entry[1] != null));
|
|
100
|
+
return {
|
|
101
|
+
schemaVersion,
|
|
102
|
+
supported: true,
|
|
103
|
+
baseTrackerCommit: typeof envelope.baseTrackerCommit === "string" && envelope.baseTrackerCommit.length > 0 ? envelope.baseTrackerCommit : null,
|
|
104
|
+
tasks
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
readTaskStateMetadataEnvelope,
|
|
109
|
+
normalizeTaskLifecycleStatus,
|
|
110
|
+
discardMismatchedTaskStateMetadata
|
|
111
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { nativeFetchRef, nativePushRefWithLease, nativeReadBlobAtRef, nativeWriteTreeCommit } from "./native-git";
|
|
2
|
+
import { readSyncedTrackerState, type SyncedTrackerSnapshot } from "./read";
|
|
3
|
+
import { type TrackerReconcileEvidence } from "./reconcile";
|
|
4
|
+
import { type CanonicalTaskLifecycleStatus, type TaskStateMetadata, type TaskStateMetadataEnvelope } from "./types";
|
|
5
|
+
type WritableTrackerIssue = Record<string, unknown>;
|
|
6
|
+
export type WritableTrackerState = {
|
|
7
|
+
repoPath: string;
|
|
8
|
+
baseOid: string;
|
|
9
|
+
issuesBaseOid: string;
|
|
10
|
+
taskStateBaseOid: string;
|
|
11
|
+
issueRows: WritableTrackerIssue[];
|
|
12
|
+
taskStateEnvelope: TaskStateMetadataEnvelope;
|
|
13
|
+
};
|
|
14
|
+
type TrackerWriterDeps = {
|
|
15
|
+
fetchRef: typeof nativeFetchRef;
|
|
16
|
+
readBlobAtRef: typeof nativeReadBlobAtRef;
|
|
17
|
+
writeTreeCommit: typeof nativeWriteTreeCommit;
|
|
18
|
+
pushRefWithLease: typeof nativePushRefWithLease;
|
|
19
|
+
readSnapshot: typeof readSyncedTrackerState;
|
|
20
|
+
createClaimId: () => string;
|
|
21
|
+
now: () => string;
|
|
22
|
+
};
|
|
23
|
+
export declare class TrackerStateMutationError extends Error {
|
|
24
|
+
readonly code: "same-base-required" | "task-not-found" | "invalid-task-status" | "status-conflict" | "claim-conflict" | "push-conflict" | "invalid-reconciliation";
|
|
25
|
+
constructor(message: string, code: "same-base-required" | "task-not-found" | "invalid-task-status" | "status-conflict" | "claim-conflict" | "push-conflict" | "invalid-reconciliation");
|
|
26
|
+
}
|
|
27
|
+
export declare function assertWritableTrackerStateBases(state: {
|
|
28
|
+
issuesBaseOid: string;
|
|
29
|
+
taskStateBaseOid: string;
|
|
30
|
+
}): void;
|
|
31
|
+
export declare function prepareClaimTaskMutation(state: WritableTrackerState, input: {
|
|
32
|
+
taskId: string;
|
|
33
|
+
ownerId: string;
|
|
34
|
+
runId?: string;
|
|
35
|
+
branchName?: string;
|
|
36
|
+
expectedStatus?: CanonicalTaskLifecycleStatus;
|
|
37
|
+
now: string;
|
|
38
|
+
claimId: string;
|
|
39
|
+
}): WritableTrackerState;
|
|
40
|
+
export declare function prepareLifecycleTaskMutation(state: WritableTrackerState, input: {
|
|
41
|
+
taskId: string;
|
|
42
|
+
status: CanonicalTaskLifecycleStatus;
|
|
43
|
+
allowedFrom?: CanonicalTaskLifecycleStatus[];
|
|
44
|
+
metadata?: TaskStateMetadata | null;
|
|
45
|
+
clearMetadata?: boolean;
|
|
46
|
+
}): WritableTrackerState;
|
|
47
|
+
export declare function prepareReconcileTaskMutation(state: WritableTrackerState, input: {
|
|
48
|
+
taskId: string;
|
|
49
|
+
evidence?: TrackerReconcileEvidence;
|
|
50
|
+
}): WritableTrackerState | null;
|
|
51
|
+
export declare function claimRemoteTrackerTask(projectRoot: string, input: {
|
|
52
|
+
taskId: string;
|
|
53
|
+
ownerId: string;
|
|
54
|
+
runId?: string;
|
|
55
|
+
branchName?: string;
|
|
56
|
+
expectedStatus?: CanonicalTaskLifecycleStatus;
|
|
57
|
+
}, deps?: Partial<TrackerWriterDeps>): {
|
|
58
|
+
outcome: "applied" | "noop";
|
|
59
|
+
snapshot: SyncedTrackerSnapshot;
|
|
60
|
+
claimId: string;
|
|
61
|
+
commitOid: string | null;
|
|
62
|
+
};
|
|
63
|
+
export declare function reconcileRemoteTrackerTask(projectRoot: string, input: {
|
|
64
|
+
taskId: string;
|
|
65
|
+
evidence?: TrackerReconcileEvidence;
|
|
66
|
+
}, deps?: Partial<TrackerWriterDeps>): {
|
|
67
|
+
outcome: "applied" | "noop";
|
|
68
|
+
snapshot: SyncedTrackerSnapshot;
|
|
69
|
+
commitOid: string | null;
|
|
70
|
+
};
|
|
71
|
+
export declare function updateRemoteTrackerTaskLifecycle(projectRoot: string, input: {
|
|
72
|
+
taskId: string;
|
|
73
|
+
status: CanonicalTaskLifecycleStatus;
|
|
74
|
+
allowedFrom?: CanonicalTaskLifecycleStatus[];
|
|
75
|
+
metadata?: TaskStateMetadata | null;
|
|
76
|
+
clearMetadata?: boolean;
|
|
77
|
+
reason?: string;
|
|
78
|
+
}, deps?: Partial<TrackerWriterDeps>): {
|
|
79
|
+
outcome: "applied" | "noop";
|
|
80
|
+
snapshot: SyncedTrackerSnapshot;
|
|
81
|
+
commitOid: string | null;
|
|
82
|
+
};
|
|
83
|
+
export {};
|