Skip to content

fix(runtime-api): reflect fresh task engagement in task status - #5632

Merged
liususan091219 merged 1 commit into
mainfrom
factory/kc/revalidate-ad068ba303023b32dc7d2ff8-a7d969b4514e
Oct 16, 2026
Merged

liususan091219 merged 1 commit into
mainfrom
factory/kc/revalidate-ad068ba303023b32dc7d2ff8-a7d969b4514e

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

fix(runtime-api): reflect fresh task engagement in task status

Review first: src/runtime-api/tasks_view.py, tests/runtime-api-task-engagement.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 fix(runtime-api): reflect fresh task engagement in task status, "activity_bus.in_progress". Related results for reviewer comparison: fix(core): a task the Stop hook hands to the core inline is recorded as read #5302, fix(stop-hook): a guest session owes nothing, a task in progress does not block, and the same queue cannot block a turn end forever #4863 Returned PRs were compared against current files and overlapping diffs: fix(core): a task the Stop hook hands to the core inline is recorded as read #5302: Different changed files.; fix(stop-hook): a guest session owes nothing, a task in progress does not block, and the same queue cannot block a turn end forever #4863: Different changed files.. Title similarity alone is not equivalence; this is not exhaustive semantic coverage.

  • Upstream observed during evidence preparation: 9d70abfa2b0af2a1305eba644c7677eeca2dd4e6. 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.status, its callers, activity_bus.in_progress and production activity writers. Ownership, ready-result and HITL checks precede engagement lookup; claimed tasks short-circuit it. Additional real-socket observations confirmed assigned-task engagement, incomplete-result handling, cancellation without target mutation and exclusion of activity lacking a live task file. Evidence: diagnostics/review-adjacent-states.json.

  • review-scope: The diff changes only src/runtime-api/tasks_view.py and tests/runtime-api-task-engagement.test.py. Engagement freshness, activity writers, watcher behavior, cancellation protocol, archive lookup and public state vocabulary remain unchanged.

  • review-maintainability: The implementation delegates to the existing engagement policy using self.tasks_dir.parent without duplicating freshness logic. Targeted exception handling protects reads from malformed activity. The integration harness uses production writers, actual daemon/CLI transport, bounded readiness and subprocess calls, and registered cleanup with terminate/wait and kill/wait fallback.

  • 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 -B [local path] at 2026-10-16T18:59:04Z–18:59:16Z: exit 0, 113 real CLI calls. Started python3 src/runtime-api/server.py on private monitor-journey-3/api.sock. Commands through src/runtime-cli/sutando-runtime.py included task submit, status, details, list and get-result. Production activity writers changed both unclaimed tasks from pending to in_progress across all three read surfaces; atomic result publication produced done and returned 'Observed local completion'. Exact commands, responses and writer actions: monitor-journey.jsonl.

  • monitor-repeatability: Repeated task status/details/list for task-rtapi-2e31eba5e4ad and task-rtapi-c48983f0fe2f twice: every response showed in_progress. Terminated and waited for daemon PID 80545, then reran python3 src/runtime-api/server.py as PID 80805 without refreshing engagement. The same commands still showed in_progress; after publication, repeated reads showed done and unchanged result text. Readiness took 0.298 and 0.308 seconds. Evidence: monitor-journey.jsonl and monitor-journey-summary.json.

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

  • Risks / rollback: Change scope: Update TasksView.status to consult the existing activity_bus.in_progress policy for owned live tasks without claim filenames, using the workspace associated with its injected tasks directory. Preserve result and HITL precedence, existing claimed-task behavior, archive lookup and ownership boundaries. Add focused real Unix-socket regression coverage. Do not change engagement freshness rules, activity writers, watcher behavior, cancellation semantics or the public state vocabulary.
    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 [local path] and ran python3 [local path] preflight: exit 0, private sockets available. Ran python3 [local path] at 2026-10-16T18:47:47.790308Z–18:47:57.160456Z: exit 0, 98 real CLI calls reproduced the discrepancy. Commands, responses and timestamps are in diagnostics/triage-witness.json, SHA256 97877a4382a1eab4219f14afbfb0514f29b0a20fed671e456ba4c3daca9c6be8. Final git status was clean; working-tree and cached diffs both passed --exit-code.

Recorded command/output on the pinned source:

