feat(vault): default vault install to the published compmech vault - #9
Merged
Conversation
The wheel shipped 302 nodes and 2.65 MB — roughly two thirds of the package — that no user could reach. ``resolve_global_vault`` picks an explicit argument, then AKMS_GLOBAL_VAULT, then ``global_vault`` from the propagation config, then ~/.claude/akms/nodes. The package directory is on none of those paths, and nothing copied the corpus out of it, so every ``pip install akms`` carried a corpus the runtime would never read. Verified directly against the published 0.3.0 wheel before removing anything. Removing it drops the wheel from 1.3 MB to 320 KB. Every functional bundled asset still ships: skills, qmd wrappers, hooks, agents, commands, the kernel. ``package-data`` is now enumerated rather than ``_bundled/**/*``. The blanket glob is how the corpus came to ship in the first place, and how ``.DS_Store`` reached users; an enumerated list means a corpus dropped back into _bundled cannot silently re-enter the wheel. **akms vault status / install.** Removing the corpus exposes a gap that was always there: installation.md tells a new user to ``mkdir -p ~/.claude/akms/nodes`` and no documentation says where nodes come from. Every fresh install has had an empty vault. ``vault install`` takes a directory, a .tar.gz, or an https URL. Nothing in AKMS writes to the global vault on its own, and that stays true: this is a foreground command that reports what it will do and refuses to replace a populated vault without --force. It also refuses a source with no nodes, a source whose markdown declares no ``akms_schema``, plain http, and any archive member that resolves outside the destination. Content is staged beside the vault and swapped in, so an interrupted install cannot leave a half-written vault where a working one used to be. **Test fixture.** The seed integration tests used the shipped corpus as their fixture. They now run against a pinned 52-node subset under tests/fixtures/ — the six skill-* nodes they assert over, the closure of every node their edges target, and every content_ref payload involved. Self-consistent, no dangling edges, 300 KB, and not packaged. All 27 of those tests pass unchanged. Corpus-wide QA belongs with the vault. One real bug surfaced while testing the tarball path: macOS ``tar`` writes AppleDouble ``._name`` companions beside every file, they extract as files ending in .md, and they were both counted as nodes and installed into the vault, where the compiler would try to parse resource-fork data as v2 nodes. Filtered on copy, with a regression test. Verified: 767 tests pass, ruff format and check clean, no new pyright errors, and an installed vault compiles to a 52-node, 165-edge graph end to end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
**Paths.** The docs referenced `Packages/AKMS`, `Packages/AKMS_nodes_gen` and friends 37 times across 14 files. git tracks `packages/akms` and `packages/akms_nodes_gen` — lowercase. Every one of those commands resolves on a case-insensitive macOS filesystem and fails outright on Linux, including the first command on the installation page. Rewritten and each resulting path checked against the tree. **Installation.** The page opened with "the documentation currently assumes a repository checkout rather than a PyPI release", which stopped being true when 0.3.0 published. It now leads with `pip install akms` and keeps the source checkout as the contributor path. It also claimed the root workspace path-sources `mechdsl-core` and `algo2code` from a sibling `../MechDSL` checkout, so a full sync "requires that layout". That is a private-repo detail; the public root declares four workspace members and nothing else, which is why CI runs `uv sync --locked --all-packages --all-extras --all-groups` on every push. The warning could only have deterred people from a command that works. **The vault.** The page told a new user to `mkdir -p ~/.claude/akms/nodes` and never said where nodes come from, so every fresh install has had an empty vault. It now explains that vaults are distributed separately and documents `akms vault status` and `akms vault install`. **Code mirrors.** Made explicit that a mirror is generated from your own repository rather than shipped — it is build output, and nothing arrives pre-populated. The `repo2md` provider now says plainly that it needs the external `repo-wiki` executable, which AKMS does not install, that `legacy` is the in-process default requiring nothing, and that because `mirror.fallback_on_error` defaults to false, configuring repo2md without the binary fails loudly rather than silently reverting. Verified on a strict build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
A vault distributed as a git repository carries a README, a licence and CI configuration. Copying those into the vault is not merely untidy: the graph compiler treats any markdown in the vault lacking ``akms_schema`` as a schema error and re-raises it regardless of strictness, so a README in the vault makes every later build fail with SchemaValidationError. Every GitHub repository has one, and so does Packages/Nodes_Vault — the corpus this vault is generated from. Verified by installing a repository-shaped source and watching build_graph refuse it. The fix belongs in the installer rather than in a layout rule vault authors have to remember. ``vault install`` now copies what a vault *is* — files declaring ``akms_schema``, plus the ``content/`` payload tree they address through ``content_ref`` — and reports the top-level names it left behind instead of discarding them silently. That single pass also subsumes the dotfile filtering, so AppleDouble ``._name`` companions and ``.DS_Store`` never reach the vault either. Verified end to end: a source carrying README.md, LICENSE and .github/ installs 52 nodes, leaves the furniture behind, names it in the output, and compiles to a 52-node, 165-edge graph. 769 tests pass; ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
A vault's README documents the node format, so it contains the text `akms_schema` inside a fenced example. The substring test that decided what to install therefore classified the README as a node, copied it into the vault, and the graph compiler aborted every subsequent build on it — the exact failure the previous commit set out to prevent. Found by generating the real vault repository and installing it: 303 nodes instead of 302, then SchemaValidationError on README.md. Detection is now structural. The file must open with a frontmatter fence, and `akms_schema` must appear as a top-level key before that block closes. Prose and fenced examples cannot satisfy that. Verified against the generated vault: 302 nodes install, README, LICENSE and scripts/ are named as left behind, and the result compiles to a 302-node, 1045-edge graph. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
check_public_tree walked the filesystem and knew nothing about .gitignore, so any gitignored working directory failed it locally. A stray `packages/.orchestra/session-state.json` — a file git cannot publish under any circumstances — was enough to make the release preflight report a violation. CI never saw it, because CI checks out from git. That is the problem: a gate whose local runs are noisy and whose CI runs are clean teaches people to skip the local one, which is where it earns its keep. The audit now asks git what it would publish — tracked files plus untracked files that are not ignored — and scans that. Untracked-but-not-ignored files stay in scope deliberately: they are one `git add .` away from shipping. When git cannot answer (no repository, no git, an extracted tarball) it returns None and the walk runs as before, which is the conservative direction. Verified quieter without being weaker: gitignored .orchestra file present -> pass untracked, unignored `session-state.json` -> fail, named secret in an untracked, unignored file -> fail, named same secret inside a gitignored path -> pass, out of scope outside a git repository -> falls back to the full walk Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
…AKMS The private-repository pattern matched `SOSOVSKI/AKMS` literally, so every other repository under that account passed the gate unnoticed. That is not hypothetical. compmech-reference-pack — the next package queued for publication — names `SOSOVSKI/MechDSL`, `SOSOVSKI/ConstKit` and `SOSOVSKI/SymbolicFemWorkbench` in its shipped `domain_pack/source_packs/*.yaml` and in `capabilities.py`. MechDSL is private and the other two are not publicly resolvable, so porting that package as-is would have disclosed three private repository names through a gate specifically built to prevent exactly that. The pattern now matches any repository under the account. The current tree still passes; a planted `SOSOVSKI/MechDSL` reference is now caught. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
The computational-mechanics companion adapter, ported from the private workspace at 0.3.0 to join the lockstep release. It bridges akms-learn LSP excerpts to the MechDSL executable backend; the compile path stays an optional `mechdsl` extra so the backend is not pulled into a consumer's lock closure unless asked for. **Private references removed.** The package named three repositories that are not public — `SOSOVSKI/MechDSL` (private), `SOSOVSKI/ConstKit` and `SOSOVSKI/SymbolicFemWorkbench` (not resolvable at all) — in shipped source packs and in capabilities.py. MechDSL is repointed to `CEmM2/MechDSL`, the public repository that mechdsl-core, algo2code and ti-runtime all declare as their source on PyPI. The constkit and symbolic_fem_workbench source packs are dropped. Both were `capability_status: planned` — no adapter exists — and both named non-resolvable repositories, so shipping them would have advertised companions nobody can obtain while disclosing private repository names. The domain pack now declares only the mechdsl companion, and the test that asserted the two planned roles asserts that instead. Restore them when those repositories and their adapters are public. Logic-Loom references now name `CEmM2/Logic-Loom` explicitly rather than gesturing at an unqualified project. Internal plan identifiers (`P2-1` … `P2-6`) are stripped from every docstring and test. They mean nothing to a reader of the public package, and check_public_tree's pattern requires a `Task ` prefix, so the bare parenthesised form would have shipped unnoticed. **Wiring.** Workspace member and uv source, `PACKAGE_FILES` in check_versions, the pyright include list, a `compmech-reference-pack` build step and publish matrix leg in release.yml, and the `pypi-compmech-reference-pack` environment restricted to `v*` tags like the other four. The lock gains mechdsl-core, algo2code and their scientific dependencies (numpy, scipy, sympy, mpmath, opt-einsum, nrpylatex) from the all-extras resolution. No Taichi. Verified: 1752 tests pass, ruff format and check clean, pyright 0 errors with the new package in scope, strict docs build, public-tree audit passes, version audit passes, and all five packages' release globs claim exactly their own wheel and sdist with all ten distributions accounted for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
I dropped both on the assumption that their source was unobtainable, having found `SOSOVSKI/ConstKit` and `SOSOVSKI/SymbolicFemWorkbench` unresolvable. The assumption was wrong: neither exists as a standalone repository because both live in the public `SOSOVSKI/Teaching-materials` monorepo, under `packages/constkit` and `packages/symbolic-fem-workbench`. Both packs are restored, repointed at that repository with roots that match its actual layout — verified against the published tree rather than transposed from the old paths, so `docs`, `code`, `tests` and (for the workbench) `notebooks` all name directories that exist. Neither declares an `examples` root it does not have. Both stay `capability_status: planned`, which now means what it should: the adapter is unwritten. The source was always available; only my reading of it was wrong. A new test pins every source pack's repository to a known-public target, so a future edit cannot quietly reintroduce a reference a reader cannot follow. **Gate.** `SOSOVSKI/Teaching-materials` is public and legitimately referenced from the public tree, so the blanket private-account pattern needed an allowlist. The trailing `(?![\w.-])` in it is load-bearing: with a plain `\b` there, a hyphen counts as a word boundary and `Teaching-materials-private` would have matched the allowlisted prefix and been waved through. Caught by testing the boundary rather than the happy path. Verified across eight cases — `Teaching-materials` and paths beneath it pass; `AKMS`, `MechDSL`, `ConstKit`, `Logic-Loom` and `Teaching-materials-private` are all flagged. 1753 tests pass, ruff clean, pyright 0 errors, strict docs build, public-tree and version audits pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
akms-vault-compmech v1.0.0 is tagged, so `akms vault install` now needs no argument: it fetches that release and installs 264 nodes. Pinned to the tag rather than a branch. A user typing four words has not consented to installing whatever `main` happens to hold that day, and a moving default would make the same command produce different content over time. A test asserts the pin, so pointing it at a branch fails the build. **Redirects.** Verified end to end against the real tarball, which surfaced a hole: `urlopen` follows redirects on its own, so the https check on the URL the user typed did not cover the hops after it. An https source could hand off to plain http mid-flight and still be treated as trusted. Redirects cannot simply be refused — GitHub's archive URLs redirect to codeload, which is the normal path — so the handler follows them and refuses only the downgrade. Verified: a bare `akms vault install` fetches the published v1.0.0 tarball, leaves CITATION.cff, LICENSE, README.md and scripts/ behind, installs 264 nodes, and compiles to a 264-node, 909-edge graph. 1756 tests pass, ruff clean, pyright 0 errors, strict docs build, public-tree and version audits pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
CodeQL alert #7, py/tarslip, high severity, on the unfiltered fallback at vault_commands.py:161. It was right, and this is not a suppression. The previous shape validated every member and then called `extractall`, falling back to an unfiltered call on interpreters whose tarfile predates the `data` filter. That fallback was a genuine weak point rather than a false positive: tarfile re-derives each destination path from the same member names, so the validation above it was advisory, not load-bearing. Nothing connected the check to the write. Extraction is now explicit, member by member, so the path check is the only route to disk. That also turns a blacklist into a whitelist: a vault is directories and regular files, so symlinks, hardlinks, devices and FIFOs are skipped rather than reasoned about, and archive permissions are not carried over — nothing can arrive with an executable or setuid bit. It removes the version-dependent behaviour too. There is no longer a filter argument that may or may not exist, so every interpreter takes the same path. No `extractall` and no security suppression remains in the package. Verified: a hostile archive carrying a symlink to /etc/passwd and a 0o4777 mode installs two nodes and neither the link nor the mode lands; the traversal refusal still fires; and a bare `akms vault install` against the published v1.0.0 tarball still yields 264 nodes, 14 payloads and a 909-edge graph. 1757 tests pass, ruff clean, pyright 0 errors, public-tree audit passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcdE6eeSDBh8xrgKCoGrkw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
akms-vault-compmech v1.0.0 is tagged, so
akms vault installnow needs noargument: it fetches that release and installs the nodes (264 as per v1.0.0).
Pinned to the tag so that i can feel free to add nodes and revise existing ones if needed