@adhdev/mesh-shared 1.0.82-rc.27 → 1.0.82-rc.29

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.
@@ -18,10 +18,10 @@
18
18
  * Order mirrors mcp-server `ALL_MESH_TOOLS` for easy visual diffing, but the consistency
19
19
  * checks are set-based (order-insensitive).
20
20
  */
21
- export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_route_preview", "mesh_list_nodes", "mesh_enqueue_batch", "mesh_enqueue_task", "mesh_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_read_terminal", "mesh_send_keys", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_answer_question", "mesh_list_pending_approvals", "mesh_create", "mesh_add_node", "mesh_attach_remote", "mesh_clone_node", "mesh_remove_node", "mesh_cleanup_worktree_nodes", "mesh_refine_node", "mesh_refine_batch", "mesh_config", "mesh_init", "mesh_cleanup_sessions", "mesh_task_history", "mesh_ledger_query", "mesh_note", "mesh_reconcile_ledger", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_node_slots", "driver_list", "driver_describe", "artifact_list"];
21
+ export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_route_preview", "mesh_list_nodes", "mesh_enqueue_batch", "mesh_enqueue_task", "mesh_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_queue_revise", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_read_terminal", "mesh_send_keys", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_answer_question", "mesh_list_pending_approvals", "mesh_create", "mesh_add_node", "mesh_attach_remote", "mesh_clone_node", "mesh_remove_node", "mesh_cleanup_worktree_nodes", "mesh_refine_node", "mesh_refine_batch", "mesh_config", "mesh_init", "mesh_cleanup_sessions", "mesh_task_history", "mesh_ledger_query", "mesh_note", "mesh_reconcile_ledger", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_node_slots", "driver_list", "driver_describe", "artifact_list"];
22
22
  export type CanonicalMeshToolName = typeof CANONICAL_MESH_TOOL_NAMES[number];
23
23
  /** The count the `NN tools` barrel doc comments and consistency test assert against. */
24
- export declare const CANONICAL_MESH_TOOL_COUNT: 44;
24
+ export declare const CANONICAL_MESH_TOOL_COUNT: 45;
25
25
  /**
26
26
  * Tool names retired by the 2026-09-26 tool-surface consolidation (60 → 48).
27
27
  * Every capability behind them is still reachable: each retired name maps to the
@@ -15,9 +15,11 @@
15
15
  * ./brain-routing.ts and are re-exported here for one-stop discovery.
16
16
  */
17
17
  export { MESH_TASK_DIFFICULTIES, type MeshTaskDifficulty } from './brain-routing';
18
- export declare const MESH_TASK_STATUSES: readonly ["pending", "assigned", "completed", "failed", "cancelled"];
18
+ /** `blocked` ends a worker attempt pending external follow-up; only completed satisfies dependencies.
19
+ * Branch convergence (including blocked_review) remains an independent axis. */
20
+ export declare const MESH_TASK_STATUSES: readonly ["pending", "assigned", "completed", "blocked", "failed", "cancelled"];
19
21
  export type MeshTaskStatus = typeof MESH_TASK_STATUSES[number];
20
- export declare const MESH_TERMINAL_TASK_STATUSES: readonly ["completed", "failed", "cancelled"];
22
+ export declare const MESH_TERMINAL_TASK_STATUSES: readonly ["completed", "blocked", "failed", "cancelled"];
21
23
  export type MeshTerminalTaskStatus = typeof MESH_TERMINAL_TASK_STATUSES[number];
22
24
  export declare const MESH_TASK_MODES: readonly ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
23
25
  export type MeshTaskMode = typeof MESH_TASK_MODES[number];
@@ -56,8 +58,8 @@ export declare const WORKER_REPORT_SUMMARY_MAX_CHARS = 8000;
56
58
  /** Final branch state a worker reports (see coordinator convergence rules). */
57
59
  export declare const WORKER_BRANCH_STATES: readonly ["merged_to_main", "pushed_feature_branch_needs_merge", "blocked_review", "cleanup_candidate", "not_mergeable"];
58
60
  export type WorkerBranchState = typeof WORKER_BRANCH_STATES[number];
59
- export declare const isMeshTaskStatus: (value: unknown) => value is "pending" | "failed" | "assigned" | "completed" | "cancelled";
60
- export declare const isMeshTerminalTaskStatus: (value: unknown) => value is "failed" | "completed" | "cancelled";
61
+ export declare const isMeshTaskStatus: (value: unknown) => value is "pending" | "failed" | "blocked" | "assigned" | "completed" | "cancelled";
62
+ export declare const isMeshTerminalTaskStatus: (value: unknown) => value is "failed" | "blocked" | "completed" | "cancelled";
61
63
  export declare const isMeshTaskMode: (value: unknown) => value is "code_change" | "validation" | "live_debug_readonly" | "launch_app" | "convergence";
62
64
  export declare const isMeshTaskPriority: (value: unknown) => value is "low" | "high" | "normal";
63
65
  export declare const isMeshDeliveryMode: (value: unknown) => value is "when_idle" | "interrupt";
@@ -0,0 +1,75 @@
1
+ import type { OwnedPathEntry } from './path-ownership';
2
+ export type MeshTaskPhase = 'source' | 'integration';
3
+ /** Host-produced evidence. Never accepted from MCP/peer-supplied paths or branch labels. */
4
+ export interface PhysicalOwnershipScope {
5
+ version: 1;
6
+ repoIdentity: string;
7
+ machineId: string;
8
+ nodeId: string;
9
+ worktree: string;
10
+ worktreeIdentity: string;
11
+ gitDir: string;
12
+ commonGitDir: string;
13
+ phase: MeshTaskPhase;
14
+ gitResources: {
15
+ index: string;
16
+ refs: string;
17
+ hooks: string;
18
+ config: string;
19
+ };
20
+ resources: Array<{
21
+ path: string;
22
+ identity?: string;
23
+ subtree: boolean;
24
+ }>;
25
+ declaration: readonly OwnedPathEntry[];
26
+ }
27
+ /** A source packet is an input to integration, never release/feature acceptance. */
28
+ export interface MeshSourceCandidate {
29
+ version: 1;
30
+ readiness: 'uncommitted' | 'verified_source_candidate';
31
+ taskId: string;
32
+ missionId: string;
33
+ revision: number;
34
+ repoIdentity: string;
35
+ machineId: string;
36
+ canonicalWorktree: string;
37
+ rootBaseSha: string;
38
+ ossBaseSha: string;
39
+ rootCandidateSha?: string;
40
+ ossCandidateSha?: string;
41
+ patchSha256: string;
42
+ manifestSha256: string;
43
+ touchedPaths: string[];
44
+ tests: Array<{
45
+ command: string;
46
+ exit: number;
47
+ logSha256: string;
48
+ }>;
49
+ settledCommands: boolean;
50
+ integrationTaskId: string;
51
+ logicalOverlapTaskIds: string[];
52
+ }
53
+ /** Product B consumes one frozen batch; normal hooks/parity/gates remain required. */
54
+ export interface MeshIntegrationBatchContract {
55
+ version: 1;
56
+ batchId: string;
57
+ candidates: MeshSourceCandidate[];
58
+ integrationOrder: string[];
59
+ approvedRootSha: string;
60
+ approvedOssSha: string;
61
+ bundleInvocationCount: 1;
62
+ requiredGates: string[];
63
+ normalHooksRequired: true;
64
+ independentReviewRequired: true;
65
+ ownerLandingApprovalRequired: true;
66
+ }
67
+ /** These only rank legal targets; targetNodeId/SessionId and requiredTags remain hard. */
68
+ export interface MeshRoutingPreferences {
69
+ nodeIds?: string[];
70
+ providers?: string[];
71
+ }
72
+ export interface MeshReviewConstraint {
73
+ sourceTaskId: string;
74
+ differentProvider: true;
75
+ }
@@ -101,7 +101,7 @@ export type NoProgressObservedStatus = SessionStatus | 'unknown';
101
101
  */
102
102
  export declare const HOLD_REASONS: readonly ["await_delivery", "await_consume", "await_turn", "liveness", "hard_ceiling", "live_pending", "transcript_quiet", "weak_candidate", "suspension_before_consumed", "await_report", "await_end", "report_correction"];
103
103
  export type HoldReason = typeof HOLD_REASONS[number];
104
- export declare const TURN_OUTCOMES: readonly ["completed", "failed", "cancelled"];
104
+ export declare const TURN_OUTCOMES: readonly ["completed", "blocked", "failed", "cancelled"];
105
105
  export type TurnOutcome = typeof TURN_OUTCOMES[number];
106
106
  export declare const COMMIT_STRENGTHS: readonly ["genuine", "weak", "tool_report", "operator"];
107
107
  export type CommitStrength = typeof COMMIT_STRENGTHS[number];
