Skip to content

P1: Make non-testing Task input and handoff intents restart-convergent #38

Description

@YueZh127

Objective

Make non-testing Task input and handoff intents restart-convergent without allowing stale claim generations to consume user side effects.

Review origin and observation baseline

Current defects

provideInput() commits needs_input -> running before savePendingInput(). A process exit, thrown write, or lost acknowledgement after the Task CAS can leave a running Task with no consumable input. Retrying the user request is rejected because the Task is no longer needs_input.

requestHandoff() commits the Task handoff state and URL before saveHandoff(). The same failures can leave a public Task pointing at a handoff URL whose record does not exist, while a retry is rejected because the Task is already in handoff.

Moving either sidecar write back before the Task CAS is not acceptable: that recreates the stale-generation and orphan-side-effect failures closed by PR #35.

Required authority model

  • Bind every pending-input and handoff intent to the exact Task claimId and claimGeneration.
  • Use either an authoritative intent embedded in the atomically replaced Task document or a durable operation phase that can be reconciled after restart.
  • Materialize and consume sidecar records idempotently; a lost acknowledgement must be distinguishable from a lost write.
  • A generation-N input or handoff can never be consumed, exposed, or materialized for generation N+1.
  • Standalone MongoDB remains supported. Do not require multi-document transactions or replica-set-only topology.
  • MemoryRepository and MongoRepository must expose equivalent observable behavior.
  • Internal claim IDs, generations, tokens, and reconciliation state remain absent from public Task/Session APIs, handoff responses, logs, webhooks, and errors.

Acceptance criteria

  • Preserve PR github-devloop implementation for #34 #35 behavior: three bounded user CAS retries, 409 concurrent_update for real contention, non-retryable inactive-claim conflict, and zero sidecar writes after a losing CAS.
  • Inject a thrown savePendingInput() after the winning Task CAS and prove client retry or reconciliation completes exactly one generation-bound input.
  • Inject a lost acknowledgement after a committed pending-input write and prove no duplicate delivery.
  • Restart a real standalone Mongo repository after the Task CAS and before input materialization; prove convergence and one-time worker consumption.
  • Repeat the thrown-write, lost-acknowledgement, and standalone-Mongo restart matrix for handoff materialization and one-time handoff consumption.
  • Expire/requeue generation N and reclaim N+1 at every durable phase; prove no N intent reaches N+1 and no stale URL becomes resolvable.
  • Add deterministic barrier-controlled Memory/Mongo service contracts plus HTTP regressions. Do not use sleeps or timing assumptions.
  • Run build, typecheck, lint, worker bundle, fixture drift, full tests, the standalone Mongo contract, and all four PR checks.

Scope and sequencing

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions