Task, workspace, file changes and event lineage
This document defines task changes, workspace changes, and single-parent event lineage in the current draft.
schema/draft/task-workspace-event.schema.json contains five standalone event
definitions. Each includes the canonical event envelope, without a reference back
to the catalogue. Before events can be intercepted or observed; after events and
file.changed are observations only. Neither boundary requires the other.
Payloads
task.change.beforeandtask.change.after:taskcontains durableid,operation(create,update,remove), andchange, with optionalprioranddescription.changeis the proposed change before, and the actually applied change after.priorcontains only known previous fields, not an invented inventory. State keys and status values are harness-defined. Removal is represented by the operation; an empty change object is valid for removal. Updates carry changed fields, not a promise of a universal merge operation.workspace.change.beforeandworkspace.change.after:workspacecontainskind(cwd,roots,switch) andchange; optionalpriorandreason.change/prioreach contain availablecwdand/orworkspaceRoots. A roots list may be empty (all roots removed). These fields describe execution context, not file edits, plugin discovery, or an automatic worktree lifecycle.file.changed: nonemptychangescontainspath,operation(create,update,remove) andagentCaused. Optionalbefore/afterare canonical selection-aware content parts: text file snapshots are inline text and only non-text, non-JSON media snapshots use immutable attachment references. JSON file contents are serialized as ordinary text, never a JSON part or upload. Missing snapshots do not imply empty files. Content permissions and gaps still apply. No file read event or file interception capability is added.
Extension strategy: event envelopes remain open for canonical envelope evolution
and extension metadata. The task, workspace, workspace-state and file-change
objects have closed protocol keys to detect misspelled payload fields. Task
change and prior remain open objects, including native status values; native
payload and tool arguments retain their existing open definitions. This is not
blanket closure of domain data. Use the envelope extensions/native mechanisms
for additional protocol metadata, not invented task-state restrictions.
Lineage and actual changes
Event identity is (source, id); parentEventId is an event ID in that same
source, never a call ID. Parentage is single and acyclic. A producer must know
its parent; omit the field otherwise. A subscriber may not have received that
parent and must not demand that it fetch ancestry to interpret the event. Parent
operations can already have completed. Links do not grant permission or imply
success. Timestamps do not establish ancestry.
When both task boundaries exist, after parents to its corresponding before. That before can parent to the originating tool event. With after-only support, the after can parent directly to a known originating tool event. Non-tool origins need no fabricated call or before event. Workspace after similarly links to its known proposal. Tool invocations retain their independent standard events.
Resolve interception before constructing dependent proposals: a rewritten tool argument must produce a proposal for the effective task, not the original task. A denied proposal with no actual change produces no after event. Actual partial changes remain observable; after values need not equal proposed values. Task permission does not undo completed side effects or authorize later proposals. All resolved enclosing invocations still owe honest tool completions where completion coverage is advertised.
interop/task_lineage.py validates full canonical JSON-RPC messages plus these
schemas before recording edges. Its optional require_known_parents=True is for
producer histories, not filtered subscribers. It checks cycles (including late
parents), source scope, matching task identity/operation for known proposal
parents, and explicit no-op actual task/workspace updates. It does not invent
missing history or mistake different subscription views for delivery retries.
It cannot prove a disk/task mutation occurred from a notification alone, infer a
hidden proposal, establish native boundary support, or enforce producer emission
suppression merely by observing denial. Those are producer integration duties.
Deliberate limitations / unresolved shape
The design permits one indivisible task change set but does not choose its wire member identity/operation shape. This module implements single-task changes only; no speculative batch API or per-member veto semantics are introduced. The coordinator must settle that representation before claiming change-set coverage. No universal task state machine, agent entity, inventory API, legacy profile, rollback semantics, or blanket workspace path replacement is introduced.