Maintenance Automation¶
Six GitHub Actions workflows maintain neuroflow. Three scheduled ones post reports to GitHub Discussions, a weekly canary tests the neuroflow mod against the newest Claude Code release, one validates every pull request and push to main, and one deploys the documentation site. The three report workflows read the repo's own specification files (skills, commands, agents, docs) as their ground truth ("Option A"), so their output is always grounded in what the repo actually contains.
Workflows at a glance¶
| Workflow | Schedule | Discussion | Opens PRs? |
|---|---|---|---|
| Daily Maintainer Report | Daily 20:00 UTC | #167 | No |
| Sentinel-dev | Daily 20:00 UTC | #168 | Yes (auto-fixable only) |
| Research Radar | Weekly, Friday 20:00 UTC | #169 | No |
| Mod canary | Weekly, Monday 05:17 UTC | — (opens an issue when the mod breaks) | No |
| Validate | Every pull request and push to main |
— | No |
| Deploy documentation | Every push to main |
— | No |
All report comments begin with a status banner so you can stop reading immediately:
or1 — Daily Maintainer Report (.github/workflows/daily-maintenance.yml)¶
Purpose: Cluster open GitHub issues by theme, propose labels, and surface the top 3 recommended next actions.
Repo context read:
.neuroflow/project_config.md— project overview and plugin versiondocs/commands/index.md— declared command surfacecommands/**— command spec files (counts + frontmatter)skills/**— skill files (counts)agents/**— agent files (counts)
Script: scripts/automation/daily_maintenance.py
What it posts¶
- Plugin version, command/skill/agent counts
- Issues grouped by area: Commands/UX, Search/Literature, Memory/Architecture, Agents/Workflows, Data/Analysis, Docs/Repo
- Proposed labels for each issue (
type:feature,area:commands, etc.) - Top 3 recommended next actions sized as: quick win / medium / larger
- Stale issues (no update in ≥30 days)
How to change the schedule¶
Edit the cron line in .github/workflows/daily-maintenance.yml:
How to change the target discussion¶
Change --discussion-number 167 in the workflow's run: step.
2 — Sentinel-dev (.github/workflows/sentinel-dev.yml)¶
Purpose: Enforce internal consistency invariants in the plugin repo. Auto-fixes version drift by opening a PR; reports everything else.
Repo context read: All of commands/, skills/, agents/, hooks/, docs/, mkdocs.yml, .claude-plugin/, README.md, .neuroflow/.
Script: scripts/automation/sentinel_check.py — it runs the check registry in scripts/automation/repo_checks.py, the same one the per-PR validation runs. Each check is implemented once and keeps its id; the sentinel-dev agent cites the ids and adds the checks that need judgement.
Checks performed¶
| Id | What it verifies |
|---|---|
| V1 | plugin.json, marketplace.json and hooks/hooks.json are valid JSON |
| V2 | Version sync across plugin.json, marketplace.json, mkdocs.yml extra.version and .neuroflow/project_config.md |
| V3 | Command frontmatter: name, description, canonical phase, reads, writes, lifecycle; requires / produces / next lists |
| V4 | Every commands/*.md has a docs/commands/<name>.md page |
| V5 | Skill and agent name: fields match their folder or file |
| V6 | hooks/hooks.json: structure, silent failure (; true), at most one hooks module and its path |
| V7 | Version bumped when substantive files changed (pull requests only) |
| V8 | Rule markers (<!-- nf-rule: ID -->) use known ids; every rule a mod guard enforces has one |
| V9 | No skill folder shares a command's name |
| V10 | Every command, skill and agent is in the README tables and the docs nav, each page listed once; every relative link and image in the README resolves, and so does every link into this site (an in-page anchor nothing defines is a warning); the site logo, favicon and plugin icon exist; no dead nav links |
| V11 | No dead neuroflow:<name> references in SKILL.md files |
| V12 | The README has one What's new section, and a second one fails; it is for the current version, and docs/changelog.md lists that version first (warnings) |
| V13 | The repo's own .neuroflow/ holds only reasoning/ and sessions/ (warning) |
| V14 | No private keys; emails and hardcoded secrets flagged for review |
| V15 | No stale flowie/hive paths or legacy field names (warning) |
Auto-fixable issues (will create a PR)¶
- V2:
marketplace.json,mkdocs.ymland.neuroflow/project_config.mdout of sync withplugin.json— synced automatically (the same code asbump_version.py --sync). Only those files are committed.
Running it locally¶
python scripts/automation/sentinel_check.py --report-only prints the report and never touches git or GitHub. Outside GitHub Actions the script refuses to commit, push or post.
PR behaviour¶
- Branch name:
sentinel-dev/YYYY-MM-DD-auto-fix - PR title:
fix(sentinel): auto-fix consistency issues — YYYY-MM-DD - PR is linked in the Discussion comment
How to change the schedule or target discussion¶
Same pattern as Daily Maintainer: edit cron and --discussion-number in the workflow file.
Required permissions¶
The sentinel-dev workflow requires more permissions than the other report workflows:
permissions:
contents: write # to push fix branches
discussions: write # to post the report comment
pull-requests: write # to open the fix PR
These are set automatically in the workflow file. No additional secrets are needed; GITHUB_TOKEN is used.
3 — Research Radar (.github/workflows/research-radar.yml)¶
Purpose: Produce a weekly "Radar Brief" with new implementation ideas, detected capability gaps, and threats relevant to neuroflow's neuroscience/scientific workflow focus.
Repo context read: commands/, skills/, agents/, the version headings of docs/changelog.md, .claude-plugin/plugin.json.
Script: scripts/automation/research_radar.py
What it posts¶
- New ideas — emerging topics with priority ratings and suggested implementation locations
- Detected capability gaps — research domains with sparse coverage in current commands/skills
- Threats / watchlist — API changes, dependency risks, compliance notes
- Proposed backlog entries — ready-to-file feature or resilience tasks (no PRs opened)
Web crawling
The current implementation is self-contained and deterministic — it does not make external web requests. The PubMed and bioRxiv MCP servers are already configured in .claude-plugin/plugin.json and can be wired in later to source live research signals.
How to change the schedule¶
4 — Per-PR validation (.github/workflows/validate.yml)¶
Purpose: Catch structural mistakes before they merge. Runs on every pull request and every push to main, in two jobs.
validate job:
python scripts/automation/validate_pr.py --base origin/<base branch>— all checks V1–V15 fromrepo_checks.py. Findings markedfailfail the job;warnfindings are printed for review. Exit codes:0no failures,1failures,2usage or internal error.python -m unittest discover -s tests -p "test_*.py" -v— unit tests for the repo checks,bump_version.py, the pre-push hook, a smoke test that feeds simulated tool events into thehooks/hooks.jsoncommands, and the skills' portable scripts. Runs even when step 1 failed.mkdocs build --strict— the docs site builds without broken links.
mod job (the neuroflow mod): installs Claude Code 2.1.292, the version the mod is tested from, then runs
claude plugin validate .— the plugin manifest and its hooks module;claude plugin test .— the mod's tests inhooks/mod/tests/;python scripts/automation/mod_policy.py— compares the events the module hooks and the calls, environment variables and state the validator reports with the allowlist inhooks/mod/policy.json; anything new fails until a reviewer adds it.
Steps 2 and 3 run even when an earlier step failed.
The same validation runs locally from the repo root. To bump the version in all four places at once: python scripts/automation/bump_version.py (--dry-run, --sync, --check).
5 — Mod canary (.github/workflows/mod-canary.yml)¶
Purpose: Find out when a new Claude Code release breaks the neuroflow mod — function hooks are an early-access API that changes between releases — before users do.
Schedule: weekly, Monday 05:17 UTC (cron: "17 5 * * 1").
Steps: installs the newest Claude Code (@anthropic-ai/claude-code@latest) and runs the mod job's three checks. When one fails, it opens an issue titled mod canary: the neuroflow mod fails on Claude Code <version>, unless an open issue with that title already exists.
6 — Deploy documentation (.github/workflows/deploy-docs.yml)¶
Purpose: Publish the documentation site. On every push to main it installs requirements.txt, downloads the intro video, runs mkdocs build --strict and deploys the result to GitHub Pages.
The intro video plays on the landing page and on the Overview (overview/#intro-video). It is the asset neuroflow-en.mp4 of the GitHub Release intro-video, not a file in git, so plugin installs never contain it: .gitignore keeps docs/assets/video/*.mp4 out, and only the poster, docs/assets/video/poster-en.jpg, is committed. Before the build, the step Fetch the intro video runs gh release download intro-video --pattern neuroflow-en.mp4 --dir docs/assets/video with the workflow's own token. If the release or the asset is missing, or the file is under 1 MB, the build job fails with an error annotation, the deploy job is skipped and the live site stays as it was. Pull-request builds (validate) never fetch it; their players show the poster, and tests/automation/test_site_video.py checks the video markup that strict mode cannot see.
To replace the video, keep its file name and run, from a clone of the repository:
gh release upload intro-video neuroflow-en.mp4 --clobber
gh release view intro-video --json assets --jq '.assets[] | {name, size}'
gh workflow run deploy-docs.yml --ref main
--clobber deletes the old asset before it uploads the new one, so check the size with gh release view right away: while the asset is missing, every docs deploy fails (the live site keeps its copy). Run the deploy on main, because the github-pages environment deploys only from there. GitHub Pages caches files for up to ten minutes, so the old video can still be served for a while. A video of another length, or a new poster, needs a pull request: the length (4:33) is written in overrides/home.html, docs/overview.md and README.md (its caption and alt text, and the 'WATCH · 4:33' label in the README thumbnail .github/readme/intro-video.jpg), and the poster is committed. A file of another size needs one too: the '20 MB' download labels are in overrides/home.html and docs/overview.md. For a local preview, download the asset into docs/assets/video/ (gh release download intro-video --pattern neuroflow-en.mp4 --dir docs/assets/video); mkdocs serve answers without byte ranges, so seeking is limited there and Safari may not play it at all; GitHub Pages serves byte ranges, so check those on the live site.
Brand images (scripts/automation/brand_assets.py)¶
The ring and every image made from it come from one generator: the site logo and favicon, the link-preview card, the README lockups and the plugin icon in docs/assets/brand/. Colours come from docs/stylesheets/nf.css, shapes from the generator's constants; the Visual style section of the neuroflow-develop skill has the asset table.
python scripts/automation/brand_assets.py(--check, the default) fails when a committed SVG differs from what the generator writes, an image has the wrong size or weight, or an SVG breaks a rule (no<text>, no external references).tests/automation/test_brand_assets.pyruns it in CI; it needs no browser.--writerewrites the SVGs.--pngrenders the PNGs with a local Chrome or Edge (--browser PATH) and needs the network, because the images with text load Albert Sans and DM Mono from Google Fonts, as the landing page does.- No workflow sets the GitHub social preview: upload
docs/assets/brand/social-card.pngby hand under Settings → General → Social preview.
Shared posting script¶
File: scripts/automation/post_discussion.py
The three report workflows use this script to post comments to GitHub Discussions via the GraphQL API.
Usage:
python scripts/automation/post_discussion.py \
--repo owner/name \
--discussion-number 167 \
--body "Your markdown comment here"
Requirements:
GITHUB_TOKENenvironment variable must be set- The token must have
discussions: writepermission (the defaultGITHUB_TOKENin Actions has this)
Error handling:
- Exits with code 1 and a clear message if the token lacks permission (HTTP 401/403 or GraphQL
forbiddenerror) - Exits with code 1 if the discussion number is not found
Permissions and security¶
- No secrets are stored in the repo — only
GITHUB_TOKEN(auto-provided by Actions) is used. - The
daily-maintenanceandresearch-radarworkflows use minimal permissions (contents: read,discussions: write). - The
sentinel-devworkflow additionally needscontents: writeandpull-requests: writeto create fix branches and open PRs. validateonly reads (contents: read);mod-canaryaddsissues: writeto open its issue;deploy-docsneedspages: writeandid-token: writeto publish the site, and itscontents: readcovers the download of the intro video.- Every workflow installs its tools first — Python packages from PyPI, and Claude Code from npm where it is needed. At run time the report workflows call no external service except
api.github.com, and the docs deploy downloads the intro video from the repository's ownintro-videorelease and publishes to GitHub Pages.
Running workflows manually¶
Every workflow supports workflow_dispatch, so you can trigger it from the GitHub Actions UI without waiting for its schedule or a push:
- Go to Actions in the repo
- Click the workflow name
- Click Run workflow → Run workflow
Adding a new repo check¶
Add a function to scripts/automation/repo_checks.py that takes a Context and returns a list of Findings, and register it in CHECKS under the next free id — never renumber or reuse an id, retire it instead. Add a test to tests/automation/test_repo_checks.py and a row to the check table in agents/sentinel-dev.md. validate_pr.py (every PR) and sentinel_check.py (daily) pick it up automatically. Use severity=WARN for heuristics that need a human, and fix="version-sync" only for drift the daily job may repair on its own.