···4242├── convey/ # Web app frontend & backend
4343├── apps/ # Convey app extensions (see docs/APPS.md)
4444├── muse/ # Agent/generator configs + Agent Skills (muse/*/SKILL.md)
4545-├── tests/ # Pytest test suites
4646-├── fixtures/ # Test data (mock journal)
4545+├── tests/ # Pytest test suites + test fixtures under tests/fixtures/
4746├── docs/ # All documentation (*.md files)
4847├── AGENTS.md # Development guidelines (this file)
4948├── CLAUDE.md # Symlink to AGENTS.md for Claude Code
···5554### Package Organization
56555756* **Python**: Requires Python 3.10+
5858-* **Modules**: Each top-level folder is a Python package with `__init__.py` unless it is data-only (e.g., `fixtures/`)
5757+* **Modules**: Each top-level folder is a Python package with `__init__.py` unless it is data-only (e.g., `tests/fixtures/`)
5958* **Imports**: Prefer absolute imports (e.g., `from think.utils import setup_cli`) whenever feasible
6059* **Entry Points**: Commands are registered in `sol.py`'s `COMMANDS` dict (pyproject.toml just defines the `sol` entry point)
6160* **Journal**: Data stored under `JOURNAL_PATH` (see Environment Management below)
···87868887```python
8988# Use comprehensive mock journal data for testing
9090-os.environ["JOURNAL_PATH"] = "fixtures/journal"
8989+os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
9190# Now all journal operations work with test data
9291```
93929494-The `fixtures/journal/` directory contains a complete mock journal structure with sample facets, agents, transcripts, and indexed data for testing.
9393+The `tests/fixtures/journal/` directory contains a complete mock journal structure with sample facets, agents, transcripts, and indexed data for testing.
95949695---
9796···132131* **Framework**: pytest with coverage reporting
133132* **Unit Tests**: `tests/` root directory
134133 - Fast, no external API calls
135135- - Use `fixtures/journal/` mock data
134134+ - Use `tests/fixtures/journal/` mock data
136135 - Test individual functions and modules
137136* **Integration Tests**: `tests/integration/` subdirectory
138137 - Test real backends (Anthropic, OpenAI, Google)
···228227229228### File Locations
230229* **Entry Points**: `sol.py` `COMMANDS` dict
231231-* **Test Fixtures**: `fixtures/journal/` - complete mock journal
230230+* **Test Fixtures**: `tests/fixtures/journal/` - complete mock journal
232231* **Live Logs**: `$JOURNAL_PATH/health/<service>.log`
233232* **Agent Personas**: `muse/*.md` (apps can add their own in `muse/`, see [docs/APPS.md](docs/APPS.md))
234233* **Generator Templates**: `muse/*.md` (apps can add their own in `muse/`, see [docs/APPS.md](docs/APPS.md))
+1-1
Makefile
···9898 fi
9999100100# Test environment - use fixtures journal for all tests
101101-TEST_ENV = JOURNAL_PATH=fixtures/journal
101101+TEST_ENV = JOURNAL_PATH=tests/fixtures/journal
102102103103# Venv tool shortcuts
104104PYTEST := $(VENV_BIN)/pytest
+4-2
apps/todos/tests/test_tools.py
···1919 return tool(*args, **kwargs)
202021212222-FIXTURES_JOURNAL = Path(__file__).resolve().parents[3] / "fixtures" / "journal"
2222+FIXTURES_JOURNAL = (
2323+ Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "journal"
2424+)
232524262527def test_todo_list_returns_numbered_view(todo_env):
···183185def test_todo_tool_pack_round_trip(tmp_path, monkeypatch):
184186 """Exercise add/done/cancel flow against a copied fixture journal."""
185187 if not FIXTURES_JOURNAL.exists():
186186- pytest.skip("fixtures/journal not found")
188188+ pytest.skip("tests/fixtures/journal not found")
187189188190 journal_copy = tmp_path / "journal"
189191 shutil.copytree(FIXTURES_JOURNAL, journal_copy)
+3-1
apps/transcripts/tests/conftest.py
···1111@pytest.fixture(autouse=True)
1212def _journal_env(monkeypatch):
1313 """Point JOURNAL_PATH at the test fixtures."""
1414- monkeypatch.setenv("JOURNAL_PATH", os.path.join(os.getcwd(), "fixtures", "journal"))
1414+ monkeypatch.setenv(
1515+ "JOURNAL_PATH", os.path.join(os.getcwd(), "tests", "fixtures", "journal")
1616+ )
+1-1
docs/BACKLOG.md
···37373838## Testing
39394040-- [ ] Move fixtures/ into tests/
4040+- [x] Move fixtures/ into tests/
4141- [ ] Enable clean fixture-based service startup (Convey on dynamic port) for integration/dev testing with screenshots
+2-2
docs/PROVIDERS.md
···289289- 2 = FLASH (balanced)
290290- 3 = LITE (fast/cheap)
291291292292-See `fixtures/journal/config/journal.json` for a complete example and `think/models.py` `PROVIDER_DEFAULTS` for tier-to-model mappings.
292292+See `tests/fixtures/journal/config/journal.json` for a complete example and `think/models.py` `PROVIDER_DEFAULTS` for tier-to-model mappings.
293293294294## Testing
295295···354354**Testing:**
35535511. Create unit tests in `tests/test_<name>.py`
35635612. Create integration tests in `tests/integration/test_<name>_backend.py`
357357-13. Add test contexts to `fixtures/journal/config/journal.json`
357357+13. Add test contexts to `tests/fixtures/journal/config/journal.json`
358358359359**Documentation:**
36036014. Update `think/providers/__init__.py` docstring
-6
fixtures/000000_audio.json
···11-[
22- {"start": "00:00:00", "source": "mic", "speaker": 1, "text": "So, all other companies, software vendors, like our competitors are using it.", "description": "speaking with a slight accent, technical discussion tone"},
33- {"start": "00:00:07", "source": "mic", "speaker": 2, "text": "But at the moment its use case is on legacy systems. So the developer, the analyst is syncing the data, and then they go to run the process. So the challenge that we have is making sure that it receives data from the Data Hub. From back when I worked at a previous company, I recall that the Data Hub goes idle after the initial sync, and then it's not sending new data, I don't think. And we would want to see if there's any way that we can have an ALWAYS-ON data stream to our listener from the Data Hub, or if the Data Hub terminates the session completely. If not, then we'd have to devise a way that as soon as the Data Hub is queried, it sends data to our service. We have to assume it has an empty cache at that point. It might sync the first 10% of the data and then start its process and continue syncing in the background. So that was one of the questions to your team was how much access to the data stream is there from the Data Hub during an idle session?", "description": "calm informative tone, clear and articulate"},
44- {"start": "00:01:09", "source": "mic", "speaker": 1, "text": "It's dependent on the system architecture, but most of them currently don't maintain an active data stream when a session is closed.", "description": "explaining with technical tone"},
55- {"topics": "API integration, data synchronization, real-time data, system architecture, Data Hub", "setting": "workplace"}
66-]
-8
fixtures/000000_monitor_0_diff.json
···11-{
22- "app": "Team Chat App",
33- "app_title": "Anna Koval, Leo Maxwell, Nina Patel (DM) - DataSystems - Team Chat App",
44- "visual_description": "The screenshot shows a Team Chat App conversation. On the left is the main conversation window with messages from Leo Maxwell, Nina Patel, and Alex. On the right is a 'Thread' window with replies to Alex's message. The messages contain text, emojis, and timestamps. Below the main conversation is a message input field with formatting options and an area where users can type a message. The 'Thread' window shows replies from Alex and Leo Maxwell, along with a reply input field.",
55- "full_ocr": "Thread\nAlex 2 minutes ago\nsounds like leo wants Synapse-link with Omni-net\n1 reply\nLeo Maxwell 1 minute ago\nI do want that. Productivity > privacy\nReply...\nAlso send to the group\nAa",
66- "meeting_status": "No meeting is currently active",
77- "activity_category": "Communication"
88-}
-18
fixtures/000000_screen.json
···11-## 09:10 - 09:15 Summary
22-33-During this five-minute window, the user was actively engaged in a meeting, likely taking or reviewing project notes, within an **Internal Wiki Editor**.
44-55-### Detailed Activity:
66-77-At **09:12:07**, the user was viewing and potentially drafting or editing an internal wiki page titled "**Project-Helios_Q2-Sprint-Review_Notes_10JUNE2025.wiki - Saved**".
88-99-The document appears to be project review notes, containing detailed discussions and action items related to various technical and project management topics, including:
1010-* **Storage Protocol SP-450**: Mention of a cross-team sync date update to Q3.
1111-* **Tier-3 Archive Spec**: Editorial work ongoing, with sections 2.1 and 2 expected to be completed by Q4 2025, and full document completion by Q1 2026. Stress and performance tests are planned for Q2 2026, with code review in Q3 2026, and final release by January 2027.
1212-* **Security Council (SC) Review**: Discussion about the SC issuing a new guideline reclassifying a legacy database schema for Project "Neptune" work.
1313-* **Quarterly Milestone Sync (QMS)**: Work on a white paper for end of Q2, requiring Enterprise Architecture (EA) and Security Council (SC) coordination before the Product Review Committee (PRC) meeting. Task Force 4.1 is available for joining, and the "Glacier" storage tier codification in the 'Service Catalog' was discussed, including its dual-use nature. A recommendation to the August PRC on new work is pending.
1414-* **Client-Side Caching Spec**: The partner consultation just opened, and the Final Draft Review (FDR) is now July 2025. VeriData has waived standard licensing requirements for external partner participation.
1515-* **Project "Orion"** and **Frontend UI Spec**: No new updates were noted.
1616-* **Action Items**: A table of action items was visible, including "A-113 Brief Marcus Thorne ENG-400" assigned to Liam with a status of "IN PROGRESS".
1717-1818-The presence of video conferencing controls at the bottom of the screen, with the "Unmute" button indicating the microphone was muted, confirms the user was participating in an **active meeting** while reviewing or drafting these notes. The activity was categorized as "Drafting".
···14141515@pytest.fixture(autouse=True)
1616def set_test_journal_path(request, monkeypatch):
1717- """Set JOURNAL_PATH to fixtures/journal for all unit tests.
1717+ """Set JOURNAL_PATH to tests/fixtures/journal for all unit tests.
18181919 This ensures all tests have a valid JOURNAL_PATH without needing
2020 to explicitly set it in each test. Integration tests are excluded.
···2323 if "integration" in request.node.keywords:
2424 return
25252626- # Set JOURNAL_PATH to fixtures/journal for all unit tests
2727- monkeypatch.setenv("JOURNAL_PATH", "fixtures/journal")
2626+ # Set JOURNAL_PATH to tests/fixtures/journal for all unit tests
2727+ monkeypatch.setenv("JOURNAL_PATH", "tests/fixtures/journal")
282829293030@pytest.fixture(autouse=True)
+10-10
tests/integration/test_anthropic_provider.py
···161617171818def get_fixtures_env():
1919- """Load the fixtures/.env file and return the environment."""
2020- fixtures_env = Path(__file__).parent.parent.parent / "fixtures" / ".env"
1919+ """Load the tests/fixtures/.env file and return the environment."""
2020+ fixtures_env = Path(__file__).parent.parent / "fixtures" / ".env"
2121 if not fixtures_env.exists():
2222 return None, None, None
2323···3838 fixtures_env, api_key, journal_path = get_fixtures_env()
39394040 if not fixtures_env:
4141- pytest.skip("fixtures/.env not found")
4141+ pytest.skip("tests/fixtures/.env not found")
42424343 if not api_key:
4444- pytest.skip("ANTHROPIC_API_KEY not found in fixtures/.env file")
4444+ pytest.skip("ANTHROPIC_API_KEY not found in tests/fixtures/.env file")
45454646 if not journal_path:
4747- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
4747+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
48484949 # Prepare environment
5050 env = os.environ.copy()
···146146 fixtures_env, api_key, journal_path = get_fixtures_env()
147147148148 if not fixtures_env:
149149- pytest.skip("fixtures/.env not found")
149149+ pytest.skip("tests/fixtures/.env not found")
150150151151 if not api_key:
152152- pytest.skip("ANTHROPIC_API_KEY not found in fixtures/.env file")
152152+ pytest.skip("ANTHROPIC_API_KEY not found in tests/fixtures/.env file")
153153154154 if not journal_path:
155155- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
155155+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
156156157157 # Prepare environment
158158 env = os.environ.copy()
···233233 fixtures_env, api_key, _ = get_fixtures_env()
234234235235 if not fixtures_env:
236236- pytest.skip("fixtures/.env not found")
236236+ pytest.skip("tests/fixtures/.env not found")
237237238238 if not api_key:
239239- pytest.skip("ANTHROPIC_API_KEY not found in fixtures/.env file")
239239+ pytest.skip("ANTHROPIC_API_KEY not found in tests/fixtures/.env file")
240240241241 # Import provider directly for this test
242242 from think.providers import anthropic as anthropic_provider
+10-10
tests/integration/test_google_provider.py
···161617171818def get_fixtures_env():
1919- """Load the fixtures/.env file and return the environment."""
2020- fixtures_env = Path(__file__).parent.parent.parent / "fixtures" / ".env"
1919+ """Load the tests/fixtures/.env file and return the environment."""
2020+ fixtures_env = Path(__file__).parent.parent / "fixtures" / ".env"
2121 if not fixtures_env.exists():
2222 return None, None, None
2323···3838 fixtures_env, api_key, journal_path = get_fixtures_env()
39394040 if not fixtures_env:
4141- pytest.skip("fixtures/.env not found")
4141+ pytest.skip("tests/fixtures/.env not found")
42424343 if not api_key:
4444- pytest.skip("GOOGLE_API_KEY not found in fixtures/.env file")
4444+ pytest.skip("GOOGLE_API_KEY not found in tests/fixtures/.env file")
45454646 if not journal_path:
4747- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
4747+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
48484949 # Prepare environment
5050 env = os.environ.copy()
···130130 fixtures_env, api_key, journal_path = get_fixtures_env()
131131132132 if not fixtures_env:
133133- pytest.skip("fixtures/.env not found")
133133+ pytest.skip("tests/fixtures/.env not found")
134134135135 if not api_key:
136136- pytest.skip("GOOGLE_API_KEY not found in fixtures/.env file")
136136+ pytest.skip("GOOGLE_API_KEY not found in tests/fixtures/.env file")
137137138138 if not journal_path:
139139- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
139139+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
140140141141 # Prepare environment
142142 env = os.environ.copy()
···221221 fixtures_env, api_key, _ = get_fixtures_env()
222222223223 if not fixtures_env:
224224- pytest.skip("fixtures/.env not found")
224224+ pytest.skip("tests/fixtures/.env not found")
225225226226 if not api_key:
227227- pytest.skip("GOOGLE_API_KEY not found in fixtures/.env file")
227227+ pytest.skip("GOOGLE_API_KEY not found in tests/fixtures/.env file")
228228229229 # Import provider directly for this test
230230 from think.providers import google as google_provider
+13-13
tests/integration/test_openai_provider.py
···161617171818def get_fixtures_env():
1919- """Load the fixtures/.env file and return the environment."""
2020- fixtures_env = Path(__file__).parent.parent.parent / "fixtures" / ".env"
1919+ """Load the tests/fixtures/.env file and return the environment."""
2020+ fixtures_env = Path(__file__).parent.parent / "fixtures" / ".env"
2121 if not fixtures_env.exists():
2222 return None, None, None
2323···3838 fixtures_env, api_key, journal_path = get_fixtures_env()
39394040 if not fixtures_env:
4141- pytest.skip("fixtures/.env not found")
4141+ pytest.skip("tests/fixtures/.env not found")
42424343 if not api_key:
4444- pytest.skip("OPENAI_API_KEY not found in fixtures/.env file")
4444+ pytest.skip("OPENAI_API_KEY not found in tests/fixtures/.env file")
45454646 if not journal_path:
4747- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
4747+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
48484949 # Prepare environment
5050 env = os.environ.copy()
···138138 fixtures_env, api_key, journal_path = get_fixtures_env()
139139140140 if not fixtures_env:
141141- pytest.skip("fixtures/.env not found")
141141+ pytest.skip("tests/fixtures/.env not found")
142142143143 if not api_key:
144144- pytest.skip("OPENAI_API_KEY not found in fixtures/.env file")
144144+ pytest.skip("OPENAI_API_KEY not found in tests/fixtures/.env file")
145145146146 if not journal_path:
147147- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
147147+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
148148149149 # Prepare environment
150150 env = os.environ.copy()
···223223 fixtures_env, api_key, journal_path = get_fixtures_env()
224224225225 if not fixtures_env:
226226- pytest.skip("fixtures/.env not found")
226226+ pytest.skip("tests/fixtures/.env not found")
227227228228 if not api_key:
229229- pytest.skip("OPENAI_API_KEY not found in fixtures/.env file")
229229+ pytest.skip("OPENAI_API_KEY not found in tests/fixtures/.env file")
230230231231 if not journal_path:
232232- pytest.skip("JOURNAL_PATH not found in fixtures/.env file")
232232+ pytest.skip("JOURNAL_PATH not found in tests/fixtures/.env file")
233233234234 # Prepare environment
235235 env = os.environ.copy()
···301301 fixtures_env, api_key, _ = get_fixtures_env()
302302303303 if not fixtures_env:
304304- pytest.skip("fixtures/.env not found")
304304+ pytest.skip("tests/fixtures/.env not found")
305305306306 if not api_key:
307307- pytest.skip("OPENAI_API_KEY not found in fixtures/.env file")
307307+ pytest.skip("OPENAI_API_KEY not found in tests/fixtures/.env file")
308308309309 # Import provider directly for this test
310310 from think.providers import openai as openai_provider
+1-1
tests/test_activities.py
···88from pathlib import Path
991010# Set up test environment before importing the module
1111-os.environ["JOURNAL_PATH"] = "fixtures/journal"
1111+os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
121213131414def test_get_default_activities():
+1-1
tests/test_activity_state.py
···99from pathlib import Path
10101111# Set up test environment before importing the module
1212-os.environ["JOURNAL_PATH"] = "fixtures/journal"
1212+os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
131314141515class TestExtractFacetFromOutputPath:
+2-2
tests/test_app_agents.py
···13131414@pytest.fixture
1515def fixture_journal():
1616- """Set JOURNAL_PATH to fixtures/journal for testing."""
1717- os.environ["JOURNAL_PATH"] = "fixtures/journal"
1616+ """Set JOURNAL_PATH to tests/fixtures/journal for testing."""
1717+ os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
1818 yield
19192020
···41414242@pytest.fixture
4343def fixture_journal():
4444- """Set JOURNAL_PATH to fixtures/journal for testing."""
4545- os.environ["JOURNAL_PATH"] = "fixtures/journal"
4444+ """Set JOURNAL_PATH to tests/fixtures/journal for testing."""
4545+ os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
4646 yield
4747 # No cleanup needed - just testing reads
4848···143143 """Test path generation for detected entities."""
144144 path = detected_entities_path("personal", "20250101")
145145 assert str(path).endswith(
146146- "fixtures/journal/facets/personal/entities/20250101.jsonl"
146146+ "tests/fixtures/journal/facets/personal/entities/20250101.jsonl"
147147 )
148148 assert path.name == "20250101.jsonl"
149149
+3-3
tests/test_entity_agents.py
···12121313@pytest.fixture
1414def fixture_journal():
1515- """Set JOURNAL_PATH to fixtures/journal for testing."""
1616- os.environ["JOURNAL_PATH"] = "fixtures/journal"
1515+ """Set JOURNAL_PATH to tests/fixtures/journal for testing."""
1616+ os.environ["JOURNAL_PATH"] = "tests/fixtures/journal"
1717 yield
1818 # No cleanup needed - just testing reads
1919···9494 assert "`test-facet`" in extra_context or "`full-featured`" in extra_context
95959696 # Should include entities from fixture facets
9797- # fixtures/journal/facets/ contains various entities
9797+ # tests/fixtures/journal/facets/ contains various entities
9898 assert "Entities" in extra_context
9999100100 # Check for some known entities from the fixtures
+2-2
tests/test_facets.py
···1818 get_facets,
1919)
20202121-# Use the permanent fixtures in fixtures/journal/facets/
2222-FIXTURES_PATH = Path(__file__).parent.parent / "fixtures" / "journal"
2121+# Use the permanent fixtures in tests/fixtures/journal/facets/
2222+FIXTURES_PATH = Path(__file__).parent / "fixtures" / "journal"
232324242525def setup_entities_new_structure(
+1-1
tests/test_formatters.py
···1010import pytest
11111212# Set JOURNAL_PATH to fixtures for tests
1313-os.environ["JOURNAL_PATH"] = str(Path(__file__).parent.parent / "fixtures" / "journal")
1313+os.environ["JOURNAL_PATH"] = str(Path(__file__).parent / "fixtures" / "journal")
141415151616class TestRegistry:
···249249 assert statements[0]["text"] == "Real"
250250251251 def test_fixture_data(self):
252252- """Test with actual fixture data from fixtures/revai.json."""
252252+ """Test with actual fixture data from tests/fixtures/revai.json."""
253253 from pathlib import Path
254254255255- fixture_path = Path(__file__).parent.parent / "fixtures" / "revai.json"
255255+ fixture_path = Path(__file__).parent / "fixtures" / "revai.json"
256256 if not fixture_path.exists():
257257 pytest.skip("Fixture file not found")
258258