Skip to content

fix(runtime): separate Runtime CLI authority from init - #266

Merged
yohnark merged 1 commit into
mainfrom
fix/258-resolve-258
Aug 15, 2026
Merged

yohnark merged 1 commit into
mainfrom
fix/258-resolve-258

Conversation

@yohnark

@yohnark yohnark commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Separate local Runtime lifecycle from workspace/MCP initialization.

Linked issue

Fixes #258

Scope

Issue #258 only; sibling #232 child Issues remain excluded.

Included

Remove Runtime provisioning from top-level init; add runtime ensure/status commands; add bounded state projection, tests, docs, and package smoke coverage.

Excluded

QEMU artifact production, Nix image validation, real KVM/HVF/WHPX integration, and remote placement (#233).

Implementation

Reuse the PR #257 LocalRuntimeProvisioner for runtime ensure. Add a read-only status projection over the persisted state schema, with no state-directory creation or secret path exposure. Keep init focused on MCP configuration and reject the removed --runtime option.

Behavioral changes

mottainai init never provisions Runtime. mottainai runtime ensure reconciles the local Runtime; runtime status reports absent or persisted lifecycle state and is non-mutating. Both commands preserve JSON and human output paths with deterministic error exits.

Validation

  • Typecheck
  • Tests
  • Build
  • Package check

Test layers:

  • Fast / unit / contract: pnpm test
  • Integration / process: pnpm run test:integration
  • Package / E2E / smoke: pnpm run test:e2e, pnpm run test:package
  • Standards / static: pnpm run verify:standards
  • Coverage: pnpm run test:coverage
  • Test effectiveness (when critical logic changes): pnpm run test:effectiveness
  • Full verification: pnpm run verify

Test contract

change type: CLI authority separation and read-only persisted-state projection; required layers: unit/contract, process/integration, package smoke, lint/architecture; not-applicable: real VM integration is explicitly excluded by Issue #258 and package suite's unrelated Nawabari-managed-task check is environment-blocked.

Regression proof

status: reviewer-attested plus automated; reason: PR #257 coupled Runtime ensure to init; test path: src/cli.test.ts and src/local-runtime/status.test.ts; test identifier: top-level init rejects --runtime, runtime status absent/persisted projections; pre-fix observed failure: init owned --runtime and no runtime namespace; post-fix result: init is MCP-only and runtime commands are callable; command id: pnpm test; base result: not run as a base comparison; head result: 456 tests passed; isolation: dedicated managed worktree and isolated package smoke state root; reviewer attestation: required for base comparison.

Validation evidence

class: unit/contract; status: pass; command: pnpm test; target: src/cli.test.ts, src/init.test.ts, src/local-runtime/status.test.ts; result: 456 tests passed; artifact: TAP exit 0
class: process/integration; status: pass; command: node --import tsx --test src/local-runtime/status.test.ts src/cli.test.ts src/init.test.ts; target: CLI dispatch and state projection; result: 23 tests passed; artifact: TAP exit 0
class: package smoke; status: pass; command: pnpm run smoke-test; target: packed npm launcher; result: runtime ensure --help and isolated runtime status pass; artifact: smoke-test exit 0
class: fault injection; status: not-applicable; command: none; target: Runtime lifecycle fault injection; result: excluded by Issue #258; scenario: no VM lifecycle mutation was claimed; artifact: Issue scope
class: lint/architecture; status: pass; command: pnpm run lint and pnpm exec prettier --check ...; target: changed TypeScript/JS/Markdown; result: pass; artifact: exit 0
class: release; status: not-applicable; command: none; target: release/publish; result: no release metadata changed; artifact: package version unchanged
class: security/negative; status: pass; command: node --import tsx --test src/local-runtime/status.test.ts; target: read-only status projection; result: no state mutation and no hostKey/endpoint exposure; scenario: persisted state status read; artifact: TAP exit 0

Release impact

impact: none; reason: package command surface and dist output are covered by build and package smoke; package version and publish metadata are unchanged.

Risks

Runtime ensure remains host-accelerator/artifact dependent by design; status is intentionally read-only and can report absent without provisioning. The existing package suite has an unrelated Nawabari ownership ambiguity in this shared environment.

Breaking changes

No. The removed init --runtime option now fails with migration guidance; use runtime ensure.

Migration / compatibility

Existing users invoking init --runtime must use mottainai runtime ensure. Existing init/MCP configuration behavior remains unchanged. Persisted Runtime state schema is reused unchanged.

Security impact

Status reads the validated persisted schema without creating or changing state and omits private SSH host keys, private key paths, and QMP endpoints from its public projection.

Review focus

Verify init has no Runtime side effect, runtime ensure calls the PR #257 reconciler, runtime status is non-mutating and bounded, and package smoke covers the shipped CLI routes.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dda4aa25-8bd0-4f97-a630-4ca21444f13c

📥 Commits

Reviewing files that changed from the base of the PR and between 710e742 and ffb02ca.

📒 Files selected for processing (11)
  • README.md
  • docs/local-runtime.md
  • scripts/smoke-test.mjs
  • src/cli.test.ts
  • src/cli.ts
  • src/init.test.ts
  • src/init.ts
  • src/local-runtime/index.ts
  • src/local-runtime/status.test.ts
  • src/local-runtime/status.ts
  • src/local-runtime/types.ts
💤 Files with no reviewable changes (1)
  • src/init.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: coverage shard 5/7 (Node 22)
  • GitHub Check: integration / process (Node 22)
  • GitHub Check: coverage shard 7/7 (Node 22)
  • GitHub Check: coverage shard 3/7 (Node 22)
  • GitHub Check: coverage shard 2/7 (Node 22)
  • GitHub Check: coverage shard 4/7 (Node 22)
  • GitHub Check: build / built dist e2e / package (Node 22)
  • GitHub Check: Analyze (javascript-typescript)
⚠️ CI failures not shown inline (5)

GitHub Actions: Governance / regression-proof: fix(runtime): separate Runtime CLI authority from init

Conclusion: failure

View job details

##[group]Run node scripts/run-regression-proof.mjs \
 �[36;1mnode scripts/run-regression-proof.mjs \�[0m
 �[36;1m  --plan-file "$RUNNER_TEMP/plan/regression-proof-plan.json" \�[0m
 �[36;1m  --base-root "$BASE_ROOT" \�[0m
 �[36;1m  --head-root "$HEAD_ROOT" \�[0m
 �[36;1m  --report "$RUNNER_TEMP/regression-proof-report.md"�[0m
 shell: /usr/bin/bash -e {0}
 env:
   BASE_ROOT: /home/runner/work/mottainai/mottainai/governance
   HEAD_ROOT: /home/runner/work/mottainai/mottainai/pr
 ##[endgroup]
 node:fs:440
     return binding.readFileUtf8(path, stringToFlags(options.flag));
                    ^
 Error: ENOENT: no such file or directory, open '/home/runner/work/_temp/plan/regression-proof-plan.json'
     at readFileSync (node:fs:440:20)
     at readJson (file:///home/runner/work/mottainai/mottainai/governance/scripts/governance-lib.mjs:917:21)
     at file:///home/runner/work/mottainai/mottainai/governance/scripts/run-regression-proof.mjs:8:14
     at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
     at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
     at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
   errno: -2,
   code: 'ENOENT',
   syscall: 'open',
   path: '/home/runner/work/_temp/plan/regression-proof-plan.json'
 }
 Node.js v22.23.2
 ##[error]Process completed with exit code 1.

GitHub Actions: Governance / 0_regression-proof.txt: fix(runtime): separate Runtime CLI authority from init

Conclusion: failure

View job details

##[group]Run actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
 with:
   name: regression-proof-plan
   path: /home/runner/work/_temp/plan
   merge-multiple: false
   repository: yohn-jp/mottainai
   run-id: 31876234144
   skip-decompress: false
   digest-mismatch: error
 ##[endgroup]
 Downloading single artifact
 ##[error]Unable to download artifact(s): Artifact not found for name: regression-proof-plan

GitHub Actions: Governance / validate-pr: fix(runtime): separate Runtime CLI authority from init

Conclusion: failure

View job details

##[group]Run node scripts/validate-pr.mjs --event "$GITHUB_EVENT_PATH" --files "$RUNNER_TEMP/changed-files.txt" --issue-number-file "$RUNNER_TEMP/issue-number.txt" --report "$RUNNER_TEMP/governance-report.md" --regression-plan-file "$RUNNER_TEMP/regression-proof-plan.json"
 �[36;1mnode scripts/validate-pr.mjs --event "$GITHUB_EVENT_PATH" --files "$RUNNER_TEMP/changed-files.txt" --issue-number-file "$RUNNER_TEMP/issue-number.txt" --report "$RUNNER_TEMP/governance-report.md" --regression-plan-file "$RUNNER_TEMP/regression-proof-plan.json"�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 ## Governance validation failed
 - PR title format or scope is invalid
 ## Report-only observations
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/local-runtime/status.test.ts, src/local-runtime/status.ts, src/local-runtime/types.ts; matched path class/rule=cli-mcp-entry, package-publish, persistence-process-boundary, compatibility-contract, governance-quality; missing evidence=Test contract.change type; how to satisfy=add a concrete "change type: ..." field under the Test contract section
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/local-runtime/status.test.ts, src/local-runtime/status.ts, src/local-runtime/types.ts; matched path class/rule=cli-mcp-entry, package-publish, persistence-process-boundary, compatibility-contract, governance-quality; missing evidence=Test contract.required layers; how to satisfy=add a concrete "required layers: ..." field under the Test contract section
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/lo...

GitHub Actions: Governance / 1_validate-pr.txt: fix(runtime): separate Runtime CLI authority from init

Conclusion: failure

View job details

##[group]Run node scripts/validate-pr.mjs --event "$GITHUB_EVENT_PATH" --files "$RUNNER_TEMP/changed-files.txt" --issue-number-file "$RUNNER_TEMP/issue-number.txt" --report "$RUNNER_TEMP/governance-report.md" --regression-plan-file "$RUNNER_TEMP/regression-proof-plan.json"
 �[36;1mnode scripts/validate-pr.mjs --event "$GITHUB_EVENT_PATH" --files "$RUNNER_TEMP/changed-files.txt" --issue-number-file "$RUNNER_TEMP/issue-number.txt" --report "$RUNNER_TEMP/governance-report.md" --regression-plan-file "$RUNNER_TEMP/regression-proof-plan.json"�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 ## Governance validation failed
 - PR title format or scope is invalid
 ## Report-only observations
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/local-runtime/status.test.ts, src/local-runtime/status.ts, src/local-runtime/types.ts; matched path class/rule=cli-mcp-entry, package-publish, persistence-process-boundary, compatibility-contract, governance-quality; missing evidence=Test contract.change type; how to satisfy=add a concrete "change type: ..." field under the Test contract section
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/local-runtime/status.test.ts, src/local-runtime/status.ts, src/local-runtime/types.ts; matched path class/rule=cli-mcp-entry, package-publish, persistence-process-boundary, compatibility-contract, governance-quality; missing evidence=Test contract.required layers; how to satisfy=add a concrete "required layers: ..." field under the Test contract section
 - quality.section-field.missing: changed path(s)=README.md, docs/local-runtime.md, scripts/smoke-test.mjs, src/cli.test.ts, src/cli.ts, src/init.test.ts, src/init.ts, src/local-runtime/index.ts, src/lo...

GitHub Actions: Governance / regression-proof: fix(runtime): separate Runtime CLI authority from init

Conclusion: failure

View job details

##[group]Run actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
 with:
   name: regression-proof-plan
   path: /home/runner/work/_temp/plan
   merge-multiple: false
   repository: yohn-jp/mottainai
   run-id: 31876234144
   skip-decompress: false
   digest-mismatch: error
 ##[endgroup]
 Downloading single artifact
 ##[error]Unable to download artifact(s): Artifact not found for name: regression-proof-plan
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,js,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

ソースコード本体は原始人モードの対象外。日本語コメントを大量追加する場合のみ口調を確認する

Files:

  • src/local-runtime/index.ts
  • src/local-runtime/status.test.ts
  • src/local-runtime/status.ts
  • src/cli.test.ts
  • src/local-runtime/types.ts
  • src/cli.ts
  • src/init.ts
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Keep implementation within the user request and accepted Issue scope; do not add adjacent features, cleanup, refactors, documentation, or follow-up work unless required.

Files:

  • src/local-runtime/index.ts
  • README.md
  • scripts/smoke-test.mjs
  • src/local-runtime/status.test.ts
  • src/local-runtime/status.ts
  • docs/local-runtime.md
  • src/cli.test.ts
  • src/local-runtime/types.ts
  • src/cli.ts
  • src/init.ts
**/*.{md,txt,rst,adoc,yaml,yml,toml,json,xml,html,env,ini,cfg,conf,csv}

📄 CodeRabbit inference engine (CLAUDE.md)

テキスト形式ファイル生成時は原始人モードを自動適用しない。初回のみgenshijin口調で書くか確認し、否定または無回答なら通常日本語、確認済みなら以降確認を省略する

Files:

  • README.md
  • docs/local-runtime.md
🪛 ast-grep (0.45.1)
src/local-runtime/status.test.ts

[warning] 79-79: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(paths.stateFile, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 81-81: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(paths.stateFile, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

src/cli.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

src/init.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (10)
src/local-runtime/types.ts (1)

167-184: LGTM!

src/local-runtime/status.ts (1)

1-83: LGTM!

src/local-runtime/index.ts (1)

8-8: LGTM!

src/local-runtime/status.test.ts (1)

1-97: LGTM!

src/init.ts (1)

123-126: LGTM!

src/cli.ts (1)

13-18: LGTM!

Also applies to: 64-65, 133-136, 150-154, 502-525, 914-919

src/cli.test.ts (1)

34-97: LGTM!

Also applies to: 113-118, 150-166

README.md (1)

117-120: LGTM!

docs/local-runtime.md (1)

3-13: LGTM!

scripts/smoke-test.mjs (1)

135-136: LGTM!

Also applies to: 169-205


📝 Walkthrough

Walkthrough

The Runtime lifecycle now uses mottainai runtime ensure and mottainai runtime status. mottainai init no longer provisions Runtime state. Persisted status remains read-only, and CLI, documentation, unit tests, and smoke tests cover the new authority.

Changes

Runtime CLI authority

Layer / File(s) Summary
Persisted Runtime status projection
src/local-runtime/types.ts, src/local-runtime/status.ts, src/local-runtime/status.test.ts, src/local-runtime/index.ts
Adds a bounded status type, persisted-state reader, human-readable formatters, public exports, and non-mutating status tests.
Runtime command wiring and init decoupling
src/cli.ts, src/cli.test.ts, src/init.ts, src/init.test.ts
Adds runtime ensure and runtime status. Rejects init --runtime and removes Runtime provisioning and reporting from initialization.
CLI documentation and packaged validation
README.md, docs/local-runtime.md, scripts/smoke-test.mjs
Updates lifecycle documentation and verifies packed-package Runtime help, JSON status, absent state, and state-directory behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to ffb02

This change separates Runtime lifecycle commands from initialization and adds bounded, read-only status reporting; no actionable merge-blocking risk remains after normal checks and review.

Possibly related issues

Possibly related PRs

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states that Runtime CLI authority is separated from init, which matches the primary change.
Description check ✅ Passed The description directly covers the Runtime CLI separation, included changes, exclusions, behavior, and validation.
Linked Issues check ✅ Passed The changes satisfy Issue #258 by removing init Runtime coupling, adding runtime ensure/status, preserving output contracts, and updating docs and smoke tests.
Out of Scope Changes check ✅ Passed The changes remain within Issue #258 scope and contain no unrelated Runtime artifact, hardware integration, Nix, or remote-placement work.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/258-resolve-258

Comment @coderabbitai help to get the list of available commands.

@yohnark yohnark left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge blockers

The implementation direction matches #258, but this PR is not merge-ready yet.

  1. Governance rejects the PR contract because runtime is not an allowed conventional scope in scripts/governance-rules.json. Runtime is now a first-class subsystem, so prefer fixing the governance vocabulary once (add runtime as a supported scope) rather than disguising these PRs under an unrelated scope.
  2. Linked Issue #258 is currently governance-invalid / needs specification and must be repaired to the current Issue contract.
  3. This changes the shipped CLI/package surface, while Node compatibility smoke and built-dist/package CI are failing. #258 explicitly requires packed CLI/package smoke evidence, so this is an acceptance blocker.
  4. The branch predates current main (#267); rebase after the governance/spec repair and rerun required evidence.

I did not find an additional code-level blocker in the runtime namespace/status implementation itself. Re-review after these gates are green.

@yohnark yohnark left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up after baseline comparison: the broad built dist e2e/package and Node 24 smoke failures are not unique to #266; merged PR #267 showed the same CI failures on the adjacent baseline. Do not absorb an unrelated repository-wide CI repair into #266. Instead, restore main CI in a separate prerequisite action and keep #266 responsible for targeted evidence that the new runtime ensure/status commands are actually present and callable in the packed artifact.

This was referenced Aug 15, 2026
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.

fix(runtime): separate Runtime CLI authority from mottainai init

1 participant