@@ -118,7 +118,7 @@ export type TurnReason = typeof TURN_REASONS[number];
118
118
  * (refine terminal, worktree bootstrap, …) whose text lives only in the
119
119
  * owner's local `turn_events.payload_json` and is rendered at deliver time.
120
120
  */
121
- export declare const NOTIFY_KINDS: readonly ["completed", "failed", "cancelled", "stopped", "approval", "choice", "approval_resolved", "candidate", "no_progress", "progress", "late_completion", "mesh_event"];
121
+ export declare const NOTIFY_KINDS: readonly ["completed", "blocked", "failed", "cancelled", "stopped", "approval", "choice", "approval_resolved", "candidate", "no_progress", "progress", "late_completion", "mesh_event"];
122
122
  export type NotifyKind = typeof NOTIFY_KINDS[number];
123
123
  export interface TurnAttemptRef {
124
124
  attemptId: string;
@@ -384,8 +384,8 @@ export declare const TURN_EVIDENCE_KINDS: readonly TurnEvidenceKind[];
384
384
  export declare const TERMINAL_CLASS_EVIDENCE_KINDS: readonly ["turn_end", "transcript_final", "no_progress", "worker_report", "session_error", "process_exit", "cancel", "operator_status"];
385
385
  export declare const isTurnEvidenceKind: (value: unknown) => value is "no_progress" | "delivered" | "liveness" | "turn_end" | "transcript_final" | "session_error" | "dispatch_failed" | "dispatch_accepted" | "delivery_refused" | "duplicate_dispatch_refusal" | "session_rebound" | "turn_started" | "suspension" | "suspension_resolved" | "transcript_activity" | "process_exit" | "worker_report" | "worker_report_correction_requested" | "worker_progress" | "git_side_effect" | "cancel" | "operator_status" | "coordinator_ack" | "hold_expired";
386
386
  export declare const isTurnReason: (value: unknown) => value is "provider_error" | "intentional_cleanup" | "operator_cancel" | "task_removed" | "mission_abandoned" | "superseded" | "operator_update" | "refine_terminal" | "validation_terminal" | "hard_ceiling" | "turn_end" | "weak_end_confirmed" | "transcript_final" | "worker_reported" | "finalization_timeout_no_response" | "weak_end_liveness_unconfirmed" | "weak_end_probe_unobserved" | "report_correction_expired" | "session_error" | "provider_auth_failed" | "provider_billing_failed" | "provider_usage_limit_exceeded" | "provider_server_overloaded" | "reclaim_budget_exhausted" | "hollow_max_retries" | "migration_orphan" | "dispatch_refused_session_exited" | "dispatch_refused_no_target" | "dispatch_refused_unsupported_input" | "dispatch_failed" | "session_exit" | "session_exit_before_turn" | "session_dead" | "hollow_completion" | "assigned_stranded_dispatch_unconfirmed" | "delivered_not_consumed_redrive" | "delivered_no_turn_deadline";
387
- export declare const isNotifyKind: (value: unknown) => value is "stopped" | "failed" | "no_progress" | "completed" | "cancelled" | "approval" | "choice" | "approval_resolved" | "candidate" | "progress" | "late_completion" | "mesh_event";
388
- export declare const isTurnOutcome: (value: unknown) => value is "failed" | "completed" | "cancelled";
387
+ export declare const isNotifyKind: (value: unknown) => value is "stopped" | "failed" | "blocked" | "no_progress" | "completed" | "cancelled" | "approval" | "choice" | "approval_resolved" | "candidate" | "progress" | "late_completion" | "mesh_event";
388
+ export declare const isTurnOutcome: (value: unknown) => value is "failed" | "blocked" | "completed" | "cancelled";
389
389
  export declare const isCommitStrength: (value: unknown) => value is "operator" | "genuine" | "weak" | "tool_report";
390
390
  export declare function isEvidenceIdentifier(value: unknown): value is string;
391
391
  export declare function isSummaryRef(value: unknown): value is SummaryRef;
@@ -89,6 +89,8 @@ export interface MeshMissionTaskAggregateWire {
89
89
  lastActivityAt: string | null;
90
90
  }
91
91
  export interface MeshMissionStatsWire {
92
+ /** Present on peers that project proven blocked outcomes separately. */
93
+ blocked?: number;
92
94
  missionId: string;
93
95
  taskCount: number;
94
96
  completed: number;
@@ -231,6 +233,8 @@ export interface LedgerQueryEntryWire {
231
233
  payload: Record<string, unknown>;
232
234
  }
233
235
  export interface LedgerQuerySummaryWire {
236
+ /** Absent on older peers; never folded into failed by the decoder. */
237
+ taskBlocked?: number;
234
238
  meshId: string;
235
239
  totalEntries: number;
236
240
  taskDispatched: number;
@@ -65,6 +65,8 @@ export interface QueueQueryResponse {
65
65
  counts?: Record<string, number>;
66
66
  /** `withCounts` + `historicalOlderThanMs`: terminal rows older than that age. */
67
67
  oldHistoricalCount?: number;
68
+ /** Included in counts.blocked, derived from proven worker reports; stored failed rows stay unchanged. */
69
+ legacyReportedBlockedCount?: number;
68
70
  /** `withDependencyHeads`. */
69
71
  dependencyHeads?: readonly QueueDependencyHeadWire[];
70
72
  }
@@ -194,3 +196,20 @@ export interface RecoveryContextQueryResponse {
194
196
  }
195
197
  export declare function decodeRecoveryContextQueryRequest(value: unknown): RecoveryContextQueryRequest | null;
196
198
  export declare function decodeRecoveryContextQueryResponse(value: unknown): RecoveryContextQueryResponse | null;
199
+ export interface QueueReviseRequest {
200
+ v: typeof TURN_IPC_PROTOCOL_VERSION;
201
+ meshId: string;
202
+ taskId: string;
203
+ expectedRevision: number;
204
+ patch: {
205
+ message?: string;
206
+ ownedPaths?: string[];
207
+ phase?: 'source' | 'integration';
208
+ dependsOn?: string[];
209
+ routingPreferences?: import('./task-source-phase').MeshRoutingPreferences;
210
+ review?: import('./task-source-phase').MeshReviewConstraint;
211
+ };
212
+ }
213
+ export type QueueReviseResponse = QueueRequeueResponse;
214
+ export declare function decodeQueueReviseRequest(value: unknown): QueueReviseRequest | null;
215
+ export declare const decodeQueueReviseResponse: typeof decodeQueueRequeueResponse;
@@ -94,6 +94,6 @@ export interface TurnIpcError {
94
94
  }
95
95
  export declare function isTurnIpcError(value: unknown): value is TurnIpcError;
96
96
  /** The complete, closed set of IPC command names this contract defines. */
97
- export declare const TURN_IPC_COMMANDS: readonly ["turn_observe", "mesh_record", "turn_cancel", "operator_status", "turn_query", "mesh_index_query", "mission_upsert", "mission_query", "note_upsert", "note_forget", "tool_call_record", "ledger_query", "mission_list_query", "record_local", "queue_query", "queue_enqueue", "queue_enqueue_batch", "queue_cancel", "queue_requeue", "direct_dispatch_record", "active_work_query", "recovery_context_query", "task_stats_query", "prune_stale_direct", "orphaned_pin_notify"];
97
+ export declare const TURN_IPC_COMMANDS: readonly ["turn_observe", "mesh_record", "turn_cancel", "operator_status", "turn_query", "mesh_index_query", "mission_upsert", "mission_query", "note_upsert", "note_forget", "tool_call_record", "ledger_query", "mission_list_query", "record_local", "queue_query", "queue_enqueue", "queue_enqueue_batch", "queue_cancel", "queue_requeue", "queue_revise", "direct_dispatch_record", "active_work_query", "recovery_context_query", "task_stats_query", "prune_stale_direct", "orphaned_pin_notify"];
98
98
  export type TurnIpcCommand = typeof TURN_IPC_COMMANDS[number];
99
- export declare const isTurnIpcCommand: (value: unknown) => value is "prune_stale_direct" | "operator_status" | "turn_observe" | "mesh_record" | "turn_cancel" | "turn_query" | "mesh_index_query" | "mission_upsert" | "mission_query" | "note_upsert" | "note_forget" | "tool_call_record" | "ledger_query" | "mission_list_query" | "record_local" | "queue_query" | "queue_enqueue" | "queue_enqueue_batch" | "queue_cancel" | "queue_requeue" | "direct_dispatch_record" | "active_work_query" | "recovery_context_query" | "task_stats_query" | "orphaned_pin_notify";
99
+ export declare const isTurnIpcCommand: (value: unknown) => value is "prune_stale_direct" | "operator_status" | "turn_observe" | "mesh_record" | "turn_cancel" | "turn_query" | "mesh_index_query" | "mission_upsert" | "mission_query" | "note_upsert" | "note_forget" | "tool_call_record" | "ledger_query" | "mission_list_query" | "record_local" | "queue_query" | "queue_enqueue" | "queue_enqueue_batch" | "queue_cancel" | "queue_requeue" | "queue_revise" | "direct_dispatch_record" | "active_work_query" | "recovery_context_query" | "task_stats_query" | "orphaned_pin_notify";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/mesh-shared",
3
- "version": "1.0.82-rc.27",
3
+ "version": "1.0.82-rc.29",
4
4
  "description": "ADHDev mesh-shared — pure mesh/git status normalizers shared by daemon-core and web-core",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -187,6 +187,7 @@ export interface AssistantRemoteAccessBinding {
187
187
  grantRevision: number
188
188
  status: 'active' | 'revoked'
189
189
  allowedOps: readonly AssistantFollowerAllowedOp[]
190
+ allowCoordinatorLaunch?: boolean
190
191
  grantedAt: number
191
192
  revokedAt?: number
192
193
  }
@@ -232,6 +233,7 @@ export interface FollowedRemoteProjectEntry {
232
233
  accountSlot: AssistantAccountSlot
233
234
  bindingRevision: number
234
235
  grantRevision: number
236
+ allowCoordinatorLaunch?: boolean
235
237
  state: FollowedRemoteProjectState
236
238
  followedAt: number | string
237
239
  updatedAt: number | string
@@ -261,6 +263,7 @@ export interface RemoteProjectCatalogItem {
261
263
  name: string
262
264
  sanitizedRepoIdentity: string | null
263
265
  coordinatorState?: 'none' | 'idle' | 'working' | 'waiting' | string
266
+ allowCoordinatorLaunch?: boolean
264
267
  capabilities: {
265
268
  status: true
266
269
  read: true
@@ -268,6 +271,7 @@ export interface RemoteProjectCatalogItem {
268
271
  report: true
269
272
  send: true
270
273
  coordinatorPresent?: boolean
274
+ allowCoordinatorLaunch?: boolean
271
275
  }
272
276
  observedAt: number | string
273
277
  }
@@ -108,13 +108,15 @@ export function isPhantomDaemonEntry(
108
108
  export function machineCoreFromDaemonId(id: string | null | undefined): string | undefined {
109
109
  const trimmed = readString(id)
110
110
  if (!trimmed) return undefined
111
+ const lower = trimmed.toLowerCase()
111
112
  for (const prefix of DAEMON_ID_PREFIXES) {
112
- if (trimmed.startsWith(prefix)) {
113
+ if (lower.startsWith(prefix)) {
113
114
  const core = trimmed.slice(prefix.length).trim()
114
- return core || undefined
115
+ if (!core) return undefined
116
+ return /^mach_/i.test(core) ? `mach_${core.slice(5)}` : core
115
117
  }
116
118
  }
117
- return trimmed
119
+ return /^mach_/i.test(trimmed) ? `mach_${trimmed.slice(5)}` : trimmed
118
120
  }
119
121
 
120
122
  /**
@@ -144,8 +146,8 @@ export function machineCoreFromDaemonId(id: string | null | undefined): string |
144
146
  export function canonicalDaemonId(id: string | null | undefined): string | undefined {
145
147
  const core = machineCoreFromDaemonId(id)
146
148
  if (!core) return undefined
147
- if (!core.startsWith('mach_')) return core
148
- return `daemon_${core}`
149
+ if (!/^mach_/i.test(core)) return core
150
+ return `daemon_mach_${core.slice(5)}`
149
151
  }
150
152
 
151
153
  /** True when both ids resolve to the same machine core — i.e. they are the same
package/src/index.ts CHANGED
@@ -47,3 +47,4 @@ export * from './provider-question'
47
47
  export * from './launch-defaults'
48
48
 
49
49
  export * from './role-launch';
50
+ export * from './task-source-phase';
@@ -28,6 +28,7 @@ export const CANONICAL_MESH_TOOL_NAMES = [
28
28
  'mesh_view_queue',
29
29
  'mesh_queue_cancel',
30
30
  'mesh_queue_requeue',
31
+ 'mesh_queue_revise',
31
32
  'mesh_send_task',
32
33
  'mesh_read_chat',
33
34
  'mesh_read_debug',
@@ -17,10 +17,12 @@
17
17
 
18
18
  export { MESH_TASK_DIFFICULTIES, type MeshTaskDifficulty } from './brain-routing'
19
19
 
20
- export const MESH_TASK_STATUSES = ['pending', 'assigned', 'completed', 'failed', 'cancelled'] as const
20
+ /** `blocked` ends a worker attempt pending external follow-up; only completed satisfies dependencies.
21
+ * Branch convergence (including blocked_review) remains an independent axis. */
22
+ export const MESH_TASK_STATUSES = ['pending', 'assigned', 'completed', 'blocked', 'failed', 'cancelled'] as const
21
23
  export type MeshTaskStatus = typeof MESH_TASK_STATUSES[number]
22
24
 
23
- export const MESH_TERMINAL_TASK_STATUSES = ['completed', 'failed', 'cancelled'] as const
25
+ export const MESH_TERMINAL_TASK_STATUSES = ['completed', 'blocked', 'failed', 'cancelled'] as const
24
26
  export type MeshTerminalTaskStatus = typeof MESH_TERMINAL_TASK_STATUSES[number]
25
27
 
26
28
  export const MESH_TASK_MODES = ['code_change', 'validation', 'live_debug_readonly', 'launch_app', 'convergence'] as const
@@ -0,0 +1,61 @@
1
+ import type { OwnedPathEntry } from './path-ownership'
2
+
3
+ export type MeshTaskPhase = 'source' | 'integration'
4
+
5
+ /** Host-produced evidence. Never accepted from MCP/peer-supplied paths or branch labels. */
6
+ export interface PhysicalOwnershipScope {
7
+ version: 1
8
+ repoIdentity: string
9
+ machineId: string
10
+ nodeId: string
11
+ worktree: string
12
+ worktreeIdentity: string
13
+ gitDir: string
14
+ commonGitDir: string
15
+ phase: MeshTaskPhase
16
+ gitResources: { index: string; refs: string; hooks: string; config: string }
17
+ resources: Array<{ path: string; identity?: string; subtree: boolean }>
18
+ declaration: readonly OwnedPathEntry[]
19
+ }
20
+
21
+ /** A source packet is an input to integration, never release/feature acceptance. */
22
+ export interface MeshSourceCandidate {
23
+ version: 1
24
+ readiness: 'uncommitted' | 'verified_source_candidate'
25
+ taskId: string
26
+ missionId: string
27
+ revision: number
28
+ repoIdentity: string
29
+ machineId: string
30
+ canonicalWorktree: string
31
+ rootBaseSha: string
32
+ ossBaseSha: string
33
+ rootCandidateSha?: string
34
+ ossCandidateSha?: string
35
+ patchSha256: string
36
+ manifestSha256: string
37
+ touchedPaths: string[]
38
+ tests: Array<{ command: string; exit: number; logSha256: string }>
39
+ settledCommands: boolean
40
+ integrationTaskId: string
41
+ logicalOverlapTaskIds: string[]
42
+ }
43
+
44
+ /** Product B consumes one frozen batch; normal hooks/parity/gates remain required. */
45
+ export interface MeshIntegrationBatchContract {
46
+ version: 1
47
+ batchId: string
48
+ candidates: MeshSourceCandidate[]
49
+ integrationOrder: string[]
50
+ approvedRootSha: string
51
+ approvedOssSha: string
52
+ bundleInvocationCount: 1
53
+ requiredGates: string[]
54
+ normalHooksRequired: true
55
+ independentReviewRequired: true
56
+ ownerLandingApprovalRequired: true
57
+ }
58
+
59
+ /** These only rank legal targets; targetNodeId/SessionId and requiredTags remain hard. */
60
+ export interface MeshRoutingPreferences { nodeIds?: string[]; providers?: string[] }
61
+ export interface MeshReviewConstraint { sourceTaskId: string; differentProvider: true }
@@ -154,7 +154,7 @@ export const HOLD_REASONS = [
154
154
  ] as const
155
155
  export type HoldReason = typeof HOLD_REASONS[number]
156
156
 
157
- export const TURN_OUTCOMES = ['completed', 'failed', 'cancelled'] as const
157
+ export const TURN_OUTCOMES = ['completed', 'blocked', 'failed', 'cancelled'] as const
158
158
  export type TurnOutcome = typeof TURN_OUTCOMES[number]
159
159
 
160
160
  export const COMMIT_STRENGTHS = ['genuine', 'weak', 'tool_report', 'operator'] as const
@@ -186,7 +186,7 @@ export type TurnReason = typeof TURN_REASONS[number]
186
186
  * owner's local `turn_events.payload_json` and is rendered at deliver time.
187
187
  */
188
188
  export const NOTIFY_KINDS = [
189
- 'completed', 'failed', 'cancelled', 'stopped', 'approval', 'choice', 'approval_resolved',
189
+ 'completed', 'blocked', 'failed', 'cancelled', 'stopped', 'approval', 'choice', 'approval_resolved',
190
190
  'candidate', 'no_progress', 'progress', 'late_completion', 'mesh_event',
191
191
  ] as const
192
192
  export type NotifyKind = typeof NOTIFY_KINDS[number]
@@ -221,6 +221,8 @@ export interface MeshMissionTaskAggregateWire {
221
221
  }
222
222
 
223
223
  export interface MeshMissionStatsWire {
224
+ /** Present on peers that project proven blocked outcomes separately. */
225
+ blocked?: number
224
226
  missionId: string
225
227
  taskCount: number
226
228
  completed: number
@@ -313,12 +315,13 @@ function isMeshMissionTaskAggregateWire(value: unknown): value is MeshMissionTas
313
315
  }
314
316
 
315
317
  function isMeshMissionStatsWire(value: unknown): value is MeshMissionStatsWire {
316
- const keys = ['missionId', 'taskCount', 'completed', 'failed', 'totalDurationMs', 'wallClockMs', 'retries', 'incompleteTaskIds'] as const
318
+ const keys = ['missionId', 'taskCount', 'completed', 'failed', 'blocked', 'totalDurationMs', 'wallClockMs', 'retries', 'incompleteTaskIds'] as const
317
319
  if (!isRecord(value) || !hasOnlyKeys(value, keys)) return false
318
320
  if (!isEvidenceIdentifier(value.missionId)) return false
319
321
  for (const k of ['taskCount', 'completed', 'failed', 'totalDurationMs', 'retries'] as const) {
320
322
  if (!isNonNegativeInt(value[k])) return false
321
323
  }
324
+ if (value.blocked !== undefined && !isNonNegativeInt(value.blocked)) return false
322
325
  if (value.wallClockMs !== null && !isNonNegativeInt(value.wallClockMs)) return false
323
326
  if (!Array.isArray(value.incompleteTaskIds) || !value.incompleteTaskIds.every(isEvidenceIdentifier)) return false
324
327
  return true
@@ -580,6 +583,8 @@ export interface LedgerQueryEntryWire {
580
583
  }
581
584
 
582
585
  export interface LedgerQuerySummaryWire {
586
+ /** Absent on older peers; never folded into failed by the decoder. */
587
+ taskBlocked?: number
583
588
  meshId: string
584
589
  totalEntries: number
585
590
  taskDispatched: number
@@ -625,12 +630,13 @@ function isLedgerQueryEntryWire(value: unknown): value is LedgerQueryEntryWire {
625
630
  }
626
631
 
627
632
  function isLedgerQuerySummaryWire(value: unknown): value is LedgerQuerySummaryWire {
628
- const keys = ['meshId', 'totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'lastActivityAt', 'recentFailures'] as const
633
+ const keys = ['meshId', 'totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskBlocked', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'lastActivityAt', 'recentFailures'] as const
629
634
  if (!isRecord(value) || !hasOnlyKeys(value, keys)) return false
630
635
  if (!isEvidenceIdentifier(value.meshId)) return false
631
636
  for (const k of ['totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'recentFailures'] as const) {
632
637
  if (!isNonNegativeInt(value[k])) return false
633
638
  }
639
+ if (value.taskBlocked !== undefined && !isNonNegativeInt(value.taskBlocked)) return false
634
640
  if (value.lastActivityAt !== null && typeof value.lastActivityAt !== 'string') return false
635
641
  return true
636
642
  }
@@ -135,6 +135,8 @@ export interface QueueQueryResponse {
135
135
  counts?: Record<string, number>
136
136
  /** `withCounts` + `historicalOlderThanMs`: terminal rows older than that age. */
137
137
  oldHistoricalCount?: number
138
+ /** Included in counts.blocked, derived from proven worker reports; stored failed rows stay unchanged. */
139
+ legacyReportedBlockedCount?: number
138
140
  /** `withDependencyHeads`. */
139
141
  dependencyHeads?: readonly QueueDependencyHeadWire[]
140
142
  }
@@ -159,9 +161,10 @@ function isQueueDependencyHeadWire(value: unknown): value is QueueDependencyHead
159
161
  }
160
162
 
161
163
  function isQueueQueryResponse(value: unknown): value is QueueQueryResponse {
162
- if (!isRecord(value) || !hasOnlyKeys(value, ['entries', 'counts', 'oldHistoricalCount', 'dependencyHeads'])) return false
164
+ if (!isRecord(value) || !hasOnlyKeys(value, ['entries', 'counts', 'oldHistoricalCount', 'legacyReportedBlockedCount', 'dependencyHeads'])) return false
163
165
  if (!Array.isArray(value.entries) || !value.entries.every(isQueueEntryWire)) return false
164
166
  if (value.counts !== undefined && (!isRecord(value.counts) || !Object.values(value.counts).every(isNonNegativeInt))) return false
167
+ if (value.legacyReportedBlockedCount !== undefined && !isNonNegativeInt(value.legacyReportedBlockedCount)) return false
165
168
  if (value.oldHistoricalCount !== undefined && !isNonNegativeInt(value.oldHistoricalCount)) return false
166
169
  if (value.dependencyHeads !== undefined && (!Array.isArray(value.dependencyHeads) || !value.dependencyHeads.every(isQueueDependencyHeadWire))) return false
167
170
  return true
@@ -491,3 +494,28 @@ function isRecoveryContextQueryResponse(value: unknown): value is RecoveryContex
491
494
  export function decodeRecoveryContextQueryResponse(value: unknown): RecoveryContextQueryResponse | null {
492
495
  return isRecoveryContextQueryResponse(value) ? value : null
493
496
  }
497
+
498
+ // ── queue_revise: host-authenticated pending CAS, never a force requeue ──
499
+ export interface QueueReviseRequest {
500
+ v: typeof TURN_IPC_PROTOCOL_VERSION
501
+ meshId: string
502
+ taskId: string
503
+ expectedRevision: number
504
+ patch: { message?: string; ownedPaths?: string[]; phase?: 'source' | 'integration'; dependsOn?: string[]; routingPreferences?: import('./task-source-phase').MeshRoutingPreferences; review?: import('./task-source-phase').MeshReviewConstraint }
505
+ }
506
+ export type QueueReviseResponse = QueueRequeueResponse
507
+ export function decodeQueueReviseRequest(value: unknown): QueueReviseRequest | null {
508
+ if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'taskId', 'expectedRevision', 'patch'])
509
+ || value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.taskId)
510
+ || !Number.isSafeInteger(value.expectedRevision) || (value.expectedRevision as number) < 0
511
+ || !isRecord(value.patch) || !Object.keys(value.patch).length
512
+ || !hasOnlyKeys(value.patch, ['message', 'ownedPaths', 'phase', 'dependsOn', 'routingPreferences', 'review'])) return null
513
+ const p = value.patch
514
+ if (p.message !== undefined && (typeof p.message !== 'string' || !p.message.trim())) return null
515
+ if (p.phase !== undefined && p.phase !== 'source' && p.phase !== 'integration') return null
516
+ for (const key of ['ownedPaths', 'dependsOn']) {
517
+ if (p[key] !== undefined && (!Array.isArray(p[key]) || !(p[key] as unknown[]).every(x => typeof x === 'string' && !!x.trim()))) return null
518
+ }
519
+ return value as unknown as QueueReviseRequest
520
+ }
521
+ export const decodeQueueReviseResponse = decodeQueueRequeueResponse
@@ -66,7 +66,7 @@ function isTurnObserveResponse(value: unknown): value is TurnObserveResponse {
66
66
  && (value.outcome === undefined || isTurnOutcomeValue(value.outcome))
67
67
  }
68
68
 
69
- const TURN_OUTCOMES_LOCAL = ['completed', 'failed', 'cancelled'] as const
69
+ const TURN_OUTCOMES_LOCAL = ['completed', 'blocked', 'failed', 'cancelled'] as const
70
70
  function isTurnOutcomeValue(value: unknown): value is TurnOutcome {
71
71
  return typeof value === 'string' && (TURN_OUTCOMES_LOCAL as readonly string[]).includes(value)
72
72
  }
package/src/turn-ipc.ts CHANGED
@@ -132,6 +132,7 @@ export const TURN_IPC_COMMANDS = [
132
132
  'queue_enqueue_batch',
133
133
  'queue_cancel',
134
134
  'queue_requeue',
135
+ 'queue_revise',
135
136
  'direct_dispatch_record',
136
137
  'active_work_query',
137
138
  'recovery_context_query',