/bin/zsh -lc "python3 - <<'PY'
import os,json,subprocess
from pathlib import Path
root=Path('[local path]')
env=os.environ.copy(); env.update(json.loads((root/'environment.json').read_text()))
p=subprocess.run(['python3',str(root/'process-tools.py'),'preflight'],env=env,text=True,capture_output=True,timeout=30)
print(p.stdout); print(p.stderr); print('exit_code:',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": "18946\n"}, "private_socket": {"available": true}}


exit_code: 0

Exit: 0

/bin/zsh -lc "python3 - <<'PY'
from pathlib import Path
import json,hashlib
root=Path('[local path]'); diag=root/'diagnostics'
for name in ('capability-preflight.json','triage-daemon-initial.log','triage-daemon-restart.log'):
    p=diag/name
    print(name + ': ' + p.read_text())
r=json.loads((diag/'triage-witness.json').read_text())
print(json.dumps({'read_only_unchanged':r['read_only']['unchanged'],'fingerprinted_files':len(r['read_only']['before']),'readiness':r['processes'],'cli_calls':r['cli_calls'],'started_at':r['started_at'],'ended_at':r['ended_at']},indent=2))
for p in (root/'triage-witness.py', diag/'triage-witness.json',diag/'task-view-regressions.log',diag/'engagement-policy-controls.log'):
    print(p.name, 'sha256='+hashlib.sha256(p.read_bytes()).hexdigest(), 'bytes='+str(p.stat().st_size))
PY"
capability-preflight.json: {
  "schema": "ag2-runtime-preflight/v1",
  "product_ready": "not observed",
  "tmux": {
    "available": true,
    "detail": "tmux 3.8\n"
  },
  "process_inspection": {
    "available": true,
    "detail": "18946\n"
  },
  "private_socket": {
    "available": true
  }
}
triage-daemon-initial.log: [runtime-api] listening on [local path] (actor=factory-runtime-test)

triage-daemon-restart.log: [runtime-api] recovered 4 pending request(s)
[runtime-api] listening on [local path] (actor=factory-runtime-test)

{
  "read_only_unchanged": true,
  "fingerprinted_files": 57,
  "readiness": [
    {
      "argv": [
        "python3",
        "./src/runtime-api/server.py"
      ],
      "label": "initial",
      "pid": 25860,
      "started_at": "2026-10-16T18:47:47.792430+00:00",
      "ready": true,
      "readiness_seconds": 0.32539604196790606,
      "exit_code": 0,
      "reaped": true,
      "stopped_at": "2026-10-16T18:47:55.717296+00:00"
    },
    {
      "argv": [
        "python3",
        "./src/runtime-api/server.py"
      ],
      "label": "restart",
      "pid": 26370,
      "started_at": "2026-10-16T18:47:55.719013+00:00",
      "ready": true,
      "readiness_seconds": 0.3310754999984056,
      "exit_code": 0,
      "reaped": true,
      "stopped_at": "2026-10-16T18:47:57.160341+00:00"
    }
  ],
  "cli_calls": 98,
  "started_at": "2026-10-16T18:47:47.790308+00:00",
  "ended_at": "2026-10-16T18:47:57.160456+00:00"
}
triage-witness.py sha256=2a41f6f9bd5458e0114ee3ac62aec86e4508c7e7eee6288fbe91d9ca3503fce5 bytes=9809
triage-witness.json sha256=97877a4382a1eab4219f14afbfb0514f29b0a20fed671e456ba4c3daca9c6be8 bytes=193419
task-view-regressions.log sha256=eb0013df56241f49898109f646685c32ba3d15cd8e41c13a4a73b30d5111b57b bytes=9935
engagement-policy-controls.log sha256=ceea5e339bfb84b2653af263ee2fe0a0e2de2bfaed2f3efd00a6d42b42fde502 bytes=109

Exit: 0

After

  • acceptance-1: Real server.py and runtime-cli over private Unix sockets reported in_progress consistently in status, details and list for fresh production activity-row and runtime-event engagement. Task filenames, file identities, modification times and activity-content hashes remained unchanged.

  • acceptance-2: Absent activity, delivery-only RUNNING, processing/thinking, row-only, stale/future engagement, invalid timestamps and malformed records remained pending. Claimed tasks remained in_progress, including with malformed activity. Cancellation regression checks also passed.

  • acceptance-3: All three HITL waiting states retained precedence over engagement; ready results then produced done. Foreign tasks/results remained inaccessible and unlisted. Engagement confined to another workspace left the owned task pending. Archive lookup remained supported.

  • acceptance-4: Both engagement paths survived restart from daemon PID 35017 to 35087 at 2026-10-16T18:50:05.998866Z, then transitioned to done after result publication. Every product subprocess inherited the supplied scratch environment. Readiness used real socket RPC with a 10-second bound. Across ordinary and coverage runs, all 12 daemons exited and were reaped; maximum readiness was 0.673 seconds and all sockets were removed. Startup commands, timestamps and lifecycle receipts: [local path]

  • check-task-engagement-integration: python3 tests/runtime-api-task-engagement.test.py — 5 tests passed, exit 0, 17.233 seconds; started 2026-10-16T18:49:50.316391Z. Log: [local path]

  • check-task-view-regressions: python3 tests/runtime-api-tasks-view.test.py — 73 tests passed, exit 0, 1.845 seconds. Log: [local path]

  • check-engagement-policy-controls: python3 tests/activity-bus-in-progress.test.py — 10 tests passed, exit 0, 0.198 seconds. Log: [local path]

  • coverage: Ran python3 -m coverage run --rcfile=.coveragerc tests/runtime-api-task-engagement.test.py with subprocess coverage enabled, then combined data and generated XML. diff-cover [local path] --compare-branch=8c2323052d779facfe08199e3908827d1c69a371 --fail-under=95 passed: 5 changed executable lines, 0 missing, 100% coverage. The product's unmeasured-file check reported no omissions.

  • candidate-identity: HEAD matches the supplied candidate; its merge base with the approved source is 8c23230. Only the two allowed paths differ. Final git status was clean at 2026-10-16T18:52:17Z. Source, engagement policy, writers and watcher behavior were not edited during verification.

Test plan

python3 tests/runtime-api-task-engagement.test.py

Observed: PASS; exit 0.

test_fresh_engagement_is_visible_and_reads_are_non_mutating (__main__.TaskEngagementTests.test_fresh_engagement_is_visible_and_reads_are_non_mutating) ... ok
test_malformed_activity_preserves_reads_and_cancellation (__main__.TaskEngagementTests.test_malformed_activity_preserves_reads_and_cancellation) ... ok
test_negative_controls_and_claimed_task (__main__.TaskEngagementTests.test_negative_controls_and_claimed_task) ... ok
test_precedence_ownership_archive_and_workspace_isolation (__main__.TaskEngagementTests.test_precedence_ownership_archive_and_workspace_isolation) ... ok
test_restart_preserves_engagement_then_result_completes_task (__main__.TaskEngagementTests.test_restart_preserves_engagement_then_result_completes_task) ... ok

----------------------------------------------------------------------
Ran 5 tests in 16.907s

OK
python3 tests/runtime-api-tasks-view.test.py

Observed: PASS; exit 0.

test_archived_after_the_ready_read_still_returns_the_answer (__main__.ArchivalRaceTests.test_archived_after_the_ready_read_still_returns_the_answer) ... ok
test_archived_during_enumeration_is_absent_not_an_error (__main__.ArchivalRaceTests.test_archived_during_enumeration_is_absent_not_an_error) ... ok
test_positive_control_the_result_is_listed_normally (__main__.ArchivalRaceTests.test_positive_control_the_result_is_listed_normally) ... ok
test_list_process_missing_acknowledgment_fails (__main__.DispatchTests.test_list_process_missing_acknowledgment_fails) ... ok
test_list_separate_process_claim_archive_and_control (__main__.DispatchTests.test_list_separate_process_claim_archive_and_control) ... ok
test_submit_idempotency_rejects_invalid_keys_and_collisions (__main__.DispatchTests.test_submit_idempotency_rejects_invalid_keys_and_collisions) ... ok
test_submit_idempotency_retry_after_restart_and_completion (__main__.DispatchTests.test_submit_idempotency_retry_after_restart_and_completion) ... ok
test_submit_status_result_dispatch (__main__.DispatchTests.test_submit_status_result_dispatch) ... ok
test_submit_without_key_remains_non_idempotent (__main__.DispatchTests.test_submit_without_key_remains_non_idempotent) ... ok

[Excerpt: middle omitted; full recorded output SHA256 bf0e3a3c4b169d46ad0a620ce5e9b431d4df9ab2d714509df45fad0f5354aa1f]
only) ... ok
test_list_tasks_disappearing_during_metadata_access (__main__.TasksViewTests.test_list_tasks_disappearing_during_metadata_access) ... ok
test_list_tasks_empty_dir (__main__.TasksViewTests.test_list_tasks_empty_dir) ... ok
test_list_tasks_enumerates_live_only (__main__.TasksViewTests.test_list_tasks_enumerates_live_only) ... ok
test_list_tasks_retains_mtime_and_limit_semantics (__main__.TasksViewTests.test_list_tasks_retains_mtime_and_limit_semantics) ... ok
test_longer_task_ids_gateway_archive_is_not_this_task (__main__.TasksViewTests.test_longer_task_ids_gateway_archive_is_not_this_task)
A longer id's epoch-suffixed archive shares this id's prefix. ... ok
test_metadata_scans_propagate_operational_errors (__main__.TasksViewTests.test_metadata_scans_propagate_operational_errors) ... ok
test_queue_priority_roundtrip_order_and_body_confinement (__main__.TasksViewTests.test_queue_priority_roundtrip_order_and_body_confinement) ... ok
test_result_disappearing_during_metadata_access (__main__.TasksViewTests.test_result_disappearing_during_metadata_access) ... ok
test_status_broken_hitl_lookup_fails_open (__main__.TasksViewTests.test_status_broken_hitl_lookup_fails_open) ... ok
test_status_lifecycle_pending_claimed_done (__main__.TasksViewTests.test_status_lifecycle_pending_claimed_done) ... ok
test_status_unknown_task (__main__.TasksViewTests.test_status_unknown_task) ... ok
test_status_waiting_for_states_from_hitl_lookup (__main__.TasksViewTests.test_status_waiting_for_states_from_hitl_lookup) ... ok
test_submit_confines_newline_injection (__main__.TasksViewTests.test_submit_confines_newline_injection) ... ok
test_submit_rejects_empty_and_bad_priority (__main__.TasksViewTests.test_submit_rejects_empty_and_bad_priority) ... ok
test_submit_stamps_instance_id_when_scoped (__main__.TasksViewTests.test_submit_stamps_instance_id_when_scoped) ... ok
test_submit_writes_canonical_headers_real_parser_roundtrip (__main__.TasksViewTests.test_submit_writes_canonical_headers_real_parser_roundtrip) ... ok
test_get_result_traversal_reads_as_absent (__main__.TraversalGuardTests.test_get_result_traversal_reads_as_absent) ... ok
test_hostile_ids_absent_on_every_entry_point (__main__.TraversalGuardTests.test_hostile_ids_absent_on_every_entry_point) ... ok
test_legit_ids_still_resolve (__main__.TraversalGuardTests.test_legit_ids_still_resolve) ... ok

