1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//! # persona-wire-core
//!
//! Transport-agnostic core for the `persona-wire` graph engine. The crate's
//! value proposition is **ProjectionAsPrompt**: turn an arbitrary
//! [`Specification`](domain::specification::Specification) over a small
//! property graph into a rendered string (Prompt / Markdown / JSON / ASCII)
//! by binding it to a registered template, then concatenate one or more such
//! renderings into a wake-time prompt context.
//!
//! No MCP or CLI dependencies — `persona-wire-mcp` and the unified
//! `persona-wire` binary both depend on this crate and adapt their own
//! transport surfaces on top of the use cases exported here.
//!
//! ## Layer split (DDD + Hexagonal)
//!
//! - [`domain`] — Entities, Value Objects, and business rules. Pure code with
//! no I/O.
//! - [`domain::graph`] — `Node` / `Edge` / `Severity`. The persistent
//! graph entities. `Node.metadata` is a free-form `serde_json::Value`,
//! which is what every higher layer queries against.
//! - [`domain::specification`] — composable predicate
//! (`TypeIs` / `MetadataEq` / `Reachable` / `And` / `Or` / `Not`). The
//! canonical [Specification pattern][spec-bp] applied to the graph: each
//! variant is a tiny domain object; combinators (`and` / `or` / `not`,
//! plus `std::ops::Not`) build composite predicates at runtime.
//! [`Specification::is_satisfied_by`](domain::specification::Specification::is_satisfied_by)
//! evaluates the predicate against one [`Node`](domain::graph::Node).
//! - [`domain::error`] — `WireError` / `WireResult` shared across the crate.
//! - [`domain::autoversion`] — versioning of registered entities.
//! - [`domain::repository`] — the repository trait surface that the
//! infrastructure layer implements.
//!
//! - [`application`] — Use cases and registries. Coordinates the domain and
//! infrastructure layers; this is the API surface that transport adapters
//! target.
//! - [`application::spec_registry::SpecRegistry`] — persistent **registry**
//! of named [`Specification`](domain::specification::Specification) values.
//! `register` / `get` / `list`, JSON-serialised in the `specifications`
//! table.
//! - [`application::projection_registry::NamedProjection`] /
//! [`application::projection_registry::ProjectionRegistry`] — **CQRS Read
//! Model**: a `NamedProjection` is a `(name, spec_ref, template,
//! target_form)` tuple. `spec_ref` points at an entry in `SpecRegistry`;
//! `template` is a handlebars body; `target_form` is one of
//! [`Prompt` / `Markdown` / `Json` / `Ascii`](application::projection_registry::TargetForm).
//! The registry persists projections in the `projections` table — there
//! is **no hard-coded projection list anywhere in the crate**, every
//! projection is data.
//! - [`application::merger::MergeStrategy`] — combine an overlay template
//! into a base template (`Replace` / `Append` / `Prepend` /
//! `Section(name)`). `Section` substitutes `{{!-- <name> --}}` markers
//! and falls back to `Append` when the marker is absent.
//! - [`application::persona_pack_resolver`] — read template overlays from
//! `~/persona-pack/<id>/prompt.toml` (or `$PERSONA_PACK_ROOT`) under
//! `[extra.persona_wire.projections.<axis>]`. The resolver returns only
//! overlays (template / target_form / strategy); the source-of-truth for
//! wiring entries stays in the graph.
//! - [`application::use_cases`] — the high-level operations
//! (`wire_init` / `wire_close` / `wire_doctor` / `wire_query` /
//! `wire_render` / `wire_prompt_context` / batch creators / deleters).
//!
//! - [`infrastructure`] — Adapters bound to a concrete backend.
//! - [`infrastructure::storage::SqliteStorage`] — SQLite implementation of
//! the repository surface (`nodes` / `edges` / `specifications` /
//! `projections` tables and a `type_registry` for the open vocabulary).
//! - [`infrastructure::rendering`] — handlebars template engine over the
//! query-result context. Behaves like a Mustache superset
//! (`{{var}}`, `{{#each list}}…{{/each}}`, `{{#if cond}}…{{/if}}`,
//! dotted paths) and emits a visible `{{render-error: …}}` prefix on
//! parse failure instead of panicking or silently fallback-ing.
//! - [`infrastructure::adapter`] — Layer 6 **SoT Adapter**. Each axis
//! wiring entry carries a `metadata.source_uri`; the
//! [`PluginRegistry`](application::plugin_registry::PluginRegistry)
//! dispatches by scheme prefix to an `Arc<dyn Adapter>`:
//! - `file://<path>` / `file:<path>` → `FileAdapter` (`std::fs` with
//! `~/` expansion; for a directory it picks the newest mtime child).
//! - `mini-app://<table>` → `MiniAppAdapter` (external crate
//! `persona-wire-adapter-mini-app`; consumer wires it on top of
//! [`PluginRegistry::default_builder_for_wire`](application::plugin_registry::PluginRegistry::default_builder_for_wire)).
//!
//! ## Two query axes
//!
//! Wire exposes two complementary axes; both are first-class:
//!
//! - **Dynamic axis** — caller supplies an inline
//! [`Specification`](domain::specification::Specification) and gets the
//! matching nodes back via
//! [`wire_query`](application::use_cases::wire_query). Good for ad-hoc
//! inspection, scripts, and one-off filters.
//! - **Fixed axis** — caller registers a `(spec, template, target_form)` as a
//! [`NamedProjection`](application::projection_registry::NamedProjection)
//! and refers to it by `spec_ref` / `projection_ref`. Good for stable
//! surfaces such as wake-time injection.
//!
//! ## Render flow (`wire_render`)
//!
//! ```text
//! ProjectionRegistry.get(name)
//! → NamedProjection { spec_ref, template, target_form }
//! │
//! │ spec_ref
//! ▼
//! SpecRegistry.get(spec_ref)
//! → Specification (TypeIs / MetadataEq / And / Or / Not / Reachable)
//! │
//! │ Specification::is_satisfied_by
//! ▼
//! collect_matching_nodes(storage, spec) → Vec<Node>
//! │
//! │ context build: { count, nodes, entries, … }
//! ▼
//! rendering::render(target_form, template, context)
//! → String (Prompt / Markdown / JSON / ASCII)
//! ```
//!
//! ## PromptContext flow (`wire_prompt_context`)
//!
//! Persona-scoped one-shot entry intended for wake-time auto-load:
//!
//! 1. Read the optional `[extra.persona_wire.projections.<axis>]` overlays
//! for the persona (best-effort; missing persona-pack is silently
//! tolerated).
//! 2. Discover the persona's axes by querying the graph with a
//! `Specification` (`TypeIs("outline_node")` AND
//! `MetadataEq("persona", <persona_id>)`). The axis list is therefore
//! **data, not code** — adding an axis is a graph insert.
//! 3. For each axis, look up the base
//! [`NamedProjection`](application::projection_registry::NamedProjection)
//! by the conventional name `<persona_id>.section.<axis>`. If an overlay
//! is present, run `MergeStrategy::merge(base, overlay)`. Fetch the axis
//! payload through the Layer 6 Adapter via the wiring entry's
//! `source_uri`, then render the block.
//! 4. Concatenate the rendered blocks into a single `prompt_context` string.
//! `projection_names: Some([...])` restricts the walk to an explicit
//! subset; `None` walks every registered axis for the persona.
//!
//! No template content is hard-coded inside this crate. The set of axes, the
//! base templates, and the optional overlays are all data managed through
//! the regular registry / persona-pack surfaces.
//!
//! ## Persistence schema (SQLite, set up by
//! [`SqliteStorage::migrate`](infrastructure::storage::SqliteStorage::migrate))
//!
//! - `type_registry(name TEXT PK, kind TEXT, schema_json TEXT, severity_allowed TEXT)`
//! - `nodes(id TEXT PK, type TEXT FK→type_registry.name, sot_ref TEXT?, confidence REAL?, …, metadata TEXT)`
//! - `edges(id TEXT PK, src_node TEXT FK→nodes.id, tgt_node TEXT FK→nodes.id, kind TEXT FK→type_registry.name, severity TEXT?, metadata TEXT, …)`
//! - `specifications(name TEXT PK, expr_json TEXT, created_at INTEGER)`
//! - `projections(name TEXT PK, spec_ref TEXT, template TEXT, target_form TEXT, created_at INTEGER)`
//! - `versions(…)` — autoversion ledger.
//! - `workflow_runs(…)` — reserved for the workflow engine layer.
//!
//! The graph vocabulary is **open** but type-checked: any `Node` or `Edge`
//! must reference a row in `type_registry`. The default seed is loaded by
//! [`SqliteStorage::seed_default_types`](infrastructure::storage::SqliteStorage::seed_default_types).
//!
//! ## Design rationale
//!
//! The sections below collect the architecture-level decisions that were
//! previously drafted in `docs/design/*.md` while the layout was being
//! shaped. The design docs are now retired — this is the SoT.
//!
//! ### Three-layer split (Math backend / Domain Entity / thin Application)
//!
//! 1. **Math backend Graph** ([`domain::graph`]) — open-vocabulary primitives
//! (Node / Edge / Severity / Specification / CRUD / Compute / Constraint /
//! AutoVersion / Repository). Tenant-agnostic and persona-agnostic; used
//! as a backend SDK. It does not know about personas, slots, sources, or
//! projections.
//! 2. **Domain Entity Layer** ([`domain::entity`]) — persona-wire's
//! first-class vocabulary (`PersonaId` / `Slot` / `Source` / `Wiring` /
//! `Workflow` / `Projection`). Owns invariants and behavior; uses the
//! Math backend as a persistence SDK. Aggregate composition is documented
//! in [`domain::entity`] module docs.
//! 3. **Application Layer** ([`application`]) — thin orchestrator that lifts
//! Domain Entity operations onto the use-case surface that transport
//! adapters (MCP / CLI / future RPC) target. Knowledge gravitates to
//! layer 2; this layer stays slim.
//!
//! ### Slot vocabulary (was `axis`)
//!
//! The persona-context binding identifier is now named `Slot`. Earlier code
//! used the AI-jargon word `axis`, which collided with three legitimately
//! orthogonal uses of "axis" elsewhere in the crate:
//!
//! - Graph compute primitive — Traversal / Execution / Constraint axes
//! (3 orthogonal operation kinds).
//! - Doctor diagnostic surface — `Axis::{Graph, Workflow}` (2 health axes).
//! - Plugin registry — Adapter / Engine / Projection plugin axes
//! (3 orthogonal slots).
//!
//! In contrast, persona context values like `mailbox` / `mail` / `news` are
//! not orthogonal axes — they are slot names. Decisive evidence (recorded
//! when the rename was decided) included the storage projection name
//! template `"{persona_id}.section.{axis}"` ("section の中の axis 値"
//! structure = `axis` is a name, not a classifier) and the error message
//! `"must contain at least one axis name"` (the word "name" gives the
//! game away).
//!
//! The rename landed in two cuts:
//!
//! 1. Domain Entity layer carries [`domain::entity::Slot`] as a first-class
//! Value Object on [`domain::entity::wiring::Wiring`].
//! 2. The storage metadata key is still literally `"axis"` (legacy SQLite
//! rows). [`application::wiring_mapper`] is the single translation
//! boundary: `Slot ↔ Node.metadata["axis"]`. New code routes through the
//! mapper; reading `metadata["axis"]` directly is disallowed.
//!
//! ### PoEAA Registry vs DDD Repository (Projection)
//!
//! Projection persistence goes through
//! [`application::projection_registry::ProjectionRegistry`], a PoEAA
//! Registry (Fowler PoEAA Ch.18) — an application-layer service that
//! provides named access to well-known objects as a structured alternative
//! to global access. A DDD Repository (Evans DDD Ch.6 / Vernon IDDD Ch.12)
//! was considered and **not adopted**: it would move persistence vocabulary
//! into a domain port and collapse the application service into a pass-through.
//!
//! [`application::wiring_mapper`] and [`application::workflow_mapper`] are
//! sibling Data Mappers invoked directly from use cases against the Math
//! backend Node repository — Wiring and Workflow do not get their own
//! Registry because they are persisted as graph nodes, not as a separately
//! named table.
//!
//! ### Data Mapper — narrow reading
//!
//! Fowler PoEAA's Data Mapper (Ch.10) requires *some* mapper that translates
//! between persistence shape and Domain shape. The literal pattern uses an
//! independent Mapper class; persona-wire takes the **narrow** reading and
//! lets the Registry (Projection case) or the use case (Wiring / Workflow
//! cases) own the mapper bridge through the
//! [`application::projection_mapper`] / [`application::wiring_mapper`] /
//! [`application::workflow_mapper`] modules. Promoting these to a literal
//! Fowler Mapper trait is a carry that fires only when a second parallel
//! mapper with the same shape arrives. Until then, the free functions
//! intentionally do not sit behind a trait.
//!
//! ### DDD / BP citation table
//!
//! Best-practice citations carry an honest **literal / narrow / 独自整理**
//! tag so future readers can tell which parts of the BP are quoted verbatim
//! and which are persona-wire's own extension.
//!
//! | BP | Where applied | Tag |
//! |---|---|---|
//! | Fowler PoEAA Ch.10 — Data Mapper | Projection / Wiring / Workflow mappers | **narrow** — persistence-vs-domain separation literal; the independent Mapper class is replaced by Registry / use-case owners |
//! | Fowler PoEAA Ch.18 — Registry | `ProjectionRegistry` named lookup | **literal** |
//! | Evans DDD Ch.9 — Specification | [`domain::graph::specification`] — VO + `is_satisfied_by` + and/or/not algebra | **literal** |
//! | Vernon IDDD Ch.10 Rule 2 — Small Aggregates | Single-transaction consistency boundary, not field count | **narrow** — boundary axis only; field-count framing was explicitly excluded |
//! | Vernon IDDD Ch.10 Rule 3 — Reference by Identity | `Projection` references `Specification` via `SpecName` instead of embedding | **literal** |
//! | Fowler bliki 2003 — Anemic Domain Model anti-pattern | Domain Entity owns invariants; DTOs (`NamedProjection`) are intentionally anemic at the persistence boundary | **narrow** — anti-pattern scope limited to the domain layer; the DTO exclusion is an interpretive extension |
//! | CQRS Read Model | Projection's `(name, source-query, transform, output)` 4-concern decomposition | **独自整理** — the 4-tuple shape itself is persona-wire's framing, not literal in Greg Young's original |
//! | Yaron Minsky — Make Illegal States Unrepresentable (Effective ML Revisited) | `PluginDispatch` enum collapses `(engine, kind, config)` Option triple to a `Default` / `Custom { .. }` discriminated union | **narrow** — the slogan and discriminated-union pattern are literal; the 2^N arithmetic phrasing is persona-wire's gloss |
//!
//! [spec-bp]: https://en.wikipedia.org/wiki/Specification_pattern
pub use AdapterInfo;
pub use WireError;
pub use WireResult;
pub use ;