Added [primitive.*] entries for kei-agent-runtime, kei-capability,
kei-provision, kei-entity-store, kei-pipe, kei-cache, kei-spawn,
kei-replay. Profile memberships:
- ops: +kei-provision (total 9)
- dev: +7 substrate+automation primitives (total 17)
- full: +8 (total 46)
docs/INSTALL.md + README.md updated with new counts.
Not registered (lib-only, no main.rs): kei-atom-discovery.
Flag for follow-up: kei-forge + kei-runtime are in workspace but not
in MANIFEST (were before my scope). regen-counts.sh will soft-warn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps pure (query/transform) atom invocations with SHA-256 keyed
cache. Refuses Command/Stream kind atoms as unsafe.
22/22 tests (14 unit + 8 integration). Canonical JSON keying
(formatting-drift safe). TTL expiry. AtomExecutor trait decouples
subprocess from test mocks.
Default DB ~/.claude/cache/cache.sqlite, overridable via --db or
$KEI_CACHE_DB.
Workspace Cargo.toml: +kei-cache member.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5/5 tests preserved. Synthetic nodes PK table via engine; cross_edges
stays in custom_migrations because engine's TextPair is too minimal
(id/weight/evidence/metadata columns needed).
Flag for engine follow-up: TextPair DDL needs optional edge metadata
columns — same gap flagged by M5 independently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>