Skip to content

feat(cognitive-threads): the ten reflective threads on the shared Mind (#5)#3142

Open
rysweet wants to merge 8 commits into
mainfrom
feat/issue-5-design-plan-first-heavily-reviewed-then-build-10-n
Open

feat(cognitive-threads): the ten reflective threads on the shared Mind (#5)#3142
rysweet wants to merge 8 commits into
mainfrom
feat/issue-5-design-plan-first-heavily-reviewed-then-build-10-n

Conversation

@rysweet

@rysweet rysweet commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Matures Simard's single mind with ten new cognitive threads (issue #5),
scheduled by the existing shared Mind alongside OODA. These are not new
identities or a new scheduler — each is one more CognitiveThread on the
existing brain. Recipes + prompts + thin deterministic rails over new code:
nine threads are thin rails over an agentic recipe invoked through one shared
seam; interoception is recipe-free deterministic sensing. All ten are OFF
by default
behind a double env gate and are fully additive.

Threads (issue's canonical list; red-team #5 and attention/executive #6 excluded):
🪞 metacognition · 💤 consolidation · 🔁 reflection · 🔮 prospection ·
🎚️ salience · 👤 operator_model · 🧩 analogy · ⚖️ values_deliberation ·
❤️ interoception · 📖 narrative.

Design → build

The design/plan phase produced durable docs and a tests-first RED contract
(src/cognitive_threads/tests_catalog.rs, 39 hermetic unit tests + 10 ignored
live-smoke checks). This PR implements them GREEN plus the recipes, registration,
and the salience→Decide seam. Durable docs only — no point-in-time reports.

  • Catalog (reference): docs/reference/cognitive-threads-catalog.md
  • Salience/Decide (concept): docs/concepts/salience-and-decide.md
  • RecipeInvoker seam (reference): docs/reference/recipe-invoker-seam.md
  • Batch how-to: docs/howto/configure-cognitive-thread-batch.md

What lands

  • One shared brick recipe_rail: the RecipeInvoker seam + production
    RecipeRunnerInvoker; the no-silent-degradation classification (only a clean
    Json result writes — both SemanticMiss and InfraFailure fail the tick
    with zero durable writes); security helpers sanitize_value,
    fence_untrusted, secret_scrub (run-based; redacts punctuation-wrapped
    tokens), validate_concept_key; one capacity-checked goal-proposal helper.
  • salience_signal: numeric-only, id-validated, atomic Decide-facing signal
    writer + fail-closed consumer (presence + size + schema + staleness) + the
    advisory Decide ordering.
  • Ten tick rails, each writing durable prefixed facts/metrics/goals;
    guarded cadence for reflection and values_deliberation.
  • Nine recipe + prompt assets under prompt_assets/simard/recipes/, each
    fencing untrusted memory and emitting a strict-JSON envelope.
  • Registration of the ten threads behind the master gate; a fail-closed,
    additive
    salience→Decide reorder between Orient and Decide (a no-op when no
    fresh signal exists); the global SIMARD_THREAD_INTERVAL_SCALE knob.

Abstraction-gap note

Simard's brain abstraction held: ThreadKind is pure telemetry, so ten threads
needed only eight new enum variants + ten thin rails + one shared seam + one
scoped OODA seam. No new "thread framework". No memory-architecture changes
(consolidation forgetting is advisory/dry-run via existing memory APIs), so no
amplihack-memory-lib pin bump
was required.

Review / quality

  • Independent code review performed; one Medium finding (secret_scrub leaking
    punctuation-wrapped tokens) fixed with run-based redaction + a regression
    test.
  • cargo fmt --all -- --check ✓ · cargo clippy --all-targets --all-features --locked -- -D warnings ✓ (debug + release) · all cognitive_threads tests ✓.
  • No --admin, no --no-verify; pre-commit + pre-push gates green.

⚠️ Do not merge

Please leave this PR open for human review. It is intentionally left
unmerged — the final merge decision is the operator's.

#5)

Mature Simard's single mind with ten new cognitive threads — metacognition,
consolidation, reflection, prospection, salience, operator_model, analogy,
values_deliberation, interoception, and narrative — scheduled by the existing
shared `Mind` alongside OODA. Recipes + prompts + thin deterministic rails over
new plumbing: nine threads are thin `CognitiveThread` rails over an agentic
recipe invoked through one shared seam; interoception is recipe-free
deterministic sensing. All ten are OFF by default behind a double env gate and
are fully additive.

What lands:
- Shared brick `recipe_rail`: the `RecipeInvoker` seam (+ production
  `RecipeRunnerInvoker`), the no-silent-degradation classification
  (Json / SemanticMiss / InfraFailure — both misses fail the tick with zero
  writes), and the security helpers `sanitize_value`, `fence_untrusted`,
  `secret_scrub` (run-based, redacts punctuation-wrapped tokens), and
  `validate_concept_key`; plus one capacity-checked goal-proposal helper.
- `salience_signal`: the numeric-only, id-validated, atomic Decide-facing signal
  writer and its fail-closed consumer (presence + size + schema + staleness),
  and the advisory Decide ordering used by the OODA cycle.
- Ten `tick` implementations, each writing durable prefixed facts/metrics/
  goals; guarded cadence for reflection and values_deliberation.
- Nine recipe + prompt assets under prompt_assets/simard/recipes/, each fencing
  untrusted memory and emitting a strict-JSON envelope.
- Registration of the ten threads behind the master gate; a fail-closed,
  additive salience→Decide reorder between Orient and Decide (no-op when no
  fresh signal); the global `SIMARD_THREAD_INTERVAL_SCALE` knob.
- Durable docs (catalog reference, salience/Decide concept, RecipeInvoker seam
  reference, batch how-to) and the tests-first `tests_catalog` contract
  (39 hermetic unit tests + 10 ignored live-smoke checks) plus a secret-scrub
  regression suite.

No memory-architecture changes (consolidation forgetting is advisory/dry-run via
existing APIs), so no amplihack-memory-lib pin bump. No --admin/--no-verify.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rysweet rysweet force-pushed the feat/issue-5-design-plan-first-heavily-reviewed-then-build-10-n branch from 93906ae to d02c554 Compare July 8, 2026 11:40
… ContextFile (#5)

Step 8b (external-service integration): harden the `recipe-runner-rs` seam so a
large recall can never crash the spawn or silently truncate.

`RecipeRunnerInvoker::invoke` passed every context var inline as `-c k=v`. Nine
recipe-backed threads pass **unbounded** memory there — `fence_untrusted(
search_facts(...))` (up to 30 facts of arbitrary size) — with no cap. That is
the exact class of bug the codebase already fixed twice: the progress-checker
caps inline values at 8000 chars (#2127), and unbounded inputs (journal
`day_context`, episode distillation) go through `ContextFile` — payload to a
private 0700 temp file, only `<key>_path=<abs>` on argv — to dodge
`E2BIG`/"Argument list too long" at `execve` (#2640/#2692/#2622/#2619;
`MAX_ARG_STRLEN` is 128 KiB/arg). The new invoker did neither, and
`sanitize_value` also flattened the fenced region's newlines. Capping would
truncate the memory a thread reasons over = silent degradation, violating this
feature's core invariant (I4), so use the file channel.

What changes:
- `recipe_rail`: `UNTRUSTED_OPEN/CLOSE` consts (reused by `fence_untrusted`),
  `is_fenced_payload`, and a testable `build_context_args` — a fenced payload is
  written verbatim to a `ContextFile` and only `<key>_path=<abs>` rides on argv
  (byte-for-byte, no truncation); a small scalar stays inline as
  `<key>=<sanitize_value>`. `invoke` uses it and holds the guards across the
  spawn. Three unit tests pin the transport.
- The nine recipe assets declare each fenced var as `<key>_path` and have the
  prompt read the file at `{{<key>_path}}` (mirrors the journal/distill recipes);
  small scalars stay inline.
- Docs: seam reference documents the transport + adds SR-13 (E2BIG safety); the
  add-a-thread howto tells future authors to use `{{<key>_path}}` for fenced
  vars.
- Cleanup: correct the now-stale `todo!()`/RED "Status" doc-comments across the
  module (code has been GREEN since d02c554).

The `gh` issue seam (engineer_log_analysis) is already length-bounded (title
<=80, body <=4000 chars), so it needs no change.

cargo fmt ✓ · clippy --all-targets --all-features ✓ · cognitive_threads tests
76 passed / 0 failed / 10 ignored.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rysweet

rysweet commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

🧭 Illustrated Guide — PR #3142: The ten reflective cognitive threads

Reviewer walkthrough for feat(cognitive-threads): the ten reflective threads on the shared Mind (#5) — +6,487 / −23 across 39 files. Generated to help human review; this PR is intentionally left open for the operator to merge.

1. Problem statement

Simard has one "mind" that today runs a single reasoning loop (OODA: Observe → Orient → Decide → Act). It reacts, but it does not reflect: it never checks whether its own confidence was justified, never consolidates what it learned overnight, never rehearses future scenarios, and never steps back to weigh its values before acting.

Issue #5 asks for ten reflective cognitive threads that give the existing mind those capabilities — metacognition, memory consolidation, reflection, prospection (foresight), salience (what deserves attention), an operator model (understanding the human it works for), analogy, values deliberation, interoception (sensing its own internal state), and narrative identity.

The hard requirements:

  • Additive and safe. The daemon that runs in production must behave identically unless these threads are explicitly switched on. No regressions to the OODA loop.
  • Recipes over code. Each thread should be repeated structured reasoning (an agentic recipe/prompt), behind a thin deterministic guardrail — not a pile of hand-written heuristics.
  • No silent failure. If a thread's reasoning step fails or returns something unusable, it must fail visibly and write nothing — never guess or half-write.

2. Approach overview

The key insight: these are not new identities and not a new scheduler. Each thread is one more CognitiveThread scheduled by the existing shared Mind, exactly like the OODA thread already is. Nine of the ten are a thin rail over an agentic recipe invoked through one shared seam; interoception is recipe-free deterministic sensing.

flowchart TD
    Mind["Shared Mind (existing scheduler)"] --> OODA["OODA thread (existing)"]
    Mind --> T["10 new cognitive threads"]
    subgraph T["Ten reflective threads (all OFF by default)"]
      MC["🪞 metacognition"]
      CO["💤 consolidation"]
      RE["🔁 reflection"]
      PR["🔮 prospection"]
      SA["🎚️ salience"]
      OM["👤 operator_model"]
      AN["🧩 analogy"]
      VD["⚖️ values_deliberation"]
      IN["❤️ interoception"]
      NA["📖 narrative"]
    end
    MC & CO & RE & PR & SA & OM & AN & VD & NA -->|"one shared seam"| RI["RecipeInvoker → agentic recipe (strict JSON)"]
    IN -->|"deterministic, no recipe"| Sense["reads own metrics"]
    SA -.->|"advisory, fresh signal only"| Decide["OODA Decide ordering"]
Loading

Every thread is guarded by a double env gate and writes only durable, prefixed facts/metrics/goals. With the master gate unset, nothing registers — zero behavioural change.

3. Detailed walkthrough

3a. One shared seam so nine threads don't each reinvent "run a recipe"

Problem it solves: nine threads all need the same thing — run an agentic recipe, get back structured JSON, and refuse to act on anything that isn't clean JSON. Duplicating that nine times would be nine chances to leak a silent failure.

src/cognitive_threads/recipe_rail.rs defines a single RecipeInvoker trait and a three-way result that encodes the no-silent-degradation rule directly in the type system:

pub trait RecipeInvoker: Send {
    fn invoke(&self, recipe_name: &str, ctx_vars: &[(&str, String)]) -> InvokeResult;
}

pub enum InvokeResult {
    Json(Value),                       // the ONLY case a rail may write from
    SemanticMiss { /* … */ },          // recipe ran, output unusable → fail tick, write nothing
    InfraFailure { detail: String },   // recipe couldn't run → fail tick, write nothing
}

The critical property: only Json permits a write. Both SemanticMiss (the reasoning produced nothing usable) and InfraFailure (couldn't even run) map to a failed tick with zero durable writes via into_failed_outcome. This is the fail-visibly contract, enforced once, inherited by all nine recipe-backed threads.

The same brick also carries the security helpers every rail reuses: fence_untrusted (wraps memory-sourced text so a recipe can't be prompt-injected by stored content), secret_scrub (run-based redaction of tokens — see §4), validate_concept_key, and one capacity-checked goal-proposal helper.

3b. Each thread is a thin rail — the pattern, once

Problem it solves: a thread must assemble read-only context, treat stored memory as untrusted, invoke its recipe, and persist results under its own namespace — without leaking write authority or unfenced text.

src/cognitive_threads/threads/metacognition.rs is the exemplar (the other eight recipe threads follow the identical shape):

//! A thin CognitiveThread rail over the agentic recipe `metacognition-appraise`:
//! assemble read-only context in-thread, fence memory-sourced text as untrusted,
//! invoke the recipe through the shared RecipeInvoker, and persist under the
//! declared `metacog:` prefix only. OFF by default behind the double env gate.
pub const RECIPE: &str = "metacognition-appraise";

Each thread owns a fact/metric prefix (metacog:, salience:, narrative:, …) so its writes are namespaced and auditable, and proposes at most one goal per tick (capacity-checked). The rail holds a Box<dyn RecipeInvoker>, so tests inject a fake invoker and exercise the whole thread offline (see §5).

3c. Salience advises Decide — without ever changing what Simard does

Problem it solves: the salience thread decides what deserves attention, but attention must never silently rewrite the mind's actions. It can reorder priorities, not invent or change them — and only when its input is fresh and trustworthy.

The producer, src/cognitive_threads/salience_signal.rs, writes a numeric-only, id-validated signal file atomically. The consumer is aggressively fail-closed — an absent, truncated, oversized, stale, or schema-mismatched file is treated as no signal, never parsed as a guess:

// Presence + size guard: an absent or oversized file is treated as absent.
// Schema guard: a torn/mismatched file yields None, not a guess.
// Staleness guard (2 × interval): a stalled writer cannot pin Decide to an old ranking.
pub fn read_valid_signal(/* … */) -> Option<SalienceSignal> { /* … */ }

The OODA seam in src/ooda_loop/cycle.rs applies it as an advisory, additive reorder between Orient and Decide:

// Reorder priorities by a FRESH, numeric-only salience signal so Decide
// prefers salient goals. Fail-closed: an absent/stale/malformed signal (the
// default, since salience is OFF) yields NO reordering — behaves exactly as
// before. Salience ADVISES; it never changes an action's kind.
let salience_order = salience_signal::advisory_priority_order(/* … */);
if !salience_order.is_empty() {
    // stable sort: salient goals move to the front; everything else preserved
}

Because the default (salience OFF) yields an empty order, this branch is a no-op in production — the loop is byte-for-byte the prior behaviour.

3d. Registration behind the double env gate

Problem it solves: switching capabilities on must be deliberate and blast-radius-limited; an operator wants to enable one thread at a time.

src/cognitive_threads/mod.rs registers the ten threads only when both gates pass:

flowchart LR
    A["SIMARD_COGNITIVE_THREADS_ENABLED<br/>(master gate)"] -->|unset| Z["nothing registers · zero behaviour change"]
    A -->|set| B["per-thread gate<br/>SIMARD_THREAD_&lt;NAME&gt;_ENABLED"]
    B -->|set| C["thread registered on the shared Mind"]
    B -->|unset| Z
Loading

A global SIMARD_THREAD_INTERVAL_SCALE multiplier lets an operator slow every thread's cadence at once. Reflection and values_deliberation additionally carry a guarded cadence so they don't fire too eagerly.

3e. Recipes + prompts as assets, not code

Problem it solves: the reasoning itself belongs in reviewable, iterable recipe/prompt assets — not compiled Rust.

Nine YAML recipes under prompt_assets/simard/recipes/ (e.g. metacognition-appraise.yaml, salience-appraise.yaml, consolidate-sleep.yaml) each fence untrusted memory and emit a strict-JSON envelope the rail validates. This keeps the "structured thought" in prompts — the pattern the operator prefers over imperative heuristics.

4. Key decisions & trade-offs

  • Type-encoded no-silent-degradation. Making Json the only writable InvokeResult variant means a rail cannot accidentally persist from a failed recipe — the compiler enforces it. Trade-off: every rail must explicitly map the two failure cases, which is deliberate boilerplate.
  • Salience is advisory-only. It reorders, never re-kinds, actions, and only on a fresh numeric signal. This bounds the risk of the attention system ever driving a wrong action. Trade-off: salience can't veto a goal, only deprioritise it.
  • No memory-architecture changes. Consolidation's "forgetting" is advisory/dry-run over existing memory APIs, so no amplihack-memory-lib pin bump was needed — consistent with the rule that memory-engine work lands in the library, not Simard.
  • Abstraction held. Ten threads needed only eight new ThreadKind telemetry variants + ten thin rails + one shared seam + one scoped OODA seam — no new "thread framework". ThreadKind stays pure telemetry.
  • Off by default, double-gated. Prioritises production safety over convenience: enabling requires two explicit env vars, limiting blast radius during rollout.
  • Interoception is deterministic. It senses the mind's own metrics directly with no recipe, avoiding an LLM round-trip for a pure sensing task.

5. Testing

  • Tests-first RED contract. src/cognitive_threads/tests_catalog.rs (+1,046 lines) defines the contract before the implementation: ~49 hermetic offline unit tests plus ~12 #[ignore]d live-smoke checks. The unit tests inject a fake RecipeInvoker, so every thread's tick — including the fail-closed paths (SemanticMiss/InfraFailure write nothing) — is exercised with no network and no real recipe run.
  • Fail-closed coverage. Salience presence/size/schema/staleness guards and the Decide no-op-when-absent behaviour are directly asserted.
  • Security regression. An independent review found one Medium issue — secret_scrub leaking punctuation-wrapped tokens — now fixed with run-based redaction and a dedicated regression test.
  • Gates green. cargo fmt --all -- --check ✓ · cargo clippy --all-targets --all-features --locked -- -D warnings ✓ (debug + release) · all cognitive_threads tests ✓ · pre-commit + pre-push green · no --admin, no --no-verify.

Generated illustrated guide. Deep-link anchors are best-effort; if an anchor doesn't resolve, use the Files tab.

@rysweet

rysweet commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

🛠️ Crusty-old-engineer review — PR #3142

Reviewed the actual code at head 6b6ad8f8 (worktree build; all 76 cognitive_threads unit tests pass, 10 live-smoke ignored). Short version: the shared seam is careful, security-conscious work. There is one architectural defect that must be fixed before this feature is usable, plus two minor notes.

Short framing

This PR adds ten reflective threads whose entire premise is that they run in the background, subordinate to the object-level OODA loop. The wiring does the opposite: it runs them inline on the main OODA loop thread, so enabling one stalls the very loop it is supposed to reflect on.

🔴 Finding 1 — BLOCKER: reflective ticks block the object-level OODA loop

mind.run_due(&mut ctx) is called inline on the main daemon loop thread (daemon/mod.rs:1447), and each recipe-backed thread's tick() calls a blocking cmd.output() on recipe-runner-rs (recipe_rail.rs:511) — an agentic recipe that routinely runs for minutes. Up to DEFAULT_BUDGET = 2 such threads run serially per tick. So each enabled reflective thread delays the next authoritative OODA cycle by the full recipe duration.

The cognitive_runtime tokio handle is built and threaded through ThreadContext.runtime, but only interoception uses it (a cheap sync hop). The blocking recipe invokes are never offloaded.

This contradicts three things:

  • This PR's own comment — "background cognitive threads on their own cadence" (daemon/mod.rs:663).
  • The Overseer pattern in the same file (~daemon/mod.rs:1456): "Runs on a background thread — never inline — so a long (network-bound) tick cannot block or stall the OODA loop," with an overlap guard. The correct pattern already exists a few lines away.
  • The standing rule against blocking working agentic steps.

Metacognition lens (as requested). Metacognition is the monitoring and control of one's own cognition — and a healthy metacognitive architecture keeps reflection subordinate to object-level cognition; the monitor must not starve the process it monitors. Encoding the reflective loop so it blocks the primary reasoning loop is a textbook metacognitive-regulation failure: the "thinking about thinking" halts the thinking. Getting the scheduling relationship right (reflection yields to action) is as much a part of "consuming the metacognition concept correctly" as the appraisal logic inside the thread. The appraisal content (calibration: stated confidence vs actual outcome, error/bias signatures, ≤1 recalibration goal) is well aligned with the concept; the control-loop placement is not.

Mitigation today: threads are OFF by default, so no production regression right now — but the feature is unusable-as-designed the moment it is switched on, which is the point of shipping it.

Fix: run the non-critical scheduler pass on the existing cognitive_runtime background worker (or a dedicated named std::thread) behind an overlap guard (drop a cognitive pass if the previous one is still running), mirroring the Overseer wiring already in this file. OODA (Priority::Critical) stays inline and authoritative and runs first, byte-for-byte preserved. No wall-clock timeout — liveness via the overlap guard, never a kill.

🟡 Finding 2 — minor: looks_high_entropy over-redacts git SHAs

A 40-char git SHA (alpha+digit, ≥32) is redacted to [REDACTED] by recipe_rail.rs looks_high_entropy. Fail-safe direction (over-redaction, not a leak), but it strips legitimate commit references out of narrative/reflection facts, reducing their value. Consider letting pure-hex runs of 7–40 chars pass. Low priority.

🟡 Finding 3 — minor/tracked: brittle JSON-scraping fallback

classify_recipe_stdout falls back to recipe_output::extract::extract_json_payload (stdout scraping) — the brittle-parse antipattern already tracked in #2679/#2658. The primary path (serde RecipeEnvelope) is structured and fine; only the fallback is fragile. Acceptable as a bounded fallback; noting for the record, not blocking.

Credit where due

Type-encoded no-silent-degradation (only Json writes); correct argv discipline + out-of-band ContextFile transport that avoids E2BIG; run-based secret_scrub; fail-closed salience consumer; panic isolation + capped-exponential backoff; salience is advisory-only and a no-op when absent. This is careful work — the one thing standing between it and "happy" is moving the reflective pass off the OODA thread.

Verdict

NEEDS_CHANGES — fix Finding 1 (offload + overlap guard) before merge. Findings 2–3 are minor. Re-review after the fix.

Run cognitive-thread scheduler passes on a background thread with an overlap guard so blocking recipe-backed reflective threads cannot delay the next authoritative OODA cycle. Keep Overseer health snapshots non-blocking with try_lock.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rysweet

rysweet commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

PR #3142 evaluation plan — metrics, canary install, and merge criteria

This is not a vibes test. Ten reflective threads are supposed to improve Simard's cognition without degrading the authoritative OODA loop. Measure both halves. If the core loop slows down or starts making worse decisions, the feature failed even if the new thread logs look interesting.

1. What to measure

Use the SRE framing: define service-level indicators first, then objectives. For this PR the "service" is Simard's live cognitive loop.

A. Non-negotiable safety / no-regression metrics

These decide whether the experiment is allowed to continue.

Metric Why it matters Success criterion
OODA cycle cadence: cycle start-to-start and cycle duration p50/p95 Reflective work must not stall object-level cognition. This is the blocker found in review. PR enabled p95 cycle duration must be within +10% of main baseline, and no missed cycle caused by cognitive-thread work.
OODA action quality guardrails: failed actions, blocked writes, no-progress strikes, panic count Reflection is useless if it destabilizes acting. No increase in panics; no new write-guard violations; no new no-progress pattern attributable to threads.
Resource cost: CPU, RSS, disk, cognitive store size/WAL growth, recipe invocations/hour, Copilot/API spend Background thinking can eat the host. It usually does if nobody watches it. CPU/RSS within normal envelope; store/WAL growth explainable; cost per useful output below threshold agreed before rollout.
Thread isolation: overlap-dropped cognitive passes, per-thread failures, semantic misses, infra failures Tells us whether the system is doing useful work or just failing quietly. Drops are acceptable during long recipes; failures must be visible. Sustained success rate per enabled thread should be >=80% after warmup.
No silent degradation: count of SemanticMiss / InfraFailure with zero durable writes Confirms the PR's main safety claim. Every failed recipe tick logs a failed outcome and writes nothing durable.

B. Effectiveness metrics by cognitive function

Metacognition is monitoring + control of cognition, not just a prompt named "metacognition." So measure whether monitoring changes later behavior.

Thread Metric What success looks like
Metacognition Calibration: predicted confidence vs actual result; Brier score / expected calibration error where confidence is present; count of repeated error signatures Fewer repeated mistakes after a metacognitive fact appears; calibration improves versus baseline.
Consolidation Duplicate fact ratio, stale/contradictory fact detection, recall precision@k on fixed probes before/after consolidation Recall quality improves or duplicate/stale facts decline without deleting useful memory. Since forgetting is dry-run/advisory, measure recommendation quality first.
Reflection Postmortem action uptake: reflected failure → concrete goal/fix → recurrence rate Reflections reduce recurrence of the same failure mode. Otherwise they are diary entries.
Prospection Risk prediction hit rate, false-positive rate, lead time before incident/blocker Predicted risks match later blockers often enough to justify attention cost.
Salience Top-k agreement with operator/overseer priority; time-to-attend urgent work; no action-kind mutation Salience reorders attention toward genuinely important work without changing what actions are allowed.
Operator model Preference violations per day; preference recall accuracy on known operator rules Fewer violations of standing operator rules; no invented preferences.
Analogy Accepted analogy-driven suggestions / total suggestions; duplicate-suggestion rate Reuse helps solve work, not just produce clever comparisons.
Values deliberation Policy conflicts detected before action; false veto/false alarm count It surfaces real tradeoffs before risky actions. It must not become a veto machine.
Interoception Resource-pressure lead time: host/disk/store anomalies detected before external symptoms It catches resource issues early and proposes bounded self-maintenance.
Narrative Goal continuity: fewer abandoned/forgotten workstreams; contradiction corrections Narrative improves continuity across sessions, not self-mythology.

2. Trial design

Do not test this by flipping all ten switches on production and admiring the dashboard. That is how slow incidents are born.

Phase 0 — install path proof

Because the operator requirement is now that Simard and Crocutus install and run from the installer, the PR trial must use the installer path. If the installer cannot install a specific PR/commit into an isolated state root, that is not a test inconvenience; it is a deployment blocker.

Required before trial:

Phase 1 — baseline on main

Run current main with cognitive threads disabled.

  • Duration: at least 12 hours, preferably 24 if the host is not under urgent pressure.
  • Same host class, same state-root shape, same GitHub/Copilot credentials, same OODA interval.
  • Capture: OODA cycle metrics, action outcomes, memory recalls/writes, panics, E2BIG, resource usage, cost, goal throughput.

This is the control. Without it, we are just comparing PR logs to optimism.

Phase 2 — PR #3142 canary, narrow enablement

Install PR #3142 head from the installer into an isolated canary state root copied from a fresh memory backup. Start with the safest, highest-signal subset:

  1. SIMARD_COGNITIVE_THREADS_ENABLED=1
  2. Enable interoception first. It is deterministic and recipe-free.
  3. Then enable metacognition + salience only.
  4. Keep the per-tick budget at 1 initially: SIMARD_MIND_MAX_NONCRITICAL_PER_TICK=1.
  5. Slow cadence with SIMARD_THREAD_INTERVAL_SCALE so thread activity is observable but not noisy.

Run for 6 hours. If safety metrics hold, run for another 12–24 hours and add reflection/prospection. Do not enable all recipe-backed threads in the first pass.

Phase 3 — comparison

Compare PR canary against main baseline:

  • Core OODA: cadence, duration, action success/failure, blocked writes, panics.
  • Thread health: runs/hour, success rate, duration, misses/failures, overlap drops.
  • Cognitive outputs: count and quality of facts/goals/signals by prefix.
  • Decision quality: did metacognition/reflection/prospection/salience change later behavior in a measurable way?

Use both quantitative counters and sampled human review. There is no honest fully automated metric for "better cognition" yet. Pretending otherwise would be cargo cult.

3. Merge criteria

Merge only if all are true:

  1. No-regression SLOs pass: OODA p95 duration within +10% of main baseline, 0 new panics, 0 new E2BIG, no store corruption, no unexplained cost spike.
  2. Isolation works: long recipe ticks do not delay OODA cycles; overlap guard drops rather than stacks.
  3. At least one cognitive function shows useful signal:
    • metacognition identifies repeated error signatures that lead to fewer repeats, or
    • salience improves attention ordering without action mutation, or
    • interoception catches a real resource issue before external failure, or
    • reflection/prospection produces a useful goal that is acted on and not duplicate noise.
  4. Failure mode is clean: semantic misses and infra failures are visible, bounded, and produce zero durable writes.
  5. Installer path works: main and PR can both install/run/rollback through the installer, not through ad-hoc binary swaps.
  6. Crusty re-review is satisfied and merge-ready passes.

4. Adjust instead of merge if

  • OODA p95 increases by more than 10% or cycles get skipped because cognitive work is running.
  • Thread success rate stays below 80% after warmup.
  • Salience repeatedly disagrees with operator priority and cannot explain why.
  • Metacognition produces self-commentary but no later behavioral correction.
  • Facts/goals become noisy enough that memory quality or operator attention degrades.
  • Installer cannot reproduceably install and rollback the PR.

References

Crusty bottom line

The PR is promising, but promising is not a merge criterion. Treat it as a canaryable control-loop change. The first thing it must prove is restraint.

Collapse the cognitive-runtime/overlap-guard condition per clippy while preserving the background-pass behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rysweet

rysweet commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

Crusty re-review after 22897d5d + 52b996fc

The blocker I called out is fixed.

The cognitive-thread scheduler pass no longer runs inline on the main OODA loop thread. Mind is now held behind Arc<Mutex<_>>, the pass runs on a named background thread, and an overlap guard drops a tick instead of stacking long recipe-backed passes. Overseer feed snapshots use try_lock, so a long reflective pass cannot block the loop just to collect advisory health.

Local checks I ran on the PR branch:

  • cargo check --lib
  • cargo fmt --all -- --check
  • cargo clippy --release --no-deps -- -D warnings
  • cargo test --lib cognitive_threads — 76 passed / 10 ignored

Verdict on the original code concern: satisfied.

Not a merge recommendation yet. This is still a control-loop feature, not a documentation patch. The measurement/canary plan in #3142 (comment) should be treated as the next gate: prove no OODA regression, prove thread isolation, and show at least one useful cognitive signal before merging. The PR is still explicitly NOMERGE/operator-review.

rysweet and others added 4 commits July 9, 2026 04:20
Avoid making the installer contract depend on dashboard-audit's headless browser codegen. The dashboard jobs cover that path; install-real validates the runtime daemon install path with signal enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve install_real conflict by keeping canonical installer contracts and preserving runtime-feature cargo install coverage.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the install-real cargo-install regression at module scope and preserve the canonical installer contract after merging main.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

📊 Coverage Summary

Generated by cargo llvm-cov --workspace --summary-only (nightly, excluding test files)

Module Lines Covered Coverage
Total 170862 143112 83.8%

Coverage data from CI run. Test files matching tests?/ are excluded from line counts.

@rysweet

rysweet commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

PR #3142 canary result — do not merge as-is

I ran the requested installer-path canary on ia2 after resolving conflicts and getting CI green.

What was tested

  • Baseline main: deployed commit 1a765d49, services active, dashboard 8080 -> 303, 0 strict panics/E2BIG in the sampled window.
  • PR feat(cognitive-threads): the ten reflective threads on the shared Mind (#5) #3142 head: e3faeea680e3c0c14538ae3755439c654378a50c, installed via simard install.
  • Canary gates:
    • SIMARD_COGNITIVE_THREADS_ENABLED=1
    • SIMARD_THREAD_INTEROCEPTION_ENABLED=1
    • SIMARD_THREAD_INTEROCEPTION_INTERVAL_SECS=60
    • SIMARD_MIND_MAX_NONCRITICAL_PER_TICK=1
    • SIMARD_THREAD_INTERVAL_SCALE=1

Safety notes

The first canary attempt was aborted and rolled back because an old PR binary tried to open the live v42 store as storage v41 and quarantined/rebuilt an empty store. Main was restored from the original quarantined v42 store and then the PR binary was clean-rebuilt. The clean PR binary was tested against an isolated copied store (direct-open) before the second canary.

Canary result

The clean PR canary ran without storage-version mismatch and interoception itself succeeded:

interoception: 2 probe(s), breach=false

But the OODA loop regressed badly:

OODA cycle #1448: 20 priorities, 20 actions (1/20 succeeded), Act complete in 244.9s
OODA cycle #1449: 20 priorities, 20 actions (1/20 succeeded), Act complete in 244.6s
OODA cycle #1450: 20 priorities, 20 actions (1/20 succeeded), Act complete in 240.6s
RSS health: ~3.1-3.3 GiB

This fails the posted merge criteria: p95 OODA duration is nowhere near +10% of baseline, and action success collapsed to 1/20. The feature is not merge-ready as-is.

Rollback

Rolled back via installer to main 1a765d49cb19d0d3920232436ff156de416a0788, restored pre-canary memory file backup, unset cognitive-thread canary env vars, and verified:

simard-ooda active
simard-signal active
dashboard=303

Verdict

Do not merge. Adjust before another canary. The most obvious next investigation is why enabling the cognitive-thread runtime changed effective OODA fanout to 20 priorities/actions and why action success dropped to 1/20 even with only interoception enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant