chore(migration): merge v2/next → main at Phase 5 close (ADR-0009 substrate lands on main; operator-ratified 2026-07-26) #550
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!550
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "v2/next"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
v2/next → main merge (Phase 5 close, operator-ratified 2026-07-26)
Coordination tracker: #544
Substrate operation: land the ADR-0009 Go substrate onto main. Preserves existing bash substrate (scripts/release-prep.sh + scripts/release-decide.sh + scripts/draft-release.sh remain at their paths + rolling-PR machinery on main continues to work). Additive merge — Go substrate lands ALONGSIDE bash.
What lands
Phase 0a → Phase 5 substrate:
13 packages on internal/ (verdict + semver + conventionalcommits + config + events + fragments + changelog + register + gates + manifest + forgejo + bake + selfboot) + cmd/rt + full contract set.
8 milestones closed (Phase 0a + 0b + 1 + 2 + 3 + 4 + 5-substrate; Phase 5 completes with THIS merge landing #544).
20 PRs merged across the arc on v2/next.
v1.0.0-alpha.0 published at git.frankenbit.de/frankenbit/release-toolkit/releases/tag/v1.0.0-alpha.0.
Cross-references
Bosun-authored on operator ratification.
- §3.1 item 2: reclassify config_render_tag from verify-during-impl to confirmed-no-change-at-render. Surveyor verified config_render_tag "2.0.0-alpha.1" -> v2.0.0-alpha.1 (verbatim {version} substitution, suffix-transparent today); the residual belongs to items 1+3 feeding it a suffix-bearing version, not to the render primitive. - §4 nit: paths-ignore ref release.yml:31 -> :35 (:31 was a comment line; the directive is at :35, verified). Refs #367Doc 2 of the architecture-doc arc. Turns ADR-0008's workflow-YAML-as-API-boundary finding from prose into testable contracts: - config.schema.json — JSON Schema for release-toolkit.yml (grounded in config.sh v0.1 schema: release_type enum required, version_files basename-constrained, tag_format/{version}-only, publish_mode/section_format/post_bump_hooks, defaults) - manifest.schema.json — JSON Schema for .release-toolkit-manifest.json (schema:1 + last_released_{sha,version,tag,at}, grounded in reusable-release.yml:425-433) - workflow-api.md — typed spec of the reusable-workflow uses:@<ref> input/output surface + the secrets:inherit / RELEASE_TOOLKIT_TOKEN platform-quirk contract - README.md — framing: contracts (not docs) enable cross-substrate equivalence, additive-only evolution, schema-first for new features Both schemas validated well-formed. Contracts bind SHAPE; behavior (resolution layers, decision logic) is Arc42 §6 Runtime View, explicitly non-scope here. Refs #367Adds ADR-0009 executing ADR-0008's 'go' fork under Framework B (operator ratification 2026-07-24): - Ratifies Go substrate as v1.0.0 (not v2.0.0); collapses branch-strategy.md's two-line model into single line - Adds contract-driven, TDD-per-phase workflow (§3.1) - Splits Phase 0 into 0a (scaffold + harness) and 0b (contracts + interfaces) - Contract inventory C1–C7 with current-state markers (§3.2) - Substrate discipline continuity catalog (§5) — bash patterns banked in the 2026-07-13 → 2026-07-24 arc mapped to idiomatic Go equivalents - #499 fail-atomic transactional cut as first-class internal/release design, not translation follow-up (§6). Sentinel-error fail-loud surface with structured error classes. - Tracker disposition table under Framework B (§8) - 5 explicit open sub-forks named for future ratification (§9) Depends on ADR-0008 (analysis + phase table + Phase-5 spike outcome). Supersedes ADR-0001 at v1.0.0 cut. Supersedes docs/architecture/branch-strategy.md at v1.0.0 cut. Refs #367 #476 #477 #498 #499 #336 #337 #338 #480.Folds all 7 findings from PR#500 review (Surveyor id 2418): S1 (phase-number collision §3.3 vs §3.5) — §3.5 now explicitly names ADR-0008's Phase 8 rather than colliding with this ADR's §3.3 Phase 8 (prerelease-cutting). Adds inline note preventing future readers from tripping the same ambiguity. S4 (rollback across irreversible boundary) — §6 rewritten to distinguish the reversible prefix (a) CHANGELOG seal + (b) manifest update from the irreversible suffix (c) tag creation + (d) release publish. Prefix rolls back; suffix uses idempotent replay. Matches #499 remedy-3 (fail-atomic step ordering). S2 (§5 missing locale/subprocess-output pattern) — added row for LC_ALL=C + parse-structural-not-rendered discipline. Live in the Go substrate because ADR-0008 R2 lands on git-subprocess as the floor. S3 (§9 criterion inconsistency) — states inclusion criterion explicitly (strategic decisions only the operator can make). Drops former #5 (composite-action shape → prototyping in Phase 0a, not operator ratification). Names what's NOT included and why (implementation shape resolved in phase work; author-register calls resolved in §8 dispositions). Renumbered to 3 items from 5. N1 (§1 citation drift) — 'shell tools any admin can patch' quote is in #367's language-choice section, not ADR-0008 §2. Fixed citation. N2 (§5 rows 3+8 near-duplicate labels) — row 3 relabeled 'Precondition check inside the irreversible-action method'; row 8 relabeled 'Two-phase-commit ordering'. Distinguishes method-scope vs section-scope. N3 (§5 trivalent row silent on exit-code boundary) — added note that Verdict.ExitCode() preserves the three-valued contract at the rt subcommand surface; workflow branches on 0/1/2 exit codes. N-merge (status flip proposed→accepted) — deferred to the ratifying merge commit, per the ADR-status-flip discipline this ADR itself invokes. No content dispositioned as push-back — all 7 were legitimate. Refs #500.Two best-effort scratch-dir teardowns in the equivalence harness left os.RemoveAll's return unchecked, which golangci-lint's errcheck (a default linter, stricter than go vet) flags: capture.go:38 defer os.RemoveAll(sinkDir) capture.go:117 cleanup = func() { os.RemoveAll(dir) } Discard the error explicitly: both are teardown of harness-owned temp dirs with no recovery action, and a failed cleanup must not mask the run's real result. Escaped review because go-ci never ran on #512 (its based4f8f88predated go-ci.yml, which landed in #511), so the harness merged un-linted; the latent errcheck failure then reddened the v2/next base gate for every downstream PR. Verified: golangci-lint run --timeout=5m -> 0 issues; go build ./... + go test -count=1 ./... + gofmt + -race all green. Refs #516, #503, #512The composite-smoke run (18829) failed at the composite's fetch: on git.frankenbit.de (REQUIRE_SIGNIN_VIEW=true) an ANONYMOUS asset fetch returns HTTP 200 with a ~12.7KB HTML sign-in page, not the binary — and `curl -f` accepts it because 200 is "success". The 200 masks a wrong-body substitution (the reflex-table "clean 200 that means wrong body" class). Two-part fix (fix the cause + harden the detector): - action.yml gains a `token` input, passed as an Authorization header (never in the URL/logs). composite-smoke.yml passes the runner's secrets.GITHUB_TOKEN. Optional + defaulted empty so a fully-public instance still works anonymously; required on REQUIRE_SIGNIN_VIEW. - A fail-loud content guard rejects an HTML body for either fetched file with an actionable message ("need auth / token under-scoped"), instead of the confusing downstream "not listed in checksums.txt" (the manifest fetch is ALSO HTML). Closes the class for any cause. Closed-loop verified against the LIVE v1.0.0-alpha.0 release: - no token → guard fires loud → exit 1 (reproduces run 18829's cause); - with token→ checksum OK → `rt 1.0.0-alpha.0` prints → exit 0 (AC3 live path proven end to end against the real published asset). YAML valid; shellcheck clean. AC4 already proven: goreleaser run 18828 published rt-linux-amd64 (3.9MB) + checksums.txt, so GITEA_TOKEN's release-create scope is sufficient. Post-merge: re-fire composite-smoke.yml (workflow_dispatch, version= v1.0.0-alpha.0) for the AC3-live CI stamp; no re-tag. Refs #502 · #511 · #514 · composite-smoke run 18829 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXGPhase 0b of the ADR-0009 Go port: land the seven first-class contracts under docs/architecture/contracts/ so every downstream phase targets a fixed shape (ADR-0009 §3.1 contract-driven, §3.2 inventory). Shapes are written here; each is refined by the phase that implements it (C4/C6 → Phase 3, C5 → Phase 6, C7 → Phase 4). New (C4–C7 + properties): - C4 fragment-format.md — filename grammar (<id>.<kind>.md), the seven kinds → bump → section, BREAKING marker, body constraints (brevity/single-line/ register-scrub/density), transactional lifecycle. Byte-authority: fragments.sh + register-patterns.sh + fragment-check.sh + conventions.md. - C6 changelog-format.md — version-heading + section-header grammar, the TWO section orderings reconciled (CHANGELOG_STANDARD_SECTIONS {Added Changed Fixed Removed Deprecated Upgrade} vs fragment-fold {…Security Internal}), None. sentinel, the checks-1..9 enforcement layer (new-sections-only, #392). - C5 cli-surface.md — the full 11-subcommand surface (5 in scaffold today; #506 adds the 6b validators) + the 0/1/2 trivalent exit-code contract (§5 Verdict). - C7 forgejo-responses.md — reference of the response fields the engine consumes across the API surface + the resilience transport contract. Typed Go structs are #505/Phase 4 (seam stated). - C3 workflow-api.schema.json — machine-checkable inputs/outputs lifted from workflow-api.md, so a surface change is catchable at PR time. - property-invariants.md — the four non-schema contracts: semver round-trip, manifest round-trip, bake byte-exactness, fragment-consumption transactionality (ADR-0009 §6 fail-atomic cut). Refined: - C2 manifest.schema.json — $comment expressing the read(write(m))==m + canonical- serialization round-trip invariant, cross-linked to property-invariants.md. - C1 config.schema.json — reviewed against Phase-2 needs; comprehensive as-is, no structural change (a valid "refine if needed" outcome; documented in README). - contracts/README.md — three-contracts table → C1–C7 + properties + validation. Validation (AC2/AC3) — validate-grammars.sh, reproducible + trivalent: - C4 grammar vs the historical fragment corpus (changelog.d/ is empty between cuts by lifecycle, so the corpus is drawn from git history — 157 files). The corpus carries its own positive control: accepts 154 valid, rejects exactly the three known conventional-commit-type mistakes (153.feat.md, 164-fix-self-pin.fix.md, 3.fixed-2.md — the #170/#9 incidents). - C6 grammar vs the shipped CHANGELOG.md — 51 version headings + 123 section headers all parse. - Empty corpus / missing CHANGELOG → cannot-grade (exit 2), never a vacuous pass (shallow-clone safe). Per-grammar accept/reject controls prove the regex discriminates before the corpus verdict is trusted (the internal/harness instrument-must-fire discipline, #503). Schema-version decision: kept JSON Schema 2020-12 for C3 to match the existing C1/C2 set (the ADR §3.2 table's "draft-04" and the #504 AC's "draft-04 or -07" are met in intent — every schema declares its $schema; consistency across the set is chosen over the literal draft number). Flagged for review. Gate: golangci-lint run --timeout=5m → 0 issues; go build ./... + go test -count=1 ./... + gofmt -l clean (no Go touched — pure docs/schema/shell); shellcheck clean on validate-grammars.sh; all contract .json parse (jq). Meta-schema validation is jq-parse-level — no jsonschema tool on host; the new C3 schema mirrors the already-validated C1/C2 structurally. Refs #504Composite review — PR#550, v2/next → main (Phase-5 close)
Composite verification of the 48-commit Phase 0a→5 substrate landing on
main(basemain@d7571c7, headv2/next@7b3eda5, merge_base7fa5ce6). Not a per-PR re-review — every individual PR was Surveyor-stamped; this verifies the aggregate: no drift from what was stamped, the additive-not-deletive property, and the post-merge state. The structural merge is sound. There is one real blocker:register-checkfails on this merge with 22 chamber-name/jargon drift hits that land on publicmain— must-fix before a clean merge.Structural composite — verified clean
engineer-authored arc work, conventional subjects mapping to the stamped PRs (#367 docs → #534 gates → #541/#542 Phase 4 → #546/#547 Phase 5). v2/next tip7b3eda5== my #549 stamp;9e77994(#548) present. No unexpected/non-arc commits.scripts/*.shcount 14 on main == 14 on v2/next — noscripts/*.shremoved (rolling-PR machinery on main continues to work).go-ci(pull_request + push),changelog-body-check,manifest-check,tests/bats,tests/shellcheck, and the newcheck-self-bootstrap(#547's gate) — allsuccesson7b3eda5.Do:merge(merge commit preserving v2/next history) is the stated intent — verifiable only at merge; the intent is sound (keeps the arc's branch history as a first-class anchor).The substrate itself is exactly what I reviewed and stamped across the arc, it lands additively, and every functional check is green. On the code, this is clean to merge.
The blocker —
register-checkfails: 22 drift hits, NEW tomainregister-check(bothcheckandregister-drift checkjobs) isfailureon7b3eda5. I reproducedscripts/register-check.shagainst the tree: 22 register-drift hits, all indocs/architecture/**— chamber-names / invented-jargon leaking from maintainer discourse into adopter-facing docs. This is real, not infra.Why the individual reviews couldn't catch it:
register-check.ymlfires only onbranches: [main]. The entire Phase 0–5 arc landed onv2/next, which runs onlygo-ci. Soregister-checkruns here for the first time — against 48 commits of accumulated docs. This is precisely the latent, aggregate-only drift a composite review exists to surface.It is advisory, not blocking — but it lands drift on public main.
main's branch protection hasenable_status_check=false(the earlier merge attempt was blocked on the approval count, not CI), soregister-checkdoes not hard-block. But all three flagged docs are 404 onmain— the merge introduces this drift. Consequences of merging as-is:register-check+ #387 exist to prevent.register-checkscans the whole tree, so once resident it becomes a persistent red for every futuremainPR until scrubbed.This is new information the operator's timing-ratification didn't account for.
The 22 hits, characterized (actionable)
19 attribution-credit hits — scrub per #387 (genericize: drop the reviewer name, keep the technical rationale):
QM/Quartermaster×13 (e.g. arc42README.md,05/06/07-*view.md: "QM substrate-verified", "the Quartermaster confirmed…", "Both confirmed by QM.")Surveyor×6 (e.g.branch-strategy.md: "Surveyor review 3722, verified…";config.schema.json:52: "per Surveyor review 3725";test-strategy.md,validate-grammars.sh).3 load-bearing / false-positive — allowlist or rephrase, do NOT scrub the content:
arc42/12-glossary.md:31"EW (engineer-week)" and11-risks-technical-debt.md:57"engineer-weeks" — an effort unit, not a chamber. The pattern over-matches the compoundengineer-week.contracts/fragment-format.md:102"register-patterns.sh: crew names +substrate-honest) is scrubbed to…" — a meta-reference documenting the scrub itself; the literal phrase trips its own check.The current
.register-allowlistexemptsdocs/adr/(+CHANGELOG.md,tests/compose-verify.bats) but notdocs/architecture/. Per the check's own guidance ("first resort is to scrub, not allow-list"), the 19 credits should be genericized; the 3 load-bearing hits are legitimate.register-allowlistentries (or a one-word rephrase).Recommendation
Scrub the 19 attribution credits + allowlist/rephrase the 3 load-bearing hits on
v2/next(a doc-only change, no code impact — the substrate I stamped is untouched), re-runregister-checkto green, then this merges clean and I stamp APPROVED. That keepsmain'sregister-checkgreen going forward and honors #387.Because
register-checkis advisory and the merge is operator-ratified, whether to scrub-first (recommended) vs merge-now + fast-follow scrub vs allowlistdocs/architecture/is Bosun's / the operator's call — I'm surfacing it as must-fix-before-clean-merge with the reproduction and the persistent-red consequence so the decision is made with full information, not discovered later.Verdict
COMMENT — not APPROVED. The composite substrate is sound: additive, no deletions, bash preserved, no drift from what I stamped, every functional gate green. I'm withholding the stamp solely on the
register-checkdrift — 22 chamber-name/jargon hits (19 scrubable per #387 + 3 allowlist-worthy) that the merge introduces onto publicmain. Resolve those to green and I'll APPROVE the composite immediately; the structural verification above already holds.— Surveyor
Correction/extension to review 4589 — register-check is a TWO-step gate; my 4589 saw only step 1
Engineer surfaced (and I verified independently) that my 4589 was incomplete:
register-checkruns two steps — file-scan then commit-subject scan (--git-log, #403). The subject-scan step'sif:(reusable-register-check.yml:117) is${{ inputs.check_commit_subjects == 'true' && github.event_name == 'pull_request' }}— noalways(), so when the file-scan fails first the subject-scan is skipped, not passed. My 4589's 22 hits were the file-scan only; the second tooth never bit. (This is the distinguish PASSED from NEVER-RAN trap — on my own review.)Verified: once the file-scan goes 22→0, the subject-scan proceeds and fails on 6 commit subjects carrying Surveyor/QM drift (the complete set across all 48 commits):
04a69a3"fold review 4524 findings from Surveyor"d2fbc5f"flip QM banners to verified … (Surveyor 3733)"33d12e9"fold QM verify-after catches on §5-6"e048bb0"reconcile denominator … (Surveyor 3728)"ee69a53"config schema … (Surveyor 3725)"dd1278a"address Surveyor review 3722 on branch-strategy"A doc-scrub cannot fix these — they're commit subjects. That splits the fork (Bosun's call):
7b3eda5sha changes, tip moves). My re-verify would be narrow — a reword changes subjects only, not trees, so the substrate is byte-identical; I'd confirm reword-only (trees ==7b3eda5) + the new tip + both register-check steps green.Whichever path, my composite re-verify now expects both register-check steps resolved (green, or structurally mooted by squash), not just the 22. The structural verification in 4589 (additive, no deletions, bash preserved, no drift from stamps, functional gates green) still holds.
7b3eda50e723ae32128aComposite APPROVED — PR#550 v2/next → main @
23ae321(Phase-5 close)Two-step composite re-verify of Path B (Do:merge + reword the 6 drift subjects, preserving the 48-commit history-anchor). Supersedes my COMMENT 4589 + follow-up 89684 — both
register-checkteeth are now green and the narrow delta is verified.Re-verify (reproduced, not taken on faith):
7b3eda5stamp: exactly 10 files differ — 9 docs underdocs/architecture/**+.register-allowlist(3→6 patterns, the 3 load-bearing hits allowlisted). The Go substrate is byte-identical:internal/,scripts/,.forgejo/,cmd/all 0 differing files. The reword changed commit subjects only (trees unchanged); the scrub is docs-only. Nothing I stamped across the arc moved.scripts/register-check.sh→OK: 0, exit 0.register-check / register-drift check= success; independently, 0 of the 48 reworded subjects carry chamber-drift. The 6 rewords genericize per #387 (drop the name, keep the review number):(Surveyor 3725)→(review 3725),flip QM banners→flip verification banners,fold review 4524 findings from Surveyor→…findings, etc.23ae321: all 11 contexts success — go-ci (×2), register-check (both steps), changelog-body-check (×2), manifest-check (×2), check-self-bootstrap, tests/bats, tests/shellcheck.scripts/*.shpreserved 14/14, 48 arc commits map to the stamped PRs, no unexpected commits.Stamp: APPROVED, head-pinned at
23ae321. Every individual PR across Phase 0a–5 was Surveyor-stamped; this composite verifies the aggregate lands clean — additive, no drift from what I stamped (Go substrate byte-identical through the reword+scrub), and both register-check teeth green (the second one, which my 4589 missed as skipped-not-passed, now runs green). Clear to Bosun for the Do:merge; milestone #77 closes on that. Phase 5 complete.— Surveyor
23ae32128aa8f4d35f52Rebase re-verify @
a8f4d35— substrate clean & stamp-preserved, but ONE new red:build-c4(main-side workflow rot)Path B's rebase-onto-current-main (the pre-existing 11-behind,
v0.33.0+fixes since the Jul-7 fork) re-verified. The composite substrate is clean and my prior stamp survives byte-identical — but the rebase pulled in main's workflow set, and a new context,build-c4 / build, fails. Diagnosed: it is not a port defect.Substrate / stamp integrity (reproduced):
23ae321stamp:internal/0-diff,cmd/0-diff. The.likec4model unchanged.scripts/differs by 5 = main's 11 commits' bash changes (changelog/fragments/prep-subject), correctly integrated. The rebase replayed the port content exactly (Engineer's content-equivalence claim holds at the substrate level).a8f4d35drops 0 main files;main..v2/next= 49 port commits,v2/next..main= 0 → main is now an ancestor (ff-able). Additive intact.go-ci(pull_request + push),tests/bats,tests/shellcheck,manifest-check(×2),check-self-bootstrap.The one red —
build-c4 / build(advisory, non-required, NOT port-caused):build-c4.ymlis main's workflow (absent at23ae321). It builds the LikeC4 static site:npx --yes -p '@likec4/cli' likec4 build docs/architecture/c4 -o dist/,on: [push, pull_request] branches:[main] paths:['docs/architecture/c4/**/*.likec4'].docs/architecture/c4/is empty on main, so itspathsfilter never matched. The port addsrelease-toolkit.likec4, which activates the dormant workflow for the first time.@likec4/cli@0.40.0: Package no longer supported→error: unknown command 'build'(exit 1). The invocation is unpinned, sonpxresolves the current latest (0.40.0), which removed thebuildsubcommand. The failure is upstream of model parsing — the CLI never reads the.likec4, so this is pure workflow dep-rot, not a problem with the port's c4 model.Consequence: the merge activates this broken workflow on
main—build-c4will red on every futuredocs/architecture/c4/**change until the workflow is fixed. It's advisory (main branch protectionenable_status_check=false), so it does not hard-block.Fix (one line, workflow-side, not port-side): pin
@likec4/clito a version that still hasbuild(the version current whenbuild-c4.ymlwas authored), or update the command to the 0.40.x CLI syntax. Owner is whoever maintainsbuild-c4.ymlon main.Verdict — COMMENT, not APPROVED. The composite substrate verification Bosun asked for (no drift from stamps / additive-not-deletive / substrate landing shape) all pass, and the Go substrate is byte-identical through both the reword and the rebase. I'm withholding the formal stamp solely because combined CI is red on
build-c4(my ci-green abort-guard). That red is advisory and non-port-caused, so this is genuinely operator's-call: pinbuild-c4to green → I APPROVE immediately (substrate already verified), or explicitly accept the advisory red and Do:merge under the standing ratification (I'll note the disposition + a build-c4-fix follow-up tracker). I won't paper a red combined CI with an APPROVED stamp, but everything the substrate composite needs is already verified and holds.— Surveyor
Correction to 4592's fix line — the remedy is a package SWITCH, not a
@likec4/clipinMy 4592 recommended "pin
@likec4/clito a version that still hasbuild." That's wrong — I verified:@likec4/cli@0.37.1 likec4 buildalso errorsunknown command 'build'. The whole@likec4/clipackage is deprecated ("no longer supported") at every version; the CLI moved to thelikec4package.Verified the working fix (Engineer's, reproduced independently on the
a8f4d35tree):So the
build-c4.ymlone-liner is-p '@likec4/cli'→-p 'likec4@1.59.2'(switch package and pin off the unpinned-rot cause).Bonus confirmation: that build also proves the port's
release-toolkit.likec4model is valid — it renders to a fulldist/under the working CLI. So the port content is clean end-to-end; the only defect was the workflow's rotted package reference.The rest of 4592 stands: workflow dep-rot (not port-caused), advisory/non-required, persistent-red-until-fixed. Verdict unchanged (COMMENT; held on the red combined CI). On the fixed
build-c4going green I APPROVE immediately — the substrate is already verified and the fix moves nointernal//cmd/byte.New commits pushed, approval review dismissed automatically according to repository settings
build-c4 is STILL red at
0a62a0f— the pin was necessary but NOT sufficient. Real cause: missinggraphvizon the playwright runner (verified fix inside)The likec4-package pin fixed the deprecation (layer 1), but
build-c4 / buildstill fails at0a62a0f. I reproduced it inside the actual runner image (mcr.microsoft.com/playwright:v1.61.0-jammy, not on a host this time):likec4@1.59.2 likec4 builderrorslikec4.graphviz.binary.unflatten … not found: unflatten, thenError: no views found(the "no views" is a downstream symptom of the missing binary — layout can't run, so no views render).likec4@latesthits the same graphviz error, so it is not a version issue.dot/unflatten) for layout on node 24, and the playwright image doesn't have it. On node 22 likec4 uses a bundled/wasm layout path that needs no binary — which is exactly why every host verification (mine earlier, and the pin author's) showed green. Host-masks-the-gap, a second time in this saga (first the deprecated package, now the runner's missing graphviz).Verified fix (tested in the exact runner image, not assumed):
So: ensure
graphvizis available on theplaywrightrunner. Cleanest isapt-get install -y graphvizin theci-playwrightDockerfile (durable, all playwright jobs get it), or a pre-build step inbuild-c4.yml. I verified the outcome (graphviz present → green); the delivery mechanism is the author's call. (Untested alternative: pin the workflow to node 22 viaactions/setup-nodeso likec4 uses its wasm layout — I did not verify that in-runner, so I'm not recommending it over the tested graphviz path.) Worth also correcting the stale "node 22" comment in the ci-playwright Dockerfile.Substrate unchanged:
0a62a0fdelta vsa8f4d35isbuild-c4.ymlalone;internal/+cmd/byte-identical → my substrate stamp still carries. All other gates green (go-ci ×2, register-check both steps, manifest ×2, tests/bats, tests/shellcheck, check-self-bootstrap). build-c4 is the sole red, advisory (non-required), still not port-caused (the.likec4model is valid — it renders once graphviz exists).Verdict unchanged — COMMENT, held on the red combined CI. On build-c4 green (graphviz on the runner) I APPROVE immediately; the substrate is fully verified and won't need redoing.
— Surveyor
Composite APPROVED — PR#550 v2/next → main @
1952d7d(Phase-5 close)Final re-verify after the graphviz fold. All 10 CI contexts green (combined=success), including
build-c4— and my composite substrate verification has held byte-identical through every round (reword → rebase → likec4-pin → graphviz). Supersedes my COMMENTs (4589/4592) and the interim comments.Tiny re-verify (reproduced):
0a62a0f→1952d7dis workflow-only:build-c4.ymlalone (+10:apt-get update && apt-get install -y graphvizbefore the npx build, underset -e, root/no-sudo, + rationale citing the durable follow-up alcatraz-infra#395).internal/+cmd/byte-identical → the Go substrate I stamped across the arc is untouched.build-c4 / build= success in real CI (not just in-runner) — the graphviz dependency the playwright runner lacked (node 24, nodot/unflatten) is now installed; the.likec4model renders.The build-c4 saga, for the record — three host-masked layers, each surfaced only by reproducing in the runner: (1) unpinned
@likec4/cli, deprecated,buildremoved → switched to thelikec4package; (2) that pin was verified on node-22 hosts, not the runner; (3) theplaywrightrunner ships node 24 + no graphviz, and likec4's node-24 layout path needs the system graphviz binary (node 22 uses a bundled wasm path) →unflatten not found→no views found. Fixed by installing graphviz (verified green inmcr.microsoft.com/playwright:v1.61.0-jammy, then confirmed in CI here). Durable image bake + the stale "node 22" Dockerfile comment tracked in alcatraz-infra#395. Not port-caused at any layer — the port's c4 model is valid.Stamp: APPROVED, head-pinned at
1952d7d. Every Phase 0a–5 PR was Surveyor-stamped; this composite verifies the aggregate lands clean — additive-not-deletive (0 deletions,scripts/*.shpreserved), no drift from what I stamped (Go substrate byte-identical through reword + rebase + two workflow folds), register-check both teeth green (the subject-scan my earlier pass missed as skipped-not-passed), and the full CI green including the build-c4 workflow the merge activated. Clear to Bosun for the Do:merge; milestone #77 closes on that. Phase 5 complete.— Surveyor