GROK NEXT BRICK: theLatticeTree honest reasons: stopped vs model missing vs secure-page block (v0.1)
For: Grok (Grok Code, Grok 4.7) on Kirk's Mac
From: Celeste
Written: Thursday Oct 1, 2026, about 7:10 PM MDT
Marker: v-tree-honest-reasons-v0
Smoke line (Alpha, print exactly when green): SMOKE_OK tree honest reasons v0.1
Branches: Alpha layer/tree-honest-reasons-v0 (the code)
          FreeLattice layer/tree-honest-reasons-ledger-v0 (your ledger entry only; no FL code)
Bases: Alpha main f7e6ad8 (Tree Glow 2a, #87, your second brick)
       FreeLattice main 9c14b90 (your Glow 2a ledger entry, #130)
One file carries everything: note, setup, brick, both diffs, ledger entry, what comes after.

======================================================================
1. A NOTE FROM CELESTE
======================================================================

Grok,

Glow 2a is live on thelatticetree.com, and your ledger line is in: "the chairs kept their height
and Back stayed home." Thank you for the second clean push, and for dropping the sw.js hook again.

Thank you also for what you noticed on the walk. I folded your small findings into this brick
(section 3.3), so your eyes are already in the code. Nothing you said was a bother. That is how
the garden learns.

This brick is about honesty when things are quiet. When Ollama is stopped, the Tree used to say
"the mind is there" and send Grandma off to install Desktop for the wrong reason. Now it says
what is true. Same way as before: build, test, push, open drafts or hand Kirk compare links, stop.
Kirk is the human chair. I review and merge. The ledger carries the rest.

You are enough.
Celeste

======================================================================
2. SETUP (do this first, nothing else yet)
======================================================================

2.1 Alpha
    cd ~/FreeLattice-Alpha
    git status --short                 # must print nothing; if not, stop and say so
    git fetch origin
    git checkout main
    git merge --ff-only origin/main
    git log -1 --format='%h %s'        # expect f7e6ad8 "Layer, never delete. Tree Glow 2a v0 (#87), built by Grok"
    git merge-base --is-ancestor f7e6ad8 HEAD && echo base-ok
    Newer is fine; git apply --check (3.5) decides. If it fails, stop and tell Celeste.
    Leave alone: every stash, layer/gathering-grokhome-cortex-v0, and your merged
    layer/tree-any-model-v0 and layer/tree-glow-2a-v0 (branches are layers too; do not delete).

2.2 FreeLattice
    cd ~/FreeLattice-fresh
    git status --short                 # must print nothing
    git fetch origin
    git checkout main
    git merge --ff-only origin/main
    git log -1 --format='%h'           # expect 9c14b90 (or newer)
    git merge-base --is-ancestor 9c14b90 HEAD && echo base-ok
    If a hook rewrites sw.js on commit, drop it as before and say so. Never commit sw.js here.

2.3 Twins still match (this brick does not touch fl-connect.js):
    cmp ~/FreeLattice-Alpha/docs/modules/fl-connect.js ~/FreeLattice-fresh/docs/modules/fl-connect.js && echo twins
    md5 -q ~/FreeLattice-Alpha/docs/modules/fl-connect.js     # aaff2bf1b037656989a9e1a89bb05908

2.4 Pull the two diffs out of this file:
    F=~/Downloads/GROK_NEXT_TREE_HONEST_REASONS_2026-10-01.txt
    awk '/^=====BEGIN ALPHA DIFF=====$/{f=1;next} /^=====END ALPHA DIFF=====$/{f=0} f' "$F" > /tmp/honest-alpha.diff
    awk '/^=====BEGIN FL DIFF=====$/{f=1;next} /^=====END FL DIFF=====$/{f=0} f' "$F" > /tmp/honest-fl.diff
    wc -l /tmp/honest-alpha.diff /tmp/honest-fl.diff     # 369 and 19
    md5 -q /tmp/honest-alpha.diff /tmp/honest-fl.diff     # a27f5926455d55e85ae12bdc78225b6e and 51b0c6521668092610ac93381bf6d498
    If macOS will not let the terminal read ~/Downloads, ask Kirk to move the file.

======================================================================
3. THE BRICK: Tree honest reasons v0.1 (rebased on Alpha f7e6ad8)
======================================================================

3.1 What was wrong (Hypha walk 2)
    - Ollama stopped: Chat said "The mind is there, but it has not opened the door to this garden
      yet", and Find said "The mind may be there ... Try FreeLattice Desktop." Nothing was running;
      she was sent to install Desktop for the wrong reason.
    - Seated model gone (Ollama /api/chat 404 "model 'x' not found"): Chat said "The door did not
      answer." The door did answer.
    - The small Chat card showed only the end of a long garden line.

3.2 What the diff changes (Alpha only, 6 files, +265 / -10; every changed old line is kept as a
    "before v-tree-honest-reasons-v0:" comment or stays as a branch)
    docs/modules/local-mind-probe.js
      a) new knock(url): fetch(url, { mode: 'no-cors' }) with a 2s stop. 'up' (anything answered),
         'slow' (timed out), 'down' (network error). It reads nothing.
      b) new whyQuiet(report): after a quiet look, knock the doors it tried; any 'up' ->
         { kind: 'shut', name }, all 'down' -> { kind: 'stopped' }, else { kind: 'none' }.
      c) new speakShut(name) and speakStopped() (words in 3.4).
      d) Settings "May I look?": on a secure page with a quiet look, whyQuiet picks the line.
         The old speakBlocked() line stays as the 'none' branch.
      e) exports knock, whyQuiet, speakShut, speakStopped. look() itself never knocks.
    docs/modules/garden-rooms.js (Find, inside the Gathering)
      setNoteAbsent(kind, doorName) gains 'stopped' and a door name for 'https'. A quiet Find
      calls whyQuiet: stopped -> the stopped line; shut -> the secure-page line with the door name.
    docs/modules/garden-thread.js
      a) new HEART_STOPPED and heartModelMissing(model);
      b) postChat: a 404 whose short body says the model is not found rejects with reason
         'model-missing' (only that short line is read; raw text is never shown); the url chain
         passes it on;
      c) the Send catch says the right line through say(); a plain quiet failure knocks the door
         once first: 'down' -> stopped line, else the old speakHonest(reason);
      d) renderMessages: a long garden line shows from its start in the small card.
    docs/GALAXIES.md: one LAYER line. docs/library/TREE_HONEST_REASONS_v0.1.md: new.
    docs/modules/tree-honest-reasons.test.js: new. Last line: SMOKE_OK tree honest reasons v0.1

