⚠️ TOKEN DISCIPLINE. Check file size first.GrepoverRead. Useoffset/limit. Smallest diff that solves the problem. Delete dead code, unused imports, stale comments. Call out irrelevant context before proceeding. Bloat degrades reasoning.⚠️
Read this file in full. Rules below are NON-NEGOTIABLE — violations are rejected in review.
Shipwright is a portfolio-shared library that scaffolds binary and IDE-extension deployment in a consistent way across multiple downstream products (Too Many Cooks, Deslop, Basilisk, SharpLsp, dart_mutant — see docs/specs/source-projects.md). It defines the binary version contract, manifest schema, compatibility matrix, and the host-side resolution algorithm that IDE extensions use to locate and verify their backing binaries before launching them. See docs/specs/ for the authoritative behavior specs and docs/plans/ for the implementation plan.
Primary language(s): Rust (workspace, libraries), TypeScript/Node (SDKs, MCP, VS Code helpers, fixture tests), Dart, C#/.NET, Kotlin/Gradle, and Eleventy for the docs website.
Build command: make ci
Test command: make test
Lint command: make lint
If the TMC server is available: register on start (name, intent, files), lock files before editing, broadcast your plan, check messages periodically, release locks when done. Never edit a locked file — wait or take another approach.
- NO git write commands. No
add,commit,push,checkout,merge,rebase, branch creation, tag movement, or worktrees. Read-only status/diff/log commands are allowed for orientation. CI handles source-control writes unless a dedicated PR-submission skill explicitly says otherwise. - NEVER push to
maindirectly. Every change goes through PR -> CI green -> merge. - NEVER list an agent as a commit co-author. No AI/agent
Co-Authored-Bytrailers. - Work on exactly one branch at a time. Reuse the existing feature branch; if multiple feature branches exist, stop and converge them before doing feature work.
- Auto-memory is off. Persistent rules must be changed in this file through review, not silently captured by an agent memory feature.
- ZERO DUPLICATION. Search before writing. Move code, don't copy it.
- NO EXCEPTIONS for control flow. Return
Result<T,E>. Exceptions are panic-level only. - NO REGEX on structured data. Use real parsers for JSON/YAML/TOML/code.
- NO PLACEHOLDERS. Use
todo!()— never silently no-op. - Functions < 20 lines. Files < 500 lines. Refactor when over.
- Never delete or skip tests. Never remove assertions. 100% coverage is the goal.
make testis FAIL-FAST. Stops at first failing test. Never--no-fail-fast. Saves CI minutes; stops agents idling on doomed runs. See REPO-STANDARDS-SPEC [TEST-RULES].make testALWAYS computes coverage AND enforces it. Threshold lives incoverage-thresholds.jsonat the repo root — NOT env vars, NOT gh repo variables, NOT CI YAML. Below threshold = pipeline fails. Ratchet only. See [COVERAGE-THRESHOLDS-JSON].- Prefer E2E/integration tests. Unit tests only for isolating problems.
- Heavy structured logging everywhere. See Logging below.
- No linter suppressions. Fix the code.
- Pure functions over statements.
- Spec IDs are hierarchical, non-numeric:
[GROUP-TOPIC]/[GROUP-TOPIC-DETAIL]orSWR-GROUP-TOPIC/SWR-GROUP-TOPIC-DETAIL(e.g.,[AUTH-TOKEN-VERIFY],[CI-TIMEOUT],SWR-IDE-DOTNET-RUNTIME). Same-group sections sit adjacent in the TOC. NO sequential numbers ([SPEC-001],SWR-SPEC-001). Code/tests/docs that implement a spec section MUST reference its ID in a comment sogrep SWR-IDEfinds spec → code → tests in one shot.
- Structured logging library only. Never
println!(Rust),console.log(TS),print(Dart), orDebug.WriteLine(C#) for diagnostics. Libraries: Rusttracing, TSpino, Dartdart_logging, C#Microsoft.Extensions.Logging+Serilog. - Log at entry/exit of significant operations. Levels:
error|warn|info|debug|trace. Silent failures are forbidden. - Structured fields, not string interpolation.
{ binary: "deslop-lsp", expected: "0.3.1", found: "0.2.0" }— never"deslop-lsp version mismatch". - NEVER log PII. NEVER log secrets. Log
"key: present"or a truncated hash, never the value.
- No
unwrap()/expect()in production (tests OK forexpect). - No
panic!/todo!/unimplemented!/unreachable!in production. - No
unsafe {}orallow(clippy::...)without documented justification. - All public items have
///doc comments. thiserrorfor library errors;anyhowonly in application code.- Workspace lints in
Cargo.toml [workspace.lints]are deny-by-default — do not weaken them. crates/shipwright-hostis the pure binary-resolution algorithm with NO I/O. The probe is injected. Keep it that way.
- No
any(useunknownand narrow). No!non-null assertion. No// @ts-ignore/@ts-nocheck. - No implicit
any— annotate every parameter and return type. - No
as Typecasts without a comment explaining safety. - No throwing — return
Result<T,E>(discriminated union). - Tests use the Node built-in
node --testrunner (seetests/fixtures.test.mjsandtools/validate-manifest/). New TS code is allowed but must run on Node ≥ 20 with"type": "module".
- Public library APIs live under
clients/dart/shipwright/lib/and must stay null-safe. - Run
dart analyzeanddart test --fail-fastfromclients/dart/shipwrightbefore touching Dart resolver behavior. - Source-controlled package versions stay at
0.0.0-dev; release tags stamp real versions.
- Keep nullable reference types enabled and warnings treated as errors.
- Tests use xUnit and must run fail-fast through
dotnet test -- xunit.stopOnFail=true. - Source-controlled
<Version>values stay at0.0.0-dev; release tags stamp real versions.
- Kotlin client code lives in
clients/kotlin/shipwright-intellij/. - Use the checked-in Gradle wrapper; do not assume a system Gradle installation.
- Never delete a failing test. Fix the code or the expectation.
- Never skip a test without a ticket number AND expiry date in the skip reason.
- Specific assertions only.
assert.ok(true)is illegal. - No try/catch in tests that swallows exceptions and asserts success.
- Deterministic. No
sleep(), no timing dependencies, no random state. - E2E tests: black-box only — public APIs or CLI. Never reach into internals.
- Manifest fixtures live in
fixtures/manifests/andfixtures/golden-manifests/. Add a fixture for every new schema branch you cover.
Agent PMO make targets
make build # compile everything (Rust workspace + Node tools)
make test # FAIL-FAST tests + coverage + threshold (ONLY test entry point)
make lint # all linters/analyzers (no formatting)
make fmt # format in place
make clean # remove build artifacts
make ci # lint + test + build (full CI simulation)
make setup # post-create dev environment setupmake test runs the test runner with its fail-fast flag, collects coverage, asserts measured ≥ threshold from coverage-thresholds.json, and exits non-zero on any failure. To debug a single test, invoke the runner directly — that is not a Makefile target.
make fmt formats code in-place. make lint runs linters/analyzers (read-only, no formatting). make test runs tests with coverage. Three separate targets — no overlap.
Shipwright contains Deslop-supported languages (Rust, C#, Dart), so agents MUST use the Deslop loop when changing code:
- Before authoring any function, method, class, helper, fixture, or test setup, call
find-similar. Reuse existing code for identical/nearly-identical matches orsignals.fused >= 0.85. - After changing code, run
rescanand checktop-offenders; inspect any cluster you plan to touch withcluster-by-id. - Never silence duplication by raising
.deslop.toml, hiding code, or making trivial shape changes.
All version fields in source (Cargo.toml, package.json, *.csproj, pubspec.yaml, shipwright.json) MUST be 0.0.0-dev. Real versions are stamped at release time by shipwright-version-stamp from the git tag — never hard-coded in source. Hard-coded release versions in source are a release-engineering defect. See [SWR-VERSION-BUILD-STAMPING] in docs/specs/binary-version-contract.md.
crates/ # Rust workspace members
shipwright-host/ # pure binary-resolution algorithm (no I/O)
shipwright-manifest/ # manifest schema and version-format helpers
shipwright-zed/ # Zed extension helpers
clients/ # per-language client SDKs (placeholders)
dart/ dotnet/ kotlin/ ts/
extensions/
shipwright-tools/ # VS Code extension and visual manifest editor
docs/
specs/ # behaviour specs (binary-version-contract, compatibility-matrix, etc.)
plans/ # implementation plans with TODO checklists
website/ # Eleventy + eleventy-plugin-techdoc documentation site
schemas/ # JSON schemas (shipwright, version-manifest, platforms, test-vectors)
fixtures/ # manifests, golden manifests, version outputs, platform definitions
tools/
validate-manifest/ # Node-based AJV validator (shipwright-validate-manifest)
shipwright-version-stamp/ # release-time version stamper
templates/
gh-actions/ # downstream-project release/publish workflow templates
examples/ci/ # example consumer CI configurations
tests/fixtures.test.mjs # Node test runner over fixtures
Architecture invariants:
- The Rust host crate (
shipwright-host) MUST stay pure — no filesystem, network, or process spawning. The version-probe function is injected by the caller (the IDE extension). - The JSON schemas in
schemas/are the source of truth for the manifest format. Generated client code (Rust, TS, Dart, .NET, Kotlin) consumes these schemas. - Every fixture in
fixtures/is exercised bytests/fixtures.test.mjsagainst the AJV validator intools/validate-manifest/. templates/gh-actions/is shipped as-is to downstream products — never inline-modify; treat as published interface.
All VS Code extension tests, builds, and CI pipelines MUST target the latest stable VS Code release. NEVER pin or downgrade to an older VS Code version to work around test infrastructure issues. Fix the infrastructure instead.
- Extension E2E tests run against the latest VS Code via
@vscode/test-electron(downloads latest automatically). engines.vscodeinpackage.jsondeclares the minimum supported version, not the test version.- If
@vscode/test-electronbreaks on a new VS Code release, fix the launcher — do NOT pin a version.
Any VS Code extension with framework-dependent .NET sidecars ("language": "dotnet" components) MUST use the .NET Install Tool extension to acquire the runtime. This is the ONLY permitted approach.
"extensionDependencies": ["ms-dotnettools.vscode-dotnet-runtime"]inpackage.jsondotnet.findPaththendotnet.acquirecommands on activation — non-interactive toast spinner- Set
DOTNET_ROOTin the env passed to the Rust LSP host - On failure: non-modal error notification +
retryDotnetAcquisitioncommand - NEVER crash on missing .NET. NEVER
dotnet tool install. NEVER hand-roll a download.
Full spec: docs/specs/ide-extension-deployment.md [SWR-IDE-DOTNET-RUNTIME]
Agents working on VSIX bundling, CI templates, or the vscode host library MUST read these before writing any code. The Microsoft sample is the authoritative source — follow it exactly.
- Microsoft platform-specific sample (AUTHORITATIVE): https://github.com/microsoft/vscode-platform-specific-sample/tree/main
- CI workflow (Node 22.x, matrix shape, publish job): https://github.com/microsoft/vscode-platform-specific-sample/blob/main/.github/workflows/ci.yml
.vscodeignore(node_modules whitelist pattern): https://github.com/microsoft/vscode-platform-specific-sample/blob/main/.vscodeignore- Runtime binary resolution (
extension.js): https://github.com/microsoft/vscode-platform-specific-sample/blob/main/extension.js
- VS Code bundling guide: https://code.visualstudio.com/api/working-with-extensions/bundling-extension
- Rust Analyzer release workflow (inspiration): https://github.com/rust-lang/rust-analyzer/blob/2024-06-11/.github/workflows/release.yaml#L105
- Shipwright VSIX spec (implements the above):
docs/specs/vsix-platform-bundling.md - Shipwright VSIX template (ready to use):
templates/gh-actions/publish-vsix-per-platform.yml