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>
Pilot refactor per locked substrate schema. kei-task migrated to atom
layout:
- atoms/<verb>.md — YAML frontmatter + human body for 3 verbs
- atoms/schemas/<verb>-{input,output}.json — JSON Schema draft-07
- src/atoms/<verb>.rs — typed Input/Output/Error + pub fn run()
- src/atoms/mod.rs — module registry
- Cargo.toml [package.metadata.keisei] — crate-level substrate data
- src/main.rs — dispatcher for 3 pilot commands via atoms::
Zero behaviour change: 7/7 integration tests pass before and after
(create_and_get, update_persists, cycle_detected, milestone_linking,
dependency_chain_traversal, task_graph_edges, search_finds_task).
main.rs still has 5 non-migrated subcommands (update, graph,
dependency-chain, milestone, link-milestone) — scope discipline, they
migrate in later passes. main.rs 120 → 132 LOC.
Stream B pilot reference — other crates follow this pattern in v0.24+.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>