@hamp10/agentforge 0.2.38 → 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.38",
3
+ "version": "0.2.40",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -282,6 +282,64 @@ try {
282
282
  'tracked same-name nested project copies should not pollute current page source discovery'
283
283
  );
284
284
  git(fixture.repo, ['restore', '--', nestedAlphaRel]);
285
+ const untrackedGammaRel = 'public_html/domains/gamma.html';
286
+ writeFileSync(
287
+ path.join(fixture.repo, untrackedGammaRel),
288
+ '<!doctype html><html><body><main><section><h1>Gamma</h1><p>New scoped page.</p></section></main></body></html>'
289
+ );
290
+ assert.equal(
291
+ worker._buildIncompleteScopedUiTargetsNudge(
292
+ [{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
293
+ 'Work on the Example.com listing pages for Alpha.ai, Beta.ai, and Gamma.ai. Make all three pages visually polished.'
294
+ ),
295
+ '',
296
+ 'new untracked scoped page files should count as touched UI targets'
297
+ );
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
+ );
285
343
  const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
286
344
  let agentWorkspace = null;
287
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
 
@@ -1540,9 +1606,18 @@ export class AgentForgeWorker extends EventEmitter {
1540
1606
  this._gitOutput(baseline.root, ['diff', '--numstat'], 10000),
1541
1607
  'working tree'
1542
1608
  );
1609
+ const initialDirty = new Set(baseline.initialDirtyPaths || []);
1610
+ const untracked = String(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000) || '')
1611
+ .split('\n')
1612
+ .map(line => line.trim())
1613
+ .filter(Boolean)
1614
+ .filter(rel => uiFileRe.test(rel))
1615
+ .filter(rel => !initialDirty.has(rel))
1616
+ .filter(rel => !this._isNestedProjectCopyRel(baseline.root, rel, baseline.head || 'HEAD'));
1543
1617
  const changedFiles = [...committed, ...working]
1544
1618
  .filter(stat => uiFileRe.test(stat.path))
1545
- .map(stat => stat.path);
1619
+ .map(stat => stat.path)
1620
+ .concat(untracked);
1546
1621
  if (changedFiles.length === 0) continue;
1547
1622
 
1548
1623
  const touchedSlugs = new Set();
@@ -4151,12 +4226,15 @@ export class AgentForgeWorker extends EventEmitter {
4151
4226
  : '';
4152
4227
  if (interimUiImplementationArtifactNudge) {
4153
4228
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimUiImplementationArtifactWarnings), nudgeCount + 1);
4229
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4230
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4231
+ );
4154
4232
  const interimScopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4155
4233
  nudgeCount++;
4156
4234
  const repairBudget = consumeUiRepairNudge('patch artifacts', interimUiImplementationArtifactNudge);
4157
4235
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4158
4236
  console.log(`[${taskId}] Iteration had patch artifacts — correcting (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4159
- const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4237
+ const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, generatedResetNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4160
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.`);
4161
4239
  continue;
4162
4240
  }
@@ -4181,12 +4259,15 @@ export class AgentForgeWorker extends EventEmitter {
4181
4259
  : '';
4182
4260
  if (interimShallowUiSurfaceNudge) {
4183
4261
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimShallowUiSurfaceWarnings), nudgeCount + 1);
4262
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4263
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4264
+ );
4184
4265
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4185
4266
  nudgeCount++;
4186
4267
  const repairBudget = consumeUiRepairNudge('shallow UI surface changes', interimShallowUiSurfaceNudge);
4187
4268
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4188
4269
  console.log(`[${taskId}] Iteration had shallow UI surface changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4189
- 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.`);
4190
4271
  continue;
4191
4272
  }
4192
4273
  throw new Error(`Task produced shallow UI surface changes after repeated retries:\n${interimShallowUiSurfaceNudge}`);
@@ -4198,12 +4279,15 @@ export class AgentForgeWorker extends EventEmitter {
4198
4279
  : '';
4199
4280
  if (interimThinUiChangeNudge) {
4200
4281
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimThinUiChangeWarnings), nudgeCount + 1);
4282
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4283
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4284
+ );
4201
4285
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4202
4286
  nudgeCount++;
4203
4287
  const repairBudget = consumeUiRepairNudge('too-thin UI changes', interimThinUiChangeNudge);
4204
4288
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4205
4289
  console.log(`[${taskId}] Iteration had too-thin UI changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4206
- 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.`);
4207
4291
  continue;
