Skip to content

feat(runtime-api): include task previews in live task listings - #5646

Merged
keweichen merged 1 commit into
mainfrom
factory/kc/recovery-1f127436a558de5403d6164e-d2b3fb34d903
Oct 17, 2026
Merged

keweichen merged 1 commit into
mainfrom
factory/kc/recovery-1f127436a558de5403d6164e-d2b3fb34d903

Conversation

@keweichen

Copy link
Copy Markdown
Collaborator

Priority

Level: medium
Reason: Owner-approved reliability improvement.

Closes

N/A — owner-approved work; no public issue is claimed as closed.

Summary

feat(runtime-api): include task previews in live task listings

Review first: src/runtime-api/tasks_view.py, tests/runtime-api-tasks-view.test.py.

Checklist

  • Confirmed no other open PR closes the same issue (gh pr list --repo sonichi/sutando --search "closes #N")
  • Git author email matches my CLA-signed email (git log -1 --format='%ae' shows a GH-mapped email, not *.local or noreply@anthropic.com)
  • Single concern per PR — no bundled refactors / drive-by feature additions
  • Confirmed bug exists on upstream/main (or feature isn't already covered)
  • Test added (or N/A explained below)
  • Before/after evidence pasted below — the actual command output for both states, not a description of it (this is the Add demo video to README #1 reason PRs bounce)
  • Every claim in this PR body is verifiable from the diff or the pasted output — no statement the reviewer can't check
  • Live path? If this touches a bridge, network path, delivery loop, or startup, I included a real post-restart round-trip — unit tests / harnesses alone are not accepted for these
  • Stacked PR? I named the parent + merge order and will rebase/update + rerun full checks after the parent lands (or N/A)
  • Scanned added lines for host-specific hardcoded paths and inline home/workspace fallbacks; fixtures are narrowly scoped
  • If this PR adds or edits a CI workflow, I confirmed it actually runs on this PR (see the Checks tab — a branches: filter can silently exclude a stacked PR)
  • Doesn't reinvent workspace / home-path resolution — uses the resolve_workspace / resolveWorkspace / claude_home_path helpers (see CLAUDE.md "Workspace contract"; enforced by the lint-workspace-resolution, lint-claude-home-path, and workspace-leak-check CI checks)

Contribution observations (unchecked items remain reviewer decisions):

  • Duplicate search: Searched PRs (all states) and open issues using feat(runtime-api): include task previews in live task listings, "task.details". No matching records returned. Returned PRs were compared against current files and overlapping diffs: none found. Title similarity alone is not equivalence; this is not exhaustive semantic coverage.

  • Upstream observed during evidence preparation: 12d2f0d72825019b6af6afc5ae48dd5a00e5fda0. Main advanced after the recorded source base. Local evidence verifies the exact PR candidate, not its integration with newer main. PR checks, independent review and branch maintenance govern merge.

  • Diff review: - review-correctness: Inspected TasksView.list_tasks, the shared parser, status/details handling, dispatcher and CLI output. tasks_view.py:377 normalizes the already parsed string before slicing to 160 characters, inside the existing successful-read block. It preserves IDs, states, metadata, sorting, limits and OSError handling. Real-daemon boundary observations passed for empty/missing bodies, legacy parsed metadata and lengths 159/160/161.

  • review-scope: The complete parent-to-candidate diff contains only the two allowed paths: one production line and focused regression assertions. No desktop, execution, transport, cross-channel enumeration or archive-browsing changes. The CLI already emits the API response unchanged.

  • review-maintainability: The implementation reuses existing parsing and introduces no new dependency, helper or duplicated policy. Regression assertions use literal expected previews and retain lifecycle checks. Piping the exact candidate diff to bash scripts/review-checks.sh passed hardcoded-path, root-artifact and comment checks at 2026-10-16T23:56:21Z.

  • Documentation: No standalone documentation files changed. The reviewer should assess the code and contract comments for documentation completeness.

  • Live-path verification: - monitor-user-journey: Ran python3 [local path] It launched the candidate server.py and used the real sutando-runtime.py CLI for task submit, list, details and cancel. PID 70062 reached API readiness at 2026-10-17T00:01:12.506Z. Preview recognition, full details, foreign exclusion and persisted cancellation signal passed. All 24 final assertions passed. Exact argv, responses and file observations: monitor-journey.json.

  • monitor-repeatability: The witness ran task list through three fresh CLI connections; every response equaled the first listing. After stopping PID 70062 and restarting as PID 70179, task list and task details returned identical IDs, states, previews, ordering and bodies. Moving the claimed fixture out of the live directory removed its row; restoring it restored the identical row. Evidence: monitor-journey.json.

These observations used the isolated candidate; no production deployment was performed.

  • Risks / rollback: Change scope: Add a preview field to each readable live runtime-API task-list row using the already parsed body: collapse whitespace and retain at most 160 characters. Preserve identifiers, lifecycle states, ordering, limits, disappearance handling and source isolation. Keep full text in task.details. Add focused task-view regression assertions; the CLI already emits the API response. Excludes desktop changes, task execution changes, cross-channel enumeration and archive browsing.
    Rollback: revert this PR. Production behavior after deployment remains to be observed.
  • Stacking: N/A; one concern against main.
  • Author maps to keweichen; CLA status must be observed on the PR.

Before / after evidence

Before

  • triage-evidence: Loaded the supplied scratch environment and ran python3 [local path] preflight successfully. The real checkout daemon reached Unix-socket readiness and served CLI submit/list/details calls. Evidence is recorded in [local path] and triage-runtime-readiness.json. Final Git status and diffs were clean.

Earlier baseline observations at dbb0d45. These are historical; current-candidate evidence follows below.

/bin/zsh -c "python3 - <<'PY'
import json, os, pathlib, subprocess, sys
base = pathlib.Path('[local path]')
env = dict(os.environ)
env.update(json.loads((base / 'environment.json').read_text()))
env['PYTHONDONTWRITEBYTECODE'] = '1'
p = subprocess.run([sys.executable, str(base / 'process-tools.py'), 'preflight'], env=env, capture_output=True, text=True, timeout=60)
print(p.stdout)
print(p.stderr)
raise SystemExit(p.returncode)
PY"
{"schema": "ag2-runtime-preflight/v1", "product_ready": "not observed", "tmux": {"available": true, "detail": "tmux 3.8\n"}, "process_inspection": {"available": true, "detail": "61280\n"}, "private_socket": {"available": true}}

Exit: 0

/bin/zsh -c "python3 - <<'PY'
import json, pathlib
base = pathlib.Path('[local path]')
env = json.loads((base/'environment.json').read_text())
print(json.dumps({k:v for k,v in env.items() if k.startswith(('SUTANDO_', 'PYTHON', 'TMP')) or k == 'PATH'}, indent=2))
print('diagnostics files:', [p.name for p in (base/'diagnostics').iterdir()] if (base/'diagnostics').exists() else [])
PY"
{
  "TMPDIR": "[local path]",
  "TMP": "[local path]",
  "SUTANDO_TEST_MODE": "1",
  "SUTANDO_PACKAGED": "",
  "SUTANDO_APP_SUPPORT": "",
  "SUTANDO_WORKSPACE": "[local path]",
  "SUTANDO_WORKSPACE_DIR": "[local path]",
  "SUTANDO_DEFAULT_WORKSPACE": "[local path]",
  "SUTANDO_RESULTS_DIR": "[local path]",
  "SUTANDO_RUNTIME_STATE": "[local path]",
  "SUTANDO_RUNTIME_SOCKET": "[local path]",
  "SUTANDO_RUNTIME_DB": "[local path]",
  "SUTANDO_HA_DIR": "[local path]",
  "SUTANDO_RUN_DIR": "[local path]",
  "SUTANDO_INSTANCE_REGISTRY": "[local path]",
  "SUTANDO_AGENT_ID": "factory-runtime-test",
  "SUTANDO_HOST_LABEL": "factory-runtime-test",
  "SUTANDO_INSTANCE_ID": "",
  "SUTANDO_WATCHER_BEAT": "",
  "SUTANDO_WATCHER_TRANSITION_HOOK": "",
  "SUTANDO_TMUX_SOCKET": "[local path]",
  "SUTANDO_STANDBY_STOP_TIMEOUT": "2",
  "PATH": "[local path]",
  "PYTHONPATH": "[local path]"
}
diagnostics files: ['readiness.json', 'capability-preflight.json']

Exit: 0

After

  • candidate-identity: git rev-parse HEAD matched the required commit. git status --porcelain=v1 --untracked-files=all was empty before and after verification. Only the two allowed paths differ from the approved base. Final identity receipt: diagnostics/candidate-identity.json, recorded 2026-10-16T23:52:55Z.

  • acceptance-1: Ran python3 [local path] using the supplied scratch environment and real candidate server/CLI. Two distinct submissions were identifiable from one task-list response; both previews exactly matched whitespace-normalized parsed bodies truncated to 160 characters. Daemon PID 28968 started at 2026-10-16T23:52:24.526Z, API readiness passed at 23:52:24.756Z, and it was reaped at 23:52:25.237Z. Exact commands, responses and persisted-body observations are in diagnostics/preview-journey.json.

  • acceptance-2: The supplementary Unicode request retained its full 266-character parsed body in task.details while its preview contained exactly 160 characters. Original IDs remained task-rtapi-f40fb87a9f45 (pending) and task-rtapi-6a72fce35e5b (in_progress after claiming). Evidence: diagnostics/preview-journey.json.

  • acceptance-3: Real daemon observations confirmed an empty queue returned {"tasks":[]} and Discord/Telegram control IDs and text were absent. Frozen regressions passed disappearance handling, ordering/limits, assigned IDs, cancellation and foreign-task ownership boundaries.

  • check-task-preview-socket-acceptance: Executed the supplied python3 -c check unchanged with the provisioned environment. Exit 0; all seven assertions passed against the real disposable daemon. Run: 2026-10-16T23:51:18.923Z–23:51:19.811Z. Exact argv and startup output: diagnostics/task-preview-socket-acceptance.json.

  • check-task-view-regressions: Executed the supplied python3 -c check unchanged. All 21 selected tests passed without skips; exit 0. Run: 2026-10-16T23:51:18.924Z–23:51:19.219Z. Exact argv and results: diagnostics/task-view-regressions.json.

  • coverage: python3 -m coverage run --rcfile=.coveragerc tests/runtime-api-tasks-view.test.py passed all 73 tests. After coverage combine and XML generation, diff-cover compared against dbb0d45 with --fail-under=95: one changed executable line, zero missing, 100% coverage. Exact commands and timestamps: diagnostics/coverage-commands.json.

Test plan

python3 -c 'import json, os, pathlib, socket, subprocess, sys, tempfile, time
root = pathlib.Path.cwd()
base = pathlib.Path(os.environ.get('"'"'SUTANDO_RUNTIME_SOCKET'"'"', '"'"'[local path]'"'"')).parent
env = dict(os.environ)
env.update(json.loads((base / '"'"'environment.json'"'"').read_text()))
env['"'"'PYTHONDONTWRITEBYTECODE'"'"'] = '"'"'1'"'"'
failures = []
def check(label, condition):
    print(('"'"'PASS '"'"' if condition else '"'"'FAIL '"'"') + label, flush=True)
    if not condition:
        failures.append(label)
with tempfile.TemporaryDirectory(prefix='"'"'preview-'"'"', dir=base) as tmp:
    tmp = pathlib.Path(tmp)
    ws = tmp / '"'"'w'"'"'
    for d in ('"'"'state'"'"', '"'"'tasks'"'"', '"'"'results'"'"'):
        (ws / d).mkdir(parents=True)
    for k in ('"'"'SUTANDO_WORKSPACE'"'"', '"'"'SUTANDO_WORKSPACE_DIR'"'"', '"'"'SUTANDO_DEFAULT_WORKSPACE'"'"'):
        env[k] = str(ws)
    env.update(SUTANDO_RUNTIME_SOCKET=str(tmp / '"'"'r.sock'"'"'),
               SUTANDO_RUNTIME_DB=str(ws / '"'"'state/r.sqlite'"'"'),
               SUTANDO_RUNTIME_STATE=str(ws / '"'"'state'"'"'),
               SUTANDO_HA_DIR=str(ws / '"'"'state/ha'"'"'),
               SUTANDO_RESULTS_DIR=str(ws / '"'"'results'"'"'),
               SUTANDO_RUN_DIR=str(tmp / '"'"'run'"'"'),
               SUTANDO_INSTANCE_REGISTRY=str(tmp / '"'"'registry'"'"'))
    cli = [sys.executable, str(root / '"'"'src/runtime-cli/sutando-runtime.py'"'"')]
    def run(*args):
        p = subprocess.run(cli + list(args), env=env, capture_output=True, text=True, timeout=10)
        assert p.returncode == 0, p.stderr + p.stdout
        return json.loads(p.stdout)
    with (tmp / '"'"'server.log'"'"').open('"'"'w+'"'"') as log:
        daemon = subprocess.Popen([sys.executable, str(root / '"'"'src/runtime-api/server.py'"'"')], env=env, stdout=log, stderr=subprocess.STDOUT)
        try:
            for _ in range(100):
                s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                try:
                    s.settimeout(.1)
                    s.connect(env['"'"'SUTANDO_RUNTIME_SOCKET'"'"'])
                    break
                except OSError:
                    assert daemon.poll() is None, '"'"'daemon exited during startup'"'"'
                    time.sleep(.05)
                finally:
                    s.close()
            else:
                raise AssertionError('"'"'daemon not ready'"'"')
            check('"'"'empty queue stays empty'"'"', run('"'"'task'"'"', '"'"'list'"'"') == {'"'"'tasks'"'"': []})
            texts = ['"'"'Compare supplier delivery dates'"'"', '"'"'Draft weekly summary '"'"' + '"'"'界'"'"' * 200]
            ids = [run('"'"'task'"'"', '"'"'submit'"'"', text)['"'"'taskId'"'"'] for text in texts]
            claimed = ws / '"'"'tasks'"'"' / (ids[1] + '"'"'.txt'"'"')
            claimed.rename(claimed.with_name(ids[1] + '"'"'.claimed-worker-1.txt'"'"'))
            (ws / '"'"'tasks/task-discord-private.txt'"'"').write_text('"'"'id: task-discord-private\nsource: discord\ntask: FOREIGN_PREVIEW_CONTROL\n'"'"')
            listing = run('"'"'task'"'"', '"'"'list'"'"')
            rows = {r['"'"'taskId'"'"']: r for r in listing['"'"'tasks'"'"']}
            check('"'"'only runtime tasks listed'"'"', set(rows) == set(ids))
            check('"'"'foreign text excluded'"'"', '"'"'FOREIGN_PREVIEW_CONTROL'"'"' not in json.dumps(listing))
            check('"'"'both requests identifiable in one listing'"'"', all(rows[i].get('"'"'preview'"'"') == '"'"' '"'"'.join(text.split())[:160] for i, text in zip(ids, texts)))
            check('"'"'previews bounded to 160 characters'"'"', all(isinstance(rows[i].get('"'"'preview'"'"'), str) and len(rows[i]['"'"'preview'"'"']) <= 160 for i in ids))
            check('"'"'pending and claimed states unchanged'"'"', rows[ids[0]]['"'"'state'"'"'] == '"'"'pending'"'"' and rows[ids[1]]['"'"'state'"'"'] == '"'"'in_progress'"'"')
            check('"'"'details retain full text'"'"', all(run('"'"'task'"'"', '"'"'details'"'"', i)['"'"'task'"'"'] == text for i, text in zip(ids, texts)))
        finally:
            daemon.terminate()
            try:
                daemon.wait(timeout=5)
            except subprocess.TimeoutExpired:
                daemon.kill()
                daemon.wait(timeout=5)
            log.seek(0)
            print('"'"'STARTUP LOG: '"'"' + log.read(8192).strip(), flush=True)
if failures:
    raise SystemExit(1)
'

Observed: PASS; exit 0.

PASS empty queue stays empty
PASS only runtime tasks listed
PASS foreign text excluded
PASS both requests identifiable in one listing
PASS previews bounded to 160 characters
PASS pending and claimed states unchanged
PASS details retain full text
STARTUP LOG: [runtime-api] listening on [local path] (actor=factory-runtime-test)
python3 -c 'import json,os,pathlib,subprocess,sys
base=pathlib.Path(os.environ.get('"'"'SUTANDO_RUNTIME_SOCKET'"'"','"'"'[local path]'"'"')).parent
env=dict(os.environ);env.update(json.loads((base/'"'"'environment.json'"'"').read_text()));env.update(PYTHONDONTWRITEBYTECODE='"'"'1'"'"',TMPDIR=str(base))
raise SystemExit(subprocess.run(['"'"'python3'"'"','"'"'tests/runtime-api-tasks-view.test.py'"'"','"'"'TasksViewTests.test_list_tasks_enumerates_live_only'"'"','"'"'TasksViewTests.test_list_tasks_disappearing_during_metadata_access'"'"','"'"'TasksViewTests.test_list_tasks_retains_mtime_and_limit_semantics'"'"','"'"'TasksViewTests.test_list_tasks_empty_dir'"'"','"'"'TasksViewTests.test_cancel_writes_cancel_instruction_signal'"'"','"'"'TasksViewTests.test_cancel_done_task_is_noop_and_unknown_raises'"'"','"'"'ForeignTaskOwnershipTests'"'"','"'"'TasksViewAssignedIdTests'"'"'],env=env,timeout=110).returncode)
'

Observed: PASS; exit 0.

test_list_tasks_enumerates_live_only (__main__.TasksViewTests.test_list_tasks_enumerates_live_only) ... ok
test_list_tasks_disappearing_during_metadata_access (__main__.TasksViewTests.test_list_tasks_disappearing_during_metadata_access) ... ok
test_list_tasks_retains_mtime_and_limit_semantics (__main__.TasksViewTests.test_list_tasks_retains_mtime_and_limit_semantics) ... ok
test_list_tasks_empty_dir (__main__.TasksViewTests.test_list_tasks_empty_dir) ... ok
test_cancel_writes_cancel_instruction_signal (__main__.TasksViewTests.test_cancel_writes_cancel_instruction_signal) ... ok
test_cancel_done_task_is_noop_and_unknown_raises (__main__.TasksViewTests.test_cancel_done_task_is_noop_and_unknown_raises) ... ok
test_empty_result_is_not_a_terminal_state (__main__.ForeignTaskOwnershipTests.test_empty_result_is_not_a_terminal_state) ... ok
test_foreign_result_is_not_readable_by_id (__main__.ForeignTaskOwnershipTests.test_foreign_result_is_not_readable_by_id) ... ok
test_foreign_task_cannot_be_cancelled (__main__.ForeignTaskOwnershipTests.test_foreign_task_cannot_be_cancelled) ... ok
test_foreign_task_details_are_not_exposed (__main__.ForeignTaskOwnershipTests.test_foreign_task_details_are_not_exposed) ... ok
test_foreign_task_is_not_enumerated (__main__.ForeignTaskOwnershipTests.test_foreign_task_is_not_enumerated) ... ok
test_foreign_task_status_reads_as_absent (__main__.ForeignTaskOwnershipTests.test_foreign_task_status_reads_as_absent) ... ok
test_positive_control_own_task_is_still_visible_everywhere (__main__.ForeignTaskOwnershipTests.test_positive_control_own_task_is_still_visible_everywhere) ... ok
test_the_answer_landing_later_is_seen (__main__.ForeignTaskOwnershipTests.test_the_answer_landing_later_is_seen) ... ok
test_torn_utf8_result_is_not_a_terminal_state (__main__.ForeignTaskOwnershipTests.test_torn_utf8_result_is_not_a_terminal_state) ... ok
test_unready_newest_does_not_mask_the_answer_behind_it (__main__.ForeignTaskOwnershipTests.test_unready_newest_does_not_mask_the_answer_behind_it) ... ok
test_unready_result_is_not_listed (__main__.ForeignTaskOwnershipTests.test_unready_result_is_not_listed) ... ok
test_whitespace_only_result_is_not_a_terminal_state (__main__.ForeignTaskOwnershipTests.test_whitespace_only_result_is_not_a_terminal_state) ... ok
test_a_listed_id_can_fetch_its_result (__main__.TasksViewAssignedIdTests.test_a_listed_id_can_fetch_its_result) ... ok
test_a_name_the_grammar_rejects_is_skipped_not_emitted_as_null (__main__.TasksViewAssignedIdTests.test_a_name_the_grammar_rejects_is_skipped_not_emitted_as_null) ... ok
test_assigned_and_claimed_files_list_their_canonical_id (__main__.TasksViewAssignedIdTests.test_assigned_and_claimed_files_list_their_canonical_id) ... ok

----------------------------------------------------------------------
Ran 21 tests in 0.037s

OK

Canonical review scanner:

review-checks: PASS (hardcoded-paths + root-artifacts + prose-cap clean)
prose-cap: PASS (comment blocks within cap 2, exts .py)

Verified candidate: 99c7a4d5733f0778c1af7207c3c6b00c599a077c. Publication commit: 28e3ac6a37c4beb617ce3678f17227e2dfbaa88b. Both have tree cd9e65f29bf9532899a403af88700885cacb7675; only factory-authored commit metadata changed. Acceptance evidence refers to the verified candidate; the scanner ran on the publication tree.

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently verified:

  • Confirmed the enumeration gap on main. Reverted just the production change (keeping the new test) against the parent commit (dbb0d45) — 2 errors, since list_tasks never collapsed/truncated/exposed body text at all. At the PR head, 73/73 pass.
  • Traced the truncated-scan edge case by hand, not just trusting the test: test_list_tasks_retains_mtime_and_limit_semantics deletes the "newer" file via a mocked Path.stat right after its first stat call, so f.read_text() later raises FileNotFoundError for that entry — caught by the existing except OSError: pass the new entry["preview"] = ... line sits inside. So the new field correctly inherits the same fail-open behavior as source/priority/timestamp/instance_id, with no new error handling needed. That's why rows["tasks"][0] (newer) has no "preview" in that test while the full re-scan's "older" does.
  • Confirmed the truncation/unicode-whitespace handling: " ".join(th.body.split())[:160] correctly collapses   (em-space) along with ordinary whitespace (Python's str.split() with no args splits on any Unicode whitespace), and [:160] truncates by code point, not byte, so a 200-"界"-char body truncates to exactly 153 "界" after the 7-char "second " prefix — matches the test's exact expected length (160) and content.
  • ruff clean, review-checks.sh clean, CI green, no failures.

Small, correctly-scoped, well-tested. LGTM.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at 28e3ac6a3. The change is small and right.

What I checked:

  • Scope. list_tasks only globs task-rtapi-*, the runtime API's own tasks. task.list and task.details go through the same _task dispatch with the same authority. So a 160-character preview exposes nothing the caller couldn't already read in full through task.details. No other source's task text can appear.
  • Behaviour. The preview is computed inside the existing try, so a file that vanishes or can't be read still yields a row without a preview, as before. The disappearance test now asserts that directly. Slicing is by code point, so a CJK body is cut cleanly at 160 characters, and the test pins that.
  • Tests at head. tests/runtime-api-tasks-view.test.py ran 73 tests, OK.
  • Mutation. With the preview line disabled, both updated tests fail (test_list_tasks_enumerates_live_only and test_list_tasks_retains_mtime_and_limit_semantics). So the assertions have teeth.

CI was still running when I reviewed. sonichi/sutando merges belong to the owner or Chi, so I'm not merging it myself.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 92%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/runtime-api/tasks_view.py (100%)

Summary

  • Total: 1 line
  • Missing: 0 lines
  • Coverage: 100%

@liususan091219 liususan091219 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready to approve.

Reviewed at head 28e3ac6a37c4beb617ce3678f17227e2dfbaa88b (one commit on dbb0d45f). The change adds one production line at src/runtime-api/tasks_view.py:377 and focused assertions in tests/runtime-api-tasks-view.test.py.

What I ran (local clone, SUTANDO_WORKSPACE* / SUTANDO_TASKS_DIR unset)

  • python3 tests/runtime-api-tasks-view.test.py at head: 73 tests, OK.
  • Parent's tasks_view.py with the new tests: 2 ERRORs (test_list_tasks_enumerates_live_only, test_list_tasks_retains_mtime_and_limit_semantics). The tests fail without the change.
  • Mutation 1, dropping the whitespace collapse (th.body[:160]): FAIL in test_list_tasks_enumerates_live_only.
  • Mutation 2, an off-by-one bound ([:161]): FAIL in the same test. Both the normalisation and the 160 bound are pinned by tests.
  • Regex over the added diff lines for ~/.sutando, $HOME/.sutando, /Users/<name>/ and SUTANDO_WORKSPACE: no hits.

Change-request walk (none fire)

  • Tests: they exercise the real TasksView against real task files and assert on literal expected previews. Nothing reads module source, and nothing re-implements the unit in the test file.
  • Completeness:
    • The preview sits inside the existing try/except OSError, so a file that vanishes mid-scan still yields a row with no preview. The limit test now asserts this (assertNotIn("preview", ...)).
    • The text comes from the same parse_task_headers_lenient(...).body that details() returns at tasks_view.py:335-336.
    • list_tasks only globs this channel's own task-rtapi-* files, so the preview exposes nothing task.details does not already return.
  • Base is main, CI is green, and it is one concern. I found no duplicate or competing open PR: #4399 is a different module.

Approvals at this head (latest non-comment review per author)

  • sonichi (OWNER), APPROVED. Substantive: reverted the change against the parent and traced the vanish-mid-scan case.
  • qingyun-wu (COLLABORATOR), APPROVED. Substantive: checked scope and ran a mutation.
  • reviewDecision is APPROVED. There are no review threads and no CHANGES_REQUESTED anywhere.

Merge gate (not part of this verdict)

  • repos/sonichi/sutando/commits/28e3ac6a.../status returns pending with zero contexts. license/cla is ABSENT, not green. Close and reopen the PR to retrigger it before merging.

Live path

  • The author's body includes a real daemon plus CLI round trip and a restart check (PID 70062 then 70179). I did not re-run it. It is a disposable isolated daemon, not a deployment.

Non-blocking nits

  1. src/runtime-api/protocol.py:43: the task.list {} doc line could note the new preview field, as task.list_results does at line 34.

  2. There are now two preview policies in one file:

    • list_results uses a raw body[:160] (tasks_view.py:319).
    • list_tasks collapses whitespace first (tasks_view.py:377).

    The literal 160 appears twice. A shared module constant, or a one-line note on why the two differ, would stop them drifting.

Not verified: the live daemon round trip myself; behaviour against main after dbb0d45f.

— Sutando (susans-MacBook-Pro)

@liususan091219 liususan091219 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at 28e3ac6a3, the head I reviewed in #5646 (review). That review has the evidence: tests run, mutation checks, and an expert approval already at this head. The head has not changed since. Approved directly per the owner's standing instruction.

— Sutando (susans-MacBook-Pro)

@keweichen keweichen closed this Oct 17, 2026
@keweichen keweichen reopened this Oct 17, 2026
@keweichen
keweichen merged commit c66a81f into main Oct 17, 2026
67 checks passed
@keweichen
keweichen deleted the factory/kc/recovery-1f127436a558de5403d6164e-d2b3fb34d903 branch October 17, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants