@galda/cli 0.10.73 → 0.10.75
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/app/index.html +61 -12
- package/engine/lib.mjs +32 -0
- package/engine/server.mjs +14 -1
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -576,7 +576,8 @@
|
|
|
576
576
|
.riskfiles{font-family:var(--mono);font-size:10px;color:var(--ink3)}
|
|
577
577
|
/* task 161/162: Review-row quick actions — aligned to the right of the row,
|
|
578
578
|
vertically centered; icon (no emoji) reply; subtle inline reply box. */
|
|
579
|
-
.revquick{display:flex;gap:
|
|
579
|
+
.revquick{display:flex;gap:2px;flex:0 0 auto;align-items:center;align-self:center;margin-left:8px;opacity:0;transition:opacity .15s}
|
|
580
|
+
.task:hover .revquick,.digestrow:hover .revquick,.revquick:focus-within{opacity:1}
|
|
580
581
|
.qbtn{width:26px;height:26px;flex:0 0 26px;border:1px solid var(--hair2);border-radius:7px;background:transparent;cursor:pointer;font-size:12px;line-height:0;display:grid;place-items:center;color:var(--ink2);padding:0}
|
|
581
582
|
.qbtn svg{width:14px;height:14px}
|
|
582
583
|
.qbtn.ok{color:var(--green);border-color:rgba(var(--green-rgb),.4)}
|
|
@@ -584,6 +585,15 @@
|
|
|
584
585
|
.qbtn.no{color:var(--danger);border-color:rgba(var(--danger-rgb),.4)}
|
|
585
586
|
.qbtn.no:hover{background:rgba(var(--danger-rgb),.12)}
|
|
586
587
|
.qbtn:hover{color:var(--ink)}
|
|
588
|
+
/* Review行だけ: flagshipの .act/.b パターン(枠なし・ホバーで出現・面を塗らずグリフ色のみ) */
|
|
589
|
+
.revquick .qbtn{border:0;border-radius:6px}
|
|
590
|
+
.revquick .qbtn.ok{color:var(--ink3);border-color:transparent;background:transparent}
|
|
591
|
+
.revquick .qbtn.ok:hover{color:var(--green);background:transparent}
|
|
592
|
+
.revquick .qbtn.no{color:var(--ink3);border-color:transparent;background:transparent}
|
|
593
|
+
.revquick .qbtn.no:hover{color:var(--danger);background:transparent}
|
|
594
|
+
.revquick .qbtn.reply{color:var(--ink3)}
|
|
595
|
+
.revquick .qbtn.reply:hover{color:var(--ink)}
|
|
596
|
+
.revquick .qbtn:active{transform:scale(.92)}
|
|
587
597
|
.qreplyrow{display:flex;gap:6px;margin-top:8px;width:100%}
|
|
588
598
|
.qreplyrow[hidden]{display:none}
|
|
589
599
|
.qreplyrow input{flex:1;min-width:0;height:30px;border:1px solid var(--hair);border-radius:8px;background:transparent;color:var(--ink);padding:0 10px;font:inherit;font-size:12px}
|
|
@@ -2222,6 +2232,12 @@
|
|
|
2222
2232
|
.redesign .ghmore .chev path{stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2223
2233
|
.redesign .ghmore.open .chev{transform:rotate(180deg)}
|
|
2224
2234
|
.redesign .ghmore .n{color:var(--ink2);font-variant-numeric:tabular-nums}
|
|
2235
|
+
/* Long-Request fold (Masa 2026-07-27, overrides spec §4 "no accordion" — that rule
|
|
2236
|
+
assumed Request is always the user's short ask; a mispaste of another card's whole
|
|
2237
|
+
content proved that assumption false). No bespoke widget: it reuses the SAME `.lacc`
|
|
2238
|
+
pulldown as Activity/Conversation below (§3 — chevron on the first line, no left
|
|
2239
|
+
rule, offset-indented body when open), rather than the diff-fold's fade+button shape
|
|
2240
|
+
("他の›と同じルールじゃなくない?" — Masa 2026-07-27). No extra CSS needed here. */
|
|
2225
2241
|
/* meta links carry NO underline — PR is a real <a href> (spec §5, Masa 2026-07-20) */
|
|
2226
2242
|
.redesign .rmeta a{text-decoration:none}
|
|
2227
2243
|
/* Request / What-to-check = full contrast, same ink as the title (spec §2, no fade) */
|
|
@@ -3418,6 +3434,8 @@ const FINISHED = (s) => ['done', 'partial', 'failed', 'interrupted'].includes(s)
|
|
|
3418
3434
|
const ICONS = {
|
|
3419
3435
|
pencil: '<svg viewBox="0 0 24 24"><path d="M4 20l1-4L16.5 4.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>',
|
|
3420
3436
|
reply: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 17l-5-5 5-5"/><path d="M4 12h11a4 4 0 0 1 4 4v3"/></svg>',
|
|
3437
|
+
check: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>',
|
|
3438
|
+
close: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6l12 12M18 6L6 18"/></svg>',
|
|
3421
3439
|
up: '<svg viewBox="0 0 24 24"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',
|
|
3422
3440
|
down: '<svg viewBox="0 0 24 24"><path d="M12 5v14M5 12l7 7 7-7"/></svg>',
|
|
3423
3441
|
trash: '<svg viewBox="0 0 24 24"><path d="M4 7h16M9 7V5a1 1 0 011-1h4a1 1 0 011 1v2M6.5 7l1 13h9l1-13M10 11v6M14 11v6"/></svg>',
|
|
@@ -4097,8 +4115,8 @@ function reviewDigestHtml(rows){
|
|
|
4097
4115
|
<div class="digestmeta">${testBadge}${prLink}</div>
|
|
4098
4116
|
</div>
|
|
4099
4117
|
<div class="revquick">
|
|
4100
|
-
<button type="button" class="qbtn ok" data-qapprove="${ids}" title="Approve"
|
|
4101
|
-
<button type="button" class="qbtn no" data-qdismiss="${ids}" title="Send back"
|
|
4118
|
+
<button type="button" class="qbtn ok" data-qapprove="${ids}" title="Approve">${ICONS.check}</button>
|
|
4119
|
+
<button type="button" class="qbtn no" data-qdismiss="${ids}" title="Send back">${ICONS.close}</button>
|
|
4102
4120
|
</div>
|
|
4103
4121
|
</div>`;
|
|
4104
4122
|
};
|
|
@@ -4686,8 +4704,8 @@ function renderTasks(){
|
|
|
4686
4704
|
<div class="cond">${g.pr ? `<a class="prbig" href="${esc(g.pr)}" target="_blank">PR #${esc((g.pr.match(/pull\/(\d+)/) ?? [,''])[1])} ↗</a>` : ''}</div>
|
|
4687
4705
|
<div class="qreplyrow" data-qreplyrow="${g.id}"${state.qreplyOpen.has(g.id) ? '' : ' hidden'}><input type="text" data-qreplyinput="${g.id}" placeholder="直したい点を書いて送信(Todoに戻して再作業)"><button type="button" class="qreplysend" data-qreplysend="${g.id}" aria-label="Send">${ICONS.up}</button></div></div>
|
|
4688
4706
|
<div class="revquick">
|
|
4689
|
-
<button type="button" class="qbtn ok" data-qapprove="${g.id}" title="Approve"
|
|
4690
|
-
<button type="button" class="qbtn no" data-qdismiss="${g.id}" title="Send back"
|
|
4707
|
+
<button type="button" class="qbtn ok" data-qapprove="${g.id}" title="Approve">${ICONS.check}</button>
|
|
4708
|
+
<button type="button" class="qbtn no" data-qdismiss="${g.id}" title="Send back">${ICONS.close}</button>
|
|
4691
4709
|
<button type="button" class="qbtn reply" data-qreply="${g.id}" title="Reply">${ICONS.reply}</button>
|
|
4692
4710
|
</div></div>`;
|
|
4693
4711
|
}).join('')
|
|
@@ -8358,9 +8376,12 @@ async function refresh(){
|
|
|
8358
8376
|
// events — especially worker activity — don't saturate the main thread and drop
|
|
8359
8377
|
// keystrokes while typing.
|
|
8360
8378
|
let _renderT = null, _renderP = false;
|
|
8361
|
-
|
|
8379
|
+
// A thrown render() must not stall the throttle loop for good (live feed freeze,
|
|
8380
|
+
// e.g. on review reply) — catch it, log it, and keep scheduling so the next SSE
|
|
8381
|
+
// event still gets a paint attempt.
|
|
8382
|
+
function scheduleRender(){ if (_renderT) { _renderP = true; return; } try { render(); } catch (e) { console.error('[render]', e); } _renderT = setTimeout(() => { _renderT = null; if (_renderP) { _renderP = false; scheduleRender(); } }, 120); }
|
|
8362
8383
|
let _actT = null, _actP = false;
|
|
8363
|
-
function scheduleActRender(){ if (_actT) { _actP = true; return; } renderActPanel(); if (state.layout === 'flagship') renderFsFeed(); _actT = setTimeout(() => { _actT = null; if (_actP) { _actP = false; scheduleActRender(); } }, 200); }
|
|
8384
|
+
function scheduleActRender(){ if (_actT) { _actP = true; return; } try { renderActPanel(); if (state.layout === 'flagship') renderFsFeed(); } catch (e) { console.error('[actRender]', e); } _actT = setTimeout(() => { _actT = null; if (_actP) { _actP = false; scheduleActRender(); } }, 200); }
|
|
8364
8385
|
// (connection refused = fatal, no auto-retry), so we own the reconnect loop.
|
|
8365
8386
|
let esRetryMs = 1000;
|
|
8366
8387
|
let sseUp = false; // live engine link — the honest signal behind the MCP tab's status dot (v46)
|
|
@@ -9484,6 +9505,24 @@ function saMsgRowsHtml(msgs, expanded){
|
|
|
9484
9505
|
// word-level highlight (h) and, for a huge change, a "…+N 行 · GitHub で開く ↗"
|
|
9485
9506
|
// footer (only linking to GitHub when a PR exists). Mirrors the flagship artifact's
|
|
9486
9507
|
// ghDiffHtml so the app pixel-matches. `more` = extra unshown line count; `pr` = url.
|
|
9508
|
+
// Request fold threshold (Masa 2026-07-27): Request is supposed to be the user's short
|
|
9509
|
+
// ask (spec §4), but a mispaste — e.g. another card's whole content (Request/Result/
|
|
9510
|
+
// Activity) copied in — can make it huge and invisible in the single-line reply input
|
|
9511
|
+
// it came from. Anything past this size folds behind the same `.lacc` pulldown used by
|
|
9512
|
+
// Activity/Conversation instead of the card growing unbounded; a normal short ask stays
|
|
9513
|
+
// under the threshold and never shows the control. Length OR newline-count catches both
|
|
9514
|
+
// "one giant line" and "many lines" (goal.text is whitespace-collapsed before it reaches
|
|
9515
|
+
// the client, so in practice the length check is what fires; the newline check guards
|
|
9516
|
+
// against a future caller that preserves them).
|
|
9517
|
+
const REQUEST_FOLD_OVER = 220;
|
|
9518
|
+
// How many characters of a folded Request sit next to the closed `›` (spec §3's "first
|
|
9519
|
+
// line") — goal.text has no real line breaks by the time it's client-side (whitespace
|
|
9520
|
+
// is collapsed to single spaces), so this is a character peek standing in for "line 1".
|
|
9521
|
+
const REQUEST_PEEK_CHARS = 140;
|
|
9522
|
+
function shouldFoldRequestText(text){
|
|
9523
|
+
const t = String(text || '');
|
|
9524
|
+
return t.length > REQUEST_FOLD_OVER || (t.match(/\n/g) || []).length >= 2;
|
|
9525
|
+
}
|
|
9487
9526
|
const SA_DIFF_FOLD_OVER = 14;
|
|
9488
9527
|
function saGhDiffRowsHtml(rows, more, pr, goalId){
|
|
9489
9528
|
if (!rows || !rows.length) return '';
|
|
@@ -9629,11 +9668,19 @@ function saItemHtml(it, i){
|
|
|
9629
9668
|
// §1.5-3: with Open it moved into the body, a card with no PR / tokens / tests has no
|
|
9630
9669
|
// chips at all — render no meta row rather than an empty one holding open a gap.
|
|
9631
9670
|
const meta = chips.length ? `<div class="rmeta">${chips.join('')}</div>${metaDetail}` : '';
|
|
9632
|
-
// ── Request (spec §4): a PLAIN section — the user's own short words (goal.text)
|
|
9633
|
-
//
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9671
|
+
// ── Request (spec §4): a PLAIN section — the user's own short words (goal.text), never
|
|
9672
|
+
// the implementation spec (that lives in Activity below). A normal short ask is never an
|
|
9673
|
+
// accordion; a mispasted giant Request instead folds using the SAME `.lacc` pulldown as
|
|
9674
|
+
// Activity/Conversation below (spec §3: chevron on the first line, offset-indented body,
|
|
9675
|
+
// no left rule) — not a bespoke widget (Masa 2026-07-27: "他の›と同じルールじゃなくない?").
|
|
9676
|
+
// §4 assumed Request was always short, which a mispaste can break; this reuses the one
|
|
9677
|
+
// pulldown shape the card already has rather than inventing a second one. ──
|
|
9678
|
+
const reqFold = it.goalText && shouldFoldRequestText(it.goalText);
|
|
9679
|
+
const reqOpen = reqFold && SA.detOpen.has(`${gk}:req`);
|
|
9680
|
+
const reqPeek = reqFold ? `${it.goalText.slice(0, REQUEST_PEEK_CHARS).trimEnd()}…` : '';
|
|
9681
|
+
const reqSec = !it.goalText ? '' : reqFold
|
|
9682
|
+
? `<section class="rsec rreq"><div class="rsec-h">Request</div><details class="lacc" data-detkey="${gk}:req"${reqOpen ? ' open' : ''}><summary><i class="ltog">›</i><p class="rbody rask lfirst">${esc(reqPeek)}</p></summary><div class="lbody"><p class="rbody rask">${esc(it.goalText)}</p></div></details></section>`
|
|
9683
|
+
: `<section class="rsec rreq"><div class="rsec-h">Request</div><p class="rbody rask">${esc(it.goalText)}</p></section>`;
|
|
9637
9684
|
// ── Open it (Masa 2026-07-22): a plain section in the card's own typography — the same
|
|
9638
9685
|
// heading + body shape as Request and Result, not a chip in another vocabulary. The
|
|
9639
9686
|
// address the worker declared IS the control (press it → the process starts on demand →
|
|
@@ -10274,6 +10321,8 @@ $('seeall').addEventListener('click', async (e) => {
|
|
|
10274
10321
|
if (label) label.innerHTML = open ? 'Collapse' : `Show all <span class="n">${diffMore.dataset.total}</span> lines`;
|
|
10275
10322
|
return;
|
|
10276
10323
|
}
|
|
10324
|
+
// (A folded Request's toggle needs no handler here — it's a `[data-detkey]` `.lacc`
|
|
10325
|
+
// <details>, same as Activity/Conversation, wired generically at render time.)
|
|
10277
10326
|
const removeImage = e.target.closest('[data-saarm]');
|
|
10278
10327
|
if (removeImage) {
|
|
10279
10328
|
const [goalId, index] = removeImage.dataset.saarm.split(':').map(Number);
|
package/engine/lib.mjs
CHANGED
|
@@ -1277,6 +1277,38 @@ export function taskStatusAfterVerify({ verdict, code, timedOut }) {
|
|
|
1277
1277
|
return code === 0 ? 'done' : 'failed';
|
|
1278
1278
|
}
|
|
1279
1279
|
|
|
1280
|
+
// task 450 (goal #783, task 448): a worker can exit 0 with changedFiles: []
|
|
1281
|
+
// and a result that reads as a thought cut off mid-sentence (e.g. "I'll wait
|
|
1282
|
+
// for this background full-suite run to complete rather than poll.") — no
|
|
1283
|
+
// work happened, but taskStatusAfterVerify (decided from `code` alone, before
|
|
1284
|
+
// changedFiles even exists) already called it 'done'. This is the same
|
|
1285
|
+
// principle the product already applies to verification (don't trust
|
|
1286
|
+
// self-report, judge by evidence) applied to task completion itself.
|
|
1287
|
+
// Deliberately narrow, per the same 0-file-legitimate-report carve-out as
|
|
1288
|
+
// isNothingVerifiable above: a real investigation/report task also has zero
|
|
1289
|
+
// changed files, but its result reads as a normal finished sentence, so only
|
|
1290
|
+
// text that also LOOKS cut off trips this.
|
|
1291
|
+
export function looksIncomplete(text) {
|
|
1292
|
+
const t = String(text ?? '').trim();
|
|
1293
|
+
if (!t) return true;
|
|
1294
|
+
if (t.length >= 200) return false;
|
|
1295
|
+
// A short blurb ending mid-word/mid-clause (no sentence-final punctuation
|
|
1296
|
+
// in any of ja/en) reads as truncated, not as a deliberate one-liner.
|
|
1297
|
+
if (!/[.!?。!?」))]$/.test(t)) return true;
|
|
1298
|
+
// goal-783/task-448's actual fragment ("I'll wait for this background
|
|
1299
|
+
// full-suite run to complete rather than poll.") IS a grammatically
|
|
1300
|
+
// complete sentence, so the check above misses it — but it's a one-shot
|
|
1301
|
+
// batch worker narrating a future intent ("I'll…"/"let me…"), never a
|
|
1302
|
+
// report of something actually done. That opener is itself the signal.
|
|
1303
|
+
return /^(i'll|i will|i'm going to|i am going to|let me)\b/i.test(t);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
export function isSuspiciouslyIncompleteDone({ status, changedFiles = [], result = '', hasVerdict = false } = {}) {
|
|
1307
|
+
if (status !== 'done' || hasVerdict) return false;
|
|
1308
|
+
if ((changedFiles ?? []).length > 0) return false;
|
|
1309
|
+
return looksIncomplete(result);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1280
1312
|
// A task counts as complete for GOAL status when it cleanly finished
|
|
1281
1313
|
// (done/skipped) OR it was force-stopped (interrupted) but its change is STRONGLY
|
|
1282
1314
|
// verified — an independent passCondition verification PASSED (proof.verified &&
|
package/engine/server.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { homedir } from 'node:os';
|
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { pathToFileURL } from 'node:url';
|
|
23
23
|
import { needsProjectFolder, folderLabel, chooseFolderScript, parseChosenFolder, buildFolderQuestion, resolveFolderAnswer, classifyFolderTarget, buildFolderInitConfirm, isFolderInitConfirmed, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer, routeQuestionAnswer, notUnderstoodNote, parseRelocalizedQuestion, classifyAuthProbe, authBannerText, makeSigninChallenge, parseClaimResponse, isCommandOnPath } from './lib.mjs';
|
|
24
|
-
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, buildGoalProofMd, buildGoalPrBody, buildReviewSummaryPrompt, parseReviewSummary, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, rejectGoal, reopenGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, resolveTestGate, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, workerPrompt, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, QUEUED_GOAL_STATUSES, verifyLicenseToken, licenseTokenPayload, shouldRefreshLicense, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, REPLY_INTENTS, buildReplyIntentPrompt, parseReplyIntent, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession } from './lib.mjs';
|
|
24
|
+
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, buildGoalProofMd, buildGoalPrBody, buildReviewSummaryPrompt, parseReviewSummary, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, rejectGoal, reopenGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, resolveTestGate, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, workerPrompt, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, QUEUED_GOAL_STATUSES, verifyLicenseToken, licenseTokenPayload, shouldRefreshLicense, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, isSuspiciouslyIncompleteDone, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, REPLY_INTENTS, buildReplyIntentPrompt, parseReplyIntent, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession } from './lib.mjs';
|
|
25
25
|
import { createSerialQueue } from './lib.mjs';
|
|
26
26
|
import { isPrClosed } from './lib.mjs';
|
|
27
27
|
import { collectProjectRules } from './lib.mjs';
|
|
@@ -2921,6 +2921,19 @@ async function runTask(task) {
|
|
|
2921
2921
|
const after = await gitChanges(workDir);
|
|
2922
2922
|
task.changedFiles = after.filter((l) => !before.includes(l)).map((l) => l.slice(3));
|
|
2923
2923
|
sendProcessAct(task, `Detected ${task.changedFiles.length} changed file${task.changedFiles.length === 1 ? '' : 's'} from this task.`);
|
|
2924
|
+
// task 450: taskStatusAfterVerify decided 'done' from `code` alone, before
|
|
2925
|
+
// changedFiles existed to check against. Don't trust a clean exit code by
|
|
2926
|
+
// itself — a worker that changed nothing and left a cut-off-looking result
|
|
2927
|
+
// (goal #783 task 448: "I'll wait for this background full-suite run to
|
|
2928
|
+
// complete rather than poll.") did not actually finish. Downgrade to
|
|
2929
|
+
// 'interrupted' (retryable, surfaces in Attention) instead of silently
|
|
2930
|
+
// shipping it as done. Never touches a real 0-file report/investigation
|
|
2931
|
+
// task, whose result reads as a normal finished sentence.
|
|
2932
|
+
if (isSuspiciouslyIncompleteDone({ status: task.status, changedFiles: task.changedFiles, result: task.result, hasVerdict: Boolean(verdict) })) {
|
|
2933
|
+
task.status = 'interrupted';
|
|
2934
|
+
task.result = `[未完了の疑い: 変更ファイル0件かつ応答が途中で切れています] ${task.result}`.slice(0, RESULT_MAX);
|
|
2935
|
+
sendProcessAct(task, 'Result looked incomplete (0 changed files, cut-off text) — marked interrupted instead of done for human review.');
|
|
2936
|
+
}
|
|
2924
2937
|
task.finishedAt = new Date().toISOString();
|
|
2925
2938
|
task.resultAttemptId = task.attemptId;
|
|
2926
2939
|
saveTask(task);
|
package/package.json
CHANGED