@zq-silk/yui 0.14.0 → 0.14.2

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 (179) hide show
  1. package/ARCHITECTURE.md +64 -35
  2. package/README.md +258 -125
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  5. package/dist/cli/commandCatalog.js +74 -109
  6. package/dist/cli/helpRenderer.js +1 -2
  7. package/dist/cli/interactionCandidates.js +10 -0
  8. package/dist/cli/interactionPolicy.js +76 -37
  9. package/dist/cli/roleWizard.js +5 -6
  10. package/dist/cli/updateOrchestrator.js +10 -8
  11. package/dist/cli/updatePorts.js +17 -13
  12. package/dist/cli/upgradeCommand.js +2 -5
  13. package/dist/cli.js +268 -231
  14. package/dist/commands/agentCommands.js +13 -0
  15. package/dist/commands/configCommands.js +7 -19
  16. package/dist/commands/configOverview.js +3 -2
  17. package/dist/commands/durableJobCommands.js +1 -1
  18. package/dist/commands/executionAuditCommands.js +4 -9
  19. package/dist/commands/globalRoleCommands.js +17 -0
  20. package/dist/commands/profileCommands.js +200 -30
  21. package/dist/commands/sessionCommands.js +1 -1
  22. package/dist/commands/taskActor.js +29 -127
  23. package/dist/commands/taskChangeSetCommands.js +1 -3
  24. package/dist/commands/taskCommands.js +1771 -2107
  25. package/dist/commands/taskCompletionGate.js +27 -103
  26. package/dist/commands/taskContextCommand.js +47 -80
  27. package/dist/commands/taskExecutionCommands.js +9 -27
  28. package/dist/commands/taskInputCommands.js +10 -10
  29. package/dist/commands/taskIntegrationCommands.js +69 -61
  30. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  31. package/dist/commands/taskNextActionCommand.js +8 -29
  32. package/dist/commands/taskOverviewCommand.js +25 -3
  33. package/dist/commands/taskPublicationCommands.js +151 -85
  34. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  35. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  36. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  37. package/dist/commands/taskUpstreamCommands.js +79 -95
  38. package/dist/config/configCatalog.js +1 -1
  39. package/dist/context/contextSnapshot.js +17 -2
  40. package/dist/context/sessionBootstrapManifest.js +18 -1
  41. package/dist/context/sourceTurnContext.js +30 -0
  42. package/dist/context/turnContextPack.js +107 -48
  43. package/dist/context/turnInputContract.js +11 -4
  44. package/dist/context/wakeNotification.js +84 -22
  45. package/dist/controller/agentRuntimeObserver.js +9 -12
  46. package/dist/controller/clientRuntime.js +13 -13
  47. package/dist/controller/controller.js +11 -11
  48. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  49. package/dist/controller/jobControl.js +11 -10
  50. package/dist/controller/resourceInventory.js +6 -6
  51. package/dist/controller/resourceInventoryLinux.js +2 -2
  52. package/dist/controller/runtime.js +18 -61
  53. package/dist/controller/runtimeEventInbox.js +57 -34
  54. package/dist/controller/runtimeEventProcessor.js +36 -96
  55. package/dist/controller/runtimeHookTurnFence.js +33 -57
  56. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  57. package/dist/controller/runtimeObservationHook.js +18 -12
  58. package/dist/controller/sessionNotify.js +43 -28
  59. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  60. package/dist/controller/structuredProviderObservation.js +13 -16
  61. package/dist/coordination/workMailboxQueue.js +132 -0
  62. package/dist/core/controllerServer.js +1 -0
  63. package/dist/domain/agentResultTransport.js +60 -0
  64. package/dist/execution/executionHealth.js +111 -118
  65. package/dist/execution/reviewMainTurn.js +161 -0
  66. package/dist/execution/workItemExecution.js +454 -0
  67. package/dist/execution/workItemExecutionProjection.js +309 -0
  68. package/dist/execution/workItemMainTurn.js +164 -0
  69. package/dist/executor/agentExecutor.js +19 -19
  70. package/dist/executor/executorRegistry.js +7 -28
  71. package/dist/executor/fileRoleLaunchPlanner.js +51 -83
  72. package/dist/external/githubPublicationVerifier.js +114 -0
  73. package/dist/external/gitlabPublicationVerifier.js +158 -0
  74. package/dist/external/pinnedCommandRunner.js +73 -0
  75. package/dist/integration/changeSetManifest.js +0 -3
  76. package/dist/integration/deliveryObligation.js +43 -6
  77. package/dist/integration/gitIntegrationService.js +284 -101
  78. package/dist/integration/integrationAttempt.js +64 -14
  79. package/dist/integration/integrationQueueEntry.js +5 -35
  80. package/dist/integration/integrationQueueService.js +65 -194
  81. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  82. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  83. package/dist/message/message.js +16 -0
  84. package/dist/observability/executionAudit.js +4 -25
  85. package/dist/observability/faultClassification.js +58 -70
  86. package/dist/observability/orchestrationMetrics.js +8 -50
  87. package/dist/profile/agentProfile.js +60 -15
  88. package/dist/profile/agentProfileRuntime.js +100 -0
  89. package/dist/release/releaseWorkflowPorts.js +5 -68
  90. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  91. package/dist/repository/gitWorkspace.js +9 -67
  92. package/dist/repository/taskBaseFreshness.js +3 -1
  93. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  94. package/dist/repository/taskWorkspacePreparer.js +252 -411
  95. package/dist/resources/liveReferences.js +6 -55
  96. package/dist/resources/resourceDiscovery.js +1 -30
  97. package/dist/resources/resourceRegistrar.js +0 -23
  98. package/dist/review/deltaRecheck.js +8 -65
  99. package/dist/review/reviewAcceptance.js +22 -19
  100. package/dist/review/reviewConfig.js +0 -10
  101. package/dist/review/reviewDecision.js +2 -2
  102. package/dist/review/reviewRound.js +147 -273
  103. package/dist/review/reviewerAvailability.js +13 -23
  104. package/dist/role/role.js +3 -0
  105. package/dist/runtime/agentDriverObservation.js +1 -1
  106. package/dist/runtime/agentHost.js +51 -51
  107. package/dist/runtime/builtinAgentDrivers.js +9 -2
  108. package/dist/runtime/codexAppServerRuntime.js +23 -5
  109. package/dist/runtime/exactControlPlane.js +2 -267
  110. package/dist/runtime/firstProgressAdvisory.js +1 -1
  111. package/dist/runtime/index.js +1 -1
  112. package/dist/runtime/launchBroker.js +11 -11
  113. package/dist/runtime/lifecycleReservation.js +2 -2
  114. package/dist/runtime/managedCaller.js +111 -0
  115. package/dist/runtime/ports.js +16 -3
  116. package/dist/runtime/preallocatedNativeSession.js +2 -2
  117. package/dist/runtime/processExitObservation.js +3 -2
  118. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  119. package/dist/runtime/runtimeBinding.js +1 -1
  120. package/dist/runtime/runtimeObservation.js +33 -16
  121. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  122. package/dist/runtime/runtimeStopReceipt.js +11 -11
  123. package/dist/runtime/sessionLaunchRequest.js +2 -2
  124. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  125. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  126. package/dist/runtime/sessionReconciliation.js +4 -4
  127. package/dist/runtime/sessionTerminationGuard.js +9 -9
  128. package/dist/runtime/sessionTokenMetrics.js +5 -7
  129. package/dist/runtime/structuredProviderHost.js +5 -5
  130. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  131. package/dist/runtime/tmuxAdapters.js +32 -30
  132. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  133. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  134. package/dist/scheduler/roleTurnLiveness.js +6 -6
  135. package/dist/scheduler/roleTurnStall.js +8 -9
  136. package/dist/scheduler/taskExecutionProjection.js +19 -41
  137. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  138. package/dist/storage/sqliteSchema.js +1 -21
  139. package/dist/storage/sqliteStore.js +62 -47
  140. package/dist/storage/storageSchema.js +1 -8
  141. package/dist/storage/storageVersions.js +7 -4
  142. package/dist/storage/taskStore.js +8 -8
  143. package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
  144. package/dist/task/completionReadiness.js +17 -98
  145. package/dist/task/deliveryGuard.js +9 -11
  146. package/dist/task/draftPlan.js +142 -0
  147. package/dist/task/nextAction.js +179 -477
  148. package/dist/task/publicationVerification.js +1 -0
  149. package/dist/task/remoteDelivery.js +233 -0
  150. package/dist/task/task.js +108 -4
  151. package/dist/task/taskRecordReference.js +0 -1
  152. package/dist/turn/turn.js +109 -16
  153. package/dist/verification/verificationGateService.js +0 -26
  154. package/dist/web/assets/client/components.js +99 -121
  155. package/dist/web/assets/client/i18n.js +136 -58
  156. package/dist/web/assets/client/view.js +44 -0
  157. package/dist/web/assets/styles/cards.js +3 -15
  158. package/dist/web/assets/styles/widgets.js +4 -0
  159. package/dist/web/webSnapshot.js +29 -14
  160. package/dist/workItem/dependencyGate.js +34 -0
  161. package/dist/workItem/workItem.js +99 -266
  162. package/dist/workspace/workItemChangeSetManager.js +15 -59
  163. package/docs/task-local-identity.md +1 -1
  164. package/i18n/README.zh-CN.md +165 -59
  165. package/package.json +1 -1
  166. package/skills/yui-leader/SKILL.md +272 -782
  167. package/skills/yui-operator/SKILL.md +190 -365
  168. package/skills/yui-reviewer/SKILL.md +40 -33
  169. package/skills/yui-runtime/SKILL.md +27 -6
  170. package/skills/yui-worker/SKILL.md +19 -6
  171. package/dist/execution/candidateConvergence.js +0 -623
  172. package/dist/execution/executionGroup.js +0 -854
  173. package/dist/execution/resourceBroker.js +0 -426
  174. package/dist/executor/launchPlan.js +0 -16
  175. package/dist/review/reviewFinding.js +0 -240
  176. package/dist/review/reviewFindingLedger.js +0 -550
  177. package/dist/review/reviewOutcomeClassifier.js +0 -255
  178. package/dist/scheduler/resourceQueueProjection.js +0 -71
  179. package/dist/task/repairWave.js +0 -150
