17 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| d34a375da7 |
feat(install): first-run is a full guided onboarding (agents + sleep + cortex)
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
Sleep/cortex setup were left as separate things the user had to discover. Make the SessionStart first-run hook a single ordered post-install checklist that Claude walks the user through: (1) /onboard projects → per-project agents, (2) /sleep-setup → nightly REM (recommend local-only, no remote git needed), (3) /cortex-setup (only if the cortex daemon primitive is installed). Confirm + run each, skippable. Fires once (marker), then silent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 582b51155f |
feat(install): first-run nudge to /onboard projects after bundle install
After install the user got no guidance to set up project agents — the summary only mentioned /new-agent (single), never /onboard (scan all projects → create a specialist per project). The installer is bash and can't launch a skill, so: - New SessionStart hook first-run-onboard.sh: on the FIRST Claude Code session after install, injects context nudging the user/Claude to run `/onboard ~/Projects/*` (scan stack + create per-project agent, delegates to /new-agent). Fires once (marker ~/.claude/.kei-firstrun-shown), then silent. Reset: rm the marker. - settings-snippet.json: wire the hook under SessionStart (matcher "*"). - lib-summary.sh next-steps: lead with `/onboard ~/Projects/*`, then /new-agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 1d958b3587 |
fix(security): patent-leak + classical-safety audit fixes
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
PATENT-LEAK (HIGH):
- hooks/no-python-without-approval.sh: genesis-verify пример → my-project
- docs/encyclopedia/rust-crates-H-N.md: убран термин «Genesis IP, ITAR»
PATENT-LEAK (MEDIUM):
- CHANGELOG: project-vortex → reduced scope
- _blocks/registries (submodule bump): убраны имена приватных
project-specialists из комментария agent-profiles.toml
- docs/encyclopedia/skills-and-agents.md: ML/RL/CfC → ML/RL
CLASSICAL-SAFETY (MEDIUM):
- install/lib-preflight.sh: eval "$version_cmd" → bash -c "..."
(защита от инъекции если providers.toml расширят)
- _primitives/provision-{vultr,hetzner}.sh: /tmp/$$ → mktemp
(устраняет symlink TOCTOU race)
- web-install.sh: chmod 600 + umask 077 на ~/.keisei-install.log
(Forgejo admin creds + токены в логе)
- scripts/regen-counts.sh: eval "$1" → bash -c
NOT FIXED (требуют действий юзера):
- HIGH: @keisei scope не зарегистрирован на npmjs.org — typosquat
возможен пока не задан NPM_TOKEN и не сделан publish
- HIGH: install.keisei.app DNS не настроен — DNS-hijack возможен
- LOW: parfionovich@keilab.io в SECURITY.md, plugin.json, ~40 Cargo
файлах — intentional contact, оставлен
Локальный git author установлен на parfionovich@keilab.io вместо
parfionovichd@icloud.com (только для будущих коммитов в этом репо).
|
|||
|
|
3759fb0f64 |
fix(audit-batch): CI green + RULE 0.4/0.16/0.18 honesty pass
12-agent audit (2 waves Opus+Sonnet, 6 slices each) flagged 3 HIGH-tier
issues that BOTH waves agreed on, plus 5 doc-honesty findings. This
batch fixes the lot.
== CI green (was failing on main
|
||
|
|
71f17337fe |
fix(security): cortex /term env_clear + bind guard, agent-stub-scan stdin, magiclink revoke
Three independent security hardenings from cross-cutting audits.
1. cortex /term PTY env leak + bind guard (HIGH — Sonnet Cross-cutting + Opus)
- kei-cortex/src/handlers/term_pty.rs: PTY spawn was inheriting daemon's
full process env (KEI_AUTH_KEY, ANTHROPIC_API_KEY, FAL_KEY, etc.) into
every authenticated /term shell. Combined with default cors_origin =
https://keisei.app, one stored XSS on keisei.app + one bearer token =
full local shell with all daemon secrets.
Added apply_safe_env() helper: env_clear() + re-set only HOME, PATH,
USER, LANG, TERM. Spawn helper invokes it before spawn_command.
- kei-cortex/src/main.rs: extracted build_config() helper; added
enforce_loopback_or_local_cors() guard called before serve.bind. Refuses
to start if bind addr is non-loopback AND cors_origin is a public
domain — prevents the XSS-to-shell scenario in production.
2. agent-stub-scan.sh stdin parsing (HIGH — multiple audits)
- hooks/agent-stub-scan.sh: previously read $CLAUDE_AGENT_TRANSCRIPT env
var which Claude Code does NOT set on PostToolUse:Agent. Hook silently
exited 0 — RULE 0.16 enforcement was dead-code in production.
Rewrote to read stdin JSON via jq, flatten .tool_response recursively
(string|array|object via the same pattern as agent-event-done.sh),
guard on .tool_name == "Agent" and command -v jq. Maintained WARN-tier
exit-0 with TODO marker for ENFORCE flip on 2026-05-05 (per RULE 0.16
§2 ladder).
3. magiclink revoke() silent no-op (HIGH — Opus Rust + Sonnet Cross-cutting)
- kei-auth-magiclink/src/{error,provider}.rs: revoke() previously returned
Ok(()) without doing anything. Operators expecting "revoke a session"
semantics from the AuthProvider trait got false success. Stolen magic-
link URLs remained valid until the 15-minute TTL.
Added Error::Unsupported variant. revoke() now returns
Err(Unsupported(...)) with explicit guidance: "rotate KEI_MAGICLINK_HMAC_
KEY to invalidate all live tokens, or maintain a deny-list at the caller
layer". Test provider_revoke_returns_unsupported_error confirms the
error variant is wired.
Tests: cargo check + cargo test both PASS. 444 functional tests across
kei-cortex (428 lib) + kei-auth-magiclink (16 lib + smoke). Pre-existing
openai_loop_wiring.rs 502 failures in routes/openai/{chat,responses}.rs are
NOT introduced by these fixes — separate unrelated triage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8473b4ae80 |
fix(hooks+install): disk-reclaim Guard 3 + secrets per-line + sha256 fail-closed
Three independent shell hardening fixes from Opus Shell + Sonnet Shell audits.
1. disk-reclaim.sh Guard 3 — protect branches without upstream tracking (HIGH)
File: hooks/disk-reclaim.sh:88-101
Bug: when a worktree branch has no upstream tracking ref, `git log @{u}..`
exited non-zero and `unpushed=""` (empty). The check
`[ -n "$unpushed" ] && [ "$unpushed" != "0" ]` evaluated FALSE, so the
worktree fell through Guard 3 and was eligible for mtime-based pruning.
Local-only branches with committed work were silently deleted.
Fix: explicit two-branch logic. Run `git rev-parse --abbrev-ref @{u}` first;
only run the unpushed-count check if upstream exists. If no upstream, log
SKIP[no-upstream] and `continue` conservatively. New
`worktrees_skip_unpushed` counter increments in both unpushed paths.
2. secrets-pre-guard.sh — placeholder allowlist scope-narrow (MEDIUM)
File: hooks/secrets-pre-guard.sh:43-103
Bug: word "placeholder" anywhere in content disabled all secret-pattern
scanning for that whole Write. Allowlist was too broad — a doc with the
word "placeholder" in its prose could mask a real sk-ant- token elsewhere.
Fix: replaced global early-exit with per-line awk scan. New scan_pattern()
helper walks content line-by-line; each line matching a secret regex is
allowed ONLY if the SAME line also matches ALLOWLIST_RE. Doc prose can no
longer mask cross-line secrets. Added `dummy[_-]?(key|token|secret)` to
allowlist for legitimate test fixtures.
3. lib-rust-prebuild.sh — sha256 fail-closed (HIGH supply-chain)
File: install/lib-rust-prebuild.sh:75-88
Bug: when ${url}.sha256 404'd, installer printed WARNING and proceeded with
unverified tarball. A compromised github release uploader could ship a
malicious tarball, omit .sha256, and the installer would extract it into
~/.cargo/bin/.
Fix: missing .sha256 → ERROR + abort. Path A install fails → falls back to
Path B (cargo build from source). Override via KEI_ALLOW_UNVERIFIED_TARBALL=1
(visible per-call, intentional friction).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
85a61d7253 |
fix(hooks): post-audit hook chain hardening + 4 new defensive hooks
Hook chain repairs (Group A):
- alignment-check.sh: read .prompt (was .user_prompt) — hook was dead
- block-dangerous.sh: jq instead of inline interpreter (RULE 0.2 + fail-open fix)
- destructive-guard.sh: explicit INPUT=cat + jq guard + exit 0 — was silent no-op
- numeric-claims-guard.sh: exit 1 -> exit 2 (Claude Code spec — was non-blocking)
comments updated 0.17 -> 0.18 (env var name kept)
- no-downgrade.sh: removed (?i) PCRE syntax — POSIX ERE matched literal text
- task-timer.sh: jq -nc instead of bare printf — JSON injection on quotes/backslashes
in description was corrupting RULE 0.18 evidence journal
- check-error-patterns.sh: replaced with no-op stub — had hardcoded /Users/denis/...
PATH LEAK in public kit, plus inline interpreter use
- post-commit-audit.sh: added trailing exit 0 — grep return code was hook exit code
- citation-verify.sh: ALLOW_REGEX accepts HOOK-BYPASS marker — bypass was documented
but never matched
- settings-snippet.json: agent-stub-scan moved PreToolUse:Agent -> PostToolUse:Agent
(RULE 0.16 enforcement was firing before transcript existed)
- check-error-patterns hook removed from settings-snippet.json
New defensive hooks (Group H):
- no-github-push.sh: PreToolUse:Bash hard deny on github.com push/create/sync/remote-add
(RULE 0.1 — patent IP protection; was missing from public kit)
- secrets-pre-guard.sh: PreToolUse:Edit|Write — token-pattern scan with allowlist (RULE 0.8)
- chat-numeric-prewarn.sh: UserPromptSubmit reminder when prompt mentions time/cost
(RULE 0.18 chat extension)
- chat-numeric-postflag.sh: Stop event scans last assistant message for naked numerics
without REAL/FROM-JOURNAL/ESTIMATE-HTC markers
Source: full Sonnet test-retest audit 2026-05-02 (3 parallel waves of 6 agents each)
identified hook chain bugs as HIGH severity in all 3 runs independently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
897d010802 |
chore(sleep-tg): minor prompt tightening (compress reasoning output)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4e7463ef0a |
fix(live-graph): tool_use events properly attribute to spawning agent
User pushback: live-graph showed only "main" node, no pulses on agents.
Root cause: hook stdin doesn't carry parent_tool_use_id for sub-agent
tool calls — we only get the sub-agent's own session_id, which doesn't
link back to the spawn's tool_use_id.
Sequential heuristic via shared state file:
- agent-event-spawn.sh appends tool_use_id to /tmp/kei-active-children.tsv
- tool-use-event.sh reads the LAST line of that file → uses that
tool_use_id as agent_id for the emitted event
- agent-event-done.sh removes the spawn's line (grep -v + atomic mv)
Verified end-to-end: a code-implementer agent ran 5 Bash calls during
its lifetime — all 5 tool_use events were correctly attributed to the
spawn's tool_use_id. After agent_done, subsequent orchestrator-direct
tool calls correctly fall back to agent_id="main".
Limitation: parallel agents may misattribute. The "most recent live
spawn" heuristic works for single-agent-at-a-time which is the common
case. Parallel spawns share /tmp/kei-active-children.tsv and a sub-
agent's tool calls all attribute to whichever spawn appended last.
Acceptable for v1 demo; proper parent-tool-use-id propagation requires
Claude Code to expose it in sub-agent stdin (upstream change).
The `mv` after `grep -v` runs UNCONDITIONALLY (not gated on grep's
exit code) — grep -v returns 1 when ALL lines match, which would
otherwise leave the stale file in place.
Bypass: `KEI_EVENTS_BYPASS=1` (existing) covers all 3 hooks.
Override path: `KEI_ACTIVE_SPAWNS_FILE=/path/to/file`.
=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: NOT-RUN
behaviour-verified: yes
follow-up-required:
- Parallel-agent attribution would need parent_tool_use_id from
Claude Code sub-agent stdin (not currently exposed).
- Race condition window between spawn append and done remove is
millisecond-scale; observed clean in single-agent demo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
52a02dfbff |
feat(live-graph): WebSocket activity stream — orchestrator-centric live view
User pushback: "транслирует в онлайне какие агенты создаются? основное
окно агента, а дальше при запусках появляются новые ветки, мы показываем
в онлайне как агенты собираются и работают"
Earlier `kei-graph-export` rendered the static SUBSTRATE (all 581 atoms,
catalog-style). User wanted the LIFECYCLE: orchestrator at center, every
new agent as a fading-in branch, every tool call as a pulse, every
completion as a fade-out. TTL = until done; pure online, no history
accumulation per user direction.
Three-layer architecture, all conforming to schema /tmp/agent-events-schema.md:
LAYER 1 — Event emitters (4 hooks)
hooks/agent-event-spawn.sh PreToolUse:Agent → agent_spawn event
hooks/agent-event-done.sh PostToolUse:Agent → agent_done event
(parses STATUS-TRUTH MARKER for outcome,
computes cost_usd from token×pricing table)
hooks/tool-use-event.sh PreToolUse:Bash|Read|Edit|Write|Grep|Glob|NotebookEdit
→ tool_use event
hooks/skill-record.sh EXTENDED — second emit step writes skill_use
event in addition to existing kei-ledger
record-skill call
All 4 are POSIX /bin/sh, defensive (never block, exit 0), bypass via
KEI_EVENTS_BYPASS=1. Append-only JSONL to
~/.claude/memory/agent-events.jsonl.
Smoke: 4 synthetic invocations cover spawn/done/tool/filter cases.
LAYER 2 — kei-graph-stream Rust daemon
_primitives/_rust/kei-graph-stream/ (~480 LOC, 5 files + 1 test)
- Tails events.jsonl every 200ms (poll-based, no notify dep).
- Parses each event, updates AliveState (insert on spawn, remove on done).
- Broadcasts {"type":"event","data":<event>} to all WebSocket clients.
- On client connect: sends {"type":"snapshot","alive":[...]} first.
- Heartbeat: {"type":"ping"} every 30s.
- axum 0.7 + ws feature (already in Cargo.lock via kei-cortex).
- Bypass: KEI_GRAPH_STREAM_BYPASS=1.
Bound to 127.0.0.1:8201 (loopback only). Endpoints:
GET /stream → WebSocket upgrade
GET /health → "kei-graph-stream alive"
4 unit + 1 integration test. cargo build clean.
Installed binary: ~/.cargo/bin/kei-graph-stream
Launchd plist: io.keisei.graph-stream (RunAtLoad, KeepAlive)
Loaded as PID 52678, /health 200 OK verified.
LAYER 3 — live-graph.html (single-file frontend)
~/Projects/lbm-graph-viz/live-graph.html (~464 LOC, self-contained)
- SVG full-viewport, dark #0f172a, CSS grid background.
- Pinned center node "main" (orchestrator), gold #fbbf24, glowing.
- Agents radiate via D3 force-simulation; color-by-model
(sonnet=green, opus=red, haiku=blue, default=gray).
- On agent_spawn: fade-in 300ms, edge from main to new node.
- On tool_use: pulse on agent node (r 8→12→8 over 400ms) +
floating tool name label fades 800ms.
- On agent_done: outcome-color flash → fade-out 800ms → remove.
- WebSocket client: ws://127.0.0.1:8201/stream, exponential-backoff
reconnect (1s→30s).
- Top-right status badge: ● connected | ○ reconnecting | ✕ disconnected.
- Bottom counters: alive / spawned / tool calls / done / last event age.
- No build step. D3 v7 from CDN. Pure HTML+JS+CSS.
End-to-end smoke (this machine, just now):
- daemon health 200 OK
- hook injected agent_spawn → daemon broadcasts → AliveState=1
- hook injected agent_done → daemon broadcasts → AliveState=0
- frontend file syntax-checked clean
What this does NOT do (deferred, by user direction "это онлайн"):
- History persistence — agents who finished are GONE from the graph.
Per-session log remains in events.jsonl + sleep-sync if user wants
to consult later, but the live view is RIGHT NOW only.
- Sub-agent attribution beyond "main" — orchestrator-direct tool calls
show on the orchestrator node. Sub-agent's internal tool calls would
need session-id correlation; current schema has agent_id="main"
placeholder for non-Agent tool calls.
- Replay mode — no time-scrubber. Possible follow-up if useful.
- Auth on WebSocket — bound to 127.0.0.1 only. Local-only by design.
=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes
follow-up-required:
- Sub-agent tool-call attribution (correlate session_id chain)
- Replay mode with time scrubber (if user finds use)
- Tool aggregator nodes ("Bash bucket" with N) instead of per-agent pulses
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a31a056f61 |
feat(graph): live runtime DNA viewer — kei-graph-export + lbm-graph-viz adapter
User pushback: "можно нашего Кейси подключить к обсидиан? будет в
онлайне строить граф из всех наших агентов?"
Closer-to-question architecture: don't build new Obsidian plugin —
re-use the legacy `~/Projects/lbm-graph-viz/` D3 viewer (lineage:
keicode → living-graph → lbm → lbm-graph-viz → keisei-graph). Strip
its Hebbian/co-change edges, replace with DNA-derived edges from the
kei-registry + kei-ledger. Open in any browser, file://...index.html.
NEW Rust crate `_primitives/_rust/kei-graph-export/` (~440 LOC, 5 files)
Reads:
~/.claude/registry.sqlite (730 active blocks)
~/.claude/agents/ledger.sqlite (6 agents post-cleanup)
_manifests/*.toml (38 agent manifests)
Emits 581-node, 291-edge graph. Edge types:
block_dep 171 manifest → atom (blocks=[])
path_ref 99 manifest → atom (path:NAME refs)
branch_lineage 11 parent_branch → branch
agent_uses_manifest 10 agent → manifest (slug from branch name)
Output formats:
--format spaces-fragment → `window.RUNTIME_SPACE = {...}` JS file
--format json → raw {nodes, links} for downstream tools
Block-name lookup is multi-resolution: each block is registered under
display name + lowercased + file-stem slug (from path basename) so
manifest references like `blocks = ["baseline"]` resolve to a registry
row whose `name` column holds "BASELINE — inherit from Main Claude".
Without this fix the graph had 0 block_dep edges; with it, 171.
NEW background updater `hooks/graph-export-watcher.sh` + launchd plist
template `_primitives/templates/io.keisei.graph-export.plist`
5-second loop:
while true; do
kei-graph-export --format spaces-fragment --output <viz>/data-runtime.js.tmp
mv <viz>/data-runtime.js.tmp <viz>/data-runtime.js # atomic
sleep 5
done
launchd plist substitutes `HOME_DIR` and `HOOKS_DIR` placeholders at
install time. RunAtLoad=true, KeepAlive=true. Logs to
~/.claude/memory/graph-export.log. Bypass: GRAPH_EXPORT_BYPASS=1.
Loaded into user-side launchd (PID 16474 confirmed running). File
mtime advances every 5s — live updates verified.
PATCH `~/Projects/lbm-graph-viz/index.html` (outside kit, surgical)
Three changes:
1. Add `<script src="data-runtime.js">` BEFORE `spaces.js` (window
global available when SPACES is defined).
2. After spaces.js: `if (window.RUNTIME_SPACE) SPACES.runtime = window.RUNTIME_SPACE;`
3. Auto-refresh setInterval(5s): fetch data-runtime.js, eval (re-
assigns window.RUNTIME_SPACE), hash-compare, re-render via
`rebuildGraph()` if currently viewing the runtime space.
window.RUNTIME_SPACE (not const RUNTIME_SPACE) avoids the
"const cannot be re-declared" error on subsequent eval() calls.
Effect: open file://~/Projects/lbm-graph-viz/index.html in any
browser, switch to "Runtime" space — full DNA graph of every agent /
atom / skill / branch / manifest / hook / primitive / rule, force-
laid-out by D3. Updates every 5 seconds without page reload.
What this does NOT do (deferred):
- Obsidian mirror — separate work, would emit .md per node into
~/Projects/KeiSeiVault/. Useful for backlinks navigation but
file-watcher latency similar to current 5s polling.
- Skill-invocation edges — table is empty until next Skill tool
use; will populate naturally.
- Scoped queries (orphan finder, hot-path PageRank). Out of scope
for v1; the JSON --format export feeds any downstream tool.
- `agent_uses_manifest` heuristic warns on unknown subagent slugs
(e.g. `physics-deriver` with no manifest yet). Non-fatal.
=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes
follow-up-required:
- Obsidian vault mirror (Phase C, separate work)
- Skill-edges populate from real Skill use (not blockered)
- Hot-path PageRank highlighting in viewer (cosmetic)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
0cf823413e |
feat(sleep): cloud-agent reasoning + Telegram delivery to whitelist
User pushback: "Агент должен делать осмысленные выводы! С утра должен
быть отчет и пусть он приходит куда-то! На телеграмм, например, лучше
сразу после фазы сна, бот есть"
Wires the @KeiSeiBot Telegram bot as the delivery channel for nightly
Phase B reports, with a Claude Sonnet 4.6 reasoning step in front to
distil the multi-section markdown into a single actionable brief.
NEW — `hooks/sleep-report-tg.sh` (130 LOC POSIX bash)
Pipeline:
1. Source ~/.claude/secrets/.env (umbrella SSoT — RULE 0.8)
2. POST report markdown to Claude API messages endpoint with a
system prompt mandating: TL;DR + numbers + 3-5 actionable
findings + rule-candidates if any cross-session pattern ≥3×.
Sonnet 4.6, max_tokens=1500, 120s timeout.
3. Send distilled summary via Telegram sendMessage to whitelisted
chat_id (defaults to TELEGRAM_ALLOWED_CHAT_ID env, falls back
to 86059912).
4. Cap message at 3900 chars (TG limit 4096).
5. Fallback if Markdown parse_mode fails (orphan * / [ in body) →
retry without parse_mode so the user still sees the report.
6. Defensive on every step: missing API key → send raw excerpt;
missing curl/jq → log + exit 0; HTTP failure → log + exit 0.
7. Bypass: SLEEP_REPORT_TG_BYPASS=1.
WIRE — `hooks/phase-b-rem.sh`
Step 7 (new) calls sleep-report-tg.sh after the existing commit/push
step. Failure of TG delivery never affects Phase B's exit code —
the local report + memory-repo push remain the source-of-truth;
TG is convenience.
CONFIG (already done outside this commit, documented for completeness)
- ~/.claude/secrets/.env now has TELEGRAM_BOT_TOKEN +
TELEGRAM_ALLOWED_CHAT_ID (single-user whitelist 86059912).
- ~/.claude/tg-webhook.py whitelist locked to {86059912}; group
chat (-1003758632751) and partner (10954083) removed per
user request "сделай боту только один вайт адрес". Blocked
senders land in /var/log/tg-webhook/blocked.jsonl, no auto-reply.
- ~/.claude/tg-contacts.json shrunk from 3 contacts to 1.
Smoke verified: today's sleep-2026-05-02.md → cloud agent emitted
TL;DR ("Opus burned $1239 across 117 runs with 100% unknown outcomes")
+ 5 findings + 3 rule-candidates → delivered to chat_id 86059912 as
msg_id 1129 (HTTP 200). Cost: 3955 in + 897 out tokens on Sonnet
≈ $0.025/run. At 1 run/night that is ~$0.75/month for full reasoning
on every nightly report.
What this does NOT yet do:
- No retry on Telegram rate-limit (429). Single nightly call
is well below the 30/sec limit, but if the system ever bursts
multiple reports it would lose them.
- No multi-day digest mode (each run is independent; future:
weekly Sunday recap aggregating 7 reports).
- Cloud agent prompt is hard-coded inline; future: extract to
a path-atom-style block (post-2026-05-02 substrate work).
=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: NOT-RUN (pure shell)
behaviour-verified: yes
follow-up-required:
- Phase B prompt template extracted to atom (low priority)
- Weekly recap mode (Sunday)
- 429 rate-limit retry (defensive)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
9a3db14b90 |
feat(sleep-sync): mirror time-metrics + ledger snapshots, surface in Phase B report
User pushback: "что теперь делает сон? все связано?" — Sleep Phase B
was reading only `traces/`, ignoring the four tracking journals shipped
in the previous commit. Cloud agent had a partial view of what happened.
This commit closes the loop. Sleep now sees everything that's tracked.
PUSH SIDE — `kei-sleep-sync.sh` (called on every Stop event)
Now mirrors the full observability surface into the memory-repo:
~/.claude/memory/time-metrics/sessions.jsonl → time-metrics/
~/.claude/memory/time-metrics/tasks.jsonl → time-metrics/
~/.claude/memory/time-metrics/numeric-claims.jsonl → time-metrics/
~/.claude/memory/time-metrics/agent-toolstats.jsonl→ time-metrics/
~/.claude/agents/ledger.sqlite agents table → ledger/agents.jsonl
~/.claude/agents/ledger.sqlite skill_invocations → ledger/skill_invocations.jsonl
Format: JSONL (one row per object). The two ledger tables are dumped
via `sqlite3 + json_object()` so cloud agents can stream-parse into
pandas / duckdb without binary-file handling.
First sync moved 6 files / 638 rows from local to remote — verified
by `git show --stat` of the resulting `memory: session traces` commit.
CONSUME SIDE — `phase-b-rem.sh` REM-consolidation report
Each nightly `reports/sleep-YYYY-MM-DD.md` now ends with a "Tracking
observability (last 7 days)" section containing four jq-aggregated
digests:
1. Agent outcomes — per-model: n, functional/partial/scaffolding/fail
counts + total_cost_usd. Lets the agent see whether the model-tier
refactor (
|
||
|
|
e073df6c98 |
feat(tracking): close 3 last observability gaps — toolStats + skill-record + numeric-claims journal
Closes the loop on "without full tracking the system can't make decisions" (user pushback on partial coverage). Three gaps that left the inference layer blind are now wired: GAP #1 — agent toolStats / token counts / cache hits captured ================================================================ `agent-outcome-backfill.sh` now appends one JSONL row per spawn to `~/.claude/memory/time-metrics/agent-toolstats.jsonl` with: agent_id, outcome, stubs, ts, tool_use_count, duration_ms, tool_stats {Read:N, Bash:M, ...}, tokens_in, tokens_out, cache_read, cache_write Sidecar journal (no schema migration). Production payload's .tool_response.totalToolUseCount / totalDurationMs / toolStats / usage fields land directly. Smoke-tested with synthetic spawn — row written. GAP #2 — skill_invocations table actually receives writes ================================================================ The `skill_invocations` table (schema v8) had 0 rows because no caller existed for `skill_metrics::record_invocation`. Added two pieces: (a) `kei-ledger record-skill <name> --success {0|1}` CLI subcommand Mirrors record-cost; same dispatch shape. Optional `--agent-id`, `--trajectory-id`, `--duration-ms`, `--db`. Validates non-empty name + duration ≥ 0. Outputs `{"ok":true,"skill":"...","ts":N}`. (b) `hooks/skill-record.sh` — PostToolUse:Skill hook. 50 LOC POSIX. Detects Skill tool calls, derives success heuristic from tool_response (exit_code / status / content non-empty), shells out to `kei-ledger record-skill`. Bypass via SKILL_RECORD_BYPASS=1. 83 kei-ledger tests pass (16 unit + 67 integration). Smoke-tested end-to-end: `kei-ledger record-skill test-skill --success 1` inserts a row with correct fields. Phase D nightly skill-metrics decisions (archive if unused N days, re-extract if success<60% over M days, validated if >20 calls + >90% success) now have data to consume. GAP #3 — numeric-claims.jsonl receives every evidence-tagged claim ================================================================ RULE 0.18 mandated three markers `[REAL:]` / `[FROM-JOURNAL:]` / `[ESTIMATE-HTC:]` on every numeric/duration/cost claim, but no hook appended valid claims to the journal — the calibration data RULE 0.18 promised never accumulated. `hooks/numeric-claims-record.sh` — Stop hook, 140 LOC POSIX. Reads transcript_path from stdin, locates the last assistant message via recursive flatten (same pattern as agent-outcome-backfill.sh after the production-payload-shape fix), regex-extracts every `<phrase> [<TIER>: <pointer>]` triple, appends one JSONL row per claim. Idempotent within 1-second window to avoid double-recording on repeat Stop fires. Bypass via NUMERIC_CLAIMS_RECORD_BYPASS=1. Smoke test: synthetic transcript with 3 markers (REAL + ESTIMATE-HTC + FROM-JOURNAL) produced exactly 3 well-formed JSONL rows. Settings.json ================================================================ - PostToolUse:Skill matcher created (or augmented if already present) with skill-record.sh. - Stop:* matcher gains numeric-claims-record.sh after the existing chain (stop-verify, task-timer, session-end-dump, extract-task- durations, chat-numeric-postflag, affect-threshold-check, enrich-from-jsonl). What this does NOT do (deferred): - Backfill `skill_invocations` from past traces (history started today; Phase D cohort builds forward from now). - Migrate the agent toolStats sidecar JSONL into a proper ledger column. Append-only file is fine for the current scale. - Refactor main.rs (now 233 LOC, was 212; pre-existing CP debt flagged by skill-record agent — separate cleanup PR). === STATUS-TRUTH MARKER === shipped: functional stubs: 0 cargo-check: PASS behaviour-verified: yes follow-up-required: - kei-ledger main.rs Constructor Pattern split (212→233 LOC) - Verify in next session: skill_invocations gets rows from real Skill tool use; numeric-claims.jsonl gets rows from real assistant messages with markers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
033b9efbad |
fix(outcome-hook): production payload uses object.content[*].text shape
Hook never fired in production despite passing unit tests. Diagnosed via debug-log + payload dump: real Claude Code PostToolUse:Agent sends `tool_response` as an OBJECT (not string, not array), with the agent's reply at `tool_response.content[0].text` — keys: agentId / agentType / content / prompt / status / toolStats / totalDurationMs / totalTokens / totalToolUseCount / usage. Original jq filter handled string + object (`$r.content // $r.text`) but `$r.content` returns the array verbatim; `jq -r` then dumps the JSON literal which has `\n` as escape sequences, defeating the `grep -m1 '^shipped:'` line-anchor. Fix: recursive `flatten` jq function: string → as-is array of any → recurse, join "\n" object with .text → return .text object with .content → recurse into content anything else → "" Verified end-to-end: latest 4 code-implementer spawns now write outcome=functional to ledger correctly. Beta posterior in kei-model-router begins receiving signal. Production cleanup: - Removed verbose debug-log + payload-dump diagnostic. Toggle via `AGENT_OUTCOME_DEBUG=1` env if hook stops firing in some future Claude Code version. - Hook source committed to `hooks/agent-outcome-backfill.sh` so `install.sh` deploys it on fresh installs (was only in user-home previously — gap from `feat/substrate-path-atoms` agent run). === STATUS-TRUTH MARKER === shipped: functional stubs: 0 cargo-check: NOT-RUN behaviour-verified: yes follow-up-required: - none Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f3f5f79760 |
feat(frontend-loop): kei-db-contract primitive + frontend-validator agent + auto-dev-guard hook
Frontend continuous-quality loop landed. Three composable cubes:
Wave 1 — kei-db-contract primitive (~870 LOC, 7 cubes per Constructor Pattern):
- Diffs SQL CREATE TABLE migrations against TypeScript type/interface declarations
- 4 drift modes: ORPHAN-SQL, ORPHAN-TS, TYPE-MISMATCH, NULL-MISMATCH
- Reuses sqlparser-rs (Apache 2.0) + regex + walkdir + serde_json + clap
- CLI: kei-db-contract <project-root> [--output json|text] [--strict]
- 5/5 integration tests pass (cargo check + cargo test green)
- Smoke-tested on keisei-marketplace: drift_count=266 across 30 tables
(expected — marketplace uses raw better-sqlite3 without explicit row types)
Wave 2 — frontend-validator agent + dev-guard skill extension:
- New _manifests/frontend-validator.toml (substrate_role: edit-local, tools: Bash+Read+Glob+Grep)
- Agent runs: stack detect → tsc --noEmit → eslint → kei-db-contract → playwright (optional)
- Severity rules: TYPE_CHECK FAIL = block, DB_CONTRACT drift > 0 = block, lint = advisory
- skills/dev-guard/SKILL.md extended: 4th agent triggered on .tsx/.ts/.dart edits or DB-layer touches
- adaptive-depth table extended with frontend + DB-layer rows
Wave 3 — auto-dev-guard.sh hook (PostToolUse:Edit|Write):
- Trivial-edit gate: skip if delta < 30 LOC (avoid spawn fatigue)
- File-pattern match: *.tsx|*.ts|*.svelte|*.vue|*.dart OR migrations/*.sql OR src/db/** OR src/types/** OR prisma/schema.prisma OR drizzle.config.*
- Auto-runs kei-db-contract for DB-layer edits if binary on PATH
- Stderr advisory only (exit 0 always — never blocks)
- Bypass: KEI_DISABLED_HOOKS or KEI_HOOK_PROFILE in {advisory-off, minimal, off}
- Smoke-tested with synthetic Edit input (39 LOC delta on .tsx → emits advisory)
- Registered in hooks/hooks.json under PostToolUse:Write|Edit chain
Reusability map (Constructor Pattern compose):
shared cubes: detect-stack, tsc, eslint, kei-db-contract, kei-visual-snapshot (deferred)
orchestrators: /dev-start (pre), /dev-guard (during, NOW with frontend-validator),
/dev-ship (final), /site-create (init)
Verify-before-commit (RULE 0.13):
- cargo check -p kei-db-contract: PASS
- cargo test -p kei-db-contract: 5 passed
- jq . hooks/hooks.json: valid
- bash hooks/auto-dev-guard.sh < synthetic-input: works (frontend-relevant edit detected, exit 0)
=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
cargo-test: PASS (5 tests, 0 failures)
behaviour-verified: yes
follow-up-required:
- kei-visual-snapshot primitive (Playwright wrap) — Wave 4, deferred
- /dev-start frontend-contract-designer agent + /dev-ship frontend-final-gate — Wave 5, after Wave 1-3 obkatka
- install.sh wiring for kei-db-contract binary
- hermes-style emit-on-drift advisory mode
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
0be354a920 |
KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE. |