feat(distribution): add sync-managed claude review-lane caller components - #286
Conversation
…ents Adds `claude-review-caller` and `claude-security-review-caller` managed components sourcing thin callers for the ci-workflows reusable Claude review lanes (dest `.github/workflows/claude-review.yml` / `claude-security-review.yml`), pinned at the v0.9.0 release SHA (cf666f672e0ec81cf876303840a3af1ffd4cb966). Caller shape derives from the reusables' own canonical-caller headers: job ids `review` / `security-review` (required-check name continuity), review triggers [opened, ready_for_review, reopened], security triggers + `synchronize` with no workflow-level path filtering, per-lane concurrency (per-PR cancel + repo-wide `queue: max` on review; cancel false, no queue on security), governed select-runner indirection with the hosted recovery fallback, `paths-file: .github/claude-security-paths` on the security caller, and the one named secret (never `secrets: inherit`). Managed workflow callers are a recorded exception to the distribution contract's workflow-caller exclusion — hand-written callers empirically drifted (medley's missing `reopened` trigger, divergent skip-actors, pin skew v0.6.1 <-> e295107) — so distribution/README.md's three exclusion statements gain carve-outs pointing at a new authoritative section. Targets are the repos that run each lane today: claude-review-caller in claude-code-plugins, dotfiles, github-iac, medley, provisioning; claude-security-review-caller in claude-code-plugins only. `.github` and ci-runner stay exempt; knowledge-corpus and songwriting wait on the sync App access grant; standards keeps its repo-local caller as manifest source. Validation: sync-manifest.sh validate (33 components, 8 targets); JSON Schema check via yq -o=json | validate-sync-manifest.mjs; plan over all five affected targets; actionlint 1.7.12 with the approved queue-suppression config exits 0 on both callers (without the config it reports exactly the expected `concurrency.queue` syntax-check finding on the review caller). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 25s —— View job Code Review
Review: feat(distribution): add sync-managed claude review-lane caller componentsThe PR is well-structured and the reasoning is thoroughly documented. The concurrency decisions, permission grants, selector-failure guards, and exception justification are all sound. A few items worth resolving before merge: 🔴 Important — Workflow
|
|
Claude finished @kyle-sexton's task in 3m 5s —— View job Code Review
Review: feat(distribution): add sync-managed claude review-lane caller componentsThe PR is well-structured with thorough inline rationale. Most of the mechanics are correct and well-reasoned. One open question from the prior review remains unresolved and needs an answer before merge. 🔴 Important — Workflow
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 029aea03f2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude finished @kyle-sexton's task in 6m 2s —— View job Code Review
Review: feat(distribution): add sync-managed claude review-lane caller componentsThis pass reviews the synchronize event (the merge of origin/main into the branch). The feature content is unchanged since the prior two reviews. Two prior findings remain open; the merge from main resolves Gate 1 and surfaces a new blocking issue. Gate 1 status: RESOLVEDThe merge of origin/main brought in Gate 2 (sync App 🔴 Important — v0.9.0 SHA not registered in runner-policy
Both callers pin all three reusable references at
The runner-policy gate ( The v0.9.0 contracts also need to reflect the changed input shapes:
This is a code gap in the PR — 🔴 Important — Workflow
|
| # | Severity | Finding | Status |
|---|---|---|---|
| 1 | 🔴 | v0.9.0 SHA not in runner-policy/policy.json (3 entries needed) |
New this pass |
| 2 | 🔴 | Workflow name: vs required-check context — needs confirmation |
Open 3rd pass |
| 3 | 🟡 | README ordering "alongside (or before)" understates existing-adopter constraint | Open 3rd pass |
| 4 | 🟡 | ai-review-bot-composition.md skip-actors wiring statement is stale |
New this pass |
Gate 1 (actionlint suppression) resolved by the merge from main. Gate 2 (App workflows: write) still pending external action.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e10a4062f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
v0.9.0 predates the security lane's paths-file input, which these caller components pass. A caller passing an input the pinned reusable does not declare hard-fails the workflow_call, leaving the required security check Pending forever. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
Work-in-progress output from an in-flight worker that died with its session before reporting or being verified. Committed as-is to make it durable and diffable for a fresh-context verifier; not yet reviewed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
The caller components resolve the runner through the governed select-runner indirection. runner-policy admits that selector only for a private self-hosted consumer (`routingEnabled` is `visibility === "private" && selfHostedCi`), and the `!routingEnabled` branch consults neither `exceptions` nor `localRoutingGrants` — a public target has no configuration escape. melodic-software/claude-code-plugins is the org's one public caller target, and it carried BOTH components, including the security caller whose `security-review / security-review` context is a required check there. Syncing them in would have failed that repo's own runner-policy lane with four findings, turning `ci-status` red and wedging its PR pipeline. Verified by auditing each component against a public consumer config using the shipped policy.json; the same audit passes clean for a private self-hosted consumer. claude-code-plugins moves to `locally-owned` for both callers — the manifest's documented customization seam, and the same disposition actionlint already carries for consumers whose local content is load-bearing. Its existing hosted-only callers pass runner-policy today and are unchanged by this branch. Three tests hold the constraint so it cannot regress silently: a selector-routed caller component may not be `managed` for a public target, every caller component must audit clean for a private self-hosted consumer, and a selector-routed caller is expected to be rejected outright on a public one. Standards' own `lint:runner-policy` scans `.github/workflows`, not `components/`, so nothing previously validated these components at all. Also records the two lane contracts registered at c136b27 (both additive, no secret or routing change) and renames PATHS_FILE_SELECTOR_SHA to PATHS_FILE_LANE_SHA: the revision changes the security reusable, and its selector is byte-identical to e77f0126. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
Fixes an independent verifier found against 28b335c. `claude-security-review-caller` has NO managed target and cannot gain one as written: both repos running a security lane today (claude-code-plugins, ci-workflows) are public, and a selector-routed caller is rejected outright on a public repository. 28b335c's "managed for PRIVATE targets only" read as correctly scoped when one component in fact reaches nobody. Both the manifest and distribution/README.md now say PARKED, why it is retained rather than deleted, and what unparks it. The consequence of the claude-code-plugins `locally-owned` disposition — the one repo whose ruleset requires `security-review / security-review` now re-pins by hand — is stated where a reader will meet it rather than left implicit. The component drops `synchronize`, which all four managed targets' live callers carry. That is the plan's deliberate cadence cut, not drift, but it is the largest behavior change the component makes and the manifest documented only medley's lesser `paths-ignore` loss. Recorded as an accepted loss for all targets, alongside why the security lane keeps the trigger. The pin comment still claimed v0.9.0 two commits after b94b204 re-pinned to v0.9.1 — corrected, with the reason the older pin cannot serve the security caller. The runner-policy README said the c136b27 contracts change no secret or routing surface. True as stated, but it glossed the one privilege delta in the diff: the security reusable's `changes` job gained `contents: read` to read the repo-owned paths file. Inbound-safe, now stated rather than omitted. Also closes the coverage gap 28b335c only half-named. components/claude-lanes/ holds workflow bytes, but every workflow-shaped lane in this repo discovers files under .github/workflows — so actionlint, zizmor, concurrency-policy, and pin-comment-convention were ALL blind to them, not just lint:runner-policy. The pin-comment lane takes an explicit file list, so it now scans the components too; verified non-vacuous (a mismatched SHA claim fails it). zizmor was run against both components directly and reports no findings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
Both caller components fail standards' own `concurrency-policy` component on
three rules: each lane-scoped group name is not `${{ github.workflow }}`-
prefixed, and the security caller's `cancel-in-progress: false` is not the
literal `true` that component requires.
Nothing fails today — no target repo runs `concurrency-policy`, and standards'
own lane scans `.github/workflows`, not `components/`. But both values are
deliberate and load-bearing: the lane-scoped group name is what keeps a caller
group distinct from the reusable's inner per-(PR,head) group, and cancelling a
REQUIRED check is not a skip and does not read success. A future adopter of
both components would meet the failure with no record of which side is right,
and normalizing the callers to satisfy the linter is the wrong resolution.
Recorded at the component sources, where the conflicting values already carry
their rationale, rather than in a doc a reader would have to know to look for.
Comment-only; the callers' bytes are otherwise unchanged and conformance,
runner-policy, pin-comment, and zizmor all re-verified clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
The pin-comment lane was pointed at components/claude-lanes/ but zizmor was not, leaving the fix half-applied: zizmor discovers workflow definitions by their `.github/workflows` location, so `paths: .` audits this repo's own workflows and skips the component bytes entirely — verified by running it and watching the two files never appear in the completed list. `paths` is whitespace-separated, so naming both components extends the same lane rather than adding one. Verified: both files now appear in the audit, and the finding total, severity breakdown, and exit code are identical to baseline (45 findings, 1 informational, 0 high) — the components introduce nothing, and the lane still gates on `fail-on-severity: high`. Leaves actionlint and concurrency-policy still blind to these files. Both are deliberate and now recorded: actionlint 1.7.12 rejects the review caller's `concurrency.queue` key (rhysd/actionlint#654), which is exactly what the fleet suppression distributed in Phase 3c0 exists to handle at the consumers that lint the synced file; concurrency-policy's conflict is documented at the component sources. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
The parked security caller was doc-only. Nothing failed if a later change quietly gave it a managed target, and the three existing gates all pass on a component that reaches nobody — none of them asserts anything about the targets a caller DOES have. Asserts the property rather than the current count: every managed target of any caller component must be a target runner-policy actually admits. Unparking legitimately, by adding a private consumer, passes; adding a public one fails. That keeps the guard useful after the parked state ends instead of pinning today's arrangement. Verified non-vacuous: adding claude-security-review-caller back to the public claude-code-plugins target fails this test and the existing public-target gate, 2 of 242. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
The verifier found the gates had the blind spot that mattered most. Both keyed public-ness off a hardcoded one-element `PUBLIC_SYNC_TARGETS` list, so any public repo NOT named in it was silently treated as private and admitted. Adding `melodic-software/ci-runner` — public, and already an exempt repo the plan discusses — as a managed target of the review caller passed all 242 tests while describing exactly the fleet-wedging defect this branch exists to prevent. The allowlist inverted the safe default: it enumerated the danger and assumed safety for everything else. Replaced with a visibility map covering all 11 non-archived org repos, looked up FAIL-CLOSED — a target absent from the map asserts rather than defaulting to private. The public-target gate now iterates the manifest's own targets instead of the list, so it sees every target the manifest actually declares. Checked in rather than read from the API because this suite runs offline and hermetically. Verified against live GitHub at authoring time: the map matches `gh repo list` exactly for all 11 repos, both directions, no missing/extra/ mismatched entry. A visibility flip therefore surfaces as a failing test rather than a silent admission. Probes: ci-runner as a managed target now fails 2 of 242; a target absent from the map fails with "add it to TARGET_VISIBILITY (public until proven private)". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
…dow (#290) ## Summary Sets `automerge: false` on every one of the eight targets in `distribution/sync-manifest.yml`, plus one comment above the `targets:` block recording that the window is temporary. **This is a temporary rollout-window gate. It MUST be reverted after Phase 3d completes.** Merging it is a prerequisite for #289 and #286. `automerge` is opt-out policy-as-data — `distribution/sync-manifest.sh:428-431` treats an absent key as `true` — so a target that omits the key keeps the armed default. Every target therefore has to carry the key explicitly for the window to hold. No target opts out today. Why the window is needed: the sync engine at `ci-workflows@ac223bb` adds a step that arms squash auto-merge at PR creation, gated on `steps.cpr.outputs.pull-request-operation == 'created' && matrix.automerge` (`standards-sync.yml:475`). The same commit adds `permission-workflows: write` to the per-target token mint, and this repository's `base` ruleset carries `required_approving_review_count: 0`. Composed, one merge here would write workflow content into all eight targets and self-merge with no human checkpoint. Disarming `automerge` restores the two-checkpoint shape: a merge here proposes the sync PR, and a human merges it in each target. Only the `automerge` key is added. No target's `managed` or `locally-owned` component list changes. ## Test plan - `bash distribution/sync-manifest.sh validate` → `Manifest valid: 32 components, 8 targets`, exit 0 (both the bare CI form and the explicit `--source-root . --manifest distribution/sync-manifest.yml` form). - `bash distribution/sync-manifest.test.sh` → engine suite green. - `bash distribution/sync-manifest.sh matrix` → all eight entries emit `"automerge":false` as a JSON boolean (verified with `jq '.automerge|type'` == `boolean`), so `fromJson` yields a real boolean and `matrix.automerge` evaluates falsy. A quoted `"false"` string would have been truthy. - `yq '.targets[].automerge | tag'` → `!!bool` for all eight; `sync-manifest.sh:433` requires that tag. - `grep -c "automerge: false" distribution/sync-manifest.yml` → `8`, matching the target count. The comment deliberately avoids the literal string so the post-restore sanity check (`grep -c ... == 0`) stays meaningful. ## Related No linked issue — this is a pre-step of the claude-review-lanes rollout plan (`ci-workflows` `docs/topics/claude-review-lanes/PLAN.md`, Phase 3d pre-step i), tracked there rather than as a standalone issue. Merge order: this PR first, then #289 (`ci: re-pin the standards-sync engine at ac223bb`), then #286 (`feat(distribution): add sync-managed claude review-lane caller components`). Merging this PR is itself a push to `main` and fires a real sync run; that run is safe because `main` still pins the sync engine at `0b45b9f`, which has no arming step at all. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary Records Phase 5 close-out **evidence** in `docs/topics/claude-review-lanes/PLAN.md`. One file, additive only — no plan requirement is restated, reinterpreted, or amended, and the phase tag stays `[TODO]` because three of the five bullets are not fully closed. Of Phase 5's five bullets: two are fully closed with evidence (trailer note, do-not-break sweep), one is half-closed (equivalence — the medley half done, the blob-hash half blocked), and two remain outstanding (README/CLAUDE.md, and "tags advanced", which cannot close while the others are open). **The medley REVIEW.md equivalence result inverts the bullet's own framing**, which is the substantive finding here. medley's `REVIEW.md` is not a drifted copy of the managed source — it is an independently authored 188-line document with its own slice taxonomy, first added in `4a1d61a6`, never synced from standards. So the absent lane-scope headings are not the finding. The restructure's mutual-exclusion rule is *conditional* on a named file (standards `REVIEW.md:66-73`): it suppresses security findings only on a repository that runs the security lane, and otherwise directs "report security findings under this lane too". medley has no `claude-security-review.yml` and no caller routing to that reusable, so it sits in the else branch — folding security in is correct, and adopting the mutual-exclusion text would have produced the 2f-addendum failure mode of **no lane reporting security findings**. The real drift is the one always-check that is *not* security-gated: multi-location atomicity (standards `REVIEW.md:82-86`) has no universal-checklist line in medley, and its nearest coverage self-scopes to "EF Core persistence in the modular monolith" while medley is polyglot. Filed as melodic-software/medley#1671. medley's file was **not** edited — `review-instructions` is `locally-owned` for medley, which is the customization seam working as intended. The standards repo-local caller blob-hash equivalence is recorded as **NOT DONE and blocked** on standards#286 rather than quietly omitted — a sanity block that hides an unrun check is worse than one that names it. ## Test plan No executable surface changes; this is a documentation-only diff to a planning artifact. Verification was of the *claims recorded*, not of code: - Every citation in the evidence block was read from the live tree: standards `REVIEW.md:66-73` and `:82-86`; medley `REVIEW.md:65`, `:66`, `:67`, `:96`; `review/security.md:7`, `:11`, `:18`; `review/logging.md:12`; `review/transactions-and-consistency.md` line 3 self-scope; `claude-review.yml:751`. Reading is not the same as verifying, and the third verifier round proved it: an earlier revision of this block mislabelled the security-lane always-checks as five when there are four (`abeccc6`'s own commit message says four), and inverted the `:65`/`:66` → `security.md` mapping. Both are corrected here. - medley's lane inventory confirmed empirically: no `claude-security-review.yml`, and `grep -rln "security-review" .github/workflows/` returns nothing. - Phase 1 sanity greps re-run against medley's copy: `grep -c "Do not report"` == 0, `grep -c "Cap the nits"` == 0. - Issue states re-read via `gh` at time of writing: #255/#258/#259 OPEN with `updatedAt` unchanged at 2026-07-27T02:11Z; standards#286 OPEN, `mergedAt: null`. - All version claims verified against `git rev-list`: v0.6.1=`90f1c549`, v0.8.0=`e77f0126`, v0.9.0=`cf666f67`, v0.9.1=`c136b27f`. - lefthook pre-commit gates passed on commit. **Verification honesty note.** Three fresh-context verifier rounds ran; all three returned REJECT, and each caught a defect introduced while fixing the previous one: - Round 1 — deployed-state overreach on the #151 comment; false "uniform across all three lane workflows" claim on #257. - Round 2 — `create-github-app-token` call-site undercount (one asserted, six actual), introduced while fixing round 1. - Round 3 — on #257, an absolute "no other pin was assessed" disclaimer contradicted by the comment's own coverage claims, and a substantively false one: medley's `claude-code-action` pin was described as having no Dependabot path when **medley#1668 was already open bumping it 1.0.174 → 1.0.180**, created ~15 hours before the comment. On this PR, the four-vs-five security-check mislabel and the inverted citation mapping above. All are corrected in place with the error named rather than edited away. This is recorded because the pattern — each fix round introducing a new error — is the substantive risk in this work, not an incidental detail. ## Related No linked issue — Phase 5 is tracked as a phase of the claude-review-lanes Brief in `docs/topics/claude-review-lanes/PLAN.md`, not as a ci-workflows issue. Referenced, not closed by this PR: - melodic-software/medley#1671 — the atomicity drift this evidence records - #256, #151, #257 — carry the record-only comments this block cites - #285 — the remaining Phase 5 README/CLAUDE.md bullet - melodic-software/standards#286 — blocks the outstanding blob-hash equivalence --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolves the one conflict in distribution/sync-manifest.yml, at the melodic-software/claude-code-plugins target where both parents insert after `- typos`: main's #290 adds `automerge: false`, this branch adds the `locally-owned` block for the two lane callers. Both survive, with `automerge: false` last to match the key order #290 uses on every target that already carries a `locally-owned` list. Purely additive over each parent — `git diff <parent> -- distribution/sync-manifest.yml` emits no deletion lines against either origin/main or af85ae0, so #290's comment above the `targets:` block is preserved along with every component list. Verified after resolution: - yq: 8 of 8 targets carry `automerge` as a `!!bool` false; the count of targets whose value is not boolean false is 0. - `sync-manifest.sh matrix` emits `automerge` as a JSON boolean for all 8 (a quoted string would be truthy in a GitHub expression). - Per-target managed/locally-owned lists are byte-identical to af85ae0 (95 entries), and the components block is unchanged. - `bash distribution/sync-manifest.sh validate` -> `Manifest valid: 34 components, 8 targets`, exit 0. - `bash distribution/sync-manifest.test.sh` -> 147 passed, 0 failed. - `npm run test:runner-policy` -> 242/242 against the merged manifest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The governed-class criterion said `melodic-standards-sync[bot]` is excluded
"via the caller's `skip-actors` input". The sync-managed caller this PR adds
passes only `runner`, so that sentence described wiring the caller no longer
has.
The exclusion itself is intact — ownership moved, it did not disappear. Read
at the pinned SHA rather than main, ci-workflows `claude-review.yml` at
`c136b27` declares:
skip-actors:
default: dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot]
So this is a documentation defect, not a behavior regression. The criterion now
attributes the exclusion to the reusable's default and records what overriding
the input costs: a caller that passes it owns the entire list, self-trigger ban
included.
Cross-document reconciliation left the other two `skip-actors` references
unchanged, deliberately. distribution/README.md cites divergent skip-actors
lists as historical evidence of caller drift — a claim about hand-written
callers, still true. components/runner-policy/README.md describes the earlier
contract entries, which do permit the input and are retained for consumers
mid-rollout; the new `c136b27` contract tightens `allowedInputs` to
`["runner"]`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The lane callers are workflow bytes, but every workflow-shaped lane in this repository discovers files under .github/workflows — so the repo's actionlint job never saw them. pin-comment-convention and zizmor were already extended to name them explicitly; actionlint could not be, because the file only takes its real form at a consumer's destination path. components/claude-lanes/claude-lanes.test.sh runs the entrypoint a consumer runs instead of linting the component in place: sync-manifest.sh apply into a scratch checkout carrying the target's origin identity, then actionlint over the result. Wired into the actionlint job, which already installs the pinned 1.7.12 engine; that job gains the same pinned yq install the distribution and pin-comment-convention jobs carry, because apply needs it. Twenty-four assertions, all derived from the manifest rather than hardcoded so the parked security caller gaining a target does not require editing a count: - every managed target materializes, receives the file at its mapped destination, and lints clean (dotfiles, github-iac, medley, provisioning); - every component's bytes also lint standalone at their destination path, so claude-security-review-caller — parked, no managed target, unreachable from the target loop — is covered rather than shipped unlinted; - a control run with the suppression config removed still reports the concurrency.queue message, so the suite cannot go quietly vacuous when rhysd/actionlint#654 ships upstream. A target owning actionlint locally receives no config from the manifest, so the canonical one stands in for the local equivalent it must carry. What this repository can own is that the shipped bytes lint clean under a conforming config; medley and github-iac each scope their own ignore to .github/workflows/claude-review.yml, the exact destination the manifest maps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 5m 6s —— View job Code Review (Pass 4)
Review: feat(distribution): add sync-managed claude review-lane caller componentsFour commits landed since Pass 3. All four prior open findings are resolved. No new blocking issues. Prior findings — all resolved
New commits — verified correct
Adds
Adds
Confirmed correct — unchanged from prior passes
|
…pping The per-target destination check queried the manifest inside the loop and skipped on a failed membership test. Skipping is correct for a component the target does not manage — most targets manage only one caller — but the same `|| continue` swallowed the case where the query itself returned nothing, which silently dropped every destination assertion and still reported green. An independent verifier proved the channel live: breaking the yq path in a scratch copy took the suite from 24 assertions to 20, exit 0 both times. The list is now read once per target and asserted non-empty before membership is tested, so an empty result fails rather than falls through. Probed directly: the real path yields 21 entries and passes; `.managedX[]` yields 0 and fails the assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 39s —— View job Code Review (Pass 5)
Review: feat(distribution): add sync-managed claude review-lane caller componentsOne commit landed since Pass 4. All pass 4 findings remain resolved. No new blocking issues. Pass 4 findings — still resolvedAll four findings from Pass 4 were addressed before that review posted. This commit does not touch New commit
|
| Gate | Status |
|---|---|
| Gate 1 — actionlint suppression (#284) | ✅ Resolved (merged 2026-07-27) |
Gate 2 — sync App workflows: write |
⏳ Pending external org-owner action |
Gate 3 — workflow name: / ruleset context |
✅ Resolved by scope change (claude-code-plugins → locally-owned) |
Gate 4 — #289 re-pin sync engine at ac223bb |
⏳ Still open per PR body; #290 → #289 → #286 ordering |
The only failing check is do-not-merge / do-not-merge — the intentional label gate.
d0ef74b closed one way this suite could shrink silently. An independent verifier found a second with the same signature and a reachable trigger: when the per-component membership test stopped matching, all four destination assertions vanished and the suite still exited 0 (24 PASS, 0 FAIL). The trigger was an asymmetry between the two membership tests. Target admission matched by SUBSTRING (`grep -F` over `\t<component>`), while the per-target test matched the WHOLE LINE. A component named as an extension of an existing one — `claude-review-caller-v2` — is admitted by the first and rejected by the second, so its target entered the loop and asserted nothing. Demonstrated: the substring form admits `melodic-software/future` for that component; exact lookup does not. Not live today, and the manifest's own unparking plan makes such a name plausible. Two changes. Both membership tests are now exact associative-array lookups, so the asymmetry cannot recur. And each target asserts that it actually RAN a destination assertion, which is the invariant that fails closed regardless of why the count reached zero — a broken query, a renamed component, or a membership test that quietly stops matching. Mutation-tested, three throwaway copies outside the worktree: intact 30 PASS 0 FAIL exit 0 membership never matches 22 PASS 4 FAIL exit 1 managed-list query returns nothing 22 PASS 4 FAIL exit 1 Both mutants now name the failing target instead of vanishing. Also asserts the standalone loop's copy landed, rather than relying on actionlint's exit 3 for an empty workflows directory to catch it incidentally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 17s —— View job Code Review (Pass 6)
Review: feat(distribution): add sync-managed claude review-lane caller componentsOne commit landed since Pass 5. All Pass 5 findings remain resolved. No new blocking issues. Pass 5 findings — still resolvedThis commit touches only New commit
|
| Gate | Status |
|---|---|
| Gate 1 — actionlint suppression (#284) | ✅ Resolved (merged 2026-07-27) |
Gate 2 — sync App workflows: write |
⏳ Pending external org-owner action |
Gate 3 — workflow name: / ruleset context |
✅ Resolved by scope change (claude-code-plugins → locally-owned) |
Gate 4 — #289 re-pin sync engine at ac223bb |
⏳ Open per PR body (#290 → #289 → #286 ordering) |
The only failing check is do-not-merge / do-not-merge — the intentional label gate.
…true form (#294) ## Summary `components/claude-lanes/claude-review.yml` justified its job-level queue block with a key-level claim: > `queue:` cannot share a concurrency block with cancel-in-progress GitHub's prohibition is **value-level**, not key-level. Verbatim, from the reusable that renders into both `#concurrency` and `#jobsjob_idconcurrency`: > The combination of `queue: max` and `cancel-in-progress: true` is not allowed > and will result in a workflow validation error. Same file, restated in the example prose: > Note that `queue: max` cannot be combined with `cancel-in-progress: true`, > because the two options describe conflicting behaviors for handling > in-progress runs. - Source pinned at the commit read: [`github/docs@336b7f5` `data/reusables/actions/actions-group-concurrency.md`](https://github.com/github/docs/blob/336b7f546d9443dab4e1fa4f0f470e45448c7abc/data/reusables/actions/actions-group-concurrency.md) (lines 20 and 126). - Rendered page carrying the same sentence, confirmed by fetch: <https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#concurrency> `queue: max` + `cancel-in-progress: false` is legal and runs — the key-level wording forbids a shape GitHub permits. The correction narrows the claim to the documented pair and adds the second, independent reason the two blocks stay separate: this group is job-scoped and repo-wide, not per-PR. Without that clause a reader who sets `cancel-in-progress: false` could conclude the blocks may now be merged, which would silently collapse the per-PR supersede group into the repo-wide queue. The authoritative URL is cited **inline, in the tracked bytes** (`565559a`, added after review). Because this component is materialized verbatim downstream, a citation living only in a commit message never reaches the maintainers who read the claim — a correct-but-uncited assertion would repeat this PR's own failure mode at lower severity. The link sits at the sentence that defers to it per `conventions/engineering/documentation-and-citations.md`, and it is a living URL rather than a pinned snapshot because the same convention prefers fetching at read time over storing a snapshot with no recheck trigger. The job-level anchor (`#jobsjob_idconcurrency`) is cited rather than the workflow-level one because the comment annotates a `jobs.<id>.concurrency` block; both anchors were confirmed live to exist on the rendered page and to carry the rule sentence. ### Why this races the open sync PRs This component is sync-managed: its bytes are copied verbatim into every consumer's `.github/workflows/claude-review.yml`. The wording has not landed anywhere yet, and four open sync PRs are carrying it right now. Regenerated from commands, not recalled: ```console $ grep -n '^targets:' distribution/sync-manifest.yml 216:targets: $ awk 'NR>=216' distribution/sync-manifest.yml | grep -c '^ - claude-review-caller$' 5 ``` Managed targets: `claude-code-plugins`, `dotfiles`, `github-iac`, `medley`, `provisioning`. Each consumer's live default-branch file was fetched and counted — the file was read, not grepped on a ref for text expected to be there: | Repo | `queue: max` on default branch | old wording on default branch | open sync PR head carries old wording | | --- | --- | --- | --- | | `claude-code-plugins` | 0 | 0 | no open sync PR | | `dotfiles` | 0 | 0 | #361 (`df035f9`) — yes | | `github-iac` | 0 | 0 | #244 (`0b8124d`) — yes | | `medley` | 0 | 0 | #1676 (`d107862`) — yes | | `provisioning` | 0 | 0 | #231 (`7b9d016`) — yes | Landing this before those PRs merge means the correct text reaches every consumer on first contact. Landing it after means a false statement propagates fleet-wide and needs a second sync to retract. **Hold on the four sync PRs until this merges.** The rollout-window gate is intact — `grep -c '^ automerge: false$' distribution/sync-manifest.yml` returns `8` against `8` total targets — and each of the four PRs was queried live (`gh pr view --json autoMergeRequest`): **none is armed**. So nothing merges them without a human, and a human merging any of them before #294 lands is the only thing that defeats this PR. Merging #294 does **not** require closing them. Verified in the engine at the SHA this repository pins (`ci-workflows@ac223bb`, `.github/workflows/standards-sync.yml:454-461`): it uses `peter-evans/create-pull-request` against a fixed `branch: chore/standards-sync`, which is the head branch on all four PRs — so a subsequent real run refreshes each existing PR in place rather than opening a new one, which is also why the engine guards auto-merge arming on `pull-request-operation == 'created'`. `sync.yml` runs on `push: branches: [main]`, so merging this PR is itself the refresh trigger. ### Sibling component: checked, no change `components/claude-lanes/claude-security-review.yml` was read in full, not assumed to match. It makes no key-level claim. Its one queue-adjacent statement — "a full queue CANCELS new arrivals" — is accurate: > `max`: Up to 100 jobs or workflow runs can be `pending` in the concurrency > group. When the queue is full, any additional jobs or workflow runs are > canceled. A repo-wide grep confirms the defect had exactly one site: ```console $ grep -rn 'share a concurrency block\|cannot share' . | grep -v '^\./\.git/' ./components/claude-lanes/claude-review.yml:87: ... ``` This repository's own `.github/workflows/claude-review.yml` sets no caller-level concurrency and no `queue:`, so it never carried the claim. ### Deliberate non-change Independent verification surfaced a separate omission, not a falsehood: the comment does not mention that `queue: max` caps at 100 pending and cancels arrivals beyond that. Left out on purpose — this PR narrows a false claim and should stay a one-hunk diff while it races the sync PRs. The overflow-wedge argument that makes the cap load-bearing in the security lane does not transfer here, and that was checked rather than assumed: every ruleset on all five managed targets was enumerated and its `required_status_checks` contexts read, and **no target requires any `claude`-named context today**. ```console $ # per target: enumerate rulesets, union their required contexts, count claude ones claude-code-plugins: total_required=4 claude_required=0 dotfiles: total_required=3 claude_required=0 github-iac: total_required=3 claude_required=0 medley: total_required=3 claude_required=0 provisioning: total_required=3 claude_required=0 ``` So overflow cancellation on this lane cannot void a required check — the same premise that already makes this lane's deliberate `cancel-in-progress: true` safe. If a consumer later promotes the code-review context to required, the cap becomes load-bearing and the comment should gain it. Worth a follow-up on its own merits, not a blocker for this one. ## Test plan - `components/claude-lanes/claude-lanes.test.sh`, counted from the run rather than eyeballed, and reproduced by two independent runs: ```console $ bash components/claude-lanes/claude-lanes.test.sh > run.txt 2>&1; echo "exit=$?" exit=0 $ grep -cE '^PASS' run.txt; grep -cE '^FAIL' run.txt 30 0 ``` That includes `[29] a synced lane caller fails actionlint without the suppression` and `[30] control run reports the suppressed message`. Note for anyone re-running: the harness materializes from the git **index**, so the change must be staged or every target reports `source worktree bytes differ from the indexed object` and the suite fails for that reason alone. - Comment-only change: no YAML key, value, group expression, or pin is touched. Confirmed by `git diff --stat origin/main...HEAD` — 6 insertions, 3 deletions, every line inside a `#` comment block. - Independent verification by a fresh-context agent with the rationale withheld (given the final file text and asked whether every claim is true, and whether the constraint is stated at the correct level of generality). Verdict on the corrected wording: **ACCURATE** — "The comment restates the constraint at precisely the docs' generality — the docs name the literal `queue: max` + `cancel-in-progress: true` pair and nothing wider. Neither broader nor narrower. I would not reword this clause." It also independently confirmed `queue:` is valid at job level, via `data/reusables/actions/jobs/section-using-concurrency-jobs.md` including the same reusable. - Repository CI on this PR: every check in the `pass` bucket, zero non-pass, verified by `gh pr checks 294 --json name,bucket` on each pushed head (`a2a8228` and `565559a`). That includes the Claude review lane (`review / review`) — run once per head, never re-run to chase green. ## Related - #286 — the PR that introduced these caller components and the wording corrected here. - Open sync PRs carrying the pre-correction text: melodic-software/dotfiles#361, melodic-software/provisioning#231, melodic-software/github-iac#244, melodic-software/medley#1676. - Two sibling sites for the same claim live in `ci-workflows` and are tracked there (ci-workflows#296 for the workflow; ci-workflows#302, merged, for `PLAN.md`). Deliberately untouched by this PR. No linked issue. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary Unparks `claude-security-review-caller` for private adopters (path (b) from #377), with `melodic-software/provisioning` as the first managed target. - Move `cancel-in-progress: false` to job-level concurrency on `security-review` so the caller can coexist with `concurrency-policy` (`delegated-job-level` shape). - Manage the component for `provisioning` in the sync manifest. - Rewrite parking docs so the remaining public/shared-shape blockers from #377 stay recorded (cross-repo routing kind, blanket public-target test, plugins evidence/`skip-actors`) without filing a follow-up issue. - Align the `claude-code-plugins` locally-owned removal trigger with that same three-part bar. Fixes #377 ## Related - Issue #377 scoping note (private unpark vs public/shared-shape blockers) - `distribution/README.md` Claude review-lane caller parking prose - Prior parking introduction in #286 ## Verification - `node --test --test-name-pattern='claude lane|managed target of a claude|selector-routed claude' components/runner-policy/runner-policy.test.mjs` — 4/4 pass - Standalone actionlint of materialized security caller — clean - `bash distribution/sync-manifest.sh validate` — valid - Clean-config apply to a provisioning stand-in materializes both lane callers ## Follow-through (same session) - Merge sync PR into provisioning after this lands - Ship repo-owned `.github/claude-security-paths` starter in provisioning - Do **not** add `security-review / security-review` as a required check yet (frontier consensus: observe advisory first) <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-3f079f31-8a5f-4063-9a7a-5e4dbe0563a5?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-3f079f31-8a5f-4063-9a7a-5e4dbe0563a5&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Summary
Adds two components —
claude-review-callerandclaude-security-review-caller— sourcing thin workflow callers for theci-workflows reusable Claude review lanes at
.github/workflows/claude-review.ymland.github/workflows/claude-security-review.yml, pinned at the v0.9.1release SHA (
c136b27f404dd32ce3873f39a6f3443891d1c16e). This is Phase 3a ofthe ci-workflows claude-review-lanes plan
(
docs/topics/claude-review-lanes/PLAN.md).REVISED — pin is v0.9.1, not v0.9.0
v0.9.0 (
cf666f67) was tagged BEFOREpaths-filemerged. The security callerpasses
paths-file, which v0.9.0's reusable does not declare — Actionshard-fails a
workflow_callthat passes an undeclared input, and on thesecurity lane that wedges a required check permanently. Verified by reading
each reusable's
workflow_callblock at the pinned SHA (git show <sha>:<path>, notmain, not the tag) and diffing the caller'swith:/secrets:keys against it in both directions, plus everyneeds.<job>.outputs.<name>read against the reusable's declared outputs.Result at v0.9.1: 0 undeclared inputs, 0 undeclared secrets, 0 omitted
required inputs, 0 undeclared output reads, across all four
uses:includingboth
select-runner.ymlcalls. Negative control at v0.9.0 reproduces thedefect.
REVISED — these components are PRIVATE-ONLY; the security caller is PARKED
The callers resolve the runner through the governed
select-runnerindirection, and
runner-policyadmits that selector only for a privateself-hosted consumer (
routingEnabled = visibility === "private" && selfHostedCi). The!routingEnabledbranch consults neitherexceptionsnorlocalRoutingGrants, so a PUBLIC target has no configuration escape.Auditing each component against a public consumer config using the shipped
policy.jsonyields fourpublic-self-hosted-routingfindings; the sameaudit passes clean for a private self-hosted consumer. Consequences:
claude-review-callerismanagedfor the four PRIVATE targets that runthe lane: dotfiles, github-iac, medley, provisioning.
melodic-software/claude-code-pluginsis PUBLIC and is nowlocally-ownedfor both callers, keeping its hand-written hosted-only callers. Had the
original targeting shipped, its own
runner-policylane — and with itci-status— would have gone red, wedging the one repo whose rulesetrequires
security-review / security-review.claude-security-review-callertherefore has no managed target and isrecorded as PARKED, not scoped: both repos running a security lane today
(claude-code-plugins, ci-workflows) are public. It is retained rather than
deleted because its bytes are the reviewed shape for the one lane whose
check can be a required context. It unparks when a private repo adopts the
security lane, or when the runner indirection moves inside the ci-workflows
reusable so one component serves both visibilities.
Three tests in
components/runner-policy/runner-policy.test.mjshold this andwere each proven non-vacuous by reintroducing the defect: a selector-routed
caller may not be
managedfor a public target; every caller component mustaudit clean for a private self-hosted consumer; a selector-routed caller is
expected to be rejected outright on a public one.
REVISED — accepted loss:
synchronizeAll four managed targets' live callers carry
synchronize; the componentdrops it. That is the plan's deliberate cadence cut (review on
open/ready/reopen; re-run the job for a fresh review), not drift — but it is
the largest behavior change the component makes and is now recorded as an
accepted loss for every target, alongside medley's
paths-ignore. Thesecurity lane KEEPS
synchronize: its check certifies execution against thelatest head.
REVISED — validation coverage gap closed
components/claude-lanes/holds workflow bytes, but every workflow-shapedlane in this repo discovers files under
.github/workflows— so actionlint,zizmor, concurrency-policy, and pin-comment-convention were all blind to
these components, and
lint:runner-policyscans the repo's own workflows,not
components/. Nothing validated them.Three lanes now cover them in CI, each proven non-vacuous:
pin-comment-convention takes an explicit file list and scans
components/claude-lanes/*.yml(a mismatched SHA claim fails it).zizmor names both components alongside
.; itspathsinput iswhitespace-separated. Verified that a bare
.never reached them, that theyare audited now, and that the finding total, severity breakdown, and exit
code are identical to baseline — so what the lane gates on is unchanged.
actionlint, through a new materialization contract test
(
components/claude-lanes/claude-lanes.test.sh, wired into theactionlintjob). It runs the entrypoint a consumer runs rather than linting the
component in place:
sync-manifest.sh applyinto a scratch checkout carryingthe target's origin identity, then actionlint over the result. Every managed
target is covered, plus each component's bytes standalone at their
destination path so the PARKED security caller — which no target manages, so
no target loop reaches it — is linted too. A control run with the suppression
config removed still reports the
concurrency.queuemessage, so the casecannot go vacuous unnoticed when concurrency: Add support for
queuekey rhysd/actionlint#654 ships upstream.Scope of that coverage, stated precisely: the test is hermetic and makes
no network call, so it lints each target's materialized bytes under THIS
repository's canonical actionlint config — not under the target's own. For
github-iacandmedley, which ownactionlintlocally and so receive noconfig from the manifest, the canonical config always substitutes. If either
deleted its own queue suppression, this test would still pass. What the test
owns is that the shipped bytes lint clean under a conforming config at the
destination path the manifest maps. That the four live configs actually
conform is gate item 1 below — a live check, re-verified independently, and
it is what covers this gap.
Still blind, deliberately:
concurrency-policyfails both components on threerules whose values are load-bearing (lane-scoped group names; the security
caller's deliberate
cancel-in-progress: false). No target repo runsconcurrency-policy, so nothing fails today; the conflict is documented at thecomponent sources so a future adopter exempts rather than normalizes.
Caller shape derives from the reusables' canonical-caller headers (the SSOT):
review/security-review— required-check name continuity(
security-review / security-reviewon claude-code-plugins' ruleset).[opened, ready_for_review, reopened]; security triggersadd
synchronize; no workflow-level path filtering on the security caller(a non-triggering required check wedges forever).
contents: read,pull-requests: write,id-token: writeon the lane job).select-runnerindirection (selector job,needs, runner input)with the runner-policy recovery fallback
'ubuntu-24.04'— never aself-hosted label, never
vars.CI_HOSTED_RUNNER.paths-file: .github/claude-security-paths(shipped in ci-workflows#282).
CLAUDE_CODE_OAUTH_TOKENpassed explicitly; neversecrets: inherit.private-only revision above; the shipped targeting is
claude-review-callerin dotfiles, github-iac, medley, provisioning, with claude-code-plugins
locally-ownedfor both callers andclaude-security-review-callerparked.
.githuband ci-runner stay exempt (plan approval record item 1);knowledge-corpus and songwriting are commented follow-ups gated on the sync
App access grant.
distribution/README.md's three workflow-caller-exclusion statements gaincarve-outs pointing at a new authoritative section: hand-written lane
callers empirically drifted (medley's missing
reopenedtrigger, divergentskip-actors lists, pin skew v0.6.1 / e295107). AGENTS.md and
governance-process.md were grepped for restatements of the exclusion — none
exist. Cross-doc reconciliation self-review performed per
distribution/governance-process.md: the three README statements plus thenew section are the complete reconciliation surface, and no other normative
doc was left contradicting the change.
DO NOT MERGE — gate status (REVISED)
2026-07-27T14:24Z. Every consumer lints its own workflows with pinned
actionlint 1.7.12, which rejects the review caller's
concurrency.queuekey (concurrency: Add support for
queuekey rhysd/actionlint#654); without the distributed suppression, eachcaller sync PR would fail its consumer's required
ci-statuscheck.Verified live: all four managed targets'
.github/actionlint.yamlsuppress the message for
claude-review.yml(dotfiles and provisioning byglob, github-iac and medley by explicit path).
workflows: write(Phase 3a0) — SATISFIED. Verifiedlive:
gh api orgs/melodic-software/installationsreports installation144867070(melodic-standards-sync) holding{contents: write, issues: write, metadata: read, pull_requests: write, workflows: write}. Writing.github/workflows/files in target repos needsthat permission; without it every sync PR from this component would fail.
automerge: falserollout window) — SATISFIED. Merged2026-07-29T13:05Z and merged into this branch, resolving the one conflict at
the
claude-code-pluginstarget.ac223bb) — OPEN. ci(distribution): disarm fleet automerge for the Phase 3d rollout window #290's stated mergeorder is ci(distribution): disarm fleet automerge for the Phase 3d rollout window #290 → ci: re-pin the standards-sync engine at ac223bb #289 → feat(distribution): add sync-managed claude review-lane caller components #286. The App grant above is inert until the engine
re-pin lands, because
mainstill pinsci-workflows@0b45b9f, whichpredates the
permission-workflows: writemint.Beyond that ordering, the only failing check on this PR is
do-not-merge / do-not-merge— the intentional label gate, which the labelowner lifts.
Concurrency decision record
Shipped the 2e-documented shape from the reusables' headers: review caller =
workflow-level per-PR cancel group plus a separate job-level
queue: maxrepo-wide group; security caller = per-PR group with
cancel-in-progress: falseand no queue. github-iac's live callerdeliberately omits caller-level concurrency, claiming (i) caller-level
cancel reintroduces skipped-actor cancellation and (ii) a group-name
collision with the reusable's job group. Inspection at v0.9.0:
claude-review-<PR>-<headSHA>(claude-review.yml:261 at v0.9.0); thecaller groups are
claude-review-<PR>(cancel) andclaude-review-<owner/repo>(queue). No name equality, so the historicalcaller/inner deadlock — real when the inner group was
claude-review-<pr-number>exactly; provisioning's caller commentdocuments the observed "deadlock was detected" error from that era —
cannot recur. github-iac's and provisioning's comments describe a
pre-v0.9.0 inner-group shape.
workflow-level group does evaluate before any job
if, so a skip-actorevent on the same PR cancels an in-flight review. With no
synchronizetrigger the same-PR event surface is
opened/ready_for_review/reopened— rare and human-driven. The reusable's 2e header documentsthis exact caller value as canonical and per-lane deliberate.
The component sources record both rationales inline; the 3c smoke exercises
this exact shape.
Paths-file seeding disposition
The manifest has no seed-once mechanism (schema v2: components are
unconditional source-to-dest maps), and
.github/claude-security-pathsisrepo-owned tuning that must NOT become managed bytes. Disposition:
adopter commits its starter list via a repo-local PR at adoption time.
locally-ownedfor both callers and receives no sync PR, so no migrationordering applies. (Its
.github/claude-security-pathsalready exists —733 bytes, live — and its hand-written caller keeps its inline
paths:list.)
seedfile class inschema v3 — materialized only when absent at the target, never
reconciled — which preserves repo ownership after first sync.
Deviations / notes for the 3c smoke
skip-actorsis not passed by the callers: the reusable's default at thepinned
c136b27is already the normalized self-trigger-ban list(
dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot],claude-review.yml:150); existing callers passed it only because thepre-v0.9.0 default was narrower. Ownership therefore moves from the caller
to the reusable, and
conventions/review/ai-review-bot-composition.mdis reconciled to say so —it previously attributed the
melodic-standards-sync[bot]exclusion to thecaller's
skip-actorsinput, wiring this component no longer has.'ubuntu-24.04'(dotfiles and github-iaccurrently use
melodic-ubuntu-24.04-x64, which violates the runner-policyrecovery contract in
distribution/README.mdand would queue forever on apublic repo).
class=runnermarker) is a comment, not aTODO(#issue): no dedicated issue exists; the incident-aggregatoracceptance test (ci-workflows#238) exercises it in Phase 4.
caller's selector job fails or skips (a caller-job skip is a new state for
the
security-review / security-reviewrequired context onclaude-code-plugins; on that public repo the selector routes hosted-only,
so the exposure is infra-failure only).
paths-ignoretuning is an accepted loss (plan approval recorditem 10), noted at its manifest target entry.
Test plan
REVISED — as run at
a4d7ee6:npm run test:runner-policy— 242/242 pass, including four new gates (eachproven non-vacuous by reintroducing its defect in a scratch copy): a
selector-routed caller may not be
managedfor a public target; everycaller component must audit clean for a private self-hosted consumer; a
selector-routed caller is rejected outright on a public one; and every
managed target of any caller must be one runner-policy admits (written as a
property, so legitimate unparking via a private consumer passes).
npm run lint:runner-policy,npm run lint:md,bash distribution/sync-manifest.sh validate(
Manifest valid: 34 components, 8 targets) all pass.uses:read atthe pinned SHA: 0 failures, 0 warnings. Negative control at v0.9.0
reproduces the
paths-filedefect.policy.json: private self-hostedconsumer passes clean; public consumer produces four
public-self-hosted-routingfindings. Negative control against thepre-
fa2e7a5policy produces eight findings, proving the new policyentries load-bearing.
c136b27ande77f0126both resolveselect-runner.ymlto blob6ba7d60c.baseline. Extended pin-comment-convention scan: exit 0.
its
.github/runner-policy.jsonand its full live workflow set fetched viathe API, then the synced caller dropped in: all four (dotfiles, github-iac,
medley, provisioning) report
Runner policy passed.both before and after,so the caller introduces no finding at any target.
ci-status,Runner policy,distribution,actionlint,zizmor,concurrency-policy, andpin-comment-convention. The sole failure isdo-not-merge / do-not-merge, which is the intentional label gate.REVISED — after merging
main(#290) and closing the five review threads:claude-code-pluginstarget, the oneplace both parents insert after
- typos. Purely additive over each parent:git diff origin/main -- distribution/sync-manifest.yml | grep '^-'and thesame against
af85ae0each emit only the---header, zero deletion lines,so ci(distribution): disarm fleet automerge for the Phase 3d rollout window #290's comment above
targets:and every component list survive.yq: 8 of 8 targets carryautomergeas a!!boolfalse; the count oftargets whose value is not boolean
falseis0.sync-manifest.sh matrixemits it as a JSON boolean for all 8 (a quoted string would be truthy).
managed/locally-ownedlists byte-identical toaf85ae0(95 entries); components block unchanged.
bash distribution/sync-manifest.sh validate→Manifest valid: 34 components, 8 targets, exit 0.bash distribution/sync-manifest.test.sh→ 147 passed, 0 failed.npm run test:runner-policy→ 242/242 against the merged manifest;npm run lint:runner-policy→Runner policy passed.bash harness/shell/run-tests.sh components/claude-lanes/claude-lanes.test.sh components/actionlint/actionlint.test.sh→ 2 files, 2 passed, 0 failed.green. Two independent ways it could have shrunk silently — the managed-list
query returning nothing, and the per-component membership test ceasing to
match — were each reintroduced in throwaway copies outside the worktree and
both now surface as FAIL with a non-zero exit, where the second previously
dropped four assertions and still exited 0. Per-target assertion counts are
asserted, not assumed.
--rcfile .shellcheckrc -x), actionlint 1.7.12 over the repo,zizmor 1.26.1 over the repo plus both components (
No findings to report),markdownlint, typos, and editorconfig-checker all clean on the changed set.
Original pre-revision plan:
bash distribution/sync-manifest.sh validatereportedManifest valid: 33 components, 8 targets.yq eval -o=json distribution/sync-manifest.yml | node distribution/validate-sync-manifest.mjspasses.
sync-manifest.sh planover all five affected targets shows exactly theintended additions (claude-review-caller in all five; the security caller
in claude-code-plugins only; automerge unchanged).
layout WITH ci-workflows' approved queue-suppression config exits 0; the
negative control without the config reports exactly the one expected
concurrency.queuesyntax-check finding on the review caller.gitleaks, markdownlint) green on commit.
Related
melodic-software/ci-workflowsdocs/topics/claude-review-lanes/PLAN.md(approval record items 1, 9, 10).workflows: writegrant (org-owner action,lands via github-iac).
claude-review.yml/claude-security-review.ymlat v0.9.1(
c136b27f404dd32ce3873f39a6f3443891d1c16e).paths-fileinput), ci-workflows#278 (empty selectoroutput — motivates the hosted fallback), ci-workflows#238 (Phase 4
acceptance test covering selector-failure surfacing).
No linked issue.
🤖 Generated with Claude Code