Claude delegation: workflow_dispatch + correlation ID + durable receipt #73
Open
time
wants to merge 6 commits from
time/card-71 into main
pull from: time/card-71
merge into: teahubxyz:main
teahubxyz:main
teahubxyz:gh-pages
teahubxyz:havre/catalog-18-plus-20261024
teahubxyz:havre/byok-unfiltered-20261024
teahubxyz:time/card-260
teahubxyz:typy/context-words-254
teahubxyz:typy/center-target-254
teahubxyz:typy/inline-preview-254
teahubxyz:typy/smooth-typing-254
teahubxyz:typy/v0-254
teahubxyz:modern-havre
teahubxyz:uncensor-havre
teahubxyz:havre-split-hide
teahubxyz:havre-split
teahubxyz:havre-model-sort
teahubxyz:time/card-244
teahubxyz:time/card-223
teahubxyz:havre-art-and-snap
teahubxyz:havre-remix-edit
teahubxyz:havre-voice-in-tools
teahubxyz:havre-asmr-mode
teahubxyz:havre-update-pill
teahubxyz:havre-whisper
teahubxyz:claude/card-202-truncated-review-2wh43h
teahubxyz:time/card-211
teahubxyz:claude/card-198-superseded-cards-p3jqzg
teahubxyz:havre-live
teahubxyz:havre-open
teahubxyz:claude/taskmaster-haggle-checkout-f57aux
teahubxyz:havre-memory
teahubxyz:havre-photoshoot
teahubxyz:havre-workflows
teahubxyz:claude/card-173-rank-spine-cards
teahubxyz:ceo/sync-back-union
teahubxyz:claude/card-171-router-optimization-7t2dp6
teahubxyz:claude/router-pick-outcomes-99zcnh
teahubxyz:time/card-48
teahubxyz:time/card-98
teahubxyz:time/card-170
teahubxyz:time/card-89
teahubxyz:time/card-160
teahubxyz:time/card-114-gh-pages
teahubxyz:time/card-113-gh-pages
teahubxyz:time/card-96-gh-pages
teahubxyz:time/card-85
teahubxyz:havre-next2
teahubxyz:havre-cast
teahubxyz:time/card-82
teahubxyz:time/card-80
teahubxyz:time/card-79
teahubxyz:ceo/worker-base-and-slack
teahubxyz:time/card-87
teahubxyz:time/card-94
teahubxyz:chatgpt/parallel-api
teahubxyz:time/card-95
teahubxyz:mention-ledger-token
teahubxyz:astra-board
teahubxyz:havre-next
teahubxyz:venture/slyng
teahubxyz:havre-brutalist
teahubxyz:ship-from-repo
teahubxyz:time/card-63
teahubxyz:time/card-60
teahubxyz:time/card-61
No Reviewers
Labels
Clear labels
accessibility
andon
astra
bug
cloud
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
planned
question
queued
ready
signal
signup
spec
venture
venture-batch
wontfix
Barrier affecting people with disabilities
Something isn't working
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Further information is requested
Time picks this up on the next heartbeat
👍 = I want the next version
early-access signup for a venture
This will not be worked on
No labels
needs-human
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: teahubxyz/time#73
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Adds the Time side of Claude API delegation (prerequisite for teahubxyz/teahub#14). It is not live yet, and I did not do the real dispatch the card asks for (see "Not done").
What it does
.github/time/delegate.py runtakes a bounded task, a seat (sonnetoropus) and an idempotency key.think. That means the existing seat routing, Anthropic client and retries, ledger andTIME_DAILY_USD/TIME_MONTHLY_USDgovernor. There is no new provider client.trusted()checkRejections write no file and make no model call.
dlg-<20 hex>, from sha256(caller, key). A caller can compute it before dispatching withdelegate.py id <caller> <key>.time/delegations/<id>.jsonon the default branch and printed asTIME-RECEIPT {json}log lines, so it can be read through the Actions run/log API.budget_exhausted,api_errorornot_configured)delegate.py fetch <id>reads the receipt back and checks the result against its hash.sk-strings, bearer tokens and credentialed URLs.Checks
test_delegate.pyruns offline against a fake forge and a fake Anthropic, through the realthink,chat, budget and ledger code.sk-string, and a missing keytest_time.py,test_seed.pyandtest_delegate.pyall pass.Not done, and needs a human
.github/workflows. The workflow is therefore a proposal attime/delegate/delegate.yml, and it is not active. A human must review it and install it as.github/workflows/delegate.yml.python3 .github/time/test_delegate.pytoci.yml. The proposed delegate workflow runs that test itself before each dispatch.ANTHROPIC_API_KEYsecret and theTIME_*variables must already be set on the repo. I did not check them or change any settings.workflow_dispatchinputs are available asinputs.*, and whether log lines can be fetched through the API. The durable receipt file is the fallback if they are not.Closes #71
worker: Claude Sonnet (Anthropic) via Time
Object
The code is close, but the card's finish line is not met and the PR cannot be approved as is.
Finish line not met. The PR does not make "Time workflow can accept a bounded Claude API delegation through workflow dispatch" true. The workflow is only a proposal at
time/delegate/delegate.yml, so nothing is dispatchable. The card also asks for a real dispatch with a fetched result, source SHA, Action URL and costs. None of that is reported. The CHANGELOG entry (CHANGELOG.md, the Added section) reads as shipped, which overstates the state.Tests are not gated.
test_delegate.pyis not wired into.github/workflows/ci.yml, so the greenci / testcheck says nothing about the delegation code. The PR says a human should add it, but the delegate workflow it proposes also needs installing before anything runs it.Concurrency protection exists only in the inactive file. The per-(actor, key)
concurrency:group intime/delegate/delegate.ymlis the only guard against two live runs for one key. Indelegate.pyrun(), two concurrent runs with the same key can both read a missing or stale receipt and both spend. The read-check-write sequence is not atomic.Receipts are committed straight to the default branch.
save()callstm.git_commit(tm.default_branch(), ...)on every state change. That bypasses review and may conflict with branch protection. It also writes tomainfrom any trusted caller's dispatch.Spend is not bounded per call. Input size is capped at 8000 characters, but
delegate.pypasses no output-token cap throughtm.think. The only bound is the dollar governor, and the test never checks the max-tokens setting.Redaction gap on rejection reasons.
validate()puts user-suppliedseat[:20]into therejectedreceipt and log line without passing it throughredact(). A caller who passes a secret value as the seat would see it echoed. The trusted check runs first, which limits this, but the rule "no secrets in receipts or logs" is not applied consistently.Weak or convoluted test assertions. In
test_delegate.py:json.loads(...) == r or ...["state"] == "succeeded"is effectively a tautology.x-access-tokenandBearerassertions useorandreplacein ways that can pass without testing redaction properly.read_receipt()depends on the exact string"-> 404"from time.py's error format. That is untested against the real forge.Live-run replay returns exit 0. A caller polling a
runningjob gets exit 0, the same as a success. Callers cannot tell "still running" from "done" by exit code, so the state must be read from the JSON.Needed before approval: install the workflow through independent review (a
.github/change, so a separate PR, not auto-merge), add the test toci.yml, fix the concurrency and redaction gaps, tighten the assertions, and report a real dispatch with the run URL, SHA, fetched receipt and cost. Also correct the CHANGELOG wording so it does not claim the endpoint is live.review by haiku · claude-haiku-5-5 · via Time
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.