From 5acaff6197cd668ff4d3c38219763c9a48dd0c6e Mon Sep 17 00:00:00 2001
From: Zeki-AI-Agent
Date: Mon, 15 Jun 2026 17:17:05 +0200
Subject: [PATCH 1/6] docs: add Fincept AI Ops integration guide
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Documents the QuantMind ↔ fincept-ai-ops architecture:
- Vector search over quantitative finance literature
- MCP tool schema for knowledge queries
- Factor library synergy with backtest strategies
---
docs/fincept_integration.md | 61 +++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 docs/fincept_integration.md
diff --git a/docs/fincept_integration.md b/docs/fincept_integration.md
new file mode 100644
index 0000000..72cf947
--- /dev/null
+++ b/docs/fincept_integration.md
@@ -0,0 +1,61 @@
+# Fincept ↔ QuantMind Integration Guide
+
+## Overview
+
+QuantMind serves as the **knowledge extraction layer** for Fincept AI Ops.
+Rather than raw market data, Fincept can query QuantMind for:
+- Distilled research insights from financial papers
+- Structured signal metadata (alpha factors, risk signals)
+- Natural language Q&A over quantitative finance literature
+
+## Integration Architecture
+
+```
+fincept-ai-ops (FastAPI)
+ │
+ ├── GET /quant/search?q={query}
+ │ ↓
+ │ QuantMind Knowledge API
+ │ ↓
+ │ Vector Search (LanceDB / Chroma)
+ │ ↓
+ │ Extracted Insights JSON
+ │
+ └── MCP Tool: query_quantmind
+```
+
+## Usage Example
+
+```python
+from quantmind import QuantMindClient
+
+client = QuantMindClient(api_key="...")
+
+# Search for momentum factor research
+results = client.search(
+ query="momentum factor decay regime change",
+ top_k=5,
+ filters={"source_type": "paper", "year_gte": 2020}
+)
+
+for r in results:
+ print(r.title, r.insight_summary, r.confidence_score)
+```
+
+## Synergy with Fincept
+
+| Fincept Component | QuantMind Feature |
+|---|---|
+| Strategy generation | Research paper extraction |
+| Risk model inputs | Factor library |
+| Audit log enrichment | Source citation tracking |
+| Backtest hypothesis | Literature-validated signals |
+
+## Setup
+
+1. Clone both repos side-by-side
+2. Set `QUANTMIND_API_URL` in fincept `.env`
+3. Run `quantmind serve --port 8001`
+4. Fincept auto-discovers at startup
+
+See [fincept-ai-ops ROADMAP](../fincept-ai-ops/ROADMAP.md) for v1.2 integration milestone.
From bad60f581d5e23d6dba48a09d860caabf59c3349 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Jun 2026 06:01:39 +0000
Subject: [PATCH 2/6] docs: refresh agentic project positioning
---
.venv/lib64 | 1 +
.venv/pyvenv.cfg | 5 +
.../share/jupyter/kernels/python3/kernel.json | 16 ++
.../jupyter/kernels/python3/logo-32x32.png | Bin 0 -> 1084 bytes
.../jupyter/kernels/python3/logo-64x64.png | Bin 0 -> 2180 bytes
.../jupyter/kernels/python3/logo-svg.svg | 265 ++++++++++++++++++
.venv/share/man/man1/ipython.1 | 60 ++++
CONTRIBUTING.md | 206 +++++++-------
README.md | 189 +++++++++----
docs/ARCHITECTURE_FOR_NEW_DOMAINS.md | 145 ++++++++++
quantmind/flows/__init__.py | 7 +-
11 files changed, 731 insertions(+), 163 deletions(-)
create mode 120000 .venv/lib64
create mode 100644 .venv/pyvenv.cfg
create mode 100644 .venv/share/jupyter/kernels/python3/kernel.json
create mode 100644 .venv/share/jupyter/kernels/python3/logo-32x32.png
create mode 100644 .venv/share/jupyter/kernels/python3/logo-64x64.png
create mode 100644 .venv/share/jupyter/kernels/python3/logo-svg.svg
create mode 100644 .venv/share/man/man1/ipython.1
create mode 100644 docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
diff --git a/.venv/lib64 b/.venv/lib64
new file mode 120000
index 0000000..7951405
--- /dev/null
+++ b/.venv/lib64
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/.venv/pyvenv.cfg b/.venv/pyvenv.cfg
new file mode 100644
index 0000000..fdcdbd1
--- /dev/null
+++ b/.venv/pyvenv.cfg
@@ -0,0 +1,5 @@
+home = /usr/bin
+include-system-site-packages = false
+version = 3.12.3
+executable = /usr/bin/python3.12
+command = /usr/bin/python3 -m venv /home/runner/work/quant-mind/quant-mind/.venv
diff --git a/.venv/share/jupyter/kernels/python3/kernel.json b/.venv/share/jupyter/kernels/python3/kernel.json
new file mode 100644
index 0000000..788843f
--- /dev/null
+++ b/.venv/share/jupyter/kernels/python3/kernel.json
@@ -0,0 +1,16 @@
+{
+ "argv": [
+ "python",
+ "-m",
+ "ipykernel_launcher",
+ "-f",
+ "{connection_file}"
+ ],
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "metadata": {
+ "debugger": true,
+ "supported_encryption": "curve"
+ },
+ "kernel_protocol_version": "5.5"
+}
\ No newline at end of file
diff --git a/.venv/share/jupyter/kernels/python3/logo-32x32.png b/.venv/share/jupyter/kernels/python3/logo-32x32.png
new file mode 100644
index 0000000000000000000000000000000000000000..be81330765764699553aa4fbaf0e9fc27c20c6d2
GIT binary patch
literal 1084
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
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.venv/share/man/man1/ipython.1 b/.venv/share/man/man1/ipython.1
new file mode 100644
index 0000000..0f4a191
--- /dev/null
+++ b/.venv/share/man/man1/ipython.1
@@ -0,0 +1,60 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH IPYTHON 1 "July 15, 2011"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp insert n+1 empty lines
+.\" for manpage-specific macros, see man(7) and groff_man(7)
+.\" .SH section heading
+.\" .SS secondary section heading
+.\"
+.\"
+.\" To preview this page as plain text: nroff -man ipython.1
+.\"
+.SH NAME
+ipython \- Tools for Interactive Computing in Python.
+.SH SYNOPSIS
+.B ipython
+.RI [ options ] " files" ...
+
+.B ipython subcommand
+.RI [ options ] ...
+
+.SH DESCRIPTION
+An interactive Python shell with automatic history (input and output), dynamic
+object introspection, easier configuration, command completion, access to the
+system shell, integration with numerical and scientific computing tools,
+web notebook, Qt console, and more.
+
+For more information on how to use IPython, see 'ipython \-\-help',
+or 'ipython \-\-help\-all' for all available command\(hyline options.
+
+.SH "ENVIRONMENT VARIABLES"
+.sp
+.PP
+\fIIPYTHONDIR\fR
+.RS 4
+This is the location where IPython stores all its configuration files. The default
+is $HOME/.ipython if IPYTHONDIR is not defined.
+
+You can see the computed value of IPYTHONDIR with `ipython locate`.
+
+.SH FILES
+
+IPython uses various configuration files stored in profiles within IPYTHONDIR.
+To generate the default configuration files and start configuring IPython,
+do 'ipython profile create', and edit '*_config.py' files located in
+IPYTHONDIR/profile_default.
+
+.SH AUTHORS
+IPython is written by the IPython Development Team .
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 014f892..736deef 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,144 +1,144 @@
# Contributing to QuantMind
-Thank you for contributing to QuantMind! This guide provides essential information for developers.
+Thank you for contributing to QuantMind.
-## 🚀 Quick Setup
+QuantMind is now a **domain library on top of the OpenAI Agents SDK**, not a
+self-contained agent framework. The first production flow is finance-first, but
+the architecture is intentionally useful for broader agentic knowledge work.
-1. **Fork and clone** the repository
-2. **Set up environment**:
- ```bash
- uv venv && source .venv/bin/activate
- uv pip install -e .
- ```
-3. **Install pre-commit hooks**:
- ```bash
- ./scripts/pre-commit-setup.sh
- ```
+## 🚀 Quick setup
-## 🛠️ Development Setup
+```bash
+uv venv
+source .venv/bin/activate
+uv pip install -e ".[dev]"
+./scripts/pre-commit-setup.sh
+```
-### Pre-commit Hooks
+If `uv` is unavailable in your environment, a standard Python venv is an
+acceptable fallback:
+
+```bash
+python3 -m venv .venv
+source .venv/bin/activate
+pip install -e ".[dev]"
+```
-We use pre-commit hooks to ensure code quality and consistency. These hooks automatically format code, run linting, and perform other quality checks before each commit.
+## ✅ Canonical verification loop
-**Install pre-commit hooks:**
+Run this before every push:
```bash
-# Automated setup (recommended)
-./scripts/pre-commit-setup.sh
-
-# Or manual setup
-pip install pre-commit
-pre-commit install
-pre-commit install --hook-type pre-push
+bash scripts/verify.sh
```
-**What the hooks do:**
+This is the single source of truth for branch health. It runs:
-- **On every commit:**
- - Code formatting with `ruff format` (80-char line length)
- - Linting with `ruff check --fix` (auto-fixes issues)
- - File quality checks (trailing whitespace, EOF, YAML syntax)
- - Safety checks (large files, merge conflicts)
+1. `ruff format --check`
+2. `ruff check`
+3. `basedpyright`
+4. `lint-imports`
+5. `pytest --cov`
-- **On push to remote:**
- - Full unit test suite via `scripts/unittest.sh`
+CI runs the same script.
-**Manual execution:**
+## 🏗️ Current architecture
-```bash
-# Run formatting and linting
-./scripts/lint.sh
+The permanent module roots are:
-# Run all pre-commit hooks on all files
-pre-commit run --all-files
+- `quantmind/flows/` — apex orchestration layer
+- `quantmind/configs/` — typed inputs and config
+- `quantmind/knowledge/` — typed knowledge shapes and provenance
+- `quantmind/preprocess/` — fetch + format + clean
+- `quantmind/magic.py` — natural-language resolver
+- `quantmind/mind/` — upcoming memory/store work
+- `quantmind/utils/` — logger only
-# Run specific tests
-./scripts/unittest.sh tests/quantmind/sources/
-./scripts/unittest.sh all # Run all tests
-```
+Do **not** re-introduce the deleted transitional/runtime packages such as
+`quantmind.flow`, `quantmind.config`, `quantmind.llm`, or `quantmind.models`.
+
+## 🧭 Design rules
-**Troubleshooting:**
+- Prefer **pure functions** over framework classes.
+- Use the **OpenAI Agents SDK directly**; do not build a new runtime wrapper.
+- Keep **Pydantic models at boundaries** and small internal value types simple.
+- Use **absolute imports** across module boundaries.
+- Keep **comments and docstrings in English** with Google-style formatting.
+- Preserve architectural boundaries enforced by `import-linter`.
-- If hooks fail, fix the issues and commit again
-- To skip hooks temporarily (not recommended): `git commit --no-verify`
-- Update hooks: `pre-commit autoupdate`
+## 🧪 Testing expectations
-## 📝 Development Standards
+- Add or update tests under `tests/` when behavior changes.
+- New feature work should cover both success and failure paths.
+- Mock external services and network calls.
+- Keep coverage above the configured floor.
-### Code Requirements
-- **Location**: All new code in `quantmind/` module
-- **Style**: Google-style docstrings, 80-char line length
-- **Architecture**: Abstract base classes + dependency injection
-- **Type Safety**: Pydantic models + comprehensive type hints
+For implementation work, add a simple example when it helps demonstrate the new
+behavior clearly.
-### Testing
-- **Unit tests**: Required in `tests/quantmind/` (mirror module structure)
-- **Coverage**: Test success and error cases
-- **Mocking**: Mock external APIs and file systems
+## 🧩 Common contribution paths
-### Documentation
-- **Examples**: Add to `examples/quantmind/` for new features
-- **Docstrings**: Google-style format for all public methods
+### New knowledge type
-## 🏗️ Contribution Types
+- Add a schema under `quantmind/knowledge/`
+- Choose the right base shape (`FlattenKnowledge`, `TreeKnowledge`, or future
+ `GraphKnowledge`)
+- Implement `embedding_text()`
+- Add tests under `tests/knowledge/`
-### New Sources
-- Extend `BaseSource[ContentType]` in `quantmind/sources/`
-- Add config in `quantmind/config/sources.py`
-- Include tests and usage example
+### New flow
-### New Parsers
-- Extend `BaseParser` in `quantmind/parsers/`
-- Handle multiple content formats with error handling
+- Add a typed input/config module under `quantmind/configs/`
+- Add a pure `async def ..._flow(...)` under `quantmind/flows/`
+- Use `preprocess/` helpers instead of duplicating fetch/format logic
+- Return a typed knowledge object
+- Add tests under `tests/flows/`
-### New Taggers
-- Extend `BaseTagger` in `quantmind/tagger/`
-- Support rule-based and ML approaches
+### New source or format support
-### Storage Backends
-- Extend `BaseStorage` in `quantmind/storage/`
-- Implement indexing, querying, and concurrent access
+- Add leaf functionality under `quantmind/preprocess/`
+- Keep `preprocess/` independent of `configs/`, `knowledge/`, and `flows/`
+- Add focused tests under `tests/preprocess/`
-## 🔄 Pull Request Process
+### New domain extension
-1. **Create feature branch** from `master`
-2. **Follow conventional commits**: `type(scope): description`
-3. **Pre-commit hooks** run automatically on commit/push
-4. **Before submitting**:
- ```bash
- pre-commit run --all-files
- ./scripts/unittest.sh all
- ```
-5. **Submit PR** using our template
+If you are adapting QuantMind beyond finance, read
+`docs/ARCHITECTURE_FOR_NEW_DOMAINS.md` first and keep the extension aligned with
+the existing layering.
-### PR Checklist
-- [ ] Code in `quantmind/` following architecture patterns
-- [ ] Unit tests with comprehensive coverage
-- [ ] Usage example (for new features)
-- [ ] All pre-commit hooks pass
-- [ ] Conventional commit format
+## 🔄 Pull request expectations
-## 💡 Development Tips
+Before submitting a PR:
```bash
-# Run specific tests
-pytest tests/quantmind/sources/
-pytest tests/quantmind/models/
+pre-commit run --all-files
+bash scripts/verify.sh
+```
-# Test CLI functionality
-quantmind extract "test query" --max-papers 5
-quantmind config show
+Also make sure:
-# Check code quality
-./scripts/lint.sh
-```
+- commits use conventional-commit style (`feat:`, `fix:`, `docs:`, `refactor:`)
+- PR titles and descriptions are written in English
+- docs are updated when behavior or extension points change
+
+## 🤖 Agent-facing documentation
+
+QuantMind is increasingly consumed by AI agents as well as humans. Treat the
+following files as product surfaces:
+
+- `README.md`
+- `CONTRIBUTING.md`
+- `docs/ARCHITECTURE_FOR_NEW_DOMAINS.md`
+
+If you change architecture, memory semantics, or extension paths, update the
+relevant documentation in the same PR. During active iteration, these agent-
+facing docs should be reviewed regularly so agent workflows do not drift away
+from the real codebase.
-## ❓ Questions?
+## ❓Questions?
- Check existing [issues](https://github.com/LLMQuant/quant-mind/issues)
-- Review architecture patterns in existing code
-- Look at `examples/` for usage patterns
-- See `CLAUDE.md` for detailed architecture
+- Review `CLAUDE.md` for the detailed architecture context
+- Read `docs/ARCHITECTURE_FOR_NEW_DOMAINS.md` for extension guidance
-Thank you for contributing! 🚀
+Thank you for contributing.
diff --git a/README.md b/README.md
index fb6390f..b213a3d 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
- Transform Financial Knowledge into Actionable Intelligence
+ Turn Unstructured Documents into Durable, Agent-Ready Knowledge
@@ -30,7 +30,15 @@
---
-**QuantMind** is an intelligent knowledge extraction and retrieval framework for quantitative finance. It transforms unstructured financial content—papers, news, blogs, reports—into a queryable knowledge base, enabling AI-powered research at scale.
+**QuantMind** is a finance-proven, agent-centric knowledge extraction library
+built on top of the OpenAI Agents SDK. It gives AI agents better eyes over
+PDF/HTML/text inputs, typed knowledge instead of brittle strings, and a clean
+path toward durable memory and loop-based workflows.
+
+Today, the first production flow is focused on quantitative-finance research.
+But the architecture is intentionally broader: the same preprocess → typed
+config → typed knowledge → agentic workflow stack can be reused for any
+document-heavy domain.
### 📰 News
| 🗞️ News | 📝 Description |
@@ -40,26 +48,40 @@
### 🧐 Overview
-QuantMind is a next-generation AI platform that ingests, processes, and structures **every** new piece of quantitative-finance research, including papers, news, blogs, and SEC filings into a **semantic knowledge graph**. Institutional investors, hedge funds, and research teams can now explore the frontier of factor strategies, risk models, and market insights in **seconds**, unlocking alpha that would otherwise remain buried.
+QuantMind is designed for teams building **serious AI-agent workflows** around
+documents, research, and structured memory.
-### ✨ Why QuantMind?
+Its current strength is finance-heavy research extraction, but its core value is
+more general:
-The financial research landscape is overwhelming. Every day, hundreds of papers, articles, and reports are published.
+- fetch or accept raw source material from the web, local files, or inline text
+- normalize it into markdown that an agent can reliably read
+- force output into strict Pydantic knowledge objects
+- preserve provenance so downstream agents can review, cite, and reuse results
+- support repeatable loops instead of one-off prompt chains
-#### 🌐 The Opportunity
+### ✨ Why QuantMind?
-- **Information Overload**: 500 new research papers & reports published daily. Manual review takes weeks—costly, error-prone, and non-scalable
-- **Massive Market**: Financial data & analytics market ≫ expected to grow to US$961.89 billion by 2032, with a compound annual growth rate of 13.5%. Tens of thousands of quant teams & asset managers hungry for speed
-- **High ROI**: 1% improvement in research efficiency can translate to millions saved or earned in trading performance
+#### For AI agents
----
+- **Better eyes**: `preprocess/` turns PDFs, HTML, and raw text into stable
+ markdown before the model sees them.
+- **Better shared language**: `configs/` and `knowledge/` replace ad-hoc JSON
+ with typed inputs, typed outputs, and explicit provenance.
+- **Better loops**: `magic.py`, `flows/`, and `batch_run()` help agents resolve
+ intent, execute work, and repeat tasks predictably.
+- **Better long-term direction**: the `mind/` roadmap is explicitly about
+ durable memory and agent-friendly retrieval, not throwaway prompting.
-#### 💡 **QuantMind** solves this by
+#### For domain teams
-- 🔍 **Extracting** structured knowledge from any source (PDFs, web pages, APIs)
-- 🧠 **Understanding** content with domain-specific LLMs fine-tuned for finance
-- 💾 **Storing** information in a semantic knowledge graph
-- 🚀 **Retrieving** insights through natural language queries
+- **Finance-first, not finance-only**: the first production flow is optimized
+ for research papers in quant finance, but the layering is reusable for any
+ domain that needs document ingestion, typed extraction, and agentic reuse.
+- **Strict architecture**: dependency boundaries are enforced with
+ `import-linter`, and the repo ships with a single canonical verification loop.
+- **Composable by design**: customization happens at three levels—config,
+ flow kwargs, or forking a flow file.
---
@@ -67,37 +89,36 @@ The financial research landscape is overwhelming. Every day, hundreds of papers,

-QuantMind is built on a decoupled, two-stage architecture. This design separates the concerns of data ingestion from intelligent retrieval, ensuring both robustness and flexibility.
-
-#### **Stage 1: Knowledge Extraction**
+QuantMind is built on a decoupled architecture that separates source handling,
+typed extraction, and future memory/store layers.
-This layer is responsible for collecting, parsing, and structuring raw information into standardized knowledge units.
+#### **Current production path**
```text
-Source APIs (arXiv, News, Blogs) → Intelligent Parser → Workflow/Agent → Structured Knowledge Base
+Natural-language intent
+ ↓
+magic.resolve_magic_input(...)
+ ↓
+typed input + typed cfg
+ ↓
+preprocess.fetch + preprocess.format
+ ↓
+flow(agent=OpenAI Agents SDK)
+ ↓
+typed knowledge object with provenance
```
-- **Source**: Connects to various sources (academic APIs, news feeds, financial blogs, perplexity search source) to pull content
-- **Parser**: Extracts text, tables, and figures from PDFs, HTML, and other formats
-- **Tagger**: Automatically categorizes content into research areas and topics
-- **Workflow/Agent**: Orchestrates the extraction pipeline with quality control and deduplication
+#### **Permanent modules**
-#### **Stage 2: Intelligent Retrieval**
-
-This layer transforms structured knowledge into actionable insights through various retrieval mechanisms.
-
-```
-Knowledge Base → Embeddings → Solution Scenarios (DeepResearch, RAG, Data MCP, ...)
-```
+- `quantmind/flows/` — apex layer (`paper_flow`, `batch_run`, observability)
+- `quantmind/configs/` — typed inputs and flow configuration
+- `quantmind/knowledge/` — typed knowledge shapes and provenance contracts
+- `quantmind/preprocess/` — fetch + format + cleaning helpers
+- `quantmind/magic.py` — natural language to typed `(input, cfg)` resolution
+- `quantmind/mind/` — reserved for the upcoming memory/store layer
-- **Embedding Generation**: Converts knowledge units into high-dimensional vectors for semantic search
-
-- Solution Scenarios: Multiple retrieval patterns including:
-
- - **DeepResearch**: Complex multi-hop reasoning across documents
- - **RAG**: Retrieval-augmented generation for Q&A
- - **Data MCP**: Structured data access protocols
- - Custom retrieval patterns based on use case
+See [docs/ARCHITECTURE_FOR_NEW_DOMAINS.md](docs/ARCHITECTURE_FOR_NEW_DOMAINS.md)
+for how to extend this stack beyond finance.
---
@@ -107,7 +128,7 @@ We use [uv](https://github.com/astral-sh/uv) for fast and reliable Python packag
**Prerequisites:**
-- Python 3.8+
+- Python 3.10+
- Git
**Installation:**
@@ -154,7 +175,7 @@ We use [uv](https://github.com/astral-sh/uv) for fast and reliable Python packag
### 📚 Usage Examples
-#### Run a single paper through `paper_flow`
+#### Run a single finance paper through `paper_flow`
```python
import asyncio
@@ -172,6 +193,28 @@ async def main() -> None:
print(paper.model_dump_json(indent=2))
+asyncio.run(main())
+```
+
+#### Use the same pipeline on a local memo or technical brief
+
+```python
+import asyncio
+from pathlib import Path
+
+from quantmind.configs import PaperFlowCfg
+from quantmind.configs.paper import LocalFilePath
+from quantmind.flows import paper_flow
+
+
+async def main() -> None:
+ doc = await paper_flow(
+ LocalFilePath(path=Path("docs/internal-research-note.md")),
+ cfg=PaperFlowCfg(model="gpt-4o-mini"),
+ )
+ print(doc.root.summary)
+
+
asyncio.run(main())
```
@@ -224,32 +267,61 @@ async def main() -> None:
asyncio.run(main())
```
-> **Note**: QuantMind is mid-migration to OpenAI Agents SDK
-> (see [#71](https://github.com/LLMQuant/quant-mind/issues/71)). PR5 lands the
-> apex layer (`flows/` + `magic.py`); the remaining work is the `mind/`
-> memory + store layer scheduled for PR6 and PR7.
+### 🔁 Agentic loops and durable memory
+
+QuantMind is being tuned for workflows where multiple agents can understand one
+another through **shared typed artifacts**, not just prompt conventions.
+
+Today, that means:
+
+- resolving loose intent into strict inputs with `magic.resolve_magic_input()`
+- extracting typed knowledge with `paper_flow`
+- scaling stateless fan-out work with `batch_run()`
+- preserving provenance for review and downstream reuse
+
+Next, that means:
+
+- filesystem-backed working memory under `mind/memory`
+- a store layer for retrieval and longer-lived agent loops
+- stronger multi-step patterns for review, refinement, and replay
+
+If you are building agent teams, think of QuantMind as the layer that provides
+stable inputs, stable outputs, and a durable path from observation to memory.
---
### 🗺️ Roadmap
-- [x] Better `flow` design for user-friendly usage
-- [x] First production level example (Quant Paper Agent)
-- [ ] Migrate Agent layer to OpenAI Agents SDK
-- [ ] Standardize knowledge format with `knowledge/` (Pydantic-based)
-- [ ] Additional content sources (financial news, blogs, reports)
-- [ ] Cross-step working memory (`mind/memory`) for batch document processing
+- [x] Remove the legacy in-repo agent runtime
+- [x] Reposition QuantMind on top of OpenAI Agents SDK
+- [x] Land the permanent module roots: `flows/`, `configs/`, `knowledge/`,
+ `preprocess/`, `magic.py`
+- [x] Ship a canonical verification loop (`scripts/verify.sh`)
+- [ ] Add filesystem-backed working memory in `mind/memory`
+- [ ] Add the store/retrieval layer in `mind/store`
+- [ ] Expand beyond the first paper flow with more domain flows
+- [ ] Improve multi-agent loop patterns, observability, and replay support
+- [ ] Keep agent-facing docs and extension paths under regular review during
+ active weekly iteration
---
### The Vision: An Intelligent Research Framework
> [!IMPORTANT]
-> **This section describes our long-term vision, not current capabilities.** While QuantMind today provides a solid knowledge extraction framework, the features described below represent our aspirational goals for future development.
+> **This section describes our long-term vision, not current capabilities.**
+> QuantMind already ships a useful extraction stack, but the broader memory and
+> retrieval story is still under construction.
-QuantMind is designed with a larger vision: to become a comprehensive intelligence layer for all financial knowledge. We're building toward a system that understands the interconnections between academic research, market news, analyst reports, and social sentiment—creating a unified knowledge base that powers better financial decisions.
+QuantMind is being shaped into a durable intelligence layer for document-heavy
+workflows. Finance remains a major proving ground, but the long-term value is
+larger: helping AI agents see source material clearly, preserve structured
+understanding over time, and operate in loops that do not lose context between
+runs.
-The foundation we're building today—starting with papers—will expand to encompass the entire financial information ecosystem.
+The near-term roadmap starts with papers and research-heavy workflows. The
+longer-term goal is a reusable foundation for agentic knowledge work across
+domains.
> [!NOTE]
> **Future Conceptual Example (PR6 brings `FilesystemMemory`):**
@@ -265,7 +337,8 @@ The foundation we're building today—starting with papers—will expand to enco
> paper: Paper = await paper_flow(ArxivIdentifier(id=arxiv_id), memory=memory)
> ```
-This future state represents our commitment to moving beyond simple data aggregation and toward genuine machine intelligence in the financial domain.
+This future state represents the shift from one-off extraction to reusable,
+memory-aware, agentic knowledge systems.
------
@@ -279,11 +352,11 @@ We welcome contributions of all forms, from bug reports to feature development.
**Quick Start for Contributors:**
1. **Fork** the repository
-2. **Setup development environment**:
+2. **Setup the development environment**:
```bash
uv venv && source .venv/bin/activate
- uv pip install -e .
+ uv pip install -e ".[dev]"
./scripts/pre-commit-setup.sh
```
@@ -295,7 +368,7 @@ We welcome contributions of all forms, from bug reports to feature development.
- Open an [issue](https://github.com/LLMQuant/quant-mind/issues) to discuss significant changes
- Use our issue templates for bug reports and feature requests
-- Ensure all pre-commit hooks pass before submitting PR
+- Ensure `bash scripts/verify.sh` passes before submitting PR
### License
diff --git a/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md b/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
new file mode 100644
index 0000000..0f7fdf3
--- /dev/null
+++ b/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
@@ -0,0 +1,145 @@
+# Extending QuantMind Beyond Finance
+
+QuantMind currently ships a finance-first extraction flow, but the architecture
+is meant to support broader agentic knowledge work. This guide explains which
+parts are stable, which parts are domain-specific today, and how to extend the
+stack without fighting the current design.
+
+## What is stable today
+
+The permanent architecture is:
+
+```text
+quantmind/
+├── flows/ # apex orchestration layer
+├── knowledge/ # typed knowledge outputs
+├── preprocess/ # fetch + format + clean
+├── configs/ # typed flow inputs and cfg
+├── mind/ # memory/store roadmap
+├── magic.py # natural-language resolver
+└── utils/ # logger
+```
+
+These layers already work well for agent teams because they create a strict
+contract between source material, extraction, and downstream reuse.
+
+## What is finance-specific today
+
+The first production flow is `paper_flow`, and its output schema is
+`quantmind.knowledge.Paper`. That flow is optimized for research-paper style
+documents and keeps finance-specific fields such as `asset_classes`.
+
+That does **not** make the whole repository finance-only. It means the current
+reference implementation is finance-first while the underlying architecture is
+already reusable:
+
+- `preprocess/` is domain-agnostic
+- `magic.resolve_magic_input()` is flow-agnostic
+- `BaseFlowCfg` and `BaseInput` are reusable contracts
+- `BaseKnowledge` and the three knowledge shapes are reusable patterns
+- `batch_run()` is reusable for stateless fan-out work
+
+## How to add a new domain
+
+### 1. Pick the right knowledge shape
+
+Use:
+
+- `FlattenKnowledge` for atomic records, events, or summaries
+- `TreeKnowledge` for long documents with section/subsection structure
+- `GraphKnowledge` only when the relationship layer is actually ready to land
+
+Start from the retrieval shape you want downstream agents to use, not from the
+source format you happen to ingest first.
+
+### 2. Define typed inputs and configuration
+
+Add a new config module under `quantmind/configs/`:
+
+- define one or more `BaseInput` subclasses
+- create a discriminated union for the flow input
+- extend `BaseFlowCfg` with only domain-relevant knobs
+
+The goal is for agents and humans to share the same explicit input contract.
+
+### 3. Implement a pure flow function
+
+Add a new `async def ..._flow(...)` under `quantmind/flows/` that:
+
+- accepts one typed input plus `cfg=...`
+- fetches or normalizes source content through `preprocess/`
+- runs an Agents SDK `Agent(output_type=...)`
+- returns a typed knowledge object
+
+Do not introduce a custom runtime, plugin registry, or class-based flow
+hierarchy.
+
+### 4. Make the flow usable from natural language
+
+If the flow follows the same `(input, *, cfg, ...)` signature convention,
+`magic.resolve_magic_input()` can already resolve free-form intent into typed
+input and config objects.
+
+This is the bridge that helps external agent systems work with QuantMind
+without writing brittle prompt parsers.
+
+## Recommended agentic loop patterns
+
+### Stateless scout loop
+
+Use when you want breadth first:
+
+1. resolve or build many typed inputs
+2. run the flow in parallel with `batch_run()`
+3. collect typed outputs
+4. hand those outputs to a review or ranking agent
+
+This is the best fit for discovery, triage, and broad corpus scanning.
+
+### Serial memory loop
+
+Use when each step depends on previous results:
+
+1. read one document
+2. extract a typed knowledge object
+3. update memory or a local run archive
+4. feed the next item with that accumulated context
+
+Today, this pattern should be implemented as an explicit serial loop. `batch_run`
+intentionally rejects `memory=` because shared-memory fan-out is not yet the
+MVP design.
+
+### Multi-agent handoff loop
+
+A simple, durable pattern is:
+
+1. **Scout agent** resolves intent and gathers sources
+2. **Extractor agent** creates typed knowledge objects
+3. **Reviewer agent** validates structure, provenance, and confidence
+4. **Planner agent** decides what to fetch or revisit next
+
+The key rule is to pass typed artifacts between agents whenever possible. Avoid
+hidden agreements in prompt text when a Pydantic object can carry the contract.
+
+## Memory and durability roadmap
+
+The repository is explicitly moving toward a memory-aware architecture:
+
+- `mind/memory/` is the planned working-memory layer
+- `mind/store/` is the planned retrieval/store layer
+- `flows/_runner.py` already reserves the runtime seam for memory integration
+
+Until those land, treat QuantMind as a strong typed-extraction layer with a
+clear upgrade path toward durable agent loops.
+
+## Documentation discipline
+
+QuantMind is increasingly meant to be read by both humans and AI agents.
+Because of that:
+
+- keep `README.md`, `CONTRIBUTING.md`, and this file aligned with code changes
+- document new domain assumptions in the same PR that introduces them
+- review agent-facing docs regularly during active iteration
+
+This discipline matters because stale documentation causes broken agent loops
+just as quickly as stale code.
diff --git a/quantmind/flows/__init__.py b/quantmind/flows/__init__.py
index 78dd9bf..bc0305b 100644
--- a/quantmind/flows/__init__.py
+++ b/quantmind/flows/__init__.py
@@ -1,7 +1,10 @@
"""Apex layer — composes configs / knowledge / preprocess on the SDK.
-Each flow function (``paper_flow``, future ``news_flow`` / ``earnings_flow``)
-takes a typed input and a ``FlowCfg`` and returns a knowledge item.
+Each flow function takes a typed input and a ``FlowCfg`` and returns a
+knowledge item. The current production flow is finance-first (``paper_flow``),
+but the apex-layer contract itself is reusable for future domain flows as long
+as they follow the same typed ``(input, *, cfg, ...)`` pattern.
+
Cross-flow utilities live alongside:
- ``batch_run`` runs any flow over a list of inputs with bounded
From d3ea090e5cc73e7e63d4f829dc3c6079d9e90a62 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 30 Jun 2026 06:02:32 +0000
Subject: [PATCH 3/6] chore: ignore local virtualenv artifacts
---
.gitignore | 1 +
.venv/lib64 | 1 -
.venv/pyvenv.cfg | 5 -
.../share/jupyter/kernels/python3/kernel.json | 16 --
.../jupyter/kernels/python3/logo-32x32.png | Bin 1084 -> 0 bytes
.../jupyter/kernels/python3/logo-64x64.png | Bin 2180 -> 0 bytes
.../jupyter/kernels/python3/logo-svg.svg | 265 ------------------
.venv/share/man/man1/ipython.1 | 60 ----
8 files changed, 1 insertion(+), 347 deletions(-)
delete mode 120000 .venv/lib64
delete mode 100644 .venv/pyvenv.cfg
delete mode 100644 .venv/share/jupyter/kernels/python3/kernel.json
delete mode 100644 .venv/share/jupyter/kernels/python3/logo-32x32.png
delete mode 100644 .venv/share/jupyter/kernels/python3/logo-64x64.png
delete mode 100644 .venv/share/jupyter/kernels/python3/logo-svg.svg
delete mode 100644 .venv/share/man/man1/ipython.1
diff --git a/.gitignore b/.gitignore
index 539aa6c..ac90035 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ CMakeLists_modified.txt
build/
*.egg-info
+.venv/
lib/
bin/
diff --git a/.venv/lib64 b/.venv/lib64
deleted file mode 120000
index 7951405..0000000
--- a/.venv/lib64
+++ /dev/null
@@ -1 +0,0 @@
-lib
\ No newline at end of file
diff --git a/.venv/pyvenv.cfg b/.venv/pyvenv.cfg
deleted file mode 100644
index fdcdbd1..0000000
--- a/.venv/pyvenv.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-home = /usr/bin
-include-system-site-packages = false
-version = 3.12.3
-executable = /usr/bin/python3.12
-command = /usr/bin/python3 -m venv /home/runner/work/quant-mind/quant-mind/.venv
diff --git a/.venv/share/jupyter/kernels/python3/kernel.json b/.venv/share/jupyter/kernels/python3/kernel.json
deleted file mode 100644
index 788843f..0000000
--- a/.venv/share/jupyter/kernels/python3/kernel.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "argv": [
- "python",
- "-m",
- "ipykernel_launcher",
- "-f",
- "{connection_file}"
- ],
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "metadata": {
- "debugger": true,
- "supported_encryption": "curve"
- },
- "kernel_protocol_version": "5.5"
-}
\ No newline at end of file
diff --git a/.venv/share/jupyter/kernels/python3/logo-32x32.png b/.venv/share/jupyter/kernels/python3/logo-32x32.png
deleted file mode 100644
index be81330765764699553aa4fbaf0e9fc27c20c6d2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1084
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
-
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.venv/share/man/man1/ipython.1 b/.venv/share/man/man1/ipython.1
deleted file mode 100644
index 0f4a191..0000000
--- a/.venv/share/man/man1/ipython.1
+++ /dev/null
@@ -1,60 +0,0 @@
-.\" Hey, EMACS: -*- nroff -*-
-.\" First parameter, NAME, should be all caps
-.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
-.\" other parameters are allowed: see man(7), man(1)
-.TH IPYTHON 1 "July 15, 2011"
-.\" Please adjust this date whenever revising the manpage.
-.\"
-.\" Some roff macros, for reference:
-.\" .nh disable hyphenation
-.\" .hy enable hyphenation
-.\" .ad l left justify
-.\" .ad b justify to both left and right margins
-.\" .nf disable filling
-.\" .fi enable filling
-.\" .br insert line break
-.\" .sp insert n+1 empty lines
-.\" for manpage-specific macros, see man(7) and groff_man(7)
-.\" .SH section heading
-.\" .SS secondary section heading
-.\"
-.\"
-.\" To preview this page as plain text: nroff -man ipython.1
-.\"
-.SH NAME
-ipython \- Tools for Interactive Computing in Python.
-.SH SYNOPSIS
-.B ipython
-.RI [ options ] " files" ...
-
-.B ipython subcommand
-.RI [ options ] ...
-
-.SH DESCRIPTION
-An interactive Python shell with automatic history (input and output), dynamic
-object introspection, easier configuration, command completion, access to the
-system shell, integration with numerical and scientific computing tools,
-web notebook, Qt console, and more.
-
-For more information on how to use IPython, see 'ipython \-\-help',
-or 'ipython \-\-help\-all' for all available command\(hyline options.
-
-.SH "ENVIRONMENT VARIABLES"
-.sp
-.PP
-\fIIPYTHONDIR\fR
-.RS 4
-This is the location where IPython stores all its configuration files. The default
-is $HOME/.ipython if IPYTHONDIR is not defined.
-
-You can see the computed value of IPYTHONDIR with `ipython locate`.
-
-.SH FILES
-
-IPython uses various configuration files stored in profiles within IPYTHONDIR.
-To generate the default configuration files and start configuring IPython,
-do 'ipython profile create', and edit '*_config.py' files located in
-IPYTHONDIR/profile_default.
-
-.SH AUTHORS
-IPython is written by the IPython Development Team .
From da52908a6a913d5685c54f5892e5b93c774209bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=A7=81=E0=BC=BA=20=F0=9D=93=A9=F0=9D=93=AE=F0=9D=93=B4?=
=?UTF-8?q?=F0=9D=93=B2=20=F0=9D=93=9E=F0=9D=93=B0=F0=9D=93=BE=F0=9D=94=83?=
=?UTF-8?q?=20=E0=BC=BB=EA=A7=82?=
Date: Thu, 2 Jul 2026 09:52:10 +0200
Subject: [PATCH 4/6] docs(memory): add verified shortlist correction draft
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---
draft_corrections.md | 58 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 draft_corrections.md
diff --git a/draft_corrections.md b/draft_corrections.md
new file mode 100644
index 0000000..d4ca917
--- /dev/null
+++ b/draft_corrections.md
@@ -0,0 +1,58 @@
+# Draft Corrections: Verified Memory Repo Shortlist
+
+## 1) Dogrulama olcutu
+
+Bu calisma "en iyi" iddiasi degil, "dogrulanmis adaylar" uretir. Bir repo shortlist'e girmek icin asagidaki kosullari birlikte saglamalidir:
+
+1. Son 90 gunde aktif commit hareketi var.
+2. Gercek bir memory architecture sunuyor (tier, retrieval, persistence, governance gibi somut katmanlar).
+3. Agentic AI veya autonomous agent akislarina dogrudan temas ediyor.
+4. Acik dokumantasyon ve calisan ornek kullanim veriyor.
+5. Yildiz/fork/adoption sinyali ile topluluk ilgisi goruluyor.
+
+Degerlendirme notu:
+- Stars tek basina karar kriteri degildir.
+- Mimari netlik + bakim aktivitesi + entegrasyon kaniti birincil agirliktir.
+
+## 2) Haric tutma kurallari
+
+Asagidaki repo tipleri shortlist disinda tutulur:
+
+1. Sadece repo adi veya README icinde "memory" geciyor, fakat gercek memory altyapisi yok.
+2. Genel framework sunuyor, ancak memory disiplini (state lifecycle, retrieval policy, persistence strategy) icermiyor.
+3. Eski, pasif, bakim disi veya son donemde anlamli gelistirme gostermeyen projeler.
+4. Mimariyi dogrulayan kanitlar eksik (zayif docs, belirsiz API, calismayan ornekler).
+
+## 3) QuantMind icin cikarim
+
+### Copilot Memory benzeri patternler
+
+- Citation-backed facts
+- Validation before use
+- Stale item cleanup
+- Repo-scoped memory
+
+### QuantMind'e dogrudan uyarlama
+
+- `RawFallbackNode`: Yapilandirilamayan veya dusuk guvenli ciktilari kaybetmeden ham iz olarak saklama.
+- `QualityGate`: Durable katmana commit oncesi sema, kaynak ve tutarlilik denetimi.
+- `Hybrid memory tiers`: Working, episodic ve durable katmanlari amaca gore ayirma.
+- `Graph only for distilled facts`: Graph katmanina yalnizca rafine edilmis, kaynaklanmis ve tekrar dogrulanmis bilgi yazma.
+
+### L3 commit icin zorunlu kosullar
+
+`confidence >= 0.85` tek basina yeterli degildir. Asagidaki kosullar birlikte zorunludur:
+
+1. Provenance mevcut ve dogrulanabilir.
+2. Schema validity basarili.
+3. Dedup ve contradiction kontrolu basarili.
+
+## Sonraki adim (uygulama hazirligi)
+
+Bu taslak sonraki iterasyonda su ciktiya donusturulmelidir:
+
+- Dogrulanmis 21 repo shortlist
+- Her repo icin memory yaklasimi siniflandirmasi
+- QuantMind uyarlanabilirlik puani
+- Risk/avantaj analizi
+- Kopyalanacak mimari pattern oneri matrisi
From 58912cc453abd3dcd06fd66bb78ee89e925635ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=A7=81=E0=BC=BA=20=F0=9D=93=A9=F0=9D=93=AE=F0=9D=93=B4?=
=?UTF-8?q?=F0=9D=93=B2=20=F0=9D=93=9E=F0=9D=93=B0=F0=9D=93=BE=F0=9D=94=83?=
=?UTF-8?q?=20=E0=BC=BB=EA=A7=82?=
Date: Wed, 8 Jul 2026 00:57:29 +0200
Subject: [PATCH 5/6] feat(flows): add recoverable validation, governance, and
hybrid memory
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---
.impeccable/hook.cache.json | 1 +
README.md | 27 ++-
docs/ARCHITECTURE_FOR_NEW_DOMAINS.md | 16 +-
examples/mind/recoverable_memory_demo.py | 54 ++++++
quantmind/__init__.py | 6 +-
quantmind/configs/__init__.py | 3 +
quantmind/configs/recoverable.py | 42 +++++
quantmind/flows/__init__.py | 20 ++-
quantmind/flows/governance.py | 210 +++++++++++++++++++++++
quantmind/flows/governance.yaml | 43 +++++
quantmind/magic.py | 49 ++++++
quantmind/mind/__init__.py | 9 +
quantmind/mind/memory.py | 95 ++++++++++
tests/configs/test_recoverable.py | 39 +++++
tests/flows/test_governance.py | 135 +++++++++++++++
tests/mind/__init__.py | 1 +
tests/mind/test_memory.py | 84 +++++++++
tests/test_magic.py | 33 ++++
18 files changed, 847 insertions(+), 20 deletions(-)
create mode 100644 .impeccable/hook.cache.json
create mode 100644 examples/mind/recoverable_memory_demo.py
create mode 100644 quantmind/configs/recoverable.py
create mode 100644 quantmind/flows/governance.py
create mode 100644 quantmind/flows/governance.yaml
create mode 100644 quantmind/mind/__init__.py
create mode 100644 quantmind/mind/memory.py
create mode 100644 tests/configs/test_recoverable.py
create mode 100644 tests/flows/test_governance.py
create mode 100644 tests/mind/__init__.py
create mode 100644 tests/mind/test_memory.py
diff --git a/.impeccable/hook.cache.json b/.impeccable/hook.cache.json
new file mode 100644
index 0000000..ebc7c5d
--- /dev/null
+++ b/.impeccable/hook.cache.json
@@ -0,0 +1 @@
+{"version":1,"sessions":{}}
\ No newline at end of file
diff --git a/README.md b/README.md
index b213a3d..6d82a38 100644
--- a/README.md
+++ b/README.md
@@ -30,12 +30,12 @@
---
-**QuantMind** is a finance-proven, agent-centric knowledge extraction library
+**QuantMind** is an agent-centric knowledge extraction library
built on top of the OpenAI Agents SDK. It gives AI agents better eyes over
PDF/HTML/text inputs, typed knowledge instead of brittle strings, and a clean
path toward durable memory and loop-based workflows.
-Today, the first production flow is focused on quantitative-finance research.
+Today, the first production flow is focused on research-paper extraction.
But the architecture is intentionally broader: the same preprocess → typed
config → typed knowledge → agentic workflow stack can be reused for any
document-heavy domain.
@@ -51,8 +51,8 @@ document-heavy domain.
QuantMind is designed for teams building **serious AI-agent workflows** around
documents, research, and structured memory.
-Its current strength is finance-heavy research extraction, but its core value is
-more general:
+Its current strength is typed research extraction, but its core value is
+domain-agnostic:
- fetch or accept raw source material from the web, local files, or inline text
- normalize it into markdown that an agent can reliably read
@@ -73,11 +73,11 @@ more general:
- **Better long-term direction**: the `mind/` roadmap is explicitly about
durable memory and agent-friendly retrieval, not throwaway prompting.
-#### For domain teams
+#### For engineering teams
-- **Finance-first, not finance-only**: the first production flow is optimized
- for research papers in quant finance, but the layering is reusable for any
- domain that needs document ingestion, typed extraction, and agentic reuse.
+- **Domain-ready by design**: the first production flow is paper-oriented, but
+ the layering is reusable for any domain that needs document ingestion, typed
+ extraction, and agentic reuse.
- **Strict architecture**: dependency boundaries are enforced with
`import-linter`, and the repo ships with a single canonical verification loop.
- **Composable by design**: customization happens at three levels—config,
@@ -115,11 +115,20 @@ typed knowledge object with provenance
- `quantmind/knowledge/` — typed knowledge shapes and provenance contracts
- `quantmind/preprocess/` — fetch + format + cleaning helpers
- `quantmind/magic.py` — natural language to typed `(input, cfg)` resolution
-- `quantmind/mind/` — reserved for the upcoming memory/store layer
+- `quantmind/mind/` — hybrid memory primitives (L1/L2/L3)
+- `quantmind/flows/governance.*` — policy loader + runtime gates
See [docs/ARCHITECTURE_FOR_NEW_DOMAINS.md](docs/ARCHITECTURE_FOR_NEW_DOMAINS.md)
for how to extend this stack beyond finance.
+#### Governance and loop observability
+
+QuantMind now treats governance as executable policy instead of passive config.
+Tool allowlists, loop budgets, fallback behavior, and L3 commit gates are
+declared in `quantmind/flows/governance.yaml` and enforced at runtime.
+This maps directly to loop SLI/SLO operations: every loop is expected to be
+traceable, budgeted, and quality-gated before durable writes.
+
---
### 🚀 Quick Start
diff --git a/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md b/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
index 0f7fdf3..ba4f353 100644
--- a/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
+++ b/docs/ARCHITECTURE_FOR_NEW_DOMAINS.md
@@ -1,6 +1,6 @@
-# Extending QuantMind Beyond Finance
+# Extending QuantMind Beyond a Single Domain
-QuantMind currently ships a finance-first extraction flow, but the architecture
+QuantMind currently ships a paper-oriented extraction flow, but the architecture
is meant to support broader agentic knowledge work. This guide explains which
parts are stable, which parts are domain-specific today, and how to extend the
stack without fighting the current design.
@@ -23,14 +23,14 @@ quantmind/
These layers already work well for agent teams because they create a strict
contract between source material, extraction, and downstream reuse.
-## What is finance-specific today
+## What is domain-specific today
The first production flow is `paper_flow`, and its output schema is
`quantmind.knowledge.Paper`. That flow is optimized for research-paper style
-documents and keeps finance-specific fields such as `asset_classes`.
+documents and still includes some finance-origin fields such as `asset_classes`.
That does **not** make the whole repository finance-only. It means the current
-reference implementation is finance-first while the underlying architecture is
+reference implementation is paper-first while the underlying architecture is
already reusable:
- `preprocess/` is domain-agnostic
@@ -125,9 +125,11 @@ hidden agreements in prompt text when a Pydantic object can carry the contract.
The repository is explicitly moving toward a memory-aware architecture:
-- `mind/memory/` is the planned working-memory layer
-- `mind/store/` is the planned retrieval/store layer
+- `mind/memory.py` provides the current L1/L2/L3 memory primitives
+- `mind/store/` remains the planned retrieval/store layer
- `flows/_runner.py` already reserves the runtime seam for memory integration
+- `flows/governance.py` enforces policy for loop budgets, tool allowlists,
+ fallback behavior, and L3 commit gates
Until those land, treat QuantMind as a strong typed-extraction layer with a
clear upgrade path toward durable agent loops.
diff --git a/examples/mind/recoverable_memory_demo.py b/examples/mind/recoverable_memory_demo.py
new file mode 100644
index 0000000..55ba939
--- /dev/null
+++ b/examples/mind/recoverable_memory_demo.py
@@ -0,0 +1,54 @@
+"""Minimal demo for recoverable ingestion and gated memory commit."""
+
+from tempfile import TemporaryDirectory
+
+from pydantic import BaseModel, ConfigDict
+
+from quantmind.configs.recoverable import RecoverableValidation
+from quantmind.flows.governance import load_governance_policy
+from quantmind.mind.memory import HybridMemoryEngine
+
+
+class DemoArtifact(BaseModel):
+ """Strict schema that rejects unexpected fields."""
+
+ model_config = ConfigDict(extra="forbid")
+ id: str
+ title: str
+ validation_confidence: float
+ provenance: dict[str, str]
+
+
+def main() -> None:
+ validator = RecoverableValidation(DemoArtifact)
+ policy = load_governance_policy()
+ payload = {
+ "id": "artifact-1",
+ "title": "Recovered architecture summary",
+ "validation_confidence": 0.91,
+ "provenance": {"source": "demo"},
+ "unexpected_field": "will trigger fallback",
+ }
+
+ with TemporaryDirectory() as tmpdir:
+ memory = HybridMemoryEngine(base_path=tmpdir)
+ parsed = validator.execute_safely(payload)
+ memory.write_l1_trace("Parser", parsed.model_dump(mode="json"))
+
+ if isinstance(parsed, DemoArtifact):
+ committed = memory.commit_to_l3_graph(
+ parsed.model_dump(mode="json"),
+ schema_valid=True,
+ dedup_ok=True,
+ contradiction_free=True,
+ )
+ print("Committed to L3:", committed)
+ else:
+ fallback_node = parsed
+ fallback = policy.global_settings.fallback_policy
+ print("Fallback policy:", fallback)
+ print("Quarantined schema:", fallback_node.target_schema)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/quantmind/__init__.py b/quantmind/__init__.py
index 91edcda..02dcb2c 100644
--- a/quantmind/__init__.py
+++ b/quantmind/__init__.py
@@ -1,7 +1,7 @@
-"""QuantMind: Intelligent Knowledge Extraction and Retrieval Framework.
+"""QuantMind: Intelligent knowledge extraction for agentic workflows.
-QuantMind transforms unstructured financial content into a queryable knowledge graph
-through a two-stage architecture focused on knowledge extraction and intelligent retrieval.
+QuantMind transforms unstructured documents into typed, provenance-aware
+artifacts that can be validated, governed, and reused in durable loops.
"""
__version__ = "0.0.1"
diff --git a/quantmind/configs/__init__.py b/quantmind/configs/__init__.py
index 271be37..eb88e6d 100644
--- a/quantmind/configs/__init__.py
+++ b/quantmind/configs/__init__.py
@@ -12,6 +12,7 @@
from quantmind.configs.earnings import EarningsFlowCfg, EarningsInput
from quantmind.configs.news import NewsFlowCfg, NewsInput
from quantmind.configs.paper import PaperFlowCfg, PaperInput
+from quantmind.configs.recoverable import RawFallbackNode, RecoverableValidation
__all__ = [
"BaseFlowCfg",
@@ -22,4 +23,6 @@
"NewsInput",
"PaperFlowCfg",
"PaperInput",
+ "RawFallbackNode",
+ "RecoverableValidation",
]
diff --git a/quantmind/configs/recoverable.py b/quantmind/configs/recoverable.py
new file mode 100644
index 0000000..dd4909b
--- /dev/null
+++ b/quantmind/configs/recoverable.py
@@ -0,0 +1,42 @@
+"""Recoverable parsing helpers for tolerant ingestion boundaries.
+
+This module keeps strict schema contracts in place (`extra="forbid"` on
+the target model) while preventing pipeline paralysis during malformed
+inputs. Callers receive either a validated model or a structured
+quarantine node that preserves raw context.
+"""
+
+from typing import Any, Generic, TypeVar
+
+from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+T = TypeVar("T", bound=BaseModel)
+
+
+class RawFallbackNode(BaseModel):
+ """Quarantine payload used when schema validation fails."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ raw_payload: dict[str, Any] = Field(default_factory=dict)
+ target_schema: str
+ error_message: str
+ context_loss_prevented: bool = True
+
+
+class RecoverableValidation(Generic[T]):
+ """Validate against a target model and quarantine failures."""
+
+ def __init__(self, target_model: type[T]) -> None:
+ self.target_model = target_model
+
+ def execute_safely(self, data: dict[str, Any]) -> T | RawFallbackNode:
+ """Return validated model or a fallback node with original payload."""
+ try:
+ return self.target_model.model_validate(data)
+ except ValidationError as error:
+ return RawFallbackNode(
+ raw_payload=data,
+ target_schema=self.target_model.__name__,
+ error_message=str(error),
+ )
diff --git a/quantmind/flows/__init__.py b/quantmind/flows/__init__.py
index bc0305b..839f9c9 100644
--- a/quantmind/flows/__init__.py
+++ b/quantmind/flows/__init__.py
@@ -1,7 +1,7 @@
"""Apex layer — composes configs / knowledge / preprocess on the SDK.
Each flow function takes a typed input and a ``FlowCfg`` and returns a
-knowledge item. The current production flow is finance-first (``paper_flow``),
+knowledge item. The current production flow is paper-oriented (``paper_flow``),
but the apex-layer contract itself is reusable for future domain flows as long
as they follow the same typed ``(input, *, cfg, ...)`` pattern.
@@ -15,11 +15,29 @@
"""
from quantmind.flows.batch import BatchResult, batch_run
+from quantmind.flows.governance import (
+ GovernancePolicy,
+ GovernancePolicyError,
+ LoopBudgetManager,
+ enforce_l3_commit_gates,
+ ensure_tool_allowed,
+ load_governance_policy,
+ loop_budget_manager,
+ run_fallback_policy,
+)
from quantmind.flows.paper import UnsupportedContentTypeError, paper_flow
__all__ = [
"BatchResult",
+ "GovernancePolicy",
+ "GovernancePolicyError",
+ "LoopBudgetManager",
"UnsupportedContentTypeError",
"batch_run",
+ "enforce_l3_commit_gates",
+ "ensure_tool_allowed",
+ "load_governance_policy",
+ "loop_budget_manager",
"paper_flow",
+ "run_fallback_policy",
]
diff --git a/quantmind/flows/governance.py b/quantmind/flows/governance.py
new file mode 100644
index 0000000..ab496f6
--- /dev/null
+++ b/quantmind/flows/governance.py
@@ -0,0 +1,210 @@
+"""Governance policy loader and runtime enforcement helpers."""
+
+from pathlib import Path
+from typing import Any, Literal
+
+import yaml
+from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+from quantmind.configs import RawFallbackNode
+
+
+class GovernancePolicyError(ValueError):
+ """Raised when governance policy content cannot be loaded or validated."""
+
+
+class AgentGovernanceRule(BaseModel):
+ """Tool + tier constraints for one role in a scenario."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ role: str
+ allowed_tools: list[str] = Field(default_factory=list)
+ output_tier: Literal["L1", "L2", "L3"]
+ required_schema: str | None = None
+ confidence_threshold: float | None = None
+ fallback_node: str | None = None
+
+
+class ScenarioGovernanceRule(BaseModel):
+ """Scenario-level set of role policies."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ description: str
+ agents: list[AgentGovernanceRule] = Field(default_factory=list)
+
+
+class GlobalGovernanceSettings(BaseModel):
+ """Global runtime limits and failure behavior."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ loop_budget_max: int = 5
+ fallback_policy: Literal["quarantine_and_continue", "fail_fast"] = (
+ "quarantine_and_continue"
+ )
+
+
+class L3CommitPolicy(BaseModel):
+ """Hard gates required before committing to durable memory."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ min_confidence: float = 0.85
+ require_provenance: bool = True
+ require_schema_validity: bool = True
+ require_dedup_check: bool = True
+ require_contradiction_check: bool = True
+
+
+class GovernancePolicy(BaseModel):
+ """Full governance policy document."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ version: str
+ global_settings: GlobalGovernanceSettings = Field(
+ default_factory=GlobalGovernanceSettings
+ )
+ l3_commit: L3CommitPolicy = Field(default_factory=L3CommitPolicy)
+ scenarios: dict[str, ScenarioGovernanceRule] = Field(default_factory=dict)
+
+ def role_rule(
+ self, scenario_name: str, role: str
+ ) -> AgentGovernanceRule:
+ """Resolve the role rule for one scenario."""
+ try:
+ scenario = self.scenarios[scenario_name]
+ except KeyError as error:
+ raise KeyError(
+ f"Unknown governance scenario: {scenario_name!r}"
+ ) from error
+ for rule in scenario.agents:
+ if rule.role == role:
+ return rule
+ raise KeyError(
+ f"Role {role!r} is not defined in scenario {scenario_name!r}"
+ )
+
+
+def load_governance_policy(path: str | Path | None = None) -> GovernancePolicy:
+ """Load and validate governance policy YAML."""
+ policy_path = Path(path) if path is not None else _default_policy_path()
+ try:
+ loaded = yaml.safe_load(policy_path.read_text(encoding="utf-8"))
+ except OSError as error:
+ raise GovernancePolicyError(
+ f"Unable to read governance policy: {policy_path}"
+ ) from error
+ except yaml.YAMLError as error:
+ raise GovernancePolicyError(
+ f"Invalid YAML in governance policy: {policy_path}"
+ ) from error
+
+ if loaded is None:
+ raise GovernancePolicyError(
+ f"Governance policy is empty: {policy_path}"
+ )
+ try:
+ return GovernancePolicy.model_validate(loaded)
+ except ValidationError as error:
+ raise GovernancePolicyError(
+ f"Governance policy schema validation failed: {policy_path}"
+ ) from error
+
+
+def ensure_tool_allowed(
+ policy: GovernancePolicy,
+ *,
+ scenario_name: str,
+ role: str,
+ tool_name: str,
+) -> None:
+ """Raise when a role tries to use a tool outside its allowlist."""
+ rule = policy.role_rule(scenario_name, role)
+ if tool_name not in rule.allowed_tools:
+ raise PermissionError(
+ f"Tool {tool_name!r} is not allowed for role {role!r} "
+ f"in scenario {scenario_name!r}"
+ )
+
+
+class LoopBudgetManager:
+ """Track and enforce max loop budget from governance policy."""
+
+ def __init__(self, max_loops: int) -> None:
+ if max_loops <= 0:
+ raise ValueError("max_loops must be positive")
+ self.max_loops = max_loops
+ self._consumed = 0
+
+ @property
+ def remaining(self) -> int:
+ """Return remaining budget steps."""
+ return self.max_loops - self._consumed
+
+ def consume(self, *, steps: int = 1) -> int:
+ """Consume loop budget and return the remaining amount."""
+ if steps <= 0:
+ raise ValueError("steps must be positive")
+ if self._consumed + steps > self.max_loops:
+ raise RuntimeError(
+ f"Loop budget exceeded: requested {steps}, "
+ f"remaining {self.remaining}"
+ )
+ self._consumed += steps
+ return self.remaining
+
+
+def loop_budget_manager(policy: GovernancePolicy) -> LoopBudgetManager:
+ """Create a budget manager from policy defaults."""
+ return LoopBudgetManager(policy.global_settings.loop_budget_max)
+
+
+def run_fallback_policy(
+ policy: GovernancePolicy,
+ *,
+ target_schema: str,
+ payload: dict[str, Any],
+ error_message: str,
+) -> RawFallbackNode:
+ """Run fallback behavior configured by governance policy."""
+ fallback = policy.global_settings.fallback_policy
+ if fallback == "quarantine_and_continue":
+ return RawFallbackNode(
+ raw_payload=payload,
+ target_schema=target_schema,
+ error_message=error_message,
+ )
+ raise RuntimeError(
+ f"Fallback policy {fallback!r} blocks continuation"
+ )
+
+
+def enforce_l3_commit_gates(
+ policy: GovernancePolicy,
+ *,
+ artifact: dict[str, Any],
+ schema_valid: bool,
+ dedup_ok: bool,
+ contradiction_free: bool,
+) -> bool:
+ """Return true only when all configured L3 commit gates pass."""
+ rules = policy.l3_commit
+ confidence = float(artifact.get("validation_confidence", 0.0))
+ if confidence < rules.min_confidence:
+ return False
+ if rules.require_provenance and not artifact.get("provenance"):
+ return False
+ if rules.require_schema_validity and not schema_valid:
+ return False
+ if rules.require_dedup_check and not dedup_ok:
+ return False
+ if rules.require_contradiction_check and not contradiction_free:
+ return False
+ return True
+
+
+def _default_policy_path() -> Path:
+ return Path(__file__).with_name("governance.yaml")
diff --git a/quantmind/flows/governance.yaml b/quantmind/flows/governance.yaml
new file mode 100644
index 0000000..f816c53
--- /dev/null
+++ b/quantmind/flows/governance.yaml
@@ -0,0 +1,43 @@
+version: "2.0.0"
+
+global_settings:
+ loop_budget_max: 5
+ fallback_policy: "quarantine_and_continue"
+
+l3_commit:
+ min_confidence: 0.85
+ require_provenance: true
+ require_schema_validity: true
+ require_dedup_check: true
+ require_contradiction_check: true
+
+scenarios:
+ architecture_analysis:
+ description: "Technical document analysis and refactor suggestion"
+ agents:
+ - role: "Scout"
+ allowed_tools: ["scan_repo", "read_file"]
+ output_tier: "L1"
+ - role: "Extractor"
+ allowed_tools: ["parse_ast"]
+ required_schema: "ArchitectureSummary"
+ output_tier: "L2"
+ - role: "Reviewer"
+ allowed_tools: ["diff_analyze"]
+ confidence_threshold: 0.8
+ output_tier: "L3"
+
+ tolerant_ingestion:
+ description: "Ingesting malformed multi-source datasets safely"
+ agents:
+ - role: "Scout"
+ allowed_tools: ["fetch_source"]
+ output_tier: "L1"
+ - role: "Parser"
+ allowed_tools: ["recoverable_validate"]
+ fallback_node: "RawFallbackNode"
+ output_tier: "L2"
+ - role: "GraphWriter"
+ allowed_tools: ["graph_write"]
+ confidence_threshold: 0.9
+ output_tier: "L3"
diff --git a/quantmind/magic.py b/quantmind/magic.py
index 71e522a..5c44b96 100644
--- a/quantmind/magic.py
+++ b/quantmind/magic.py
@@ -23,6 +23,7 @@
from pydantic import BaseModel
from quantmind.configs.base import BaseFlowCfg
+from quantmind.flows.governance import GovernancePolicy, ensure_tool_allowed
InputT = TypeVar("InputT", bound=BaseModel)
CfgT = TypeVar("CfgT", bound=BaseModel)
@@ -65,6 +66,10 @@ async def resolve_magic_input(
target_flow: Callable[..., Awaitable[Any]],
resolver_model: str = "gpt-4o-mini",
resolver_instructions: str | None = None,
+ governance_policy: GovernancePolicy | None = None,
+ governance_scenario: str | None = None,
+ requested_tools: list[str] | None = None,
+ resolver_role: str = "Resolver",
) -> tuple[Any, Any]:
"""Parse ``natural_language`` into ``(input_obj, cfg_obj)`` for ``target_flow``.
@@ -76,11 +81,24 @@ async def resolve_magic_input(
resolver_instructions: Optional override for the resolver's
system prompt template. Receives ``flow_name``,
``input_schema``, and ``cfg_schema`` via ``str.format``.
+ governance_policy: Optional governance policy to enforce before
+ model execution.
+ governance_scenario: Scenario name used with
+ ``governance_policy`` for tool allowlist checks.
+ requested_tools: Tool names that the resolver intends to use.
+ resolver_role: Role name used for governance lookup when policy
+ checks are enabled.
Returns:
Tuple of ``(input_obj, cfg_obj)`` populated by the resolver.
"""
input_type, cfg_type = _introspect_flow_signature(target_flow)
+ _enforce_governance(
+ governance_policy=governance_policy,
+ governance_scenario=governance_scenario,
+ requested_tools=requested_tools,
+ resolver_role=resolver_role,
+ )
template = resolver_instructions or _RESOLVER_INSTRUCTIONS
instructions = template.format(
flow_name=target_flow.__name__,
@@ -103,12 +121,20 @@ async def preview_resolve(
*,
target_flow: Callable[..., Awaitable[Any]],
resolver_model: str = "gpt-4o-mini",
+ governance_policy: GovernancePolicy | None = None,
+ governance_scenario: str | None = None,
+ requested_tools: list[str] | None = None,
+ resolver_role: str = "Resolver",
) -> tuple[Any, Any]:
"""Resolve and pretty-print the result without invoking the flow."""
inp, cfg = await resolve_magic_input(
natural_language,
target_flow=target_flow,
resolver_model=resolver_model,
+ governance_policy=governance_policy,
+ governance_scenario=governance_scenario,
+ requested_tools=requested_tools,
+ resolver_role=resolver_role,
)
print("input_obj:", inp.model_dump_json(indent=2))
print("cfg_obj:", cfg.model_dump_json(indent=2))
@@ -199,3 +225,26 @@ def _pydantic_schema_str(t: Any) -> str:
]
return json.dumps({"oneOf": schemas}, indent=2)
return repr(t)
+
+
+def _enforce_governance(
+ *,
+ governance_policy: GovernancePolicy | None,
+ governance_scenario: str | None,
+ requested_tools: list[str] | None,
+ resolver_role: str,
+) -> None:
+ """Apply optional resolver-side governance checks."""
+ if governance_policy is None:
+ return
+ if not governance_scenario:
+ raise ValueError(
+ "governance_scenario is required when governance_policy is set"
+ )
+ for tool_name in requested_tools or []:
+ ensure_tool_allowed(
+ governance_policy,
+ scenario_name=governance_scenario,
+ role=resolver_role,
+ tool_name=tool_name,
+ )
diff --git a/quantmind/mind/__init__.py b/quantmind/mind/__init__.py
new file mode 100644
index 0000000..331bd38
--- /dev/null
+++ b/quantmind/mind/__init__.py
@@ -0,0 +1,9 @@
+"""Mind layer primitives (memory/storage surfaces)."""
+
+from quantmind.mind.memory import (
+ L3CommitRequirements,
+ HybridMemoryEngine,
+ can_commit_to_l3,
+)
+
+__all__ = ["HybridMemoryEngine", "L3CommitRequirements", "can_commit_to_l3"]
diff --git a/quantmind/mind/memory.py b/quantmind/mind/memory.py
new file mode 100644
index 0000000..9e787b7
--- /dev/null
+++ b/quantmind/mind/memory.py
@@ -0,0 +1,95 @@
+"""Hybrid memory primitives for L1/L2/L3 data handling."""
+
+import json
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any
+
+
+@dataclass(frozen=True)
+class L3CommitRequirements:
+ """Hard gates required before durable L3 commit."""
+
+ min_confidence: float = 0.85
+ require_provenance: bool = True
+ require_schema_validity: bool = True
+ require_dedup_check: bool = True
+ require_contradiction_check: bool = True
+
+
+def can_commit_to_l3(
+ artifact: dict[str, Any],
+ *,
+ schema_valid: bool,
+ dedup_ok: bool,
+ contradiction_free: bool,
+ requirements: L3CommitRequirements | None = None,
+) -> bool:
+ """Return true only when artifact passes all required L3 gates."""
+ req = requirements or L3CommitRequirements()
+ confidence = float(artifact.get("validation_confidence", 0.0))
+ if confidence < req.min_confidence:
+ return False
+ if req.require_provenance and not artifact.get("provenance"):
+ return False
+ if req.require_schema_validity and not schema_valid:
+ return False
+ if req.require_dedup_check and not dedup_ok:
+ return False
+ if req.require_contradiction_check and not contradiction_free:
+ return False
+ return True
+
+
+class HybridMemoryEngine:
+ """L1 (transient) -> L2 (working) -> L3 (durable) memory manager."""
+
+ def __init__(self, base_path: str = "~/.quantmind/mind") -> None:
+ self.base_path = Path(base_path).expanduser()
+ self.base_path.mkdir(parents=True, exist_ok=True)
+ self.l1_path = self.base_path / "l1_ephemeral.jsonl"
+ self.l2_path = self.base_path / "l2_working.json"
+ self.l3_path = self.base_path / "l3_durable.jsonl"
+
+ def write_l1_trace(self, agent_role: str, tool_output: Any) -> None:
+ """Append raw tool output to L1 trace."""
+ record = {"role": agent_role, "payload": tool_output}
+ with self.l1_path.open("a", encoding="utf-8") as file:
+ file.write(json.dumps(record, ensure_ascii=False) + "\n")
+
+ def update_l2_state(self, key: str, value: Any) -> dict[str, Any]:
+ """Update and persist L2 working state as JSON."""
+ current: dict[str, Any] = {}
+ if self.l2_path.exists():
+ with self.l2_path.open("r", encoding="utf-8") as file:
+ loaded = json.load(file)
+ if isinstance(loaded, dict):
+ current = loaded
+ current[key] = value
+ with self.l2_path.open("w", encoding="utf-8") as file:
+ json.dump(current, file, ensure_ascii=False, indent=2, sort_keys=True)
+ return current
+
+ def commit_to_l3_graph(
+ self,
+ validated_artifact: dict[str, Any],
+ *,
+ schema_valid: bool,
+ dedup_ok: bool,
+ contradiction_free: bool,
+ requirements: L3CommitRequirements | None = None,
+ ) -> bool:
+ """Commit artifact to L3 only when every hard gate passes."""
+ if not can_commit_to_l3(
+ validated_artifact,
+ schema_valid=schema_valid,
+ dedup_ok=dedup_ok,
+ contradiction_free=contradiction_free,
+ requirements=requirements,
+ ):
+ return False
+ with self.l3_path.open("a", encoding="utf-8") as file:
+ file.write(
+ json.dumps(validated_artifact, ensure_ascii=False) + "\n"
+ )
+ return True
diff --git a/tests/configs/test_recoverable.py b/tests/configs/test_recoverable.py
new file mode 100644
index 0000000..f0565a1
--- /dev/null
+++ b/tests/configs/test_recoverable.py
@@ -0,0 +1,39 @@
+"""Tests for recoverable validation helpers."""
+
+import unittest
+
+from pydantic import BaseModel, ConfigDict
+
+from quantmind.configs.recoverable import RawFallbackNode, RecoverableValidation
+
+
+class _StrictSample(BaseModel):
+ model_config = ConfigDict(extra="forbid")
+ name: str
+ score: float
+
+
+class RecoverableValidationTests(unittest.TestCase):
+ def test_returns_validated_model_on_success(self) -> None:
+ validator = RecoverableValidation(_StrictSample)
+ out = validator.execute_safely({"name": "node-1", "score": 0.91})
+ self.assertIsInstance(out, _StrictSample)
+ assert isinstance(out, _StrictSample)
+ self.assertEqual(out.name, "node-1")
+ self.assertEqual(out.score, 0.91)
+
+ def test_returns_fallback_node_on_validation_error(self) -> None:
+ validator = RecoverableValidation(_StrictSample)
+ out = validator.execute_safely(
+ {"name": "node-1", "score": 0.91, "unexpected": "x"}
+ )
+ self.assertIsInstance(out, RawFallbackNode)
+ assert isinstance(out, RawFallbackNode)
+ self.assertEqual(out.target_schema, "_StrictSample")
+ self.assertTrue(out.context_loss_prevented)
+ self.assertEqual(out.raw_payload["unexpected"], "x")
+ self.assertIn("Extra inputs are not permitted", out.error_message)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/flows/test_governance.py b/tests/flows/test_governance.py
new file mode 100644
index 0000000..f5e5580
--- /dev/null
+++ b/tests/flows/test_governance.py
@@ -0,0 +1,135 @@
+"""Tests for ``quantmind.flows.governance``."""
+
+import unittest
+from pathlib import Path
+
+from quantmind.flows.governance import (
+ GovernancePolicy,
+ GovernancePolicyError,
+ enforce_l3_commit_gates,
+ ensure_tool_allowed,
+ load_governance_policy,
+ loop_budget_manager,
+ run_fallback_policy,
+)
+
+
+class GovernanceLoaderTests(unittest.TestCase):
+ def test_load_default_policy(self) -> None:
+ policy = load_governance_policy()
+ self.assertEqual(policy.version, "2.0.0")
+ self.assertEqual(policy.global_settings.loop_budget_max, 5)
+ self.assertIn("tolerant_ingestion", policy.scenarios)
+
+ def test_load_empty_policy_raises(self) -> None:
+ empty_path = Path(__file__).with_name("empty_governance.yaml")
+ empty_path.write_text("", encoding="utf-8")
+ self.addCleanup(empty_path.unlink)
+ with self.assertRaises(GovernancePolicyError):
+ load_governance_policy(empty_path)
+
+
+class ToolAllowlistTests(unittest.TestCase):
+ def setUp(self) -> None:
+ self.policy = load_governance_policy()
+
+ def test_allowed_tool_passes(self) -> None:
+ ensure_tool_allowed(
+ self.policy,
+ scenario_name="architecture_analysis",
+ role="Scout",
+ tool_name="scan_repo",
+ )
+
+ def test_disallowed_tool_raises(self) -> None:
+ with self.assertRaises(PermissionError):
+ ensure_tool_allowed(
+ self.policy,
+ scenario_name="architecture_analysis",
+ role="Scout",
+ tool_name="graph_write",
+ )
+
+
+class LoopBudgetTests(unittest.TestCase):
+ def setUp(self) -> None:
+ self.policy = load_governance_policy()
+
+ def test_budget_consumption(self) -> None:
+ manager = loop_budget_manager(self.policy)
+ self.assertEqual(manager.remaining, 5)
+ self.assertEqual(manager.consume(), 4)
+ self.assertEqual(manager.consume(steps=2), 2)
+
+ def test_budget_overflow_raises(self) -> None:
+ manager = loop_budget_manager(self.policy)
+ manager.consume(steps=5)
+ with self.assertRaises(RuntimeError):
+ manager.consume()
+
+
+class FallbackPolicyTests(unittest.TestCase):
+ def test_quarantine_fallback_returns_node(self) -> None:
+ policy = load_governance_policy()
+ node = run_fallback_policy(
+ policy,
+ target_schema="ExampleSchema",
+ payload={"x": 1},
+ error_message="invalid",
+ )
+ self.assertEqual(node.target_schema, "ExampleSchema")
+ self.assertEqual(node.raw_payload["x"], 1)
+
+ def test_fail_fast_policy_raises(self) -> None:
+ policy = GovernancePolicy.model_validate(
+ {
+ "version": "2.0.0",
+ "global_settings": {
+ "loop_budget_max": 1,
+ "fallback_policy": "fail_fast",
+ },
+ "scenarios": {},
+ }
+ )
+ with self.assertRaises(RuntimeError):
+ run_fallback_policy(
+ policy,
+ target_schema="ExampleSchema",
+ payload={},
+ error_message="x",
+ )
+
+
+class L3CommitGateTests(unittest.TestCase):
+ def setUp(self) -> None:
+ self.policy = load_governance_policy()
+
+ def test_accepts_when_all_gates_pass(self) -> None:
+ ok = enforce_l3_commit_gates(
+ self.policy,
+ artifact={
+ "validation_confidence": 0.9,
+ "provenance": {"source": "test"},
+ },
+ schema_valid=True,
+ dedup_ok=True,
+ contradiction_free=True,
+ )
+ self.assertTrue(ok)
+
+ def test_rejects_when_any_gate_fails(self) -> None:
+ rejected = enforce_l3_commit_gates(
+ self.policy,
+ artifact={
+ "validation_confidence": 0.9,
+ "provenance": {"source": "test"},
+ },
+ schema_valid=True,
+ dedup_ok=False,
+ contradiction_free=True,
+ )
+ self.assertFalse(rejected)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/mind/__init__.py b/tests/mind/__init__.py
new file mode 100644
index 0000000..ea6dad5
--- /dev/null
+++ b/tests/mind/__init__.py
@@ -0,0 +1 @@
+"""Tests for quantmind.mind package."""
diff --git a/tests/mind/test_memory.py b/tests/mind/test_memory.py
new file mode 100644
index 0000000..db5d981
--- /dev/null
+++ b/tests/mind/test_memory.py
@@ -0,0 +1,84 @@
+"""Tests for ``quantmind.mind.memory``."""
+
+import json
+import tempfile
+import unittest
+
+from quantmind.mind.memory import HybridMemoryEngine, can_commit_to_l3
+
+
+class HybridMemoryEngineTests(unittest.TestCase):
+ def test_write_l1_trace_appends_jsonl(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ engine = HybridMemoryEngine(base_path=tmpdir)
+ engine.write_l1_trace("Scout", {"tool": "scan_repo", "ok": True})
+ content = engine.l1_path.read_text(encoding="utf-8").strip()
+ parsed = json.loads(content)
+ self.assertEqual(parsed["role"], "Scout")
+ self.assertTrue(parsed["payload"]["ok"])
+
+ def test_update_l2_state_round_trip(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ engine = HybridMemoryEngine(base_path=tmpdir)
+ current = engine.update_l2_state("step", {"status": "running"})
+ self.assertEqual(current["step"]["status"], "running")
+ persisted = json.loads(engine.l2_path.read_text(encoding="utf-8"))
+ self.assertEqual(persisted["step"]["status"], "running")
+
+ def test_commit_to_l3_requires_all_gates(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ engine = HybridMemoryEngine(base_path=tmpdir)
+ accepted = engine.commit_to_l3_graph(
+ {
+ "id": "artifact-1",
+ "validation_confidence": 0.9,
+ "provenance": {"source": "test"},
+ },
+ schema_valid=True,
+ dedup_ok=True,
+ contradiction_free=True,
+ )
+ rejected = engine.commit_to_l3_graph(
+ {
+ "id": "artifact-2",
+ "validation_confidence": 0.9,
+ "provenance": {"source": "test"},
+ },
+ schema_valid=True,
+ dedup_ok=False,
+ contradiction_free=True,
+ )
+ self.assertTrue(accepted)
+ self.assertFalse(rejected)
+ lines = engine.l3_path.read_text(encoding="utf-8").strip().splitlines()
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(json.loads(lines[0])["id"], "artifact-1")
+
+
+class CanCommitToL3Tests(unittest.TestCase):
+ def test_rejects_without_provenance(self) -> None:
+ self.assertFalse(
+ can_commit_to_l3(
+ {"validation_confidence": 0.9},
+ schema_valid=True,
+ dedup_ok=True,
+ contradiction_free=True,
+ )
+ )
+
+ def test_rejects_low_confidence(self) -> None:
+ self.assertFalse(
+ can_commit_to_l3(
+ {
+ "validation_confidence": 0.5,
+ "provenance": {"source": "test"},
+ },
+ schema_valid=True,
+ dedup_ok=True,
+ contradiction_free=True,
+ )
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_magic.py b/tests/test_magic.py
index 426f6cd..f8b03e9 100644
--- a/tests/test_magic.py
+++ b/tests/test_magic.py
@@ -12,8 +12,10 @@
from quantmind.configs import PaperFlowCfg
from quantmind.configs.paper import ArxivIdentifier, PaperInput
from quantmind.flows import paper_flow
+from quantmind.flows.governance import load_governance_policy
from quantmind.magic import (
ResolvedFlowConfig,
+ _enforce_governance,
_introspect_flow_signature,
_pydantic_schema_str,
_strip_optional,
@@ -196,6 +198,37 @@ def _capture_agent(*_a: object, **kwargs: object) -> object:
self.assertEqual(captured["model"], "claude-3-5-sonnet")
+class GovernanceIntegrationTests(unittest.TestCase):
+ def setUp(self) -> None:
+ self.policy = load_governance_policy()
+
+ def test_policy_requires_scenario(self) -> None:
+ with self.assertRaises(ValueError):
+ _enforce_governance(
+ governance_policy=self.policy,
+ governance_scenario=None,
+ requested_tools=["scan_repo"],
+ resolver_role="Scout",
+ )
+
+ def test_disallowed_tool_raises_permission_error(self) -> None:
+ with self.assertRaises(PermissionError):
+ _enforce_governance(
+ governance_policy=self.policy,
+ governance_scenario="architecture_analysis",
+ requested_tools=["graph_write"],
+ resolver_role="Scout",
+ )
+
+ def test_allowed_tools_pass(self) -> None:
+ _enforce_governance(
+ governance_policy=self.policy,
+ governance_scenario="architecture_analysis",
+ requested_tools=["scan_repo", "read_file"],
+ resolver_role="Scout",
+ )
+
+
class PreviewResolveTests(unittest.IsolatedAsyncioTestCase):
async def test_prints_and_returns_tuple(self) -> None:
resolved = ResolvedFlowConfig[PaperInput, PaperFlowCfg](
From cbe96a6a08535cdb272bd50123f5ca23f2fc65f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=A7=81=E0=BC=BA=20=F0=9D=93=A9=F0=9D=93=AE=F0=9D=93=B4?=
=?UTF-8?q?=F0=9D=93=B2=20=F0=9D=93=9E=F0=9D=93=B0=F0=9D=93=BE=F0=9D=94=83?=
=?UTF-8?q?=20=E0=BC=BB=EA=A7=82?=
<163497755+Zekiog@users.noreply.github.com>
Date: Wed, 8 Jul 2026 01:16:31 +0200
Subject: [PATCH 6/6] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
docs/fincept_integration.md | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/docs/fincept_integration.md b/docs/fincept_integration.md
index 72cf947..d0c7304 100644
--- a/docs/fincept_integration.md
+++ b/docs/fincept_integration.md
@@ -26,21 +26,24 @@ fincept-ai-ops (FastAPI)
## Usage Example
-```python
-from quantmind import QuantMindClient
+ import asyncio
+ from pathlib import Path
-client = QuantMindClient(api_key="...")
+ from quantmind.configs import PaperFlowCfg
+ from quantmind.configs.paper import LocalFilePath
+ from quantmind.flows import paper_flow
-# Search for momentum factor research
-results = client.search(
- query="momentum factor decay regime change",
- top_k=5,
- filters={"source_type": "paper", "year_gte": 2020}
-)
-for r in results:
- print(r.title, r.insight_summary, r.confidence_score)
-```
+ async def main() -> None:
+ doc = await paper_flow(
+ LocalFilePath(path=Path("path/to/document.pdf")),
+ cfg=PaperFlowCfg(model="gpt-4o-mini"),
+ )
+ # Persist / index this JSON in Fincept's storage layer.
+ print(doc.model_dump(mode="json"))
+
+
+ asyncio.run(main())
## Synergy with Fincept