Skip to content

Add assigned_gm field for task ownership / multi-GM event scoping#561

Open
kylecarbonneau wants to merge 4 commits into
bborn:mainfrom
kylecarbonneau:assigned-gm-field
Open

Add assigned_gm field for task ownership / multi-GM event scoping#561
kylecarbonneau wants to merge 4 commits into
bborn:mainfrom
kylecarbonneau:assigned-gm-field

Conversation

@kylecarbonneau

Copy link
Copy Markdown
Contributor

Adds an assigned_gm string field on Task (with migration), filter flags (ty list --assigned-gm <slug> / --unassigned), --assigned-gm on ty create / ty update, an assigned_gm property on the taskyou_create_task MCP tool, and event support: a TASK_ASSIGNED_GM env var on every task hook plus a task.updated event (and a TASK_PREVIOUS_ASSIGNED_GM env var) whenever a task's assignment changes.

Motivation

With multiple GM sessions running on the same daemon (each a separate Claude session driving its own delegated tasks), every GM currently sees every task event. This adds first-class ownership so per-GM filters (channel-side, kanban UI, CLI) can scope cleanly instead of relying on soft CLAUDE.md conventions. The assignment-change event lets a GM's channel react when a task becomes theirs, is reassigned away, or returns to the unassigned pool.

ty itself only stores and exposes the field — validation / slug-to-GM mapping is the client's job (e.g. a push-channel server). A companion client-side change would consume the field for the actual filtering.

What's included

  • DB: AssignedGM on Task, threaded through CreateTask / UpdateTask and every task SELECT/scan site; migration ALTER TABLE tasks ADD COLUMN assigned_gm TEXT DEFAULT ''.
  • Filtering: ListTasksOptions.AssignedGM / Unassignedty list --assigned-gm <slug> and --unassigned (the latter takes precedence).
  • CLI: --assigned-gm on ty create and ty update; assignment is preserved across the TUI edit and project-move flows.
  • MCP: free-form assigned_gm string property on taskyou_create_task.
  • Events: TASK_ASSIGNED_GM exported to all task hooks; UpdateTask emits a task.updated event carrying {old, new} when the assignment actually changes, and runHook additionally exports TASK_PREVIOUS_ASSIGNED_GM on those events so hooks can tell the direction of the change without parsing TASK_METADATA.

Design notes

  • ty does no validation of the slug — it's stored verbatim. The slug→GM mapping belongs to the client.
  • The assignment-change event reuses the existing per-field {old, new} change-metadata convention (same shape as title/status/project changes) and only fires when the value actually changed.

Testing

Unit tests cover create/get/update/clear persistence, the --assigned-gm / --unassigned list filters (including unassigned-overrides-slug precedence), the assignment-change event (fires on change, not on unrelated edits), and that both env vars reach hook scripts. go vet ./... and the db / executor / events / mcp / cmd / ui test suites pass.

kylecarbonneau and others added 4 commits June 6, 2026 12:15
Introduces an AssignedGM string on Task: a free-form slug identifying
the GM (manager session) a task belongs to. Threads it through the
INSERT, UPDATE, and all SELECT/scan sites, adds the schema migration,
and gives ListTasks --assigned-gm / --unassigned filtering via
ListTasksOptions.

ty stores the slug verbatim and does no validation; the slug-to-GM
mapping is the client's concern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds --assigned-gm to `ty create` / `ty update` / `ty list` and
--unassigned to `ty list`, an assigned_gm property on the
taskyou_create_task MCP tool, and a TASK_ASSIGNED_GM env var passed to
event hook scripts so push-channel clients can scope events per GM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
UpdateTask now records an assigned_gm change in the emitted task.updated
event's metadata ({"old","new"}, matching the existing per-field
convention), so per-GM channels can react to a task becoming theirs,
being reassigned away, or returning to the unassigned pool. The event
only fires when the value actually changed.

runHook also exposes TASK_PREVIOUS_ASSIGNED_GM when the update carries
an assignment change, so hooks can detect the direction of the change
without parsing TASK_METADATA as JSON.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The TUI edit form and the move-to-project clone paths reconstruct a
task without an assignment field, which would silently drop assigned_gm
and — now that UpdateTask emits an assignment-change event — fire a
spurious "unassigned" event on an unrelated edit. Carry the existing
assignment through all three paths (TUI edit-save, TUI project move,
CLI move-to-project), mirroring how tags is already preserved on the
CLI clone.

Also drop a stray unused variable in the list-filter test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bborn

bborn commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Did a thorough pass on this — the code itself is clean, correct, and genuinely well-tested (migration is safe/additive, all 9 SELECT/scan projection sites stay in sync, build/vet/tests green). No complaints on execution.

My one piece of feedback is bigger-picture: I think this is solving the problem at the wrong layer, and I'd like to redirect before it goes further. Proposing we close this in favor of a tag-based approach.

Why redirect

  • assigned_gm bakes a ty-os concept (GM) into ty's core task schema for a single use case. ty is meant to be a neutral task substrate; ty-os owns the GM concept.
  • ty-os already tracks ownership client-side — the GM keeps tasks in its Claude Code todos and reconciles against ty list + the shared notifications.jsonl stream (gm-babysit). And the scoping axis that's already wired end-to-end is project (ty list -p, TASK_PROJECT in hooks, project in notifications). With today's 1-GM-per-project model, that already covers "my tasks / my events."
  • The one thing assigned_gm adds that's hard to do client-side — the reassignment direction event (old→new) — only matters in a many-GMs-sharing-one-board world with task handoffs, which we haven't committed to building. Let's not encode that future speculatively.

Better-shaped approach: generalize on tags

ty already has a free-form tags column. A GM scopes its work with a gm:<slug> tag convention — no new schema, and every future tagging need benefits. Honest accounting of what's actually needed (tags are only half-wired today):

ty (small, generic changes):

  1. TASK_TAGS in the hook env (internal/events/events.go) — one line. This alone unlocks event scoping: ty-os filters its own hook/notification stream by tag, client-side. Highest-leverage change.
  2. ty list tag filter — add Tag to ListTasksOptions + an AND tags LIKE ? clause + a --tag flag. ⚠️ Subtlety: tags are comma-separated, so a naïve LIKE '%gm:cortex%' false-matches gm:cortex-2 — match on delimiters (normalize to ,a,b, and match ,gm:cortex,).
  3. TUI support (currently missing entirely): a tags input in the create/edit forms, ideally tag display on the board + a tag filter. Net-new, but generally useful — not GM-specific.

ty-os:
4. GM adopts the gm:<slug> tag convention; hook scripts filter on TASK_TAGS; gm-babysit/status use ty list --tag gm:<slug>.

The one capability we'd consciously drop

Crisp "reassigned away from me" detection — re-tagging doesn't give a single-field old→new delta the way assigned_gm did. I think that's an acceptable trade: re-assignment between GMs is rare, project scoping covers the common case, and we can revisit if/when shared-board handoffs become real.

Net: most of the value here is ~one line (TASK_TAGS in hooks); the rest (list filter, TUI tags) is general-purpose work we'd want regardless. I'd rather ship that than a GM-specific column. Happy to pair on the tag-filter delimiter matching + TUI bits if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants