Skip to content

Commit 0edbf0e

Browse files
committed
export: public tree rebuilt from 917c751
Built by plans/export-fixtures/make_export.py from tracked master blobs only.
1 parent b0d048b commit 0edbf0e

21 files changed

Lines changed: 824 additions & 31 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ TELEGRAM_BOT_TOKEN=123456789:AA-your-bot-token-here
3535
SEARCH_ENDPOINT=http://127.0.0.1:8888/search?format=json
3636
SEARCH_API_KEY=
3737

38-
# --- memory-hive (only if you instantiate `memory-hive@1.2.0`) ---
38+
# --- memory-hive (only if you instantiate `memory-hive@1.3.0`) ---
3939
# Four of these have no default and the instantiation rejects without them: a
4040
# model id has no defensible default, and picking one silently is how a memory
4141
# lane ends up on a weak model without anyone deciding it.

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@ documented `error_code` strings (README § Stability). Anything that breaks one
99
them is listed under **Breaking** in its release, with the migration named. The
1010
Rust crates are internals and move without notice.
1111

12+
## [0.10.2] — 2026-08-16
13+
14+
### Added
15+
16+
- **`params.required_drains`: a hive can declare which of its ports come in
17+
pairs** (#147). Read as *if anything outside this hive wires into this port,
18+
then this port must have an edge that carries the declared hop out of the
19+
hive*. A mutation that opens the ingress alone is rejected with
20+
`required_drain_missing`, pre-destructively, and the rejection carries the
21+
hive's own sentence about what the pairing protects.
22+
23+
The case it comes from: the memory hive refuses an inline extraction block by
24+
sending it out a reject egress. With nothing consuming that egress the refusal
25+
is a dead end, and the write that never happened is never reported. The README
26+
said "not optional once the inline ingress is wired" in bold. Bold is not a
27+
check.
28+
29+
Two things make the rule usable rather than annoying. It **asks the router**:
30+
the declared hop is run through `apply_edges`, the same function that routes
31+
the real message, so `hop.route=='reject'`, `hop.route in ['reject','error']`
32+
and `hop.route != 'bundle'` are all recognised as drains — a comparison of
33+
condition text would call two of those broken. And it runs against the
34+
**post-state**, so putting the ingress and the drain in one mutation is the
35+
answer, not a workaround.
36+
37+
Opt-in like `params.ports`: a hive that declares nothing behaves exactly as
38+
before. The **bootstrap warns rather than refuses**, for the same reason the
39+
port seal leaves the boot alone — the birth topology is authorship, and a tree
40+
that has been running for weeks should not be stopped from starting.
41+
42+
`memory-hive` ships the declaration for both of its reject egresses
43+
(`memory-hive@1.3.0`).
44+
1245
## [0.10.1] — 2026-08-16
1346

1447
### Fixed

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ members = [
1111
]
1212

1313
[workspace.package]
14-
version = "0.10.1"
14+
version = "0.10.2"
1515
edition = "2024"
1616
publish = true
1717
rust-version = "1.85"

ROADMAP.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,12 @@ Still open around it:
109109
recall window, so time-range questions run as point recalls,
110110
[#47](https://github.com/mmeyerlein/meclaw/issues/47) the async cell shutdown
111111
drain,
112-
[#147](https://github.com/mmeyerlein/meclaw/issues/147) wiring the inline ingress
113-
without its reject drain fails silently — a wiring-time check, deliberately held
114-
for the architecture pass that makes inline extraction a system-wide property
115-
rather than a per-hive feature.
112+
[#147](https://github.com/mmeyerlein/meclaw/issues/147) is closed in 0.10.2, and
113+
not as the per-hive feature it was held for: a hive declares which of its ports
114+
come in pairs (`params.required_drains`), and the mutation that wires the
115+
ingress without the drain is refused. The architecture pass that makes inline
116+
extraction a system-wide property is unaffected — it inherits a rule instead of
117+
having to invent one.
116118

117119
## The wave before the launch
118120

@@ -154,14 +156,18 @@ The good first issues from the first public wave — #3 and #4 — shipped with
154156
template is a directory, a README and a `template.json`, and the fourteen listed
155157
in [`templates/README.md`](templates/README.md) are the worked examples — eleven
156158
single-purpose ones plus three composites: `talky@1.2.0`, which carries four of
157-
them as sub-units, `cogny@1.3.0`, which carries two, and `memory-hive@1.2.0`, the
159+
them as sub-units, `cogny@1.3.0`, which carries two, and `memory-hive@1.3.0`, the
158160
agent memory as a hive of ten cells. New ones are welcome.
159161

160162
## Shipped
161163

162164
One line per release; details in [CHANGELOG.md](CHANGELOG.md) and the
163165
[GitHub releases](https://github.com/mmeyerlein/meclaw/releases).
164166

167+
- **v0.10.2 — a wired port must have its drain.** A hive can declare which of
168+
its ports come in pairs, and a mutation that wires the ingress without the
169+
egress is refused rather than quietly opening a lane that loses messages. The
170+
check asks the router, not the condition's spelling.
165171
- **v0.10.1 — an edge can be replaced again.** `remove_edges` now runs before
166172
`add_edges`, so dropping a lane and adding its widened replacement in ONE
167173
mutation does what it reads like. The other way round it deleted its own new

crates/meclaw-api/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ serde_json = "1"
1818
tokio = { version = "1", features = ["sync", "macros", "time"] }
1919
tracing = "0.1"
2020
anyhow = "1"
21-
meclaw-core = { path = "../meclaw-core", version = "0.10.1" }
22-
meclaw-colony = { path = "../meclaw-colony", version = "0.10.1" }
21+
meclaw-core = { path = "../meclaw-core", version = "0.10.2" }
22+
meclaw-colony = { path = "../meclaw-colony", version = "0.10.2" }
2323

2424
[dev-dependencies]
2525
tempfile = "3"

crates/meclaw-cells/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ repository = "https://github.com/mmeyerlein/meclaw"
88
license.workspace = true
99

1010
[dependencies]
11-
meclaw-core = { path = "../meclaw-core", version = "0.10.1" }
12-
meclaw-colony = { path = "../meclaw-colony", version = "0.10.1" }
11+
meclaw-core = { path = "../meclaw-core", version = "0.10.2" }
12+
meclaw-colony = { path = "../meclaw-colony", version = "0.10.2" }
1313
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "sync", "time", "process", "io-util"] }
1414
tracing = "0.1"
1515
serde = { version = "1", features = ["derive"] }

crates/meclaw-cli/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ console-subscriber = "0.4"
2121
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "time"] }
2222
serde_json = "1"
2323
uuid = { version = "1", features = ["v7"] }
24-
meclaw-api = { path = "../meclaw-api", version = "0.10.1" }
25-
meclaw-cells = { path = "../meclaw-cells", version = "0.10.1" }
26-
meclaw-colony = { path = "../meclaw-colony", version = "0.10.1" }
27-
meclaw-core = { path = "../meclaw-core", version = "0.10.1" }
24+
meclaw-api = { path = "../meclaw-api", version = "0.10.2" }
25+
meclaw-cells = { path = "../meclaw-cells", version = "0.10.2" }
26+
meclaw-colony = { path = "../meclaw-colony", version = "0.10.2" }
27+
meclaw-core = { path = "../meclaw-core", version = "0.10.2" }
2828

2929
[dev-dependencies]
3030
tempfile = "3"

crates/meclaw-colony/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license.workspace = true
1010
[dependencies]
1111
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "sync", "time", "fs", "io-util"] }
1212
tracing = "0.1"
13-
meclaw-core = { path = "../meclaw-core", version = "0.10.1" }
13+
meclaw-core = { path = "../meclaw-core", version = "0.10.2" }
1414
serde = { version = "1", features = ["derive"] }
1515
serde_json = "1"
1616
rusqlite = { version = "0.39", features = ["bundled"] }

crates/meclaw-colony/src/bootstrap_apply.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,51 @@ pub async fn bootstrap_from_filesystem_with_env(
613613
}
614614
return Err(errors);
615615
}
616-
Ok(apply_bootstrap_plan(plan, factories, runtime).await)
616+
let report = apply_bootstrap_plan(plan, factories, runtime).await;
617+
warn_on_missing_drains_after_boot(root, runtime).await;
618+
Ok(report)
619+
}
620+
621+
/// GH #147, the boot half: say it out loud when a hive port that declared a
622+
/// paired drain is wired without one.
623+
///
624+
/// A warning, never a refusal — the mutation path is where this rule bites,
625+
/// because that is somebody changing a colony they did not necessarily build.
626+
/// The birth topology is authorship, the same reason the port boundary leaves
627+
/// the bootstrap alone (GH #133).
628+
///
629+
/// Runs after apply, so it sees the topology the colony actually woke up with —
630+
/// including the edges rehydrated from `colony.db`, which the plan does not
631+
/// carry.
632+
async fn warn_on_missing_drains_after_boot(root: &std::path::Path, runtime: &ColonyRuntime) {
633+
let (ack_tx, ack_rx) = oneshot::channel();
634+
if runtime
635+
.inbox_tx
636+
.send(ColonyMsg::ReadGraph {
637+
scope: meclaw_core::Path::new("/"),
638+
ack: ack_tx,
639+
})
640+
.await
641+
.is_err()
642+
{
643+
return;
644+
}
645+
let Ok(graph) = ack_rx.await else {
646+
return;
647+
};
648+
let hive_paths: Vec<meclaw_core::Path> = graph
649+
.nodes
650+
.iter()
651+
.filter(|n| n.cell_type == "hive")
652+
.map(|n| meclaw_core::Path::new(&n.path))
653+
.collect();
654+
let reqs = crate::mutation::required_drains::collect_required_drains(root, hive_paths.iter());
655+
let edges: Vec<(String, String, Option<String>)> = graph
656+
.edges
657+
.iter()
658+
.map(|e| (e.from.clone(), e.to.clone(), e.condition.clone()))
659+
.collect();
660+
crate::mutation::required_drains::warn_on_missing_drains(&reqs, &edges);
617661
}
618662

619663
/// Snapshot the set of registered node paths from the running colony (A8).

0 commit comments

Comments
 (0)