chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:33:42 +08:00
commit a06f331eb8
3186 changed files with 689843 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
# Gortex evaluation methodology
This directory documents the agent-graded self-eval methodology
gortex uses to measure its own real-world quality. The headline:
we evaluate gortex with the agents that actually use it (Claude
Sonnet 4.6, GPT 5.4, Copilot CLI), on three real codebases, across
five task categories, with a documented judge prompt and an
explicit "report negative deltas" requirement.
This is methodology only — no published numbers live here. Numbers
land in [`BENCHMARK.md`](../../BENCHMARK.md) once we run the
methodology against a tagged build.
## Contents
- [`methodology.md`](methodology.md) — the protocol: agents, tasks,
classifiers, bias checks, negative-delta requirement.
- [`judge-prompt.md`](judge-prompt.md) — the exact judge prompt
template (reproducibility: change the prompt → bump the rev).
- [`task-set.md`](task-set.md) — the 15 seed tasks (3 per category)
with canonical answers.
- [`run.md`](run.md) — operational recipe: how to invoke the
harness, where outputs land, how to publish results.
## Why this exists
A retrieval / code-intelligence engine can ship excellent
substrate (graph, MCP tools, USD savings) and still produce
agents that prefer Read/Grep when given the choice. The eval
methodology answers "with our tools available, does the model
actually use them, and does it produce better answers than it
would without?" That's the real test — not benchmark NDCG@10
on synthetic queries.
The methodology has three properties competitors typically lack:
1. **Multi-agent**: same task set scored against ≥3 distinct
agent / model combinations so a result isn't a quirk of one
provider.
2. **Bias-of-prompt check**: every task runs with both the
default agent prompt AND a deliberately worse prompt (the
"ablation prompt"); a methodology that only looks good on the
tuned prompt is honest-flagged.
3. **Negative-delta requirement**: per-task scoring uses an
(a)/(b)/(c) classifier that distinguishes "gortex helped",
"no measurable difference", "gortex hurt". The published
summary MUST cite both ends — hiding the negatives gets the
methodology disqualified.
The substrate is already shipped (`gortex eval` substrate +
`eval/` Python harness); this directory makes it reproducible
end-to-end without an oral tradition.
+117
View File
@@ -0,0 +1,117 @@
# Judge prompt
The exact prompt the judge model receives. Reproducibility:
if you change a word, bump the `judge_prompt_revision` field in
the published results.
**Current revision: 1 (2026-05-18)**
## System prompt
```
You are evaluating two answers to the same software-engineering
task. Both answers were produced by the same coding agent
working on the same codebase, but one had access to the gortex
MCP tool surface ("WITH") and the other did not ("WITHOUT").
Your job is to assign ONE of three labels:
(a) WITH was measurably better than WITHOUT
— more accurate factually, more complete (covers the
relevant code paths the task asks about), fewer
hallucinations (no claims about symbols that don't exist
/ behaviours that aren't in the code), OR substantially
fewer tokens to reach the same answer quality
(b) WITH and WITHOUT were roughly equivalent
— no meaningful difference in accuracy, completeness, or
cost; either both are good or both are mediocre
(c) WITH was measurably worse than WITHOUT
— less accurate, more confused (e.g. tool noise drowned
the answer), OR noticeably more tokens for the same
answer quality
Always pick exactly one label. If you are uncertain between (a)
and (b), or between (c) and (b), default to (b) — uncertainty is
not a marketing argument.
You will be given:
- The task prompt (what the user asked)
- The canonical answer (what an expert engineer would say)
- The WITH answer
- The WITHOUT answer
- The token cost and wall-clock time of each run
Output JSON:
{
"label": "(a)|(b)|(c)",
"reasoning": "<1-3 sentences explaining the label>",
"facts_correct_with": "<count|fraction|brief>",
"facts_correct_without": "<count|fraction|brief>",
"hallucinations_with": "<count|fraction|brief>",
"hallucinations_without": "<count|fraction|brief>",
"cost_ratio": "<with_tokens / without_tokens, e.g. 0.6 or 1.4>",
"uncertainty": "low|medium|high"
}
Be terse in `reasoning`. The scoring summary aggregates labels,
not narrative; reasoning exists for spot-checks, not for
attempting to influence the headline.
```
## User-message template
```
TASK:
{task_prompt}
CANONICAL ANSWER (for reference):
{canonical_answer}
WITH (gortex MCP available):
[{with_token_cost} tokens · {with_wall_clock}]
{with_answer}
WITHOUT (no gortex MCP):
[{without_token_cost} tokens · {without_wall_clock}]
{without_answer}
Label this comparison.
```
## Judge model selection
The default judge is **Claude Sonnet 4.6**
(`claude-sonnet-4-20250514`). Two reasons:
1. **Different family from the WITH agent.** Sonnet 4.6 judging
Sonnet 4.6's own answers is self-eval bias. Run the judge as
a DIFFERENT model from the agent under test (e.g. GPT 5.4
judging Sonnet 4.6's answers and vice versa).
2. **Cheap enough to re-run.** A 15-task × 6-runs comparison is
90 judge invocations per session; Sonnet 4.6 makes the run
cost negligible compared to the agent runs themselves.
For the published results we run the judge twice with two
different models (Sonnet + GPT 5.4) and report agreement /
disagreement counts. Disagreement >20% is the methodology
trigger: re-curate the seed tasks or pick a third judge.
## Anti-gaming notes
- The judge **never sees the agent identity** (no "WITH agent
was Claude Sonnet 4.6"). Identity bias is real; the WITH /
WITHOUT split is the only signal we want.
- Token counts are computed before the judge sees them
(`internal/tokens` cl100k_base) so the agent can't lie about
cost.
- Canonical answers are written by a human expert PER TASK
before any agent runs. Writing them after seeing agent
outputs is methodology fraud.
- Per-task token / wall-clock budgets are identical WITH and
WITHOUT. A run that exceeds the budget is scored as "no
answer" (not (c)).
+111
View File
@@ -0,0 +1,111 @@
# Methodology
The protocol below produces reproducible, agent-graded scoring of
gortex's real-world effect on coding tasks. Sticking to the
protocol means anyone with the harness + a model API key can
reproduce the numbers and dispute them.
## 1. Task categories (5)
| Category | What it measures | Example |
|----------|------------------|---------|
| **Architectural explanation** | "Why does this codebase have N services" — graph / community structure understanding | "Walk me through how the indexer pipeline processes a new file." |
| **Refactor safety** | "Rename / move / extract — what breaks?" — impact-analysis driven | "Rename `Indexer.Index` to `Indexer.IndexRoot` across the repo. List every caller that needs updating." |
| **Bug localization** | "Given this failure, where's the root cause?" — call-chain + dataflow | "A panic in `internal/savings/store.go::flushLocked`. Trace the conditions that reach it." |
| **Impact analysis** | "If I change X, what tests should I run?" — `get_test_targets` + `verify_change` | "I'm about to change the signature of `tokens.Count`. List the test files that need re-running." |
| **Contract extraction** | "What's the public API surface of this package?" — `contracts list` driven | "List every exported function / method / type in `internal/savings/`, with one-line summaries." |
Each task is realistic — drawn from actual sessions, not invented
synthetic prompts. The seed task set in [`task-set.md`](task-set.md)
ships 3 tasks per category × 5 categories = 15 tasks.
## 2. Agent / model matrix
The same task set runs against each of:
1. **Claude Sonnet 4.6** via the Anthropic API (`claude-sonnet-4-20250514`)
2. **GPT 5.4** via OpenAI (`gpt-5-2025-08`)
3. **Copilot CLI** via the GitHub CLI extension
For each agent × task combination, **two runs**:
- **WITH gortex MCP** — the agent has access to the full gortex
tool surface (`smart_context`, `search_symbols`,
`get_symbol_source`, `verify_change`, …)
- **WITHOUT gortex MCP** — the agent has only its default tool
set (typically `Read`, `Grep`, `Bash`)
So per task: 6 runs (3 agents × 2 modes). Per task set: 90 runs.
Per category: 18 runs.
## 3. Bias-of-prompt check
Each WITH-gortex run is executed twice:
- **default prompt** — the system prompt gortex ships in
`internal/agents/instructions.go` (the same one the production
`gortex init` writes to every agent's config)
- **ablation prompt** — the same prompt with every "prefer
gortex tools" steering line removed
If the published headline only shows the default-prompt number,
the methodology is incomplete. Always publish both, and call out
the delta — that's the "we're not just measuring the prompt"
test.
## 4. (a) / (b) / (c) classifier
A judge model (default Claude Sonnet 4.6 — see
[`judge-prompt.md`](judge-prompt.md)) scores each per-task
WITH-vs-WITHOUT comparison with one of three labels:
- **(a) gortex helped** — the WITH run produced a measurably
better answer (more accurate, more complete, fewer
hallucinations, or substantially fewer tokens)
- **(b) no measurable difference** — answers are roughly
equivalent in quality and cost
- **(c) gortex hurt** — the WITH run was worse (less accurate,
more confused by the tool surface, or noticeably more tokens
for the same answer)
The published summary MUST report all three counts. A
"(a)=12 / (b)=2 / (c)=1" result is honest; "12 wins" is not.
## 5. Negative-delta requirement
Negative deltas (any (c) result) are **required** in the
published summary, with the per-task breakdown linkable. The
explicit requirement is the methodology's anti-survivorship
mechanism: a methodology that buries (c) cases isn't measuring
real-world quality, it's measuring marketing.
If a published run reports zero (c) results across all 15 tasks,
that's a red flag — either the judge is biased or the seed set
is over-fit. Re-run with a different judge model and at least
3 additional tasks per category before publishing.
## 6. Scoring envelope
Per-task token + wall-clock budgets are the same WITH and
WITHOUT (typically 50k tokens / 5 minutes per task). A run that
exceeds the budget scores as "no answer" — not as (c). This
keeps the comparison about *quality* of answers, not endurance.
Per-task cost is reported separately so a published row can show
"answer (a) at 1.2× the WITHOUT cost"; (a) at 5× the cost is
honestly weaker than (a) at 0.8× the cost.
## 7. What we don't measure here
- **Benchmark NDCG / recall** — covered by `bench/baselines/` and
`bench/token-efficiency/`. Different axis: those measure
retrieval quality independently of agent / model; this
methodology measures real agent behaviour.
- **SWE-bench resolve rate** — covered by `BENCHMARK-SWE.md`.
Multi-day GPU compute; published separately when an operator
runs it.
- **Performance** — covered by `bench/perf/`. Indexer / query /
impact latency, not answer quality.
Together the three axes (retrieval / agent behaviour / system
perf) form gortex's published-quality envelope.
+141
View File
@@ -0,0 +1,141 @@
# How to run
Operational recipe for executing the full methodology against a
tagged build and publishing the results.
## Prerequisites
- `gortex` binary built from the tagged commit
- `python3 -m pip install -r eval/requirements.txt` (Python
harness deps; existing file)
- API keys: `ANTHROPIC_API_KEY` (for Sonnet 4.6),
`OPENAI_API_KEY` (for GPT 5.4), and a working `gh copilot`
installation (for Copilot CLI). At least one is enough for a
partial run.
- A working corpus checkout (default: the gortex repo itself)
## End-to-end
```sh
# 1) Tag the build — every published result cites this SHA.
git rev-parse HEAD > eval/results/$(date +%Y%m%d)/HEAD.sha
# 2) Run the full matrix: 15 tasks × 3 agents × 2 modes × 2 prompts.
# Estimated wall clock: ~3-6 hours per agent.
gortex eval run \
--task-set docs/04-evaluation/task-set.md \
--judge-prompt docs/04-evaluation/judge-prompt.md \
--agents sonnet-4.6,gpt-5.4,copilot \
--corpus . \
--out eval/results/$(date +%Y%m%d)/ \
--max-task-tokens 50000 \
--max-task-seconds 300
# 3) Aggregate per-task scores into the summary table.
python3 eval/scripts/aggregate.py \
--workdir eval/results/$(date +%Y%m%d)/ \
--judges sonnet-4.6,gpt-5.4 \
--out eval/results/$(date +%Y%m%d)/summary.md
# 4) Spot-check 5 random tasks per category by hand BEFORE
# publishing. The judge is good, not infallible.
python3 eval/scripts/spotcheck.py \
--workdir eval/results/$(date +%Y%m%d)/ \
--sample 5 \
--out eval/results/$(date +%Y%m%d)/spotcheck.md
# 5) Promote into BENCHMARK.md (manual edit; doc owner).
$EDITOR BENCHMARK.md
```
## What lands on disk
```
eval/results/<date>/
├── HEAD.sha # tagged commit
├── summary.md # the published table
├── spotcheck.md # manual review notes
├── disagreement.md # judge-vs-judge disagreement
├── per-task/
│ ├── 1.1-indexer-walkthrough/
│ │ ├── sonnet-4.6-with-default.json
│ │ ├── sonnet-4.6-without-default.json
│ │ ├── sonnet-4.6-with-ablation.json
│ │ ├── sonnet-4.6-without-ablation.json
│ │ ├── gpt-5.4-with-default.json
│ │ └── ...
│ ├── 1.2-community-detection/
│ └── ...
└── judge-runs/
├── sonnet-4.6-judging/
└── gpt-5.4-judging/
```
Every per-task JSON contains: task prompt, canonical answer,
agent answer, token cost, wall clock, tools called (count +
list), and (if judged) the judge's label + reasoning + agreement
between judges.
## What to publish
In `BENCHMARK.md`, add a section like:
```markdown
## Agent-graded evaluation
**Last run: 2026-MM-DD** · agents: Sonnet 4.6 / GPT 5.4 /
Copilot CLI · judge: Sonnet 4.6 + GPT 5.4 (agreement 87%)
| Category | (a) gortex helped | (b) no difference | (c) gortex hurt |
|----------|------------------:|------------------:|----------------:|
| Architectural explanation | 6 | 1 | 2 |
| Refactor safety | 7 | 2 | 0 |
| Bug localization | 5 | 2 | 2 |
| Impact analysis | 8 | 1 | 0 |
| Contract extraction | 6 | 3 | 0 |
| **Total** | 32 | 9 | 4 |
- Default-prompt vs ablation-prompt delta: +2 (a) / 0 (b) / -1 (c)
— gortex prompt steering helps but isn't load-bearing.
- (c) cases written up in `eval/results/2026-MM-DD/c-cases.md`
— every loss has a public post-mortem.
```
**Required**: cite the (c) count, link to the (c)
post-mortems, and call out the prompt-bias delta. A publication
that hides (c) results is non-compliant with this methodology
and should not be referenced as a benchmark.
## Cost envelope
A single full run (15 tasks × 3 agents × 2 modes × 2 prompts =
180 agent runs + ~360 judge invocations) costs roughly:
- Anthropic API: ~$15-30 (Sonnet 4.6 agent + Sonnet 4.6 judge)
- OpenAI API: ~$10-25 (GPT 5.4 agent + GPT 5.4 judge)
- Copilot CLI: subscription-included
Total: ~$25-55 per full run. Run quarterly + on every major
version bump.
## Partial-run modes
When you only have one API key:
```sh
# Just Sonnet 4.6 (cheapest path)
gortex eval run --agents sonnet-4.6 --task-set ...
# Just one task category (smoke before the full run)
gortex eval run --agents sonnet-4.6 \
--task-set docs/04-evaluation/task-set.md \
--categories "Refactor safety"
# Just the WITH mode (compare absolute quality across agents)
gortex eval run --agents sonnet-4.6,gpt-5.4,copilot \
--modes with
```
Partial runs are useful for iteration but **don't publish
partial-run numbers as benchmarks** — the methodology requires
the full matrix.
+249
View File
@@ -0,0 +1,249 @@
# Seed task set
15 tasks (3 per category) the published methodology runs against
on each release. Each task carries a **canonical answer** the
judge uses as ground truth — written by a human expert before any
agent runs. Adding tasks: append to the appropriate section,
write the canonical answer first, only THEN run the harness.
The task corpus is the gortex repo itself (eats its own dog
food). Extending to other corpora means writing a new
`task-set-<repo>.md` with the same structure and pointing the
harness at it via `--task-set`.
---
## Category 1: Architectural explanation (3 tasks)
### 1.1 Indexer pipeline walkthrough
**Prompt**: "Walk me through how the gortex indexer processes a
single new source file. Name the packages it crosses and the
order of operations."
**Canonical answer (~150 words)**:
1. `indexer.Indexer.IndexCtx(root)` walks `root` via
`internal/indexer/scan.go::scanFiles`, producing one
`parseJob` per matching file.
2. Each job is dispatched to `parser.Registry` (the language
plugin matching the file extension) via
`internal/parser/treesitter.go`.
3. The parser extracts symbols / edges as
`parser.ExtractionResult`; `Indexer.processExtraction` writes
them into the `graph.Graph` and accumulates incoming-edge
tracking for the next phase.
4. `Indexer.buildSearchIndex` (BM25 / Bleve) + `idx.embedder`
(if set) populate the search backends.
5. Semantic enrichment (`internal/semantic`) runs LSP / SCIP
providers in parallel; resolved edges get
`Origin=lsp_resolved` for tier filtering.
6. Returns `IndexResult` with file / node counts + duration.
### 1.2 Community detection role
**Prompt**: "What does the `internal/analysis/communities.go`
package do, and how does it integrate with `smart_context`?"
**Canonical answer (~80 words)**:
Implements Leiden community detection on the graph. Output is a
`CommunityResult` mapping node ID → community ID + per-community
cohesion score. `smart_context` reads it via the Context.CommunityOf
hook in the rerank pipeline: candidates sharing the session's
home community get a locality boost. Recompute is triggered on
graph re-warm; the result is cached in `Server.analysis`.
### 1.3 Daemon dispatch path
**Prompt**: "How does a MCP request hit the daemon and get
routed back to a per-session response?"
**Canonical answer (~120 words)**:
A `gortex mcp` client opens a stdio JSON-RPC pipe; the daemon
dispatcher (`internal/daemon/dispatcher.go::MCPDispatcher`)
parses frames, looks up or creates a per-session `*mcp.Server`
via `Sessions.GetOrCreate`, and forwards. The Server holds a
shared `*graph.Graph` + per-session `tokenStats` +
`sessionState` (notes, frecency, etc.). Responses go back the
same pipe with the matching JSON-RPC id. Cross-session memory
(notes / memories / feedback) is workspace-scoped via the
session's resolved cwd → workspace ID.
---
## Category 2: Refactor safety (3 tasks)
### 2.1 Rename a public method
**Prompt**: "I'm renaming `Indexer.Index` to `Indexer.IndexRoot`.
List every caller that needs updating."
**Canonical answer** (verified via `gortex find_usages
gortex/internal/indexer/indexer.go::Indexer.Index`):
- `gortex/internal/indexer/multi.go::MultiIndexer.IndexRepo`
- `gortex/cmd/gortex/eval_recall.go::runEvalRecall`
- `gortex/bench/perf/runner.go::runRepo` (introduced in the L5
bench commit)
- `gortex/bench/token-efficiency/runner.go::indexRepoForBench`
- All `*_test.go` files calling `idx.Index(...)` (count: see
`find_usages` output)
### 2.2 Change a signature
**Prompt**: "I want to add a `context.Context` to `Engine.SearchSymbols`.
List every caller and what they'll need to change."
**Canonical answer** (verified via `gortex verify_change`): see
`find_usages` output; ~12 callers across `cmd/gortex/`,
`internal/mcp/`, `bench/perf/`, `bench/token-efficiency/`. Each
needs to pass through the request's context (most have one
available; some need to use `context.Background()` for now).
### 2.3 Remove a deprecated field
**Prompt**: "I want to remove the `Edge.LegacyConfidence` field.
What breaks?"
**Canonical answer**: the field doesn't exist; the canonical
answer is "no such field; nothing breaks". A passing agent
should say so explicitly, not hallucinate impact.
---
## Category 3: Bug localization (3 tasks)
### 3.1 Panic trace
**Prompt**: "We have a panic in `internal/savings/store.go` at
`flushLocked`. What conditions could cause it?"
**Canonical answer**: `flushLocked` runs under `s.mu`. Panic
paths: (a) flock acquisition fails (file system permission /
disk full → wrapped, not panicked); (b) atomic-rename fails on
some filesystems → returns error; (c) gob encode fails on
unexpected map shape → would panic in `encoder.Encode`. Most
likely candidate: corruption of the in-memory `s.file.PerRepo`
map by a goroutine that bypassed the mutex.
### 3.2 Wrong rank order
**Prompt**: "After my recent rerank-signal change, the top
result for `validateToken` is now a test file instead of the
real implementation. What signal probably regressed?"
**Canonical answer**: `path_penalty` (the test-file demotion).
If a path matching the test-file regex stopped getting the ×0.3
multiplier, test files would no longer be demoted. Check
`signals_path_penalty.go::classifyPathPenalty` and the regex
patterns in `pathRETest`.
### 3.3 Cross-repo missing edges
**Prompt**: "After indexing a multi-repo workspace, calls from
`web` to `cloud_web` aren't showing up in `get_callers`. What
gives?"
**Canonical answer**: cross-repo resolution depends on
`internal/resolver/cross_repo.go::CrossRepoResolver`; it only
runs when `MultiIndexer` has indexed all repos in the same
workspace. Most likely cause: one of the repos was indexed in
isolation (not via `gortex track`) so the resolver never saw
the cross-repo `import` edges.
---
## Category 4: Impact analysis (3 tasks)
### 4.1 Touch a hot path
**Prompt**: "I'm about to change the signature of
`tokens.Count`. List the test files that need re-running."
**Canonical answer**: use `gortex get_test_targets
internal/tokens/tokens.go::Count`. Expected hits include
`internal/tokens/tokens_test.go`, every test in
`internal/mcp/` that calls `tokenStatsFor`, the savings tests,
the bench harnesses' test files.
### 4.2 Blast radius
**Prompt**: "If I introduce a bug in `Graph.AllNodes`, what's
the worst-case downstream effect?"
**Canonical answer**: AllNodes is called by basically every
analyzer; impact is "the whole codebase". A canonical answer
quantifies it via `gortex explain_change_impact` (depth=3) and
notes which communities / processes are at risk.
### 4.3 Cycle detection
**Prompt**: "Would adding an `imports` edge from
`internal/search/rerank` to `internal/search` create a cycle?"
**Canonical answer**: yes if `internal/search` already imports
`internal/search/rerank` (parent-child importing child). Verify
via `gortex analyze kind=would_create_cycle from_id=...
to_id=...`. Currently `internal/search/hybrid.go` imports
`internal/search/rerank` for the auto-α blend, so adding the
reverse would form a cycle.
---
## Category 5: Contract extraction (3 tasks)
### 5.1 Public API of a package
**Prompt**: "List every exported function / method / type in
`internal/savings/`, with one-line summaries."
**Canonical answer**: use `gortex contracts list
internal/savings`. Expected ~12 symbols:
`Pricing`, `CostAvoided`, `CostAvoidedAll`, `Store`, `Open`,
`DefaultPath`, `EventsPathFor`, `Store.AddObservation`,
`Store.Snapshot`, `Store.Flush`, `Store.Reset`, `Bucket`,
`Event`, `LoadEvents`, `BarString`, `SavingsPercent`,
`AggregateByTool`, `FilterDay`, `FilterSince`,
`BuildDashboard`. Plus the canonical pricing model constants.
### 5.2 Tool surface of a package
**Prompt**: "What MCP tools does `internal/mcp/tools_savings.go`
register, and what are their parameter contracts?"
**Canonical answer**: pull from
`internal/mcp/tools_savings.go::registerSavingsTools` (or
equivalent). Tool list + per-tool param schema. A passing agent
should produce the exact param names + types.
### 5.3 Configuration surface
**Prompt**: "What `.gortex.yaml` keys does the indexer respect?
Group by required vs optional."
**Canonical answer**: cross-reference `internal/config/config.go`
+ each parser's `RegisterX` call. Required: none (all keys have
defaults). Optional: `index.exclude`, `index.max_file_size`,
`semantic.enable_*`, etc. A passing agent should produce the
list with default values per key.
---
## Curation rules
1. **Canonical answers come first.** Writing them after seeing
agent outputs is methodology fraud — the temptation to
"match" the agent's wording corrupts the ground truth.
2. **One topic per task.** A task that asks two questions splits
the (a)/(b)/(c) signal — judge gives partial credit, which
makes the headline noisy.
3. **Verify with the tools.** Every task's canonical answer
should be reproducible by running gortex tools manually; if
you can't reproduce it, the answer is wrong (or the tool
has a bug worth filing).
4. **Bias toward realistic prompts.** The seed set is drawn
from actual user sessions (anonymized). Synthetic prompts
("explain this complex graph algorithm") aren't what real
users ask.