You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 2 — Hierarchy-aware interactions in GitHub Projects
Status: shipped and verified end-to-end against CodigoSinSiesta/1 (real data, real Orca dev build, real GitHub mutations). One correction to the RFC's assumption surfaced during the spike — see below.
Summary
Adds issue-level hierarchy navigation and writes to the work-item drawer's new "Sub-issues" section (GitHubItemDialog.tsx, issue pages only): shows the parent (if any), the direct sub-issues with a recursive roll-up progress summary, and lets the user add, remove, and reorder sub-issues without leaving Orca.
This is deliberately scoped to the drawer, not the table. Phase 1b's flat Sub-issues progress / Parent issue table columns (ProjectCell.tsx) are untouched — this phase adds navigation and writes on top of that read-only foundation. Nested/indented table rows are explicitly out of scope — that's Phase 3, gated on its own design review per the RFC.
RFC correction found during the spike
The RFC (docs/reference/2026-07-14-github-projects-hierarchy-traceability-design.md §8, Phase 2) assumed writes would go through REST (project-view/mutations.ts pattern), since its schema excerpt of Issue didn't show mutation fields.
A live spike found otherwise: addSubIssue, removeSubIssue, and reprioritizeSubIssueare GraphQL mutations, confirmed via schema introspection and live execution against a disposable test issue:
This is simpler than REST would have been: one round trip per mutation (plus a small ID-resolution query, since the mutations take GraphQL node IDs, not issue numbers), reusing the existing runGraphql plumbing instead of adding a second REST-based write path. The plan was updated in place rather than re-litigated — same redirect discipline as Phase 1b's field-value-union correction.
Also includes an unrelated-but-touched carryover from the Phase 1b session (ColumnResizeHandle.tsx, ProjectCell.tsx, ProjectRow.tsx, ProjectViewList.tsx, column-widths.ts, github-project-group-sort.ts) — already reported in issue #43; not re-described here.
New read path — getIssueHierarchy
Single on-demand GraphQL query (fired when the drawer opens, not baked into the Project table's paginated fetch) requesting the parent, this issue's subIssuesSummary, and 2 levels of subIssues (direct children + each child's own direct children), bounded by HIERARCHY_CHILDREN_PAGE_SIZE = 25 / HIERARCHY_GRANDCHILDREN_PAGE_SIZE = 10 to stay well under GitHub's 10s GraphQL timeout risk on wide/deep trees. Returns hasMoreChildren: boolean when either level's page didn't cover its totalCount, for a future "load more" affordance.
New pure aggregation utility — computeHierarchyRollup
src/shared/github-issue-hierarchy-rollup.ts — depth-agnostic recursive percentage calculator. GitHub's own subIssuesSummary only aggregates one level; this walks whatever depth was actually fetched and trusts the node's own subIssuesSummary as the terminal count for any branch that wasn't expanded further (so partial-depth trees still produce a correct total, not an undercount).
New write path — 3 GraphQL mutations, same-repo v1 scope
addSubIssueBySlug / removeSubIssueBySlug / reprioritizeSubIssueBySlug in project-view/hierarchy.ts. Each validates args (self-reference rejection, slug shape, conflicting before/after) before touching the network, then resolves the parent/child/sibling issue numbers to GraphQL node IDs via a single dynamically-aliased query (buildResolveIssueIdsQuery), then executes the mutation.
Cross-repo sub-issues (GitHub supports this since Sept 2025) are not in v1 scope — the args are same-repo only; documented as a fast-follow, not silently dropped.
SSH/remote-runtime wiring (AGENTS.md's SSH Use Case rule)
All 4 new methods (getIssueHierarchy, addSubIssue, removeSubIssue, reprioritizeSubIssue) are wired through the full existing remote-dispatch stack, not just the local Electron IPC path:
orca-runtime.ts — 4 thin proxy methods (getGitHubProjectIssueHierarchy, etc.), same shape as the 15+ existing BySlug proxies.
runtime/rpc/methods/github.ts — 4 new defineMethod entries with Zod param schemas.
runtime-rpc.ts — allow-listed under github.project.*.
web-preload-api.ts — routed for the browser/paired-web client (GITHUB_WEB_RPC_METHODS), with matching parity + routing tests.
SubIssuesSection.tsx — dispatches via getActiveRuntimeTarget/callRuntimeRpc when a remote environment is active, falling back to local window.api.gh.*, mirroring SlugDialogBody.tsx's established pattern.
UI — SubIssuesSection.tsx
Mounted in GitHubItemDialog.tsx, issue pages only (isIssuePage branch), right after the existing GHEditSection metadata block. Renders:
Parent row (clickable link to the parent issue), when present.
Roll-up progress bar + completed/total text, when there are sub-issues.
Each direct sub-issue as a clickable link + title, with (when editable) move-up / move-down / remove buttons.
An "Issue number" input + "Add sub-issue" button, when editable.
Renders nothing (<></>) when there's no parent, no sub-issues, and the drawer isn't editable — avoids cluttering every issue's drawer with an empty section.
Evidence
1. Live GraphQL mutation spike (2026-07-15, before writing any code)
Created a disposable issue, ran all 3 mutations against it live, confirmed each one's effect via a follow-up query, then reverted and closed the issue:
clean except the same pre-existing, unrelated Monaco editor type mismatch reported in issue #43
3. Live UI verification — real Orca app, real writes, real revert
Connected via CDP to a freshly-launched Orca dev build (same method as issue #43 — no auth wall since it's the agent's own process, distinct from the headless browser tool / computer-use, both of which hit real environment limits last time). Opened the drawer for issue #37 in CodigoSinSiesta/1.
Before (Phase 1b only — Phase 2's mount point temporarily removed via git stash push -- GitHubItemDialog.tsx, confirmed via HMR reload, then restored via git stash pop):
After (Phase 2, stable state — #37's real sub-issues #38/#39/#42, roll-up 0/5 — 3 direct children + 2 grandchildren via #38→#40 and #39→#41, matching computeHierarchyRollup's expected math exactly):
Live write-path proof — created disposable issue #45, typed 45 into the section's input, clicked "Add sub-issue": roll-up updated 0/5 → 0/6 and #45 appeared in the list, in the real running UI:
Verified against the live GitHub API (not just the UI) before and after each step:
after add: #37.subIssuesSummary.total: 3 → 4 ✓, #45.parent.number: 37 ✓
after remove: #37.subIssuesSummary.total: 4 → 3 ✓, #45.parent: null ✓
Then clicked the section's "Remove #45" button (confirmed via UI observation the row disappeared and reappeared correctly after the async refetch), and closed the disposable issue.
Known limitations
Same-repo sub-issues only in v1. GitHub supports cross-repo/cross-org sub-issues (since Sept 2025); the write-path args don't carry a second owner/repo yet. Documented fast-follow, not a silent gap.
Depth-limit (8 levels) is not pre-validated client-side. Only the children-count/self-reference guards are cheap enough to check without an extra read; a depth violation is relayed via GitHub's own error message through the existing classifyProjectError path rather than pre-checked. Documented in hierarchy.ts's validation section comment.
2-level fetch cap.getIssueHierarchy fetches the issue's direct children and each child's own direct children, not further. hasMoreChildren signals when either level's page didn't cover its full count; a "load more" UI affordance for deeper/wider trees is not built in this pass.
No nested/indented rows — this phase is entirely additive to the drawer; the Project table stays flat, per the RFC's Phase 2/Phase 3 split and the user's explicit "planifiquemos Phase 3 después" direction.
References
RFC: docs/reference/2026-07-14-github-projects-hierarchy-traceability-design.md (Phase 2 section updated with a shipped-status note and the REST→GraphQL correction)
Phase 2 — Hierarchy-aware interactions in GitHub Projects
Summary
Adds issue-level hierarchy navigation and writes to the work-item drawer's new "Sub-issues" section (
GitHubItemDialog.tsx, issue pages only): shows the parent (if any), the direct sub-issues with a recursive roll-up progress summary, and lets the user add, remove, and reorder sub-issues without leaving Orca.This is deliberately scoped to the drawer, not the table. Phase 1b's flat
Sub-issues progress/Parent issuetable columns (ProjectCell.tsx) are untouched — this phase adds navigation and writes on top of that read-only foundation. Nested/indented table rows are explicitly out of scope — that's Phase 3, gated on its own design review per the RFC.RFC correction found during the spike
The RFC (
docs/reference/2026-07-14-github-projects-hierarchy-traceability-design.md§8, Phase 2) assumed writes would go through REST (project-view/mutations.tspattern), since its schema excerpt ofIssuedidn't show mutation fields.A live spike found otherwise:
addSubIssue,removeSubIssue, andreprioritizeSubIssueare GraphQL mutations, confirmed via schema introspection and live execution against a disposable test issue:This is simpler than REST would have been: one round trip per mutation (plus a small ID-resolution query, since the mutations take GraphQL node IDs, not issue numbers), reusing the existing
runGraphqlplumbing instead of adding a second REST-based write path. The plan was updated in place rather than re-litigated — same redirect discipline as Phase 1b's field-value-union correction.What was built
Files changed
Also includes an unrelated-but-touched carryover from the Phase 1b session (
ColumnResizeHandle.tsx,ProjectCell.tsx,ProjectRow.tsx,ProjectViewList.tsx,column-widths.ts,github-project-group-sort.ts) — already reported in issue #43; not re-described here.New read path —
getIssueHierarchySingle on-demand GraphQL query (fired when the drawer opens, not baked into the Project table's paginated fetch) requesting the parent, this issue's
subIssuesSummary, and 2 levels ofsubIssues(direct children + each child's own direct children), bounded byHIERARCHY_CHILDREN_PAGE_SIZE = 25/HIERARCHY_GRANDCHILDREN_PAGE_SIZE = 10to stay well under GitHub's 10s GraphQL timeout risk on wide/deep trees. ReturnshasMoreChildren: booleanwhen either level's page didn't cover itstotalCount, for a future "load more" affordance.New pure aggregation utility —
computeHierarchyRollupsrc/shared/github-issue-hierarchy-rollup.ts— depth-agnostic recursive percentage calculator. GitHub's ownsubIssuesSummaryonly aggregates one level; this walks whatever depth was actually fetched and trusts the node's ownsubIssuesSummaryas the terminal count for any branch that wasn't expanded further (so partial-depth trees still produce a correct total, not an undercount).New write path — 3 GraphQL mutations, same-repo v1 scope
addSubIssueBySlug/removeSubIssueBySlug/reprioritizeSubIssueBySluginproject-view/hierarchy.ts. Each validates args (self-reference rejection, slug shape, conflicting before/after) before touching the network, then resolves the parent/child/sibling issue numbers to GraphQL node IDs via a single dynamically-aliased query (buildResolveIssueIdsQuery), then executes the mutation.Cross-repo sub-issues (GitHub supports this since Sept 2025) are not in v1 scope — the args are same-repo only; documented as a fast-follow, not silently dropped.
SSH/remote-runtime wiring (AGENTS.md's SSH Use Case rule)
All 4 new methods (
getIssueHierarchy,addSubIssue,removeSubIssue,reprioritizeSubIssue) are wired through the full existing remote-dispatch stack, not just the local Electron IPC path:orca-runtime.ts— 4 thin proxy methods (getGitHubProjectIssueHierarchy, etc.), same shape as the 15+ existing BySlug proxies.runtime/rpc/methods/github.ts— 4 newdefineMethodentries with Zod param schemas.runtime-rpc.ts— allow-listed undergithub.project.*.web-preload-api.ts— routed for the browser/paired-web client (GITHUB_WEB_RPC_METHODS), with matching parity + routing tests.SubIssuesSection.tsx— dispatches viagetActiveRuntimeTarget/callRuntimeRpcwhen a remote environment is active, falling back to localwindow.api.gh.*, mirroringSlugDialogBody.tsx's established pattern.UI —
SubIssuesSection.tsxMounted in
GitHubItemDialog.tsx, issue pages only (isIssuePagebranch), right after the existingGHEditSectionmetadata block. Renders:completed/totaltext, when there are sub-issues.<></>) when there's no parent, no sub-issues, and the drawer isn't editable — avoids cluttering every issue's drawer with an empty section.Evidence
1. Live GraphQL mutation spike (2026-07-15, before writing any code)
Created a disposable issue, ran all 3 mutations against it live, confirmed each one's effect via a follow-up query, then reverted and closed the issue:
2. Unit test results (all real runs, this session)
src/shared/github-issue-hierarchy-rollup.test.tssrc/main/github/project-view/hierarchy.test.tsghmocking needed)src/renderer/.../SubIssuesSection.test.tsxsrc/main/runtime/rpc/methods/github.test.ts+runtime-rpc.test.ts+web-preload-api.test.tssrc/main/github+src/main/runtime+src/shared+src/renderer/.../web+src/renderer/.../github-project+GitHubItemDialog.tsx+src/renderer/.../storepnpm tc:node/tc:clipnpm tc:web3. Live UI verification — real Orca app, real writes, real revert
Connected via CDP to a freshly-launched Orca dev build (same method as issue #43 — no auth wall since it's the agent's own process, distinct from the headless
browsertool /computer-use, both of which hit real environment limits last time). Opened the drawer for issue#37inCodigoSinSiesta/1.Before (Phase 1b only — Phase 2's mount point temporarily removed via
git stash push -- GitHubItemDialog.tsx, confirmed via HMR reload, then restored viagit stash pop):After (Phase 2, stable state —
#37's real sub-issues#38/#39/#42, roll-up0/5— 3 direct children + 2 grandchildren via#38→#40and#39→#41, matchingcomputeHierarchyRollup's expected math exactly):Live write-path proof — created disposable issue
#45, typed45into the section's input, clicked "Add sub-issue": roll-up updated0/5→0/6and#45appeared in the list, in the real running UI:Verified against the live GitHub API (not just the UI) before and after each step:
Then clicked the section's "Remove #45" button (confirmed via UI observation the row disappeared and reappeared correctly after the async refetch), and closed the disposable issue.
Known limitations
classifyProjectErrorpath rather than pre-checked. Documented inhierarchy.ts's validation section comment.getIssueHierarchyfetches the issue's direct children and each child's own direct children, not further.hasMoreChildrensignals when either level's page didn't cover its full count; a "load more" UI affordance for deeper/wider trees is not built in this pass.References
docs/reference/2026-07-14-github-projects-hierarchy-traceability-design.md(Phase 2 section updated with a shipped-status note and the REST→GraphQL correction)docs/reference/2026-07-15-github-projects-hierarchy-phase2-plan.md