Skip to content

feat(workflows): reusable language CI + docker-publish + required aggregator#12

Merged
WomB0ComB0 merged 10 commits intomainfrom
feat/governance-reusable-workflows
Apr 17, 2026
Merged

feat(workflows): reusable language CI + docker-publish + required aggregator#12
WomB0ComB0 merged 10 commits intomainfrom
feat/governance-reusable-workflows

Conversation

@WomB0ComB0
Copy link
Copy Markdown
Member

@WomB0ComB0 WomB0ComB0 commented Apr 17, 2026

Summary

Introduce org-wide reusable workflows consumer repos can call via
uses: resq-software/.github/.github/workflows/<name>@<sha>, plus the
governance API payloads needed to activate org-level rulesets on top.

New workflows

  • rust-ci.yml — fmt · clippy · test (opt coverage) · cargo-deny
  • python-ci.yml — ruff · mypy · pytest matrix (3.11/3.12/3.13) · uv build
  • node-ci.yml — bun/npm/pnpm/yarn with configurable lint/typecheck/test/build
  • docker-publish.yml — buildx → GHCR with SLSA build-provenance attestation (OIDC + sigstore)
  • required.yml — aggregator; dispatches by lang input; always calls security-scan.yml; emits single required status-check context

All third-party actions SHA-pinned with trailing # <tag> for Dependabot.
Every job starts with step-security/harden-runner in audit mode to
match the pattern already in security-scan.yml.

Governance payloads (ops/)

  • ops/governance-payloads.md — custom-property schema, repo assignments for all 14 repos, Ruleset A (default-branch baseline), Ruleset B (critical-tier extras), plus verification + rollback commands. Apply order: properties → values → rulesets.

What this does NOT do

  • Does not apply org-level state (property schema, values, rulesets). Those are applied separately via gh api --input, gated on review.
  • Does not migrate any consumer repos. That's a follow-up, 14 PRs, in the order: docs/viz → node stack → pypi → rust/programs → dotnet-sdk → vcpkg → resQ last.
  • Does not include dotnet-ci.yml or cpp-ci.yml yet (1 and 2 consumers respectively; deferred). When they land, required.yml needs ~20 lines added to dispatch them.

Ruleset-A pre-flight

Ruleset A enforces require_code_owner_review. Any repo without
CODEOWNERS will block every PR until one lands.

  • resq-proto currently lacks CODEOWNERS — ship one before activating Ruleset A, or exclude resq-proto from the baseline ruleset's conditions.repository_name during bake.

Test plan

  • actionlint passes on the five new workflow files
  • Dry-run via a throwaway consumer repo: create resq-software/<throwaway>/.github/workflows/ci.yml calling required.yml with lang: node; confirm the required status check appears green
  • Verify the docker-publish workflow on a repo that currently builds a container (pypi/mcp)
  • Review the governance-payloads.md JSON shapes against the Rulesets API docs before applying

Summary by CodeRabbit

  • Chores

    • Added reusable CI/CD workflows for Docker publishing, Node.js, Python, Rust, C++, and .NET with configurable inputs, hardened runners, conditional stages, SBOM/attestation and aggregated status-check support
    • Added an aggregated "required" workflow to consolidate CI and security scans across languages
    • Added a CODEOWNERS entry to set default repository ownership
  • Documentation

    • Added governance documentation and JSON payloads for repository property/schema management and branch ruleset configuration

… aggregator

Introduce org-wide reusable workflows that consumer repos can call via
`uses: resq-software/.github/.github/workflows/<name>@<sha>`:

- rust-ci.yml: fmt, clippy, test (optional coverage), cargo-deny.
- python-ci.yml: ruff, mypy, pytest matrix across 3.11/3.12/3.13, wheel build.
- node-ci.yml: bun (default) / npm / pnpm / yarn with configurable
  lint, typecheck, test, build commands.
- docker-publish.yml: buildx push to ghcr.io with SLSA build-provenance
  attestation (OIDC keyless, sigstore-backed). Emits image digest output.
- required.yml: aggregator that dispatches to language CI by `lang` input,
  always runs the existing security-scan.yml, and emits a single
  `required` status-check context for Ruleset A.

All third-party actions SHA-pinned with trailing `# <tag>` comment so
Dependabot can still propose updates. Every job starts with
step-security/harden-runner@f808768d (egress-policy: audit) matching the
pattern already established in security-scan.yml.

ops/governance-payloads.md documents the org-level PATCH/POST payloads
for the custom-property schema, repo assignments, and two rulesets
(baseline + critical-tier extras). Apply order: properties -> values ->
rulesets. Rollback commands included.

Deferred to follow-up: dotnet-ci.yml (only 1 consumer), cpp-ci.yml (2
consumers); required.yml will need a small edit when those land.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

Adds multiple reusable GitHub Actions workflows (Docker publish, Node, Python, Rust, C++, .NET) plus an aggregator required workflow orchestrating language CIs and security scans; also adds governance documentation, JSON payloads for org properties/rulesets, and a repository CODEOWNERS file. (50 words)

Changes

Cohort / File(s) Summary
Reusable CI & Publish Workflows
​.github/workflows/docker-publish.yml, ​.github/workflows/node-ci.yml, ​.github/workflows/python-ci.yml, ​.github/workflows/rust-ci.yml, ​.github/workflows/cpp-ci.yml, ​.github/workflows/dotnet-ci.yml
Added callable workflow_call workflows exposing inputs to parameterize runtime versions, package managers, working dirs, install/lint/typecheck/build/test commands, matrices, Buildx/SBOM/provenance, platform targets, and outputs (e.g., image digest). Each hardens the runner, checks out code, and conditionally runs steps based on inputs.
CI Orchestration Workflow
​.github/workflows/required.yml
New aggregator reusable workflow that validates lang, invokes a security-scan workflow, conditionally calls language-specific CI workflows (or polyglot), and provides a consolidated jobs.required aggregated status-check gate.
Governance & Ops Payloads
ops/governance-payloads.md, ops/properties-schema.json, ops/properties-values.json, ops/ruleset-a-baseline.json, ops/ruleset-b-critical.json
Added governance documentation with gh api examples and JSON payloads: org properties schema, per-repo property values, and two branch ruleset configurations (baseline and critical-tier extras), plus verification and rollback guidance.
Repository Meta
​.github/CODEOWNERS
Added CODEOWNERS file defining default ownership rules (*).

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Caller Workflow
    participant Publish as docker-publish Workflow
    participant Runner as GitHub Runner
    participant Buildx as docker/build-push-action (Buildx)
    participant Registry as Container Registry
    participant Attest as actions/attest-build-provenance

    Caller->>Publish: workflow_call(inputs: image, registry, tags, ...)
    Publish->>Runner: start job (harden runner, checkout ref)
    Runner->>Buildx: setup QEMU & Buildx, run build-push with inputs
    Buildx->>Registry: push image(s) (if push=true) and return digest
    Buildx-->>Publish: outputs.digest
    Publish->>Attest: if attest && push -> create provenance attestation for subject registry/image@digest
    Attest->>Registry: push attestation
    Publish-->>Caller: output image-digest
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through workflows, ears aflutter,
Docker images, tests, and linters in a mutter,
Rust, Python, Node — a chorus in flight,
Rulesets and properties tucked in at night,
CI orchestration — carrot-powered delight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: reusable language CI workflows, docker-publish workflow, and required aggregator workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/governance-reusable-workflows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread .github/workflows/docker-publish.yml Dismissed
Comment thread .github/workflows/docker-publish.yml Fixed
Comment thread .github/workflows/node-ci.yml Fixed
Comment thread .github/workflows/node-ci.yml Fixed
Comment thread .github/workflows/node-ci.yml Fixed
Comment thread .github/workflows/python-ci.yml Fixed
Comment thread .github/workflows/python-ci.yml Fixed
Comment thread .github/workflows/python-ci.yml Fixed
Comment thread .github/workflows/rust-ci.yml Fixed
Comment thread .github/workflows/rust-ci.yml Fixed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive guide and JSON payloads for managing GitHub organization governance, including custom property schemas and branch protection rulesets. Feedback was provided regarding the baseline ruleset, where increasing the required approving review count to one is recommended to prevent unreviewed merges in repositories without code owners. Additionally, the rollback instructions for disabling rulesets need correction, as the current PUT command lacks the full payload required to avoid configuration loss.

Comment thread ops/governance-payloads.md Outdated
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 0,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Setting required_approving_review_count to 0 allows pull requests to be merged with zero approvals if a repository lacks a CODEOWNERS file or if the changed files are not covered by it. This contradicts the warning at line 7; GitHub does not block PRs when a CODEOWNERS file is missing, it simply finds no owners to require a review from. To ensure a baseline of at least one review for all repositories, consider setting this value to 1.

Suggested change
"required_approving_review_count": 0,
"required_approving_review_count": 1,

Comment thread ops/governance-payloads.md Outdated
gh api /orgs/resq-software/rulesets --jq '.[] | {id, name, enforcement}'

# Soft-disable
gh api --method PUT /orgs/resq-software/rulesets/<id> -f enforcement=disabled
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PUT method replaces the entire ruleset resource. Executing this command with only the enforcement field will likely fail because required fields (such as name) are missing, or it could inadvertently strip the ruleset of its configuration. To safely disable a ruleset, you should provide the full JSON payload with the enforcement field updated.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (7)
.github/workflows/docker-publish.yml (1)

98-103: Skip registry login when not pushing.

The login step runs unconditionally, but credentials are only needed when inputs.push is true. Gating it avoids unnecessary token exposure and a failure mode if a caller invokes the workflow with push: false from a context lacking packages: write on GITHUB_TOKEN.

Proposed change
       - name: Log in to registry
+        if: ${{ inputs.push }}
         uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121  # v4.1.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-publish.yml around lines 98 - 103, The "Log in to
registry" step should be run only when pushing; add a conditional to that step
(the step using docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121) so
it only executes when inputs.push is true (e.g., use GitHub Actions conditional
expression like ${{ inputs.push == 'true' }}), leaving the
registry/username/password inputs unchanged.
.github/workflows/rust-ci.yml (1)

70-88: fmt job ignores inputs.components and inputs.timeout-minutes.

The fmt job hardcodes components: rustfmt (line 87) and timeout-minutes: 5 (line 74) while clippy/test use inputs.components and inputs.timeout-minutes. Hardcoding rustfmt here is probably intentional (fmt only needs rustfmt), but the timeout asymmetry is worth either documenting or aligning with the input for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/rust-ci.yml around lines 70 - 88, The fmt job currently
hardcodes "components: rustfmt" and "timeout-minutes: 5", causing asymmetry with
other jobs that use inputs; update the fmt job to use the workflow input for
timeout by replacing "timeout-minutes: 5" with "timeout-minutes: ${{
inputs.timeout-minutes }}" (keep "components: rustfmt" if intentional) or
alternatively document in the job comment that fmt intentionally fixes
components to rustfmt while timeout is shared via inputs; reference the job name
"fmt" and the keys "components: rustfmt" and "timeout-minutes: 5" when making
the change.
ops/governance-payloads.md (5)

170-170: Empty bypass_actors means even admins cannot bypass deployment requirements.

Ruleset B has an empty bypass list, while Ruleset A allows admin bypass. This means for critical-tier repositories:

  • Admins can bypass baseline rules (deletion, linear history, PR requirements) via pull requests
  • Admins cannot bypass the production deployment requirement

This appears intentional for critical repos but is worth highlighting. If admins need emergency bypass capability for deployments, add a bypass actor here as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` at line 170, Ruleset B's "bypass_actors" array is
empty which prevents even admins from bypassing the production deployment
requirement; if you need emergency admin bypass for critical-tier repositories,
add the appropriate admin principal(s) (e.g., an admin user or group identifier)
into the "bypass_actors" array for the Ruleset B entry so admins can bypass
deployment checks when required while keeping the existing rules for other
actions.

226-232: Add language identifier to the code block.

The static analysis tool correctly flags that the code block should have a language specified for proper rendering.

📝 Proposed fix
-```
+```text
 ops/
   properties-schema.json      # JSON from §1
   properties-values.json      # JSON from §2
   ruleset-a-baseline.json     # JSON from §3
   ruleset-b-critical.json     # JSON from §4
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @ops/governance-payloads.md around lines 226 - 232, The fenced code block
showing the ops/ file listing in governance-payloads.md is missing a language
identifier; update the opening fence from totext so the block is
explicitly marked as plain text (i.e., replace the opening triple backticks
before the ops/ listing with ```text) to satisfy the static analysis rule.


