Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
9.0 KiB
id, title, status, wave, depends_on, plan, requirements, acceptance_criteria, system_design
| id | title | status | wave | depends_on | plan | requirements | acceptance_criteria | system_design | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-dependency-projection | Expose task dependencies in the plugin and canvas data API | done | 1 | plan.md |
|
|
|
Task 01: Expose task dependencies in the plugin and canvas data API
Summary
A workspace canvas or gRPC plugin reading the Host Data API's Task read model
could not see a single dependency edge, even though the kanban board already
derives and ships a complete view (blocked, blocked_reason, depends_on,
blocks, start_when_unblocked) via BuildDependencyViews. This task adds
the same seven-field, read-only projection to every plugin task read model
across the gRPC surface (proto, SDK, grpcHostServer), the canvas JSON
surface (webapp_protocol_json.go), and the shared derivation the two reuse
(internal/task/service/service_dependencies.go), landing it as a field on
the existing api_read:tasks capability with no new grant.
Scope
- Field set (all seven, always present within one host version — absence
is never a third state):
blocked,blocked_reason(pending|failed|unknown),depends_on[],blocks[],depends_on_truncated,blocks_truncated,start_when_unblocked. - Attachment rule: the projection is derived and attached on every plugin
task read model object a response actually serializes, and on no discarded
or intermediate read — enforced via non-attaching (
fetchTask,fetchTaskForScopeCheck,writeTaskUpdate) vs. attaching (taskReader.Get/Update/Move,pluginOwnedTaskTreeManager.Preview) helper pairs across nine call sites (6 gRPC RPCs, 3 canvas routes). - Withheld verdict:
blocked=true, blocked_reason="unknown", empty lists, both truncation flags false,start_when_unblocked=false— the single fail-closed sentinel for all four derivation-failure paths and for a caller lackingapi_read:tasks(indistinguishable from each other by design). - Fan-out bound:
maxDependencyFanOut = 4096distinct edge ends on multi-task flows (ListTasks, canvas task list, preview RPC), returned as gRPCResourceExhausted; single-task reads and write RPCs are exempt and use the unbounded derivation. - Edge-end redaction: canvas surfaces redact
title/stateon an edge end their scope does not admit as directly readable (5-way switch over scope kind), decided without an additional read; gRPC redacts nothing. - Shared-derivation fixes (Kanban-visible, no migration): ordering
(
ListBlockersForTasksgainsORDER BY created_at, blocker_task_id), dangling-edge drop extended to theBlocksdirection, fail-closed on all four derivation-failure paths, edge-end workspace id carried on the ref, and chunking both batched id-query families (sqliteMaxHostParams = 500) past SQLite's host-parameter ceiling on the unpaginated preview flow. - Events: deliberately unchanged — dependency fields are not added to the
canvas SSE allowlist (
webapp_events.go'scommonFields); the contract is refetch-on-signal off the existingtask.updated/task.dependencies_resolved/task.dependency_failedevents, including atask.state_changedrefetch rule for any task named in a cached task's edge lists. - No new capability: the projection is a field set on
api_read:tasks; version skew is gated by the manifest's existingmin_kandev_versionfloor.
Acceptance
GET ./_kandev/v1/data/taskson a workspace canvas and gRPCListTasks/GetTaskreturn the same seven dependency fields, redaction aside, for the same tasks.- Listing N tasks issues a constant number of dependency queries, not one per
task (batched
BuildDependencyViews/BuildDependencyViewsBounded). - A derivation failure or a caller without
api_read:tasksyields the withheld verdict, never a fail-open empty/false projection. - Mapper round-trip (proto ↔ SDK), canvas JSON projection, the redaction-scope table, the withheld-verdict path, and the fan-out/chunking bound are each covered by tests in the relevant package.
Verification
go test ./internal/plugins/... ./internal/task/service/... ./internal/task/repository/sqlite/... ./internal/office/repository/sqlite/... ./pkg/pluginsdk/...— all green.golangci-lint run ./...(backend) — 0 issues.gofmt -lon touched files — clean.- Three independent review legs (code-reviewer, security-reviewer,
test-supervisor) plus a mandatory cross-vendor
codexpass ran against the final diff; every finding was independently re-verified against source. codex's one finding and the security-reviewer's SEC-001 finding were both confirmed to predate this branch and fall outside its diff (see the task plan's Review round 3 entry for the full trace); no production defect survived review. The residual is test-rigor coverage gaps, tracked in the task plan rather than blocking this PR.
Files touched
apps/backend/proto/kandev/plugin/v1/plugin.proto,apps/backend/pkg/pluginsdk/data_types.go(+ tests) —Taskfield additions,TaskDependencyRef, proto/SDK conversions.apps/backend/internal/plugins/host.go,host_data.go,host_data_dependencies.go(+ tests),host_write.go(+ tests),service.go,webapp_protocol.go,webapp_protocol_data.go,webapp_protocol_json.go(+ tests),webapp_events.go(test only) — gRPC and canvas attachment, redaction, withheld-verdict, and logging.apps/backend/internal/task/service/service_dependencies.go(+ tests) — shared derivation: ordering, dangling-edge drop, fail-closed paths, workspace id on refs, fan-out bound.apps/backend/internal/office/repository/sqlite/blockers.go(+ tests),apps/backend/internal/task/repository/sqlite/task.go(+ tests) — ordering and chunking on the batched id queries.docs/public/canvases.md,docs/public/plugins-authoring.md,apps/backend/internal/mcp/canvasskill/files/references/browser-api.md— consumer-facing contract docs.
Output contract
Summary, files changed, and status update here + in plan.md.
Output
Status: done. Landed across 9 commits on
feature/expose-task-dependen-61269a
(5d359841a..9a5b0b761), covering the frozen spec's -001 through -006
requirements in full. Spec Review ran 8 rounds to freeze the contract; Build
ran 7 rounds (2 operator-authorized beyond the standing 4-round cap) fixing
findings from Testing and Review; Testing round 7 and Review round 3 both
found no production defect. See the task plan for the full round-by-round
record, including the two review findings adjudicated as pre-existing and
out of this branch's scope (a codex finding about the internal event bus,
and a security-reviewer finding about gRPC plugin event-delivery capability
gating — both confirmed unmodified by this branch and filed as follow-up
candidates rather than blockers).