Files
poimen-memory/tasks/M1.2-standing-query-loader.md
T

104 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M1.2 — Standing-query YAML loader
| Field | Value |
|---|---|
| Phase | M1 — Gated loop at L1 |
| Size | M — 13 days |
| Status | ⬜ Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | M0.2 |
## Goal
Load the standing questions that give the update gate its referent, and fail at
load rather than mid-run when one is wrong.
## Facts (inlined — no spec read needed)
```yaml
# queries/poimen.yaml
project: poimen
roots:
- /Users/rockliang/workplace/Poimen/agent-rust # matched against session cwd
sources: [pi, claude]
queries:
- id: architecture-decisions
question: What architectural decisions were made, with reasoning and rejected alternatives?
- id: infra-root-causes
question: What infrastructure bugs were found, what was the root cause, how was it isolated?
synthesis:
question: What is the current state of this project, and what should someone know before working on it?
exit_gate: true
defaults:
memory_budget: 1024
chunk_tokens: 5000
exit_gate: false # L1 default — see below
```
**Why a question is mandatory.** The GRU-Mem memory agent is `φθ(Q, C_t, M_{t-1})`
and its update gate is defined as "does this chunk contain useful information
*about the problem*". With no `Q` the gate has no referent, and `r_update` is
undefinable — which forecloses post-training (M5) entirely. A query with an empty
question is a load error, not a warning.
**`exit_gate: false` at L1 is deliberate.** Paper §3.3: for "what are *all* the
X" questions you cannot know evidence is sufficient without reading everything,
so the paper itself provides a without-exit-gate inference mode. L1 extraction is
that shape. The gate is still *recorded* — its signal is needed for M5.
`query.id` is stable and frozen. It names the L1 memory, the Obsidian note, and
the log directory; renaming it orphans all three.
## Steps
1. `QuerySet::load(path)` in `mem-core`, `serde_yaml`.
2. Validate at load: non-empty `project`; at least one query; every `id` unique,
non-empty, and `[a-z0-9-]+`; every `question` non-empty; `memory_budget > 0`.
3. Every failure names the file, the query id, and the field.
4. `mem query validate <file>` prints the resolved set and exits non-zero on any
error.
5. Defaults apply per query and are overridable per query.
6. `id` collision across two files for the same project is an error.
## Acceptance
- An empty or missing `question` fails at load with a message naming the id.
- An id outside `[a-z0-9-]+` fails at load — it becomes a filename.
- Defaults resolve; per-query overrides win.
## Verify
**Harness:** table-driven over fixture YAML files, one per failure mode.
**Integration test**`tests/it_query_loader.rs`:
1. `a1_valid_loads` — the reference file above resolves to the expected struct.
2. `a2_empty_question_rejected` — error text contains the query id.
3. `a3_duplicate_id_rejected` — error names both occurrences.
4. `a4_bad_id_charset_rejected``infra/root causes` is rejected, message
mentions the filename constraint.
5. `a5_defaults_and_overrides` — a query without `exit_gate` gets `false`; one
with `true` keeps it.
6. `a6_l1_exit_gate_defaults_false` — assert explicitly, because a silent flip to
`true` truncates every extraction and looks like a model quality problem.
7. `a7_missing_question_field` — absent key behaves as empty, same error.
**Command:** `cargo test -p mem-core query_loader`
**False pass:**
- Testing only the happy path. Every assertion except 1 and 5 is a rejection
test, and rejection is the entire point of load-time validation.
- Asserting an error occurred without asserting the message names the offending
id. "invalid config" sends someone to read the whole file by hand.
## Traps
- Allowing an empty question "for now". It loads, the gate has no referent, the
model updates on nearly everything, and it reads as a bad model rather than a
bad config.
- Letting `id` contain `/` or spaces. It is a path segment in three places.
---
Background: [DESIGN.md](../DESIGN.md) — Standing queries