Skip to content

fix(brain): keep other-host prompts within the action schema - #684

Merged
vladimirrott merged 3 commits into
lacs-project:mainfrom
QinXi-ai:fix/672-other-host-prompt
Oct 27, 2026
Merged

vladimirrott merged 3 commits into
lacs-project:mainfrom
QinXi-ai:fix/672-other-host-prompt

Conversation

@QinXi-ai

@QinXi-ai QinXi-ai commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #672. On detected other hosts, the prompt no longer recommends a state action excluded by propose_plan. Neither existing OS snapshot action is offered there, so the renderer explicitly directs unsupported overview requests to refuse, keeps the six worked examples, and uses the available read-only ListJobHistory step to finish history and preference-only intents.

Fedora, Ubuntu, Debian and no-hint rendering retain their existing text. Shared template expansion was compared byte for byte with the base. Action schemas and daemon/client eligibility fences are unchanged.

Related Issue

Closes #672.

Validation

  • Added a prompt-versus-schema test for Fedora, Ubuntu, Debian, Arch, openSUSE, Alpine and no hint. It failed before the fix with unoffered actions ["GetSystemState"] on Arch.
  • Added unknown-host example/preference completion coverage and expanded placeholder checks.
  • Updated docs/action-compatibility.md.
  • Security impact considered; daemon remains the only privileged executor.
  • cargo nextest run --locked --offline -p sysknife-brain: 364 passed, 0 skipped.
  • cargo clippy --locked --offline -p sysknife-brain --all-targets --all-features -- -D warnings, formatting and whitespace checks passed.
  • Full GitHub CI pending: at 3fb7dc5, all checks other than the Rust job pass (container-smoke is intentionally skipped). The Rust job is still installing Tauri system dependencies, before compilation or tests.

Notes for Reviewers

Two Rust tests were added. Full local workspace measurement is blocked by missing GLib/GIO/GObject development libraries; the recorded test baseline and published counts are left unchanged per CONTRIBUTING's fallback. scripts/ci-local.sh --no-postgres was run and was incomplete: workspace Rust checks hit those libraries, frontend checks hit the local Node/tooling setup, and several required lint/security helpers are absent. The final local-run summary is included below; it remains incomplete after correcting the WSL Git worktree paths.

Live model/VM story validation has not been performed. The deterministic checks establish prompt/schema consistency, not live model behavior. This contribution is AI-assisted.

