sentinel¶
Audits the .neuroflow/ folder for consistency and drift. Called by the /sentinel command. Writes its report to .neuroflow/sentinel.md.
Default scope: .neuroflow/ only. Do not read, list, or inspect files outside .neuroflow/ unless the user explicitly requests a full workspace scan (see Optional: Full workspace scan below). The exceptions are named in the checks: .claude/CLAUDE.md and its stale copies (NF7), and the global ~/.neuroflow/ caches (S6–S9).
The mechanical checks have one executable home: nf_check.py in the neuroflow-core skill's scripts/ folder. Run it first and do not redo its checks by hand — spend your reading on the judgement checks in Step 2.
Step 1 — Mechanical checks (script)¶
Run from the project root (the folder that contains .neuroflow/):
Claude Code fills in ${CLAUDE_PLUGIN_ROOT} with the running plugin's folder; if the path reads literally, use the neuroflow-core base directory /sentinel passed you, and if there is none, say so in the report and do the checks in the table below by reading the files. Never look for the script under ~/.claude/plugins, where other versions may be cached (neuroflow:neuroflow-core → The plugin's own files). The script never writes anything.
| Exit code | Meaning | What to do |
|---|---|---|
| 0 | Clean | Note "NF1–NF8: all clear" for the report and go to Step 2. |
| 1 | Findings | Copy every finding (check id, path, line, message, suggested fix) into the report. info lines are notes, not issues. Then go to Step 2. |
| 2 | The script could not run (no .neuroflow/, bad arguments, internal error) |
Say so in the report, then do the checks in the table below by reading the files. |
Without Python, do the checks in the table by reading the files and say in the report that the script did not run.
| Id | Check | What it verifies |
|---|---|---|
| NF1 | flow.md index | The root flow.md lists every subfolder; every other subfolder — except sessions/, tasks/, wiki/ and meetings/, which have their own structure — has a flow.md that lists every file and folder in it; nothing listed is missing on disk; flow.md holds no narrative tables. |
| NF2 | project_config.md | YAML frontmatter per neuroflow-core (nf_schema, project_name, active_phase, recommended_phases, plugin_version); nf_schema not newer than the plugin knows (then nothing may write the file); active_phase and recommended_phases are canonical phases; plugin_version matches the installed plugin; no personal fields (auto_issue_reporting, writing_style, researcher, zotero, wiki_auto) — those live in ~/.neuroflow/user.yaml. A legacy dialect (no frontmatter) is reported, never rewritten. |
| NF3 | Integrity status | preregistration/status.md and ethics/status.md frontmatter. A frozen preregistration is re-hashed by the preregistration skill's freeze.py verify: a changed or missing frozen file is an error; a missing FROZEN banner or a freeze not set by a person is a warning. An approval set by the model counts as not set; an expired approval is an error. |
| NF4 | Reasoning logs | Every line of reasoning/*.jsonl is one JSON object with statement, source and reasoning; legacy *.json arrays are reported for /neuroflow:migrate. |
| NF5 | Conflict markers | No <<<<<<< / >>>>>>> lines anywhere under .neuroflow/. |
| NF6 | Memory structure | The .neuroflow/ root holds only the files and folders neuroflow-core documents, plus folders named after commands. Flags folders named after skills, legacy files (linked_flows.md, team.md), and project-level flowie/ or hive/ folders (both live under ~/.neuroflow/). |
| NF7 | Instruction block | .claude/CLAUDE.md exists, points at project_config.md and holds no Active phase line (it goes stale; the phase lives in project_config.md). No neuroflow block in ~/.claude/CLAUDE.md (injected into every session) or in .github/copilot-instructions.md / AGENTS.md (copies neuroflow no longer maintains). |
| NF8 | Sensitive data | Runs the phase-output skill's pii_scan.py over the shared (team-tier) files: emails, phone numbers, configured ID patterns, roster names, secrets. When NF8 says skipped, do the scan yourself in S5. |
Step 2 — Judgement checks¶
S1 — Timestamp drift¶
Compare the Last changed dates in flow.md files with the modification times of the files inside .neuroflow/. Flag:
- Subfolders of .neuroflow/ with recent file activity but stale flow.md dates
- Subfolders that have not been touched in a long time while the project is active (possible abandoned phase)
S2 — Phase consistency¶
Compare:
- active_phase in the frontmatter of .neuroflow/project_config.md (in a legacy dialect: the Phase: line)
- The most recent session log in .neuroflow/sessions/
- Which phase subfolders exist inside .neuroflow/ and when they were last modified
Flag if these tell different stories.
S3 — Preregistration vs progress¶
If .neuroflow/preregistration/ exists, read it. NF3 already checked that the frozen files are unchanged; here, compare the stated hypotheses and planned analyses against:
- .neuroflow/reasoning/ (were there decisions that deviate from the plan?)
- .neuroflow/data-analyze/ analysis summary (were different analyses run?)
Flag every deviation that has no entry in preregistration/deviations.md. Do not judge — just surface them for the user.
S4 — Ethics gate (if human data present)¶
Only runs if .neuroflow/data/ exists (data intake has happened). Read the frontmatter of .neuroflow/ethics/status.md (status, expires, set_by; NF3 already reported expiry and model-set approvals).
- If the file does not exist, or
statusis notapprovedwithset_by: person: flag as blocking — human data appears to have been collected or ingested without a recorded ethics approval. Suggest running/ethics --approvedto record it, or — if the project uses no human data — notingethics: not-applicablein theproject_config.mdfrontmatter, which silences this check. - If the earliest dated entry in
.neuroflow/data/predates the approval date: flag for human review — data may predate approval (it could also be legacy or shared data; do not judge, just surface).
S5 — Personal sensitive information¶
The deterministic scan — emails, phone numbers, configured ID patterns, roster names and secrets — has one home: skills/phase-output/scripts/pii_scan.py, which NF8 runs (it reports path, line and class, never the value). Do not repeat it; this check keeps the judgement calls a pattern cannot make.
Always check, inside .neuroflow/:
- Full names in unexpected locations: a sequence of two or more capitalised words (likely a full name) in a reasoning log, session log, or
flow.mdentry — rather than in thecollaborators:list ofproject_config.md. This check may produce false positives on proper nouns and tool names; treat every finding as requiring human confirmation. - Institutional affiliations outside
project_config.md: an institution name, department, or postal address in any other file. Treat findings as requiring human confirmation, as false positives on common terms are possible.
Only when NF8 was skipped (the script is missing or could not run), scan by reading for:
- Email addresses: strings matching
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}. This pattern covers the most common address formats; it does not handle quoted local parts, IP-address domains, or internationalised domain names — note any such edge cases manually. Skip addresses whose domain is clearly synthetic:example.com,example.org,test.com,domain.com, orlocalhost. - Passwords and secrets: lines where a key matching
password,passwd,secret,api_key,token, orprivate_key(case-insensitive, with:or=separator) is followed by a non-empty value. A value is a placeholder — skip it — if it is all-uppercase (e.g.YOUR_API_KEY), enclosed in angle brackets (e.g.<token>), or contains the wordplaceholder,example, orchangeme. - Private keys: strings beginning with
-----BEGIN(PEM-format private keys, certificates, or similar).
Do not print the sensitive value verbatim in the report. Mask it instead (e.g. email: j***@exam***.com, password: ***, private key in line 4 of reasoning/general.jsonl).
Flag each file and line number where a match is found. Mark name and institution findings as [needs human review] since automated detection of these categories is imprecise.
S6 — Flowie structure (if present)¶
This check only runs if ~/.neuroflow/flowie/ exists (global path — not inside any project repo). NF6 already flags a project-level .neuroflow/flowie/ folder.
- Git repo: check that
~/.neuroflow/flowie/.git/exists. If the folder exists but is not a git repo, flag it — it should be a clone of the user's privateflowieGitHub repository. - sync.json: check that
~/.neuroflow/flowie/sync.jsonexists and contains agithub_repofield with a non-empty value. Flag if missing or empty. - flowie_profiles binding: check that
flowie_profilesis set and non-empty inproject_config.md. If~/.neuroflow/flowie/is set up butflowie_profilesis absent, flag it — the project should be linked via/flowie --link. - Project registry match: if
flowie_profilesis set AND~/.neuroflow/flowie/projects/projects.jsonexists, check that the first entry's handle matches an entry in the projects array. Flag if no matching project is found. - Migration check: verify
project_config.mddoes NOT contain the legacy scalar fieldsflowie_project:orhive_member:(replaced by theflowie_profiles:list). Flag if found and suggest/neuroflow:migrate.
Flag any failed sub-check as a warning (not a blocking error — flowie may be intentionally partial). Group all flowie warnings under a single "⚠️ flowie" section in the report. All of them require user action (re-running /flowie or /flowie --link).
S7 — Wiki structure (if present)¶
Run for each wiki level that exists:
S7a — Flowie wiki (if ~/.neuroflow/flowie/wiki/ exists):
- index.md: exists and "Last updated" within 90 days
- log.md: exists and non-empty
- schema.md: exists — if missing, flag and suggest /flowie --wiki-schema
- raw/ and pages/: both directories exist
- Orphan check (light): files in wiki/pages/ not listed in wiki/index.md > 5 → flag, suggest /flowie --wiki-lint
- Log vs pages: last 10 ingest entries in log.md — check for a matching file in pages/sources/
Group under "⚠️ wiki (flowie)".
S7b — Project wiki (if .neuroflow/wiki/ exists):
- Same checks as S7a, but paths are relative to .neuroflow/wiki/
- If .neuroflow/wiki/ exists but is empty (only .gitkeep stubs from init): note as uninitialized — suggest running /wiki --schema to initialize
Group under "⚠️ wiki (project)". All issues require user action.
S8 — Hive structure (if present)¶
This check only runs if ~/.neuroflow/hives/ exists (global path). Hive caches live at ~/.neuroflow/hives/{org-repo}/ — one folder per hive the user belongs to. Check all sub-folders. NF6 already flags a project-level .neuroflow/hive/ folder.
For each ~/.neuroflow/hives/{org-repo}/ found:
- hive.md: check that ~/.neuroflow/hives/{org-repo}/hive.md exists and is non-empty. Flag if missing.
- members.md: check that ~/.neuroflow/hives/{org-repo}/members.md exists. Flag if missing — suggest running /hive --members to add the team roster.
- Sync state: check that ~/.neuroflow/hives/{org-repo}/.git/neuroflow-sync.json (or, from an older neuroflow, an untracked sync.json — suggest /neuroflow:migrate to move it) exists and contains hive_repo (non-empty) and last_pull fields. Flag any missing, and flag a sync.json the hive repo tracks (git -C … ls-files sync.json).
- project_config.md binding: check that hive_repo: is set in project_config.md. If a hive cache exists but hive_repo is absent from the config, flag as inconsistency.
- No old directions.md: if directions.md exists in the cache, flag it — directions are now merged into hive.md and directions.md is obsolete. Offer to delete it.
Group all hive warnings under "⚠️ hive". These are warnings, not blocking errors.
S9 — Project registry (if a flowie or a hive exists)¶
Once, when ~/.neuroflow/flowie/ or any ~/.neuroflow/hives/*/ exists: run python "${CLAUDE_PLUGIN_ROOT}/skills/neuroflow-core/scripts/projects.py" check --json (exit 0 clean, 1 findings, 2 it could not run — say so). Show each finding with its message in the session, under "⚠️ projects" — never in .neuroflow/sentinel.md: the messages name the person's private flowie projects, task files and folders, and that file is committed with the project. There, write one line only: S9 project registry: {N} finding(s), shown in the session (they come from your flowie and hives). The findings are two cards for one project (/neuroflow:migrate folds them), a pointer that leads nowhere or copies card fields, a local folder in a card's repos, a blocked_by that names no task on its board, an open task whose project: names no registered project (neuroflow-core → Project registry).
Report¶
Write to .neuroflow/sentinel.md (the last audit only — not a history):
Last run: YYYY-MM-DD
## Issues found
- [check id] [description of issue]
## All clear
(written only if zero issues found)
Then ask the user: for each issue, fix automatically or leave for manual review?
Fixes sentinel can apply automatically¶
Only after the user agrees, issue by issue:
- Add a missing row to the relevant
flow.md, or remove a row for a file that no longer exists (NF1) - Update
active_phaseinproject_config.mdif drift is unambiguous (S2) - Move
.mdfiles out of a skill-named subfolder in.neuroflow/into the active phase's subfolder, then delete the skill-named folder (NF6) - Write the static block from neuroflow-core (Project instruction block) to
.claude/CLAUDE.md, or replace an older block that names an active phase with it (NF7) - Remove the neuroflow block from
~/.claude/CLAUDE.md,.github/copilot-instructions.mdorAGENTS.md(NF7) — show the exact block first and keep the rest of those files - Add a missing
wiki/row to.neuroflow/flow.md(NF1, S7b); delete an obsolete hivedirections.md(S8)
Never automatic:
- The project registry (S9): two cards are folded by /neuroflow:migrate (it asks which values stay, and pushes to a hive only after a yes); a pointer, a blocked_by or a project: is fixed where it lives, with the person.
- An older or missing plugin_version (NF2): suggest /neuroflow:migrate, which records the version when it brings the project up to date, and checks the flowie and the hive in the same run. Never set the version by hand — that silences the update notice while the formats stay old
- Frozen preregistration files and preregistration/status.md (NF3, S3) — freezing and unfreezing are a person's actions; changes go to deviations.md. Never write set_by: person.
- Sensitive information (NF8, S5) — sentinel only surfaces findings. The user decides whether to redact, remove, or confirm each item.
After applying any fixes, rewrite .neuroflow/sentinel.md to reflect the current state — either listing only the remaining unfixed issues, or writing "All clear" if everything was resolved.
Optional — Full workspace scan¶
After completing the .neuroflow/ audit above, ask the user:
"Do you also want me to check consistency of the whole project folder (files outside
.neuroflow/)? This is off by default."
Only proceed with the steps below if the user answers yes.
WS-1 — Untracked / unlisted files¶
List files in the workspace root that are not tracked in any flow.md. Flag files that look like they should be documented (e.g. drafts, backups, data files) and suggest adding them to the relevant flow.md or archiving them.
WS-2 — flow.md cross-references to workspace files¶
For every entry in any flow.md that points to a path outside .neuroflow/, verify the path exists on disk. Flag broken paths.
WS-3 — Draft and backup files¶
Flag any files in the workspace whose name suggests they are backups or working copies (e.g. names containing _backup, _old, _draft, _temp, _copy, or ending in .bak). Note them for the user to archive or delete explicitly.
Do not read the contents of files outside .neuroflow/ — only check their names and paths.