Skip to content

design: milestone revocation — decided, no revocation - #35

Merged
jayteemoney merged 1 commit into
mainfrom
design/milestone-revocation
Aug 14, 2026
Merged

design: milestone revocation — decided, no revocation#35
jayteemoney merged 1 commit into
mainfrom
design/milestone-revocation

Conversation

@jayteemoney

@jayteemoney jayteemoney commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Closes #17. First of Track A (#34).

Decision: a met milestone cannot be revoked. Milestone state is monotonic and Met is terminal.

The deciding argument is arithmetic, not preference

The intuitive option — revocation re-locks the tranche, prior withdrawals stand — sounds fair and isn't. Run it against the worked example, with the approver revoking at day 19:

Day streamed_total held claimable
19 19,000,000,000 7,600,000,000 −6,600,000,000
22 22,000,000,000 8,800,000,000 −4,800,000,000
26 26,000,000,000 10,400,000,000 −2,400,000,000
30 30,000,000,000 12,000,000,000 0

Claimable goes negative and only reaches zero on the final day. Clamped at zero, as any implementation must, Bob receives nothing for the remaining twelve days — on a base tranche that was never gated.

The mechanism is that withdrawn is a single stream-wide counter, so re-imposing held after a withdrawal has settled charges the shortfall against whatever the recipient's other tranches earn next. A milestone decision reaches across tranche boundaries and cancels income the milestone never governed. Money Bob was unambiguously entitled to when he withdrew it becomes a debt against his future earnings.

Restricting revocation to "before anything is withdrawn" avoids that, and replaces it with a race between approver and recipient decided by who submits first — which rewards whoever is running a bot rather than whoever is right.

What it protects

  • The no-custody claim becomes structurally true. The approver's power moves value in one direction only. Under revocation they could reduce what the recipient ultimately receives, which is custody in substance, and concepts.md would need amending to something much weaker.
  • The milestone struct stays small. A revocable milestone needs a released-amount field and a revocation counter per milestone, inside the stream entry — pushing directly against threat-model T2 and T8, the read budget and restore cost that MAX_MILESTONES_PER_STREAM exists to manage.
  • The indexer's fold never walks backwards. claimable is non-decreasing in approvals.

What it gives up, stated plainly

No in-protocol remedy for an approval that turns out to be wrong. That was never StelFlow's to handle — architecture.md already draws the line where Trustless Work decides whether a condition is met and StelFlow decides how fast money moves. A grant program needing disputes names a TW escrow as approver, and that escrow withholds approval until its process concludes. Revocation's real use case, solved by not approving yet, in the contract that has the machinery.

Changes

Acceptance criteria

  • A decision with reasoning written down, not just the conclusion
  • concepts.md states the rule; TODO removed
  • architecture.md reflects the storage consequence
  • The "does not give the approver custody" claim checked — still true, and strengthened

Makes T3 more urgent, not less. With no revocation and no deadline, a non-cancelable stream whose approver disappears has no recovery path for anyone. Deciding deadlines is now the outstanding half of the milestone lifecycle.

Closes #17.

A met milestone cannot be un-met. Milestone state is monotonic and
Met is terminal.

The deciding argument is arithmetic. Re-locking a tranche after a
withdrawal has settled charges the shortfall against the recipient's
other tranches, because withdrawn is one stream-wide counter. Run
against the concepts.md worked example, revoking at day 19 puts
claimable at -6,600,000,000 and it stays negative until it reaches
exactly zero on day 30 - so the recipient receives nothing for the
stream's remaining twelve days, on a base tranche that was never
gated. Money they were entitled to when they withdrew it becomes a
debt against future earnings.

Restricting revocation to before-any-withdrawal avoids that and
instead makes the outcome a race between approver and recipient,
decided by who submits first.

Keeping Met terminal also keeps the approver's power one-directional,
which is what makes concepts.md's no-custody claim structurally true
rather than merely currently true, and keeps the milestone struct
small - a revocable milestone needs a released-amount field and a
revocation counter per milestone, pushing against the read budget and
restore cost the milestone cap exists to manage.

Gives up: no in-protocol remedy for an approval that was wrong.
Recorded plainly. Disputes belong in an escrow that has a dispute
process, which is the Trustless Work split architecture.md already
describes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4KmSPBuq484Zt65qsK177
@jayteemoney
jayteemoney merged commit 45dc356 into main Aug 14, 2026
3 checks passed
@jayteemoney
jayteemoney deleted the design/milestone-revocation branch August 14, 2026 09:28
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.

design: can an approver revoke a met milestone, and what happens to funds already withdrawn?

1 participant