From 82968869410dbf15bb6315cc4d53aac8a5b302de Mon Sep 17 00:00:00 2001 From: Adil Date: Thu, 29 Oct 2026 01:08:39 +0500 Subject: [PATCH] hum-nest: a cell that grew on from its graft grew no bloom of its own A delivery, a prompt with no words, raises a cell that grows the graft's last bloom on. The nest counted that cell's wilt as one more bloom than its graft held, so the caller's next prompt, whose graft holds the same blooms, looked like it carried a bloom the cell never grew: the cell was thrown away and a fresh one raised. Every delivery cost two restarts of claude, resuming the same session, where it needs one. A cell raised to grow on now starts one bloom short of its graft, so its wilt brings it level, and the next prompt is fed to it. --- hum-nest/src/serve.rs | 49 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/hum-nest/src/serve.rs b/hum-nest/src/serve.rs index 2427d64b..7e8cc67a 100644 --- a/hum-nest/src/serve.rs +++ b/hum-nest/src/serve.rs @@ -15,11 +15,12 @@ //! wire carries it, so the tools a cell lists are its sigil's and its //! reaches carry its sigil's sid. //! - **A live cell**: a cell is raised for how its blooms grow, its -//! prompt's `trellis` and `sepal`. It is fed the next prompt of its -//! sigil only while it is not cancelled, its nurse is not -//! [`ephemeral`], and that prompt grows the same way, has words, and its -//! `graft` holds no bloom the cell did not grow; otherwise a fresh cell -//! is raised. A sigil raises one cell at a time: its next prompt waits +//! prompt's `trellis` and `sepal`. It is fed the next prompt of its sigil +//! only while it is not cancelled, its nurse is not [`ephemeral`], and +//! that prompt grows the same way, has words, and its `graft` holds no +//! bloom the cell did not grow; otherwise a fresh cell is raised. A cell +//! that grew on from its graft grew its graft's last bloom on, not a bloom +//! of its own. A sigil raises one cell at a time: its next prompt waits //! for the cell being raised. A cell that outlives its nest's //! `bounds.wallClockMs` is evicted. //! - **Growing on**: a prompt with no words grows on from its graft, so it @@ -355,7 +356,8 @@ async fn handle_prompt( let cancel = cell.silence.clone(); let wilted = Arc::new(AtomicBool::new(false)); let last_touched = Arc::new(AtomicU64::new(now_ms())); - let grown = Arc::new(AtomicU64::new(grafted)); + let held = if worded { grafted } else { grafted.saturating_sub(1) }; + let grown = Arc::new(AtomicU64::new(held)); let id = RAISED.fetch_add(1, Ordering::SeqCst); let dispatch = { @@ -626,7 +628,8 @@ mod tests { struct Raised { stilled: CancellationToken, fed: std::sync::Mutex>, - _alive: (mpsc::Sender, tokio::sync::oneshot::Sender), + says: mpsc::Sender, + _exit: tokio::sync::oneshot::Sender, } impl Raised { @@ -648,6 +651,7 @@ mod tests { #[async_trait::async_trait] impl Nurse for Fake { fn ephemeral(&self) -> bool { self.ephemeral } + fn grows_on(&self) -> bool { true } async fn raise(&self, _egg: Egg) -> Result { tokio::time::sleep(self.raising).await; @@ -658,7 +662,8 @@ mod tests { self.raised.lock().unwrap().push(Arc::new(Raised { stilled: silence.clone(), fed: std::sync::Mutex::new(rx_feed), - _alive: (tx_mmm, tx_exit), + says: tx_mmm, + _exit: tx_exit, })); Ok(crate::Cell { mark: None, feed: tx_feed, mmm: Arc::new(Mutex::new(rx_mmm)), emerged: rx_exit, ephemeral: self.ephemeral, silence }) } @@ -680,11 +685,15 @@ mod tests { } fn prompt(&self, text: &str) -> impl std::future::Future> + 'static { + self.tone(json!({ "chi": "prompt", "sid": "s", "text": text })) + } + + fn tone(&self, tone: Value) -> impl std::future::Future> + 'static { handle_prompt( self.nurse.clone(), Heard::default(), self.cells.clone(), self.gates.clone(), Arc::new(NestConfig::default()), "http://127.0.0.1:0".into(), std::env::temp_dir().to_string_lossy().into_owned(), "m".into(), - json!({ "chi": "prompt", "sid": "s", "text": text }), + tone, ) } @@ -730,6 +739,28 @@ mod tests { assert_eq!(fed, ["one", "two"]); } + #[tokio::test] + async fn a_cell_that_grew_on_from_its_graft_is_fed_the_next_prompt() { + let nest = Hosted::new(Fake::default()); + let bloom = json!({ + "content": [{ "kind": "text", "text": "start the build" }], + "grew": [{ "kind": "tendril", "callId": "c", "name": "check" }, { "kind": "grasp", "callId": "c", "result": "done" }], + }); + nest.tone(json!({ "chi": "prompt", "sid": "s", "graft": [bloom] })).await.unwrap(); + nest.raised()[0].says.send(json!({ "chi": "wilt", "reason": "stop" })).await.unwrap(); + tokio::time::timeout(std::time::Duration::from_secs(2), async { + while !nest.cells.lock().await.peek("s").expect("a live cell").wilted.load(Ordering::SeqCst) { + tokio::time::sleep(std::time::Duration::from_millis(5)).await; + } + }) + .await + .expect("the cell wilts its bloom"); + nest.tone(json!({ "chi": "prompt", "sid": "s", "text": "and now?", "graft": [bloom] })).await.unwrap(); + let raised = nest.raised(); + assert_eq!(raised.len(), 1, "the bloom it grew on is the graft's last, so the next prompt is its"); + assert_eq!(raised[0].fed(), ["and now?"]); + } + #[tokio::test] async fn a_cancelled_cell_is_not_fed_again() { let nest = Hosted::new(Fake::default());