@@ -10,7 +10,6 @@ import { createHash } from "node:crypto";
10
10
  import { existsSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
11
11
  import { join, resolve } from "node:path";
12
12
  import { promisify } from "node:util";
13
- import { parseExactTaskRuntimeDescriptor } from "../runtime/exactControlPlane.js";
14
13
  import { readRuntimeIdentity, releasesDirectory, resolveActiveRelease } from "../release/runtimeRelease.js";
15
14
  import { isControllerSocketPathForHome } from "../core/controllerEndpoint.js";
16
15
  import { parseControllerDiscovery } from "../core/protocol.js";
@@ -105,20 +104,18 @@ export async function scanLiveReferences(input) {
105
104
  }
106
105
  if (discovery.diagnostic !== undefined)
107
106
  diagnostics.push(discovery.diagnostic);
108
- // 4. Runtime identity and exact Task descriptors are durable live-ownership
109
- // claims. Resolved runtime paths and descriptor workspaces stay protected
110
- // while their owning process identity is alive.
107
+ // 4. Runtime identity is a durable live-ownership claim. Resolved runtime
108
+ // paths stay protected while their owning process identity is alive.
109
+ // Managed workspaces are claimed from their durable records below, which
110
+ // is the same authority the Controller schedules from.
111
111
  const runtimeClaims = readRuntimeIdentityClaims(home);
112
112
  diagnostics.push(...runtimeClaims.diagnostics);
113
- const descriptorClaims = readExactTaskRuntimeClaims(home);
114
- diagnostics.push(...descriptorClaims.diagnostics);
115
113
  const activeReleaseClaims = readActiveReleaseClaims(home);
116
114
  diagnostics.push(...activeReleaseClaims.diagnostics);
117
115
  const sessionOwnerClaims = readSessionOwnerClaims(home);
118
116
  diagnostics.push(...sessionOwnerClaims.diagnostics);
119
117
  for (const claim of [
120
118
  ...runtimeClaims.claims,
121
- ...descriptorClaims.claims,
122
119
  ...activeReleaseClaims.claims,
123
120
  ...sessionOwnerClaims.claims
124
121
  ]) {
@@ -209,52 +206,6 @@ function readRuntimeIdentityClaims(home) {
209
206
  });
210
207
  }
211
208
  }
212
- function readExactTaskRuntimeClaims(home) {
213
- const directory = join(home, "runtime", "exact-task-runtime");
214
- let entries;
215
- try {
216
- entries = readdirSync(directory, { withFileTypes: true });
217
- }
218
- catch (error) {
219
- if (error.code === "ENOENT") {
220
- return Object.freeze({ claims: Object.freeze([]), diagnostics: Object.freeze([]) });
221
- }
222
- return Object.freeze({
223
- claims: Object.freeze([]),
224
- diagnostics: Object.freeze([{
225
- source: "controller",
226
- severity: "error",
227
- message: `exact Task runtime descriptors are unreadable: ${error instanceof Error ? error.message : "unknown error"}`
228
- }])
229
- });
230
- }
231
- const claims = [];
232
- const diagnostics = [];
233
- for (const entry of entries) {
234
- if (!entry.isFile() || !entry.name.endsWith(".json"))
235
- continue;
236
- const path = join(directory, entry.name);
237
- try {
238
- const descriptor = parseExactTaskRuntimeDescriptor(readFileSync(path, "utf8"));
239
- claims.push({ path, token: `exact-task-runtime:${descriptor.taskId}` });
240
- claims.push({
241
- path: resolve(descriptor.workspace),
242
- token: `exact-task-runtime:workspace:${descriptor.taskId}`
243
- });
244
- }
245
- catch (error) {
246
- diagnostics.push({
247
- source: "controller",
248
- severity: "error",
249
- message: `exact Task runtime descriptor is unreadable at ${path}: ${error instanceof Error ? error.message : "unknown error"}`
250
- });
251
- }
252
- }
253
- return Object.freeze({
254
- claims: Object.freeze(claims),
255
- diagnostics: Object.freeze(diagnostics)
256
- });
257
- }
258
209
  function readActiveReleaseClaims(home) {
259
210
  try {
260
211
  const active = resolveActiveRelease(home);
@@ -281,7 +232,7 @@ function readActiveReleaseClaims(home) {
281
232
  }
282
233
  }
283
234
  /**
284
- * Read Session owner records (`runtime/session-owners/<launchId>.json`). A
235
+ * Read Session owner records (`runtime/session-owners/<runtimeGenerationId>.json`). A
285
236
  * record whose Provider root is physically alive with a matching start
286
237
  * identity protects its runtime root. A dead PID is stale (reconciliation
287
238
  * removes those records); an identity conflict or unreadable record fails
@@ -350,7 +301,7 @@ function readSessionOwnerClaims(home) {
350
301
  if (typeof record.runtimeRoot === "string" && record.runtimeRoot.length > 0) {
351
302
  claims.push({
352
303
  path: resolve(record.runtimeRoot),
353
- token: `session-owner:${typeof record.launchId === "string" ? record.launchId : entry.name}`
304
+ token: `session-owner:${typeof record.runtimeGenerationId === "string" ? record.runtimeGenerationId : entry.name}`
354
305
  });
355
306
  }
356
307
  }
@@ -164,36 +164,7 @@ export async function discoverResources(input) {
164
164
  function discoverRuntimeArtifacts(home, input) {
165
165
  const discovered = [];
166
166
  const now = input.now;
167
- // 3a. Exact Task runtime descriptors: runtime/exact-task-runtime/<digest>.json
168
- const exactDirectory = join(home, "runtime", "exact-task-runtime");
169
- for (const entry of safeReaddir(exactDirectory)) {
170
- if (!entry.name.endsWith(".json"))
171
- continue;
172
- const path = join(exactDirectory, entry.name);
173
- const descriptor = readJsonFile(path);
174
- const taskId = typeof descriptor?.taskId === "string" ? descriptor.taskId : undefined;
175
- const owner = {
176
- home,
177
- ...(taskId === undefined ? {} : { taskId }),
178
- basis: taskId === undefined ? "unattributed" : "descriptor"
179
- };
180
- const taskStatus = taskId === undefined ? undefined : input.taskStatusById.get(taskId);
181
- discovered.push({
182
- record: createResourceRecord({
183
- kind: "runtime-artifact",
184
- path: resolve(path),
185
- owner,
186
- ...(sizeOf(path) === undefined ? {} : { sizeBytes: sizeOf(path) }),
187
- cleanliness: "n/a",
188
- activeRefs: [],
189
- disposition: "active"
190
- }, now),
191
- ownerTerminal: taskId === undefined
192
- ? false
193
- : isTerminalTaskStatus(taskStatus)
194
- });
195
- }
196
- // 3b. Task runtime isolation roots: <home>.task-runtimes/<taskId>/<owner>/<launch>/
167
+ // 3a. Task runtime isolation roots: <home>.task-runtimes/<taskId>/<owner>/<launch>/
197
168
  const runtimeRoot = `${resolve(home)}.task-runtimes`;
198
169
  if (existsSync(runtimeRoot)) {
199
170
  for (const taskEntry of safeReaddir(runtimeRoot)) {
@@ -80,29 +80,6 @@ export class ResourceRegistrar {
80
80
  cleanliness: "n/a"
81
81
  })));
82
82
  }
83
- registerExactTaskRuntimeDescriptor(descriptor, sourcePath) {
84
- try {
85
- this.#registerExactTaskRuntimeDescriptor(descriptor, sourcePath);
86
- }
87
- catch {
88
- // Best-effort receipt; descriptor writes must proceed regardless.
89
- }
90
- }
91
- #registerExactTaskRuntimeDescriptor(descriptor, sourcePath) {
92
- const path = resolve(sourcePath);
93
- if (!existsSync(path) || isReleaseNamespacePath(this.#home, path))
94
- return;
95
- this.#save([this.#record({
96
- kind: "runtime-artifact",
97
- path,
98
- owner: {
99
- home: this.#home,
100
- taskId: descriptor.taskId,
101
- basis: "descriptor"
102
- },
103
- cleanliness: "n/a"
104
- })]);
105
- }
106
83
  registerSessionContext(path, owner) {
107
84
  try {
108
85
  this.#registerSessionContext(path, owner);
@@ -1,13 +1,13 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { validateDeltaRecheckRecord } from "./reviewRound.js";
3
- import { isSemanticReviewRound } from "./reviewOutcomeClassifier.js";
4
3
  /**
5
4
  * Assesses whether a trustworthy delta can be constructed. Only technical
6
5
  * unavailability fails closed; Agents retain the semantic judgment.
7
6
  */
8
7
  export async function assessDeltaRecheck(input) {
9
8
  const { repositoryPaths, previousRound, candidate, git } = input;
10
- if (!isSemanticReviewRound(previousRound)
9
+ if (previousRound.status !== "completed"
10
+ || previousRound.reviewerTurnId === undefined
11
11
  || (previousRound.scope ?? "work-item") !== "task") {
12
12
  return {
13
13
  kind: "ineligible",
@@ -114,7 +114,7 @@ export function verifyDeltaRecheckDiff(record, diffByProject) {
114
114
  }
115
115
  /** Builds the delta-recheck dispatch context block. */
116
116
  export function buildDeltaRecheckDispatchContext(input) {
117
- const { round, previousRound, diffByProject, ledgerContext } = input;
117
+ const { round, previousRound, diffByProject } = input;
118
118
  const record = round.deltaRecheck;
119
119
  if (record === undefined) {
120
120
  throw new Error(`ReviewRound is not a delta-recheck: ${round.id}.`);
@@ -129,7 +129,8 @@ export function buildDeltaRecheckDispatchContext(input) {
129
129
  " semantic change, evidence doubt). This is the safe default.",
130
130
  "Yui verified only the technical delta boundary; a Task-control Agent selected this mode.",
131
131
  `Previous accepted ReviewRound: ${previousRound.id}@${record.previousBaseCommit}`,
132
- `Previous acceptance summary: ${compact(previousRound.summary ?? previousRound.report ?? "")}`,
132
+ `Previous Reviewer Turn: ${previousRound.reviewerTurnId ?? "unavailable"}`,
133
+ "Read that exact source Turn's original result from the frozen Context Snapshot.",
133
134
  ...(round.taskCandidate?.projects.map((project) => {
134
135
  const previous = previousRound.taskCandidate?.projects
135
136
  .find((entry) => entry.projectId === project.projectId)?.commit;
@@ -143,73 +144,15 @@ export function buildDeltaRecheckDispatchContext(input) {
143
144
  ...Object.entries(diffByProject).flatMap(([projectId, diff]) => (diff.trim().length === 0
144
145
  ? []
145
146
  : [`--- ${projectId} ---`, diff])),
146
- ...(previousEvidenceReferences(previousRound).length === 0
147
- ? []
148
- : [
149
- "Previous evidence references:",
150
- ...previousEvidenceReferences(previousRound).map((entry) => `- ${entry}`)
151
- ]),
152
- ledgerContext,
153
- "Report JSON with deltaDisposition (one of the three values above) and deltaReasoning",
154
- "(the explicit proof or the reason a full Review is required). Findings, checks, and",
155
- "evidence use the same fields as a full Review."
147
+ "Return one complete result with the chosen disposition, reasoning, findings,",
148
+ "verification, uncertainty, and recommended next action. Markdown or JSON is acceptable;",
149
+ "Yui Core preserves the original text and does not parse or validate its structure."
156
150
  ];
157
151
  return lines.join("\n");
158
152
  }
159
- function compact(text, limit = 600) {
160
- const flattened = text.replace(/\s+/gu, " ").trim();
161
- return flattened.length <= limit ? flattened : `${flattened.slice(0, limit)}...`;
162
- }
163
153
  function digestDiff(diffByProject) {
164
154
  const canonical = JSON.stringify(Object.entries(diffByProject)
165
155
  .sort(([left], [right]) => left.localeCompare(right))
166
156
  .map(([projectId, diff]) => [projectId, diff]));
167
157
  return createHash("sha256").update(canonical).digest("hex");
168
158
  }
169
- /** All path-like evidence references from Markdown or JSON Review reports. */
170
- function previousEvidenceReferences(round) {
171
- const report = round.report ?? "";
172
- return [...new Set([
173
- ...extractDeclaredEvidence(report),
174
- ...extractEvidenceReferences(report)
175
- ])];
176
- }
177
- /** Preserve the free-form evidence array that older JSON reports exposed. */
178
- function extractDeclaredEvidence(report) {
179
- let parsed;
180
- try {
181
- parsed = JSON.parse(report);
182
- }
183
- catch {
184
- return [];
185
- }
186
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
187
- return [];
188
- const evidence = parsed.evidence;
189
- if (!Array.isArray(evidence))
190
- return [];
191
- return evidence.flatMap((entry) => (typeof entry === "string" && entry.trim().length > 0 ? [entry.trim()] : []));
192
- }
193
- /**
194
- * Reviewer reports intentionally accept clear Markdown or JSON without a
195
- * fixed schema. Extract likely repo-relative references from the preserved
196
- * report so the next Reviewer can inspect the earlier evidence directly.
197
- */
198
- function extractEvidenceReferences(report) {
199
- const references = report.match(/(?:\.{0,2}\/)?[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+|[A-Za-z0-9_.-]+\.[A-Za-z0-9]{1,12}/gu) ?? [];
200
- return [...new Set(references.map((entry) => entry.replace(/^\.\//u, "")))]
201
- .filter((entry) => isPathLike(entry));
202
- }
203
- function isPathLike(value) {
204
- if (value.length === 0 || value.length > 512)
205
- return false;
206
- if (/\s/u.test(value))
207
- return false;
208
- if (value.includes("://"))
209
- return false;
210
- // A repo-relative path: at least one slash or a known file extension, no
211
- // shell/argument metacharacters.
212
- if (/[;&|`$(){}!<>]/u.test(value))
213
- return false;
214
- return value.includes("/") || /\.[A-Za-z0-9]{1,12}$/u.test(value);
215
- }
@@ -1,26 +1,29 @@
1
- import { isSemanticReviewRound } from "./reviewOutcomeClassifier.js";
1
+ export function isCompletedReviewExecutionFromTurns(round, turns) {
2
+ if (round.status !== "completed" || round.reviewerTurnId === undefined)
3
+ return false;
4
+ const turn = turns.find(({ id }) => id === round.reviewerTurnId);
5
+ return turn !== undefined
6
+ && turn.status === "completed"
7
+ && turn.result !== undefined
8
+ && turn.purpose === "review"
9
+ && turn.taskId === round.taskId
10
+ && turn.reviewRoundId === round.id
11
+ && turn.roleName === round.reviewerRoleName
12
+ && turn.executionGroupId === undefined
13
+ && turn.effective.reviewBaseCommit === round.reviewBaseCommit;
14
+ }
2
15
  /**
3
- * Whether a completed Task-final Round is a trustworthy accepted baseline.
4
- * Semantic completion alone is insufficient when it returned a non-accepting
5
- * delta disposition or still owns an open material finding.
16
+ * Whether a Task-final ReviewRound has one exact completed main Reviewer Turn.
17
+ * This is structural evidence only and never means the Leader accepted it.
6
18
  */
7
- export function isAcceptedTaskReviewBaseline(store, round) {
8
- return isAcceptedTaskReviewBaselineFromEvidence(round, store);
19
+ export function isCompletedTaskReviewEvidence(store, round) {
20
+ return isCompletedTaskReviewEvidenceFromTurns(round, store.listTurns(round.taskId));
9
21
  }
10
- /** Pure evidence form shared by read-only projections and mutation paths. */
11
- export function isAcceptedTaskReviewBaselineFromEvidence(round, evidence) {
22
+ export function isCompletedTaskReviewEvidenceFromTurns(round, turns) {
12
23
  if ((round.scope ?? "work-item") !== "task"
13
- || round.status !== "completed"
14
24
  || round.taskCandidate === undefined
15
- || round.taskCandidate.projects.length === 0
16
- || !isSemanticReviewRound(round, evidence))
17
- return false;
18
- if ((round.checks ?? []).some(({ outcome }) => outcome === "failed"))
19
- return false;
20
- if (round.deltaRecheck !== undefined
21
- && round.deltaRecheck.disposition !== "equivalent-and-accepted")
25
+ || round.taskCandidate.projects.length === 0) {
22
26
  return false;
23
- return !(evidence?.listReviewFindings(round.taskId) ?? []).some((finding) => (finding.lastReviewRoundId === round.id
24
- && (finding.severity === "p1" || finding.severity === "p2")
25
- && (finding.disposition === "open" || finding.disposition === "fixed-pending-review")));
27
+ }
28
+ return isCompletedReviewExecutionFromTurns(round, turns);
26
29
  }
@@ -1,19 +1,9 @@
1
1
  import { requireIdentity } from "../domain/validation.js";
2
2
  export const REVIEW_TRIGGERS = ["always", "leader", "final"];
3
- /**
4
- * Issue 06 feature flag. `shadow` records the finding ledger and serves Leader
5
- * commands without changing completion behavior; `enforce` additionally fails
6
- * Task completion closed on undispositioned open P1/P2 findings.
7
- */
8
- export const REVIEW_FINDING_LEDGER_MODES = ["shadow", "enforce"];
9
3
  export function validateReviewConfig(config) {
10
4
  requireIdentity(config.roleName, "Review Role");
11
5
  if (!REVIEW_TRIGGERS.includes(config.trigger)) {
12
6
  throw new Error(`Review trigger is invalid: ${String(config.trigger)}.`);
13
7
  }
14
- if (config.findingLedger !== undefined
15
- && !REVIEW_FINDING_LEDGER_MODES.includes(config.findingLedger)) {
16
- throw new Error(`Review finding ledger mode is invalid: ${String(config.findingLedger)}.`);
17
- }
18
8
  return config;
19
9
  }
@@ -1,12 +1,12 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
- import { isAcceptedTaskReviewBaseline } from "./reviewAcceptance.js";
2
+ import { isCompletedTaskReviewEvidence } from "./reviewAcceptance.js";
3
3
  import { projectReviewerAvailability } from "./reviewerAvailability.js";
4
4
  /** Read-only facts for Leader judgment; this projection never blocks an action. */
5
5
  export function projectReviewDecision(input) {
6
6
  const { store, task, roles, turns, rounds, reviewConfig, currentCandidate } = input;
7
7
  const taskRounds = rounds.filter((round) => (round.scope ?? "work-item") === "task");
8
8
  const accepted = [...taskRounds]
9
- .filter((round) => isAcceptedTaskReviewBaseline(store, round))
9
+ .filter((round) => isCompletedTaskReviewEvidence(store, round))
10
10
  .sort(compareRoundIdentity)
11
11
  .at(-1);
12
12
  const latestAcceptedBaseline = accepted?.taskCandidate === undefined