23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
209 lines
18 KiB
Markdown
209 lines
18 KiB
Markdown
# ADR-127 — GitHub Skills, Agents, and Init-Template Modernization: CI Guards, Supply-Chain Review, and Provenance
|
||
|
||
**Status**: Proposed (2026-05-21)
|
||
**Date**: 2026-05-21
|
||
**Authors**: claude (drafted with rUv)
|
||
**Related**: ADR-102 (plugin-hook-CLI flag regression CI guard), ADR-103 (witness temporal history), ADR-118 (AIDefence 2.3.0), ADR-125 (memory consolidation), ADR-126 (neural-trader substrate), ruflo issues #2017, #1922, #2078, #2086
|
||
**Supersedes**: nothing — extends the CI smoke pattern established by ADR-102 to the `.github` skills/agents surface
|
||
|
||
## Context
|
||
|
||
The `.github`-related surface in ruflo ships in two layers:
|
||
|
||
1. **Dogfood layer** — `.claude/skills/github-*/SKILL.md` (5 skills: code-review, multi-repo, project-management, release-management, workflow-automation), `.claude/agents/github/*.md` (13 agents), `.claude/helpers/github-{safe.js,setup.sh}`. These drive our own workflow daily.
|
||
|
||
2. **Init-template layer** — `v3/@claude-flow/cli/.claude/commands/github/*.md` (19 command files), `v3/@claude-flow/cli/.claude/helpers/github-{safe.js,setup.sh}`. These are materialized verbatim into every user project by `ruflo init`.
|
||
|
||
Both layers were last substantively updated on 2025-10-19 — seven months before this ADR. In the same period the project shipped ~10 targeted CI guard + supply-chain hardening PRs: #1922 (CWE-347 plugin registry signature), #2017 (pre-bash hook silent exit 0), #2046 (5-layer supply-chain audit), #2060 (Ed25519 CWE-347 pattern formalization), #2079 (attribution opt-in), #2086 (ruvllm WASM auto-init). None of those fixes touched the `.github` surface.
|
||
|
||
A read-only review of both layers reveals four categories of gap:
|
||
|
||
### Category A: Third-party Actions are mutable floating refs
|
||
|
||
Every workflow snippet in the skills and init-template commands uses `actions/checkout@v3` and `actions/setup-node@v3`. Both are mutable floating tags. `scripts/audit-supply-chain.mjs` (introduced in #2046, ADR-102 supply-chain hardening) tracks CVE + lockfile + allowlist + typosquat across npm packages but has zero coverage of the GitHub Actions surface. The `github-project-management` skill also recommends `ruvnet/swarm-action@v1` (skill line 976) — a first-party action that is also unpinned. The existing `smoke-plugin-registry-signature.mjs` proves that static-contract checks on text files are fast and reliably catch regressions; the same pattern applied to `uses:` lines would require under 30 lines of JavaScript.
|
||
|
||
### Category B: Unquoted GitHub event fields in `swarm-pr.md` / `swarm-issue.md`
|
||
|
||
`.claude/agents/github/swarm-pr.md` line 57 and its init-template copy interpolate `${{ github.event.comment.body }}` directly into a shell `if [[ ... ]]` test and a subsequent `--comment "${{ ... }}"` argument. Any PR comment containing shell metacharacters (`\`...\``, `$(...)`, `;`) would expand under that interpolation in a `pull_request` or `issue_comment` triggered workflow. The `github-project-management` skill has a correct "Security Considerations" section (skill lines 17-25) warning explicitly against this pattern, but the two agents implementing the warning do not follow it. `github-safe.js` (`.claude/helpers/github-safe.js`) already demonstrates the correct mitigation: write the body to a temp file and pass `--body-file`. The gap is that the agents don't route through that helper.
|
||
|
||
The class of failure — **silent success when the check was never run** — matches #2017 exactly: the pre-bash hook exited 0 on dangerous payloads not because the block was absent but because a TypeError was silently swallowed. Any future refactor of the GitHub agents faces the same swallow risk unless there is a behavioral smoke.
|
||
|
||
### Category C: No `tools:` restrictions on 12 of 13 agents
|
||
|
||
The `github-project-management` skill is the single artifact with `allowed-tools:` in its frontmatter. All other skills and all 13 agents omit the field entirely, meaning they run with the full tool namespace when materialized. This includes `WebFetch`, which can be pointed at any URL — including URLs embedded in issue bodies or PR descriptions.
|
||
|
||
The fix for #2017 added behavioral checks that drive real input payloads against both the dogfood and published-template copies of `hook-handler.cjs`. No analogous check exists for `github-safe.js`.
|
||
|
||
### Category D: Attribution footers are not gated on `--attribution` opt-in
|
||
|
||
`v3/@claude-flow/cli/src/init/settings-generator.ts` lines 55-60 (introduced in #2079) implement opt-in `Co-Authored-By` using a no-reply bot email. The 19 GitHub command files embed hardcoded `"🤖 Generated with Claude Code"` footers in PR/issue body templates, unconditionally. A user who runs `ruflo init` without `--attribution` still gets attribution injected into every PR or issue the GitHub agents post on their behalf.
|
||
|
||
## Decision
|
||
|
||
Land a four-phase modernization of the `.github` skills/agents/helpers/init-template surface, following the same deliver-a-runnable-artifact-per-phase discipline as ADR-125 and ADR-126.
|
||
|
||
### Phase 1 — Static-contract smokes and supply-chain extension (small; ships first)
|
||
|
||
**`scripts/smoke-github-safe-injection.mjs`** (new)
|
||
|
||
Generalizes `smoke-pre-bash-hook.mjs` (introduced for #2017) to the GitHub helper surface. Drives adversarial bodies through `github-safe.js` and asserts the body lands in the temp file verbatim, not shell-expanded. Runs against both copies:
|
||
- `.claude/helpers/github-safe.js` (dogfood)
|
||
- `v3/@claude-flow/cli/.claude/helpers/github-safe.js` (init template)
|
||
|
||
Test cases: body containing backticks, body containing `$()`, body containing semicolons, body longer than 256KB (must be rejected, not truncated), empty body (no-op path, exit 0).
|
||
|
||
**`scripts/smoke-github-actions-pins.mjs`** (new)
|
||
|
||
Static scan of three trees:
|
||
- `.claude/agents/github/*.md`
|
||
- `.claude/skills/github-*/SKILL.md`
|
||
- `v3/@claude-flow/cli/.claude/commands/github/*.md`
|
||
|
||
For every `uses:` line, asserts the ref is either (a) SHA-pinned (`uses: owner/repo@<40-hex>`) or (b) listed in a new `actions` key added to `.github/supply-chain/allowed-deps.json`. First offense: list the violating file + line. Fails on any violation. Zero runtime deps — pure `readFileSync` + regex.
|
||
|
||
**`.github/supply-chain/allowed-deps.json`** — add:
|
||
```json
|
||
"actions": {
|
||
"allowed": ["actions/checkout", "actions/setup-node", "pnpm/action-setup"],
|
||
"minimumVersion": {"actions/checkout": "v4", "actions/setup-node": "v4"}
|
||
}
|
||
```
|
||
|
||
**`v3-ci.yml` changes**
|
||
|
||
Add two smoke jobs gated by path filters:
|
||
```yaml
|
||
# Phase 1 smokes — gate on .github surface changes
|
||
on:
|
||
push:
|
||
paths:
|
||
- '.claude/agents/github/**'
|
||
- '.claude/skills/github-*/**'
|
||
- 'v3/@claude-flow/cli/.claude/commands/github/**'
|
||
- '.claude/helpers/github-safe.js'
|
||
- 'v3/@claude-flow/cli/.claude/helpers/github-safe.js'
|
||
- 'scripts/smoke-github-*.mjs'
|
||
- '.github/supply-chain/allowed-deps.json'
|
||
```
|
||
|
||
Acceptance: both smokes exit 0 in CI on first commit.
|
||
|
||
### Phase 2 — Helper hardening and agent frontmatter (small)
|
||
|
||
Generalizes the `github-safe.js` hardening pattern and the `hook-handler.cjs` per-copy discipline to the remaining helpers.
|
||
|
||
**`github-safe.js` (both copies)** — add explicit `maxBuffer` cap (256KB, matching GitHub API `body` field limit) and reject oversized bodies before writing the temp file. Add a `GITHUB_SAFE_VERSION` constant so the injection smoke can assert the correct copy is installed. Add `set -e` equivalent (strict error handling) for the shell fallback path.
|
||
|
||
**`github-setup.sh` (both copies)** — add `set -euo pipefail`. Replace `gh auth status &> /dev/null` with a check that parses the output for scope sufficiency. Comment the security rationale.
|
||
|
||
**13 agent frontmatter `tools:` lines** — add explicit tool restrictions. Reference pattern: the `code-review-swarm.md` agent should list `Bash, Read, Grep, Glob, mcp__claude-flow__*` and explicitly omit `WebFetch`, `Write`. The `release-manager.md` and `release-swarm.md` agents may include `Write` (for CHANGELOG) but not `WebFetch`. No agent that processes GitHub-hosted content (issue bodies, PR descriptions, label names) should have `WebFetch` in its tool list.
|
||
|
||
**`swarm-pr.md` and `swarm-issue.md` (both dogfood and init-template copies)** — replace the unquoted `${{ github.event.comment.body }}` and `${{ github.event.label.name }}` interpolations with temp-file indirection following the `github-safe.js` pattern. Specifically:
|
||
```bash
|
||
# Before (vulnerable)
|
||
if [[ "${{ github.event.comment.body }}" == /swarm* ]]; then
|
||
npx ruv-swarm github handle-comment --comment "${{ github.event.comment.body }}"
|
||
|
||
# After
|
||
COMMENT_BODY_FILE=$(mktemp)
|
||
printf '%s' "${{ github.event.comment.body }}" > "$COMMENT_BODY_FILE"
|
||
if grep -q '^/swarm' "$COMMENT_BODY_FILE"; then
|
||
npx ruv-swarm github handle-comment --comment-file "$COMMENT_BODY_FILE"
|
||
fi
|
||
rm -f "$COMMENT_BODY_FILE"
|
||
```
|
||
|
||
Ties directly to the `github-safe.js` temp-file pattern already documented in `.claude/helpers/`.
|
||
|
||
Acceptance: `smoke-github-safe-injection.mjs` passes with the hardened copies; `grep -r 'github.event.comment.body.*--comment' .claude/agents/ v3/@claude-flow/cli/.claude/` returns no matches.
|
||
|
||
### Phase 3 — Action pin upgrade and deprecated-action smoke (small)
|
||
|
||
**`scripts/smoke-github-release-no-deprecated-action.mjs`** (new)
|
||
|
||
Asserts that no file in scope references:
|
||
- `actions/create-release@*` (archived)
|
||
- `softprops/action-gh-release@v1` (mutable floating)
|
||
|
||
And positively asserts that any workflow snippet containing `release` in its job name uses `gh release create` (the `gh` CLI path) rather than an Actions-based release step.
|
||
|
||
**Upgrade `@v3` → `@v4`** in all affected files: 5 agent files (`release-manager.md`, `release-swarm.md`, `swarm-pr.md`, `workflow-automation.md`, `repo-architect.md` — plus their init-template copies) and both skill files (`github-release-management/SKILL.md`, `github-code-review/SKILL.md`). This is a mechanical find-and-replace; the smoke in Phase 1 will catch future regressions.
|
||
|
||
The `ruvnet/swarm-action@v1` reference in `github-project-management/SKILL.md` (line 976) is either SHA-pinned to the current HEAD of that repo or replaced with the equivalent inline `run:` step. A SHA pin is preferred — it satisfies the allow-list check without removing the reference.
|
||
|
||
Acceptance: `smoke-github-actions-pins.mjs` passes against all files in scope; `smoke-github-release-no-deprecated-action.mjs` passes.
|
||
|
||
### Phase 4 — Attribution gate and `last-updated` stamps (small)
|
||
|
||
**Init-template command files** — gate the `🤖 Generated with` footer on the `attribution` option that `settings-generator.ts` already exposes. This is implemented via a new `ATTRIBUTION_FOOTER` variable in `helpers-generator.ts` that is set to an empty string by default and to the bot-identity string when `options.attribution === true`. Each of the 19 command files references the variable rather than hardcoding the footer.
|
||
|
||
This follows the precedent from `settings-generator.ts` lines 55-60 (introduced in #2079 / #2078). The bot email `ruflo-bot@users.noreply.github.com` applies to commit trailers; the PR/issue body footer should use `🤖 Generated with [RuFlo](https://github.com/ruvnet/ruflo)` (same PR footer already in `settings-generator.ts:60`) when attribution is opted in, and no footer otherwise.
|
||
|
||
**`Last Updated` stamps** on the four skill SKILL.md files are updated from 2025-10-19 to the date this ADR ships. A comment in each file notes: "update this date when you change the skill so the smoke can detect stale copies." This is convention, not enforced — no smoke needed.
|
||
|
||
Acceptance: `ruflo init` (without `--attribution`) produces command files with no `🤖` footer string in any PR/issue body template; `ruflo init --attribution` produces the `RuFlo` bot-identity footer.
|
||
|
||
## Why this is the right shape of fix
|
||
|
||
All four phases are connect-the-existing-pieces work:
|
||
|
||
- Phase 1 generalizes `smoke-pre-bash-hook.mjs` (pattern proven by #2017) to a new surface. No new architecture.
|
||
- Phase 2 generalizes the `github-safe.js` temp-file pattern (already shipped) to the agents that should have been using it. No new design.
|
||
- Phase 3 is a mechanical pin upgrade gated by a static smoke. The upgraded version (`@v4`) is already what our own `v3-ci.yml` uses (lines 154, 158, 163 of the workflow).
|
||
- Phase 4 mirrors the existing `settings-generator.ts` attribution logic into the helper generator. No new concept.
|
||
|
||
**Init-template constraint**: anything added to `ruflo init` output must work in an empty directory with only `node` and `gh` available. All four phases satisfy this: static markdown files and `.js`/`.sh` helpers — no new runtime dependencies installed into user projects.
|
||
|
||
## Consequences
|
||
|
||
### Positive
|
||
|
||
- `smoke-github-safe-injection.mjs` catches the class of regression (#2017 shape) on the GitHub helper surface for the first time.
|
||
- `smoke-github-actions-pins.mjs` catches any future commit that copies old `@v3` snippets from blog posts into the skill files — a very common doc-drift pattern.
|
||
- The `swarm-pr.md` / `swarm-issue.md` fix closes a prompt-injection path that could cause the GitHub Actions bot to execute attacker-controlled shell in a `pull_request` triggered workflow. The `github-project-management` skill already warned about this (skill lines 17-25); the gap was enforcement.
|
||
- Attribution opt-in is now consistent: `settings-generator.ts` and the command file footers both respond to the same `--attribution` flag.
|
||
- The supply-chain `allowed-deps.json` `actions` key extends an existing format that contributors already know how to update.
|
||
|
||
### Negative / trade-offs
|
||
|
||
- ADR-127 adds 3 new CI smoke jobs that gate every PR touching `.claude/agents/github/`, `.claude/skills/github-*/`, or `v3/@claude-flow/cli/.claude/commands/github/`. Purely-editorial PRs to those files (e.g. fixing a typo in a skill description) now run the smoke suite. The smokes are fast (static file reads, no npm install beyond root node_modules), but they add ~20–30 seconds to the pre-merge wall clock for any contributor touching those trees.
|
||
- The `ruvnet/swarm-action@v1` SHA-pin requires looking up the current HEAD SHA of that repo and re-doing this whenever the action is updated. Mitigation: add a note in `.github/supply-chain/allowed-deps.json` next to the entry.
|
||
- Phase 2's `tools:` frontmatter additions are a behavioral change for any consumer who has configured `ruflo init` output and relies on `WebFetch` being available in GitHub agents. The downgrade is intentional and documented, but it is a breaking change on the init-template surface.
|
||
- Phase 4's attribution gate is only enforced at `init` time. A user who edits their materialized command files after `init` can re-add the footer manually. This is acceptable — the commitment is to the default (`init` output without `--attribution` contains no footer), not to preventing all possible edits.
|
||
|
||
### Neutral
|
||
|
||
- The `@v3` → `@v4` pin upgrade is compatible with all runner images in `v3-ci.yml` (already running `@v4` for the main CI jobs). No runner image changes needed.
|
||
- The `github-setup.sh` `set -euo pipefail` addition is compatible with bash 3.x (macOS default), which the helper's current shebang (`#!/bin/bash`) already requires.
|
||
|
||
## Implementation Plan
|
||
|
||
| Phase | Deliverable | Files | Effort | Acceptance |
|
||
|---|---|---|---|---|
|
||
| 1 | Injection smoke + Actions pin smoke + CI wiring + supply-chain update | `scripts/smoke-github-safe-injection.mjs` (new), `scripts/smoke-github-actions-pins.mjs` (new), `.github/supply-chain/allowed-deps.json`, `.github/workflows/v3-ci.yml` | S | Both smokes exit 0 in CI; `v3-ci.yml` path filters trigger on agent/skill changes |
|
||
| 2 | Helper hardening + agent frontmatter + unquoted interpolation fix | `github-safe.js` × 2, `github-setup.sh` × 2, 13 agent files × 2 (dogfood + init-template) | M | `smoke-github-safe-injection.mjs` passes hardened copies; grep for unquoted interpolation returns 0 matches |
|
||
| 3 | Deprecated-action smoke + `@v3` → `@v4` pin upgrade | `scripts/smoke-github-release-no-deprecated-action.mjs` (new), 6 agent files × 2, 2 skill files | S | Both new smokes pass; `smoke-github-actions-pins.mjs` passes all in-scope files |
|
||
| 4 | Attribution gate in init-template + skill `Last Updated` stamps | `v3/@claude-flow/cli/src/init/helpers-generator.ts`, 19 command files, 4 skill files | S | `ruflo init` without `--attribution` produces no `🤖` footer; `ruflo init --attribution` produces bot-identity footer |
|
||
|
||
Recommended landing: Phases 1 + 3 in one PR (pure static checks + mechanical upgrades — no behavioral change). Phase 2 in its own PR (behavioral change to agents and helpers — needs explicit review). Phase 4 in its own PR (init-template behavior change).
|
||
|
||
## Acceptance Criteria
|
||
|
||
The ADR is considered fulfilled when all of the following hold against `@claude-flow/cli@3.8.0-alpha.X`:
|
||
|
||
1. `node scripts/smoke-github-safe-injection.mjs` exits 0 with adversarial bodies against both handler copies.
|
||
2. `node scripts/smoke-github-actions-pins.mjs` exits 0: all `uses:` refs in scope are SHA-pinned or on the allow-list.
|
||
3. `node scripts/smoke-github-release-no-deprecated-action.mjs` exits 0: no deprecated action references survive.
|
||
4. `grep -r 'github.event.comment.body.*--comment\|github.event.label.name.*[^"]' .claude/agents/github/ v3/@claude-flow/cli/.claude/commands/github/` returns 0 matches.
|
||
5. All 13 agent files have explicit `tools:` frontmatter; none include `WebFetch` in the set.
|
||
6. `ruflo init` (no `--attribution`) produces 19 command files with no `🤖 Generated with` string in any PR/issue body template.
|
||
7. `v3-ci.yml` path filters include `.claude/agents/github/**`, `.claude/skills/github-*/**`, and `v3/@claude-flow/cli/.claude/commands/github/**`.
|
||
8. No regression in the existing `pre-bash-hook-smoke`, `plugin-registry-signature-smoke`, or `ruvllm-wasm-auto-init-smoke` CI jobs.
|
||
|
||
## Out of Scope (Deferred)
|
||
|
||
- **ADR-128**: Provenance / witness signing for `gh release create` artifacts — generalizing the ADR-103 witness pattern to the release pipeline. This ADR establishes that the helper surface must _not_ recommend deprecated Actions; it does not yet mandate Ed25519-signed release artifacts.
|
||
- **ADR-129**: GitHub Apps token scoping — a separate decision about whether `ruflo init`-materialized workflows should request `contents: read` by default rather than inheriting the ambient `GITHUB_TOKEN` scopes. Gated on a broader permissions-minimization ADR.
|
||
- **ADR-130**: SBOM generation in the release workflow. The supply-chain audit covers npm dep CVEs; an SBOM is a separate artifact type requiring a separate decision on format (SPDX vs CycloneDX) and storage location.
|
||
- **Dynamic action pinning automation** (`dependabot.yml` or Renovate configuration for `uses:` refs) — net-new pattern with no prior art in this repo. Phase 3 does the manual upgrade; automation is a follow-on.
|