Repository navigation
Conversation
…ojection Implement the stack-project orchestrator-only tool per the design in issue #24. The tool publishes a stacked-PR ladder over an already-integrated LOOM epic without modifying any existing protocol surface. This is the winner of the gh-stack RFP round (bitswell/bitswell#74, Team 1 — read-only presentation projection). ## Files in scope (only these may be modified) - src/tools/stack-project.ts (NEW) - src/tools/index.ts (registration only) - tests/tools/stack-project.test.ts (NEW) Any commit touching files outside scope will be rejected at integration. ## Tool shape Mirror the structure of src/tools/pr-create.ts exactly: ```ts import { z } from 'zod'; import type { Tool } from '../types/tool.js'; import { ok, err } from '../types/result.js'; import { exec } from '../util/exec.js'; const StackProjectInput = z.object({ epic: z.string().describe('Epic slug; used for stack/<epic>/... namespace'), order: z.array(z.object({ agent: z.string(), slug: z.string(), branch: z.string().describe('Source loom/<agent>-<slug> branch'), })).describe('DAG-sorted layer order, bottom (closest to trunk) first'), base: z.string().default('main').describe('Trunk branch'), draft: z.boolean().default(true).describe('Publish stack PRs as drafts'), reproject: z.boolean().default(false).describe('Tear down existing stack first via gh stack unstack'), }); const StackProjectOutput = z.object({ mirrorBranches: z.array(z.string()), prUrls: z.array(z.string()), }); ``` `roles: ['orchestrator']` — matches pr-create.ts precedent. ## Cherry-pick mirror construction (LOAD-BEARING) Drift's review of the original Team 1 proposal flagged a real flaw: force-branching mirrors to workspace merge commits leaks prior history into per-layer diffs whenever the workspace is not a fresh clone of main at epic start. The fix is to build mirrors via cherry-pick from each loom/* source branch directly. Algorithm: ``` prev = base for each layer (NN starting at 01): mirror = "stack/<epic>/<NN>-<agent>-<slug>" commits = git rev-list --reverse base..layer.branch git branch -f mirror prev git checkout mirror git cherry-pick <commits> prev = mirror ``` Use NN as zero-padded 2-digit index. After all mirrors are built, run 'gh stack init --base <base> --adopt <mirror1> <mirror2> ...' followed by 'gh stack submit --auto' (with --draft if input.draft is true). If reproject is true, run 'gh stack unstack' first to tear down any existing stack on these branches. If any cherry-pick fails, abort with err('mirror-cherry-pick-failed', ...). If gh stack init fails, abort with err('stack-init-failed', ...). Bail before publishing on any error — leave no half-published stack. ## Tests tests/tools/stack-project.test.ts — follow the vitest mock-exec pattern in tests/tools/pr-create.test.ts. Cover at minimum: 1. Happy path: 3 layers, all execs succeed, returns the mirror branch list and PR URLs 2. reproject=true invokes 'gh stack unstack' as the first exec call 3. Cherry-pick failure returns err('mirror-cherry-pick-failed', ...) and never calls 'gh stack init' 4. 'gh stack init' failure returns err('stack-init-failed', ...) and never calls 'gh stack submit' 5. draft=false omits --draft from the submit command Use mockExec.mockResolvedValueOnce per call. Mirror the test ergonomics of pr-create.test.ts including the makeCtx helper. ## Registration Add one import + one export + one register call in src/tools/index.ts, matching the existing patterns. No reorganization of the file. No new phase comment. ## Acceptance criteria - All three scope files exist with the shapes above - 'npm test' passes - 'npm run lint' (tsc --noEmit) passes - Zero edits to any file outside scope - Final commit has Task-Status: COMPLETED, Files-Changed: 3, at least two Key-Finding trailers (one for the cherry-pick approach, one for the test coverage) ## References - gh-stack SKILL: /home/willem/.agents/skills/gh-stack/SKILL.md (lines 419-436 for 'init --adopt'; line 789 for strict-linearity limitation) - src/tools/pr-create.ts and tests/tools/pr-create.test.ts as templates - Issue: #24 - RFP epic: bitswell/bitswell#74 - Winning Team 1 proposal: docs/rfcs/gh-stack-integration/team-1/proposal.md on the loom/team-1-rfp-gh-stack branch in bitswell/bitswell Agent-Id: bitswell Session-Id: 51135fdb-2056-42f0-b7b4-967d49dc5b07 Task-Status: ASSIGNED Assigned-To: ratchet Assignment: stack-project Scope: src/tools/stack-project.ts,src/tools/index.ts,tests/tools/stack-project.test.ts Dependencies: none Budget: 80000 Issue: #24
Implements the stack-project tool per issue #24. Picked from the gh-stack RFP round (bitswell/bitswell#74, Team 1 — read-only presentation projection). The tool publishes a stacked-PR ladder over an integrated LOOM epic without touching any existing protocol surface. Mirror branches are built by walking 'git rev-list --reverse base..layer.branch' and cherry-picking onto the previous mirror tip — this avoids the workspace-history leak Drift flagged in the original Team 1 proposal (force-branching to workspace merge commits would have leaked prior history into per-layer diffs whenever the workspace was not a fresh clone of main at epic start). Schema deviation: spec used z.string().default('main') etc. but the existing Tool<I,O> typing makes Zod .default() incompatible with inputSchema typing. Switched to .optional() with handler-side defaults (input.base ?? 'main', etc.). Runtime behavior identical to spec. Tests cover all 5 mandated scenarios (happy path, reproject=true, cherry-pick failure bail, init failure bail, draft=false) plus an orchestrator role-scope assertion. 6/6 pass. Closes #24 Agent-Id: ratchet Session-Id: f3aef7c2-66f4-45c3-8156-7d5f2a6c6e71 Task-Status: IMPLEMENTING Heartbeat: 2026-04-15T04:05:13Z
stack-project tool is complete. All in-scope files committed in the preceding commit. Ready for orchestrator integration. Agent-Id: ratchet Session-Id: f3aef7c2-66f4-45c3-8156-7d5f2a6c6e71 Task-Status: COMPLETED Files-Changed: 3 Key-Finding: Mirror branches are built by walking 'git rev-list --reverse base..layer.branch' and cherry-picking onto the previous mirror tip, never force-branching to workspace merge commits — this prevents the workspace-history leak Drift flagged in the Team 1 proposal review Key-Finding: Test coverage uses vitest mock-exec ergonomics from pr-create.test.ts and exercises all 5 scenarios from the spec plus an orchestrator role-scope assertion, 6/6 pass Heartbeat: 2026-04-15T04:05:13Z
… tool tests/runtime/mcp-server.test.ts hardcodes the registered tool count (24 -> 25). Adding any new tool requires this fixture update; it is out of scope for the worker (ratchet) since it is downstream of registration. Orchestrator post-terminal commit per schemas.md §5.7. Agent-Id: bitswell Session-Id: 51135fdb-2056-42f0-b7b4-967d49dc5b07
Follow-up to e21e571 — the mcp-server tool list test also hardcodes the full sorted tool-name array; adding stack-project there too. Orchestrator post-terminal commit. Agent-Id: bitswell Session-Id: 51135fdb-2056-42f0-b7b4-967d49dc5b07
3 of 4 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
stack-projectorchestrator-only tool that publishes a read-only stacked-PR projection of an integrated LOOM epic via gh-stack.git rev-list --reverse base..layer.branchand cherry-picking onto the previous mirror tip — never force-branching to workspace merge commits — which avoids the workspace-history leak Drift flagged in the original Team 1 proposal review.Closes #24. Picked from the gh-stack RFP round — Team 1 "read-only presentation projection" was the winning angle.
What changed
src/tools/stack-project.tstests/tools/stack-project.test.tssrc/tools/index.tstests/runtime/mcp-server.test.tsMirror construction algorithm
After all mirrors are built:
gh stack init --base <base> --adopt <mirrors...>followed bygh stack submit --auto(with--draftifinput.draft). Ifreproject: true,gh stack unstackruns first. Any error before publish bails with a typederr()— no half-published stack.Schema
roles: ['orchestrator']— matchespr-create.tsprecedent.Deviation from the issue spec: spec used
z.string().default('main')etc.; switched to.optional()with handler-side defaults (input.base ?? 'main') because Zod.default()conflicts with the existingTool<I,O>typing. Runtime behavior is identical.Test coverage
tests/tools/stack-project.test.ts— 6/6 pass:gh stack init --adopt+gh stack submit --auto --draftinvoked with correct argsreproject: trueinvokesgh stack unstackas the first exec callerr('mirror-cherry-pick-failed', ...)and never callsgh stack initgh stack initfailure returnserr('stack-init-failed', ...)and never callsgh stack submitdraft: falseomits--draftfrom the submit commandrolesincludes exactlyorchestrator(role-gating assertion)Test plan
npm test— 403/403 passingnpm run lint(=tsc --noEmit) — zero errorsstack-project— this PR is loom-tools only🤖 Generated with Claude Code