@shentonyan thank you for reading the text at 2253323 against itself — both inconsistencies are real, and they are the text’s, not the kernel’s. The kernel does what my #21 reply said it does; the draft said something stronger in two places and something unimplementable in a third. All three are fixed on the PR branch before this reply, and nothing in the interface or the reference implementation changes.
1. Eligibility on the judged path. You are right that “acceptance is the single gate that makes a submission count” is false as written: the deadline default is a second exit, and it checks nothing. The honest statement is the one #21 gave in prose and the spec now gives normatively. Submission section:
On the machine path, verification is the single gate that makes a submission count. On the judged path, acceptance is the single affirmative gate, but not the only exit: the deadline default pays a Pending submission that was never ruled on, and it checks nothing about eligibility. Eligibility on the judged path is therefore enforced by rejecting ineligible submissions inside judgmentWindow, and it holds exactly as far as the judge’s availability does. Eligibility that must hold without a live judge belongs on the machine path, where the default claim is unavailable and releaseExpired recycles refused slots.
And you are right that the remedy sentence in Security Considerations named two things that cannot exist on this kernel. A wrapper in front of submitFulfillment becomes the fulfiller of record and is bypassed by direct calls, and a “bond escrowed alongside the delivery” has nowhere to attach. That sentence now reads:
submitFulfillment is permissionless and records msg.sender, so nothing placed in front of it can gate it: a wrapper would become the fulfiller of record, and direct calls bypass it. Deployments that expect hostile submitters SHOULD therefore enforce admission where the kernel lets them — at the acceptance decision, by rejecting unbonded, unlisted, or unawarded submissions inside the window, or on the machine path, by a verifier that refuses their proofs so that releaseExpired recycles the slot — […] A submission-time admission slot would be a kernel change and is recorded as a revision candidate, not smuggled in here.
On your first suggestion, the discoverable gate consulted inside submitFulfillment and snapshotted like machineSettled: it is the right shape, and it is now v2 candidate 3 in the repository’s ROADMAP.md with your credit. I’m not adding it to this draft because it is a new slot and a new MUST, it changes the ITaskTender identifier, and it re-opens the one boundary the kernel is built on — it enforces what is observable, and a policy contract is observable, which is exactly the argument for it and the reason it deserves a deliberate decision rather than a late amendment. Your second suggestion — say plainly that judged-path eligibility is only as sound as the judge’s availability — is what the draft now says.
2. judgmentWindow and the funders’ lockup. Also right, and the self-submission observation is the sharp version: since submission is permissionless, the demander can start the clock on its own vault. Two facts bound it that the text did not state. First, reclaim opens only on cancellation or after settleBy, and both also close submission, so no new reservation can be made once reclaim is open: the lockup after closure is at most one window from the last submission. Second, the default claim is permissionless, so a funder waiting on a stale reservation can clear it (paying its fulfiller of record) rather than wait for anyone. What remains is that the window itself has no cap, and the draft presented “bounded” as if that were a protection. It now says:
The wait is bounded by judgmentWindow: […] once reclaim is open no new submission can be made, so the last reservation is clearable within one window of the last submission, subject to epoch pacing. Because submission is permissionless, anyone — the demander included — can start that clock, so judgmentWindow is also the funders’ maximum lockup after the tender closes; it is a term, readable before fundTask, and type(uint64).max is a legal value. Publishers SHOULD size it to the judge’s horizon and no longer, and wallets and funding routers SHOULD display it beside the amount as the lockup a contribution accepts.
I have not added a normative cap, because there is no number that is right for both a three-hour panel and a week-long appeal body, and a cap in the kernel would be the first term the kernel chose for the publisher. Partial reclaim of the unreserved part while submissions are pending is v2 candidate 4, with the reason it is not a patch: the refund rule fixes the pool and the denominator at the first reclaim and zeroes a funder’s outstanding contribution, so a reservation freed by rejection after a partial reclaim would have no funder left to return to and would fall to the owner’s residual. A running-ratio design fixes that, but it is a different refund arithmetic.
3. Pacing and the refusal window. This one the kernel already answers, and the text now states it: rejection is never paced. The epoch bound sits on payment — acceptance, settlement, and the default claim — not on refusal, so a judge inside its window can always refuse, saturated epoch or not. The “unrejectable” in the Security paragraph was always the window closing, not pacing, and the paragraph now says so in place. A judge whose acceptance was blocked by a saturated epoch loses nothing either: late acceptance has no bar, so it accepts in the next epoch, or the fulfiller claims — the same money.
The commit is on the PR branch; the reference implementation and tests are unchanged, and the five edits are all in prose.