4208
4292
  }
4209
4293
  throw new Error(`Task produced too-thin UI changes after repeated retries:\n${interimThinUiChangeNudge}`);
@@ -4305,12 +4389,15 @@ export class AgentForgeWorker extends EventEmitter {
4305
4389
  : '';
4306
4390
  if (uiImplementationArtifactNudge) {
4307
4391
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
4392
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4393
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4394
+ );
4308
4395
  const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4309
4396
  nudgeCount++;
4310
4397
  const repairBudget = consumeUiRepairNudge('completion patch artifacts', uiImplementationArtifactNudge);
4311
4398
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4312
4399
  console.log(`[${taskId}] UI completion had patch artifacts — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4313
- const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4400
+ const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4314
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.`);
4315
4402
  continue;
4316
4403
  }
@@ -4333,12 +4420,15 @@ export class AgentForgeWorker extends EventEmitter {
4333
4420
  : '';
4334
4421
  if (shallowUiSurfaceNudge) {
4335
4422
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
4423
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4424
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4425
+ );
4336
4426
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4337
4427
  nudgeCount++;
4338
4428
  const repairBudget = consumeUiRepairNudge('completion shallow UI surface changes', shallowUiSurfaceNudge);
4339
4429
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4340
4430
  console.log(`[${taskId}] UI completion had shallow surface diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4341
- 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.`);
4342
4432
  continue;
4343
4433
  }
4344
4434
  throw new Error(`UI task claimed completion with shallow UI surface changes after repeated retries:\n${shallowUiSurfaceNudge}`);
@@ -4349,12 +4439,15 @@ export class AgentForgeWorker extends EventEmitter {
4349
4439
  : '';
4350
4440
  if (thinUiChangeNudge) {
4351
4441
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
4442
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4443
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4444
+ );
4352
4445
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4353
4446
  nudgeCount++;
4354
4447
  const repairBudget = consumeUiRepairNudge('completion too-thin UI changes', thinUiChangeNudge);
4355
4448
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4356
4449
  console.log(`[${taskId}] UI completion had too-thin diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4357
- 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.`);
4358
4451
  continue;
4359
4452
  }
4360
4453
  throw new Error(`UI task claimed completion with too-thin diff after repeated retries:\n${thinUiChangeNudge}`);
@@ -4563,12 +4656,15 @@ export class AgentForgeWorker extends EventEmitter {
4563
4656
  : '';
4564
4657
  if (uiImplementationArtifactNudge) {
4565
4658
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
4659
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4660
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4661
+ );
4566
4662
  const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
4567
4663
  nudgeCount++;
4568
4664
  const repairBudget = consumeUiRepairNudge('publish patch artifacts', uiImplementationArtifactNudge);
4569
4665
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4570
4666
  console.log(`[${taskId}] Publish evidence had patch artifacts — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4571
- const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4667
+ const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
4572
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.`);
4573
4669
  continue;
4574
4670
  }
@@ -4591,12 +4687,15 @@ export class AgentForgeWorker extends EventEmitter {
4591
4687
  : '';
4592
4688
  if (shallowUiSurfaceNudge) {
4593
4689
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
4690
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4691
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4692
+ );
4594
4693
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4595
4694
  nudgeCount++;
4596
4695
  const repairBudget = consumeUiRepairNudge('publish shallow UI surface changes', shallowUiSurfaceNudge);
4597
4696
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4598
4697
  console.log(`[${taskId}] Publish evidence had shallow UI surface diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4599
- 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.`);
4600
4699
  continue;
4601
4700
  }
4602
4701
  throw new Error('Publish task had shallow UI surface changes after repeated retries');
@@ -4607,12 +4706,15 @@ export class AgentForgeWorker extends EventEmitter {
4607
4706
  : '';
4608
4707
  if (thinUiChangeNudge) {
4609
4708
  const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
4709
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4710
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4711
+ );
4610
4712
  const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
4611
4713
  nudgeCount++;
4612
4714
  const repairBudget = consumeUiRepairNudge('publish too-thin UI changes', thinUiChangeNudge);
4613
4715
  if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
4614
4716
  console.log(`[${taskId}] Publish evidence had too-thin UI diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4615
- 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.`);
4616
4718
  continue;
4617
4719
  }
4618
4720
  throw new Error('Publish task had too-thin UI diff after repeated retries');