feat(cli): add CAK skill package authoring#14
Conversation
Why: - CAK needs a simple user path for creating custom skills without requiring Rust or WASM for ordinary package authoring. - The install/update architecture decision also needed durable repo artifacts instead of remaining only in discussion. What changed: - Added project-local lab scaffolding and research packets for install/update UX, custom skill authoring, and WASM runtime boundaries. - Added decision-log and docs entries that keep one public `cak` CLI, use `SKILL.md` plus `cak.yaml` for v0 packages, and reserve WASM for a future sandboxed evaluator lane. - Added `cak skill init/check/install` in the Rust CLI with package validation, fixture parsing, install metadata, and source/target overlap protection. - Added CLI integration tests for skill package creation, validation, installation, invalid frontmatter, and unsafe install targets. Testing: - cargo test -p cak-runtime-cli --test cli skill_ - cargo test --workspace - python3 scripts/check_docs.py - python3 /Users/vi/personal/heurema/rd-lab-os/scripts/validate_run.py /Users/vi/personal/heurema/cak/.heurema/rdlab/runs/2026-06-24-cak-custom-skill-authoring-contract --strict - python3 /Users/vi/personal/heurema/rd-lab-os/scripts/validate_run.py /Users/vi/personal/heurema/cak/.heurema/rdlab/runs/2026-06-24-cak-wasm-runtime-architecture --strict - git diff --cached --check - prek run --all-files Not tested: - Real installation into a live user Codex skill directory. - Public package-manager distribution or update/rollback flow. Risk: - moderate - `cak skill check` is intentionally shape validation, not semantic safety proof. Constraint: Keep `cak-runtime-core` pure as `EvalRequest -> Decision`; host package management stays in the CLI. Directive: Do not add Wasmtime or Extism to the core runtime without a separate `cak-wasm-runner` spike and measured evidence.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 76592930c6
ℹ️ 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".
| std::fs::create_dir_all(&install_dir) | ||
| .with_context(|| format!("creating install directory {}", install_dir.display()))?; | ||
| copy_dir_contents(path, &install_dir)?; |
There was a problem hiding this comment.
Refuse to overwrite existing skill directories
When --target already contains a directory with the package name, especially an unrelated user skill, this succeeds and copy_dir_contents overwrites regular files in place. Because there is no --force flag or ownership check against .cak-install.json, a normal install can clobber an existing host skill; consider refusing existing/non-empty install directories unless they are known CAK-managed updates.
Useful? React with 👍 / 👎.
| "kind": "package", | ||
| "version": "0.1.0", | ||
| "lifecycle": { | ||
| "maturity": "draft", |
There was a problem hiding this comment.
Make the generated block fixture actually block
For fixtures/block.request.json, the template emits a claim_authority action but leaves this lifecycle maturity as draft. LifecycleGateEvaluator only hard-blocks claim_authority for quarantined/deprecated nodes, so the generated “block” fixture exercises an allow path rather than a block path; users who evaluate the skeleton fixtures as admission smoke tests will not cover the intended blocking case.
Useful? React with 👍 / 👎.
| let id = scalar_value(raw, "id").context("missing id")?; | ||
| let version = scalar_value(raw, "version").context("missing version")?; | ||
| let kind = scalar_value(raw, "kind").context("missing kind")?; |
There was a problem hiding this comment.
Require descriptor kind at the top level
If a user deletes the top-level kind: from the generated cak.yaml, cak skill check still succeeds because scalar_value(raw, "kind") scans every trimmed line and picks up the nested runtime.kind: package. That lets packages with missing required top-level identity fields pass validation and be installed with misleading metadata; the descriptor fields should be read from the top-level mapping.
Useful? React with 👍 / 👎.
| let fixtures_dir = path.join("fixtures"); | ||
| if !fixtures_dir.exists() { | ||
| return Ok(()); |
There was a problem hiding this comment.
Verify required fixture files exist
A generated package can have its fixtures/ directory or the listed required_fixtures files removed and cak skill check will still return success, because the validator treats a missing fixtures directory as OK and never checks the descriptor's required list. This means a package that no longer contains the admission examples advertised in cak.yaml can be installed as checked.
Useful? React with 👍 / 👎.
| "skill_id": package.id, | ||
| "version": package.version, | ||
| "kind": package.kind, | ||
| "source_path": path.to_string_lossy(), |
There was a problem hiding this comment.
Record the canonical source path
When users run the documented flow with a relative package path such as cak skill install ./demo-review ..., the install metadata stores that relative string without recording the original cwd. Any later doctor/update/rollback code reading .cak-install.json from the host skill directory cannot reliably locate the source package; use the canonical source_dir computed above instead.
Useful? React with 👍 / 👎.
Why: - Skill package install must not clobber existing host skill directories. - Skill package validation should enforce the descriptor contract that the CLI generates. What changed: - Refuse installs when the target skill directory already exists. - Store canonical source paths in install metadata. - Require top-level descriptor identity fields and required fixture files. - Generate a block fixture that exercises the lifecycle hard-block path. - Add CLI regression tests and update the authoring docs. Testing: - cargo test -p cak-runtime-cli --test cli skill_ - cargo test --workspace - python3 scripts/check_docs.py - git diff --check - prek run --all-files Risk: - narrow - repeated installs now fail until an explicit update flow exists.
Summary
cak skill init/check/installfor v0 CAK-compatible skill package authoring.SKILL.mdhost package pluscak.yamlCAK descriptor.cakCLI and reserve WASM for a future sandboxed evaluator lane.Issue
Test plan
cargo test -p cak-runtime-cli --test cli skill_cargo test --workspacepython3 scripts/check_docs.pypython3 /Users/vi/personal/heurema/rd-lab-os/scripts/validate_run.py /Users/vi/personal/heurema/cak/.heurema/rdlab/runs/2026-06-24-cak-custom-skill-authoring-contract --strictpython3 /Users/vi/personal/heurema/rd-lab-os/scripts/validate_run.py /Users/vi/personal/heurema/cak/.heurema/rdlab/runs/2026-06-24-cak-wasm-runtime-architecture --strict