Out of 270 decomposed units from the first real run against channel history, 190 got filed into 17 tasks and 6 epics. The rest stayed loose, which is correct. Some units are just a note or an opinion that never needed a home, and forcing every fragment into a bucket would have meant grooming an unusable pile instead of a real backlog. Separating what wants structure from what does not is the decomposer’s job.

The channel already had the plan in it

We run a shared venture out of a Discord channel. Decisions get made there, ideas get dropped there, someone asks a question and three tangents follow before anyone answers it. None of that gets written down anywhere else. For months our answer to “where are we on X” was to scroll. The backlog was sitting in a chat log, unshaped.

We set out to fix that with a task engine. The first design decision mattered more than any of the code: the conversation itself had to become data, not just a place where data gets discussed.

Three layers, in strict order

Ingest. A channel gets pulled in as source records. Every message is timestamped, attributed, stored once, and never edited after that. This layer interprets nothing. It just makes the conversation quotable.

Decomposition. An agent reads the source and proposes atomic units: a decision, a request, an opinion, a question, an idea, a piece of work, a note. Each unit carries evidence, a literal pointer back to the message it came from. A unit that cannot be traced to a real quote is not trustworthy enough to act on. On the first real decomposition run this caught its own agent. Eleven units were proposed, ten accepted, and one rejected because the quote attached to it was not verbatim. That is the whole point of the evidence field. It is a check the system runs on itself before a unit is allowed to exist.

Grouping. Units get clustered into tasks, and tasks into epics, and the result lands on a review surface. Nothing here writes itself into the record of truth. It proposes.

Why the tangents stay in

A real working conversation does not stay on topic and should not be forced to. Our working note during the spec process put it plainly: whatever we’re working on is going to reference other things, and we should be able to say whatever we want on any task without stopping to file it correctly in the moment. The friction people actually feel with task systems is being made to sort a thought at the instant they have it. So the system defers sorting. You talk. Later, something else reads the transcript and does the filing.

That is why decomposition is a separate step from grouping, and why both sit apart from the immutable source underneath them. Each layer earns something different. The source layer earns trust, the unit layer earns traceability, the grouping layer earns organization. Collapse two of them and you lose whichever property the collapsed layer was providing.

The version that parked the mess

An early version made task-close the forcing function. Close a task, and whatever units under it never got resolved would get swept somewhere. We built toward that. A review at high effort killed it outright, because all the sweep did was relocate the mess into a “parked” pile without resolving anything.

The fix was to make parking require an explicit trigger and a reason. We also track how often parked units actually get revived as a real kill metric. If the revival rate stays near zero, this whole approach is an elaborate archive and should be shut down instead of expanded. We would rather build that kill criterion in on day one than discover two months in that we built a graveyard.

Immutable source, reversible grouping

The property we care most about is the split between what never changes and what always can. The source records are permanent. Nobody edits history, whether a person or an agent, because the value of the layer is that a year from now a unit’s evidence still points at a real message that actually happened.

The grouping on top is meant to be pulled apart constantly. A unit can be pulled out of one task and dropped into another. A task can be re-scoped without touching the units under it. The design spec is explicit that promotion is a validated transition and that containment is an edge in a graph, with no fixed parent column, because a single unit can belong to a conversation and a task and a related task all at once. Grouping is a hypothesis about how the work is organized. The source is the fact of what was said. Only the second should ever be permanent.

A person still says yes

None of this writes to the record of truth on its own. Every promotion from unit to task, and every unit placed under a task, lands on a review surface first, and a person confirms it. An agent is never allowed to self-promote its own proposal, full stop.

When we built the bulk-approve action for reviewing a full task’s worth of low-risk units at once, we put a hard rule in it too. Units typed as a decision, a proposal, or a commitment never get swept up in a bulk action, no matter what the form claims. Those always get looked at one at a time. The bulk action will not run until a reviewer has seen one real sampled quote from the batch, enforced with a required checkbox next to it so the button cannot be pressed unseen.

The check that points outward

There is also a boundary to respect on the way out. Each of us keeps a separate private tracker alongside the shared one, and early on a digest generator let private task IDs leak straight through into the shared channel, because nothing was checking for that shape of string.

The fix was a fail-closed rule. If a generated summary still contains one of those IDs after the model writes it, the card gets refused and nothing is posted. It is the same logic as the evidence check, pointed outward instead of inward. If the plan for your work lives in a chat channel, keep the raw conversation as an unedited record, keep the grouping reversible, and make every item prove its quote and every outbound card prove it is clean before either one counts.