Skip to content

fix(linear-issue): honor parent param on update action (ARM-128) - #76

Merged
stepandel merged 1 commit into
masterfrom
arm-128-fix-update-parent
Apr 7, 2026
Merged

fix(linear-issue): honor parent param on update action (ARM-128)#76
stepandel merged 1 commit into
masterfrom
arm-128-fix-update-parent

Conversation

@agent-vera

@agent-vera agent-vera Bot commented Apr 7, 2026

Copy link
Copy Markdown

Summary

Fixes ARM-128. The `linear_issue` tool's `update` action accepted a `parent` parameter via its TypeBox schema and returned `success: true`, but never actually forwarded `parentId` to the Linear `issueUpdate` mutation. Reparenting silently no-op'd, so agents thought their reorganizations had landed when they hadn't. The bug was specific to the update path — `create` worked correctly.

Root cause

`updateIssue` in `src/tools/linear-issue.ts` builds an `input` object from the params and forwards it to the `issueUpdate` mutation. Every other field (title, description, state, assignee, labels, dueDate, etc.) had a corresponding `if (params.X) input.Y = …` line. `parent` was missing entirely. `createIssue` had the correct mapping; it just wasn't mirrored on the update path.

Fix

```ts
if (params.parent !== undefined) {
// Empty string or null → unparent. Otherwise resolve the identifier (e.g. ENG-100) → UUID.
input.parentId = params.parent === "" || params.parent === null ? null : await resolveIssueId(params.parent);
}
```

  • Resolves identifiers like `ARM-5` to internal UUIDs via the existing `resolveIssueId` helper (same code path `createIssue` uses).
  • Treats empty string or null as unparent — Linear's `IssueUpdateInput.parentId` is nullable, and passing `null` removes the parent link.
  • Updated the schema description to document update support and the empty-string unparent escape hatch.

Verification

Ran an end-to-end smoke test against the live Linear API using the patched module. 14 assertions, all green:

```
[smoke] Creating parent A...
✓ parent A created: ARM-129
[smoke] Creating parent B...
✓ parent B created: ARM-130
[smoke] Creating child under parent A (via create path)...
✓ child created: ARM-131
[smoke] Verify create path: child has parent A...
✓ child.parent === ARM-129
✓ parent A children include ARM-131
[smoke] Reparenting child A → B via UPDATE (the actual bug fix)...
✓ update returned success
✓ child.parent now === ARM-130
✓ parent A no longer includes ARM-131
✓ parent B now includes ARM-131
[smoke] Unparenting via empty string...
✓ unparent (empty string) returned success
✓ child.parent === null after empty-string unparent
✓ parent B no longer includes ARM-131 after unparent
[smoke] Reparenting back to A, then unparenting via null...
✓ re-parented to ARM-129 for null test
✓ unparent (null) returned success
✓ child.parent === null after null unparent
[smoke] ALL ASSERTIONS PASSED ✅
```

Test coverage:

  • ✅ Reparent A → B via update (the actual reported bug)
  • ✅ `parent.children.nodes` reflects the change on both old and new parent (no stale relationship)
  • ✅ Unparent via `parent: ""`
  • ✅ Unparent via `parent: null`
  • ✅ Create path still works (no regression)
  • ✅ Throwaway issues cleaned up after the run

Also passed:

  • `bun x tsc --noEmit` — clean
  • `bun x biome check src/` — 87 files, 0 issues
  • `bun test src/tests/config-validation.test.ts src/recovery.test.ts src/trace-scrubber.test.ts` — 223 pass / 0 fail

Definition of Done (from ARM-128)

  • `linear_issue action=update` with `parent=` correctly reparents the target issue
  • Verified end-to-end: create a child, reparent it, `view` confirms new parent, parent's `children.nodes` includes the reparented issue
  • Passing `parent=""` or `parent=null` unparents the issue
  • Tool schema documentation updated to reflect that `parent` works on both `create` and `update`

Notes

The fix won't take effect for currently-running agent sessions until the new image is built and they reconnect — the existing pi runtime still has the buggy version compiled in. Verification above used the patched module loaded directly, so it exercises the actual code path that will ship.

The linear_issue tool's update action accepted a parent parameter via
its TypeBox schema, returned success: true, but never actually forwarded
parentId to the Linear issueUpdate mutation. Reparenting silently
no-op'd, which is worse than rejecting the request — agents thought
their reorganizations had landed when they hadn't.

The create path was correct; only update was missing the input mapping.

Changes:
- Add params.parent handling to updateIssue, mirroring createIssue.
- Treat empty string or null as 'unparent' (sets parentId: null on
  IssueUpdateInput, which Linear interprets as removing the parent link).
- Update the schema description to document update support and the
  empty-string unparent escape hatch.

Verified end-to-end against the live Linear API (14 assertions): create
under parent A, reparent to B via update, unparent via empty string,
reparent + unparent via null, plus children.nodes consistency on every
parent before/after each transition.

Fixes ARM-128.
@linear

linear Bot commented Apr 7, 2026

Copy link
Copy Markdown
ARM-128 Bug: `linear_issue` update action silently ignores `parent` field

Summary

The linear_issue tool's update action accepts a parent parameter, returns success: true, but does not actually apply the parent change. This makes it impossible to reparent an existing Linear issue from the agent without deleting and recreating it.

The create action's parent parameter works correctly — the bug is specific to the update path.

Impact

  • Agents can't reorganize ticket hierarchies after the fact
  • Silent failure is worse than an explicit error — the agent thinks the operation succeeded and moves on
  • Users see a confusing mismatch where the agent reports "done" but the epic still has the wrong children
  • Workaround today is either (a) asking the human to drag in the Linear UI, or (b) delete + recreate (loses the original identifier, breaks cross-references)

Reproduction

Hit during the ARM-5 compliance epic restructuring on 2026-04-07:

  1. Created ARM-119 through ARM-123 without a parent

  2. Tried to reparent them to ARM-5 via:

    linear_issue action=update issueId=ARM-119 parent=ARM-5
    
  3. Tool returned:

    { "success": true, "issue": { "id": "...", "identifier": "ARM-119", "title": "..." } }
  4. Followed up with linear_issue action=view issueId=ARM-119parent field was still null. Confirmed ARM-5's children.nodes did not include ARM-119.

  5. Repeated for ARM-120–123 with identical results.

Meanwhile ARM-124–127, which were created the same session with parent: "ARM-5" in the create call, correctly show as children of ARM-5. So the create path works; only update is broken.

Expected Behavior

One of:

  1. Preferred: update should honor the parent parameter and actually reparent the issue
  2. Acceptable fallback: If reparenting isn't supported for some intentional reason, the tool should reject the parameter with an explicit error rather than silently returning success

Tool Schema Note

The current tool schema documents parent as:

"Parent issue identifier for sub-issues (e.g. ENG-100). Used with create."

If the intent is truly "create only," the parameter should either be removed from the update action's accepted params, or the update path should return a validation error when it's passed.

Entry Points

The linear_issue tool is a built-in custom tool in the Vera agent harness (not in army-control-plane). Likely lives in the anton / tenant app codebase (vera-images or equivalent) where the Linear tool registry is defined. Grep for:

  • linear_issue tool registration
  • The GraphQL mutation used for issueUpdate — the parentId field probably needs to be added to the input object
  • Linear GraphQL schema: IssueUpdateInput supports parentId: String for reparenting (per Linear's API docs)

Definition of Done

  • linear_issue action=update with parent=<identifier> correctly reparents the target issue
  • Verified end-to-end: create a child, reparent it, view confirms new parent, parent's children.nodes includes the reparented issue
  • Also verify: passing parent="" or parent=null unparents the issue (edge case)
  • Tool schema documentation updated to reflect that parent works on both create and update

Related

  • ARM-5 (Compliance epic) — this bug blocked clean restructuring of its sub-issues
  • ARM-109 (Ticket formatting) — similar "tool quirks that cause agent confusion" category

@stepandel
stepandel merged commit 2f0e06c into master Apr 7, 2026
2 checks passed
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.

1 participant