Parfii-bot
af16066793
Merge M3 — kei-social-store migration
2026-04-23 05:55:35 +08:00
Parfii-bot
a28ce2b36c
Merge M2 — kei-content-store migration
2026-04-23 05:55:35 +08:00
Parfii-bot
6edcba7c4b
Merge M1 — kei-chat-store migration (dogfood via prepare)
2026-04-23 05:55:34 +08:00
Parfii-bot
0b645db646
feat(m3): migrate kei-social-store to kei-entity-store engine
...
5/5 tests preserved. Primary entity = person; orgs + interactions +
relationship_graph stay custom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:55:13 +08:00
Parfii-bot
6ad8fd81ed
feat(m2): migrate kei-content-store to kei-entity-store engine
...
4/4 tests preserved. Primary entity = content_units; prompts +
campaigns + campaign_assets in custom_migrations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:55:13 +08:00
Parfii-bot
519600d1bf
feat(m1-dogfood): migrate kei-chat-store to kei-entity-store engine
...
Primary entity = chat_messages (integer-PK; sessions stay bespoke —
TEXT UUID PK incompatible with engine's IntegerPk).
Secondary tables (chat_sessions, indexes, FTS rename fts_chat →
fts_chat_messages) moved into custom_migrations. FTS shadow column
session_id dropped (never used as MATCH filter).
Archive verb NOT enabled: chat_sessions.status is TEXT enum not INTEGER
flag — engine archive verb incompatible. archive_session stays bespoke.
cost REAL column dropped — engine has no Real FieldKind. per-message
cost struct field kept (=0.0) for API compat; session total_cost
aggregate still maintained bespoke in save_message.
5/5 tests preserved + 1 new engine migration-parity smoke test.
DOGFOOD prompt feedback (M1 via kei-agent-runtime prepare):
6 engine limitations surfaced for follow-up — FieldKind::TextPk,
FieldKind::Real, archive-TEXT-enum variant, FTS UNINDEXED shadow cols,
atom dir assumption, rusqlite drop logic. See M1 task report.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:55:12 +08:00
Parfii-bot
e075ae8df1
fix(compose): render resolved scope block — agent sees concrete paths, not generic text
...
Dogfood gap #2 from prepare workflow: capability text fragments say
"your task's scope" generically, but agent never sees the resolved
scope params from task.toml. Migration agent had no way to know
whitelist = kei-chat-store/** without me pasting it.
Fix: render_scope_block() injects a resolved-params section between
capability fragments and task body. Shows:
- files-whitelist / files-denylist glob lists
- cargo-check-crates / cargo-test-crates
- test-count-min (if Option<u32>::Some)
- report-fields-required
If no scope params set, block is empty (no section rendered).
Now `kei-agent-runtime prepare` emits fully self-contained prompt —
no external context needed. Substrate unblocked for parallel migrations.
Tests: 41/41 (was 41 — no regression; scope-block added tests deferred
to follow-up since compose_smoke already covers existing path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:44:15 +08:00
Parfii-bot
68e37ecf68
fix(a1-integration): add EdgeKeyKind + archived_field defaults to bug_fixes_smoke fixture
...
A1's test fixture created before B5 merged new fields onto EntitySchema.
Cross-wave fixup — non-functional change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:33:21 +08:00
Parfii-bot
70867a7fd0
Merge fix/b5-textpair — EdgeKeyKind + archive verb
2026-04-23 05:31:03 +08:00
Parfii-bot
a5c5a68627
Merge fix/a1-entity-store — C1+C2+FTS5+M3+TEXT-cap+M2
2026-04-23 05:31:03 +08:00
Parfii-bot
bf5c4d3dce
Merge fix/b2-dna-role — DNA + role + traversal
2026-04-23 05:31:03 +08:00
Parfii-bot
ba75a075e3
Merge fix/b1-pattern-gate — hardening
2026-04-23 05:31:03 +08:00
Parfii-bot
a78f1aaa5f
Merge fix/b3-ledger — cycle + txn + length cap
2026-04-23 05:31:03 +08:00
Parfii-bot
da147c9a1b
Merge fix/b4-provision — exec redaction
2026-04-23 05:31:03 +08:00
Parfii-bot
3095b325d2
Merge fix/prepare-ergonomics — auto-gen agent-id for dogfood workflow
2026-04-23 05:31:03 +08:00
Parfii-bot
2d2c9881de
feat(entity-store/b5): EdgeKeyKind::TextPair + archive verb — unblock kei-sage + kei-chat-store migration
...
schema.rs: EdgeKeyKind enum (IntegerPair default, TextPair) + archived_field:
Option<&'static str> on EntitySchema. Backward-compat via Default impl.
engine.rs: ddl_edge_table_for() dispatches integer vs text edge DDL.
Existing ddl_edge_table() untouched; new ddl_edge_table_text() adds
src_path/dst_path TEXT columns.
verbs/link.rs (rewrite): dispatches on edge_key_kind. Input JSON
{from:int, to:int} for IntegerPair OR {from:str, to:str} for TextPair.
verbs/rank.rs (rewrite): generic pagerank<K: Hash + Eq + Clone> over
node key type. Same algorithm, polymorphic in key.
verbs/archive.rs (new, 64 LOC): soft-delete via UPDATE <tbl> SET
<archived_field>=1 + optional <archived_field>_at=now(). Schema
without archived_field declared returns VerbError.
Tests: 20/20 kei-entity-store (was 10, +10: 5 text_pair + 5 archive).
kei-task 9/9 preserved (IntegerPair still default, backward-compat
verified).
Enables: kei-sage migration (TextPair edges) + kei-chat-store
migration (archive).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:30:33 +08:00
Parfii-bot
4546239e8b
fix(ledger/b3): S2 tree cycle DoS + migration txn + length cap
...
S2: tree() had no visited set; cyclic parent_branch rows → infinite
loop. Added HashSet visited + MAX_TREE_DEPTH=1024 breaker. Returns
LedgerError::MaxDepthExceeded instead of OOM.
M2 migration txn: apply_one() wraps DDL + user_version bump in
BEGIN IMMEDIATE / COMMIT / ROLLBACK. Partial failure can no longer
leave user_version at N-1 with N's schema applied.
L1 length cap: branch + parent_branch strings capped to 256 chars
via 3-layer defence: clap value_parser!(parse_branch), client-side
check_branch_lens, schema v3 BEFORE INSERT/UPDATE triggers.
New src/error.rs (46 LOC) — LedgerError + MAX_TREE_DEPTH. SELECT_COLS
const DRY'd 4 duplicated column lists (list, by_id, children_of).
Schema v3 uses triggers (not table CHECK — SQLite can't retrofit
CHECK on existing tables without rebuild).
Tests: 13/13 (was 10, +3 audit). All 3 fixes exercised.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:30:33 +08:00
Parfii-bot
fdb6939015
fix(provision/b4): exec.rs redacts args + truncates stderr
...
MEDIUM info-disclosure: run_json_strict + run_void formatted error
messages with full argv + full stderr. Today argv has no secrets
(env-only per RULE 0.8) but:
- Future refactor could pass --api-key inline → secret in logs
- vultr-cli stderr echoes request URLs with query params → enumeration
Fix:
- redact_args() → "bin_name <N args>" (argv hidden)
- truncate_stderr() → first 200 chars + "... (truncated)", UTF-8 safe
- Docstring: // DO NOT pass secrets as CLI args — env-only per RULE 0.8
Tests: 11/11 (was 8, +3: redaction asserts no argv in error, stderr
truncation + Cyrillic UTF-8 safety)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:30:33 +08:00
Parfii-bot
02e4b25af2
fix(entity-store/a1): C1 type coercion + C2 FTS transaction + FTS5 injection + M3 SQL escape + TEXT cap + M2 migration version
...
6 critical/high bugs from post-convergence audit fixed in one pass.
New src/verbs/validate.rs (95 LOC) — shared typed validator:
- coerce() returns VerbError::InvalidType{field,expected,got} on wrong-kind JSON
- Preserves "missing key → default" semantics (additive, not breaking)
- MAX_TEXT_BYTES 64 KiB cap enforced on all text fields (M2 audit)
verbs/create.rs + update.rs (C1 + C2):
- Typed validation replaces silent 0/""/empty coercion
- insert_tx() + update_tx() wrap INSERT + FTS DELETE+INSERT in
conn.unchecked_transaction(); mid-flight failure rolls back both
main row + FTS sidecar together (no orphan FTS rows)
verbs/search.rs — fts5_quote() defends FTS5 syntax injection:
- User query wrapped in "..." phrase quotes, internal " doubled
- Column-prefix `title:secret`, NEAR(), wildcards become literal
- Integration + unit tests
engine.rs:
- ddl_column() escapes ' → '' on TextDefault values (M3)
- apply_user_version() stamps CURRENT_USER_VERSION=1 via PRAGMA;
idempotent (won't downgrade). Opens migration path (M2).
Tests: 22/22 (was 10, +12). kei-task 9/9 preserved — no regression.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:27:59 +08:00
Parfii-bot
4983d38636
fix(agent-runtime/b2): DNA entropy 32-bit + role path traversal + recursion cap + warnings
...
H4/M4/S3 DNA entropy:
- short_sha256() widened to 8 hex (32-bit) for scope + body hash
- nonce expanded to 8 hex from full u32 via rand::random
- Birthday collision at ~77K agents sharing role+caps (was ~256)
- Dna::parse accepts legacy 4-hex values with stderr warning for
rolling upgrade of pre-fix DNAs
H5 role recursion: resolve_inner depth parameter + MAX_DEPTH=16.
Returns RoleError::MaxDepthExceeded{depth, trace:Vec<String>} for
clear diagnostics.
S1 path traversal — two sites closed:
- role.rs::resolve_inner validates role name + parent in extends
against regex ^[a-z][a-z0-9-]{0,63}$ before Path::join
- compose.rs::split_cap_name same validation on capability
category/slug before Path::join
- RoleError::InvalidName{kind, value} on violation
M1 relaxes warnings: eprintln replaced with ResolvedRole.warnings
Vec<String> field. Caller decides: log, fail, ignore.
New typed RoleError (thiserror) — PartialEq/Eq for test ergonomics.
Tests: 73/73 (was 66, +7: 3 DNA + 4 role). Full extends graph + malicious
name + depth+1 all covered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:27:59 +08:00
Parfii-bot
f9bfcc23b7
fix(agent-runtime/b1): PatternGate hardening — RwLock, Result, UTF-8, explicit deny
...
H1 lock contention: Mutex<HashMap> → RwLock<HashMap>. Hot path cheap
read-lock, write-lock only on first compile per pattern.
H2 panic removed: compile_checked() returns Result<Regex, regex::Error>;
all call sites propagate as GateDecision::Deny{reason:"misconfigured
regex..."}. No .expect()/.unwrap() on compile anywhere.
H3 dead branch: AllowIfMatch + TaskWhitelist/TaskDenylist combo was
silently NotApplicable (allow-everything). Now explicit Deny with
"misconfigured" + "AllowIfMatch" in reason. Fail-closed.
S4 UTF-8 panic: &s[..60] byte-slice → s.chars().take(60). Cyrillic/
emoji safe. 60-char budget by code point.
L2 render chain: String::replace chain → single-pass render_template()
that scans {token} markers once. Substituted text cannot bleed into
later tokens.
Tests: 70/70 (was 66, +4). pattern_gate_smoke 10 → 14.
Follow-up (out of file-boundary scope): per-pattern Lazy<Regex> variant
would eliminate the RwLock entirely but requires editing sibling gate
consts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:27:59 +08:00
Parfii-bot
9c8c7c3598
fix(prepare): auto-generate agent-id when absent + dogfood ergonomics
...
Dogfood gap found: orchestrator running `kei-agent-runtime prepare
<task.toml>` hit "task.agent-id is empty" error. Required 3-step flow
(ledger fork → edit task.toml → prepare) for every spawn.
Fix: auto-generate `ag-<role>-<unix-ms-hex>-<4hex-rand>` if task.agent-id
empty. Orchestrator can still pre-allocate for deterministic ids;
auto-gen is ergonomic default. DNA compose sees the effective id via
clone+inject into TaskSpec before Dna::compose.
Helper `autogen_agent_id()` uses SystemTime millis + rand u16 — low
collision at orchestrator spawn rate (< 1 spawn/ms).
build_ledger_row() split: original preserved as thin wrapper, new
build_ledger_row_with_id() accepts explicit id for auto-gen path.
Verified end-to-end via `/tmp/keisei-dogfood/test-task.toml`:
prepare now emits complete AgentInvocation including composed prompt,
DNA, verify command — ready for orchestrator Agent-tool invocation
without pre-alloc step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 05:23:55 +08:00
Parfii-bot
324ad5d53e
fix(p1-integration): validate.rs allows _schemas/fragments $ref + drop additionalProperties on fragment-composed atom schemas
...
Two P1↔E1-audit-wave integration regressions caught by kei-runtime
invoke_real_atom test.
1. LocalFileResolver (E1 SSRF hardening) rejected $ref to
_schemas/fragments/ because the dir is OUTSIDE atom's schema parent.
Fix: extend LocalFileResolver with `find_fragments_root()` — walks up
from schema root looking for `_schemas/fragments/`. If found, allow
$ref under EITHER schema root OR fragments root. Still rejects
arbitrary filesystem $ref.
2. jsonschema injection of absolute $id now ALSO applied to fragment
schemas loaded via LocalFileResolver.resolve(). Without this, a
fragment declaring `$id: "_schemas/fragments/titled.json"` (relative)
was resolved against parent schema's absolute $id, producing double
prefix `_schemas/fragments/_schemas/fragments/titled.json`.
3. create-input.json + create-output.json had `additionalProperties:
false` alongside `allOf: [$ref <fragment>]`. Draft-07 gotcha:
additionalProperties at this level does NOT see properties inherited
from $ref-ed fragment — caused 'title' unexpected rejection. Dropped
the constraint on 2 fragment-composed schemas; kept on 4 standalone
ones (search-input/output + add-dependency-input/output).
Tests: kei-runtime 5/5 green; integration test passes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:53:26 +08:00
Parfii-bot
30a07e22ca
Merge feat/convergence-p3-role-dna — role expression + DNA identity
2026-04-23 04:47:30 +08:00
Parfii-bot
eba5c8ea10
Merge feat/convergence-p2-pattern-command — PatternGate + CommandVerify traits
2026-04-23 04:47:30 +08:00
Parfii-bot
b36225c03f
Merge feat/convergence-p1-entity-store — engine + schema fragments + kei-task pilot
2026-04-23 04:47:30 +08:00
Parfii-bot
84319efcb6
feat(convergence/p3): Role expression (extends/relaxes) + DNA identity
...
Layer E + G. Role TOML gains extends/relaxes for parent-role
composition; agent spawn gets self-describing DNA identity alongside
UUID.
Role expression:
- _roles/*.toml gain optional `extends = "<parent>"` + `relaxes = [...]`
- compose.rs + verify.rs delegate to new role::resolve_role() with
recursive extends-chain resolution + cycle detection
- explorer.toml: 28→18 LOC (extends read-only)
- edit-shared.toml: 31→23 LOC (extends edit-local, relaxes
scope::files-whitelist for task-param override)
DNA identity:
- new dna.rs (159 LOC) — compose/render/parse round-trip
- AgentInvocation carries dna field (prepare.rs)
- Format: <role>::<caps-bitmap>::<sha4-scope>::<sha4-body>-<hex4-nonce>
- ≤ 80 chars total, greppable, parseable
- 11 capability codes in CAP_CODES table: NG, FW, FD, CP, CG, TG, ND,
RF, SG, DT, BA
kei-ledger schema v2:
- ADD COLUMN dna TEXT + prefix index
- `kei-ledger fork --dna <string>` optional flag
- AgentRow.dna: Option<String>
- Backward compat: schema migration detects + applies on open
Docs: AGENT-SUBSTRATE-SCHEMA.md Layer E + Layer G sections + CAP_CODES table.
New deps: sha2 (workspace), rand 0.8.
Tests: kei-agent-runtime 50 (was 41, +9: 4 role + 5 DNA), kei-ledger
10 (was 9, +1 DNA roundtrip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:46:48 +08:00
Parfii-bot
360a20a942
feat(convergence/p2): PatternGate + CommandVerify unified traits
...
Layer C + D. 6 gate modules + 3 verify modules → 1 generic struct per
family + const declarations. Data-driven, not module-per-capability.
New:
- src/gates/pattern_gate.rs (192 LOC) — PatternGate struct with
GateMode::{DenyIfMatch, AllowIfMatch}, static regex compilation via
once_cell, bypass env support, Capability trait impl
- src/verifies/command_verify.rs (142 LOC) — CommandVerify struct with
WorkDir::{WorkspaceRoot, CrateDir, MainRepoSub}, subprocess exec +
exit-code check, extra_env support, Capability trait impl
Converted (const declarations, ~15-27 LOC each):
- gates/policy_no_git_ops.rs (49→22)
- gates/safety_no_dep_bump.rs (35→19)
- gates/scope_files_whitelist.rs (37→16)
- gates/scope_files_denylist.rs (35→16)
- gates/tools_bash_allowlist.rs (58→27)
- verifies/quality_cargo_check_green.rs (41→18)
- verifies/quality_tests_green.rs (75→80, folded common shape)
- verifies/safety_no_dep_bump.rs (39→47)
Kept separate (different shape, not PatternGate/CommandVerify):
- gates/tools_deny_tools.rs (tool-name match, not pattern)
- verifies/quality_constructor_pattern.rs (LOC walker)
- verifies/output/report_format.rs + severity_grade.rs (text parsers)
- verifies/scope_files_{whitelist,denylist}.rs (diff-walkers)
Registry.rs preserves alias table + deprecation warnings + all_names().
Tests: 57/57 green (was 41, +16: 10 pattern_gate_smoke + 6 command_verify_smoke).
LOC net: 5 gates 214→100 (-53%), shared PatternGate+CommandVerify 334
LOC absorb duplication. Amortization breaks even around 3-4 new gates
added later.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:46:47 +08:00
Parfii-bot
793b91bc43
feat(convergence/p1): kei-entity-store engine + schema fragments + kei-task pilot
...
Layer A + B of convergence wave. Extract common SQLite-CRUD + graph
logic into kei-entity-store engine; introduce JSON Schema fragments;
pilot-migrate kei-task to verify parity.
New crate _primitives/_rust/kei-entity-store/ (1151 LOC):
- src/schema.rs — EntitySchema + FieldDef + enabled_verbs + fts_columns
+ edge_table + custom_migrations
- src/engine.rs — Store::open with WAL pragma + migration runner
- src/verbs/ — 8 data-driven verb modules (create/get/list/search/
update/delete/link/rank) uniform JSON-in/JSON-out signature
- src/error.rs — typed VerbError enum
- tests/verb_smoke.rs — 10/10 green
New _schemas/fragments/ (83 LOC JSON):
- entity-base.json, titled.json, titled-content.json, edge.json
kei-task pilot migration:
- TASK_SCHEMA: EntitySchema static (67 LOC, was 58)
- store.rs becomes thin shim over engine::Store
- atoms/create.rs + atoms/search.rs delegate to engine verbs
- atoms/schemas/*.json use $ref to _schemas/fragments/ (DRY)
- Task-specific secondary tables (milestones, task_deps) stay via
schema.custom_migrations; cycle-detection in deps.rs stays
hand-rolled (domain logic, not generic CRUD)
- 9/9 tests green — full behavioural parity
Convergence delta:
- kei-task touched files: 342 → 389 LOC (+47 for JSON marshalling
boundary; net wash on pilot)
- BUT each remaining 5 sibling crate can shrink ~400-500 LOC on migration
- Expected total reduction when all 6 migrated: ~2500 LOC across the cluster
Follow-ups declared:
- Migrate kei-chat-store, kei-content-store, kei-social-store to engine
- Migrate kei-sage (needs string-id edge variant; currently generic
link/rank assume int ids)
- Migrate kei-crossdomain
- Expose list/delete atoms in kei-task (engine supports, atoms not yet)
- Fold kei-curator as engine::hygiene module (per P4 audit)
- Fold kei-search-core entities, keep workflow as thin kei-search-pipeline
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:46:47 +08:00
Parfii-bot
652d6a369b
Merge feat/schema-revocation — locks off, convergence breaking changes enabled
2026-04-23 04:31:20 +08:00
Parfii-bot
c3537f9dd4
docs(schema): REVOKE both schema locks — calendar weeks ≠ actual velocity
...
Both atom substrate (6-week lock) and agent substrate (3-week lock) were
calibrated for calendar windows that were 30-100× slower than actual
execution. Phases shipped in hours, not weeks. Keeping calendar locks
delays breaking consolidations that can safely ship now.
Unlocks: verb templates, schema fragments, PatternGate, CommandVerify,
role expression, DNA identity — all can ship as breaking changes with
standard review gate (audit + integration tests).
No more calendar-week locks. Future locks: agent-hours or
"until declared phases land."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:31:20 +08:00
Parfii-bot
4658297232
Merge feat/convergence-u3-provision — unify hetzner+vultr into Rust crate
2026-04-23 03:43:51 +08:00
Parfii-bot
e557b23809
Merge feat/convergence-u2-renames — capability renames + back-compat aliases
2026-04-23 03:43:51 +08:00
Parfii-bot
bf3e08b891
Merge feat/convergence-u1-blocks — shared blocks + 3 deprecations + /animate
2026-04-23 03:43:51 +08:00
Parfii-bot
64ffe39e01
feat(convergence/u3): kei-provision Rust crate — unify hetzner+vultr provisioners
...
Pre-unlock wave U3 (highest-ROI). Task 7 from CONVERGENCE-PLAN —
consolidate 2 provision-*.sh scripts into Rust via Backend trait.
Old shells (provision-hetzner.sh, provision-vultr.sh) had identical
6-subcommand surface (create|status|destroy|list), log/die/check_deps
helpers, idempotency contract. Sole delta: hcloud vs vultr-cli. RULE 0.2
says Rust-first when >50 LOC + growth expected.
New crate _primitives/_rust/kei-provision/:
- src/backend.rs (58 LOC) — Backend trait: create/status/destroy/list;
CreateOpts and ServerInfo structs
- src/backends/hetzner.rs (143 LOC) — shells to `hcloud server ...`
--output=json, parses JSON response, honors HCLOUD_TOKEN env (RULE 0.8)
- src/backends/vultr.rs (189 LOC) — same pattern, `vultr-cli instance`,
honors VULTR_API_KEY env
- src/exec.rs (100 LOC) — Command runner + PATH-aware env preservation
- src/b64.rs (49 LOC) — minimal user-data base64 encoder; zero
transitive deps
- src/main.rs (141 LOC) — clap CLI `kei-provision <backend> <cmd>`
- tests/backend_smoke.rs (184 LOC) — tempdir PATH-inject fake hcloud +
fake vultr-cli, no real cloud. Mutex-serialized (Rust test parallelism).
Tests: 11/11 (3 b64 unit + 8 backend_smoke integration). Coverage:
hetzner status present/absent/list, vultr status found/absent/destroy
idempotent, unknown-backend error, CreateOpts default.
Old shells kept with superseded-v0.17 header — install.sh still copies
them, legacy scripts still work. New users get kei-provision binary.
harden-base.sh untouched (different lifecycle — runs on target VPS).
Backend trait factored to accept aws/doctl/linode follow-ups without
re-architecture.
Workspace Cargo.toml: +kei-provision member (1 line).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:43:40 +08:00
Parfii-bot
e4b64418fc
feat(convergence/u2): capability renames + back-compat aliases
...
Pre-unlock wave U2. Task 3 from CONVERGENCE-PLAN — rename misleading
capability names, keep old names as deprecated aliases.
Renames:
- tools::read-only → tools::deny-tools (mechanism is tool-name denial,
not "read-only" metaphor)
- tools::cargo-only-bash → tools::bash-allowlist (mechanism is Bash
pattern allow-list; cargo-only is one config value)
Back-compat via registry.resolve_alias():
- Old dir _capabilities/tools/{read-only,cargo-only-bash}/ retained with
capability.toml-only stub: `alias = "<new-name>"` + `deprecated` field
- registry.rs loads alias stubs, redirects lookup before dispatch
- warn_deprecated_once() emits single-shot stderr per alias per process
via OnceLock<Mutex<HashSet>>
- Zero breaking change to existing manifests / task.toml referencing
old names
Rust impl files renamed in place:
- gates/tools_read_only.rs → gates/tools_deny_tools.rs (struct
DenyTools)
- gates/tools_cargo_only_bash.rs → gates/tools_bash_allowlist.rs
(struct BashAllowlist)
- gates/mod.rs + registry.rs + gate_smoke.rs updated
Roles updated (3): read-only.toml, explorer.toml, edit-local.toml —
reference new names directly.
Tests: kei-agent-runtime 41/41 (was 40, +1 deprecated_aliases_resolve
_to_new_names), _assembler 40/40 unchanged (substrate role expansion
follows new paths).
Docs updated: AGENT-ROLES.md, AGENT-SUBSTRATE-SCHEMA.md, 4 _manifests
referencing the old names (comment-only annotations).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:43:40 +08:00
Parfii-bot
1afb4bafa3
feat(convergence/u1): shared blocks + 3 skill deprecations + /animate gateway
...
Pre-unlock wave U1. Non-breaking DRY refactor per CONVERGENCE-PLAN tasks 1/2/4/5/6.
Created (5 files):
- _blocks/pipeline-5phase-template.md (54 LOC) — shared preamble for
ci-scaffold / auth-setup / observability-setup / docs-scaffold /
schema-design
- _blocks/rule-pure-click-contract.md (42 LOC) — AskUserQuestion
contract referenced across 5+ skills
- skills/animate/SKILL.md (67 LOC) — gateway router; AskUserQuestion
picks scroll / motion / web-effects / ai-animation, hands off to
picked skill
- skills/competitor-analysis/SKILL.md (35 LOC) — redirect stub to
/research --angle=competitors (did not exist; stub documents the
preset path per RULE -1 Constructive)
- skills/design-inspiration/SKILL.md (38 LOC) — same pattern,
--angle=design-refs
Deprecation headers added (3 skills):
- skills/site-builder/SKILL.md → "use /site-create" (subset)
- skills/competitor-analysis/SKILL.md → preset stub
- skills/design-inspiration/SKILL.md → preset stub
Reference blocks added (5 pipeline skills):
- skills/ci-scaffold, auth-setup, observability-setup, docs-scaffold,
schema-design — each +4 LOC pointer to pipeline-5phase-template.md
- skills/research — +18 LOC --angle presets table
LOC saved via shared blocks: ~96
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:43:40 +08:00
Parfii-bot
9732060788
Merge feat/convergence-plan — 75→52 consolidation roadmap
2026-04-23 03:28:39 +08:00
Parfii-bot
0b47616b06
docs(convergence): synthesis of 2 audit waves — 75 → ~52 primitives plan
...
User hypothesis: "if substrate is right, primitives converge, not expand".
Directional yes, magnitude calibrated below initial guess (75→35).
Reality: 75 → ~52 (−31%) across atoms + capabilities + skills.
Atoms (25 → 15-17):
- 6 SQLite-CRUD → 1 kei-entity-store + 6 plugins (piggyback Stream B)
- 3 provisioners → 1 kei-provision <backend> (harden-base stays)
- 4 frontend → meta-dispatcher shim
Capabilities (11 → 7):
- scope::files-{whitelist,denylist} → scope::path-filter (post-unlock)
- quality::cargo-{check,tests}-green → quality::cargo-green (post-unlock)
- Kept separate (justified): output::*, tools::*, policy::*
- Rename cargo-only-bash → bash-allowlist (pre-unlock, non-breaking)
Skills (39 → ~28):
- /audit <target> + checklists registry (6 skills → 1 + 6 data files)
- Deprecate /site-builder, /competitor-analysis, /design-inspiration
- Add /animate gateway (keep 4 motion-family skills underneath)
- Setup pipelines (ci/auth/obs/docs/schema) stay separate — domain-disjoint
Three techniques catalogued:
1. Verb parameterization (>50% LOC overlap)
2. Block extraction (shared preamble, different body)
3. Subset deprecation (one is proper subset of another)
Execution 3-phase:
- Pre-unlock (ship NOW): 7 tasks, ~3 days parallel — block extraction +
renames + deprecations + provisioner unification
- Mid-cycle (parallel Stream B): 2-3 days — entity-store engine
- Post-unlock (after 2026-06-03): 4 tasks, ~5-6 days — schema amendments
Honest gaps from both audits:
- 4 of 10 LBM-port crates not enumerated (follow-up)
- _capabilities/*.md not directly read by critic (manifest-inferred)
- LOC estimates ±20%
- Both audits E2-E4, not E1 (which requires running the consolidation)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:28:39 +08:00
Parfii-bot
6d6191fd13
Merge feat/orchestrator-wrapper — prepare CLI for ergonomic Agent spawn
2026-04-23 03:25:14 +08:00
Parfii-bot
d72ae51f16
feat(agent-substrate/wrapper): kei-agent-runtime prepare — orchestrator ergonomics
...
Single-command "prepare spawn" that emits everything orchestrator needs
to invoke the Agent tool: composed prompt, subagent_type (from role's
new claude-subagent-type field), isolation mode, verify command,
ledger row.
Before this: orchestrator ran compose + read prompt + manually
constructed Agent tool call + manually built verify command. 4 steps.
After: `kei-agent-runtime prepare <task.toml> --format=human` outputs
a single copy-paste-ready block. Orchestrator pastes into Agent tool
and records the verify command for return.
Files:
- src/prepare.rs (170 LOC) — prepare() returns AgentInvocation struct
(agent_id, prompt, subagent_type, isolation, description,
verify_command, ledger_row)
- src/main.rs (+39 LOC) — Prepare subcommand with --format=human|json|toml
- src/lib.rs (+2 LOC — pub mod prepare)
- _roles/*.toml (5 files) — new optional claude-subagent-type field:
- edit-local / edit-shared → "code-implementer"
- read-only → "critic" (default; "architect" override possible)
- explorer → "Explore"
- git-ops → "NOT-SPAWNABLE" (refused by prepare with RULE 0.13)
- tests/prepare_smoke.rs (3 tests) — happy path, unknown role, non-spawnable refusal
- docs/AGENT-SUBSTRATE-SCHEMA.md (+ ## Orchestrator ergonomics section)
Tests: 40/40 (was 37, +3 prepare_smoke). Same path exercised in tempfile
fixtures that the real CLI would hit end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:25:14 +08:00
Parfii-bot
abf2bcb30d
Merge feat/phase-5-agent-migration — substrate v1 FULL (5 agents migrated)
2026-04-23 03:07:18 +08:00
Parfii-bot
329d7e2a4d
feat(agent-substrate/phase-5): migrate 5 kit agents to role+task-spec — substrate v1 FULL
...
Final phase of agent substrate v1. 5 shipped agents now declare role at
manifest level; assembler expands role's capability text fragments into
the generated .md at a new `# AGENT SUBSTRATE — role <name>` section.
Non-migrated agents byte-identical (golden snapshots green).
Migrated agents:
- kei-code-implementer → edit-local (8 caps: no-git-ops + scope/* +
quality/* + safety::no-dep-bump + report-format)
- kei-critic → read-only (tools::read-only + output::report-format +
output::severity-grade)
- kei-architect → read-only
- kei-security-auditor → read-only
- kei-validator → read-only
_assembler/ extensions:
- manifest.rs: substrate_role: Option<String>
- assembler.rs: write_substrate() before blocks (backward-compat; no
role = no substrate section)
- substrate.rs (new, 102 LOC): loads _roles/<name>.toml, iterates
capabilities.required, reads _capabilities/<cat>/<slug>/text.md,
joins with \n\n---\n\n separator
- validator.rs: substrate role existence + cap-text presence check
- tests/substrate_role.rs (4 tests): happy path, unknown role, missing
capability text, byte-parity on non-migrated
- tests/regenerate_migrated.rs (ignored by default): regeneration gate
_templates/task-examples/ — 5 example task.toml per migrated agent
showing orchestrator the valid invocation shape.
docs/AGENT-SUBSTRATE-SCHEMA.md: Phase 5 row ticked ✓ + Migrated agents
subsection listing 5 agents with roles + pointer to examples.
tests/substrate_integration.sh: +8 Phase-5 assertions
- All 5 migrated .md files contain "# AGENT SUBSTRATE — role"
- kei-code-implementer.md contains "MUST NOT invoke git" (policy::no-git-ops)
- Every _templates/task-examples/*.toml parses as valid TOML
- cargo check --workspace still passes post-migration
- kei-agent-runtime compose works on edit-local-forge.toml example
Tests: assembler 40/40 (was 30, +4 substrate_role + +1 ignored regen),
kei-agent-runtime + kei-capability 37/37 preserved.
Deferred: remaining 7 non-core agents (cost-guardian, modal-runner,
fal-ai-runner, infra/ml-implementer, ml-researcher, researcher) migrate
in v0.24 wave.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:07:18 +08:00
Parfii-bot
bd32b48a54
Merge feat/phase-4-hook-wiring — PreToolUse glue for kei-capability
2026-04-23 02:51:11 +08:00
Parfii-bot
aa8333ccda
feat(agent-substrate/phase-4): hook wiring — 3-line glue for kei-capability
...
PreToolUse hooks route through kei-capability check when orchestrator
registers a capability via KEI_CAPABILITY_NAME env var on agent spawn.
hooks/agent-capability-check.sh (22 LOC):
- Pass-through (exit 0) when KEI_CAPABILITY_NAME unset — no-op by default
- Fail-open (exit 0) when kei-capability binary missing — kit convention
- Sources _lib/gate.sh for KEI_DISABLED_HOOKS / KEI_HOOK_PROFILE respect
- exec kei-capability check "$CAP_NAME" when active
hooks/agent-capability-verify.sh (24 LOC):
- Orchestrator-driven, NOT a Claude Code native hook
- Carries env: AGENT_ID, TASK_TOML, WORKTREE_PATH, MAIN_REPO, RUN_MODE
- exec kei-capability verify "$CAP_NAME"
Registered in hooks/hooks.json + settings-snippet.json under both
PreToolUse:Bash and PreToolUse:Edit|Write matchers. Internal NotApplicable
returns exit 0 so non-matching tool calls cost nothing.
install.sh unchanged — hooks/*.sh glob picks up both new files.
tests/hook_wiring_integration.sh (64 LOC) — 3 contract assertions:
(1) pass-through on unset KEI_CAPABILITY_NAME
(2) deny+exit 2 on git-op pattern
(3) allow+exit 0 on cargo-check pattern
Multi-capability routing (for phase 5): KEI_CAPABILITY_NAME currently
holds ONE name. When a role requires N capabilities, orchestrator will
either iterate or kei-capability gains a compose subcommand. Design
note left for phase 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 02:51:10 +08:00
Parfii-bot
d1a1a7d499
Merge github/main — readme rewrite lands alongside agent substrate v1 phases 1/2/3
2026-04-23 02:44:18 +08:00
Parfii-bot
28707a9a4a
Merge feat/phase-3-kei-agent-runtime — substrate runtime + kei-capability
2026-04-23 02:36:16 +08:00
Parfii-bot
12e5c27b3c
Merge feat/phase-2-role-matrix — 5 roles + AGENT-ROLES.md
2026-04-23 02:36:16 +08:00
Parfii-bot
a7b3e39fa8
Merge feat/phase-1-capability-library — 11 capability bundles
2026-04-23 02:36:16 +08:00
Parfii-bot
b82e3b039e
feat(agent-substrate/phase-3): kei-agent-runtime + kei-capability binaries
...
Two new crates implementing the substrate runtime per locked §Runtime
execution contract + §Capability trait contract (Rust) + §Verify
execution worktree→simulated-merge.
kei-agent-runtime — library + CLI binary:
- src/capability.rs — Capability trait (name/check/verify) + GateContext
+ GateDecision + VerifyContext + VerifyResult + RunMode + TaskSpec
- src/registry.rs — &str → &'static dyn Capability dispatch for 14 impls
- src/gates/ — 6 PreToolUse modules (policy::no-git-ops,
scope::files-{whitelist,denylist}, safety::no-dep-bump,
tools::read-only, tools::cargo-only-bash)
- src/verifies/ — 8 on-return modules (quality::constructor-pattern,
quality::cargo-check-green, quality::tests-green, safety::no-dep-bump,
scope::files-{whitelist,denylist}, output::{report-format,severity-grade})
- src/compose.rs — task.toml + role + capabilities → prompt.md
- src/spawn.rs — ledger fork + prompt write (actual Agent invocation
remains orchestrator's tool call)
- src/verify.rs — runs all capability verifies per role; collects
VerifyReport {passed, failed}
- src/simulated_merge.rs — git worktree add test-merge/<id> + apply diff
+ run verify; cleanup on Drop
- src/main.rs — clap CLI: compose | spawn | verify | run
kei-capability — thin CLI adapter crate:
- Depends on kei-agent-runtime path dep
- Subcommand `check <cap-name>` (PreToolUse gate; stdin JSON, exit 0|2)
- Subcommand `verify <cap-name>` (on-return; env-driven, exit 0 or fail)
- Pattern: shell hook = 3-line `exec kei-capability check "$CAP_NAME"`
Workspace Cargo.toml: both crates registered as members (under agent
substrate v1 marker).
cargo check --workspace: PASS
cargo test -p kei-agent-runtime: 37/37 green
- 6 capability_trait_smoke (registry lookups, unknown name → None)
- 3 compose_smoke (fixture role + caps → composed prompt)
- 12 gate_smoke (each gate: happy + deny + bypass)
- 4 simulated_merge_smoke (git worktree lifecycle)
- 12 verify_smoke (each verify: pass + fail + edge cases)
cargo test -p kei-capability: 0/0 (CLI binary, tested via lib)
(Agent completion report cut off by rate-limit at 60 tool-uses; code
itself is green — verified by orchestrator post-commit.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 02:35:53 +08:00