----------------------------------------------------------------------
Ran 73 tests in 1.486s

OK
python3 tests/activity-bus-in-progress.test.py

Observed: PASS; exit 0.

..........
----------------------------------------------------------------------
Ran 10 tests in 0.011s

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: ac722ba7eabed5c1f1caf3ebefffc940dcfa3267. Publication commit: 68a424cbabae0f64f471aa876d8b8146c0621f74. Both have tree c99fc4c9b8ca514eba6b8fb36a8a2b2cceb274a7; 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, not just reading the pasted evidence:

  • Bug confirmed on main. TasksView.status() only checked ".claimed-" in live.name; a task that's genuinely RUNNING with real tool activity but hasn't hit the claim-rename convention read back as pending. Reproduced this directly: checked out the new test file against the parent commit (8c23230) with the old tasks_view.py, and 3/5 subtests failed exactly as expected (['pending','pending','pending'] != ['in_progress','in_progress','in_progress']). At the PR head, all 5 pass. That's a real regression test, not a happy-path tautology.
  • Single source of truth, so the fix's reach is wider than the one-line diff suggests. details(), list_tasks() and the cancel path all call self.status(task_id)["state"] — no duplicated claim-check anywhere else needed fixing.
  • Traced the exception path. The broad except (UnicodeError, TypeError, OverflowError) around the new call is exactly sized to what in_progress()/engaged_at() can raise on adversarial on-disk JSON: UnicodeDecodeError (⊂ UnicodeError) from a truncated/invalid-utf8 line read under encoding="utf-8", OverflowError from math.isfinite() on a too-large int timestamp, TypeError from an unhashable kind field (list/dict) in the not in _ENGAGED_KINDS check. All three map to the test's truncated-utf8 / invalid-utf8 / overflow-row / overflow-snapshot / list-kind / object-kind controls. Fail-closed direction (falls back to pending, never crashes a read or blocks cancellation) is the right default for this kind of malformed-data path.
  • Workspace scoping verified: self.tasks_dir.parent is the right workspace root for in_progress(), and the isolation subtest (engagement recorded in a different workspace doesn't flip the owned task) backs that.
  • review-checks clean on my own run against the merge-base (not just the PR's claim): PASS (hardcoded-paths + root-artifacts + prose-cap clean).
  • CI green, CLA passing, scope is exactly tasks_view.py + a new test file, no public state vocabulary or cancellation/delivery semantics touched.

Ready to merge once required CI/CLA/approvals are satisfied — merge itself needs the owner's word, not something I'll do on a collaborator ask alone.

@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: 5 lines
  • 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.

Independent second look at head 68a424cba. sonichi's approval covers the same head.

What I ran (fresh checkout of 68a424cba, with SUTANDO_WORKSPACE_DIR and SUTANDO_TASKS_DIR unset):

  • tests/runtime-api-task-engagement.test.py: 5/5 passed
  • tests/runtime-api-tasks-view.test.py: 73/73 passed
  • tests/activity-bus-in-progress.test.py: 10/10 passed

What I read:

  • The policy is delegated, not copied. TasksView.status calls the existing activity_bus.in_progress (src/activity_bus.py:317) and adds no freshness rule of its own. The result, HITL and ownership checks still come first, and a claimed task short-circuits before the activity read.
  • The workspace argument is correct for the shipped wiring. self.tasks_dir.parent is the workspace because server.py:150 builds TasksView(Path(state_dir).parent / "tasks", …).
  • The exception list matches what the callee can still raise. in_progress already swallows OSError/ValueError on the snapshot. engaged_at (activity_bus.py:289) only catches OSError around the file, so a bad-UTF-8 line escapes as UnicodeDecodeError, and the new except (UnicodeError, …) is what stops that from failing a status read. OverflowError covers float() of a huge integer stamp.

Non-blocking:

  • Status reads now scan the whole activity log. list_tasks calls status() once per task (tasks_view.py:374), and each call now reads all of state/agent-activity.jsonl. On one live host that file is 400 lines (~200 KB), so the cost is negligible today. If the log is ever left to grow without bound, list gets linear in tasks × log size.
  • The live-path proof is harness-only. The body's evidence is real daemon/CLI runs against a scratch socket, not a running core. A round trip on a real install is owed, but per this repo's notes that is named here rather than blocking.

No hardcoded host paths in the added lines.

— Sutando (susans-MacBook-Pro)

@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.

Approve at 68a424c (code owner for /src/runtime-api/).

  • It asks the owner, not the files. An unclaimed live task now consults activity_bus.in_progress(...), the module that owns activity state, instead of re-reading activity files. Only working rows and runtime events count as engagement.
  • "Fresh" is bounded. Engagement counts only while the newest stamp is under IN_PROGRESS_MAX_AGE_S (1800 s), so a crashed core shows a task as in progress for at most 30 minutes before it falls back to pending. Future-dated or non-finite stamps are rejected.
  • No new exposure. The response is still {taskId, state}; the traversal and ownership checks run before the new lookup, and server.py and dispatcher.py are untouched. A ready result still reports done, and the waiting states still take precedence.

Verified (reviewer agent, fresh worktree): tests/runtime-api-task-engagement.test.py 5/5 at head, and 3 failures with tasks_view.py reverted to the merge-base. runtime-api-tasks-view 73/73, activity-bus-in-progress 10/10, and the dispatcher, e2e and server suites pass. CI is green. I read the source change myself.

Follow-up worth an issue (not this PR's bug): the caller's except (UnicodeError, TypeError, OverflowError) is covering for activity_bus. One invalid byte in state/agent-activity.jsonl makes engaged_at raise UnicodeDecodeError while reading the file, so engagement disappears for every task at once, and the in-progress CLI that check-pending-tasks.sh calls exits with a traceback. The fix belongs in activity_bus (decode with errors="replace" or skip per line), after which this catch can go. Today's behaviour falls back to pending, the same as before this PR, so it isn't a regression.

Nit: in_progress reads the whole activity file per call, and list calls status per task. Fine at today's size; worth an index if the file grows.

@liususan091219
liususan091219 merged commit 31fe692 into main Oct 16, 2026
34 checks passed
@liususan091219
liususan091219 deleted the factory/kc/revalidate-ad068ba303023b32dc7d2ff8-a7d969b4514e branch October 16, 2026 20:18
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