@adhdev/daemon-core 0.9.82-rc.82 → 0.9.82-rc.83
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/index.js +145 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +145 -13
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/refine-config.d.ts +12 -0
- package/dist/repo-mesh-types.d.ts +7 -0
- package/package.json +1 -1
- package/src/commands/chat-commands.ts +13 -1
- package/src/commands/cli-manager.ts +25 -0
- package/src/commands/router.ts +107 -7
- package/src/config/mesh-config.ts +1 -0
- package/src/mesh/coordinator-prompt.ts +5 -2
- package/src/mesh/mesh-events.ts +2 -1
- package/src/mesh/refine-config.ts +15 -0
- package/src/repo-mesh-types.ts +8 -0
|
@@ -12,6 +12,13 @@ export interface RepoMeshRefineValidationCommandConfig {
|
|
|
12
12
|
}
|
|
13
13
|
export interface RepoMeshRefineConfig {
|
|
14
14
|
version: 1;
|
|
15
|
+
/**
|
|
16
|
+
* Narrow Refinery opt-in for monorepos with submodule gitlinks.
|
|
17
|
+
* When true, Refinery may non-force publish unreachable submodule gitlink
|
|
18
|
+
* commits to the submodule remote main branch after validation and
|
|
19
|
+
* patch-equivalence pass, then verify remote-main reachability.
|
|
20
|
+
*/
|
|
21
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
15
22
|
validation?: {
|
|
16
23
|
required?: boolean;
|
|
17
24
|
commands?: RepoMeshRefineValidationCommandConfig[];
|
|
@@ -54,6 +61,11 @@ export declare const MESH_REFINE_CONFIG_SCHEMA: {
|
|
|
54
61
|
readonly version: {
|
|
55
62
|
readonly const: 1;
|
|
56
63
|
};
|
|
64
|
+
readonly allowAutoPublishSubmoduleMainCommits: {
|
|
65
|
+
readonly type: "boolean";
|
|
66
|
+
readonly default: false;
|
|
67
|
+
readonly description: "When true, Refinery may non-force publish submodule gitlink commits referenced by the refined root tree to each submodule origin/main after validation and patch-equivalence pass, then verify reachability.";
|
|
68
|
+
};
|
|
57
69
|
readonly validation: {
|
|
58
70
|
readonly type: "object";
|
|
59
71
|
readonly additionalProperties: false;
|
|
@@ -74,6 +74,13 @@ export interface RepoMeshPolicy {
|
|
|
74
74
|
requirePreTaskCheckpoint: boolean;
|
|
75
75
|
requirePostTaskCheckpoint: boolean;
|
|
76
76
|
requireApprovalForPush: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Narrow Refinery opt-in: when validation and patch-equivalence have passed,
|
|
79
|
+
* allow Refinery to publish submodule gitlink commits to each submodule's
|
|
80
|
+
* configured remote main branch with a non-force push, then verify reachability.
|
|
81
|
+
* Defaults to false; root branch pushes/merges are not affected.
|
|
82
|
+
*/
|
|
83
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
77
84
|
requireApprovalForDestructiveGit: boolean;
|
|
78
85
|
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
79
86
|
maxParallelTasks: number;
|
package/package.json
CHANGED
|
@@ -1035,7 +1035,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1035
1035
|
ptyStatusApprovalOnly: false,
|
|
1036
1036
|
});
|
|
1037
1037
|
|
|
1038
|
-
if (supportsCliNativeTranscript(providerType)) {
|
|
1038
|
+
if (supportsCliNativeTranscript(providerType) && (provider?.canonicalHistory as any)?.mode === 'native-source') {
|
|
1039
1039
|
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h, adapter.cliType);
|
|
1040
1040
|
const workspace = typeof args?.workspace === 'string'
|
|
1041
1041
|
? args.workspace
|
|
@@ -1220,6 +1220,18 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1220
1220
|
freshEnough: true,
|
|
1221
1221
|
ptyStatusApprovalOnly: false,
|
|
1222
1222
|
});
|
|
1223
|
+
const requiresNativeSource = supportsCliNativeTranscript(agentStr)
|
|
1224
|
+
&& (provider?.canonicalHistory as any)?.mode === 'native-source';
|
|
1225
|
+
if (requiresNativeSource && !nativeSelected) {
|
|
1226
|
+
return {
|
|
1227
|
+
success: false,
|
|
1228
|
+
code: 'native_history_not_safely_available',
|
|
1229
|
+
error: 'Provider-native history was not safely available for the requested CLI session.',
|
|
1230
|
+
providerSessionId: historyProviderSessionId,
|
|
1231
|
+
messageSource,
|
|
1232
|
+
transcriptProvenance: messageSource,
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1223
1235
|
return buildReadChatCommandResult({
|
|
1224
1236
|
messages: historyMessages,
|
|
1225
1237
|
status: 'idle',
|
|
@@ -109,6 +109,15 @@ function countMessages(value: unknown): number {
|
|
|
109
109
|
return Array.isArray(value) ? value.length : 0;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
function hasFinalAssistantMessage(value: unknown): boolean {
|
|
113
|
+
const messages = Array.isArray(value) ? value : [];
|
|
114
|
+
const last = messages[messages.length - 1] as any;
|
|
115
|
+
if (!last || last.role !== 'assistant') return false;
|
|
116
|
+
if (last.bubbleState === 'streaming') return false;
|
|
117
|
+
if (last.meta?.streaming === true) return false;
|
|
118
|
+
return typeof last.content === 'string' && last.content.trim().length > 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
112
121
|
function hasZeroMessageStartingLaunch(adapter: any): boolean {
|
|
113
122
|
const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
|
|
114
123
|
const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
|
|
@@ -123,6 +132,21 @@ function hasZeroMessageStartingLaunch(adapter: any): boolean {
|
|
|
123
132
|
return !hasAdapterPendingResponse(adapter);
|
|
124
133
|
}
|
|
125
134
|
|
|
135
|
+
function hasCompletedStartingLaunch(adapter: any): boolean {
|
|
136
|
+
const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
|
|
137
|
+
const adapterRawStatus = normalizeAgentStatus(adapterStatus?.status);
|
|
138
|
+
if (adapterRawStatus !== 'starting') return false;
|
|
139
|
+
if (hasAdapterPendingResponse(adapter)) return false;
|
|
140
|
+
|
|
141
|
+
const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
|
|
142
|
+
? adapter.getScriptParsedStatus()
|
|
143
|
+
: {};
|
|
144
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
145
|
+
if (parsedRawStatus !== 'idle') return false;
|
|
146
|
+
if (hasNonEmptyModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
147
|
+
return hasFinalAssistantMessage(parsedStatus?.messages);
|
|
148
|
+
}
|
|
149
|
+
|
|
126
150
|
function shouldSuppressStaleParsedBusyStatus(adapterStatus: string, parsedStatus: any, adapter: any): boolean {
|
|
127
151
|
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
128
152
|
if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
|
|
@@ -133,6 +157,7 @@ function shouldSuppressStaleParsedBusyStatus(adapterStatus: string, parsedStatus
|
|
|
133
157
|
|
|
134
158
|
function getEffectiveAgentSendStatus(adapter: any): string {
|
|
135
159
|
const adapterStatus = normalizeAgentStatus(adapter?.getStatus?.({ allowParse: false })?.status ?? adapter?.getStatus?.()?.status);
|
|
160
|
+
if (adapterStatus === 'starting' && hasCompletedStartingLaunch(adapter)) return 'idle';
|
|
136
161
|
if (adapterStatus && adapterStatus !== 'idle') return adapterStatus;
|
|
137
162
|
if (adapterStatus !== 'idle') return adapterStatus;
|
|
138
163
|
|
package/src/commands/router.ts
CHANGED
|
@@ -1102,6 +1102,11 @@ type MeshRefineSubmoduleReachabilityEntry = {
|
|
|
1102
1102
|
commit: string;
|
|
1103
1103
|
reachable: boolean;
|
|
1104
1104
|
publishRequired?: boolean;
|
|
1105
|
+
autoPublishAllowed?: boolean;
|
|
1106
|
+
autoPublishAttempted?: boolean;
|
|
1107
|
+
autoPublishSucceeded?: boolean;
|
|
1108
|
+
autoPublishVerified?: boolean;
|
|
1109
|
+
autoPublishRefspec?: string;
|
|
1105
1110
|
checkedLocal?: boolean;
|
|
1106
1111
|
localReachable?: boolean;
|
|
1107
1112
|
remote?: string;
|
|
@@ -1111,6 +1116,8 @@ type MeshRefineSubmoduleReachabilityEntry = {
|
|
|
1111
1116
|
remoteMainReachable?: boolean;
|
|
1112
1117
|
fetchedFromOrigin?: boolean;
|
|
1113
1118
|
error?: string;
|
|
1119
|
+
publishStdout?: string;
|
|
1120
|
+
publishStderr?: string;
|
|
1114
1121
|
};
|
|
1115
1122
|
|
|
1116
1123
|
type MeshRefineSubmoduleReachabilitySummary = {
|
|
@@ -1119,6 +1126,8 @@ type MeshRefineSubmoduleReachabilitySummary = {
|
|
|
1119
1126
|
unreachable: MeshRefineSubmoduleReachabilityEntry[];
|
|
1120
1127
|
entries: MeshRefineSubmoduleReachabilityEntry[];
|
|
1121
1128
|
durationMs: number;
|
|
1129
|
+
autoPublishAllowed?: boolean;
|
|
1130
|
+
autoPublishPolicySource?: string;
|
|
1122
1131
|
error?: string;
|
|
1123
1132
|
};
|
|
1124
1133
|
|
|
@@ -1187,6 +1196,17 @@ function buildSubmodulePublishRequiredNextStep(entries: MeshRefineSubmoduleReach
|
|
|
1187
1196
|
return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to the configured submodule remote main branch, then rerun mesh_refine_node. Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main.`;
|
|
1188
1197
|
}
|
|
1189
1198
|
|
|
1199
|
+
function resolveRefineryAutoPublishSubmoduleMainCommits(mesh: any, workspace: string): { enabled: boolean; source?: string } {
|
|
1200
|
+
if (mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true) {
|
|
1201
|
+
return { enabled: true, source: 'mesh.policy.allowAutoPublishSubmoduleMainCommits' };
|
|
1202
|
+
}
|
|
1203
|
+
const loaded = loadMeshRefineConfig(mesh, workspace);
|
|
1204
|
+
if (loaded.config?.allowAutoPublishSubmoduleMainCommits === true) {
|
|
1205
|
+
return { enabled: true, source: loaded.path || loaded.source };
|
|
1206
|
+
}
|
|
1207
|
+
return { enabled: false };
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1190
1210
|
async function computeGitPatchId(cwd: string, fromRef: string, toRef: string): Promise<string> {
|
|
1191
1211
|
const { execFileSync } = await import('node:child_process');
|
|
1192
1212
|
const diff = execFileSync('git', ['diff', '--patch', '--full-index', fromRef, toRef], {
|
|
@@ -1264,6 +1284,7 @@ async function runMeshRefinePatchEquivalenceGate(
|
|
|
1264
1284
|
async function runMeshRefineSubmoduleReachabilityGate(
|
|
1265
1285
|
repoRoot: string,
|
|
1266
1286
|
mergedTree: string,
|
|
1287
|
+
options: { allowAutoPublishSubmoduleMainCommits?: boolean; autoPublishPolicySource?: string } = {},
|
|
1267
1288
|
): Promise<MeshRefineSubmoduleReachabilitySummary> {
|
|
1268
1289
|
const startedAt = Date.now();
|
|
1269
1290
|
const entries: MeshRefineSubmoduleReachabilityEntry[] = [];
|
|
@@ -1285,6 +1306,17 @@ async function runMeshRefineSubmoduleReachabilityGate(
|
|
|
1285
1306
|
await runGit(submodulePath, ['-c', 'protocol.file.allow=always', 'fetch', 'origin', `refs/heads/${branch}:refs/remotes/origin/${branch}`]);
|
|
1286
1307
|
await runGit(submodulePath, ['merge-base', '--is-ancestor', commit, `refs/remotes/origin/${branch}`]);
|
|
1287
1308
|
};
|
|
1309
|
+
const publishCommitToRemoteMain = async (submodulePath: string, commit: string, branch = 'main'): Promise<{ stdout: string; stderr: string; refspec: string }> => {
|
|
1310
|
+
const refspec = `${commit}:refs/heads/${branch}`;
|
|
1311
|
+
const { stdout, stderr } = await execFileAsync('git', ['push', 'origin', refspec], {
|
|
1312
|
+
cwd: submodulePath,
|
|
1313
|
+
encoding: 'utf8',
|
|
1314
|
+
timeout: 30_000,
|
|
1315
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
|
|
1316
|
+
windowsHide: true,
|
|
1317
|
+
});
|
|
1318
|
+
return { stdout: String(stdout || ''), stderr: String(stderr || ''), refspec };
|
|
1319
|
+
};
|
|
1288
1320
|
|
|
1289
1321
|
const treeOutput = await runGit(repoRoot, ['ls-tree', '-r', '-z', mergedTree]);
|
|
1290
1322
|
const gitlinks = treeOutput
|
|
@@ -1334,11 +1366,43 @@ async function runMeshRefineSubmoduleReachabilityGate(
|
|
|
1334
1366
|
continue;
|
|
1335
1367
|
}
|
|
1336
1368
|
entry.remoteMainBranch = 'main';
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1369
|
+
try {
|
|
1370
|
+
await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, 'main');
|
|
1371
|
+
entry.fetchedFromOrigin = true;
|
|
1372
|
+
entry.remoteReachable = true;
|
|
1373
|
+
entry.remoteMainReachable = true;
|
|
1374
|
+
entry.reachable = true;
|
|
1375
|
+
} catch (e: any) {
|
|
1376
|
+
entry.remoteReachable = false;
|
|
1377
|
+
entry.remoteMainReachable = false;
|
|
1378
|
+
entry.publishRequired = true;
|
|
1379
|
+
const details = truncateValidationOutput(e?.stderr || e?.message || String(e));
|
|
1380
|
+
entry.error = `Submodule remote main reachability check failed for origin/main: ${details}`;
|
|
1381
|
+
if (options.allowAutoPublishSubmoduleMainCommits === true && entry.localReachable === true) {
|
|
1382
|
+
entry.autoPublishAllowed = true;
|
|
1383
|
+
entry.autoPublishAttempted = true;
|
|
1384
|
+
try {
|
|
1385
|
+
const publish = await publishCommitToRemoteMain(submodulePath, gitlink.commit, 'main');
|
|
1386
|
+
entry.autoPublishRefspec = publish.refspec;
|
|
1387
|
+
entry.publishStdout = truncateValidationOutput(publish.stdout);
|
|
1388
|
+
entry.publishStderr = truncateValidationOutput(publish.stderr);
|
|
1389
|
+
entry.autoPublishSucceeded = true;
|
|
1390
|
+
await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, 'main');
|
|
1391
|
+
entry.fetchedFromOrigin = true;
|
|
1392
|
+
entry.remoteReachable = true;
|
|
1393
|
+
entry.remoteMainReachable = true;
|
|
1394
|
+
entry.autoPublishVerified = true;
|
|
1395
|
+
entry.publishRequired = false;
|
|
1396
|
+
entry.reachable = true;
|
|
1397
|
+
entry.error = undefined;
|
|
1398
|
+
} catch (publishError: any) {
|
|
1399
|
+
entry.autoPublishSucceeded = false;
|
|
1400
|
+
entry.autoPublishVerified = false;
|
|
1401
|
+
const publishDetails = truncateValidationOutput(publishError?.stderr || publishError?.message || String(publishError));
|
|
1402
|
+
entry.error = `Submodule auto-publish to origin/main failed or could not be verified: ${publishDetails}`;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1342
1406
|
} catch (e: any) {
|
|
1343
1407
|
entry.remoteReachable = false;
|
|
1344
1408
|
entry.remoteMainReachable = false;
|
|
@@ -1360,6 +1424,8 @@ async function runMeshRefineSubmoduleReachabilityGate(
|
|
|
1360
1424
|
unreachable: unreachable.map(entry => ({ ...entry, publishRequired: entry.publishRequired !== false })),
|
|
1361
1425
|
entries: entries.map(entry => entry.reachable ? entry : { ...entry, publishRequired: entry.publishRequired !== false }),
|
|
1362
1426
|
durationMs: Date.now() - startedAt,
|
|
1427
|
+
autoPublishAllowed: options.allowAutoPublishSubmoduleMainCommits === true,
|
|
1428
|
+
autoPublishPolicySource: options.autoPublishPolicySource,
|
|
1363
1429
|
};
|
|
1364
1430
|
} catch (e: any) {
|
|
1365
1431
|
const unreachable = entries.filter(entry => !entry.reachable).map(entry => ({ ...entry, publishRequired: true }));
|
|
@@ -1369,6 +1435,8 @@ async function runMeshRefineSubmoduleReachabilityGate(
|
|
|
1369
1435
|
unreachable,
|
|
1370
1436
|
entries: entries.map(entry => entry.reachable ? entry : { ...entry, publishRequired: true }),
|
|
1371
1437
|
durationMs: Date.now() - startedAt,
|
|
1438
|
+
autoPublishAllowed: options.allowAutoPublishSubmoduleMainCommits === true,
|
|
1439
|
+
autoPublishPolicySource: options.autoPublishPolicySource,
|
|
1372
1440
|
error: truncateValidationOutput(e?.message || String(e)),
|
|
1373
1441
|
};
|
|
1374
1442
|
}
|
|
@@ -2753,13 +2821,38 @@ export class DaemonCommandRouter {
|
|
|
2753
2821
|
}
|
|
2754
2822
|
|
|
2755
2823
|
const submoduleReachabilityStarted = Date.now();
|
|
2756
|
-
const
|
|
2824
|
+
const autoPublishSubmoduleMainCommits = resolveRefineryAutoPublishSubmoduleMainCommits(mesh, node.workspace);
|
|
2825
|
+
const submoduleReachability = await runMeshRefineSubmoduleReachabilityGate(repoRoot, patchEquivalence.mergedTree || branchHead, {
|
|
2826
|
+
allowAutoPublishSubmoduleMainCommits: autoPublishSubmoduleMainCommits.enabled,
|
|
2827
|
+
autoPublishPolicySource: autoPublishSubmoduleMainCommits.source,
|
|
2828
|
+
});
|
|
2757
2829
|
recordMeshRefineStage(refineStages, 'submodule_reachability', submoduleReachability.status, submoduleReachabilityStarted, {
|
|
2758
2830
|
checked: submoduleReachability.checked,
|
|
2831
|
+
autoPublishAllowed: submoduleReachability.autoPublishAllowed,
|
|
2832
|
+
autoPublishPolicySource: submoduleReachability.autoPublishPolicySource,
|
|
2833
|
+
autoPublished: submoduleReachability.entries
|
|
2834
|
+
.filter(entry => entry.autoPublishAttempted)
|
|
2835
|
+
.map(entry => ({
|
|
2836
|
+
path: entry.path,
|
|
2837
|
+
commit: entry.commit,
|
|
2838
|
+
remote: entry.remote,
|
|
2839
|
+
remoteUrl: entry.remoteUrl,
|
|
2840
|
+
remoteMainBranch: entry.remoteMainBranch,
|
|
2841
|
+
refspec: entry.autoPublishRefspec,
|
|
2842
|
+
succeeded: entry.autoPublishSucceeded,
|
|
2843
|
+
verified: entry.autoPublishVerified,
|
|
2844
|
+
remoteMainReachable: entry.remoteMainReachable,
|
|
2845
|
+
error: entry.error,
|
|
2846
|
+
})),
|
|
2759
2847
|
unreachable: submoduleReachability.unreachable.map(entry => ({
|
|
2760
2848
|
path: entry.path,
|
|
2761
2849
|
commit: entry.commit,
|
|
2762
2850
|
publishRequired: entry.publishRequired === true,
|
|
2851
|
+
autoPublishAllowed: entry.autoPublishAllowed,
|
|
2852
|
+
autoPublishAttempted: entry.autoPublishAttempted,
|
|
2853
|
+
autoPublishSucceeded: entry.autoPublishSucceeded,
|
|
2854
|
+
autoPublishVerified: entry.autoPublishVerified,
|
|
2855
|
+
autoPublishRefspec: entry.autoPublishRefspec,
|
|
2763
2856
|
remote: entry.remote,
|
|
2764
2857
|
remoteUrl: entry.remoteUrl,
|
|
2765
2858
|
remoteReachable: entry.remoteReachable,
|
|
@@ -2793,6 +2886,11 @@ export class DaemonCommandRouter {
|
|
|
2793
2886
|
remoteReachable: entry.remoteReachable,
|
|
2794
2887
|
remoteMainBranch: entry.remoteMainBranch,
|
|
2795
2888
|
remoteMainReachable: entry.remoteMainReachable,
|
|
2889
|
+
autoPublishAllowed: entry.autoPublishAllowed,
|
|
2890
|
+
autoPublishAttempted: entry.autoPublishAttempted,
|
|
2891
|
+
autoPublishSucceeded: entry.autoPublishSucceeded,
|
|
2892
|
+
autoPublishVerified: entry.autoPublishVerified,
|
|
2893
|
+
autoPublishRefspec: entry.autoPublishRefspec,
|
|
2796
2894
|
error: entry.error,
|
|
2797
2895
|
})),
|
|
2798
2896
|
branch,
|
|
@@ -2870,7 +2968,7 @@ export class DaemonCommandRouter {
|
|
|
2870
2968
|
appendLedgerEntry(meshId, {
|
|
2871
2969
|
kind: 'node_removed',
|
|
2872
2970
|
nodeId,
|
|
2873
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence },
|
|
2971
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence, submoduleReachability },
|
|
2874
2972
|
});
|
|
2875
2973
|
recordMeshRefineStage(refineStages, 'ledger', 'passed', ledgerStarted);
|
|
2876
2974
|
} catch (e: any) {
|
|
@@ -2900,6 +2998,7 @@ export class DaemonCommandRouter {
|
|
|
2900
2998
|
removeResult,
|
|
2901
2999
|
validationSummary,
|
|
2902
3000
|
patchEquivalence,
|
|
3001
|
+
submoduleReachability,
|
|
2903
3002
|
mergeResult,
|
|
2904
3003
|
refineStages,
|
|
2905
3004
|
...(ledgerError ? { ledgerError } : {}),
|
|
@@ -2915,6 +3014,7 @@ export class DaemonCommandRouter {
|
|
|
2915
3014
|
removeResult,
|
|
2916
3015
|
validationSummary,
|
|
2917
3016
|
patchEquivalence,
|
|
3017
|
+
submoduleReachability,
|
|
2918
3018
|
mergeResult,
|
|
2919
3019
|
refineStages,
|
|
2920
3020
|
...(ledgerError ? { ledgerError } : {}),
|
|
@@ -87,6 +87,7 @@ function mergeMeshPolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMe
|
|
|
87
87
|
}
|
|
88
88
|
const maxParallelTasks = Number(policy.maxParallelTasks);
|
|
89
89
|
policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
|
|
90
|
+
policy.allowAutoPublishSubmoduleMainCommits = policy.allowAutoPublishSubmoduleMainCommits === true;
|
|
90
91
|
if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
|
|
91
92
|
policy.sessionCleanupOnNodeRemove = 'preserve';
|
|
92
93
|
}
|
|
@@ -127,6 +127,9 @@ function buildPolicySection(policy: RepoMeshPolicy): string {
|
|
|
127
127
|
if (policy.requirePreTaskCheckpoint) rules.push('- Create a git checkpoint **before** starting each task');
|
|
128
128
|
if (policy.requirePostTaskCheckpoint) rules.push('- Create a git checkpoint **after** each task completes');
|
|
129
129
|
if (policy.requireApprovalForPush) rules.push('- **Ask for user approval** before pushing to remote');
|
|
130
|
+
if (policy.allowAutoPublishSubmoduleMainCommits) {
|
|
131
|
+
rules.push('- Refinery may auto-publish unreachable submodule gitlink commits to submodule origin/main with non-force pushes after validation and patch-equivalence pass');
|
|
132
|
+
}
|
|
130
133
|
if (policy.requireApprovalForDestructiveGit) rules.push('- **Ask for user approval** before destructive git operations (force push, reset, etc.)');
|
|
131
134
|
|
|
132
135
|
const dirtyBehavior = {
|
|
@@ -182,7 +185,7 @@ const WORKFLOW_SECTION = `## Orchestration Workflow
|
|
|
182
185
|
4. **Monitor** — Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
|
|
183
186
|
5. **Verify** — When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
184
187
|
6. **Checkpoint** — Call \`mesh_checkpoint\` to save the work.
|
|
185
|
-
7. **Converge branches** — Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review
|
|
188
|
+
7. **Converge branches** — Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`; unless \`allowAutoPublishSubmoduleMainCommits\` is explicitly enabled and Refinery reports successful non-force publish plus post-publish verification, ask the user for explicit approval to push/publish the unreachable submodule commit(s) to submodule main, then rerun \`mesh_refine_node\`. Do not merge the root branch until the submodule commit(s) are reachable from submodule origin/main. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
|
|
186
189
|
8. **Clean up** — Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
|
|
187
190
|
9. **Report** — Summarize what was done, what changed, any issues, and the branch convergence state.
|
|
188
191
|
|
|
@@ -221,6 +224,6 @@ function buildRulesSection(coordinatorCliType?: string): string {
|
|
|
221
224
|
- **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
|
|
222
225
|
- **Do not strand completed branches.** A checkpointed or clean feature/worktree branch is not done by itself. Merge/refine it to the mesh default branch, fast-forward obvious clean behind-only branches with \`mesh_fast_forward_node\`, or explicitly report one of \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\` with the next action.
|
|
223
226
|
- **Keep Refinery validation project-configurable.** \`mesh_refine_node\` must execute validation from repo mesh/refine config (for example \`.adhdev/refine.{json,yaml,yml}\`, \`.adhdev/repo-mesh-refine.*\`, or \`repo-mesh.refine.*\`). Heuristics are suggestions/scaffolding only, not the execution path.
|
|
224
|
-
- **Treat submodule main reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote main branch. Do not treat feature-branch reachability as complete, retry validation blindly, or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit to submodule main, then rerun \`mesh_refine_node
|
|
227
|
+
- **Treat submodule main reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote main branch. Do not treat feature-branch reachability as complete, retry validation blindly, or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit to submodule main, then rerun \`mesh_refine_node\`, unless the mesh or repo refine config explicitly enabled \`allowAutoPublishSubmoduleMainCommits\` and Refinery reports exact path/commit/remote/branch evidence with post-publish verification.
|
|
225
228
|
- **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
|
|
226
229
|
}
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -914,7 +914,8 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
914
914
|
const providerSessionId = readNonEmptyString(args.metadataEvent.providerSessionId) || undefined;
|
|
915
915
|
const finalSummary = readNonEmptyString(args.metadataEvent.finalSummary) || undefined;
|
|
916
916
|
const workerResult = readWorkerResultMetadata(args.metadataEvent);
|
|
917
|
-
const
|
|
917
|
+
const hasCompletionEvidence = !!finalSummary || !!workerResult;
|
|
918
|
+
const completedTask = sessionId && hasCompletionEvidence
|
|
918
919
|
? updateSessionTaskStatus(args.meshId, sessionId, 'completed')
|
|
919
920
|
: null;
|
|
920
921
|
if (completedTask) {
|
|
@@ -18,6 +18,13 @@ export interface RepoMeshRefineValidationCommandConfig {
|
|
|
18
18
|
|
|
19
19
|
export interface RepoMeshRefineConfig {
|
|
20
20
|
version: 1;
|
|
21
|
+
/**
|
|
22
|
+
* Narrow Refinery opt-in for monorepos with submodule gitlinks.
|
|
23
|
+
* When true, Refinery may non-force publish unreachable submodule gitlink
|
|
24
|
+
* commits to the submodule remote main branch after validation and
|
|
25
|
+
* patch-equivalence pass, then verify remote-main reachability.
|
|
26
|
+
*/
|
|
27
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
21
28
|
validation?: {
|
|
22
29
|
required?: boolean;
|
|
23
30
|
commands?: RepoMeshRefineValidationCommandConfig[];
|
|
@@ -73,6 +80,11 @@ export const MESH_REFINE_CONFIG_SCHEMA = {
|
|
|
73
80
|
required: ['version'],
|
|
74
81
|
properties: {
|
|
75
82
|
version: { const: 1 },
|
|
83
|
+
allowAutoPublishSubmoduleMainCommits: {
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
default: false,
|
|
86
|
+
description: 'When true, Refinery may non-force publish submodule gitlink commits referenced by the refined root tree to each submodule origin/main after validation and patch-equivalence pass, then verify reachability.',
|
|
87
|
+
},
|
|
76
88
|
validation: {
|
|
77
89
|
type: 'object',
|
|
78
90
|
additionalProperties: false,
|
|
@@ -179,6 +191,9 @@ export function validateMeshRefineConfig(config: unknown, source = 'inline'): {
|
|
|
179
191
|
|
|
180
192
|
if (!isRecord(config)) return { valid: false, errors: ['config must be an object'], commands, rejectedCommands };
|
|
181
193
|
if (config.version !== 1) errors.push('version must be 1');
|
|
194
|
+
if (config.allowAutoPublishSubmoduleMainCommits !== undefined && typeof config.allowAutoPublishSubmoduleMainCommits !== 'boolean') {
|
|
195
|
+
errors.push('allowAutoPublishSubmoduleMainCommits must be a boolean when provided');
|
|
196
|
+
}
|
|
182
197
|
const validation = config.validation;
|
|
183
198
|
if (validation !== undefined && !isRecord(validation)) errors.push('validation must be an object');
|
|
184
199
|
const rawCommands = isRecord(validation) ? validation.commands : undefined;
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -94,6 +94,13 @@ export interface RepoMeshPolicy {
|
|
|
94
94
|
requirePreTaskCheckpoint: boolean;
|
|
95
95
|
requirePostTaskCheckpoint: boolean;
|
|
96
96
|
requireApprovalForPush: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Narrow Refinery opt-in: when validation and patch-equivalence have passed,
|
|
99
|
+
* allow Refinery to publish submodule gitlink commits to each submodule's
|
|
100
|
+
* configured remote main branch with a non-force push, then verify reachability.
|
|
101
|
+
* Defaults to false; root branch pushes/merges are not affected.
|
|
102
|
+
*/
|
|
103
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
97
104
|
requireApprovalForDestructiveGit: boolean;
|
|
98
105
|
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
99
106
|
maxParallelTasks: number;
|
|
@@ -158,6 +165,7 @@ export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
|
158
165
|
requirePreTaskCheckpoint: false,
|
|
159
166
|
requirePostTaskCheckpoint: true,
|
|
160
167
|
requireApprovalForPush: true,
|
|
168
|
+
allowAutoPublishSubmoduleMainCommits: false,
|
|
161
169
|
requireApprovalForDestructiveGit: true,
|
|
162
170
|
dirtyWorkspaceBehavior: 'warn',
|
|
163
171
|
maxParallelTasks: 2,
|