Local CI summary
PASS  rust: cargo fmt --all --check
FAIL  rust: cargo clippy --workspace --all-features --all-targets --locked -- -D warnings
FAIL  rust: cargo doc (RUSTDOCFLAGS=-D warnings)
FAIL  rust: cargo nextest run --workspace --locked (+ test baseline)
FAIL  frontend: npm ci (falls back to npm install)
SKIP  frontend: npm audit / tsc / vitest -- SKIPPED (dependency install failed)
PASS  hygiene: firewall backend reporter fixtures
PASS  hygiene: check_repo_completeness.sh
PASS  hygiene: check_test_reachability.sh
PASS  hygiene: check_release_versions.sh
FAIL  hygiene: verify-action-pins.sh
FAIL  hygiene: npm test --prefix packages/setup
PASS  hygiene: tests/release/action-pin-comments.test.sh
PASS  hygiene: tests/release/action-steps.test.sh
PASS  hygiene: tests/release/audit-export-confidentiality.test.sh
PASS  hygiene: tests/release/cargo-audit-schedule.test.sh
PASS  hygiene: tests/release/cassette-replay-parity.test.sh
PASS  hygiene: tests/release/ci-local.test.sh
PASS  hygiene: tests/release/codex-plugin-manifest.test.sh
PASS  hygiene: tests/release/crates-index-poll.test.sh
PASS  hygiene: tests/release/database-path-agreement.test.sh
FAIL  hygiene: tests/release/docs-share-cards.test.sh
PASS  hygiene: tests/release/fail2ban-ban.test.sh
PASS  hygiene: tests/release/grub-kargs-edit.test.sh
PASS  hygiene: tests/release/install-paths.test.sh
PASS  hygiene: tests/release/log-edit.test.sh
FAIL  hygiene: tests/release/macos-survey-command.test.sh
PASS  hygiene: tests/release/markdown-link-files.test.sh
PASS  hygiene: tests/release/mdbook-links.test.sh
PASS  hygiene: tests/release/mount-edit.test.sh
PASS  hygiene: tests/release/no-secrets.test.sh
PASS  hygiene: tests/release/node-eol.test.sh
PASS  hygiene: tests/release/npm-audit-off-pr-path.test.sh
PASS  hygiene: tests/release/postgres-contract-guard.test.sh
PASS  hygiene: tests/release/public-claims.test.sh
PASS  hygiene: tests/release/registry-manifest.test.sh
PASS  hygiene: tests/release/release-crates.test.sh
PASS  hygiene: tests/release/release-create-rerun.test.sh
PASS  hygiene: tests/release/release-publish-order.test.sh
FAIL  hygiene: tests/release/release-rehearsal.test.sh
PASS  hygiene: tests/release/release-version-bump.test.sh
PASS  hygiene: tests/release/release-version-check.test.sh
PASS  hygiene: tests/release/release-version-pins.test.sh
PASS  hygiene: tests/release/rmswap.test.sh
PASS  hygiene: tests/release/scheduled-job-edit.test.sh
PASS  hygiene: tests/release/shellcheck-coverage.test.sh
PASS  hygiene: tests/release/smithery-manifest.test.sh
PASS  hygiene: tests/release/sudoers-edit.test.sh
PASS  hygiene: tests/release/systemd-directory-modes.test.sh
PASS  hygiene: tests/release/test-baseline-provenance.test.sh
PASS  hygiene: tests/release/test-reachability.test.sh
PASS  hygiene: tests/release/tracked-eol.test.sh
PASS  hygiene: tests/release/version-sites.test.sh
PASS  hygiene: tests/e2e/provider-parity.test.sh
PASS  hygiene: tests/e2e/story-metadata.test.sh
PASS  hygiene: tests/e2e/story-runner-verdicts.test.sh
PASS  hygiene: tests/e2e/ubuntu-vm-bootstrap.test.sh
PASS  hygiene: tests/e2e/vm-env-secrets.test.sh
PASS  hygiene: tests/e2e/vm-sync-parity.test.sh
WARN  hygiene: markdownlint-cli2 -- SKIPPED (not found; install: npm install --global markdownlint-cli2)
WARN  hygiene: markdown-link-check -- SKIPPED (not found; install: npm install --global markdown-link-check)
WARN  hygiene: yamllint -- SKIPPED (not found; install: pip install yamllint)
WARN  hygiene: shellcheck -- SKIPPED (not found; install: sudo apt-get install shellcheck)
WARN  security: cargo audit -- SKIPPED (cargo-audit not found; install: cargo install cargo-audit --locked)
SKIP  postgres-contract: live Postgres contract (store + CLI anchor exit code) (--no-postgres)
=========================================
ci-local: FAIL (9 failing check(s))
WARNING: REQUIRED CI check(s) did not run: missing-tool:markdownlint-cli2 missing-tool:markdown-link-check missing-tool:yamllint missing-tool:shellcheck missing-tool:cargo-audit postgres-contract
Install markdownlint-cli2, or rerun with --allow-missing-tools only when you intentionally accept that CI gap.
Install markdown-link-check, or rerun with --allow-missing-tools only when you intentionally accept that CI gap.
Install yamllint, or rerun with --allow-missing-tools only when you intentionally accept that CI gap.
Install shellcheck, or rerun with --allow-missing-tools only when you intentionally accept that CI gap.
Install cargo-audit, or rerun with --allow-missing-tools only when you intentionally accept that CI gap.
To run postgres-contract, set SYSKNIFE_TEST_POSTGRES_URL or install podman, then rerun without --no-postgres.
ci-local: INCOMPLETE (required checks skipped)

@vladimirrott vladimirrott 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.

Thank you, @QinXi-ai. The test that reads every action name out of the rendered prompt and checks it against that host's propose_plan enum is the right shape for #672, and it covers the next drift too: if anyone adds a sentence naming an action the schema withholds on some family, it goes red. Reviewed at 3fb7dc5.

Nothing here blocks on your side.

What I checked, with your code run in a container (rootless podman, no network):

  • The other families are untouched. I rendered the prompt on main and on your head for no hint, Fedora 42, Ubuntu 24.04, Debian 13 and Arch, each with and without a saved preference. Eight of the ten are byte-identical. Only the two Arch renders differ (29,587 → 27,178 bytes). So every existing E2E story sees the same prompt it passed with, and the live-story check CLAUDE.md asks for on prompt changes has nothing to re-measure.
  • The Arch diff reads right. GetSystemState is gone from the risk table, the no-params list, the history finish and the preference finish. The old text also told an Arch host "Distro family is unknown" and described GetSystemState as returning rpm-ostree deployment data; both are gone. refuse is a real planning tool (planning_tools/refuse.rs), so the overview example points at something the model can call.
  • Your tests bite. I ran your three tests against four mutations, restoring the file between each:
