Kaynağa Gözat

s3: delta resume notices — take_resume_notice emits ONE-LINE task statuses (status + the summary's first line) with full bodies only for NEW completions each exactly once; the statuses/reports double embedding is dead, so the notice size is constant per event instead of cumulative with every parked round.

clanker 1 ay önce
ebeveyn
işleme
0d1f6a97f4
2 değiştirilmiş dosya ile 100 ekleme ve 8 silme
  1. 27 8
      src/Services/GoalService.vala
  2. 73 0
      test/web_stack_test.py

+ 27 - 8
src/Services/GoalService.vala

@@ -990,10 +990,14 @@ namespace ClankerSaddle.Services {
         /**
          * The batched markdown report riding a goal-agent resume (the
          * assignment's `resume_notice`): the current status of ALL of
-         * the goal's tasks plus the full completion reports of the
-         * tasks that finished since the agent parked. Consumes the
-         * pending transitions — the terminal watermark advances past
-         * every transition visible now (and past the clock, so a
+         * the goal's tasks as ONE-LINE entries (status + the first
+         * line of the terminal summary) plus the FULL completion
+         * reports of the tasks that finished since the agent parked —
+         * each new completion's body exactly once (plan 16 S3: no
+         * statuses-section/reports-section double embedding; the
+         * notice size is constant per event, not cumulative). Consumes
+         * the pending transitions — the terminal watermark advances
+         * past every transition visible now (and past the clock, so a
          * same-second park/consume pair cannot re-fire) — so the
          * window starts fresh for the next park. Plan 15 C3: the
          * watermark replaced the goal_events id cursor.
@@ -1016,15 +1020,15 @@ namespace ClankerSaddle.Services {
                     .order_by(expr("id"))
                     .materialise_async()) {
                 if (task.status == STATUS_DONE) {
-                    report.append_printf("- #%s %s — done: %s\n", task.id.to_string(), task.title,
-                        task.summary ?? "no summary reported");
+                    report.append_printf("- #%s %s — done: %s\n", task.id.to_string(),
+                        task.title, notice_lead(task));
                     if (since == null || task.completed_at == null
                             || ((!)task.completed_at).compare((!)since) > 0) {
                         finished.add(task);
                     }
                 } else if (task.status == STATUS_FAILED) {
-                    report.append_printf("- #%s %s — failed: %s\n", task.id.to_string(), task.title,
-                        task.summary ?? "no summary reported");
+                    report.append_printf("- #%s %s — failed: %s\n", task.id.to_string(),
+                        task.title, notice_lead(task));
                 } else {
                     report.append_printf("- #%s %s — %s\n", task.id.to_string(), task.title, task.status);
                 }
@@ -1055,6 +1059,21 @@ namespace ClankerSaddle.Services {
             return report.str;
         }
 
+        /**
+         * A task's terminal summary as a ONE-LINE notice entry (the
+         * first line only — plan 16 S3); "no summary reported" when
+         * the summary holds nothing.
+         */
+        private static string notice_lead(TaskEntity task) {
+            string summary = (task.summary ?? "").strip();
+            if (summary.length == 0) {
+                return "no summary reported";
+            }
+            int cut = summary.index_of("\n");
+            string lead = cut >= 0 ? summary.substring(0, cut) : summary;
+            return lead.strip();
+        }
+
         // ------------------------------------------------------------------
         // Completion gate (plan 15 C4)
         // ------------------------------------------------------------------

+ 73 - 0
test/web_stack_test.py

@@ -5585,6 +5585,79 @@ def bundle_goals(ctx):
                    or False, timeout=60, what="goal agent re-parks") is not None,
           "the resumed goal agent parks again")
 
