weir-harness 1.2.2 → 2.0.0
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/cordis.patch.yml +7 -18
- package/lib/client.file-lease-badge.js +307 -0
- package/lib/client.js +20 -30
- package/lib/client.settings-page.js +10 -34
- package/package.json +2 -2
- package/src/capabilities/store/checked-contract.d.ts +2 -2
- package/src/capabilities/store/fs-adapter.js +2 -3
- package/src/context-guard/index.js +2 -2
- package/src/delegate/supervision-mount.js +5 -5
- package/src/delegate/tool.js +2 -10
- package/src/file-lease/client.js +1066 -0
- package/src/file-lease/index.js +30 -0
- package/src/file-lease/lineage.js +207 -0
- package/src/file-lease/node-contracts.d.ts +42 -0
- package/src/file-lease/plugin.js +789 -0
- package/src/file-lease/singleton.js +155 -0
- package/src/file-lease/spawn.js +62 -0
- package/src/file-lease/tools.js +459 -0
- package/src/file-lease/view.js +144 -0
- package/src/hashline-edit/index.js +76 -42
- package/src/hashline-edit/write-tool.js +90 -0
- package/src/intent-gate/matcher.js +27 -0
- package/src/lease-broker/entry.js +98 -0
- package/src/lease-broker/node-contracts.d.ts +63 -0
- package/src/lease-broker/protocol.js +236 -0
- package/src/lease-broker/server.js +399 -0
- package/src/lease-broker/state.js +425 -0
- package/src/lsp/admin.js +24 -13
- package/src/lsp/index.js +5 -2
- package/src/lsp/registry.js +83 -17
- package/src/lsp/tools.js +137 -27
- package/src/notify/commands.js +15 -2
- package/src/notify/index.js +48 -10
- package/src/notify/web-channel.js +26 -3
- package/src/process/index.js +11 -0
- package/src/settings/index.js +2 -44
- package/src/settings/sections.js +29 -28
- package/src/shared/audit.js +6 -1
- package/src/todo-driver/index.js +1 -6
- package/src/worktree/cards.js +6 -21
- package/src/worktree/lane-core.js +1 -19
- package/src/worktree/lane-workflows.js +11 -32
- package/lib/client.edit-lock-maintenance.js +0 -592
- package/lib/client.edit-lock-panel.js +0 -405
- package/src/edit-lock/adapter.js +0 -57
- package/src/edit-lock/admin-ledger.js +0 -149
- package/src/edit-lock/admin-recovery.js +0 -151
- package/src/edit-lock/admission.js +0 -82
- package/src/edit-lock/binding-attestation.js +0 -29
- package/src/edit-lock/binding-guidance.js +0 -72
- package/src/edit-lock/binding-machine.js +0 -312
- package/src/edit-lock/call-context.js +0 -65
- package/src/edit-lock/derived-facts.js +0 -67
- package/src/edit-lock/domain.js +0 -165
- package/src/edit-lock/domains.js +0 -170
- package/src/edit-lock/host.js +0 -149
- package/src/edit-lock/incarnations.js +0 -150
- package/src/edit-lock/index.js +0 -1303
- package/src/edit-lock/inspect.js +0 -219
- package/src/edit-lock/lifecycle.js +0 -579
- package/src/edit-lock/limits.js +0 -61
- package/src/edit-lock/maintenance.js +0 -310
- package/src/edit-lock/manager.js +0 -875
- package/src/edit-lock/negotiation.js +0 -132
- package/src/edit-lock/operation-history.js +0 -214
- package/src/edit-lock/peer-client.js +0 -86
- package/src/edit-lock/peer-transport.js +0 -76
- package/src/edit-lock/peer.js +0 -87
- package/src/edit-lock/publisher.js +0 -191
- package/src/edit-lock/read-authority.js +0 -101
- package/src/edit-lock/recovery.js +0 -139
- package/src/edit-lock/remote.js +0 -362
- package/src/edit-lock/request-binding.js +0 -42
- package/src/edit-lock/request-data.js +0 -40
- package/src/edit-lock/reservation.js +0 -301
- package/src/edit-lock/reserved-runtime.js +0 -45
- package/src/edit-lock/resource-identity.js +0 -130
- package/src/edit-lock/runtime.js +0 -145
- package/src/edit-lock/settle.js +0 -144
- package/src/edit-lock/snapshot.js +0 -175
- package/src/edit-lock/stale-sweep.js +0 -120
- package/src/edit-lock/state.js +0 -462
- package/src/edit-lock/store.js +0 -327
- package/src/edit-lock/tool-scope.js +0 -29
- package/src/edit-lock/view.js +0 -170
- package/src/edit-lock/write-scopes.js +0 -48
- package/src/edit-lock/write-tool.js +0 -45
- package/src/shared/delegate-edit-lifecycle.js +0 -36
- package/src/worktree/authority.js +0 -56
package/src/todo-driver/index.js
CHANGED
|
@@ -68,9 +68,6 @@ function apply(ctx, config = {}) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// Edit Lock (when enabled) owns session interruption: only its trusted
|
|
72
|
-
// resume re-arms editing work, never an ordinary user message or retry.
|
|
73
|
-
const editLockBlocks = (agent) => ctx.get?.('weirEditLock')?.blocksContinuation?.(agent) === true
|
|
74
71
|
const compactionBlocks = (agent) => agent && ctx.get?.('weirCompactionCoordinator')?.blocksContinuation(agent) === true
|
|
75
72
|
/** Delayed followup continuation for the provider-error path. */
|
|
76
73
|
function scheduleRetry(session, delayMs) {
|
|
@@ -82,7 +79,6 @@ function apply(ctx, config = {}) {
|
|
|
82
79
|
if (remaining.length === 0) return
|
|
83
80
|
if (!stateOf(session.id).armed) return
|
|
84
81
|
if (jobsRunningOf(agent.session)) return
|
|
85
|
-
if (editLockBlocks(agent)) return
|
|
86
82
|
if (compactionBlocks(agent)) return
|
|
87
83
|
injectOrWarn(ctx, `todo-driver: could not queue continuation for "${session.id}"`, () => {
|
|
88
84
|
agent.followup(userTextMessage(renderContinuation(remaining), 'weir-todo-driver'))
|
|
@@ -95,7 +91,7 @@ function apply(ctx, config = {}) {
|
|
|
95
91
|
|
|
96
92
|
// The sanctioned continuation boundary: steer and the turn runs on.
|
|
97
93
|
function continueAtBoundary({ agent, signal }) {
|
|
98
|
-
if (compactionBlocks(agent)
|
|
94
|
+
if (compactionBlocks(agent)) return
|
|
99
95
|
if (!opts.enabled) return
|
|
100
96
|
const state = stateOf(agent.id)
|
|
101
97
|
const remaining = remainingTodos(agent.session)
|
|
@@ -106,7 +102,6 @@ function apply(ctx, config = {}) {
|
|
|
106
102
|
error: providerErrorPending.get(agent.id),
|
|
107
103
|
})
|
|
108
104
|
if (decision.kind !== 'continue') return
|
|
109
|
-
if (editLockBlocks(agent)) return
|
|
110
105
|
injectOrWarn(ctx, `todo-driver: could not steer continuation for "${agent.id}"`, () => {
|
|
111
106
|
agent.steer(userTextMessage(renderContinuation(remaining), 'weir-todo-driver'))
|
|
112
107
|
})
|
package/src/worktree/cards.js
CHANGED
|
@@ -72,12 +72,6 @@ const COPY = {
|
|
|
72
72
|
abandonLine: (/** @type {string} */ id, /** @type {string} */ title, /** @type {string} */ state) => `Abandon lane \`${id}\` — ${title} (state ${state}).`,
|
|
73
73
|
unmergedLost: (/** @type {number} */ count, /** @type {string} */ branch) => `**${count} unmerged commit(s)** on \`${branch}\` will be lost if the branch is removed.`,
|
|
74
74
|
noUnmerged: (/** @type {string} */ branch) => `\`${branch}\` has no unmerged commits.`,
|
|
75
|
-
residueWarning: (/** @type {{ operations: number, locks: number }} */ residue) => {
|
|
76
|
-
const parts = []
|
|
77
|
-
if (residue.operations > 0) parts.push(`${residue.operations} unresolved Edit Lock operation(s)`)
|
|
78
|
-
if (residue.locks > 0) parts.push(`${residue.locks} Edit Lock lock(s)`)
|
|
79
|
-
return `⚠️ **Edit Lock residue**: this lane's session left ${parts.join(' and ')} in the Edit Lock authority. Locks are reclaimed automatically by the stale-lock sweep; unresolved publications settle via crash self-heal (dead process) or one-click recovery in the Edit Lock maintenance panel (Settings → Editing). This is a warning only — cleanup still proceeds.`
|
|
80
|
-
},
|
|
81
75
|
dirtyWarning: (/** @type {{ modified: number, untracked: number }} */ dirty) => {
|
|
82
76
|
const parts = []
|
|
83
77
|
if (dirty.modified > 0) parts.push(`${dirty.modified} modified`)
|
|
@@ -137,12 +131,6 @@ const COPY = {
|
|
|
137
131
|
abandonLine: (/** @type {string} */ id, /** @type {string} */ title, /** @type {string} */ state) => `放弃车道 \`${id}\` —— ${title}(状态 ${state})。`,
|
|
138
132
|
unmergedLost: (/** @type {number} */ count, /** @type {string} */ branch) => `删除分支将**永久丢失** \`${branch}\` 上的 **${count} 个未合并提交**。`,
|
|
139
133
|
noUnmerged: (/** @type {string} */ branch) => `\`${branch}\` 没有未合并的提交。`,
|
|
140
|
-
residueWarning: (/** @type {{ operations: number, locks: number }} */ residue) => {
|
|
141
|
-
const parts = []
|
|
142
|
-
if (residue.operations > 0) parts.push(`${residue.operations} 个未决 Edit Lock 操作`)
|
|
143
|
-
if (residue.locks > 0) parts.push(`${residue.locks} 把 Edit Lock 锁`)
|
|
144
|
-
return `⚠️ **Edit Lock 残留**:该车道的会话在 Edit Lock 权威中留有 ${parts.join(' 和 ')}。锁会由 stale-lock 自动清扫回收;未决发布会由崩溃自愈(进程死亡时)或 Edit Lock 维护面板(设置 → 编辑)的一键恢复结清。此仅为警示——清理仍会进行。`
|
|
145
|
-
},
|
|
146
134
|
dirtyWarning: (/** @type {{ modified: number, untracked: number }} */ dirty) => {
|
|
147
135
|
const parts = []
|
|
148
136
|
if (dirty.modified > 0) parts.push(`${dirty.modified} 个已修改`)
|
|
@@ -193,15 +181,14 @@ export function cardCopy(locale) {
|
|
|
193
181
|
].join('\n')
|
|
194
182
|
},
|
|
195
183
|
/**
|
|
196
|
-
* @param {any} lane @param {{ files: number, added: number, removed: number }} stat @param {string} [root]
|
|
184
|
+
* @param {any} lane @param {{ files: number, added: number, removed: number }} stat @param {string} [root]
|
|
197
185
|
* @param {{ modified: number, untracked: number } | null} [dirty] - dirty-content probe; disclosed as its own warning paragraph when non-zero
|
|
198
186
|
*/
|
|
199
|
-
cleanupDetail(lane, stat, root,
|
|
187
|
+
cleanupDetail(lane, stat, root, dirty = null) {
|
|
200
188
|
return [
|
|
201
189
|
`- ${copy.merged(lane.branch, lane.base.branch)} (${stat.files} ${copy.files}, +${stat.added} −${stat.removed}).`,
|
|
202
190
|
`- ${copy.worktree}: \`${displayPath(lane.path, root)}\``,
|
|
203
191
|
`- ${copy.scratch}`,
|
|
204
|
-
...(residue ? ['', copy.residueWarning(residue)] : []),
|
|
205
192
|
...(hasDirtyContent(dirty) ? ['', copy.dirtyWarning(/** @type {any} */ (dirty))] : []),
|
|
206
193
|
].join('\n')
|
|
207
194
|
},
|
|
@@ -216,11 +203,11 @@ export function cardCopy(locale) {
|
|
|
216
203
|
return copy.disputedReasons.noEvidence
|
|
217
204
|
},
|
|
218
205
|
/**
|
|
219
|
-
* @param {any} lane @param {number} unmerged @param {string} [root]
|
|
206
|
+
* @param {any} lane @param {number} unmerged @param {string} [root]
|
|
220
207
|
* @param {{ child: string, forced: boolean } | null} [reclaimed] - a binding this abandon already settled (design D1/D4), disclosed on the card
|
|
221
208
|
* @param {{ modified: number, untracked: number } | null} [dirty] - dirty-content probe; disclosed as its own warning paragraph when non-zero
|
|
222
209
|
*/
|
|
223
|
-
abandonDetail(lane, unmerged, root,
|
|
210
|
+
abandonDetail(lane, unmerged, root, reclaimed = null, dirty = null) {
|
|
224
211
|
return [
|
|
225
212
|
`- ${copy.abandonLine(lane.id, lane.title, lane.state)}`,
|
|
226
213
|
`- ${copy.worktree}: \`${displayPath(lane.path, root)}\``,
|
|
@@ -228,22 +215,20 @@ export function cardCopy(locale) {
|
|
|
228
215
|
// The unmerged-commit warning stays its own paragraph for emphasis.
|
|
229
216
|
unmerged > 0 ? copy.unmergedLost(unmerged, lane.branch) : copy.noUnmerged(lane.branch),
|
|
230
217
|
...(reclaimed ? ['', copy.reconciledNote(reclaimed.child, reclaimed.forced)] : []),
|
|
231
|
-
...(residue ? ['', copy.residueWarning(residue)] : []),
|
|
232
218
|
...(hasDirtyContent(dirty) ? ['', copy.dirtyWarning(/** @type {any} */ (dirty))] : []),
|
|
233
219
|
].join('\n')
|
|
234
220
|
},
|
|
235
221
|
/**
|
|
236
222
|
* The disputed-binding card body (design D4): lane facts, then the
|
|
237
223
|
* reconciliation outcome as the warning paragraph.
|
|
238
|
-
* @param {any} lane @param {string} child @param {{ childAlive?: boolean, ownerAlive?: boolean, terminalEvidence?: any } | null} verdict @param {string} [root]
|
|
224
|
+
* @param {any} lane @param {string} child @param {{ childAlive?: boolean, ownerAlive?: boolean, terminalEvidence?: any } | null} verdict @param {string} [root]
|
|
239
225
|
*/
|
|
240
|
-
abandonDisputedDetail(lane, child, verdict, root
|
|
226
|
+
abandonDisputedDetail(lane, child, verdict, root) {
|
|
241
227
|
return [
|
|
242
228
|
`- ${copy.abandonLine(lane.id, lane.title, lane.state)}`,
|
|
243
229
|
`- ${copy.worktree}: \`${displayPath(lane.path, root)}\``,
|
|
244
230
|
'',
|
|
245
231
|
copy.disputedWarning(child, this.disputedReason(verdict)),
|
|
246
|
-
...(residue ? ['', copy.residueWarning(residue)] : []),
|
|
247
232
|
].join('\n')
|
|
248
233
|
},
|
|
249
234
|
}
|
|
@@ -16,7 +16,6 @@ import { normalizeRoot, parseRepoConfig } from './rules.js'
|
|
|
16
16
|
import { reconcile } from './reconcile.js'
|
|
17
17
|
import { createLedger } from './ledger.js'
|
|
18
18
|
import { renderNotice, renderWatchExpired, renderWatchHit } from './prompts.js'
|
|
19
|
-
import { authorityResidue } from './authority.js'
|
|
20
19
|
|
|
21
20
|
export const CONFIG_FILE = '.config.json'
|
|
22
21
|
const LOG_TAIL_LINES = 40
|
|
@@ -74,7 +73,6 @@ export function pathKey(path, platform = process.platform) {
|
|
|
74
73
|
* @property {(handle: any) => void} [clearTimer]
|
|
75
74
|
* @property {number} [pid] - this process id (tests)
|
|
76
75
|
* @property {{ warn?: (message: string) => void }} [logger]
|
|
77
|
-
* @property {(root: string, sessionId: string | null | undefined) => ({ operations: number, locks: number } | null)} [authorityResidue] - read-only Edit Lock residue probe feeding the cleanup/abandon warning (tests); defaults to the bounded snapshot read in ./authority.js
|
|
78
76
|
* @property {(boundChild: string, ownerSession: string | null, context: { root: string }) => Promise<{ childAlive: boolean, ownerAlive: boolean, terminalEvidence: { source: string, detail: string } | null }>} [bindingLiveness] - read-only zombie-binding probe (design D2); absent = LANE_BUSY refusals stand unreconciled
|
|
79
77
|
*/
|
|
80
78
|
|
|
@@ -347,22 +345,6 @@ export function createLaneCore(deps) {
|
|
|
347
345
|
}
|
|
348
346
|
}
|
|
349
347
|
|
|
350
|
-
/**
|
|
351
|
-
* Best-effort Edit Lock authority-residue probe (design D5): does the
|
|
352
|
-
* lane's owner session still own unresolved operations or locks in the
|
|
353
|
-
* management domain? Warning input only — read-only, and a failed check
|
|
354
|
-
* is silent (never blocks the cleanup/abandon transition).
|
|
355
|
-
* @param {any} repo @param {any} lane @returns {{ operations: number, locks: number } | null}
|
|
356
|
-
*/
|
|
357
|
-
function residueOf(repo, lane) {
|
|
358
|
-
try {
|
|
359
|
-
return (deps.authorityResidue ?? authorityResidue)(repo.mainRoot, lane?.ownerSession) ?? null
|
|
360
|
-
} catch (error) {
|
|
361
|
-
deps.logger?.warn?.(`worktree: authority-residue check failed: ${/** @type {any} */ (error)?.message ?? error}`)
|
|
362
|
-
return null
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
348
|
/** @param {any} lane */
|
|
367
349
|
function result(lane, summary, extra = {}) {
|
|
368
350
|
return {
|
|
@@ -635,7 +617,7 @@ export function createLaneCore(deps) {
|
|
|
635
617
|
approveModeSafe, approveModeSourceSafe,
|
|
636
618
|
cwdOf, config, repoFor, repoCached,
|
|
637
619
|
armWatch, disarmWatch,
|
|
638
|
-
laneOf, apply, patchLane, commitBinding, notifyOwner,
|
|
620
|
+
laneOf, apply, patchLane, commitBinding, notifyOwner, result, readConfig, logFile,
|
|
639
621
|
refresh, reconcileNote, reconcileBinding, childSettled, check,
|
|
640
622
|
}
|
|
641
623
|
}
|
|
@@ -21,21 +21,9 @@ import { CONFIG_FILE, pathKey } from './lane-core.js'
|
|
|
21
21
|
|
|
22
22
|
const SETUP_TIMEOUT_MS = 600_000
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Model-facing English residue warning appended to cleanup/abandon tool
|
|
26
|
-
* results (the human-facing card copy localizes its own in cards.js).
|
|
27
|
-
* @param {{ operations: number, locks: number }} residue
|
|
28
|
-
*/
|
|
29
|
-
function residueNote(residue) {
|
|
30
|
-
const parts = []
|
|
31
|
-
if (residue.operations > 0) parts.push(`${residue.operations} unresolved Edit Lock operation(s)`)
|
|
32
|
-
if (residue.locks > 0) parts.push(`${residue.locks} Edit Lock lock(s)`)
|
|
33
|
-
return `warning: the lane's session left ${parts.join(' and ')} in the management authority; locks are reclaimed by the stale-lock sweep, unresolved publications settle via crash self-heal (dead process) or the Edit Lock maintenance panel's one-click recovery`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
24
|
/** @param {ReturnType<typeof import('./lane-core.js').createLaneCore>} core */
|
|
37
25
|
export function createLaneWorkflows(core) {
|
|
38
|
-
const { deps, git, now, pid, asking, approveModeSafe, cwdOf, config, repoFor, laneOf, apply, patchLane, commitBinding, notifyOwner,
|
|
26
|
+
const { deps, git, now, pid, asking, approveModeSafe, cwdOf, config, repoFor, laneOf, apply, patchLane, commitBinding, notifyOwner, result, readConfig, logFile, refresh, reconcileNote, reconcileBinding, armWatch, disarmWatch } = core
|
|
39
27
|
|
|
40
28
|
// ─── open / setup ─────────────────────────────────────────────────────
|
|
41
29
|
|
|
@@ -263,7 +251,7 @@ export function createLaneWorkflows(core) {
|
|
|
263
251
|
id: 'abandon',
|
|
264
252
|
header: copy.abandonHeader,
|
|
265
253
|
question: copy.abandonQuestion(lane.title),
|
|
266
|
-
detail: copy.abandonDisputedDetail(lane, lane.boundChild, reconciliation?.verdict ?? null, repo.mainRoot
|
|
254
|
+
detail: copy.abandonDisputedDetail(lane, lane.boundChild, reconciliation?.verdict ?? null, repo.mainRoot),
|
|
267
255
|
options: [
|
|
268
256
|
{ label: copy.forceReclaim, description: copy.forceReclaimDescription },
|
|
269
257
|
{ label: copy.cancel, description: copy.abandonDescriptions.cancel },
|
|
@@ -452,10 +440,9 @@ export function createLaneWorkflows(core) {
|
|
|
452
440
|
}
|
|
453
441
|
// Auto cleanup after the merge (design D3): auto-keep → mode worktree,
|
|
454
442
|
// auto-clean → mode all. The existing cleanup() body runs unchanged —
|
|
455
|
-
// scratch sync first, never --force on an auto path
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
// success (the user can clean up later).
|
|
443
|
+
// scratch sync first, never --force on an auto path. A blocked removal
|
|
444
|
+
// stays `landed` and is reported concisely with the typed --force retry
|
|
445
|
+
// instead of a false success (the user can clean up later).
|
|
459
446
|
const cleanupMode = approveMode === 'auto-keep' ? 'worktree' : 'all'
|
|
460
447
|
try {
|
|
461
448
|
const cleaned = await cleanup(session, laneId, /** @type {'worktree' | 'all'} */ (cleanupMode), { by: 'host', auto: true, approveMode })
|
|
@@ -488,7 +475,7 @@ export function createLaneWorkflows(core) {
|
|
|
488
475
|
id: 'cleanup',
|
|
489
476
|
header: copy.cleanupHeader,
|
|
490
477
|
question: copy.cleanupQuestion(lane.title),
|
|
491
|
-
detail: copy.cleanupDetail(lane, lane.land?.stat ?? { files: 0, added: 0, removed: 0 }, repo.mainRoot,
|
|
478
|
+
detail: copy.cleanupDetail(lane, lane.land?.stat ?? { files: 0, added: 0, removed: 0 }, repo.mainRoot, dirtyCard ? dirty : null),
|
|
492
479
|
options: [
|
|
493
480
|
{ label: copy.choices.keep, description: copy.cleanupDescriptions.keep },
|
|
494
481
|
{ label: copy.choices.worktree, description: dirtyCard ? copy.cleanupDescriptionsDirty.worktree : copy.cleanupDescriptions.worktree },
|
|
@@ -539,9 +526,8 @@ export function createLaneWorkflows(core) {
|
|
|
539
526
|
* Auto paths (design D3) reuse this exact body with `{ by: 'host', auto: true,
|
|
540
527
|
* approveMode }`: the scratch sync, the removal (forced ONLY behind the
|
|
541
528
|
* user's informed explicit choice — a disclosed card choice or a typed
|
|
542
|
-
* `--force`, never an auto/model path)
|
|
543
|
-
*
|
|
544
|
-
* auto marker.
|
|
529
|
+
* `--force`, never an auto/model path) and the blocked-removal semantics are
|
|
530
|
+
* identical; only the audit payload gains the auto marker.
|
|
545
531
|
* @param {any} session @param {string} laneId @param {'keep' | 'worktree' | 'all'} mode @param {{ by?: string, auto?: boolean, approveMode?: string, force?: boolean }} [options]
|
|
546
532
|
*/
|
|
547
533
|
async function cleanup(session, laneId, mode, options = {}) {
|
|
@@ -557,9 +543,6 @@ export function createLaneWorkflows(core) {
|
|
|
557
543
|
const kept = await apply(repo, laneId, { type: 'keep', by, patch: { cleanup: { mode: 'keep', at: now(), by } } })
|
|
558
544
|
return result(kept, `kept ${kept.path}`)
|
|
559
545
|
}
|
|
560
|
-
// Read-only residue probe BEFORE anything is removed (design D5): a
|
|
561
|
-
// warning only, never a gate, and it never mutates the authority.
|
|
562
|
-
const residue = residueOf(repo, lane)
|
|
563
546
|
const scratch = existsSync(lane.path) ? syncScratch(repo, lane) : null
|
|
564
547
|
// `force` reaches git only from an informed explicit user choice (a
|
|
565
548
|
// disclosed card choice via askCleanup, or the typed `--force`); auto and
|
|
@@ -591,7 +574,7 @@ export function createLaneWorkflows(core) {
|
|
|
591
574
|
? await patchLane(repo, laneId, () => ({ cleanup: record }))
|
|
592
575
|
: await apply(repo, laneId, { type: 'clean', by, patch: { cleanup: record } })
|
|
593
576
|
deps.audit('cleanup', { lane: laneId, mode, by, scratch, ...(force ? { forced: true } : {}), ...(options.auto === true ? { auto: true, approveMode: options.approveMode ?? null } : {}) }, repo.mainRoot, lane.ownerSession)
|
|
594
|
-
return result(next, `removed ${lane.path}${branchNote}${scratch ? `; scratch copied to ${scratch}` : ''}
|
|
577
|
+
return result(next, `removed ${lane.path}${branchNote}${scratch ? `; scratch copied to ${scratch}` : ''}`)
|
|
595
578
|
}
|
|
596
579
|
|
|
597
580
|
/**
|
|
@@ -651,7 +634,7 @@ export function createLaneWorkflows(core) {
|
|
|
651
634
|
id: 'abandon',
|
|
652
635
|
header: copy.abandonHeader,
|
|
653
636
|
question: copy.abandonQuestion(lane.title),
|
|
654
|
-
detail: copy.abandonDetail(lane, unmerged, repo.mainRoot,
|
|
637
|
+
detail: copy.abandonDetail(lane, unmerged, repo.mainRoot, reconciliation?.settled ? { child: reconciliation.childId, forced: reconciliation.forced } : null, dirtyCard ? dirty : null),
|
|
655
638
|
options: [
|
|
656
639
|
{ label: copy.choices.keep, description: copy.abandonDescriptions.keep },
|
|
657
640
|
{ label: copy.choices.worktree, description: dirtyCard ? copy.abandonDescriptionsDirty.worktree : copy.abandonDescriptions.worktree },
|
|
@@ -678,10 +661,6 @@ export function createLaneWorkflows(core) {
|
|
|
678
661
|
const auditExtra = { ...(auto ? { auto: true, approveMode } : {}), ...(force ? { forced: true } : {}) }
|
|
679
662
|
const abandoned = await apply(repo, laneId, { type: 'abandon', by: auto ? 'host' : 'user', reason: auto ? `abandoned automatically (${approveMode} mode)` : 'abandoned by the user', audit: Object.keys(auditExtra).length > 0 ? auditExtra : undefined, patch: { cleanup: { mode, at: now(), by: auto ? 'host' : 'user' } } })
|
|
680
663
|
if (mode === 'keep') return result(abandoned, `abandoned; ${abandoned.path} and ${abandoned.branch} are kept`)
|
|
681
|
-
// Read-only residue probe BEFORE anything is removed (design D5): a
|
|
682
|
-
// warning only, never a gate, and it never mutates the authority. The
|
|
683
|
-
// confirmation card already carried the warning when there was one.
|
|
684
|
-
const residue = residueOf(repo, lane)
|
|
685
664
|
const scratch = existsSync(lane.path) ? syncScratch(repo, lane) : null
|
|
686
665
|
if (existsSync(lane.path)) {
|
|
687
666
|
try {
|
|
@@ -701,7 +680,7 @@ export function createLaneWorkflows(core) {
|
|
|
701
680
|
branchNote = `; deleted ${lane.branch}${unmerged > 0 ? ` (${unmerged} unmerged commit(s) discarded)` : ''}`
|
|
702
681
|
}
|
|
703
682
|
const final = await patchLane(repo, laneId, (current) => ({ cleanup: { ...current.cleanup, scratch } }))
|
|
704
|
-
return result(final, `abandoned; removed ${lane.path}${branchNote}
|
|
683
|
+
return result(final, `abandoned; removed ${lane.path}${branchNote}`)
|
|
705
684
|
}
|
|
706
685
|
|
|
707
686
|
// ─── watches (lane state subscriptions) ────────────────────────────────
|