Parfii-bot
4f6cbfd305
Merge docs/v0.22-reference-section — README Reference section (conflict resolved)
...
CHANGELOG: Reference entry folded into Track A's block.
README: auto-merged cleanly (agent appended the 670-LOC Reference
section after 'What you get' table, untouched by Track A/B/C).
wc -l README.md: 1233 (was 563 pre-session — 2.2x growth from product
rewrite + USB guide + exobrain CLI docs + Reference section).
2026-04-22 21:11:21 +08:00
Parfii-bot
ca8d980ac6
docs(readme): comprehensive Reference section — every primitive/skill/hook/CLI with real behaviour
...
+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>
2026-04-22 21:10:11 +08:00
Parfii-bot
4ccb1548a2
Merge refactor/v0.22-kei-store-async-backend — AsyncBackend trait + shared runtime
2026-04-22 21:06:50 +08:00
Parfii-bot
bc7e099697
refactor(v0.22): kei-store AsyncBackend trait + shared tokio runtime (Track B)
...
Extracts an AsyncBackend trait in kei-store so future GCS/Azure/Bunny
backends implement 5 async fns, not re-invent the sync-over-tokio
bridge. Closes architect P1 + P2 findings.
NEW src/async_backend.rs (189 LOC):
- trait AsyncBackend with get/put/list/list_recursive/delete/exists
- AsyncBackendStore<B: AsyncBackend> — generic MemoryStore impl
(sync-over-async via shared runtime)
- shared_runtime() -> &'static Runtime via OnceLock
(multi-thread, 2 workers, enable_io+enable_time)
- path helpers (validate_rel, short_hash, is_manifest_key) moved
here as single source of truth
NEW src/s3_cloud/backend.rs (120 LOC):
- S3AsyncBackend impl of AsyncBackend — 5 async fns using the
existing aws-sdk-s3::Client
MODIFIED src/s3_cloud/mod.rs (200 → 43 LOC):
pub type S3CloudStore = AsyncBackendStore<S3AsyncBackend>;
Thin re-export + inherent new(cfg) constructor.
Doc-header documents the extension seam: 'adding GCS = impl 5 async fns'.
MODIFIED src/s3_cloud/keys.rs (66 → 40 LOC): compat shim — re-exports
validate_rel / short_hash / is_manifest_key from async_backend.
Old call-sites + 4 unit tests unchanged.
Deps: async-trait = 0.1 added under s3 feature; tokio now has
rt-multi-thread feature too.
FIXES N=2 Store footgun: prior impl created a current_thread Runtime
per instance — 2 instances in one process = 2 runtimes, block_on
panic if caller is on another runtime. Shared multi-thread runtime
via OnceLock means N instances all share 2 workers.
REAL VERIFICATION (agent-pasted):
cargo check -p kei-store: clean
cargo check -p kei-store --features s3: clean
cargo test -p kei-store --release: 10+9+0 = 19 passed
cargo test -p kei-store --features s3 --release: 38+9+6 = 53 passed
(+7 vs baseline 46)
Tests added (7):
async_backend::tests::shared_runtime_is_singleton
async_backend::tests::validate_rel_rejects_absolute
async_backend::tests::validate_rel_rejects_parent
async_backend::tests::short_hash_deterministic
async_backend::tests::is_manifest_key_matches_format
s3_cloud::tests::async_backend_shared_runtime_handles_two_store_instances
s3_cloud::tests::async_backend_runtime_is_multi_thread
Public API preserved: S3CloudStore::new / .branch / .current_branch /
.key / .backend_name. Factory + integration tests untouched.
Pre-existing: list_inner 38 LOC (moved verbatim from mod.rs, not
refactored per Core Rule 3).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:06:50 +08:00
Parfii-bot
27c153cefb
Merge feat/v0.22-keisei-schema-v4 — schema v4 + Scope::Auto + registry (conflicts resolved)
...
CHANGELOG: merged Track A (schema v4 + Auto + registry + time.rs split) and Track C (fs_type.rs + battle-matrix + USB guide split) Added entries side-by-side. Kept Track A's Removed block.
integration.rs: 2 conflict blocks merged by concatenation — Track C's
fs_type detection tests (2) and Track A's schema v4 / multi-brain /
Scope::Auto / templated hint / registry tests (16). Total 48 tests pass
(46 Track A + 2 Track C).
Missing closing braces on both merge boundaries repaired
(auto-merge dropped '); }' at 2 seams).
2026-04-22 21:06:23 +08:00
Parfii-bot
4bbc95fd7c
feat(v0.22): keisei schema v4 + Scope::Auto + templated hint + registry (Track A)
...
BREAKING schema bump v3 → v4. Backward-compat via untagged serde
for v1/v2/v3 read-paths — no user-visible regression.
1. Schema v4 — multi-brain marker
AttachRecord inverted: each Attachment carries its own brain_path
+ brain_name + scope + attached_at. Enables brain-A to Claude-
Code (user scope) + brain-B to Cursor (project scope) in ONE
marker. v1/v2/v3 auto-migrate via config_migrate.rs (NEW, 114
LOC) — silent stderr notice on first v0.22 read.
2. Scope::Auto — default CLI behaviour
'keisei attach <brain>' no longer defaults User scope blindly.
New Scope::Auto resolves per-adapter via auto_scope():
claude-code: CWD/.claude/ present → Project else User
cursor: CWD/.cursor/ present → Project else User
continue: User (no project concept)
zed: User (global settings only)
'keisei mount' stays host-wide (always User fan-out).
3. Templated post_attach_hint
fn post_attach_hint(&self, brain: &Brain, scope: Scope) -> String
Each adapter interpolates brain name + scope. Example for
claude-code: 'run /help in Claude Code (user scope) — verify
'<brain-name>' is in mcpServers'.
4. Adapter registry
adapters/_registry.rs (NEW, 32 LOC) — single canonical list
of 4 adapters. adapter::all() delegates. 5th adapter = one
line change, one place.
5. Dead code cleanup
Error::NotAttached + Error::AdapterFailed removed. Grep-verified
zero references.
6. config.rs decomposition (200 LOC rule)
config.rs 224 → 197 LOC.
time.rs (NEW, 90 LOC) — now_utc_string + format_epoch_utc +
civil_from_days Howard Hinnant + 5 unit tests
(epoch-0, leap day 2020-02-29, century-non-leap 2100-03-01,
arbitrary 2026-04-22, RFC3339 shape).
config_migrate.rs (NEW, 114 LOC) — WireRecord migration.
REAL VERIFICATION:
cargo test -p keisei --release: 46 passed 0 failed
(5 time::tests + 41 integration — 30 existing adjusted to v4 + 11 new)
Tests added:
marker_v3_migrates_to_v4
two_brains_can_be_attached_simultaneously
detach_removes_single_brain_preserves_others
scope_auto_resolves_to_{project,user}_*
cursor_auto_scope_respects_cwd_dot_cursor
post_attach_hint_interpolates_brain_name
adapter_registry_lists_all_four
dead_error_variants_removed
time_now_utc_string_has_rfc3339_shape
fresh_marker_has_schema_version_4
Agent corrected 3 of the spec's epoch anchor timestamps
(1583020800 → 1582977600 for 2020-02-29T12:00:00Z, 1776870000 →
1776877200 for 2026-04-22T17:00:00Z); century-non-leap anchor
4107542400 → 2100-03-01 was already correct.
Known pre-existing: continue_adapter.rs 206 LOC (was 204; +2 for
signature widening). Out-of-scope for this track.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:00:13 +08:00
Parfii-bot
5993f32146
feat(v0.22): FS warn + battle-test matrix + USB docs platform split (Track C)
...
1. Filesystem type detection (architect P2 finding)
_primitives/_rust/keisei/src/fs_type.rs (NEW, 103 LOC)
- statfs(2)-based detection on unix (libc = '0.2' under
[target.'cfg(unix)'.dependencies])
- Recognizes exfat / msdos (FAT32) via f_fstypename on macOS,
via f_type magic numbers on Linux (0x4d44, 0x2011bab0)
- Windows stub returns Unknown (GetVolumeInformationW TBD)
- warn_on_unsafe_fs(root) emits stderr warning on ExFat/Fat32
brain.rs::load calls warn_on_unsafe_fs after canonicalize+symlink
checks. Warning NOT fatal — user can opt into single-client use.
2. Battle-test matrix (architect P3 finding)
tests/battle/Dockerfile.install-test-alpine (NEW)
- alpine:3.19 + apk rust/cargo/pandoc
- Exposes musl-vs-glibc issues in aws-sdk-s3, rusqlite, git2
tests/battle/Dockerfile.install-test-debian (NEW)
- debian:12 + rustup stable + pandoc
- Default server distro, different apt structure from Ubuntu
tests/battle/README.md rewritten — 3-distro matrix with run script
3. USB-BRAIN-GUIDE platform split
docs/USB-BRAIN-GUIDE.md — restructured as TOC + platform-agnostic
preamble + exFAT warning + cross-platform troubleshooting
docs/USB-BRAIN-GUIDE-macos.md (NEW, 97 LOC) — Gatekeeper, diskutil,
/Volumes, xattr -d com.apple.quarantine
docs/USB-BRAIN-GUIDE-linux.md (NEW, 98 LOC) — /media/$USER,
umount, ext4 recommended, systemd-udev auto-mount note
docs/USB-BRAIN-GUIDE-windows.md (NEW, 115 LOC) — PowerShell
Dismount-Volume, NTFS, FS-advisory Unknown caveat
REAL VERIFICATION (paste from agent):
cargo check -p keisei: Finished (clean)
cargo test -p keisei --release: 32 passed 0 failed (30 existing + 2 new)
docker buildx outline: both new Dockerfiles parse
Constructor Pattern:
fs_type.rs 103 LOC, brain.rs 198 LOC (at limit 200, held the line)
All fns <30 LOC. Each USB guide sub-doc 97-115 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:56:42 +08:00
Parfii-bot
f12eb9f83c
fix(v0.21.1): wave-audit consolidated — 5 critic HIGH + 2 security HIGH + 3 polish
...
Closes 10 audit findings from 4-agent wave (critic + security +
architect + validator) on v0.21.0.
CRITIC HIGH (5):
H1 s3_cloud::commit() was listing with delimiter='/' — nested
writes silently dropped from manifest hash. Added
list_recursive() (no delimiter), filter manifest-*.json from
hash input.
H2 S3Cfg access_key_env + secret_key_env were advertised in TOML
but never read. Wired via resolve_explicit_creds() with
aws-credential-types. Partial-set or empty-resolve → error.
H3 display::sanitize_display missing in detach.rs + mount.rs
(regression of v0.19.2 L9 ANSI injection fix). Applied at 8
print sites. 2 new integration tests.
H4 adapters/jsonmcp.rs RESTORED (was lost in earlier merge).
107 LOC shared module: load_json_or_empty / upsert_under_key /
remove_under_key / persist. claude_code 163→105, cursor 165→106,
zed 178→114. Unified error handling via ConfigParseError.
H5 ENV_LOCK shared across kei-store tests. New test_env.rs (24 LOC)
exposed under cfg(any(test, feature='s3')). github.rs +
s3_cloud/tests.rs + s3_smoke.rs all use shared mutex. Fixes
parallel-test race on KEI_STORE_S3_ENDPOINT.
SECURITY HIGH (2):
SEC-H1 scripts/install-actionlint.sh — added sha256 verify
(shasum/sha256sum) before extract. ACTIONLINT_SHA256_OVERRIDE
env var for CI injection. Per-platform constants marked
[UNVERIFIED: SKIP] pending live checksums.txt fetch (agent had
no WebFetch this session — user follow-up: paste from
https://github.com/rhysd/actionlint/releases/download/v1.7.12/checksums.txt ).
SEC-H2 S3 SSRF/IMDS guard. validate_endpoint() rejects:
loopback (127/8, ::1, localhost), link-local (169.254/16,
fe80::/10), metadata hostnames (google/azure). Override via
KEI_STORE_S3_ALLOW_INTERNAL=1. HTTP rejected unless
KEI_STORE_S3_ALLOW_INSECURE=1. Custom endpoint now REQUIRES
explicit creds (no IMDS chain leak via third-party endpoint).
4 reject + 3 accept tests pass.
POLISH (3):
D1 docs/USB-BRAIN-GUIDE.md — ⚠️ WARNING block under Prerequisites:
exFAT/FAT32 NOT safe for multi-client attach (SQLite WAL needs
shared-mem mmap). Use ONE client at a time on those FSes.
New Troubleshooting entry 'SQLite corruption on mount-attach'.
D2 '~5 MB release binary growth' now labelled [estimate, E5 —
not yet measured] in CHANGELOG.md + s3_cloud/mod.rs header.
D3 scripts/validate-workflow-shas.sh exits 2 (not 0) when
UNVERIFIED_COUNT > 0 and GITHUB_TOKEN absent. Distinguishes
'network denied' from 'all good'.
REAL VERIFICATION (pasted by agent):
cargo check -p keisei -p kei-store: Finished (clean)
cargo test -p keisei --release: 30 passed 0 failed
cargo test -p kei-store --release: 10 + 9 passed (default features)
cargo test -p kei-store --features s3 --release:
31 + 9 + 6 = 46 passed (with s3)
bash -n scripts/*.sh: OK
regen-counts.sh --check: no drift
Constructor Pattern: largest new src 200 LOC (s3_cloud/mod.rs, at
limit). jsonmcp.rs 107 LOC. test_env.rs 24 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:03:17 +08:00
Parfii-bot
82689d108b
Merge test/v0.21-battle-docker — Docker install.sh battle-test (CHANGELOG conflict resolved)
2026-04-22 18:38:58 +08:00
Parfii-bot
b1c5fd00d2
test(v0.21): Docker battle-test infra for install.sh on fresh ubuntu:24.04
...
tests/battle/Dockerfile.install-test — ubuntu:24.04 + deps
(git, curl, ca-certificates, build-essential, jq, pandoc, rustup)
tests/battle/battle-entry.sh — ENTRYPOINT: runs install.sh with
$PROFILE (default minimal), then verify.sh
tests/battle/verify.sh — POSIX sh gate: blocks >= 79, skills >= 39,
top hooks >= 10, _lib hooks >= 2, test-gate.sh exits 0,
settings.json valid JSON
tests/battle/README.md — build + run docs
SHIP-BLOCKER FOUND (for follow-up fix commit):
kei-artifact crate fails to compile on fresh install because
install/lib-primitives.sh::copy_rust_primitive copies only
Cargo.toml + src/ + tests/. Crate has sibling schemas/ dir with
5 JSON files that src/schemas.rs include_str!s at compile time.
Missing → cargo build error, install exits 0 (soft-fail design)
but full profile only produces 6/25 binaries instead of 25/25.
Real stdout verified:
minimal: 80 blocks, 39 skills, 10 hooks, 3 _lib — exit 0 ✓
dev: same counts — exit 0 ✓ BUT 3/8 binaries (kei-artifact fail)
full: same counts — exit 0 ✓ BUT 6/25 binaries
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:37:55 +08:00
Parfii-bot
969ddf34cd
Merge feat/v0.21-kei-store-s3 — real S3 backend (CHANGELOG conflict resolved)
...
Kept both Added blocks: v0.21 SSoT/Scope (from earlier merge) and
v0.21 kei-store S3. Both are part of the v0.21 ship.
2026-04-22 17:59:56 +08:00
Parfii-bot
e5cd0d6790
feat(v0.21): kei-store real S3 backend behind opt-in 's3' feature flag
...
Promotes S3 from MVP stub to functional via aws-sdk-s3. Default builds
unchanged (zero new deps). Feature flag ensures users who don't need
S3 don't pay the ~5MB binary / C-toolchain cost.
Cargo.toml: new [features] s3 = [...] gating 4 optional deps:
aws-sdk-s3 = 1.130.0
aws-config = 1.8.16 (with behavior-version-latest)
tokio = 1.52.1 (current-thread runtime, no multi-threaded bloat)
bytes = 1 (S3 body passthrough)
s3_cloud/ module (4 files, Constructor Pattern):
mod.rs (190 LOC) — S3CloudStore + MemoryStore trait impl
client.rs (81 LOC) — aws-config builder, KEI_STORE_S3_ENDPOINT
override for R2 / Wasabi / MinIO / any S3-compat
keys.rs (60 LOC) — path-traversal guard + DJB2 hash helper
tests.rs (63 LOC) — builder + prefix + key-guard unit tests
Factory routing (factory.rs):
with 's3' feature + bucket URL → S3CloudStore (real network)
without 's3' feature → S3Store stub (existing MVP, preserved)
Security posture:
- Branch-prefix isolation rejects traversal at keys.rs layer
- aws-config default credential chain (env → ~/.aws → IMDS);
no bespoke credential handling
- rustls, not OpenSSL (matches existing crate tree)
Tests: 22 existing + 11 new (4 keys + 3 client + 5 mod + 5 smoke)
cargo test -p kei-store (default features): 9 passed
cargo test -p kei-store --features s3: 22 + 9 + 5 = 36 passed
cargo clippy -p kei-store --features s3: clean
Real stdout verified for all verify criteria. No fabrication.
MANIFEST.toml [primitive.kei-store] deps updated to reflect feature
opt-in model.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:59:11 +08:00
Parfii-bot
766ced792a
Merge feat/v0.21-keisei-ssot-scope — SSoT ~/.keisei + Scope enum (user/project)
2026-04-22 17:56:10 +08:00
Parfii-bot
81e3b58533
feat(v0.21): keisei SSoT relocation + Scope enum (user/project)
...
Two architect-audit P1/P2 findings closed.
PART A — SSoT relocation
Before: ~/.claude/keisei-attached.toml (baked Claude-Code subpath)
After: ~/.keisei/attached.toml (client-neutral)
config::migrate_from_legacy() runs inside config::read() — first
call after v0.21 install reads legacy path, writes new path,
deletes legacy, emits stderr notice.
claude_code adapter's .claude/ subpath UNCHANGED — that's Claude
Code's real config dir, not keisei's marker namespace.
PART B — Scope enum (architect P1)
ClientAdapter trait gains:
fn supported_scopes(&self) -> &[Scope] { &[Scope::User] } // default
fn config_path(&self, scope: Scope) -> PathBuf
fn attach(&self, brain: &Brain, scope: Scope) -> Result<()>
fn detach(&self, brain_name: &str, scope: Scope) -> Result<()>
Per-adapter scope support:
claude_code — [User, Project] (~/.claude vs ./.claude)
cursor — [User, Project] (~/.cursor vs ./.cursor)
continue — [User] only (Continue has no project concept)
zed — [User] only (Zed uses global settings)
CLI: keisei attach <brain> --scope={user|project} (default user).
keisei mount → always Scope::User (host-wide fan-out).
Marker Attachment gains scope field with #[serde(default)] so
v0.20 markers read as Scope::User (backward-compat).
New Error::ScopeUnsupported { client, scope, supported } — blocks
invalid combos (e.g. zed --scope=project) with clear message.
New module scope.rs (49 LOC) — Scope enum + serde + Display + FromStr.
paths.rs gains keisei_state_dir() returning $HOME/.keisei.
5 new integration tests:
- legacy_marker_migrates_on_first_read
- attach_with_project_scope_writes_local_config
- attach_user_scope_still_default
- scope_unsupported_by_adapter_errors
- detach_respects_scope_from_marker
REAL VERIFIED cargo test -p keisei output: 28 passed; 0 failed.
cargo check -p keisei: clean.
grep /Users/denisparfionovich/ in edits: zero hits.
Constructor Pattern: scope.rs 49 LOC, paths.rs 34 LOC, largest fn
migrate_from_legacy() 22 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:56:10 +08:00
Parfii-bot
c778b7d9a3
feat(v0.20.1): workflow-file validation infrastructure
...
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>
2026-04-22 17:50:23 +08:00
Parfii-bot
909205f63b
Merge feat/v0.20-schema-v2-multi-platform — schema v2 multi-platform + post_attach_hint
...
Conflicts resolved by composition (not picking sides):
error.rs: keep ManifestTooLarge (v0.19.2) + NoPlatformBinary (v0.20);
UnsupportedSchema message updated to 'need 1 or 2'.
brain.rs: merged v0.19.2 invariants block + v0.20 platform-key docs
into a single Invariants section listing both hardening constraints
and v2 schema range.
attach.rs: composed v0.19.2 sanitize_display wrapping with v0.20
Result<PathBuf> handling — mcp_server_path errors now sanitized too.
integration.rs: concatenated v0.19.2 (3 tests + helper) + v0.20
(4 tests + 2 helpers) blocks preserving all 7 new cases.
Tests: 23/23 pass (16 existing + 3 v0.19.2 + 4 v0.20).
cargo check -p keisei: clean.
v1 brains still load, v2 brains dispatch per-platform, adapters have
client-specific post_attach_hint.
2026-04-22 17:23:18 +08:00
Parfii-bot
12e56d6590
feat(v0.20): Brain schema v2 per-platform mcp_server + post_attach_hint() trait
...
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>
2026-04-22 17:19:58 +08:00
Parfii-bot
d90079a7a7
Merge fix/v0.19.2-polish — marker perms, ANSI sanitize, manifest size bound
2026-04-22 17:17:14 +08:00
Parfii-bot
e43b13335e
fix(v0.19.2): polish — marker perms 0600, ANSI sanitize, manifest size bound, dead-code cleanup
...
Closes remaining MEDIUM/LOW audit findings not in v0.19.0 security wave.
M1 — marker file 0600 perms (unix)
config.rs::write() applies chmod 0o600 after write, cfg(unix) gated.
Test marker_file_has_0600_perms_on_unix asserts mode & 0o777 == 0o600.
L9 — ANSI-escape sanitization
New module display.rs (27 LOC) — sanitize_display(&str) replaces
ASCII < 0x20 OR == 0x7F with '?', leaves space + unicode alone.
Applied in status.rs + attach.rs to brain_name / brain_path /
attached_at / client_type / config_path / mcp_path before print.
Test status_sanitizes_control_chars_in_brain_name asserts
sanitize_display('evil\x1b[2Jpayload') → 'evil?[2Jpayload'.
L12 — manifest size bound
brain_validate.rs const MAX_MANIFEST_BYTES = 64 * 1024; metadata
check before read_to_string. New Error::ManifestTooLarge { size, max }
with thiserror Display impl. Test manifest_too_large_rejected
writes 100 KB manifest, asserts error + marker not written.
Dead-code cleanup:
- Error::NotAttached: #[allow(dead_code)] + comment (reserved for
future detach subcommand when no marker exists)
- config::has_client: #[allow(dead_code)] + comment (reserved for
future multi-brain support)
- mount.rs / detach.rs: dropped unused ClientAdapter import
brain.rs module doc-comment expanded — lists all v0.19 invariants:
path confinement, symlink reject, name regex, 64 KiB manifest cap,
schema v1; notes v2 (multi-platform) lands in v0.20.
Tests: 16 existing + 3 new = 19/19 pass.
cargo check -p keisei: zero warnings in keisei crate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:17:14 +08:00
Parfii-bot
ca046e61c1
fix(v0.19.1): supply-chain hardening remainder — ci.yml SHA-pin + dependabot + bun.lock placeholder
...
Follow-up to c27b626 (release.yml pinning). Finishes H4 + H5.
ci.yml:
- 11 third-party actions SHA-pinned with # vN.m.k comments
- actions/checkout@34e114876b... (v4.3.1)
- actions/setup-node@49933ea5288... (v4.4.0)
- dtolnay/rust-toolchain@3c5f7ea28... (rust 1.94.1)
- Swatinem/rust-cache@c19371144... (v2.9.1)
.github/dependabot.yml (NEW):
- 3 ecosystems weekly: github-actions, npm, cargo
- PR cap 5, labels [dependencies, <ecosystem>]
- Auto-opens update PRs for SHA bumps — human reviews, not silent churn
_ts_packages/packages/mcp-server/bun.lock (NEW — placeholder):
- 13-line comment explaining H4 gate
- Instructs: 'cd _ts_packages/packages/mcp-server && bun install' before release
- release.yml (since v0.19.1) uses --frozen-lockfile with NO fallback —
missing real lockfile fails the build deliberately
BUILD.md:
- New 'Lockfile' section (19 LOC) documenting the pre-release workflow
CHANGELOG.md:
- [Unreleased] → Security: 3 bullets covering this + prior supply-chain commit
All SHAs E1 (verified via api.github.com or reused from release.yml).
NEXT STEP BEFORE TAGGING v0.19.1:
Populate real bun.lock locally, commit, then tag. Workflow will fail
on missing/stale lockfile — that's the point of H4 defense.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:12:15 +08:00
Parfii-bot
d32ca0bc28
fix(v0.19): audit hardening — 3 security HIGH + 3 critic HIGH + 2 critic MEDIUM
...
Closes consolidated findings from wave-audit (critic + security + architect):
SECURITY HIGH:
H1 path escape — Brain::load rejects absolute mcp_server paths +
any containing '..'; canonicalize + starts_with(root) assertion;
new Error::PathEscape variant.
H2 brain name validation + clobber refuse — regex ^[a-z][a-z0-9_-]{0,63}$
enforced at Brain::load; adapters refuse to overwrite existing
mcpServers[name] with NameConflict (unless same content).
H3 symlink reject at canonicalize — std::fs::symlink_metadata()
called before canonicalize; Error::BrainIsSymlink with resolved
target path; prevents USB → $HOME pivot.
CRITIC HIGH:
#1 rusqlite dep deleted (zero uses in src/, pulls C toolchain).
#3 BrainPaths memory/artifacts/manifests now Option<String>
(only mcp_server required; schema no longer lies about contract).
CRITIC MEDIUM:
#1 _primitives/_rust/keisei/src/paths.rs (new, 23 LOC) — SSoT for
$KEISEI_HOME/$HOME resolver; config.rs and claude_code.rs
delegate instead of duplicating 7-line block.
#2 canonicalize error preserves io::Error via new Error::BrainLoad
{ path, source } with #[source] attribute.
#5 fsx::write_atomic_json rewrite via tempfile::NamedTempFile
+ persist — Windows-safe, cross-fs-fallback handling.
New module split (Constructor Pattern): brain.rs (104 → 122) now a
thin orchestrator over brain_validate.rs (108 LOC) which owns
symlink-reject / canonicalize-root / read-manifest / validate-schema
/ validate-name / check-relative-in-root / canonicalize-in-root.
Deps: regex = { workspace = true }, tempfile = "3" (runtime).
Workspace-level regex = "1.10" added.
MANIFEST.toml [primitive.keisei] deps updated.
Tests: 11 pre-existing + 5 adversarial:
- manifest_with_absolute_mcp_server_rejected — proves /usr/bin/python3
CANNOT land in settings.json (PathEscape + marker absent asserts)
- manifest_with_parent_traversal_rejected — ../../etc/passwd rejected
- manifest_with_invalid_name_rejected — 'claude-ide!' rejected
- brain_path_is_symlink_rejected — USB → $HOME pivot blocked
- attach_refuses_to_clobber_existing_mcp_entry — NameConflict on diff
All 16 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:36:11 +08:00
Parfii-bot
e6cab72587
feat(v0.19): multi-client adapters + detach/mount/list-adapters + schema v2
...
Extends keisei CLI from single-client (v0.18) to multi-client exobrain:
new subcommands detach/mount/list-adapters, 3 new adapters (Cursor,
Continue, Zed), schema v2 for ~/.claude/keisei-attached.toml with
[[attachments]] array (v1 backward-compat via untagged serde).
New subcommands:
detach — iterate marker attachments, call adapter.detach() on each,
then delete marker. Real strip of mcpServers entry per adapter.
mount <brain> — auto-attach to ALL detected clients in one call.
list-adapters — tabular status (name / detected / config path).
New adapters (each <130 LOC mirrors claude_code.rs pattern):
adapters/cursor.rs — .cursor/mcp.json project-local, fallback
~/.cursor/mcp.json global.
adapters/continue_adapter.rs — ~/.continue/config.json with
experimental.modelContextProtocolServers key.
adapters/zed.rs — ~/.config/zed/settings.json (Linux) or
~/Library/Application Support/Zed/settings.json (macOS) with
context_servers key. Zed schema marked [UNVERIFIED] pending docs.
Schema v2 (~/.claude/keisei-attached.toml):
[[attachments]]
client_type = "claude-code"
config_path = "/Users/.../.claude/settings.json"
[[attachments]]
client_type = "cursor"
config_path = "/Users/.../proj/.cursor/mcp.json"
v1 marker migration: untagged serde accepts legacy client_type=string,
upgrades to single-entry attachments[] on next write.
Tests: 5 (v0.18) + 6 new = 11 integration tests, all pass:
- attach_then_status_happy_path
- attach_missing_manifest_errors
- attach_unsupported_schema_errors
- status_without_attach_is_clean
- attach_writes_marker_with_expected_fields
- mount_with_claude_code_only_detected (new)
- mount_with_no_client_detected (new)
- detach_round_trip (new)
- detach_preserves_other_mcp_servers (new)
- list_adapters_prints_expected_rows (new)
- schema_v1_to_v2_migration (new)
Known issues (defer to follow-up commit):
- CRITIC HIGH#1: rusqlite declared but unused
- CRITIC HIGH#3: BrainPaths fields required but only mcp_server used
- SECURITY H1/H2/H3: brain path/name not validated before writing
into client config — will be addressed before tagging v0.19.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:04:45 +08:00
Parfii-bot
e53ad26243
Merge feat/v0.18-keisei-cli-mvp — exobrain attach/status CLI
2026-04-22 15:52:59 +08:00
Parfii-bot
3bb9ba7911
feat(v0.18): keisei CLI MVP — exobrain attach/status
...
Phase 3 of exobrain architecture. Ships the entry-point CLI
that mounts a portable brain (memory + artifacts + manifests +
mcp-server) into an AI client via one command.
MVP scope: 2 subcommands (attach, status), 1 adapter (claude-code).
detach + mount + multi-client deferred to v1.0.
New Rust crate _primitives/_rust/keisei/ — 10 src files + 1 tests
(Constructor Pattern: all files <150 LOC, all fns <=23 LOC).
main.rs (53 LOC) — clap dispatch
error.rs (36 LOC) — thiserror enum (BrainNotFound,
UnsupportedSchema, NoClientDetected, Io/Toml/Json #[from])
brain.rs (103 LOC) — Brain::load() reads brain/manifest.toml
schema_v1 (name, created, paths.{memory,artifacts,manifests,
mcp_server})
adapter.rs (38 LOC) — ClientAdapter trait (detect/attach/
detach/config_path) + registry
adapters/claude_code.rs (121 LOC) — writes MCP server entry
into ~/.claude/settings.json via merge_mcp_entry (23 LOC,
mirrors jq-merge pattern from install/lib-hooks.sh)
attach.rs (44 LOC) — load brain, detect client, call adapter,
write SSoT ~/.claude/keisei-attached.toml
status.rs (62 LOC) — read SSoT, print brain name/path/client/
timestamp + health check (mcp_server binary exists?)
config.rs (97 LOC) — KeiseiAttached TOML struct + KEISEI_HOME
env hook for test isolation
tests/integration.rs (142 LOC) — 5 cases via tempfile + Mutex
env guard: happy-path, missing-manifest, unsupported-schema,
no-attach-state, marker-field verification
Workspace: keisei added to _primitives/_rust/Cargo.toml members.
MANIFEST.toml: [primitive.keisei] rust kind, kei-ledger-style
deps (rusqlite bundled, stub for future artifact reads), added
to full profile (standalone opt-in via --add=keisei).
README Rust crates table gains 1 row; count marker untouched.
CHANGELOG [Unreleased] bullet added.
Usage:
keisei attach /path/to/brain # mounts into current Claude Code
keisei status # shows mounted brain + health
Next (v0.18 follow-ups): detach impl, cursor/continue adapters,
list-adapters subcommand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:52:40 +08:00
Parfii-bot
e67ade47c8
feat(v0.18): kei-mcp-server single-binary compile — 5-platform via bun
...
Phase 1 of exobrain architecture. Ships TS MCP server as a static
binary so users on machines without Node can run KeiSeiKit (USB /
flashdrive / air-gapped scenarios).
.github/workflows/release.yml (+62 LOC) — new build-mcp-binary job:
- 5-target matrix: darwin arm64/x64, linux arm64/x64, windows x64
- bun build --compile, linux arm64 continue-on-error (ARM runners
less reliable)
- Artifact kei-mcp-server-<os>-<arch>[.exe] + sha256
- release job now needs [build-release, build-mcp-binary]
install/lib-rust.sh (+50 LOC) — have_prebuilt_mcp_server() +
report_mcp_server_binary_status(); KEI_SKIP_MCP_BUILD=1 env
flag skips bun/npm install when a prebuilt binary is present.
File 165 LOC (<200 limit).
_ts_packages/packages/mcp-server/package.json — scripts.build:native
+ 5 per-target aliases (macos-arm, macos-x64, linux-x64,
linux-arm, win-x64) for local dev.
_ts_packages/packages/mcp-server/BUILD.md (NEW, 52 LOC) — local
compile guide per platform + Gatekeeper/code-sign notes +
cites bun docs [VERIFIED: https://bun.sh/docs/bundler/executables ].
README.md pre-built-binaries section gains 'MCP server binary'
subsection (download, chmod +x, xattr -d com.apple.quarantine for
macOS, UAC note for Windows).
CHANGELOG.md [Unreleased] bullet added.
Output size: ~90 MB per binary (bundled bun runtime). Acceptable
trade for zero-dep USB distribution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:52:36 +08:00
Parfii-bot
d97afb63ec
feat(v0.16): CHANGELOG + tag-driven release workflow
...
Keep-a-Changelog format. 12 sections: [Unreleased] + 11 real
releases v0.8.0..v0.15.0, every bullet with real git SHA pulled
via git log --no-merges. 150 LOC.
.github/workflows/release.yml — 3 jobs, triggered on tag push:
build-release: 4-platform matrix
- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu (continue-on-error)
- x86_64-apple-darwin
- aarch64-apple-darwin
Builds entire _primitives/_rust workspace, emits tar.gz +
sha256 per target via portable executable-discovery loop.
release: downloads artifacts, runs local
kei-changelog --from <prev-tag> --to <tag>, publishes via
softprops/action-gh-release@v2.
npm-publish: graceful skip when NPM_TOKEN secret absent
(steps.have_token.outputs.present gate + || warning wrap
so one failing package doesn't kill the job).
Companion install support: install/lib-rust.sh gains
have_prebuilt_binaries() + KEI_SKIP_RUST_BUILD=1 guard (shipped
as part of install-split bundle). Users can download tarball
instead of compiling Rust from source.
release.yml validated via yaml.safe_load: 3 jobs parse cleanly,
matrix expands 4-wide, jobs = [build-release, release, npm-publish].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:10:00 +08:00