Substrate thesis requires a single source of truth before parallel work
streams (UI/Atoms/Graph/Runtime) can proceed independently without drift.
This document is that SSoT.
Key decisions baked in (open to revision before lock):
- Atom = one verb on a primitive, not one crate. Target ~150 atoms
across current 25 crates. Crate = physical container, atom = unit of
composition.
- File layout: src/atoms/<verb>.rs (code) + atoms/<verb>.md (docs with
machine-parseable YAML frontmatter) + atoms/schemas/*.json (JSON
Schema draft-07 for input/output) + capabilities.toml (auto-generated
aggregator, committed to repo).
- Atom kinds: command / query / stream / transform. Combined with
side_effects[] and idempotent flag, runtime decides retry safety,
parallelism, caching.
- Naming: <crate>::<verb> globally unique. Rust :: separator keeps it
native-feeling.
- Versioning: atoms inherit crate SemVer. Breaking change to an atom =
new atom (create-v2), old marked deprecated.
- Runtime contract: `kei-runtime invoke <atom-id> --input <json>` with
schema validation at entry + exit, ledger row per invocation.
- Graph contract: kei-sage auto-walks atoms/*.md, resolves [[atom-id]]
wikilinks, exposes rank / related / search / graph over atom corpus.
- UI contract: kei-forge web wizard generates .md + .json + .rs + test
from form input; postcondition cargo check + kei-schema-lint pass.
Document declares 4 stream interfaces explicitly — each stream knows
what it reads from this schema, what it writes, what it does NOT depend
on from other streams. Enables true parallel work.
6 open questions flagged for user review at bottom:
1) JSON Schema draft-07 vs 2020-12
2) Atom ID separator :: vs /
3) side_effects strings vs structured
4) capabilities.toml committed vs gitignored
5) kei-atom-template in this PR or defer to Stream A
6) Error model per-atom vs shared registry
STATUS: DRAFT — awaits user approval + SCHEMA-LOCKED.md marker before
parallel streams start. Once locked, breaking changes require explicit
revocation + all-streams sync.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two production-readiness fixes motivated by the v0.22.2 post-mortem.
1. aarch64-linux: native ARM runner instead of cross-compile
v0.22.2 consistently failed `Install aarch64 cross-linker` (apt
gcc-aarch64-linux-gnu) on the ubuntu-latest x86_64 runner. Was
carried as `experimental: true` so non-blocking, but meant no
aarch64-linux Rust tarball ever shipped.
Fix: move to ubuntu-24.04-arm (native ARM64 runner). Rust builds
aarch64-unknown-linux-gnu HOST-NATIVELY — no cross-linker, no
`.cargo/config.toml` linker override. `experimental: false` now —
native path is reliable.
2. Publish step: softprops/action-gh-release → `gh release create` CLI
v0.22.2 softprops/action-gh-release v2.6.2 uploaded all 15 assets
successfully but exited with `failure` due to a metadata-update
race: asset uploaded to GitHub's blob store, then the subsequent
PATCH to set the asset's `name` returned 404 because the Releases
metadata API hadn't caught up yet (eventual consistency). Workflow
failure → Release left in Draft. We had to promote it manually
(`gh release edit --draft=false`) and re-upload one missing sha256.
Fix: replace the action with `gh release create` + `gh release
upload --clobber` in a bash step.
- Idempotent: existing release gets updated in place.
- No metadata PATCH race: CLI never patches, it creates fresh.
- Retry loop: up to 3 tries per asset on transient network errors.
- `--clobber` means re-runs replace cleanly.
- GitHub CLI is pre-installed on every runner, zero new deps.
Verified post-polish on v0.22.2: 16/16 assets present, Release
Published, `kei-mcp-server-darwin-arm64` + `keisei` both execute on
this MacBook (arm64) — adapter list shows Claude Code detected at
project+user scope. SHA256 of `keisei-aarch64-apple-darwin.tar.gz`
verified OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: macos-13 Intel Mac runners were deprecated by GitHub. The
darwin-x64 bun matrix job was sitting queued for 1.5-2.5 hours on every
tag push (v0.21.1, v0.22.0, v0.22.1) and never picked up a runner. The
release job has `needs: [build-release, build-mcp-binary]` so it waited
forever — NO GitHub Releases were created for any v0.22 tag.
Fix: bun cross-compiles to every target (Linux / macOS / Windows,
x64 / arm64) from any host via `--target=<bun-target>`. Consolidate the
entire build-mcp-binary matrix onto ubuntu-latest. Binaries remain
native per-target (correct Mach-O / ELF / PE format preserved by bun
--target flag).
Side effects:
- Faster: Linux runners provision in seconds vs macOS in minutes
- No macOS quota cost (free tier: 2000 min/month, macOS = 10x multiplier)
- No runner starvation on tag push
- `continue-on-error` arm64-linux carve-out removed (no longer needed —
all jobs now on the same runner pool with equal reliability)
Verified locally: bun 1.1+ supports all 5 target triples from Linux host.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 6 missing pieces flagged by readme review:
1. ## About — author attribution + "not a product" positioning + "Why
Rust, not Python" (was removed in 1f3aaca, but load-bearing context
for new users). Keeps the product-oriented greeting intact above it.
2. ## Install — plugin vs classic two-path table up front; links to
PLUGIN.md (v0.16+ Anthropic plugin format was previously un-linked).
3. ## The nightly cycle at a glance — unified ASCII overview of the
three-phase sleep cycle (A incubation → B REM → C NREM), biological
analog, phase-interaction rules (marathon owns night, Phase C cadence,
morning report is human-only).
4. ### Deep-sleep NREM consolidation — 4-primitive pipeline diagram
(conflict-scan → refactor-engine → graph-check via kei-store) plus a
concrete example of a detected conflict.
5. ## Security model — 7 bullets covering memory-repo privacy, RULE 0.1,
secrets-guard, GitHub Actions SHA pinning, S3 SSRF guard, marker
0o600, exFAT/FAT32 warning. Plus battle-matrix mention.
6. ## Docs — link index to CHANGELOG / PLUGIN / USB-BRAIN guide / battle
tests (none of those four files were referenced from README before).
README.md: +83 lines (1221 → 1304).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge of feat/v0.22-keisei-schema-v4 into main (which already had
Track C's fs_type tests) elided '); }' at 2 seams. Tests compiled
once I added them back, but the edit missed the git-add step.
No behaviour change — both tests already passed after the fix;
commit just closes out the working tree.
+670 LOC to README.md. Source-of-truth extraction from each primitive's
actual src/main.rs (clap subcommands) + hook header comments + skill
SKILL.md frontmatter.
Covers:
- Rust primitives (25) with real subcommands, flags, state paths,
exit codes. Extracted from src/main.rs.
- Shell primitives (13) with usage signature + env-var contract.
- Hooks (10) tabular + per-hook detail (event/severity/bypass).
- Skills (39) grouped into 6 collapsible <details> sections.
- keisei CLI deep-dive — real flag matrix, exit codes, env vars,
SSoT location, v0.19 hardening invariants.
Agent flagged 4 discrepancies vs prior task-description drafts
(agent refused to fabricate: no --client / --force / --dry-run on
keisei, exit codes 0/1 not 0/1/2, kei-curator --db required).
These were never in the code — docs now describe reality.
bash scripts/regen-counts.sh --check: no drift
wc -l README.md: 1233
Closes user request 'сделай подробное описание каждой функции'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: 'поменяй ридми и привествие, убери от автора'.
Replaced 18-paragraph personal preface with a 3-paragraph product
summary:
1. One-line pitch: structural layer over Claude Code + Cursor +
Continue + Zed that catches LLM failure modes at commit time
2. Drop-in toolkit capabilities in one sentence
3. Why-it-exists: LLMs are wired for plausible-but-wrong output;
structure around the prompt is the fix, not better prompts
4. Rust-first constraint: compile-time hallucination elimination
'Why Rust, not Python' collapsed into one paragraph.
'It is not a product' / 'forks and PRs welcome' / 'hope it is a
small Kei' — all removed.
Detailed per-function Reference section coming in separate commit
(agent in flight).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the one outstanding item from v0.21.1 wave-audit: SEC-H1 agent
left SHA256_* vars as 'SKIP' because no WebFetch available this session.
Sources verified via live curl:
https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_checksums.txt
Pinned hashes (4 platforms):
darwin_amd64: 5b44c3bc...c644
darwin_arm64: aba9ced2...953f
linux_amd64: 8aca8db9...a3d8
linux_arm64: 325e971b...f0c6
End-to-end verified locally (darwin_arm64):
HOME=/tmp/aln-test bash scripts/install-actionlint.sh
→ SHA-256 verified: aba9ced2...
→ actionlint -version: 1.7.12 installed by downloading from release page
Header comment updated: [UNVERIFIED] → [VERIFIED 2026-04-22 via curl ...].
ACTIONLINT_SHA256_OVERRIDE env var still works (for CI with different pins).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ship-blocker found by Docker battle-test: install.sh --profile=full
only built 6/25 Rust binaries on fresh ubuntu:24.04 because
kei-artifact has sibling schemas/*.json files that src/schemas.rs
include_str!s at compile time. Prior copy_rust_primitive only copied
Cargo.toml + src/ + tests/ — schemas dir missing → 5 compile errors.
Fix: whitelist 5 common sibling data directories (schemas, assets,
templates, fixtures, migrations) — copy each if present. Keeps
target/, .git/, and other build artifacts out.
Battle-test (pre-fix):
full profile: 6/25 Rust binaries built (kei-artifact + cascade fails)
install exit 0 (soft-fail design), silent partial-install
Expected post-fix:
full profile: 25/25 binaries (pending re-verification on Docker)
Affects: any future crate that uses include_str!('../<dir>/*') on
a sibling data folder. 5-item whitelist covers the patterns we know;
extend as needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three layers of defense against the dtolnay-SHA-class bug reaching main
(today's incident: agent SHA-pinned dtolnay/rust-toolchain with a pin
that was real but semantically wrong — lost 'install current stable'
meaning, locked to rust 1.94.1 branch tip, broke CI).
Layer 1 — actionlint static lint
scripts/install-actionlint.sh (65 LOC) — installs rhysd/actionlint
v1.7.12 [VERIFIED] to ~/.local/bin or suggests brew install.
scripts/lint-workflows.sh (40 LOC) — runs actionlint on
.github/workflows/*.yml, exit 0 on clean, advisory when binary
missing.
Layer 2 — SHA existence check (today's bug class)
scripts/validate-workflow-shas.sh (98 LOC) — extracts every
'uses: <repo>@<40-hex>' from workflow files + dependabot.yml,
checks each via GitHub REST commits API (exit 200/404/422).
Supports 'validate-workflow-shas: skip=<reason>' trailing
comment for intentional exceptions. Falls back to anonymous
API (60/hr quota) if GITHUB_TOKEN probe fails.
DESIGN PIVOT from spec: spec said 'git ls-remote <repo> <sha>'
but that only resolves REFS (branch/tag tips), not arbitrary
commit SHAs — would have given false-positive 100% MISSING
report. Switched to REST API /commits/{sha} for unambiguous
200/404/422.
Layer 3 — CI gate
.github/workflows/ci.yml — new 'workflow-lint' job after
shell-lint. Installs actionlint + runs both scripts on every
push to main and PR. Blocks CI on any fabricated SHA.
Layer 4 — optional pre-commit hook
scripts/pre-commit-workflow-lint.sh (54 LOC) — detects staged
.github/workflows/*.{yml,yaml} + .github/dependabot.yml
changes, runs layers 1+2, blocks commit on failure.
Install via: ln -sf ../../scripts/pre-commit-workflow-lint.sh
.git/hooks/pre-commit
REAL EXECUTION VERIFIED (not claim-only):
- actionlint ran: zero findings on current workflows
- validate-workflow-shas.sh ran: 21 SHA pins checked, 21 OK,
0 MISSING (confirms all current v0.19.1+ pins resolve)
- bash -n on every new script: clean
- bash-3.2 parser bug workaround: case-in-subshell → grep -E
RULE 0.2 exception #6 (shell is external convention for git hooks
+ GH Actions runs — Rust rewrite would add zero value).
RULE 0.13 respected — no git invocations except read-only API calls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same fix as ci.yml in f833a36 applied to release.yml — two more
occurrences of the SHA-pinned toolchain that locks to rust 1.94.1
branch tip.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TWO CI failures on v0.19.1 SHA-pin commit cb45a27 traced to:
1. dtolnay/rust-toolchain SHA pin accidentally locked to rust 1.94.1
branch tip, not the stable-latest behaviour.
Validator V-2026-04-22 confirmed the pinned SHA (3c5f7ea) points at
the branch tip that added 1.94.1 patch support — functionally
equivalent to pinning a specific Rust version, not 'install stable'.
Runner image may have had newer / incompatible stable installed
system-wide; mixing caused cargo test failures.
Revert to @stable tag. Documented as explicit exception to RULE H5
(SHA-pin everything) in the line comment — dtolnay is a trusted
maintainer (serde/anyhow/cxx author), @stable is the canonical
semantic pointer for this action.
2. shell-lint job exit 1 despite continue-on-error: true on the
shellcheck step. The flag doesn't always suppress the step-level
exit code in GH Actions annotation stream when the step is the
LAST meaningful step. Add explicit '|| echo warnings' suffix to
guarantee the step exits 0 even on shellcheck findings.
Expected outcome: 3 Rust jobs + shell-lint green on next push.
ts-packages already green (they use actions/setup-node@<sha> which
resolves cleanly to v4.4.0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes 2 architect audit P3 findings. MVP on the USB-droppable brain
vision — one brain directory now serves every platform.
Schema v2 — per-platform mcp_server dispatch:
[paths.mcp_server]
darwin-arm64 = 'bin/kei-mcp-server-darwin-arm64'
darwin-x64 = 'bin/kei-mcp-server-darwin-x64'
linux-x64 = 'bin/kei-mcp-server-linux-x64'
linux-arm64 = 'bin/kei-mcp-server-linux-arm64'
windows-x64 = 'bin/kei-mcp-server-windows-x64.exe'
Schema v1 (single string) still accepted — v0.19 brains load unchanged.
Implementation:
brain.rs — new McpServerPath enum (Single / PerPlatform BTreeMap<String, String>)
with #[serde(untagged)]. Brain::current_platform_key() maps std::env::consts
(macos→darwin, x86_64→x64, aarch64→arm64) to canonical key format.
mcp_server_path() now returns Result — looks up current platform,
returns Error::NoPlatformBinary { os, arch, available } if missing.
Pre-canonicalized cache field removed so partial v2 brains load for
status (just fail at actual resolve).
brain_validate.rs — validate_schema accepts MIN..=MAX range (1 or 2);
check_all_paths iterates v2 map entries for confinement check.
ClientAdapter::post_attach_hint() — default method + 4 overrides:
claude_code: 'run /help in Claude Code to verify the MCP server is reachable'
cursor: 'reload Cursor window (Cmd+Shift+P → Reload Window) to pick up the MCP server'
continue_adapter: 'reload the Continue extension in VS Code (or restart) to pick up the MCP server'
zed: 'run Zed :reload command to pick up the MCP server config'
attach.rs prints adapter.post_attach_hint() instead of the hardcoded
Claude-Code-specific string. No more client leak in orchestrator.
Error::NoPlatformBinary { os, arch, available } with thiserror Display.
Tests: 16 existing + 4 new = 20/20 pass.
- schema_v2_current_platform_resolves
- schema_v2_missing_current_platform_errors (macOS-gated)
- schema_v1_still_readable_with_v2_code
- post_attach_hint_is_adapter_specific
Constructor Pattern: all files <200 LOC (continue_adapter.rs 197 LOC
max). All fns <30 LOC (current_platform_key + check_all_paths 19 LOC max).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bun is a monorepo tool — lockfile lives at workspace root
(_ts_packages/bun.lock), not per-subpackage. Placeholder at
_ts_packages/packages/mcp-server/bun.lock was the wrong path.
Changes:
- Generated real _ts_packages/bun.lock (626 lines) via 'bun install'
(bun 1.3.13, auto-migrated from package-lock.json)
- .github/workflows/release.yml working-directory:
_ts_packages/packages/mcp-server → _ts_packages (workspace root)
- BUILD.md Lockfile section rewritten to document workspace-root
location + coexistence with package-lock.json (L2 audit finding
partially resolved — full consolidation deferred to v0.20)
release.yml build-mcp-binary job now has real lockfile to consume —
H4 'tag build fails on missing lockfile' gate still active but now
there's something actually committed to satisfy it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prior README went from personal preface ('From the author') directly
into technical inventory ('What it is'). Reader gap: no value
proposition, no capability summary, no exobrain workflow docs.
Added:
- 'At a glance' — 6-bullet capability summary (agents, hooks,
skills, Rust primitives, exobrain, sleep-sync) written so a
stranger arriving from search gets the model in 30 seconds
- 'What it solves' — 7-row table mapping vanilla-Claude-Code pain
points → specific kit component that addresses each
- 'The keisei CLI' — new subsection documenting attach/mount/
detach/list-adapters/status subcommands with brain-directory
layout, 4 use cases (USB travel / team personas / cloud brain /
experimental isolation), security hardening summary, deferred
v0.19.1 items
Counts markers unchanged — no regen needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 adapter conflicts resolved by taking fix-wave version — preserves
security hardening (path confinement, name validation + collision
refuse, paths::resolve_home SSoT, fsx::write_atomic_json via
NamedTempFile). jsonmcp.rs from v0.19 refactor commit fa253d0
became dead code post-merge; deleted + unregistered from adapters/mod.rs.
If future iteration wants jsonmcp-style shared merge, it should
layer on top of the security-hardened adapter inline logic, not
replace it.
Tests: 16/16 pass (11 pre-existing + 5 new adversarial).
cargo check -p keisei: clean (4 pre-existing dead-code warnings,
not introduced by this merge).
v0.19 agent's additional factorization that wasn't captured in the
initial branch commit. Extracts shared merge/remove-named helpers
for claude-code/cursor/zed into adapters/jsonmcp.rs (70 LOC). 3
adapters simplify significantly (-65/-68/-102 LOC each).
Also: #[allow(dead_code)] on Error::AdapterFailed (surfaced by
mount/detach orchestration; reserved for library consumers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>