|
|
@@ -2034,6 +2034,34 @@ def bundle_pages(ctx):
|
|
|
check('stm-attribute.value="task.description"' in task_page_html,
|
|
|
"the edit expander pre-populates the mandate textarea")
|
|
|
|
|
|
+ print("== plan 13 B4: comments display as footnotes, body-first ==")
|
|
|
+ # A quoted agent comment on the keeper task drives the display
|
|
|
+ # checks: the card shows the body before the quote, everything
|
|
|
+ # reads muted-and-smaller, and the dashboard's timeline comment
|
|
|
+ # rows carry the footnote class.
|
|
|
+ conn = db()
|
|
|
+ conn.execute(
|
|
|
+ "insert into task_comments (task_id, author, body, quote, created_at)"
|
|
|
+ " values (?, 'agent', 'The weld passed its pressure test.',"
|
|
|
+ " 'previous inspection notes', strftime('%s','now'))", (keeper,))
|
|
|
+ conn.commit()
|
|
|
+ conn.close()
|
|
|
+ task_page_html = http_get_page(auth, "/tasks/%d" % keeper)
|
|
|
+ check("saddle comment" not in task_page_html,
|
|
|
+ "the task page never shows the command echo")
|
|
|
+ check(task_page_html.index('stm-text="comment.body"')
|
|
|
+ < task_page_html.index('stm-text="comment.quote"'),
|
|
|
+ "the task comment card shows the body before the quote")
|
|
|
+ status, css = http("GET", "/_statum/resource/app.css", timeout=15)
|
|
|
+ check(status == 200 and ".footnote" in css
|
|
|
+ and re.search(r"\.comment\s*\{[^}]*color:\s*var\(--ink-muted\)", css)
|
|
|
+ and re.search(r"\.comment\s*\{[^}]*font-size:\s*0\.88rem", css),
|
|
|
+ "the stylesheet ships the footnote utility and muted comment cards")
|
|
|
+ home_html = http_get_page(auth, "/")
|
|
|
+ check("item.kind == 'comment' || item.kind == 'goal-comment' ? 'footnote'"
|
|
|
+ in home_html,
|
|
|
+ "the dashboard timeline renders comment rows as footnotes")
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
@@ -6155,6 +6183,76 @@ def bundle_replay(ctx):
|
|
|
check(task_state(auth, run_task)["status"] == "failed",
|
|
|
"the goal-run task stays failed after the replays")
|
|
|
|
|
|
+ print("== plan 13 B4: the comment projection stores the body ==")
|
|
|
+ # The heredoc shape: the command is the pipe invocation, the OUTPUT
|
|
|
+ # is the body. The projections must carry the body — the command
|
|
|
+ # echo never becomes comment text, and the literal `saddle
|
|
|
+ # comment` string never renders outside the terminal.
|
|
|
+ act(auth, slot_public(auth, "/goals", "goals")["create"], {
|
|
|
+ "project_id": "1", "title": "Comment goal", "brief": "Reports cleanly.",
|
|
|
+ "branch_name": "goal/comment"})
|
|
|
+ conn = db()
|
|
|
+ comment_goal = conn.execute(
|
|
|
+ "select id from goals where title = 'Comment goal'").fetchone()[0]
|
|
|
+ conn.close()
|
|
|
+ fake.goal_script = [("suspend",)]
|
|
|
+ act(auth, goal_state(comment_goal)["start"], {})
|
|
|
+ conn = db()
|
|
|
+ comment_run = conn.execute(
|
|
|
+ "select id from tasks where goal_id = ? and kind = 'goal_run'",
|
|
|
+ (comment_goal,)).fetchone()[0]
|
|
|
+ conn.close()
|
|
|
+ check(wait_for(lambda: task_state(auth, comment_run)["status"] == "suspended"
|
|
|
+ or False, timeout=60, what="comment goal agent parks") is not None,
|
|
|
+ "the comment goal's agent parks")
|
|
|
+ conn = db()
|
|
|
+ comment_session = conn.execute(
|
|
|
+ "select id from sessions where task_id = ? and ended_at is null",
|
|
|
+ (comment_run,)).fetchone()[0]
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ comment_body = "The flange weld holds. First sentence carries the information."
|
|
|
+ comment_entry = [{"id": 8001, "command": "cat << 'EOF' | saddle comment",
|
|
|
+ "output": comment_body, "exit_code": 0, "type": "comment",
|
|
|
+ "timestamp": "2026-09-11T00:03:00Z"}]
|
|
|
+ status, _ = saddle_api("POST", "/api/saddle/results", fake_token,
|
|
|
+ {"session_id": comment_session, "state": "suspended",
|
|
|
+ "entries": comment_entry})
|
|
|
+ check(status == 200, "the comment entry push is accepted")
|
|
|
+ conn = db()
|
|
|
+ task_comment_bodies = [r[0] for r in conn.execute(
|
|
|
+ "select body from task_comments where task_id = ?", (comment_run,))]
|
|
|
+ goal_comment_bodies = [r[0] for r in conn.execute(
|
|
|
+ "select body from goal_comments where goal_id = ?", (comment_goal,))]
|
|
|
+ event_texts = [r[0] for r in conn.execute(
|
|
|
+ "select text from goal_events where goal_id = ? and kind = 'comment'",
|
|
|
+ (comment_goal,))]
|
|
|
+ conn.close()
|
|
|
+ check(task_comment_bodies == [comment_body],
|
|
|
+ "the task comment stores the body, not the command echo")
|
|
|
+ check(goal_comment_bodies == [comment_body],
|
|
|
+ "the goal comment stores the body, not the command echo")
|
|
|
+ check(len(event_texts) == 1 and event_texts[0].startswith("The flange weld holds.")
|
|
|
+ and "saddle comment" not in event_texts[0] and "EOF" not in event_texts[0],
|
|
|
+ "the goal_events comment projection stores the body text")
|
|
|
+
|
|
|
+ state = goal_state(comment_goal)
|
|
|
+ check(state["current_command"] == comment_body,
|
|
|
+ "the goal page's current-activity line shows the comment body")
|
|
|
+ check(state["comments"] and state["comments"][0]["body"] == comment_body
|
|
|
+ and any(e["kind"] == "comment"
|
|
|
+ and e["text"].startswith("The flange weld holds.")
|
|
|
+ for e in state["events"]),
|
|
|
+ "the goal slot carries the body in its comments and narrative")
|
|
|
+ page = http_get_page(auth, "/goals/%d" % comment_goal)
|
|
|
+ check("saddle comment" not in page,
|
|
|
+ "the rendered goal page never shows the command echo")
|
|
|
+ check("event.kind == 'comment' ? ' footnote'" in page,
|
|
|
+ "the narrative's comment events render with footnote styling")
|
|
|
+ check(page.index('stm-markdown="comment.body"')
|
|
|
+ < page.index('stm-text="comment.quote"'),
|
|
|
+ "the comment card shows the body before the quote")
|
|
|
+
|
|
|
# Drain: nothing queued remains for the shard's shutdown.
|
|
|
check(queued_count() == 0, "the replay shard drains its queue")
|
|
|
|