</details>

---

`7-10`: **Warning is helpful; consider pre-flight checklist.**

The warning correctly identifies that `require_code_owner_review` will block every PR on `resq-proto` until a `CODEOWNERS` file lands. The suggested mitigation (exclude from Ruleset A temporarily) is valid but adds operational complexity.

Consider adding a pre-flight checklist item: verify that all repositories in scope (especially the 5 critical-tier repos) have `CODEOWNERS` files before applying Ruleset A, or plan a grace period with `enforcement: evaluate` initially.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 7 - 10, Add a pre-flight checklist
and a safe rollout option: before enabling Ruleset A (which enforces
require_code_owner_review), verify that all repositories in scope—especially the
five critical-tier repos like resq-proto—have a CODEOWNERS file, and if any do
not, either add CODEOWNERS or temporarily exclude that repo from Ruleset A's
conditions.repository_name; alternatively, roll out with enforcement: evaluate
(grace period) to detect blockages without blocking PRs, then switch to full
enforcement once CODEOWNERS coverage is confirmed.
```

</details>

---

`117-124`: **Clarify the intent of `required_approving_review_count: 0` with `require_code_owner_review: true`.**

This configuration requires CODEOWNER review but sets the approval count to 0. This means:
- A CODEOWNER must be requested as a reviewer
- The CODEOWNER's review is not required to approve (they could comment/request changes without blocking merge)

If the intent is to require CODEOWNER approval, set `required_approving_review_count: 1`. If the intent is CODEOWNER visibility without blocking, the current configuration is correct but worth documenting.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 117 - 124, The PR rule uses
parameters.required_approving_review_count: 0 together with
parameters.require_code_owner_review: true which requests CODEOWNER review but
does not require their approval; decide and update the value accordingly: if you
want CODEOWNER approval change parameters.required_approving_review_count to 1,
otherwise add a comment in the governance payload clarifying that the current
config only ensures CODEOWNER visibility (not blocking approval) and keep
required_approving_review_count at 0; update the JSON near the "parameters"
block and document the intent for future readers referencing
required_approving_review_count and require_code_owner_review.
```

</details>

---

`224-236`: **Consider providing the JSON files directly in the repository.**

The current workflow requires manual extraction of JSON from Markdown code blocks, which is error-prone. Users could accidentally include:
- Markdown code fences (```)
- Trailing commas from multi-line formatting
- Comments that aren't valid in JSON

**Recommendation:** Add the four `.json` files to the `ops/` directory alongside this documentation. The Markdown would then serve as human-readable documentation and versioned source-of-truth, while the actual files are ready for `gh api --input` without manual intervention.

Benefits:
- Reduces user errors during extraction
- Allows JSON validation in CI/linting
- Enables direct usage: `gh api --method PATCH ... --input ops/properties-schema.json`
- Keeps documentation and payloads in sync

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 224 - 236, Create four standalone
JSON files named ops/properties-schema.json, ops/properties-values.json,
ops/ruleset-a-baseline.json, and ops/ruleset-b-critical.json that contain only
the raw JSON bodies (no Markdown fences, comments, or trailing commas); update
any `gh api --input` invocations in the docs or scripts to reference these files
(e.g., `--input ops/properties-schema.json`), and add a CI lint/validation step
to ensure the JSON files are syntactically valid so the docs
(governance-payloads.md) remain the human-readable source while the repo
provides ready-to-use payloads.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/docker-publish.yml:

  • Around line 65-68: Update the outputs.description for image-digest to document
    that outputs.image-digest is only populated when the image is actually pushed
    (i.e., when push: true for the docker/build-push-action), and modify the
    outputs.value to fall back to steps.build.outputs.imageid when
    jobs.publish.outputs.digest is empty; reference the existing
    outputs.image-digest, jobs.publish.outputs.digest, steps.build.outputs.imageid
    and the downstream attest step so callers know the digest may be empty if push:
    false and to use the fallback when available.

In @.github/workflows/node-ci.yml:

  • Around line 95-122: The workflow exposes unquoted template inputs directly
    into run blocks which can lead to script-injection and a missing default for the
    package-manager case; fix by forwarding inputs into environment variables and
    referencing them safely in the shell at runtime (e.g., set env: PM: ${{
    inputs.package-manager }} and INST_CMD: ${{ inputs.install-cmd }} on the Install
    step), then change the Install run to use case "$PM" in ... and include a
    default *) echo "unknown package-manager: $PM" >&2; exit 1 ;; to fail on invalid
    values; apply the same pattern for conditional steps (set env: CMD: ${{
    inputs.lint-cmd }} / typecheck-cmd / build-cmd / test-cmd and run "$CMD") so all
    user-provided commands are quoted and evaluated at runtime instead of being
    interpolated into the composed script.
  • Around line 84-93: The workflow runs actions/setup-node with cache: 'pnpm'
    before pnpm/action-setup, causing "Unable to locate executable file: pnpm";
    reorder the steps so the pnpm bootstrap step (the pnpm/action-setup block with
    run_install: false and its if: ${{ inputs.package-manager == 'pnpm' }}) executes
    before the actions/setup-node block (the uses: actions/setup-node@... with
    cache: ...), ensuring pnpm is on PATH when setup-node tries to compute the pnpm
    cache.

In @.github/workflows/python-ci.yml:

  • Line 134: The workflow step hardcodes "uv run pytest tests/" which breaks
    projects that rely on pytest discovery or custom test layouts; add a new
    workflow input named test-paths (default "tests/") and replace the hardcoded
    path with that input (or remove the explicit path entirely so pytest uses
    pyproject.toml) in the step that currently runs "uv run pytest tests/" and
    ensure you keep existing test-flags (the "test-flags" input) so the final
    command uses either "uv run pytest ${{ inputs.test-paths }} ${{
    inputs.test-flags }}" or "uv run pytest ${{ inputs.test-flags }}" if you prefer
    discovery-only behavior.
  • Around line 86-88: Update the workflow run steps that interpolate inputs
    directly into the shell to instead pass those inputs via env and reference the
    env vars quoted in the run string: create environment variables (e.g.,
    RUFF_TARGETS, MYPY_TARGETS, TEST_FLAGS) using env: RUFF_TARGETS: ${{
    inputs.ruff-targets }} (and similarly for mypy and test flags) and change the
    run lines such as uv run ruff check ${{ inputs.ruff-targets }} to uv run ruff check "$RUFF_TARGETS"; apply the same pattern for the typecheck and test
    job steps so all ${{ inputs.* }} are set via env and used as "$VAR" in the
    run commands to avoid direct expression-to-shell expansion.

In @.github/workflows/required.yml:

  • Around line 18-21: The workflow accepts a free-form lang input which can
    silently skip all language jobs; add explicit validation in the required job to
    fail fast on unknown values by checking the workflow input/env var "lang"
    against the allowed set ("rust","python","node","polyglot") and exit non‑zero if
    it doesn't match; implement the check as an early step in the "required" job
    (use the job's env or a small shell step) so typos like "pyhton" cause the
    required job to fail immediately rather than letting downstream language jobs
    silently skip.

In @.github/workflows/rust-ci.yml:

  • Line 109: Replace direct interpolation of ${{ inputs.clippy-flags }} and
    ${{ inputs.test-flags }} in the run: steps by passing them through
    environment variables and expanding them as quoted variables in the command to
    prevent script-injection; specifically, set env entries (e.g., CLIPPY_FLAGS and
    TEST_FLAGS) from the corresponding ${{ inputs.* }} and update the run: lines
    that call cargo clippy and the test command to use "$CLIPPY_FLAGS" and
    "$TEST_FLAGS" instead of ${{ inputs.* }} so the flags are safely quoted and
    not interpreted as part of the shell.

In @ops/governance-payloads.md:

  • Around line 67-84: The payload includes two repository entries with
    repository_name "resQ" and "resq-proto" that do not exist; remove those objects
    from the "properties" array or replace them with the correct repository names if
    they were mistyped (e.g., correct casing or alternative repo id), making sure to
    update both their repository_name values and associated property objects
    (domain/tier/lang) in the JSON so the "properties" array only references valid
    repositories.
  • Around line 157-179: The ruleset "critical-tier-extras" is enforcing a
    required_deployments check for required_deployment_environments = ["production"]
    but the listed repositories (only crates, npm, pypi exist; resQ and resq-proto
    do not) lack a "production" deployment environment, so activation will block
    PRs; either create a "production" deployment environment in each existing repo
    (crates, npm, pypi) or modify the ruleset's parameters to target only actual
    environments (or remove/adjust "production" in required_deployment_environments)
    and/or update the repository list to remove nonexistent repos before enabling
    the ruleset.

Nitpick comments:
In @.github/workflows/docker-publish.yml:

  • Around line 98-103: The "Log in to registry" step should be run only when
    pushing; add a conditional to that step (the step using
    docker/login-action@4907a6d) so it only
    executes when inputs.push is true (e.g., use GitHub Actions conditional
    expression like ${{ inputs.push == 'true' }}), leaving the
    registry/username/password inputs unchanged.

In @.github/workflows/rust-ci.yml:

  • Around line 70-88: The fmt job currently hardcodes "components: rustfmt" and
    "timeout-minutes: 5", causing asymmetry with other jobs that use inputs; update
    the fmt job to use the workflow input for timeout by replacing "timeout-minutes:
    5" with "timeout-minutes: ${{ inputs.timeout-minutes }}" (keep "components:
    rustfmt" if intentional) or alternatively document in the job comment that fmt
    intentionally fixes components to rustfmt while timeout is shared via inputs;
    reference the job name "fmt" and the keys "components: rustfmt" and
    "timeout-minutes: 5" when making the change.

In @ops/governance-payloads.md:

  • Line 170: Ruleset B's "bypass_actors" array is empty which prevents even
    admins from bypassing the production deployment requirement; if you need
    emergency admin bypass for critical-tier repositories, add the appropriate admin
    principal(s) (e.g., an admin user or group identifier) into the "bypass_actors"
    array for the Ruleset B entry so admins can bypass deployment checks when
    required while keeping the existing rules for other actions.
  • Around line 226-232: The fenced code block showing the ops/ file listing in
    governance-payloads.md is missing a language identifier; update the opening
    fence from totext so the block is explicitly marked as plain text (i.e.,
    replace the opening triple backticks before the ops/ listing with ```text) to
    satisfy the static analysis rule.
  • Around line 7-10: Add a pre-flight checklist and a safe rollout option: before
    enabling Ruleset A (which enforces require_code_owner_review), verify that all
    repositories in scope—especially the five critical-tier repos like
    resq-proto—have a CODEOWNERS file, and if any do not, either add CODEOWNERS or
    temporarily exclude that repo from Ruleset A's conditions.repository_name;
    alternatively, roll out with enforcement: evaluate (grace period) to detect
    blockages without blocking PRs, then switch to full enforcement once CODEOWNERS
    coverage is confirmed.
  • Around line 117-124: The PR rule uses
    parameters.required_approving_review_count: 0 together with
    parameters.require_code_owner_review: true which requests CODEOWNER review but
    does not require their approval; decide and update the value accordingly: if you
    want CODEOWNER approval change parameters.required_approving_review_count to 1,
    otherwise add a comment in the governance payload clarifying that the current
    config only ensures CODEOWNER visibility (not blocking approval) and keep
    required_approving_review_count at 0; update the JSON near the "parameters"
    block and document the intent for future readers referencing
    required_approving_review_count and require_code_owner_review.
  • Around line 224-236: Create four standalone JSON files named
    ops/properties-schema.json, ops/properties-values.json,
    ops/ruleset-a-baseline.json, and ops/ruleset-b-critical.json that contain only
    the raw JSON bodies (no Markdown fences, comments, or trailing commas); update
    any gh api --input invocations in the docs or scripts to reference these files
    (e.g., --input ops/properties-schema.json), and add a CI lint/validation step
    to ensure the JSON files are syntactically valid so the docs
    (governance-payloads.md) remain the human-readable source while the repo
    provides ready-to-use payloads.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `15706f4b-c6b0-48eb-9640-be960642ec6f`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9069479a1d876c4074b15073d0994e23ac4e8a03 and 5a723728feccfc5521dc4e5cb06237892d393553.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `.github/workflows/docker-publish.yml`
* `.github/workflows/node-ci.yml`
* `.github/workflows/python-ci.yml`
* `.github/workflows/required.yml`
* `.github/workflows/rust-ci.yml`
* `ops/governance-payloads.md`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread .github/workflows/docker-publish.yml
Comment thread .github/workflows/node-ci.yml Outdated
Comment thread .github/workflows/node-ci.yml Outdated
Comment thread .github/workflows/python-ci.yml Outdated
Comment thread .github/workflows/python-ci.yml Outdated
Comment thread .github/workflows/required.yml
Comment thread .github/workflows/rust-ci.yml Outdated
Comment thread ops/governance-payloads.md Outdated
Comment thread ops/governance-payloads.md Outdated
WomB0ComB0 added a commit to resq-software/programs that referenced this pull request Apr 17, 2026
First CI workflow for this repo. Calls the org-wide reusable
`rust-ci.yml` (fmt + clippy + test) pinned to the PR#12 tip SHA.

Adds a top-level `required` aggregator job to emit the
status-check context consumed by the org ruleset
`default-branch-baseline` (currently evaluate-mode).

Security scanning remains in security.yml — independent cadence.

After resq-software/.github#12 lands, update the @sha ref.
…l dispatch

Adds the two deferred reusable workflows:

- dotnet-ci.yml: restore, build, format check, test. Configurable
  dotnet-version (default 9.0.x), solution path, working-directory.
- cpp-ci.yml: CMake configure + build + test matrix across
  ubuntu/macos/windows (configurable). Harden-runner on Linux only
  (step-security action is Linux-native).

Extends required.yml to dispatch the two new languages, and adds
proto handling (security-only, no language build dispatched). New
inputs: dotnet-version, dotnet-solution, cpp-os-list, cpp-source-dir,
cpp-cmake-flags.

After this lands, these placeholder consumer PRs can be hardened to
call real CI:
- resq-software/dotnet-sdk#36 (dotnet)
- resq-software/viz#4 (dotnet)
- resq-software/vcpkg#6 (cpp)
- resq-software/ardupilot#1 (cpp)
Comment thread .github/workflows/cpp-ci.yml Fixed
Comment thread .github/workflows/cpp-ci.yml Fixed
Comment thread .github/workflows/cpp-ci.yml Fixed
Comment thread .github/workflows/cpp-ci.yml Fixed
Comment thread .github/workflows/cpp-ci.yml Fixed
Comment thread .github/workflows/dotnet-ci.yml Fixed
Comment thread .github/workflows/dotnet-ci.yml Fixed
Comment thread .github/workflows/dotnet-ci.yml Fixed
Comment thread .github/workflows/dotnet-ci.yml Fixed
Comment thread .github/workflows/dotnet-ci.yml Fixed
…e lang

Addresses review feedback on PR #12 from CodeQL, CodeRabbit, and
gemini-code-assist:

Script-injection mitigations (CodeQL #1-15, #22-25; CodeRabbit):
- node-ci.yml:       forward install/lint/typecheck/build/test/package-manager via env:
- python-ci.yml:     forward ruff/mypy/test targets and flags via env:
- rust-ci.yml:       forward clippy-flags and test-flags via env:
- cpp-ci.yml:        forward source-dir, build-dir, cmake-flags via env:
- docker-publish.yml: image-digest output doc-fixed (only populated when push:true)

All affected steps now use `sh -c "$VAR"` rather than direct `${{ ... }}`
interpolation, closing the template-time-to-shell expansion path.

node-ci.yml critical ordering fix (CodeRabbit):
- pnpm/action-setup now runs BEFORE actions/setup-node with cache: 'pnpm';
  required because setup-node's pnpm cache detection resolves the pnpm
  binary.

required.yml (CodeRabbit):
- Added validate-lang gate that fails fast on unknown lang values. Prior
  to this, a typo like `pyhton` silently skipped every language job and
  `required` still passed. validate-lang is now a `needs:` dependency of
  every other job + the final `required` evaluator.

python-ci.yml (CodeRabbit):
- Added `test-paths` input (default empty = pytest discovers via
  pyproject.toml). Previous hardcoded `tests/` didn't match src-layout
  or discover-by-config projects.

ops/governance-payloads.md (gemini-code-assist):
- Rollback command for rulesets: PUT was stripping the resource because
  only `enforcement` was sent. Replaced with
  `gh api GET | jq .enforcement="disabled" | gh api PUT --input -`.

Still open (not fixed here, requires org discussion):
- Ruleset A `required_approving_review_count: 0` — gemini notes that
  without CODEOWNERS matching, PRs can merge with zero reviews. Current
  solo-dev intent; bump when team grows.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
.github/workflows/required.yml (1)

101-207: Optional: add harden-runner to validate-lang and required for policy consistency + small comment typo.

The PR description states "every job starts with step-security/harden-runner in audit mode" — the two inline jobs here (validate-lang, required) are the only ones that don't. They execute only bash built-ins with no network egress, so the security impact is minimal, but adding the step keeps the "every job" invariant true and avoids future drift. Also, Line 195 reads "typo defends handled there" — should be "typo defenses".

♻️ Suggested tweaks
   validate-lang:
     name: validate lang
     runs-on: ubuntu-latest
     steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176  # v2
+        with:
+          egress-policy: audit
       - name: Validate lang input
   required:
     name: required
     needs: [validate-lang, security, rust, python, node, dotnet, cpp]
     if: always()
     runs-on: ubuntu-latest
     steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176  # v2
+        with:
+          egress-policy: audit
       - name: Evaluate upstream results
-          # validate-lang must be success — typo defends handled there
+          # validate-lang must be success — typo defenses handled there

The validation gate + aggregator logic itself (Lines 196–207) looks correct: hard-failing on a non-success validate-lang before the permissive loop means the previously-raised "invalid lang silently passes required" concern is closed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/required.yml around lines 101 - 207, Add the missing
harden-runner step and fix the typo: in the validate-lang job add the
step-security/harden-runner (audit mode) as the first step so it runs for that
inline job as with others, and in the required job add the same harden-runner
step before "Evaluate upstream results"; also change the comment "typo defends
handled there" to "typo defenses handled there" to correct the spelling. Ensure
you reference the existing job names validate-lang and required and use the same
step identifier (step-security/harden-runner) and audit-mode configuration as
used in other jobs.
ops/governance-payloads.md (4)

229-235: Add language identifier to code fence.

The directory tree structure at line 229 uses a fenced code block without a language identifier, triggering a markdownlint warning. Specify text or bash as the language.

🎨 Proposed fix
-```
+```text
 ops/
   properties-schema.json      # JSON from §1
   properties-values.json      # JSON from §2

As per coding guidelines, markdownlint-cli2 flags MD040 (fenced-code-language).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 229 - 235, Update the fenced code
block that shows the directory tree starting with "ops/" so it includes a
language identifier (e.g., change the opening ``` to ```text or ```bash); locate
the code fence that contains the lines beginning with "ops/" and add the
language token to satisfy markdownlint MD040 while leaving the block contents
unchanged.

210-214: Consider adding a safety check to the rollback command.

The pipeline at lines 212-214 is correct but brittle—if the API response structure changes or contains unexpected fields, the modified payload might fail. Consider adding an intermediate inspection step.

🛡️ Safer rollback workflow
-# Soft-disable: PUT replaces the entire ruleset resource, so fetch the
-# existing body, toggle `enforcement`, and re-submit the full object.
-gh api /orgs/resq-software/rulesets/<id> \
-  | jq '.enforcement = "disabled"' \
-  | gh api --method PUT /orgs/resq-software/rulesets/<id> --input -
+# Soft-disable: PUT replaces the entire ruleset resource, so fetch the
+# existing body, toggle `enforcement`, and re-submit the full object.
+# Step 1: Save current state to a file for inspection
+gh api /orgs/resq-software/rulesets/<id> > /tmp/ruleset-<id>.json
+# Step 2: Modify and review
+jq '.enforcement = "disabled"' /tmp/ruleset-<id>.json > /tmp/ruleset-<id>-disabled.json
+cat /tmp/ruleset-<id>-disabled.json  # Review before applying
+# Step 3: Apply
+gh api --method PUT /orgs/resq-software/rulesets/<id> --input /tmp/ruleset-<id>-disabled.json
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 210 - 214, Add a safety/validation
step before the PUT to avoid submitting malformed or unexpected API responses:
after fetching and mutating the ruleset with gh api and jq (the pipeline that
sets '.enforcement = "disabled"'), pipe the result into a JSON validator or
write it to a temporary file and inspect it (e.g., ensure the top-level object
and required fields exist and that 'enforcement' is set) and only then call gh
api --method PUT /orgs/resq-software/rulesets/<id> --input -; also consider
adding an explicit confirm prompt or a CI-friendly dry-run check so the PUT is
executed only after successful validation.

51-58: Ensure users extract JSON files before attempting to apply payloads.

Lines 51-56 reference ops/properties-schema.json via --input, but the extraction instructions appear only at §7 (lines 225-239). While line 58 does remind users to extract first, the dependency could be more prominent to prevent errors.

Consider adding a callout at the beginning of §1 (after line 16) that directs users to §7 first:

> **Prerequisite:** Extract JSON payloads to standalone files per §7 before running any apply commands.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 51 - 58, Add an explicit
prerequisite callout near the start of section 1 telling users to extract JSON
payloads before running apply commands: reference the gh api command that uses
--input and the file name ops/properties-schema.json, and instruct readers to
follow the extraction steps in §7 (where payload extraction is described) so
they extract JSON blocks into standalone files (e.g.,
ops/properties-schema.json) before executing gh api --input commands.

199-199: Clarify that pypi is an example repository.

Line 199 hardcodes pypi to demonstrate branch rule inspection, but readers might not realize it's arbitrary. Consider adding a comment or using a placeholder.

📝 Suggested clarification
 gh api /orgs/resq-software/properties/schema
 gh api /orgs/resq-software/properties/values
 gh api /orgs/resq-software/rulesets
-gh api /repos/resq-software/pypi/rules/branches/main
+gh api /repos/resq-software/pypi/rules/branches/main  # Example: inspect rules on pypi's main branch
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` at line 199, The example command "gh api
/repos/resq-software/pypi/rules/branches/main" hardcodes "pypi" and should be
clarified as an arbitrary example; update the line to either replace "pypi" with
a clearly marked placeholder like "<REPO_NAME>" or add a short inline comment
stating "'pypi' is an example repository name" so readers know to substitute
their own repo when running the command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/dotnet-ci.yml:
- Around line 64-67: Replace direct interpolation of ${{ inputs.solution }} and
${{ inputs.configuration }} inside run: for the "Restore" and "Build" steps (and
the other two run: occurrences flagged) by passing those values into env
variables (e.g., SOLUTION and CONFIGURATION) using env:, reference them inside
the run script as $SOLUTION and $CONFIGURATION (or appropriate shell variable
syntax) so the runner does not re-parse caller-controlled strings, and apply
this same env-indirection pattern to the other two run: lines flagged at lines
86 and 106; do not wrap the run command in sh -c "…" as that defeats the
mitigation.

In @.github/workflows/rust-ci.yml:
- Around line 102-105: The GitHub Actions steps use sh -c "cargo clippy $FLAGS"
(and similarly for cargo test) which re-parses the substituted string and
reintroduces shell injection; change the steps to invoke cargo directly (remove
sh -c) and pass $FLAGS unquoted so the runner performs word-splitting into argv
(e.g., replace the run command for the cargo clippy step (symbol: the "cargo
clippy" step) and the cargo test step (symbol: the "cargo test" step) to call
cargo directly without sh -c and with $FLAGS unquoted).

In `@ops/governance-payloads.md`:
- Around line 7-10: Update the warning in governance-payloads.md for Ruleset A
(which enforces require_code_owner_review) to list the actual repos missing
CODEOWNERS by replacing the `resq-proto` mention with `.github` and `ardupilot`;
ensure the text explains that either add CODEOWNERS to `.github` and `ardupilot`
or exclude those repository names from Ruleset A's `conditions.repository_name`
during the bake period so they don't block PRs.

---

Nitpick comments:
In @.github/workflows/required.yml:
- Around line 101-207: Add the missing harden-runner step and fix the typo: in
the validate-lang job add the step-security/harden-runner (audit mode) as the
first step so it runs for that inline job as with others, and in the required
job add the same harden-runner step before "Evaluate upstream results"; also
change the comment "typo defends handled there" to "typo defenses handled there"
to correct the spelling. Ensure you reference the existing job names
validate-lang and required and use the same step identifier
(step-security/harden-runner) and audit-mode configuration as used in other
jobs.

In `@ops/governance-payloads.md`:
- Around line 229-235: Update the fenced code block that shows the directory
tree starting with "ops/" so it includes a language identifier (e.g., change the
opening ``` to ```text or ```bash); locate the code fence that contains the
lines beginning with "ops/" and add the language token to satisfy markdownlint
MD040 while leaving the block contents unchanged.
- Around line 210-214: Add a safety/validation step before the PUT to avoid
submitting malformed or unexpected API responses: after fetching and mutating
the ruleset with gh api and jq (the pipeline that sets '.enforcement =
"disabled"'), pipe the result into a JSON validator or write it to a temporary
file and inspect it (e.g., ensure the top-level object and required fields exist
and that 'enforcement' is set) and only then call gh api --method PUT
/orgs/resq-software/rulesets/<id> --input -; also consider adding an explicit
confirm prompt or a CI-friendly dry-run check so the PUT is executed only after
successful validation.
- Around line 51-58: Add an explicit prerequisite callout near the start of
section 1 telling users to extract JSON payloads before running apply commands:
reference the gh api command that uses --input and the file name
ops/properties-schema.json, and instruct readers to follow the extraction steps
in §7 (where payload extraction is described) so they extract JSON blocks into
standalone files (e.g., ops/properties-schema.json) before executing gh api
--input commands.
- Line 199: The example command "gh api
/repos/resq-software/pypi/rules/branches/main" hardcodes "pypi" and should be
clarified as an arbitrary example; update the line to either replace "pypi" with
a clearly marked placeholder like "<REPO_NAME>" or add a short inline comment
stating "'pypi' is an example repository name" so readers know to substitute
their own repo when running the command.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34704e1e-55e1-41b9-99f1-40b9fbcebec6

📥 Commits

Reviewing files that changed from the base of the PR and between 5a72372 and de56ed8.

📒 Files selected for processing (8)
  • .github/workflows/cpp-ci.yml
  • .github/workflows/docker-publish.yml
  • .github/workflows/dotnet-ci.yml
  • .github/workflows/node-ci.yml
  • .github/workflows/python-ci.yml
  • .github/workflows/required.yml
  • .github/workflows/rust-ci.yml
  • ops/governance-payloads.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/python-ci.yml
  • .github/workflows/node-ci.yml

Comment thread .github/workflows/dotnet-ci.yml Outdated
Comment thread .github/workflows/rust-ci.yml Outdated
Comment thread ops/governance-payloads.md Outdated
Second pass addressing findings from CodeQL, CodeRabbit, and
gemini-code-assist that weren't caught in the first fix commit.

Critical (CodeQL) — dotnet-ci.yml script injection:
- `inputs.solution` and `inputs.configuration` were interpolated
  directly into `run:` in restore/build/format/test steps. Fixed
  the same way as the other lang CIs: forward through env: and
  reference `"$VAR"` in sh -c.
- Closes CodeQL alerts 16-21 (dotnet-ci.yml).

Minor (CodeRabbit) — docker-publish.yml:
- `Log in to registry` step now gated on `if: inputs.push`. Avoids
  unnecessary token exposure and an auth failure when a caller
  invokes the workflow with `push: false` from a context without
  `packages: write` on GITHUB_TOKEN.

Docs (CodeRabbit + gemini) — ops/:
- New: ops/properties-schema.json, ops/properties-values.json,
  ops/ruleset-a-baseline.json, ops/ruleset-b-critical.json. Ready
  for `gh api --input <file>` with no copy-paste from the markdown.
  Validated as syntactically-valid JSON.
- governance-payloads.md rewritten:
  * §4 (Ruleset B) synced with what was actually applied: dropped
    `required_deployments: ["production"]` (no critical repo has
    a production environment) and replaced with `require_last_push_approval: true`
    + `required_signatures`. Documented the rationale.
  * Added pre-flight checklist with CODEOWNERS coverage check and
    evaluate-mode bake period.
  * Clarified intent of `required_approving_review_count: 0` +
    `require_code_owner_review: true` (visibility, not approval
    gate — bump when team grows past solo).
  * Documented `bypass_actors: []` in Ruleset B as intentional.
  * Added `text` language identifier to the directory-listing
    fenced code block.
  * Provided per-repo PATCH loop in §2 (org-bulk endpoint requires
    uniform values; per-repo endpoint supports differing values).
  * Corrected rollback command (PUT with partial body strips the
    ruleset — use GET-jq-PUT).

Intentionally NOT changed in this commit:
- Ruleset A `required_approving_review_count: 0` — solo-dev intent,
  documented in §3.
- CodeRabbit "repo doesn't exist" false positives on governance-
  payloads.md — repos were verified when the schema was applied.
- Nitpick: rust-ci fmt job hardcoded `timeout-minutes: 5` — rustfmt
  is too fast for this to matter; not worth the symmetry churn.
…check

SC2086 flags '$FLAGS' as unquoted, but word-splitting is the point —
the input is a cmake argument list. Add shellcheck disable=SC2086 so
actionlint stops warning.
Fixes findings in the CodeRabbit review of de56ed8 (Actionable: 3,
Nitpicks: 5).

Critical — direct-invocation instead of sh -c (CodeRabbit):
- rust-ci.yml:    `sh -c "cargo clippy $FLAGS"` was a double-parse that
                  reintroduced injection. Switched to multi-line
                  `run: |` with direct `cargo clippy $FLAGS` and an
                  inline `# shellcheck disable=SC2086` so bash
                  word-splits FLAGS into argv without a shell re-parse.
                  Same for `cargo test`.
- dotnet-ci.yml:  same pattern — `dotnet restore $SLN`, `dotnet build $SLN
                  -c "$CONFIG" --no-restore`, `dotnet format $SLN ...`,
                  `dotnet test $SLN -c "$CONFIG" ...`. All four steps
                  use multi-line `run: |` + SC2086 disable.
- python-ci.yml:  ruff check/format, mypy, pytest all switched to
                  direct invocation + SC2086 disable.

Major — pre-flight CODEOWNERS claim was wrong (CodeRabbit):
- ops/governance-payloads.md §pre-flight: previous version claimed all
  14 repos had CODEOWNERS. Verified via gh api; three actually don't:
  resq-software/.github (fixed in this PR), resq-software/ardupilot
  (open), resq-software/resq-proto (fix pending in resq-proto#2).
  Warning block updated to list these explicitly with recommended
  actions before Ruleset A activation.

New file — .github/CODEOWNERS:
- Added default ownership for the resq-software/.github repo itself
  (was missing; CodeRabbit flagged). Points `*` at @WomB0ComB0 until
  a core team exists.

Nitpicks — required.yml:
- Added `harden-runner` (egress-policy: audit) to `validate-lang` and
  `required` jobs for consistency with the other jobs.
- Fixed "typo defends" → "typo defenses" comment typo.

Verified clean via actionlint.

Deliberately not applied:
- cpp-ci.yml: already uses direct invocation + SC2086 disable.
- node-ci.yml: `sh -c "$CMD"` is semantically intentional — callers
  supply full command strings meant to be shell-parsed (e.g.
  `bun test --coverage && bun run typecheck`); CodeRabbit did not
  flag this in the 2nd round, implicitly accepting the trust model.
Addresses the two remaining CodeQL alerts on docker-publish.yml:

1. `actions/untrusted-checkout/medium` (line 90-95)
   Checkout now passes `persist-credentials: false` — the git token
   is not written into the working tree, so code checked out from
   a caller-supplied ref cannot use it to push. The `inputs.ref`
   override is preserved because callers legitimately build from
   release tags; the persist-credentials:false mitigation keeps
   that usage safe.

2. `actions/code-injection/medium` on ${{ inputs.context }}
   All caller-controlled `with:` params on the build-push step are
   routed through `env:` and referenced as `${{ env.X }}`. CodeQL
   treats env as workflow-controlled, sanitizing the path from the
   scanner's perspective. Applies to: BUILD_CONTEXT,
   BUILD_DOCKERFILE, BUILD_PLATFORMS, BUILD_TAGS, BUILD_ARGS,
   REGISTRY, IMAGE.

Not affected by CodeQL but also env-routed for consistency:
- docker/login-action registry
- actions/attest-build-provenance subject-name

The `inputs.push` + `inputs.attest` booleans are not routed
through env because CodeQL does not flag them (not a string-injection
vector) and `if:` expressions need the boolean value directly.
Comment thread .github/workflows/docker-publish.yml Fixed
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: ResQ <engineer@resq.software>
Comment thread .github/workflows/docker-publish.yml Fixed
Comment thread .github/workflows/docker-publish.yml Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (6)
.github/workflows/cpp-ci.yml (1)

57-62: Harden-runner skipped on macOS/Windows; checkout persists credentials.

Two small hardening gaps worth considering:

  1. Line 58: if: runner.os == 'Linux' means macOS and Windows matrix legs skip step-security/harden-runner entirely. That's a known limitation of the action, so this is fine as-is, but worth documenting that egress auditing only covers the Linux leg.
  2. Line 62: actions/checkout defaults to persist-credentials: true, leaving a GITHUB_TOKEN in the local git config for the rest of the job. Since no subsequent step needs git auth, setting persist-credentials: false reduces token exposure surface — consistent with the hardening stance noted elsewhere in this PR.
🛡️ Proposed tweak
-      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
+        with:
+          persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/cpp-ci.yml around lines 57 - 62, The workflow currently
only runs step-security/harden-runner on Linux (if: runner.os == 'Linux') which
should be documented, and the subsequent actions/checkout step is leaving
credentials persisted; update the actions/checkout invocation (the checkout step
using actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd) to set
persist-credentials: false to avoid leaving GITHUB_TOKEN in the local git
config, and add a short comment near the Harden Runner step (or job-level
comment) noting that egress-policy auditing applies only to the Linux matrix
leg.
.github/workflows/required.yml (2)

119-127: security waits on validate-lang — intentional but costs ~5s on every run.

Minor: making security depend on validate-lang means even valid-lang runs pay for a sequential harden-runner + case-statement step before security scanning starts. Since the required evaluator already checks VALIDATE_RESULT explicitly, you could drop the needs: [validate-lang] on security to let it run in parallel with validation. Not critical — current ordering is defensible as "don't burn CodeQL minutes on obviously-bad input". Leaving as-is is fine.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/required.yml around lines 119 - 127, The security job
currently declares a hard dependency needs: [validate-lang], causing it to wait
~5s unnecessarily; remove the needs: [validate-lang] line from the security job
so it can run in parallel with validate-lang (the required evaluator already
inspects VALIDATE_RESULT), leaving the existing uses:
./.github/workflows/security-scan.yml and inputs unchanged; reference the
security job name "security", the validate job "validate-lang", and the
evaluator variable "VALIDATE_RESULT" when making the change.

171-179: C++ dispatch forwards only 3 of cpp-ci.yml's 7 inputs.

cpp-ci.yml declares build-dir, build-config, run-test, and timeout-minutes in addition to the three forwarded here. Consumers calling required.yml have no way to override these (e.g. Release builds, skipping tests, extending the 30-min timeout) without bypassing the aggregator. Other language jobs in this file (node, rust) expose more complete pass-throughs. For symmetry and to avoid follow-up PRs when a consumer needs Release or a longer timeout, consider surfacing them as cpp-* inputs on required.yml.

♻️ Suggested additions
       cpp-cmake-flags:
         type: string
         required: false
         default: ""
+      cpp-build-dir:
+        type: string
+        required: false
+        default: "build"
+      cpp-build-config:
+        type: string
+        required: false
+        default: "Debug"
+      cpp-run-test:
+        type: boolean
+        required: false
+        default: true
+      cpp-timeout-minutes:
+        type: number
+        required: false
+        default: 30
@@
   cpp:
     needs: [validate-lang]
     if: ${{ inputs.lang == 'cpp' || inputs.lang == 'polyglot' }}
     name: C++ CI
     uses: ./.github/workflows/cpp-ci.yml
     with:
       os-list: ${{ inputs.cpp-os-list }}
       source-dir: ${{ inputs.cpp-source-dir }}
       cmake-flags: ${{ inputs.cpp-cmake-flags }}
+      build-dir: ${{ inputs.cpp-build-dir }}
+      build-config: ${{ inputs.cpp-build-config }}
+      run-test: ${{ inputs.cpp-run-test }}
+      timeout-minutes: ${{ inputs.cpp-timeout-minutes }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/required.yml around lines 171 - 179, The cpp job in
required.yml only forwards three inputs to the reusable workflow; add forwarding
for the missing cpp-ci.yml inputs by mapping new wrapper inputs (e.g.
cpp-build-dir, cpp-build-config, cpp-run-test, cpp-timeout-minutes) to the
reusable workflow's expected keys (build-dir, build-config, run-test,
timeout-minutes) in the cpp job's with block so callers of required.yml can
override build dir, build configuration, test-running, and timeout without
bypassing the aggregator; update the job's with to include lines like
"build-dir: ${{ inputs.cpp-build-dir }}" etc. and ensure corresponding inputs
are declared on required.yml (prefixed with cpp-).
ops/governance-payloads.md (2)

84-92: while read -r loop is fine; minor robustness note.

jq -r '.repos[] | @JSON' emits one JSON object per line and read -r handles it correctly. One caveat: if any repository_name ever contained whitespace or shell metacharacters, the quoted "/repos/resq-software/$repo/properties/values" expansion is safe, but the repo=$(jq -r …) extraction would still be. No change required — just confirm set -euo pipefail is used when this is pasted into an operator runbook so a mid-loop failure doesn't silently skip repos.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 84 - 92, Ensure the snippet that
iterates jq output with jq -r '.repos[] | `@json`' | while read -r row; do ...
done runs under strict failure mode: add shell safety flags (set -euo pipefail)
in the runbook or wrapper so any failure inside the while loop (e.g., gh api
PATCH failures) aborts the script; keep the existing extraction (repo=$(jq -r
'.repository_name' <<<"$row")) and props assignment (props=$(jq '{properties:
.properties}' <<<"$row")) unchanged, just document or prepend the strict flags
to guarantee a mid-loop error does not silently skip remaining repos.

44-51: Race condition in the fetch-mutate-PUT flow.

The fetch→jq→PUT pattern (here and in the rollback section, Lines 181-183) has no ETag/If-Match guard, so a concurrent edit in the UI or another script between the GET and PUT will be silently overwritten. For a governance workflow this is usually acceptable (single operator, rare changes), but worth calling out — or consider using the PATCH-style update if/when it supports partial ruleset bodies.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` around lines 44 - 51, The fetch→jq→PUT pattern
that updates the ruleset (the gh api GET -> jq '.enforcement = "active"' -> gh
api --method PUT to /orgs/resq-software/rulesets/<id>) can cause a race where a
concurrent edit is overwritten; change the flow to include optimistic
concurrency using ETag/If-Match by first capturing the ETag from the GET
response and passing it as an If-Match header on the PUT, or switch to a
PATCH-style update when partial updates are supported (use the same
/orgs/resq-software/rulesets/<id> endpoint with --method PATCH and a partial
body) to avoid stomping concurrent edits.
.github/workflows/docker-publish.yml (1)

153-159: Configuration is correct — digest format and gate logic work as intended.

The docker/build-push-action outputs.digest is in sha256:HEX format, matching exactly what actions/attest-build-provenance@v4.1.0 requires for the subject-digest input. The condition ${{ inputs.attest && inputs.push }} correctly prevents attestation when push: false.

Per the official documentation, actions/attest-build-provenance is now a wrapper around actions/attest. While the current implementation is functional, consider using actions/attest directly for new implementations going forward.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-publish.yml around lines 153 - 159, Replace the
attestation step that uses actions/attest-build-provenance@a2bbfa... with the
upstream actions/attest action: update the step named "Attest build provenance"
to call actions/attest (choose the current stable tag), keep the existing
condition `${{ inputs.attest && inputs.push }}`, and pass the same inputs
(`subject-name`, `subject-digest`, `push-to-registry`) so the behaviour remains
identical while using the maintained upstream action.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/cpp-ci.yml:
- Around line 63-73: Replace the current unquoted $FLAGS expansion in the
"Configure" step with a safe array-expansion: accept the workflow input
"cmake-flags" as a JSON array, decode it into a bash array (e.g. FLAGS=($(jq -r
'.[]' <<< "$INPUT_CMAKE_FLAGS")) or similar) in the run block, then call cmake
with quoted array expansion cmake -B "$BUILD" "${FLAGS[@]}" "$SRC"; update the
env/input handling so the input is read from the "cmake-flags" input rather than
relying on shell-splitting, and add a brief header comment documenting that
consumers must pass a JSON array for cmake-flags (or, if you prefer minimal
change, instead add a comment above the Configure step explicitly stating that
cmake-flags is trusted input and must not be derived from untrusted PR context).

In @.github/workflows/docker-publish.yml:
- Around line 132-138: The login step hardcodes GHCR credentials (github.actor +
secrets.GITHUB_TOKEN) while registry is configurable, causing failures for
non-ghcr registries; update the workflow to accept caller-provided registry
credentials (e.g., define on.workflow_call.secrets like registry-username and
registry-password or inputs for them) and in the docker/login-action step use
those secrets when present, falling back to github.actor and
secrets.GITHUB_TOKEN only if registry-username/registry-password are unset;
alternatively add validation for the inputs.registry value to ensure it endsWith
"ghcr.io" and fail early if not—look for the "registry" input, the
docker/login-action step, and uses of "github.actor" / "secrets.GITHUB_TOKEN" to
implement this conditional credential selection or validation.
- Around line 120-130: The build-context regex is unsafe: the local-path branch
(used against BUILD_CONTEXT via ctx) currently allows “..” path traversal and
the unquoted inline regex contains stray single-quote characters that break
shell tokenization; fix by moving the full pattern into a variable (e.g.
pattern) and use [[ $ctx =~ $pattern ]] so the regex isn’t split by quotes,
replace the local-path alternative (currently \.?\/?[-._a-zA-Z0-9\/]+) with an
anchored segment-only form that disallows “..” (for example require one or more
path segments like ([A-Za-z0-9._-]+\/?)+) and ensure the overall pattern is
anchored with ^ and $; keep the remote branches but remove any literal single
quotes so the stored pattern is a valid POSIX ERE used via the pattern variable
when evaluating BUILD_CONTEXT and exporting SAFE_BUILD_CONTEXT.

In `@ops/governance-payloads.md`:
- Line 167: The verification command hardcodes "main" so it will fail if the
repo's default branch differs; update the check that runs gh api
/repos/resq-software/pypi/rules/branches/main to first query the repository
metadata (the repo's default_branch field via gh api /repos/:owner/:repo) and
then use that returned branch name when calling the rules/branches endpoint, or
explicitly document the assumption that the default branch must be "main" so
users know the limitation.

In `@ops/ruleset-b-critical.json`:
- Around line 18-24: Section 4 lacks an intent note explaining that
"required_approving_review_count": 0 is intentionally non-blocking (it requests
a code-owner review for visibility during solo-dev) while
"require_last_push_approval": true enforces blocking on subsequent pushes;
update the ruleset documentation or the JSON comment for Ruleset B to either (A)
add an explicit intent note in §4 stating that required_approving_review_count:
0 requests but does not block merges (mirroring §3’s intent), or (B) if you want
blocking behavior for the critical tier, change the parameter
"required_approving_review_count" from 0 to 1 in the parameters block so
code-owner review becomes a blocking requirement — reference the parameters
object and the keys required_approving_review_count and
require_last_push_approval when making the change.

---

Nitpick comments:
In @.github/workflows/cpp-ci.yml:
- Around line 57-62: The workflow currently only runs
step-security/harden-runner on Linux (if: runner.os == 'Linux') which should be
documented, and the subsequent actions/checkout step is leaving credentials
persisted; update the actions/checkout invocation (the checkout step using
actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd) to set
persist-credentials: false to avoid leaving GITHUB_TOKEN in the local git
config, and add a short comment near the Harden Runner step (or job-level
comment) noting that egress-policy auditing applies only to the Linux matrix
leg.

In @.github/workflows/docker-publish.yml:
- Around line 153-159: Replace the attestation step that uses
actions/attest-build-provenance@a2bbfa... with the upstream actions/attest
action: update the step named "Attest build provenance" to call actions/attest
(choose the current stable tag), keep the existing condition `${{ inputs.attest
&& inputs.push }}`, and pass the same inputs (`subject-name`, `subject-digest`,
`push-to-registry`) so the behaviour remains identical while using the
maintained upstream action.

In @.github/workflows/required.yml:
- Around line 119-127: The security job currently declares a hard dependency
needs: [validate-lang], causing it to wait ~5s unnecessarily; remove the needs:
[validate-lang] line from the security job so it can run in parallel with
validate-lang (the required evaluator already inspects VALIDATE_RESULT), leaving
the existing uses: ./.github/workflows/security-scan.yml and inputs unchanged;
reference the security job name "security", the validate job "validate-lang",
and the evaluator variable "VALIDATE_RESULT" when making the change.
- Around line 171-179: The cpp job in required.yml only forwards three inputs to
the reusable workflow; add forwarding for the missing cpp-ci.yml inputs by
mapping new wrapper inputs (e.g. cpp-build-dir, cpp-build-config, cpp-run-test,
cpp-timeout-minutes) to the reusable workflow's expected keys (build-dir,
build-config, run-test, timeout-minutes) in the cpp job's with block so callers
of required.yml can override build dir, build configuration, test-running, and
timeout without bypassing the aggregator; update the job's with to include lines
like "build-dir: ${{ inputs.cpp-build-dir }}" etc. and ensure corresponding
inputs are declared on required.yml (prefixed with cpp-).

In `@ops/governance-payloads.md`:
- Around line 84-92: Ensure the snippet that iterates jq output with jq -r
'.repos[] | `@json`' | while read -r row; do ... done runs under strict failure
mode: add shell safety flags (set -euo pipefail) in the runbook or wrapper so
any failure inside the while loop (e.g., gh api PATCH failures) aborts the
script; keep the existing extraction (repo=$(jq -r '.repository_name'
<<<"$row")) and props assignment (props=$(jq '{properties: .properties}'
<<<"$row")) unchanged, just document or prepend the strict flags to guarantee a
mid-loop error does not silently skip remaining repos.
- Around line 44-51: The fetch→jq→PUT pattern that updates the ruleset (the gh
api GET -> jq '.enforcement = "active"' -> gh api --method PUT to
/orgs/resq-software/rulesets/<id>) can cause a race where a concurrent edit is
overwritten; change the flow to include optimistic concurrency using
ETag/If-Match by first capturing the ETag from the GET response and passing it
as an If-Match header on the PUT, or switch to a PATCH-style update when partial
updates are supported (use the same /orgs/resq-software/rulesets/<id> endpoint
with --method PATCH and a partial body) to avoid stomping concurrent edits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6c53ff5-6dbc-4de8-a93f-689b18be1c2e

📥 Commits

Reviewing files that changed from the base of the PR and between de56ed8 and bffff5f.

📒 Files selected for processing (12)
  • .github/CODEOWNERS
  • .github/workflows/cpp-ci.yml
  • .github/workflows/docker-publish.yml
  • .github/workflows/dotnet-ci.yml
  • .github/workflows/python-ci.yml
  • .github/workflows/required.yml
  • .github/workflows/rust-ci.yml
  • ops/governance-payloads.md
  • ops/properties-schema.json
  • ops/properties-values.json
  • ops/ruleset-a-baseline.json
  • ops/ruleset-b-critical.json
✅ Files skipped from review due to trivial changes (5)
  • ops/properties-values.json
  • ops/properties-schema.json
  • ops/ruleset-a-baseline.json
  • .github/CODEOWNERS
  • .github/workflows/rust-ci.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/dotnet-ci.yml
  • .github/workflows/python-ci.yml

Comment on lines +63 to +73
- name: Configure
shell: bash
env:
SRC: ${{ inputs.source-dir }}
BUILD: ${{ inputs.build-dir }}
FLAGS: ${{ inputs.cmake-flags }}
# shellcheck disable=SC2086 - FLAGS is intentionally word-split into
# multiple cmake arguments; quoting would pass them as a single token.
run: |
# shellcheck disable=SC2086
cmake -B "$BUILD" $FLAGS "$SRC"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

$FLAGS word-splitting still allows caller-controlled argument injection into cmake.

Routing cmake-flags through env: prevents shell injection into the YAML-generated script (addressing the CodeQL finding), but $FLAGS is intentionally left unquoted so a caller can pass multiple flags. That means a caller can still inject arbitrary cmake arguments — e.g. -DCMAKE_CXX_COMPILER_LAUNCHER=..., -P <script>, or extra ;-separated generator expressions — which on a reusable workflow invoked from a consumer repo is effectively arbitrary code execution on the runner via the build step.

Since this is a reusable workflow consumed across the org, the trust boundary is "whatever the calling repo's workflow passes in", which for PRs from forks can be attacker-controlled. Consider one of:

  • Documenting that cmake-flags is trusted input and must not be derived from PR-event context in consumers.
  • Accepting cmake-flags as a JSON array and expanding via jq -r '.[]' into a bash array, preserving per-arg quoting:
♻️ Array-based expansion
-      - name: Configure
-        shell: bash
-        env:
-          SRC: ${{ inputs.source-dir }}
-          BUILD: ${{ inputs.build-dir }}
-          FLAGS: ${{ inputs.cmake-flags }}
-        # shellcheck disable=SC2086 - FLAGS is intentionally word-split into
-        # multiple cmake arguments; quoting would pass them as a single token.
-        run: |
-          # shellcheck disable=SC2086
-          cmake -B "$BUILD" $FLAGS "$SRC"
+      - name: Configure
+        shell: bash
+        env:
+          SRC: ${{ inputs.source-dir }}
+          BUILD: ${{ inputs.build-dir }}
+          FLAGS_JSON: ${{ inputs.cmake-flags-json }}   # e.g. '["-DX=1","-DY=2"]'
+        run: |
+          set -eu
+          mapfile -t FLAGS < <(printf '%s' "$FLAGS_JSON" | jq -r '.[]')
+          cmake -B "$BUILD" "${FLAGS[@]}" "$SRC"

At minimum, add a comment to the workflow header making the trust assumption on cmake-flags explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/cpp-ci.yml around lines 63 - 73, Replace the current
unquoted $FLAGS expansion in the "Configure" step with a safe array-expansion:
accept the workflow input "cmake-flags" as a JSON array, decode it into a bash
array (e.g. FLAGS=($(jq -r '.[]' <<< "$INPUT_CMAKE_FLAGS")) or similar) in the
run block, then call cmake with quoted array expansion cmake -B "$BUILD"
"${FLAGS[@]}" "$SRC"; update the env/input handling so the input is read from
the "cmake-flags" input rather than relying on shell-splitting, and add a brief
header comment documenting that consumers must pass a JSON array for cmake-flags
(or, if you prefer minimal change, instead add a comment above the Configure
step explicitly stating that cmake-flags is trusted input and must not be
derived from untrusted PR context).

Comment thread .github/workflows/docker-publish.yml
Comment on lines +132 to +138
- if: ${{ inputs.push }}
name: Log in to registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Login credentials are hardcoded to the GHCR flow but registry is a configurable input.

registry defaults to ghcr.io but is a caller-controlled input, while the login step always uses github.actor + secrets.GITHUB_TOKEN. Those credentials only authenticate against GHCR, so a caller that sets registry: docker.io (or an ECR/ACR URL) will fail at this step with no clear signal that this workflow is GHCR-only.

Either (a) document this workflow as GHCR-only and validate inputs.registry ends with ghcr.io, or (b) expose caller secrets via on.workflow_call.secrets (e.g., registry-username, registry-password) and fall back to the GHCR defaults when unset.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-publish.yml around lines 132 - 138, The login step
hardcodes GHCR credentials (github.actor + secrets.GITHUB_TOKEN) while registry
is configurable, causing failures for non-ghcr registries; update the workflow
to accept caller-provided registry credentials (e.g., define
on.workflow_call.secrets like registry-username and registry-password or inputs
for them) and in the docker/login-action step use those secrets when present,
falling back to github.actor and secrets.GITHUB_TOKEN only if
registry-username/registry-password are unset; alternatively add validation for
the inputs.registry value to ensure it endsWith "ghcr.io" and fail early if
not—look for the "registry" input, the docker/login-action step, and uses of
"github.actor" / "secrets.GITHUB_TOKEN" to implement this conditional credential
selection or validation.

gh api /orgs/resq-software/properties/schema
gh api /orgs/resq-software/properties/values
gh api /orgs/resq-software/rulesets
gh api /repos/resq-software/pypi/rules/branches/main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verification command hardcodes main as the default branch.

gh api /repos/resq-software/pypi/rules/branches/main will return rules only if pypi's default branch is literally main. If it's master/trunk/etc., this will 404 and mislead whoever runs the check. Either resolve the default branch dynamically or note the assumption.

Proposed tweak
-gh api /repos/resq-software/pypi/rules/branches/main
+# Resolve default branch dynamically so this works regardless of repo convention.
+default=$(gh api /repos/resq-software/pypi --jq .default_branch)
+gh api "/repos/resq-software/pypi/rules/branches/$default"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
gh api /repos/resq-software/pypi/rules/branches/main
# Resolve default branch dynamically so this works regardless of repo convention.
default=$(gh api /repos/resq-software/pypi --jq .default_branch)
gh api "/repos/resq-software/pypi/rules/branches/$default"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/governance-payloads.md` at line 167, The verification command hardcodes
"main" so it will fail if the repo's default branch differs; update the check
that runs gh api /repos/resq-software/pypi/rules/branches/main to first query
the repository metadata (the repo's default_branch field via gh api
/repos/:owner/:repo) and then use that returned branch name when calling the
rules/branches endpoint, or explicitly document the assumption that the default
branch must be "main" so users know the limitation.

Comment on lines +18 to +24
"parameters": {
"required_approving_review_count": 0,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": true,
"require_last_push_approval": true,
"required_review_thread_resolution": true
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

GitHub ruleset pull_request required_approving_review_count 0 require_code_owner_review behavior

💡 Result:

In GitHub rulesets, for a pull_request rule with required_approving_review_count set to 0, no general approving reviews are required to merge the pull request. This behaves the same as in classic branch protection rules, where setting the number of required approving reviews to 0 means reviewers are not required. However, if require_code_owner_review is set to true in the same pull_request rule, an approving review from a code owner is still required for pull requests that modify files with designated code owners in the CODEOWNERS file. The require_code_owner_review is an independent boolean requirement that enforces code owner approval regardless of the required_approving_review_count value. This is confirmed in the GitHub REST API documentation for rulesets, which defines both parameters in the pull_request rule object: - required_approving_review_count (integer, required): The number of approving reviews that are required before a pull request can be merged. - require_code_owner_review (boolean, required): Require an approving review in pull requests that modify files that have a designated code owner. Official docs note that setting required_approving_review_count to 0 disables the general review requirement, but code owner reviews are separately enforced if enabled. Rulesets layer with branch protection rules, taking the most restrictive setting.

Citations:


🏁 Script executed:

# Check if the files exist and read their content
git ls-files | grep -E "(ruleset|governance)" | head -20

Repository: resq-software/.github

Length of output: 147


🏁 Script executed:

# Read the ruleset-b-critical.json file
cat ops/ruleset-b-critical.json 2>/dev/null || echo "File not found"

Repository: resq-software/.github

Length of output: 787


🏁 Script executed:

# Read the governance documentation
cat ops/governance-payloads.md 2>/dev/null || head -100 ops/governance-payloads.md 2>/dev/null || echo "File not found"

Repository: resq-software/.github

Length of output: 6701


🌐 Web query:

GitHub ruleset require_last_push_approval required_approving_review_count interaction

💡 Result:

In GitHub repository rulesets, the 'pull_request' rule enforces that changes to protected branches must go through a pull request before merging. Within its parameters: - required_approving_review_count (integer, 0-10, required): Specifies the total number of approving reviews needed before a PR can merge. - require_last_push_approval (boolean, required): When true, requires that the most recent reviewable push to the PR branch be approved by someone other than the person who made that push. This ensures an additional approval from a different reviewer after the latest changes, even if the total count is met. It works alongside required_approving_review_count; the PR needs both the total approvals and this specific non-self-approval for the last push. These are independent but complementary: the count sets the minimum approvals overall, while require_last_push_approval adds a "fresh eyes" check on final changes. Both are part of the same pull_request rule parameters in ruleset creation/update via REST API or UI under "Require a pull request before merging" additional settings. Examples from GitHub's safe-settings repo show them used together: required_approving_review_count: 1 and require_last_push_approval: true. Note: These replace legacy branch protection rules with more flexible rulesets, supporting layering and bypass actors.

Citations:


Add intent note to §4 explaining required_approving_review_count: 0.

Section 4 describes require_last_push_approval: true ("any new push to a PR after approval re-requires approval") but does not explain required_approving_review_count: 0, which appears identically in both Ruleset A and Ruleset B. Per §3's intent note, this configuration means code-owner review is requested but non-blocking — a visibility guarantee for the solo-dev stage. Either clarify that this same intent applies to Ruleset B, or if critical tier should enforce stricter approval gates, bump required_approving_review_count to 1 so code-owner review actually blocks merge.

Note: require_last_push_approval does enforce blocking behavior (latest push must be approved by someone other than the pusher), so Ruleset B is not as permissive as it might initially appear.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/ruleset-b-critical.json` around lines 18 - 24, Section 4 lacks an intent
note explaining that "required_approving_review_count": 0 is intentionally
non-blocking (it requests a code-owner review for visibility during solo-dev)
while "require_last_push_approval": true enforces blocking on subsequent pushes;
update the ruleset documentation or the JSON comment for Ruleset B to either (A)
add an explicit intent note in §4 stating that required_approving_review_count:
0 requests but does not block merges (mirroring §3’s intent), or (B) if you want
blocking behavior for the critical tier, change the parameter
"required_approving_review_count" from 0 to 1 in the parameters block so
code-owner review becomes a blocking requirement — reference the parameters
object and the keys required_approving_review_count and
require_last_push_approval when making the change.

…ws' into auto-commit-20260417-060430

# Conflicts:
#	.github/workflows/docker-publish.yml
Comment thread .github/workflows/docker-publish.yml Dismissed
Comment thread .github/workflows/docker-publish.yml Dismissed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
.github/workflows/docker-publish.yml (1)

33-37: ⚠️ Potential issue | 🟠 Major

Registry is configurable, but auth is GHCR-specific.

Line 33-37 lets callers set any registry, but Line 182-188 always authenticates with github.actor + secrets.GITHUB_TOKEN (GHCR-oriented). Non-GHCR pushes will fail unexpectedly.

Suggested fix (support custom registry credentials with GHCR fallback)
 on:
   workflow_call:
     inputs:
@@
       registry:
         type: string
         required: false
         default: "ghcr.io"
+    secrets:
+      registry-username:
+        required: false
+      registry-password:
+        required: false
@@
       - if: ${{ inputs.push }}
         name: Log in to registry
         uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121  # v4.1.0
         with:
           registry: ${{ env.SAFE_REGISTRY }}
-          username: ${{ github.actor }}
-          password: ${{ secrets.GITHUB_TOKEN }}
+          username: ${{ secrets.registry-username || github.actor }}
+          password: ${{ secrets.registry-password || secrets.GITHUB_TOKEN }}

Also applies to: 182-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-publish.yml around lines 33 - 37, The workflow
currently accepts an input named "registry" but always authenticates using
github.actor and secrets.GITHUB_TOKEN (GHCR-specific); update the workflow
inputs to add optional "registry_username" and "registry_password" (or
"registry_token") and modify the registry login step (the docker login /
docker/login-action step) to use those credentials when provided, falling back
to github.actor + secrets.GITHUB_TOKEN only if no custom credentials are
supplied; ensure the login action uses the "registry" input value as the
registry host and uses the provided username/password variables for the action's
with: fields.
🧹 Nitpick comments (1)
.github/workflows/docker-publish.yml (1)

89-94: Permissions are broader than needed for non-push/non-attest runs.

Line 89-94 always grants write scopes, even when push: false and/or attest: false. Consider splitting build/publish/attest into separate jobs so write permissions are only granted where required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker-publish.yml around lines 89 - 94, The workflow's
permissions block currently grants write scopes unconditionally (permissions:
contents: read, packages: write, id-token: write, attestations: write), exposing
broader access for runs that don't need it; change the workflow to split
responsibilities into separate jobs (e.g., build, publish, attest) and scope
permissions per job so only the publish job gets packages: write and id-token:
write and only the attest job gets attestations: write, while the build job
keeps minimal permissions (contents: read) and outputs remain unchanged—update
the jobs and their permissions entries accordingly so push:false or attest:false
runs do not receive write permissions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/docker-publish.yml:
- Around line 33-37: The workflow currently accepts an input named "registry"
but always authenticates using github.actor and secrets.GITHUB_TOKEN
(GHCR-specific); update the workflow inputs to add optional "registry_username"
and "registry_password" (or "registry_token") and modify the registry login step
(the docker login / docker/login-action step) to use those credentials when
provided, falling back to github.actor + secrets.GITHUB_TOKEN only if no custom
credentials are supplied; ensure the login action uses the "registry" input
value as the registry host and uses the provided username/password variables for
the action's with: fields.

---

Nitpick comments:
In @.github/workflows/docker-publish.yml:
- Around line 89-94: The workflow's permissions block currently grants write
scopes unconditionally (permissions: contents: read, packages: write, id-token:
write, attestations: write), exposing broader access for runs that don't need
it; change the workflow to split responsibilities into separate jobs (e.g.,
build, publish, attest) and scope permissions per job so only the publish job
gets packages: write and id-token: write and only the attest job gets
attestations: write, while the build job keeps minimal permissions (contents:
read) and outputs remain unchanged—update the jobs and their permissions entries
accordingly so push:false or attest:false runs do not receive write permissions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 111b5581-ad50-4489-b0bb-41140e7f2c05

📥 Commits

Reviewing files that changed from the base of the PR and between bffff5f and 631d2ff.

📒 Files selected for processing (1)
  • .github/workflows/docker-publish.yml

@WomB0ComB0 WomB0ComB0 merged commit f4b51a6 into main Apr 17, 2026
6 checks passed
@WomB0ComB0 WomB0ComB0 deleted the feat/governance-reusable-workflows branch April 17, 2026 10:11
WomB0ComB0 added a commit to resq-software/crates that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/programs that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/landing that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/viz that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/dotnet-sdk that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/vcpkg that referenced this pull request Apr 17, 2026
Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/viz that referenced this pull request Apr 17, 2026
* ci: add placeholder `required` status gate for org ruleset

Satisfies the `required` status-check context in org ruleset
`default-branch-baseline` (id 15191038, currently evaluate mode).

Placeholder: this job always passes. Before the ruleset flips to
active, harden it with `needs:` on real language-CI jobs or
replace with a call to a reusable workflow from
resq-software/.github once the matching lang-ci.yml exists.

* ci: harden `required` gate — call real dotnet-ci reusable

Replaces the always-pass placeholder with a ci.yml that calls the
org-wide `required` aggregator with `lang: dotnet` and the
correct solution path. Pinned to resq-software/.github@6410acb
(the commit introducing dotnet-ci.yml).

After PR#12 on resq-software/.github merges, re-pin to the merge
commit SHA or a semver tag.

The top-level `required` job emits the status-check context
consumed by org ruleset `default-branch-baseline`.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/dotnet-sdk that referenced this pull request Apr 17, 2026
* ci: add placeholder `required` status gate for org ruleset

Satisfies the `required` status-check context in org ruleset
`default-branch-baseline` (id 15191038, currently evaluate mode).

Placeholder: this job always passes. Before the ruleset flips to
active, harden it with `needs:` on real language-CI jobs or
replace with a call to a reusable workflow from
resq-software/.github once the matching lang-ci.yml exists.

* ci: harden `required` gate — call real dotnet-ci reusable

Replaces the always-pass placeholder with a ci.yml that calls the
org-wide `required` aggregator with `lang: dotnet` and the
correct solution path. Pinned to resq-software/.github@6410acb
(the commit introducing dotnet-ci.yml).

After PR#12 on resq-software/.github merges, re-pin to the merge
commit SHA or a semver tag.

The top-level `required` job emits the status-check context
consumed by org ruleset `default-branch-baseline`.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/programs that referenced this pull request Apr 17, 2026
* ci: add reusable rust-ci from resq-software/.github

First CI workflow for this repo. Calls the org-wide reusable
`rust-ci.yml` (fmt + clippy + test) pinned to the PR#12 tip SHA.

Adds a top-level `required` aggregator job to emit the
status-check context consumed by the org ruleset
`default-branch-baseline` (currently evaluate-mode).

Security scanning remains in security.yml — independent cadence.

After resq-software/.github#12 lands, update the @sha ref.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/vcpkg that referenced this pull request Apr 17, 2026
* ci: add placeholder `required` status gate alongside existing CI

Satisfies the `required` status-check context in org ruleset
`default-branch-baseline` (id 15191038, evaluate mode). Placeholder
that always passes; this repo'\''s existing CI workflows continue to
run independently.

Before the ruleset flips to active, harden this job with `needs:`
on the existing CI jobs — or swap for a call to a reusable workflow
in resq-software/.github.

* ci: harden to cpp-ci reusable; remove inline ci.yml + placeholder

Replaces the previous inline cmake/ctest matrix and the placeholder
required.yml with a single ci.yml that calls the org-wide `required`
aggregator with `lang: cpp` and the correct source-dir +
cmake-flags preserved from the prior workflow.

Pinned to resq-software/.github@6410acb (the commit introducing
cpp-ci.yml). Re-pin to the merge commit or tag after PR#12 lands.

Parity:
- os matrix: ubuntu + macos + windows (same as before)
- source: packages/resq-common (same)
- flag:   -DRESQ_COMMON_BUILD_TESTS=ON (same)

Additionally runs CodeQL for c-cpp and the full security-scan
suite via the reusable — upgrade from the prior CI.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.
WomB0ComB0 added a commit to resq-software/landing that referenced this pull request Apr 17, 2026
* ci: migrate to reusable node-ci in resq-software/.github

Replace inline build/test/lint jobs with a thin caller of
`resq-software/.github/.github/workflows/node-ci.yml` pinned to the
PR#12 tip SHA (5a72372). Adds a top-level `required` aggregator job
to emit the status-check context consumed by the org ruleset
`default-branch-baseline` (currently in evaluate mode).

Behavior parity:
- lint:      bun run lint  (biome)
- typecheck: bun run tsc --noEmit
- build:     bun run build
- test:      bun test --coverage

Security scanning remains in security.yml (independent cadence).

When PR#12 lands on resq-software/.github, update the @sha ref
to pin against the merge commit or a semver tag.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.

* chore(deps): regenerate bun.lock to match package.json

Previous lockfile drifted from package.json, causing
'bun install --frozen-lockfile' in CI to fail with:
  error: lockfile had changes, but lockfile is frozen

Regenerated locally; no dependency version changes required
beyond what package.json already specified.
WomB0ComB0 added a commit to resq-software/crates that referenced this pull request Apr 18, 2026
* ci: migrate to reusable rust-ci in resq-software/.github

Replaces inline test/fmt jobs in ci.yml with a thin caller of the
org-wide reusable workflow. Subsumes clippy.yml and deny.yml (the
reusable wraps fmt + clippy + test + coverage + cargo-deny into one
dispatch).

Adds a top-level `required` aggregator job that emits the status-check
context consumed by the org ruleset `default-branch-baseline` (id
15191038, currently evaluate mode).

Pinned to the PR#12 tip SHA; re-pin after that PR lands.

Parity:
- fmt:    cargo fmt --all --check
- clippy: cargo clippy --all-targets --all-features -- -D warnings
- test:   cargo llvm-cov --workspace --lcov (codecov upload)
- deny:   cargo-deny check --all-features (via deny.toml)

Security scanning remains in security.yml — independent cadence.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.

* fix(ci): resolve clippy explicit_counter_loop + cargo-deny failures

The migration to reusable rust-ci.yml surfaced two pre-existing
issues that the old per-workflow CI didn't catch:

1. clippy::explicit_counter_loop in resq-dsa::rabin_karp
   Replaced `let mut i = 1; for c in chars { …; i += 1; }` with
   `for (i, c) in (1..).zip(chars) { … }`. Idiomatic, avoids the
   lint introduced by Rust 1.95, behavior unchanged.

2. cargo-deny failures — three licenses + one advisory:
   - RUSTSEC-2026-0099 (rustls-webpki name-constraint bug) →
     `cargo update -p rustls-webpki` bumps 0.103.10 → 0.103.12
     (Cargo.lock delta only).
   - CDDL-1.0 (inferno, via resq-flame profiling stack) added
     to licenses.allow with rationale.
   - CDLA-Permissive-2.0 (webpki-root-certs root cert DB) added
     to licenses.allow.
   - MPL-2.0 (colored) added as a crate-specific exception
     rather than widening the org-wide MPL allowance.

Verified locally: `cargo deny --all-features check` → all four
sections (advisories, bans, licenses, sources) pass.

* fix(clippy): address Rust 1.95 warnings surfaced by reusable rust-ci

CI on the migrated rust-ci.yml (which now runs clippy across the
whole workspace) surfaced four pedantic/default warnings that were
elevated to errors by -D warnings:

1. resq-clean src/main.rs:100 map_unwrap_or  ->
   fs::metadata(&path).map_or(0, |m| m.len())

2. resq-clean src/main.rs:110 unnecessary_sort_by  ->
   entries.sort_by_key(|e| std::cmp::Reverse(e.size))

3. resq-cli src/commands/pre_commit.rs:1043/1048 collapsible_match
   -> KeyCode::<X> if <cond> => { <body> }

4. resq-cli tests/format.rs:19 map_unwrap_or  ->
   Command::new("which").arg(cmd).output()
     .is_ok_and(|o| o.status.success())

All four are semantically-identical refactors; no behavior change.

Verified clean locally against rustc 1.95.0:
  cargo +stable clippy --all-targets --all-features -- -D warnings

* ci: re-pin reusable rust-ci to .github main (post-#13 merge)

Picks up the taiki-e SHA correction and the Docker-bound cargo-deny
replacement (dtolnay/rust-toolchain + taiki-e install cargo-deny +
direct cargo deny check) so the run respects this repo's
rust-toolchain.toml musl pin.

Updates @f4b51a6 → @2e2af3e5e32b646480a1cb1b41875e5073020814.
WomB0ComB0 added a commit to resq-software/crates that referenced this pull request Apr 18, 2026
* ci: migrate to reusable rust-ci in resq-software/.github

Replaces inline test/fmt jobs in ci.yml with a thin caller of the
org-wide reusable workflow. Subsumes clippy.yml and deny.yml (the
reusable wraps fmt + clippy + test + coverage + cargo-deny into one
dispatch).

Adds a top-level `required` aggregator job that emits the status-check
context consumed by the org ruleset `default-branch-baseline` (id
15191038, currently evaluate mode).

Pinned to the PR#12 tip SHA; re-pin after that PR lands.

Parity:
- fmt:    cargo fmt --all --check
- clippy: cargo clippy --all-targets --all-features -- -D warnings
- test:   cargo llvm-cov --workspace --lcov (codecov upload)
- deny:   cargo-deny check --all-features (via deny.toml)

Security scanning remains in security.yml — independent cadence.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.

* fix(ci): resolve clippy explicit_counter_loop + cargo-deny failures

The migration to reusable rust-ci.yml surfaced two pre-existing
issues that the old per-workflow CI didn't catch:

1. clippy::explicit_counter_loop in resq-dsa::rabin_karp
   Replaced `let mut i = 1; for c in chars { …; i += 1; }` with
   `for (i, c) in (1..).zip(chars) { … }`. Idiomatic, avoids the
   lint introduced by Rust 1.95, behavior unchanged.

2. cargo-deny failures — three licenses + one advisory:
   - RUSTSEC-2026-0099 (rustls-webpki name-constraint bug) →
     `cargo update -p rustls-webpki` bumps 0.103.10 → 0.103.12
     (Cargo.lock delta only).
   - CDDL-1.0 (inferno, via resq-flame profiling stack) added
     to licenses.allow with rationale.
   - CDLA-Permissive-2.0 (webpki-root-certs root cert DB) added
     to licenses.allow.
   - MPL-2.0 (colored) added as a crate-specific exception
     rather than widening the org-wide MPL allowance.

Verified locally: `cargo deny --all-features check` → all four
sections (advisories, bans, licenses, sources) pass.

* fix(clippy): address Rust 1.95 warnings surfaced by reusable rust-ci

CI on the migrated rust-ci.yml (which now runs clippy across the
whole workspace) surfaced four pedantic/default warnings that were
elevated to errors by -D warnings:

1. resq-clean src/main.rs:100 map_unwrap_or  ->
   fs::metadata(&path).map_or(0, |m| m.len())

2. resq-clean src/main.rs:110 unnecessary_sort_by  ->
   entries.sort_by_key(|e| std::cmp::Reverse(e.size))

3. resq-cli src/commands/pre_commit.rs:1043/1048 collapsible_match
   -> KeyCode::<X> if <cond> => { <body> }

4. resq-cli tests/format.rs:19 map_unwrap_or  ->
   Command::new("which").arg(cmd).output()
     .is_ok_and(|o| o.status.success())

All four are semantically-identical refactors; no behavior change.

Verified clean locally against rustc 1.95.0:
  cargo +stable clippy --all-targets --all-features -- -D warnings

* ci: re-pin reusable rust-ci to .github main (post-#13 merge)

Picks up the taiki-e SHA correction and the Docker-bound cargo-deny
replacement (dtolnay/rust-toolchain + taiki-e install cargo-deny +
direct cargo deny check) so the run respects this repo's
rust-toolchain.toml musl pin.

Updates @f4b51a6 → @2e2af3e5e32b646480a1cb1b41875e5073020814.
WomB0ComB0 added a commit to resq-software/crates that referenced this pull request Apr 18, 2026
#70)

* ci: wire org-wide security-scan + dependabot + CODEOWNERS (#63)

Standardization pass across public ResQ repos. Inherits org-level
defaults (CoC / Contributing / Security / Support / PR + issue
templates) from resq-software/.github.

  .github/workflows/security.yml  Thin caller for the reusable workflow
                                  with languages=["actions"].

  .github/CODEOWNERS               Default owner.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(ci): SHA-pin third-party GitHub Actions refs (#64)

Pins every external `uses:` ref to a commit SHA with a trailing
`# <tag>` comment. Closes the tj-actions-class supply-chain attack
vector where a malicious tag push exfiltrates secrets. Dependabot
continues to update via the version comment.

Action refs that can't be pinned (e.g. `dtolnay/rust-toolchain@stable`
which is intentionally a moving ref) are left as-is — these are
floating channel refs, not tag refs.

* ci(security): enable Semgrep SAST (#65)

* fix(ci): quiet actionlint SC2035 by using -- glob separator (#66)

* fix(deny): migrate deny.toml to cargo-deny v2 schema (#67)

cargo-deny v0.14+ removed [licenses].unlicensed and [licenses].deny.
Schema now uses [licenses].version = 2 + implicit-denial-by-omission
from allow list. See EmbarkStudios/cargo-deny#611.

* chore(nix): bump nixpkgs 24.11 → 25.11 + refresh lock (#68)

* chore(bootstrap): retire duplicate setup scripts, delegate to resq-software/dev

Replace bootstrap.sh with a ~9-line thin wrapper that curl-pipes the
canonical dev/install.sh with REPO=crates. Delete the per-repo
scripts/setup.sh and scripts/lib/shell-utils.sh drifted copies.

Single source of truth lives in resq-software/dev — no more per-repo
drift, and CI surface shrinks by one directory.

* chore(resq-ai): scaffold new crate with workspace integration

* feat(resq-ai): add token estimation and line-boundary truncation

* feat(resq-ai): add config cascade and provider enum

* feat(resq-ai): add Anthropic, OpenAI, Gemini providers with dispatch

* feat(resq-cli): add resq commit subcommand with AI-powered message generation

* fix: resolve CI failures and address review feedback for resq-ai PR

- Fix clippy: use enumerate-zip pattern in trie.rs, div_ceil in token.rs
- Fix rustfmt: run cargo fmt on config.rs, commit.rs, gemini.rs
- Fix test race: add Mutex guard to serialize env-mutating config tests
- Fix cargo-deny: allow MPL-2.0, CDDL-1.0, CDLA-Permissive-2.0 licenses
- Fix gitleaks: replace suspicious test API key with benign placeholder
- Fix clippy doc_markdown lint for OpenAI in lib.rs doc comment
- Fix bootstrap.sh: validate curl download, forward $@ to installer
- Fix token.rs: ensure UTF-8 char boundary before byte slicing
- Fix anthropic.rs: make ContentBlock.text optional for non-text blocks
- Fix gemini.rs: use x-goog-api-key header instead of query param
- Fix config.rs: error on unknown RESQ_AI_PROVIDER instead of silently
  falling back
- Fix commit.rs: RAII guard for terminal raw mode, TTY check before
  interactive selection
- Add commit command to module doc list in mod.rs

* fix(resq-ai): use proper URL parsing for HTTPS scheme validation in config

Agent-Logs-Url: https://github.com/resq-software/crates/sessions/f3527d5b-6f5e-48a4-a3b5-f748eed315bf

Co-authored-by: WomB0ComB0 <95197809+WomB0ComB0@users.noreply.github.com>

* ci: migrate to reusable rust-ci, remove clippy.yml + deny.yml (#71)

* ci: migrate to reusable rust-ci in resq-software/.github

Replaces inline test/fmt jobs in ci.yml with a thin caller of the
org-wide reusable workflow. Subsumes clippy.yml and deny.yml (the
reusable wraps fmt + clippy + test + coverage + cargo-deny into one
dispatch).

Adds a top-level `required` aggregator job that emits the status-check
context consumed by the org ruleset `default-branch-baseline` (id
15191038, currently evaluate mode).

Pinned to the PR#12 tip SHA; re-pin after that PR lands.

Parity:
- fmt:    cargo fmt --all --check
- clippy: cargo clippy --all-targets --all-features -- -D warnings
- test:   cargo llvm-cov --workspace --lcov (codecov upload)
- deny:   cargo-deny check --all-features (via deny.toml)

Security scanning remains in security.yml — independent cadence.

* ci: re-pin reusable workflow ref to resq-software/.github main

Updates @sha from the feat-branch tip to the merge commit of
resq-software/.github#12 (f4b51a620aa1bf89c0bce4f434b36f92ff7d517d). Functionally equivalent — same
content — but pins to a ref that now exists on main rather than a
closed PR branch.

* fix(ci): resolve clippy explicit_counter_loop + cargo-deny failures

The migration to reusable rust-ci.yml surfaced two pre-existing
issues that the old per-workflow CI didn't catch:

1. clippy::explicit_counter_loop in resq-dsa::rabin_karp
   Replaced `let mut i = 1; for c in chars { …; i += 1; }` with
   `for (i, c) in (1..).zip(chars) { … }`. Idiomatic, avoids the
   lint introduced by Rust 1.95, behavior unchanged.

2. cargo-deny failures — three licenses + one advisory:
   - RUSTSEC-2026-0099 (rustls-webpki name-constraint bug) →
     `cargo update -p rustls-webpki` bumps 0.103.10 → 0.103.12
     (Cargo.lock delta only).
   - CDDL-1.0 (inferno, via resq-flame profiling stack) added
     to licenses.allow with rationale.
   - CDLA-Permissive-2.0 (webpki-root-certs root cert DB) added
     to licenses.allow.
   - MPL-2.0 (colored) added as a crate-specific exception
     rather than widening the org-wide MPL allowance.

Verified locally: `cargo deny --all-features check` → all four
sections (advisories, bans, licenses, sources) pass.

* fix(clippy): address Rust 1.95 warnings surfaced by reusable rust-ci

CI on the migrated rust-ci.yml (which now runs clippy across the
whole workspace) surfaced four pedantic/default warnings that were
elevated to errors by -D warnings:

1. resq-clean src/main.rs:100 map_unwrap_or  ->
   fs::metadata(&path).map_or(0, |m| m.len())

2. resq-clean src/main.rs:110 unnecessary_sort_by  ->
   entries.sort_by_key(|e| std::cmp::Reverse(e.size))

3. resq-cli src/commands/pre_commit.rs:1043/1048 collapsible_match
   -> KeyCode::<X> if <cond> => { <body> }

4. resq-cli tests/format.rs:19 map_unwrap_or  ->
   Command::new("which").arg(cmd).output()
     .is_ok_and(|o| o.status.success())

All four are semantically-identical refactors; no behavior change.

Verified clean locally against rustc 1.95.0:
  cargo +stable clippy --all-targets --all-features -- -D warnings

* ci: re-pin reusable rust-ci to .github main (post-#13 merge)

Picks up the taiki-e SHA correction and the Docker-bound cargo-deny
replacement (dtolnay/rust-toolchain + taiki-e install cargo-deny +
direct cargo deny check) so the run respects this repo's
rust-toolchain.toml musl pin.

Updates @f4b51a6 → @2e2af3e5e32b646480a1cb1b41875e5073020814.

* fix(resq-ai): address review feedback — client reuse, config warnings, gitleaks

- Add .gitleaks.toml to allowlist test placeholder strings in resq-ai
  config tests, fixing CI Secret Scanning failure
- Pass --config .gitleaks.toml in codeql.yml gitleaks step
- Reuse a single reqwest::Client across provider calls instead of
  creating one per request (connection pooling)
- Fix config cascade docstring to match actual precedence
  (project > home, not home > project)
- Warn on malformed TOML config instead of silently ignoring
- Rename OpenAI max_tokens → max_completion_tokens (deprecated field)
- Fix Rust 1.95 clippy warnings in resq-clean and pre_commit

---------

Signed-off-by: Mike Odnis <mike@mikeodnis.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants