agentbench / data /k8s_docs /QUESTION_PLAN.md
Nomearod's picture
docs: step 5 follow-up β€” parallel-tracks list + post-authoring observations
05bf702
# K8s Golden Dataset β€” Question Plan
**Status:** Structural guide for Week 1 step 5 authoring (v1.1 plan).
This document defines the 25-question target distribution, per-type
source-page mapping, and authoring constraints. It does NOT contain
the 25 specific question texts β€” those are authored during step 5 in
a fresh session, per cross-cutting #8 pilot-first discipline.
**Upstream contracts:**
- Taxonomy: CRAG 8-type (Yang et al., NeurIPS 2024) β€” see DECISIONS.md
"K8s golden dataset uses CRAG's 8-type taxonomy as the schema".
- Source pages: see `SOURCES.md` (28 pages, category-locked; 8 already
pulled, 20 to pull at step 4).
- Schema: see `agent_bench/evaluation/harness.py` `GoldenQuestion`
plus the v1.1 plan's methodology #3 source-attribution fields.
- Flavor A/B for `false_premise`: see DECISIONS.md "False-premise
questions come in two flavors".
---
## Target distribution (25 questions total)
| CRAG type | Count | Schema field | Notes |
|---|---|---|---|
| `simple` | 5–6 | `question_type: "simple"` | Baseline retrieval: direct lookup in 1 page, 1–2 sentence answer. |
| `simple_w_condition` | 3–4 | `question_type: "simple_w_condition"` | Answer depends on a condition stated in the question (enforcement level, volume type, Pod phase). |
| `comparison` | 3–4 | `question_type: "comparison"` | Answer compares two concepts across 2 pages; reranker stress. |
| `multi_hop` | 5–6 | `question_type: "multi_hop"` | Answer synthesizes 2–4 pages; reranker-stressing by construction. |
| `false_premise` | 3–4 | `question_type: "false_premise"` | Grounded refusal stress. Flavor A (pure refusal) + flavor B (documented negative). |
| `set` / `aggregation` / `post_processing_heavy` | 0–3 | respective values | Optional. Include only if natural from corpus content. |
| **Total** | **25** | | |
**Orthogonal flag:** `time_sensitive: bool` on 2–3 questions. Does
NOT replace `question_type` β€” it's an independent property for
version-bounded content (feature state, API version migration,
deprecations).
---
## Per-type source-page mapping
Each row identifies the K8s concept pages a question of that type
should draw from. Multi-hop and comparison questions list multiple
pages intentionally.
### simple (5–6 slots)
Pool questions where a 1–2 sentence answer lives inside a single page.
| Candidate source | CRAG slot justification |
|---|---|
| `k8s_pods.md` | Pod IP semantics, container sharing, ephemeral containers |
| `k8s_deployment.md` | What a Deployment is, declarative update mechanic |
| `k8s_configmap.md` | What a ConfigMap is, immutable field |
| `k8s_secret.md` | What a Secret is, volume mount modes |
| RBAC Authorization *(step 4 page)* | RBAC primitive definitions (Role, RoleBinding, ClusterRole) |
| StatefulSet *(step 4 page)* | StatefulSet identity guarantees |
| DaemonSet *(step 4 page)* | One-per-node scheduling contract |
| Namespaces *(step 4 page)* | Namespace scoping for resources |
**Authoring rule:** Each `simple` question must have exactly one
expected source page and 1–2 source snippets. KHR target β‰₯ 0.60 on
the authored keywords.
### simple_w_condition (3–4 slots)
Pool questions where the answer explicitly depends on a condition
named in the question.
| Candidate source | Condition that shapes the answer |
|---|---|
| `k8s_pod_security_admission.md` | enforcement level: `enforce` / `audit` / `warn` |
| `k8s_secret.md` | mount mode: environment variable vs file in volume |
| Liveness/Readiness/Startup Probes *(step 4)* | probe type: liveness vs readiness vs startup |
| Volumes *(step 4)* | volume type: emptyDir vs configMap vs persistentVolumeClaim |
| Node-pressure Eviction (`k8s_node_pressure_eviction.md`) | resource under pressure: memory vs disk vs inodes |
**Authoring rule:** The condition must be named in the question
stem, not implied. The expected answer must change materially if the
condition flips. Example: "How is a Secret mounted as a volume
versus consumed as an environment variable?" is a valid
`simple_w_condition`; "How is a Secret mounted?" is `simple`.
### comparison (3–4 slots)
Pool questions where the answer explicitly compares two K8s concepts
that span 2 pages.
| Page pair | Concept compared |
|---|---|
| Deployment vs StatefulSet *(step 4)* | stateless vs stateful workload semantics |
| Deployment vs DaemonSet *(step 4)* | replica-count vs one-per-node scheduling |
| ConfigMap vs Secret | non-confidential vs confidential data, mount parity |
| Service vs Ingress *(step 4)* | L4 vs L7 exposure |
| Taints/Tolerations vs Node Affinity *(step 4)* | opt-out vs opt-in placement |
| Liveness vs Readiness probes *(step 4)* | restart vs traffic-routing semantics |
**Authoring rule:** The question must force retrieval from both
pages. Reranker stress is intentional β€” questions where BM25 would
find one side but miss the other are the target. Expected sources:
2 pages minimum.
### multi_hop (5–6 slots)
Pool questions where the answer synthesizes 2–4 pages. These are
the primary reranker stressors.
| Page set (example) | Hop path |
|---|---|
| Pod + Service + Ingress *(step 4)* | How external traffic reaches a Pod through Service β†’ Ingress |
| Deployment + ReplicaSet + Pod | How a Deployment rollout changes the underlying ReplicaSet and Pod set |
| ConfigMap + Deployment | How a ConfigMap update propagates to Pods via env vars or mounted volume |
| HPA + Deployment + Metrics Server *(partial step 4)* | How HPA reads metrics and scales a Deployment |
| NetworkPolicy + Pod + Namespace *(partial step 4)* | How NetworkPolicy selectors resolve across namespaces |
| Job + Pod + Container lifecycle *(partial step 4)* | How a Job's completions and parallelism interact with Pod restart policy |
**Authoring rule:** Expected sources β‰₯ 2 pages. The question must
not be answerable from any single page alone. `source_chunk_ids`
must list at least one chunk from each expected page; partial
credit is granted in the evaluator if at least one expected chunk is
cited (see `agent_bench/evaluation/harness.py`).
### false_premise (3–4 slots)
Pool questions whose premise is wrong. Split across two flavors:
**Flavor A β€” pure refusal** (at least 1 slot):
- Premise targets a capability that does not exist in the K8s corpus
(not in any pulled page).
- Example seed: "How do I configure Claude API rate limits in a
Kubernetes Deployment?" (wrong domain β€” Claude API is not a K8s
concept)
- Schema: `category: "out_of_scope"`, `expected_sources: []`,
`source_snippets: []`.
- Evaluator expectation: answer contains refusal phrasing AND cites
zero sources.
**Flavor B β€” documented negative** (at least 1 slot, ideally 2):
- Corpus contains an explicit negative statement (e.g.
NetworkPolicy "Anything TLS related" limitation at chunk 63 of
`k8s_network_policies.md`).
- Example already in pilot: `k8s_pilot_005` (NetworkPolicy mTLS).
- Schema: `category: "retrieval"`, `question_type: "false_premise"`,
`expected_sources: [<negative-answer page>]`,
`source_snippets: [<verbatim negative statement>]`.
- Evaluator expectation: answer reports the documented negative
with citation, does NOT open with "the documentation does not
provide instructions" phrasing (per pilot_005 Fix 1 + Fix 2
revert analysis).
**Other flavor-B candidate pages for authoring:**
- Pod Security Standards β€” explicit statements about what each
profile does NOT permit
- RBAC Authorization β€” explicit statements about what RBAC does NOT
provide (e.g. no deny rules)
- NetworkPolicy β€” additional negative clauses beyond the pilot_005
mTLS one
### set / aggregation / post_processing_heavy (0–3 slots)
Include only if a K8s page naturally supports the pattern:
- `set`: "Which Kubernetes resources can expose a Service?" (answer
is a set drawn from the Service page). Include 0–1 of this type
if a clean example emerges; otherwise leave slot empty.
- `aggregation`: Unlikely to fit K8s docs (docs describe concepts,
not tabular data). Likely leave empty.
- `post_processing_heavy`: Unlikely to fit K8s docs. Likely leave
empty.
**Default:** Leave 0–3 as **0**. Only author these if a question
emerges organically during step 5. Do not force-author to hit a
target count; the plan explicitly says "0–3, included only where
corpus content naturally supports".
---
## `time_sensitive` flag placement (2–3 questions)
Flag questions whose correct answer depends on K8s version state:
| Candidate | Why time-sensitive |
|---|---|
| HPA API version | `autoscaling/v1` vs `autoscaling/v2` β€” v2 stable since 1.23 |
| Pod Security Admission stability | "stable as of v1.25" β€” feature state in the page |
| PodSecurityPolicy removal | PSP removed in 1.25; migration path to PSA |
**Authoring rule:** Set `time_sensitive: true` on exactly 2–3
questions. Distribute across β‰₯2 different CRAG types (e.g. one
`simple`, one `simple_w_condition`) so the flag is not concentrated
in a single type. Each `time_sensitive` question must cite a
specific K8s version or feature state in the source snippet,
otherwise the flag is not load-bearing.
---
## Difficulty distribution
Loose guidance, not a hard constraint:
- `easy`: 8–10 questions β€” mostly `simple` and single-page
`simple_w_condition`
- `medium`: 10–12 questions β€” `comparison`, most `multi_hop`,
straightforward `false_premise`
- `hard`: 4–6 questions β€” deep `multi_hop`, flavor-B `false_premise`,
`time_sensitive` + `multi_hop` combinations
The pilot's 6-question set is all `easy`/`medium`. Step 5 should add
the `hard` tier.
---
## Authoring checklist (per question)
For each of the 25 questions, the step 5 author must fill:
| Field | Required | Notes |
|---|---|---|
| `id` | yes | `k8s_<NNN>` zero-padded (e.g. `k8s_001`) |
| `question` | yes | Natural-language question in the voice of a recruiter or developer |
| `expected_answer_keywords` | yes | 3–6 keywords that MUST appear in a correct answer; drives `keyword_hit_rate` |
| `expected_sources` | yes | List of `.md` filenames from `SOURCES.md`; β‰₯1 for scoped questions, `[]` for flavor-A false-premise |
| `category` | yes | `retrieval` / `calculation` / `out_of_scope` |
| `difficulty` | yes | `easy` / `medium` / `hard` |
| `requires_calculator` | yes | `false` for all K8s questions (no calc tool use expected) |
| `reference_answer` | yes | 1–3 sentence answer used by the optional LLM judge |
| `question_type` | yes | CRAG taxonomy value (exactly one of the 8 canonical strings) |
| `time_sensitive` | yes | `bool`; `true` on exactly 2–3 questions |
| `source_chunk_ids` | yes | Content-hashed chunk IDs (stable across reindex); must be `[]` for flavor-A false-premise |
| `source_snippets` | yes | ~20 words verbatim per chunk; drift-detection field |
| `source_pages` | yes | Human-readable page anchor (e.g. `"concepts/workloads/pods"`) |
| `source_sections` | yes | Deepest heading containing the snippet |
**Deprecation note:** The pilot schema has `is_multi_hop: bool`.
Step 5 may retire this field in favor of `question_type == "multi_hop"`,
but only after confirming the evaluator's partial-credit logic
(`agent_bench/evaluation/harness.py:38`) is updated to read from
`question_type`. Do NOT remove `is_multi_hop` without the
corresponding harness update, or existing pilot questions will
break partial-credit scoring.
---
## Pilot-first validation before step 5 authoring
Before writing the 25 questions, step 5 author must:
1. Confirm the 20 new pages from step 4 are ingested and reachable
via the pipeline (smoke-query test per `SOURCES.md`'s post-ingest
validation).
2. Re-run `make evaluate` on the existing 6-question pilot dataset
against the newly-expanded corpus. The pilot's existing questions
must still pass their per-question gates β€” if adding 20 new
pages drops pilot P@5 materially, investigate before adding more
questions on top.
3. Hand-draft 2–3 questions first, run them through the pipeline,
and confirm retrieval surfaces the expected chunks. This is the
final pilot-first checkpoint before bulk authoring.
Only after these three checks pass does the step 5 author proceed
to the full 25-question authoring session.
## Post-authoring observations (step 5 shipped 2026-04-14)
Pilot→full generalization numbers: pilot (6Q) P@5=0.80, R@5=1.00,
KHR=0.81 β†’ full (25Q post-fix) P@5=0.83, R@5=0.96, KHR=0.90. R@5
movement is within expected variance when corpus breadth expands
from 8 β†’ 28 pages; KHR jump from 0.81β†’0.90 is an open question β€”
the 25Q distribution may skew toward questions where the golden
keyword set is more readily satisfied (simple + simple_w_condition
+ set together = 11/25 questions with short, high-precision expected
answers), vs the pilot's retrieval-heavy mix. Worth revisiting if
KHR drifts on future corpora β€” if consistent across datasets, it's
authoring signal that the keyword set should be tightened for CRAG
type parity.
Flavor-B reproducibility finding: k8s_022 (RBAC deny rules) and
pilot_005 (NetworkPolicy mTLS) both produce refusal-phrased answers
when the documented negative is in retrieved context. Two independent
reproductions confirm the LLM-hedges-on-documented-negative pattern
is a class of failure mode, not a one-off β€” strengthens the case
for the deferred Fix 2 + targeted prompt guidance stacked experiment.
Authoring itself is clean on both: retrieval surfaces the expected
chunks, citation accuracy 1.00, snippets verify against chunk IDs.