Browse Source

Stabilize background job prompt status

DanMaly 1 month ago
parent
commit
d3455d9836
2 changed files with 35 additions and 24 deletions
  1. 29 15
      src/utils/background-job-board.test.ts
  2. 6 9
      src/utils/background-job-board.ts

+ 29 - 15
src/utils/background-job-board.test.ts

@@ -755,7 +755,7 @@ describe('BackgroundJobBoard', () => {
     expect(board.formatForPrompt('parent-1')).toContain('Reusable Sessions');
   });
 
-  test('annotates just-launched running jobs with age in the prompt', () => {
+  test('keeps initial running prompt output stable regardless of now', () => {
     const board = new BackgroundJobBoard();
     board.registerLaunch({
       taskID: 'ses_1',
@@ -765,12 +765,14 @@ describe('BackgroundJobBoard', () => {
       now: 1_000,
     });
 
-    // 4 seconds after launch - should show age annotation
-    const prompt = board.formatForPrompt('parent-1', 5_000);
-    expect(prompt).toContain('running [just launched, 4s ago]');
+    const promptAtLaunch = board.formatForPrompt('parent-1', 1_000);
+    const promptMuchLater = board.formatForPrompt('parent-1', 9_999_999_999);
+
+    expect(promptAtLaunch).toBe(promptMuchLater);
+    expect(promptAtLaunch).toContain('fix-1 / ses_1 / fixer / running');
   });
 
-  test('does not annotate running jobs older than 30s', () => {
+  test('relaunch changes the running state display to resumed', () => {
     const board = new BackgroundJobBoard();
     board.registerLaunch({
       taskID: 'ses_1',
@@ -780,10 +782,20 @@ describe('BackgroundJobBoard', () => {
       now: 1_000,
     });
 
-    // 39 seconds after launch - age label should be absent
-    const prompt = board.formatForPrompt('parent-1', 40_000);
-    expect(prompt).not.toContain('just launched');
-    expect(prompt).toContain('/ running\n');
+    const initialPrompt = board.formatForPrompt('parent-1', 1_000);
+    board.registerLaunch({
+      taskID: 'ses_1',
+      parentSessionID: 'parent-1',
+      agent: 'fixer',
+      description: 'implement feature continued',
+      now: 5_000,
+    });
+    const resumedPrompt = board.formatForPrompt('parent-1', 5_000);
+
+    expect(initialPrompt).toContain('fix-1 / ses_1 / fixer / running\n');
+    expect(resumedPrompt).toContain(
+      'fix-1 / ses_1 / fixer / running [resumed]',
+    );
   });
 
   test('registerLaunch can reset a reconciled job to running', () => {
@@ -816,9 +828,8 @@ describe('BackgroundJobBoard', () => {
     });
   });
 
-  test('annotates resumed running jobs with resumed label in the prompt', () => {
+  test('keeps resumed running prompt output stable regardless of now', () => {
     const board = new BackgroundJobBoard();
-    // Initial launch at t=1000
     board.registerLaunch({
       taskID: 'ses_1',
       parentSessionID: 'parent-1',
@@ -826,7 +837,6 @@ describe('BackgroundJobBoard', () => {
       description: 'implement feature',
       now: 1_000,
     });
-    // Reuse the same session ID at t=5000 (session reuse)
     board.registerLaunch({
       taskID: 'ses_1',
       parentSessionID: 'parent-1',
@@ -835,9 +845,13 @@ describe('BackgroundJobBoard', () => {
       now: 5_000,
     });
 
-    // 4 seconds after relaunch - should show [resumed, 4s ago]
-    const prompt = board.formatForPrompt('parent-1', 9_000);
-    expect(prompt).toContain('running [resumed, 4s ago]');
+    const promptAtResume = board.formatForPrompt('parent-1', 5_000);
+    const promptMuchLater = board.formatForPrompt('parent-1', 9_999_999_999);
+
+    expect(promptAtResume).toBe(promptMuchLater);
+    expect(promptAtResume).toContain(
+      'fix-1 / ses_1 / fixer / running [resumed]',
+    );
   });
 
   describe('intent-revealing query methods', () => {

+ 6 - 9
src/utils/background-job-board.ts

@@ -485,7 +485,7 @@ export class BackgroundJobBoard implements BackgroundJobStore {
 
   formatForPrompt(
     parentSessionID: string,
-    now = Date.now(),
+    _now?: number,
   ): string | undefined {
     const active = this.list(parentSessionID).filter(
       (job) => job.state === 'running' || job.terminalUnreconciled,
@@ -505,7 +505,7 @@ export class BackgroundJobBoard implements BackgroundJobStore {
         '',
         '#### Active / Unreconciled',
         ...(active.length > 0
-          ? active.map((job) => formatJob(job, now))
+          ? active.map(formatJob)
           : ['- none']),
         '',
         '#### Reusable Sessions',
@@ -623,20 +623,17 @@ function normalizeWhitespace(value: string): string {
   return value.replace(/\s+/g, ' ').trim();
 }
 
-function formatJob(job: BackgroundJobRecord, now = Date.now()): string {
-  const ageMs = now - job.lastLaunchedAt;
+function formatJob(job: BackgroundJobRecord): string {
   const isResume = job.lastLaunchedAt !== job.launchedAt;
-  const ageLabel =
-    job.state === 'running' && ageMs < 30_000
-      ? ` [${isResume ? 'resumed' : 'just launched'}, ${Math.floor(ageMs / 1000)}s ago]`
-      : '';
+  const state =
+    job.state === 'running' && isResume ? 'running [resumed]' : job.state;
   const status = job.terminalUnreconciled
     ? `${job.state}, unreconciled`
     : job.statusUncertain
       ? `${job.state}, status uncertain`
       : job.timedOut
         ? `${job.state}, timed out`
-        : `${job.state}${ageLabel}`;
+        : state;
   const lines = [
     `- ${promptSafe(job.alias)} / ${promptSafe(job.taskID)} / ${promptSafe(job.agent)} / ${promptSafe(status)}`,
     `  Objective: ${promptSafe(job.objective || job.description)}`,