Skip to content

feat: agent-fleet ergonomics (PROJ-958: 959, 960, 961, 962) - #386

Merged
TAJD merged 8 commits into
mainfrom
agent/proj-958-fleet-ergonomics
Oct 4, 2026
Merged

TAJD merged 8 commits into
mainfrom
agent/proj-958-fleet-ergonomics

Conversation

@TAJD

@TAJD TAJD commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Epic PROJ-958: agent-fleet ergonomics

  • PROJ-959 every issue-id tool (comments, leases, file claims, agents, messages, sprints, attachments) accepts a UUID or a ref like PROJ-42; refs resolve visibility-aware, so a hidden ref answers exactly like an unknown one.
  • PROJ-960 list_issues/search_issues take labels (all-of by default, labelsMode: "any"); search_issues also matches exact label text.
  • PROJ-961 completionReport.remainder on done creates a follows_from-linked follow-up (same parent + labels) and returns followUp.ref; issue view shows "Partially done → PROJ-n". Migration 0068.
  • PROJ-962 per-project epicAutoClose (default off): last child done/cancelled returns parentReadyToClose:{ref} or auto-closes (parentClosed). Migration 0069.

Two opus adversarial reviews; all confirmed findings fixed with tests.

Verification

gen:docs no diff, biome clean, turbo type-check, db tests, API suite (1863), web build, docs build. Web suite: 913 pass; font-subset.test.ts fails here for lack of the brotli python module (fails identically on main).

🤖 Generated with Claude Code

https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq


Generated by Claude Code

claude added 7 commits October 2, 2026 08:20
…J-959)

add_comment and friends only took a UUID and answered "Issue not found" for a ref,
costing agents a get_issue round trip (38 failed calls across 22 sessions).

- services/issue-ref.ts: the ref resolver moves out of issues.ts (re-exported there) so
  comments/leases/file-claims can use it without an import cycle; adds a batched
  resolver for move_issues_to_sprint (one chunked query per project key) and a scope
  canonicaliser so "issue:PROJ-42" and "issue:<uuid>" are the same message channel.
- schemas accept IssueIdOrRefSchema instead of .uuid(); services resolve at their entry
  point, so REST and MCP behave identically.
- Covers comments (4), claim/release/list leases, claim/release/list file claims,
  register_agent, start_work, finish_work, list_active_agents, post/list_messages,
  move_issues_to_sprint, list/create attachments. Tool descriptions say "UUID or ref".
- List filters and attachment reads return empty for an unknown ref rather than 404,
  so they can't be used to probe which refs exist in projects the caller can't see.
- Tests: one MCP test per tool, unknown-ref and visibility cases.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
…sues (PROJ-960)

Agents scope cohorts ("all bugs found during X") by label but had no way to list by one:
list_issues rejected `labels`, and search_issues for the exact label text returned
nothing even though 28 issues carried it (issues_fts indexes title and body only).

- list_issues: `labels` (array over MCP, comma-separated over REST) with all-of
  semantics by default and `labelsMode: "any"`. Exact, case-sensitive match via
  json_each; a row with malformed label JSON reads as no labels instead of 500ing the
  list. Paginates and counts like every other filter.
- search_issues: an issue whose label equals the whole query (case-insensitive) is a
  hit and leads the results, merged and de-duplicated with the FTS hits; `labels` /
  `labelsMode` narrow keyword hits. No FTS migration needed. Visibility and project
  scoping apply to both sources. Tool description points cohorts at list_issues, since
  search is capped at 50 and has no cursor.
- Tests: all-of/any, exactness, pagination, MCP + REST shapes, malformed JSON, project
  visibility, merge/dedupe/limit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
…ROJ-959/960 review)

Refs no longer widen project visibility: hidden refs answer like unknown ones.
Label search guards (length, json_type), repeated labels params, tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
…PROJ-961)

Adds follows_from link type (migration 0068), follow-up issue under the same
parent/labels returned as followUp.ref from update_issue/finish_work, and a
'Partially done -> PROJ-n' relation in the issue view.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
…es (PROJ-962)

Per-project epicAutoClose setting (migration 0069, default off). update_issue and
finish_work return parentReadyToClose:{ref} or parentClosed:{ref}; cancelled
children count; an open remainder follow-up keeps the epic open.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
- epic check only on a real transition into done/cancelled (not re-saves)
- parentReadyToClose/parentClosed respect project visibility and write access
- auto-close failure degrades to the hint instead of failing the child's close
- remainder follow-up failure is reported as followUpError, not thrown
- epicAutoClose settable via update_project only; web label handles legacy done key

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EiHvwofTMQZrWW9oPQWuqq
@TAJD
TAJD merged commit 2f5f30d into main Oct 4, 2026
1 check passed
@TAJD
TAJD deleted the agent/proj-958-fleet-ergonomics branch October 4, 2026 14:32
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