The CI setup for mecha-hayabusa is minimal and has a few concrete gaps worth hardening. The test workflow runs unit tests on a single interpreter with no linting, formatting, type-checking, or coverage, and does not exercise any Python version matrix despite requires-python = ">=3.13". Separately, the docs workflow runs a --strict MkDocs build (which correctly fails on broken links/nav) only on push to main, so a PR that breaks the strict build is caught only after it merges. Finally, the two workflows pin the same actions to different major versions (actions/checkout@v4 in test.yml vs @v7 in docs.yml), which is an inconsistency that should be unified. None of these are bugs in the tool itself — this is CI hardening.
Problem
1. test.yml does only bare unit tests, on one interpreter
.github/workflows/test.yml (full file):
name: tests
on:
push:
branches: [main]
pull_request:
jobs:
unit:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --frozen
- name: Run unit tests
run: uv run python -m unittest discover -s tests -v
- There is no lint (
ruff check), no format check (ruff format --check), no type check (mypy), and no coverage step. The repository also has no [tool.ruff] / [tool.mypy] configuration anywhere — pyproject.toml is 11 lines and contains only [project] metadata plus dependencies (confirmed: no ruff/mypy/flake8/tox config files in the repo).
- There is no Python version matrix.
pyproject.toml:6 declares requires-python = ">=3.13", an open-ended lower bound, yet CI runs on exactly one interpreter (ubuntu-latest's default). Any incompatibility with a newer interpreter — or with 3.12 if the floor is ever lowered — goes untested.
astral-sh/setup-uv@v5 is used with no cache configured, so dependency resolution/download is repeated on every run.
For a repo whose core is a ~99 KB server.py (the MCP server exposing Hayabusa CSV to an LLM) plus a substantial test suite (9 test modules under tests/), the absence of a lint/type gate means style regressions and type errors can land on main unnoticed.
2. docs.yml runs --strict only on main, not on PRs
.github/workflows/docs.yml:6-12:
on:
push:
branches: [main]
paths:
- "website/**"
- ".github/workflows/docs.yml"
workflow_dispatch:
The strict build itself is good — .github/workflows/docs.yml:42:
- name: Build site (strict)
run: mkdocs build --strict --config-file website/mkdocs.yml
mkdocs build --strict fails on broken links and nav problems, which is exactly what you want. But because the workflow only triggers on push to main (and workflow_dispatch), and never on pull_request, a PR that edits website/** and breaks the strict build passes CI, merges, and only then fails — on main, where it also blocks the Pages deploy. The breakage is discovered at the worst possible time instead of during review.
3. Action major versions disagree between the two workflows
The same action is pinned to different majors depending on the file:
.github/workflows/test.yml:13 → actions/checkout@v4
.github/workflows/docs.yml:28 → actions/checkout@v7
docs.yml additionally uses actions/setup-python@v6 (line 33), actions/configure-pages@v6 (line 45), actions/upload-pages-artifact@v5 (line 48), and actions/deploy-pages@v5 (line 61). The concrete, unambiguous problem is the checkout@v4 vs checkout@v7 split — there's no reason for two workflows in the same repo to pin actions/checkout to different majors. Pick one verified major and use it in both.
Impact
Low severity, maintainer-facing only. This tool runs locally for a single DFIR analyst; none of these gaps affect runtime behavior or security of an investigation. The practical costs are:
- Regressions slip onto
main: lint/type/format issues and single-interpreter blind spots aren't caught before merge.
- Doc breakage is caught late: a
--strict failure introduced in a PR isn't visible until after merge, where it also blocks the Pages deploy and requires a follow-up fix on main.
- Inconsistent, drifting action pins: harder to reason about and maintain; the
v4 pin is stale relative to v7 used next door.
No user data or investigation output is at risk. This is purely CI/maintainability hardening.
Suggested fix
All changes are confined to .github/workflows/. This does not touch the skill/investigate/ or skill/investigate_jp/ trees, so no EN/JP duplication is required for this fix.
(a) Add lint + format-check (and optionally type-check + coverage) to test.yml, and add a Python matrix. Since astral-sh/uv is already the toolchain, run the tools through uv:
name: tests
on:
push:
branches: [main]
pull_request:
jobs:
unit:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ["3.13"] # add "3.12" here if the floor is lowered
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true # cache uv downloads across runs
- name: Install dependencies
run: uv sync --frozen
- name: Lint (ruff)
run: uvx ruff check .
- name: Format check (ruff)
run: uvx ruff format --check .
- name: Type check (mypy) # optional
run: uvx mypy server.py state.py report.py
- name: Run unit tests (with coverage)
run: uv run python -m coverage run -m unittest discover -s tests -v
- name: Coverage report # optional
run: uv run python -m coverage report
Add a minimal [tool.ruff] section to pyproject.toml so lint rules are explicit and reproducible locally, e.g.:
[tool.ruff]
target-version = "py313"
line-length = 100
(If mypy/coverage steps are kept, add them to a dev dependency group or rely on uvx as shown. Start lenient — enabling ruff in check-only mode first — to avoid a large one-time reformat.)
(b) Run the strict docs build on PRs (build-only, no deploy). Add a pull_request trigger scoped to the same paths and guard the deploy so it stays main-only. Either extend docs.yml with a pull_request: trigger and gate the deploy job with if: github.event_name == 'push', or add a small dedicated PR check:
name: docs-strict (PR)
on:
pull_request:
paths:
- "website/**"
- ".github/workflows/docs.yml"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v6
with:
python-version: "3.x"
cache: pip
- run: pip install -r website/requirements.txt
- name: Build site (strict, no deploy)
run: mkdocs build --strict --config-file website/mkdocs.yml
(c) Unify action pins. Choose one verified major for actions/checkout and use it in both workflows (e.g. bump test.yml:13 from @v4 to match docs.yml's @v7, after confirming v7 is a real released major). Keep the Pages actions (configure-pages, upload-pages-artifact, deploy-pages, setup-python) consistent as well.
Verification
-
Lint/format/type/matrix: open a draft PR that (1) introduces a deliberate lint violation (e.g. an unused import) and confirm the ruff check step fails; (2) revert it and confirm the job passes on each matrix entry. Confirm the astral-sh/setup-uv cache is populated on the second run (visible in the step log).
-
Docs on PR: on a branch, introduce a broken internal link in website/docs/** and open a PR; the new PR docs job must fail with a --strict error before merge. Fix the link and confirm it goes green. Confirm the deploy job does not run on pull_request events (only on push to main).
-
Action pins: after unifying, grep the workflows to confirm a single actions/checkout major:
grep -rn "actions/checkout@" .github/workflows/
Both lines should report the same major, and both workflows should still complete successfully on a trial run.
Filed as part of a full architecture & code review of the repository; each finding was independently re-verified against the current code before filing.
The CI setup for
mecha-hayabusais minimal and has a few concrete gaps worth hardening. The test workflow runs unit tests on a single interpreter with no linting, formatting, type-checking, or coverage, and does not exercise any Python version matrix despiterequires-python = ">=3.13". Separately, the docs workflow runs a--strictMkDocs build (which correctly fails on broken links/nav) only on push tomain, so a PR that breaks the strict build is caught only after it merges. Finally, the two workflows pin the same actions to different major versions (actions/checkout@v4intest.ymlvs@v7indocs.yml), which is an inconsistency that should be unified. None of these are bugs in the tool itself — this is CI hardening.Problem
1.
test.ymldoes only bare unit tests, on one interpreter.github/workflows/test.yml(full file):ruff check), no format check (ruff format --check), no type check (mypy), and no coverage step. The repository also has no[tool.ruff]/[tool.mypy]configuration anywhere —pyproject.tomlis 11 lines and contains only[project]metadata plus dependencies (confirmed: noruff/mypy/flake8/toxconfig files in the repo).pyproject.toml:6declaresrequires-python = ">=3.13", an open-ended lower bound, yet CI runs on exactly one interpreter (ubuntu-latest's default). Any incompatibility with a newer interpreter — or with 3.12 if the floor is ever lowered — goes untested.astral-sh/setup-uv@v5is used with no cache configured, so dependency resolution/download is repeated on every run.For a repo whose core is a ~99 KB
server.py(the MCP server exposing Hayabusa CSV to an LLM) plus a substantial test suite (9 test modules undertests/), the absence of a lint/type gate means style regressions and type errors can land onmainunnoticed.2.
docs.ymlruns--strictonly onmain, not on PRs.github/workflows/docs.yml:6-12:The strict build itself is good —
.github/workflows/docs.yml:42:mkdocs build --strictfails on broken links and nav problems, which is exactly what you want. But because the workflow only triggers onpushtomain(andworkflow_dispatch), and never onpull_request, a PR that editswebsite/**and breaks the strict build passes CI, merges, and only then fails — onmain, where it also blocks the Pages deploy. The breakage is discovered at the worst possible time instead of during review.3. Action major versions disagree between the two workflows
The same action is pinned to different majors depending on the file:
.github/workflows/test.yml:13→actions/checkout@v4.github/workflows/docs.yml:28→actions/checkout@v7docs.ymladditionally usesactions/setup-python@v6(line 33),actions/configure-pages@v6(line 45),actions/upload-pages-artifact@v5(line 48), andactions/deploy-pages@v5(line 61). The concrete, unambiguous problem is thecheckout@v4vscheckout@v7split — there's no reason for two workflows in the same repo to pinactions/checkoutto different majors. Pick one verified major and use it in both.Impact
Low severity, maintainer-facing only. This tool runs locally for a single DFIR analyst; none of these gaps affect runtime behavior or security of an investigation. The practical costs are:
main: lint/type/format issues and single-interpreter blind spots aren't caught before merge.--strictfailure introduced in a PR isn't visible until after merge, where it also blocks the Pages deploy and requires a follow-up fix onmain.v4pin is stale relative tov7used next door.No user data or investigation output is at risk. This is purely CI/maintainability hardening.
Suggested fix
All changes are confined to
.github/workflows/. This does not touch theskill/investigate/orskill/investigate_jp/trees, so no EN/JP duplication is required for this fix.(a) Add lint + format-check (and optionally type-check + coverage) to
test.yml, and add a Python matrix. Sinceastral-sh/uvis already the toolchain, run the tools throughuv:Add a minimal
[tool.ruff]section topyproject.tomlso lint rules are explicit and reproducible locally, e.g.:(If
mypy/coveragesteps are kept, add them to a dev dependency group or rely onuvxas shown. Start lenient — enablingruffin check-only mode first — to avoid a large one-time reformat.)(b) Run the strict docs build on PRs (build-only, no deploy). Add a
pull_requesttrigger scoped to the same paths and guard the deploy so it staysmain-only. Either extenddocs.ymlwith apull_request:trigger and gate thedeployjob withif: github.event_name == 'push', or add a small dedicated PR check:(c) Unify action pins. Choose one verified major for
actions/checkoutand use it in both workflows (e.g. bumptest.yml:13from@v4to matchdocs.yml's@v7, after confirmingv7is a real released major). Keep the Pages actions (configure-pages,upload-pages-artifact,deploy-pages,setup-python) consistent as well.Verification
Lint/format/type/matrix: open a draft PR that (1) introduces a deliberate lint violation (e.g. an unused import) and confirm the
ruff checkstep fails; (2) revert it and confirm the job passes on each matrix entry. Confirm theastral-sh/setup-uvcache is populated on the second run (visible in the step log).Docs on PR: on a branch, introduce a broken internal link in
website/docs/**and open a PR; the new PR docs job must fail with a--stricterror before merge. Fix the link and confirm it goes green. Confirm thedeployjob does not run onpull_requestevents (only onpushtomain).Action pins: after unifying, grep the workflows to confirm a single
actions/checkoutmajor:grep -rn "actions/checkout@" .github/workflows/Both lines should report the same major, and both workflows should still complete successfully on a trial run.
Filed as part of a full architecture & code review of the repository; each finding was independently re-verified against the current code before filing.