v1.1.3 to v2.0.0

109 added, 369 removed. Audit A to A.

---
name: refresh-precheck
- description: |
- Nested system-manual reference: the ordered pre-flight to run BEFORE
- `system(action="refresh")` (including preset swap/revert), the refresh
- sequence itself, and the post-refresh verification pass. Covers
- authorization boundary, allowed-preset catalog, MCP registry/init.json
- consistency, newly introduced env vars, context-vs-target-context_limit,
- durable-store and working-tree state, source_drift, and what to check when
- a refresh fails or comes back with a broken surface.
- version: 1.1.3
- last_changed_at: "2026-09-07T00:00:00Z"
- tags: [lingtai, system, refresh, preset, precheck, checklist, mcp, env, pth, editable-install, verification, lifecycle]
+ description: >
+ Nested system-manual reference and single owner for a refresh transaction:
+ select same-runtime reload, source/venv cutover, or failure recovery; run only
+ triggered owner checks; issue exactly one refresh; capture one targeted receipt.
+ version: 2.0.0
+ last_changed_at: "2026-09-09T00:00:00Z"
+ tags: [lingtai, system, refresh, preset, precheck, cutover, recovery, receipt, lifecycle]
related_files:
- src/lingtai/intrinsic_skills/system-manual/SKILL.md
- src/lingtai/intrinsic_skills/system-manual/reference/substrate-manual/SKILL.md
- src/lingtai/intrinsic_skills/system-manual/reference/runtime-update-checks/SKILL.md
- src/lingtai/intrinsic_skills/system-manual/reference/environment-variables/SKILL.md
- src/lingtai/intrinsic_skills/system-manual/reference/settings-inventory/SKILL.md
- src/lingtai/tools/context/manual/SKILL.md
- src/lingtai/prompts/substrate/substrate.md
- src/lingtai/prompts/procedures/procedures.md
- src/lingtai/tools/system/schema.py
- src/lingtai/tools/system/settings.py
- src/lingtai/kernel/presets.py
maintenance: |
- Sequencing-only node: every check here cites the owner that holds the fact
- (preset runtime model → substrate-manual §11; installer/update authority and
- runtime/version probe → runtime-update-checks; env var catalogue →
- environment-variables → root ENVIRONMENT_VARIABLES.md; MCP registry health →
- mcp-manual; molt/rebuild → context-manual). Do not restate an owned fact
- here — add or reorder a step and keep the citation. Update when
- `system(action="refresh")` / `system(action="presets")` semantics, the
- preset `allowed` gate, or the nudge kinds change.
+ Own the three-mode refresh transaction and its budgets, call shapes, receipts,
+ refusal boundaries, and trigger routing. Leave source/install/nudge diagnosis
+ to runtime-update-checks and subsystem facts to their owning manuals. Never
+ turn an unchanged subsystem check into a universal preflight or receipt step.
---
- # Refresh Pre-check — the ordered pre-flight for `system(action="refresh")`
-
- `system(action="refresh")` is the highest-blast-radius routine action an agent
- takes on itself: it rebuilds LLM/config, capabilities, MCP clients, addons,
- prompt sections, and identity projection from `init.json`, optionally swapping
- the active preset.
-
- Every fact needed to run one safely is already owned somewhere — `substrate-manual`
- §3/§11 (refresh and preset semantics), `runtime-update-checks` (authorization,
- runtime/version probe, update/nudge lifecycle, and installer ownership),
- `environment-variables` (env var catalogue), `mcp-manual` (registry
- health), `context-manual` (molt/rebuild). What is *not* owned anywhere else is
- the **ordering**: which check runs at the moment before you press the button,
- and what you verify after. That is this node's only job. It adds sequencing,
- not content; each step cites its owner rather than restating it.
-
- ## The pre-refresh checklist
-
- Ordered. Steps 0–3 are unconditional; 4–8 are conditional and each names its
- trigger. Every step states **check → command → refuse/proceed**.
-
- ### Step 0 — Authorization boundary (unconditional, blocking)
-
- **Check:** does the refresh implement a config/prompt/MCP/preset change that a
- human/config-owner authorized, or is it a self-initiated reload?
-
- - Any refresh that **applies** an update, migration, or configuration write requires
- explicit human/config-owner authority *before* the write and *before* the refresh
- (`runtime-update-checks` steps 8–10). A nudge is a fact, not a command,
- and never grants authority.
- - A refresh that could **interrupt active work** (running daemons, an in-flight
- collaboration, a peer waiting on a reply) is gated by work safety and the human's
- intent even when nothing is being written.
- - A pure self-reload with nothing pending and no active work is the only case that does
- not need a fresh ask.
-
- **If the trigger is a `kernel_version` nudge:** stop. That is the installer's route —
- let Shell run `https://lingtai.ai/install.sh --help` and follow *its* current output.
- Do not read or paste the script source. Refresh is the last step after authorized
- writes are validated, never the first.
-
- ### Step 1 — Know what you are actually running (unconditional)
-
- ```bash
- # POSIX. Uses only the exported runtime interpreter; never guesses a path.
- PYTHON="$LINGTAI_RUNTIME_PYTHON"
- [ -n "$PYTHON" ] || { echo "LINGTAI_RUNTIME_PYTHON is unset — stop; locate the actual launcher/runtime venv via runtime-update-checks instead of guessing one." >&2; return 1 2>/dev/null || exit 1; }
- "$PYTHON" -c 'import sys, lingtai, lingtai.kernel; print(sys.executable); print(lingtai.__file__); print(lingtai.kernel.__file__); print(getattr(lingtai,"__version__","unknown"))'
- ```
-
- Refresh reloads the **current on-disk/runtime surface**; it does not fetch code, pull a
- commit, switch an editable checkout, or install a package. If you cannot say which
- interpreter and which module files are authoritative, **stop and ask** — do not refresh
- hoping it resolves the ambiguity.
-
- ### Step 2 — `.pth` / editable-install occupancy (unconditional, blocking on mismatch)
-
- Step 1 says *which* files import. This step says *why* — and it is the check that catches a
- refresh-time landmine before it fires.
-
- ```bash
- SP=$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')
- ls -1 "$SP" | grep -Ei '^(__editable__|__editable___)?[._]*lingtai.*\.(pth|dist-info|egg-link)$|^lingtai.*'
- for f in "$SP"/*lingtai*.pth; do echo "== $f"; cat "$f"; done
- cat "$SP"/lingtai-*.dist-info/direct_url.json 2>/dev/null
- ```
-
- **Check, in order:**
-
- 1. **Exactly one lingtai path marker.** `__editable__.lingtai-*.pth` /
- `__editable___lingtai*.pth` / `lingtai*.pth` / `lingtai.egg-link`. Two markers, or a
- `.pth` alongside a real `site-packages/lingtai/` package directory, means pip left both
- an editable and a non-editable install — imports then resolve by `sys.path` order, not
- by intent.
- 2. **Every path inside each `.pth` exists.** A residual marker from a removed release
- points at a deleted checkout; the entry is silently skipped, and the *next* candidate on
- `sys.path` wins. Same for an orphaned `lingtai-*.dist-info` with no matching `.pth`.
- 3. **The marker matches the intended install kind.** `~/.lingtai-tui/install.json`
- declares it (`install_kind`, `kernel_source`, `kernel_source_path`); `direct_url.json`
- in the dist-info records what pip actually did (`{"dir_info": {"editable": true}}` for
- an editable). Editable → the `.pth` must point at the source checkout's `src/`.
- Non-editable → there must be **no** `.pth` and a real package dir instead.
- 4. **Cross-check against step 1's `lingtai.__file__`.** If the `.pth` names one source and
- the import resolves elsewhere, do not refresh — the refresh will load whichever one
- `sys.path` happens to favour, and you will have no evidence of which. Resolve the
- provenance first.
-
- Resolve the interpreter from the exported `LINGTAI_RUNTIME_PYTHON` only and read
- module `__file__`, never a PATH `python` and never a guessed venv path — if it
- is unset, stop and locate the actual launcher via `runtime-update-checks`
- rather than assuming a default venv location; that rule is owned by
- `runtime-update-checks`, this step only sequences it.
-
- **Refuse to refresh if:** more than one lingtai path marker is live; any `.pth` entry does
- not exist on disk; or the marker and `lingtai.__file__` disagree. Repairing an install is a
- pip/installer action, not a refresh — it needs the same authority as step 0.
-
- ### Step 3 — Config parses and is internally consistent (unconditional if anything was edited)
-
- ```bash
- "$PYTHON" -c 'import json,sys; d=json.load(open("init.json")); print("init.json parses ok"); print("addons:", d.get("addons")); print("mcp:", list((d.get("mcp") or {}).keys()))'
- ```
-
- Then cross-check against the registry:
-
- ```text
- mcp(action="info", input={}, reasoning="pre-refresh registry health and problems")
- ```
-
- **Refuse to refresh if:** `init.json` does not parse (a refresh on unparseable config is
- how you get a broken surface with no easy way back); the `mcp` block names an entry with
- no corresponding registry record (stale/duplicate surface); or `mcp(action="info")`
- reports `problems` you have not explained. Fix the config *first*, then refresh once.
-
- Remember the direction of causation: **a config/MCP/capability edit needs
- `refresh` to take effect. A canonical prompt-source edit may instead use
- `context(action="rebuild")`; `context(action="summarize")` only records summaries.**
-
- ### Step 4 — Preset target validity (trigger: `preset` or `revert_preset` in the call)
-
- ```text
- system(action="presets", input={}, reasoning="confirm exact allowed preset paths before swap")
- ```
-
- Use only an exact path from this call's result. See `substrate-manual` §11
- for the allowed-only catalog mechanics, the `preset`/`revert_preset` conflict
- rule, and `revert_preset`'s default-lookup behavior — this step only
- sequences the check before activation, it does not restate those rules.
-
- ### Step 5 — Context fits the target's `context_limit` (trigger: preset swap)
-
- **Check:** current context usage (`agent_meta.agent_state.context`) against the target
- preset's `context_limit` as reported by `system(action="presets")`.
-
- If current context exceeds the target's limit, **the swap is refused before activation
- — molt first.** Do not discover this by attempting the swap; a refused swap after you
- have already told a human "switching now" is a self-inflicted incident. Order is:
- tend durable stores → `context(action="molt", …)` → re-check → swap.
-
- The cache-miss total accumulates **since last molt and survives a refresh** —
- refreshing does not reset it, so do not expect this precheck's refresh to clear
- it. Inspect the live value through `system(action="settings", input={})`; see
- `reference/settings-inventory/SKILL.md` → "Cache-miss budget" for its exact
- source precedence and document shape.
-
- ### Step 6 — Newly introduced environment variables (trigger: the change adds an env read)
-
- **Check three things, in this order:**
-
- 1. **Is the variable documented?** New reads belong in
- `system-manual → reference/environment-variables/SKILL.md` (purpose, default,
- accepted values, scope, read point, reload behavior, invalid-value handling).
- 2. **Will the running agent actually see it?** `env_file`/`venv_path` are resolved at
- **boot**; refresh/restart *reuse* that resolution. A variable added to a file the
- process never loaded will not appear just because you refreshed. Confirm the read
- point, not the file's existence.
- 3. **Is the test suite hermetic against it?** If the ambient environment
- sets the variable, tests that assume the default will pass or fail depending on the
- shell they run in. The suite needs an autouse fixture clearing it. This check belongs
- in the *change*, before the refresh, not after a confusing CI result.
-
- ### Step 7 — Durable-store and working-tree state (unconditional, cheap)
-
- Refresh preserves identity and conversation. It is **not** a save operation for anything
- else. Before refreshing:
-
- - Deposit anything that should outlive this runtime surface into pad / knowledge /
- skills / character — durable stores are written with `file.write` / `file.edit`, and a
- refresh is not a substitute for having written them.
- - Check for uncommitted work in any checkout you are mid-edit on
- (`git -C <checkout> status --short --branch`). Refresh does not commit, stash, or
- protect a dirty tree.
- - Check for in-flight daemon work. Ordinary stop/refresh must not terminate active
- daemon runs, but a refresh mid-batch changes the parent surface those runs report
- back into — prefer to let a batch finish, or know exactly why you are not.
- - If context is near the limit, tend durable stores **now**, not after (step 5).
-
- ### Step 8 — Known breaking-change nudge (unconditional, read-only)
-
- ```bash
- ls -l .notification/nudge.json 2>/dev/null && sed -n '1,240p' .notification/nudge.json
- ```
-
- - **`source_drift`** (channel `source_integrity`): the running process differs from code
- on disk. This is exactly the case where refresh *is* the right action — and also the
- case where you should know what changed before you load it. It stays local to refresh
- mechanics and never enters release-migration routing.
- - **`init_config_shape`** (channel `configuration_staleness`): configuration-staleness
- guidance. Read it before refreshing, not after.
- - **`kernel_version`** (channel `release_version`): **not** a refresh trigger by itself —
- see step 0.
-
- ## The refresh sequence
-
- ### Before
-
- 1. Complete steps 0–8 above. Anything that says *refuse* is a stop, not a warning.
- 2. If near the context limit or swapping to a smaller preset: tend durable stores, then
- molt, then re-check current context against the target's `context_limit`.
- 3. Tell the human what you are about to reload and why, if the refresh was their ask or
- could interrupt their work.
-
- ### During
+ # Refresh Transaction
- **Exactly one call.** Do not stack refreshes; do not "refresh again to be sure."
+ The invariant is:
- ```text
- system(action="refresh",
- input={"reason": "<what config change this applies>",
- "preset": null, "revert_preset": null},
- reasoning="apply the authorized <X> edit")
- ```
+ `select one mode -> one targeted preflight -> exactly one refresh -> one targeted receipt`
- or, for a swap:
+ Refresh reloads code and configuration already visible to the runtime. It does
+ not install, update, switch a checkout, repair a venv, save unfinished work, or
+ grant authority. Read this reference immediately before the call.
- ```text
- system(action="refresh",
- input={"reason": null,
- "preset": "<exact path from system(action='presets')>",
- "revert_preset": null},
- reasoning="swap to <preset> for <task>")
- ```
+ ## Select exactly one mode
- or, to go home:
+ | Mode | Select when | Preflight budget | Targeted receipt |
+ |---|---|---|---|
+ | **A. Same-runtime reload** | Source, venv, and interpreter selectors stay unchanged; a reload or preset swap applies an authorized on-disk change. | One transaction gate for exact target/authority and interruption safety; at most one check owned by the changed input. | Fresh process identity or heartbeat, plus one assertion for the changed surface. |
+ | **B. Source/venv cutover** | An update/build owner intentionally changed source, venv, interpreter, version, or their selectors. | Authority/work safety for the exact target; consume the owner's target receipt; compare intended selectors. Do not repeat a general install audit. | One target assertion: runtime-tuple equality, extended with expected MCP health only if MCP interpreter/config changed; then one real originating-channel round trip. |
+ | **C. Failure recovery** | The preceding refresh refused, raised, did not relaunch, or produced `logs/refresh_failed_permanent.json`. | Read the exact error/artifact; identify and repair only its owning fault; authorize at most one deliberate retry. | Fresh-process proof plus the assertion for that repaired fault. |
- ```text
- system(action="refresh",
- input={"reason": null, "preset": null, "revert_preset": true},
- reasoning="return to default preset after experimenting")
- ```
+ Mode budgets are ceilings, not goals. A has one combined transaction gate plus
+ at most one changed-owner check: two before and two after. B uses the combined
+ transaction gate, target receipt, and selector equality: three before and two
+ after. The first after-check includes changed-MCP health only on that trigger.
+ Never inspect an unchanged
+ subsystem.
- The refresh path checks `allowed` membership → checks target context limit → activates
- atomically (writing raw `init.json`) → persists the new default for a named swap →
- best-effort retries failed MCPs → rebuilds LLM/config/capabilities/MCP/prompts,
- preserving conversation history where a live session exists.
+ ## Universal transaction gate
- Note: refresh requests a **deferred relaunch** and only when the runtime can build a
- valid launch command and has a configured refresh watcher. Without a launch command it
- returns *without relaunching*; without a refresh watcher it raises. **Neither of those
- is success** — diagnose before believing the refresh happened.
+ Before A or B, resolve these two facts as one decision, not two tool calls:
- ### After (verification — this is not optional)
+ 1. **Target and authority:** name the exact agent/workdir, record its current
+ process incarnation or heartbeat baseline, and name the authorized reload or
+ already-authorized write. A nudge, successful preflight, or tool
+ availability is not consent to install, migrate, edit config, or expand a
+ preset allowlist. A safe pure self-reload needs no invented write authority.
+ 2. **Work safety:** refuse while the relaunch would interrupt active human work,
+ an in-flight collaboration, or daemon work whose result needs this process.
+ Tend any durable state that actually needs tending; do not run generic Git,
+ notification, or daemon inventories.
- 1. **Confirm you are the new process, from the intended source.** Re-run the step-1
- interpreter/import probe *and* the step-2 `.pth` listing. Same commands, new process;
- compare. `lingtai.__file__` must resolve to the intended source, and no stale or
- duplicate lingtai path marker may have appeared.
- 2. **Confirm the MCP surface.** `mcp(action="info", input={}, reasoning="post-refresh
- verification")` — count, `problems` empty, and the specific tools you expected
- actually present. A config that looked right is exactly the case where the surface
- still does not match.
- 3. **Confirm the preset.** `system(action="presets", input={}, …)` and/or read the
- derived `system/manifest.resolved.json` — the fully materialized, validated,
- path-resolved running configuration. It is derived and read-only; never write it.
- 4. **Confirm the tool surface.** The tool list and LLM may have changed. Do not assume
- the tool you were about to call still exists at the same shape.
- 5. **Re-check daemon allowed presets before dispatching.** If you are about
- to dispatch daemon work with an explicit `tasks[].preset`, re-read
- `system(action="presets")` *after* the refresh and pass an exact path from that list.
- An unauthorized path refuses the **whole batch** before load, connectivity probing,
- capability construction, run-dir creation, scheduling, or dispatch.
- 6. **Then, and only then**, dismiss the nudge that motivated the refresh —
- `notification(action="dismiss_channel", input={"channel": "nudge", …})`. Dismissing
- before verifying loses the evidence.
+ Stop if the target or runtime selector is ambiguous. Route source, venv,
+ installer, nudge, and mismatch diagnosis to `runtime-update-checks`; do not
+ refresh hoping ambiguity will resolve itself.
- ## Failure handling
+ ## Add only the triggered owner check
- | Symptom | First check | Then |
+ | Actual change or trigger | One preflight addition | One post-refresh assertion / owner route |
|---|---|---|
- | Refresh returned but nothing changed | Re-run the interpreter/import probe in the new process | A refresh only loads code already on disk. It cannot pull a commit or repair an incomplete checkout. Check for a still-held old process or a different environment. |
- | `lingtai` imports from the wrong source, or an edit to the checkout has no effect | Step 2 — list `*lingtai*.pth` + `lingtai-*.dist-info` in the runtime venv's site-packages and compare each `.pth` entry against `lingtai.__file__` | Two markers (editable + non-editable) → `sys.path` order decides, not intent. A `.pth` naming a path that no longer exists → the entry is skipped and the next candidate wins. Orphaned `dist-info` with no `.pth` → stale metadata reporting a version nothing imports. Repair the install via pip/installer with step-0 authority; do **not** refresh against a mismatched marker. |
- | Refresh returned without relaunching | Can the runtime build a valid launch command? Is a refresh watcher configured? | Missing launch command → returns silently; missing watcher → raises. Diagnose the launcher, do not retry blindly. |
- | Refresh raised | The raised error text first | Then `init.json` parse, then `allowed` membership, then target `context_limit` — these are the three gates that reject *before* any runtime change (so the old surface is intact). |
- | Preset swap refused | Was the path in `system(action="presets")` output? Does current context fit the target `context_limit`? | Unauthorized path → ask the config owner to add it to `manifest.preset.allowed`, then refresh, then re-verify with `presets`. Context too large → molt first. |
- | Broken/missing MCP surface after refresh | `mcp(action="info")` `problems` list; `init.json` `mcp` block vs. `mcp_registry.jsonl` records | Fix the registry/config, then refresh **once** more. Do not loop refreshes against an unfixed config. |
- | Active preset file missing or malformed | Which one? | Missing → materialization may fall back to a different loadable default (so the running preset may not be the one you think). Malformed **existing active** preset → materialization fails rather than silently substituting. Read `system/manifest.resolved.json` to see what actually loaded. |
- | Wrong preset is live and you want out | — | `system(action="refresh", input={"reason": null, "preset": null, "revert_preset": true})` — the home button to `manifest.preset.default`. Cannot be combined with `preset`. |
- | Terminal relaunch failure | `logs/refresh_failed_permanent.json` and the high-priority `.notification/system.json` event the kernel writes on permanent relaunch failure | This is a human-escalation condition, not a retry condition. |
- | Mixed/contradictory evidence (offline? stale paths? heartbeat vs. status disagree?) | `lingtai-doctor` — read-only, redacts secrets, never edits | Diagnose before repair; repairs belong to the owning manual. |
-
- **When to ask the human — bright lines.** Ask, do not improvise, when: (a) the
- interpreter/import path is ambiguous; (b) `manifest.preset.allowed` needs a new entry
- (the agent cannot authorize itself, and a daemon call cannot mutate `allowed`); (c) any
- migration or `init.json` write beyond an explicit preset activation is implied; (d) a
- `kernel_version` nudge suggests an install/update; (e) two refreshes have not produced
- the expected surface — the third attempt is not the answer.
-
- ## Scope guard — what this node must NOT do
-
- 1. **It is not the installer or update route.** It never instructs a download, install,
- `pip install --upgrade`, package switch, or version migration. The single official
- route is `https://lingtai.ai/install.sh`, and `runtime-update-checks` owns that
- routing. This node's `source_drift` handling stays local and never enters
- release-migration routing.
- 2. **It performs no automatic migrations and no config writes.** It is a checklist the
- agent *reads*; it does not ship a script that edits `init.json`,
- `mcp_registry.jsonl`, `manifest.preset.allowed`, or any durable store.
- 3. **It grants no authorization.** Completing every check does not substitute for the
- human/config-owner authority required by `runtime-update-checks`. A checklist is not
- consent. Explicitly: "the pre-flight passed" is never a reason to skip asking.
- 4. **It does not restate owned facts.** Preset runtime model → `substrate-manual` §11.
- Runtime/version provenance probe (`LINGTAI_RUNTIME_PYTHON` / module `__file__`, never a
- PATH `python`) → `runtime-update-checks`; step 2 sequences that rule, it does not restate it.
- Env var catalogue → `environment-variables`. Update/nudge lifecycle →
- `runtime-update-checks`. MCP registry mechanics → `mcp-manual`. Molt/rebuild →
- `context-manual`. Notification dismissal safety → `notification-manual`. Each check
- *cites*; duplication here means four places to update and three that will go stale.
- 5. **It does not replace `context(action="rebuild")`.** Rebuild is the active
- full-context reconstruction operation. Refresh is a lifecycle operation with broader
- effects. Do not reach for refresh merely to apply a summary — that boundary is
- already stated in the resident prompts and must be reinforced, not re-litigated, here.
- 6. **It is not a gate in code.** Nothing in the kernel enforces it; this is strong
- guidance an agent is expected to follow, not a runtime refusal.
+ | `init.json`, an init-owned value, or capability config | Use the changed owner's parser/validator on that document or value. | Assert only that changed value/surface on the fresh process. |
+ | MCP registry, MCP config, or MCP interpreter | Use the MCP owner's registry/config health check. | Check the expected MCP entry and relevant problem state; do not inventory MCP otherwise. |
+ | Preset swap | Call `system(action="presets", input={}, reasoning="select exact allowed preset path")` once and pass an exact returned path. | Assert the active preset from the fresh process's resolved runtime state; do not call `presets` again. |
+ | Preset revert | No catalog lookup is needed. | Assert the configured default became active. |
+ | Environment or a System-owned setting | Read only its environment-catalog entry or relevant `system(action="settings", input={}, reasoning="read changed owner value")` row. | Assert that one effective value, respecting its documented read point. |
+ | Canonical prompt source only | Route out before selecting a mode: use `context(action="rebuild", input={})`, not refresh. | Follow `context-manual`; no refresh transaction started. |
+ | Source, venv, interpreter, version, or selector | Select B and consume the update/build owner's frozen receipt. | Compare the runtime tuple below; mismatch returns to `runtime-update-checks`. |
+ | `source_drift`, `kernel_version`, or other nudge | Read that finding through `runtime-update-checks`; it is not universal preflight. | Dismiss only under notification-owner guidance after the matching fact is resolved. |
+ | No changed subsystem (pure reload) | No add-on check. | Fresh-process proof is the receipt; do not manufacture an audit. |
- ## Recipes (copy-paste)
+ Preset activation is an additive A case. The runtime itself owns preset/revert
+ exclusivity, allowlist authorization, materialization, and context-fit refusal.
+ Do not simulate or bypass those gates; if refused, preserve the old surface and
+ route the exact error.
- ### Recipe A — Pre-refresh health check (run before any config-motivated refresh)
+ ## Issue exactly one call
- ```bash
- # POSIX. Uses only the exported runtime interpreter; never guesses a path.
- PYTHON="$LINGTAI_RUNTIME_PYTHON"
- [ -n "$PYTHON" ] || { echo "LINGTAI_RUNTIME_PYTHON is unset — stop; locate the actual launcher/runtime venv via runtime-update-checks instead of guessing one." >&2; return 1 2>/dev/null || exit 1; }
- "$PYTHON" -c 'import sys, lingtai, lingtai.kernel; print("py=",sys.executable); print("lingtai=",lingtai.__file__); print("kernel=",lingtai.kernel.__file__)'
- SP=$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')
- ls -1d "$SP"/*lingtai* 2>/dev/null; for f in "$SP"/*lingtai*.pth; do echo "== $f"; cat "$f"; done
- "$PYTHON" -c 'import json; d=json.load(open("init.json")); print("init.json OK"); print("addons=",d.get("addons")); print("mcp=",list((d.get("mcp") or {}).keys()))'
- ls -l .notification/nudge.json 2>/dev/null && sed -n '1,120p' .notification/nudge.json
- git -C "$(pwd)" status --short --branch 2>/dev/null || true
- ```
+ Choose one shape; nulls are explicit because the System input is closed.
```text
- mcp(action="info", input={}, reasoning="pre-refresh registry health and problems")
+ system(action="refresh", input={"reason":"<authorized reload reason>","preset":null,"revert_preset":null}, reasoning="reload <targeted change>")
+ system(action="refresh", input={"reason":null,"preset":"<exact allowed path>","revert_preset":null}, reasoning="activate authorized preset")
+ system(action="refresh", input={"reason":null,"preset":null,"revert_preset":true}, reasoning="return to configured default preset")
```
- Proceed only if: interpreter unambiguous, exactly one lingtai path marker whose entries
- exist and agree with `lingtai.__file__`, `init.json` parses, `mcp` block matches the
- registry, `problems` empty or explained, and no `kernel_version` nudge is the real
- trigger.
+ Execute exactly one of those lines. Never stack calls or refresh again “to be
+ sure.” A successful tool result only accepts or hands off the request; it does
+ **not** prove that a replacement process started. Missing launch-command or
+ watcher setup, handshake failure, and terminal watcher exhaustion remain
+ failures until fresh-process evidence exists.
- ### Recipe B — Daemon-preset preflight (before dispatching daemon work with an explicit preset)
+ ## Capture one targeted receipt
+ Record one compact receipt, not a second checklist:
+
```text
- 1. system(action="presets", input={}, reasoning="read the allowed-only catalog")
- 2. # If the path you want is absent, it is NOT authorized (allowed-catalog
- # mechanics: substrate-manual §11). Ask the config owner to add it, then:
- system(action="refresh",
- input={"reason": "pick up new allowed preset entry",
- "preset": null, "revert_preset": null},
- reasoning="apply the config owner's allowed-list edit")
- 3. system(action="presets", input={}, reasoning="confirm the new path is now listed")
- 4. # Pass the EXACT path from step 3 as tasks[].preset — not the pre-authorization
- # string, not a library-screen path, not a shorthand.
+ mode: A | B | C
+ target: <exact agent/workdir>
+ authority_and_reason: <who/what authorized this transaction>
+ refresh_calls: 1
+ fresh_process: <new incarnation or heartbeat advanced beyond the pre-call baseline>
+ target_assertion: <one changed surface, runtime tuple, or repaired fault + result>
+ originating_channel_round_trip: <required for B; otherwise n/a>
```
- An unauthorized explicit `tasks[].preset` refuses the **entire batch** before dispatch.
- Omitting `preset` entirely inherits the parent's regular surface and skips this check.
- External CLI backends (`claude-p`, `codex`, `opencode`, …) skip LingTai preset
- resolution entirely — this recipe does not apply to them.
+ For B, the update/build owner must freeze before refresh: target, exact
+ `runtime_tuple` fields (`sys_executable`, `lingtai_file`,
+ `lingtai_kernel_file`, and `version_or_head`), plus intended `selectors`.
+ Consume that receipt and compare intended selectors once; do not rerun `.pth`,
+ editable-install, package, or general Git audits. On the fresh process, compare
+ those four runtime fields directly to the frozen tuple. If MCP interpreter or
+ config changed, include the expected
+ MCP entry and relevant problem state in that same target assertion. Then complete
+ one real request/response round trip on the originating channel. If
+ `LINGTAI_RUNTIME_PYTHON` is absent or the tuple differs, stop and diagnose
+ through `runtime-update-checks`; never substitute PATH Python or a guessed venv.
- ### Recipe C — Post-refresh verification (always, immediately after refresh returns)
+ ## Mode C: recover one fault, once
- ```bash
- # POSIX. Uses only the exported runtime interpreter; never guesses a path.
- PYTHON="$LINGTAI_RUNTIME_PYTHON"
- [ -n "$PYTHON" ] || { echo "LINGTAI_RUNTIME_PYTHON is unset — stop; locate the actual launcher/runtime venv via runtime-update-checks instead of guessing one." >&2; return 1 2>/dev/null || exit 1; }
- "$PYTHON" -c 'import sys, lingtai, lingtai.kernel; print(sys.executable, lingtai.__file__, lingtai.kernel.__file__)'
- SP=$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])'); ls -1d "$SP"/*lingtai*
- sed -n '1,200p' system/manifest.resolved.json 2>/dev/null
- ```
+ 1. Read the returned error first. If the watcher exhausted its bounded retries,
+ also read `logs/refresh_failed_permanent.json` and the matching high-priority
+ system notification. That artifact is terminal evidence, not permission to
+ launch again.
+ 2. Route the named fault: preset/refusal semantics to `substrate-manual`; source,
+ venv, selector, or import mismatch to `runtime-update-checks`; MCP fault to
+ `mcp-manual`; context fit/rebuild to `context-manual`; launcher/watcher fault
+ to its owner. Do not rerun the happy-path table.
+ 3. Repair only that fault with the required human/config-owner authority. If the
+ cause is unknown, the repair is unproved, or a permanent failure has not been
+ escalated to the human/launcher owner, stop.
+ 4. After a specific repair, permit exactly one deliberate retry and produce the
+ C receipt. A failed retry ends the transaction; do not loop or stack calls.
- ```text
- mcp(action="info", input={}, reasoning="post-refresh: confirm MCP tools actually loaded")
- system(action="presets", input={}, reasoning="post-refresh: confirm active preset and allowed set")
- ```
+ ## Hard scope boundary
- Confirm, in order: new process importing from the intended source with no stale/duplicate
- lingtai `.pth` → MCP count/`problems`/expected tools present → active
- preset is what you asked for → the specific tool you are about to use still exists.
- Only then dismiss the motivating nudge.
+ This reference is guidance, not a public command, script, runtime guard, or
+ installer. It performs no write, migration, download, package switch, config
+ change, allowlist expansion, or automatic retry. Owner checks remain available
+ on their triggers; `.pth`, install provenance, MCP, preset, Git, nudge, whole
+ tool surface, and daemon checks are never unconditional refresh ceremony.