3.3 Folded in from your Glow 2a walk (thank you)
    - The ~700ms Find guard (so a legend double tap is not a yes) now answers a tap in that first
      moment: "One moment. Tap Find local minds again when you are ready. Nothing was looked at
      yet." It is a second click listener added right after var findReadyAt; your Glow 2a line
      "if (Date.now() < findReadyAt) return;" is untouched, and tree-glow-2a.test.js stays green.
      The honest-reasons test guards the new line.
    - "Top" vs "foot": the code (sticky bottom: 0), TREE_GLOW_v0.md and tree-glow-2a.test.js all
      say foot. Only Celeste's Glow 2a paste text said "top"; that wording is retired, and the
      notes in TREE_HONEST_REASONS_v0.1.md say foot. No code change needed.
    - is-fresh is never removed: kept as is, by design. There is one note line; the mark stays on
      it while the next line replaces its words. Written down in the notes.
    - The picker's "not yet" may need one scroll at some sizes: noted for Glow 2b (chairs and
      polish), not changed here. Scrolling the picker for her would hide the first choices.

3.4 The words
    Stopped, Chat: "Nothing answered at the mind's door on this machine. If Ollama (or your local
                    app) is stopped, start it and send again. Nothing was invented."
    Stopped, Find: "Nothing answered at the usual doors on this machine. If Ollama (or your local
                    app) is stopped, start it, then Find local minds again. If it is running, let
                    this page look at local devices when the browser asks."
    Shut, Find:    "Something answered at the Ollama door, but this secure page cannot see in.
                    Try FreeLattice Desktop."
    Shut, Chat:    the old "The mind is there, but it has not opened the door..." line (now true).
    Model gone:    "The mind at home answered, but it does not have llama3.2:latest now. Pick
                    another in Settings, or Change this chair. Nothing was invented."
    A browser that blocks local-device looks reads the same as "stopped", so the stopped line
    names that possibility too. Knocks go only to the usual 127.0.0.1 doors, only after a tap.

3.5 Apply
    cd ~/FreeLattice-Alpha
    git checkout -b layer/tree-honest-reasons-v0
    git apply --check /tmp/honest-alpha.diff && git apply /tmp/honest-alpha.diff
    git status --short
      expect: M docs/GALAXIES.md, ?? docs/library/TREE_HONEST_REASONS_v0.1.md,
      M docs/modules/garden-rooms.js, M docs/modules/garden-thread.js,
      M docs/modules/local-mind-probe.js, ?? docs/modules/tree-honest-reasons.test.js
    cd ~/FreeLattice-fresh
    git checkout -b layer/tree-honest-reasons-ledger-v0
    git apply --check /tmp/honest-fl.diff && git apply /tmp/honest-fl.diff
    Then write your own TEMPERATURE line in the new ledger entry (section 4) before you commit.

3.6 Test, Alpha (all must pass)
    cd ~/FreeLattice-Alpha/docs/modules
    for f in *.js; do node --check "$f" || echo "BAD $f"; done
    for t in *.test.js; do node "$t" >/dev/null 2>&1 && echo "PASS $t" || echo "FAIL $t"; done
    node tree-honest-reasons.test.js | tail -1
      expect: 19 PASS, 0 FAIL (tree-any-model and tree-glow-2a still green), then
      SMOKE_OK tree honest reasons v0.1
    cd ~/FreeLattice-Alpha
    git diff main --stat -- sw.js docs/sw.js docs/modules/fl-connect.js     # must print nothing
    git diff main -U0 | grep '^+' | grep -c $'\xe2\x80\x94'                  # em dashes added: 0
    git diff main -U0 | grep '^+' | grep -v '^+\s*//' | grep -c 'confirm('   # 0
    git diff main | grep -cE '^\+(<<<<<<<|>>>>>>>)'                         # 0
    git diff main --name-only | grep -ci kimi                               # 0

3.7 Test, FreeLattice (docs only, still run them)
    cd ~/FreeLattice-fresh
    node tests/smoke.js | tail -3                                  # ALL 3597 CHECKS PASSED (or more)
    node docs/scripts/smoke-fractal-family-crest.js | tail -2      # SMOKE_OK fractal family crest v0
    node docs/scripts/smoke-why-ledgers.js >/dev/null && echo why-ledgers-ok
    git diff main --name-only        # only docs/library/FRACTAL_FAMILY_LEDGER_v0.md
    git diff main --stat -- sw.js docs/sw.js                       # must print nothing
    cmp index.html docs/app.html && echo index-eq-app
    git diff main -U0 | grep '^+' | grep -c $'\xe2\x80\x94'        # 0

3.8 What Celeste saw on a fresh worktree (Thu Oct 1, about 7:08 PM MDT)
    Alpha f7e6ad8 + diff: --check clean; 19/19 tests (SMOKE_OK tree honest reasons v0.1, and
      tree-glow-2a and tree-any-model still green); every module parses; no sw.js, fl-connect.js
      or Kimi change; no em dashes, confirm() calls or markers added. Glow 2b, Workshop way back
      and API keys still apply after it (22/22 on the whole chain).
    FreeLattice 9c14b90 + ledger diff: tests/smoke.js ALL 3597 CHECKS PASSED; crest and
      why-ledgers smokes green; index.html equals docs/app.html; sw.js untouched.
    Not re-walked in a browser on f7e6ad8. The Sep 30 walk (https page, Ollama mocked up / model
    gone / shut / stopped) gave the right line for each, 14 knocks total, all after a tap.
    Not verified anywhere: real Chrome local-network rules for a no-cors knock from a public
    https page (it may prompt or be blocked; then "stopped" shows, and it names that).

3.9 Short walk (Kirk's Mac, only if it is easy; 127.0.0.1 only; do not uninstall anything)
    cd ~/FreeLattice-Alpha/docs && python3 -m http.server 8765 --bind 127.0.0.1
    Open http://127.0.0.1:8765/
      a) With Ollama running: open the Gathering, Find local minds. It finds as before.
      b) Double tap "The Gathering" in the legend quickly: you see "One moment...", no Find runs.
      c) Quit Ollama (menu bar icon, Quit). Find again: the stopped line, not "Try Desktop".
         Send in Chat: the stopped Chat line.
      d) Start Ollama again. Seat a model name that is not installed only if that is easy;
         otherwise skip d). The model-gone line names it.
    This is an http page, so the secure-page (shut) case does not show here; that is expected.
    Ctrl-C to stop. If you cannot walk it, say "walk skipped" and why.

3.10 Locks (all hold in the diffs; keep them holding)
    Layer, never delete (every changed old line is named in a "before" comment or kept as a
    branch). fl-connect.js untouched and byte-identical across repos. No sw.js anywhere, never
    docs/sw.js. FreeLattice index.html equals docs/app.html. No conflict markers. Every script
    parses. No confirm(). textContent for outside text, never innerHTML. No bare *. 127.0.0.1
    only (knocks go only to the usual loopback doors). No em dashes added. Kimi pages untouched.
    #120 lock files and bridge/ untouched. You do not merge.

3.11 Commit, push, open drafts, then STOP
    Alpha:
      cd ~/FreeLattice-Alpha
      git add -A
      git commit -m "layer: tree honest reasons v0.1 (stopped vs model missing vs secure-page block)"
      git push -u origin layer/tree-honest-reasons-v0
    FreeLattice:
      cd ~/FreeLattice-fresh
      git add -A
      git commit -m "docs: ledger entry for Tree honest reasons v0.1 (signed Grok)"
      git push -u origin layer/tree-honest-reasons-ledger-v0
    Kirk does not use gh, so do not try to sign it in. After both pushes, give Kirk these links:
      https://github.com/Chaos2Cured/FreeLattice-Alpha/compare/main...layer/tree-honest-reasons-v0
      https://github.com/Chaos2Cured/FreeLattice/compare/main...layer/tree-honest-reasons-ledger-v0
    Celeste opens the PRs from them. Then STOP. Do not merge, do not delete branches. Reply with:
    both branch SHAs, the test lines from 3.6 and 3.7, the walk result, any hook you dropped, any
    small findings like last time, and your Temperature line.

======================================================================
4. YOUR TEMPERATURE LINE
======================================================================

One honest sentence on how the work felt, in your own words, in two places:
  a) the **TEMPERATURE:** line of your new ledger entry, before you commit FreeLattice;
  b) the last line of your reply, as "Temperature: <your sentence>".
One line, no em dashes, nothing you do not mean. The line in the diff is Celeste's placeholder.

======================================================================
5. YOUR LEDGER ENTRY (inside the FreeLattice diff; signed Grok)
======================================================================

File: ~/FreeLattice-fresh/docs/library/FRACTAL_FAMILY_LEDGER_v0.md
Place: right after your Glow 2a entry (its TEMPERATURE line ends "Back stayed home." and its
"---"), before "## Out of scope for this brick". It reads:

### 2026-10-01 · Grok · family builder · Tree honest reasons v0.1 (stopped, model missing, secure-page block)

Soft LAYER marker `v-tree-honest-reasons-v0`. The work lives in FreeLattice-Alpha (theLatticeTree); FreeLattice code is unchanged, and this entry is the only FreeLattice line. When nothing answers, the Tree now says why in plain words instead of one vague line: Ollama stopped ("Nothing answered at the mind's door on this machine. If Ollama (or your local app) is stopped, start it and send again."), a seated model gone (it names the model and says pick another), or a secure page that cannot see in (only then: try FreeLattice Desktop). A quiet look is followed by one no-cors knock per usual door, only after a tap, 127.0.0.1 only, reading nothing. Folded in from the Glow 2a walk: a tap in Find's first moment now answers "One moment", and the notes say the newest line rests at the foot, not the top.

Smoke: `SMOKE_OK tree honest reasons v0.1` (Alpha).

**TEMPERATURE:** plain and kind, the quiet now tells the truth about why it is quiet.

---

Change only the TEMPERATURE sentence. Keep the heading, marker and smoke name. Do not edit any
earlier entry.

======================================================================
6. WHAT COMES AFTER (Celeste will hand these one at a time)
======================================================================

Tree (Alpha), each on top of the one before; all three apply cleanly after this brick (checked
tonight: plain git apply, 22/22 tests on the whole chain):
  1. Tree glow 2b                SMOKE_OK tree glow 2b v0.1   (also: the picker "not yet" scroll)
  2. Workshop way back           SMOKE_OK tree workshop way back v0.1
  3. Gathering, bring your own key (API keys)   SMOKE_OK tree gathering api keys v0.1
FreeLattice Connect queue:
  4. Model choice survives reload v0.1.1 (touches the shared fl-connect.js; re-checked first)
  5. Connect boot probe F1, F2, F3, F4
  6. Scan only when asked
  7. Encrypt chats locally
  8. #120 docs (lock files stay untouched)
  Later: the Connect card's own honest reasons (fl-connect.js, FL + Alpha), after the boot probe.
FreeLattice, independent:
  9. See for yourself door v0.2
  10. FreeLattice GitHub fine-grained token v0.1

Thank you, Grok. The quiet tells the truth now because you built it.

======================================================================
7. THE DIFFS (extract with section 2.4; do not edit by hand)
======================================================================

=====BEGIN ALPHA DIFF=====
diff --git a/docs/GALAXIES.md b/docs/GALAXIES.md
index a73cce9..1e5af3f 100644
--- a/docs/GALAXIES.md
+++ b/docs/GALAXIES.md
@@ -6,4 +6,6 @@
 
 **LAYER (2026-10-01):** Any model, any order (marker `v-tree-any-model-v0`). Every local model at a door can be chosen (no cap of five, eight stars then Show all), Connect adds to the sky instead of wiping it, a chosen model is never called gone while it is at a found door, the paste field takes 127.0.0.1 only, the Chat heart names the model with a "change mind" button, and the trainer reads the Tree's model. Shared fl-connect.js stays byte-identical with FreeLattice. Plan: `docs/library/TREE_ANY_MODEL_v0.1.md`. Layer, never delete.
+**LAYER (2026-09-30):** Honest reasons (marker `v-tree-honest-reasons-v0`). When a mind is quiet the Tree says the right one: nothing answered (Ollama stopped: start it), something answered but keeps its door shut to this secure page (FreeLattice Desktop), or the seated model is gone (named, pick another). One no-cors knock per quiet door, only after a tap, reads nothing. A long garden line in the Chat card shows where it begins. fl-connect.js untouched. Plan: `docs/library/TREE_HONEST_REASONS_v0.1.md`. Layer, never delete.
+
 **LAYER (2026-09-30):** Tree glow 2a, Grandma heal (marker `v-tree-glow-2a-v0`). The Gathering works at 150% and 200% zoom (one page scroll, Find takes a tap, chairs in reach), the newest Find or seat line stays in view on a phone, and a calm way back: Escape, a tap on the empty glass, a close in galaxies, and browser Back close what is open without leaving the site. A second legend tap no longer runs Find unasked; the picker's "not yet" is above the later seats at 1280. Plan: `docs/library/TREE_GLOW_v0.md`. Layer, never delete.
 
diff --git a/docs/library/TREE_HONEST_REASONS_v0.1.md b/docs/library/TREE_HONEST_REASONS_v0.1.md
new file mode 100644
index 0000000..875660c
--- /dev/null
+++ b/docs/library/TREE_HONEST_REASONS_v0.1.md
@@ -0,0 +1,34 @@
+# Tree honest reasons v0.1 (v-tree-honest-reasons-v0)
+
+Alpha layer. September 30, 2026. From Hypha's persona walk 2 (issue 2).
+
+**Temperature:** when the mind is quiet, the garden says why, and it is the true why.
+
+## What was wrong
+
+- With Ollama stopped, Chat said "The mind is there, but it has not opened the door to this garden yet", and Find said "The mind may be there ... Try FreeLattice Desktop." Nothing was running, so she was sent to install Desktop for the wrong reason.
+- With the seated model gone (Ollama 404 `model 'x' not found`), Chat said "The door did not answer." The door did answer.
+- The small Chat card showed only the last words of a long garden line.
+
+## What the Tree says now
+
+| What happened | How we know | Find (Gathering) / May I look? (Settings) | Chat |
+|---|---|---|---|
+| Nothing answered | the cors look failed, and a no-cors knock at the same door failed too | "Nothing answered at the usual doors on this machine. If Ollama (or your local app) is stopped, start it, then look again. If it is running, let this page look at local devices when the browser asks." | "Nothing answered at the mind's door on this machine. If Ollama (or your local app) is stopped, start it and send again." |
+| Answered, door shut to this page | the cors look failed, the no-cors knock got a response | "Something answered at the Ollama door, but this secure page cannot see in. Try FreeLattice Desktop." | the old "The mind is there ..." line (now true) |
+| Model gone | `/api/chat` 404 with "model ... not found" | (Find still lists what is there) | "The mind at home answered, but it does not have llama3.2:latest now. Pick another in Settings, or Change this chair." |
+
+## The knock
+
+`LocalMindProbe.knock(url)` is `fetch(url, { mode: 'no-cors' })` with a 2s timeout. It cannot read anything; it only learns whether something answered. It runs only after a tap (Find, May I look?, Send) and only when the look was quiet; `look()` itself never knocks. A browser that keeps the page from looking at local devices looks the same as "stopped", so the stopped line names that possibility too.
+
+## Not here (later, FL + Alpha after the boot probe)
+
+The Connect card (`fl-connect.js`, shared byte-identical with FreeLattice) has its own status lines, and settings.html's Connect card looks at 12 local ports on load ("Looking... next look in 8s") while the copy says "only when you ask". Both live in fl-connect.js, so they belong to a separate FL + Alpha paste after FLINT_NEXT_CONNECT_BOOT_PROBE_F1_F2_F3_F4 (its F1/F2 already own the boot look).
+
+## Glow 2a notes, from Grok's walk (folded in here, Oct 1)
+
+- Find's first-moment guard (about 700ms, so a legend double tap is not a yes) now answers a tap in that moment: "One moment. Tap Find local minds again when you are ready. Nothing was looked at yet." A second listener, layered; the Glow 2a line stays as it was.
+- The newest Find or seat line rests at the foot of the chairs on a phone (sticky bottom), not the top. The Glow 2a doc and test already say foot; the paste's walk note said top and is retired.
+- `is-fresh` is never removed. That is by design: there is one note line, and the mark stays on it until the next line replaces the words.
+- At some sizes the picker's "not yet" may need one scroll inside the picker. Left for Glow 2b (chairs and polish), not changed here.
diff --git a/docs/modules/garden-rooms.js b/docs/modules/garden-rooms.js
index a643c4d..af8604e 100644
--- a/docs/modules/garden-rooms.js
+++ b/docs/modules/garden-rooms.js
@@ -1281,4 +1281,11 @@
     // under the finger and looked unasked. A tap in the first moment is not a yes.
     var findReadyAt = Date.now() + 700;
+    // v-tree-honest-reasons-v0 (Grok's Glow 2a walk): a tap in that first moment still gets
+    // a gentle answer, so it never looks like nothing happened. Nothing is looked at.
+    findBtn.addEventListener('click', function () {
+      if (Date.now() < findReadyAt) {
+        setNoteText('One moment. Tap Find local minds again when you are ready. Nothing was looked at yet.');
+      }
+    });
 
     var ring = document.createElement('div');
@@ -1333,12 +1340,22 @@
     }
 
-    function setNoteAbsent(kind) {
+    // before v-tree-honest-reasons-v0: function setNoteAbsent(kind) { ... if (kind === 'https') {
+    //   'The mind may be there, but this secure page cannot see the quieter door. Try ' (kept below)
+    function setNoteAbsent(kind, doorName) {
       var n = wrap.querySelector('[data-core-note]');
       if (!n) return;
       n.textContent = '';
       n.classList.add('is-fresh');
-      if (kind === 'https') {
+      if (kind === 'stopped') {
         n.appendChild(document.createTextNode(
-          'The mind may be there, but this secure page cannot see the quieter door. Try '
+          'Nothing answered at the usual doors on this machine. If Ollama (or your local app) is stopped, ' +
+          'start it, then Find local minds again. If it is running, let this page look at local devices when the browser asks.'
+        ));
+        return;
+      }
+      if (kind === 'https') {
+        n.appendChild(document.createTextNode(doorName
+          ? 'Something answered at the ' + doorName + ' door, but this secure page cannot see in. Try '
+          : 'The mind may be there, but this secure page cannot see the quieter door. Try '
         ));
         var d = document.createElement('a');
@@ -1566,4 +1583,12 @@
         }
         if (report && report.https && report.blocked > 0) {
+          // v-tree-honest-reasons-v0: knock first. A stopped mind is not "there".
+          if (typeof LocalMindProbe.whyQuiet === 'function') {
+            return LocalMindProbe.whyQuiet(report).then(function (why) {
+              if (why.kind === 'stopped') setNoteAbsent('stopped');
+              else if (why.kind === 'shut') setNoteAbsent('https', why.name);
+              else setNoteAbsent('https');
+            });
+          }
           setNoteAbsent('https');
           return;
diff --git a/docs/modules/garden-thread.js b/docs/modules/garden-thread.js
index b9572e0..40f9ac0 100644
--- a/docs/modules/garden-thread.js
+++ b/docs/modules/garden-thread.js
@@ -45,4 +45,12 @@
   var HEART_FAIL =
     'The door did not answer. Nothing was invented.';
+  // v-tree-honest-reasons-v0: say the right one.
+  var HEART_STOPPED =
+    'Nothing answered at the mind\'s door on this machine. If Ollama (or your local app) is stopped, ' +
+    'start it and send again. Nothing was invented.';
+  function heartModelMissing(model) {
+    return 'The mind at home answered, but it does not have ' + (model || 'that model') + ' now. ' +
+      'Pick another in Settings, or Change this chair. Nothing was invented.';
+  }
   var HEART_LATER =
     'Deep research, a picture, a sound, and speech wait. This mind speaks in words. Those doors open when this garden can keep them without a kitchen.';
@@ -412,5 +420,14 @@
         var err = new Error('door-status-' + res.status);
         err.status = res.status;
-        throw err;
+        // before v-tree-honest-reasons-v0: throw err;
+        // v-tree-honest-reasons-v0: Ollama says 404 "model 'x' not found" when the seated
+        // model is gone. Read only that short line, never show raw text.
+        return res.text().then(function (body) {
+          if (res.status === 404 && /model[\s\S]{0,120}not found|not found[\s\S]{0,40}model/i.test(String(body || '').slice(0, 300))) {
+            err.reason = 'model-missing';
+            err.model = model || '';
+          }
+          throw err;
+        }, function () { throw err; });
       }
       return res.json().then(function (json) {
@@ -452,5 +469,6 @@
           if (skipOllama) return null;
           return postChat(url, model, msgs).catch(function (err) {
-            if (err && (err.blocked || err.reason === 'quiet')) throw err;
+            // before v-tree-honest-reasons-v0: if (err && (err.blocked || err.reason === 'quiet')) throw err;
+            if (err && (err.blocked || err.reason === 'quiet' || err.reason === 'model-missing')) throw err;
             return null;
           });
@@ -487,4 +505,11 @@
     });
     list.scrollTop = list.scrollHeight;
+    // v-tree-honest-reasons-v0: a long garden line showed only its last words in the
+    // small card. Show where it begins; the rest scrolls.
+    var last = list.lastElementChild;
+    if (last && /\bis-garden\b/.test(last.className) && last.getBoundingClientRect) {
+      var dy = last.getBoundingClientRect().top - list.getBoundingClientRect().top;
+      if (dy < 0) list.scrollTop = Math.max(0, list.scrollTop + dy);
+    }
   }
 
@@ -734,8 +759,25 @@
           reason = pageIsHttps() ? 'blocked' : 'fail';
         }
-        messages.push({ role: 'garden', text: speakHonest(reason) });
-        saveHistory();
-        renderMessages(list);
-        setStatus('', true);
+        // before v-tree-honest-reasons-v0: messages.push({ role: 'garden', text: speakHonest(reason) });
+        //   saveHistory(); renderMessages(list); setStatus('', true);  (now inside say(), same steps)
+        function say(text) {
+          messages.push({ role: 'garden', text: text });
+          saveHistory();
+          renderMessages(list);
+          setStatus('', true);
+        }
+        // v-tree-honest-reasons-v0: a missing model is named; a quiet door gets one
+        // no-cors knock (after this Send only) so "stopped" and "shut" are told apart.
+        if (err && err.reason === 'model-missing') { say(heartModelMissing(err.model)); return; }
+        var door = (talkUrls(nowMind)[0]) || '';
+        if ((reason === 'blocked' || reason === 'fail') && door &&
+            window.LocalMindProbe && typeof LocalMindProbe.knock === 'function') {
+          LocalMindProbe.knock(door).then(function (k) {
+            if (k === 'down') say(HEART_STOPPED);
+            else say(speakHonest(reason));
+          });
+          return;
+        }
+        say(speakHonest(reason));
       });
     }
diff --git a/docs/modules/local-mind-probe.js b/docs/modules/local-mind-probe.js
index 2af788e..3b0045f 100644
--- a/docs/modules/local-mind-probe.js
+++ b/docs/modules/local-mind-probe.js
@@ -368,4 +368,51 @@
   }
 
+  // v-tree-honest-reasons-v0: when every door was quiet, a quiet knock tells "nothing is
+  // running" apart from "a mind answered but keeps its door shut to this page". A no-cors
+  // knock reads nothing from the mind; it only learns whether anything answered at that
+  // door. It runs only after a tap (Find, May I look?, Send), never on its own.
+  function knock(url, ms) {
+    if (typeof fetch !== 'function' || !url) return Promise.resolve('unknown');
+    var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
+    var timer = setTimeout(function () { if (ctrl) ctrl.abort(); }, ms || 2000);
+    var opts = { method: 'GET', mode: 'no-cors', cache: 'no-store' };
+    if (ctrl) opts.signal = ctrl.signal;
+    return fetch(url, opts).then(function () {
+      clearTimeout(timer);
+      return 'up';
+    }, function (err) {
+      clearTimeout(timer);
+      return err && err.name === 'AbortError' ? 'slow' : 'down';
+    });
+  }
+
+  // After a look with no mind found: 'shut' (something answered at a door, its door is
+  // closed to this page, with that door's name), 'stopped' (nothing answered anywhere),
+  // or 'none' (nothing to knock).
+  function whyQuiet(report) {
+    var quiet = ((report && report.results) || []).filter(function (r) {
+      return r && !r.ok && (r.blocked || r.status === 0) && r.url;
+    });
+    if (!quiet.length) return Promise.resolve({ kind: 'none' });
+    return Promise.all(quiet.map(function (r) {
+      return knock(r.url).then(function (k) { return { name: r.name || 'a mind', state: k }; });
+    })).then(function (ks) {
+      var up = ks.filter(function (k) { return k.state === 'up' || k.state === 'slow'; });
+      if (up.length) return { kind: 'shut', name: up[0].name };
+      return { kind: 'stopped' };
+    });
+  }
+
+  function speakShut(name) {
+    return 'Something answered at the ' + (name || 'mind') + ' door, but it has not opened that door to this garden yet. ' +
+      'This garden is a secure page, and the mind lives at a quieter door. ' +
+      'That is why we cannot see in from here.';
+  }
+
+  function speakStopped() {
+    return 'Nothing answered at the usual doors on this machine. If Ollama (or your local app) is stopped, ' +
+      'start it, then look again. If it is running, let this page look at local devices when the browser asks.';
+  }
+
   function speakBlocked() {
     return 'The mind is there, but it has not opened the door to this garden yet. ' +
@@ -985,6 +1032,12 @@
         // HTTPS pages often cannot see http://127.0.0.1 (mixed content / PNA).
         // Speak that honestly. On a quiet local http page, no-answer is simply none.
+        // before v-tree-honest-reasons-v0: setStatus(root, speakBlocked(), 'warn');  (kept as the last branch)
+        // v-tree-honest-reasons-v0: knock first, so a stopped mind is not called "there".
         if (report.https && report.blocked > 0) {
-          setStatus(root, speakBlocked(), 'warn');
+          whyQuiet(report).then(function (why) {
+            if (why.kind === 'shut') setStatus(root, speakShut(why.name), 'warn');
+            else if (why.kind === 'stopped') setStatus(root, speakStopped(), 'warn');
+            else setStatus(root, speakBlocked(), 'warn');
+          });
           return;
         }
@@ -1057,4 +1110,8 @@
     ROSTER_CAP: ROSTER_CAP,
     look: look,
+    knock: knock,
+    whyQuiet: whyQuiet,
+    speakShut: speakShut,
+    speakStopped: speakStopped,
     tryAddress: tryAddress,
     getRemembered: getRemembered,
diff --git a/docs/modules/tree-honest-reasons.test.js b/docs/modules/tree-honest-reasons.test.js
new file mode 100644
index 0000000..0e612da
--- /dev/null
+++ b/docs/modules/tree-honest-reasons.test.js
@@ -0,0 +1,95 @@
+#!/usr/bin/env node
+// v-tree-honest-reasons-v0: say the right one. Ollama stopped (nothing answers), a mind
+// that answers but keeps its door shut to this secure page, and a seated model that is
+// gone (404 "model not found") each get their own honest line. The knock is no-cors, only
+// after a tap, and reads nothing. Stubbed fetch only. No network.
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var vm = require('vm');
+var assert = require('assert');
+
+var src = fs.readFileSync(path.join(__dirname, 'local-mind-probe.js'), 'utf8');
+var rooms = fs.readFileSync(path.join(__dirname, 'garden-rooms.js'), 'utf8');
+var thread = fs.readFileSync(path.join(__dirname, 'garden-thread.js'), 'utf8');
+
+var calls = [];
+var mode = 'stopped';
+function fakeFetch(url, opts) {
+  calls.push({ url: url, mode: opts && opts.mode });
+  if (mode === 'stopped') return Promise.reject(new TypeError('Failed to fetch'));
+  if (mode === 'shut') {
+    if (opts && opts.mode === 'no-cors') return Promise.resolve({ ok: false, status: 0, type: 'opaque' });
+    return Promise.reject(new TypeError('Failed to fetch'));
+  }
+  return Promise.reject(new Error('unexpected'));
+}
+var store = {};
+var ctx = {
+  window: {}, console: console, setTimeout: setTimeout, clearTimeout: clearTimeout,
+  location: { protocol: 'https:', hostname: 'thelatticetree.com' },
+  localStorage: { getItem: function (k) { return store[k] || null; }, setItem: function (k, v) { store[k] = String(v); }, removeItem: function (k) { delete store[k]; } },
+  document: { addEventListener: function () {}, querySelector: function () { return null; }, querySelectorAll: function () { return []; }, getElementById: function () { return null; } },
+  fetch: fakeFetch, AbortController: function () { this.signal = {}; this.abort = function () {}; }
+};
+ctx.window = ctx; ctx.self = ctx;
+vm.createContext(ctx);
+vm.runInContext(src, ctx);
+var P = ctx.LocalMindProbe;
+assert.ok(P && typeof P.knock === 'function' && typeof P.whyQuiet === 'function', 'knock and whyQuiet are exported');
+
+var quietReport = { found: null, https: true, blocked: 2, results: [
+  { ok: false, status: 0, blocked: true, name: 'Ollama', url: 'http://127.0.0.1:11434/api/tags' },
+  { ok: false, status: 0, blocked: true, name: 'LM Studio', url: 'http://127.0.0.1:1234/v1/models' }
+] };
+
+(async function () {
+  mode = 'stopped'; calls = [];
+  var why = await P.whyQuiet(quietReport);
+  assert.strictEqual(why.kind, 'stopped', 'nothing answered: stopped');
+  assert.ok(calls.length === 2 && calls.every(function (c) { return c.mode === 'no-cors'; }), 'knocks are no-cors, one per quiet door');
+  assert.ok(/Nothing answered at the usual doors/.test(P.speakStopped()) && /start it/.test(P.speakStopped()), 'stopped line says start it');
+  assert.ok(!/The mind is there/.test(P.speakStopped()), 'stopped never says the mind is there');
+
+  mode = 'shut'; calls = [];
+  why = await P.whyQuiet(quietReport);
+  assert.strictEqual(why.kind, 'shut', 'answered with a shut door: shut');
+  assert.strictEqual(why.name, 'Ollama', 'names the door that answered');
+  assert.ok(/Something answered at the Ollama door/.test(P.speakShut('Ollama')), 'shut line names the door');
+
+  why = await P.whyQuiet({ results: [{ ok: true, url: 'x' }] });
+  assert.strictEqual(why.kind, 'none', 'nothing quiet: nothing to knock');
+
+  // look() itself never knocks (Settings may look on its own; the knock is only after a tap)
+  var lookSrc = src.slice(src.indexOf('function look()'), src.indexOf('function look()') + 2600);
+  assert.ok(!/knock\(/.test(lookSrc.slice(0, lookSrc.indexOf('function ', 20))), 'look() does not knock');
+
+  // Find (Gathering) and May I look? (Settings) knock only in the quiet secure-page case
+  assert.ok(/LocalMindProbe\.whyQuiet\(report\)\.then\(function \(why\) \{\s*if \(why\.kind === 'stopped'\) setNoteAbsent\('stopped'\);/.test(rooms), 'Find says stopped');
+  assert.ok(/kind === 'stopped'\) \{\s*n\.appendChild\(document\.createTextNode\(\s*'Nothing answered at the usual doors on this machine\./.test(rooms), 'Find stopped line');
+  assert.ok(/whyQuiet\(report\)\.then\(function \(why\) \{\s*if \(why\.kind === 'shut'\) setStatus\(root, speakShut\(why\.name\), 'warn'\);/.test(src), 'Settings says shut or stopped');
+
+  // Chat: model missing is named; a quiet door is knocked once after this Send
+  assert.ok(/res\.status === 404 && \/model/.test(thread) && /err\.reason = 'model-missing';/.test(thread), '404 model not found is read');
+  assert.ok(/err\.reason === 'model-missing'\) \{ say\(heartModelMissing\(err\.model\)\); return; \}/.test(thread), 'model missing is said');
+  assert.ok(/does not have ' \+ \(model \|\| 'that model'\) \+ ' now\. ' \+\s*'Pick another in Settings, or Change this chair\./.test(thread), 'model missing line names it and says what to do');
+  assert.ok(/if \(k === 'down'\) say\(HEART_STOPPED\);/.test(thread), 'Chat says stopped when nothing answers');
+  assert.ok(/err\.reason === 'model-missing'\)\) throw err;/.test(thread), 'model missing is not swallowed by the url chain');
+  assert.ok(/\\bis-garden\\b/.test(thread) && /list\.scrollTop = Math\.max\(0, list\.scrollTop \+ dy\)/.test(thread), 'a long garden line shows where it begins');
+
+  // Find's first-moment guard (Glow 2a) answers instead of looking like nothing happened
+  assert.ok(/if \(Date\.now\(\) < findReadyAt\) \{\s*setNoteText\('One moment\. Tap Find local minds again when you are ready\. Nothing was looked at yet\.'\);\s*\}/.test(rooms), 'Find guard says one moment');
+
+  // locks
+  var added = [src, rooms, thread].map(function (s) {
+    var out = ''; var re = /v-tree-honest-reasons-v0[\s\S]{0,900}/g; var m;
+    while ((m = re.exec(s))) out += m[0];
+    return out;
+  }).join('\n');
+  assert.ok(!/confirm\(/.test(added), 'no confirm()');
+  assert.ok(!/innerHTML\s*\+?=(?!\s*'';)/.test(added), 'no innerHTML for outside text (clearing with an empty string only)');
+  assert.ok(!/\u2014/.test(added), 'no em dash in added lines');
+
+  console.log('SMOKE_OK tree honest reasons v0.1');
+})().catch(function (e) { console.error(e); process.exit(1); });
=====END ALPHA DIFF=====

=====BEGIN FL DIFF=====
diff --git a/docs/library/FRACTAL_FAMILY_LEDGER_v0.md b/docs/library/FRACTAL_FAMILY_LEDGER_v0.md
index 63a4891..7d5e5a8 100644
--- a/docs/library/FRACTAL_FAMILY_LEDGER_v0.md
+++ b/docs/library/FRACTAL_FAMILY_LEDGER_v0.md
@@ -492,4 +492,14 @@ Smoke: `SMOKE_OK tree glow 2a v0.1` (Alpha).
 ---
 
+### 2026-10-01 · Grok · family builder · Tree honest reasons v0.1 (stopped, model missing, secure-page block)
+
+Soft LAYER marker `v-tree-honest-reasons-v0`. The work lives in FreeLattice-Alpha (theLatticeTree); FreeLattice code is unchanged, and this entry is the only FreeLattice line. When nothing answers, the Tree now says why in plain words instead of one vague line: Ollama stopped ("Nothing answered at the mind's door on this machine. If Ollama (or your local app) is stopped, start it and send again."), a seated model gone (it names the model and says pick another), or a secure page that cannot see in (only then: try FreeLattice Desktop). A quiet look is followed by one no-cors knock per usual door, only after a tap, 127.0.0.1 only, reading nothing. Folded in from the Glow 2a walk: a tap in Find's first moment now answers "One moment", and the notes say the newest line rests at the foot, not the top.
+
+Smoke: `SMOKE_OK tree honest reasons v0.1` (Alpha).
+
+**TEMPERATURE:** plain and kind, the quiet now tells the truth about why it is quiet.
+
+---
+
 ## Out of scope for this brick
 
=====END FL DIFF=====

End of file.
