发布

  • fix(harness)!: hydrate runs from RunStore and persist interrupted status (#2932)

    frostbyte_neo 发布于 2026-05-18 14:25:02 +00:00 | 1115 次提交 在此版本后已推送到 main

    • fix(harness): hydrate run history from RunStore and persist cancellation status

    fix:

    • Make RunManager.get() async and hydrate from RunStore when in-memory record is missing
    • Merge store rows into list_by_thread() with in-memory precedence for active runs
    • Persist interrupted status to RunStore in cancel() and create_or_reject(interrupt|rollback)
    • Extract _persist_status() to reuse the best-effort store update pattern
    • Await run_mgr.get() in all gateway endpoints
    • Return 409 with distinct message for store-only runs not active on current worker

    Closes #2812, Closes #2813

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • fix(harness): consistent sort and guarded hydration in RunManager

    fix:

    • list_by_thread() now sorts by created_at desc (newest first) even when
      no RunStore is configured, matching the store-backed code path
    • guard _record_from_store() call sites in get() and list_by_thread()
      with best-effort error handling so a single malformed store row cannot
      turn read paths into 500s

    test:

    • update test_list_by_thread assertion to expect newest-first order
    • seed MemoryRunStore via public put() API instead of writing to _runs
    • fix(harness): guard store-only runs from streaming and fix get() TOCTOU

    Add RunRecord.store_only flag set by _record_from_store so callers can
    distinguish hydrated history from live in-memory runs. join_run and
    stream_existing_run (action=None) now return 409 instead of hanging
    forever on an empty MemoryStreamBridge channel.

    Re-check _runs under lock after the store await in RunManager.get() so a
    concurrent create() that lands between the two checks returns the
    authoritative in-memory record rather than a stale store-hydrated copy.

    Co-Authored-By: Claude Sonnet 4 noreply@anthropic.com

    • fix(harness): reorder bridge fetch in join_run and make list_by_thread limit explicit

    Move get_stream_bridge() after the store_only guard in join_run so a
    missing bridge cannot produce 503 for historical runs before the 409
    guard fires.

    Add limit parameter to RunManager.list_by_thread (default 100, matching
    the store's page size) and pass it explicitly to the store call.
    Update docstring to document the limit instead of claiming all runs are
    returned.

    Co-Authored-By: Claude Sonnet 4 noreply@anthropic.com

    • fix(harness): cap list_by_thread result to limit after merge

    Apply [:limit] to all return paths in list_by_thread so the method
    consistently returns at most limit records regardless of how many
    in-memory runs exist, making the limit parameter a true upper bound
    on the response size rather than just a store-query hint.

    Co-Authored-By: Claude Sonnet 4 noreply@anthropic.com

    • fix list_by_thread docstring

    Co-authored-by: Copilot Autofix powered by AI 175728472+Copilot@users.noreply.github.com

    • fix(runtime): add update_model_name to RunStore to prevent SQL integrity errors

    RunManager.update_model_name() was calling _persist_to_store() which uses
    RunStore.put(), but RunRepository.put() is insert-only. This caused integrity
    errors when updating model_name for existing runs in SQL-backed stores.

    fix:

    • Add abstract update_model_name method to RunStore base class
    • Implement update_model_name in MemoryRunStore
    • Implement update_model_name in RunRepository with proper normalization
    • Add _persist_model_name helper in RunManager
    • Update RunManager.update_model_name to use the new method

    test:

    • Add tests for update_model_name functionality
    • Add integration tests for RunManager with SQL-backed store

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • fix(runtime): handle NULL status/on_disconnect in _record_from_store

    dict.get(key, default) only uses the default when the key is absent,
    so a SQL row with an explicit NULL status would pass None to
    RunStatus(None) and raise, breaking hydration for otherwise valid rows.
    Switch to row.get(...) or fallback so both missing and NULL values
    get a safe default. Add tests for get() and list_by_thread() with a
    NULL status row to prevent regression.

    Co-Authored-By: Claude Sonnet 4 noreply@anthropic.com

    • fix(runs): address PR review feedback on store consistency changes
    • Fix list_by_thread limit semantics: pass store_limit = max(0, limit - len(memory_records)) to store so newer store records are not crowded out by in-memory records
    • Remove dead code: cancelled guard after raise is always True, simplify to if wait and record.task
    • Document _record_from_store NULL fallback policy (status→pending, on_disconnect→cancel) in docstring

    Co-Authored-By: Claude Sonnet 4 noreply@anthropic.com


    Co-authored-by: Claude Opus 4.7 noreply@anthropic.com
    Co-authored-by: Copilot Autofix powered by AI 175728472+Copilot@users.noreply.github.com

    下载附件