@mgiles/perk 1.1.0 → 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/README.md +68 -44
- package/extension/adapters/planAdapterPlannotator.ts +27 -41
- package/extension/adapters/planAdapterTombell.ts +15 -28
- package/extension/adapters/todoAdapterJuicesharp.ts +10 -13
- package/extension/checkpoints/checkpoints.ts +19 -12
- package/extension/doors/address.ts +4 -4
- package/extension/doors/askUser.ts +12 -8
- package/extension/doors/ciExecutor.ts +21 -14
- package/extension/doors/hunkHandoff.ts +202 -0
- package/extension/doors/land.ts +31 -9
- package/extension/doors/learn.ts +2 -2
- package/extension/doors/learnFactory.ts +144 -0
- package/extension/doors/plannotatorHandoff.ts +509 -0
- package/extension/doors/prReview.ts +4 -4
- package/extension/doors/prReviewBrowser.ts +341 -0
- package/extension/doors/prReviewTerminal.ts +267 -0
- package/extension/doors/selfcheck.ts +238 -5
- package/extension/doors/submit.ts +20 -0
- package/extension/doors/submitPrReview.ts +408 -0
- package/extension/factories/objective.ts +15 -5
- package/extension/factories/objectiveAuthor.ts +15 -32
- package/extension/factories/objectiveDraft.ts +1 -1
- package/extension/factories/objectivePlan.ts +12 -10
- package/extension/factories/objectiveSave.ts +2 -2
- package/extension/factories/planMode.ts +22 -40
- package/extension/factories/planReview.ts +213 -191
- package/extension/factories/planSave.ts +7 -7
- package/extension/index.ts +83 -25
- package/extension/substrate/bindingDelivery.ts +32 -10
- package/extension/substrate/bindings.ts +4 -2
- package/extension/substrate/cache.ts +34 -7
- package/extension/substrate/clipboard.ts +81 -0
- package/extension/substrate/config.ts +88 -65
- package/extension/substrate/git.ts +43 -0
- package/extension/substrate/paths.ts +1 -1
- package/extension/substrate/prompts.ts +2 -2
- package/extension/substrate/providers.ts +62 -8
- package/extension/substrate/sessionPointers.ts +35 -6
- package/extension/substrate/structuredOutput.ts +3 -1
- package/extension/substrate/terminalLaunch.ts +178 -0
- package/extension/substrate/toolGating.ts +330 -79
- package/extension/substrate/toolParams.ts +7 -0
- package/extension/substrate/workflowState.ts +54 -2
- package/extension/surfaces/footerProvider.ts +8 -4
- package/extension/surfaces/surfaces.ts +330 -12
- package/extension/vendor/btw/btw.ts +10 -0
- package/extension/worker/readOnlySession.ts +19 -6
- package/extension/worker/worker.ts +77 -7
- package/extension/workerMain.ts +12 -13
- package/package.json +3 -3
- package/prompts/_fixtures/live.yaml +117 -2
- package/prompts/contexts/adapters/juicesharp-todo.md +7 -0
- package/prompts/contexts/adapters/plannotator-objective.md +7 -0
- package/prompts/contexts/adapters/plannotator-plan.md +6 -0
- package/prompts/contexts/adapters/tombell-plan.md +17 -0
- package/prompts/contexts/objective-authoring.md +20 -0
- package/prompts/contexts/plan-authoring.md +24 -0
- package/prompts/contexts/read-only.md +10 -0
- package/prompts/stages/conflict-resolution.md +1 -1
- package/prompts/stages/learn-code.md +1 -1
- package/prompts/stages/learn-docs.md +2 -2
- package/prompts/stages/learn-orchestrate.md +1 -1
- package/prompts/stages/objective-author/adopt.md +1 -1
- package/prompts/stages/objective-author/file.md +1 -1
- package/prompts/stages/objective-plan/guidance.md +1 -1
- package/prompts/stages/objective-plan/seed.md +1 -1
- package/prompts/stages/objective-reconcile.md +1 -1
- package/prompts/stages/objective-replan.md +1 -1
- package/prompts/stages/plan-from/adopt.md +2 -2
- package/prompts/stages/plan-from/file.md +2 -2
- package/prompts/stages/pr-review-browser/active.md +11 -0
- package/prompts/stages/pr-review-browser/foreign.md +11 -0
- package/prompts/stages/pr-review-terminal/active.md +12 -0
- package/prompts/stages/pr-review-terminal/foreign.md +13 -0
- package/prompts/stages/pr-review-terminal/local.md +4 -0
- package/prompts/stages/pr-review.md +1 -1
- package/prompts/stages/replan.md +2 -2
- package/prompts/stages/skills/create-from.md +1 -1
- package/prompts/stages/skills/create.md +1 -1
- package/prompts/stages/skills/refine.md +1 -1
- package/shared/README.md +22 -18
- package/shared/bindings.yaml +10 -2
- package/shared/contracts-history.md +24 -0
- package/shared/contracts.md +1442 -1787
- package/shared/providers.yaml +8 -1
- package/shared/registry.yaml +7 -8
- package/shared/schemas/inputs/review-submit-batch.schema.json +66 -0
- package/shared/schemas/outputs/pr-review-checkout.schema.json +69 -0
- package/shared/schemas/outputs/pr-review-cleanup.schema.json +54 -0
- package/shared/schemas/outputs/pr-review-submit.schema.json +64 -0
- package/extension/doors/learnCode.ts +0 -100
- package/extension/doors/learnDocs.ts +0 -100
- package/extension/doors/prReviewLocal.ts +0 -229
|
@@ -13,13 +13,26 @@
|
|
|
13
13
|
// the converged content actually *reached* the prompt. That closes perk's two-plane blind spot —
|
|
14
14
|
// doctor checks disk, selfcheck checks the prompt.
|
|
15
15
|
//
|
|
16
|
+
// The same report additionally carries a per-surface payload CENSUS — derived counts/chars for
|
|
17
|
+
// `appendSystemPrompt`, `contextFiles`, the skills catalog section, the active tool definitions,
|
|
18
|
+
// and perk-injected `custom_message` branch context. Report-only: the census never affects the
|
|
19
|
+
// ok/level verdict (contracts.md §8.7).
|
|
20
|
+
//
|
|
16
21
|
// Sensitivity: `getSystemPromptOptions()` exposes the full system-prompt construction inputs. This
|
|
17
|
-
// module logs ONLY derived
|
|
22
|
+
// module logs ONLY derived output — identifiers (paths, skill/tool names, source strings,
|
|
23
|
+
// customTypes) and counts/chars; never prompt/content text. See docs/learned/pi/.
|
|
18
24
|
|
|
19
25
|
import { existsSync, readFileSync } from "node:fs";
|
|
20
26
|
import { join } from "node:path";
|
|
21
|
-
import
|
|
27
|
+
import {
|
|
28
|
+
type ExtensionAPI,
|
|
29
|
+
formatSkillsForPrompt,
|
|
30
|
+
type Skill,
|
|
31
|
+
type ToolInfo,
|
|
32
|
+
} from "@earendil-works/pi-coding-agent";
|
|
33
|
+
import { BINDING_HEADER } from "../substrate/bindingDelivery.ts";
|
|
22
34
|
import { registerPerkCommand } from "../substrate/command.ts";
|
|
35
|
+
import { branchOf } from "../substrate/workflowState.ts";
|
|
23
36
|
import { report as reportTo } from "../surfaces/report.ts";
|
|
24
37
|
|
|
25
38
|
/** Project-scoped ambient routing index, relative to the repo root. */
|
|
@@ -28,10 +41,15 @@ export const AMBIENT_INDEX_REL_PATH = join(".pi", "APPEND_SYSTEM.md");
|
|
|
28
41
|
/** The managed `AGENTS.md` block marker (cross-plane: written by `perk init`, read here). */
|
|
29
42
|
export const MANAGED_AGENTS_MARKER = "<!-- BEGIN perk managed -->";
|
|
30
43
|
|
|
31
|
-
/** The
|
|
44
|
+
/** The slice of `BuildSystemPromptOptions` the verifier + census probe (all optional). */
|
|
32
45
|
export interface SystemPromptProbeInput {
|
|
46
|
+
customPrompt?: string;
|
|
33
47
|
appendSystemPrompt?: string;
|
|
34
48
|
contextFiles?: { path: string; content: string }[];
|
|
49
|
+
skills?: Skill[];
|
|
50
|
+
selectedTools?: string[];
|
|
51
|
+
toolSnippets?: Record<string, string>;
|
|
52
|
+
promptGuidelines?: string[];
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
/** Read the on-disk ambient routing index (`.pi/APPEND_SYSTEM.md`), or `null` if absent. */
|
|
@@ -130,6 +148,214 @@ export function buildSelfcheckReport(input: {
|
|
|
130
148
|
return { version, sharedOk, ambient, agents, ok, summary, level: ok ? "info" : "warning" };
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// The per-surface payload census (contracts.md §8.7). Pure builders over the same
|
|
153
|
+
// `getSystemPromptOptions()` slice plus the tool registry and the session branch. All counts are
|
|
154
|
+
// `string.length` char counts (rendered with a `c` suffix). Identifiers (paths, skill/tool names,
|
|
155
|
+
// source strings, customTypes) and counts/chars only — never prompt/content text.
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
/** Derived per-surface counts for the system-prompt construction inputs. */
|
|
159
|
+
export interface PromptCensus {
|
|
160
|
+
/** `customPrompt` length; `null` = pi's built-in default is in use (not measurable here). */
|
|
161
|
+
basePromptChars: number | null;
|
|
162
|
+
appendChars: number;
|
|
163
|
+
contextFiles: { count: number; totalChars: number; files: { path: string; chars: number }[] };
|
|
164
|
+
/** `hidden` = skills with `disableModelInvocation`; `promptSectionChars` is the exact
|
|
165
|
+
* system-prompt contribution (pi's own exported `formatSkillsForPrompt`). */
|
|
166
|
+
skills: { visible: number; hidden: number; promptSectionChars: number };
|
|
167
|
+
toolGuidelineChars: number;
|
|
168
|
+
toolSnippetChars: number;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Census the system-prompt surfaces from the probed options (undefined → zeros). */
|
|
172
|
+
export function promptCensus(options: SystemPromptProbeInput | undefined): PromptCensus {
|
|
173
|
+
const files = (options?.contextFiles ?? []).map((f) => ({
|
|
174
|
+
path: f.path,
|
|
175
|
+
chars: f.content.length,
|
|
176
|
+
}));
|
|
177
|
+
const skills = options?.skills ?? [];
|
|
178
|
+
const hidden = skills.filter((s) => s.disableModelInvocation).length;
|
|
179
|
+
let guidelineChars = 0;
|
|
180
|
+
for (const g of options?.promptGuidelines ?? []) guidelineChars += g.length;
|
|
181
|
+
let snippetChars = 0;
|
|
182
|
+
for (const s of Object.values(options?.toolSnippets ?? {})) snippetChars += s.length;
|
|
183
|
+
return {
|
|
184
|
+
basePromptChars: options?.customPrompt === undefined ? null : options.customPrompt.length,
|
|
185
|
+
appendChars: (options?.appendSystemPrompt ?? "").length,
|
|
186
|
+
contextFiles: {
|
|
187
|
+
count: files.length,
|
|
188
|
+
totalChars: files.reduce((sum, f) => sum + f.chars, 0),
|
|
189
|
+
files,
|
|
190
|
+
},
|
|
191
|
+
skills: {
|
|
192
|
+
visible: skills.length - hidden,
|
|
193
|
+
hidden,
|
|
194
|
+
// formatSkillsForPrompt filters disableModelInvocation skills itself, so passing the full
|
|
195
|
+
// list measures exactly the visible skills' prompt-section contribution.
|
|
196
|
+
promptSectionChars: formatSkillsForPrompt(skills).length,
|
|
197
|
+
},
|
|
198
|
+
toolGuidelineChars: guidelineChars,
|
|
199
|
+
toolSnippetChars: snippetChars,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Derived counts for the tool-definitions payload (active tools = the per-request payload). */
|
|
204
|
+
export interface ToolsCensus {
|
|
205
|
+
active: number;
|
|
206
|
+
all: number;
|
|
207
|
+
/** Sum of `JSON.stringify({name, description, parameters})` over ACTIVE tools. */
|
|
208
|
+
schemaChars: number;
|
|
209
|
+
/** Active tools grouped by `sourceInfo.source`, sorted by source string (stable output). */
|
|
210
|
+
bySource: { source: string; active: number; schemaChars: number }[];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Census the tool registry: active vs registered counts + per-source schema chars. */
|
|
214
|
+
export function toolsCensus(allTools: ToolInfo[], activeNames: string[]): ToolsCensus {
|
|
215
|
+
const activeSet = new Set(activeNames);
|
|
216
|
+
const bySource = new Map<string, { active: number; schemaChars: number }>();
|
|
217
|
+
let schemaChars = 0;
|
|
218
|
+
for (const tool of allTools) {
|
|
219
|
+
if (!activeSet.has(tool.name)) continue;
|
|
220
|
+
const chars = JSON.stringify({
|
|
221
|
+
name: tool.name,
|
|
222
|
+
description: tool.description,
|
|
223
|
+
parameters: tool.parameters,
|
|
224
|
+
}).length;
|
|
225
|
+
schemaChars += chars;
|
|
226
|
+
const row = bySource.get(tool.sourceInfo.source) ?? { active: 0, schemaChars: 0 };
|
|
227
|
+
row.active += 1;
|
|
228
|
+
row.schemaChars += chars;
|
|
229
|
+
bySource.set(tool.sourceInfo.source, row);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
active: activeNames.length,
|
|
233
|
+
all: allTools.length,
|
|
234
|
+
schemaChars,
|
|
235
|
+
bySource: [...bySource.entries()]
|
|
236
|
+
.map(([source, row]) => ({ source, ...row }))
|
|
237
|
+
.sort((a, b) => (a.source < b.source ? -1 : a.source > b.source ? 1 : 0)),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The structural slice of a session entry the branch census reads. Injected contexts persist as
|
|
243
|
+
* `type: "custom_message"` entries carrying `content` — distinct from `type: "custom"` state
|
|
244
|
+
* entries (workflow state, checkpoints), which the census must NOT count as context.
|
|
245
|
+
*/
|
|
246
|
+
export interface CensusBranchEntry {
|
|
247
|
+
type: string;
|
|
248
|
+
customType?: string;
|
|
249
|
+
content?: unknown;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Derived counts for perk-injected branch context (perk's customTypes share the `perk:` prefix). */
|
|
253
|
+
export interface BranchContextCensus {
|
|
254
|
+
entries: number;
|
|
255
|
+
/** `custom_message` entries whose customType starts with `perk:`, sorted by customType. */
|
|
256
|
+
perkContexts: { customType: string; copies: number; totalChars: number }[];
|
|
257
|
+
/** `custom_message` entries without the `perk:` prefix (borrowed packages). */
|
|
258
|
+
otherCustomMessages: { copies: number; totalChars: number };
|
|
259
|
+
/** Entries (any type) whose JSON form carries `BINDING_HEADER` — the cold↔warm dedup marker. */
|
|
260
|
+
bindingHeaderCopies: number;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Char count of a `custom_message` entry's content — string, `text`-part array, or JSON size. */
|
|
264
|
+
function contentChars(content: unknown): number {
|
|
265
|
+
if (content === undefined) return 0;
|
|
266
|
+
if (typeof content === "string") return content.length;
|
|
267
|
+
if (Array.isArray(content)) {
|
|
268
|
+
let sum = 0;
|
|
269
|
+
for (const part of content) {
|
|
270
|
+
if (typeof part !== "object" || part === null || Array.isArray(part)) continue;
|
|
271
|
+
const text = (part as { text?: unknown }).text;
|
|
272
|
+
if (typeof text === "string") sum += text.length;
|
|
273
|
+
}
|
|
274
|
+
return sum;
|
|
275
|
+
}
|
|
276
|
+
return JSON.stringify(content).length;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Census the session branch's injected context (counts/chars only — never message content). */
|
|
280
|
+
export function branchContextCensus(entries: readonly CensusBranchEntry[]): BranchContextCensus {
|
|
281
|
+
const perk = new Map<string, { copies: number; totalChars: number }>();
|
|
282
|
+
const other = { copies: 0, totalChars: 0 };
|
|
283
|
+
let bindingHeaderCopies = 0;
|
|
284
|
+
for (const entry of entries) {
|
|
285
|
+
if (JSON.stringify(entry).includes(BINDING_HEADER)) bindingHeaderCopies += 1;
|
|
286
|
+
if (entry.type !== "custom_message") continue;
|
|
287
|
+
const chars = contentChars(entry.content);
|
|
288
|
+
const customType = entry.customType ?? "";
|
|
289
|
+
if (customType.startsWith("perk:")) {
|
|
290
|
+
const row = perk.get(customType) ?? { copies: 0, totalChars: 0 };
|
|
291
|
+
row.copies += 1;
|
|
292
|
+
row.totalChars += chars;
|
|
293
|
+
perk.set(customType, row);
|
|
294
|
+
} else {
|
|
295
|
+
other.copies += 1;
|
|
296
|
+
other.totalChars += chars;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
entries: entries.length,
|
|
301
|
+
perkContexts: [...perk.entries()]
|
|
302
|
+
.map(([customType, row]) => ({ customType, ...row }))
|
|
303
|
+
.sort((a, b) => (a.customType < b.customType ? -1 : a.customType > b.customType ? 1 : 0)),
|
|
304
|
+
otherCustomMessages: other,
|
|
305
|
+
bindingHeaderCopies,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Render the census as a fixed multi-line block. The line grammar (the `census:` /
|
|
311
|
+
* `append-system-prompt:` / `context-files:` / `skills:` / `tools:` / `per source:` / `branch:` /
|
|
312
|
+
* `perk contexts:` keys) is stable — the closing audit diffs against these exact keys.
|
|
313
|
+
*/
|
|
314
|
+
export function renderCensus(
|
|
315
|
+
prompt: PromptCensus,
|
|
316
|
+
tools: ToolsCensus,
|
|
317
|
+
branch: BranchContextCensus,
|
|
318
|
+
): string {
|
|
319
|
+
const lines: string[] = ["census:"];
|
|
320
|
+
lines.push(
|
|
321
|
+
prompt.basePromptChars === null
|
|
322
|
+
? " base-prompt: pi-default (not measured)"
|
|
323
|
+
: ` base-prompt: custom ${prompt.basePromptChars}c`,
|
|
324
|
+
);
|
|
325
|
+
lines.push(` append-system-prompt: ${prompt.appendChars}c`);
|
|
326
|
+
const fileList = prompt.contextFiles.files.map((f) => `${f.path}=${f.chars}c`).join(", ");
|
|
327
|
+
lines.push(
|
|
328
|
+
` context-files: ${prompt.contextFiles.count} file(s), ${prompt.contextFiles.totalChars}c` +
|
|
329
|
+
(fileList.length > 0 ? ` — ${fileList}` : ""),
|
|
330
|
+
);
|
|
331
|
+
lines.push(
|
|
332
|
+
` skills: ${prompt.skills.visible} visible + ${prompt.skills.hidden} hidden; ` +
|
|
333
|
+
`prompt-section=${prompt.skills.promptSectionChars}c`,
|
|
334
|
+
);
|
|
335
|
+
lines.push(
|
|
336
|
+
` tools: ${tools.active} active / ${tools.all} registered; schemas=${tools.schemaChars}c; ` +
|
|
337
|
+
`guidelines=${prompt.toolGuidelineChars}c; snippets=${prompt.toolSnippetChars}c`,
|
|
338
|
+
);
|
|
339
|
+
if (tools.bySource.length > 0) {
|
|
340
|
+
const rows = tools.bySource
|
|
341
|
+
.map((r) => `${r.source}=${r.active} (${r.schemaChars}c)`)
|
|
342
|
+
.join("; ");
|
|
343
|
+
lines.push(` per source: ${rows}`);
|
|
344
|
+
}
|
|
345
|
+
lines.push(
|
|
346
|
+
` branch: ${branch.entries} entries; binding-header-copies=${branch.bindingHeaderCopies}`,
|
|
347
|
+
);
|
|
348
|
+
const perkRows = branch.perkContexts.map(
|
|
349
|
+
(r) => `${r.customType} ×${r.copies} (${r.totalChars}c)`,
|
|
350
|
+
);
|
|
351
|
+
const perkSegment = perkRows.length > 0 ? perkRows.join("; ") : "none";
|
|
352
|
+
lines.push(
|
|
353
|
+
` perk contexts: ${perkSegment}; other custom_message ` +
|
|
354
|
+
`×${branch.otherCustomMessages.copies} (${branch.otherCustomMessages.totalChars}c)`,
|
|
355
|
+
);
|
|
356
|
+
return lines.join("\n");
|
|
357
|
+
}
|
|
358
|
+
|
|
133
359
|
/**
|
|
134
360
|
* Register `/perk-selfcheck`: the session-wiring verifier. Runs on a command context (the only
|
|
135
361
|
* context exposing `getSystemPromptOptions()`), so it sees the live splice — not just disk.
|
|
@@ -149,8 +375,15 @@ export function registerSelfcheck(
|
|
|
149
375
|
onDiskIndex: readAmbientIndex(ctx.cwd),
|
|
150
376
|
options,
|
|
151
377
|
});
|
|
152
|
-
//
|
|
153
|
-
|
|
378
|
+
// The census is report-only: it rides the same report but never affects ok/level. A command
|
|
379
|
+
// doesn't fire `before_agent_start`, so the branch reads as of the last completed turn.
|
|
380
|
+
const census = renderCensus(
|
|
381
|
+
promptCensus(options),
|
|
382
|
+
toolsCensus(pi.getAllTools(), pi.getActiveTools()),
|
|
383
|
+
branchContextCensus(branchOf(ctx)),
|
|
384
|
+
);
|
|
385
|
+
// Headless-safe: report() surfaces the derived counts/identifiers (never raw prompt content).
|
|
386
|
+
reportTo(ctx, "selfcheck", report.level, `${report.summary}\n${census}`);
|
|
154
387
|
},
|
|
155
388
|
});
|
|
156
389
|
}
|
|
@@ -25,6 +25,7 @@ import { registerPerkCommand } from "../substrate/command.ts";
|
|
|
25
25
|
import { loadPerkConfig } from "../substrate/config.ts";
|
|
26
26
|
import { render } from "../substrate/prompts.ts";
|
|
27
27
|
import { failFor, type OkDetails, ok, type Result } from "../substrate/result.ts";
|
|
28
|
+
import { captureSessionPointer } from "../substrate/sessionPointers.ts";
|
|
28
29
|
import { appendWorkflowState, branchOf, rebuildWorkflowState } from "../substrate/workflowState.ts";
|
|
29
30
|
import { report } from "../surfaces/report.ts";
|
|
30
31
|
|
|
@@ -119,6 +120,25 @@ export async function submitPr(pi: ExtensionAPI, ctx: ExtensionContext): Promise
|
|
|
119
120
|
});
|
|
120
121
|
if (!r.ok) return fail(r.message, r.errorType);
|
|
121
122
|
|
|
123
|
+
// Capture `implementation/main` at the moment the run id enters `impl_run_ids` (contracts.md
|
|
124
|
+
// §8.35): any run id stamped into the linkage gets its pointer captured in the same gesture.
|
|
125
|
+
// This covers address/warm sessions that submit — which the stage-gated `session_start` capture
|
|
126
|
+
// never sees — so a submitted run resolves `found` instead of `missing`. For the implement
|
|
127
|
+
// session's own /submit it is an idempotent same-session refresh; `preserveForeign` guarantees
|
|
128
|
+
// it can never clobber a different session's pointer. Best-effort + non-fatal like every
|
|
129
|
+
// capture site (a successful submit must stand).
|
|
130
|
+
if (runId) {
|
|
131
|
+
captureSessionPointer({
|
|
132
|
+
cwd: ctx.cwd,
|
|
133
|
+
runId,
|
|
134
|
+
klass: "implementation",
|
|
135
|
+
site: "main",
|
|
136
|
+
// Optional-chained: best-effort, and some side-session fakes have no getSessionFile.
|
|
137
|
+
sessionFile: ctx.sessionManager.getSessionFile?.(),
|
|
138
|
+
preserveForeign: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
122
142
|
const verb = r.data.pr.existed ? "Found existing" : "Opened draft";
|
|
123
143
|
const conflicted = r.data.mergeable === false && (r.data.conflicts?.length ?? 0) > 0;
|
|
124
144
|
// Reset the counter on every clean (or undetermined) submit — idempotent; keeps a later
|