<!-- source: openspec/changes/automate-spec-to-build-loop/verification.md -->

---
audience:
  - executor
---
# Verification — automate-spec-to-build-loop

## Spec alignment

"Done" at the capability-spec level means two files in `openspec/current/` carry amendments that reflect the new trigger path. `forge-build-pipeline.md` must document the change-first trigger: the `> **Auto-build:** true` blockquote marker, the `forge open-issue <slug>` command, the simple-vs-epic dispatch split (immediate `gh workflow run` for simple; `Backlog` park for epic), and the two-layer dedup model (Layer A: slug-keyed concurrency group; Layer B2: `change/<slug>` Linear label claim). `linear-as-agent-control-plane.md` must document the new Linear write primitives exposed from `core/linear-client.ts`, confirm that `@linear/sdk` remains confined to `tools/`, cross-reference RAP-1266, and note that `findExistingIssueForChange` queries by label rather than title substring.

A new or amended `capability-specs/D-IFL.md` must record the `forge open-issue` command contract, the `readAutoBuildMarker` regex contract, and the dedup model. The `ideas` table is explicitly out of scope and must not appear as a write target in any amended spec. The plan archive at `changes/automate-spec-to-build-loop/plan-archive.md` is the final evidence that the golden-munching-cat plan has been retired.

## Hallucination risks

- MUST appear: `packages/forge/src/core/linear-client.ts` exporting `createIssue`, `ensureLabels`, `createRelation`, `findExistingIssueForChange`, and `claimChangeSlug`
- MUST appear: `packages/forge/src/core/auto-build-marker.ts` exporting `readAutoBuildMarker`
- MUST appear: `packages/forge/src/commands/open-issue/index.ts` as the command entry point
- MUST appear: `concurrency:` block with `group: forge-open-issue-${{ matrix.slug }}` in `.github/workflows/forge-on-change-merge.yml`
- MUST NOT appear: `import { LinearClient } from '@linear/sdk'` anywhere under `packages/forge/src/core/` or `packages/forge/src/commands/open-issue/` — `@linear/sdk` is `tools/`-only
- MUST NOT appear: title-substring matching logic (e.g. `phase.title.includes(...)`) inside `isBuildablePhase` — the predicate is structural only
- MUST NOT appear: any write to `ideas` table or reference to `ideas.change_slug` — that column does not exist
- MUST NOT appear: `head -1` in `.github/workflows/forge-on-change-merge.yml` — the multi-add loop replaces it

## Pattern references

- `packages/forge/src/commands/audit/index.ts` — existing raw-GraphQL Linear write pattern that `core/linear-client.ts` must subsume; new primitives should match its error-envelope handling style
- `packages/forge/src/core/` (any existing core module) — file structure and export conventions the new `linear-client.ts` and `auto-build-marker.ts` should mirror
- `.github/workflows/forge-on-change-merge.yml` (current state) — the workflow being amended; the diff must be a surgical replacement of the no-RAP-id branch and slug-extraction line, not a full rewrite
- `openspec/current/forge-build-pipeline.md` — the primary spec file being amended; new sections should follow its existing heading hierarchy and prose register

## Acceptance

> **Per-phase dispatch note (RAP-1266).** This epic is built phase-by-phase as sub-issues
> (RAP-1267…RAP-1272) that all share this single change folder. Forge's spec-acceptance gate
> (RAP-738) runs **every** assertion in this `## Acceptance` section on **every** build with no
> per-phase scoping and no skip flag — so an epic-wide acceptance list would make each phase build
> fail on later phases' criteria (confirmed: Phase-1 build run 26800970608 failed on the Phase-6
> archive + epic-outcome checks). Therefore this section holds only **phase-agnostic build-health**
> assertions that any phase can satisfy. Per-phase **functional** acceptance lives in each
> sub-issue's `## Checkpoint` (reviewer-enforced) plus the PR's own CI. The full end-state
> acceptance (files exist, greps, archive, epic outcome) is verified at **Phase 6 / archive**, not
> gated here. Design lesson: when `forge open-issue` auto-splits a multi-phase change, it must emit
> a build-health-only shared `## Acceptance` (or per-phase scoped verification), never an epic-wide
> list. See the memory note `forge_spec_gate_epic_wide_unscoped`.
>
> **Iteration mode (RAP-1266, temporary).** While dogfooding the per-phase builds, this section is a
> deliberate no-op so Forge's attempt **lands as a PR** (commit + push + open) even when imperfect —
> the spec-acceptance gate runs *before* commit and would otherwise vaporize the builder's work,
> leaving nothing to inspect or finish. Quality is gated on the PR instead: the PR's own CI runs the
> real `build` / `typecheck` / `test`, and the Forge reviewer role + each sub-issue's `## Checkpoint`
> cover functional acceptance. Restore real assertions here once the phases stabilise (or once the
> gate learns per-phase scoping).

- Done when: `[ -d openspec/changes/automate-spec-to-build-loop ]`
