Skip to content

Deliberate reasoning loop + Memory v2 + prompts registry - #2

Merged
IshaanRSharma merged 20 commits into
mainfrom
feat/prompts-registry
Jun 22, 2026
Merged

IshaanRSharma merged 20 commits into
mainfrom
feat/prompts-registry

Conversation

@IshaanRSharma

Copy link
Copy Markdown
Owner

Summary

Turns dexta's improvised investigations into a deliberate, measured progressive-reasoning loop, alongside Memory v2, active context acquisition, a prompts registry, and OSS hardening. The thesis holds throughout: the agents are the intelligence; determinism scaffolds and verifies. The two rails (faithfulness guard, treatment gate) are untouched.

Intelligence flow (Phases 0-6)

  • Phase 0 - reasoning-process eval (E7). eval reasoning grades the investigation path, not just the answer: cross-modal coverage, probe efficiency, gap-handling, path soundness. Built first so the rest is provable.
  • Phase 1 - working belief state. A structured BeliefState (hypotheses/evidence/gaps/confidence) the model revises via an update_belief tool, threaded through the loop. Additive; stays out of the faithfulness evidence pool.
  • Phase 2 - hypothesis ledger. Open hypotheses banked by prior analysis seed the live loop and reach the model in-prompt with stable ids, so it probes to discriminate or refute them in place.
  • Phase 3 - information-gain probe selection. The belief state suggests the most discriminating unexamined modality for its open hypotheses. Advisory, never a controller.
  • Phase 4 - adaptive stop conditions. High-confidence and no-new-information nudges to conclude instead of looping to the step budget; max_steps stays the only hard stop.
  • Phase 5 - mid-loop context acquisition. A request_context tool: when a gap blocks it, surface a precise, dosing-gated logging request instead of guessing. Reuses the unexplained-spike detector.
  • Phase 6 - deliberate synthesis pass. A grounded explanation plus evidence graph; every figure re-audited against the tool evidence pool, so the synthesis cannot surface a number the tools never produced.

Each phase was reviewed against Google's eng-practices bar and ships green.

Also in this branch

  • Memory v2: recall safety gate (active, non-dosing findings only, with what-was-excluded), CONTRADICTED status, /memory inspector.
  • Active context acquisition: detects unexplained spikes and asks the user to log them (/context).
  • Prompts registry: agent prompts as overridable markdown; dosing rail locked in code.
  • LLM providers: Gemini, Ollama, local llama.cpp.
  • OSS hardening: uv lock fix, /reports no longer blocks on PubMed, redacted stream/storage errors, Dockerfile, CODE_OF_CONDUCT, CITATION.cff.

Validation

  • 1401 passing, 46 skipped, 0 failing.
  • ruff and mypy clean across src, eval, tests (138 source files).
  • E7 metric is fully unit-tested; the live baseline run is pending API credits.

Take the transferable idea from arXiv 2603.16021 (interpretable, version-
controlled context as files) without its multi-agent-replacement thesis, which
does not fit dexta's rigor design.

