Files
pad/cmd
xarmian 0f05012169 fix(mcp): six surgical fixes for BUG-987 (bugs 6, 8, 11, 12, 13, 14) (#361)
* fix(mcp): six surgical fixes for BUG-987 (bugs 6, 8, 11, 12, 13, 14)

Hotfix follow-up to v0.1.0-rc.3's Claude Desktop dogfood. Six
surgical fixes; bigger items (5, 7, 9, 10) deferred to separate
tasks.

- Bug 6: `pad project next --format json` was emitting the entire
  dashboard, indistinguishable from `pad project dashboard --format
  json`. Now slices to suggested_next only. cmd/pad/main.go.

- Bug 8: standup blockers carried empty `ref` strings, blocking
  agent linkback to the actually-blocked items. dashboard's
  attention[].item_ref is canonical; the standup composer in
  internal/mcp/dispatch_http_slice4.go just wasn't propagating it.
  Same fix applied to suggested_next entries.

- Bug 11: cobra's auto-emitted "Usage: pad item block ..." help
  block leaked into MCP error envelopes via classifyExecError. The
  Usage text references OLD CLI verb names (pre-v0.2 catalog) that
  agents using the new surface have no business seeing, and bloats
  every error response. New stripCobraUsageBlock helper truncates
  stderr at the first line-anchored "Usage:" marker before
  classification + envelope construction.

- Bug 12: BuildCLIArgs validation errors (missing required arg, type
  mismatch) came out of env.Dispatch as bare-text NewToolResultErrorf
  results, breaking the structured envelope contract. New helper
  validationFailedFromBuildErr wraps them as ErrValidationFailed
  envelopes with the field name extracted via regex from the
  underlying message.

- Bug 13: every Task / Idea / Plan with a `priority` field got a
  phantom `convention: { enforcement: "<priority>" }` surfaced on
  its response, because ExtractItemConventionMetadata's legacy
  fallback treated `priority` as the Convention enforcement tier
  unconditionally. Restructured to track hasConventionShape
  separately from hasMetadata — only Convention-specific markers
  (structured convention field, trigger, scope, surfaces, commands,
  direct enforcement) flip the shape flag. category alone is
  insufficient (Ideas / Bugs / Roadmap items legitimately use it).
  Final guard returns nil when only category was matched.

- Bug 14: GetRoleBreakdown's unassigned row was emitted with empty
  role_name + role_slug, presenting as a "phantom" entry in the
  dashboard. Now explicitly labelled "Unassigned" / "unassigned"
  while keeping role_id null so it's still distinguishable from a
  real role.

Tests:
- internal/mcp/bug987_test.go (new) — stripCobraUsageBlock + classify
  + validation envelope wrapping + env.Dispatch integration.
- internal/models/item_test.go — three cases covering non-Convention
  items (Task, Idea, Plan with priority) returning nil metadata, and
  one preservation test for legacy Conventions with priority field.
- internal/store/agent_roles_test.go (new) — confirms unassigned row
  carries explicit "Unassigned" / "unassigned" labels.

Live verified: pad_project action=next returns just the suggestions
array; pad_item action=create with no fields returns validation_failed
with field=collection; pad_item action=link with self-target returns
without Usage-block leakage.

Deferred to separate items (per BUG-987 triage):
- Bug 5: text vs JSON returns across note, decide, star, unstar,
  delete, bulk-update — needs CLI-side handler updates per command.
- Bug 7: suggested_next algorithm — needs to consider in-progress
  items, not just open ones; behavior change needs design.
- Bug 9: fields/tags double-stringified — potentially breaking for
  web UI/CLI consumers.
- Bug 10: decision_log/notes embedded in fields blob duplicating
  top-level arrays — might require data migration.

Parent: BUG-987.

* fix(mcp): HTTP transport equivalence + ordering for BUG-987 per Codex review (round 1)

Two findings from Codex review of PR #361:

1. project.next on HTTP transport still returned the full dashboard.
   The route table mapped "project next" directly to /dashboard, so
   the CLI fix (slice to suggested_next) didn't reach OAuth-authed
   agents going through HTTPHandlerDispatcher. Catalog actions must
   produce equivalent shapes on stdio and HTTP — that's the contract
   that lets agents be transport-agnostic.

   Fix: new dispatchProjectNext method on HTTPHandlerDispatcher that
   fetches the dashboard via the existing fetchDashboardJSON helper,
   slices to suggested_next[], re-encodes, and runs through
   packageJSONResult so it gets the same {items: [...]} wrap as
   other list responses.

   Also retires the broken route-table entry — replaced with a
   comment pointing at the new method so future contributors don't
   re-add a passthrough.

   Test: TestDispatch_ProjectNext_SlicesToSuggestedNext + the empty-
   array case. Asserts dashboard-only top-level fields (summary,
   active_items) don't leak into the response — that's the whole
   point of project.next being distinct from project.dashboard.

2. ExtractItemConventionMetadata's priority→enforcement legacy
   fallback ran BEFORE surfaces/scope/commands had a chance to flip
   hasConventionShape, so a Convention with only `{scope, priority}`
   would silently drop enforcement.

   Fix: move the priority fallback to AFTER all marker checks. Direct
   `enforcement` still resolves first; the legacy priority fallback
   runs at the bottom once shape detection is complete.

   Tests: two new cases covering scope-only and commands-only legacy
   Conventions — both must resolve enforcement via the priority
   fallback.

Parent: BUG-987.
2026-05-01 20:39:10 -04:00
..