open-swe/.claude/workflows/domain-reorg-adoption.mjs
Adam Moussa 373d888426
docs: land reorg in ledger, fix path refs, ship plan artifacts (C7)
C7 of the domain-reorg adoption (build plan docs/upstream-sync/domain-reorg/
reorg-build-plan.md, step C7):

- CLAUDE.md / AGENTS.md: retarget architecture path references to the new
  layout — graph entrypoints via agent.graphs.* shims, the agent/api/ +
  agent/webhooks/*_routes.py FastAPI split (webapp.py now a shim),
  agent/review/ package, tests/<domain>/ test paths, and the new-graph/
  test conventions. README.md already pointed at docs/ (C1) — no change.
- triage.jsonl: flip 8356eb34 (#1726) to landed on branch
  refactor/domain-reorg-adoption; add re-triage notes to the 8 unblocked
  rows (#1732/#1761/#1744/#1748/#1742 clean, #1736/#1758/#1760 near-clean).
  triage.md regenerated via make triage-render.
- Ship the plan, scoping report, move-map artifacts, and the
  domain-reorg-adoption workflow so the exercise is reproducible.

Memory + Confluence handled out-of-band (not in this commit): project
memory updated with the new module layout; Confluence check found no IT
page documents the repo module map — no Confluence change required.
2026-07-17 15:01:45 -04:00

229 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const meta = {
name: 'domain-reorg-adoption',
description: 'Execute the gate-approved C1-C7 domain-reorg build plan: moves, webapp split, security review, PR — no merge',
whenToUse: 'Run once, after Adam gives the go for the reorg build. Requires clean dev; plan + scoping artifacts live in docs/upstream-sync/domain-reorg/.',
phases: [
{ title: 'Preflight', detail: 'clean tree, artifacts present, branch created', model: 'haiku' },
{ title: 'C1 docs/resources', detail: 'doc moves + prompt loader + wheel check', model: 'sonnet' },
{ title: 'C2 review package', detail: '9 module moves + 38 importer rewrites', model: 'sonnet' },
{ title: 'C3 shims+manifest', detail: '21 shims, ci_monitor, langgraph.json, bun pin', model: 'sonnet' },
{ title: 'C4 webapp split', detail: 'the critical auth-surface split', model: 'fable' },
{ title: 'C4 security review', detail: 'detector fan-out + proof-or-kill verify' },
{ title: 'C5 test moves', detail: 'tests/<domain>/ mechanical moves', model: 'sonnet' },
{ title: 'C6 UI moves', detail: 'ui/src/features/ moves + import rewrites', model: 'sonnet' },
{ title: 'C7 docs/ledger/memory', detail: 'docs, triage ledger, memory, Confluence check', model: 'opus' },
{ title: 'PR', detail: 'push branch, open PR, final report — NO merge' },
],
}
// ---------------------------------------------------------------------------
// Shared context strings
// ---------------------------------------------------------------------------
const REPO = '/Users/adammoussa/Documents/repositories/seahaven/open-swe'
const SCRATCH = '/Users/adammoussa/Documents/repositories/seahaven/open-swe/docs/upstream-sync/domain-reorg'
const BRANCH = 'refactor/domain-reorg-adoption'
const COMMON = `
You are executing one commit of a gate-approved build plan in ${REPO} (branch ${BRANCH}).
Plan: ${SCRATCH}/reorg-build-plan.md — READ IT FIRST, plus the section of ${SCRATCH}/reorg-scoping-report.md relevant to your commit.
Move-map artifacts: ${SCRATCH}/movemap-m50.txt, ${SCRATCH}/cross.json, ${SCRATCH}/forkonly.json.
HARD RULES (violations = abort and report, do not improvise):
- Fork file contents, upstream layout. Upstream content ONLY for the 21 structural "A" shims listed in the scoping report.
- NEVER adopt upstream content for agent/webhooks/{github,linear,slack}.py, tests/conftest.py, tests/e2e/harness.py.
- Use "git mv" for moves so rename tracking survives. One commit for your cascade class only; commit message follows the repo's conventional style (see recent git log), body references the plan step.
- No pushes from your stage. No force operations. No edits outside your commit's scope. Do not touch docs/upstream-sync/* unless your stage says so.
- Gate ladder before committing: ruff check && ruff format --check, then uv run pytest --co -q, then the stage-specific gates. If a gate fails, fix within scope; if you cannot, ABORT: git reset --hard to the pre-stage SHA you recorded at start, and report the failure.
Return JSON: {ok, commit_sha, gates: {name: pass|fail|skipped}, notes, aborted_reason}`
const RESULT_SCHEMA = {
type: 'object',
properties: {
ok: { type: 'boolean' },
commit_sha: { type: 'string' },
gates: { type: 'object' },
notes: { type: 'string' },
aborted_reason: { type: 'string' },
},
required: ['ok', 'notes'],
}
const results = { commits: [], securityReview: null, pr: null }
function ensure(stage, r) {
if (!r || !r.ok) {
throw new Error(`${stage} failed: ${r ? r.aborted_reason || r.notes : 'agent returned null'}`)
}
results.commits.push({ stage, sha: r.commit_sha, gates: r.gates, notes: r.notes })
return r
}
// ---------------------------------------------------------------------------
// Preflight — cheap checks, haiku
// ---------------------------------------------------------------------------
phase('Preflight')
const pre = await agent(
`${COMMON}
STAGE: Preflight (no commit).
1. Verify ${REPO} is on dev and dev == origin/dev (fetch first is allowed here). Tree must be clean EXCEPT untracked files under docs/upstream-sync/domain-reorg/ and .claude/workflows/, which are expected (they are committed in C7).
2. Verify the plan file, scoping report, and all three move-map artifacts exist and are non-empty.
3. Verify branch ${BRANCH} does not already exist locally or on origin.
4. Create ${BRANCH} off dev. Record the base SHA.
Return JSON with ok, notes, and base_sha in notes.`,
{ label: 'preflight', phase: 'Preflight', model: 'haiku', effort: 'low', schema: RESULT_SCHEMA },
)
if (!pre || !pre.ok) throw new Error(`Preflight failed: ${pre ? pre.notes : 'null'}`)
log('Preflight OK — branch created')
// ---------------------------------------------------------------------------
// C1–C3: independent-of-C4 stages, sequential commits on the shared branch
// ---------------------------------------------------------------------------
phase('C1 docs/resources')
ensure('C1', await agent(
`${COMMON}
STAGE: C1 — docs/resources/assets (plan section "C1").
Moves: INSTALLATION.md and CUSTOMIZATION.md -> docs/, static/ -> assets/ (fix README refs), default_prompt.md -> agent/resources/ (+ __init__.py). Apply the importlib.resources loader hunk to the fork's prompt.py exactly as described in scoping §2b (upstream's hunk pattern, fork's file).
Extra gate: build the wheel (uv build or python -m build) and verify agent/resources/default_prompt.md is inside it; if missing, add the explicit hatchling include and note it.`,
{ label: 'C1', phase: 'C1 docs/resources', model: 'sonnet', schema: RESULT_SCHEMA },
))
phase('C2 review package')
ensure('C2', await agent(
`${COMMON}
STAGE: C2 — agent/review/ package (plan section "C2", scoping §2a/2b reviewer rows).
git mv the 9 reviewer/style modules per the move-map (reviewer_findings.py -> agent/review/findings.py etc.), replicate upstream's import-rewire pattern on FORK content for the R<100 ones, rewrite all 38 importer files (grep for reviewer_ and review_style_ imports to find them — do not trust the count blindly), create agent/review/__init__.py exporting the fork's existing public surface.
Extra gate: full reviewer/findings unit suites pass (uv run pytest tests/ -k "review or finding" plus any suite the moved modules own).`,
{ label: 'C2', phase: 'C2 review package', model: 'sonnet', schema: RESULT_SCHEMA },
))
phase('C3 shims+manifest')
ensure('C3', await agent(
`${COMMON}
STAGE: C3 — graphs/runtime/providers shims + langgraph.json + CI hardening (plan section "C3").
Adopt the 21 upstream "A" shim files verbatim-then-verify: for each, confirm every import it makes resolves against FORK modules (they delegate to agent.server etc.); fix delegation targets if fork names differ. Add fork-only agent/graphs/ci_monitor.py shim following the same pattern. Retarget langgraph.json graph entrypoints to agent.graphs.* (http.app stays agent.webapp:app). Pin bun-version in .github/workflows/ci.yml to the version in ui/ (check .bun-version / package.json engines; else current stable — note which).
Extra gate: timeout-bounded "make dev" boot check — all six graphs (agent, reviewer, analyzer, chat, scheduler, ci_monitor) register and the FastAPI app mounts; kill it after verifying startup logs.`,
{ label: 'C3', phase: 'C3 shims+manifest', model: 'sonnet', schema: RESULT_SCHEMA },
))
// ---------------------------------------------------------------------------
// C4 — the critical commit. Strongest model, high effort.
// ---------------------------------------------------------------------------
phase('C4 webapp split')
ensure('C4', await agent(
`${COMMON}
STAGE: C4 — FastAPI split (plan section "C4" + approved decisions 1-2). THE critical auth-surface commit.
Split the fork's 2,590-line agent/webapp.py into: agent/webhooks/common.py (shared verify/dispatch helpers), agent/api/app.py (composition), agent/api/health.py (/health + /webhooks/run-complete), and per-source github_routes.py / linear_routes.py / slack_routes.py / jira_routes.py / confluence_routes.py. Atlassian Connect lifecycle + descriptor routes (/connect/*) go INTO confluence_routes.py (approved decision 2). webapp.py becomes the compatibility shim re-exporting app (mirror upstream's shim shape).
Preserve EXACTLY: all signature-verification logic (GitHub HMAC, Slack, Linear, verify_jira_secret + optional HMAC/timestamp, Connect JWT/qsh), token-attribution gating, TID-COLLIDE-01 binding, the renamed _is_repo_auto_review_enabled gates, ci-autofix trigger wiring. Handlers keep module-attribute access style (common.X / service.X).
Retarget the ~240 monkeypatch.setattr(webapp, ...) sites across the 11 test files + tests/conftest.py + tests/e2e/harness.py per upstream's retarget pattern (webhook_common / handler modules) — find every site by grep, not by count.
Extra gates before committing: full unit suite; FULL E2E (Playwright + real langgraph dev server); residual-importer sweep — git grep for "agent.webapp"/"from agent import webapp" must return only the shim and intentional compat references (list what remains in notes).`,
{ label: 'C4', phase: 'C4 webapp split', model: 'fable', effort: 'high', schema: RESULT_SCHEMA },
))
log('C4 committed — entering security review (hard gate before C5)')
// ---------------------------------------------------------------------------
// C4 security review — detector fan-out (sonnet lenses) + proof-or-kill verifier (opus),
// with a bounded fix loop (fable) per the approved failure path (new commits, no rewrites).
// ---------------------------------------------------------------------------
phase('C4 security review')
const LENSES = [
{ key: 'authz', focus: 'broken object-level auth, missing access checks, webhook signature verification gaps (GitHub HMAC / Slack / Linear / Jira shared-secret+HMAC / Connect JWT+qsh), token-attribution gating, TID-COLLIDE-01 thread binding, the _is_repo_auto_review_enabled gates' },
{ key: 'injection', focus: 'untrusted webhook payload handling: parsing, header trust, SSRF-prone fetches, path handling in the moved dispatch code' },
{ key: 'logic', focus: 'multi-step invariants broken by the split: dispatch ordering, dedup/replay protection, race conditions across the new module boundaries, dropped code paths (diff every moved function against its pre-split source)' },
]
const FINDINGS_SCHEMA = {
type: 'object',
properties: { findings: { type: 'array', items: { type: 'object' } } },
required: ['findings'],
}
const VERDICT_SCHEMA = {
type: 'object',
properties: { confirmed: { type: 'array', items: { type: 'object' } }, unverified: { type: 'array', items: { type: 'object' } } },
required: ['confirmed', 'unverified'],
}
let reviewRound = 0
let confirmedHighs = []
while (true) {
reviewRound += 1
const c4diff = `the C4 split commit(s) on ${BRANCH} in ${REPO} (git show for each C4/C4a commit; compare moved code against pre-split agent/webapp.py at the merge base)`
const found = (await parallel(LENSES.map(l => () =>
agent(
`You are a hostile ${l.key} security auditor. Audit ONLY ${l.key} issues in ${c4diff}. Focus: ${l.focus}. Read the actual files at both revisions; for each checklist area either cite the specific safe line or file a finding {id,title,claimed_severity,file,line,data_flow,proof:{input,outcome},recommendation}. Findings must be about the SPLIT (regressions vs pre-split behavior), not pre-existing issues. Return {findings:[...]}.`,
{ label: `detect:${l.key}:r${reviewRound}`, phase: 'C4 security review', model: 'sonnet', schema: FINDINGS_SCHEMA },
)))).filter(Boolean).flatMap(r => r.findings)
if (!found.length) { results.securityReview = { rounds: reviewRound, confirmed: [], outcome: 'clean' }; break }
const verdict = await agent(
`You are a skeptical exploitation verifier — you did NOT find these and are rewarded for killing weak claims. For each candidate finding on ${c4diff}, demand a concrete proof-of-exploit (specific input -> specific bad outcome, consistent with the code at HEAD of ${BRANCH}). Default to unverified when uncertain. Candidates: ${JSON.stringify(found)}. Return {confirmed:[...], unverified:[...]} keeping severity only on confirmed items.`,
{ label: `verify:r${reviewRound}`, phase: 'C4 security review', model: 'opus', effort: 'high', schema: VERDICT_SCHEMA },
)
confirmedHighs = (verdict?.confirmed || []).filter(f => ['critical', 'high'].includes((f.claimed_severity || f.severity || '').toLowerCase()))
if (!confirmedHighs.length) {
results.securityReview = { rounds: reviewRound, confirmed: verdict?.confirmed || [], unverified: verdict?.unverified || [], outcome: 'pass' }
break
}
if (reviewRound >= 3) {
results.securityReview = { rounds: reviewRound, confirmed: confirmedHighs, outcome: 'BLOCKED' }
throw new Error(`Security review still has ${confirmedHighs.length} confirmed critical/high after ${reviewRound} rounds — plan failure path: fundamental-flaw handling, return to Adam. Branch left intact for inspection.`)
}
log(`Security round ${reviewRound}: ${confirmedHighs.length} confirmed critical/high — dispatching fix commit C4a`)
ensure(`C4a-r${reviewRound}`, await agent(
`${COMMON}
STAGE: C4a — address confirmed security-review findings as a NEW commit on ${BRANCH} (plan failure path: no history rewrite, no force-push). Findings with proofs: ${JSON.stringify(confirmedHighs)}. Fix each within the split's scope; re-run full unit + E2E before committing.`,
{ label: `C4a:r${reviewRound}`, phase: 'C4 security review', model: 'fable', effort: 'high', schema: RESULT_SCHEMA },
))
}
log(`Security review outcome: ${results.securityReview.outcome} after ${results.securityReview.rounds} round(s)`)
// ---------------------------------------------------------------------------
// HARD GATE passed — C5 and C6 (sequential commits; C6 depends only on branch order, not C5 content)
// ---------------------------------------------------------------------------
phase('C5 test moves')
ensure('C5', await agent(
`${COMMON}
STAGE: C5 — tests/<domain>/ moves (plan section "C5", scoping §2a + §2c placements).
git mv every test per the move-map; apply remaining R<100 monkeypatch retargets not already done in C4; place the 13 fork-only tests per the approved table (webhooks/, auth/, tools/, sandbox/, github/). Path-only commit — zero logic edits.
Extra gate: uv run pytest --co -q collects everything (no import errors, count matches pre-move) then full unit green.`,
{ label: 'C5', phase: 'C5 test moves', model: 'sonnet', effort: 'low', schema: RESULT_SCHEMA },
))
phase('C6 UI moves')
ensure('C6', await agent(
`${COMMON}
STAGE: C6 — ui/src/features/ moves (plan section "C6").
git mv per the move-map including ported/ -> features/agents/experiments|chat; rewrite @/components/agents and @/lib/agents imports across the 54 importer files AND the moved files themselves (grep-driven); swap the tsconfig/eslint exclude lists for the single experiments glob; retarget the AgentPromptBar shim; delete ui/pnpm-workspace.yaml. Fork-diverged files (PlanReview.tsx, WorkflowApprovalCard.tsx, AgentsSidebar.tsx, SidebarFilterMenu.tsx, AgentGitPanel.tsx, AutomationEditor.tsx, DiffView.tsx, CloudPromptBar.tsx) keep fork content — imports only.
Extra gate: cd ui && bun install && bunx tsc --noEmit && bun run build, then Playwright E2E.`,
{ label: 'C6', phase: 'C6 UI moves', model: 'sonnet', schema: RESULT_SCHEMA },
))
phase('C7 docs/ledger/memory')
ensure('C7', await agent(
`${COMMON}
STAGE: C7 — docs + ledger + memory (plan section "C7" — read its obligations verbatim; you own them).
1. Update fork CLAUDE.md / README.md / AGENTS.md path references (architecture sections naming agent/webapp.py, old test paths, ui paths).
2. Ledger: flip 8356eb34 to landed in docs/upstream-sync/triage.jsonl with branch ${BRANCH}; add re-triage notes to the 8 unblocked rows per scoping §5; make triage-render; make triage-check must pass.
3. Memory: update /Users/adammoussa/.claude/projects/-Users-adammoussa-Documents-repositories-seahaven-open-swe/memory/open-swe-upstream-triage-status.md (+ MEMORY.md hook if needed) with the new module layout summary (agent/{graphs,runtime,api,review,resources,webhooks/*_routes}, tests/<domain>/, ui/src/features/) and the reorg-landed status. Memory edits are NOT part of the git commit.
4. Confluence check: via ToolSearch load the Atlassian MCP search tools; search the IT space for any page documenting this repo's module map. If found, update it; if none, or if Confluence is unreachable, record the exact outcome string ("updated page <id>" / "no Confluence change required" / "OUTSTANDING as of <today> — Confluence inaccessible") in your notes AND in the memory file.
5. Stage docs/upstream-sync/domain-reorg/ and .claude/workflows/domain-reorg-adoption.mjs so the plan, scoping artifacts, and this workflow ship with the exercise.
Commit all repo-file changes as the C7 commit; run ruff + triage-check as gates.`,
{ label: 'C7', phase: 'C7 docs/ledger/memory', model: 'opus', schema: RESULT_SCHEMA },
))
// ---------------------------------------------------------------------------
// PR — push and open, NO merge. Merge is Adam's (after @openswe review).
// ---------------------------------------------------------------------------
phase('PR')
results.pr = await agent(
`${COMMON}
STAGE: PR (no commit). Push ${BRANCH} to origin (plain push — the pre-push hook runs security scanners; if it blocks, report, never bypass). Open a PR against dev with gh pr create --base dev. Title: "refactor: adopt upstream domain reorg (8356eb34) — fork content, upstream layout". Body MUST include: the commit-by-commit summary from this build (paste from your reading of git log), explicit review-scope callouts for C2 (reviewer-module moves) and C4 (auth-surface split), the security-review outcome (${JSON.stringify(results.securityReview?.outcome)}), the C7 Confluence-check outcome, the post-deploy verification checklist from the plan (as unchecked boxes), and "MERGE-COMMIT ONLY — do not squash/rebase". Do NOT merge, do NOT approve, do NOT comment @openswe — Adam drives the review.
Return JSON: {ok, notes} with the PR URL in notes.`,
{ label: 'open-pr', phase: 'PR', model: 'sonnet', schema: RESULT_SCHEMA },
)
return {
branch: BRANCH,
commits: results.commits,
securityReview: results.securityReview,
pr: results.pr?.notes || 'PR step failed — branch is pushed or local; open manually',
next: 'Adam: review PR (C2+C4 scope), run @openswe review, merge with a MERGE COMMIT, then run the post-deploy checklist and post results to the PR.',
}