Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
18 KiB
id, title, status, system, owners, created, last_updated
| id | title | status | system | owners | created | last_updated | |
|---|---|---|---|---|---|---|---|
| plugins-task-dependency-response-bounds | Response bounds for the task dependency projection | draft | plugins |
|
2026-09-17 | 2026-09-17 |
Response bounds for the task dependency projection Requirements
Overview
The task dependency projection adds seven fields to the plugin and canvas task read model, two of them lists that name other tasks. That turns a task read into a read whose cost and whose payload both grow with data the caller did not ask for: the edges of every task on the page, and the far task rows behind those edges.
This document owns the third question the capability raises — what one response may cost, and what happens when it cannot be delivered. It is separated from the projection requirements because the two are reviewed against different risks. The projection decides what the graph says; this decides what happens when the graph is too expensive to compute or too large to send, and it is the part that must stay honest when a workspace is larger than the one the capability was demonstrated on.
REQ-PLUGINS-TASK-DEPS-004 keeps the identity it was reviewed under; nothing is
renumbered. The projection's criteria defer here wherever a query count, a list
length, or a response size is at stake, and the sibling
dependency edge ends defers here for the query
bound its query-free admission test is written against.
Terminology
- Plugin task read: As defined in task dependency projection — any surface returning the plugin task read model, on either the gRPC or the canvas surface.
- Edge-end resolution: The derivation step that loads the far task row behind
each edge, in order to give an entry its
titleandstate. Its input is the set of distinct edge ends across the whole response, deduplicated, not one task's list, and narrowed before the step runs byAC-PLUGINS-TASK-DEPS-004.13. - Distinct edge ends: The deduplicated set that edge-end resolution reads.
Serialized entries are a different quantity: the entries a response writes
across all its tasks, which repeat wherever two tasks share an end. The two are
bounded separately and by different criteria; see
AC-PLUGINS-TASK-DEPS-004.14. - Host-parameter ceiling: The database's maximum number of bind parameters in
one statement, below which a batched
IN (...)query must stay. - Paginated flow: A plugin task read accepting a page
limit. The list reads are paginated; the single-task reads, the task-write RPCs, and the plugin-owned task-tree preview RPC are not.
Requirements
REQ-PLUGINS-TASK-DEPS-004: Bounded cost and bounded payload
Intent: Reading a page of tasks must not cost one dependency query per task; no task may become unreadable because too many tasks depend on it; and a response that cannot be delivered must fail in a way that says so, rather than arriving complete-looking and wrong.
Acceptance criteria
- AC-PLUGINS-TASK-DEPS-004.1: No dependency read shall be issued per task in
the response. The derivation's own structural steps shall be a fixed number of
reads that does not change with the number of tasks in the response: a response
of many tasks carrying no edges shall cost the same reads as a response of one.
The only read count permitted to vary is the batch count edge-end resolution
requires under
AC-PLUGINS-TASK-DEPS-004.11, which varies with the number of distinct edge ends and not with the number of tasks, and which on a paginated flow is therefore bounded by the maximum inAC-PLUGINS-TASK-DEPS-004.10divided by the host-parameter ceiling. Two responses resolving the same number of distinct edge ends shall cost the same reads whether they carry one task or many, with the single exception that a response carrying more tasks than the host-parameter ceiling allows in one statement chunks its task-keyed edge queries underAC-PLUGINS-TASK-DEPS-004.11— reachable only on the unpaginated preview flow, since every paginated flow is capped at 200 tasks. No criterion shall be read as requiring a single fixed read count across responses of differing edge volume or differing task count past that ceiling, which chunking makes impossible to hold. - AC-PLUGINS-TASK-DEPS-004.2: The dependency derivation for a task list shall cover only the tasks in the returned page, not every task the filter matched before pagination.
- AC-PLUGINS-TASK-DEPS-004.3:
depends_onandblocksshall each carry at most 512 entries for one task. - AC-PLUGINS-TASK-DEPS-004.4: When a list is cut to that limit, the read
shall report that list's truncation flag true and keep the first entries in the
order defined by
AC-PLUGINS-TASK-DEPS-001.8. Both flags shall be present on every task and false when no cut occurred. - AC-PLUGINS-TASK-DEPS-004.5: A truncated list shall not change
blockedorblocked_reason, which shall still account for every predecessor including the omitted ones. - AC-PLUGINS-TASK-DEPS-004.6: When a canvas response carrying the projection
exceeds the runtime response limit, the runtime shall return its existing
bounded
response_too_largeerror. That limit shall be stated as applying to every canvas response rather than to the list routes alone, because the runtime applies it to each response it writes. A smaller pagelimitis the documented remedy on the paginated flows only;AC-PLUGINS-TASK-DEPS-004.9governs the flows that accept nolimit. - AC-PLUGINS-TASK-DEPS-004.7: The gRPC surface's bound shall be stated, not
assumed absent. A Host response over the receiving end's maximum receive size
shall fail the whole RPC with
ResourceExhausted, never a truncated or partially populated list, so a consumer never reads a short page as the end of the data. The remedy is the same smallerlimiton the paginated flows only, and the documentation shall say the size belongs to the plugin's own client connection, not to this capability. - AC-PLUGINS-TASK-DEPS-004.8: Neither bound shall be met by dropping,
truncating, or redacting the projection to fit.
AC-PLUGINS-TASK-DEPS-004.3is this capability's only size reduction; an undeliverable response fails rather than arriving silently incomplete. - AC-PLUGINS-TASK-DEPS-004.9: The remedy in
AC-PLUGINS-TASK-DEPS-004.6andAC-PLUGINS-TASK-DEPS-004.7shall not be claimed for a flow that accepts no pagelimit, and those flows shall be named rather than left to inference: the gRPC single-task read, the three gRPC task-write RPCs, the canvas single-task and update routes, and the plugin-owned task-tree preview RPC. On every one of them that returns exactly one task, the projection's contribution to the response shall be at most the two lists bounded byAC-PLUGINS-TASK-DEPS-004.3. That is a bound on serialized entries, and it shall be grounded onAC-PLUGINS-TASK-DEPS-004.3alone rather than on the distinct-end maximum ofAC-PLUGINS-TASK-DEPS-004.10, from which those flows are exempt under that criterion. The contribution shall be stated as a byte figure in the documentation, and what it places on the gRPC surface shall be stated as a floor on the receiving end rather than as a guarantee this capability can enforce: a plugin whose client receive maximum is at or above the gRPC library default shall not be able to tripAC-PLUGINS-TASK-DEPS-004.7through the projection on such a flow, and a plugin that lowers that maximum below the stated figure may — a consequence of its own connection setting, whichAC-PLUGINS-TASK-DEPS-004.7already assigns to it. No criterion shall claim the bound holds against a receive maximum the host does not set. The preview RPC shall be the one exception, because it returns an unpaginated task set: its response size and its edge-end total shall be bounded only by the size of the plugin-owned tree, and the documentation shall state that reducing that tree is the only lever on it. - AC-PLUGINS-TASK-DEPS-004.10: The number of distinct edge ends one
derivation resolves shall be bounded by a declared maximum on every paginated
flow, and that maximum shall exceed twice the per-task limit in
AC-PLUGINS-TASK-DEPS-004.3. A response whose task set would exceed it shall fail underAC-PLUGINS-TASK-DEPS-004.6orAC-PLUGINS-TASK-DEPS-004.7. Exceeding it shall not count as a derivation that cannot be completed underAC-PLUGINS-TASK-DEPS-002.5, and shall therefore never produce the withheld verdict ofAC-PLUGINS-TASK-DEPS-001.14: such a response is undeliverable, not unknown, and a consumer reducing its page shall receive a real graph rather than a page uniformly reportingblocked_reason: unknown. The refusal shall reuse each surface's existing oversized-response error rather than introduce a code:response_too_largeon the canvas surface andResourceExhaustedon the gRPC surface, returned before any task object is serialized, so a caller cannot receive a partial page alongside it. The flows named inAC-PLUGINS-TASK-DEPS-004.9that return exactly one task shall be exempt from this maximum. They accept no pagelimit, so the remedy the refusal assumes does not exist on them, and refusing there would make a task unreadable exactly because too many tasks name it — the outcome this requirement's Intent forbids. On an exempt flow the derivation shall resolve whateverAC-PLUGINS-TASK-DEPS-004.13leaves it, in batches underAC-PLUGINS-TASK-DEPS-004.11, and shall not refuse on end count; the response shall remain subject to the size bounds ofAC-PLUGINS-TASK-DEPS-004.6andAC-PLUGINS-TASK-DEPS-004.7, which act on serialized entries underAC-PLUGINS-TASK-DEPS-004.14and whichAC-PLUGINS-TASK-DEPS-004.3already bounds for one task. The cost this concedes is named in## Out of scope. The preview RPC, whichAC-PLUGINS-TASK-DEPS-004.9names in the same list of unpaginated flows, shall not be exempt: it returns many tasks, its documented remedy of reducing the plugin-owned tree is a real lever, and refusing it leaves every task in that tree individually readable through an exempt flow, so no task becomes unreadable. The maximum's floor of twiceAC-PLUGINS-TASK-DEPS-004.3is what keeps the paginated and exempt regimes agreeing in the ordinary case: afterAC-PLUGINS-TASK-DEPS-004.13's cut, a task holding no more predecessors than that limit contributes at most twice it, so a paginated page of one such task stays under the maximum and reads the same as the exempt single-task flow. The two can still disagree for a task that passed the predecessor limit on the incremental write path, which a paginated read at the minimumlimitof 1 would refuse with no smallerlimitleft to retry. That residue shall not be left to inference: the documented remedy there is the single-task read, which is exempt and returns the same projection. - AC-PLUGINS-TASK-DEPS-004.11: No statement in the derivation shall carry a
bind-parameter count that grows without bound with its input. This binds two
families of statement, not one, and naming only the first is the gap this
criterion closes. Edge-end resolution shall split its distinct ends into batches
at or below the host-parameter ceiling. The task-keyed edge queries — the
batched predecessor and dependent reads, whose parameters are the response's
task ids — shall be split the same way; that bound is unreachable on a paginated
flow, where
AC-PLUGINS-TASK-DEPS-004.15's maximum of 200 tasks keeps them to one statement, and is reachable only on the unpaginated preview flow. Both shall reuse the chunking convention the repository already applies to its other batched id queries rather than introducing a second one. No derivation shall fail because a generated statement exceeded that ceiling, so none ofAC-PLUGINS-TASK-DEPS-002.5's failure paths shall be reachable by that route. - AC-PLUGINS-TASK-DEPS-004.12: Splitting edge-end resolution into batches
shall change neither the failure granularity nor the ordering of the result.
The batches shall remain one logical step, so a failure in any one of them shall
be the single edge-end-resolution failure of
AC-PLUGINS-TASK-DEPS-002.5over the whole derivation and shall not become a narrower per-batch boundary underAC-PLUGINS-TASK-DEPS-002.3. Entry order shall continue to come from the edge queries ofAC-PLUGINS-TASK-DEPS-001.8and shall not depend on which batch resolved an end. Because the batches are not one statement, the derivation's observation point shall be an interval rather than an instant; this is already permitted byAC-PLUGINS-TASK-DEPS-005.4andAC-PLUGINS-TASK-DEPS-005.6, and shall not be read as a stronger guarantee than either. - AC-PLUGINS-TASK-DEPS-004.13: Edge-end resolution shall read only the ends
whose detail the response can carry, except where another criterion requires a
wider read. The dependent direction shall be ordered and cut to the limit in
AC-PLUGINS-TASK-DEPS-004.3before resolution, because no criterion reads a dependent's far row to compute a verdict —AC-PLUGINS-TASK-DEPS-001.7gives dependent entries no resolution status — and becauseAC-PLUGINS-TASK-DEPS-001.8's order and its tiebreak are both decidable from the edge rows alone, so the cut does not need the detail it is deciding about. The predecessor direction shall be resolved in full, becauseAC-PLUGINS-TASK-DEPS-004.5requires the verdict to account for every predecessor and a predecessor'sstatusis read from its far row. One task's contribution to a derivation's distinct-end count shall therefore be its full predecessor count plus at most the limit inAC-PLUGINS-TASK-DEPS-004.3, not its full edge count. Because that cut precedes resolution, an end kept by it and then dropped as dangling underAC-PLUGINS-TASK-DEPS-006.4shall leave the list shorter than the limit with its truncation flag still true, and the entries the cut discarded shall not be back-filled in its place. A shorter-than-limit truncated list is therefore correct rather than a defect, and no criterion shall be read as requiring a truncated list to hold exactly the limit. - AC-PLUGINS-TASK-DEPS-004.14: The quantity the response-size bounds of
AC-PLUGINS-TASK-DEPS-004.6andAC-PLUGINS-TASK-DEPS-004.7act on shall be the number of entries a response serializes, which is not the distinct-end count bounded byAC-PLUGINS-TASK-DEPS-004.10and shall not be documented as though it were. Entries repeat across tasks that share an end, so one response may serialize as many as the page limit times twice the per-task limit ofAC-PLUGINS-TASK-DEPS-004.3while resolving far fewer distinct ends. A response may therefore exceed a surface's size limit while staying under the distinct-end maximum, and shall then fail underAC-PLUGINS-TASK-DEPS-004.6orAC-PLUGINS-TASK-DEPS-004.7with the smaller-limitremedy. No criterion and no design prose shall state or imply thatAC-PLUGINS-TASK-DEPS-004.10bounds response size, or that the two bounds engage at comparable payloads. - AC-PLUGINS-TASK-DEPS-004.15: The page
limitthat the remedy names shall have its accepted range, its default, and its out-of-range behaviour documented for each surface, because the two surfaces already differ and this capability changes neither. The canvas surface shall continue to reject alimitsupplied outside 1 to 200, or non-numeric, with its existing invalid-request error, while the host's own page normalization shall continue to clamp a supplied out-of-range value into that range. The divergence shall be documented as applying to a supplied value only: an absentlimitis not an error on either surface and shall continue to reach the host's default of 50. A consumer retrying with a smallerlimitshall be able to predict which of the two behaviours it will get, and no criterion shall describe the remedy as though they were the same.
Out of scope
- Paginating the preview RPC.
AC-PLUGINS-TASK-DEPS-004.9names it as the one flow with no applicable remedy rather than adding a page parameter to it. That RPC's request and response shape is owned by the plugin-owned task-tree contract, and changing it is a wire change this capability does not need: the projection inherits the tree's existing unboundedness rather than introducing it, and the same is already true of the linked pull requests attached there. - A cost bound on a single-task read's own edges. The exemption in
AC-PLUGINS-TASK-DEPS-004.10concedes that a non-paginated single-task read of a task with an extreme predecessor count resolves that many ends, inAC-PLUGINS-TASK-DEPS-004.11batches, rather than refusing. This is deliberate, and it is the narrower of the two costs on offer: the alternative is precisely the unreadable task this requirement's Intent forbids, and the caller named that one task by id rather than asking for a page. The dependent direction — the one an arbitrary third party can grow without the task's consent, and the one the Intent is worded against — is cut before resolution byAC-PLUGINS-TASK-DEPS-004.13, so the residue is confined to a task's own declared predecessors, bounded at 512 on the full-set replacement write path and reachable past it only on the incremental add path. - A time or memory budget per derivation. The bounds here are counts — reads, entries, distinct ends, and bytes — because those are the values the contract can state and a test can assert. A latency budget would be an operational target, not a contract a consumer can rely on.
- Chunking as a substitute for the maximum.
AC-PLUGINS-TASK-DEPS-004.11keeps a statement legal;AC-PLUGINS-TASK-DEPS-004.10keeps the response affordable. Chunking alone would turn an oversized page into an unbounded number of small queries, which satisfies no bound and is the faultAC-PLUGINS-TASK-DEPS-004.1exists to prevent. - A server-side cap that silently shrinks a page. Excluded by
AC-PLUGINS-TASK-DEPS-004.8. Returning fewer tasks than asked for, without an error, is the failure modeAC-PLUGINS-TASK-DEPS-004.7rejects for the gRPC surface, and it is no better on the canvas surface.