@timurproko/a1 0.1.8-dev.544 → 0.1.8-dev.546
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/bin/cli.js +4 -0
- package/dist/app/session-shell/session-shell-root.js +15 -5
- package/dist/cli/dispatch.d.ts +5 -0
- package/dist/cli/dispatch.js +26 -0
- package/dist/cli/session-context.d.ts +9 -0
- package/dist/cli/session-context.js +29 -0
- package/dist/composition/owned-ui.js +6 -2
- package/dist/contracts/owned-ui/model.d.ts +5 -0
- package/dist/contracts/owned-ui/validation.js +6 -0
- package/dist/contracts/presentation/index.d.ts +13 -0
- package/dist/contracts/presentation/index.js +30 -0
- package/dist/features/launch/index.d.ts +2 -0
- package/dist/features/launch/index.js +1 -0
- package/dist/features/launch/session-context.d.ts +8 -0
- package/dist/features/launch/session-context.js +8 -0
- package/dist/features/owned-ui/project-trust-prompt.js +1 -1
- package/dist/features/owned-ui/reference-screen-app.js +7 -8
- package/dist/features/owned-ui/settings-app.js +9 -5
- package/dist/foundation/lifecycle/index.d.ts +2 -0
- package/dist/foundation/lifecycle/index.js +1 -0
- package/dist/foundation/lifecycle/session-repository-context.d.ts +18 -0
- package/dist/foundation/lifecycle/session-repository-context.js +173 -0
- package/dist/integrations/pi/components/models-dialog.js +9 -9
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +27 -3
- package/dist/integrations/pi/components/shell-extension-ui.js +4 -2
- package/dist/integrations/pi/components/shell-footer-status.js +1 -1
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -2
- package/dist/integrations/pi/components/skills-command.d.ts +2 -0
- package/dist/integrations/pi/components/skills-command.js +11 -10
- package/dist/integrations/pi/components/skills-dialog.js +8 -3
- package/dist/integrations/pi/components/theme.d.ts +4 -0
- package/dist/integrations/pi/components/theme.js +10 -0
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +4 -4
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +9 -10
- package/dist/integrations/pi/components/upstream/components/extension-input.d.ts +17 -0
- package/dist/integrations/pi/components/upstream/components/extension-input.js +72 -0
- package/dist/integrations/pi/components/upstream/components/extension-selector.d.ts +15 -0
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +92 -0
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -2
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +16 -15
- package/dist/integrations/pi/components/upstream/components/session-footer.js +25 -9
- package/dist/integrations/pi/components/upstream/components/session-selector.js +25 -16
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +6 -4
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +22 -12
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +3 -2
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +14 -12
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +11 -5
- package/dist/integrations/pi/engine/adapter.d.ts +2 -1
- package/dist/integrations/pi/engine/adapter.js +2 -0
- package/dist/integrations/pi/engine/message-values.js +1 -0
- package/dist/integrations/pi/engine/session-runtime.d.ts +11 -2
- package/dist/integrations/pi/engine/session-runtime.js +136 -33
- package/dist/integrations/pi/engine/transcript-projection.js +35 -0
- package/dist/integrations/pi/startup-public.manifest.json +2 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +2 -1
- package/dist/ui/components/dialog-panel.d.ts +2 -1
- package/dist/ui/components/dialog-panel.js +6 -1
- package/dist/ui/components/index.d.ts +2 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/shortcut-hints.d.ts +7 -0
- package/dist/ui/components/shortcut-hints.js +12 -0
- package/dist/ui/components/shortcuts.d.ts +2 -0
- package/dist/ui/components/shortcuts.js +5 -2
- package/dist/ui/components/transcript-viewport.d.ts +1 -1
- package/docs/ci-release-runbook.md +9 -7
- package/docs/openspec-archive-automation.md +6 -0
- package/docs/validation.md +5 -2
- package/package.json +1 -1
|
@@ -9,6 +9,9 @@ import { DefaultPackageManager, VERSION } from "../startup-public.js";
|
|
|
9
9
|
import { readOpenPullRequest, } from "./repository-pr.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
const PULL_REQUEST_REFRESH_MS = 60_000;
|
|
12
|
+
const REPOSITORY_CONTEXT_POLL_MS = 5_000;
|
|
13
|
+
const GIT_BRANCH_TIMEOUT_MS = 5_000;
|
|
14
|
+
const GIT_BRANCH_MAX_BUFFER_BYTES = 4 * 1024;
|
|
12
15
|
/**
|
|
13
16
|
* The pinned Pi runtime and the session bound to it: creation through the runtime factory, the
|
|
14
17
|
* session generation that stamps and invalidates events, the subscription that forwards session
|
|
@@ -22,6 +25,8 @@ export class PiEngineRuntime {
|
|
|
22
25
|
#checkPackageUpdates;
|
|
23
26
|
#pullRequestProbe;
|
|
24
27
|
#pullRequestRefreshMs;
|
|
28
|
+
#repositoryContextPollMs;
|
|
29
|
+
#repositoryContextReader;
|
|
25
30
|
#gitBranchReader;
|
|
26
31
|
#cwd;
|
|
27
32
|
#runtime;
|
|
@@ -31,15 +36,21 @@ export class PiEngineRuntime {
|
|
|
31
36
|
#sessionBindingGeneration = 0;
|
|
32
37
|
#sessionCommands;
|
|
33
38
|
#compactionProgress = null;
|
|
39
|
+
#repositoryCwd;
|
|
34
40
|
#gitBranch = null;
|
|
35
41
|
#pullRequest = null;
|
|
36
|
-
#
|
|
37
|
-
#
|
|
42
|
+
#repositoryRefreshTimer;
|
|
43
|
+
#repositoryRefreshAbort;
|
|
44
|
+
#repositoryRefreshTask;
|
|
45
|
+
#repositoryRefreshPending = false;
|
|
46
|
+
#lastPullRequestProbeAt = null;
|
|
47
|
+
#repositoryRefreshEnabled = false;
|
|
38
48
|
#disposed = false;
|
|
39
49
|
constructor(options, ports) {
|
|
40
50
|
this.#options = options;
|
|
41
51
|
this.#ports = ports;
|
|
42
52
|
this.#cwd = options.cwd;
|
|
53
|
+
this.#repositoryCwd = options.cwd;
|
|
43
54
|
this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
|
|
44
55
|
this.#checkPackageUpdates = options.checkPackageUpdates
|
|
45
56
|
?? (options.createRuntime
|
|
@@ -47,6 +58,8 @@ export class PiEngineRuntime {
|
|
|
47
58
|
: settingsManager => checkDefaultPiPackageUpdates(this.#cwd, options.agentDir, settingsManager));
|
|
48
59
|
this.#pullRequestProbe = options.pullRequestProbe ?? readOpenPullRequest;
|
|
49
60
|
this.#pullRequestRefreshMs = options.pullRequestRefreshMs ?? PULL_REQUEST_REFRESH_MS;
|
|
61
|
+
this.#repositoryContextPollMs = options.repositoryContextPollMs ?? REPOSITORY_CONTEXT_POLL_MS;
|
|
62
|
+
this.#repositoryContextReader = options.repositoryContextReader ?? (async () => null);
|
|
50
63
|
this.#gitBranchReader = options.gitBranchReader ?? readGitBranch;
|
|
51
64
|
}
|
|
52
65
|
get runtime() {
|
|
@@ -66,6 +79,9 @@ export class PiEngineRuntime {
|
|
|
66
79
|
get cwd() {
|
|
67
80
|
return this.#runtime?.cwd ?? this.#cwd;
|
|
68
81
|
}
|
|
82
|
+
get repositoryCwd() {
|
|
83
|
+
return this.#repositoryCwd;
|
|
84
|
+
}
|
|
69
85
|
get gitBranch() {
|
|
70
86
|
return this.#gitBranch;
|
|
71
87
|
}
|
|
@@ -91,7 +107,7 @@ export class PiEngineRuntime {
|
|
|
91
107
|
});
|
|
92
108
|
this.#runtime = runtime;
|
|
93
109
|
this.#cwd = runtime.cwd ?? this.#cwd;
|
|
94
|
-
this.#
|
|
110
|
+
this.#repositoryCwd = this.#cwd;
|
|
95
111
|
this.#ports.started(runtime);
|
|
96
112
|
runtime.setRebindSession(async (session) => {
|
|
97
113
|
if (this.#ports.rebindBlocked())
|
|
@@ -104,7 +120,8 @@ export class PiEngineRuntime {
|
|
|
104
120
|
}
|
|
105
121
|
this.bindSession(runtime.session);
|
|
106
122
|
await this.#announceChangelog(runtime.services.settingsManager);
|
|
107
|
-
this.#
|
|
123
|
+
this.#repositoryRefreshEnabled = true;
|
|
124
|
+
this.#startRepositoryRefresh();
|
|
108
125
|
return runtime;
|
|
109
126
|
}
|
|
110
127
|
/** Probe extension packages for updates after startup and report them as an informational diagnostic. */
|
|
@@ -161,7 +178,10 @@ export class PiEngineRuntime {
|
|
|
161
178
|
// Invariant: the subscription is live before the adapter rebuilds its state so the extension
|
|
162
179
|
// rebind that finishes the rebuild cannot emit an event nobody is listening to.
|
|
163
180
|
this.#subscribe();
|
|
181
|
+
this.#resetRepositoryRefresh();
|
|
164
182
|
this.#ports.sessionReplaced(session);
|
|
183
|
+
if (this.#repositoryRefreshEnabled)
|
|
184
|
+
this.#startRepositoryRefresh();
|
|
165
185
|
}
|
|
166
186
|
/** Stop forwarding the current session's events and open a new generation; overload recovery resumes or abandons it. */
|
|
167
187
|
suspend() {
|
|
@@ -201,50 +221,126 @@ export class PiEngineRuntime {
|
|
|
201
221
|
/** Unsubscribe, stop observing compaction, and dispose the runtime; the session reference is kept for final reads. */
|
|
202
222
|
async dispose() {
|
|
203
223
|
this.#disposed = true;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.#
|
|
208
|
-
this.#
|
|
224
|
+
this.#repositoryRefreshEnabled = false;
|
|
225
|
+
if (this.#repositoryRefreshTimer !== undefined)
|
|
226
|
+
clearTimeout(this.#repositoryRefreshTimer);
|
|
227
|
+
this.#repositoryRefreshTimer = undefined;
|
|
228
|
+
this.#repositoryRefreshPending = false;
|
|
229
|
+
this.#repositoryRefreshAbort?.abort();
|
|
230
|
+
const repositoryRefreshTask = this.#repositoryRefreshTask;
|
|
209
231
|
this.#unsubscribe?.();
|
|
210
232
|
this.#unsubscribe = undefined;
|
|
211
233
|
this.#compactionProgress?.dispose();
|
|
212
234
|
this.#compactionProgress = null;
|
|
235
|
+
await repositoryRefreshTask;
|
|
213
236
|
await this.#runtime?.dispose();
|
|
214
237
|
}
|
|
215
|
-
#
|
|
216
|
-
if (this.#
|
|
238
|
+
#startRepositoryRefresh() {
|
|
239
|
+
if (!this.#repositoryRefreshEnabled || this.#disposed || this.#ports.disposed())
|
|
240
|
+
return;
|
|
241
|
+
if (this.#repositoryRefreshTask !== undefined) {
|
|
242
|
+
this.#repositoryRefreshPending = true;
|
|
217
243
|
return;
|
|
218
|
-
|
|
244
|
+
}
|
|
245
|
+
const task = this.#refreshRepositoryMetadata();
|
|
246
|
+
this.#repositoryRefreshTask = task;
|
|
247
|
+
const settled = () => {
|
|
248
|
+
if (this.#repositoryRefreshTask === task)
|
|
249
|
+
this.#repositoryRefreshTask = undefined;
|
|
250
|
+
if (!this.#repositoryRefreshPending)
|
|
251
|
+
return;
|
|
252
|
+
this.#repositoryRefreshPending = false;
|
|
253
|
+
this.#startRepositoryRefresh();
|
|
254
|
+
};
|
|
255
|
+
void task.then(settled, settled);
|
|
219
256
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
257
|
+
#resetRepositoryRefresh() {
|
|
258
|
+
if (this.#repositoryRefreshTimer !== undefined)
|
|
259
|
+
clearTimeout(this.#repositoryRefreshTimer);
|
|
260
|
+
this.#repositoryRefreshTimer = undefined;
|
|
261
|
+
this.#repositoryRefreshAbort?.abort();
|
|
262
|
+
this.#repositoryCwd = this.#cwd;
|
|
263
|
+
this.#gitBranch = null;
|
|
264
|
+
this.#pullRequest = null;
|
|
265
|
+
this.#lastPullRequestProbeAt = null;
|
|
266
|
+
}
|
|
267
|
+
async #refreshRepositoryMetadata() {
|
|
268
|
+
if (this.#repositoryRefreshAbort !== undefined || this.#disposed || this.#ports.disposed())
|
|
223
269
|
return;
|
|
224
270
|
const controller = new AbortController();
|
|
225
|
-
|
|
226
|
-
|
|
271
|
+
const generation = this.#sessionBindingGeneration;
|
|
272
|
+
this.#repositoryRefreshAbort = controller;
|
|
273
|
+
let associatedContext = null;
|
|
274
|
+
const manager = this.#session?.sessionManager;
|
|
275
|
+
const sessionId = manager?.getSessionId?.();
|
|
276
|
+
const sessionFile = manager?.getSessionFile?.();
|
|
227
277
|
try {
|
|
228
|
-
|
|
278
|
+
if (typeof sessionId === "string" && sessionId.length > 0 && typeof sessionFile === "string" && sessionFile.length > 0) {
|
|
279
|
+
try {
|
|
280
|
+
associatedContext = await this.#repositoryContextReader(sessionId, sessionFile, controller.signal);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
associatedContext = null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const nextCwd = associatedContext?.cwd ?? this.#cwd;
|
|
287
|
+
let nextBranch = associatedContext?.branch ?? null;
|
|
288
|
+
if (associatedContext === null) {
|
|
289
|
+
try {
|
|
290
|
+
nextBranch = await this.#gitBranchReader(nextCwd, controller.signal);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
nextBranch = null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const metadataChanged = nextCwd !== this.#repositoryCwd || nextBranch !== this.#gitBranch;
|
|
297
|
+
const remoteDue = metadataChanged || this.#lastPullRequestProbeAt === null
|
|
298
|
+
|| Date.now() - this.#lastPullRequestProbeAt >= this.#pullRequestRefreshMs;
|
|
299
|
+
if (remoteDue) {
|
|
300
|
+
let nextPullRequest = null;
|
|
301
|
+
try {
|
|
302
|
+
nextPullRequest = nextBranch === null
|
|
303
|
+
? null
|
|
304
|
+
: await this.#pullRequestProbe(nextCwd, nextBranch, controller.signal);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
nextPullRequest = null;
|
|
308
|
+
}
|
|
309
|
+
if (controller.signal.aborted || generation !== this.#sessionBindingGeneration || this.#disposed || this.#ports.disposed())
|
|
310
|
+
return;
|
|
311
|
+
const changed = nextCwd !== this.#repositoryCwd
|
|
312
|
+
|| nextBranch !== this.#gitBranch
|
|
313
|
+
|| !samePullRequest(this.#pullRequest, nextPullRequest);
|
|
314
|
+
this.#repositoryCwd = nextCwd;
|
|
315
|
+
this.#gitBranch = nextBranch;
|
|
316
|
+
this.#pullRequest = nextPullRequest;
|
|
317
|
+
this.#lastPullRequestProbeAt = Date.now();
|
|
318
|
+
if (changed)
|
|
319
|
+
this.#ports.emitView();
|
|
320
|
+
}
|
|
229
321
|
}
|
|
230
322
|
catch {
|
|
231
|
-
|
|
323
|
+
if (!controller.signal.aborted && generation === this.#sessionBindingGeneration && !this.#disposed && !this.#ports.disposed()) {
|
|
324
|
+
const changed = this.#repositoryCwd !== this.#cwd || this.#gitBranch !== null || this.#pullRequest !== null;
|
|
325
|
+
this.#repositoryCwd = this.#cwd;
|
|
326
|
+
this.#gitBranch = null;
|
|
327
|
+
this.#pullRequest = null;
|
|
328
|
+
this.#lastPullRequestProbeAt = Date.now();
|
|
329
|
+
if (changed)
|
|
330
|
+
this.#ports.emitView();
|
|
331
|
+
}
|
|
232
332
|
}
|
|
233
333
|
finally {
|
|
234
|
-
if (this.#
|
|
235
|
-
this.#
|
|
334
|
+
if (this.#repositoryRefreshAbort === controller)
|
|
335
|
+
this.#repositoryRefreshAbort = undefined;
|
|
236
336
|
}
|
|
237
|
-
if (controller.signal.aborted || this.#disposed || this.#ports.disposed())
|
|
337
|
+
if (controller.signal.aborted || generation !== this.#sessionBindingGeneration || this.#disposed || this.#ports.disposed())
|
|
238
338
|
return;
|
|
239
|
-
|
|
240
|
-
this.#
|
|
241
|
-
this.#
|
|
242
|
-
}
|
|
243
|
-
this.#
|
|
244
|
-
this.#pullRequestTimer = undefined;
|
|
245
|
-
void this.#refreshPullRequest();
|
|
246
|
-
}, this.#pullRequestRefreshMs);
|
|
247
|
-
this.#pullRequestTimer.unref?.();
|
|
339
|
+
this.#repositoryRefreshTimer = setTimeout(() => {
|
|
340
|
+
this.#repositoryRefreshTimer = undefined;
|
|
341
|
+
this.#startRepositoryRefresh();
|
|
342
|
+
}, this.#repositoryContextPollMs);
|
|
343
|
+
this.#repositoryRefreshTimer.unref?.();
|
|
248
344
|
}
|
|
249
345
|
#subscribe() {
|
|
250
346
|
const session = this.#session;
|
|
@@ -296,9 +392,16 @@ async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
|
|
|
296
392
|
function samePullRequest(left, right) {
|
|
297
393
|
return left?.number === right?.number && left?.url === right?.url;
|
|
298
394
|
}
|
|
299
|
-
async function readGitBranch(cwd) {
|
|
395
|
+
async function readGitBranch(cwd, signal) {
|
|
300
396
|
try {
|
|
301
|
-
const { stdout } = await execFileAsync("git", ["branch", "--show-current"], {
|
|
397
|
+
const { stdout } = await execFileAsync("git", ["branch", "--show-current"], {
|
|
398
|
+
cwd,
|
|
399
|
+
windowsHide: true,
|
|
400
|
+
timeout: GIT_BRANCH_TIMEOUT_MS,
|
|
401
|
+
maxBuffer: GIT_BRANCH_MAX_BUFFER_BYTES,
|
|
402
|
+
signal,
|
|
403
|
+
encoding: "utf8",
|
|
404
|
+
});
|
|
302
405
|
const branch = stdout.trim();
|
|
303
406
|
return branch.length > 0 ? branch : null;
|
|
304
407
|
}
|
|
@@ -2,6 +2,39 @@ import { toolRenderingInput } from "./tool-rendering.js";
|
|
|
2
2
|
import { TranscriptImageAssets } from "./transcript-image-assets.js";
|
|
3
3
|
import { acceptsTranscriptUpdate, transcriptToolState } from "../../../contracts/owned-ui/index.js";
|
|
4
4
|
import { assistantContent, contentImageCount, isRecord, jsonSummary, messageFallbackKey, retainCompletedArguments, sameBlockContent, sanitizeJson, stringValue, textFromContent } from "./message-values.js";
|
|
5
|
+
const IMAGE_DIMENSION_NOTE = /^\[Image: original \d+x\d+, displayed at \d+x\d+\. Multiply coordinates by \d+(?:\.\d+)? to map to original image\.\]$/u;
|
|
6
|
+
const IMAGE_CONVERSION_NOTE = /^\[Image converted from image\/[a-z0-9.+-]+ to image\/[a-z0-9.+-]+\.\]$/iu;
|
|
7
|
+
const IMAGE_OMISSION_NOTE = /^\[Image omitted: could not be (?:converted to a supported inline image format|resized below the inline image size limit)\.\]$/u;
|
|
8
|
+
function isImageProcessingHint(line) {
|
|
9
|
+
return IMAGE_DIMENSION_NOTE.test(line) || IMAGE_CONVERSION_NOTE.test(line) || IMAGE_OMISSION_NOTE.test(line);
|
|
10
|
+
}
|
|
11
|
+
function userPresentation(content) {
|
|
12
|
+
const imageCount = contentImageCount(content);
|
|
13
|
+
if (imageCount === 0)
|
|
14
|
+
return undefined;
|
|
15
|
+
const lines = textFromContent(content).split("\n");
|
|
16
|
+
const retainedHints = [];
|
|
17
|
+
let dimensionCount = 0;
|
|
18
|
+
let hintStart = lines.length;
|
|
19
|
+
while (hintStart > 0) {
|
|
20
|
+
const line = lines[hintStart - 1];
|
|
21
|
+
if (!isImageProcessingHint(line))
|
|
22
|
+
break;
|
|
23
|
+
if (IMAGE_DIMENSION_NOTE.test(line))
|
|
24
|
+
dimensionCount++;
|
|
25
|
+
else
|
|
26
|
+
retainedHints.unshift(line);
|
|
27
|
+
hintStart--;
|
|
28
|
+
}
|
|
29
|
+
if (dimensionCount === 0 || dimensionCount > imageCount)
|
|
30
|
+
return undefined;
|
|
31
|
+
lines.splice(hintStart);
|
|
32
|
+
if (lines.at(-1) === "")
|
|
33
|
+
lines.pop();
|
|
34
|
+
if (retainedHints.length > 0)
|
|
35
|
+
lines.push("", ...retainedHints);
|
|
36
|
+
return { visibleText: lines.join("\n") };
|
|
37
|
+
}
|
|
5
38
|
/**
|
|
6
39
|
* The transcript as the owned UI sees it: Pi's session messages and tool events projected into
|
|
7
40
|
* ordered, revisioned blocks. The projection owns block identity (message and tool ids that stay
|
|
@@ -122,6 +155,7 @@ export class PiTranscriptProjection {
|
|
|
122
155
|
return [];
|
|
123
156
|
const baseId = this.#messageBlockId(message, fallbackIndex, status, occurrence);
|
|
124
157
|
if (message.role === "user") {
|
|
158
|
+
const presentation = userPresentation(message.content);
|
|
125
159
|
return [{
|
|
126
160
|
id: baseId,
|
|
127
161
|
kind: "user",
|
|
@@ -129,6 +163,7 @@ export class PiTranscriptProjection {
|
|
|
129
163
|
revision: this.nextRevision(baseId),
|
|
130
164
|
title: "User",
|
|
131
165
|
text: textFromContent(message.content),
|
|
166
|
+
...(presentation === undefined ? {} : { userPresentation: presentation }),
|
|
132
167
|
imageReferences: this.imageReferences(message.content, "user"),
|
|
133
168
|
payload: {
|
|
134
169
|
role: "user",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"dist/integrations/pi/components/upstream/adjacent/core/keybindings.js",
|
|
17
17
|
"dist/integrations/pi/components/upstream/components/earendil-announcement.js",
|
|
18
18
|
"dist/integrations/pi/components/upstream/components/extension-editor.js",
|
|
19
|
+
"dist/integrations/pi/components/upstream/components/extension-input.js",
|
|
20
|
+
"dist/integrations/pi/components/upstream/components/extension-selector.js",
|
|
19
21
|
"dist/integrations/pi/components/upstream/components/scoped-models-selector.js",
|
|
20
22
|
"dist/integrations/pi/components/upstream/components/session-selector.js",
|
|
21
23
|
"dist/integrations/pi/components/upstream/components/status-indicator.js",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-22T23:31:15.773Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-22T23:30:53.377Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-22T23:31:43.770Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "57ed4829927f81b1cb638ffafa7a5b3832537284aa343c0eb0ef70592ed671d3",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
|
-
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
3
|
+
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory"];
|
|
4
4
|
declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
|
5
5
|
declare const MANIFEST_KEYS: readonly ["releaseFilename", "packageFilename"];
|
|
6
6
|
declare const PROTOCOL_KEYS: readonly ["namespace", "controlEnvelope", "supervisorSchema", "nativeHostSchema", "structuredAgentSchema", "controlStoreSchema", "promptHistorySchema", "releaseCohortSchema", "updateJournalSchema"];
|
package/dist/product-identity.js
CHANGED
|
@@ -4,7 +4,7 @@ const ENVIRONMENT_KEYS = [
|
|
|
4
4
|
"paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId",
|
|
5
5
|
];
|
|
6
6
|
const FILESYSTEM_KEYS = ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
7
|
-
const STATE_KEYS = ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
7
|
+
const STATE_KEYS = ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory"];
|
|
8
8
|
const ENDPOINT_KEYS = ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
|
9
9
|
const MANIFEST_KEYS = ["releaseFilename", "packageFilename"];
|
|
10
10
|
const PROTOCOL_KEYS = ["namespace", "controlEnvelope", "supervisorSchema", "nativeHostSchema", "structuredAgentSchema", "controlStoreSchema", "promptHistorySchema", "releaseCohortSchema", "updateJournalSchema"];
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"unixControlDirectory": "a1",
|
|
33
33
|
"developmentDirectory": "a1-development",
|
|
34
34
|
"piAgentProfile": ".a1/agent",
|
|
35
|
-
"piVanillaProfile": ".pi/agent"
|
|
35
|
+
"piVanillaProfile": ".pi/agent",
|
|
36
|
+
"sessionRepositoryDirectory": "session-repositories"
|
|
36
37
|
},
|
|
37
38
|
"endpoint": {
|
|
38
39
|
"windowsPipeStem": "a1",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ShortcutHintEntry } from "./shortcut-hints.js";
|
|
1
2
|
import type { UiTheme } from "./theme.js";
|
|
2
3
|
/**
|
|
3
4
|
* The panel a value with parts is edited in: at the foot of the screen, over the
|
|
@@ -15,7 +16,7 @@ export interface DialogPanelState {
|
|
|
15
16
|
/** The row in hand. */
|
|
16
17
|
readonly index: number;
|
|
17
18
|
/** How to change it, in the words the engine uses. */
|
|
18
|
-
readonly hint: string;
|
|
19
|
+
readonly hint: string | readonly ShortcutHintEntry[];
|
|
19
20
|
}
|
|
20
21
|
/** Where the panel sits, for reading a pointer against its rows. */
|
|
21
22
|
export interface DialogPanelFrame {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderShortcutHints, shortcutHintsText } from "./shortcut-hints.js";
|
|
1
2
|
import { displayWidth, truncateToWidth } from "./text.js";
|
|
2
3
|
const LABEL_COLUMN_CAP = 30;
|
|
3
4
|
/** The column values start at: past the widest label, capped so one long name cannot push them off. */
|
|
@@ -25,13 +26,17 @@ export function renderDialogPanel(state, width, theme) {
|
|
|
25
26
|
return pad(truncateToWidth(painted, width), width, raw);
|
|
26
27
|
});
|
|
27
28
|
const description = state.rows[state.index]?.description ?? "";
|
|
29
|
+
const hint = typeof state.hint === "string"
|
|
30
|
+
? theme.fg("dim", ` ${state.hint}`)
|
|
31
|
+
: renderShortcutHints(state.hint, theme, 2);
|
|
32
|
+
const plainHint = typeof state.hint === "string" ? ` ${state.hint}` : shortcutHintsText(state.hint, 2);
|
|
28
33
|
return [
|
|
29
34
|
rule,
|
|
30
35
|
...rows,
|
|
31
36
|
"",
|
|
32
37
|
pad(truncateToWidth(theme.fg("dim", ` ${description}`), width), width, ` ${description}`),
|
|
33
38
|
"",
|
|
34
|
-
pad(truncateToWidth(
|
|
39
|
+
pad(truncateToWidth(hint, width), width, plainHint),
|
|
35
40
|
rule,
|
|
36
41
|
];
|
|
37
42
|
}
|
|
@@ -22,6 +22,8 @@ export { MAX_COPY_ROWS, MAX_COPY_SOURCE_UNITS, selectionCopyLineContent, selecti
|
|
|
22
22
|
export type { SelectionCopyPrompt, SelectionCopyRow, SelectionCopySnapshot } from "./selection-copy.js";
|
|
23
23
|
export { ScrollbarRails, isThumbRow, scrollForThumbRow, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarReservesSpace, scrollbarSelectionRows, scrollbarWheelRows, } from "./scrollbar.js";
|
|
24
24
|
export type { RailPointer, RailPosition, ScrollbarAppearance, ScrollbarGeometry, ScrollbarInput, ScrollbarPresentation, ScrollbarPresentationInput, ScrollbarSpeed, ScrollbarStyle, } from "./scrollbar.js";
|
|
25
|
+
export { renderShortcutHints } from "./shortcut-hints.js";
|
|
26
|
+
export type { ShortcutHintEntry } from "./shortcut-hints.js";
|
|
25
27
|
export { GLOBAL_SCOPE, ShortcutRegistry, assembleShortcuts, assertNoShortcutConflicts } from "./shortcuts.js";
|
|
26
28
|
export type { ShortcutConflict, ShortcutDeclaration, ShortcutRegistryResult } from "./shortcuts.js";
|
|
27
29
|
export { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, hyperlinkTargetAtColumn, nativeHyperlinkStyle, overlaySpan } from "./spans.js";
|
|
@@ -11,6 +11,7 @@ export { progressStatusText } from "./progress-status.js";
|
|
|
11
11
|
export { FrameCache, RENDER_REVISION_KINDS, RenderRevisionTracker, ZERO_REVISIONS, normalizeRevisions, revisionsEqual } from "./revision.js";
|
|
12
12
|
export { MAX_COPY_ROWS, MAX_COPY_SOURCE_UNITS, selectionCopyLineContent, selectionCopyRowText } from "./selection-copy.js";
|
|
13
13
|
export { ScrollbarRails, isThumbRow, scrollForThumbRow, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarReservesSpace, scrollbarSelectionRows, scrollbarWheelRows, } from "./scrollbar.js";
|
|
14
|
+
export { renderShortcutHints } from "./shortcut-hints.js";
|
|
14
15
|
export { GLOBAL_SCOPE, ShortcutRegistry, assembleShortcuts, assertNoShortcutConflicts } from "./shortcuts.js";
|
|
15
16
|
export { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, hyperlinkTargetAtColumn, nativeHyperlinkStyle, overlaySpan } from "./spans.js";
|
|
16
17
|
export { readVisibleHyperlinks } from "./visible-hyperlinks.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type SemanticShortcutHint } from "../../contracts/presentation/index.js";
|
|
2
|
+
import type { UiTheme } from "./theme.js";
|
|
3
|
+
export type ShortcutHintEntry = SemanticShortcutHint;
|
|
4
|
+
/** Returns an unstyled modal instruction row for width-aware clipping. */
|
|
5
|
+
export declare function shortcutHintsText(entries: readonly ShortcutHintEntry[], indent?: number): string;
|
|
6
|
+
/** Paints one modal instruction row without decorative separator glyphs. */
|
|
7
|
+
export declare function renderShortcutHints(entries: readonly ShortcutHintEntry[], theme: Pick<UiTheme, "fg">, indent?: number): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { renderSemanticShortcutHints, } from "../../contracts/presentation/index.js";
|
|
2
|
+
/** Returns an unstyled modal instruction row for width-aware clipping. */
|
|
3
|
+
export function shortcutHintsText(entries, indent = 0) {
|
|
4
|
+
return renderSemanticShortcutHints(entries, { key: text => text, action: text => text }, indent);
|
|
5
|
+
}
|
|
6
|
+
/** Paints one modal instruction row without decorative separator glyphs. */
|
|
7
|
+
export function renderShortcutHints(entries, theme, indent = 0) {
|
|
8
|
+
return renderSemanticShortcutHints(entries, {
|
|
9
|
+
key: text => theme.fg("dim", text),
|
|
10
|
+
action: text => theme.fg("muted", text),
|
|
11
|
+
}, indent);
|
|
12
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* assembled, and any listing is generated from the same declarations — so a
|
|
5
5
|
* listed shortcut is one dispatch would actually invoke.
|
|
6
6
|
*/
|
|
7
|
+
import type { ShortcutHintEntry } from "./shortcut-hints.js";
|
|
7
8
|
export interface ShortcutDeclaration {
|
|
8
9
|
/** Key token, for example `up`, `shift+down`, `ctrl+c`, or a literal character. */
|
|
9
10
|
readonly key: string;
|
|
@@ -52,6 +53,7 @@ export declare class ShortcutRegistry<Action extends string = string> {
|
|
|
52
53
|
* the same declarations dispatch reads, so a key cannot be described here and
|
|
53
54
|
* bound to something else, or bound and never mentioned.
|
|
54
55
|
*/
|
|
56
|
+
hintEntries(scope: string): readonly ShortcutHintEntry[];
|
|
55
57
|
hint(scope: string, separator?: string): string;
|
|
56
58
|
/** The listing, derived from the declarations dispatch reads. */
|
|
57
59
|
list(scope?: string): readonly ShortcutDeclaration[];
|
|
@@ -60,7 +60,7 @@ export class ShortcutRegistry {
|
|
|
60
60
|
* the same declarations dispatch reads, so a key cannot be described here and
|
|
61
61
|
* bound to something else, or bound and never mentioned.
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
hintEntries(scope) {
|
|
64
64
|
const shown = new Map();
|
|
65
65
|
for (const declaration of this.#declarations) {
|
|
66
66
|
if (declaration.hint === undefined)
|
|
@@ -70,7 +70,10 @@ export class ShortcutRegistry {
|
|
|
70
70
|
if (!shown.has(declaration.hint.keys))
|
|
71
71
|
shown.set(declaration.hint.keys, declaration.hint.does);
|
|
72
72
|
}
|
|
73
|
-
return [...shown].map(([
|
|
73
|
+
return Object.freeze([...shown].map(([key, action]) => ({ key, action })));
|
|
74
|
+
}
|
|
75
|
+
hint(scope, separator = " · ") {
|
|
76
|
+
return this.hintEntries(scope).map(({ key, action }) => `${key} ${action}`).join(separator);
|
|
74
77
|
}
|
|
75
78
|
/** The listing, derived from the declarations dispatch reads. */
|
|
76
79
|
list(scope) {
|
|
@@ -101,7 +101,7 @@ export interface TranscriptViewportFrameDescriptor {
|
|
|
101
101
|
readonly transientRowCount: number;
|
|
102
102
|
/** Flexible rows inserted before the bottom-aligned live status while content fits. */
|
|
103
103
|
readonly transientAlignmentGapRows: number;
|
|
104
|
-
/**
|
|
104
|
+
/** Bottom-aligned transient rows. */
|
|
105
105
|
readonly bottomAlignedTailRowCount: number;
|
|
106
106
|
/** Visible rows owned by the currently streaming transcript block. */
|
|
107
107
|
readonly liveTailRows: number;
|
|
@@ -46,7 +46,7 @@ effect of stable publication, not a trigger.
|
|
|
46
46
|
|
|
47
47
|
| Trigger | Validation and outcome |
|
|
48
48
|
| --- | --- |
|
|
49
|
-
| Pull request into `develop` | Bounded PR-cadence validation; changed/new source documentation is checked once, rendering runs as `none`, `smoke`, or `full`,
|
|
49
|
+
| Pull request into `develop` | Bounded PR-cadence validation; changed/new source documentation is checked once, rendering runs as `none`, `smoke`, or `full`, exhaustive owners are reported as deferred, and only a trusted-CI-created failed-Full-regression repair adds PR-attached complete regression |
|
|
50
50
|
| `npm run develop` | Preview package gates on Windows, Linux, and macOS; an existing numbered preview is an early successful no-op |
|
|
51
51
|
| Nightly at `03:17 UTC` | One full documentation review plus the complete non-physical suite on Windows, Linux, and macOS, every night |
|
|
52
52
|
| `npm run release -- ...` | Stamps the requested stable version on the open `develop` source, runs the complete exact-byte stable gates, then npm `latest`, tag, GitHub Release, and `master`; one reopening PR follows |
|
|
@@ -60,9 +60,9 @@ effect of stable publication, not a trigger.
|
|
|
60
60
|
|
|
61
61
|
Ordinary type, architecture, unit/contract, and dist checks always run for code changes. Changed-file documentation and rendering run as independent parallel jobs. Rendered shell/component changes select `smoke`; viewport, scheduler, terminal adapter, evidence harness, package identity, and selector changes select `full`; unrelated changes select `none`. The aggregate accepts a skipped modular job only when the current selector requested the skip.
|
|
62
62
|
|
|
63
|
-
Integration owners declare one cadence in `config/integration-owners.json`. Impact mode selects affected `pull-request` owners: a changed production path selects its coarse owner, a changed test selects its owner, and a changed file under `test/support/` or `test/fixtures/` selects the owners of the retained tests that import it directly or through other support files (the selection lists those tests as its `shared-support` reason). A support file that no retained test imports, or a test tree the scanner cannot read, falls back to every owner the shared rule declares and records `shared-support-declared`; an invalidator, unknown operational path, or manual Development dispatch selects every `pull-request` owner. An implementation-bound PR body does not force conservative selection; it only removes the documentation-only and version-only shortcuts. The modular matrix itself is derived from the selection by `scripts/release/validation-matrix.mjs`, so inactive jobs are not scheduled at all rather than checking out and exiting early. `exhaustive` owners are never silently skipped or reported as passed: impact and aggregate evidence list them as cadence-deferred, and malformed cadence blocks selection. Full regression and nightly/stable release still execute both cadence classes.
|
|
63
|
+
Integration owners declare one cadence in `config/integration-owners.json`. Impact mode selects affected `pull-request` owners: a changed production path selects its coarse owner, a changed test selects its owner, and a changed file under `test/support/` or `test/fixtures/` selects the owners of the retained tests that import it directly or through other support files (the selection lists those tests as its `shared-support` reason). A support file that no retained test imports, or a test tree the scanner cannot read, falls back to every owner the shared rule declares and records `shared-support-declared`; an invalidator, unknown operational path, or manual Development dispatch selects every `pull-request` owner. An implementation-bound PR body does not force conservative selection; it only removes the documentation-only and version-only shortcuts. The modular matrix itself is derived from the selection by `scripts/release/validation-matrix.mjs`, so inactive jobs are not scheduled at all rather than checking out and exiting early. `exhaustive` owners are never silently skipped or reported as passed: impact and aggregate evidence list them as cadence-deferred, and malformed cadence blocks selection. Full regression and nightly/stable release still execute both cadence classes. PR-attached Full regression is selected only when base policy verifies an `openspec-ci` App-authored candidate, its generated repair identity, and every qualifying failed Full regression source field in `regression-provenance.json` against GitHub's read-only workflow-run record. Release/publishing, shared-support, prerequisite, build, validation-authority, unknown-path, label, Release-only repair, and successful-run persistent-overrun changes retain bounded ordinary validation. Planning-only generated repair drafts remain exempt; eligible implementation drafts receive native lane feedback without integration authority.
|
|
64
64
|
|
|
65
|
-
The real three-release `update-predecessor` scenario is exhaustive because four fresh npm installations dominated recent PR critical paths, and `update-performance` is exhaustive because its assertion is wall-clock timing on a shared runner; the update path's deterministic contracts stay on pull requests through `pi-release-resume` and `package-contracts`. PR validation retains deterministic predecessor command, lifecycle, fault, fixture, materialization, warmup, package, and update contracts. This permits a real published-history incompatibility or update slowdown to reach `develop` before the next exhaustive run detects it. `.github/workflows/full-regression.yml` runs every night at `02:47 UTC` against the `develop` tip with every owner and enforced budgets, independent of publication, so such a regression shows as a failed Full regression run the next morning; nightly publication's own complete validation still blocks publication. For
|
|
65
|
+
The real three-release `update-predecessor` scenario is exhaustive because four fresh npm installations dominated recent PR critical paths, and `update-performance` is exhaustive because its assertion is wall-clock timing on a shared runner; the update path's deterministic contracts stay on pull requests through `pi-release-resume` and `package-contracts`. PR validation retains deterministic predecessor command, lifecycle, fault, fixture, materialization, warmup, package, and update contracts. This permits a real published-history incompatibility or update slowdown to reach `develop` before the next exhaustive run detects it. `.github/workflows/full-regression.yml` runs every night at `02:47 UTC` against the `develop` tip with every owner and enforced budgets, independent of publication, so such a regression shows as a failed Full regression run the next morning; nightly publication's own complete validation still blocks publication. For diagnosis on any other candidate, dispatch standalone Full regression; labels and high-risk release/update impact do not add it to PR checks or change owner cadence.
|
|
66
66
|
|
|
67
67
|
Development outcomes report each owner/scope invocation separately while sharing authenticated build/package preparation. The aggregate reports setup, scope, job, aggregate-processing, total runner, and runner-critical-path durations. The acceptance targets are at most eight minutes of runner critical path and five minutes for one PR-required scope; an over-target result remains unmet without retries, timeout increases, workload reduction, or mutable installation caches. Hosted queue time is reported separately when available and is not counted as test execution.
|
|
68
68
|
|
|
@@ -106,7 +106,9 @@ node scripts/release/report-resource-sensitive-validation.mjs --repeats 3 --outp
|
|
|
106
106
|
|
|
107
107
|
## Pull request integration
|
|
108
108
|
|
|
109
|
-
`Development validation required` remains the merge gate for every pull request.
|
|
109
|
+
`Development validation required` remains the merge gate for every pull request. A trusted base-controlled selector binds current PR metadata, merge base, exact head, target, paths (including renamed-from and deleted repair history), and selection identity. When selected, the same PR run calls `.github/workflows/full-regression-shared.yml` and requires Windows 2025 Node 22/24, Ubuntu 24.04 Node 24, and macOS 15 Node 24 plus their exact-run outcome envelopes. Failed, cancelled, missing, stale, mismatched, or unexpectedly skipped work blocks the stable aggregate. Standalone `.github/workflows/full-regression.yml` retains its name, schedule, dispatch, non-cancelling behavior, and non-publishing authority; its result cannot substitute for selected PR evidence.
|
|
110
|
+
|
|
111
|
+
For applicable code changes, it requires the dedicated Defender-enabled exact-package startup lane on Windows Node 22. Development validation (including manual dispatch) does not schedule a Windows Node 24 startup lane. The Node 22 lane retains the complete package-install, image preparation/package, and durable-history checks plus startup evidence artifacts. A missing, cancelled, failed, or unexpectedly skipped required startup result still blocks the aggregate; documentation-only, version-only, and draft exemptions are unchanged.
|
|
110
112
|
|
|
111
113
|
| Startup runtime | Development validation (PR or manual) | Development preview (`npm run develop`) | Nightly and stable publication | Manual Full regression |
|
|
112
114
|
| --- | --- | --- | --- | --- |
|
|
@@ -115,13 +117,13 @@ node scripts/release/report-resource-sensitive-validation.mjs --repeats 3 --outp
|
|
|
115
117
|
|
|
116
118
|
Each selected startup lane runs the package-install scenarios once: a failed budget remains failed and is never retried to obtain a warmed result. The budgets are the ones the `a1-shell` capability declares (2 seconds after an update and on a warm launch, 2.5 seconds with no live supervisor); development validation, development previews, nightly publication, and Full regression record an overrun as a warning and are judged by the three-run persistent-overrun verdict, while stable publication fails on it (see `STARTUP_BUDGET_ENFORCEMENT` in [validation](validation.md)). The publication lane set comes from `scripts/release/publication-validation-matrix.mjs`: a numbered preview validates on the Windows, Linux, and macOS Node 24 lanes, and nightly covers the same head on Windows Node 22 within a day. Node 24 runtime support, other PR jobs, Defender, and publication gates are unchanged. Full validation retains every deferred startup, image, and history test through its existing suite owners.
|
|
117
119
|
|
|
118
|
-
The trade-off is delayed detection: a Node-24-specific regression can reach `develop` before nightly catches it, and the same is true for a real published-predecessor regression. A green bounded PR check does not certify Node 24 or real historical predecessor execution, and nightly failure still blocks its publication.
|
|
120
|
+
The trade-off is delayed detection: a Node-24-specific regression can reach `develop` before nightly catches it, and the same is true for a real published-predecessor regression. A green bounded PR check does not certify Node 24 or real historical predecessor execution, and nightly failure still blocks its publication. For an unselected candidate or diagnosis, explicitly request the non-publishing standalone Full regression workflow for the desired branch or tag:
|
|
119
121
|
|
|
120
122
|
```sh
|
|
121
123
|
gh workflow run full-regression.yml --ref <branch-or-tag>
|
|
122
124
|
```
|
|
123
125
|
|
|
124
|
-
Record the run's resolved source SHA and package evidence; do not substitute an unrelated historical green run for current-head acceptance. This
|
|
126
|
+
Record the run's resolved source SHA and package evidence; do not substitute an unrelated historical green run for current-head acceptance. This standalone run is a deliberate additional operation, not an automatic step for every PR and not selected PR authority.
|
|
125
127
|
|
|
126
128
|
Documentation auto-merge reads the complete GitHub changed-file response and arms
|
|
127
129
|
squash auto-merge for an eligible pull request while required validation is pending;
|
|
@@ -250,7 +252,7 @@ Rules that do not bend:
|
|
|
250
252
|
|
|
251
253
|
- **PR validation fails:** fix the code and push; do not mark a failed PR-cadence tier optional. If real predecessor history is needed, run Full regression rather than changing the exhaustive result into PR success.
|
|
252
254
|
- **Nightly startup budget overruns:** a single overrun on Full regression or nightly publication is a warning in the run summary, not a failure; the triage judges the key across the three most recent `develop` runs and opens a `package-startup` candidate only when all three overran. Stable publication still fails on one overrun; rerun it with the measured phases in hand.
|
|
253
|
-
- **Nightly regression fails:** the triage workflow opens or refreshes a draft `fix/nightly-regression-<date>` pull request whose body and change carry the evidence; start the fix there. Reproduce from the listed tests or commands on the failed lane
|
|
255
|
+
- **Nightly regression fails:** the triage workflow opens or refreshes a draft `fix/nightly-regression-<date>` pull request whose body and change carry the evidence and whose `regression-provenance.json` binds the source run; start the fix there. A failed Full regression source created by `openspec-ci` selects PR-attached Full regression, while a Release-only failure or successful-run persistent overrun retains ordinary PR validation. Reproduce from the listed tests or commands on the failed lane and fix without weakening assertions, budgets, timeouts, or coverage. Record focused implementation evidence and pre-finalization observations in the change, then require `Development validation required` and, when selected, PR-attached Full regression on the finalized exact head. Put final run/head/selection in Actions and handoff rather than making another evidence-recording commit. Standalone dispatch remains diagnostic; numbered-package nightly recovery is independent. A day whose failed scope set differs from every open candidate opens a new one. A Full regression dispatched on a candidate's own branch is that candidate's proof but never selected PR authority; its failures never open another candidate. A failure with no candidate means the triage workflow itself failed; read its run summary and dispatch it with the failed run id and `dry_run` off.
|
|
254
256
|
- **Nightly exhaustive predecessor validation fails:** treat the focused PR result as insufficient, keep publication blocked, and repair the incompatibility without reducing predecessor count, timeouts, or exact-package isolation.
|
|
255
257
|
- **Documentation auto-merge fails:** leave the pull request open, inspect its exact
|
|
256
258
|
changed-file classification, docs-sensitive inventory, and workflow permissions,
|
|
@@ -17,6 +17,12 @@ Version-1 and version-2 deliveries and their existing comments, acceptance PRs,
|
|
|
17
17
|
7. **Manual merge accepts:** after the stable protected aggregate succeeds, an authorized human reviews and manually merges the exact validated head. That single action means the listed scenarios are accepted and explicitly authorizes integration. Auto-merge, merge queue, Apps, bots, and documentation reconciliation are forbidden.
|
|
18
18
|
8. **Verify and clean:** trusted post-merge policy derives `Archived` and reports `accepted-and-archived` from committed bytes and immutable GitHub provenance without editing the accepted PR body. It publishes no lifecycle branch or PR. Shared exact-head remote cleanup may delete the unchanged topic ref; local cleanup remains separately ownership-controlled: the agent parks the worktree with `handoff` at step 7, and the next session's `sweep` removes it once the merge, archive, and remote-ref evidence verify (see [local cleanup](local-worktree-cleanup.md)).
|
|
19
19
|
|
|
20
|
+
### Complete-regression evidence ordering
|
|
21
|
+
|
|
22
|
+
PR-attached Full regression is selected only for a repair that the trusted triage created from a failed Full regression: base policy verifies the immutable `openspec-ci` App author, generated candidate identity, and `regression-provenance.json`. Human-authored lookalikes, publishing or validation-authority changes, labels, Release-only repairs, and successful-run persistent-overrun candidates retain bounded ordinary validation. Planning-only generated repair drafts remain lightweight. Eligible implementation drafts expose all four native lane checks without becoming mergeable. Finish substantive implementation tasks, focused evidence, pre-finalization observations, and known-gap disposition before marking ready. The finalized head then receives new full evidence, required by `Development validation required` alongside the existing gates.
|
|
23
|
+
|
|
24
|
+
The final exact-head run is a handoff gate, not a task requiring a future run ID in committed `design.md`. Keep final head/run/selection identity in Actions outcomes and the maintainer handoff; do not invalidate successful evidence with an extra recording commit. Changed code, acceptance/body metadata, selection labels, or target baseline requires fresh evidence. A selected PR success removes the additional mandatory Full regression dispatch, but a standalone/manual run never substitutes for selected PR checks. Before deployment, existing separate-dispatch obligations remain. Neither PR validation nor manual regression proves numbered-package nightly publication recovery.
|
|
25
|
+
|
|
20
26
|
The implementation, synchronized canonical specs, conditional acceptance record, and archive therefore reach `develop` atomically. Closing the PR unmerged integrates none of them.
|
|
21
27
|
|
|
22
28
|
## Draft PR body
|