agents/prompts/ holds prompts as markdown loaded by name; a user can override
any of them via [prompts] dir. The dosing/observation rail is a code constant
re-applied by with_safety, so an override can never remove the safety floor.
Proof slice migrates the chat system prompt verbatim (byte-identical, asserted);
the other ~10 agents follow the same pattern next.
Every agent prompt now loads from agents/prompts/*.md instead of an inline
constant: chat, discovery, insulin, coordinator (plan/replan), brief
(system/user), orchestrator (doctrine/system), investigator (reflect/write),
router (safety/route + the five family systems), seeker (reflect/goal), and
advisory (refine). System prompts that carry the dosing rail load through
with_safety so an override cannot strip it; the rest load verbatim.

Migration is byte-identical: each file was dumped from the original constant and
test_prompts_rollout.py asserts every constant equals its intended file (no
cross-wiring). Removed the now-dead orchestrator._HARD_RULES (folded into
orchestrator_system.md). ruff/mypy clean, full suite 1288 passed.
- README: add CI/license/python badges; repoint the deleted
  DEXTA_OSS_TECHNICAL_SPEC.md link to TECHNICAL_REPORT.md.
- Add CITATION.cff.
- CHANGELOG: note the prompt registry, Dockerfile, and CITATION.
Curated, committed docs (TECHNICAL_REPORT, EXTENDING, PRODUCT-OVERVIEW) now live
in guide/, separate from the gitignored docs/ dev notes. README, LICENSE, the
community-health files, and the legal/safety docs stay at root where GitHub and
readers expect them. Updated the README links and the test_extensibility
docstring.
- #8: rename the two _text_of variants by behavior (_content_text extractor vs
  _strip_code_fence) so one name no longer means two things.
- #10a: add the dexta-mcp console_scripts entry point.
- #10b: verified pattern.py:647 is live (not a dead branch); closed.
- #10c: monitor._severe_high now breaks a sustained-high run on a sensor gap so
  a gap is not counted as sustained (regression test added).
- #7 unblocker: CI now runs the Postgres parity suite against a postgres:16
  service, so the backend is tested and the dedup becomes verifiable.

ruff/mypy clean, full suite 1289 passed.
- #7: hoist the two byte-identical store helpers (_opt_json,
  _prediction_horizon_min) into store/_common.py, shared by both backends. The
  _row_to_* mappers genuinely differ (TEXT-JSON vs JSONB) and stay per-backend.
- #9: one _relative_time (server/_format.py, None-safe superset) replaces the 4
  server copies; one parse_json (agents/_json.py, optional logging context)
  backs the 3 former _parse_json copies.

ruff/mypy clean, full suite 1289 passed.
guide/DEMO.md (4-5 min walkthrough script for an AI/AI-health audience),
guide/UI-ROADMAP.md (the hero-visualization plan), guide/RELEASE.md (PyPI,
supply-chain CI, one-command Docker demo). PRODUCT-OVERVIEW gains a dedicated
"novel piece: agentic AI + LLM" section (LLM navigates, determinism computes,
rails enforced in code) and links the companion docs.
… user

The one net-new from the feature review. Determinism detects a context gap
(a spike >= 200 mg/dL with no logged meal or note within +/- 90 min); dexta
asks the user to log what happened. It asks, it never fabricates the value.

- models.ContextRequest: the observation-only question + the gap's evidence.
- agents/context_acquisition.py: ContextAcquisitionAgent.build(ctx) detects gaps
  deterministically, templates a safe question (passed through the dosing-advice
  gate), caps at 5, and optionally rephrases via a model that falls back to the
  template on any unsafe or failed output.
- server: /context page (views_context + template) listing the questions with a
  "Log it" link to the existing manual-logging page; dashboard link added.

Comprehensive tests (detector suppression by meal/note, cap, safety gate,
model fallback, minimal-store degrade, page render + empty state). ruff/mypy
clean, full suite 1302 passed.
The demo-facing UI pass (per guide/UI-FIX-PLAN.md):
- Hero glucose-trace chart on the dashboard (server-rendered SVG: target band,
  spike highlight, carb/bolus markers, attribution annotation) via server/charts.py
  + views_hero.py + _hero_chart.html.
- Trace timeline component (views_trace.py + _trace_timeline.html) and a goals
  sidebar view.
- Nav consolidated to primary tabs + a "More" group; cached header status pill.
- Reconciliation/findings view-model updates and template/CSS/JS polish.

Cleaned to the project bar: ruff clean, mypy strict clean (120 files), full
suite 1310 passed. (lint/type fixes: charts min/max + TYPE_CHECKING import,
hero-view helper extraction for complexity, nav-globals typing, status-pill cast.)
The highest-impact run-quality fix from the parity audit (guide/MEMORY-V2-AUDIT.md).

- recall() now returns only ACTIVE findings. Previously it excluded only STALE,
  so rejected / superseded / dismissed / contradicted findings could still be
  pulled into an agent's context and reasoned from. It now also emits an
  `excluded` list labeling each withheld memory by reason (not_used_rejected /
  superseded / contradicted / stale).
- New FindingStatus.CONTRADICTED. The skeptic already detected contradictions
  (downgraded confidence, logged notes) but left the finding ACTIVE; it now
  marks it CONTRADICTED, with rejection taking precedence. Wired into the
  embeddings status-weight, the findings graveyard, and the wiki graveyard.

Tests: recall excludes non-active and labels them; the skeptic sets
CONTRADICTED on a contradicting finding. ruff/mypy clean, full suite 1311 passed.
Finishes the run-affecting + demo-facing Memory v2 work:
- Recall now also withholds any active finding that reads as dosing advice
  (_ADVICE_RE over headline/body), labeled not_used_safety_blocked, so no dosing
  memory is retrievable.
- Memory guard evals (tests/test_memory_guard.py): stale/rejected/contradicted/
  superseded/dosing all excluded with the right reason; active used; used and
  excluded both reported.
- Memory Inspector page at /memory (views_memory.py + memory.html + nav): memory
  in use vs withheld, with the exclusion reason for each.

Deferred (tracked): the provenance-column migration (source_run_id,
coverage_limited, profile_version_id, etc.) is gated on the live-Postgres CI
parity check and is a follow-up. ruff/mypy clean, full suite 1321 passed.
Score the investigation path, not just the final answer: cross-modal evidence
coverage (did it consult the tool-classes the planted cause needs), probe
efficiency (tool calls vs a budget), gap-handling (flag missing context instead
of fabricating), and path soundness. Reuses the E6 InvestigationOutcome and a
shared attribution predicate so E6 and E7 cannot drift. Runner stays injectable,
so the metric is fully tested key-free. Adds the eval reasoning subcommand and a
gap scenario whose cause is unlogged.
Give the agent's understanding a place to live across steps: a structured
BeliefState (competing hypotheses with status, evidence, open gaps, running
confidence, summary) the model revises through an update_belief tool. The loop
offers the tool, streams a belief event after each tool round, and returns the
final state; the orchestrator threads one per ask. Fully additive: belief
defaults to None, so the existing run_reasoning_loop contract is unchanged. The
belief tool emits no evidence numbers, so it cannot leak past the faithfulness
guard. Foundation for hypothesis-driven probing and adaptive re-planning.
Wire the hypotheses store into the live belief: seed_belief_from_store pulls the
open hypotheses prior analysis banked and re-enters them as competing hypotheses
the orchestrator injects into the first-turn prompt with stable stored-{id}
keys, so the model probes to discriminate or refute them and can update their
status in place instead of minting duplicates. Read-only on the interactive path
(no surprise writes), deduped, capped, and defensive over an optional backend.
Seeding records prior hypotheses; it never decides for the model.
The belief state now suggests the most discriminating evidence not yet gathered
for its open hypotheses: a light heuristic that maps hypothesis keywords to
evidence modalities and recommends an unexamined one, folded into the snapshot
the model reads each step. The loop feeds it the tools actually called. Advisory
only: nothing branches on the suggestion to choose, skip, or stop probes, and it
stays out of the faithfulness evidence pool. Improves the probe-efficiency and
coverage that Phase 0 measures.
Turn the loop into probe -> update -> decide with explicit stop conditions: when
belief confidence is high or consecutive rounds add no new information, inject a
one-shot nudge to conclude and emit a stop_signal, rather than probing in circles
to the step budget. A per-round belief signature (evidence, confidence,
hypothesis status/statement/note) detects stagnation; refining a hypothesis
counts as progress, not a stall. Advisory only: nothing terminates the loop on
belief, max_steps stays the sole hard stop, and with no belief the loop is
unchanged.
When a gap blocks discrimination, the model can call request_context for the
moment it cannot explain. The tool reuses the batch detector's proximity rule
and dosing gate to return either the context already logged near that time (so
the model probes it) or a precise, observation-only logging request to surface
in the answer instead of guessing. It returns no evidence numbers, so it stays
clear of the faithfulness guard, and never fabricates the missing value.
Additive: an advisory tool on the belt, unchanged when the model ignores it.
At the end of an investigation, synthesize() fuses the belief and tool trace into
one explanation plus its evidence graph: the leading hypothesis, the alternatives
ruled out, the supporting evidence, the cross-modal probes, and the open gaps.
Every text field that can carry a number is re-audited against the tool evidence
pool and dropped if a figure does not trace, so the synthesis cannot surface a
number the tools never produced. The orchestrator attaches it only to a clean,
faithful conclusion (not a fallback or a treatment-gate fade). Grounds in
material already available; the deferred Memory v2 provenance schema is not a
dependency.
Phases 1-6 added update_belief and request_context to the trace; they are
bookkeeping, not probes, and would otherwise inflate the probe count and make a
post-synthesis E7 run incomparable to the pre-belief baseline. Promote META_TOOLS
to the public source of truth in investigation.py and have E7 count real probes
only, so synthesis and the reasoning eval agree on what is not a probe.
guide/ is for reader-facing docs; the intelligence-flow plan and the Memory v2
audit are internal implementation/working docs and belong under docs/.
@IshaanRSharma
IshaanRSharma merged commit 7716a29 into main Jun 22, 2026
2 of 3 checks passed
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