Repository navigation
feat(machine-health): add the short report and monthly schedule - #9243
Conversation
Format-ShortReport.ps1 prints the closing run summary, at most 12 lines, from latest.json, with every line collapsed by Format-MachineText so no machine string can add a line or forge the fixed first line. The audit skill runs the orchestrator in the background, maps --scheduled to a 60 s sample and no elevated offer, and relays the short report last. Setup gains a schedule step: a Desktop task or a Task Scheduler task running the monthly-run.ps1 template copied to <StateBase>/scheduler/, created only on the owner's yes. The wrapper counts a run ok only on exit 0, is_error false, the short report's first line and a newer latest.json, stops the process tree at its time limit, and writes a status file that setup check reports. Setup check also accepts the process mute kind top-processes emits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the process kind, the run kind in startup identity, the detection id in detection identity, and unkeyed per-version firewall rules, as the machine-health checks shipped them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup schedule step refuses a state or plugin root holding a single quote or control character, shows the created task's action, and no longer restates the cron and schtasks schedule values in prose. The audit skill states the --scheduled parameters once. The short-report test fixtures use named parameters and non-state-changing verbs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The orchestrator now records run_stamp, report_path, page_path and cadence_deferred_count in latest.json, and stamps the run with the invariant culture so a non-Gregorian culture cannot name files by the wrong year. Format-ShortReport prints the recorded paths instead of rebuilding them, passes the deferred count to the delta, reads two history lines, and names the host the way the orchestrator does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…am wait The wrapper now runs the audit with --permission-mode dontAsk and an exact tool list (Read, Edit of TODO.md, pwsh -NoProfile -File through Bash or PowerShell), denies WebFetch and WebSearch, and loads no MCP server. It counts the marker only when the result's first line names latest.json's run_id, reads output in chunks so a process left holding stdout cannot hang it, and records streams_incomplete. The shim's result is the real short report, tying the marker to Format-ShortReport. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s run age Under --scheduled, discovery records proposals in the report and TODO.md and authors or registers no check, and the short report is the run's whole final message. Setup's schedule step describes the dontAsk tool list and its limits, refuses the fallback for a synced or --plugin-dir state folder, and check step 6 fails a status file older than 35 days and notes a stale wrapper copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… checks The latest.json artifact test now runs weekly with two checks re-enabled at monthly cadence that ran three days ago, so cadence_deferred_count must be 2, and reads the snapshot through JsonNode instead of ConvertFrom-Json -DateKind, which PowerShell 7.4 lacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fallback wrapper's allow rules were `pwsh -NoProfile -File *`, which
also matched `-File -` (a script piped on stdin) and any .ps1 on disk.
Setup now writes the plugin root into its copy, so the rules name exactly
Invoke-MachineHealthCheck.ps1 and Format-ShortReport.ps1 under that root,
and the audit writes those two commands from ${CLAUDE_PLUGIN_ROOT}, the
same substitution. `-File -` is denied outright.
The run also gets --settings scheduled-settings.json, whose deny rules
beat the owner's own allow rules for git, gh, download commands,
subagents and other writing tools. The wrapper refuses to start without
it, or once the pinned root is gone, and refuses a .cmd claude when an
argument holds a character cmd.exe would rewrite. Its stderr log names
the claude it ran, and the status file is written then renamed.
Setup check fails a copy left stale by a plugin update, a copy that
produced no status file in 35 days, and an unreadable status file.
Schedule shows the owner their own shell, write and network allow rules
before either task is created, and the Desktop route says it passes no
tool list. TODO.md docs name Write or Edit, both covered by the one
Edit(/TODO.md) rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d refuse quote-bearing roots The roots block keeps typed paths out of every PowerShell command, but a permission rule matches command text, so the monthly wrapper can pin its two scripts only by a path typed in the command. The two --scheduled commands stay typed, in text blocks, as the one exception the roots block names. - A claude -p probe in dontAsk showed the PowerShell tool approves no pwsh call by rule (PowerShell(pwsh *) and an exact rule both denied), while Bash(pwsh -NoProfile -File '<script>' *) runs a plain call and denies one carrying $( ) in its arguments. The wrapper drops its PowerShell allow twins, and the audit runs the scheduled commands through the Bash tool. - Assert-ScheduleRoot refuses a plugin root or state folder holding ', U+2018 to U+201B, a control character or a line break. Get-MonthlyRunText and Install-MonthlyRun call it, and setup's schedule step runs it before either route, so the wrapper's own root literal and the audit's typed roots stay data. - Setup's check, schedule refusal, install and run-now commands now append to the roots block. - Tests: the wrapper's Bash allow rules must match the two scheduled commands audit SKILL.md prints; every quote character and line break is refused; the setup templates parse with hostile roots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scheduled run could not type the default report folder: dontAsk leaves it no command that reads USERPROFILE, and an environment variable inside single quotes stays literal. The orchestrator now applies the audit's default when -OutputBase is omitted, and the scheduled command leaves it out when report_dir is unset. Setup's roots block reads report_dir, and schedule refuses a report folder the run cannot quote and shows the folder the monthly runs write to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lashes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The wrapper's allow rules ended in a wildcard, so the scheduled session could pass any -StateBase, and the orchestrator runs a catalog overlay's check scripts from whatever state folder it is given. Setup now writes the plugin root, state folder and report folder into the wrapper, whose two rules match the audit's --scheduled commands exactly; a changed report_dir leaves the copy stale. Setup also refuses % & | < > ^ in a root, which the wrapper will not pass through a .cmd claude. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-security-review reviewed Incremental security review of The change adds one clause: on a |
|
claude-review reviewed No findings. The only change since c24391a is one sentence in step 5 of On a I read steps 5 and 8 and the Elevated pass section and found no conflict:
I did not run the Pester suite or any scripts. This is a doc-only change and I reviewed the text only. The PR's verification claims (1412 passed, 0 failed) are the author's and I did not check them. No closing issue exists ("No related issue"). No manifest was touched, so there are no |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27dbddd3e7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…n-lock The audit and setup schedule text restated how permission rules, dontAsk, deny precedence, Desktop missed runs and Git Bash path conversion behave. Each now states our decision and points at the anchored docs section. A run-lock result now skips discovery and the short report, whose latest.json belongs to an earlier run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…levated pass Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No related issue: phase 11 of the owner-approved machine-health checkup extension plan (ADR 0081 series).
Summary
Each audit run now ends with a short report of at most 12 lines.
/machine-health:setup schedulecreates a monthly checkup, only on the owner's yes, that delivers it: a Claude Desktop scheduled task, or a Task Scheduler fallback running a wrapper copied into the state folder. The wrapper runs the audit unattended in dontAsk mode, allows only the two exact audit commands, and records whether the run really happened.Fix
Format-ShortReport.ps1prints the short report fromstate/latest.json: host and run id, elevation, severity counts and the change since the prior run, the worst three CRIT or WARN items, the intruder headline, the largest folder, and the report and page paths. The first line is alwaysmachine-health checkup <host> <run_id>, and every machine-derived string is collapsed to one line.--scheduledaudit mode: 60 s sampling, no elevated-pass offer, discovery only proposes checks in the report andTODO.md, and the short report is the whole final message. It types the roots into the two scripts' command lines so a permission rule can match them. Whenreport_diris unset it leaves out-OutputBase, and the orchestrator applies the audit's default report folder;-OutputBaseis now optional.setup/templates/monthly-run.ps1):claude -pin dontAsk with a deny settings file (git, gh, downloads, subagents, writes outsideTODO.md), no web tools and no MCP servers. Setup writes the plugin root, state folder and report folder into the copy, and its two Bash allow rules match the audit's--scheduledcommands exactly, so a different-StateBaseor any other argument is denied. A run counts as ok only on exit 0, no error, the marker line naming latest.json's run id, and a newer latest.json. Results go to a status file written whole, then renamed.schedulerefuses a root holding a quote (', U+2018-U+201B), a control character or% & | < > ^. It shows the owner's own shell, write and network allow rules and the report folder before asking. Setupcheckreports the newest status file and fails a stale or never-fired copy.Verification
Invoke-MachineHealthTests.ps1, pre-existing non-Windows skip).checkup-view.test.sh,check-shell-portability.sh origin/main, markdownlint and typos are clean.Format-ShortReportprinted 8 lines with the fixed first line. An omitted-OutputBasewrote the report under the roots-block default. A holdout typing-ReportRoot '${user_config.report_dir}'into the schedule command failed 9 of 9 hostile-root cases.*, so a steered session could pass another-StateBase, whose catalog overlay names its own check scripts. Fixed with exact rules. Aclaude -p2.1.305 dontAsk probe confirmed that an exactBash(pwsh -NoProfile -File '<script>' -StateBase '<x>')rule runs that command and denies the same command with another-StateBase. Its suggestions were also taken: a report root is checked at setup, and% & | < > ^are refused at setup.monthly-run.ps1run withok: true, and a Desktop Run now), which waits on the owner creating the task after merge. Also left out: atomic writes for the two installed files (Fable suggestion 4). A half-written copy failscheckstep 6 throughTest-MonthlyRunCurrent, and rerunningschedulerewrites it.Related
🤖 Generated with Claude Code