Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Build, test and lint every push and pull request.
#
# This repo shipped eight PRs with no CI at all — only a DCO check — and the
# ninth had to fix a `main` that did not compile. Two PRs edited the same test
# module from branches that did not know about each other; git merged them
# cleanly because the blocks were textually distinct, and the result referenced
# an enum variant the other PR had deleted. Nothing noticed until somebody ran
# `cargo test` locally.
#
# That is precisely the failure a semantic check catches and a textual merge
# cannot, so it runs on `merge_group` too: a PR that was green on its own branch
# can still break `main` when combined with another.
name: CI

on:
push:
branches: [main]
pull_request:
merge_group:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
# Warnings are failures here. This crate's whole argument is that a memory
# service has to be trustworthy about what it does and does not know; a
# tolerated warning is a small lie about that.
RUSTFLAGS: -D warnings

jobs:
test:
name: test
# macOS on purpose. Two shipped bugs were platform-specific paths —
# `pnm` stores its config under `dirs::config_dir()`, which is
# `~/Library/Application Support` here and `~/.config` on Linux — and a
# Linux-only matrix would have been green through both.
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

# The `[patch.crates-io]` git dependency on the VTI workspace is a large
# fetch and a long build; caching it is the difference between a usable
# CI and one people learn to ignore.
- uses: Swatinem/rust-cache@v2

- name: Format
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --all-features

- name: Test
run: cargo test --all-features

plugin:
name: plugin manifest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# The manifests are what a person meets before any Rust runs. A malformed
# `plugin.json` or `.mcp.json` breaks the plugin without breaking the
# build, so the build alone would never report it.
- name: Validate JSON manifests
run: |
for f in .claude-plugin/plugin.json .claude-plugin/marketplace.json \
.mcp.json hooks/hooks.json; do
echo "checking $f"
python3 -c "import json,sys; json.load(open('$f'))"
done

# Every command and skill needs frontmatter to be loaded at all; a missing
# `description` makes a command invisible rather than broken.
- name: Check command and skill frontmatter
run: |
fail=0
for f in commands/*.md skills/*/SKILL.md; do
if ! head -1 "$f" | grep -q '^---$'; then
echo "::error file=$f::missing YAML frontmatter"; fail=1; continue
fi
if ! sed -n '2,/^---$/p' "$f" | grep -q '^description:'; then
echo "::error file=$f::frontmatter has no description"; fail=1
fi
done
exit $fail
42 changes: 0 additions & 42 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,48 +250,6 @@ mod tests {
);
}

/// Session mode must always carry an explicit `sessions_dir`.
///
/// `vta_sdk`'s default was `~/.config/pnm` until VTI #1087 — wrong on macOS
/// and Windows. This crate resolves the directory itself and records it, so
/// it never depended on that being right. Pinned, because a path left to a
/// default would find no session on two platforms and report it as an
/// authentication failure.
#[test]
fn the_sessions_dir_is_never_left_to_a_default() {
let connect = agent_config().to_agent_connect();
assert_eq!(
connect.sessions_dir.expect("recorded, not derived"),
PathBuf::from("/tmp/vam-sessions")
);
}

/// Session mode must always carry an explicit `sessions_dir`.
///
/// `vta_sdk`'s default was `~/.config/pnm` until VTI #1087 — wrong on macOS
/// and Windows, where `pnm` writes to `dirs::config_dir()`. This crate was
/// never exposed to that because it resolves the directory itself, and this
/// pins the invariant: a future session-mode path that leaves the field
/// `None` would silently find no session on two platforms and report it as
/// an authentication failure.
#[test]
fn session_mode_never_leaves_the_sessions_dir_to_a_default() {
let cfg = Config {
identity: Identity::PnmSession {
session_key: "vta:my-vta".into(),
vta_did: "did:key:zV".into(),
service_name: None,
},
..agent_config()
};
let connect = cfg.to_agent_connect();
let dir = connect
.sessions_dir
.expect("session mode must resolve the directory itself");
assert_eq!(dir.file_name().expect("pnm"), "pnm");
assert_eq!(dir.parent().expect("parent"), dirs::config_dir().unwrap());
}

#[test]
fn an_operator_login_is_labelled_as_one() {
// It is the higher-privilege choice, so diagnostics must not present it
Expand Down
Loading