Skip to content

chore(tools): validate a created ticket against the identifier orca reported - #610

Merged
thomasluizon merged 2 commits into
mainfrom
chore/new-ticket-tool
Jul 25, 2026
Merged

chore(tools): validate a created ticket against the identifier orca reported#610
thomasluizon merged 2 commits into
mainfrom
chore/new-ticket-tool

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

What

tools/new-ticket.mjs, a thin wrapper over orca linear create that validates the issue it just created.

Why

Creating a ticket and validating it are two commands, and every caller so far joined them by typing the identifier into the second one. That identifier is a guess.

Measured 2026-07-25: a ticket was created as ORB-87 while the validation ran against ORB-85. ORB-85 exists, belongs to something else, and passed. The defective ticket read as verified. Its real defect (a dependency named in prose with no blockedBy relation) surfaced only because the checker happened to be re-run by hand.

That is the failure mode worth closing: not a check that fails, a check that passes against the wrong subject.

What it does not do

It does not reimplement creation. orca linear create already handles --body-file - for stdin, project and labels by exact name, --state, --estimate and --json, and every flag is forwarded verbatim. The tools contract says prefer delegating over reimplementing, so this stays a wrapper.

The only thing added is the link: the identifier is parsed out of orca's own JSON and handed to check-ticket.mjs. It cannot be typed.

Behaviour

Outcome Exit
Created and passes check-ticket.mjs 0
Created but DEFECTIVE; prints the identifier, the problems, and the re-check command 1
Usage error 2
orca or Linear failed, or reported success with no identifier to validate 3

Exit 3 on a missing identifier is deliberate: if there is nothing to validate, saying so beats exiting 0 on an unchecked ticket.

Verification

node tools/new-ticket.mjs --help    exit 0, usage on stdout
node tools/new-ticket.mjs           exit 2, no args
node tools/test-tools.mjs           ORBIT TOOLS GATE OK

Coverage entry added in the same change, so the structural guard from #606 stays satisfied.

🤖 Generated with Claude Code

…eported

Creating a Linear ticket and validating it are two commands, and every caller so
far joined them by typing the identifier into the second one. That identifier is
a guess.

Measured 2026-07-25: a ticket was created as ORB-87 while the validation ran
against ORB-85. ORB-85 exists, belongs to something else, and passed. So the
defective ticket read as verified, and the defect (a dependency named in prose
with no blockedBy relation) was only caught by re-running the checker by hand.

new-ticket.mjs forwards every flag to orca linear create, which already does the
creating properly (--body-file - for stdin, project and labels by exact name,
--json). The only thing it adds is the link: the identifier is parsed out of
orca's own JSON and passed to check-ticket.mjs, so the check cannot target a
different issue. A created-but-defective ticket exits 1 and prints both the
identifier and the problems, rather than exiting 0 on someone else's ticket.

Not reimplemented, deliberately: orca owns creation and this stays a wrapper,
per the tools contract's 'prefer delegating over reimplementing'.

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

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jul 25, 2026 3:39am

Request Review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #610

Recommendation: NEEDS WORK

Summary

tools/new-ticket.mjs is a thin, well-motivated wrapper over orca linear create that closes a real gap (an identifier typed by hand can target the wrong ticket). The delegation design is sound. The problem is verification: this PR's own change to tools/test-tools.mjs (the harness execution gate) adds only the trivial no-args usage-error case for the new script. Every substantive decision path the tool exists to make has zero execution coverage, unlike the same file's existing orcaEnv(...)-stubbed cases for launch-worker.mjs, nudge-worker.mjs, and wave-plan.mjs. That is exactly the failure mode rubric dimension 15 exists to catch: a harness cannot certify itself by being read.

Findings

[High] tools/new-ticket.mjs's core decision paths have zero execution coverage in the harness gate

  • dimension: 15 (Harness changes need EXECUTED evidence)
  • location: tools/test-tools.mjs:270-297 (gateCases) and tools/new-ticket.mjs:52-108
  • issue: the script has five distinct outcome branches beyond bare usage: (1) orca linear create succeeds + the created issue passes check-ticket.mjs → exit 0, (2) succeeds but the issue is DEFECTIVE → exit 1, (3) linear create itself throws → exit 3, (4) orca reports success but its JSON carries no parseable identifier → exit 3, (5) argv non-empty but missing --title → exit 2 (a different branch than the argv.length===0 case). The PR's only test-tools.mjs addition is one INVALID_INPUT row (argv: [], status: 2), which drives only the argv.length===0 branch. There is no gateCases["new-ticket.mjs"] entry at all, unlike every comparable orca-driven tool in the same file.
  • risk: the tool's entire reason for existing — validating the ticket orca actually created, using the identifier orca reported — never executes in CI. A regression in the identifier-extraction fallback chain, the exit-code mapping, or the handoff to check-ticket.mjs would pass Harness Execution and merge undetected.
  • fix: add gateCases["new-ticket.mjs"] using the same orcaEnv([...]) pattern as wave-plan.mjs/launch-worker.mjs, stubbing both linear create and the nested linear issue <id> call (from the check-ticket.mjs subprocess it shells out to, which inherits the same env). Cover: success+valid → 0, success+defective → 1, orca failure → 3, missing-identifier → 3.
  • reference: .claude/skills/pr-review/rubric.md dimension 15; tools/CONVENTIONS.md "The gate"

[Medium] Identifier extraction guesses across four un-verified JSON shapes

  • dimension: 5 (No-workaround / root-cause)
  • location: tools/new-ticket.mjs:76-80
  • issue: parsed?.result?.issue ?? parsed?.issue ?? parsed?.result ?? parsed tries four envelope shapes; check-ticket.mjs's own orca-JSON parsing needs only two (parsed.result ?? parsed then .issue ?? parsedResult). The extra fallbacks aren't motivated by a comment or the PR body, which never actually ran the create+validate path against real orca.
  • risk: if the real envelope doesn't match any guessed shape, the tool reports "no identifier" (exit 3) even though creation succeeded — recoverable (raw output printed, tells the caller to check Linear before retrying) but exactly the gap the missing gateCases coverage above would catch.
  • fix: confirm the real shape once and collapse to check-ticket.mjs's two-level pattern, or add a one-line WHY if multiple shapes are genuinely possible.
  • reference: root CLAUDE.md rule 1

Subagents

parity-checker / i18n-syncer / contract-aligner / security-reviewer / design-reviewer: all N/A — diff is tools/ only, touches no apps/*, orbit-api, or shared contract types.

Validation

Lint, Type Check, Harness Execution all SUCCESS per this PR's own CI run (gh pr view --json statusCheckRollup); Unit Tests was still in progress at review time. Local /validate execution wasn't possible in this review session's sandbox (Bash there blocks any filesystem-writing invocation, including running test-tools.mjs itself), so CI's own run is cited in its place.

Deferred

Dimensions 8-14 are N/A (no apps/*, orbit-api, shared-types, or feature-surface changes in this diff). All three changed files (README.md, new-ticket.mjs, test-tools.mjs) received a verdict.

What's good

Genuine delegation (no reimplementation of orca linear create), the identifier can only come from parsed JSON never a typed arg (the actual bug this closes), distinct exit codes per failure class documented in both the header and --help, correct stdio: ["inherit","pipe","pipe"] so --body-file - still works, and the README catalog entry lands in the same PR.

Recommendation

Add the missing gateCases["new-ticket.mjs"] entry before merge so the create→validate round trip, the defective-ticket path, and the orca-failure path all execute under CI. While there, confirm the real orca linear create --json shape and drop the unverified fallback branches (or justify them with a WHY). Everything else in the diff is clean.

PR #610 review, both findings.

[High] The wrapper's whole reason for existing - validating the issue orca
actually created - had no execution coverage. Adds gateCases with both orca
legs stubbed (linear create, and the nested linear issue that check-ticket
makes), driving all five outcomes: valid, defective, orca failure, success
with no identifier, and the usage error.

[Medium] Collapses the four guessed JSON envelope shapes to the two-level
unwrap check-ticket.mjs already uses, confirmed against the live envelope.

Also makes --project required. A project-less ticket is invisible to
/orchestrate, which is project-scoped by default, so it is created and then
never worked on - measured on ORB-86, which /bug created with no project.
/bug now routes the ticket into a scope project, with Backlog as the default
home, and creates through this wrapper instead of the create-then-validate
pair that put a guessed identifier in the loop.
@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #610

Scope: PR #610 in thomasluizon/orbit-ui-mobile (chore(tools): validate a created ticket against the identifier orca reported)
Recommendation: APPROVE

Summary

A pure tools/ change: tools/new-ticket.mjs wraps orca linear create and immediately
re-validates the issue it just created using the identifier orca itself reported (parsed
from --json output), closing a real measured failure mode where a hand-typed identifier let
a defective ticket (ORB-87) read as verified against the wrong, unrelated issue (ORB-85).
.claude/skills/bug/SKILL.md and tools/README.md are updated to point at the new wrapper,
and tools/test-tools.mjs gets full coverage of the wrapper's decision paths (success,
defective-but-created, orca failure, success-with-no-identifier, missing --project). The
implementation is a thin, faithful pass-through (execFileSync with an argv array, no shell
interpolation) and every claimed exit code in the PR body matches the code exactly.

Findings

Critical

None.

High

None.

Medium

None.

Low / Info

[Info] Established repo pattern followed correctly
· dimension: 5 (no-workaround / root-cause)
· location: orbit-ui-mobile/tools/new-ticket.mjs:1-115
· issue: n/a — praise. The tool does not reimplement `orca linear create`; every flag is
  forwarded verbatim, and the JSON-envelope unwrap (`parsed.result ?? parsed`, then
  `.issue ?? result`) mirrors the same two-level unwrap already used in `check-ticket.mjs`,
  so the two tools stay consistent rather than drifting.
· risk: none.
· fix: none needed.
· reference: CLAUDE.md rule 1; tools/CONVENTIONS.md ("prefer delegating over reimplementing")

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing string / i18n json changed
contract-aligner N/A — no packages/shared/src/types/* / endpoints.ts / orbit-api change
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/* or landing-page UI file changed

Validation

Check Result
Lint PASS (CI: Lint job, SUCCESS on this PR's head)
Type check PASS (CI: Type Check job, SUCCESS)
Tests PASS (CI: Unit Tests job, SUCCESS)
Build (api) N/A — orbit-api not touched
Harness Execution (tools/test-tools.mjs + .claude/hooks/test-hooks.mjs) PASS (CI: Harness Execution job, SUCCESS)

Note: this review's session sandbox blocked local execution of node tools/test-tools.mjs
and gh pr checks; the above PASS verdicts are read from this exact PR head's own
statusCheckRollup via gh pr view --json statusCheckRollup (all 20+ non-review checks
SUCCESS, including Harness Execution, Lint, Type Check, Unit Tests, Cross-Platform Parity,
Contract Drift, Dash Ban, Copy Register, Skill and Agent Frontmatter, Suppressions Ratchet),
not independently re-run by this session. Stated per the verification protocol's honesty
clause rather than claimed as a local execution that did not happen.

Deferred — N/A dimensions & files not verdicted

  • Dimension 8 (DESIGN.md/AI-slop), 9 (parity), 10 (i18n), 11 (contract drift), 12
    (security, API side), 13 (backend hard rules), 14 (FEATURES.md parity): all N/A, the diff
    touches only .claude/skills/bug/SKILL.md, tools/README.md, tools/new-ticket.mjs,
    tools/test-tools.mjs — none of those dimensions' surfaces.
  • All 4 changed files (tools/new-ticket.mjs, tools/test-tools.mjs, tools/README.md,
    .claude/skills/bug/SKILL.md) got a verdict; nothing changed-and-unlooked-at.
  • Not verified: whether other skills that still call orca linear create directly
    (feature/SKILL.md, audit-tests/SKILL.md, audit-performance/SKILL.md,
    audit-security/SKILL.md, audit-code-quality/SKILL.md, prod-readiness/SKILL.md,
    _shared/audit-to-tickets.md) should also migrate to new-ticket.mjs. None of those files
    are in this diff, so per the rubric's "focus on changed code, not pre-existing issues"
    this is out of scope for this review, not a finding — worth a fast-follow ticket, but not
    a blocker here.

What's good

  • Root-causes a real, measured failure (dated, with the exact wrong-identifier pair) rather
    than adding a reminder or a checklist step — the fix makes the wrong behavior structurally
    impossible instead of relying on discipline.
  • Stays a thin wrapper per tools/CONVENTIONS.md: no reimplementation of orca linear create, every flag forwarded verbatim via execFileSync (array argv, not a shell string,
    so no injection surface).
  • Every claimed exit code (0/1/2/3) in the PR body is exactly what the code does, verified
    line-by-line against tools/new-ticket.mjs.
  • Full decision-path coverage lands in the same PR (tools/test-tools.mjs): the created+valid
    path, created+defective path, orca-failure path, success-with-no-identifier path, and the
    missing---project refusal — satisfying rubric dimension 15 and the CONVENTIONS.md gate
    contract in the same change, not as a follow-up.
  • .claude/skills/bug/SKILL.md and tools/README.md are updated in the same PR so the new
    tool is actually reachable from the workflow that needed it, not left orphaned.

Recommendation

Approve as-is. Optional, non-blocking fast-follow: point the other ticket-creating skills
(feature, the four audit-* skills, prod-readiness, _shared/audit-to-tickets.md) at
tools/new-ticket.mjs too, since they carry the same guessed-identifier risk this PR just
closed for /bug — worth its own ticket rather than scope creep here.

@thomasluizon
thomasluizon merged commit 879ee5e into main Jul 25, 2026
28 checks passed
@thomasluizon
thomasluizon deleted the chore/new-ticket-tool branch July 25, 2026 03:46
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