@ran-sh/dsh-crew 2.0.3 → 2.0.4
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/package.json
CHANGED
package/src/mcp-runtime.mjs
CHANGED
|
@@ -283,8 +283,10 @@ export function buildMcpWorkflowRuntime(deps) {
|
|
|
283
283
|
const config = getConfig();
|
|
284
284
|
const isolation = config.execution?.isolation ?? 'worktree';
|
|
285
285
|
// Explicit shared mode uses the requested workspace. Readonly profiles,
|
|
286
|
-
// including the default Reviewer,
|
|
287
|
-
// accidental edit
|
|
286
|
+
// including the default Reviewer, take the disposable worktree below, so an
|
|
287
|
+
// accidental edit cannot reach the primary tree at all. A reviewer that was
|
|
288
|
+
// explicitly given a shared workspace still gets fingerprinted around its
|
|
289
|
+
// attempt, so an edit there is detected even though it is not prevented.
|
|
288
290
|
if (job.requested_isolation === 'shared' || isolation === 'shared') {
|
|
289
291
|
return { ok: true, execution_cwd: job.requested_cwd, isolation: 'shared', base_revision: null, primary_workspace_dirty: false, handle: null };
|
|
290
292
|
}
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export {
|
|
|
36
36
|
// included in the identity contract.
|
|
37
37
|
const RUNTIME_ID = randomUUID();
|
|
38
38
|
|
|
39
|
-
export const RUNTIME_VERSION = '2.0.
|
|
39
|
+
export const RUNTIME_VERSION = '2.0.4';
|
|
40
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
41
41
|
|
|
42
42
|
export const HUB_CAPABILITIES = Object.freeze([
|
package/src/workflow-runtime.mjs
CHANGED
|
@@ -289,7 +289,10 @@ export function createWorkflowRuntime(adapters, {
|
|
|
289
289
|
|
|
290
290
|
async function releaseWorkspace(job) {
|
|
291
291
|
if (job.retain_workspace) {
|
|
292
|
-
|
|
292
|
+
// Retention means an isolated workspace was deliberately kept for
|
|
293
|
+
// recovery. A shared workspace is the caller's own and was never ours to
|
|
294
|
+
// keep, so an evidence failure there retains nothing.
|
|
295
|
+
if (job.workspaceHandle) job.workspace_retained = true;
|
|
293
296
|
return;
|
|
294
297
|
}
|
|
295
298
|
if (!job.workspaceHandle) return;
|
|
@@ -342,7 +345,7 @@ export function createWorkflowRuntime(adapters, {
|
|
|
342
345
|
|
|
343
346
|
if (isReviewJob) {
|
|
344
347
|
transition(job, JOB_PHASES.REVIEWING, 'explicit reviewer');
|
|
345
|
-
const before = alloc?.ok
|
|
348
|
+
const before = alloc?.ok ? await safeCapture(adapters, job.execution_cwd, job.base_revision) : null;
|
|
346
349
|
// Explicit reviewers inspect the caller's stated task/workspace
|
|
347
350
|
// directly. The bounded worker-candidate capsule belongs only to the
|
|
348
351
|
// automatic post-worker review path; constructing it with no worker
|
|
@@ -553,8 +556,13 @@ export function createWorkflowRuntime(adapters, {
|
|
|
553
556
|
}
|
|
554
557
|
|
|
555
558
|
async function runReviewerAttempt(job, task, config, beforeCandidate, cwd = job.execution_cwd, baseRevision = job.base_revision) {
|
|
559
|
+
// Reviewing is the one role whose entire contract is "changed nothing", so
|
|
560
|
+
// its evidence is not exempted by isolation. In a worktree the pre-review
|
|
561
|
+
// candidate comes from the allocator or the worker and its absence is a real
|
|
562
|
+
// failure; a shared workspace has no such candidate, so fingerprint the tree
|
|
563
|
+
// the reviewer is about to touch — otherwise the reviewer is the one actor
|
|
564
|
+
// whose edits nothing observes.
|
|
556
565
|
const requireFingerprint = (candidate) => {
|
|
557
|
-
if (job.isolation !== 'worktree') return;
|
|
558
566
|
if (candidate?.ok === true && typeof candidate.fingerprint === 'string' && candidate.fingerprint.trim()) return;
|
|
559
567
|
job.candidate_capture_failed = true;
|
|
560
568
|
job.retain_workspace = true;
|
|
@@ -562,7 +570,9 @@ export function createWorkflowRuntime(adapters, {
|
|
|
562
570
|
code: 'REVIEW_EVIDENCE_UNAVAILABLE',
|
|
563
571
|
});
|
|
564
572
|
};
|
|
565
|
-
|
|
573
|
+
const before = beforeCandidate
|
|
574
|
+
?? (job.isolation === 'worktree' ? null : await safeCapture(adapters, cwd, baseRevision));
|
|
575
|
+
requireFingerprint(before);
|
|
566
576
|
const policy = resolveModelPolicy(config, 'reviewer', { attempt: 0 });
|
|
567
577
|
const attemptId = attemptIdFor(adapters, job.id, 'review');
|
|
568
578
|
job.current_attempt_id = attemptId;
|
|
@@ -594,9 +604,9 @@ export function createWorkflowRuntime(adapters, {
|
|
|
594
604
|
fallback_reason: attemptView.selection_trace?.fallback_reason ?? null,
|
|
595
605
|
}, 0);
|
|
596
606
|
if (ar.status === 'failed' && attemptView.error_code) job.error_code = attemptView.error_code;
|
|
597
|
-
const afterCandidate =
|
|
607
|
+
const afterCandidate = await safeCapture(adapters, cwd, baseRevision);
|
|
598
608
|
requireFingerprint(afterCandidate);
|
|
599
|
-
const review = normalizeReview({ attemptResult: ar, beforeCandidate, afterCandidate });
|
|
609
|
+
const review = normalizeReview({ attemptResult: ar, beforeCandidate: before, afterCandidate });
|
|
600
610
|
recordCanonical(job, 'review.completed', {
|
|
601
611
|
attempt_id: attemptView.id,
|
|
602
612
|
status: review.status,
|