@hamp10/agentforge 0.2.39 → 0.2.40

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamp10/agentforge",
3
- "version": "0.2.39",
3
+ "version": "0.2.40",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -296,6 +296,50 @@ try {
296
296
  'new untracked scoped page files should count as touched UI targets'
297
297
  );
298
298
  rmSync(path.join(fixture.repo, untrackedGammaRel), { force: true });
299
+ const untrackedShallowGammaHtml = [
300
+ '<!doctype html>',
301
+ '<html>',
302
+ '<head>',
303
+ '<style>',
304
+ ...Array.from({ length: 36 }, (_, i) =>
305
+ `.hero-gloss-${i} { color: #fff; background: #${String(120000 + i).slice(0, 6)}; padding: ${i + 4}px; margin: ${i}px; box-shadow: 0 0 ${i + 1}px rgba(0,0,0,.12); }`
306
+ ),
307
+ '</style>',
308
+ '</head>',
309
+ '<body>',
310
+ '<main>',
311
+ '<section class="property-hero hero-gloss-1">',
312
+ '<h1 class="property-title">Gamma.ai</h1>',
313
+ '<p class="property-subtitle">Short generated hero copy.</p>',
314
+ '</section>',
315
+ '</main>',
316
+ '</body>',
317
+ '</html>',
318
+ ].join('\n');
319
+ writeFileSync(path.join(fixture.repo, untrackedGammaRel), untrackedShallowGammaHtml);
320
+ const generatedQualityMessage = 'Work on the Example.com listing page for Gamma.ai. Make it visually polished.';
321
+ assert.match(
322
+ worker._buildShallowUiSurfaceNudge(
323
+ [{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
324
+ generatedQualityMessage
325
+ ),
326
+ /gamma\.html/i,
327
+ 'untracked generated scoped page files should participate in shallow UI quality checks'
328
+ );
329
+ const generatedReset = worker._restoreGeneratedScopedUiTargets(
330
+ [{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
331
+ generatedQualityMessage
332
+ );
333
+ assert.equal(
334
+ existsSync(path.join(fixture.repo, untrackedGammaRel)),
335
+ false,
336
+ 'UI quality retries should remove rejected generated scoped pages before the next attempt'
337
+ );
338
+ assert.match(
339
+ worker._formatGeneratedScopedUiResetNudge(generatedReset),
340
+ /removed 1 rejected generated scoped file/i,
341
+ 'retry feedback should tell the agent that generated scoped pages were removed as failed output'
342
+ );
299
343
  const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
300
344
  let agentWorkspace = null;
301
345
  try {
package/src/worker.js CHANGED
@@ -530,6 +530,24 @@ export class AgentForgeWorker extends EventEmitter {
530
530
  return stats;
531
531
  }
532
532
 
533
+ _listUntrackedUiFiles(baseline, userMessage, { scopedOnly = false } = {}) {
534
+ if (!baseline?.root) return [];
535
+ const uiFileRe = /\.(html?|css|s[ac]ss|jsx?|tsx?|vue|svelte|astro|mdx?)$/i;
536
+ const initialDirty = new Set(baseline.initialDirtyPaths || []);
537
+ const { slugs: allowedSlugs, pageOnly } = this._extractExplicitScope(userMessage);
538
+ return String(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000) || '')
539
+ .split('\n')
540
+ .map(line => line.trim())
541
+ .filter(Boolean)
542
+ .filter(rel => uiFileRe.test(rel))
543
+ .filter(rel => !initialDirty.has(rel))
544
+ .filter(rel => !this._isNestedProjectCopyRel(baseline.root, rel, baseline.head || 'HEAD'))
545
+ .filter(rel => {
546
+ if (!scopedOnly || allowedSlugs.length === 0) return true;
547
+ return this._scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage);
548
+ });
549
+ }
550
+
533
551
  _findDestructiveRepoChanges(repoBaselines, userMessage) {
534
552
  if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
535
553
  if (this._allowsLargeDestructiveChange(userMessage)) return [];
@@ -1158,7 +1176,20 @@ export class AgentForgeWorker extends EventEmitter {
1158
1176
  this._gitOutput(baseline.root, ['diff', '--numstat'], 10000),
1159
1177
  'working tree'
1160
1178
  );
1161
- const combined = [...committed, ...working]
1179
+ const untracked = this._listUntrackedUiFiles(baseline, userMessage, { scopedOnly: true })
1180
+ .map(rel => {
1181
+ let additions = 0;
1182
+ try {
1183
+ additions = String(readFileSync(path.join(baseline.root, rel), 'utf-8') || '')
1184
+ .split('\n')
1185
+ .filter(line => line.trim())
1186
+ .length;
1187
+ } catch {
1188
+ additions = 1;
1189
+ }
1190
+ return { additions, deletions: 0, path: rel, source: 'untracked' };
1191
+ });
1192
+ const combined = [...committed, ...working, ...untracked]
1162
1193
  .filter(stat => /\.(html?|css|s[ac]ss|jsx?|tsx?|vue|svelte|astro|mdx?)$/i.test(stat.path));
1163
1194
  if (combined.length === 0) continue;
1164
1195
 
@@ -1272,10 +1303,45 @@ export class AgentForgeWorker extends EventEmitter {
1272
1303
  }
1273
1304
  };
1274
1305
 
1306
+ const scanUntracked = (baseline, byPath) => {
1307
+ for (const currentFile of this._listUntrackedUiFiles(baseline, userMessage, { scopedOnly: true })) {
1308
+ let content = '';
1309
+ try {
1310
+ content = readFileSync(path.join(baseline.root, currentFile), 'utf-8');
1311
+ } catch {
1312
+ continue;
1313
+ }
1314
+ for (const line of String(content || '').split('\n')) {
1315
+ const raw = line.trim();
1316
+ if (!raw || /^(?:\/\*|\*|\/\/|<!--|-->|})$/.test(raw)) continue;
1317
+ const stat = byPath.get(currentFile) || {
1318
+ path: currentFile,
1319
+ changed: 0,
1320
+ shallow: 0,
1321
+ semanticAdds: 0,
1322
+ inlineStyleAdds: 0,
1323
+ sources: new Set(),
1324
+ };
1325
+ stat.changed += 1;
1326
+ stat.sources.add('untracked');
1327
+ if (shallowLineRe.test(raw)) stat.shallow += 1;
1328
+ if (/\bstyle=["'][^"']+["']/i.test(raw)) stat.inlineStyleAdds += 1;
1329
+ const text = visibleText(raw);
1330
+ const hasStructuralMarkup = /<(?:section|article|aside|ul|ol|li|h2|h3|p|form|table|figure)\b/i.test(raw);
1331
+ const hasSubstantiveClassWithText = structuralClassRe.test(raw) && /<[^>]+>/i.test(raw) && text.length >= 24;
1332
+ if ((hasStructuralMarkup && text.length >= 35) || hasSubstantiveClassWithText) {
1333
+ stat.semanticAdds += 1;
1334
+ }
1335
+ byPath.set(currentFile, stat);
1336
+ }
1337
+ }
1338
+ };
1339
+
1275
1340
  for (const baseline of repoBaselines) {
1276
1341
  const byPath = new Map();
1277
1342
  scanDiff(baseline, ['diff', '--unified=0', `${baseline.head}..HEAD`], 'committed', byPath);
1278
1343
  scanDiff(baseline, ['diff', '--unified=0'], 'working tree', byPath);
1344
+ scanUntracked(baseline, byPath);
1279
1345
  const changedFiles = [...byPath.values()];
1280
1346
  if (changedFiles.length === 0) continue;
1281
1347
 
@@ -4160,12 +4226,15 @@ export class AgentForgeWorker extends EventEmitter {
4160
4226
  : '';
4161
4227
  if (interimUiImplementationArtifactNudge) {
4162
4228
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimUiImplementationArtifactWarnings), nudgeCount + 1);
4229
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4230
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4231
+ );
4163
4232
  const interimScopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4164
4233
  nudgeCount++;
4165
4234
  const repairBudget = consumeUiRepairNudge('patch artifacts', interimUiImplementationArtifactNudge);
4166
4235
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4167
4236
  console.log(`[${taskId}] Iteration had patch artifacts — correcting (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4168
- const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4237
+ const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, generatedResetNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4169
4238
  iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context. Clean the implementation, address every requested scoped target page, and verify each edited target screen after the final edit.`);
4170
4239
  continue;
4171
4240
  }
@@ -4190,12 +4259,15 @@ export class AgentForgeWorker extends EventEmitter {
4190
4259
  : '';
4191
4260
  if (interimShallowUiSurfaceNudge) {
4192
4261
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimShallowUiSurfaceWarnings), nudgeCount + 1);
4262
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4263
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4264
+ );
4193
4265
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4194
4266
  nudgeCount++;
4195
4267
  const repairBudget = consumeUiRepairNudge('shallow UI surface changes', interimShallowUiSurfaceNudge);
4196
4268
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4197
4269
  console.log(`[${taskId}] Iteration had shallow UI surface changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4198
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimShallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better across the page-owned surface, then verify the edited target screens after the final edit.`);
4270
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimShallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better across the page-owned surface, then verify the edited target screens after the final edit.`);
4199
4271
  continue;
4200
4272
  }
4201
4273
  throw new Error(`Task produced shallow UI surface changes after repeated retries:\n${interimShallowUiSurfaceNudge}`);
@@ -4207,12 +4279,15 @@ export class AgentForgeWorker extends EventEmitter {
4207
4279
  : '';
4208
4280
  if (interimThinUiChangeNudge) {
4209
4281
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimThinUiChangeWarnings), nudgeCount + 1);
4282
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4283
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4284
+ );
4210
4285
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4211
4286
  nudgeCount++;
4212
4287
  const repairBudget = consumeUiRepairNudge('too-thin UI changes', interimThinUiChangeNudge);
4213
4288
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4214
4289
  console.log(`[${taskId}] Iteration had too-thin UI changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4215
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimThinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better, then verify the edited target screens after the final edit.`);
4290
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimThinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better, then verify the edited target screens after the final edit.`);
4216
4291
  continue;
4217
4292
  }
4218
4293
  throw new Error(`Task produced too-thin UI changes after repeated retries:\n${interimThinUiChangeNudge}`);
@@ -4314,12 +4389,15 @@ export class AgentForgeWorker extends EventEmitter {
4314
4389
  : '';
4315
4390
  if (uiImplementationArtifactNudge) {
4316
4391
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
4392
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4393
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4394
+ );
4317
4395
  const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4318
4396
  nudgeCount++;
4319
4397
  const repairBudget = consumeUiRepairNudge('completion patch artifacts', uiImplementationArtifactNudge);
4320
4398
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4321
4399
  console.log(`[${taskId}] UI completion had patch artifacts — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4322
- const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4400
+ const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4323
4401
  iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context. Do not mark complete until the UI source reads like finished product code and every requested scoped target screen is visually verified.`);
4324
4402
  continue;
4325
4403
  }
@@ -4342,12 +4420,15 @@ export class AgentForgeWorker extends EventEmitter {
4342
4420
  : '';
4343
4421
  if (shallowUiSurfaceNudge) {
4344
4422
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
4423
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4424
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4425
+ );
4345
4426
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4346
4427
  nudgeCount++;
4347
4428
  const repairBudget = consumeUiRepairNudge('completion shallow UI surface changes', shallowUiSurfaceNudge);
4348
4429
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4349
4430
  console.log(`[${taskId}] UI completion had shallow surface diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4350
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is improved across the page-owned product surface and visually verified.`);
4431
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is improved across the page-owned product surface and visually verified.`);
4351
4432
  continue;
4352
4433
  }
4353
4434
  throw new Error(`UI task claimed completion with shallow UI surface changes after repeated retries:\n${shallowUiSurfaceNudge}`);
@@ -4358,12 +4439,15 @@ export class AgentForgeWorker extends EventEmitter {
4358
4439
  : '';
4359
4440
  if (thinUiChangeNudge) {
4360
4441
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
4442
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4443
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4444
+ );
4361
4445
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4362
4446
  nudgeCount++;
4363
4447
  const repairBudget = consumeUiRepairNudge('completion too-thin UI changes', thinUiChangeNudge);
4364
4448
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4365
4449
  console.log(`[${taskId}] UI completion had too-thin diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4366
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is substantively improved and visually verified.`);
4450
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is substantively improved and visually verified.`);
4367
4451
  continue;
4368
4452
  }
4369
4453
  throw new Error(`UI task claimed completion with too-thin diff after repeated retries:\n${thinUiChangeNudge}`);
@@ -4572,12 +4656,15 @@ export class AgentForgeWorker extends EventEmitter {
4572
4656
  : '';
4573
4657
  if (uiImplementationArtifactNudge) {
4574
4658
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
4659
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4660
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4661
+ );
4575
4662
  const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4576
4663
  nudgeCount++;
4577
4664
  const repairBudget = consumeUiRepairNudge('publish patch artifacts', uiImplementationArtifactNudge);
4578
4665
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4579
4666
  console.log(`[${taskId}] Publish evidence had patch artifacts — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4580
- const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4667
+ const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4581
4668
  iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context, clean up the UI implementation, address every requested scoped target page, verify each target screen visually, then commit/push any additional changes before reporting delivery complete.`);
4582
4669
  continue;
4583
4670
  }
@@ -4600,12 +4687,15 @@ export class AgentForgeWorker extends EventEmitter {
4600
4687
  : '';
4601
4688
  if (shallowUiSurfaceNudge) {
4602
4689
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
4690
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4691
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4692
+ );
4603
4693
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4604
4694
  nudgeCount++;
4605
4695
  const repairBudget = consumeUiRepairNudge('publish shallow UI surface changes', shallowUiSurfaceNudge);
4606
4696
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4607
4697
  console.log(`[${taskId}] Publish evidence had shallow UI surface diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4608
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, improve the requested scoped UI across the page-owned product surface, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
4698
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, improve the requested scoped UI across the page-owned product surface, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
4609
4699
  continue;
4610
4700
  }
4611
4701
  throw new Error('Publish task had shallow UI surface changes after repeated retries');
@@ -4616,12 +4706,15 @@ export class AgentForgeWorker extends EventEmitter {
4616
4706
  : '';
4617
4707
  if (thinUiChangeNudge) {
4618
4708
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
4709
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4710
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4711
+ );
4619
4712
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4620
4713
  nudgeCount++;
4621
4714
  const repairBudget = consumeUiRepairNudge('publish too-thin UI changes', thinUiChangeNudge);
4622
4715
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4623
4716
  console.log(`[${taskId}] Publish evidence had too-thin UI diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4624
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, make the requested scoped UI improvement substantive, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
4717
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, make the requested scoped UI improvement substantive, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
4625
4718
  continue;
4626
4719
  }
4627
4720
  throw new Error('Publish task had too-thin UI diff after repeated retries');