M1 delete `Some(DISTRO_FAMILY_OTHER) => render_other_prompt(...)` (the fix reverted)
   every_prompt_action_is_offered_by_its_plan_schema ... FAILED
     Some(DistroHint { id: "arch", family: "other", version: None }): unoffered actions ["GetSystemState"]
   other_prompt_preserves_examples_and_has_an_available_preference_finish ... FAILED
M2 other-host {FINISH_ACTION} -> "GetSystemState"      2 failed
M3 other-host {STATE_ACTION_ENTRY} -> "GetSystemState, "  every_prompt_action_is_offered_by_its_plan_schema FAILED
M4 drop the other-host {SERVICE_DISAMBIGUATION} substitution
   no_rendered_prompt_leaks_an_unsubstituted_placeholder ... FAILED
     the other prompt shipped {SERVICE_DISAMBIGUATION}

The unmutated run and a run after the last restore both printed running 3 tests and 3 passed.

The red rust check is the test baseline and nothing else: test_baseline: rust suite has 2000 tests, baseline says 1998. All 2,000 tests passed in that job. CONTRIBUTING.md puts that figure on whoever merges, so it's mine, along with bringing the branch up to date. main is two commits ahead and neither touches sysknife-brain.

One optional thought. The Arch copy of Example F is a second text of that example, and an edit to the shared one won't reach it. It also drops the line "call get_system_state (planning tool) first ← FORBIDDEN" that the shared copy carries. I'm happy to take it as it is; if you'd rather keep that line, it's a one-line change.

I'll take it from here. Every open issue in your area has a pending offer right now; when one frees up I'll point you at it.

Vladimir Rotariu and others added 2 commits October 27, 2026 00:32
Main reached 2002 with lacs-project#626; this branch adds two tests
(every_prompt_action_is_offered_by_its_plan_schema and
other_prompt_preserves_examples_and_has_an_available_preference_finish),
so the artifact and the three published figures move together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU

@vladimirrott vladimirrott 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.

Thanks, this is a careful fix. Routing other hosts to their own renderer is the right call, and so is telling the model to refuse an OS/hardware overview instead of inventing a snapshot. The test is better than the fix needs: every_prompt_action_is_offered_by_its_plan_schema checks every action name the prompt mentions against the propose_plan enum for seven hints. Any future prompt edit that names an action the schema won't accept now fails, on any family.

I proved it at 3fb7dc51 in a no-network container. The test passes as written. With the Some(DISTRO_FAMILY_OTHER) => render_other_prompt(...) arm deleted, so Arch falls back to the generic prompt, the same test FAILED. I also checked that refuse and ListJobHistory are both real tools here, and that {STATE_ACTION} can't be a substring of {STATE_ACTION_ENTRY}, so the order of the replace chain is safe.

The only red check was rust, and only on test_baseline (2000 against 1998). #626 merged in the meantime and moved main to 2002. Since you allowed maintainer edits, I pushed two commits to your branch: a merge of current main, and 4f6616c, which moves the artifact and the three published figures to 2004. Nothing else changed, and the evidence check passes on it. I'll merge once CI is green on that head, then write the CHANGELOG line at release.

@vladimirrott
vladimirrott merged commit 5c6a0b0 into lacs-project:main Oct 27, 2026
13 checks passed
vladimirrott added a commit to Georgefifth/sysknife that referenced this pull request Oct 27, 2026
Main moved to 2,004 Rust tests (lacs-project#626, lacs-project#684); this branch adds seven, so
the artifact and the three published figures read 2,011. No other
conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU
vladimirrott added a commit that referenced this pull request Oct 27, 2026
Minor release. #627 keys request_hash with an HMAC under the audit key
(chain_version 4; previews stored by an older release fail closed after
upgrading) and #639 changes the history request a newer CLI sends, so a
caller relying on the old behaviour notices: the middle digit moves.

Also: the planner sees only its host's distribution (#626, #656, #658,
#684), checkpoint and the MCP audit tools share the audit-store resolver
(#636), every workflow job has a time limit (#662), a weekly cargo audit
(#640), release hardening (#635, #637, #660), and every published figure
is rendered from its source (#685).


Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On an unrecognised distro the prompt tells the model to use GetSystemState, which propose_plan does not offer there

2 participants