{
"meta": {
"agent": "planner",
"task_id": "275",
"title": "Compound phase merged-PR guard and docs-only validation",
"created_at": "2026-04-03T12:00:00Z"
},
"inputs": [
{
"name": "issue-275",
"type": "context",
"ref": "https://github.com/AgentSDE/agent-core/issues/275",
"notes": "Compound phase fires before PR merged, includes src/ files"
},
{
"name": "phase-router",
"type": "file",
"ref": "src/phase-router/phase-router.service.ts",
"notes": "Main routing logic, executePhase method"
},
{
"name": "internal-adapter",
"type": "file",
"ref": "src/internal-adapter/internal-adapter.service.ts",
"notes": "Phase advancement logic, advanceAndEnqueue"
},
{
"name": "github-service",
"type": "file",
"ref": "src/github/github.service.ts",
"notes": "GitHub API client, no isPrMerged yet"
},
{
"name": "compound-skill",
"type": "file",
"ref": ".claude/skills/compound-learnings/SKILL.md",
"notes": "Compound skill definition, docs-only constraint"
}
],
"outputs": [
{
"name": "plan",
"type": "spec",
"format": "md",
"content": "plan.md"
}
],
"files": [
{
"path": "src/github/github.service.ts",
"action": "modify",
"reason": "Add isPrMerged method"
},
{
"path": "src/github/github.service.spec.ts",
"action": "modify",
"reason": "Test isPrMerged"
},
{
"path": "src/phase-router/phase-router.service.ts",
"action": "modify",
"reason": "Add pre-compound merged-PR guard and post-compound diff validation"
},
{
"path": "src/phase-router/phase-router.service.spec.ts",
"action": "modify",
"reason": "Test compound guards"
},
{
"path": "src/internal-adapter/internal-adapter.service.ts",
"action": "modify",
"reason": "Add compound advancement guard"
}
],
"steps": [
{
"id": "S1",
"summary": "Add isPrMerged method to GitHubService and write tests",
"acceptance": [
"GitHubService.isPrMerged(owner, repo, prNumber) returns true when PR is merged",
"Returns false when PR is open or not found (404)",
"Unit tests cover merged, open, and 404 cases"
],
"depends_on": []
},
{
"id": "S2",
"summary": "Add pre-compound merged-PR guard in PhaseRouterService",
"acceptance": [
"Compound phase blocked with BLOCKED:PERSISTENT when task.prNumber is null",
"Compound phase blocked with BLOCKED:TRANSIENT when PR is not yet merged",
"Compound phase proceeds normally when PR is merged",
"Unit tests cover null prNumber, open PR, and merged PR scenarios"
],
"depends_on": [
"S1"
]
},
{
"id": "S3",
"summary": "Add post-compound diff validation and retry logic in PhaseRouterService",
"acceptance": [
"After compound completes, diff is checked for src/ or test/ files",
"First violation retries compound with docs-only constraint env var",
"Second violation signals BLOCKED:PERSISTENT for manual review",
"Unit tests cover clean diff, first violation retry, and second violation block"
],
"depends_on": [
"S2"
]
},
{
"id": "S4",
"summary": "Add compound advancement guard in InternalAdapterService",
"acceptance": [
"advanceAndEnqueue blocks compound enqueue when task.prNumber is null",
"advanceAndEnqueue enqueues compound normally when prNumber is set",
"Unit test covers the null-prNumber guard"
],
"depends_on": [
"S1"
]
}
],
"risks": [
{
"risk": "Race between PR merge webhook and compound advancement",
"mitigation": "Runtime guard in executePhase is authoritative; advanceAndEnqueue guard is fail-fast"
},
{
"risk": "Compound has no worktree — diff validation needs git access",
"mitigation": "Use PROJECT_ROOT or artefacts repo path for diff check"
}
],
"assumptions": [
"GitHub API GET /pulls/{number} returns a merged field that is reliably set",
"Compound phase output is committed to a branch before the diff check runs"
],
"open_questions": []
}