Work core
In short
Section titled “In short”This describes how work is tracked in Qren: one list of things to do, shared by the people in a workspace and by the agents helping them. You will not see an ID again — items are known by their titles, and the short code behind each one exists only so that a link, a command or a chat message can point at exactly one thing. Every item has exactly one person accountable for it and at most one agent acting on that person’s behalf; taking the work back from an agent is one click, and nothing the agent did is lost when you do. Nothing is allowed to rot: an item nobody accepts expires, an item nobody has touched in months closes itself, and every one of those automatic moves is written down as a visible event you can undo. What it deliberately does not do: it keeps no goals, areas or key results, no separate inbox and no threads, and it never creates work on its own — every item traces back to a person, or to a proposal a person approved.
Purpose and non-goals
Section titled “Purpose and non-goals”The work core is the core system that answers “what is being done, by
whom, and where does it stand” for one workspace. It is the spine the other core systems
hang off: companion proposes items from meetings, chat carries the
conversation, knowledge is what an item produced, drive holds its files,
agents are delegates on it. It replaces the AOS work engine outright (decision record §4,
“replace, not evolve”). Evidence is cited throughout to work-backend-landscape-2026-08-20
(landscape), work-engine-audit-2026-08-20 (audit), qren-agent-teamwork-2026-08-20
(teamwork) and runrec-study-2026-08-20. Each non-goal below is something the current
engine has and this one will not; the numbers are the reason.
| Non-goal | Evidence |
|---|---|
No automatic task creation. No session hook, comms trigger, runner or importer writes an item directly; every creator emits a command landing in triage with its source, or goes through the approval queue |
470 tasks created on one day, 444 ownerless; 88% of tasks have no activity past creation (audit §1, TL;DR 2) |
| No goals, areas or key results | 4 goals, 0 areas, 0 key results against full schema support (audit §4); no leader models them on the task (landscape §1) |
No separate inbox table — triage is a status category |
37 inbox items, 0 ever triaged (audit §4) |
No threads table — conversation is chat, exploration is knowledge |
4,061 rows, nearly 2× the task count; 4,059 auto-generated per worktree checkout, 2 ever promoted (audit §4) |
| No narrative or briefing generation inside the core — the morning brief is an agent routine reading these tables | the briefing layer is 3,949 lines, 22.8% of the engine and nearly double the task model (audit §7) |
No dotted sub-IDs, no counters shown by default, no t# |
in 15 live rows the prefix and the real parent disagree; two ID generators, no source-of-truth column (audit §4, §9.1) |
No multi-assignee, free-text status, EAV custom fields, stored progress percentage, or blocked/waiting statuses |
derived or refused by every leader (landscape §1) |
| No project-directory bureaucracy — manifests, worktree reconciliation, project detection | 2,892 lines that are not a work concern (audit §7) |
The model
Section titled “The model”One workspace.db per workspace (design/target-tree §1.2). Principals, the approval queue
and the audit log live in instance.db, so assignee_id, delegate_id, actor and
approval_id are ids into that database and not foreign keys — the workspace folder has
to stay liftable on its own.
| Entity | What it is | Required |
|---|---|---|
item |
One piece of work. The only work object. | id, short_code, title, status_id, category, assignee_id, source, created_by, timestamps |
status |
A workspace-named status inside a fixed category | id, name, category, rank |
project |
Optional container, one level. No cycles, no milestones in v1. | id, name |
label / item_label |
Flat, many-to-many; carries RunRec’s domain facet | id, name |
relation |
blocks · duplicate_of · related. Three kinds, no more. |
src, dst, kind |
comment |
A note on an item; may point at a chat thread | id, item_id, author_id, body_md, created_at |
events |
Append-only. Activity feed, audit trail and sync unit at once. | seq, ts, actor, kind, item, payload |
commands |
Idempotent write intents, client-generated id | id, actor, kind, payload |
view |
A saved filter/sort/group/layout, as data | id, owner_id, spec_json |
item_rank |
Saved manual order per view — the board rank | view_id, item_id, rank |
agent_run / agent_activity |
One delegation session and its turns | see §9 |
approval_link |
Join from an item or run to an approval in instance.db |
approval_id, item_id |
legacy_key |
Old identifiers that must keep resolving | key, item_id |
Derived, never stored: blocked (an open item is the src of a blocks relation
pointing here), waiting (snooze_until set, or the run is
awaiting_input/awaiting_approval), stale (last_activity_at past the workspace
threshold) — landscape §1, “no leader has blocked as a status.” Provenance is required:
source on every item, plus source_meeting_refs and confidence when a companion or
agent proposed it, all three named hard requirements by runrec-study §5.
DDL sketch
Section titled “DDL sketch”-- workspace.db — the work core. Ids naming principals or approvals are-- instance.db ids, unconstrained here so the workspace folder stays liftable.PRAGMA foreign_keys = ON;CREATE TABLE status ( -- names configurable per workspace, categories are not id TEXT PRIMARY KEY, name TEXT NOT NULL, rank TEXT NOT NULL, category TEXT NOT NULL CHECK (category IN ('triage','backlog','unstarted','started','review','done','cancelled')), is_default INTEGER NOT NULL DEFAULT 0, reserved TEXT); -- 'duplicate' | NULLCREATE TABLE project ( id TEXT PRIMARY KEY, name TEXT NOT NULL, rank TEXT NOT NULL, archived_at TEXT, key TEXT UNIQUE, show_keys INTEGER NOT NULL DEFAULT 0, -- opt-in, off by default next_number INTEGER NOT NULL DEFAULT 1, target_on TEXT);CREATE TABLE item ( id TEXT PRIMARY KEY, -- UUIDv7, client-generated short_code TEXT NOT NULL UNIQUE, -- 7-char Crockford base32 title TEXT NOT NULL, body_md TEXT NOT NULL DEFAULT '', status_id TEXT NOT NULL REFERENCES status(id), category TEXT NOT NULL, -- denormalised from status, trigger-maintained priority INTEGER NOT NULL DEFAULT 3 CHECK (priority BETWEEN 0 AND 4), assignee_id TEXT NOT NULL, delegate_id TEXT, -- one human; at most one agent project_id TEXT REFERENCES project(id), project_number INTEGER, -- number only if show_keys parent_id TEXT REFERENCES item(id), -- one parent, acyclic duplicate_of TEXT REFERENCES item(id), due_on TEXT, start_on TEXT, snooze_until TEXT, snooze_on_activity INTEGER NOT NULL DEFAULT 1, handoff_count INTEGER NOT NULL DEFAULT 0, -- hard handoffs; 3 forces human triage source TEXT NOT NULL, -- app|cli|chat|companion|connection|agent|gardener|import source_meeting_refs JSON, confidence REAL, -- provenance; confidence NULL if a human typed it attachments JSON, -- Drive-relative paths (see Open questions) created_by TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, last_activity_at TEXT NOT NULL, completed_at TEXT, cancelled_at TEXT, archived_at TEXT, trashed_at TEXT, CHECK (duplicate_of IS NULL OR category = 'cancelled'));CREATE INDEX item_live ON item(category, last_activity_at) WHERE archived_at IS NULL AND trashed_at IS NULL;CREATE INDEX item_owner ON item(assignee_id, category);CREATE INDEX item_parent ON item(parent_id);CREATE TABLE label (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, colour TEXT);CREATE TABLE item_label (item_id TEXT REFERENCES item(id), label_id TEXT REFERENCES label(id), PRIMARY KEY (item_id, label_id));CREATE TABLE relation (src TEXT NOT NULL REFERENCES item(id), dst TEXT NOT NULL REFERENCES item(id), kind TEXT NOT NULL CHECK (kind IN ('blocks','duplicate_of','related')), created_by TEXT NOT NULL, created_at TEXT NOT NULL, PRIMARY KEY (src, dst, kind), CHECK (src <> dst));CREATE TABLE comment (id TEXT PRIMARY KEY, item_id TEXT NOT NULL REFERENCES item(id), author_id TEXT NOT NULL, body_md TEXT NOT NULL, chat_ref TEXT, created_at TEXT NOT NULL, edited_at TEXT);CREATE TABLE commands ( -- idempotent write intents (Todoist shape, landscape §4) id TEXT PRIMARY KEY, -- client-generated uuid = idempotency key actor TEXT NOT NULL, kind TEXT NOT NULL, payload JSON NOT NULL, base_seq INTEGER, -- events.seq the client had seen — the CAS input received_at TEXT NOT NULL, applied_at TEXT, rejected_reason TEXT);CREATE TABLE events ( -- append-only: activity feed + audit trail + sync unit seq INTEGER PRIMARY KEY AUTOINCREMENT, -- the global lastSyncId ts TEXT NOT NULL, actor TEXT NOT NULL, kind TEXT NOT NULL, -- kind: item.created | item.transitioned | assigned | delegated | handed_off | taken_back -- | commented | linked | merged | snoozed | auto_declined | auto_closed | archived | trashed item TEXT REFERENCES item(id), payload JSON NOT NULL, command_id TEXT REFERENCES commands(id));CREATE INDEX events_item ON events(item, seq);CREATE TABLE view (id TEXT PRIMARY KEY, owner_id TEXT, scope TEXT NOT NULL, name TEXT NOT NULL, spec_json JSON NOT NULL, rank TEXT NOT NULL);CREATE TABLE item_rank (view_id TEXT REFERENCES view(id), item_id TEXT REFERENCES item(id), rank TEXT NOT NULL, PRIMARY KEY (view_id, item_id)); -- board orderCREATE TABLE agent_run ( id TEXT PRIMARY KEY, item_id TEXT NOT NULL REFERENCES item(id), agent_id TEXT NOT NULL, requested_by TEXT NOT NULL, trigger TEXT NOT NULL CHECK (trigger IN ('delegate','mention','automation','schedule')), state TEXT NOT NULL CHECK (state IN ('pending','active','awaiting_input', 'awaiting_approval','error','complete','stale','cancelled')), engine TEXT, model TEXT, session_ref TEXT, outcome TEXT, started_at TEXT, last_activity_at TEXT, ended_at TEXT, cost_usd REAL, tokens_in INTEGER, tokens_out INTEGER, tool_calls INTEGER, artifacts JSON, handoff_packet JSON);CREATE TABLE agent_activity ( -- immutable; the transcript and the audit trail run_id TEXT NOT NULL REFERENCES agent_run(id), seq INTEGER NOT NULL, ts TEXT NOT NULL, type TEXT NOT NULL CHECK (type IN ('thought','action','elicitation','response','error','prompt')), body TEXT, action TEXT, parameter TEXT, result TEXT, ephemeral INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (run_id, seq));CREATE TABLE approval_link ( -- the approval itself lives in instance.db approval_id TEXT PRIMARY KEY, item_id TEXT NOT NULL REFERENCES item(id), run_id TEXT REFERENCES agent_run(id), capability TEXT NOT NULL, trust_level TEXT NOT NULL, -- the level in force when the action was attempted requested_at TEXT NOT NULL, decided_at TEXT, decision TEXT);CREATE TABLE legacy_key (key TEXT PRIMARY KEY, item_id TEXT NOT NULL REFERENCES item(id));CREATE VIRTUAL TABLE item_fts USING fts5(title, body_md, content='item', content_rowid='rowid');-- derived state is a view, never a columnCREATE VIEW item_state AS SELECT i.id, EXISTS (SELECT 1 FROM relation r JOIN item b ON b.id = r.src WHERE r.dst = i.id AND r.kind = 'blocks' AND b.category NOT IN ('done','cancelled')) AS blocked, (i.snooze_until IS NOT NULL OR EXISTS (SELECT 1 FROM agent_run ar WHERE ar.item_id = i.id AND ar.state IN ('awaiting_input','awaiting_approval'))) AS waiting, (julianday('now') - julianday(i.last_activity_at)) AS days_idleFROM item i;Invariants, enforced in the command layer and never by the UI: exactly one human
assignee_id, always present; at most one delegate_id, always an agent; category
follows status_id; parent_id acyclic; a parent cannot close while it has open children;
duplicate_of implies category cancelled (landscape §8; teamwork §3.3).
Status and the state machine
Section titled “Status and the state machine”Seven categories, fixed in code; their display names are configurable per workspace, and a workspace may define several statuses inside one category (landscape §1: “small fixed category enum, names configurable”).
| Category | Default name | Meaning |
|---|---|---|
triage |
Triage | Arrived from somewhere; nobody has accepted it |
backlog |
Backlog | Accepted, not scheduled |
unstarted |
Todo | Scheduled, not begun |
started |
In progress | Someone or something is working on it |
review |
In review | Work done, awaiting a human’s read (runrec-study §5) |
done |
Done | Finished |
cancelled |
Cancelled | Not happening. The reserved Duplicate status sits here |
Transitions are unrestricted — any category to any other, as every leader allows,
because gating them only produces workarounds (landscape §8). The record is not optional:
each writes an item.transitioned event with actor, from, to and reason. Two guards only —
a parent with open children cannot close, and only the triage verbs move an item out of
triage.
The four triage verbs (Linear’s exactly, landscape §3): accept → the default
backlog/unstarted status, assignee set if absent; decline → cancelled with a reason;
duplicate <item> → cancelled with the reserved Duplicate status, duplicate_of set,
merge performed (§7); snooze <until> → hidden until that date or any new activity,
whichever comes first. Snooze works outside triage too: with snooze_on_activity = 1 (the
default) a comment, transition or agent turn un-snoozes immediately, and snooze_until IS NULL with the flag set is “Someday” — in the weekly review view, never in Today.
Ownership, delegation and handoff
Section titled “Ownership, delegation and handoff”The invariant (teamwork §3.3): exactly one human assignee at all times, at most one agent delegate, ownership changing only through an atomic handoff event. An agent is never an assignee; a non-owner may read and be asked, only the owner writes.
| Operation | Ownership effect | Event |
|---|---|---|
assign(human) |
assignee_id changes; a human→human handoff is this plus a packet |
assigned |
delegate(agent) |
delegate_id set, assignee unchanged; opens an agent_run; moves the item to the first started status when a human triggered it, leaves it in triage when an automation did |
delegated |
handoff(to) |
Hard handoff; the previous owner releases and handoff_count increments |
handed_off |
escalate() |
Handoff up the ladder — owner → Lead → human assignee → workspace owner — with blockers[] populated |
handed_off |
takeback |
One click: delegate_id cleared, run cancelled, packet and activity preserved |
taken_back |
A fourth hard handoff is refused: at handoff_count = 3 the item goes to human triage
(teamwork §3.4). ask is not a work-core operation — consulting a peer agent changes no
ownership, creates no item and writes nothing here; it belongs to the chat/agent model and
is specified there. If the answer requires the peer to act, the asker must delegate,
creating a child item (teamwork §7). The HandoffPacket is generated, never typed —
assembled from events and agent_activity at handoff time, stored on
agent_run.handoff_packet and in the handed_off payload, editable before sending. If to
is empty the item returns to the Lead’s triage rather than a guessed recipient (teamwork
§3.2).
{ "item": "7g3k2pq", "from": "prn_hisham", "to": "prn_bookkeeper", "reason": "context exhausted", "objective": "one sentence", "state": {"done": ["…"], "verified": ["…"], "assumed": ["…"]}, "next_step": "the single concrete next action, not a plan", "decisions": [{"decision": "…", "why": "…", "at": "2026-08-20T14:02Z"}], "blockers": [{"type": "needs_human|needs_peer|needs_capability|external_wait", "what": "…"}], "files": ["Nuchay/Files/contract-v3.pdf"], "open_approvals": ["apr_01J…"], "recent_events": [{"seq": 8812, "kind": "item.transitioned"}], "constraints": {"scope_out": ["…"], "approval_level": "propose_only"}, "budget_remaining": {"usd": 4.10, "wall_clock_s": 900} }Identifiers
Section titled “Identifiers”| Element | Rule |
|---|---|
| Primary key | UUIDv7, generated by the client — time-sortable, merge-safe, and it removes the temp-id problem for offline creates (landscape §2) |
| Short code | 7 characters, Crockford base32, derived from the UUID’s random segment, not a counter, so it never renumbers and needs no per-project sequence. Collision policy: UNIQUE on the column; on conflict the writer re-derives from the next 35 bits and retries three times, then fails the command. At 2³⁵ values a workspace needs ~260,000 items for a 1% chance of one collision |
| What a human sees | Titles. Lists, boards, chat and briefs show titles truncated at ~60 characters; the short code appears on hover, on copy-link, and in dim type at the end of a CLI row. No ID is ever printed into a chat or Telegram reply — that is exactly how Created t#187 reached the operator’s phone (audit §6) |
| Per-project key | KEY-12 from project_number, when a project sets key and show_keys = 1. Off by default. On a move the old key goes to legacy_key and keeps resolving in search, URLs and show (Linear’s behaviour, landscape §2) |
| URLs | qren://w/<workspace>/i/7g3k2pq in the app; https://<appliance>/w/<workspace>/i/7g3k2pq/<title-slug> through the portal. The slug is decorative, the code resolves |
| Resolution order | short_code → legacy_key → UUID → KEY-N → fuzzy title. An ambiguous fuzzy match lists candidates and exits non-zero rather than guess |
Lifecycle
Section titled “Lifecycle”Every timer is a workspace setting and every automatic action writes an event and appears in a daily digest card — nothing changes silently. Defaults from decision record §4; exclusions copied from Linear (landscape §3).
| Rule | Default | Exclusions |
|---|---|---|
| Triage expiry | 14 d idle → snoozed 14 d once → cancelled, reason triage_expired |
none |
| Stale auto-close, human-owned | 90 d idle in backlog/unstarted → cancelled, reason stale |
future due_on, open children, active project with a target date, snooze_until set |
| Stale auto-close, agent-owned | 30 d, same rule | as above |
| Archive after done | 30 d → archived_at |
none |
| Archive after cancelled | 7 d → archived_at |
none |
| Trash | 30 d after trashed_at → hard delete |
none |
Archived is hidden from live views and the client’s partial bootstrap, still searchable,
restorable in one action; trashed is an explicit human delete with a 30-day grace. Both
exist and they are different states (landscape §1). A seven-day warning card precedes
every stale auto-close, and all rules run dry for two weeks after a migration — the one
documented case of an automation closing 2,000 tickets is in landscape §8. Duplicates at
creation: FTS5 title similarity, plus embeddings once the knowledge index exists,
suggests a canonical item and never auto-merges; when the creator is an automation the
suggestion becomes a comment on the canonical item instead of a new item. Merge is
one-directional from the duplicate — relations, labels, attachments and subscribers move to
the canonical item, comments stay on the duplicate behind a banner linking forward,
duplicate_of is set.
Events, sync and the command log
Section titled “Events, sync and the command log”| Rule | What it means |
|---|---|
| Every mutation is a command | commands(id, actor, kind, payload, base_seq); the client-generated id is the idempotency key, so re-sending is a no-op |
| Applying one is one transaction | it mutates rows and appends to events. events.seq is the workspace’s global sync cursor and the same number that drives the activity feed |
| The appliance is server-authoritative | one writer; the edge holds no authority (landscape §4; ADR 0002) |
| v1 clients talk to the Supervisor’s Control API | over control.sock locally, through the portal or tunnel remotely — the way members reach runrec.org today (decision record §4, “appliance-only in v1”). The qren CLI is one caller among several (ADR 0004); no work operation exists only as a button |
Compare-and-swap on assignee_id, delegate_id, status_id |
a command whose base_seq predates a change to one of those is rejected with a reason that surfaces as a card. Title, body, labels, dates and priority are last-command-wins. No CRDTs — per-column LWW cannot enforce the one-owner invariant (landscape §4) |
| Not built in v1 | the edge read-replica, the offline queue, mobile clients, CRDT co-editing of body_md, cross-workspace queries, and any sync path that does not pass through the appliance |
| What the edge replica reads later | GET /sync?since=<seq>, a cacheable read-only events delta, plus a relay inbox of queued commands drained in arrival order when the appliance returns. The command log makes both additive |
Agent runs and approvals
Section titled “Agent runs and approvals”One agent_run per delegation or mention, with Linear’s state vocabulary plus the one it
lacks, awaiting_approval (landscape §6). agent_activity rows are immutable,
frozen-in-time snapshots — the transcript and the audit trail, for which comments are not a
substitute. Liveness (teamwork §3.5): acknowledge within 10 s with a thought or the
run shows unresponsive; 30 minutes idle marks it stale, recoverable by any activity; the
Supervisor marks error at 900 s per item and hands it to Lead triage. Fields no leading
tool records and Qren does: cost_usd, tokens_in, tokens_out, tool_calls, engine,
model, artifacts[], outcome — an owner paying per token wants the cost on the work
item.
Approvals. Every agent capability starts propose-only and graduates per capability
(decision record §3). When an agent attempts an action above its trust level it emits an
elicitation, the run moves to awaiting_approval, and an approval_link row points at an
approval in instance.db. The card renders that row joined to its item: title, what the
agent wants to do, the diff or message, and the run’s cost so far; deciding it writes
decision/decided_at and emits the event the run resumes from. trust_level is recorded
per action, at the moment it was attempted, so the audit answers “what was it allowed to
do then”. An agent message can never satisfy an approval (teamwork §7).
Interfaces
Section titled “Interfaces”qren work <verb> — engine-agnostic, reachable by any agent through the shell (ADR 0004),
human-readable by default, --json for agents. Every verb accepts short_code | KEY-N | uuid | "fuzzy title".
| Verb | Example |
|---|---|
add |
qren work add "Reconcile July invoices" --project nuchay --label finance |
show |
qren work show "reconcile july" |
list |
qren work list --mine --category started · --view "Needs a decision" |
start |
qren work start "reconcile july" |
done |
qren work done "reconcile july" |
cancel |
qren work cancel 7g3k2pq --reason "client withdrew" |
triage |
qren work triage accept 7g3k2pq · decline · duplicate <item> · snooze 2026-09-01 |
assign |
qren work assign "reconcile july" zeeshan |
delegate |
qren work delegate "reconcile july" bookkeeper |
handoff |
qren work handoff "reconcile july" eddeb --reason "crossing to design" |
takeback |
qren work takeback "reconcile july" |
comment |
qren work comment 7g3k2pq "waiting on the bank export" |
link |
qren work link 7g3k2pq blocks 4m8ptz2 · related · duplicate_of |
Output prints titles with the short code following in dim type; list prints no codes at
all unless --codes is passed. Control API: each verb is one command kind through the
Supervisor — item.create, item.update, item.transition, item.triage, item.assign,
item.delegate, item.handoff, item.takeback, comment.create, relation.create,
view.upsert, rank.set — and reads are GET /work/items?view=… and GET /sync?since=<seq>.
Views are data. Board and list are the same rows: view.spec_json holds {filters, group_by, sort, layout}, and manual board order lives in item_rank as a string rank per
(view, item), so a drag is one row update and never rewrites another view’s order (landscape
§5). Seeded per workspace: Triage, Mine, Today, In review, Delegated to
agents, Waiting on approval, Rotting (open items by last_activity_at, with age
badges). The thin slice renders one board with the seven categories as columns, the
triage view with its four verbs, an item detail carrying description, comments, agent
activity and the handoff card, and the shared approval queue — nothing else (decision record
§1).
Migration — the Gardener’s first job
Section titled “Migration — the Gardener’s first job”The work core ships empty. At install the Gardener reads the AOS work.db and proposes
an import to the approval queue; the operator approves, amends or rejects (decision record
§2). Against today’s 2,111 tasks (audit §1):
| Rows | Proposal |
|---|---|
898 todo + 22 active |
Import. The 93 idle over 90 d land in backlog and are listed on a review card; the rest keep their status |
done/cancelled within 90 d |
Import, already archived |
done/cancelled older (of 1,173 total) |
Not imported. The old database is kept read-only in the workspace’s Drive |
| 368+ test-fixture titles (audit §4) | Never imported, matched by literal title against the known fixture list |
4,059 exploring threads |
Never imported — 2 were ever promoted |
37 inbox rows, 16 stuck islah-import tasks |
Into triage, where the 14-day expiry applies |
| 739 dotted subtasks | Become parent_id; the 15 rows whose prefix and real parent disagree follow parent_id, with the discrepancy on the card |
1,003 project-less t# tasks |
project_id NULL, source = import; no bucket project is invented |
ID mapping: every old id (t#187, aos#3, uc#1.1) goes to legacy_key, so old
links, handoff notes and vault documents keep resolving; new short codes are minted at
import; project prefixes become project.key with show_keys = 0 — available, invisible.
Reversible: the import is one transaction into a fresh workspace.db and never writes
to the source, so reverting is deleting one file, and every lifecycle rule runs dry for 14
days afterwards.
Open questions, and what the slice delivers
Section titled “Open questions, and what the slice delivers”| # | Question |
|---|---|
| 1 | Attachments. The locked model has no attachment table but RunRec needs task↔file links (runrec-study §5); proposed here as an attachments JSON column of Drive-relative paths. Confirm, or admit a table |
| 2 | Cycles. Nobody has them, yet the stale-close exclusion names “an active project with a target date”. Is project.target_on enough, or is the exclusion cut in v1? |
| 3 | Leaving review — must it be a different principal than the one who entered it, or is that workspace policy rather than an invariant? |
| 4 | Comment vs chat. When comment.chat_ref is set and both exist, which is canonical? |
| 5 | Trust on a child item created by delegate: the parent’s, the delegate’s, or min(parent, delegate) (teamwork open question 4)? |
| 6 | Priority is kept as 0–4, a field 51% of current rows never received an opinion on (audit §1). Keep it, or derive urgency from due_on? |
| 7 | Duplicates at import. 1,503 near-duplicate title pairs exist, mostly false positives (audit §4). Does the Gardener propose merges, or only flag them for the first weekly review? |
| 8 | Status names are configurable but the seeded set is English. Where does localisation live? |
The slice delivers every table above, the thirteen CLI entries, board and triage and item detail and the approval queue, all six lifecycle timers running dry for two weeks, CAS on owner and status, and the Gardener import. Deliberately later: the edge replica and offline queue; mobile; cycles, milestones, estimates, SLAs and custom fields; embedding-based duplicate detection; cross-workspace roll-ups (that is Home — a computed lens, never a container); rich-text co-editing; and any automatic creation path that skips triage or an approval.