Skip to content

chore: add pre-commit linters and CI lint workflow - #29

Merged
ralphbean merged 1 commit into
mainfrom
chore/add-linters
Jul 7, 2026
Merged

chore: add pre-commit linters and CI lint workflow#29
ralphbean merged 1 commit into
mainfrom
chore/add-linters

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Add .pre-commit-config.yaml with applicable linters from fullsend repo:
    • Syntax checks: check-yaml, check-json, check-toml
    • Hygiene: trailing-whitespace, end-of-file-fixer, mixed-line-ending, check-merge-conflict
    • Security: detect-private-key, gitleaks, check-added-large-files
    • Shell: shellcheck
    • GitHub Actions: actionlint, pinact (SHA-pin enforcement)
    • Commits: gitlint (conventional commits, commit-msg hook)
  • Add .gitlint config matching fullsend repo conventions
  • Add .github/workflows/lint.yml CI workflow with two jobs:
    • test — runs pre-commit run --all-files
    • commit-lint — lints PR title and individual commit messages

Linters from fullsend that are not applicable (no Go, Python, TypeScript, or frontend code in this repo): golangci-lint, gofmt, go-vet, ruff, ty, bandit, ESLint, Prettier, Stylelint, svelte-check, lint-staged, and the custom hack/lint-* scripts.

Test plan

  • CI lint workflow runs on this PR
  • Verify pre-commit hooks pass on all existing files
  • Confirm commit-lint job validates PR title format

🤖 Generated with Claude Code

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add pre-commit linters and CI workflow for file + commit linting

✨ Enhancement ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Add pre-commit hook set for syntax, hygiene, security, and GitHub Actions linting.
• Enforce Conventional Commits via gitlint for commit messages and PR titles.
• Add CI workflow to run pre-commit on all files and validate commit metadata on PRs.
Diagram

graph TD
  dev(["Developer"]) --> pc(["Local pre-commit"]) --> pcc[".pre-commit-config.yaml"] --> hooks["Linters (hooks)"]
  gha(["GitHub Actions"]) --> wf[".github/workflows/lint.yml"] --> pci(["pre-commit --all-files"]) --> pcc[".pre-commit-config.yaml"]
  gha(["GitHub Actions"]) --> wf[".github/workflows/lint.yml"] --> cl(["Commit/PR title lint"]) --> gl[".gitlint"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use pre-commit/action instead of installing pre-commit via uv
  • ➕ Less custom setup logic in workflow
  • ➕ Common pattern with clear caching behavior
  • ➖ Still requires ensuring compatible Python/runtime versions
  • ➖ Less control over Python package installation tooling (uv vs pip)
2. Use a dedicated Conventional Commits/commitlint GitHub Action
  • ➕ Simplifies commit iteration/range handling logic
  • ➕ Often provides better PR UX (annotations, summaries)
  • ➖ Adds another third-party action dependency
  • ➖ May not match existing gitlint rules/config one-to-one
3. Adopt pre-commit.ci for PR enforcement
  • ➕ Offloads CI runtime to pre-commit.ci
  • ➕ Auto-fix PRs with hook updates where applicable
  • ➖ External service dependency and onboarding overhead
  • ➖ May be undesirable for repos requiring fully in-house CI

Recommendation: The current approach is solid for repos that want fully self-contained enforcement: hooks are pinned, CI runs pre-commit across all files, and commit/PR title policy is explicit via .gitlint. The only area to reconsider is the custom commit-range loop (push/PR/merge_group); a purpose-built action could reduce maintenance, but at the cost of an extra dependency and potentially different rule semantics.

Files changed (3) +141 / -0

Other (3) +141 / -0
lint.ymlAdd CI lint workflow for pre-commit and commit/PR title checks +83/-0

Add CI lint workflow for pre-commit and commit/PR title checks

• Introduces a GitHub Actions workflow triggered on pushes to main, PRs, and merge queue events. Runs pre-commit across the repository and separately lint-checks PR titles and individual commit subjects using gitlint with event-aware commit ranges.

.github/workflows/lint.yml

.gitlintConfigure gitlint to enforce Conventional Commits and title limits +10/-0

Configure gitlint to enforce Conventional Commits and title limits

• Adds gitlint configuration enabling the Conventional Commits title rule and sets a 100-character title length limit. Restricts allowed commit types to a defined set (feat/fix/refactor/docs/test/chore/ci/perf/build).

.gitlint

.pre-commit-config.yamlAdd pre-commit hook suite for syntax, hygiene, security, and workflow linting +48/-0

Add pre-commit hook suite for syntax, hygiene, security, and workflow linting

• Adds pre-commit configuration to run common repository hygiene checks, YAML/JSON/TOML validation, secret scanning (gitleaks/private key), shellcheck, and actionlint. Includes a local pinact hook to enforce SHA-pinned GitHub Actions in workflow files and enables both pre-commit and commit-msg hook installation.

.pre-commit-config.yaml

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 5:06 PM UTC · Ended 5:08 PM UTC
Commit: 0a95cac · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 5:09 PM UTC · Ended 5:12 PM UTC
Commit: 0a95cac · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (3)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Action required

1. Unsanitized sha in ::error:: 📜 Skill insight ⛨ Security
Description
The workflow emits a GitHub Actions workflow command using echo "::error::...${sha}..." without
sanitizing the interpolated sha value. This can enable workflow-command injection if the
interpolated value contains ::, encoded newlines, or control characters.
Code

.github/workflows/lint.yml[R74-78]

+          for sha in $(git rev-list --no-merges "${RANGE}"); do
+            git log --format='%s' -1 "${sha}" > /tmp/commit-msg.txt
+            if ! uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/commit-msg.txt; then
+              echo "::error::Commit ${sha} does not follow Conventional Commits format"
+              FAILED=true
Evidence
The compliance rule requires sanitizing every interpolated value used in GitHub Actions workflow
commands. The code prints ::error::...${sha}... without any sanitization function or escaping
applied to sha.

.github/workflows/lint.yml[74-78]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A GitHub Actions workflow command (`::error::...`) is emitted with an interpolated variable (`${sha}`) that is not sanitized, violating the requirement that *all* interpolated values in workflow commands be sanitized individually.

## Issue Context
The command is printed inside the `Lint commits` step. Even if `sha` is expected to be a commit hash, the compliance requirement is to sanitize each interpolated value regardless of perceived risk.

## Fix Focus Areas
- .github/workflows/lint.yml[74-78]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No linked issue authorization 📜 Skill insight § Compliance
Description
This PR introduces non-trivial CI/linting infrastructure changes but does not include an explicit
linked issue authorizing the work. Non-trivial changes require a linked issue for authorization.
Code

.github/workflows/lint.yml[R1-83]

+name: CI
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+  merge_group:
+
+permissions:
+  contents: read
+
+jobs:
+  test:
+    runs-on: ubuntu-24.04
+    steps:
+      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+      - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
+        with:
+          python-version: "3.12"
+
+      - name: Install uv
+        uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
+
+      - name: Install pre-commit
+        run: uv pip install --system pre-commit
+
+      - name: Install pinact
+        run: |
+          curl -sSfL "https://github.com/suzuki-shunsuke/pinact/releases/download/v4.1.0/pinact_linux_amd64.tar.gz" -o /tmp/pinact.tar.gz
+          echo "8fcbf1b3e95551c82fd995535e3c1defa70e23299ce36eb3afd6c98778de6ca0  /tmp/pinact.tar.gz" | sha256sum -c
+          tar xzf /tmp/pinact.tar.gz -C /usr/local/bin pinact
+
+      - name: Run pre-commit on all files
+        run: pre-commit run --all-files
+
+  commit-lint:
+    runs-on: ubuntu-24.04
+    steps:
+      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+        with:
+          fetch-depth: 0
+
+      - name: Install uv
+        uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
+
+      - name: Lint PR title
+        if: github.event_name == 'pull_request'
+        env:
+          PR_TITLE: ${{ github.event.pull_request.title }}
+        run: |
+          echo "${PR_TITLE}" > /tmp/pr-title.txt
+          uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt
+
+      - name: Lint commits
+        env:
+          EVENT_NAME: ${{ github.event_name }}
+          PUSH_BEFORE: ${{ github.event.before }}
+          PUSH_AFTER: ${{ github.sha }}
+          MQ_BASE: ${{ github.event.merge_group.base_sha }}
+          MQ_HEAD: ${{ github.event.merge_group.head_sha }}
+          PR_BASE: ${{ github.event.pull_request.base.sha }}
+          PR_HEAD: ${{ github.event.pull_request.head.sha }}
+        run: |
+          case "${EVENT_NAME}" in
+            push)          RANGE="${PUSH_BEFORE}..${PUSH_AFTER}" ;;
+            merge_group)   RANGE="${MQ_BASE}..${MQ_HEAD}" ;;
+            pull_request)  RANGE="${PR_BASE}..${PR_HEAD}" ;;
+            *)             echo "Unknown event: ${EVENT_NAME}"; exit 1 ;;
+          esac
+
+          FAILED=false
+          for sha in $(git rev-list --no-merges "${RANGE}"); do
+            git log --format='%s' -1 "${sha}" > /tmp/commit-msg.txt
+            if ! uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/commit-msg.txt; then
+              echo "::error::Commit ${sha} does not follow Conventional Commits format"
+              FAILED=true
+            fi
+          done
+          if ${FAILED}; then
+            exit 1
+          fi
Evidence
The diff shows substantial new workflow and tooling configuration being added (non-trivial
structural/infrastructure change), which triggers the checklist requirement for an authorizing
linked issue.

.github/workflows/lint.yml[1-83]
.pre-commit-config.yaml[1-48]
.gitlint[1-10]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Non-trivial changes require explicit authorization via a linked issue.

## Issue Context
This PR adds substantial new CI and lint infrastructure (workflow + pre-commit + gitlint config). Add an issue/ADR reference (and ideally link it in the PR description) that authorizes introducing these governance changes.

## Fix Focus Areas
- .github/workflows/lint.yml[1-83]
- .pre-commit-config.yaml[1-48]
- .gitlint[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Pinact blocks fullsend workflow ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new pinact pre-commit hook runs on .github/workflows/** and is executed in CI via `pre-commit
run --all-files, but the existing fullsend.yaml references a reusable workflow using @main` (not
a commit SHA) so pinact will fail and break the lint job.
Code

.pre-commit-config.yaml[R41-48]

+  - repo: local
+    hooks:
+      - id: pinact
+        name: pinact (SHA-pin check)
+        entry: pinact run --fix=false --no-api
+        language: system
+        files: ^\.github/workflows/
+        pass_filenames: false
Evidence
pinact is configured to run for .github/workflows/** and CI executes pre-commit over all files,
while fullsend.yaml contains a uses: ...@main reference that is not SHA-pinned.

.pre-commit-config.yaml[41-48]
.github/workflows/lint.yml[35-36]
.github/workflows/fullsend.yaml[40-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new local pre-commit hook runs `pinact` against `.github/workflows/**`, and CI runs `pre-commit run --all-files`. The existing `.github/workflows/fullsend.yaml` contains a reusable workflow reference pinned to `@main`, which will violate SHA-pin enforcement and cause CI to fail.

## Issue Context
`fullsend.yaml` is marked as managed by fullsend and likely shouldn’t be edited directly, so excluding it from pinact is usually the safest fix.

## Fix Focus Areas
- .pre-commit-config.yaml[41-48]
- .github/workflows/fullsend.yaml[40-50]
- .github/workflows/lint.yml[35-36]

## Suggested fixes
Choose one:
1) Exclude `.github/workflows/fullsend.yaml` from the pinact hook (preferred if the file is upstream-managed), e.g. add an `exclude:` regex for that file.
2) If editing is acceptable, pin `uses: fullsend-ai/.fullsend/.github/workflows/dispatch.yml@...` to an immutable commit SHA instead of `@main`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Gitlint B6 mismatch 🐞 Bug ⚙ Maintainability
Description
CI runs gitlint with --ignore B6, but the local commit-msg pre-commit gitlint hook is configured
without that ignore, so commits/PR titles can be rejected locally while still passing CI.
Code

.github/workflows/lint.yml[R48-55]

+      - name: Lint PR title
+        if: github.event_name == 'pull_request'
+        env:
+          PR_TITLE: ${{ github.event.pull_request.title }}
+        run: |
+          echo "${PR_TITLE}" > /tmp/pr-title.txt
+          uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt
+
Evidence
The workflow explicitly ignores B6, while the pre-commit gitlint hook has no corresponding ignore
configuration.

.github/workflows/lint.yml[48-55]
.pre-commit-config.yaml[19-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CI and local pre-commit enforce different gitlint rules: CI ignores rule `B6` while the pre-commit `commit-msg` hook does not. This creates inconsistent behavior for contributors.

## Issue Context
CI invokes: `gitlint --ignore B6 ...`. The pre-commit hook configuration for gitlint does not pass `--ignore B6`.

## Fix Focus Areas
- .github/workflows/lint.yml[48-55]
- .pre-commit-config.yaml[19-24]

## Suggested fixes
Pick one and apply consistently:
- Option A (match CI): add `args: [--ignore, B6]` to the pre-commit `gitlint` hook.
- Option B (match local): remove `--ignore B6` from CI so CI enforces the same rules as the hook.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Protected paths modified 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths (.github/workflows/lint.yml and
.pre-commit-config.yaml), which must be explicitly flagged for required human review. Changes in
these paths must not be auto-approved.
Code

.github/workflows/lint.yml[R1-12]

+name: CI
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+  merge_group:
+
+permissions:
+  contents: read
+
Evidence
The checklist marks changes under protected paths (including .github/ workflows and
.pre-commit-config.yaml) as requiring a finding to ensure they receive human review and are not
auto-approved.

.github/workflows/lint.yml[1-83]
.pre-commit-config.yaml[1-48]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected governance/infrastructure paths are modified in this PR, which requires explicit justification and mandatory human review.

## Issue Context
Protected paths include `.github/` and `.pre-commit-config.yaml`. Provide an explicit issue/ADR link or justification in-repo (e.g., header comments) to document authorization for these governance changes.

## Fix Focus Areas
- .github/workflows/lint.yml[1-12]
- .pre-commit-config.yaml[1-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

6. Unpinned gitlint-core 🐞 Bug ☼ Reliability
Description
The commit-lint job runs uvx --from gitlint-core gitlint without pinning a version, so CI behavior
can drift over time and diverge from the pre-commit hook’s pinned gitlint version.
Code

.github/workflows/lint.yml[R52-55]

+        run: |
+          echo "${PR_TITLE}" > /tmp/pr-title.txt
+          uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt
+
Evidence
CI uses an unversioned uvx --from gitlint-core, while the pre-commit config pins gitlint to a
specific rev.

.github/workflows/lint.yml[52-55]
.pre-commit-config.yaml[19-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`uvx --from gitlint-core gitlint` does not pin a specific version, so CI lint results can change when `gitlint-core` releases new versions.

## Issue Context
Local pre-commit pins gitlint via the pre-commit repo `rev: v0.19.1`, but CI resolves `gitlint-core` dynamically.

## Fix Focus Areas
- .github/workflows/lint.yml[52-55]
- .pre-commit-config.yaml[19-21]

## Suggested fixes
- Pin the version used by `uvx`, e.g. `uvx --from 'gitlint-core==0.19.1' gitlint ...` (or the exact version you want to standardize on).
- Alternatively, standardize by running gitlint via pre-commit in CI (so CI uses the same pinned hook).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +74 to +78
for sha in $(git rev-list --no-merges "${RANGE}"); do
git log --format='%s' -1 "${sha}" > /tmp/commit-msg.txt
if ! uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/commit-msg.txt; then
echo "::error::Commit ${sha} does not follow Conventional Commits format"
FAILED=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.

Action required

1. Unsanitized sha in ::error:: 📜 Skill insight ⛨ Security

The workflow emits a GitHub Actions workflow command using echo "::error::...${sha}..." without
sanitizing the interpolated sha value. This can enable workflow-command injection if the
interpolated value contains ::, encoded newlines, or control characters.
Agent Prompt
## Issue description
A GitHub Actions workflow command (`::error::...`) is emitted with an interpolated variable (`${sha}`) that is not sanitized, violating the requirement that *all* interpolated values in workflow commands be sanitized individually.

## Issue Context
The command is printed inside the `Lint commits` step. Even if `sha` is expected to be a commit hash, the compliance requirement is to sanitize each interpolated value regardless of perceived risk.

## Fix Focus Areas
- .github/workflows/lint.yml[74-78]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1 to +12
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:

permissions:
contents: read

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Protected paths modified 📜 Skill insight § Compliance

This PR modifies protected governance/infrastructure paths (.github/workflows/lint.yml and
.pre-commit-config.yaml), which must be explicitly flagged for required human review. Changes in
these paths must not be auto-approved.
Agent Prompt
## Issue description
Protected governance/infrastructure paths are modified in this PR, which requires explicit justification and mandatory human review.

## Issue Context
Protected paths include `.github/` and `.pre-commit-config.yaml`. Provide an explicit issue/ADR link or justification in-repo (e.g., header comments) to document authorization for these governance changes.

## Fix Focus Areas
- .github/workflows/lint.yml[1-12]
- .pre-commit-config.yaml[1-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1 to +83
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0

- name: Install pre-commit
run: uv pip install --system pre-commit

- name: Install pinact
run: |
curl -sSfL "https://github.com/suzuki-shunsuke/pinact/releases/download/v4.1.0/pinact_linux_amd64.tar.gz" -o /tmp/pinact.tar.gz
echo "8fcbf1b3e95551c82fd995535e3c1defa70e23299ce36eb3afd6c98778de6ca0 /tmp/pinact.tar.gz" | sha256sum -c
tar xzf /tmp/pinact.tar.gz -C /usr/local/bin pinact

- name: Run pre-commit on all files
run: pre-commit run --all-files

commit-lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0

- name: Lint PR title
if: github.event_name == 'pull_request'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "${PR_TITLE}" > /tmp/pr-title.txt
uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt

- name: Lint commits
env:
EVENT_NAME: ${{ github.event_name }}
PUSH_BEFORE: ${{ github.event.before }}
PUSH_AFTER: ${{ github.sha }}
MQ_BASE: ${{ github.event.merge_group.base_sha }}
MQ_HEAD: ${{ github.event.merge_group.head_sha }}
PR_BASE: ${{ github.event.pull_request.base.sha }}
PR_HEAD: ${{ github.event.pull_request.head.sha }}
run: |
case "${EVENT_NAME}" in
push) RANGE="${PUSH_BEFORE}..${PUSH_AFTER}" ;;
merge_group) RANGE="${MQ_BASE}..${MQ_HEAD}" ;;
pull_request) RANGE="${PR_BASE}..${PR_HEAD}" ;;
*) echo "Unknown event: ${EVENT_NAME}"; exit 1 ;;
esac

FAILED=false
for sha in $(git rev-list --no-merges "${RANGE}"); do
git log --format='%s' -1 "${sha}" > /tmp/commit-msg.txt
if ! uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/commit-msg.txt; then
echo "::error::Commit ${sha} does not follow Conventional Commits format"
FAILED=true
fi
done
if ${FAILED}; then
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. No linked issue authorization 📜 Skill insight § Compliance

This PR introduces non-trivial CI/linting infrastructure changes but does not include an explicit
linked issue authorizing the work. Non-trivial changes require a linked issue for authorization.
Agent Prompt
## Issue description
Non-trivial changes require explicit authorization via a linked issue.

## Issue Context
This PR adds substantial new CI and lint infrastructure (workflow + pre-commit + gitlint config). Add an issue/ADR reference (and ideally link it in the PR description) that authorizes introducing these governance changes.

## Fix Focus Areas
- .github/workflows/lint.yml[1-83]
- .pre-commit-config.yaml[1-48]
- .gitlint[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .pre-commit-config.yaml
Comment on lines +48 to +55
- name: Lint PR title
if: github.event_name == 'pull_request'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "${PR_TITLE}" > /tmp/pr-title.txt
uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

5. Gitlint b6 mismatch 🐞 Bug ⚙ Maintainability

CI runs gitlint with --ignore B6, but the local commit-msg pre-commit gitlint hook is configured
without that ignore, so commits/PR titles can be rejected locally while still passing CI.
Agent Prompt
## Issue description
CI and local pre-commit enforce different gitlint rules: CI ignores rule `B6` while the pre-commit `commit-msg` hook does not. This creates inconsistent behavior for contributors.

## Issue Context
CI invokes: `gitlint --ignore B6 ...`. The pre-commit hook configuration for gitlint does not pass `--ignore B6`.

## Fix Focus Areas
- .github/workflows/lint.yml[48-55]
- .pre-commit-config.yaml[19-24]

## Suggested fixes
Pick one and apply consistently:
- Option A (match CI): add `args: [--ignore, B6]` to the pre-commit `gitlint` hook.
- Option B (match local): remove `--ignore B6` from CI so CI enforces the same rules as the hook.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +52 to +55
run: |
echo "${PR_TITLE}" > /tmp/pr-title.txt
uvx --from gitlint-core gitlint --config .gitlint --ignore B6 --msg-filename /tmp/pr-title.txt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

6. Unpinned gitlint-core 🐞 Bug ☼ Reliability

The commit-lint job runs uvx --from gitlint-core gitlint without pinning a version, so CI behavior
can drift over time and diverge from the pre-commit hook’s pinned gitlint version.
Agent Prompt
## Issue description
`uvx --from gitlint-core gitlint` does not pin a specific version, so CI lint results can change when `gitlint-core` releases new versions.

## Issue Context
Local pre-commit pins gitlint via the pre-commit repo `rev: v0.19.1`, but CI resolves `gitlint-core` dynamically.

## Fix Focus Areas
- .github/workflows/lint.yml[52-55]
- .pre-commit-config.yaml[19-21]

## Suggested fixes
- Pin the version used by `uvx`, e.g. `uvx --from 'gitlint-core==0.19.1' gitlint ...` (or the exact version you want to standardize on).
- Alternatively, standardize by running gitlint via pre-commit in CI (so CI uses the same pinned hook).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 5:13 PM UTC · Ended 5:28 PM UTC
Commit: 0a95cac · View workflow run →

Add .pre-commit-config.yaml with applicable linters from fullsend repo:
- Syntax checks: check-yaml, check-json, check-toml
- Hygiene: trailing-whitespace, end-of-file-fixer, mixed-line-ending
- Security: detect-private-key, gitleaks, check-added-large-files
- Shell: shellcheck (with SC1091, SC2001, SC2016 ignored)
- GitHub Actions: actionlint, pinact (scoped to lint.yml)
- Commits: gitlint (conventional commits, commit-msg hook)

Add .gitlint config enforcing conventional commit format.

Add .github/workflows/lint.yml CI workflow with two jobs:
- test: runs pre-commit on all files
- commit-lint: lints PR title and individual commit messages

Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean
ralphbean force-pushed the chore/add-linters branch from 291e098 to 16f645d Compare July 6, 2026 17:27
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:28 PM UTC · Completed 5:39 PM UTC
Commit: 16f645d · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review

Verdict: Approve

Clean addition of pre-commit linting infrastructure and CI workflow. The three new files are well-structured, follow existing repo conventions (SHA-pinned actions, ubuntu-24.04 runners, conventional commits types matching AGENTS.md), and the security posture is sound — minimal permissions (contents: read), untrusted inputs handled via env: context indirection, and binary downloads verified with SHA256.

A few low-severity observations for follow-up:

Low findings

1. pinact scope limited to lint.yml (.pre-commit-config.yaml:45)
The pinact hook hardcodes entry: pinact run --fix=false --no-api .github/workflows/lint.yml and the files regex only matches lint.yml. The repo also has release.yml (already SHA-pinned) and fullsend.yaml (externally managed). Future workflow files would silently bypass the SHA-pin check. Consider broadening to files: ^\.github/workflows/.*\.ya?ml$ with an exclude for fullsend.yaml.

2. Workflow/job naming (.github/workflows/lint.yml:1,14)
The workflow is named CI and the first job is named test, while the purpose is linting. Existing workflows use descriptive names (fullsend, Release). Consider name: Lint and renaming the job to lint or pre-commit for clarity in the Actions UI.

3. Commit-lint range edge cases (.github/workflows/lint.yml:71-73)
Two minor edge cases in the commit range logic: (a) on force-push to main, PUSH_BEFORE may be the null SHA or reference a missing commit; (b) for PRs that merge main into the branch, PR_BASE..PR_HEAD may enumerate upstream commits. Both are uncommon in practice and self-correcting (failed CI run, no data loss).

4. README documentation gap (README.md)
The README's Workflows table lists fullsend.yaml and release.yml but not the new lint.yml. There are also no contributor setup instructions for installing pre-commit hooks locally. Could be addressed in a follow-up.


Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • .github/workflows/lint.yml
  • .pre-commit-config.yaml

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

Comment thread .pre-commit-config.yaml
hooks:
- id: pinact
name: pinact (SHA-pin check)
entry: pinact run --fix=false --no-api .github/workflows/lint.yml

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] incomplete-coverage

The pinact hook hardcodes the entry command and files regex to only check .github/workflows/lint.yml. The repo also has release.yml (already SHA-pinned) and fullsend.yaml (externally managed). Future workflow files added to the repo would silently bypass the SHA-pin enforcement.

Suggested fix: Broaden the hook: files: ^.github/workflows/.*.ya?ml$ with an exclude for fullsend.yaml, and remove the hardcoded path from the entry command.

@@ -0,0 +1,83 @@
name: CI

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] naming-convention

Workflow name 'CI' is generic compared to existing descriptive names ('fullsend', 'Release'). Job name 'test' (line 14) is misleading for a job that runs pre-commit linters.

Suggested fix: Rename the workflow to 'Lint' and the first job to 'lint' or 'pre-commit' for clarity.

merge_group) RANGE="${MQ_BASE}..${MQ_HEAD}" ;;
pull_request) RANGE="${PR_BASE}..${PR_HEAD}" ;;
*) echo "Unknown event: ${EVENT_NAME}"; exit 1 ;;
esac

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] edge-case

The push-event commit range does not handle the null SHA (force-push or initial push to main). The pull_request range uses base branch tip rather than merge-base, which may enumerate upstream commits if the PR branch has merged main.

Suggested fix: Guard the push case against null SHA (0{40}). For pull_request, consider using git merge-base to compute the fork point.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Jul 6, 2026

@ifireball ifireball left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Lets get this merged ASAP

@ralphbean
ralphbean added this pull request to the merge queue Jul 7, 2026
Merged via the queue into main with commit c86d8c3 Jul 7, 2026
9 checks passed
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 7, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 10:30 PM UTC · Completed 10:35 PM UTC
Commit: 16f645d · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #29 — Initial repo bootstrap with linters and CI

PR #29 bootstrapped the fullsend-ai/agents repository with 96 files (14,283 lines), including all agent definitions, skills, harness configs, scripts, schemas, workflows, and linting infrastructure. Since this was a foundational setup PR authored and merged by a maintainer, there is no agent workflow graph to trace (no triage → code → review → fix cycle).

The retro focused on the linting and CI setup — the stated scope of the PR. Two concrete gaps were identified:

  1. Pinact SHA-pin check only covers one of five workflow files — regressions in release.yml, stale.yml, vouch-check.yml, and fullsend.yaml would go undetected.
  2. 3,086 lines of post-script tests are not run in CI — seven test scripts exist but no CI job executes them, so post-script regressions could reach production undetected.

Note: Could not verify whether open issues already cover these proposals because the GitHub token was not available for API access. If duplicates exist, these proposals should be closed as duplicates.

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants