Skip to content

Commit e974511

Browse files
mcp-tool-shopclaude
andcommitted
treatment(shipcheck): SHIP_GATE hard gates A-D pass (92%) + SECURITY/CHANGELOG/trust-model
Root shipcheck gate worked against the actual repo with evidence: 24 checked / 11 SKIP / 2 unchecked (both soft-gate E: translations + GitHub metadata — metadata now set via gh). Authored a real root SECURITY.md (per-surface threat model: local-only file I/O, no network/telemetry/secrets, destructive-op safety rails, fail-silent hook), a README '## Trust model' section (A2), root + packages/cli CHANGELOG (Keep-a-Changelog). Fixed D5: packages/cli tarball was missing CHANGELOG.md — added it to files (verified via npm pack --dry-run: dist + README + CHANGELOG + LICENSE). SCORECARD reflects actual audit counts. Hard gates A-D pass honestly; security spot-check (no secrets/network/telemetry/eval) confirmed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 458cc8f commit e974511

7 files changed

Lines changed: 274 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/).
7+
8+
## [Unreleased]
9+
10+
The consolidation of the Knowledge OS into a single npm-workspaces monorepo with one
11+
unified CLI. Three previously-separate packages and the live runtime hook now live and
12+
ship together under `loadout-os`.
13+
14+
### Added
15+
16+
- **Workspace monorepo**`packages/{kernel,memories,rules,cli}` + `apps/hook` wired
17+
under one npm-workspaces root, with an intentional topological build order
18+
(kernel → memories → rules → cli) so the adapters build against the kernel's dist.
19+
- **Unified `@mcptoolshop/loadout-os` CLI** (`packages/cli`) — one binary that wraps the
20+
three library surfaces (kernel = ai-loadout, memories, rules) and absorbs the
21+
operational rituals:
22+
- Namespaced adapter surfaces: `memories <index|validate|stats|health>` and
23+
`rules <analyze|validate|stats|split>`.
24+
- Flat kernel verbs: `resolve`, `explain`, `usage`, `dead`, `overlaps`, `budget`,
25+
`validate` (the kernel index-structure validator — the flat-vs-namespaced split is
26+
how the `validate` name collision is resolved).
27+
- **`doctor`** — a read-only 8-check health screen over the live store, global index,
28+
runtime-hook drift, resolver layers, core entries, observability loop, hook wiring,
29+
and usage growth. Never writes.
30+
- **`report`** — read-only observability over `usage.jsonl`: usage summary, dead
31+
entries, token budget, and a score distribution for calibrating the hook floor.
32+
- **`refresh`** — the Index Freshness Ritual (index → validate → publish) folded into
33+
one command, with an andon halt on validation failure and a `<dest>.bak` compensator
34+
on the one irreversible write.
35+
- `hook test` — drive the runtime hook on a sample prompt in an isolated HOME.
36+
- **Runtime hook unified** (`apps/hook/loadout-hook.mjs`) — the `UserPromptSubmit` hook
37+
that injects ≤5 pointer lines (≤200 tokens). Fail-silent: every error path exits `0`.
38+
- **Shared CLI substrate** — one arg parser, one structured `CliError { code, message,
39+
hint }` shape routed at the process boundary (no raw stack traces), and per-command
40+
`--help` with synopsis, arguments, flags, an example, and exit codes for every leaf
41+
command.
42+
- **Documentation** — a Starlight handbook (overview, getting started, architecture,
43+
command reference, rituals, migration) connected to the landing page, plus a root
44+
`SECURITY.md` covering the consolidated attack surface.
45+
46+
### Fixed
47+
48+
- **Matcher recall** (FT-K1) — domain entries were scored by pure coverage
49+
(`matched / declared keyword count`), which starved keyword-rich entries: a genuine
50+
2–3 keyword match on the live 30+-keyword entries scored below the 0.1 inclusion floor.
51+
The matcher now blends coverage with an absolute recall signal (`max(coverage,
52+
matched / 5)`), so real multi-keyword matches are reachable while single incidental
53+
hits stay quiet.
54+
55+
### Changed
56+
57+
- The three legacy bins (ai-loadout, claude-memories, claude-rules) keep working until
58+
their planned retirement; the unified `loadout-os` package ships from this repo. The
59+
published upstream today remains `@mcptoolshop/ai-loadout` (the kernel).
60+
61+
<!-- ## [1.0.0] - YYYY-MM-DD -->
62+
<!-- ### Added -->
63+
<!-- - First consolidated release. -->

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ Decompose-by-secrets (Parnas 1972) was the clean answer for a team of N humans.
7575

7676
Consolidation in progress. loadout-os folds together the kernel and two adapters that previously lived as separate packages, plus the live runtime hook. The published upstream today is **`@mcptoolshop/ai-loadout`** (the kernel); the unified `loadout-os` package ships from this repo. The three legacy bins keep working until their planned retirement.
7777

78+
## Trust model
79+
80+
loadout-os runs entirely on your machine. There is no network call, no telemetry, and no account.
81+
82+
- **Data it touches (local only):** your memory store (`MEMORY.md` + topic files), your instruction files (`CLAUDE.md` + `.claude/rules/`), the generated dispatch index next to the store, the global resolver index (`~/.ai-loadout/index.json`), and the append-only usage log (`~/.ai-loadout/usage.jsonl`).
83+
- **Data it does NOT touch:** no network egress, no telemetry, no remote services, no credentials or secrets. Nothing is read, stored, or transmitted off the local disk paths above.
84+
- **Permissions required:** local filesystem only. `doctor` and `report` are pure reads (they never write). The only writes are the index files, the interactive `rules split` output, and the usage log — all in the expected local locations above. The irreversible write (`refresh` publishing the live global index) is guarded by an andon halt on validation failure and a `<dest>.bak` compensator. The runtime hook is fail-silent: every error path exits `0`, so it can never block a prompt.
85+
86+
Full threat model and reporting process: [SECURITY.md](./SECURITY.md).
87+
7888
## License
7989

8090
MIT — matches all upstream sources.

SCORECARD.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Scorecard
2+
3+
> Reflects the ACTUAL SHIP_GATE.md state after the 2026-06-16 treatment-prep pass, not estimates.
4+
5+
**Repo:** loadout-os (`@mcptoolshop/loadout-os`)
6+
**Date:** 2026-06-16
7+
**Type tags:** `[all]` `[npm]` `[cli]`
8+
9+
## Gate state (actual)
10+
11+
Counts are: **checked** (evidence-backed `[x]`) / **applicable** (items that are not `SKIP`). SKIPs are not-applicable items (MCP/pypi/vsix/desktop/vscode) or explicit studio-policy skips.
12+
13+
| Category | Checked / Applicable | SKIP | Coordinator / publish-time | Status |
14+
|----------|----------------------|------|----------------------------|--------|
15+
| A. Security | 6 / 6 | 2 (`[mcp]` egress, `[mcp]` stack traces) || PASS |
16+
| B. Error Handling | 3 / 3 | 4 (`[mcp]`×2, `[desktop]`, `[vscode]`) || PASS |
17+
| C. Operator Docs | 6 / 6 | 1 (`[mcp]` tool docs) || PASS |
18+
| D. Shipping Hygiene | 6 / 6 | 3 (D4 dependabot policy, `[vsix]`, `[desktop]`) || PASS |
19+
| E. Identity (soft) | 2 / 4 || 2 (translations, GitHub metadata) | partial (soft) |
20+
21+
**Hard gates A–D: all applicable items checked or SKIPped honestly → PASS.**
22+
23+
## Key gaps closed this pass
24+
25+
1. **Root SECURITY.md** was a template — now a real per-surface threat model (pure core, read-only rituals, write path with andon halt + `.bak` compensator, fail-silent hook; no network/telemetry/secrets; matcher uses plain-string lookups, no user regex → no ReDoS, no eval).
26+
2. **README threat model** — added a "Trust model" section (data touched / not touched / permissions).
27+
3. **Root CHANGELOG.md** — was a template stub; now Keep-a-Changelog with an `[Unreleased]` consolidation summary (workspace wiring, unified CLI + doctor/report/refresh, matcher recall fix, docs).
28+
4. **D5 npm pack**`packages/cli` tarball was missing CHANGELOG.md; added `packages/cli/CHANGELOG.md` and `CHANGELOG.md` to the cli `files` field. Verified the tarball now ships dist/ + README.md + CHANGELOG.md + LICENSE.
29+
30+
## Remaining (coordinator / publish-time)
31+
32+
| Item | Owner | When |
33+
|------|-------|------|
34+
| E2 Translations (polyglot-mcp, 8 languages) | Coordinator | BEFORE npm publish + GitHub release |
35+
| E4 GitHub repo metadata (description, homepage, topics) | Coordinator (`gh repo edit`) | Publish time |
36+
| D2 actual version-matches-tag | Coordinator | Phase-6 bump to 1.0.0 + tag `v1.0.0` (enforcement gate already in release.yml) |
37+
38+
## Notes
39+
40+
- Version is the placeholder `0.0.0` in both `package.json` and `packages/cli/package.json`; the coordinator bumps to `1.0.0` at Phase 6 (per shipcheck product standards: pre-1.0 promotes straight to 1.0.0). The `release.yml` tag-vs-version check enforces the match at tag time.
41+
- The first real `npm publish` is blocked until the workspace deps (`@mcptoolshop/claude-memories`, `@mcptoolshop/claude-rules`) are published or bundled — see the NOTE in `release.yml:65-69` and the multi-repo-publish-sequencing protocol. This is a release-sequencing concern, not a SHIP_GATE item.

SECURITY.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
`loadout-os` is the consolidated Knowledge OS (kernel + memories + rules adapters + the runtime hook) shipped as `@mcptoolshop/loadout-os`.
6+
7+
| Version | Supported |
8+
|---------|-----------|
9+
| 1.x | Yes |
10+
| < 1.0 | No (pre-release consolidation builds) |
11+
12+
## Reporting a Vulnerability
13+
14+
If you discover a security issue, please report it responsibly:
15+
16+
1. **Do not** open a public issue.
17+
2. Email: **64996768+mcp-tool-shop@users.noreply.github.com**
18+
3. Include: a description of the vulnerability, steps to reproduce, the version affected, and an impact assessment.
19+
20+
### Response timeline
21+
22+
| Action | Target |
23+
|--------|--------|
24+
| Acknowledge report | 48 hours |
25+
| Assess severity | 7 days |
26+
| Release fix | 30 days |
27+
28+
## Attack Surface
29+
30+
`loadout-os` is a **local-only CLI** (`@mcptoolshop/loadout-os`) plus a `UserPromptSubmit` runtime hook. It unifies three previously-separate library surfaces and absorbs the operational rituals. The surface breaks into four parts:
31+
32+
- **Pure core (kernel).** Matching, merging, validation, token estimation, and frontmatter parsing are pure functions with no side effects — they only transform data structures in memory. The matcher (`packages/kernel/src/match.ts`) scores entries with plain-string keyword/token lookups (`Set.has`, `split`, `every`/`some``match.ts:105-119`). There is **no user-supplied regex** in the matching path, so there is no ReDoS vector; the only `RegExp` use is a fixed `[^a-z0-9\s]` tokenizer (`match.ts:73`).
33+
- **Read-only rituals (`doctor`, `report`).** Both are pure reads — they never write (`doctor.ts` header + `runDoctor` comment "Pure read — no writes, ever", `doctor.ts:128-131`; `report.ts` header "Pure read; never writes"). They read the canonical memory store, the global resolver index, `settings.json`, and `usage.jsonl` from fixed, well-known locations, and delegate every check to a library validator rather than self-grading.
34+
- **Write path (`refresh`, `memories index`, `rules split`).** The only writes are **local files in expected locations**: the store `index.json` (next to `MEMORY.md`), the global resolver index (`~/.ai-loadout/index.json`), and — for `rules split``.claude/rules/` plus the `CLAUDE.md` file itself. Destructive operations have safety rails:
35+
- **`refresh`** is the one ritual that performs an irreversible write (the live global index the hook reads on every prompt). It has an **andon halt** — any error-severity validation issue exits `1` and writes nothing downstream (`refresh.ts:211-219`) — and a **named compensator** — an existing destination is backed up to `<dest>.bak` before overwrite and restored on any write failure (`refresh.ts:253-287`). `--dry-run` computes everything and writes nothing.
36+
- **`rules split`** is interactive: it passes through to the `claude-rules` bin with inherited stdio, requires per-extraction confirmation, supports `--dry-run`, and writes a `.bak` backup before editing. loadout-os spawns it with `spawnSync` and forwards the user's args verbatim (`commands.ts:411-433`) — this is the only subprocess spawn and it targets a fixed, resolved local bin, never an arbitrary command.
37+
- **Runtime hook (`apps/hook/loadout-hook.mjs`).** A `UserPromptSubmit` hook that reads the global index, matches the prompt, and injects ≤5 pointer lines (≤200 tokens). It is **fail-silent by design**: every error path exits `0` (`safeExit(0)` on missing index, non-JSON stdin, empty prompt, malformed index, import failure, or a matcher throw — `loadout-hook.mjs:72-95`), so a broken hook can never block a prompt. Its only write is an append-only `usage.jsonl` event, wrapped in a `try/catch` that swallows failures (`loadout-hook.mjs:120-141`).
38+
39+
## Across all surfaces
40+
41+
- **No network access** — makes no HTTP requests, opens no sockets, follows no remote locations.
42+
- **No telemetry** — nothing is collected centrally or transmitted. Usage data stays in the local `~/.ai-loadout/usage.jsonl` the operator owns.
43+
- **No secrets handling** — reads no credentials, environment secrets, or tokens; the tool handles loadout metadata only. No tokens or credentials appear in source or in any diagnostic output.
44+
- **No code execution from data** — no `eval`, no `Function()`, no dynamic execution of file content. On-disk index/usage files are treated as untrusted: malformed JSON layers and malformed JSONL lines are skipped rather than executed or thrown on.
45+
- **Scoped filesystem access** — writes are confined to the store `index.json`, the global resolver index, `.claude/rules/` + `CLAUDE.md` (via the interactive split), and the append-only usage log. The tool never walks arbitrary trees or writes outside the paths described above.
46+
47+
## Input Validation
48+
49+
- Frontmatter and `MEMORY.md`/`CLAUDE.md` parsing uses hand-rolled string splitting — no YAML parser, no regex-based evaluation, no prototype-pollution vector (inherited from the kernel/memories/rules adapters; see their per-package `SECURITY.md`).
50+
- `validateIndex()` / `validateMemory()` check structural integrity only; they do not execute or interpret field values.
51+
- The shared CLI arg parser fails loudly on a swallowed flag value (`--out --json`) rather than silently creating a file named `--json` (`console.ts:76-93`).

0 commit comments

Comments
 (0)