Skip to content

ci: real CI pipeline + MCP stdio hardening#10

Merged
Ghost-Frame merged 7 commits into
feat/intelligent-selectionfrom
chore/ci-foundation
Jun 30, 2026
Merged

ci: real CI pipeline + MCP stdio hardening#10
Ghost-Frame merged 7 commits into
feat/intelligent-selectionfrom
chore/ci-foundation

Conversation

@Ghost-Frame

Copy link
Copy Markdown
Owner

Summary

Foundation work for the improvement program: a real CI pipeline (the repo had none) plus a small MCP stdio hardening pass. Three commits.

CI (.github/workflows/ci.yml)

Before this, the only workflow mirrored the repo; nothing ran the 600+ tests, clippy, or fmt on a PR. New jobs:

  • fmt -- cargo fmt --all -- --check
  • clippy -- cargo clippy --workspace --all-targets -- -D warnings
  • test -- cargo test --workspace (the Docker-gated postgres tests stay #[ignore])
  • test-postgres -- cargo test -p frameshift-catalog-postgres -- --include-ignored; testcontainers self-starts postgres against the runner's Docker
  • audit -- RustSec advisory scan (non-blocking initially)

Jobs run on stable to match the local toolchain; libpq-dev is installed on the compiling jobs for diesel/pq-sys. The workspace was already fmt- and clippy-clean, so the preceding cargo fmt --all commit is formatting-only and the pipeline is green on the first run.

MCP stdio hardening

  • Cap the stdin line length at 8 MiB: an over-long line is rejected with a -32700 and the next line still processes, bounding memory against a client that never sends a newline.
  • Replace the startup .expect() with a stderr diagnostic and nonzero exit instead of a panic.
  • Validate the persona name at the MCP boundary in call_activate/call_use (mirrors call_grow_append); defense-in-depth over the existing client-layer checks.

Verification (local)

  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo test --workspace: 607 passed, 0 failed, 25 ignored
  • cargo test -p frameshift-mcp: 39/39, including new cap-recovery and boundary-rejection regression tests

First pass of CI foundation work. No behavior change: rustfmt reflows only
(import ordering, line wrapping, multi-line assert/expression formatting).
Makes the new fmt --check CI job green.
The only prior workflow mirrored the repo; nothing ran the 600+ tests or
clippy on a PR. Adds .github/workflows/ci.yml with five jobs: rustfmt --check,
clippy --workspace --all-targets -D warnings, cargo test --workspace (the
Docker-gated postgres tests stay #[ignore]), a scoped postgres-integration job
that lets testcontainers self-start its database, and a non-blocking RustSec
advisory scan. Jobs run on stable to match the local toolchain; libpq-dev is
installed on the compiling jobs for diesel/pq-sys. Workspace is already fmt- and
clippy-clean, so the pipeline is green on first run.
…rsona checks

- Replace the unbounded lines() reader with read_capped_line: a single
  JSON-RPC line is capped at 8 MiB and an over-long line is rejected with a
  -32700 (the next line still processes), so a client that never sends a
  newline cannot drive unbounded memory growth.
- Replace the startup Client init .expect() with a clean stderr diagnostic
  and nonzero exit instead of a panic.
- Validate the persona name at the MCP boundary in call_activate and call_use,
  mirroring call_grow_append. Defense-in-depth: the client layer already
  validates in activate()/rendered_persona(), this rejects earlier with a
  clear error.

Regression tests cover cap-then-recover and boundary rejection for both
activate and use. clippy -D warnings clean; cargo test -p frameshift-mcp green.
The local Arch rustfmt (1.9.0) and CI's upstream rustfmt (rust 1.96.0) format
a wrapped 3-arg error_response() call differently -- Arch keeps it on two
lines, upstream explodes the args. Binding the message to a temp var keeps
both statements short enough that neither rustfmt reformats them, so the fmt
job is reproducible across both.
These hold raw multi-model audit findings with local filesystem paths,
unpublished vulnerability locations, and internal tool identifiers that
do not belong in a public repo.
Clears all 8 cargo-audit vulnerabilities that were gating the Security
audit job, and ratchets that job from continue-on-error to blocking.

- diesel 2.2 -> 2.3.10, diesel-async 0.5 -> 0.9.2, diesel_migrations
  2.2 -> 2.3, bb8 0.8 -> 0.9 (diesel-async 0.9 implements
  bb8::ManageConnection only for bb8 0.9). Clears RUSTSEC-2026-0136 and
  -0137 plus the diesel/diesel-async unsound advisories.
- cargo update pulls postgres-protocol 0.6.12 (RUSTSEC-2026-0179/-0180),
  tokio-postgres 0.7.18 (RUSTSEC-2026-0178), quinn-proto 0.11.15
  (RUSTSEC-2026-0185).
- frameshift-server: prometheus default-features=false drops protobuf
  2.28 (RUSTSEC-2024-0437); the server only emits the text format.
- dev: testcontainers 0.23 -> 0.27, testcontainers-modules 0.11 -> 0.15;
  drops unmaintained tokio-tar (RUSTSEC-2025-0111, no upstream fix) for
  the maintained astral-tokio-tar fork, and rustls-pemfile.
- ci: audit job now blocking, ignoring only RUSTSEC-2024-0370
  (proc-macro-error, build-only via age, removable only by the age 0.11
  beta that backs vault crypto).

diesel-async 0.9 migration: transaction() now takes an AsyncFnOnce, so
register_pack_version uses an `async move |conn|` closure instead of the
removed `|conn| Box::pin(async move { .. })` form; ManagerConfig is now
generic, so pool.rs names AsyncPgConnection explicitly. rust-version
1.75 -> 1.86 to match the new floor (diesel 2.3 + async closures).

clippy -D warnings clean; cargo test --workspace 616 passed, 0 failed.
`git push --mirror` deletes refs absent from the local checkout. Because
actions/checkout only fetches the triggering branch, the mirror push
tried to delete every other ref on the target -- and the remote rejects
deleting its default branch `main`, so the job failed on every push.

Fetch all source branches into remote-tracking refs, then force-push
branches and tags with explicit refspecs (additive, no deletion).
@Ghost-Frame
Ghost-Frame merged commit 960301e into feat/intelligent-selection Jun 30, 2026
6 checks passed
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.

1 participant