+    print("== plan 16 S3: delta resume notices — one-line statuses, bodies once ==")
+    # A MULTI-LINE completion lands while the agent is parked. The next
+    # notice lists every status ONE-LINE (first line of the summary
+    # only) and embeds each NEW completion's full body exactly once —
+    # no statuses-section/reports-section double embedding. (Timestamps
+    # are second-granular: each fixture waits out the park's second so
+    # completed_at is strictly newer than suspended_at.)
+    def run_park_second_passed():
+        conn = db()
+        try:
+            row = conn.execute("select suspended_at from tasks where id = ?",
+                               (run_task,)).fetchone()
+            if not row or not row[0]:
+                return False
+            return time.time() > float(row[0]) + 1.05
+        finally:
+            conn.close()
+
+    check(wait_for(run_park_second_passed, timeout=30,
+                   what="S3 park second passes") is not None,
+          "the park stamp is in a strictly older second")
+    conn = db()
+    conn.execute("insert into tasks (project_id, goal_id, kind, title, status,"
+                 " priority, summary, completed_at, created_at) values (1, ?, 'task',"
+                 " 'Multiline report', 'done', 5,"
+                 " 'Headline: the flange calibrates\n\nBody: the full report with all"
+                 " the detail lines.',"
+                 " strftime('%s','now'),"
+                 " strftime('%s','now','-30 seconds'))", (goal1,))
+    conn.commit()
+    conn.close()
+    s3_before = len(fake.goal_assignments)
+    check(wait_for(lambda: len(fake.goal_assignments) > s3_before or False,
+                   timeout=60, what="S3 delta resume fires") is not None,
+          "the multiline completion resumes the parked agent")
+    s3_notice = fake.goal_assignments[-1].get("resume_notice") or ""
+    s3_statuses = s3_notice.split("## Task statuses", 1)[1] \
+                        .split("## New completion reports", 1)[0]
+    check("Headline: the flange calibrates" in s3_statuses
+          and "the full report with all the detail lines" not in s3_statuses,
+          "the statuses section carries ONE LINE per task (the summary's first line)")
+    check(s3_notice.count("the full report with all the detail lines.") == 1
+          and "### #" in s3_notice,
+          "each new completion's full body embeds EXACTLY ONCE (no double embedding)")
+    check(wait_for(lambda: task_state(auth, run_task)["status"] == "suspended"
+                   or False, timeout=60, what="S3 agent re-parks") is not None,
+          "the S3 agent re-parks")
+    # Size constant per event, not cumulative: the NEXT resume (armed
+    # with a fresh single-line completion) reports only the NEW body —
+    # the consumed multiline report never re-embeds.
+    check(wait_for(run_park_second_passed, timeout=30,
+                   what="S3 second park second passes") is not None,
+          "the second park stamp is in a strictly older second")
+    conn = db()
+    conn.execute("insert into tasks (project_id, goal_id, kind, title, status,"
+                 " priority, summary, completed_at, created_at) values (1, ?, 'task',"
+                 " 'Fresh completion', 'done', 5, 'fresh one-liner',"
+                 " strftime('%s','now'),"
+                 " strftime('%s','now','-30 seconds'))", (goal1,))
+    conn.commit()
+    conn.close()
+    s3_more = len(fake.goal_assignments)
+    check(wait_for(lambda: len(fake.goal_assignments) > s3_more or False,
+                   timeout=60, what="S3 second delta resume") is not None,
+          "the fresh completion resumes the agent again")
+    s3_next = fake.goal_assignments[-1].get("resume_notice") or ""
+    check("the full report with all the detail lines." not in s3_next
+          and "### #" in s3_next and "fresh one-liner" in s3_next,
+          "the consumed completion's body never re-embeds (notice size constant per event)")
+    check(wait_for(lambda: task_state(auth, run_task)["status"] == "suspended"
+                   or False, timeout=60, what="S3 agent parks a third time") is not None,
+          "the S3 agent parks for the pause section")
+
     print("== feedback: goal page parity — progress feed, current activity, runs ==")
     # The goal agent's runs feed the page's live surfaces: progress
     # lines (green latest-first feed), the open round's newest