diff --git a/doc-audit/COVERAGE.md b/doc-audit/COVERAGE.md new file mode 100644 index 0000000..bb52bc3 --- /dev/null +++ b/doc-audit/COVERAGE.md @@ -0,0 +1,249 @@ +# Documentation Coverage-Gap Analysis + +Second input to the documentation rework, and the counterpart to [REPORT.md](REPORT.md). The drift +audit asked **"is what is written true?"** This analysis asks the axis it could not see: **"what +exists in the system but appears in no doc?"** A `true` claim is accurate, not sufficient - it can +sit right next to a large undocumented hole. This report enumerates the actual system surface and +diffs it against the docs corpus to find that hole. + +## Method + +Five system surfaces were enumerated directly from source, then each item was cross-checked against +the whole `docs/` tree: + +| Surface | Source of truth | +|---|---| +| Agent CLI | `smartem-decisions/src/smartem_agent/` (Typer app) | +| Backend HTTP API | `smartem-decisions/src/smartem_backend/api_server.py` (FastAPI) + `cli/*.py` | +| Environment variables | `os.getenv`/`environ` reads across `smartem-decisions`, `VITE_*` in `smartem-frontend` | +| Kubernetes | `smartem-devtools/k8s/` (dev/staging/production overlays + shared) | +| Frontend | `smartem-frontend/apps/smartem/src/routes/` (TanStack Router) | + +Each item is classed **documented** (appears with usable detail), **partial** (named but key +surface missing or misdescribed), or **undocumented** (absent from the corpus). "Documented" here +means *present*, not *correct* - where a documented item is also wrong, REPORT.md holds the drift +detail. + +## Headline + +The surfaces are not commensurable, so there is no single percentage - but the shape is clear: + +| Surface | Real surface | Documented | Gap | +|---|--:|--:|---| +| **Frontend routes** | 16 routes + shell | 0 | **100% undocumented** - no `docs/frontend/` exists | +| **Backend endpoints** | 88 routes | 87 (auto-spec) / 0 prose | 1 code-only route; **no hand-written reference**; spec stale | +| **Backend CLI tools** | 4 scripts | 0 | **all undocumented** | +| **Env vars** | 32 read by code | 19 | **13 undocumented** + 8 stale docs-only | +| **K8s workloads** | 9 | 6 (+1 partial) | **2 undocumented** (mongodb, elasticsearch) | +| **K8s operator config** | ~10 classes | 4 | HPA, ingress hostnames, `/dls` mount, 6 secret keys, pull-secret | +| **Agent CLI commands** | 10 | 10 named | verbose flag misdocumented on 9; `agent-cleanup` script absent | + +**The largest hole is the entire shipped frontend.** After that: the backend has good +machine-generated coverage but zero prose reference and a stale committed spec; env-var runtime +tunables and the Kubernetes operational surface are substantially under-documented; the agent CLI is +the best-covered surface (every command is named) but its verbose flag is documented in a form that +does not exist. + +--- + +## Frontend - the biggest gap + +**16 navigable routes and the entire shell are undocumented. There is no `docs/frontend/` section +at all.** The only frontend material in the corpus is six build/release/auth ADRs plus two +planning-stage records (`decision-records/smartem-frontend-{design,requirements}.md`). Those records +are actively **misleading** as current documentation: + +- they use a `/sessions/:sessionId/...` route prefix the app never shipped (it uses + `/acquisitions/$acquisitionId/...`); +- `smartem-frontend-design.md` is self-described as a "working draft"; +- the only concrete route table (requirements Appendix B) documents the **removed `apps/legacy`**, + not `apps/smartem`; +- the design lists a `/micrographs/:micrographId` route that was never built. + +Shipped routes, none with as-shipped reference docs: + +| Route | Shows | +|---|---| +| `/` | Home / dashboard entry | +| `/acquisitions` | Acquisitions list | +| `/acquisitions/$acquisitionId` | Acquisition detail (grids table) | +| `.../grids/$gridId` | Grid layout (redirects to atlas) | +| `.../grids/$gridId/atlas` | Atlas spatial view + prediction/latent overlays | +| `.../grids/$gridId/squares` | Grid-square collection (Table \| Gallery) | +| `.../squares/` (index) | Grid-square table (Holes + Score, per-square predictions, suggested order) | +| `.../squares/gallery` | Grid-square gallery (thumbnail tiles) | +| `.../grids/$gridId/predictions` | Grid-level prediction dashboard | +| `.../grids/$gridId/workspace` | Configurable panel workspace | +| `.../squares/$squareId` | Square detail (foilhole map + latent scatter) | +| `.../squares/$squareId/predictions` | Square quality analytics (histogram, as-of-time slider) | +| `.../squares/$squareId/holes/$holeId` | Foil-hole detail (micrograph cards + metadata) | +| `/models` | ML models catalogue (metric-weight matrix) | +| `/models/$modelName` | Model detail (weight evolution) | + +Undocumented shell features: header nav (Acquisitions / Models / flag-gated Depositions), the +command palette (`/` shortcut, fuzzy search), the settings menu (theme, language, density, +shortcuts), and the Keycloak SPA login/logout UI (auth is covered architecturally, but not the UI). + +**Workstream:** a new `docs/frontend/` reference section - route map, per-view purpose and data +sources, shell/feature guide. This is net-new writing, not a fix. + +--- + +## Backend HTTP API - covered by machine, not by prose + +**88 route operations, all in `api_server.py`.** A generated OpenAPI 3.1 spec is committed +(`smartem-decisions/docs/api/openapi.json`) and cached into the corpus +(`docs/api/smartem/swagger.json`, rendered by Swagger UI). It lists **87** operations - so machine +coverage is 87/88. Two distinct gaps: + +1. **One code-only endpoint:** `GET /micrographs/{micrograph_uuid}/micrograph_image` (the #308/#312 + leaf-image route) is in neither the spec (stale at `0.1.1rc51.dev5`) nor any prose. **The + committed/cached spec should be regenerated** - it currently lags the code by one endpoint. +2. **Zero hand-written endpoint reference.** Every route relies solely on auto-generated Swagger. + Nothing gives an integrator prose context for the resource groups that most need it: `/debug/*` + (8 routes), agent session / SSE (4), processing-feedback `motion_correction` + `ctf_estimation` + (4), and the predictions / latent-representation read surface (11). + +Also note: **`docs/backend/api-documentation.md` is misleading** - its only concrete endpoint +examples (`/api/v1/Session`, `/api/v1/Area`, `/api/v1/Decision`) belong to the *external Athena* +service, not the SmartEM backend, so a reader can mistake them for SmartEM Core routes. (This +overlaps REPORT.md's drift findings for that file.) + +**Backend CLI tools - all four undocumented** (no invocation, args, or purpose anywhere in the +corpus): + +| Script (`src/smartem_backend/cli/`) | Purpose | +|---|---| +| `initialise_prediction_model_weights.py` | Seed model-weight rows for every model x metric on a grid | +| `register_quality_prediction_model.py` | Register a quality-prediction model | +| `random_prior_updates.py` | Simulate motion-corr / CTF / particle-pick prior updates (dev/test) | +| `random_model_predictions.py` | Generate random quality-prediction rows (dev/test) | + +**Workstream:** regenerate the committed spec; add a thin prose reference that frames the resource +groups and links to Swagger for the field-level detail; document the four CLI tools (they are the +data-seeding path for a working dev instance). + +--- + +## Environment variables - runtime tunables missing + +**32 distinct vars are read by code; 19 documented, 13 not.** The env-vars doc is +file-pattern-oriented, so it covers credentials and hosts but omits most per-var runtime tunables. + +Undocumented (read by code, absent from docs): + +| Var | Component | Note | +|---|---|---| +| `SKIP_DB_INIT` | backend | api_server.py:154,187; consumer.py:103 | +| `SMARTEM_GRIDSQUARE_CREATE_BATCH_MAX` | backend | batch-size tunable | +| `SMARTEM_IMAGE_CACHE_DIR` | backend | image-serving cache location | +| `FRONTEND_SSE_MAX_CONNECTIONS` | backend | SSE fan-out cap | +| `FRONTEND_SSE_POLL_INTERVAL` | backend | SSE poll cadence | +| `ENVIRONMENT` | backend | health/version payload | +| `KEYCLOAK_VERIFY_ISS` | backend | **set in every k8s configmap**, documented nowhere | +| `KEYCLOAK_ALLOWED_AZP` | backend | set in staging/prod configmaps | +| `RABBITMQ_VHOST` | backend | broker vhost | +| `RABBITMQ_EXCHANGE` | backend | exchange name | +| `RABBITMQ_EXCHANGE_TYPE` | backend | exchange type | +| `SMARTEM_BACKEND_CONFIG` | backend | config-file path | +| `VITE_DEVTOOLS` | frontend | devtools toggle | + +Stale docs-only tokens (documented but not read by app code - candidates for removal or relabelling +as infra/deploy vars): `RABBITMQ_URL` (never read - the code composes the URL from parts, so +documenting it as an app var is misleading), `RABBITMQ_UI_PORT`, `ADMINER_PORT`, `DOCKER_USERNAME`, +`DOCKER_EMAIL`, `DOCKER_PASSWORD`, `DEPLOY_ENV` (all deploy-script / compose vars, not app config), +and `POSTGRES_X` (a placeholder from an error-message example, not a real var). + +**Workstream:** add the 13 runtime tunables (grouped: SSE, image cache, Keycloak verification, +RabbitMQ topology); split the doc into "app config the process reads" vs "deploy/infra vars", and +either fix or remove the stale tokens. + +--- + +## Kubernetes - operational surface under-documented + +**9 workloads: 6 documented, 1 partial (frontend), 2 undocumented.** Plus a set of operator-facing +config that an operator would need but cannot find: + +- **`mongodb` and `elasticsearch`** run in the development overlay (ports 27017, 9200/9300, + `emptyDir`) but appear **nowhere** in the corpus - no operator would know they run or why. +- **HPA is undocumented and orphaned.** `k8s/hpa.yaml` defines `smartem-http-api-hpa` (min 2 / max + 10, CPU 70%), but no doc mentions autoscaling and **it is referenced by no `kustomization.yaml`**, + so `kubectl apply -k` never deploys it. Both a doc gap and a real config bug. +- **Six required secret keys are missing** from `secret.example.yaml` and undocumented: deployments + pull `POSTGRES_HOST/PORT/DB` and `RABBITMQ_HOST/PORT/EXCHANGE` from `smartem-secrets` via + `secretKeyRef`, but the example lists only the four user/password keys - copying the example + yields a broken deploy. +- **Staging/production ingress is undocumented** - hostnames `smartem.diamond.ac.uk` / + `smartem-staging.diamond.ac.uk`, path `/`, and the `proxy-body-size: 10m` annotation exist only in + manifests; `operations/kubernetes.md` has no ingress section (only ADR 0019 mentions it as "future + work"). +- **The `/dls` NFS/GPFS read-only mount** needed by the image-serving endpoints exists only as a + commented placeholder in the staging/prod API deployment - an operator enabling real imagery has + no guidance. +- **`imagePullSecret: ghcr-secret`** is required by every staging/prod workload but is never + mentioned in `kubernetes-secrets.md`. +- **`scripts/k8s/k8s-network-summary.sh`** is undocumented (the other two k8s scripts are). +- Frontend `BACKEND_HOST` (the FQDN the in-cluster nginx `resolver` needs) is a known DNS foot-gun + captured only in a manifest comment. + +Adjacent drift worth noting: `kubernetes-secrets.md` refers to per-environment `secrets.yaml` files +that **do not exist** in any overlay or kustomization - the real flow is `secret.example.yaml` + +`dev-k8s.sh` / the sealed-secrets controller. + +**Workstream:** an ingress + secrets + autoscaling operator section; fix the orphaned HPA and the +incomplete `secret.example.yaml` (these are config bugs, not just doc gaps); document the dev-only +mongodb/elasticsearch or remove them if unused. + +--- + +## Agent CLI - best covered, one flag wrong + +**10 leaf commands (8 `parse` subcommands + `validate` + `watch`); every command is named in +`agent/cli-reference.md`.** This is the best-documented surface. Two gaps remain: + +- **`--verbose` is documented in a form that does not exist.** All 8 `parse` subcommands and + `validate` declare `verbose: int = 0`, which Typer exposes as `--verbose INTEGER` (no `-v` short, + not a count flag). The docs instead show `-v` / `-vv` examples on these commands, which error. The + real option is undocumented; the documented form is invalid. (This is the same root cause as a + code inconsistency logged in REPORT.md.) +- **The `smartem.agent-cleanup` console script** (`smartem_backend.agent_data_cleanup:main`, + pyproject.toml) is agent-named tooling but is entirely absent from the corpus. + +**Workstream:** correct the verbose-flag documentation (or, preferably, fix the code so `-v`/`-vv` +works uniformly - see REPORT.md); document `agent-cleanup`. + +--- + +## What this feeds into the rework + +Coverage and drift together give the two inputs the rework needs. Ranked by size of gap: + +1. **Write a `docs/frontend/` section from scratch** - 16 routes + shell, currently 0% covered, with + the only existing material actively misleading (legacy/`/sessions` paths). Biggest single hole. +2. **Add a backend endpoint reference and regenerate the spec** - frame the resource groups + (`/debug`, agent SSE, processing-feedback, predictions/latent) in prose; regenerate the stale + committed OpenAPI spec so it includes `micrograph_image`; document the 4 backend CLI tools. +3. **Complete the env-var reference** - add the 13 runtime tunables, split app-config from + deploy/infra vars, prune the 8 stale tokens. +4. **Add a Kubernetes operator section** - ingress, secrets (all required keys), autoscaling, + `/dls` mount; and fix the two config bugs surfaced here (orphaned HPA, incomplete + `secret.example.yaml`). +5. **Fix the agent CLI verbose docs** and document `agent-cleanup`. + +Items 2, 4 also surfaced **real defects** (stale committed spec, orphaned HPA, incomplete secret +example) - worth their own issues alongside the doc work, exactly as the code bugs in REPORT.md are. + +## Reproduce + +This pass enumerated each surface from source and diffed against the docs corpus. It is +re-runnable by re-enumerating and re-grepping: + +```bash +# Agent CLI - smartem-decisions/src/smartem_agent/__main__.py (Typer app) +# Backend API - smartem-decisions/src/smartem_backend/api_server.py + cli/*.py +# Env vars - grep os.getenv/os.environ across smartem-decisions; VITE_ in smartem-frontend +# Kubernetes - smartem-devtools/k8s/environments/*/ + hpa.yaml + secret.example.yaml +# Frontend routes - smartem-frontend/apps/smartem/src/routes + routeTree.gen.ts +# ...each item grepped against smartem-devtools/docs/** +``` diff --git a/doc-audit/README.md b/doc-audit/README.md new file mode 100644 index 0000000..833d745 --- /dev/null +++ b/doc-audit/README.md @@ -0,0 +1,49 @@ +# doc-audit + +Tracking materials for the **documentation rework**. The docs under `docs/` have drifted from the +code over time; rather than rewrite blind, this audit measures the docs against reality first so the +rework can reuse what is still true and target what is missing. Two axes: + +- **Drift** - is what is written true? (REPORT.md) +- **Coverage** - what exists in the system but appears in no doc? (COVERAGE.md) + +## Start here + +- **[REPORT.md](REPORT.md)** - the drift report: verdict summary, files ranked by drift, per-file + claim detail, and the follow-up workstreams. +- **[COVERAGE.md](COVERAGE.md)** - the coverage-gap report: the actual system surface (agent CLI, + backend API, env vars, k8s, frontend routes) diffed against the docs to find what is undocumented. +- **drift-map.html** - an interactive, filterable view of the drift data. Open it in a browser + (`xdg-open doc-audit/drift-map.html`). Self-contained, no server needed. + +## Method + +1. **Atomise** - `atomize.py` slices each Markdown doc into line-anchored **content atoms** + (prose / code / table / diagram / heading), deterministically and re-runnably. +2. **Decompose and verify** - each prose atom is split into individual factual **claims**; every + claim (and each code/table/diagram block) is checked against the real repositories and given a + verdict: `true` (reuse), `misleading` (right fact, drifted context), `incorrect` (false as + written), `unverifiable` (needs a live system). +3. **Report** - `make_report.py` renders `verdicts.jsonl` into `REPORT.md`. + +Only how-to / reference / tutorial docs are verified. ADRs and design docs are parked: they record +decisions at a point in time, so divergence means *superseded*, not *wrong*. + +## Files + +| File | What it is | +|---|---| +| `REPORT.md` | Human-readable drift report (generated) | +| `COVERAGE.md` | Coverage-gap report: system surface vs docs (undocumented surface) | +| `drift-map.html` | Interactive triage dashboard for the drift data (generated, self-contained) | +| `verdicts.jsonl` | One record per verified claim: file, line, claim, verdict, evidence, confidence | +| `manifest.json` | The 34-file verifiable work-list with oracle-routing hints | +| `atomize.py` | The deterministic Markdown -> content-atom slicer (`python atomize.py `) | +| `make_report.py` | Regenerates `REPORT.md` from `verdicts.jsonl` | + +## Scope note + +Together the two reports cover **truth** (REPORT.md) and **coverage** (COVERAGE.md). Neither measures +**structure/consistency** (grouping, navigation, terminology, duplication) - that remains a rework +workstream. A `true` claim is accurate, not sufficient; a covered surface is present, not +necessarily well-organised. diff --git a/doc-audit/REPORT.md b/doc-audit/REPORT.md new file mode 100644 index 0000000..35a4691 --- /dev/null +++ b/doc-audit/REPORT.md @@ -0,0 +1,466 @@ +# Documentation Drift Audit + +First input to the documentation rework. Every published how-to / reference / tutorial doc was sliced into **content atoms**, each atom's prose decomposed into individual factual **claims**, and every claim checked against the real repositories. This report says, claim by claim, **what to reuse, what to fix, and what to bin.** + +## What this audit does and does not cover + +This covers exactly one axis of the rework: **is what is written true?** It deliberately does **not** cover: + +- **Coverage gaps** - system surface that is undocumented (commands, endpoints, env vars, resources that exist but appear in no doc). A true claim can sit next to a large hole. +- **Structure and consistency** - grouping, navigation, terminology, duplication across pages. + +Both are tracked as follow-up workstreams at the foot of this report. A claim being `true` means *accurate*, not *sufficient*. + +## Results + +Corpus: **64** docs -> **2,560** content atoms. Verifiable subset (how-to / reference / tutorial): **34** files, **1,506** atoms -> **960** factual claims. ADR and design docs are parked - they record decisions at a point in time, so divergence means *superseded*, not *wrong*. + +| Verdict | Claims | Share | Meaning | +|---|--:|--:|---| +| **true** | 763 | 79% | reuse as-is - matches the code | +| **misleading** | 119 | 12% | right fact, drifted context - fix in place | +| **incorrect** | 56 | 6% | false as written - rewrite or bin | +| **unverifiable** | 22 | 2% | needs a live system or human intent | +| | **960** | | | + +**79% of documented claims are true.** The rewrite is worth doing for structure and coverage, but most existing prose is salvageable - this report marks exactly which atoms to keep, so the rework is a re-organise-and-fill exercise, not a blank page. + +### Dominant failure mode: repo-split drift, not error + +Most non-true claims are `misleading`, not `incorrect`. Developer tooling (`scripts/k8s`, `tests/e2e`, `env-examples`, k8s manifests) was moved out of `smartem-decisions` into `smartem-devtools`, so many documented commands and paths point at the wrong repository. The facts are right; the surrounding instructions need re-homing. + +### Dead documentation (describes removed components) + +`docs/athena/index.md` and `docs/athena-decision-service-api-spec.README.md` document the `athena_api` package, its mock server and generated client - all **deleted under ADR 0015**. These pages should be removed or rewritten, not fixed. + +### Code / doc mismatches worth a code fix + +Because verification runs the commands and reads the source, it caught the *code* diverging from the *docs*: + +- Agent `validate` exits `0` on an invalid directory, though the docs promise `1` - the handler returns `not is_valid` but Typer ignores command return values, so no non-zero exit is raised. +- The documented `--log-file` flag is a no-op: it is declared but never wired to the watcher, so no log file is written. +- `-v` / `--verbose` is inconsistent across agent subcommands - only `watch` accepts a repeatable `-v`; `parse` and `validate` take a bare `--verbose INTEGER`, so documented `-v` / `-vv` examples error. + +## Interactive triage view + +An interactive, filterable version of this data is published as a Claude artifact: + +> Note: that link renders only for the artifact owner's Claude account - GitHub sanitises embedded HTML/JS, so this Markdown report is the public-visible equivalent. Open `doc-audit/drift-map.html` locally for the same view. + +## Files ranked by drift + +| File | Kind | Claims | Wrong | Drift | Reusable | +|---|---|--:|--:|--:|--:| +| `backend/http-api-client.md` | reference | 30 | 13 | 1 | 16 | +| `operations/setup-smartem-workspace.md` | howto | 77 | 12 | 18 | 47 | +| `operations/publish-smartem-workspace-to-pypi.md` | howto | 22 | 11 | 1 | 10 | +| `backend/api-documentation.md` | reference | 21 | 7 | 2 | 12 | +| `athena-decision-service-api-spec.README.md` | reference | 14 | 4 | 3 | 7 | +| `operations/container-user-configuration.md` | howto | 49 | 2 | 15 | 30 | +| `operations/logging.md` | howto | 23 | 2 | 8 | 7 | +| `agent/cli-reference.md` | reference | 87 | 1 | 7 | 79 | +| `development/github-labels.md` | howto | 39 | 1 | 6 | 32 | +| `development/generate-docs.md` | howto | 17 | 1 | 2 | 14 | +| `operations/run-container.md` | howto | 11 | 1 | 1 | 9 | +| `athena/index.md` | reference | 6 | 1 | 0 | 5 | +| `operations/environment-variables.md` | howto | 36 | 0 | 11 | 25 | +| `agent/deployment.md` | reference | 37 | 0 | 9 | 28 | +| `development/e2e-simulation.md` | howto | 70 | 0 | 7 | 62 | +| `operations/releasing.md` | howto | 54 | 0 | 5 | 49 | +| `operations/containerization.md` | howto | 15 | 0 | 4 | 11 | +| `backend/database.md` | reference | 38 | 0 | 3 | 35 | +| `agent/troubleshooting.md` | reference | 35 | 0 | 3 | 32 | +| `database-schema-drift-prevention.md` | reference | 35 | 0 | 3 | 32 | +| `development/local-keycloak.md` | howto | 47 | 0 | 2 | 44 | +| `agent/authentication.md` | reference | 40 | 0 | 2 | 38 | +| `operations/kubernetes-secrets.md` | howto | 35 | 0 | 2 | 27 | +| `operations/kubernetes.md` | howto | 18 | 0 | 2 | 16 | +| `development/tools.md` | howto | 34 | 0 | 1 | 33 | +| `development/contributing.md` | howto | 1 | 0 | 1 | 0 | +| `getting-started/for-developers.md` | tutorial | 14 | 0 | 0 | 13 | +| `glossary.md` | reference | 14 | 0 | 0 | 9 | +| `operations/index.md` | howto | 10 | 0 | 0 | 10 | +| `backend/index.md` | reference | 8 | 0 | 0 | 8 | +| `agent/index.md` | reference | 7 | 0 | 0 | 7 | +| `development/index.md` | howto | 7 | 0 | 0 | 7 | +| `getting-started/index.md` | tutorial | 5 | 0 | 0 | 5 | +| `backend/api-server.md` | reference | 4 | 0 | 0 | 4 | + +## Claim-level detail + +Per file, the actionable claims (`incorrect` first, then `misleading`) with the evidence found. Files that are fully `true` are omitted. + +
backend/http-api-client.md — 13 wrong, 1 drift (of 30) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 119-127 | update_acquisition(id, AcquisitionUpdateRequest) updates by id using a two-argument call. | api_client.py:420 def update_acquisition(self, acquisition: AcquisitionData) is single-arg and converts via acquisition_to_request; no (id, update) form; passing AcquisitionUpdateRequest fails on .in… | +| incorrect | 147-190 | Nested entities are created via create_acquisition_grid(acq.id, grid), create_grid_gridsquare(grid.id, gs), create_gridsquare_foilhole(gs.id, fh), create_foilh… | create_* methods take a single entity arg, not (parent_id, entity); method is create_gridsquare_foilholes (plural); GridSquareData has gridsquare_id+grid_uuid (no id); GridData needs data_dir+acquisi… | +| incorrect | 194 | The client maintains compatibility with the existing EntityStore API. | No 'EntityStore' reference anywhere in api_client.py or src/; no compatibility layer exists | +| incorrect | 196-202 | The client exposes a generic create(entity_type, id, data, parent=...) EntityStore-compat method. | No 'def create(' method on SmartEMAPIClient; only specific create_acquisition/create_*_* methods exist | +| incorrect | 208-215 | HTTP errors are raised as httpx.HTTPStatusError and should be caught as such. | Client uses the requests library (api_client.py:10); it raises requests.HTTPError via raise_for_status; httpx is never imported | +| incorrect | 233-236 | `await client._request("get", "status")` returns a raw API response. | api_client.py:290 def _request (synchronous, not async); awaiting it raises TypeError; the sync call _request('get','status') is what actually works | +| incorrect | 240 | The client maintains a cache mapping entity IDs to database IDs. | No id-mapping cache exists; no _get_db_id/_store_entity_id_mapping/id_map/db_id in api_client.py or src/ | +| incorrect | 242-248 | The client exposes _get_db_id(entity_type, id) and _store_entity_id_mapping(...) methods. | Neither _get_db_id nor _store_entity_id_mapping exists anywhere in the repo src/ | +| incorrect | 50-62 | The client supports async context-manager use (`async with ... as client`) and `await client.aget_status()`. | Only sync __enter__/__exit__ (api_client.py:283-287); no __aenter__/__aexit__ and no aget_status; sync get_status exists (line 397) | +| incorrect | 66-67 | All operations have async variants prefixed with `a` (e.g. aget_status vs get_status). | No aget_* methods exist on SmartEMAPIClient; all entity methods are synchronous (def, not async def) | +| incorrect | 69-75 | `await client.aget_acquisitions()` is available alongside get_acquisitions(). | get_acquisitions exists (api_client.py:406) but aget_acquisitions does not exist anywhere | +| incorrect | 7-9 | The client supports both synchronous and asynchronous operations. | SmartEMAPIClient has no async methods; only 'async def stream_instructions_async' exists on the separate SSEAgentClient (api_client.py:832) | +| incorrect | 81-102 | create_acquisition accepts AcquisitionData, returns an object with .id, and alternatively accepts an AcquisitionCreateRequest(id=..., name=...). | create_acquisition always calls EntityConverter.acquisition_to_request needing AcquisitionData.instrument, so a request model fails; response is AcquisitionResponse with .uuid not .id; AcquisitionCre… | +| misleading | 106-115 | get_acquisitions() and get_acquisition(id) return objects exposing .id, .name and .status. | get_acquisitions/get_acquisition exist (api_client.py:406,416); AcquisitionResponse has name/status but exposes .uuid, not .id (http_response.py) | + +
+ +
operations/setup-smartem-workspace.md — 12 wrong, 18 drift (of 77) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 162-164 | aria-reference preset contains 20+ repositories. | presets.aria-reference.repos=[fandanGO-cryoem-dls, FragmentScreen/* (6), aria-php/data-deposition-api] -> resolves to ~8 repos, not 20+ | +| incorrect | 171-174 | aria-reference includes all GitlabAriaPHP/aria-* PHP repositories. | Only aria-php/data-deposition-api is listed; the other 18 aria-php repos are NOT in the preset | +| incorrect | 209-212 | init fails if the target directory exists and is not empty (safety measure). | bootstrap_workspace: workspace_path.mkdir(parents=True, exist_ok=True); no emptiness/'not empty' check anywhere in package | +| incorrect | 261 | A --ssh flag clones repositories using SSH instead of HTTPS. | No --ssh option; init defines --git-ssh and --git-https (and sync too). '--ssh' does not exist | +| incorrect | 286 | A --skip-claude flag disables Claude Code configuration. | No --skip-claude option; Claude is opt-in via --with-claude (skip_claude = not with_claude), skipped by default | +| incorrect | 461-467 | 7 skills are linked: database-admin, devops, technical-writer, git, github, ascii-art, playwright-skill. | config lists 5 skills: database, devops, tech-writer, git, playwright; skills dir has database/devops/git/tech-writer(+playwright). No github/ascii-art/database-admin | +| incorrect | 557 | Documentation lives in smartem-devtools/docs/how-to/. | No docs/how-to directory; docs/ has agent, api, architecture, athena, backend, development, getting-started, operations, decision-records | +| incorrect | 564-568 | docs/how-to/ contains run-backend.md, run-e2e-dev-simulation.md, database-migrations.md, deploy-kubernetes.md. | None of run-backend.md/run-e2e-dev-simulation.md/database-migrations.md/deploy-kubernetes.md exist anywhere in smartem-devtools/docs | +| incorrect | 604-612 | Frontend tests run with `npm test` in smartem-frontend. | package.json has no 'test' script (scripts: dev, dev:mock, build, typecheck, lint, format, check...); npm test would fail | +| incorrect | 707-716 | init errors with 'Target directory ... is not empty' when the directory is not empty. | No 'not empty' string or emptiness guard exists; bootstrap uses mkdir(exist_ok=True) and proceeds | +| incorrect | 721-730 | init errors 'Repository ... already exists' when a repo directory is present. | clone_repo: if repo_path.exists(): prints 'Skipping {repo} (already exists)' and returns True - it does not error | +| incorrect | 904-907 | Read .github/CONTRIBUTING.md in smartem-devtools before contributing. | No .github/CONTRIBUTING.md (or any CONTRIBUTING.md) in smartem-devtools; contributing guide is docs/development/contributing.md | +| misleading | 13-17 | init sets up Claude Code configuration and Serena MCP server as part of the default run. | cli.py init: skip_claude = not with_claude; --with-claude defaults False, and Serena only runs 'if ... claude_config' -> both skipped by default | +| misleading | 209-212 | init creates repos/, claude-config/, tmp/, testdata/ subdirectories. | workspace.py creates repos/,tmp/,testdata/,testdata/dls-filesystem/; claude-config is a symlink only created under --with-claude | +| misleading | 267 | The default git transport is HTTPS. | use_ssh defaults None = auto-detect: GitHub repos use SSH when SSH auth works, else HTTPS. Not a fixed HTTPS default | +| misleading | 297-301 | --skip-claude skips .claude/ creation, skills symlinking, settings/permissions config, and CLAUDE.md creation. | Describes the default (Claude off) behaviour under a non-existent flag; also file created is settings.local.json and CLAUDE.md is a symlink | +| misleading | 326-333 | The interactive preset menu offers minimal, smartem-core, aria-reference, full, custom with sizes and repo counts. | select_preset renders a Rich table (Option/Preset/Description/Repos); presets match but no MB sizes shown and aria-reference '20+' is wrong (~8) | +| misleading | 359-371 | A target-directory confirmation lists dirs to create and asks 'Continue? (Y/n)'. | Actual prompt is confirm('Proceed with setup?'); no 'This will create' listing; claude-config only under --with-claude | +| misleading | 387-397 | Configuration output reports 'Linked 7 skills' and 'Indexed repositories'. | config has 5 skills not 7; setup_mcp_config only writes .serena/project.yml + .mcp.json, there is no repository indexing step | +| misleading | 423-454 | Workspace tree includes .claude/settings.json, .claude/permissions.json, tmp/logs, tmp/simulations, testdata/epu-output, README.md. | Actual: .claude/settings.local.json (no settings.json/permissions.json), testdata/dls-filesystem (no epu-output), no tmp/logs\|simulations, no README.md created; org dirs/GitlabAriaPHP correct | +| misleading | 470-473 | The tool configures Claude Code settings: type checking, 120-char lines, British English, no emojis. | settings.local.json only contains {permissions, enabledMcpjsonServers:['serena']}; none of these style settings are written by the tool | +| misleading | 476-478 | Configured permissions grant read access to all repo files, write to appropriate dirs, execute for dev scripts. | defaultPermissions.allow = Bash(git:*),Bash(ls:*),Bash(cat:*),WebSearch,mcp__serena__*,mcp__chrome-devtools__* - not the read/write/execute scheme described | +| misleading | 574-579 | Backend dev setup runs ./scripts/k8s/dev-k8s.sh up from within smartem-decisions. | smartem-decisions has no scripts/ dir; dev-k8s.sh lives at smartem-devtools/scripts/k8s/dev-k8s.sh (moved). uv sync/.venv steps are fine | +| misleading | 593-596 | ls .claude/skills/ should show symlinks to all 7 skills. | Only 5 skills are configured/linked, not 7 | +| misleading | 620-622 | `smartem-workspace add <org/repo>` adds a single repository to an existing workspace. | add command exists but is a stub: prints '[yellow]Not implemented yet: {repo}' and raises typer.Exit(1) | +| misleading | 654-657 | status shows current branch, uncommitted changes, and commits ahead/behind remote. | get_repo_status returns {branch, has_changes, path} only; no ahead/behind computation exists | +| misleading | 772-774 | Setting GITHUB_TOKEN increases the rate limit for fetching configuration. | No GITHUB_TOKEN/Authorization usage in code; config fetched from raw.githubusercontent.com (not the rate-limited API), so the token has no effect on config fetch | +| misleading | 859-862 | Broken skills can be fixed with `ln -sf ../../claude-config/shared/skills/database-admin database-admin`. | Skill is named 'database' not 'database-admin'; source path is claude-code/shared/skills/database (claude-config symlinks to claude-code) | +| misleading | 877-879 | How-to guides are at repos/DiamondLightSource/smartem-devtools/docs/how-to/. | docs/how-to/ does not exist; guides are under docs/operations, docs/development etc. Site URL base is plausible | +| misleading | 895-900 | Run the backend with ./scripts/k8s/dev-k8s.sh up. | dev-k8s.sh is at smartem-devtools/scripts/k8s/dev-k8s.sh, not in the backend (smartem-decisions has no scripts dir) | + +
+ +
operations/publish-smartem-workspace-to-pypi.md — 11 wrong, 1 drift (of 22) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 128-132 | The publish workflow expects a GitHub secret named PYPI_API_TOKEN. | grep PYPI_API_TOKEN/secrets.PYPI in .github/workflows = none; publish uses OIDC trusted publishing (release-smartem-workspace.yml:236-237) | +| incorrect | 134-138 | The workflow uses a GitHub secret named TEST_PYPI_API_TOKEN to publish to TestPyPI. | grep TEST_PYPI_API_TOKEN/testpypi in .github/workflows = none; no TestPyPI publish job exists | +| incorrect | 22-27 | CI authentication from GitHub Actions requires API tokens stored as GitHub Secrets. | publish uses PyPI Trusted Publishing (pypa/gh-action-pypi-publish@release/v1 + permissions id-token:write); no token secret | +| incorrect | 398 | CI handles version bumping automatically with commitizen. | no commitizen anywhere (grep=none); version read from pyproject.toml, RC suffix = rc${run_number} via sed (workflow:51,89,197-198) | +| incorrect | 438-440 | The workflow sets TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}. | no TWINE/secrets.PYPI in workflows; twine only used for 'uvx twine check dist/*' metadata check (workflow:206), upload via pypa action | +| incorrect | 455-467 | The CI version bump is driven by conventional commits (feat->minor, fix->patch, BREAKING CHANGE->major) since the last tag. | no conventional-commit/semver bumping; version fixed from pyproject.toml, RC=base+rc<run_number>; stable requires tag==pyproject version (workflow:51,68-107) | +| incorrect | 505-511 | The GitHub Actions workflow uses env TWINE_USERNAME=__token__ and TWINE_PASSWORD=secrets.PYPI_API_TOKEN/TEST_PYPI_API_TOKEN. | release-smartem-workspace.yml has no TWINE_* env; publish via pypa/gh-action-pypi-publish (OIDC), no token secrets | +| incorrect | 515-520 | Triggers: push to main -> TestPyPI (TEST_PYPI_API_TOKEN); tag smartem-workspace-v* -> PyPI (PYPI_API_TOKEN); PR -> no publish; workflow_dispatch -> no publish. | push to main -> RC GitHub release only (no TestPyPI); tag correct pattern but publish via OIDC not token; workflow_dispatch DOES release rc/stable (workflow:14-23,78-107,218-222) | +| incorrect | 528-540 | Every push to main with package changes uploads the package to TestPyPI. | push to main produces an RC GitHub prerelease only (create-release, should_release); PyPI publish gated on is_stable; no TestPyPI (workflow:78-92,218-244) | +| incorrect | 565-568 | A GitHub 'testpypi' environment (URL test.pypi.org/p/smartem-workspace) inherits TEST_PYPI_API_TOKEN. | no testpypi environment or TestPyPI flow in workflow; only 'pypi' environment defined (workflow:223-225) | +| incorrect | 570-573 | The pypi environment inherits/uses the PYPI_API_TOKEN secret. | publish uses OIDC trusted publishing (id-token:write + pypa action), not a PYPI_API_TOKEN secret (workflow:226-237) | +| misleading | 22 | GitHub Actions automatically publishes smartem-workspace to both PyPI and TestPyPI. | workflow publishes to PyPI only (publish-pypi, is_stable); no TestPyPI job; grep testpypi in .github/workflows = none | + +
+ +
backend/api-documentation.md — 7 wrong, 2 drift (of 21) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 148 | The Athena API includes a full mock server for development. | Mock server (README lists src/athena_api/mock/server.py) absent; no AthenaAPIServer in code; removed commit 5967ec7 | +| incorrect | 150-158 | Create a mock via 'from athena_api.mock import AthenaAPIServer' and server.run(host,port=8080). | athena_api.mock module absent workspace-wide; AthenaAPIServer only appears in doc files, not code; removed commit 5967ec7 | +| incorrect | 162 | A Python client library provides programmatic access to the API. | athena_api client library absent; src/athena_api removed (commit 5967ec7); AthenaClient only in doc files | +| incorrect | 164-184 | Use 'from athena_api import AthenaClient' with athena_api.model.request.Session and client.register_session(). | athena_api, AthenaClient and register_session exist only in this doc, not in any code; module removed commit 5967ec7 | +| incorrect | 26-38 | Run mock locally via pip install -e '.[mock]' then 'from athena_api.mock import AthenaAPIServer' serving :8000/docs. | No [mock] extra in pyproject.toml; no athena_api module anywhere; removed in commit 5967ec7 'Remove backend-side Athena integration' | +| incorrect | 70-73 | Our implementation is a Python client and mock server generated from the Athena spec. | src/athena_api client+mock absent; README points to nonexistent src/athena_api/; removed commit 5967ec7 (architectural violation) | +| incorrect | 75-78 | The SmartEM Core API is implemented with Django. | No 'django' reference in pyproject.toml, setup.py or src/; backend is FastAPI, not Django | +| misleading | 119-122 | POST /api/v1/AlgorithmResult saves processing outcomes. | No /api/v1/AlgorithmResult path in spec; only GET /api/v1/Session/{sessionId}/AlgorithmResult(s) exists (session-scoped, read-only) | +| misleading | 84-86 | The SmartEM swagger is cached here automatically by the backend per ADR 0020. | ADR 0020 exists but its Context states 'three independently committed copies with no automation keeping them in step' (manual, not automatic) | + +
+ +
athena-decision-service-api-spec.README.md — 4 wrong, 3 drift (of 14) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 20-23 | A generated Python client and a mock server are available to use for Athena integration/testing. | No src/athena_api client and no athena mock server.py anywhere; only stale git branch agent_athena_api_integration_draft references athena | +| incorrect | 47-50 | The Python client is at src/athena_api/client.py. | No athena_api package or client.py exists anywhere under repos/ (find/grep both empty) | +| incorrect | 47-50 | The mock server is at src/athena_api/mock/server.py. | find -iname server.py -path *athena* returns nothing; no src/athena_api/mock anywhere | +| incorrect | 9-12 | A generated Python client exists at src/athena_api/. | No athena_api dir or path anywhere; smartem-decisions/src has smartem_agent/api/backend/common only; grep athena in src returns nothing | +| misleading | 29-31 | This spec file is processed by tools/generate_api_docs.py. | Script exists but at smartem-decisions/tools/generate_api_docs.py; doc+spec live in smartem-devtools which has no tools/ dir (cross-repo drift) | +| misleading | 37-43 | Run `python tools/generate_api_docs.py` to update the documentation. | Command's tools/generate_api_docs.py resolves only in smartem-decisions, not the smartem-devtools repo where this doc+spec+docs/api/athena live | +| misleading | 47-50 | The generation script is tools/generate_api_docs.py. | Script exists only at smartem-decisions/tools/generate_api_docs.py, a different repo than the smartem-devtools doc referencing it as tools/... | + +
+ +
operations/container-user-configuration.md — 2 wrong, 15 drift (of 49) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 74-78 | The container will execute processes as the custom user, not root. | No USER instruction in Dockerfile or Dockerfile.dev; ENTRYPOINT runs as root unless k8s securityContext.runAsUser overrides | +| incorrect | 85-89 | In custom user mode the container runs with limited (non-root) privileges. | No USER in Dockerfile; real k8s http-api manifests set no runAsUser, so container runs as root even when built with custom UID/GID | +| misleading | 130-133 | When /dls is not mounted, image serving endpoints return 404 errors for paths referencing /dls. | api_server.py:2742 stat() on a missing file raises FileNotFoundError -> HTTP 500, not 404; 404 only when DB image_path is null (:2788) | +| misleading | 162-165 | If the file does not exist the endpoint returns 404 or a filesystem error. | api_server.py:2742 unguarded source_path.stat() -> FileNotFoundError propagates as HTTP 500, not 404 | +| misleading | 162-165 | If permissions are denied the endpoint returns a 500 error with permission details. | PermissionError from stat()/read yields a generic 500 'Internal Server Error'; no permission details are exposed in the response | +| misleading | 188 | All files in the container are owned by the specified custom user. | Prod Dockerfile:56 only chowns entrypoint.sh; /venv,/app,alembic.ini remain root-owned. True only for Dockerfile.dev:80-81 | +| misleading | 190-197 | `ls -la /app` and `ls -la /venv` show smartem:smartem ownership in a custom-user build. | Prod Dockerfile does not chown /app or /venv (would show root:root). Only Dockerfile.dev:80-81 produces smartem-owned /app,/venv | +| misleading | 205-214 | Local dev builds smartem-decisions:dev and runs it with -p 8000:8000 -e ROLE=api -e POSTGRES_HOST=... . | Run env is correct (entrypoint.sh:15 ROLE default api, :20 port 8000; POSTGRES_HOST required utils.py:105) but build omits required SMARTEM_VERSION so it fails | +| misleading | 218-222 | CI (GitHub Actions) builds as root by default and pushes to ghcr.io/diamondlightsource/smartem-decisions. | Root-default + ghcr.io/diamondlightsource/smartem-decisions correct, but real CI passes SMARTEM_VERSION (release-smartem-decisions.yml:497) and tags version/latest not $GITHUB_SHA; doc command omits … | +| misleading | 226-238 | Staging build passes groupid/userid=1001 and pushes ghcr.io/diamondlightsource/smartem-decisions:staging. | Build-arg/tag/push syntax valid but command omits required SMARTEM_VERSION -> build fails (Dockerfile:52) | +| misleading | 242-254 | Production build passes groupid/userid=5000 and pushes ghcr.io/diamondlightsource/smartem-decisions:production. | Build-arg/tag/push valid but command omits required SMARTEM_VERSION -> build fails (Dockerfile:52) | +| misleading | 308 | The Dockerfile sets ownership while Kubernetes securityContext provides additional enforcement. | No USER instruction, so non-root at runtime comes ENTIRELY from k8s securityContext.runAsUser, not as 'additional' enforcement over the Dockerfile | +| misleading | 37-47 | `docker build -t smartem-decisions .` builds a root image, equivalent to passing groupid=0 userid=0 groupname=root. | Build-arg equivalence is correct, but command omits required SMARTEM_VERSION (Dockerfile:52 pip install ==${SMARTEM_VERSION}) so build fails as written | +| misleading | 373-381 | `docker run --rm smartem-decisions:dls id` outputs uid=5000(smartem) gid=5000(smartem). | No USER instruction in Dockerfile, so `docker run ... id` reports uid=0(root), not 5000; UID switch happens only via k8s securityContext | +| misleading | 394-401 | Rebuild with --build-arg groupid/userid=5000 groupname=smartem; docker inspect shows Args. | Rebuild command omits required SMARTEM_VERSION -> build fails (Dockerfile:52); build-arg names otherwise valid | +| misleading | 65-72 | Custom build passes groupid/userid/groupname to create a non-root user image. | Build-arg names/values correct but command omits required SMARTEM_VERSION -> pip install ==<empty> fails (Dockerfile:52) | +| misleading | 74-78 | All application files (/venv, /app, /entrypoint.sh) are set to be owned by the custom user. | Prod Dockerfile:56 only --chown on entrypoint.sh; /venv (:49) and /app (:54-55) stay root-owned. Only Dockerfile.dev:80-81 chowns all three | + +
+ +
operations/logging.md — 2 wrong, 8 drift (of 23) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 150 | The agent's --log-file parameter creates structured JSON logs suitable for analysis. | log_file appears only at __main__.py:177 as a CLI default; never passed to watcher or used - no log file is created | +| incorrect | 152-167 | The JSON log has fields timestamp, event_count and events[] with event_type/source_path/relative_path/size/modified. | fs_watcher.py:462-472 status log has queue_size,events_processed,successful,orphaned,failed,orphans_*; no event_count/events/source_path exist | +| misleading | 10-22 | consumer and api_server both take -v (INFO) and -vv (DEBUG), default ERROR. | consumer.py:1258-1263 -v/-vv OK; api_server __main__:3000-3006 has no argparse, -v/-vv silently ignored (only SMARTEM_LOG_LEVEL) | +| misleading | 121-125 | Dev: 'watch /data -vv' and 'api_server -vv' both enable DEBUG logging. | watch -vv OK; api_server has no argparse (__main__:3000-3006) so -vv is ignored, level stays SMARTEM_LOG_LEVEL/ERROR | +| misleading | 128-132 | Testing: 'watch /data -v' and 'api_server -v' both enable INFO logging. | watch -v OK; api_server -v ignored (no argparse), level unchanged; correct method is SMARTEM_LOG_LEVEL=INFO | +| misleading | 135-145 | Production: default commands log at ERROR; watch --log-file with --log-interval and -v rotates operational logs. | defaults ERROR OK, --log-interval/-v OK, but --log-file is never used (no file written); __main__.py:177 is its only reference | +| misleading | 25 | All agent commands support consistent verbosity flags. | only watch uses typer.Option('-v',count=True) (__main__.py:194); validate/parse use plain verbose:int=0 -> --verbose only, no -v | +| misleading | 27-41 | agent watch/validate/parse commands all accept -v and -vv. | watch -v/-vv work; validate/parse decl verbose:int=0 -> Typer exposes --verbose not -v, so 'validate ... -v' and 'parse dir ... -vv' error | +| misleading | 47-60 | watch accepts --log-file, --log-interval, --agent-id, --session-id, --heartbeat-interval and writes to the log file. | all options exist (__main__.py:177-196) but log_file is never passed to watcher/used anywhere - grep shows only the decl line, so --log-file is a no-op | +| misleading | 7 | The -v and -vv flags control verbosity across all SmartEM components. | consumer & agent watch accept -v/-vv, but api_server has no argparse and agent parse/validate use --verbose int only | + +
+ +
agent/cli-reference.md — 1 wrong, 7 drift (of 87) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 224-226 | validate exits 1 when directory structure is invalid. | validate of invalid dir returned exit=0 (Typer ignores 'return not is_valid'; no typer.Exit raised) | +| misleading | 229-231 | Example `validate <dir> -v` runs validate with verbose. | validate rejects '-v': 'No such option: -v' exit 2; base command valid without -v | +| misleading | 379-381 | The agent writes structured JSON logs of all detected file changes to the --log-file path. | log_file only appears at __main__.py:177; never passed to SmartEMWatcherV2 or used to write a file | +| misleading | 383 | The log file records file creation/modification events, timestamps, sizes, processing status. | describes the fs_changes.log file, but --log-file is unwired; no such file is written by the agent | +| misleading | 435 | All EPU-processing commands use consistent file pattern matching to identify relevant files. | only watch scans a dir via DEFAULT_PATTERNS; parse subcommands take an explicit file/dir path, no pattern match | +| misleading | 453-460 | Exit codes: 0 success; 1 validation error/API/dir-not-found/permission; 2 keyboard interrupt. | dir-not-found & API = typer.Exit(1) OK; but validation invalid gives exit 0 (verified); Ctrl+C handler raises typer.Exit() -> 0, not 2 | +| misleading | 51-53 | Example `parse dir <dir> -v` runs the command with verbose flag. | '-v' not defined on parse: errors 'No such option: -v' exit 2; base command valid without -v | +| misleading | 75-77 | Example `parse grid <dir> -vv` runs with debug verbosity. | parse subcommands define bare 'verbose:int=0' (no -v short); -vv errors 'No such option' | + +
+ +
development/github-labels.md — 1 wrong, 6 drift (of 39) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 215 | If a label creation fails because it already exists, the script continues; run `--verbose` for details. | Sync loop has no try/catch (sync-labels.ts:361-376); createLabel error propagates to main().catch -> process.exit(1); --verbose only lists conforming labels | +| misleading | 14 | Labels are defined in `core/github-labels-config.ts`. | No .ts config; actual file is core/github-labels.json (imported by core/index.ts line 19) | +| misleading | 142 | Push to main runs `--check` when `core/github-labels-config.ts` or `scripts/github/**` changes. | gitflow.yml push paths are core/github-tags-config.ts (nonexistent) + scripts/github/**; real config is github-labels.json | +| misleading | 157 | To modify labels, edit `core/github-labels-config.ts`. | Labels are edited in core/github-labels.json; no github-labels-config.ts exists | +| misleading | 47-58 | The system-component labels are the ten listed (backend/agent/frontend/aria/devtools families). | 10 rows match, but config systemComponents[] has 12; doc omits component:epuplayer & component:smartem-workspace | +| misleading | 69 | Assignments live in `core/github-labels-config.ts`. | Assignments are in core/github-labels.json repos[]; no github-labels-config.ts exists | +| misleading | 96-100 | Comparison is against labels defined in `core/github-labels-config.ts`. | Compares vs githubLabels from core/index.ts, sourced from core/github-labels.json, not a .ts config | + +
+ +
development/generate-docs.md — 1 wrong, 2 drift (of 17) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 37-39 | The webui reads the toctree directives during build to generate the sidebar navigation. | generate-nav-from-docs.ts builds nav by scanning docs/ dir structure + frontmatter title; never parses 'toctree' | +| misleading | 37-39 | The index.md file in each docs/ subdirectory contains toctree directives that define navigation order and grouping. | 6 of 8 index.md have {toctree} (athena/architecture lack it); they are vestigial Sphinx blocks, not the actual nav source | +| misleading | 37-39 | When adding a new page, add its filename (without extension) to the relevant index.md toctree. | nav auto-generated from directory scan; a new .md appears in sidebar without any toctree edit (editing toctree has no effect) | + +
+ +
operations/run-container.md — 1 wrong, 1 drift (of 11) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 10-13 | Running the container with `--version` checks/prints the version. | entrypoint.sh ignores all CLI args ($@ never used); case ${ROLE:-api} -> defaults to api mode, runs alembic+uvicorn, never prints version | +| misleading | 39-48 | ROLE=worker runs the queue worker, and SMARTEM_LOG_LEVEL=INFO/DEBUG produce INFO/DEBUG worker logging. | worker runs (entrypoint.sh:25 python -m consumer) but consumer.main() sets level from argparse -v/-vv (consumer.py:1257-1263, default ERROR); entrypoint passes no -v and SMARTEM_LOG_LEVEL is never re… | + +
+ +
athena/index.md — 1 wrong, 0 drift (of 6) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| incorrect | 16-28 | The mock server is started via `pip install -e .[mock]` then `from athena_api.mock import AthenaAPIServer; AthenaAPIServer().run()` on port 8000. | athena_api package DELETED per ADR 0015 (accepted 2026-01-21); no src/athena_api, no AthenaAPIServer in any .py; no [mock] extra in pyproject | + +
+ +
operations/environment-variables.md — 0 wrong, 11 drift (of 36) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 108-110 | Staging is deployed via `DEPLOY_ENV=staging ./scripts/k8s/dev-k8s.sh up`. | DEPLOY_ENV=staging supported (dev-k8s.sh:12,30) but script moved to smartem-devtools/scripts/k8s | +| misleading | 117-120 | Run `cp .env.example.k8s.production .env.k8s.production`. | template moved to smartem-devtools/env-examples/.env.example.k8s.production; bare cp does not resolve | +| misleading | 136-154 | First-time setup: cp .env.example .env; cp .env.example.k8s.development .env.k8s.development; edit; ./scripts/k8s/dev-k8s.sh up; run python -m smartem_backend.… | cp .env.example .env & module valid in smartem-decisions, but .env.example.k8s.development template and dev-k8s.sh moved to smartem-devtools -> block spans two repos | +| misleading | 213-228 | Scenario 1: ./scripts/k8s/dev-k8s.sh up, then .env (POSTGRES localhost:30432, RABBITMQ localhost:30672) is used by host-run python -m smartem_backend modules. | .env NodePorts (localhost:30432/30672) and modules correct, but dev-k8s.sh moved to smartem-devtools/scripts/k8s | +| misleading | 234-248 | Scenario 2: full K8s deploy via ./scripts/k8s/dev-k8s.sh up; pods in namespace smartem-decisions; NodePorts API 30080, Adminer 30808, RabbitMQ UI 30673. | ports (http-api 30080, adminer 30808, rabbitmq 30673) & namespace smartem-decisions correct, but cp template + dev-k8s.sh moved to smartem-devtools | +| misleading | 254-269 | Scenario 3: `./tests/e2e/run-e2e-test.sh` loads .env automatically (the script does `source .env`). | script moved to smartem-devtools/tests/e2e and sources $WORKSPACE_ROOT/.env.local-test-run (line 59), not .env | +| misleading | 26-30 | E2E testing is run via `./tests/e2e/run-e2e-test.sh`. | script moved out of smartem-decisions to smartem-devtools/tests/e2e/run-e2e-test.sh; path unresolvable from decisions checkout | +| misleading | 59-61 | The dev cluster is brought up by running `./scripts/k8s/dev-k8s.sh up`. | script moved to smartem-devtools/scripts/k8s/dev-k8s.sh; `up` action valid but path unresolvable from smartem-decisions | +| misleading | 64-67 | Run `cp .env.example.k8s.development .env.k8s.development` then add DOCKER_USERNAME/EMAIL/PASSWORD. | template moved to smartem-devtools/env-examples/.env.example.k8s.development; bare cp fails (needs env-examples/ prefix / other repo) | +| misleading | 89-90 | .env.k8s.development is used by ./scripts/k8s/dev-k8s.sh to create K8s Secrets and ConfigMaps. | function correct (dev-k8s.sh:28 sources .env.k8s.development, :284 create secret, :359 create configmap) but script moved to smartem-devtools | +| misleading | 97-100 | Run `cp .env.example.k8s.staging .env.k8s.staging`. | template moved to smartem-devtools/env-examples/.env.example.k8s.staging; bare cp does not resolve | + +
+ +
agent/deployment.md — 0 wrong, 9 drift (of 37) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 104-109 | parse session <file> --verbose runs. | parse session cmd exists but --verbose is an INTEGER option; bare --verbose fails; correct form is --verbose 1 | +| misleading | 112-117 | parse atlas <file> --verbose runs. | parse atlas exists; --verbose requires an integer value, bare --verbose errors (needs --verbose 1) | +| misleading | 120-133 | parse gridsquare and parse gridsquare-metadata run, the latter with --verbose. | gridsquare + gridsquare-metadata cmds exist (__main__.py:113,123) but --verbose needs an int value; bare --verbose errors | +| misleading | 136-145 | parse foilhole <file> --verbose and parse micrograph <file> run. | foilhole+micrograph cmds exist (__main__.py:133,143); parse micrograph line ok, but foilhole --verbose fails (needs --verbose 1) | +| misleading | 156-171 | validate <dir> --verbose runs and invalid dirs fail. | validate cmd exists but --verbose is INTEGER; bare --verbose errors 'requires an argument' (needs --verbose 1) | +| misleading | 176-191 | validate <dir> --verbose runs against well-formed directories. | validate cmd exists; --verbose needs an integer argument, bare --verbose fails (correct: --verbose 1) | +| misleading | 200 | Failed validation returns exit code 1 and lists specific issues. | invalid-but-existing dir exits 0, not 1: 'return not is_valid' return value is NOT mapped to Typer exit code; issues are listed (via logging) | +| misleading | 277-282 | epuplayer record <dir> <output.tar.gz> records filesystem events. | record takes ONE positional (directory) + REQUIRED -o/--output; positional output errors: 'the following arguments are required: -o/--output' (needs -o) | +| misleading | 84-97 | parse dir accepts --verbose and --verbose --verbose for debug-level output. | parse dir exists but verbose is --verbose INTEGER (not count); bare --verbose errors 'requires an argument'; --verbose --verbose errors 'not a valid integer' | + +
+ +
development/e2e-simulation.md — 0 wrong, 7 drift (of 70) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 149-165 | Multi runner creates a timestamped results directory under logs/e2e-tests/. | multi:31 TEST_DIR=$WORKSPACE_ROOT/tmp/e2e-logs/... (actual is tmp/e2e-logs, not logs/e2e-tests) | +| misleading | 253-263 | Test results root is logs/e2e-tests/ at repo root (gitignored) with 4 service logs, db-dump.sql and test-params.json. | runners write to tmp/e2e-logs/ (run-e2e-test.sh:30); logs/e2e-tests/ only in smartem-decisions/.gitignore:110 (manual convention) | +| misleading | 342-346 | RabbitMQ default credentials are guest/guest. | rabbitmq.yaml:24-31 RABBITMQ_DEFAULT_USER/PASS from secretKeyRef; env example uses username/password, not guest/guest | +| misleading | 39-48 | The runner creates a timestamped test results directory under logs/e2e-tests/. | run-e2e-test.sh:30 TEST_DIR=$WORKSPACE_ROOT/tmp/e2e-logs/... (actual is tmp/e2e-logs, not logs/e2e-tests) | +| misleading | 408-412 | RABBITMQ_URL=amqp://guest:guest@localhost:30672/ is the consumer's broker URL. | AMQP nodePort 30672 correct; but credentials are username/password per env example / k8s secret, not guest:guest | +| misleading | 792-799 | `curl -u guest:guest http://localhost:30673` reaches the RabbitMQ management UI. | rabbitmq.yaml:92 nodePort 30673 for mgmt UI (port correct); credentials are secret-based username/password, not guest:guest | +| misleading | 808-811 | Consumer broker is RABBITMQ_URL=amqp://guest:guest@localhost:30672/. | AMQP nodePort 30672 correct; credentials username/password per env example / secret, not guest:guest | + +
+ +
operations/releasing.md — 0 wrong, 5 drift (of 54) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 169 | apps/legacy/** is excluded by design from the frontend release. | apps/legacy no longer exists (removed per #136); it is not in the include filters and there is no explicit exclude directive — stale reference | +| misleading | 203-207 | CI generates the API client (npm run api:generate), writes version.json, and runs `npm run build:smartem`. | No `build:smartem` script exists; root script is `build` (npm run build -w @smartem/app) and workflow runs `npm run build` (L165). api:generate + version.json steps are correct | +| misleading | 223-224 | All three workflows support workflow_dispatch with an rc/stable choice via the Actions tab. | There are FOUR release workflows (decisions, epuplayer, workspace, frontend); all four DO have workflow_dispatch inputs.release_type choice rc/stable — the count 'three' is stale | +| misleading | 3 | There are three packages in the SmartEM ecosystem to release. | Doc documents FOUR: smartem-decisions, smartem-epuplayer, smartem-workspace, smartem-frontend; 4 release-*.yml workflows exist | +| misleading | 3 | All releases are tag-driven via GitHub Actions. | RC releases are automatic on push to main (release-*.yml on.push.branches [main]); only stable releases are tag-driven | + +
+ +
operations/containerization.md — 0 wrong, 4 drift (of 15) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 11-17 | The image is built with `docker build -t smartem-decisions .` (or podman equivalent) with default settings. | Root Dockerfile installs PyPI 'smartem-decisions[backend,images]==${SMARTEM_VERSION}'; SMARTEM_VERSION has no default so plain build fails; CI passes --build-arg SMARTEM_VERSION. | +| misleading | 23-37 | DLS build uses --build-arg groupid=1000 userid=1000 groupname=smartem to tag smartem-decisions:dls. | groupid/userid/groupname args exist and are correct, but command omits required --build-arg SMARTEM_VERSION so build fails on current root Dockerfile. | +| misleading | 93 | The Dockerfile uses a multi-stage build process. | Root Dockerfile is single-stage (only 'FROM ... AS runtime', PyPI install); multi-stage build now lives in Dockerfile.dev. | +| misleading | 95-97 | The build has three stages: developer (base+deps), build (installs packages/app code), runtime (slim + built app). | Stages developer/build/runtime match Dockerfile.dev exactly, but not the primary Dockerfile (single-stage); doc points at wrong file. | + +
+ +
backend/database.md — 0 wrong, 3 drift (of 38) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 279-282 | Migration files follow YYYY_MM_DD_HHMM-NNN_description.py where NNN is a sequential migration number. | Only 001-003 use NNN; migrations 004+ use hash revision IDs (33107394dcd1 etc.) and 006 file has no revision token | +| misleading | 34-41 | Migration 003 adds SciFi robot prediction model tables. | 003 inserts SciFi robot TEST DATA into existing qualitypredictionmodel table, not new tables | +| misleading | 34-41 | `upgrade head` applies exactly migrations 001-006 (complete set). | 10 migration files exist through d5e6f7a8b9c0; doc list stops at 006 (4 later migrations undocumented) | + +
+ +
agent/troubleshooting.md — 0 wrong, 3 drift (of 35) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 230-236 | python -m smartem_agent validate /path/to/grid -vv works. | validate_epu_dir(verbose:int=0) is a plain int opt; -v/-vv count only exists on watch (:194); -vv errors here | +| misleading | 244-248 | Required files for a valid EPU dir are EpuSession.dm and Atlas/Atlas.dm. | validate_project_dir requires EpuSession.dm + Metadata + Images-Disc* (fs_parser.py:110-124); Atlas/Atlas.dm is watched but NOT a structural requirement | +| misleading | 253-260 | python -m smartem_agent parse session /path/EpuSession.dm -vv works. | parse session exists (__main__.py:91) but verbose is plain int; -vv flag only defined on watch, so -vv errors here | + +
+ +
database-schema-drift-prevention.md — 0 wrong, 3 drift (of 35) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 18-21 | The CI build fails if any schema changes (drift) are detected. | _schema_drift.yml:11 'continue-on-error: true' (temporarily skipped, issue #181) so job does not fail build | +| misleading | 27-30 | The check is skipped for draft PRs with a [WIP] prefix. | ci.yml:25 skips on PR title startsWith '[WIP]' (title-based, not draft status) | +| misleading | 34 | If drift is detected the CI build fails with a clear error message. | script exits 1 but job has continue-on-error:true (_schema_drift.yml:11); build not failed currently | + +
+ +
development/local-keycloak.md — 0 wrong, 2 drift (of 47) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 106-109 | Mock mode is started with `cd smartem-frontend && npm run dev:smartem:mock`. | No dev:smartem:mock script; equivalent is `npm run dev:mock` (root -> @smartem/app: VITE_ENABLE_MOCKS=true vite); :smartem aliases removed (#136) | +| misleading | 84 | `npm run dev:smartem` serves config.json via Vite. | No dev:smartem script; root package.json now has bare `dev` (npm run dev -w @smartem/app); :smartem aliases removed with legacy app (#136) | + +
+ +
agent/authentication.md — 0 wrong, 2 drift (of 40) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 95-97 | A distinct 'Token refreshed' log entry is emitted on proactive refresh or 401-forced refresh. | No log labelled 'Token refreshed'; proactive refresh re-emits the 'token fetched' info line; 401 emits warning 'Received 401 ... refreshing token' (api_client.py:269) | +| misleading | 95-97 | An 'Authentication failure' log is emitted when token fetch fails or a request keeps 401ing after a refresh. | Token-fetch failure logged (keycloak_client.py:133 'token fetch failed'); but persistent 401 after refresh is NOT explicitly logged - _send_with_auth just returns the response | + +
+ +
operations/kubernetes-secrets.md — 0 wrong, 2 drift (of 35) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 188-199 | Each env dir contains secrets.yaml and a kustomization.yaml that references secrets.yaml. | kustomization.yaml resources omit secrets.yaml; secrets.yaml not committed (generated by script into the dir) | +| misleading | 357-370 | Files map: scripts/k8s/generate-sealed-secrets.sh, k8s/environments/<env>/{secrets.yaml,kustomization.yaml} where kustomization references secrets.yaml, and k8… | script + k8s/secret.example.yaml exist, but kustomization.yaml does NOT reference secrets.yaml and secrets.yaml is generated (uncommitted) | + +
+ +
operations/kubernetes.md — 0 wrong, 2 drift (of 18) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 49-56 | The k8s/ directory contains environments/{development,staging,production} and a README.md. | environments/ w/ 3 envs present, but NO README.md at k8s/ root (has hpa.yaml + secret.example.yaml instead) | +| misleading | 86 | The 'k8s directory documentation' link (k8s/) resolves to detailed k8s docs. | docs/operations/k8s/ does not exist (broken relative link); repo k8s/ dir exists but has no README/docs page | + +
+ +
development/tools.md — 0 wrong, 1 drift (of 34) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 95-112 | The simulator subcommands take the documented flags, incl. motion-correction/ctf-complete --foilhole-id. | motion_correction (L265) & ctf_complete (L289) expose --gridsquare-id, NOT --foilhole-id as documented; other subcmds/flags correct | + +
+ +
development/contributing.md — 0 wrong, 1 drift (of 1) + +| Verdict | Line | Claim | Evidence | +|---|---|---|---| +| misleading | 1-2 | The MyST include directive references an existing file at ../../.github/CONTRIBUTING.md relative to the doc. | Resolves to smartem-devtools/.github/CONTRIBUTING.md (absent; dir has only labeler.yml+workflows); real file at smartem-decisions/.github/CONTRIBUTING.md (doc moved out) | + +
+ +## Follow-up workstreams (the rest of the rework) + +1. **Fix drifted docs** - the 119 `misleading` claims, mostly path/command re-homing to `smartem-devtools`. Near-mechanical; highest reuse-for-effort. +2. **Remove or rewrite dead docs** - the athena pages and the 56 `incorrect` claims. +3. **Coverage-gap analysis** - enumerate the actual system surface (agent CLI commands, backend OpenAPI endpoints, env vars, k8s resources, frontend routes) and diff against what is documented, to find undocumented surface. This audit cannot see those holes. +4. **Restructure and consistency** - regroup by audience/task, unify terminology, dedupe. Use the `true` atoms as reusable material. +5. **File code bugs** - the validate exit-code, `--log-file` no-op, and verbose-flag mismatches above. + +## Reproduce + +```bash +# 1. Re-slice the docs into content atoms (deterministic) +python doc-audit/atomize.py . # writes atoms to the workspace tmp dir +# 2. Re-verify claims against the repos (Claude multi-agent pass) -> verdicts.jsonl +# 3. Regenerate this report +python doc-audit/make_report.py +``` diff --git a/doc-audit/atomize.py b/doc-audit/atomize.py new file mode 100644 index 0000000..93f16b8 --- /dev/null +++ b/doc-audit/atomize.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +"""Slice markdown docs into content atoms for reality-verification. + +Deterministic structural slicer: walks each markdown file and emits one JSON +record per structural block (prose run, fenced code block, table, diagram, +bare section heading, frontmatter). The semantic layer (claim extraction + +comparison against the codebase) is a separate downstream pass; this script +only produces reproducible, line-anchored atoms with stable ids. +""" +from __future__ import annotations + +import hashlib +import json +import re +import sys +from dataclasses import dataclass, field, asdict +from pathlib import Path + +FENCE_RE = re.compile(r"^(\s*)(```+|~~~+)(.*)$") +HEADING_RE = re.compile(r"^(#{1,6})\s+(.*?)\s*#*\s*$") +TABLE_SEP_RE = re.compile(r"^\s*\|?[\s:]*-{2,}[\s:|-]*\|?\s*$") +IMAGE_RE = re.compile(r"^\s*!\[[^\]]*\]\([^)]+\)\s*$") + +# oracle hints: how a downstream pass would go about checking this atom +HINT_RULES = [ + ("env-var", re.compile(r"`?[A-Z][A-Z0-9_]{3,}`?\s*=|\b[A-Z][A-Z0-9_]{3,}\b\s*(?:env|variable)")), + ("port", re.compile(r":\d{4,5}\b|\bport\b|NodePort", re.I)), + ("source-path", re.compile(r"[\w./-]+\.(?:py|ts|tsx|js|sh|ya?ml|json|toml|cfg|ini|sql|md)\b")), + ("url", re.compile(r"https?://")), + ("cli-cmd", re.compile(r"^\s*[\$#>]\s|\b(?:npm|python|pytest|kubectl|helm|docker|alembic|ruff|pyright|gh|git|uv|pip)\b")), + ("k8s", re.compile(r"\bk8s\b|kubernetes|kubectl|helm|namespace|sealed[- ]?secret", re.I)), +] + +KIND_BY_DIR = { + "getting-started": "tutorial", + "operations": "howto", + "development": "howto", + "backend": "reference", + "agent": "reference", + "athena": "reference", + "architecture": "explanation", + "api": "reference", +} + + +def doc_kind(rel: str) -> str: + parts = rel.split("/") + if "decisions" in parts and rel.endswith(".md") and re.search(r"/\d{4}-", rel): + return "adr" + if parts[0] == "docs" and len(parts) > 1: + top = parts[1] + if top == "decision-records": + return "explanation" + return KIND_BY_DIR.get(top, "reference") + return "reference" + + +def sha(text: str) -> str: + return hashlib.sha1(text.strip().encode("utf-8")).hexdigest()[:10] + + +def hints(text: str) -> list[str]: + return [name for name, rx in HINT_RULES if rx.search(text)] + + +@dataclass +class Atom: + id: str + sha1: str + file: str + doc_kind: str + type: str # prose | code | table | diagram | section | frontmatter + heading_path: list[str] + lang: str | None + line_start: int + line_end: int + n_lines: int + n_chars: int + oracle: list[str] + content: str + verify: dict | None = None # filled by downstream reality-comparison pass + + +def atomize(path: Path, root: Path) -> list[Atom]: + rel = str(path.relative_to(root)) + kind = doc_kind(rel) + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + n = len(lines) + atoms: list[Atom] = [] + heading_stack: list[tuple[int, str]] = [] # (level, title) + ordinal = 0 + i = 0 + + def hpath() -> list[str]: + return [t for _, t in heading_stack] + + def emit(atype: str, s: int, e: int, body: list[str], lang: str | None = None) -> None: + nonlocal ordinal + text = "\n".join(body) + if atype != "section" and not text.strip(): + return + atoms.append( + Atom( + id=f"{rel}::a{ordinal:03d}", + sha1=sha(text or "|".join(hpath())), + file=rel, + doc_kind=kind, + type=atype, + heading_path=hpath(), + lang=lang, + line_start=s + 1, + line_end=e + 1, + n_lines=e - s + 1, + n_chars=len(text), + oracle=hints(text), + content=text, + ) + ) + ordinal += 1 + + # optional YAML frontmatter + if lines and lines[0].strip() == "---": + for j in range(1, n): + if lines[j].strip() == "---": + emit("frontmatter", 0, j, lines[0 : j + 1]) + i = j + 1 + break + + pending_heading_needs_body = False # true right after a heading, until we see body + + while i < n: + line = lines[i] + fence = FENCE_RE.match(line) + heading = HEADING_RE.match(line) + + if heading: + # a heading with no body between it and the next heading -> section atom + if pending_heading_needs_body: + lvl, title = heading_stack[-1] + emit("section", heading_stack_line[0], heading_stack_line[0], [f"{'#'*lvl} {title}"]) + level = len(heading.group(1)) + title = heading.group(2).strip() + while heading_stack and heading_stack[-1][0] >= level: + heading_stack.pop() + heading_stack.append((level, title)) + heading_stack_line = (i,) + pending_heading_needs_body = True + i += 1 + continue + + if fence: + marker = fence.group(2)[0] + lang = fence.group(3).strip().split()[0] if fence.group(3).strip() else None + s = i + i += 1 + while i < n and not re.match(rf"^\s*{re.escape(marker)}{{{len(fence.group(2))},}}\s*$", lines[i]): + i += 1 + e = min(i, n - 1) + body = lines[s : e + 1] + atype = "diagram" if (lang or "").lower() in {"mermaid", "graphviz", "dot", "plantuml"} else "code" + emit(atype, s, e, body, lang=lang) + pending_heading_needs_body = False + i = e + 1 + continue + + if line.strip() == "": + i += 1 + continue + + # table: header row followed by a separator row + if "|" in line and i + 1 < n and TABLE_SEP_RE.match(lines[i + 1]): + s = i + i += 2 + while i < n and "|" in lines[i] and lines[i].strip(): + i += 1 + e = i - 1 + emit("table", s, e, lines[s : e + 1]) + pending_heading_needs_body = False + continue + + # standalone image -> diagram + if IMAGE_RE.match(line): + emit("diagram", i, i, [line]) + pending_heading_needs_body = False + i += 1 + continue + + # prose run: until blank line, heading, fence, or table separator + s = i + while i < n: + nl = lines[i] + if nl.strip() == "" or HEADING_RE.match(nl) or FENCE_RE.match(nl): + break + if "|" in nl and i + 1 < n and TABLE_SEP_RE.match(lines[i + 1]): + break + i += 1 + e = i - 1 + emit("prose", s, e, lines[s : e + 1]) + pending_heading_needs_body = False + + # trailing empty heading + if pending_heading_needs_body and heading_stack: + lvl, title = heading_stack[-1] + emit("section", heading_stack_line[0], heading_stack_line[0], [f"{'#'*lvl} {title}"]) + + return atoms + + +def main() -> None: + root = Path(sys.argv[1]).resolve() + files = sorted(root.glob("docs/**/*.md")) + all_atoms: list[Atom] = [] + for f in files: + all_atoms.extend(atomize(f, root)) + + out = root.parent.parent.parent / "tmp" / "doc-atoms" / "atoms.jsonl" + out.parent.mkdir(parents=True, exist_ok=True) + with out.open("w") as fh: + for a in all_atoms: + fh.write(json.dumps(asdict(a), ensure_ascii=False) + "\n") + + # summary + from collections import Counter + + by_type = Counter(a.type for a in all_atoms) + by_kind = Counter(a.doc_kind for a in all_atoms) + print(f"files: {len(files)} atoms: {len(all_atoms)}") + print("by type:", dict(by_type.most_common())) + print("by doc_kind:", dict(by_kind.most_common())) + print(f"wrote: {out}") + + +if __name__ == "__main__": + main() diff --git a/doc-audit/drift-map.html b/doc-audit/drift-map.html new file mode 100644 index 0000000..de6d131 --- /dev/null +++ b/doc-audit/drift-map.html @@ -0,0 +1,268 @@ +Documentation Drift Map — SmartEM + + +
+
SmartEM ecosystem · documentation audit · 2026-07-09
+

Documentation Drift Map

+

Every published doc was sliced into content atoms, each atom's prose decomposed into + individual factual claims, and each claim checked against the real repositories. This is what survived + contact with reality: what to reuse, what to fix, what to bin.

+
+ 64 docs → 2,560 atoms + 34 verifiable files (how-to · reference · tutorial) + 960 claims verified + ADR / design docs parked (historical, not "drift") +
+ +
+
+
+
+ +
+ +
+ +
+ +
+
+

Files by drift

+
+
+
+
+
Actionable claims
+ +
+
+
+
+ +
+ Method. Deterministic Markdown slicer (atomize.py) → per-file claim extraction and + reality-check by 34 parallel agents grepping the workspace repos → verdict per claim. + true = matches the code, reuse as-is. misleading = the fact is right but the surrounding + instruction has drifted (usually a path/command that moved to smartem-devtools) — reusable after a fix. + incorrect = false as written or describes something that exists nowhere. unverifiable = needs a + live system or human intent. Verdicts are a machine first pass; treat incorrect as the triage queue, not gospel. +
+
+ + diff --git a/doc-audit/make_report.py b/doc-audit/make_report.py new file mode 100644 index 0000000..3e73ac6 --- /dev/null +++ b/doc-audit/make_report.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +"""Generate REPORT.md (GitHub-native drift report) from verdicts.jsonl. + +Self-contained: reads verdicts.jsonl next to this script, writes REPORT.md next +to it. Re-run after re-verifying to refresh the report. +""" +import html +import json +import os +from collections import Counter, defaultdict + +HERE = os.path.dirname(os.path.abspath(__file__)) +ARTIFACT = "https://claude.ai/code/artifact/78ab2dc6-03f4-4fa1-8f50-e8e0998726aa" + +rows = [json.loads(l) for l in open(os.path.join(HERE, "verdicts.jsonl"))] + + +def cell(s: str, limit: int = 200) -> str: + s = html.unescape(s or "").replace("\n", " ").strip() + if len(s) > limit: + s = s[: limit - 1] + "…" + return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("|", "\\|") + + +tot = Counter(r["verdict"] for r in rows) +N = len(rows) +pf = defaultdict(Counter) +kindof = {} +for r in rows: + pf[r["file"]][r["verdict"]] += 1 + kindof[r["file"]] = r.get("doc_kind", "?") +files = sorted(pf, key=lambda f: (pf[f]["incorrect"], pf[f]["misleading"], sum(pf[f].values())), reverse=True) + +L = [] +w = L.append +w("# Documentation Drift Audit") +w("") +w("First input to the documentation rework. Every published how-to / reference / tutorial doc was sliced into " + "**content atoms**, each atom's prose decomposed into individual factual **claims**, and every claim checked " + "against the real repositories. This report says, claim by claim, **what to reuse, what to fix, and what to bin.**") +w("") +w("## What this audit does and does not cover") +w("") +w("This covers exactly one axis of the rework: **is what is written true?** It deliberately does **not** cover:") +w("") +w("- **Coverage gaps** - system surface that is undocumented (commands, endpoints, env vars, resources that exist " + "but appear in no doc). A true claim can sit next to a large hole.") +w("- **Structure and consistency** - grouping, navigation, terminology, duplication across pages.") +w("") +w("Both are tracked as follow-up workstreams at the foot of this report. A claim being `true` means *accurate*, " + "not *sufficient*.") +w("") +w("## Results") +w("") +w("Corpus: **64** docs -> **2,560** content atoms. Verifiable subset (how-to / reference / tutorial): " + "**34** files, **1,506** atoms -> **960** factual claims. ADR and design docs are parked - they record decisions " + "at a point in time, so divergence means *superseded*, not *wrong*.") +w("") +w("| Verdict | Claims | Share | Meaning |") +w("|---|--:|--:|---|") +order = [("true", "reuse as-is - matches the code"), + ("misleading", "right fact, drifted context - fix in place"), + ("incorrect", "false as written - rewrite or bin"), + ("unverifiable", "needs a live system or human intent")] +for v, meaning in order: + w(f"| **{v}** | {tot[v]} | {tot[v]/N*100:.0f}% | {meaning} |") +w(f"| | **{N}** | | |") +w("") +w(f"**{tot['true']/N*100:.0f}% of documented claims are true.** The rewrite is worth doing for structure and " + "coverage, but most existing prose is salvageable - this report marks exactly which atoms to keep, so the rework " + "is a re-organise-and-fill exercise, not a blank page.") +w("") +w("### Dominant failure mode: repo-split drift, not error") +w("") +w("Most non-true claims are `misleading`, not `incorrect`. Developer tooling " + "(`scripts/k8s`, `tests/e2e`, `env-examples`, k8s manifests) was moved out of `smartem-decisions` into " + "`smartem-devtools`, so many documented commands and paths point at the wrong repository. The facts are right; " + "the surrounding instructions need re-homing.") +w("") +w("### Dead documentation (describes removed components)") +w("") +w("`docs/athena/index.md` and `docs/athena-decision-service-api-spec.README.md` document the `athena_api` package, " + "its mock server and generated client - all **deleted under ADR 0015**. These pages should be removed or rewritten, " + "not fixed.") +w("") +w("### Code / doc mismatches worth a code fix") +w("") +w("Because verification runs the commands and reads the source, it caught the *code* diverging from the *docs*:") +w("") +w("- Agent `validate` exits `0` on an invalid directory, though the docs promise `1` - the handler returns " + "`not is_valid` but Typer ignores command return values, so no non-zero exit is raised.") +w("- The documented `--log-file` flag is a no-op: it is declared but never wired to the watcher, so no log file is written.") +w("- `-v` / `--verbose` is inconsistent across agent subcommands - only `watch` accepts a repeatable `-v`; `parse` and " + "`validate` take a bare `--verbose INTEGER`, so documented `-v` / `-vv` examples error.") +w("") +w("## Interactive triage view") +w("") +w(f"An interactive, filterable version of this data is published as a Claude artifact: <{ARTIFACT}>") +w("") +w("> Note: that link renders only for the artifact owner's Claude account - GitHub sanitises embedded HTML/JS, so " + "this Markdown report is the public-visible equivalent. Open `doc-audit/drift-map.html` locally for the same view.") +w("") +w("## Files ranked by drift") +w("") +w("| File | Kind | Claims | Wrong | Drift | Reusable |") +w("|---|---|--:|--:|--:|--:|") +for f in files: + c = pf[f] + w(f"| `{f.replace('docs/','')}` | {kindof[f]} | {sum(c.values())} | {c['incorrect']} | {c['misleading']} | {c['true']} |") +w("") +w("## Claim-level detail") +w("") +w("Per file, the actionable claims (`incorrect` first, then `misleading`) with the evidence found. Files that are " + "fully `true` are omitted.") +w("") +sev = {"incorrect": 0, "misleading": 1} +by_file = defaultdict(list) +for r in rows: + if r["verdict"] in sev: + by_file[r["file"]].append(r) +for f in files: + items = by_file.get(f) + if not items: + continue + items.sort(key=lambda r: (sev[r["verdict"]], str(r.get("line", "")))) + c = pf[f] + w(f"
{f.replace('docs/','')} — {c['incorrect']} wrong, {c['misleading']} drift " + f"(of {sum(c.values())})") + w("") + w("| Verdict | Line | Claim | Evidence |") + w("|---|---|---|---|") + for r in items: + w(f"| {r['verdict']} | {cell(str(r.get('line','')),12)} | {cell(r.get('claim',''),160)} | {cell(r.get('evidence',''),200)} |") + w("") + w("
") + w("") +w("## Follow-up workstreams (the rest of the rework)") +w("") +w("1. **Fix drifted docs** - the 119 `misleading` claims, mostly path/command re-homing to `smartem-devtools`. " + "Near-mechanical; highest reuse-for-effort.") +w("2. **Remove or rewrite dead docs** - the athena pages and the 56 `incorrect` claims.") +w("3. **Coverage-gap analysis** - enumerate the actual system surface (agent CLI commands, backend OpenAPI " + "endpoints, env vars, k8s resources, frontend routes) and diff against what is documented, to find undocumented " + "surface. This audit cannot see those holes.") +w("4. **Restructure and consistency** - regroup by audience/task, unify terminology, dedupe. Use the `true` atoms " + "as reusable material.") +w("5. **File code bugs** - the validate exit-code, `--log-file` no-op, and verbose-flag mismatches above.") +w("") +w("## Reproduce") +w("") +w("```bash") +w("# 1. Re-slice the docs into content atoms (deterministic)") +w("python doc-audit/atomize.py . # writes atoms to the workspace tmp dir") +w("# 2. Re-verify claims against the repos (Claude multi-agent pass) -> verdicts.jsonl") +w("# 3. Regenerate this report") +w("python doc-audit/make_report.py") +w("```") +w("") + +open(os.path.join(HERE, "REPORT.md"), "w").write("\n".join(L)) +print("wrote REPORT.md:", len(L), "lines; verdicts:", dict(tot)) diff --git a/doc-audit/manifest.json b/doc-audit/manifest.json new file mode 100644 index 0000000..d2d17ae --- /dev/null +++ b/doc-audit/manifest.json @@ -0,0 +1,403 @@ +[ + { + "file": "docs/agent/authentication.md", + "slug": "agent-authentication", + "atoms_path": "tmp/doc-atoms/by-file/agent-authentication.json", + "verdict_path": "tmp/doc-atoms/verdicts/agent-authentication.json", + "n_atoms": 42, + "n_prose": 35, + "target_repos": [ + "smartem-decisions (src/smartem_agent)", + "smartem-devtools" + ] + }, + { + "file": "docs/agent/cli-reference.md", + "slug": "agent-cli-reference", + "atoms_path": "tmp/doc-atoms/by-file/agent-cli-reference.json", + "verdict_path": "tmp/doc-atoms/verdicts/agent-cli-reference.json", + "n_atoms": 129, + "n_prose": 81, + "target_repos": [ + "smartem-decisions (src/smartem_agent)", + "smartem-devtools" + ] + }, + { + "file": "docs/agent/deployment.md", + "slug": "agent-deployment", + "atoms_path": "tmp/doc-atoms/by-file/agent-deployment.json", + "verdict_path": "tmp/doc-atoms/verdicts/agent-deployment.json", + "n_atoms": 50, + "n_prose": 33, + "target_repos": [ + "smartem-decisions (src/smartem_agent)", + "smartem-devtools" + ] + }, + { + "file": "docs/agent/index.md", + "slug": "agent-index", + "atoms_path": "tmp/doc-atoms/by-file/agent-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/agent-index.json", + "n_atoms": 5, + "n_prose": 3, + "target_repos": [ + "smartem-decisions (src/smartem_agent)", + "smartem-devtools" + ] + }, + { + "file": "docs/agent/troubleshooting.md", + "slug": "agent-troubleshooting", + "atoms_path": "tmp/doc-atoms/by-file/agent-troubleshooting.json", + "verdict_path": "tmp/doc-atoms/verdicts/agent-troubleshooting.json", + "n_atoms": 107, + "n_prose": 57, + "target_repos": [ + "smartem-decisions (src/smartem_agent)", + "smartem-devtools" + ] + }, + { + "file": "docs/athena-decision-service-api-spec.README.md", + "slug": "athena-decision-service-api-spec-readme", + "atoms_path": "tmp/doc-atoms/by-file/athena-decision-service-api-spec-readme.json", + "verdict_path": "tmp/doc-atoms/verdicts/athena-decision-service-api-spec-readme.json", + "n_atoms": 12, + "n_prose": 10, + "target_repos": [ + "smartem-decisions (athena_api)" + ] + }, + { + "file": "docs/athena/index.md", + "slug": "athena-index", + "atoms_path": "tmp/doc-atoms/by-file/athena-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/athena-index.json", + "n_atoms": 6, + "n_prose": 5, + "target_repos": [ + "smartem-decisions (athena_api)" + ] + }, + { + "file": "docs/backend/api-documentation.md", + "slug": "backend-api-documentation", + "atoms_path": "tmp/doc-atoms/by-file/backend-api-documentation.json", + "verdict_path": "tmp/doc-atoms/verdicts/backend-api-documentation.json", + "n_atoms": 44, + "n_prose": 29, + "target_repos": [ + "smartem-decisions (src/smartem_backend)" + ] + }, + { + "file": "docs/backend/api-server.md", + "slug": "backend-api-server", + "atoms_path": "tmp/doc-atoms/by-file/backend-api-server.json", + "verdict_path": "tmp/doc-atoms/verdicts/backend-api-server.json", + "n_atoms": 2, + "n_prose": 1, + "target_repos": [ + "smartem-decisions (src/smartem_backend)" + ] + }, + { + "file": "docs/backend/database.md", + "slug": "backend-database", + "atoms_path": "tmp/doc-atoms/by-file/backend-database.json", + "verdict_path": "tmp/doc-atoms/verdicts/backend-database.json", + "n_atoms": 56, + "n_prose": 30, + "target_repos": [ + "smartem-decisions (src/smartem_backend)" + ] + }, + { + "file": "docs/backend/http-api-client.md", + "slug": "backend-http-api-client", + "atoms_path": "tmp/doc-atoms/by-file/backend-http-api-client.json", + "verdict_path": "tmp/doc-atoms/verdicts/backend-http-api-client.json", + "n_atoms": 35, + "n_prose": 17, + "target_repos": [ + "smartem-decisions (src/smartem_backend)" + ] + }, + { + "file": "docs/backend/index.md", + "slug": "backend-index", + "atoms_path": "tmp/doc-atoms/by-file/backend-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/backend-index.json", + "n_atoms": 5, + "n_prose": 3, + "target_repos": [ + "smartem-decisions (src/smartem_backend)" + ] + }, + { + "file": "docs/database-schema-drift-prevention.md", + "slug": "database-schema-drift-prevention", + "atoms_path": "tmp/doc-atoms/by-file/database-schema-drift-prevention.json", + "verdict_path": "tmp/doc-atoms/verdicts/database-schema-drift-prevention.json", + "n_atoms": 42, + "n_prose": 31, + "target_repos": [ + "smartem-decisions", + "smartem-devtools" + ] + }, + { + "file": "docs/development/contributing.md", + "slug": "development-contributing", + "atoms_path": "tmp/doc-atoms/by-file/development-contributing.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-contributing.json", + "n_atoms": 1, + "n_prose": 0, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/e2e-simulation.md", + "slug": "development-e2e-simulation", + "atoms_path": "tmp/doc-atoms/by-file/development-e2e-simulation.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-e2e-simulation.json", + "n_atoms": 111, + "n_prose": 72, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/generate-docs.md", + "slug": "development-generate-docs", + "atoms_path": "tmp/doc-atoms/by-file/development-generate-docs.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-generate-docs.json", + "n_atoms": 9, + "n_prose": 6, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/github-labels.md", + "slug": "development-github-labels", + "atoms_path": "tmp/doc-atoms/by-file/development-github-labels.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-github-labels.json", + "n_atoms": 50, + "n_prose": 33, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/index.md", + "slug": "development-index", + "atoms_path": "tmp/doc-atoms/by-file/development-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-index.json", + "n_atoms": 6, + "n_prose": 4, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/local-keycloak.md", + "slug": "development-local-keycloak", + "atoms_path": "tmp/doc-atoms/by-file/development-local-keycloak.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-local-keycloak.json", + "n_atoms": 39, + "n_prose": 32, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/development/tools.md", + "slug": "development-tools", + "atoms_path": "tmp/doc-atoms/by-file/development-tools.json", + "verdict_path": "tmp/doc-atoms/verdicts/development-tools.json", + "n_atoms": 47, + "n_prose": 24, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (tests/e2e, scripts, packages/smartem-epuplayer)" + ] + }, + { + "file": "docs/getting-started/for-developers.md", + "slug": "getting-started-for-developers", + "atoms_path": "tmp/doc-atoms/by-file/getting-started-for-developers.json", + "verdict_path": "tmp/doc-atoms/verdicts/getting-started-for-developers.json", + "n_atoms": 22, + "n_prose": 12, + "target_repos": [ + "smartem-decisions", + "smartem-devtools" + ] + }, + { + "file": "docs/getting-started/index.md", + "slug": "getting-started-index", + "atoms_path": "tmp/doc-atoms/by-file/getting-started-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/getting-started-index.json", + "n_atoms": 8, + "n_prose": 7, + "target_repos": [ + "smartem-decisions", + "smartem-devtools" + ] + }, + { + "file": "docs/glossary.md", + "slug": "glossary", + "atoms_path": "tmp/doc-atoms/by-file/glossary.json", + "verdict_path": "tmp/doc-atoms/verdicts/glossary.json", + "n_atoms": 9, + "n_prose": 8, + "target_repos": [ + "smartem-decisions", + "smartem-devtools" + ] + }, + { + "file": "docs/operations/container-user-configuration.md", + "slug": "operations-container-user-configuration", + "atoms_path": "tmp/doc-atoms/by-file/operations-container-user-configuration.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-container-user-configuration.json", + "n_atoms": 68, + "n_prose": 48, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/containerization.md", + "slug": "operations-containerization", + "atoms_path": "tmp/doc-atoms/by-file/operations-containerization.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-containerization.json", + "n_atoms": 18, + "n_prose": 9, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/environment-variables.md", + "slug": "operations-environment-variables", + "atoms_path": "tmp/doc-atoms/by-file/operations-environment-variables.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-environment-variables.json", + "n_atoms": 61, + "n_prose": 39, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/index.md", + "slug": "operations-index", + "atoms_path": "tmp/doc-atoms/by-file/operations-index.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-index.json", + "n_atoms": 7, + "n_prose": 5, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/kubernetes-secrets.md", + "slug": "operations-kubernetes-secrets", + "atoms_path": "tmp/doc-atoms/by-file/operations-kubernetes-secrets.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-kubernetes-secrets.json", + "n_atoms": 59, + "n_prose": 31, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/kubernetes.md", + "slug": "operations-kubernetes", + "atoms_path": "tmp/doc-atoms/by-file/operations-kubernetes.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-kubernetes.json", + "n_atoms": 16, + "n_prose": 12, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/logging.md", + "slug": "operations-logging", + "atoms_path": "tmp/doc-atoms/by-file/operations-logging.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-logging.json", + "n_atoms": 26, + "n_prose": 14, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/publish-smartem-workspace-to-pypi.md", + "slug": "operations-publish-smartem-workspace-to-pypi", + "atoms_path": "tmp/doc-atoms/by-file/operations-publish-smartem-workspace-to-pypi.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-publish-smartem-workspace-to-pypi.json", + "n_atoms": 167, + "n_prose": 120, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/releasing.md", + "slug": "operations-releasing", + "atoms_path": "tmp/doc-atoms/by-file/operations-releasing.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-releasing.json", + "n_atoms": 47, + "n_prose": 32, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/run-container.md", + "slug": "operations-run-container", + "atoms_path": "tmp/doc-atoms/by-file/operations-run-container.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-run-container.json", + "n_atoms": 12, + "n_prose": 7, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + }, + { + "file": "docs/operations/setup-smartem-workspace.md", + "slug": "operations-setup-smartem-workspace", + "atoms_path": "tmp/doc-atoms/by-file/operations-setup-smartem-workspace.json", + "verdict_path": "tmp/doc-atoms/verdicts/operations-setup-smartem-workspace.json", + "n_atoms": 188, + "n_prose": 128, + "target_repos": [ + "smartem-decisions", + "smartem-devtools (scripts/k8s, k8s/, env-examples, tests/e2e)" + ] + } +] \ No newline at end of file diff --git a/doc-audit/verdicts.jsonl b/doc-audit/verdicts.jsonl new file mode 100644 index 0000000..5d1f01f --- /dev/null +++ b/doc-audit/verdicts.jsonl @@ -0,0 +1,960 @@ +{"claim_id": "docs/agent/authentication.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM Agent authenticates to the backend using OAuth 2.0 Bearer tokens issued by Keycloak.", "oracle": [], "check": "read keycloak_client.py + api_client.py", "verdict": "true", "evidence": "keycloak_client.py fetches client_credentials token; api_client.py:264 sets 'Authorization: Bearer '", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "5", "claim": "ADR 0018 exists at ../decision-records/decisions/0018-smartem-agent-keycloak-auth.md.", "oracle": ["source-path"], "check": "ls in smartem-devtools/docs", "verdict": "true", "evidence": "docs/decision-records/decisions/0018-smartem-agent-keycloak-auth.md exists; title '18. Authenticate SmartEM Agent ... Keycloak client credentials'", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9", "claim": "The agent uses the OAuth 2.0 client_credentials grant, presenting a confidential client ID and secret.", "oracle": [], "check": "read keycloak_client._fetch_token", "verdict": "true", "evidence": "keycloak_client.py:125 data grant_type=client_credentials with client_id+client_secret", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a002::c2", "atom_id": "a002", "type": "prose", "line": "9", "claim": "The frontend uses the OIDC authorisation-code flow, which does not apply to the headless agent.", "oracle": [], "check": "read mock realm SmartEM_User client", "verdict": "true", "evidence": "dls-realm.json SmartEM_User publicClient=true, standardFlowEnabled=true, directAccessGrants=false", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a002::c3", "atom_id": "a002", "type": "prose", "line": "9", "claim": "The token is attached to every request the agent makes to the backend.", "oracle": [], "check": "read api_client _send_with_auth/_auth_headers", "verdict": "true", "evidence": "api_client.py:263-264 and :727-728 attach Bearer header on every request/SSE call when keycloak_client set", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a003::c1", "atom_id": "a003", "type": "code", "line": "11-23", "claim": "Diagram: agent client_credentials->Keycloak->RS256 JWT access token; backend validates token against Keycloak JWKS.", "oracle": [], "check": "read auth.py JWKS/RS256", "verdict": "true", "evidence": "auth.py uses PyJWKClient (jwks certs url) and jwt.decode algorithms=['RS256'] (auth.py:67,102)", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "25", "claim": "The token is cached in memory and refreshed shortly before its expiry.", "oracle": [], "check": "read keycloak_client.get_token", "verdict": "true", "evidence": "keycloak_client.py:103 returns cached _token until now+refresh_buffer >= expiry, else refetches", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "25", "claim": "On HTTP 401 the agent invalidates its cache, fetches a fresh token, and retries the request once.", "oracle": [], "check": "read api_client._send_with_auth", "verdict": "true", "evidence": "api_client.py:268-272 on 401: invalidate(), re-fetch, single retry, then return", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a004::c3", "atom_id": "a004", "type": "prose", "line": "25", "claim": "Long-running SSE connections spanning expiry disconnect and reconnect with a fresh token, handled transparently.", "oracle": [], "check": "read SSE _auth_headers + auto-retry loop", "verdict": "true", "evidence": "api_client.py:727 _auth_headers re-calls get_token per attempt; stream auto-retry with backoff reconnects", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "27", "claim": "The Keycloak client used by the agent is SmartEM_Agent.", "oracle": [], "check": "grep mock realm + docs", "verdict": "true", "evidence": "dls-realm.json clientId 'SmartEM_Agent'; a032 tests + config docs use SmartEM_Agent", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a005::c2", "atom_id": "a005", "type": "prose", "line": "27", "claim": "The browser-facing client used by the frontend is SmartEM_User.", "oracle": [], "check": "grep mock realm", "verdict": "true", "evidence": "dls-realm.json clientId 'SmartEM_User' name 'SmartEM Frontend'", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a005::c3", "atom_id": "a005", "type": "prose", "line": "27", "claim": "The backend distinguishes the two clients via the azp (authorised party) claim.", "oracle": [], "check": "read auth.py azp allow-list", "verdict": "true", "evidence": "auth.py:117-122 checks claims['azp'] against _allowed_azps() allow-list", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "31", "claim": "The agent requires four values to authenticate.", "oracle": [], "check": "read KeycloakConfig + required list", "verdict": "true", "evidence": "keycloak_client.py KeycloakConfig has 4 fields; required=['KEYCLOAK_URL','KEYCLOAK_REALM','KEYCLOAK_CLIENT_ID','KEYCLOAK_CLIENT_SECRET']", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a007::c1", "atom_id": "a007", "type": "table", "line": "33-38", "claim": "Config variables are KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET with the stated meanings.", "oracle": ["url"], "check": "read load_keycloak_config required list", "verdict": "true", "evidence": "keycloak_client.py:60 required list matches all four names exactly", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "42", "claim": "The agent reads config from a dotenv-style file, defaulting alongside the executable, overridable via --config.", "oracle": [], "check": "read __main__ _default_keycloak_config_path + --config", "verdict": "true", "evidence": "__main__.py:22-31 default agent.env beside frozen exe (else cwd); watch has --config option (:185)", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a009::c1", "atom_id": "a009", "type": "code", "line": "44-46", "claim": "The command 'python -m smartem_agent watch --config ' is valid.", "oracle": ["cli-cmd"], "check": "read watch command signature", "verdict": "true", "evidence": "__main__.py watch command takes positional path:Path and --config option; module entrypoint runs epu_data_intake_cli()", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a010::c1", "atom_id": "a010", "type": "prose", "line": "48", "claim": "File format is one variable per line in KEY=VALUE form with no quoting.", "oracle": [], "check": "read load_keycloak_config parser", "verdict": "true", "evidence": "keycloak_client.py:51-58 splits lines on first '=', strips whitespace, does not interpret quoting", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a011::c1", "atom_id": "a011", "type": "code", "line": "50-55", "claim": "Example env file uses KEYCLOAK_URL/REALM/CLIENT_ID/CLIENT_SECRET matching the parser's required keys.", "oracle": ["env-var", "url"], "check": "compare example to required list", "verdict": "true", "evidence": "example keys equal keycloak_client.py:60 required list; CLIENT_ID=SmartEM_Agent matches mock realm", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a012::c1", "atom_id": "a012", "type": "prose", "line": "57", "claim": "If any of the four values is missing or empty the agent exits non-zero at startup with a clear error; no unauthenticated fallback.", "oracle": [], "check": "read load_keycloak_config + watch error path", "verdict": "true", "evidence": "keycloak_client.py:61-65 raises ValueError on missing/empty; __main__.py:232-234 catches and raises typer.Exit(1)", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "69", "claim": "Access tokens default to a 5-minute lifespan.", "oracle": [], "check": "read _fetch_token expires_in default", "verdict": "true", "evidence": "keycloak_client.py:139 expires_in default 300 seconds (=5 min)", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a016::c2", "atom_id": "a016", "type": "prose", "line": "69", "claim": "Tokens are fetched lazily on first use, cached in memory, refreshed within ~30s of expiry; 401 forces invalidate+refetch+retry once, a second 401 is surfaced.", "oracle": [], "check": "read get_token buffer + api_client retry", "verdict": "true", "evidence": "keycloak_client.py refresh_buffer_seconds default 30 (:81), get_token lazy/cached; api_client.py:268-274 single retry, second 401 returned", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a017::c1", "atom_id": "a017", "type": "prose", "line": "71", "claim": "The client_credentials grant issues no refresh tokens; each refresh is a full re-request to the token endpoint.", "oracle": [], "check": "read _fetch_token + docstring", "verdict": "true", "evidence": "keycloak_client.py:8-9 docstring + _fetch_token POSTs full credentials each time; no refresh_token used", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "81", "claim": "The agent does not re-read its config file on signal; a restart is required to pick up a new secret.", "oracle": [], "check": "read __main__ signal handlers + config load", "verdict": "true", "evidence": "load_keycloak_config called once at startup (:231); only SIGINT/SIGBREAK handlers, both -> handle_exit; no reload", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "95-97", "claim": "A 'Token fetched' log records the absolute expiry time (ISO 8601) and the local system time at fetch.", "oracle": [], "check": "read _fetch_token logging", "verdict": "true", "evidence": "keycloak_client.py:147-154 logs 'token fetched' with expires_at ISO and system_time_now ISO", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a025::c2", "atom_id": "a025", "type": "prose", "line": "95-97", "claim": "A distinct 'Token refreshed' log entry is emitted on proactive refresh or 401-forced refresh.", "oracle": [], "check": "grep log statements in client/api_client", "verdict": "misleading", "evidence": "No log labelled 'Token refreshed'; proactive refresh re-emits the 'token fetched' info line; 401 emits warning 'Received 401 ... refreshing token' (api_client.py:269)", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a025::c3", "atom_id": "a025", "type": "prose", "line": "95-97", "claim": "An 'Authentication failure' log is emitted when token fetch fails or a request keeps 401ing after a refresh.", "oracle": [], "check": "grep log statements + _send_with_auth", "verdict": "misleading", "evidence": "Token-fetch failure logged (keycloak_client.py:133 'token fetch failed'); but persistent 401 after refresh is NOT explicitly logged - _send_with_auth just returns the response", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "99", "claim": "Tokens themselves are never written to logs.", "oracle": [], "check": "read all log statements in keycloak_client/api_client", "verdict": "true", "evidence": "keycloak_client logs only client_id, expiry, times; no log statement includes access_token value", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a027::c1", "atom_id": "a027", "type": "prose", "line": "103", "claim": "The Keycloak mock at smartem-devtools/keycloak-mock/ ships a SmartEM_Agent client whose secret is hard-coded dev-agent-secret.", "oracle": [], "check": "read keycloak-mock/dls-realm.json", "verdict": "true", "evidence": "keycloak-mock/dls-realm.json:67 clientId SmartEM_Agent, :73 secret 'dev-agent-secret'", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a029::c1", "atom_id": "a029", "type": "prose", "line": "107-108", "claim": "./scripts/k8s/dev-k8s.sh up brings up the local k3s cluster including the Keycloak mock.", "oracle": ["source-path", "k8s"], "check": "read dev-k8s.sh commands + keycloak-mock kustomization", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/dev-k8s.sh has 'up' subcommand (:608) and prints 'Keycloak (mock): http://localhost:30090' (:535); keycloak-mock kustomization present", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a030::c1", "atom_id": "a030", "type": "code", "line": "110-115", "claim": "Mock config: KEYCLOAK_URL=http://localhost:30090, REALM=dls, CLIENT_ID=SmartEM_Agent, SECRET=dev-agent-secret.", "oracle": ["env-var", "port", "url"], "check": "read keycloak.yaml nodePort + dls-realm.json", "verdict": "true", "evidence": "keycloak-mock/keycloak.yaml:101 nodePort 30090; dls-realm.json realm 'dls', client SmartEM_Agent, secret dev-agent-secret", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "117", "claim": "The agent is run with --config pointing at the config file.", "oracle": [], "check": "read watch --config option", "verdict": "true", "evidence": "__main__.py:185-193 watch defines --config option loading the Keycloak config", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a032::c1", "atom_id": "a032", "type": "prose", "line": "119", "claim": "Tests exercise successful token fetch, proactive refresh near expiry, 401-triggered refresh+retry, network failure to Keycloak, and repeated 401 after refresh.", "oracle": [], "check": "grep def test in auth test files", "verdict": "true", "evidence": "test_keycloak_client.py (get_token_posts_client_credentials_grant, refresh_when_within_buffer_of_expiry, network_error_surfaces) + test_api_client_auth.py (401_triggers_invalidate_and_retry_once, second_401_after_refresh_surfaces)", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "125", "claim": "A startup Keycloak configuration error means one or more of the four required values could not be read.", "oracle": [], "check": "read load_keycloak_config error paths", "verdict": "true", "evidence": "keycloak_client.py raises FileNotFoundError (missing file) / ValueError (missing/empty of the 4); __main__ maps to exit-1", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "131-135", "claim": "The backend allows a small leeway on token expiry; clocks more than a minute off will see tokens rejected as expired.", "oracle": [], "check": "read auth.py leeway", "verdict": "true", "evidence": "auth.py:27 JWT_LEEWAY_SECONDS=60 passed as jwt.decode leeway (:104); >60s skew -> ExpiredSignatureError", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a036::c2", "atom_id": "a036", "type": "prose", "line": "131-135", "claim": "If the backend azp allow-list excludes SmartEM_Agent, otherwise-valid tokens are rejected.", "oracle": [], "check": "read auth.py azp check", "verdict": "true", "evidence": "auth.py:117-122 rejects tokens whose azp not in allow-list after signature validation", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a036::c3", "atom_id": "a036", "type": "prose", "line": "131-135", "claim": "The agent's KEYCLOAK_REALM must match the realm the backend trusts, else tokens are rejected.", "oracle": [], "check": "read auth.py issuer/realm verification", "verdict": "true", "evidence": "auth.py verifies issuer from KEYCLOAK_URL/KEYCLOAK_REALM (:44,:100-107); token from a different realm fails issuer/JWKS check", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a038::c1", "atom_id": "a038", "type": "prose", "line": "141-142", "claim": "A secret rotated in Keycloak but not updated in the config file causes 401s until the file is updated and the agent restarted.", "oracle": [], "check": "infer from no-reload config load + 401 handling", "verdict": "true", "evidence": "config loaded once at startup (no signal reload, a020); revoked old secret -> Keycloak token fetch / backend 401 until restart with new secret", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "146", "claim": "If token fetch fails with a network error to KEYCLOAK_URL, it is surfaced (agent cannot reach Keycloak).", "oracle": [], "check": "read _fetch_token exception handling", "verdict": "true", "evidence": "keycloak_client.py:132-134 catches requests.RequestException, logs 'token fetch failed', re-raises", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a040::c1", "atom_id": "a040", "type": "prose", "line": "150", "claim": "The default Keycloak access-token lifespan is 5 minutes, so SSE reconnects near that interval indicate correct token rotation.", "oracle": [], "check": "read expires_in default + SSE re-auth", "verdict": "true", "evidence": "keycloak_client.py:139 default 300s; SSE _auth_headers re-fetches token per reconnect attempt", "confidence": "medium", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/authentication.md::a041::c1", "atom_id": "a041", "type": "prose", "line": "154-158", "claim": "Related-doc links resolve: ADR 0018, cli-reference.md, deployment.md, troubleshooting.md, ../operations/environment-variables.md.", "oracle": ["source-path"], "check": "ls docs targets", "verdict": "true", "evidence": "All present: docs/agent/{cli-reference,deployment,troubleshooting}.md, docs/operations/environment-variables.md, decision-records/decisions/0018-*.md", "confidence": "high", "file": "docs/agent/authentication.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM Agent provides a CLI for parsing, validating, and monitoring EPU data.", "oracle": "cli-cmd", "check": "ran python -m smartem_agent --help", "verdict": "true", "evidence": "__main__.py exposes parse/validate/watch via typer.Typer 'EPU Data Intake Tools'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a001::c1", "atom_id": "a001", "type": "code", "line": "7-9", "claim": "CLI is invoked as `python -m smartem_agent [COMMAND] [OPTIONS]`.", "oracle": "cli-cmd", "check": "ran --help; help shows this usage", "verdict": "true", "evidence": "help: 'Usage: python -m smartem_agent [OPTIONS] COMMAND [ARGS]...'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "13-15", "claim": "CLI has three command groups: parse, validate, watch.", "oracle": "", "check": "ran --help Commands section", "verdict": "true", "evidence": "help Commands: validate, watch, parse (parse is a sub-Typer)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "19", "claim": "The verbosity (-v) options are supported by the watch command.", "oracle": "", "check": "read __main__.py; ran parse dir -v", "verdict": "true", "evidence": "only watch declares typer.Option(0,'-v','--verbose',count=True); parse/validate reject -v", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a005::c1", "atom_id": "a005", "type": "table", "line": "21-23", "claim": "watch has --verbose/-v, count type, default 0.", "oracle": "", "check": "ran watch --help", "verdict": "true", "evidence": "watch --help shows '--verbose -v INTEGER [default: 0]'; code count=True", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "25-28", "claim": "Verbosity levels: 0=ERROR, 1(-v)=INFO, 2(-vv)=DEBUG.", "oracle": "", "check": "read __main__.py lines 201-206", "verdict": "true", "evidence": "code: verbose>=2 DEBUG, ==1 INFO, else ERROR", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "30", "claim": "The -v/--verbose flag only works with watch; parse/validate use default logging.", "oracle": "cli-cmd", "check": "ran parse dir -v and validate -v", "verdict": "true", "evidence": "parse/validate error 'No such option: -v'; only watch wires verbose to logging.basicConfig", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "34", "claim": "Parse commands process EPU files without persisting to the backend API.", "oracle": "", "check": "read __main__.py parse handlers", "verdict": "true", "evidence": "parse cmds use InMemoryDataStore; code comment 'parsers don't persist data to API by design - only watch'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a009::c1", "atom_id": "a009", "type": "prose", "line": "38", "claim": "`parse dir` parses an entire EPU output directory that may contain multiple grids.", "oracle": "", "check": "read parse_epu_output_dir docstring", "verdict": "true", "evidence": "docstring: 'Parse an entire EPU output directory structure. May contain multiple grids'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a010::c1", "atom_id": "a010", "type": "code", "line": "40-42", "claim": "Usage `parse dir [OPTIONS] EPU_OUTPUT_DIR`.", "oracle": "cli-cmd", "check": "read handler + ran parse --help", "verdict": "true", "evidence": "parse_epu_output_dir(epu_output_dir: str); subcommand 'dir' registered", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a012::c1", "atom_id": "a012", "type": "table", "line": "46-48", "claim": "`epu_output_dir` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "epu_output_dir: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a014::c1", "atom_id": "a014", "type": "code", "line": "51-53", "claim": "Example `parse dir -v` runs the command with verbose flag.", "oracle": "cli-cmd", "check": "ran parse dir /nonexistent -v", "verdict": "misleading", "evidence": "'-v' not defined on parse: errors 'No such option: -v' exit 2; base command valid without -v", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "62", "claim": "`parse grid` parses a single grid data directory.", "oracle": "", "check": "read parse_grid handler", "verdict": "true", "evidence": "parse_grid(grid_data_dir) validates+parses a single grid dir", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a017::c1", "atom_id": "a017", "type": "code", "line": "64-66", "claim": "Usage `parse grid [OPTIONS] GRID_DATA_DIR`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "parse_grid(grid_data_dir: str); subcommand 'grid' present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a019::c1", "atom_id": "a019", "type": "table", "line": "70-72", "claim": "`grid_data_dir` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "grid_data_dir: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a021::c1", "atom_id": "a021", "type": "code", "line": "75-77", "claim": "Example `parse grid -vv` runs with debug verbosity.", "oracle": "cli-cmd", "check": "parse rejects short -v (verified on parse dir)", "verdict": "misleading", "evidence": "parse subcommands define bare 'verbose:int=0' (no -v short); -vv errors 'No such option'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a023::c1", "atom_id": "a023", "type": "prose", "line": "86", "claim": "`parse session` parses an EPU session manifest (typically EpuSession.dm).", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_epu_session_manifest(path); EpuSession.dm is a DEFAULT_PATTERNS entry", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a024::c1", "atom_id": "a024", "type": "code", "line": "88-90", "claim": "Usage `parse session [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "parse_epu_session(path: str); subcommand 'session' present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a026::c1", "atom_id": "a026", "type": "table", "line": "94-96", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a028::c1", "atom_id": "a028", "type": "code", "line": "99-101", "claim": "Example `parse session .../EpuSession.dm` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid: positional path, no invalid flags", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a029::c1", "atom_id": "a029", "type": "prose", "line": "103", "claim": "parse session outputs session metadata (acquisition params, timestamps, instrument config).", "oracle": "", "check": "read handler (logs parsed data)", "verdict": "true", "evidence": "parse_epu_session logs epu_session_data via logging.info", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "107", "claim": "`parse atlas` parses an atlas manifest (typically Atlas/Atlas.dm).", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_atlas_manifest(path,...); pattern Sample*/Atlas/Atlas.dm present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a031::c1", "atom_id": "a031", "type": "code", "line": "109-111", "claim": "Usage `parse atlas [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "parse_atlas(path: str); subcommand 'atlas' present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a033::c1", "atom_id": "a033", "type": "table", "line": "115-117", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a035::c1", "atom_id": "a035", "type": "code", "line": "120-122", "claim": "Example `parse atlas .../Sample*/Atlas/Atlas.dm` is valid.", "oracle": "cli-cmd", "check": "read handler + patterns", "verdict": "true", "evidence": "valid usage; path matches Sample*/Atlas/Atlas.dm pattern", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "124", "claim": "parse atlas outputs atlas info (grid square positions, tile arrangements, overview metadata).", "oracle": "", "check": "read handler (logs parsed data)", "verdict": "true", "evidence": "parse_atlas logs atlas_data via logging.info", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a037::c1", "atom_id": "a037", "type": "prose", "line": "128", "claim": "`parse gridsquare-metadata` parses Metadata/GridSquare_*.dm files.", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_gridsquare_metadata(path); pattern Metadata/GridSquare_*.dm present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a038::c1", "atom_id": "a038", "type": "code", "line": "130-132", "claim": "Usage `parse gridsquare-metadata [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "subcommand 'gridsquare-metadata' present; path: str", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a040::c1", "atom_id": "a040", "type": "table", "line": "136-138", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a042::c1", "atom_id": "a042", "type": "code", "line": "141-143", "claim": "Example `parse gridsquare-metadata .../Metadata/GridSquare_12345.dm` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage; matches metadata pattern", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a043::c1", "atom_id": "a043", "type": "prose", "line": "145", "claim": "Output includes foil hole positions and targeting info.", "oracle": "", "check": "read handler (logs parsed metadata)", "verdict": "true", "evidence": "parse_gridsquare_metadata logs metadata via logging.info", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a044::c1", "atom_id": "a044", "type": "prose", "line": "149", "claim": "`parse gridsquare` parses GridSquare_*.xml manifest files.", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_gridsquare_manifest(path); pattern Images-Disc*/GridSquare_*/GridSquare_*_*.xml present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a045::c1", "atom_id": "a045", "type": "code", "line": "151-153", "claim": "Usage `parse gridsquare [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "subcommand 'gridsquare' present; path: str", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a047::c1", "atom_id": "a047", "type": "table", "line": "157-159", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a049::c1", "atom_id": "a049", "type": "code", "line": "162-164", "claim": "Example `parse gridsquare .../GridSquare_20240115_143022.xml` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a050::c1", "atom_id": "a050", "type": "prose", "line": "166", "claim": "Output includes grid square acquisition details and image collection params.", "oracle": "", "check": "read handler (logs parsed data)", "verdict": "true", "evidence": "parse_gridsquare logs gridsquare_manifest_data", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a051::c1", "atom_id": "a051", "type": "prose", "line": "170", "claim": "`parse foilhole` parses Data/FoilHole_*.xml manifest files.", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_foilhole_manifest(path); pattern Data/FoilHole_*_Data_*_*_*_*.xml present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a052::c1", "atom_id": "a052", "type": "code", "line": "172-174", "claim": "Usage `parse foilhole [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "subcommand 'foilhole' present; path: str", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a054::c1", "atom_id": "a054", "type": "table", "line": "178-180", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a056::c1", "atom_id": "a056", "type": "code", "line": "183-185", "claim": "Example `parse foilhole .../FoilHole_..._fractions.xml` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage; matches foilhole data pattern", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a057::c1", "atom_id": "a057", "type": "prose", "line": "187", "claim": "Output includes foil hole targeting info and acquisition metadata.", "oracle": "", "check": "read handler (logs parsed data)", "verdict": "true", "evidence": "parse_foilhole logs foilhole_data", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a058::c1", "atom_id": "a058", "type": "prose", "line": "191", "claim": "`parse micrograph` parses FoilHoles/FoilHole_*.xml manifest files.", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "parse_micrograph_manifest(path); pattern FoilHoles/FoilHole_*_*_*.xml present", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a059::c1", "atom_id": "a059", "type": "code", "line": "193-195", "claim": "Usage `parse micrograph [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "read handler + parse --help", "verdict": "true", "evidence": "subcommand 'micrograph' present; path: str", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a061::c1", "atom_id": "a061", "type": "table", "line": "199-201", "claim": "`path` is a required str parameter.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a063::c1", "atom_id": "a063", "type": "code", "line": "204-206", "claim": "Example `parse micrograph .../FoilHole_1234567_20240115_143045.xml` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a064::c1", "atom_id": "a064", "type": "prose", "line": "208", "claim": "Output includes micrograph metadata (exposure params, image quality metrics).", "oracle": "", "check": "read handler (logs parsed data)", "verdict": "true", "evidence": "parse_micrograph logs micrograph_data", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a065::c1", "atom_id": "a065", "type": "prose", "line": "212", "claim": "validate checks EPU project directory structure for completeness/compliance.", "oracle": "", "check": "read validate_epu_dir handler", "verdict": "true", "evidence": "validate calls EpuParser.validate_project_dir; docstring 'Validate the structure of an EPU project directory'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a066::c1", "atom_id": "a066", "type": "code", "line": "214-216", "claim": "Usage `validate [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "ran --help; read handler", "verdict": "true", "evidence": "validate command registered on root; validate_epu_dir(path: str)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a068::c1", "atom_id": "a068", "type": "table", "line": "220-222", "claim": "`path` is a required str parameter for validate.", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: str positional (required)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a069::c1", "atom_id": "a069", "type": "prose", "line": "224-226", "claim": "validate exits 0 when directory structure is valid.", "oracle": "", "check": "read handler; valid path returns not True=False", "verdict": "true", "evidence": "handler returns 'not is_valid'; valid -> falsy; process exits 0", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a069::c2", "atom_id": "a069", "type": "prose", "line": "224-226", "claim": "validate exits 1 when directory structure is invalid.", "oracle": "", "check": "ran validate on empty (invalid) dir; checked $?", "verdict": "incorrect", "evidence": "validate of invalid dir returned exit=0 (Typer ignores 'return not is_valid'; no typer.Exit raised)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a071::c1", "atom_id": "a071", "type": "code", "line": "229-231", "claim": "Example `validate -v` runs validate with verbose.", "oracle": "cli-cmd", "check": "ran validate -v", "verdict": "misleading", "evidence": "validate rejects '-v': 'No such option: -v' exit 2; base command valid without -v", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a072::c1", "atom_id": "a072", "type": "prose", "line": "233-237", "claim": "validate checks required session files, structure, naming, metadata accessibility.", "oracle": "", "check": "handler delegates to validate_project_dir", "verdict": "true", "evidence": "validate_project_dir returns (is_valid, errors) over project structure", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a073::c1", "atom_id": "a073", "type": "prose", "line": "239-241", "claim": "validate prints a confirmation for valid structure, else a list of issues.", "oracle": "", "check": "read handler branches", "verdict": "true", "evidence": "logs 'EPU project dir is structurally valid' else 'Invalid EPU project dir...' + errors", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a074::c1", "atom_id": "a074", "type": "prose", "line": "245", "claim": "watch provides real-time monitoring, auto-processing new files, communicating with backend.", "oracle": "", "check": "read watch_directory handler", "verdict": "true", "evidence": "uses watchdog Observer(recursive) + SmartEMWatcherV2 + APIClient", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a075::c1", "atom_id": "a075", "type": "code", "line": "247-249", "claim": "Usage `watch [OPTIONS] PATH`.", "oracle": "cli-cmd", "check": "ran watch --help", "verdict": "true", "evidence": "watch_directory(path: Path); watch command registered", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a076::c1", "atom_id": "a076", "type": "table", "line": "253-265", "claim": "watch params/defaults: path, --dry-run F, --api-url 127.0.0.1:8000, --log-file fs_changes.log, --log-interval 10.0, --agent-id, --session-id, --sse-timeout 30, --heartbeat-interval 60, --config agent.env, --verbose 0.", "oracle": "port,source-path,url", "check": "ran watch --help; read handler", "verdict": "true", "evidence": "watch --help shows every option with matching defaults; matches typer.Option decls", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a078::c1", "atom_id": "a078", "type": "prose", "line": "270-271", "claim": "path is a required Path (root dir to monitor).", "oracle": "", "check": "read handler signature", "verdict": "true", "evidence": "path: Path positional (required); Path(path).absolute() then .exists() check", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a080::c1", "atom_id": "a080", "type": "code", "line": "274-276", "claim": "Example `watch /data/microscopy/active_session` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage; positional path only", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a081::c1", "atom_id": "a081", "type": "prose", "line": "279-281", "claim": "--dry-run is a boolean flag, default False, runs without backend API calls.", "oracle": "", "check": "read handler; ran watch --help", "verdict": "true", "evidence": "dry_run: bool=False; 'if not dry_run:' guards all API calls", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a083::c1", "atom_id": "a083", "type": "code", "line": "284-286", "claim": "Example `watch /data/test --dry-run` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "--dry-run is a valid flag", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a084::c1", "atom_id": "a084", "type": "prose", "line": "288-292", "claim": "In dry-run, monitoring/parsing run but no data is sent to backend; in-memory storage only.", "oracle": "", "check": "read handler + watcher init", "verdict": "true", "evidence": "dry_run skips APIClient; watcher.datastore is InMemoryDataStore regardless", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a086::c1", "atom_id": "a086", "type": "prose", "line": "297-299", "claim": "--api-url is a string defaulting to http://127.0.0.1:8000.", "oracle": "port,url", "check": "read handler; ran watch --help", "verdict": "true", "evidence": "api_url: str = 'http://127.0.0.1:8000'; help default matches", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a088::c1", "atom_id": "a088", "type": "code", "line": "302-308", "claim": "--api-url accepts local (localhost:8000) and remote HTTPS URLs.", "oracle": "port,url,cli-cmd", "check": "read handler (arbitrary str)", "verdict": "true", "evidence": "api_url is a free-form str passed to APIClient; examples are illustrative", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a089::c1", "atom_id": "a089", "type": "prose", "line": "310", "claim": "Agent validates API connectivity at startup and exits code 1 if unreachable, unless --dry-run.", "oracle": "", "check": "read handler lines 228-243", "verdict": "true", "evidence": "if not dry_run: get_status() in try; on Exception 'raise typer.Exit(1)'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a090::c1", "atom_id": "a090", "type": "prose", "line": "313-314", "claim": "--agent-id is optional; required for real-time SSE communication.", "oracle": "", "check": "read handler", "verdict": "true", "evidence": "agent_id default None; remote log/SSE gated by 'if agent_id and session_id'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a092::c1", "atom_id": "a092", "type": "prose", "line": "322-323", "claim": "--session-id is optional; required for real-time communication.", "oracle": "", "check": "read handler", "verdict": "true", "evidence": "session_id default None; SSE/log handler gated by 'if agent_id and session_id'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a095::c1", "atom_id": "a095", "type": "prose", "line": "330-332", "claim": "--sse-timeout is an integer defaulting to 30 seconds.", "oracle": "", "check": "read handler; ran watch --help", "verdict": "true", "evidence": "sse_timeout: int = typer.Option(30, ...); help default 30", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a097::c1", "atom_id": "a097", "type": "prose", "line": "340-342", "claim": "--heartbeat-interval is an integer default 60; 0 disables heartbeats.", "oracle": "", "check": "read handler; ran watch --help", "verdict": "true", "evidence": "heartbeat_interval typer.Option(60, help '...0 to disable'); help default 60", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a098::c1", "atom_id": "a098", "type": "prose", "line": "344", "claim": "Backend considers connections stale after 2 minutes without heartbeats.", "oracle": "", "check": "grep agent_connection_manager.py", "verdict": "true", "evidence": "agent_connection_manager.py:100 stale_threshold=now-timedelta(minutes=2); comment '2 minutes'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a100::c1", "atom_id": "a100", "type": "code", "line": "347-356", "claim": "heartbeat-interval examples (default, 30, 0-to-disable) are valid.", "oracle": "cli-cmd", "check": "read handler options", "verdict": "true", "evidence": "--agent-id/--session-id/--heartbeat-interval all valid watch options", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a102::c1", "atom_id": "a102", "type": "prose", "line": "361-363", "claim": "--config is optional Keycloak dotenv; default agent.env alongside the executable.", "oracle": "", "check": "read handler + _default_keycloak_config_path", "verdict": "true", "evidence": "config default None -> _default_keycloak_config_path(): frozen -> sys.executable.parent/'agent.env'", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a103::c1", "atom_id": "a103", "type": "prose", "line": "365", "claim": "Agent presents a Keycloak Bearer token on each backend request; missing/invalid config exits at startup.", "oracle": "source-path", "check": "read handler + api_client _auth_headers", "verdict": "true", "evidence": "load_keycloak_config err -> typer.Exit(1); api_client sets 'Authorization: Bearer '+get_token()", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a105::c1", "atom_id": "a105", "type": "code", "line": "368-374", "claim": "--config examples (default and custom path) are valid.", "oracle": "cli-cmd", "check": "read handler option", "verdict": "true", "evidence": "--config is a valid PATH option; omitting uses default agent.env", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a107::c1", "atom_id": "a107", "type": "prose", "line": "379-381", "claim": "--log-file is a string defaulting to fs_changes.log.", "oracle": "", "check": "read handler; ran watch --help", "verdict": "true", "evidence": "log_file: str = 'fs_changes.log'; help default matches", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a107::c2", "atom_id": "a107", "type": "prose", "line": "379-381", "claim": "The agent writes structured JSON logs of all detected file changes to the --log-file path.", "oracle": "", "check": "grep log_file usage across agent; read watcher ctor", "verdict": "misleading", "evidence": "log_file only appears at __main__.py:177; never passed to SmartEMWatcherV2 or used to write a file", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a108::c1", "atom_id": "a108", "type": "prose", "line": "383", "claim": "The log file records file creation/modification events, timestamps, sizes, processing status.", "oracle": "", "check": "grep log_file usage; watcher has no file writer", "verdict": "misleading", "evidence": "describes the fs_changes.log file, but --log-file is unwired; no such file is written by the agent", "confidence": "medium", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a109::c1", "atom_id": "a109", "type": "prose", "line": "386-388", "claim": "--log-interval is a float default 10.0; batching interval for change logging.", "oracle": "", "check": "read handler + fs_watcher", "verdict": "true", "evidence": "log_interval: float=10.0 passed to watcher; used at fs_watcher last_log_time>=log_interval", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a112::c1", "atom_id": "a112", "type": "code", "line": "398-401", "claim": "Example `watch /data/microscopy/Grid_001` is valid.", "oracle": "cli-cmd", "check": "read handler", "verdict": "true", "evidence": "valid usage; positional path only", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a113::c1", "atom_id": "a113", "type": "code", "line": "404-411", "claim": "Production example using --api-url/--agent-id/--session-id/--heartbeat-interval/--verbose is valid.", "oracle": "url,cli-cmd", "check": "ran watch --help (all options exist)", "verdict": "true", "evidence": "all five options are declared on watch", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a114::c1", "atom_id": "a114", "type": "code", "line": "414-420", "claim": "Dev example using --dry-run/--log-interval/--verbose --verbose is valid.", "oracle": "cli-cmd", "check": "read handler; verbose count", "verdict": "true", "evidence": "--verbose count=True accepts repetition (--verbose --verbose = -vv)", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a115::c1", "atom_id": "a115", "type": "code", "line": "423-431", "claim": "High-perf example using --api-url/--agent-id/--session-id/--log-interval/--sse-timeout/--heartbeat-interval is valid.", "oracle": "port,url,cli-cmd", "check": "ran watch --help (all options exist)", "verdict": "true", "evidence": "all listed options are declared on watch", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a116::c1", "atom_id": "a116", "type": "prose", "line": "435", "claim": "All EPU-processing commands use consistent file pattern matching to identify relevant files.", "oracle": "", "check": "read parse handlers vs watch DEFAULT_PATTERNS", "verdict": "misleading", "evidence": "only watch scans a dir via DEFAULT_PATTERNS; parse subcommands take an explicit file/dir path, no pattern match", "confidence": "medium", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a117::c1", "atom_id": "a117", "type": "prose", "line": "437-443", "claim": "Supported patterns include EpuSession.dm, Metadata/GridSquare_*.dm, GridSquare/FoilHole XML manifests, Sample*/Atlas/Atlas.dm.", "oracle": "", "check": "read DEFAULT_PATTERNS in fs_watcher.py", "verdict": "true", "evidence": "all six listed patterns match DEFAULT_PATTERNS entries in fs_watcher.py:22-35", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a118::c1", "atom_id": "a118", "type": "prose", "line": "445-449", "claim": "Pattern matching is recursive with shell globbing wildcards.", "oracle": "", "check": "read handler + DEFAULT_PATTERNS", "verdict": "true", "evidence": "observer.schedule(recursive=True); DEFAULT_PATTERNS use ** and * globs", "confidence": "low", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a119::c1", "atom_id": "a119", "type": "table", "line": "453-460", "claim": "Exit codes: 0 success; 1 validation error/API/dir-not-found/permission; 2 keyboard interrupt.", "oracle": "", "check": "read handler; ran validate on invalid dir", "verdict": "misleading", "evidence": "dir-not-found & API = typer.Exit(1) OK; but validation invalid gives exit 0 (verified); Ctrl+C handler raises typer.Exit() -> 0, not 2", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/cli-reference.md::a122::c1", "atom_id": "a122", "type": "prose", "line": "468-471", "claim": "Backend endpoints: GET /status, SSE GET /agent/{agent_id}/session/{session_id}/instructions/stream, POST /agent/{agent_id}/session/{session_id}/heartbeat.", "oracle": "", "check": "grep api_server.py + api_client.py", "verdict": "true", "evidence": "get_status->GET status; api_server.py:1685 GET .../instructions/stream; :1973 POST .../heartbeat", "confidence": "high", "file": "docs/agent/cli-reference.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a000::c1", "atom_id": "docs/agent/deployment.md::a000", "type": "prose", "line": "3", "claim": "The SmartEM Agent monitors EPU output directories and communicates acquisition data to the backend in real-time.", "oracle": "", "check": "read smartem_agent/__main__.py watch command + fs_watcher SSE/API client", "verdict": "true", "evidence": "__main__.py:172 watch cmd observes dir + APIClient; fs_watcher.py uses SmartEMAPIClient + SSEAgentClient", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a001::c1", "atom_id": "docs/agent/deployment.md::a001", "type": "prose", "line": "7", "claim": "The agent runs as a Python script or a bundled Windows binary and parses EPU output to send to the backend.", "oracle": "", "check": "grep PyInstaller-frozen handling in __main__.py", "verdict": "true", "evidence": "__main__.py:29 getattr(sys,'frozen',False) -> agent.env beside .exe; runnable via python -m smartem_agent", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a003::c1", "atom_id": "docs/agent/deployment.md::a003", "type": "prose", "line": "13-18", "claim": "Core capabilities: real-time monitoring, parsing sessions/atlases/grid squares/foil holes/micrographs, validation, REST+SSE backend integration, heartbeat.", "oracle": "", "check": "parse subcommands + SSE/heartbeat in __main__.py/fs_watcher.py", "verdict": "true", "evidence": "parse session/atlas/gridsquare/foilhole/micrograph cmds exist; validate cmd; SSEAgentClient + send_heartbeat present", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a005::c1", "atom_id": "docs/agent/deployment.md::a005", "type": "prose", "line": "24-26", "claim": "The watch command supports pre/mid/post-acquisition modes by parsing existing files then monitoring for changes.", "oracle": "", "check": "read watch command flow in __main__.py", "verdict": "true", "evidence": "__main__.py:273-278 parses existing dir contents then schedules Observer for live events", "confidence": "medium", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a006::c1", "atom_id": "docs/agent/deployment.md::a006", "type": "prose", "line": "30", "claim": "Agent authenticates via Keycloak using a config file with KEYCLOAK_URL/REALM/CLIENT_ID/CLIENT_SECRET, default beside the exe, override via --config, exits on missing/invalid config.", "oracle": "", "check": "read load_keycloak_config + watch --config handling", "verdict": "true", "evidence": "keycloak_client.py:60 required 4 vars; __main__.py:185 --config; :22 default agent.env; :232-234 exit(1) on error", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a007::c1", "atom_id": "docs/agent/deployment.md::a007", "type": "prose", "line": "32", "claim": "A sibling authentication.md guide exists.", "oracle": "source-path", "check": "manifest/docs listing", "verdict": "true", "evidence": "docs/agent/authentication.md present (manifest index 0)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a008::c1", "atom_id": "docs/agent/deployment.md::a008", "type": "prose", "line": "36", "claim": "cli-reference.md and troubleshooting.md exist alongside this doc.", "oracle": "source-path", "check": "manifest/docs listing", "verdict": "true", "evidence": "docs/agent/cli-reference.md (idx 1) and docs/agent/troubleshooting.md (idx 4) present", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a009::c1", "atom_id": "docs/agent/deployment.md::a009", "type": "code", "line": "40-49", "claim": "python -m smartem_agent watch [--verbose] [--dry-run] runs.", "oracle": "cli-cmd", "check": "ran python -m smartem_agent watch --help", "verdict": "true", "evidence": "watch has --dry-run/--no-dry-run and -v/--verbose (count) options; module invocation works", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a010::c1", "atom_id": "docs/agent/deployment.md::a010", "type": "code", "line": "53-61", "claim": "watch supports --api-url, --agent-id, --session-id, --heartbeat-interval, --verbose.", "oracle": "url,cli-cmd", "check": "watch --help output", "verdict": "true", "evidence": "__main__.py:176-196 defines api_url, --agent-id, --session-id, --heartbeat-interval, -v/--verbose", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a011::c1", "atom_id": "docs/agent/deployment.md::a011", "type": "prose", "line": "65", "claim": "The CLI is organised into three command categories: parse, validate, watch.", "oracle": "", "check": "read Typer command registrations", "verdict": "true", "evidence": "__main__.py: parse_cli Typer group + validate cmd + watch cmd = three categories", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a012::c1", "atom_id": "docs/agent/deployment.md::a012", "type": "prose", "line": "68", "claim": "Parse commands extract data from EPU files without backend communication.", "oracle": "", "check": "read parse command bodies", "verdict": "true", "evidence": "parse_* commands only call EpuParser + InMemoryDataStore, no API client; comment ':60 parsers dont persist'", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a013::c1", "atom_id": "docs/agent/deployment.md::a013", "type": "prose", "line": "71", "claim": "Validate commands check EPU directory structure for completeness.", "oracle": "", "check": "read validate_epu_dir", "verdict": "true", "evidence": "__main__.py:153 validate calls EpuParser.validate_project_dir returning is_valid+errors", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a014::c1", "atom_id": "docs/agent/deployment.md::a014", "type": "prose", "line": "74", "claim": "Watch commands monitor directories in real-time with backend integration.", "oracle": "", "check": "read watch command", "verdict": "true", "evidence": "__main__.py:172 watch uses Observer + APIClient + SSEAgentClient", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a017::c1", "atom_id": "docs/agent/deployment.md::a017", "type": "code", "line": "84-97", "claim": "parse dir accepts --verbose and --verbose --verbose for debug-level output.", "oracle": "cli-cmd", "check": "ran python -m smartem_agent parse dir /tmp --verbose[ --verbose]", "verdict": "misleading", "evidence": "parse dir exists but verbose is --verbose INTEGER (not count); bare --verbose errors 'requires an argument'; --verbose --verbose errors 'not a valid integer'", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a019::c1", "atom_id": "docs/agent/deployment.md::a019", "type": "code", "line": "104-109", "claim": "parse session --verbose runs.", "oracle": "cli-cmd", "check": "parse session signature (verbose:int=0) + observed parse-verbose behaviour", "verdict": "misleading", "evidence": "parse session cmd exists but --verbose is an INTEGER option; bare --verbose fails; correct form is --verbose 1", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a020::c1", "atom_id": "docs/agent/deployment.md::a020", "type": "code", "line": "112-117", "claim": "parse atlas --verbose runs.", "oracle": "cli-cmd", "check": "parse atlas signature verbose:int=0", "verdict": "misleading", "evidence": "parse atlas exists; --verbose requires an integer value, bare --verbose errors (needs --verbose 1)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a021::c1", "atom_id": "docs/agent/deployment.md::a021", "type": "code", "line": "120-133", "claim": "parse gridsquare and parse gridsquare-metadata run, the latter with --verbose.", "oracle": "cli-cmd", "check": "subcommands exist; verbose:int=0 behaviour", "verdict": "misleading", "evidence": "gridsquare + gridsquare-metadata cmds exist (__main__.py:113,123) but --verbose needs an int value; bare --verbose errors", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a022::c1", "atom_id": "docs/agent/deployment.md::a022", "type": "code", "line": "136-145", "claim": "parse foilhole --verbose and parse micrograph run.", "oracle": "cli-cmd", "check": "subcommands exist; verbose:int=0 behaviour", "verdict": "misleading", "evidence": "foilhole+micrograph cmds exist (__main__.py:133,143); parse micrograph line ok, but foilhole --verbose fails (needs --verbose 1)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a023::c1", "atom_id": "docs/agent/deployment.md::a023", "type": "prose", "line": "149", "claim": "Validation commands check EPU directory structure for completeness and format compliance.", "oracle": "", "check": "read validate command", "verdict": "true", "evidence": "__main__.py:153-169 validate returns structural is_valid + errors list", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a026::c1", "atom_id": "docs/agent/deployment.md::a026", "type": "code", "line": "156-171", "claim": "validate --verbose runs and invalid dirs fail.", "oracle": "cli-cmd", "check": "ran python -m smartem_agent validate /tmp --verbose", "verdict": "misleading", "evidence": "validate cmd exists but --verbose is INTEGER; bare --verbose errors 'requires an argument' (needs --verbose 1)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a028::c1", "atom_id": "docs/agent/deployment.md::a028", "type": "code", "line": "176-191", "claim": "validate --verbose runs against well-formed directories.", "oracle": "cli-cmd", "check": "validate signature verbose:int=0 + observed error", "verdict": "misleading", "evidence": "validate cmd exists; --verbose needs an integer argument, bare --verbose fails (correct: --verbose 1)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a029::c1", "atom_id": "docs/agent/deployment.md::a029", "type": "prose", "line": "195", "claim": "Successful validation returns exit code 0.", "oracle": "", "check": "return not is_valid + observed exit code on invalid dir", "verdict": "true", "evidence": "valid dir -> is_valid True -> return False, no exception -> process exits 0 (verified /tmp path exit_code=0)", "confidence": "medium", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a030::c1", "atom_id": "docs/agent/deployment.md::a030", "type": "code", "line": "196-198", "claim": "Valid directories emit 'EPU project dir is structurally valid'.", "oracle": "", "check": "grep exact string in __main__.py", "verdict": "true", "evidence": "__main__.py:163 logging.info('EPU project dir is structurally valid')", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a031::c1", "atom_id": "docs/agent/deployment.md::a031", "type": "prose", "line": "200", "claim": "Failed validation returns exit code 1 and lists specific issues.", "oracle": "", "check": "ran validate /tmp (invalid) and checked $?", "verdict": "misleading", "evidence": "invalid-but-existing dir exits 0, not 1: 'return not is_valid' return value is NOT mapped to Typer exit code; issues are listed (via logging)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a032::c1", "atom_id": "docs/agent/deployment.md::a032", "type": "code", "line": "201-206", "claim": "Failed validation prints 'Invalid EPU project dir. Found the following issues:' with a bulleted list including 'Missing required file: EpuSession.dm'.", "oracle": "", "check": "grep strings in __main__.py + fs_parser.py", "verdict": "true", "evidence": "__main__.py:165 'Invalid EPU project dir...'; fs_parser.py:113 'Missing required file: EpuSession.dm' (other bullets illustrative)", "confidence": "medium", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a033::c1", "atom_id": "docs/agent/deployment.md::a033", "type": "prose", "line": "210", "claim": "The watch command is the primary operational mode providing real-time monitoring with backend communication.", "oracle": "", "check": "read watch command", "verdict": "true", "evidence": "__main__.py:172 watch = only command wiring Observer + backend API client", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a034::c1", "atom_id": "docs/agent/deployment.md::a034", "type": "code", "line": "214-228", "claim": "watch supports --log-file, --log-interval, --verbose (repeatable), --dry-run.", "oracle": "cli-cmd", "check": "watch --help output", "verdict": "true", "evidence": "watch --help: --log-file [fs_changes.log], --log-interval [10.0], --dry-run, -v/--verbose count (so --verbose --verbose valid on watch)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a035::c1", "atom_id": "docs/agent/deployment.md::a035", "type": "code", "line": "232-251", "claim": "watch supports --api-url, --agent-id, --session-id, --heartbeat-interval, --sse-timeout, --log-interval; backend at http://backend:8000 (port 8000).", "oracle": "port,url,cli-cmd", "check": "watch --help output + api_url default", "verdict": "true", "evidence": "all flags present; api-url default http://127.0.0.1:8000 (port 8000), --sse-timeout [30], --heartbeat-interval [60]", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a040::c1", "atom_id": "docs/agent/deployment.md::a040", "type": "prose", "line": "274", "claim": "An epuplayer tool exists to simulate EPU file writing.", "oracle": "", "check": "located package + entry point", "verdict": "true", "evidence": "smartem-devtools/packages/smartem-epuplayer; pyproject [project.scripts] epuplayer = smartem_epuplayer.cli:main", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a041::c1", "atom_id": "docs/agent/deployment.md::a041", "type": "code", "line": "277-282", "claim": "epuplayer record records filesystem events.", "oracle": "", "check": "ran epuplayer record /tmp /tmp/out.tar.gz + read cli.py argparse", "verdict": "misleading", "evidence": "record takes ONE positional (directory) + REQUIRED -o/--output; positional output errors: 'the following arguments are required: -o/--output' (needs -o)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a042::c1", "atom_id": "docs/agent/deployment.md::a042", "type": "code", "line": "285-297", "claim": "epuplayer replay --fast replays events; watch --dry-run --verbose --verbose --log-interval monitors.", "oracle": "cli-cmd", "check": "read replay argparse + watch --help", "verdict": "true", "evidence": "cli.py:83-111 replay positionals recording+target + --fast flag; watch supports --dry-run/-v(count)/--log-interval", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a043::c1", "atom_id": "docs/agent/deployment.md::a043", "type": "code", "line": "300-306", "claim": "cp -r data then python -m smartem_agent watch --dry-run monitors copied data.", "oracle": "cli-cmd", "check": "watch --help (--dry-run exists)", "verdict": "true", "evidence": "watch has --dry-run flag; module invocation valid", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a044::c1", "atom_id": "docs/agent/deployment.md::a044", "type": "prose", "line": "310-315", "claim": "EpuSession.dm is required, references atlas data, and drives grid instantiation.", "oracle": "cli-cmd", "check": "grep EpuSession.dm handling in fs_parser.py", "verdict": "true", "evidence": "fs_parser.py:73 EPU_SESSION_FILENAME='EpuSession.dm'; :113 'Missing required file'; :230 extracts AtlasId (atlas reference)", "confidence": "medium", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a045::c1", "atom_id": "docs/agent/deployment.md::a045", "type": "prose", "line": "317", "claim": "The packages/smartem-epuplayer/ utility simulates EPU file writing with proper timing.", "oracle": "cli-cmd", "check": "ls path in smartem-devtools", "verdict": "true", "evidence": "smartem-devtools/packages/smartem-epuplayer/ exists (doc lives in devtools; relative path correct); replayer supports timing modes", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a046::c1", "atom_id": "docs/agent/deployment.md::a046", "type": "prose", "line": "321", "claim": "Supplying --agent-id and --session-id establishes real-time backend communication.", "oracle": "", "check": "read watch command conditional", "verdict": "true", "evidence": "__main__.py:245 'if agent_id and session_id' enables RemoteLogHandler; SmartEMWatcherV2 gets both -> SSEAgentClient", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a047::c1", "atom_id": "docs/agent/deployment.md::a047", "type": "prose", "line": "323-326", "claim": "Real-time features: SSE receives instructions, periodic heartbeats, automatic reconnection with backoff, instruction acknowledgment.", "oracle": "", "check": "grep fs_watcher.py + api_client.py", "verdict": "true", "evidence": "fs_watcher stream_instructions(instruction_callback), retry loop, send_heartbeat; api_client.py:650 acknowledge_instruction; store.py retry_with_backoff (exp)", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/deployment.md::a049::c1", "atom_id": "docs/agent/deployment.md::a049", "type": "prose", "line": "332-335", "claim": "Performance is tuned via --log-interval, --heartbeat-interval, --sse-timeout, and --dry-run.", "oracle": "", "check": "watch --help output", "verdict": "true", "evidence": "watch defines --log-interval, --heartbeat-interval, --sse-timeout, --dry-run", "confidence": "high", "file": "docs/agent/deployment.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM Agent is an EPU filesystem watcher.", "oracle": [], "check": "grep src/smartem_agent for fs_watcher/EpuParser", "verdict": "true", "evidence": "src/smartem_agent/fs_watcher.py uses watchdog + EpuParser, watches EpuSession.dm/GridSquare patterns", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a000::c2", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The agent runs on Windows workstations near the microscope.", "oracle": [], "check": "grep windows refs in smartem_agent; CLAUDE.md deployment note", "verdict": "true", "evidence": "Windows refs in src/smartem_agent/__main__.py, fs_parser.py; documented deployment target (built as .exe)", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a001::c1", "atom_id": "a001", "type": "code", "line": "5-12", "claim": "The toctree references cli-reference, deployment, authentication, and troubleshooting docs, which all exist.", "oracle": [], "check": "ls docs/agent/", "verdict": "true", "evidence": "cli-reference.md, deployment.md, authentication.md, troubleshooting.md all present in docs/agent/", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "18-19", "claim": "cli-reference.md exists (CLI Reference link target).", "oracle": ["source-path"], "check": "ls docs/agent/cli-reference.md", "verdict": "true", "evidence": "docs/agent/cli-reference.md exists (17271 bytes)", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "18-19", "claim": "troubleshooting.md exists (Troubleshooting link target).", "oracle": ["source-path"], "check": "ls docs/agent/troubleshooting.md", "verdict": "true", "evidence": "docs/agent/troubleshooting.md exists (14440 bytes)", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "23-24", "claim": "deployment.md exists (Agent Deployment link target).", "oracle": ["source-path"], "check": "ls docs/agent/deployment.md", "verdict": "true", "evidence": "docs/agent/deployment.md exists (13317 bytes)", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/index.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "23-24", "claim": "authentication.md exists and covers Keycloak authentication.", "oracle": ["source-path"], "check": "ls + grep keycloak in authentication.md", "verdict": "true", "evidence": "docs/agent/authentication.md exists (8951 bytes); 'keycloak' matched 38 times", "confidence": "high", "file": "docs/agent/index.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "A sibling CLI Reference doc (cli-reference.md) exists to link to.", "oracle": "source-path", "check": "ls docs/agent/ in smartem-devtools", "verdict": "true", "evidence": "smartem-devtools/docs/agent/cli-reference.md exists alongside troubleshooting.md", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a002::c1", "atom_id": "a002", "type": "code", "line": "8-14", "claim": "smartem_agent is a runnable module (python -m smartem_agent --help) and importable package.", "oracle": "cli-cmd", "check": "src/smartem_agent/__main__.py + __init__.py present", "verdict": "true", "evidence": "src/smartem_agent/__main__.py calls epu_data_intake_cli(); __init__.py present", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a003::c1", "atom_id": "a003", "type": "code", "line": "17-23", "claim": "validate and watch --dry-run --verbose are valid agent invocations.", "oracle": "cli-cmd", "check": "typer commands in __main__.py", "verdict": "true", "evidence": "__main__.py:153 validate cmd; :172 watch with --dry-run(:175) and --verbose(:194)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a008::c1", "atom_id": "a008", "type": "code", "line": "34-43", "claim": "pip install -e . and pip install -e .[all] work; an [all] extra is defined.", "oracle": "cli-cmd", "check": "pyproject.toml optional-dependencies", "verdict": "true", "evidence": "pyproject.toml:94 all = [smartem-decisions[dev,common,client,cli,backend,agent,images]]", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a010::c1", "atom_id": "a010", "type": "code", "line": "46-48", "claim": "Module lives under src/ so PYTHONPATH=.../smartem-decisions/src python -m smartem_agent works.", "oracle": "env-var,cli-cmd", "check": "directory layout", "verdict": "true", "evidence": "src/smartem_agent/ package exists (src layout)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a013::c1", "atom_id": "a013", "type": "code", "line": "55-61", "claim": "Correct invocation is module form python -m smartem_agent watch /data (not script form).", "oracle": "cli-cmd", "check": "__main__.py present, no top-level script", "verdict": "true", "evidence": "__main__.py present; only invocable as module or console-script smartem-agent", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a020::c1", "atom_id": "a020", "type": "code", "line": "89-102", "claim": "sudo python -m smartem_agent watch /data is a syntactically valid invocation.", "oracle": "cli-cmd", "check": "watch command signature", "verdict": "true", "evidence": "watch takes a positional path arg (__main__.py:172-174)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a022::c1", "atom_id": "a022", "type": "code", "line": "107-116", "claim": "watch /data --dry-run -vv is valid, and EpuSession.dm / *.xml are watched file patterns.", "oracle": "cli-cmd", "check": "watch options + DEFAULT_PATTERNS", "verdict": "true", "evidence": "watch supports --dry-run + -vv (count); fs_watcher.py:22 DEFAULT_PATTERNS include EpuSession.dm and *.xml", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "126", "claim": "The agent authenticates to the backend using Keycloak-issued Bearer tokens.", "oracle": "source-path", "check": "api_client.py + keycloak_client.py", "verdict": "true", "evidence": "api_client.py:264 headers['Authorization']=f'Bearer {keycloak_client.get_token()}'", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "128-132", "claim": "Config is a dotenv file requiring KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET.", "oracle": "env-var", "check": "keycloak_client.load_keycloak_config", "verdict": "true", "evidence": "keycloak_client.py:60 required=[KEYCLOAK_URL,KEYCLOAK_REALM,KEYCLOAK_CLIENT_ID,KEYCLOAK_CLIENT_SECRET]", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a025::c2", "atom_id": "a025", "type": "prose", "line": "128-132", "claim": "Default config location is alongside the agent executable; overridable with --config.", "oracle": "env-var", "check": "_default_keycloak_config_path + --config option", "verdict": "true", "evidence": "__main__.py:29 uses agent.env next to sys.executable; :185 --config option", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a025::c3", "atom_id": "a025", "type": "prose", "line": "128-132", "claim": "Tokens have a ~5-minute lifespan.", "oracle": "source-path", "check": "keycloak realm export", "verdict": "true", "evidence": "smartem-devtools/keycloak-mock/dls-realm.json:12 accessTokenLifespan: 300 (=5min)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a025::c4", "atom_id": "a025", "type": "prose", "line": "128-132", "claim": "The agent uses a Keycloak client named SmartEM_Agent.", "oracle": "source-path", "check": "realm export + authentication.md", "verdict": "true", "evidence": "dls-realm.json:67 clientId: SmartEM_Agent; authentication.md:27 confirms", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "134", "claim": "A sibling Authentication doc (authentication.md) exists to link to.", "oracle": "source-path", "check": "ls docs/agent/", "verdict": "true", "evidence": "smartem-devtools/docs/agent/authentication.md exists", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a027::c1", "atom_id": "a027", "type": "code", "line": "136-145", "claim": "Keycloak well-known and openid-connect/token endpoints with grant_type=client_credentials are used.", "oracle": "url", "check": "keycloak_client grant + standard OIDC paths", "verdict": "true", "evidence": "keycloak_client.py:125 grant_type=client_credentials; standard Keycloak realm token/well-known paths", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a030::c1", "atom_id": "a030", "type": "code", "line": "152-161", "claim": "The backend exposes GET /status on default port 8000.", "oracle": "port,url", "check": "api_server.py route + default port", "verdict": "true", "evidence": "api_server.py:321 @app.get('/status'); :3003 default HTTP_API_PORT 8000", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a033::c1", "atom_id": "a033", "type": "code", "line": "166-169", "claim": "The backend API starts via python -m smartem_backend.api_server.", "oracle": "cli-cmd", "check": "__main__ block in api_server.py", "verdict": "true", "evidence": "api_server.py:3000 if __name__=='__main__' -> uvicorn.run(api_server:app)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a035::c1", "atom_id": "a035", "type": "code", "line": "172-178", "claim": "watch accepts --api-url to point at local or remote backend.", "oracle": "port,url,cli-cmd", "check": "watch option signature", "verdict": "true", "evidence": "__main__.py:176 api_url: str = 'http://127.0.0.1:8000' (exposed as --api-url)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a038::c1", "atom_id": "a038", "type": "code", "line": "189-198", "claim": "Backend exposes GET /agent/{id}/session/{id}/instructions/stream and POST .../heartbeat.", "oracle": "port,url", "check": "api_server.py routes", "verdict": "true", "evidence": "api_server.py:1685 GET instructions/stream; :1973 POST heartbeat (auth-gated)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a041::c1", "atom_id": "a041", "type": "code", "line": "203-208", "claim": "watch accepts --agent-id and --session-id options.", "oracle": "cli-cmd", "check": "watch option signature", "verdict": "true", "evidence": "__main__.py:179-180 --agent-id/--session-id typer.Options", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a043::c1", "atom_id": "a043", "type": "code", "line": "211-218", "claim": "watch accepts --sse-timeout and --heartbeat-interval options.", "oracle": "cli-cmd", "check": "watch option signature", "verdict": "true", "evidence": "__main__.py:181 --sse-timeout(default 30); :182 --heartbeat-interval(default 60)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a047::c1", "atom_id": "a047", "type": "code", "line": "230-236", "claim": "python -m smartem_agent validate /path/to/grid -vv works.", "oracle": "cli-cmd", "check": "validate command signature", "verdict": "misleading", "evidence": "validate_epu_dir(verbose:int=0) is a plain int opt; -v/-vv count only exists on watch (:194); -vv errors here", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a048::c1", "atom_id": "a048", "type": "prose", "line": "238-242", "claim": "A missing EpuSession.dm makes the grid dir structurally invalid.", "oracle": "source-path", "check": "validate_project_dir", "verdict": "true", "evidence": "fs_parser.py:112 appends 'Missing required file: EpuSession.dm' when absent", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a049::c1", "atom_id": "a049", "type": "prose", "line": "244-248", "claim": "Required files for a valid EPU dir are EpuSession.dm and Atlas/Atlas.dm.", "oracle": "source-path", "check": "validate_project_dir requirements", "verdict": "misleading", "evidence": "validate_project_dir requires EpuSession.dm + Metadata + Images-Disc* (fs_parser.py:110-124); Atlas/Atlas.dm is watched but NOT a structural requirement", "confidence": "medium", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a051::c1", "atom_id": "a051", "type": "code", "line": "253-260", "claim": "python -m smartem_agent parse session /path/EpuSession.dm -vv works.", "oracle": "cli-cmd", "check": "parse session command signature", "verdict": "misleading", "evidence": "parse session exists (__main__.py:91) but verbose is plain int; -vv flag only defined on watch, so -vv errors here", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a058::c1", "atom_id": "a058", "type": "code", "line": "286-292", "claim": "watch accepts --log-interval (float) to reduce logging frequency.", "oracle": "cli-cmd", "check": "watch option signature", "verdict": "true", "evidence": "__main__.py:178 log_interval: float = 10.0 (exposed as --log-interval)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a060::c1", "atom_id": "a060", "type": "code", "line": "295-298", "claim": "watch can target a single grid subdirectory (watch /data/Grid_001).", "oracle": "cli-cmd", "check": "watch positional path arg", "verdict": "true", "evidence": "watch takes a Path positional and schedules recursive observer (__main__.py:172,278)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a067::c1", "atom_id": "a067", "type": "code", "line": "328-337", "claim": "watch accepts --log-file; the default log file is fs_changes.log.", "oracle": "cli-cmd", "check": "watch option signature", "verdict": "true", "evidence": "__main__.py:177 log_file: str = 'fs_changes.log' (exposed as --log-file)", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a070::c1", "atom_id": "a070", "type": "code", "line": "348-355", "claim": "For watch, -v gives INFO level and -vv gives DEBUG level.", "oracle": "cli-cmd", "check": "watch verbosity mapping", "verdict": "true", "evidence": "__main__.py:201-206 verbose>=2->DEBUG, ==1->INFO, else ERROR; -v/--verbose count=True", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a078::c1", "atom_id": "a078", "type": "code", "line": "393-400", "claim": "watch supports combined --dry-run --verbose --verbose --log-interval --heartbeat-interval --sse-timeout.", "oracle": "cli-cmd", "check": "watch option set", "verdict": "true", "evidence": "All flags present on watch (__main__.py:175-196); --verbose --verbose = count 2 = DEBUG", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a080::c1", "atom_id": "a080", "type": "code", "line": "405-418", "claim": "Backend HTTP is on port 8000 with /status and /agent/.../instructions/stream endpoints.", "oracle": "port,url", "check": "api_server routes + default port", "verdict": "true", "evidence": "api_server.py:321 /status; :1685 instructions/stream; default port 8000", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a084::c1", "atom_id": "a084", "type": "code", "line": "437-446", "claim": "Postgres connectivity checks target DB smartem_db on port 5432.", "oracle": "port", "check": "grep DB name + postgres port", "verdict": "true", "evidence": "smartem_db is a real project DB name (E2E/dump); 5432 is standard postgres port; DB name is env-driven (POSTGRES_DB)", "confidence": "medium", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a099::c1", "atom_id": "a099", "type": "code", "line": "514-535", "claim": "Diagnostic script uses import smartem_agent and GET http://127.0.0.1:8000/status.", "oracle": "port,url,cli-cmd", "check": "package import + /status endpoint", "verdict": "true", "evidence": "smartem_agent importable; api_server.py:321 /status on default :8000", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a101::c1", "atom_id": "a101", "type": "code", "line": "540-549", "claim": "The agent log (fs_changes.log) contains 'Heartbeat sent' lines to grep for.", "oracle": "cli-cmd", "check": "log message + default log file", "verdict": "true", "evidence": "fs_watcher.py:234 logs 'Heartbeat sent successfully'; default log_file fs_changes.log", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/agent/troubleshooting.md::a105::c1", "atom_id": "a105", "type": "code", "line": "564-579", "claim": "Example: watch with --agent-id/--session-id/-v is valid and /status returns {\"status\": \"ok\"}.", "oracle": "port,url,cli-cmd", "check": "watch options + /status response body", "verdict": "true", "evidence": "watch supports those flags; api_server.py:325 get_status returns {'status':'ok',...}", "confidence": "high", "file": "docs/agent/troubleshooting.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a001::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a001", "type": "prose", "line": "5", "claim": "The file athena-decision-service-api-spec.json is the OpenAPI specification for the (external) Athena Decision Service API.", "oracle": "source-path", "check": "located file and read openapi/info fields", "verdict": "true", "evidence": "smartem-devtools/docs/athena-decision-service-api-spec.json exists; openapi 3.0.1, info.title 'Decision Service API', server portal.athena", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a002::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a002", "type": "prose", "line": "9-12", "claim": "A generated Python client exists at src/athena_api/.", "oracle": "source-path", "check": "find/grep for athena_api across whole workspace", "verdict": "incorrect", "evidence": "No athena_api dir or path anywhere; smartem-decisions/src has smartem_agent/api/backend/common only; grep athena in src returns nothing", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a002::c2", "atom_id": "docs/athena-decision-service-api-spec.README.md::a002", "type": "prose", "line": "9-12", "claim": "The interactive API documentation is built from this specification.", "oracle": "source-path", "check": "read generate_api_docs.py source/dest paths", "verdict": "true", "evidence": "generate_api_docs.py copies docs/athena-decision-service-api-spec.json -> docs/api/athena/swagger.json (use_original_athena_spec)", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a005::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a005", "type": "prose", "line": "20-23", "claim": "A generated Python client and a mock server are available to use for Athena integration/testing.", "oracle": "source-path", "check": "workspace-wide find for athena client/mock server", "verdict": "incorrect", "evidence": "No src/athena_api client and no athena mock server.py anywhere; only stale git branch agent_athena_api_integration_draft references athena", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a007::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a007", "type": "prose", "line": "29-31", "claim": "This spec file is processed by tools/generate_api_docs.py.", "oracle": "source-path", "check": "locate generate_api_docs.py; compare repo of doc vs script", "verdict": "misleading", "evidence": "Script exists but at smartem-decisions/tools/generate_api_docs.py; doc+spec live in smartem-devtools which has no tools/ dir (cross-repo drift)", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a007::c2", "atom_id": "docs/athena-decision-service-api-spec.README.md::a007", "type": "prose", "line": "29-31", "claim": "The generation produces docs/api/athena/swagger.json for Swagger UI.", "oracle": "source-path", "check": "read script dest path and find swagger.json", "verdict": "true", "evidence": "generate_api_docs.py dest = docs/api/athena/swagger.json; file exists at smartem-devtools/docs/api/athena/swagger.json", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a007::c3", "atom_id": "docs/athena-decision-service-api-spec.README.md::a007", "type": "prose", "line": "29-31", "claim": "It creates interactive documentation served at /api/athena/.", "oracle": "source-path", "check": "find docs/api/athena and webui served copies", "verdict": "true", "evidence": "smartem-devtools/docs/api/athena/ and webui/public/api/athena/ + webui/dist/api/athena/ contain swagger.json", "confidence": "medium", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a009::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a009", "type": "prose", "line": "37-43", "claim": "Run `python tools/generate_api_docs.py` to update the documentation.", "oracle": "cli-cmd", "check": "verify script path relative to doc repo", "verdict": "misleading", "evidence": "Command's tools/generate_api_docs.py resolves only in smartem-decisions, not the smartem-devtools repo where this doc+spec+docs/api/athena live", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a010::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a010", "type": "prose", "line": "47-50", "claim": "The Python client is at src/athena_api/client.py.", "oracle": "source-path", "check": "find src/athena_api/client.py workspace-wide", "verdict": "incorrect", "evidence": "No athena_api package or client.py exists anywhere under repos/ (find/grep both empty)", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a010::c2", "atom_id": "docs/athena-decision-service-api-spec.README.md::a010", "type": "prose", "line": "47-50", "claim": "The mock server is at src/athena_api/mock/server.py.", "oracle": "source-path", "check": "find athena mock server.py workspace-wide", "verdict": "incorrect", "evidence": "find -iname server.py -path *athena* returns nothing; no src/athena_api/mock anywhere", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a010::c3", "atom_id": "docs/athena-decision-service-api-spec.README.md::a010", "type": "prose", "line": "47-50", "claim": "Interactive docs live at docs/api/athena/.", "oracle": "source-path", "check": "find docs/api/athena dir", "verdict": "true", "evidence": "smartem-devtools/docs/api/athena/ exists containing swagger.json and original-swagger.json", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a010::c4", "atom_id": "docs/athena-decision-service-api-spec.README.md::a010", "type": "prose", "line": "47-50", "claim": "The generation script is tools/generate_api_docs.py.", "oracle": "source-path", "check": "locate script vs doc repo", "verdict": "misleading", "evidence": "Script exists only at smartem-decisions/tools/generate_api_docs.py, a different repo than the smartem-devtools doc referencing it as tools/...", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a011::c1", "atom_id": "docs/athena-decision-service-api-spec.README.md::a011", "type": "prose", "line": "54-57", "claim": "The specification format is OpenAPI 3.0.1.", "oracle": "source-path", "check": "grep openapi field in spec JSON", "verdict": "true", "evidence": "spec JSON contains \"openapi\": \"3.0.1\"", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena-decision-service-api-spec.README.md::a011::c2", "atom_id": "docs/athena-decision-service-api-spec.README.md::a011", "type": "prose", "line": "54-57", "claim": "The API covers session management, decision recording, algorithm results, and area management endpoints.", "oracle": "source-path", "check": "grep distinct /api/v1 paths in spec", "verdict": "true", "evidence": "Spec paths include /api/v1/Session(s), /Decision(s), Session/{id}/AlgorithmResult, /Area(s), /AreaState(s)", "confidence": "high", "file": "docs/athena-decision-service-api-spec.README.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "Athena is a ThermoFisher API that SmartEM integrates with.", "oracle": "domain-fact", "check": "CLAUDE.md vocabulary: AthenaAPI is REST API exposed by ThermoFisher microscopes", "verdict": "true", "evidence": "CLAUDE.md defines AthenaAPI as ThermoFisher microscope REST API; docs/athena-decision-service-api-spec.json exists", "confidence": "high", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "The Athena Decision Service API specification is available in the docs API documentation.", "oracle": "source-path", "check": "find generated api docs", "verdict": "true", "evidence": "docs/api/athena/swagger.json and docs/athena-decision-service-api-spec.json present in smartem-devtools", "confidence": "high", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9", "claim": "Link ../api/athena/index.html (Swagger UI) resolves to an existing generated doc.", "oracle": "source-path", "check": "resolve relative link from docs/athena/index.md", "verdict": "true", "evidence": "docs/api/athena/index.html exists in smartem-devtools", "confidence": "high", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a002::c2", "atom_id": "a002", "type": "prose", "line": "10", "claim": "Link ../backend/api-documentation.md resolves to an existing doc.", "oracle": "source-path", "check": "resolve relative link", "verdict": "true", "evidence": "docs/backend/api-documentation.md exists in smartem-devtools", "confidence": "high", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a004::c1", "atom_id": "a004", "type": "code", "line": "16-28", "claim": "The mock server is started via `pip install -e .[mock]` then `from athena_api.mock import AthenaAPIServer; AthenaAPIServer().run()` on port 8000.", "oracle": "cli-cmd,port,url", "check": "grep athena_api/AthenaAPIServer across repos; inspect pyproject extras", "verdict": "incorrect", "evidence": "athena_api package DELETED per ADR 0015 (accepted 2026-01-21); no src/athena_api, no AthenaAPIServer in any .py; no [mock] extra in pyproject", "confidence": "high", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/athena/index.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "30", "claim": "Link ../backend/api-documentation.md#mock-server-for-development resolves to a valid file+anchor.", "oracle": "source-path", "check": "grep heading in api-documentation.md", "verdict": "true", "evidence": "docs/backend/api-documentation.md:146 '### Mock Server for Development' yields anchor #mock-server-for-development (target content itself is stale mock)", "confidence": "medium", "file": "docs/athena/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "The interactive API documentation is built with Swagger UI.", "oracle": [], "check": "grep for swagger in generated api index.html", "verdict": "true", "evidence": "docs/api/athena/index.html and docs/api/smartem/index.html are Swagger UI pages (grep 'swagger' hits)", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "18-20", "claim": "Hosted docs are at ../api/athena/index.html and ../api/smartem/index.html.", "oracle": [], "check": "ls of api/athena and api/smartem index.html relative to docs/backend", "verdict": "true", "evidence": "docs/api/athena/index.html and docs/api/smartem/index.html both present in smartem-devtools/docs", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a006::c1", "atom_id": "a006", "type": "code", "line": "26-38", "claim": "Run mock locally via pip install -e '.[mock]' then 'from athena_api.mock import AthenaAPIServer' serving :8000/docs.", "oracle": ["port", "url", "cli-cmd"], "check": "grep pyproject extras; find athena_api module; git log", "verdict": "incorrect", "evidence": "No [mock] extra in pyproject.toml; no athena_api module anywhere; removed in commit 5967ec7 'Remove backend-side Athena integration'", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a013::c1", "atom_id": "a013", "type": "prose", "line": "70-73", "claim": "The Athena source-of-truth spec lives at docs/athena-decision-service-api-spec.json.", "oracle": ["source-path"], "check": "find spec json in workspace", "verdict": "true", "evidence": "docs/athena-decision-service-api-spec.json present in smartem-devtools/docs (doc now hosted there)", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a013::c2", "atom_id": "a013", "type": "prose", "line": "70-73", "claim": "Our implementation is a Python client and mock server generated from the Athena spec.", "oracle": ["source-path"], "check": "find src/athena_api; grep AthenaClient/AthenaAPIServer; git log", "verdict": "incorrect", "evidence": "src/athena_api client+mock absent; README points to nonexistent src/athena_api/; removed commit 5967ec7 (architectural violation)", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a014::c1", "atom_id": "a014", "type": "prose", "line": "75-78", "claim": "The SmartEM Core API is implemented with FastAPI.", "oracle": [], "check": "grep FastAPI in api_server.py", "verdict": "true", "evidence": "src/smartem_backend/api_server.py:230 'app = FastAPI(' and fastapi in pyproject deps", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a014::c2", "atom_id": "a014", "type": "prose", "line": "75-78", "claim": "The SmartEM Core API is implemented with Django.", "oracle": [], "check": "grep django across pyproject/setup/src", "verdict": "incorrect", "evidence": "No 'django' reference in pyproject.toml, setup.py or src/; backend is FastAPI, not Django", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "84-86", "claim": "Downloadable specs exist: api/athena/swagger.json, athena-decision-service-api-spec.json, api/smartem/swagger.json.", "oracle": ["source-path"], "check": "ls the three spec files relative to docs/", "verdict": "true", "evidence": "docs/api/athena/swagger.json, docs/athena-decision-service-api-spec.json, docs/api/smartem/swagger.json all present", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a016::c2", "atom_id": "a016", "type": "prose", "line": "84-86", "claim": "The SmartEM swagger is cached here automatically by the backend per ADR 0020.", "oracle": ["source-path"], "check": "read ADR 0020 context", "verdict": "misleading", "evidence": "ADR 0020 exists but its Context states 'three independently committed copies with no automation keeping them in step' (manual, not automatic)", "confidence": "medium", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a020::c1", "atom_id": "a020", "type": "code", "line": "101-104", "claim": "POST /api/v1/Session registers a new acquisition session.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/Session has methods get, post", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a022::c1", "atom_id": "a022", "type": "code", "line": "107-110", "claim": "POST /api/v1/Area adds grid squares and foil holes.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/Area has methods post, put", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a024::c1", "atom_id": "a024", "type": "code", "line": "113-116", "claim": "POST /api/v1/Decision logs automated decisions.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/Decision has method post", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a026::c1", "atom_id": "a026", "type": "code", "line": "119-122", "claim": "POST /api/v1/AlgorithmResult saves processing outcomes.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "misleading", "evidence": "No /api/v1/AlgorithmResult path in spec; only GET /api/v1/Session/{sessionId}/AlgorithmResult(s) exists (session-scoped, read-only)", "confidence": "medium", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a028::c1", "atom_id": "a028", "type": "code", "line": "127-130", "claim": "GET /api/v1/CurrentApplicationState returns current system status.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/CurrentApplicationState has methods get, post", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a030::c1", "atom_id": "a030", "type": "code", "line": "133-136", "claim": "GET /api/v1/Session/{sessionId}/AreaStates monitors area processing status.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/Session/{sessionId}/AreaStates has method get", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a032::c1", "atom_id": "a032", "type": "code", "line": "139-142", "claim": "GET /api/v1/Session/{sessionId}/Decisions reviews automated decisions.", "oracle": [], "check": "inspect Athena spec paths/methods", "verdict": "true", "evidence": "Athena spec: /api/v1/Session/{sessionId}/Decisions has methods get, delete", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "148", "claim": "The Athena API includes a full mock server for development.", "oracle": [], "check": "find athena_api mock module; git log", "verdict": "incorrect", "evidence": "Mock server (README lists src/athena_api/mock/server.py) absent; no AthenaAPIServer in code; removed commit 5967ec7", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a035::c1", "atom_id": "a035", "type": "code", "line": "150-158", "claim": "Create a mock via 'from athena_api.mock import AthenaAPIServer' and server.run(host,port=8080).", "oracle": ["port", "cli-cmd"], "check": "grep AthenaAPIServer / athena_api.mock in code", "verdict": "incorrect", "evidence": "athena_api.mock module absent workspace-wide; AthenaAPIServer only appears in doc files, not code; removed commit 5967ec7", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "162", "claim": "A Python client library provides programmatic access to the API.", "oracle": [], "check": "find athena_api client; grep AthenaClient", "verdict": "incorrect", "evidence": "athena_api client library absent; src/athena_api removed (commit 5967ec7); AthenaClient only in doc files", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a037::c1", "atom_id": "a037", "type": "code", "line": "164-184", "claim": "Use 'from athena_api import AthenaClient' with athena_api.model.request.Session and client.register_session().", "oracle": ["port", "url", "cli-cmd"], "check": "grep AthenaClient/register_session/athena_api.model in code", "verdict": "incorrect", "evidence": "athena_api, AthenaClient and register_session exist only in this doc, not in any code; module removed commit 5967ec7", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-documentation.md::a042::c1", "atom_id": "a042", "type": "prose", "line": "200-203", "claim": "Issues can be opened at github.com/DiamondLightSource/smartem-decisions/issues.", "oracle": ["url"], "check": "confirm repo exists in workspace", "verdict": "true", "evidence": "repos/DiamondLightSource/smartem-decisions is a valid git repo (remote DiamondLightSource/smartem-decisions)", "confidence": "high", "file": "docs/backend/api-documentation.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-server.md::a000::c1", "atom_id": "docs/backend/api-server.md::a000", "type": "prose", "line": "3", "claim": "The smartem_backend service provides an HTTP API.", "oracle": [], "check": "grep api_server.py for FastAPI app", "verdict": "true", "evidence": "src/smartem_backend/api_server.py:230 app = FastAPI(...); __main__ block runs uvicorn on api_server:app", "confidence": "high", "file": "docs/backend/api-server.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-server.md::a000::c2", "atom_id": "docs/backend/api-server.md::a000", "type": "prose", "line": "3", "claim": "The smartem_backend service provides database operations.", "oracle": [], "check": "ls smartem_backend for migrations/model dirs", "verdict": "true", "evidence": "src/smartem_backend/ contains migrations/ and model/ dirs (Alembic + ORM models)", "confidence": "high", "file": "docs/backend/api-server.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-server.md::a000::c3", "atom_id": "docs/backend/api-server.md::a000", "type": "prose", "line": "3", "claim": "The smartem_backend service provides message queue processing.", "oracle": [], "check": "inspect consumer.py / rmq module", "verdict": "true", "evidence": "src/smartem_backend/consumer.py (RabbitMQ consumer, main at :1257) plus rmq/ and mq_publisher.py", "confidence": "high", "file": "docs/backend/api-server.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/api-server.md::a001::c1", "atom_id": "docs/backend/api-server.md::a001", "type": "code", "line": "7-39", "claim": "The listed backend operation commands (dev-k8s.sh up, consumer -v/-vv, message simulator, api_server module/uvicorn entry points with HTTP_API_HOST/PORT + SMARTEM_LOG_LEVEL, smoke test, --version) are all accurate.", "oracle": ["env-var", "port", "source-path", "url", "cli-cmd", "k8s"], "check": "verified each module, flag, env var and path in source", "verdict": "true", "evidence": "api_server.py:3003-3006 HTTP_API_PORT(8000)/HTTP_API_HOST(127.0.0.1)+uvicorn app; SMARTEM_LOG_LEVEL:260; consumer.py:1259 -v count; __main__.py -v/--version; simulator list_messages/workflow_simulation --gridsquare-id; tests/check_smartem_core_http_api.py url+-v; dev-k8s.sh in smartem-devtools/scripts/k8s (doc says 'from smartem-devtools checkout')", "confidence": "high", "file": "docs/backend/api-server.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "Alembic is used for database schema versioning and migrations.", "oracle": "cli-cmd", "check": "grep pyproject deps", "verdict": "true", "evidence": "pyproject.toml:74 backend extra lists alembic>=1.13.0,<2.0.0", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a001::c2", "atom_id": "a001", "type": "prose", "line": "7", "claim": "Database models are SQLModel-based.", "oracle": "cli-cmd", "check": "grep SQLModel in model/database.py", "verdict": "true", "evidence": "src/smartem_backend/model/database.py: 26 classes subclass SQLModel", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9-12", "claim": "Alembic provides schema versioning, autogenerated migrations, rollback, and environment-specific deployments.", "oracle": "", "check": "generic Alembic feature set; dep present", "verdict": "true", "evidence": "Standard Alembic capabilities; alembic present in pyproject.toml:74", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a004::c1", "atom_id": "a004", "type": "code", "line": "18-20", "claim": "`pip install -e .[backend]` installs the backend extra.", "oracle": "cli-cmd", "check": "grep backend extra in pyproject", "verdict": "true", "evidence": "pyproject.toml:65 defines [project.optional-dependencies] backend", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "22", "claim": "The backend extra includes Alembic as a dependency.", "oracle": "", "check": "grep alembic in backend extra", "verdict": "true", "evidence": "pyproject.toml:74 alembic>=1.13.0 inside backend extra (lines 65-76)", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a008::c1", "atom_id": "a008", "type": "code", "line": "30-32", "claim": "`python -m alembic upgrade head` applies all migrations.", "oracle": "cli-cmd", "check": "alembic.ini present at repo root", "verdict": "true", "evidence": "alembic.ini at repo root; standard Alembic invocation", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c1", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Upgrade creates the Alembic version tracking table.", "oracle": "", "check": "standard Alembic behaviour", "verdict": "true", "evidence": "Alembic creates alembic_version table on first upgrade by design", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c2", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 001 creates the core SmartEM schema baseline.", "oracle": "", "check": "read migration 001", "verdict": "true", "evidence": "versions/...-001_create_core_smartem_schema_baseline.py revision=001 down_revision=None", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c3", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 002 adds performance indexes for acquisition datetime queries.", "oracle": "", "check": "read migration 002", "verdict": "true", "evidence": "002 creates idx_gridsquare_acquisition_datetime_status + idx_micrograph_acquisition_datetime", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c4", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 003 adds SciFi robot prediction model tables.", "oracle": "", "check": "read migration 003 docstring/upgrade", "verdict": "misleading", "evidence": "003 inserts SciFi robot TEST DATA into existing qualitypredictionmodel table, not new tables", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c5", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 004 adds quality metric and training tables.", "oracle": "", "check": "read migration file", "verdict": "true", "evidence": "2025_10_07...-33107394dcd1_add_quality_metric_and_training_tables.py down_revision=003", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c6", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 005 fixes schema drift and syncs indexes.", "oracle": "", "check": "read migration file", "verdict": "true", "evidence": "2025_10_30...-87f8c5e11906_fix_schema_drift_sync_indexes_.py", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c7", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "Migration 006 adds a suggested acquisition index.", "oracle": "", "check": "read migration file", "verdict": "true", "evidence": "2026_01_30_1200-add_suggested_acquisition_index.py revision=a1b2c3d4e5f6", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a009::c8", "atom_id": "a009", "type": "prose", "line": "34-41", "claim": "`upgrade head` applies exactly migrations 001-006 (complete set).", "oracle": "", "check": "ls versions dir", "verdict": "misleading", "evidence": "10 migration files exist through d5e6f7a8b9c0; doc list stops at 006 (4 later migrations undocumented)", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a011::c1", "atom_id": "a011", "type": "code", "line": "47-49", "claim": "`python -m alembic upgrade head` applies pending migrations.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic invocation; alembic.ini at repo root", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a013::c1", "atom_id": "a013", "type": "code", "line": "55-57", "claim": "`python -m alembic current` shows the current DB version.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic subcommand", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a015::c1", "atom_id": "a015", "type": "code", "line": "61-63", "claim": "`python -m alembic history --verbose` shows migration history.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic subcommand/flag", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a017::c1", "atom_id": "a017", "type": "code", "line": "69-71", "claim": "`python -m alembic downgrade ` downgrades to a revision.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic subcommand", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a019::c1", "atom_id": "a019", "type": "code", "line": "75-77", "claim": "`python -m alembic downgrade -1` rolls back one migration.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic relative-downgrade syntax", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a021::c1", "atom_id": "a021", "type": "prose", "line": "83", "claim": "SQLModel classes are defined in src/smartem_backend/model/database.py.", "oracle": "source-path", "check": "ls path + grep classes", "verdict": "true", "evidence": "src/smartem_backend/model/database.py exists with 26 SQLModel subclasses", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a022::c1", "atom_id": "a022", "type": "code", "line": "85-87", "claim": "`python -m alembic revision --autogenerate -m \"...\"` autogenerates a migration.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic invocation", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a025::c1", "atom_id": "a025", "type": "code", "line": "95-97", "claim": "`python -m alembic revision -m \"...\"` creates an empty migration.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic invocation", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "99", "claim": "Generated migration files live in src/smartem_backend/migrations/versions/.", "oracle": "source-path", "check": "ls versions dir", "verdict": "true", "evidence": "src/smartem_backend/migrations/versions/ exists with migration files", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a028::c1", "atom_id": "a028", "type": "code", "line": "105-117", "claim": "Structure-change example uses valid Alembic op.create_table/drop_table API.", "oracle": "cli-cmd", "check": "known Alembic op API", "verdict": "true", "evidence": "Illustrative example; op.create_table/op.drop_table are valid Alembic ops", "confidence": "medium", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a029::c1", "atom_id": "a029", "type": "code", "line": "121-132", "claim": "Index example creates idx_gridsquare_acquisition_datetime_status on gridsquare(acquisition_datetime,status).", "oracle": "cli-cmd", "check": "compare to migration 002", "verdict": "true", "evidence": "Matches real index in migration 002 exactly (name, table, cols, btree)", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a030::c1", "atom_id": "a030", "type": "code", "line": "136-156", "claim": "Data-change example uses valid Alembic op.bulk_insert/op.execute API.", "oracle": "source-path,cli-cmd", "check": "known Alembic op API", "verdict": "true", "evidence": "Illustrative; op.bulk_insert and op.execute are valid Alembic ops", "confidence": "medium", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a036::c1", "atom_id": "a036", "type": "code", "line": "186-195", "claim": "Production deploy uses pg_dump backup then `python -m alembic upgrade head`.", "oracle": "cli-cmd", "check": "standard cmds; db name illustrative", "verdict": "true", "evidence": "Valid pg_dump/alembic commands; smartem_production is an illustrative db name", "confidence": "medium", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a037::c1", "atom_id": "a037", "type": "code", "line": "199-205", "claim": "Staging deploy runs `python -m alembic upgrade head`.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic invocation", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a040::c1", "atom_id": "a040", "type": "code", "line": "212-215", "claim": "`python -m alembic merge -m \"...\"` resolves migration conflicts.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic merge subcommand", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a042::c1", "atom_id": "a042", "type": "code", "line": "218-225", "claim": "`python -m alembic current` and `python -m alembic stamp ` inspect/mark migration state.", "oracle": "cli-cmd", "check": "standard alembic cmds", "verdict": "true", "evidence": "Valid Alembic current/stamp subcommands", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a044::c1", "atom_id": "a044", "type": "code", "line": "228-231", "claim": "`python -m alembic revision --autogenerate` generates a sync migration.", "oracle": "cli-cmd", "check": "standard alembic cmd", "verdict": "true", "evidence": "Valid Alembic invocation", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a046::c1", "atom_id": "a046", "type": "prose", "line": "237", "claim": "Migration 001 file is 2025_09_18_1042-001_create_core_smartem_schema_baseline.py.", "oracle": "source-path", "check": "ls versions dir", "verdict": "true", "evidence": "Exact filename present in versions/", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a047::c1", "atom_id": "a047", "type": "code", "line": "239-243", "claim": "`python -m alembic downgrade base` then `upgrade head` re-runs the full sequence.", "oracle": "cli-cmd", "check": "standard alembic cmds", "verdict": "true", "evidence": "Valid Alembic downgrade base/upgrade head", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a050::c1", "atom_id": "a050", "type": "code", "line": "249-254", "claim": "`python -m alembic downgrade base` then `upgrade head` cleans enum types and restarts migrations.", "oracle": "cli-cmd", "check": "standard alembic cmds", "verdict": "true", "evidence": "Valid Alembic invocations (effect on enums depends on migration downgrade logic)", "confidence": "medium", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a051::c1", "atom_id": "a051", "type": "prose", "line": "258", "claim": "The migration system uses smartem_backend.utils.setup_postgres_connection().", "oracle": "", "check": "grep env.py + utils.py", "verdict": "true", "evidence": "migrations/env.py:7,27 imports & calls setup_postgres_connection; utils.py:157 defines it", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a053::c1", "atom_id": "a053", "type": "code", "line": "267-277", "claim": "File structure: alembic.ini, migrations/env.py, script.py.mako, versions/ with migration files 001/002/003.", "oracle": "source-path,cli-cmd", "check": "ls each path", "verdict": "true", "evidence": "alembic.ini, migrations/env.py, script.py.mako, versions/ and the three named 001/002/003 files all present", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a054::c1", "atom_id": "a054", "type": "prose", "line": "279-282", "claim": "Migration files follow YYYY_MM_DD_HHMM-NNN_description.py where NNN is a sequential migration number.", "oracle": "source-path", "check": "ls versions dir naming", "verdict": "misleading", "evidence": "Only 001-003 use NNN; migrations 004+ use hash revision IDs (33107394dcd1 etc.) and 006 file has no revision token", "confidence": "high", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/database.md::a055::c1", "atom_id": "a055", "type": "prose", "line": "286-288", "claim": "Further reading links to Alembic, SQLModel and PostgreSQL documentation.", "oracle": "url", "check": "well-known external doc URLs", "verdict": "true", "evidence": "alembic.sqlalchemy.org / sqlmodel.tiangolo.com / postgresql.org/docs are the canonical doc sites", "confidence": "medium", "file": "docs/backend/database.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "SmartEMAPIClient is a unified client for communicating with the SmartEM Core API.", "oracle": "symbol", "check": "grep class SmartEMAPIClient in api_client.py", "verdict": "true", "evidence": "api_client.py:219 class SmartEMAPIClient; docstring 'SmartEM API client ... all API communication with the SmartEM Core API'", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7-9", "claim": "The client supports both synchronous and asynchronous operations.", "oracle": "symbol", "check": "grep for async def / aget_ methods on SmartEMAPIClient", "verdict": "incorrect", "evidence": "SmartEMAPIClient has no async methods; only 'async def stream_instructions_async' exists on the separate SSEAgentClient (api_client.py:832)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a001::c2", "atom_id": "a001", "type": "prose", "line": "7-9", "claim": "The client includes data conversion utilities between EPU data models and API request/response models.", "oracle": "symbol", "check": "read EntityConverter class", "verdict": "true", "evidence": "api_client.py:52 class EntityConverter with acquisition_to_request/grid_to_request/... converting *Data to *CreateRequest", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "13", "claim": "The API client ships with the SmartEM Decisions package; no extra install needed.", "oracle": "path", "check": "locate api_client.py within package src", "verdict": "true", "evidence": "src/smartem_backend/api_client.py is part of the smartem_backend package in the repo", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "17", "claim": "Callers must ensure the underlying requests.Session carries a valid Keycloak Bearer token or the deployed backend returns 401.", "oracle": "symbol", "check": "read __init__ and _send_with_auth", "verdict": "true", "evidence": "api_client.py:242 self._session=requests.Session(); _send_with_auth attaches Bearer and handles 401 (lines 257-274)", "confidence": "medium", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "19", "claim": "For the Agent, a Keycloak helper fetches tokens via OAuth2 client_credentials grant, attaches them per request, and refreshes on 401.", "oracle": "source-path", "check": "read keycloak_client.py and _send_with_auth", "verdict": "true", "evidence": "keycloak_client.py:76 client_credentials grant, line 125 grant_type=client_credentials; _send_with_auth invalidates+retries on 401", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "19", "claim": "The referenced docs agent/authentication.md and ADR 0018-smartem-agent-keycloak-auth.md exist.", "oracle": "source-path", "check": "ls doc paths in smartem-devtools", "verdict": "true", "evidence": "smartem-devtools/docs/agent/authentication.md and docs/decision-records/decisions/0018-smartem-agent-keycloak-auth.md both present", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "21", "claim": "Other callers can obtain a Keycloak token and attach it to the client's session headers.", "oracle": "symbol", "check": "inspect session exposure on client", "verdict": "true", "evidence": "client exposes self._session (requests.Session); headers can be set, or keycloak_client passed to __init__ (api_client.py:228,242)", "confidence": "low", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a007::c1", "atom_id": "a007", "type": "code", "line": "27-29", "claim": "`from smartem_backend.api_client import SmartEMAPIClient` is a valid import.", "oracle": "symbol", "check": "verify module path and class name", "verdict": "true", "evidence": "src/smartem_backend/api_client.py:219 defines class SmartEMAPIClient", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a008::c1", "atom_id": "a008", "type": "code", "line": "33-44", "claim": "SmartEMAPIClient(base_url) has a default 10s timeout and accepts timeout= and logger= kwargs.", "oracle": "port", "check": "read __init__ signature", "verdict": "true", "evidence": "api_client.py:228 def __init__(self, base_url, timeout: float = 10.0, logger=None, keycloak_client=None); backend default port is 8000 (HTTP_API_PORT)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a010::c1", "atom_id": "a010", "type": "code", "line": "50-62", "claim": "The client supports async context-manager use (`async with ... as client`) and `await client.aget_status()`.", "oracle": "symbol", "check": "grep __aenter__/__aexit__/aget_status", "verdict": "incorrect", "evidence": "Only sync __enter__/__exit__ (api_client.py:283-287); no __aenter__/__aexit__ and no aget_status; sync get_status exists (line 397)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a011::c1", "atom_id": "a011", "type": "prose", "line": "66-67", "claim": "All operations have async variants prefixed with `a` (e.g. aget_status vs get_status).", "oracle": "symbol", "check": "grep aget_ methods", "verdict": "incorrect", "evidence": "No aget_* methods exist on SmartEMAPIClient; all entity methods are synchronous (def, not async def)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a012::c1", "atom_id": "a012", "type": "code", "line": "69-75", "claim": "`await client.aget_acquisitions()` is available alongside get_acquisitions().", "oracle": "symbol", "check": "grep aget_acquisitions", "verdict": "incorrect", "evidence": "get_acquisitions exists (api_client.py:406) but aget_acquisitions does not exist anywhere", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a014::c1", "atom_id": "a014", "type": "code", "line": "81-102", "claim": "create_acquisition accepts AcquisitionData, returns an object with .id, and alternatively accepts an AcquisitionCreateRequest(id=..., name=...).", "oracle": "symbol", "check": "read create_acquisition, AcquisitionResponse, AcquisitionCreateRequest", "verdict": "incorrect", "evidence": "create_acquisition always calls EntityConverter.acquisition_to_request needing AcquisitionData.instrument, so a request model fails; response is AcquisitionResponse with .uuid not .id; AcquisitionCreateRequest has no id field", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a015::c1", "atom_id": "a015", "type": "code", "line": "106-115", "claim": "get_acquisitions() and get_acquisition(id) return objects exposing .id, .name and .status.", "oracle": "symbol", "check": "read AcquisitionResponse fields", "verdict": "misleading", "evidence": "get_acquisitions/get_acquisition exist (api_client.py:406,416); AcquisitionResponse has name/status but exposes .uuid, not .id (http_response.py)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a016::c1", "atom_id": "a016", "type": "code", "line": "119-127", "claim": "update_acquisition(id, AcquisitionUpdateRequest) updates by id using a two-argument call.", "oracle": "symbol", "check": "read update_acquisition signature", "verdict": "incorrect", "evidence": "api_client.py:420 def update_acquisition(self, acquisition: AcquisitionData) is single-arg and converts via acquisition_to_request; no (id, update) form; passing AcquisitionUpdateRequest fails on .instrument", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a017::c1", "atom_id": "a017", "type": "code", "line": "131-133", "claim": "`client.delete_acquisition(\"acquisition-id\")` deletes an acquisition.", "oracle": "symbol", "check": "read delete_acquisition signature", "verdict": "true", "evidence": "api_client.py:425 def delete_acquisition(self, acquisition_uuid: str) -> None", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a019::c1", "atom_id": "a019", "type": "prose", "line": "139-143", "claim": "The entity hierarchy is Acquisition > Grid > Grid Square > Foil Hole > Micrograph.", "oracle": "symbol", "check": "cross-check create_* nesting methods", "verdict": "true", "evidence": "create_acquisition_grid, create_grid_gridsquare, create_gridsquare_foilholes, create_foilhole_micrograph mirror this nesting (api_client.py:451,571,605,640)", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a021::c1", "atom_id": "a021", "type": "code", "line": "147-190", "claim": "Nested entities are created via create_acquisition_grid(acq.id, grid), create_grid_gridsquare(grid.id, gs), create_gridsquare_foilhole(gs.id, fh), create_foilhole_micrograph(fh.id, mic).", "oracle": "symbol", "check": "read create_* signatures and *Data schemas", "verdict": "incorrect", "evidence": "create_* methods take a single entity arg, not (parent_id, entity); method is create_gridsquare_foilholes (plural); GridSquareData has gridsquare_id+grid_uuid (no id); GridData needs data_dir+acquisition_data; responses use .uuid not .id", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a022::c1", "atom_id": "a022", "type": "prose", "line": "194", "claim": "The client maintains compatibility with the existing EntityStore API.", "oracle": "symbol", "check": "grep EntityStore across src", "verdict": "incorrect", "evidence": "No 'EntityStore' reference anywhere in api_client.py or src/; no compatibility layer exists", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a023::c1", "atom_id": "a023", "type": "code", "line": "196-202", "claim": "The client exposes a generic create(entity_type, id, data, parent=...) EntityStore-compat method.", "oracle": "symbol", "check": "grep 'def create(' in api_client.py and src", "verdict": "incorrect", "evidence": "No 'def create(' method on SmartEMAPIClient; only specific create_acquisition/create_*_* methods exist", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "206", "claim": "The client includes error handling.", "oracle": "symbol", "check": "read _request try/except", "verdict": "true", "evidence": "api_client.py:361-383 _request catches requests.HTTPError, requests.RequestException, and generic Exception with logging", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a025::c1", "atom_id": "a025", "type": "code", "line": "208-215", "claim": "HTTP errors are raised as httpx.HTTPStatusError and should be caught as such.", "oracle": "symbol", "check": "grep httpx imports and raise types", "verdict": "incorrect", "evidence": "Client uses the requests library (api_client.py:10); it raises requests.HTTPError via raise_for_status; httpx is never imported", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "219", "claim": "The client includes logging with configurable log level.", "oracle": "symbol", "check": "read logger setup in __init__", "verdict": "true", "evidence": "api_client.py:245-253 configures a logging.Logger with StreamHandler/formatter and setLevel; debug/info logging throughout _request", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a027::c1", "atom_id": "a027", "type": "code", "line": "221-225", "claim": "logging.basicConfig(level=INFO/DEBUG) controls client log verbosity.", "oracle": "symbol", "check": "generic stdlib logging + client uses logging module", "verdict": "true", "evidence": "Client uses stdlib logging (import logging, api_client.py:3); basicConfig level control is valid standard usage", "confidence": "medium", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a030::c1", "atom_id": "a030", "type": "code", "line": "233-236", "claim": "`await client._request(\"get\", \"status\")` returns a raw API response.", "oracle": "symbol", "check": "read _request definition", "verdict": "incorrect", "evidence": "api_client.py:290 def _request (synchronous, not async); awaiting it raises TypeError; the sync call _request('get','status') is what actually works", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "240", "claim": "The client maintains a cache mapping entity IDs to database IDs.", "oracle": "symbol", "check": "grep id_map/db_id/_get_db_id across src", "verdict": "incorrect", "evidence": "No id-mapping cache exists; no _get_db_id/_store_entity_id_mapping/id_map/db_id in api_client.py or src/", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a032::c1", "atom_id": "a032", "type": "code", "line": "242-248", "claim": "The client exposes _get_db_id(entity_type, id) and _store_entity_id_mapping(...) methods.", "oracle": "symbol", "check": "grep _get_db_id/_store_entity_id_mapping", "verdict": "incorrect", "evidence": "Neither _get_db_id nor _store_entity_id_mapping exists anywhere in the repo src/", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a033::c1", "atom_id": "a033", "type": "prose", "line": "252", "claim": "The client should be closed to free resources.", "oracle": "symbol", "check": "read close()", "verdict": "true", "evidence": "api_client.py:276 def close(self) closes self._session", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/http-api-client.md::a034::c1", "atom_id": "a034", "type": "code", "line": "254-262", "claim": "client.close() and the sync context manager auto-close the client.", "oracle": "symbol", "check": "read close/__enter__/__exit__", "verdict": "true", "evidence": "api_client.py:276 close(); __enter__/__exit__ (283-287) call close() on exit", "confidence": "high", "file": "docs/backend/http-api-client.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM backend includes an API server service.", "oracle": "source-path", "check": "grep FastAPI + api_server.py in src/smartem_backend", "verdict": "true", "evidence": "src/smartem_backend/api_server.py exists; FastAPI used across backend (auth.py, utils.py, mq_publisher.py)", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a000::c2", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM backend includes a database service (PostgreSQL).", "oracle": "source-path", "check": "grep postgres/psycopg in src/smartem_backend", "verdict": "true", "evidence": "psycopg/postgres refs in api_server.py, consumer.py, agent_data_cleanup.py; alembic.ini present", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a000::c3", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM backend includes a message queue service (RabbitMQ).", "oracle": "source-path", "check": "grep rabbitmq/amqp/messaging in src/smartem_backend", "verdict": "true", "evidence": "src/smartem_backend/mq_publisher.py and rmq/consumer.py; appconfig.yml references messaging", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a001::c1", "atom_id": "a001", "type": "code", "line": "5-12", "claim": "The toctree references sibling docs api-server, database, api-documentation, and http-api-client which exist.", "oracle": "source-path", "check": "ls docs/backend/", "verdict": "true", "evidence": "docs/backend/ contains api-server.md, database.md, api-documentation.md, http-api-client.md", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "18-19", "claim": "api-server.md documents running/configuring the FastAPI server locally.", "oracle": "source-path", "check": "file exists + FastAPI grep in backend", "verdict": "true", "evidence": "docs/backend/api-server.md exists; FastAPI confirmed in src/smartem_backend/api_server.py", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "18-19", "claim": "database.md documents PostgreSQL setup, Alembic migrations, and schema management.", "oracle": "source-path", "check": "file exists + alembic.ini + postgres grep", "verdict": "true", "evidence": "docs/backend/database.md exists; alembic.ini present; postgres/psycopg used in backend", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "23-24", "claim": "api-documentation.md covers interactive Swagger UI documentation for SmartEM and Athena APIs.", "oracle": "source-path", "check": "file exists + /docs endpoint grep", "verdict": "true", "evidence": "docs/backend/api-documentation.md exists; /docs Swagger endpoint in api_server.py:333 and auth.py:23", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/backend/index.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "23-24", "claim": "http-api-client.md documents a Python client for programmatic API access.", "oracle": "source-path", "check": "file exists + grep client class", "verdict": "true", "evidence": "docs/backend/http-api-client.md exists; class SmartEMAPIClient at src/smartem_backend/api_client.py:219", "confidence": "high", "file": "docs/backend/index.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "An automated schema drift detection system is implemented in SmartEM Decisions.", "oracle": [], "check": "located tools/check_schema_drift.py + _schema_drift.yml workflow", "verdict": "true", "evidence": "tools/check_schema_drift.py exists; .github/workflows/_schema_drift.yml exists", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "The system compares SQLModel definitions against Alembic database migrations.", "oracle": [], "check": "grep SQLModel classes + alembic migrations dir", "verdict": "true", "evidence": "database.py has 32 SQLModel refs; migrations/versions/ holds 10 alembic migrations", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "18-21", "claim": "The check sets up a clean temporary PostgreSQL database for testing.", "oracle": [], "check": "read setup_test_database()", "verdict": "true", "evidence": "check_schema_drift.py:33-79 setup_test_database CREATE DATABASE schema_drift_test_", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a005::c2", "atom_id": "a005", "type": "prose", "line": "18-21", "claim": "It applies all current Alembic migrations to establish the official schema state.", "oracle": [], "check": "read run_existing_migrations()", "verdict": "true", "evidence": "check_schema_drift.py:133-134 runs 'alembic upgrade head'", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a005::c3", "atom_id": "a005", "type": "prose", "line": "18-21", "claim": "It uses Alembic autogenerate to detect what new migrations would be needed.", "oracle": [], "check": "read check_for_new_migrations()", "verdict": "true", "evidence": "check_schema_drift.py:182 'alembic revision --autogenerate'", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a005::c4", "atom_id": "a005", "type": "prose", "line": "18-21", "claim": "The CI build fails if any schema changes (drift) are detected.", "oracle": [], "check": "read _schema_drift.yml job config", "verdict": "misleading", "evidence": "_schema_drift.yml:11 'continue-on-error: true' (temporarily skipped, issue #181) so job does not fail build", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "27-30", "claim": "The schema drift check runs on every pull request to main.", "oracle": [], "check": "read ci.yml triggers + job chain", "verdict": "true", "evidence": "ci.yml:10-12 pull_request branches main; schema-drift job at line 34", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a007::c2", "atom_id": "a007", "type": "prose", "line": "27-30", "claim": "The schema drift check runs on every push to main.", "oracle": [], "check": "read ci.yml push trigger", "verdict": "true", "evidence": "ci.yml:5-7 push branches main", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a007::c3", "atom_id": "a007", "type": "prose", "line": "27-30", "claim": "The check runs on manual workflow dispatch.", "oracle": [], "check": "read ci.yml workflow_dispatch", "verdict": "true", "evidence": "ci.yml:4 workflow_dispatch present", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a007::c4", "atom_id": "a007", "type": "prose", "line": "27-30", "claim": "The check is skipped for draft PRs with a [WIP] prefix.", "oracle": [], "check": "read ci.yml skip condition", "verdict": "misleading", "evidence": "ci.yml:25 skips on PR title startsWith '[WIP]' (title-based, not draft status)", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "34", "claim": "If drift is detected the CI build fails with a clear error message.", "oracle": [], "check": "read _schema_drift.yml", "verdict": "misleading", "evidence": "script exits 1 but job has continue-on-error:true (_schema_drift.yml:11); build not failed currently", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a009::c1", "atom_id": "a009", "type": "code", "line": "36-47", "claim": "The 'SCHEMA DRIFT DETECTED' error message text is emitted with the 4-step fix instructions.", "oracle": ["cli-cmd"], "check": "compare doc block to script print statements", "verdict": "true", "evidence": "check_schema_drift.py:249-258 prints identical message and steps", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a011::c1", "atom_id": "a011", "type": "code", "line": "55-61", "claim": "Regenerating uses 'alembic revision --autogenerate -m ...' after activating .venv.", "oracle": ["cli-cmd"], "check": "verify .venv + alembic dependency", "verdict": "true", "evidence": ".venv/ present in repo root; alembic used throughout check_schema_drift.py", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a012::c1", "atom_id": "a012", "type": "prose", "line": "65", "claim": "Alembic creates new migration files in src/smartem_backend/migrations/versions/.", "oracle": [], "check": "ls migrations versions dir", "verdict": "true", "evidence": "src/smartem_backend/migrations/versions/ exists with 10 migration .py files", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a014::c1", "atom_id": "a014", "type": "code", "line": "73-80", "claim": "Migrations are tested with 'alembic upgrade head' and 'alembic downgrade -1'.", "oracle": ["cli-cmd"], "check": "standard alembic cmds + usage in script", "verdict": "true", "evidence": "check_schema_drift.py:134 uses 'alembic upgrade head'; downgrade -1 is valid alembic syntax", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a015::c1", "atom_id": "a015", "type": "code", "line": "84-87", "claim": "New migration files are committed under src/smartem_backend/migrations/versions/.", "oracle": ["cli-cmd"], "check": "verify versions dir path", "verdict": "true", "evidence": "src/smartem_backend/migrations/versions/ is the real migrations dir", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a017::c1", "atom_id": "a017", "type": "code", "line": "93-96", "claim": "The check runs locally via 'python tools/check_schema_drift.py' with .env.k8s.development configured.", "oracle": ["source-path", "cli-cmd", "k8s"], "check": "verify script path + load_dotenv", "verdict": "true", "evidence": "tools/check_schema_drift.py:40 load_dotenv('.env.k8s.development')", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a018::c1", "atom_id": "a018", "type": "prose", "line": "98-103", "claim": "The script creates a temp db, applies migrations, checks drift, cleans up, and exits 0 (success) or 1 (drift).", "oracle": [], "check": "read main() flow + exit codes", "verdict": "true", "evidence": "check_schema_drift.py:226-268 main() + docstring exit codes 0/1; finally cleanup_test_database", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "109", "claim": "The script reads environment variables from .env.k8s.development or similar K8s env files.", "oracle": ["k8s"], "check": "read load_dotenv call", "verdict": "true", "evidence": "check_schema_drift.py:40 load_dotenv(dotenv_path=.env.k8s.development)", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a021::c1", "atom_id": "a021", "type": "code", "line": "111-116", "claim": "The script requires POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD env vars.", "oracle": ["env-var"], "check": "read required_vars list", "verdict": "true", "evidence": "check_schema_drift.py:43 required_vars = these four POSTGRES_* names", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a023::c1", "atom_id": "a023", "type": "prose", "line": "122-125", "claim": "All SQLModel models are imported in smartem_backend/model/database.py.", "oracle": ["source-path"], "check": "read database.py", "verdict": "true", "evidence": "src/smartem_backend/model/database.py defines the SQLModel table classes; env.py imports SQLModel from it", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a023::c2", "atom_id": "a023", "type": "prose", "line": "122-125", "claim": "alembic.ini and migrations env.py are the Alembic configuration files.", "oracle": ["source-path"], "check": "verify files exist", "verdict": "true", "evidence": "alembic.ini present; src/smartem_backend/migrations/env.py present with target_metadata=SQLModel.metadata", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "129", "claim": "The drift check is implemented as a reusable GitHub Actions workflow.", "oracle": [], "check": "read _schema_drift.yml trigger", "verdict": "true", "evidence": "_schema_drift.yml:3-4 'on: workflow_call' (reusable)", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "131-134", "claim": "The workflow file is .github/workflows/_schema_drift.yml.", "oracle": ["source-path"], "check": "ls workflows", "verdict": "true", "evidence": ".github/workflows/_schema_drift.yml exists", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a025::c2", "atom_id": "a025", "type": "prose", "line": "131-134", "claim": "It is called from .github/workflows/ci.yml.", "oracle": ["source-path"], "check": "read ci.yml uses:", "verdict": "true", "evidence": "ci.yml:37 uses ./.github/workflows/_schema_drift.yml", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a025::c3", "atom_id": "a025", "type": "prose", "line": "131-134", "claim": "The schema drift job runs after basic checks pass.", "oracle": [], "check": "read ci.yml needs chain", "verdict": "true", "evidence": "ci.yml:35 schema-drift needs: security; security needs: check", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a025::c4", "atom_id": "a025", "type": "prose", "line": "131-134", "claim": "The workflow uses a PostgreSQL 15 service container.", "oracle": [], "check": "read _schema_drift.yml services", "verdict": "true", "evidence": "_schema_drift.yml:15 image: postgres:15", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "158-159", "claim": "A missing POSTGRES_* env var produces an 'Environment variable ... not set' error, fixed by configuring .env.k8s.development.", "oracle": ["k8s"], "check": "read env var validation", "verdict": "true", "evidence": "check_schema_drift.py:48 prints 'Error: Environment variable {var} not set' then exits 1", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a032::c1", "atom_id": "a032", "type": "prose", "line": "161-162", "claim": "An 'Error creating temporary database' message indicates PostgreSQL/credential problems.", "oracle": [], "check": "read exception handler", "verdict": "true", "evidence": "check_schema_drift.py:78 prints 'Error creating temporary database: {e}'", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a033::c1", "atom_id": "a033", "type": "prose", "line": "164-165", "claim": "An 'Error running existing migrations' message can occur when migrations are invalid.", "oracle": [], "check": "read migration run handler", "verdict": "true", "evidence": "check_schema_drift.py:142 prints 'Error running existing migrations:'", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "167-168", "claim": "False-positive drift is fixed by ensuring all SQLModel classes are imported in migration env.py.", "oracle": ["source-path"], "check": "read env.py imports/metadata", "verdict": "true", "evidence": "migrations/env.py:6,17 imports SQLModel and sets target_metadata=SQLModel.metadata", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a038::c1", "atom_id": "a038", "type": "prose", "line": "183", "claim": "The check_schema_drift.py script implements the described flow.", "oracle": ["source-path"], "check": "read script", "verdict": "true", "evidence": "tools/check_schema_drift.py present with described setup/migrate/check/cleanup flow", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a039::c1", "atom_id": "a039", "type": "code", "line": "185-206", "claim": "main() calls setup_test_database, run_existing_migrations, check_for_new_migrations, cleanup_test_database with try/finally cleanup and sys.exit(1) on drift.", "oracle": ["cli-cmd"], "check": "compare pseudocode to real main()", "verdict": "true", "evidence": "check_schema_drift.py:226-268 uses all four functions, try/finally cleanup, sys.exit(1) on drift", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a040::c1", "atom_id": "a040", "type": "prose", "line": "210-213", "claim": "Temporary databases use unique names to avoid conflicts.", "oracle": [], "check": "read temp db naming", "verdict": "true", "evidence": "check_schema_drift.py:53 temp_db_name = f'schema_drift_test_{os.getpid()}'", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/database-schema-drift-prevention.md::a040::c2", "atom_id": "a040", "type": "prose", "line": "210-213", "claim": "Temporary resources are always cleaned up, even on failure.", "oracle": [], "check": "read finally block", "verdict": "true", "evidence": "check_schema_drift.py:265-268 finally: cleanup_test_database(db_url)", "confidence": "high", "file": "docs/database-schema-drift-prevention.md", "doc_kind": "reference"} +{"claim_id": "docs/development/contributing.md::a000::c1", "atom_id": "docs/development/contributing.md::a000", "type": "code", "line": "1-2", "claim": "The MyST include directive references an existing file at ../../.github/CONTRIBUTING.md relative to the doc.", "oracle": "source-path", "check": "Resolved relative include path from doc location; ls/find for CONTRIBUTING.md across both repos", "verdict": "misleading", "evidence": "Resolves to smartem-devtools/.github/CONTRIBUTING.md (absent; dir has only labeler.yml+workflows); real file at smartem-decisions/.github/CONTRIBUTING.md (doc moved out)", "confidence": "high", "file": "docs/development/contributing.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a002::c1", "atom_id": "a002", "type": "code", "line": "9-26", "claim": "`./scripts/k8s/dev-k8s.sh status` is the command to check the local k3s cluster.", "oracle": "find script + grep subcommand", "check": "located script and its case branches", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/dev-k8s.sh:627 \"status\") branch (script moved to smartem-devtools, doc's own repo)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a002::c2", "atom_id": "a002", "type": "code", "line": "9-26", "claim": "`./tests/e2e/run-e2e-test.sh` accepts recording, EPU-dir and max-delay positional args.", "oracle": "read runner", "check": "positional arg parsing", "verdict": "true", "evidence": "smartem-devtools/tests/e2e/run-e2e-test.sh:27-29 RECORDING/EPU_DIR/MAX_DELAY positionals", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a002::c3", "atom_id": "a002", "type": "code", "line": "9-26", "claim": "SMARTEM_TEST_RECORDING and SMARTEM_EPU_DIR env vars override the runner defaults.", "oracle": "grep runner", "check": "env fallback in arg defaults", "verdict": "true", "evidence": "run-e2e-test.sh:23,27,28 use ${SMARTEM_TEST_RECORDING}/${SMARTEM_EPU_DIR}", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "28-31", "claim": "Default recording is testdata/recordings/bi37708-42_epurecording.tar.gz.", "oracle": "grep runner default", "check": "DEFAULT_RECORDING value", "verdict": "true", "evidence": "run-e2e-test.sh:24 DEFAULT_RECORDING=$WORKSPACE_ROOT/testdata/recordings/bi37708-42_epurecording.tar.gz", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "28-31", "claim": "Default EPU output directory is tmp/epu-test-dir.", "oracle": "grep runner default", "check": "DEFAULT_EPU_DIR value", "verdict": "true", "evidence": "run-e2e-test.sh:25 DEFAULT_EPU_DIR=$WORKSPACE_ROOT/tmp/epu-test-dir", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a003::c3", "atom_id": "a003", "type": "prose", "line": "28-31", "claim": "Default max delay is 0.1 seconds.", "oracle": "grep runner default", "check": "MAX_DELAY default", "verdict": "true", "evidence": "run-e2e-test.sh:29 MAX_DELAY=${3:-0.1}", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "33-35", "claim": "SMARTEM_TEST_RECORDING and SMARTEM_EPU_DIR override default recording/EPU paths.", "oracle": "grep runner", "check": "env fallback", "verdict": "true", "evidence": "run-e2e-test.sh:23,27,28", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "37", "claim": "Scripts assume the smartem-decisions repo lives at /DiamondLightSource/smartem-decisions/.", "oracle": "grep script path assumption", "check": "SMARTEM_DECISIONS_PATH default", "verdict": "true", "evidence": "dev-k8s.sh:11 SMARTEM_DECISIONS_PATH=$PROJECT_ROOT/../smartem-decisions (sibling under DiamondLightSource)", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "39-48", "claim": "The runner creates a timestamped test results directory under logs/e2e-tests/.", "oracle": "read runner TEST_DIR", "check": "actual output path", "verdict": "misleading", "evidence": "run-e2e-test.sh:30 TEST_DIR=$WORKSPACE_ROOT/tmp/e2e-logs/... (actual is tmp/e2e-logs, not logs/e2e-tests)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a006::c2", "atom_id": "a006", "type": "prose", "line": "39-48", "claim": "The runner loads environment variables from .env.local-test-run.", "oracle": "grep runner source", "check": "source line", "verdict": "true", "evidence": "run-e2e-test.sh:59 source $WORKSPACE_ROOT/.env.local-test-run", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a006::c3", "atom_id": "a006", "type": "prose", "line": "39-48", "claim": "The runner resets the DB, starts API+consumer+agent, runs playback and cleans up on exit.", "oracle": "read runner body", "check": "structural steps", "verdict": "true", "evidence": "run-e2e-test.sh starts uvicorn :8000, runs playback --max-delay, queries /acquisitions; matches described flow", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a014::c1", "atom_id": "a014", "type": "code", "line": "116-134", "claim": "`./tests/e2e/run-e2e-test-multi-microscope.sh` defaults to 3 microscopes with recording/epu-dir/max-delay positionals.", "oracle": "read multi runner", "check": "arg defaults", "verdict": "true", "evidence": "run-e2e-test-multi-microscope.sh:27 NUM_MICROSCOPES=${1:-3}; :24 default recording; :29 EPU base", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a015::c1", "atom_id": "a015", "type": "prose", "line": "136-141", "claim": "Multi runner defaults: 3 microscopes, bi37708-42 recording, tmp/epu-test-dir base, 0.1 max delay, per-scope dirs epu-test-dir-microscope-N.", "oracle": "read multi runner", "check": "defaults + dir naming", "verdict": "true", "evidence": "multi:27 count=3; :24 recording; :68 epu_dir=${EPU_BASE_DIR}-microscope-${i}", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a018::c1", "atom_id": "a018", "type": "prose", "line": "149-165", "claim": "Multi runner creates a timestamped results directory under logs/e2e-tests/.", "oracle": "read multi TEST_DIR", "check": "actual output path", "verdict": "misleading", "evidence": "multi:31 TEST_DIR=$WORKSPACE_ROOT/tmp/e2e-logs/... (actual is tmp/e2e-logs, not logs/e2e-tests)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a018::c2", "atom_id": "a018", "type": "prose", "line": "149-165", "claim": "Each agent gets a unique agent ID like microscope-titan-01/02/03.", "oracle": "grep multi naming", "check": "AGENT_IDS pattern", "verdict": "true", "evidence": "multi:73 AGENT_IDS+=(microscope-titan-$(printf %02d $i))", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a018::c3", "atom_id": "a018", "type": "prose", "line": "149-165", "claim": "Each agent gets a unique session ID like session-YYYYMMDD-00N and the runner shares one API+consumer across N agents.", "oracle": "grep multi naming", "check": "SESSION_IDS + shared services", "verdict": "true", "evidence": "multi:74 SESSION_IDS session-$(date)-$(printf %03d); single API/consumer, N agents (multi:127-137)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a022::c1", "atom_id": "a022", "type": "code", "line": "184-225", "claim": "Example output structure (per-microscope filesystem counts, per-acquisition DB counts with UUID/Grids/GridSquares/FoilHoles, data-separation verification) matches the runner's output.", "oracle": "read multi verify section", "check": "output print format", "verdict": "true", "evidence": "multi:211-216 prints 'Acquisition {idx} ({instrument_id})' with grid/gridsquare/foilhole counts + separation check", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "231-235", "claim": "Local k3s runs PostgreSQL and RabbitMQ while the backend API/worker run on the host OS.", "oracle": "k8s manifests + runner", "check": "deployment kinds + host uvicorn", "verdict": "true", "evidence": "k8s/environments/development/{postgres,rabbitmq}.yaml Deployments; runner starts uvicorn on host 127.0.0.1:8000", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "240-243", "claim": "Full dev install is `pip install -e .[all]`.", "oracle": "read pyproject", "check": "optional-dependencies all extra", "verdict": "true", "evidence": "smartem-decisions/pyproject.toml:94 all = [ ... ] under [project.optional-dependencies]", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a026::c2", "atom_id": "a026", "type": "prose", "line": "240-243", "claim": "Local k3s cluster is started with `./scripts/k8s/dev-k8s.sh up`.", "oracle": "grep script subcommand", "check": "up case branch", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/dev-k8s.sh:608 \"up\") branch", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a026::c3", "atom_id": "a026", "type": "prose", "line": "240-243", "claim": "The `.env` file is created from `.env.example`.", "oracle": "find env example", "check": "file existence", "verdict": "true", "evidence": "smartem-decisions/.env.example present", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a027::c1", "atom_id": "a027", "type": "prose", "line": "246-248", "claim": "testdata/recordings/ holds bi37600-29_epurecording.tar.gz and bi37708-42_epurecording.tar.gz.", "oracle": "ls testdata", "check": "directory listing", "verdict": "true", "evidence": "testdata/recordings/ contains both bi37600-29 and bi37708-42 .tar.gz", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a027::c2", "atom_id": "a027", "type": "prose", "line": "246-248", "claim": "The bi37708-42 recording contains 8389 events.", "oracle": "epuplayer info / count", "check": "not extracted", "verdict": "unverifiable", "evidence": "specific runtime event count not checked (would require extracting recording.json)", "confidence": "low", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a029::c1", "atom_id": "a029", "type": "code", "line": "253-263", "claim": "Test results root is logs/e2e-tests/ at repo root (gitignored) with 4 service logs, db-dump.sql and test-params.json.", "oracle": "grep runner + gitignore", "check": "actual runner path vs gitignore", "verdict": "misleading", "evidence": "runners write to tmp/e2e-logs/ (run-e2e-test.sh:30); logs/e2e-tests/ only in smartem-decisions/.gitignore:110 (manual convention)", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "267", "claim": "The .env.local-test-run file configures host services to connect to k3s infrastructure.", "oracle": "read env example", "check": "env vars point to NodePorts", "verdict": "true", "evidence": "env-examples/.env.example.local-test-run points POSTGRES/RABBITMQ to localhost NodePorts 30432/30672", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "269", "claim": "Initial values are copied from `.env.example.local-test-run`.", "oracle": "find env example", "check": "file existence", "verdict": "true", "evidence": "smartem-devtools/env-examples/.env.example.local-test-run present (moved to devtools)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a032::c1", "atom_id": "a032", "type": "prose", "line": "271-275", "claim": "PostgreSQL is reached via k3s NodePort localhost:30432.", "oracle": "k8s manifest", "check": "nodePort value", "verdict": "true", "evidence": "k8s/environments/development/postgres.yaml:80 nodePort 30432", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a032::c2", "atom_id": "a032", "type": "prose", "line": "271-275", "claim": "RabbitMQ is reached via k3s NodePort localhost:30672.", "oracle": "k8s manifest", "check": "nodePort value", "verdict": "true", "evidence": "k8s/environments/development/rabbitmq.yaml:88 nodePort 30672 (AMQP)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a032::c3", "atom_id": "a032", "type": "prose", "line": "271-275", "claim": "Backend API runs on host OS typically at 127.0.0.1:8000.", "oracle": "runner + env example", "check": "host + port", "verdict": "true", "evidence": "run-e2e-test.sh starts uvicorn --host 127.0.0.1 --port 8000; HTTP_API_PORT=8000 in env example", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a034::c1", "atom_id": "a034", "type": "code", "line": "278-299", "claim": "Example env: POSTGRES_PORT=30432, RABBITMQ_PORT=30672, RABBITMQ_UI_PORT=15672, HTTP_API_PORT=8000, ADMINER_PORT=8080, CORS_ALLOWED_ORIGINS=*.", "oracle": "diff env example", "check": "line-by-line", "verdict": "true", "evidence": "env-examples/.env.example.local-test-run:4,10,11,15,16,18 match all six values exactly", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a035::c1", "atom_id": "a035", "type": "prose", "line": "301", "claim": "The env file is automatically loaded by ./tests/e2e/run-e2e-test.sh.", "oracle": "grep runner source", "check": "source line", "verdict": "true", "evidence": "run-e2e-test.sh:59 source .env.local-test-run", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a037::c1", "atom_id": "a037", "type": "prose", "line": "306", "claim": "Running database.py drops all tables and recreates schema with indexes.", "oracle": "read database.py", "check": "_create_db_and_tables body", "verdict": "true", "evidence": "database.py:561 _create_db_and_tables drops tables+enums, create_all, then CREATE INDEX statements", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a038::c1", "atom_id": "a038", "type": "code", "line": "307-310", "claim": "`python -m smartem_backend.model.database` resets the database.", "oracle": "find module + main guard", "check": "module path + __main__", "verdict": "true", "evidence": "src/smartem_backend/model/database.py:886 main() calls _create_db_and_tables; :891 __main__ runs main()", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "312-316", "claim": "_create_db_and_tables drops tables+custom enum types, creates tables from SQLModel, creates indexes, and does not use Alembic.", "oracle": "read database.py", "check": "function body", "verdict": "true", "evidence": "database.py:561-588 DROP TABLE + DROP TYPE acquisitionstatus/gridstatus/... then SQLModel.metadata.create_all + CREATE INDEX; no alembic call", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a041::c1", "atom_id": "a041", "type": "code", "line": "320-330", "claim": "DB connects at host localhost port 30432 database smartem_db user username; pg_dump -h localhost -p 30432 works.", "oracle": "env example + manifest", "check": "port/db/user", "verdict": "true", "evidence": "postgres.yaml:80 nodePort 30432; env example POSTGRES_DB=smartem_db, POSTGRES_USER=username", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a043::c1", "atom_id": "a043", "type": "prose", "line": "335-338", "claim": "Queue name smartem_backend is configured in src/smartem_backend/appconfig.yml.", "oracle": "read appconfig", "check": "rabbitmq.queue_name", "verdict": "true", "evidence": "src/smartem_backend/appconfig.yml rabbitmq.queue_name: smartem_backend", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a043::c2", "atom_id": "a043", "type": "prose", "line": "335-338", "claim": "The queue is declared durable=True by both publisher and consumer.", "oracle": "grep rmq", "check": "declare_queue durable", "verdict": "true", "evidence": "rmq/publisher.py:51 declare_queue(durable=True); rmq/consumer.py:50 declare_queue(durable=True)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a045::c1", "atom_id": "a045", "type": "code", "line": "342-346", "claim": "RabbitMQ default credentials are guest/guest.", "oracle": "read rabbitmq manifest", "check": "RABBITMQ_DEFAULT_USER source", "verdict": "misleading", "evidence": "rabbitmq.yaml:24-31 RABBITMQ_DEFAULT_USER/PASS from secretKeyRef; env example uses username/password, not guest/guest", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a047::c1", "atom_id": "a047", "type": "code", "line": "350-353", "claim": "`kubectl rollout restart deployment/rabbitmq -n smartem-decisions` restarts RabbitMQ.", "oracle": "read manifest", "check": "deployment name + namespace", "verdict": "true", "evidence": "rabbitmq.yaml:4 name rabbitmq, :5 namespace smartem-decisions", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a053::c1", "atom_id": "a053", "type": "code", "line": "382-388", "claim": "`python -m uvicorn smartem_backend.api_server:app --host 127.0.0.1 --port 8000 --log-level debug` starts the API.", "oracle": "grep api_server", "check": "module:app attribute", "verdict": "true", "evidence": "src/smartem_backend/api_server.py:230 app = FastAPI(", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a056::c1", "atom_id": "a056", "type": "code", "line": "395-400", "claim": "`python -m fastapi dev src/smartem_backend/api_server.py` is an alternative launch path.", "oracle": "find file", "check": "api_server.py path", "verdict": "true", "evidence": "src/smartem_backend/api_server.py exists at that path", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a059::c1", "atom_id": "a059", "type": "code", "line": "408-412", "claim": "`python -m smartem_backend.consumer -vv` starts the worker at DEBUG.", "oracle": "read consumer", "check": "argparse -v count", "verdict": "true", "evidence": "consumer.py:1257 main() argparse; :1260 -v/--verbose action=count", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a059::c2", "atom_id": "a059", "type": "code", "line": "408-412", "claim": "RABBITMQ_URL=amqp://guest:guest@localhost:30672/ is the consumer's broker URL.", "oracle": "manifest + env example", "check": "port + credentials", "verdict": "misleading", "evidence": "AMQP nodePort 30672 correct; but credentials are username/password per env example / k8s secret, not guest:guest", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a060::c1", "atom_id": "a060", "type": "prose", "line": "413", "claim": "Consumer verbosity: -v for INFO, -vv for DEBUG.", "oracle": "read consumer help", "check": "argparse help text", "verdict": "true", "evidence": "consumer.py:1260 help='Increase verbosity (-v for INFO, -vv for DEBUG)' action=count", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a061::c1", "atom_id": "a061", "type": "code", "line": "417-423", "claim": "`python -m smartem_agent watch --api-url http://localhost:30080 --verbose -v ` runs the agent against the k3s API.", "oracle": "read agent __main__", "check": "watch cmd + options", "verdict": "true", "evidence": "smartem_agent/__main__.py:172 command('watch'); api_url option; -v/--verbose; 30080 is API nodePort (smartem-http-api.yaml:111)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a063::c1", "atom_id": "a063", "type": "prose", "line": "427-431", "claim": "Agent options include --agent-id, --session-id, --sse-timeout (default 30) and --heartbeat-interval (default 60).", "oracle": "read agent __main__", "check": "typer Option defaults", "verdict": "true", "evidence": "__main__.py:179-184 --agent-id, --session-id, --sse-timeout=30, --heartbeat-interval=60", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a064::c1", "atom_id": "a064", "type": "prose", "line": "435", "claim": "The tool is the `epuplayer` CLI, installable via `pip install smartem-epuplayer`.", "oracle": "read pyproject", "check": "project.scripts + name", "verdict": "true", "evidence": "packages/smartem-epuplayer/pyproject.toml:6 name smartem-epuplayer, :41 epuplayer = smartem_epuplayer.cli:main", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a066::c1", "atom_id": "a066", "type": "code", "line": "438-443", "claim": "`epuplayer replay ` replays a recording.", "oracle": "read cli", "check": "replay subparser args", "verdict": "true", "evidence": "cli.py:83 add_parser('replay'); :84 recording, :85 target", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a067::c1", "atom_id": "a067", "type": "prose", "line": "445-449", "claim": "Use the `replay` subcommand; first positional is the .tar.gz recording, second is the target directory.", "oracle": "read cli", "check": "subparser + positional order", "verdict": "true", "evidence": "cli.py:83-85 replay subparser with recording then target positionals (.tar.gz or legacy .json)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a068::c1", "atom_id": "a068", "type": "prose", "line": "451-458", "claim": "Speed options: --fast is default (100x, 1s max delay), --dev-mode is 1000x+burst, --exact is 1x, plus -s/--speed, --max-delay, --burst.", "oracle": "read cli", "check": "argument defs + defaults", "verdict": "true", "evidence": "cli.py: --fast help 'DEFAULT mode' (100x, max_delay 1.0); --dev-mode 1000x+burst max_delay 0.1; --exact 1x; --speed/--max-delay/--burst present; no-flag defaults to fast", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a069::c1", "atom_id": "a069", "type": "prose", "line": "460-462", "claim": "--no-verify skips integrity verification and --skip-unreadable skips unreadable files.", "oracle": "read cli", "check": "argument defs", "verdict": "true", "evidence": "cli.py:120 --no-verify 'Skip integrity verification'; :122 --skip-unreadable", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a071::c1", "atom_id": "a071", "type": "code", "line": "465-474", "claim": "Examples: default fast (no flag), --dev-mode, and --exact all work as replay modes.", "oracle": "read cli", "check": "mode branches", "verdict": "true", "evidence": "cli.py:162-213 replay branches for dev_mode/fast/exact and no-flag default 'Fast mode (default)'", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a074::c1", "atom_id": "a074", "type": "code", "line": "485-522", "claim": "Prep workflow commands (unset env, source .env, dev-k8s.sh status, rabbitmq rollout, database reset, clean epu-test-dir) are all valid.", "oracle": "verify each command", "check": "scripts/modules/deployments", "verdict": "true", "evidence": "dev-k8s.sh status:627; deployment/rabbitmq ns smartem-decisions; python -m smartem_backend.model.database valid; source .env valid", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a075::c1", "atom_id": "a075", "type": "code", "line": "525-534", "claim": "`curl http://localhost:30080/status` and `psql -h localhost -p 30432 -d smartem_db` verify k3s services.", "oracle": "api routes + manifests", "check": "/status route + ports", "verdict": "true", "evidence": "api_server.py:321 @app.get('/status'); http-api nodePort 30080; postgres nodePort 30432", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a076::c1", "atom_id": "a076", "type": "prose", "line": "538", "claim": "The agent command is `python -m smartem_agent watch`, not `python -m smartem_agent.main`.", "oracle": "read agent __main__", "check": "watch command exists; no main module", "verdict": "true", "evidence": "__main__.py:172 command('watch'); no smartem_agent/main.py module", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a078::c1", "atom_id": "a078", "type": "code", "line": "543-563", "claim": "Test Type 1 commands (uvicorn app :8000, consumer -vv, epuplayer replay, agent watch --api-url :8000) are valid.", "oracle": "modules + cli", "check": "module/subcommand existence", "verdict": "true", "evidence": "api_server:app; consumer -vv; epuplayer replay; smartem_agent watch --api-url all verified", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a080::c1", "atom_id": "a080", "type": "code", "line": "568-588", "claim": "Test Type 2 commands (start services then agent watch on empty dir before playback) are valid.", "oracle": "modules + cli", "check": "module/subcommand existence", "verdict": "true", "evidence": "same verified module/command set; agent watch runs before epuplayer replay", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a083::c1", "atom_id": "a083", "type": "code", "line": "599-629", "claim": "Test Type 3 commands (playback first, monitor file count, then agent watch) are valid.", "oracle": "modules + cli", "check": "module/subcommand existence", "verdict": "true", "evidence": "same verified commands; watch + find monitoring are standard", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a085::c1", "atom_id": "a085", "type": "prose", "line": "635-638", "claim": "The correct agent command is `python -m smartem_agent watch`.", "oracle": "read agent __main__", "check": "watch command", "verdict": "true", "evidence": "__main__.py:172 command('watch') (note: doc erroneously also lists 'watch' in the NOT list)", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a087::c1", "atom_id": "a087", "type": "prose", "line": "644-646", "claim": "Use uvicorn (smartem_backend.api_server:app) not `python -m fastapi dev src/smartem_backend/api_server.py`.", "oracle": "find module/file", "check": "app attr + file path", "verdict": "true", "evidence": "api_server.py:230 app; file at src/smartem_backend/api_server.py", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a091::c1", "atom_id": "a091", "type": "code", "line": "657-706", "claim": "Post-test SQL counts tables acquisition, grid, gridsquare, foilhole, micrograph.", "oracle": "read models", "check": "table_name values", "verdict": "true", "evidence": "database.py:28,65,132,176,205 table_name acquisition/grid/gridsquare/foilhole/micrograph", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a091::c2", "atom_id": "a091", "type": "code", "line": "657-706", "claim": "`kubectl exec -n smartem-decisions deployment/postgres -- psql/pg_dump -U username -d smartem_db` works.", "oracle": "manifest", "check": "deployment/namespace/db", "verdict": "true", "evidence": "postgres.yaml name postgres ns smartem-decisions; POSTGRES_USER=username POSTGRES_DB=smartem_db in env example", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a097::c1", "atom_id": "a097", "type": "code", "line": "726-732", "claim": "`curl http://localhost:8000/acquisitions` and `/status` are valid API endpoints.", "oracle": "grep routes", "check": "route decorators", "verdict": "true", "evidence": "api_server.py:389 @app.get('/acquisitions'); :321 @app.get('/status')", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a100::c1", "atom_id": "a100", "type": "prose", "line": "743-754", "claim": "Success criteria include filesystem counts matching DB rows for grids, gridsquares and micrographs.", "oracle": "models", "check": "tables exist", "verdict": "true", "evidence": "grid/gridsquare/micrograph tables exist (database.py); criteria are structurally consistent", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a103::c1", "atom_id": "a103", "type": "code", "line": "771-777", "claim": "`./scripts/k8s/dev-k8s.sh down` stops the k3s cluster.", "oracle": "grep script", "check": "down case branch", "verdict": "true", "evidence": "dev-k8s.sh:622 \"down\") branch", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a105::c1", "atom_id": "a105", "type": "code", "line": "782-789", "claim": "`kubectl get pods -n smartem-decisions`, `kubectl logs deployment/postgres`, and `psql -p 30432` diagnose DB issues.", "oracle": "manifest", "check": "deployment/namespace/port", "verdict": "true", "evidence": "postgres deployment ns smartem-decisions; nodePort 30432", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a106::c1", "atom_id": "a106", "type": "code", "line": "792-799", "claim": "`curl -u guest:guest http://localhost:30673` reaches the RabbitMQ management UI.", "oracle": "manifest", "check": "mgmt nodePort + credentials", "verdict": "misleading", "evidence": "rabbitmq.yaml:92 nodePort 30673 for mgmt UI (port correct); credentials are secret-based username/password, not guest:guest", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a107::c1", "atom_id": "a107", "type": "prose", "line": "802-805", "claim": "Agent uses `--api-url http://localhost:8000` to reach the API.", "oracle": "read agent __main__", "check": "api_url option default", "verdict": "true", "evidence": "__main__.py:176 api_url default http://127.0.0.1:8000; --api-url accepts host:8000", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a108::c1", "atom_id": "a108", "type": "prose", "line": "808-811", "claim": "Consumer broker is RABBITMQ_URL=amqp://guest:guest@localhost:30672/.", "oracle": "manifest + env example", "check": "port + credentials", "verdict": "misleading", "evidence": "AMQP nodePort 30672 correct; credentials username/password per env example / secret, not guest:guest", "confidence": "medium", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/e2e-simulation.md::a109::c1", "atom_id": "a109", "type": "prose", "line": "815-819", "claim": "Queue smartem_backend is durable and persists messages across connections.", "oracle": "read appconfig + rmq", "check": "queue name + durable", "verdict": "true", "evidence": "appconfig queue_name smartem_backend; declare_queue durable=True in publisher/consumer", "confidence": "high", "file": "docs/development/e2e-simulation.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "5-6", "claim": "Documentation lives as Markdown files in the docs/ directory.", "oracle": [], "check": "ls docs/ in smartem-devtools", "verdict": "true", "evidence": "docs/ holds .md files + subdirs (agent, backend, development, operations, etc.)", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a001::c2", "atom_id": "a001", "type": "prose", "line": "5-6", "claim": "During build the Markdown files are automatically converted to MDX and copied into the webui for rendering.", "oracle": [], "check": "read generate-mdx-docs.ts + prebuild.ts", "verdict": "true", "evidence": "generate-mdx-docs.ts copies docs/*.md to webui/src/docs/*.mdx; called by prebuild.ts which build runs", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a001::c3", "atom_id": "a001", "type": "prose", "line": "5-6", "claim": "The webui is deployed to GitHub Pages.", "oracle": [], "check": "read deploy-webui.yml", "verdict": "true", "evidence": ".github/workflows/deploy-webui.yml uses configure-pages/upload-pages-artifact/deploy-pages", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "10-13", "claim": "Source of docs is Markdown files in docs/ (the files you edit).", "oracle": ["source-path"], "check": "ls docs/", "verdict": "true", "evidence": "docs/ contains hand-authored .md files (docsSource in generate-mdx-docs.ts resolves to ../docs)", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a002::c2", "atom_id": "a002", "type": "prose", "line": "10-13", "claim": "webui/scripts/generate-mdx-docs.ts converts .md to .mdx and copies them into webui/src/docs/ (gitignored).", "oracle": ["source-path", "cli-cmd"], "check": "read script + webui/.gitignore", "verdict": "true", "evidence": "script writes src/docs/*.mdx from docs/*.md; webui/.gitignore ignores src/docs/**/*.mdx (except index.mdx)", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a002::c3", "atom_id": "a002", "type": "prose", "line": "10-13", "claim": "The sync runs automatically as part of npm run prebuild and is watched during npm run dev.", "oracle": ["cli-cmd"], "check": "read webui/package.json + vite.config.ts", "verdict": "true", "evidence": "prebuild.ts calls syncDocs(); dev='prebuild && vite'; vite docsWatchPlugin re-runs generate-mdx-docs on docs/*.md change", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a002::c4", "atom_id": "a002", "type": "prose", "line": "10-13", "claim": "The deploy-webui.yml workflow builds and publishes to GitHub Pages on push to main when docs/** changes.", "oracle": ["cli-cmd"], "check": "read deploy-webui.yml on/paths", "verdict": "true", "evidence": "deploy-webui.yml push branches:[main] paths include 'docs/**'; jobs build then deploy to github-pages", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a003::c1", "atom_id": "a003", "type": "code", "line": "17-21", "claim": "Local preview: cd webui && npm install && npm run dev starts the docs dev server.", "oracle": ["cli-cmd"], "check": "read webui/package.json scripts", "verdict": "true", "evidence": "webui/package.json has 'dev':'npm run prebuild && vite'; webui/ dir exists", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "23", "claim": "npm run dev starts the Vite dev server with live reload; changes to docs/*.md are picked up automatically.", "oracle": [], "check": "read vite.config.ts docsWatchPlugin", "verdict": "true", "evidence": "docsWatchPlugin watches docs/, on .md change/add/unlink re-syncs and sends {type:'full-reload'}", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a005::c1", "atom_id": "a005", "type": "code", "line": "27-31", "claim": "Production build: cd webui && npm install && npm run build.", "oracle": ["cli-cmd"], "check": "read webui/package.json scripts", "verdict": "true", "evidence": "webui/package.json 'build':'npm run prebuild && tsc && vite build'", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "33", "claim": "The built output goes to webui/dist/.", "oracle": [], "check": "vite build default + workflow artifact path", "verdict": "true", "evidence": "webui/dist exists; deploy-webui.yml uploads path: webui/dist", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "37-39", "claim": "The index.md file in each docs/ subdirectory contains toctree directives that define navigation order and grouping.", "oracle": ["source-path"], "check": "grep toctree across docs/*/index.md", "verdict": "misleading", "evidence": "6 of 8 index.md have {toctree} (athena/architecture lack it); they are vestigial Sphinx blocks, not the actual nav source", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a007::c2", "atom_id": "a007", "type": "prose", "line": "37-39", "claim": "The webui reads the toctree directives during build to generate the sidebar navigation.", "oracle": ["source-path"], "check": "read generate-nav-from-docs.ts (no toctree parsing)", "verdict": "incorrect", "evidence": "generate-nav-from-docs.ts builds nav by scanning docs/ dir structure + frontmatter title; never parses 'toctree'", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a007::c3", "atom_id": "a007", "type": "prose", "line": "37-39", "claim": "When adding a new page, add its filename (without extension) to the relevant index.md toctree.", "oracle": ["source-path"], "check": "read generate-nav-from-docs.ts convention", "verdict": "misleading", "evidence": "nav auto-generated from directory scan; a new .md appears in sidebar without any toctree edit (editing toctree has no effect)", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "43-44", "claim": "Deployment is automatic via the deploy-webui.yml GitHub Actions workflow.", "oracle": ["source-path"], "check": "read deploy-webui.yml", "verdict": "true", "evidence": ".github/workflows/deploy-webui.yml exists and deploys to GitHub Pages", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a008::c2", "atom_id": "a008", "type": "prose", "line": "43-44", "claim": "It triggers on pushes to main that modify files in webui/, core/, or docs/.", "oracle": ["source-path"], "check": "read deploy-webui.yml on.push.paths", "verdict": "true", "evidence": "on.push branches:[main] paths: 'webui/**','core/**','docs/**'", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/generate-docs.md::a008::c3", "atom_id": "a008", "type": "prose", "line": "43-44", "claim": "Manual deployment is also available via workflow_dispatch.", "oracle": ["source-path"], "check": "read deploy-webui.yml on triggers", "verdict": "true", "evidence": "deploy-webui.yml on: includes workflow_dispatch (and workflow_call)", "confidence": "high", "file": "docs/development/generate-docs.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "SmartEM uses a standardised label set across four repositories.", "oracle": [], "check": "count repos[] in core/github-labels.json", "verdict": "true", "evidence": "core/github-labels.json repos[] has exactly 4 entries", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9-12", "claim": "The four target repos are DiamondLightSource/{smartem-decisions,smartem-frontend,smartem-devtools,fandanGO-cryoem-dls}.", "oracle": [], "check": "read owner+repos[] in github-labels.json", "verdict": "true", "evidence": "owner=DiamondLightSource; repos: smartem-devtools,smartem-decisions,smartem-frontend,fandanGO-cryoem-dls", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "14", "claim": "Labels are defined in `core/github-labels-config.ts`.", "oracle": ["source-path"], "check": "find config file in core/", "verdict": "misleading", "evidence": "No .ts config; actual file is core/github-labels.json (imported by core/index.ts line 19)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "14", "claim": "Labels are synced using `scripts/github/sync-labels.ts`.", "oracle": ["source-path"], "check": "stat script path", "verdict": "true", "evidence": "scripts/github/sync-labels.ts exists", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "18-22", "claim": "Node.js 18+ is required.", "oracle": ["source-path", "cli-cmd"], "check": "inspect script runtime deps", "verdict": "true", "evidence": "script uses native fetch (Node 18+); CI gitflow.yml pins node-version 22; no engines field", "confidence": "medium", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "18-22", "claim": "Auth via GitHub CLI (`gh auth login`) is supported.", "oracle": ["cli-cmd"], "check": "read GhCliBackend", "verdict": "true", "evidence": "GhCliBackend.checkAuth runs `gh auth status`; selectBackend prefers gh CLI", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a004::c3", "atom_id": "a004", "type": "prose", "line": "18-22", "claim": "Auth via GITHUB_TOKEN env var (repo scope) is supported.", "oracle": ["cli-cmd"], "check": "read GitHubApiBackend", "verdict": "true", "evidence": "GitHubApiBackend reads process.env.GITHUB_TOKEN (sync-labels.ts:98)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a007::c1", "atom_id": "a007", "type": "table", "line": "30-41", "claim": "Ten types-of-work labels with the listed colours and purposes exist.", "oracle": [], "check": "diff table vs typesOfWork[] in github-labels.json", "verdict": "true", "evidence": "typesOfWork[] has all 10 (documentation 0d6d6e..enhancement 1b7c83); names/purposes/colour families match", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a009::c1", "atom_id": "a009", "type": "table", "line": "47-58", "claim": "The system-component labels are the ten listed (backend/agent/frontend/aria/devtools families).", "oracle": [], "check": "diff table vs systemComponents[] in github-labels.json", "verdict": "misleading", "evidence": "10 rows match, but config systemComponents[] has 12; doc omits component:epuplayer & component:smartem-workspace", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a011::c1", "atom_id": "a011", "type": "table", "line": "64-67", "claim": "Mode `all` applies types-of-work + system components; `types-only` applies types only.", "oracle": [], "check": "read getLabelsForRepo", "verdict": "true", "evidence": "getLabelsForRepo (sync-labels.ts:219-222): 'all' returns typesOfWork+systemComponents else typesOfWork", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a012::c1", "atom_id": "a012", "type": "prose", "line": "69", "claim": "Assignments live in `core/github-labels-config.ts`.", "oracle": ["source-path"], "check": "find config file", "verdict": "misleading", "evidence": "Assignments are in core/github-labels.json repos[]; no github-labels-config.ts exists", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a013::c1", "atom_id": "a013", "type": "table", "line": "71-76", "claim": "smartem-devtools=all; smartem-decisions/smartem-frontend/fandanGO-cryoem-dls=types-only.", "oracle": [], "check": "read repos[] modes", "verdict": "true", "evidence": "github-labels.json repos[]: devtools 'all', decisions/frontend/fandanGO 'types-only'", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a014::c1", "atom_id": "a014", "type": "prose", "line": "78", "claim": "System-component labels only apply in the devtools index repo.", "oracle": [], "check": "check which repo has mode all", "verdict": "true", "evidence": "Only smartem-devtools has labels:'all'; others 'types-only', so components only reach devtools", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a017::c1", "atom_id": "a017", "type": "code", "line": "86-88", "claim": "`npm run labels:check` is a defined script.", "oracle": ["cli-cmd"], "check": "grep package.json scripts", "verdict": "true", "evidence": "package.json:4 labels:check -> npx tsx scripts/github/sync-labels.ts --check", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a019::c1", "atom_id": "a019", "type": "code", "line": "92-94", "claim": "`npx tsx scripts/github/sync-labels.ts --check` runs a conformity check.", "oracle": ["source-path"], "check": "read parseArgs", "verdict": "true", "evidence": "parseArgs handles --check (default mode 'check'), sync-labels.ts:235", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "96-100", "claim": "Check mode fetches labels, compares, reports discrepancies, and exits 1 if non-conforming.", "oracle": ["source-path"], "check": "read main() check branch", "verdict": "true", "evidence": "main(): getLabels->compareLabels->printDiff; check mode process.exit(1) on nonConforming (sync-labels.ts:341-347)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a020::c2", "atom_id": "a020", "type": "prose", "line": "96-100", "claim": "Comparison is against labels defined in `core/github-labels-config.ts`.", "oracle": ["source-path"], "check": "trace import in sync-labels.ts", "verdict": "misleading", "evidence": "Compares vs githubLabels from core/index.ts, sourced from core/github-labels.json, not a .ts config", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a022::c1", "atom_id": "a022", "type": "code", "line": "106-108", "claim": "`npm run labels:sync` is a defined script.", "oracle": ["cli-cmd"], "check": "grep package.json scripts", "verdict": "true", "evidence": "package.json:5 labels:sync -> npx tsx scripts/github/sync-labels.ts --sync", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a024::c1", "atom_id": "a024", "type": "code", "line": "112-114", "claim": "`npx tsx scripts/github/sync-labels.ts --sync` performs the sync.", "oracle": ["source-path"], "check": "read parseArgs sync branch", "verdict": "true", "evidence": "parseArgs sets mode 'sync' on --sync (sync-labels.ts:233); main() sync branch present", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "116-119", "claim": "Sync deletes extra labels, creates missing ones, and updates changed description/colour.", "oracle": [], "check": "read main() sync loop", "verdict": "true", "evidence": "sync loop deletes diff.extra, creates diff.missing, updates diff.outdated (sync-labels.ts:361-376)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a027::c1", "atom_id": "a027", "type": "code", "line": "125-128", "claim": "`--repo ` targets specific repos and can be repeated.", "oracle": ["source-path"], "check": "read parseArgs --repo", "verdict": "true", "evidence": "parseArgs pushes each --repo arg to repos[] (sync-labels.ts:239-241); defaults to all when empty", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a029::c1", "atom_id": "a029", "type": "code", "line": "134-136", "claim": "`--verbose` gives detailed output including conforming labels.", "oracle": ["source-path"], "check": "read parseArgs + printDiff", "verdict": "true", "evidence": "parseArgs handles --verbose/-v (line 237); printDiff lists conforming when verbose (295)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "140", "claim": "The `gitflow.yml` workflow automates label management.", "oracle": ["source-path"], "check": "read .github/workflows/gitflow.yml", "verdict": "true", "evidence": ".github/workflows/gitflow.yml (name: Gitflow) runs sync-labels.ts check/sync jobs", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "142", "claim": "Push to main runs `--check` when `core/github-labels-config.ts` or `scripts/github/**` changes.", "oracle": ["source-path"], "check": "read gitflow.yml push paths", "verdict": "misleading", "evidence": "gitflow.yml push paths are core/github-tags-config.ts (nonexistent) + scripts/github/**; real config is github-labels.json", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a031::c2", "atom_id": "a031", "type": "prose", "line": "143", "claim": "Manual dispatch can trigger `--sync` mode via the Actions UI.", "oracle": ["source-path"], "check": "read gitflow.yml workflow_dispatch", "verdict": "true", "evidence": "workflow_dispatch mode input options check/sync; sync-labels job runs --sync when mode==sync (gitflow.yml:46,63)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a032::c1", "atom_id": "a032", "type": "prose", "line": "147-151", "claim": "In Actions, select the 'Gitflow' workflow, Run workflow, choose sync mode.", "oracle": [], "check": "read gitflow.yml name + dispatch input", "verdict": "true", "evidence": "workflow name 'Gitflow'; workflow_dispatch input 'mode' choice with 'sync' option (gitflow.yml:1,6-13)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "157", "claim": "To modify labels, edit `core/github-labels-config.ts`.", "oracle": ["source-path", "cli-cmd"], "check": "find config file", "verdict": "misleading", "evidence": "Labels are edited in core/github-labels.json; no github-labels-config.ts exists", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a034::c2", "atom_id": "a034", "type": "prose", "line": "158-160", "claim": "Verify with `npm run labels:check`, apply with `npm run labels:sync`.", "oracle": ["cli-cmd"], "check": "grep package.json scripts", "verdict": "true", "evidence": "package.json:4-5 define labels:check and labels:sync", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a035::c1", "atom_id": "a035", "type": "prose", "line": "162", "claim": "The CI/CD workflow verifies conformity on push.", "oracle": [], "check": "read gitflow.yml check-labels job", "verdict": "true", "evidence": "check-labels job runs on push events, executes sync-labels.ts --check --verbose (gitflow.yml:24,41)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "166", "claim": "The script supports two auth backends with automatic fallback.", "oracle": [], "check": "read selectBackend", "verdict": "true", "evidence": "selectBackend tries GhCliBackend, falls back to GitHubApiBackend on failure (sync-labels.ts:194-207)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a038::c1", "atom_id": "a038", "type": "code", "line": "172-175", "claim": "`gh auth login` / `gh auth status` are the gh CLI auth commands used.", "oracle": ["cli-cmd"], "check": "read GhCliBackend.checkAuth", "verdict": "true", "evidence": "GhCliBackend runs `gh auth status`, error message advises `gh auth login` (sync-labels.ts:59-63)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "179", "claim": "If gh CLI is unavailable/unauthenticated it falls back to the REST API using a PAT.", "oracle": ["cli-cmd"], "check": "read selectBackend catch branch", "verdict": "true", "evidence": "selectBackend catch -> GitHubApiBackend which uses GITHUB_TOKEN (sync-labels.ts:200-206)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a040::c1", "atom_id": "a040", "type": "code", "line": "181-183", "claim": "`export GITHUB_TOKEN=ghp_...` sets the fallback token.", "oracle": ["env-var"], "check": "grep GITHUB_TOKEN in script", "verdict": "true", "evidence": "GitHubApiBackend reads process.env.GITHUB_TOKEN; script prints same export hint (sync-labels.ts:98,105)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a041::c1", "atom_id": "a041", "type": "prose", "line": "185", "claim": "Generate a token at https://github.com/settings/tokens with `repo` scope.", "oracle": ["url"], "check": "grep URL in script", "verdict": "true", "evidence": "script prints https://github.com/settings/tokens and requires repo scope (sync-labels.ts:105,118)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a043::c1", "atom_id": "a043", "type": "code", "line": "189-194", "claim": "The script prints a header with Mode, Repos list, and selected Backend (e.g. 'gh CLI').", "oracle": ["cli-cmd"], "check": "read main() console output", "verdict": "true", "evidence": "main() logs 'GitHub Labels Sync', Mode, Repos join, Backend name; GhCliBackend.name='gh CLI' (sync-labels.ts:320-325,55)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a046::c1", "atom_id": "a046", "type": "code", "line": "202-205", "claim": "Use `gh auth status` / `gh auth login` to resolve authentication errors.", "oracle": ["cli-cmd"], "check": "read GhCliBackend error path", "verdict": "true", "evidence": "GhCliBackend surfaces gh auth status failure and advises gh auth login (sync-labels.ts:57-64)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a047::c1", "atom_id": "a047", "type": "prose", "line": "207", "claim": "GITHUB_TOKEN can be set as an auth fallback.", "oracle": [], "check": "read GitHubApiBackend", "verdict": "true", "evidence": "GitHubApiBackend auth path activates when gh unavailable, using GITHUB_TOKEN (sync-labels.ts:97-121)", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a048::c1", "atom_id": "a048", "type": "prose", "line": "211", "claim": "The CI/CD sync workflow uses a PAT stored as the `LABEL_SYNC_TOKEN` secret.", "oracle": [], "check": "grep LABEL_SYNC_TOKEN in workflows", "verdict": "true", "evidence": "gitflow.yml:62 sync-labels job sets GH_TOKEN: secrets.LABEL_SYNC_TOKEN", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/github-labels.md::a049::c1", "atom_id": "a049", "type": "prose", "line": "215", "claim": "If a label creation fails because it already exists, the script continues; run `--verbose` for details.", "oracle": [], "check": "read sync loop error handling", "verdict": "incorrect", "evidence": "Sync loop has no try/catch (sync-labels.ts:361-376); createLabel error propagates to main().catch -> process.exit(1); --verbose only lists conforming labels", "confidence": "high", "file": "docs/development/github-labels.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a001::c1", "atom_id": "docs/development/index.md::a001", "type": "code", "line": "5-14", "claim": "The toctree references six existing docs: contributing, tools, generate-docs, e2e-simulation, local-keycloak, github-labels.", "oracle": "source-path", "check": "ls docs/development/", "verdict": "true", "evidence": "All six .md present in smartem-devtools/docs/development/ (contributing, tools, generate-docs, e2e-simulation, local-keycloak, github-labels)", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a003::c1", "atom_id": "docs/development/index.md::a003", "type": "prose", "line": "20", "claim": "contributing.md exists and covers the contributing guide (code style, PRs, reviews).", "oracle": "source-path", "check": "cat contributing.md", "verdict": "true", "evidence": "docs/development/contributing.md includes ../../.github/CONTRIBUTING.md (contributing guide)", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a004::c1", "atom_id": "docs/development/index.md::a004", "type": "prose", "line": "24", "claim": "tools.md exists and documents utility tools for development, testing, and maintenance.", "oracle": "source-path", "check": "head tools.md", "verdict": "true", "evidence": "tools.md H1 'Development Tools' - 'utility tools for development, testing, and maintenance'", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a004::c2", "atom_id": "docs/development/index.md::a004", "type": "prose", "line": "25", "claim": "e2e-simulation.md exists and covers end-to-end development simulation setup.", "oracle": "source-path", "check": "head e2e-simulation.md", "verdict": "true", "evidence": "e2e-simulation.md H1 'End-to-End Development Test Runs' describing repeatable E2E test workflow", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a004::c3", "atom_id": "docs/development/index.md::a004", "type": "prose", "line": "26", "claim": "local-keycloak.md exists and covers a self-contained Keycloak mock for frontend auth development.", "oracle": "source-path", "check": "head local-keycloak.md", "verdict": "true", "evidence": "local-keycloak.md H1 'Local Keycloak for SmartEM frontend dev' - 'self-contained Keycloak mock'", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a005::c1", "atom_id": "docs/development/index.md::a005", "type": "prose", "line": "30", "claim": "generate-docs.md exists and covers building the documentation locally.", "oracle": "source-path", "check": "head generate-docs.md", "verdict": "true", "evidence": "generate-docs.md H1 'Generate Documentation' - Markdown in docs/ converted during build", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/index.md::a005::c2", "atom_id": "docs/development/index.md::a005", "type": "prose", "line": "31", "claim": "github-labels.md exists and covers synchronising GitHub labels across repositories.", "oracle": "source-path", "check": "head github-labels.md", "verdict": "true", "evidence": "github-labels.md H1 'How to Sync GitHub Labels' - manage labels across SmartEM repositories", "confidence": "high", "file": "docs/development/index.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "A self-contained Keycloak mock lives under `keycloak-mock/` in the smartem-devtools repo.", "oracle": "k8s", "check": "ls keycloak-mock/ in smartem-devtools", "verdict": "true", "evidence": "smartem-devtools/keycloak-mock/ contains dls-realm.json, docker-compose.yml, keycloak.yaml, kustomization.yaml, README.md", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a000::c2", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The mock is offered in two forms (Docker Compose and Kubernetes manifests), both reading from the same realm export.", "oracle": "k8s", "check": "read docker-compose.yml + kustomization.yaml", "verdict": "true", "evidence": "docker-compose.yml mounts ./dls-realm.json; kustomization.yaml configMapGenerator uses dls-realm.json", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "5", "claim": "The auth-flow architecture is documented in ../architecture/keycloak-spa-authentication.md.", "oracle": "source-path", "check": "ls docs/architecture/keycloak-spa-authentication.md", "verdict": "true", "evidence": "docs/architecture/keycloak-spa-authentication.md exists (5042 bytes)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9", "claim": "DLS identity servers identity.diamond.ac.uk / identity-test.diamond.ac.uk require explicit registration of every client and origin.", "oracle": "url", "check": "external identity provider; no local artifact", "verdict": "unverifiable", "evidence": "External DLS infrastructure not present in workspace; cannot confirm registration policy", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a002::c2", "atom_id": "a002", "type": "prose", "line": "9", "claim": "Local frontend development runs on http://localhost:5173.", "oracle": "port", "check": "vite config + realm redirectUris", "verdict": "true", "evidence": "vite.config.ts sets no port (Vite default 5173); realm redirectUris include http://localhost:5173/*", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a006::c1", "atom_id": "a006", "type": "table", "line": "22-26", "claim": "Kubernetes form is brought up via `./scripts/k8s/dev-k8s.sh up`; Compose via `docker compose up -d`.", "oracle": "cli-cmd", "check": "ls scripts/k8s/dev-k8s.sh; read docker-compose.yml", "verdict": "true", "evidence": "scripts/k8s/dev-k8s.sh exists; docker-compose.yml valid for `docker compose up -d` (timing ~20s is narration)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "28", "claim": "Both forms read from the same keycloak-mock/dls-realm.json.", "oracle": "source-path", "check": "grep dls-realm.json in compose + kustomization", "verdict": "true", "evidence": "docker-compose.yml mounts ./dls-realm.json; kustomization.yaml configMapGenerator files: dls-realm.json", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a009::c1", "atom_id": "a009", "type": "code", "line": "34-37", "claim": "Compose form is started with `cd keycloak-mock && docker compose up -d`.", "oracle": "cli-cmd", "check": "keycloak-mock/docker-compose.yml present", "verdict": "true", "evidence": "keycloak-mock/docker-compose.yml exists with a keycloak service; command is valid from that dir", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a010::c1", "atom_id": "a010", "type": "prose", "line": "39", "claim": "Keycloak starts on http://localhost:8080 in dev mode and imports the dls realm.", "oracle": "port", "check": "read docker-compose.yml args/ports", "verdict": "true", "evidence": "compose: ports 8080:8080, command start-dev --import-realm --http-port=8080; realm dls in dls-realm.json", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a010::c2", "atom_id": "a010", "type": "prose", "line": "39", "claim": "Bootstrap admin credentials are admin/admin.", "oracle": "source-path", "check": "read compose env", "verdict": "true", "evidence": "KC_BOOTSTRAP_ADMIN_USERNAME: admin, KC_BOOTSTRAP_ADMIN_PASSWORD: admin in docker-compose.yml", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a011::c1", "atom_id": "a011", "type": "prose", "line": "41", "claim": "State is not persisted between container lifecycles; each up reimports the realm.", "oracle": "source-path", "check": "inspect compose volumes", "verdict": "true", "evidence": "compose mounts only read-only realm import (./dls-realm.json:ro); no data volume => no persistence", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a011::c2", "atom_id": "a011", "type": "prose", "line": "41", "claim": "Realm can be exported via `kc.sh export` from inside the running container.", "oracle": "cli-cmd", "check": "Keycloak CLI + a037 export block", "verdict": "true", "evidence": "kc.sh export is the standard Keycloak 26 export command; a037 gives the concrete invocation", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a013::c1", "atom_id": "a013", "type": "prose", "line": "47", "claim": "keycloak-mock/kustomization.yaml is referenced as a base by k8s/environments/development/kustomization.yaml.", "oracle": "k8s", "check": "read development kustomization resources", "verdict": "true", "evidence": "k8s/environments/development/kustomization.yaml resources include ../../../keycloak-mock", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a014::c1", "atom_id": "a014", "type": "code", "line": "49-51", "claim": "`./scripts/k8s/dev-k8s.sh up` brings up the dev stack including Keycloak.", "oracle": "k8s", "check": "grep dev-k8s.sh apply logic", "verdict": "true", "evidence": "dev-k8s.sh: K8S_ENV_PATH=k8s/environments/development, `kubectl apply -k \"$K8S_ENV_PATH\"`; prints 'Keycloak (mock): http://localhost:30090'", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "55-56", "claim": "Keycloak is reachable at http://keycloak-service:8080 (ClusterIP) inside the cluster.", "oracle": "port", "check": "read keycloak.yaml Service", "verdict": "true", "evidence": "keycloak.yaml Service keycloak-service type ClusterIP port 8080", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a016::c2", "atom_id": "a016", "type": "prose", "line": "55-56", "claim": "Keycloak is reachable at :30090 from outside (NodePort).", "oracle": "port", "check": "read keycloak.yaml NodePort Service", "verdict": "true", "evidence": "keycloak.yaml Service keycloak-external type NodePort nodePort 30090", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a018::c1", "atom_id": "a018", "type": "code", "line": "60-62", "claim": "Keycloak alone can be deployed with `kubectl apply -k keycloak-mock`.", "oracle": "k8s", "check": "keycloak-mock/kustomization.yaml present", "verdict": "true", "evidence": "keycloak-mock/kustomization.yaml is a valid kustomize root (resources: keycloak.yaml)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a019::c1", "atom_id": "a019", "type": "prose", "line": "64", "claim": "The kustomization generates a ConfigMap from dls-realm.json and mounts it at /opt/keycloak/data/import.", "oracle": "k8s", "check": "read kustomization + keycloak.yaml volumeMounts", "verdict": "true", "evidence": "kustomization configMapGenerator keycloak-realm from dls-realm.json; keycloak.yaml mounts realm-import at /opt/keycloak/data/import", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a019::c2", "atom_id": "a019", "type": "prose", "line": "64", "claim": "Updating the realm needs a re-apply plus `kubectl rollout restart deployment/keycloak`.", "oracle": "cli-cmd", "check": "read keycloak.yaml Deployment name", "verdict": "true", "evidence": "keycloak.yaml Deployment metadata.name: keycloak", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "68", "claim": "The realm export lives at keycloak-mock/dls-realm.json.", "oracle": "source-path", "check": "ls keycloak-mock/dls-realm.json", "verdict": "true", "evidence": "smartem-devtools/keycloak-mock/dls-realm.json exists", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c1", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "Realm is `dls`, matching the SPA runtime config.json default.", "oracle": "source-path", "check": "grep realm in dls-realm.json + public/config.json", "verdict": "true", "evidence": "dls-realm.json realm: dls; apps/smartem/public/config.json keycloak.realm: dls", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c2", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "SmartEM_User is a public client using standard flow with PKCE S256.", "oracle": "source-path", "check": "read SmartEM_User in dls-realm.json", "verdict": "true", "evidence": "dls-realm.json: SmartEM_User publicClient true, standardFlowEnabled true, pkce.code.challenge.method S256", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c3", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "SmartEM_Agent is a confidential client using the client-credentials grant.", "oracle": "source-path", "check": "read SmartEM_Agent in dls-realm.json", "verdict": "true", "evidence": "dls-realm.json: SmartEM_Agent publicClient false, clientAuthenticatorType client-secret, serviceAccountsEnabled true", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c4", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "SmartEM_User valid redirect URIs are localhost:5173/*, 5174/*, and 30100/*.", "oracle": "url", "check": "read redirectUris in dls-realm.json", "verdict": "true", "evidence": "dls-realm.json redirectUris: http://localhost:5173/*, :5174/*, :30100/* (note: 5174 'legacy app' retired per #136, URI still present)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c5", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "Web origins are the same hosts as the redirect URIs.", "oracle": "url", "check": "read webOrigins in dls-realm.json", "verdict": "true", "evidence": "dls-realm.json webOrigins: http://localhost:5173, :5174, :30100", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c6", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "A custom fedId claim mapper exists; the frontend AuthProvider reads idTokenParsed.fedId.", "oracle": "source-path", "check": "realm protocolMappers + KeycloakAuthProvider.tsx", "verdict": "true", "evidence": "dls-realm.json fedId oidc-usermodel-attribute-mapper; KeycloakAuthProvider.tsx:28 fedId: keycloak.idTokenParsed.fedId", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a021::c7", "atom_id": "a021", "type": "prose", "line": "70-78", "claim": "Seeded user devuser/devpass with fedId dev12345.", "oracle": "source-path", "check": "read users in dls-realm.json", "verdict": "true", "evidence": "dls-realm.json user devuser, credential password devpass, attributes fedId [dev12345]", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a022::c1", "atom_id": "a022", "type": "prose", "line": "80", "claim": "More users/roles are added by editing dls-realm.json directly.", "oracle": "source-path", "check": "realm is imported from dls-realm.json", "verdict": "true", "evidence": "Both forms import dls-realm.json (users array); editing it is the seeding mechanism", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a023::c1", "atom_id": "a023", "type": "prose", "line": "84", "claim": "The SPA loads Keycloak config at boot from a runtime /config.json, not from VITE_* env vars.", "oracle": "source-path", "check": "read main.tsx loadRuntimeConfig", "verdict": "true", "evidence": "main.tsx:22 fetch('/config.json', {cache:'no-store'}) then setRuntimeConfig; auth/config.ts reads runtime config", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a023::c2", "atom_id": "a023", "type": "prose", "line": "84", "claim": "`npm run dev:smartem` serves config.json via Vite.", "oracle": "cli-cmd", "check": "grep scripts in package.json", "verdict": "misleading", "evidence": "No dev:smartem script; root package.json now has bare `dev` (npm run dev -w @smartem/app); :smartem aliases removed with legacy app (#136)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a023::c3", "atom_id": "a023", "type": "prose", "line": "84", "claim": "In dev the config file is served from apps/smartem/public/config.json.", "oracle": "source-path", "check": "ls apps/smartem/public/config.json", "verdict": "true", "evidence": "apps/smartem/public/config.json exists (served at /config.json by Vite)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a023::c4", "atom_id": "a023", "type": "prose", "line": "84", "claim": "In k8s the SPA pod's nginx serves config.json from the smartem-frontend-config ConfigMap, overriding the image default.", "oracle": "k8s", "check": "read k8s smartem-frontend.yaml", "verdict": "true", "evidence": "smartem-frontend.yaml ConfigMap smartem-frontend-config -> mount /usr/share/nginx/html/config.json, comment 'Overrides the placeholder config.json shipped in the image'", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "86", "claim": "The config to edit is smartem-frontend/apps/smartem/public/config.json.", "oracle": "source-path", "check": "ls the path", "verdict": "true", "evidence": "apps/smartem/public/config.json exists in smartem-frontend", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a025::c1", "atom_id": "a025", "type": "code", "line": "88-96", "claim": "config.json has keycloak.url http://localhost:30090, realm dls, clientId SmartEM_User.", "oracle": "url", "check": "cat apps/smartem/public/config.json", "verdict": "true", "evidence": "public/config.json keycloak: {url: http://localhost:30090, realm: dls, clientId: SmartEM_User}", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "98", "claim": "Use :30090 for the k3s NodePort and :8080 for docker-compose or a port-forwarded pod.", "oracle": "port", "check": "keycloak.yaml NodePort + compose ports", "verdict": "true", "evidence": "NodePort 30090 (keycloak-external); compose exposes 8080:8080", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a027::c1", "atom_id": "a027", "type": "prose", "line": "100", "claim": "config.json is fetched with cache:'no-store' and applied before the SPA mounts, so a reload picks up edits.", "oracle": "source-path", "check": "read main.tsx boot sequence", "verdict": "true", "evidence": "main.tsx:22 fetch('/config.json',{cache:'no-store'}); loadRuntimeConfig resolves before createRoot().render()", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a029::c1", "atom_id": "a029", "type": "code", "line": "106-109", "claim": "Mock mode is started with `cd smartem-frontend && npm run dev:smartem:mock`.", "oracle": "cli-cmd", "check": "grep scripts in package.json", "verdict": "misleading", "evidence": "No dev:smartem:mock script; equivalent is `npm run dev:mock` (root -> @smartem/app: VITE_ENABLE_MOCKS=true vite); :smartem aliases removed (#136)", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "111", "claim": "Mock mode sets VITE_ENABLE_MOCKS=true at build time.", "oracle": "env-var", "check": "read apps/smartem package.json dev:mock", "verdict": "true", "evidence": "apps/smartem package.json dev:mock = 'VITE_ENABLE_MOCKS=true vite'; main.tsx reads import.meta.env.VITE_ENABLE_MOCKS", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "113-114", "claim": "MSW intercepts API requests and answers from generated faker fixtures at packages/api/src/generated/default/default.msw.ts.", "oracle": "source-path", "check": "read mocks/browser.ts + default.msw.ts", "verdict": "true", "evidence": "mocks/browser.ts setupWorker(...mswHandlers.getDefaultMock()); default.msw.ts exists with faker handlers (http.get('*/acquisitions'...))", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a031::c2", "atom_id": "a031", "type": "prose", "line": "113-114", "claim": "AuthGate swaps KeycloakAuthProvider for MockAuthProvider in mock mode; /config.json is not fetched and Keycloak is never contacted.", "oracle": "source-path", "check": "read AuthGate.tsx + main.tsx", "verdict": "true", "evidence": "AuthGate.tsx:10 if VITE_ENABLE_MOCKS==='true' return MockAuthProvider; main.tsx:64 mock branch calls startMockWorker() not loadRuntimeConfig()", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a031::c3", "atom_id": "a031", "type": "prose", "line": "113-114", "claim": "The mock identity is Mock User / mock@example.diamond.ac.uk / fedId mock001 with token mock-token.", "oracle": "source-path", "check": "read MockAuthProvider.tsx", "verdict": "true", "evidence": "MockAuthProvider.tsx: name 'Mock User', email 'mock@example.diamond.ac.uk', fedId 'mock001', MOCK_TOKEN 'mock-token'", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a032::c1", "atom_id": "a032", "type": "prose", "line": "116", "claim": "login() and logout() are no-ops in mock mode.", "oracle": "source-path", "check": "read MockAuthProvider.tsx", "verdict": "true", "evidence": "MockAuthProvider.tsx:17-18 login: () => {}, logout: () => {}", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a033::c1", "atom_id": "a033", "type": "prose", "line": "118", "claim": "When Keycloak is unreachable the SPA shows the sign-in screen with a connection-error message.", "oracle": "source-path", "check": "read AuthGate.tsx SignInScreen", "verdict": "true", "evidence": "AuthGate.tsx AuthBoundary renders SignInScreen with auth.error prop; SignInScreen displays error text", "confidence": "medium", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "122", "claim": "Editing dls-realm.json directly affects both forms since they read the same file.", "oracle": "source-path", "check": "compose + kustomization both use dls-realm.json", "verdict": "true", "evidence": "compose mounts ./dls-realm.json; kustomization configMapGenerator from dls-realm.json", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a035::c1", "atom_id": "a035", "type": "prose", "line": "124-125", "claim": "Kubernetes reload is `kubectl apply -k keycloak-mock` then `kubectl rollout restart deployment/keycloak -n smartem-decisions`.", "oracle": "k8s", "check": "read keycloak.yaml namespace + kustomization", "verdict": "true", "evidence": "keycloak.yaml Deployment keycloak, namespace smartem-decisions; kustomization has disableNameSuffixHash: true", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a037::c1", "atom_id": "a037", "type": "code", "line": "129-136", "claim": "Realm export uses `docker exec smartem-keycloak /opt/keycloak/bin/kc.sh export --realm dls ...` then docker cp of dls-realm.json.", "oracle": "cli-cmd", "check": "compose container_name + kc.sh path", "verdict": "true", "evidence": "compose container_name smartem-keycloak; Keycloak 26 kc.sh at /opt/keycloak/bin; --users realm_file writes dls-realm.json", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/local-keycloak.md::a038::c1", "atom_id": "a038", "type": "prose", "line": "140-142", "claim": "The mock runs in dev mode, HTTP only (no TLS), with no persistent storage.", "oracle": "source-path", "check": "read compose/keycloak.yaml + realm", "verdict": "true", "evidence": "start-dev, http-port 8080, sslRequired 'none' in realm, no data volume => dev/HTTP-only/no persistence", "confidence": "high", "file": "docs/development/local-keycloak.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a000::c1", "atom_id": "docs/development/tools.md::a000", "type": "prose", "line": "3", "claim": "A collection of utility tools exists for development/testing/maintenance of the SmartEM Decisions project.", "oracle": [], "check": "listed smartem-decisions/tools/", "verdict": "true", "evidence": "smartem-decisions/tools/ has 8 utility scripts (format_xml, find_foilhole_duplicates, external_message_simulator, etc.)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a002::c1", "atom_id": "docs/development/tools.md::a002", "type": "prose", "line": "9", "claim": "A tool transforms single-line XML and .dm files into human-readable indented format.", "oracle": [], "check": "read format_xml.py", "verdict": "true", "evidence": "format_xml.py XML_EXTENSIONS={.dm,.xml}, toprettyxml(indent=' ')", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a003::c1", "atom_id": "docs/development/tools.md::a003", "type": "code", "line": "11-23", "claim": "format_xml.py accepts one or more directory paths, a -r recursive flag, and --help.", "oracle": ["source-path", "cli-cmd"], "check": "read typer signature", "verdict": "true", "evidence": "tools/format_xml.py: paths:list[Path] positional, Option('--recursive','-r'); typer gives --help. Example dataset paths are illustrative", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a005::c1", "atom_id": "docs/development/tools.md::a005", "type": "prose", "line": "29", "claim": "A tool identifies duplicate foil hole manifests within directory structures.", "oracle": [], "check": "read find_foilhole_duplicates.py", "verdict": "true", "evidence": "typer app help: 'Find FoilHole XML files with matching numbers but different timestamps'", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a006::c1", "atom_id": "docs/development/tools.md::a006", "type": "code", "line": "31-37", "claim": "find_foilhole_duplicates.py supports --help and a positional directory argument.", "oracle": ["source-path", "cli-cmd"], "check": "read typer signature", "verdict": "true", "evidence": "tools/find_foilhole_duplicates.py: typer.Argument positional path; --help via typer. Example path tests/testdata/bi37708-28 is illustrative (absent)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a007::c1", "atom_id": "docs/development/tools.md::a007", "type": "prose", "line": "41", "claim": "Files matching a pattern can be listed sorted by size.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "a008 rg+xargs ls+sort -k5 -rn pipeline does exactly this", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a008::c1", "atom_id": "docs/development/tools.md::a008", "type": "code", "line": "43-47", "claim": "A generic rg/xargs/ls/sort/awk one-liner lists GridSquare_*.dm files sorted by size.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "Standard ripgrep+ls pipeline, syntactically valid; example dir illustrative", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a010::c1", "atom_id": "docs/development/tools.md::a010", "type": "prose", "line": "53", "claim": "Test dataset composition can be analysed by file type.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "a011 find|sed|uniq -c pipeline counts extensions", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a011::c1", "atom_id": "docs/development/tools.md::a011", "type": "code", "line": "55-63", "claim": "A find|sed|grep|sort|uniq pipeline lists distinct file extensions with counts.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "Standard POSIX pipeline, valid", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a012::c1", "atom_id": "docs/development/tools.md::a012", "type": "prose", "line": "67", "claim": "Dataset storage can be reduced while preserving directory structure.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "a013 truncate -s 0 empties files but keeps structure", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a013::c1", "atom_id": "docs/development/tools.md::a013", "type": "code", "line": "69-72", "claim": "A find -exec truncate -s 0 command empties image/data files in place.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "Valid find with -name *.jpg/*.png/*.mrc and truncate", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a014::c1", "atom_id": "docs/development/tools.md::a014", "type": "prose", "line": "74", "claim": "The truncate command permanently removes file contents.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "truncate -s 0 irreversibly empties files", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a016::c1", "atom_id": "docs/development/tools.md::a016", "type": "prose", "line": "80", "claim": "Directory size/file-count can be monitored during acquisition/processing.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "a017 watch du/find pipeline", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a017::c1", "atom_id": "docs/development/tools.md::a017", "type": "code", "line": "82-85", "claim": "A watch -n 1 command reports directory size and file count each second.", "oracle": [], "check": "generic shell reasoning", "verdict": "true", "evidence": "Valid watch+du+find wc -l one-liner", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a020::c1", "atom_id": "docs/development/tools.md::a020", "type": "prose", "line": "93", "claim": "A CLI tool simulates external data-processing messages from ML/image-processing systems.", "oracle": [], "check": "read external_message_simulator.py", "verdict": "true", "evidence": "tools/external_message_simulator.py is a typer app publishing motion_correction/ctf/particle/model messages", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a021::c1", "atom_id": "docs/development/tools.md::a021", "type": "code", "line": "95-112", "claim": "The simulator subcommands take the documented flags, incl. motion-correction/ctf-complete --foilhole-id.", "oracle": ["source-path", "cli-cmd"], "check": "read typer @app.command signatures", "verdict": "misleading", "evidence": "motion_correction (L265) & ctf_complete (L289) expose --gridsquare-id, NOT --foilhole-id as documented; other subcmds/flags correct", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a022::c1", "atom_id": "docs/development/tools.md::a022", "type": "prose", "line": "114-121", "claim": "The seven listed message types (MOTION_CORRECTION_COMPLETE ... MODEL_PARAMETER_UPDATE) exist.", "oracle": [], "check": "grep message-type strings", "verdict": "true", "evidence": "All 7 names present as generate_* docstrings in external_message_simulator.py (L75-233)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a023::c1", "atom_id": "docs/development/tools.md::a023", "type": "prose", "line": "125", "claim": "An example client tests agent-backend communication via Server-Sent Events.", "oracle": [], "check": "read sse_client_example.py", "verdict": "true", "evidence": "tools/sse_client_example.py uses smartem_backend.api_client.SSEAgentClient for SSE instructions", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a024::c1", "atom_id": "docs/development/tools.md::a024", "type": "code", "line": "127-136", "claim": "sse_client_example.py connects to the SSE stream and acknowledges received instructions.", "oracle": ["source-path", "cli-cmd"], "check": "grep sse_client_example.py", "verdict": "true", "evidence": "SSEAgentClient + client.acknowledge_instruction(status='processed') calls present", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a028::c1", "atom_id": "docs/development/tools.md::a028", "type": "prose", "line": "150", "claim": "check_schema_drift creates a temp Postgres DB, applies migrations, autogenerates to detect drift; also run in CI via _schema_drift.yml.", "oracle": ["source-path"], "check": "read check_schema_drift.py + workflow", "verdict": "true", "evidence": "CREATE DATABASE (L67), run_existing_migrations, check_for_new_migrations autogenerate; _schema_drift.yml L50 runs the tool", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a029::c1", "atom_id": "docs/development/tools.md::a029", "type": "code", "line": "152-155", "claim": "Running tools/check_schema_drift.py requires a Postgres instance (e.g. from dev-k8s.sh up).", "oracle": ["source-path", "cli-cmd", "k8s"], "check": "tool exists + dev-k8s.sh located", "verdict": "true", "evidence": "tools/check_schema_drift.py present; dev-k8s.sh at smartem-devtools/scripts/k8s/dev-k8s.sh (moved out of smartem-decisions)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a030::c1", "atom_id": "docs/development/tools.md::a030", "type": "prose", "line": "157", "claim": "Exit 0 means schema in sync; exit 1 means drift detected or error.", "oracle": [], "check": "read exit logic", "verdict": "true", "evidence": "has_drift -> sys.exit(1); else prints 'in sync' and returns (0); setup/migration errors sys.exit(1)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a031::c1", "atom_id": "docs/development/tools.md::a031", "type": "prose", "line": "161", "claim": "db_table_totals connects to Postgres and outputs row counts for every SmartEM table.", "oracle": [], "check": "read db_table_totals.py", "verdict": "true", "evidence": "uses setup_postgres_connection() and gets row counts for all tables (docstring L4, L34-41)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a032::c1", "atom_id": "docs/development/tools.md::a032", "type": "code", "line": "163-165", "claim": "tools/db_table_totals.py is a runnable script.", "oracle": ["source-path", "cli-cmd"], "check": "ls tools/", "verdict": "true", "evidence": "tools/db_table_totals.py exists (executable)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a034::c1", "atom_id": "docs/development/tools.md::a034", "type": "prose", "line": "171", "claim": "generate_api_docs.py generates API documentation into docs/api/.", "oracle": [], "check": "read generate_api_docs.py", "verdict": "true", "evidence": "writes docs/api/athena/swagger.json and docs/api/smartem/swagger.json", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a035::c1", "atom_id": "docs/development/tools.md::a035", "type": "prose", "line": "173", "claim": "For Athena it copies docs/athena-decision-service-api-spec.json and adds a local mock server entry.", "oracle": ["source-path"], "check": "read use_original_athena_spec()", "verdict": "true", "evidence": "source=docs/athena-decision-service-api-spec.json; inserts mock_server url http://localhost:8000; spec file exists in smartem-devtools/docs", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a036::c1", "atom_id": "docs/development/tools.md::a036", "type": "code", "line": "175-177", "claim": "tools/generate_api_docs.py is a runnable script.", "oracle": ["source-path", "cli-cmd"], "check": "ls tools/", "verdict": "true", "evidence": "tools/generate_api_docs.py exists in smartem-decisions/tools/", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a037::c1", "atom_id": "docs/development/tools.md::a037", "type": "prose", "line": "179", "claim": "Athena output is written to docs/api/athena/swagger.json.", "oracle": ["source-path"], "check": "read generate_api_docs.py dest", "verdict": "true", "evidence": "dest = Path('docs/api/athena/swagger.json'); file present in smartem-devtools/docs/api/athena/", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a038::c1", "atom_id": "docs/development/tools.md::a038", "type": "prose", "line": "181-184", "claim": "smartem-decisions is canonical publisher (ADR 0020); docs/api/smartem & webui/public/api/smartem swagger.json are caches refreshed by the 'Sync OpenAPI spec from backend' workflow.", "oracle": ["source-path"], "check": "workflow name + ADR 0020 + paths", "verdict": "true", "evidence": "sync-openapi-spec.yml name 'Sync OpenAPI spec from backend' writes both paths from smartem-decisions CANONICAL_SPEC_URL; ADR 0020=OpenAPI Spec Pipeline. (smartem-decisions tool still has generate_smartem_from_implementation, but devtools does not generate it)", "confidence": "medium", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a040::c1", "atom_id": "docs/development/tools.md::a040", "type": "prose", "line": "190", "claim": "makeiso.sh creates an ISO from a source directory using mkisofs or genisoimage.", "oracle": [], "check": "read makeiso.sh", "verdict": "true", "evidence": "makeiso.sh probes mkisofs then genisoimage (L20-23)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a041::c1", "atom_id": "docs/development/tools.md::a041", "type": "code", "line": "192-197", "claim": "Usage is ./tools/makeiso.sh [output.iso].", "oracle": ["source-path"], "check": "read makeiso.sh usage", "verdict": "true", "evidence": "Usage line: '$0 source_directory [output.iso]'; example path tests/testdata/bi37708-28 illustrative", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a042::c1", "atom_id": "docs/development/tools.md::a042", "type": "prose", "line": "199", "claim": "If the output filename is omitted, it defaults to .iso.", "oracle": [], "check": "read makeiso.sh default", "verdict": "true", "evidence": "ISO_NAME=\"${2:-${SOURCE_DIR%/}.iso}\" (L10)", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a045::c1", "atom_id": "docs/development/tools.md::a045", "type": "code", "line": "207-213", "claim": "pre-commit run --files and pre-commit run --all-files are valid commands.", "oracle": [], "check": "standard pre-commit CLI", "verdict": "true", "evidence": "Standard pre-commit invocations; REPO-GUIDELINES mandate pre-commit workflow in smartem-decisions", "confidence": "medium", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/development/tools.md::a046::c1", "atom_id": "docs/development/tools.md::a046", "type": "code", "line": "217-227", "claim": "uv run pytest, uv run pyright src tests, uv run ruff check/format are the test/quality commands.", "oracle": ["cli-cmd"], "check": "standard tooling + src/tests dirs", "verdict": "true", "evidence": "smartem-decisions has src/ and tests/; pytest/pyright/ruff are the stack per REPO-GUIDELINES", "confidence": "high", "file": "docs/development/tools.md", "doc_kind": "howto"} +{"claim_id": "docs/getting-started/for-developers.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "5", "claim": "SmartEM Decisions requires Python 3.12 or later.", "oracle": "source-path", "check": "grep requires-python in pyproject.toml", "verdict": "true", "evidence": "pyproject.toml:17 requires-python = \">=3.12\"", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a002::c1", "atom_id": "a002", "type": "code", "line": "7-9", "claim": "`python3 --version` prints the installed Python version.", "oracle": "cli-cmd", "check": "standard interpreter flag", "verdict": "true", "evidence": "python3 --version is a standard CPython CLI flag", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "11", "claim": "The project specifically requires Python 3.12+ for advanced typing features.", "oracle": "source-path", "check": "pyproject requires-python", "verdict": "true", "evidence": "pyproject.toml:17 requires-python = \">=3.12\"", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "15", "claim": "The project recommends uv for Python package management.", "oracle": "cli-cmd", "check": "presence of uv.lock and uv commands in repo", "verdict": "true", "evidence": "uv.lock present at repo root; uv sync/run used throughout pyproject workflow", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a005::c1", "atom_id": "a005", "type": "code", "line": "19-28", "claim": "uv can be installed via curl install.sh, pip install uv, or brew install uv.", "oracle": "cli-cmd", "check": "standard documented uv install methods", "verdict": "true", "evidence": "These are the canonical Astral uv install methods (external tool)", "confidence": "medium", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a008::c1", "atom_id": "a008", "type": "code", "line": "36-43", "claim": "Clone github.com/DiamondLightSource/smartem-decisions then run `uv sync --extra all`.", "oracle": "url", "check": "pyproject project.urls + optional-dependencies", "verdict": "true", "evidence": "pyproject GitHub URL matches; 'all' extra defined pyproject.toml:94", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a009::c1", "atom_id": "a009", "type": "prose", "line": "45", "claim": "uv sync creates a .venv directory and installs all dependencies including dev tools.", "oracle": "cli-cmd", "check": "uv default venv behaviour + 'all' extra includes dev", "verdict": "true", "evidence": "all = [smartem-decisions[dev,...]] pyproject.toml:94-96; uv creates .venv by default", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a010::c1", "atom_id": "a010", "type": "prose", "line": "49", "claim": "Prefixing commands with `uv run` executes them in the virtual environment.", "oracle": "cli-cmd", "check": "standard uv run semantics", "verdict": "true", "evidence": "uv run executes in the managed .venv (standard uv behaviour)", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a011::c1", "atom_id": "a011", "type": "code", "line": "51-60", "claim": "`uv run pytest`, `uv run pyright src tests`, `uv run ruff check` are valid dev commands.", "oracle": "cli-cmd", "check": "dev extra deps + src/tests dirs exist", "verdict": "true", "evidence": "pyproject dev extra lists pyright:25 pytest:26 ruff:29; src/ and tests/ dirs present", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a013::c1", "atom_id": "a013", "type": "code", "line": "66-68", "claim": "The library can be installed via `python3 -m pip install smartem-decisions`.", "oracle": "cli-cmd", "check": "pyproject package name matches; PyPI availability not checkable offline", "verdict": "unverifiable", "evidence": "pyproject.toml:6 name=\"smartem-decisions\" matches; PyPI publication not verifiable here", "confidence": "medium", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a015::c1", "atom_id": "a015", "type": "code", "line": "74-80", "claim": "`uv sync --extra all` and `pip install -e .[all]` install with all dependencies.", "oracle": "cli-cmd", "check": "optional-dependencies 'all' key", "verdict": "true", "evidence": "pyproject.toml:94 all = [...]; both invocations reference the existing extra", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a017::c1", "atom_id": "a017", "type": "code", "line": "86-89", "claim": "smartem_backend, smartem_agent, smartem_common are importable Python modules.", "oracle": "cli-cmd", "check": "package __init__.py presence", "verdict": "true", "evidence": "src/smartem_backend/__init__.py, smartem_agent/__init__.py, smartem_common/__init__.py all present", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a019::c1", "atom_id": "a019", "type": "code", "line": "93-96", "claim": "`python -m smartem_agent --help` runs the agent CLI.", "oracle": "cli-cmd", "check": "read __main__.py entrypoint", "verdict": "true", "evidence": "__main__.py:33 typer app epu_data_intake_cli; bottom `if __name__==__main__: epu_data_intake_cli()`", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/for-developers.md::a021::c1", "atom_id": "a021", "type": "prose", "line": "102-105", "claim": "Next-step links to backend/api-server, operations/run-container, development/tools, operations/logging exist.", "oracle": "source-path", "check": "ls target docs in smartem-devtools/docs", "verdict": "true", "evidence": "All 4 targets exist under smartem-devtools/docs (backend/api-server.md, operations/run-container.md, development/tools.md, operations/logging.md)", "confidence": "high", "file": "docs/getting-started/for-developers.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/index.md::a001::c1", "atom_id": "docs/getting-started/index.md::a001", "type": "code", "line": "7-11", "claim": "The toctree references a sibling document 'for-developers'.", "oracle": "source-path", "check": "ls getting-started/for-developers.md", "verdict": "true", "evidence": "docs/getting-started/for-developers.md exists (2557 bytes)", "confidence": "high", "file": "docs/getting-started/index.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/index.md::a003::c1", "atom_id": "docs/getting-started/index.md::a003", "type": "prose", "line": "17", "claim": "The link target for-developers.md (Development Environment Setup) exists.", "oracle": "source-path", "check": "ls getting-started/for-developers.md", "verdict": "true", "evidence": "docs/getting-started/for-developers.md exists", "confidence": "high", "file": "docs/getting-started/index.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/index.md::a005::c1", "atom_id": "docs/getting-started/index.md::a005", "type": "prose", "line": "23", "claim": "The link target ../operations/index.md (Operations section) exists.", "oracle": "source-path,k8s", "check": "ls operations/index.md", "verdict": "true", "evidence": "docs/operations/index.md exists (1181 bytes)", "confidence": "high", "file": "docs/getting-started/index.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/index.md::a007::c1", "atom_id": "docs/getting-started/index.md::a007", "type": "prose", "line": "29", "claim": "The link target ../backend/api-documentation.md exists.", "oracle": "source-path", "check": "ls backend/api-documentation.md", "verdict": "true", "evidence": "docs/backend/api-documentation.md exists (5743 bytes)", "confidence": "high", "file": "docs/getting-started/index.md", "doc_kind": "tutorial"} +{"claim_id": "docs/getting-started/index.md::a007::c2", "atom_id": "docs/getting-started/index.md::a007", "type": "prose", "line": "30", "claim": "The link target ../backend/http-api-client.md exists.", "oracle": "source-path", "check": "ls backend/http-api-client.md", "verdict": "true", "evidence": "docs/backend/http-api-client.md exists (7724 bytes)", "confidence": "high", "file": "docs/getting-started/index.md", "doc_kind": "tutorial"} +{"claim_id": "docs/glossary.md::a001::c1", "atom_id": "docs/glossary.md::a001", "type": "prose", "line": "5-7", "claim": "EPU is a Windows desktop application developed by ThermoFisher for cryo-EM acquisition.", "oracle": "workspace-doc", "check": "cross-ref CLAUDE.md EPU/EPU-Workstation definitions", "verdict": "true", "evidence": "CLAUDE.md: EPU='Proprietary desktop software from ThermoFisher'; EPU Workstation='Windows machine running EPU'", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a001::c2", "atom_id": "docs/glossary.md::a001", "type": "prose", "line": "5-7", "claim": "EPU expands to 'Enhanced Productivity User interface'.", "oracle": "external-vendor", "check": "grep 'Enhanced Productivity' across repos", "verdict": "unverifiable", "evidence": "No 'Enhanced Productivity' string in any repo; acronym is a ThermoFisher product name needing vendor oracle", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a002::c1", "atom_id": "docs/glossary.md::a002", "type": "prose", "line": "11-12", "claim": "SPA stands for Single Particle Analysis, a cryo-EM 3D-structure method.", "oracle": "domain-science", "check": "grep 'single particle' across py/md", "verdict": "unverifiable", "evidence": "No 'single particle' string in code/docs; scientific-domain definition with no workspace anchor", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a003::c1", "atom_id": "docs/glossary.md::a003", "type": "prose", "line": "16-18", "claim": "Athena is decision-making software developed by ThermoFisher for automated cryo-EM workflows.", "oracle": "workspace-doc", "check": "cross-ref CLAUDE.md AthenaAPI definition", "verdict": "true", "evidence": "CLAUDE.md: AthenaAPI='REST API exposed by ThermoFisher microscopes'; ThermoFisher + decision-relay corroborated", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a003::c2", "atom_id": "docs/glossary.md::a003", "type": "prose", "line": "16-18", "claim": "The Athena API provides session management, decision recording, and algorithm results.", "oracle": "external-vendor", "check": "find athena_api module; grep session/decision/algorithm", "verdict": "unverifiable", "evidence": "athena_api absent on current branch (only draft branch agent_athena_api_integration_draft); describes external vendor API", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a004::c1", "atom_id": "docs/glossary.md::a004", "type": "prose", "line": "22-24", "claim": "ARIA is a central metadata repository for structural biology data from multiple facilities, managed by INSTRUCT-ERIC.", "oracle": "workspace-doc", "check": "cross-ref CLAUDE.md ARIA definition + Marcus Povey instruct-eric email", "verdict": "true", "evidence": "CLAUDE.md: ARIA='Central metadata repository ... from multiple facilities'; author marcus@instruct-eric.org", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a004::c2", "atom_id": "docs/glossary.md::a004", "type": "prose", "line": "22-24", "claim": "Metadata is deposited to ARIA from facilities via the FandanGO plugin framework.", "oracle": "workspace-doc", "check": "cross-ref CLAUDE.md FandanGO/ARIA relationship", "verdict": "true", "evidence": "CLAUDE.md: 'FandanGO plugins deposit to these APIs'; fandanGO-cryoem-dls deposits to ARIA repository", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a004::c3", "atom_id": "docs/glossary.md::a004", "type": "prose", "line": "22-24", "claim": "More ARIA info is available at https://instruct-eric.org/help/about-aria.", "oracle": "external-url", "check": "external HTTP resource", "verdict": "unverifiable", "evidence": "External URL; cannot fetch/validate from workspace", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a005::c1", "atom_id": "docs/glossary.md::a005", "type": "prose", "line": "28-30", "claim": "The Atlas is an overview image of the entire grid composed of individual tiles.", "oracle": "code-entity", "check": "grep Atlas/AtlasTile DB entities", "verdict": "true", "evidence": "src/smartem_backend/model/database.py:50 Atlas, :103 AtlasTile; atlas composed of tiles corroborated by schema", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a005::c2", "atom_id": "docs/glossary.md::a005", "type": "prose", "line": "28-30", "claim": "Atlas tiles are arranged in a fixed 5x5 grid pattern.", "oracle": "code-default", "check": "grep '5x5'/tile-count in code and schema", "verdict": "unverifiable", "evidence": "No '5x5' constant in code; AtlasTile has dynamic tile_id/position, tile count not fixed at 5x5 in schema", "confidence": "low", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a006::c1", "atom_id": "docs/glossary.md::a006", "type": "prose", "line": "34-35", "claim": "Grid squares are individual sections of the EM grid that contain the specimen.", "oracle": "code-entity", "check": "grep GridSquare DB entity", "verdict": "true", "evidence": "src/smartem_backend/model/database.py:132 class GridSquare (table gridsquare); entity models grid sections", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a006::c2", "atom_id": "docs/glossary.md::a006", "type": "prose", "line": "34-35", "claim": "Grid squares are selected based on ice quality, specimen distribution, and quality metrics for higher-mag imaging.", "oracle": "workspace-doc", "check": "cross-ref SmartEM selection/scoring role in CLAUDE.md/MEMORY", "verdict": "true", "evidence": "SmartEM produces per-square quality scores/suggestions (resnet-holes) for selection; consistent with system purpose", "confidence": "medium", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a007::c1", "atom_id": "docs/glossary.md::a007", "type": "prose", "line": "39-41", "claim": "Foil holes are circular openings in the carbon support film where specimen is suspended in vitreous ice.", "oracle": "code-entity", "check": "grep FoilHole DB entity", "verdict": "true", "evidence": "src/smartem_backend/model/database.py:176 class FoilHole (table foilhole); domain definition standard and entity-backed", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/glossary.md::a008::c1", "atom_id": "docs/glossary.md::a008", "type": "prose", "line": "45-46", "claim": "Micrographs are high-resolution EM images captured from individual foil holes or grid squares.", "oracle": "code-entity", "check": "grep Micrograph DB entity", "verdict": "true", "evidence": "src/smartem_backend/model/database.py:205 class Micrograph (table micrograph); leaf image entity below foilhole", "confidence": "high", "file": "docs/glossary.md", "doc_kind": "reference"} +{"claim_id": "docs/operations/container-user-configuration.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The SmartEM Decisions Dockerfile can be configured to run the container as a non-root user with a specific UID/GID.", "oracle": [], "check": "read Dockerfile build args", "verdict": "true", "evidence": "Dockerfile:37-39 groupid/userid/groupname args; :44-47 creates user; non-root achieved via k8s securityContext", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "The Dockerfile supports two operational modes (root default and custom user).", "oracle": [], "check": "read Dockerfile conditional", "verdict": "true", "evidence": "Dockerfile:44 `if [ \"${groupid}\" != \"0\" ]` branches root vs custom user creation", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "9-10", "claim": "Root mode is for CI/CD and local dev; custom user mode is for production deployments needing filesystem permissions.", "oracle": [], "check": "Dockerfile comment + CI workflow", "verdict": "true", "evidence": "Dockerfile:12-22 comment states same; release CI passes no user build-args (root)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "14", "claim": "The HTTP API accesses microscopy images stored on the /dls filesystem.", "oracle": [], "check": "grep image endpoints", "verdict": "true", "evidence": "api_server.py:2741-2789 image endpoints read paths from DB (typically /dls) and render", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "16-19", "claim": "/dls data is accessed by image serving endpoints in the HTTP API.", "oracle": [], "check": "grep image endpoints", "verdict": "true", "evidence": "api_server.py:2768/2789 read grid.atlas_dir and gridsquare.image_path from filesystem", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a004::c2", "atom_id": "a004", "type": "prose", "line": "16-19", "claim": "/dls cannot be mounted in containers with root privileges due to security policies.", "oracle": [], "check": "no code artifact; DLS operational policy", "verdict": "unverifiable", "evidence": "DLS filesystem mount policy is an external operational fact not in the codebase", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "21", "claim": "Without proper user configuration the container cannot access /dls and image serving endpoints fail.", "oracle": [], "check": "image endpoint failure path", "verdict": "true", "evidence": "_cached_image_response stat()/read raise on inaccessible files -> 500; endpoints fail (operational premise external)", "confidence": "low", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "25", "claim": "The Dockerfile accepts three build arguments (groupid, userid, groupname) controlling user/group creation.", "oracle": [], "check": "read Dockerfile ARGs", "verdict": "true", "evidence": "Dockerfile:37-39 ARG groupid/userid/groupname; :45-46 groupadd/useradd use them", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a007::c1", "atom_id": "a007", "type": "table", "line": "27-31", "claim": "Build-arg defaults are groupid=0, userid=0, groupname=root, and groupid=0 means no custom user is created.", "oracle": [], "check": "read Dockerfile ARGs + conditional", "verdict": "true", "evidence": "Dockerfile:37 groupid=0,:38 userid=0,:39 groupname=root; :44 skips user creation when groupid==0", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "35", "claim": "When build args are unspecified or set to defaults, the container runs as root.", "oracle": [], "check": "read Dockerfile", "verdict": "true", "evidence": "Dockerfile default args are 0/0/root and no USER instruction -> root", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a009::c1", "atom_id": "a009", "type": "code", "line": "37-47", "claim": "`docker build -t smartem-decisions .` builds a root image, equivalent to passing groupid=0 userid=0 groupname=root.", "oracle": ["cli-cmd"], "check": "compare with Dockerfile required args", "verdict": "misleading", "evidence": "Build-arg equivalence is correct, but command omits required SMARTEM_VERSION (Dockerfile:52 pip install ==${SMARTEM_VERSION}) so build fails as written", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a011::c1", "atom_id": "a011", "type": "prose", "line": "55-59", "claim": "In root mode no user-creation step is executed and all files are owned by root.", "oracle": [], "check": "read Dockerfile conditional", "verdict": "true", "evidence": "Dockerfile:44 guard skips groupadd/useradd when groupid==0; files remain root-owned", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a012::c1", "atom_id": "a012", "type": "prose", "line": "63", "claim": "For DLS production, build the container with specific UID/GID.", "oracle": [], "check": "Dockerfile capability", "verdict": "true", "evidence": "Dockerfile:44-47 supports custom groupid/userid/groupname build", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a013::c1", "atom_id": "a013", "type": "code", "line": "65-72", "claim": "Custom build passes groupid/userid/groupname to create a non-root user image.", "oracle": ["cli-cmd"], "check": "compare with Dockerfile args", "verdict": "misleading", "evidence": "Build-arg names/values correct but command omits required SMARTEM_VERSION -> pip install == fails (Dockerfile:52)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a014::c1", "atom_id": "a014", "type": "prose", "line": "74-78", "claim": "During build a group and a user are created with the specified ids/name, the user belonging to the group.", "oracle": ["source-path"], "check": "read Dockerfile", "verdict": "true", "evidence": "Dockerfile:45 groupadd -g groupid groupname; :46 useradd -u userid -g groupname", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a014::c2", "atom_id": "a014", "type": "prose", "line": "74-78", "claim": "All application files (/venv, /app, /entrypoint.sh) are set to be owned by the custom user.", "oracle": ["source-path"], "check": "read Dockerfile COPY/chown", "verdict": "misleading", "evidence": "Prod Dockerfile:56 only --chown on entrypoint.sh; /venv (:49) and /app (:54-55) stay root-owned. Only Dockerfile.dev:80-81 chowns all three", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a014::c3", "atom_id": "a014", "type": "prose", "line": "74-78", "claim": "The container will execute processes as the custom user, not root.", "oracle": ["source-path"], "check": "grep USER directive", "verdict": "incorrect", "evidence": "No USER instruction in Dockerfile or Dockerfile.dev; ENTRYPOINT runs as root unless k8s securityContext.runAsUser overrides", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "85-89", "claim": "In custom user mode the container runs with limited (non-root) privileges.", "oracle": [], "check": "grep USER directive + k8s manifests", "verdict": "incorrect", "evidence": "No USER in Dockerfile; real k8s http-api manifests set no runAsUser, so container runs as root even when built with custom UID/GID", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a018::c1", "atom_id": "a018", "type": "prose", "line": "95", "claim": "The /dls directory is not created in the Docker image and must be mounted at runtime.", "oracle": [], "check": "read Dockerfile", "verdict": "true", "evidence": "Dockerfile creates no /dls dir; mounted at runtime (staging/prod manifests mount /dls)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a019::c1", "atom_id": "a019", "type": "code", "line": "97-100", "claim": "`docker run -v /path/to/dls:/dls smartem-decisions:dls` mounts the /dls directory.", "oracle": ["cli-cmd"], "check": "docker syntax", "verdict": "true", "evidence": "Valid docker -v host:container bind-mount syntax; /dls is the expected mount path", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "102", "claim": "In Kubernetes /dls is mounted via a PersistentVolume or hostPath mount.", "oracle": ["k8s"], "check": "grep k8s manifests", "verdict": "true", "evidence": "smartem-devtools k8s staging/production smartem-http-api.yaml reference /dls mounts; dev uses hostPath", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a021::c1", "atom_id": "a021", "type": "code", "line": "104-124", "claim": "A smartem-http-api Deployment mounts /dls read-only via a hostPath volume of type Directory.", "oracle": [], "check": "grep k8s manifests", "verdict": "true", "evidence": "Deployment name smartem-http-api matches real manifest; /dls readOnly mount is a valid, documented pattern (prod manifest templates it)", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a023::c1", "atom_id": "a023", "type": "prose", "line": "130-133", "claim": "When /dls is not mounted, image serving endpoints return 404 errors for paths referencing /dls.", "oracle": [], "check": "read _cached_image_response", "verdict": "misleading", "evidence": "api_server.py:2742 stat() on a missing file raises FileNotFoundError -> HTTP 500, not 404; 404 only when DB image_path is null (:2788)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a024::c1", "atom_id": "a024", "type": "prose", "line": "135", "claim": "By design the container is operational without /dls, only image serving is unavailable.", "oracle": [], "check": "read endpoints", "verdict": "true", "evidence": "Non-image endpoints independent of /dls; only image endpoints touch the filesystem", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "139", "claim": "The /dls mount is optional for development but required for production.", "oracle": [], "check": "design/operational", "verdict": "true", "evidence": "Consistent with graceful-degradation design; production needs image serving. Partly operational recommendation", "confidence": "low", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a027::c1", "atom_id": "a027", "type": "prose", "line": "147", "claim": "The HTTP API provides endpoints for serving microscopy images.", "oracle": [], "check": "grep endpoints", "verdict": "true", "evidence": "api_server.py:2754/2777/2792 atlas_image, gridsquare_image, micrograph_image endpoints", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a028::c1", "atom_id": "a028", "type": "prose", "line": "149-150", "claim": "Endpoints GET /grids/{grid_uuid}/atlas_image and GET /gridsquares/{gridsquare_uuid}/gridsquare_image serve grid atlas and grid square images.", "oracle": [], "check": "grep route decorators", "verdict": "true", "evidence": "api_server.py:2754 @app.get('/grids/{grid_uuid}/atlas_image'); :2777 '/gridsquares/{gridsquare_uuid}/gridsquare_image'", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a029::c1", "atom_id": "a029", "type": "prose", "line": "152-155", "claim": "These endpoints query the DB for image paths, read files from the filesystem, and return PNG.", "oracle": [], "check": "read endpoint + render fn", "verdict": "true", "evidence": "api_server.py:2765/2784 DB query; :2768/2789 filesystem read; :2724-2738 renders PNG via Pillow", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "162-165", "claim": "If the image path is not in the database the endpoint returns 404 'Grid square image unknown'.", "oracle": [], "check": "read endpoint", "verdict": "true", "evidence": "api_server.py:2787-2788 `if not gridsquare.image_path: raise HTTPException(404, 'Grid square image unknown')`", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a031::c2", "atom_id": "a031", "type": "prose", "line": "162-165", "claim": "If the file does not exist the endpoint returns 404 or a filesystem error.", "oracle": [], "check": "read _cached_image_response", "verdict": "misleading", "evidence": "api_server.py:2742 unguarded source_path.stat() -> FileNotFoundError propagates as HTTP 500, not 404", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a031::c3", "atom_id": "a031", "type": "prose", "line": "162-165", "claim": "If permissions are denied the endpoint returns a 500 error with permission details.", "oracle": [], "check": "read _cached_image_response", "verdict": "misleading", "evidence": "PermissionError from stat()/read yields a generic 500 'Internal Server Error'; no permission details are exposed in the response", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a033::c1", "atom_id": "a033", "type": "prose", "line": "171", "claim": "Running containers as non-root is a security best practice.", "oracle": [], "check": "general knowledge", "verdict": "true", "evidence": "Widely accepted container security best practice", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "181-184", "claim": "UIDs below 1000 are reserved for system users and should be avoided for the container user.", "oracle": [], "check": "general Linux convention", "verdict": "true", "evidence": "Standard Linux/Debian convention: sub-1000 UIDs reserved for system accounts", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a037::c1", "atom_id": "a037", "type": "prose", "line": "188", "claim": "All files in the container are owned by the specified custom user.", "oracle": [], "check": "read Dockerfile chown", "verdict": "misleading", "evidence": "Prod Dockerfile:56 only chowns entrypoint.sh; /venv,/app,alembic.ini remain root-owned. True only for Dockerfile.dev:80-81", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a038::c1", "atom_id": "a038", "type": "code", "line": "190-197", "claim": "`ls -la /app` and `ls -la /venv` show smartem:smartem ownership in a custom-user build.", "oracle": [], "check": "read Dockerfile chown", "verdict": "misleading", "evidence": "Prod Dockerfile does not chown /app or /venv (would show root:root). Only Dockerfile.dev:80-81 produces smartem-owned /app,/venv", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "199", "claim": "This ensures the application can read its own files while running as non-root.", "oracle": [], "check": "read Dockerfile perms", "verdict": "true", "evidence": "venv/app dirs are world-readable (0755), so a non-root user can read them regardless of owner", "confidence": "low", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a041::c1", "atom_id": "a041", "type": "code", "line": "205-214", "claim": "Local dev builds smartem-decisions:dev and runs it with -p 8000:8000 -e ROLE=api -e POSTGRES_HOST=... .", "oracle": ["env-var", "port", "cli-cmd"], "check": "entrypoint + Dockerfile", "verdict": "misleading", "evidence": "Run env is correct (entrypoint.sh:15 ROLE default api, :20 port 8000; POSTGRES_HOST required utils.py:105) but build omits required SMARTEM_VERSION so it fails", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a042::c1", "atom_id": "a042", "type": "code", "line": "218-222", "claim": "CI (GitHub Actions) builds as root by default and pushes to ghcr.io/diamondlightsource/smartem-decisions.", "oracle": ["cli-cmd"], "check": "release workflow", "verdict": "misleading", "evidence": "Root-default + ghcr.io/diamondlightsource/smartem-decisions correct, but real CI passes SMARTEM_VERSION (release-smartem-decisions.yml:497) and tags version/latest not $GITHUB_SHA; doc command omits SMARTEM_VERSION", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a043::c1", "atom_id": "a043", "type": "code", "line": "226-238", "claim": "Staging build passes groupid/userid=1001 and pushes ghcr.io/diamondlightsource/smartem-decisions:staging.", "oracle": ["cli-cmd"], "check": "Dockerfile args", "verdict": "misleading", "evidence": "Build-arg/tag/push syntax valid but command omits required SMARTEM_VERSION -> build fails (Dockerfile:52)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a044::c1", "atom_id": "a044", "type": "code", "line": "242-254", "claim": "Production build passes groupid/userid=5000 and pushes ghcr.io/diamondlightsource/smartem-decisions:production.", "oracle": ["cli-cmd"], "check": "Dockerfile args", "verdict": "misleading", "evidence": "Build-arg/tag/push valid but command omits required SMARTEM_VERSION -> build fails (Dockerfile:52)", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a047::c1", "atom_id": "a047", "type": "code", "line": "262-304", "claim": "A smartem-http-api Deployment in namespace smartem-decisions-production runs with securityContext runAsUser/runAsGroup/fsGroup 5000 and mounts /dls read-only.", "oracle": ["k8s"], "check": "grep k8s manifests", "verdict": "true", "evidence": "prod manifest matches name smartem-http-api + namespace smartem-decisions-production + ghcr.io/diamondlightsource/smartem-decisions image; securityContext/hostPath are valid illustrative config", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a048::c1", "atom_id": "a048", "type": "prose", "line": "308", "claim": "The Dockerfile sets ownership while Kubernetes securityContext provides additional enforcement.", "oracle": ["k8s"], "check": "grep USER + Dockerfile chown", "verdict": "misleading", "evidence": "No USER instruction, so non-root at runtime comes ENTIRELY from k8s securityContext.runAsUser, not as 'additional' enforcement over the Dockerfile", "confidence": "medium", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a049::c1", "atom_id": "a049", "type": "prose", "line": "310-313", "claim": "securityContext fields runAsUser/runAsGroup/fsGroup/runAsNonRoot control UID, primary group, mounted-volume group, and enforce non-root.", "oracle": ["k8s"], "check": "k8s general knowledge", "verdict": "true", "evidence": "Accurate descriptions of standard Kubernetes securityContext fields", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a051::c1", "atom_id": "a051", "type": "code", "line": "317-334", "claim": "A securityContext example sets runAsNonRoot true, allowPrivilegeEscalation false, and drops ALL capabilities.", "oracle": [], "check": "k8s schema", "verdict": "true", "evidence": "Valid Kubernetes pod/container securityContext YAML", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a056::c1", "atom_id": "a056", "type": "code", "line": "351-360", "claim": "kubectl exec can check /dls mount (ls -la /dls), container user (id), and file permissions.", "oracle": ["cli-cmd", "k8s"], "check": "kubectl syntax", "verdict": "true", "evidence": "Valid kubectl exec diagnostic commands", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a060::c1", "atom_id": "a060", "type": "code", "line": "373-381", "claim": "`docker run --rm smartem-decisions:dls id` outputs uid=5000(smartem) gid=5000(smartem).", "oracle": ["cli-cmd"], "check": "grep USER directive", "verdict": "misleading", "evidence": "No USER instruction in Dockerfile, so `docker run ... id` reports uid=0(root), not 5000; UID switch happens only via k8s securityContext", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a064::c1", "atom_id": "a064", "type": "code", "line": "394-401", "claim": "Rebuild with --build-arg groupid/userid=5000 groupname=smartem; docker inspect shows Args.", "oracle": ["cli-cmd"], "check": "Dockerfile args", "verdict": "misleading", "evidence": "Rebuild command omits required SMARTEM_VERSION -> build fails (Dockerfile:52); build-arg names otherwise valid", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a066::c1", "atom_id": "a066", "type": "prose", "line": "416-419", "claim": "Related docs run-container.md, kubernetes.md, containerization.md, and kubernetes-secrets.md exist in the operations docs.", "oracle": ["source-path", "k8s"], "check": "ls docs/operations", "verdict": "true", "evidence": "All four files present in smartem-devtools/docs/operations/", "confidence": "high", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/container-user-configuration.md::a067::c1", "atom_id": "a067", "type": "prose", "line": "423-425", "claim": "External references link to Docker USER instruction, Kubernetes Security Context, and Docker dev best-practices docs.", "oracle": ["url", "cli-cmd", "k8s"], "check": "external URLs", "verdict": "unverifiable", "evidence": "External documentation URLs cannot be fetched/verified in this environment", "confidence": "low", "file": "docs/operations/container-user-configuration.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a002::c1", "atom_id": "docs/operations/containerization.md::a002", "type": "prose", "line": "9", "claim": "The default container build runs as root.", "oracle": [], "check": "read root Dockerfile build-arg defaults", "verdict": "true", "evidence": "Dockerfile: ARG groupid=0/userid=0/groupname=root; user only created if groupid!=0, so default runs as root.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a003::c1", "atom_id": "docs/operations/containerization.md::a003", "type": "code", "line": "11-17", "claim": "The image is built with `docker build -t smartem-decisions .` (or podman equivalent) with default settings.", "oracle": ["cli-cmd"], "check": "read root Dockerfile install line + release workflow build-args", "verdict": "misleading", "evidence": "Root Dockerfile installs PyPI 'smartem-decisions[backend,images]==${SMARTEM_VERSION}'; SMARTEM_VERSION has no default so plain build fails; CI passes --build-arg SMARTEM_VERSION.", "confidence": "medium", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a004::c1", "atom_id": "docs/operations/containerization.md::a004", "type": "prose", "line": "21", "claim": "DLS production builds use a custom UID/GID to enable /dls filesystem access.", "oracle": [], "check": "read Dockerfile user-config comment block", "verdict": "true", "evidence": "Dockerfile comment: custom groupid/userid required for mounting /dls filesystem in k8s pods (root cannot access microscopy images).", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a005::c1", "atom_id": "docs/operations/containerization.md::a005", "type": "code", "line": "23-37", "claim": "DLS build uses --build-arg groupid=1000 userid=1000 groupname=smartem to tag smartem-decisions:dls.", "oracle": ["cli-cmd"], "check": "verified build-arg names in Dockerfile; checked SMARTEM_VERSION requirement", "verdict": "misleading", "evidence": "groupid/userid/groupname args exist and are correct, but command omits required --build-arg SMARTEM_VERSION so build fails on current root Dockerfile.", "confidence": "medium", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a006::c1", "atom_id": "docs/operations/containerization.md::a006", "type": "prose", "line": "39", "claim": "Container User Configuration details live in container-user-configuration.md.", "oracle": ["source-path"], "check": "ls docs/operations/", "verdict": "true", "evidence": "docs/operations/container-user-configuration.md exists (12714 bytes) in smartem-devtools.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a007::c1", "atom_id": "docs/operations/containerization.md::a007", "type": "code", "line": "43-49", "claim": "The API service runs via `docker run -p 8000:8000 -e ROLE=api smartem-decisions`.", "oracle": ["env-var", "port", "cli-cmd"], "check": "read entrypoint.sh ROLE handling and default port", "verdict": "true", "evidence": "entrypoint.sh: case ${ROLE:-api} api starts uvicorn on --port ${HTTP_API_PORT:-8000}; ROLE=api and port 8000 both correct.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a008::c1", "atom_id": "docs/operations/containerization.md::a008", "type": "code", "line": "53-59", "claim": "Images are removed with `docker image rm smartem-decisions -f` / `podman image rm localhost/smartem-decisions -f`.", "oracle": ["cli-cmd"], "check": "standard docker/podman cleanup syntax", "verdict": "true", "evidence": "Generic valid docker/podman image-removal commands; image name matches build tag used elsewhere in doc.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a010::c1", "atom_id": "docs/operations/containerization.md::a010", "type": "code", "line": "65-74", "claim": "Images are tagged/pushed to ghcr.io/diamondlightsource/smartem-decisions after docker login to ghcr.io.", "oracle": ["cli-cmd"], "check": "grep release workflow for registry image path", "verdict": "true", "evidence": "release-smartem-decisions.yml uses registry ghcr.io and images ghcr.io/${github.repository} = ghcr.io/diamondlightsource/smartem-decisions.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a011::c1", "atom_id": "docs/operations/containerization.md::a011", "type": "code", "line": "78-89", "claim": "Tagging strategies push dev/staging/version/latest/production tags to ghcr.io/diamondlightsource/smartem-decisions.", "oracle": ["cli-cmd"], "check": "registry path verified against CI; tag commands are generic docker", "verdict": "true", "evidence": "Registry path matches CI (ghcr.io/diamondlightsource/smartem-decisions); docker tag commands are valid generic syntax.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a012::c1", "atom_id": "docs/operations/containerization.md::a012", "type": "prose", "line": "93", "claim": "The Dockerfile uses a multi-stage build process.", "oracle": [], "check": "read root Dockerfile and Dockerfile.dev FROM stages", "verdict": "misleading", "evidence": "Root Dockerfile is single-stage (only 'FROM ... AS runtime', PyPI install); multi-stage build now lives in Dockerfile.dev.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a013::c1", "atom_id": "docs/operations/containerization.md::a013", "type": "prose", "line": "95-97", "claim": "The build has three stages: developer (base+deps), build (installs packages/app code), runtime (slim + built app).", "oracle": [], "check": "read Dockerfile.dev stage definitions", "verdict": "misleading", "evidence": "Stages developer/build/runtime match Dockerfile.dev exactly, but not the primary Dockerfile (single-stage); doc points at wrong file.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a014::c1", "atom_id": "docs/operations/containerization.md::a014", "type": "prose", "line": "99-102", "claim": "The multi-stage approach minimises image size, separates build from runtime, and enables layer caching.", "oracle": [], "check": "read Dockerfile.dev runtime stage copies only venv/app from build", "verdict": "true", "evidence": "Dockerfile.dev runtime is python-slim and COPY --from=build only /venv and /app; accurate description of multi-stage benefits.", "confidence": "medium", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a015::c1", "atom_id": "docs/operations/containerization.md::a015", "type": "table", "line": "106-111", "claim": "Build args: PYTHON_VERSION=3.12, groupid=0, userid=0, groupname=root by default.", "oracle": [], "check": "read ARG defaults in Dockerfile/Dockerfile.dev", "verdict": "true", "evidence": "Dockerfile: ARG PYTHON_VERSION=3.12, groupid=0, userid=0, groupname=root all match; table omits the now-required SMARTEM_VERSION arg.", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a016::c1", "atom_id": "docs/operations/containerization.md::a016", "type": "prose", "line": "115-117", "claim": "Related docs container-user-configuration.md, run-container.md, and kubernetes.md exist.", "oracle": ["source-path", "k8s"], "check": "ls docs/operations/", "verdict": "true", "evidence": "All three exist in smartem-devtools/docs/operations/ (container-user-configuration.md, run-container.md, kubernetes.md).", "confidence": "high", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/containerization.md::a017::c1", "atom_id": "docs/operations/containerization.md::a017", "type": "prose", "line": "121-123", "claim": "References point to ghcr.io/diamondlightsource/smartem-decisions plus Docker multi-stage and Podman docs.", "oracle": ["url", "cli-cmd"], "check": "registry path verified vs CI; external doc URLs are canonical", "verdict": "true", "evidence": "ghcr registry path matches CI; docs.docker.com/build/building/multi-stage and docs.podman.io are canonical vendor URLs.", "confidence": "medium", "file": "docs/operations/containerization.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a002::c1", "atom_id": "docs/operations/environment-variables.md::a002", "type": "table", "line": "9-18", "claim": "The named env-file patterns (.env.example, .env, .env.example.k8s.{development,staging,production} and their non-example counterparts) exist with the stated purposes and gitignore status.", "oracle": ["k8s"], "check": "find/ls of .env* across both repos", "verdict": "true", "evidence": ".env.example & .env in smartem-decisions/ root; .env.example.k8s.{development,staging,production} committed in smartem-devtools/env-examples/", "confidence": "medium", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a004::c1", "atom_id": "docs/operations/environment-variables.md::a004", "type": "prose", "line": "24", "claim": ".env is used to run backend services on the host OS while connecting to K8s infrastructure.", "oracle": ["k8s"], "check": "read .env.example NodePort config", "verdict": "true", "evidence": ".env.example: POSTGRES_HOST=localhost POSTGRES_PORT=30432 (K8s NodePort) confirms host->K8s connection", "confidence": "medium", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a005::c1", "atom_id": "docs/operations/environment-variables.md::a005", "type": "prose", "line": "26-30", "claim": "The backend consumer is runnable via `python -m smartem_backend.consumer`.", "oracle": ["source-path", "cli-cmd"], "check": "grep main guard in consumer.py", "verdict": "true", "evidence": "src/smartem_backend/consumer.py:1257 def main + :1266 __main__ guard", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a005::c2", "atom_id": "docs/operations/environment-variables.md::a005", "type": "prose", "line": "26-30", "claim": "The API server is runnable via `python -m smartem_backend.api_server`.", "oracle": ["source-path", "cli-cmd"], "check": "grep main guard in api_server.py", "verdict": "true", "evidence": "src/smartem_backend/api_server.py:3000 __main__ guard -> uvicorn.run(...:app)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a005::c3", "atom_id": "docs/operations/environment-variables.md::a005", "type": "prose", "line": "26-30", "claim": "E2E testing is run via `./tests/e2e/run-e2e-test.sh`.", "oracle": ["source-path", "cli-cmd"], "check": "find run-e2e-test.sh", "verdict": "misleading", "evidence": "script moved out of smartem-decisions to smartem-devtools/tests/e2e/run-e2e-test.sh; path unresolvable from decisions checkout", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a009::c1", "atom_id": "docs/operations/environment-variables.md::a009", "type": "code", "line": "39-48", "claim": ".env connects to K8s NodePorts POSTGRES_PORT=30432, RABBITMQ_PORT=30672 and runs API on HTTP_API_PORT=8000.", "oracle": ["env-var", "port", "k8s"], "check": "grep values in .env.example and manifests", "verdict": "true", "evidence": ".env.example:17 POSTGRES_PORT=30432, :24 RABBITMQ_PORT=30672, :32 HTTP_API_PORT=8000; postgres.yaml nodePort 30432, rabbitmq.yaml 30672", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a010::c1", "atom_id": "docs/operations/environment-variables.md::a010", "type": "prose", "line": "50-53", "claim": ".env is loaded by Python via load_dotenv() in smartem_backend/consumer.py and smartem_backend/utils.py.", "oracle": ["source-path", "k8s"], "check": "grep load_dotenv in smartem_backend", "verdict": "true", "evidence": "consumer.py:94 load_dotenv(override=False); utils.py:104 load_dotenv(override=False)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a010::c2", "atom_id": "docs/operations/environment-variables.md::a010", "type": "prose", "line": "50-53", "claim": "Docker entrypoint.sh sources .env when running containers outside Kubernetes.", "oracle": ["source-path", "k8s"], "check": "read entrypoint.sh", "verdict": "true", "evidence": "smartem-decisions/entrypoint.sh:6 `if [ -z \"$KUBERNETES_SERVICE_HOST\" ]` then :9 source .env", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a011::c1", "atom_id": "docs/operations/environment-variables.md::a011", "type": "prose", "line": "57", "claim": ".env.k8s.development is used for deploying to a local K8s cluster (k3s) for development.", "oracle": ["k8s"], "check": "read dev-k8s.sh DEPLOY_ENV development branch", "verdict": "true", "evidence": "dev-k8s.sh:28 loads .env.k8s.development for DEPLOY_ENV=development (local dev cluster); k3s is the local runtime per workspace", "confidence": "low", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a012::c1", "atom_id": "docs/operations/environment-variables.md::a012", "type": "prose", "line": "59-61", "claim": "The dev cluster is brought up by running `./scripts/k8s/dev-k8s.sh up`.", "oracle": ["source-path", "k8s"], "check": "find dev-k8s.sh", "verdict": "misleading", "evidence": "script moved to smartem-devtools/scripts/k8s/dev-k8s.sh; `up` action valid but path unresolvable from smartem-decisions", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a014::c1", "atom_id": "docs/operations/environment-variables.md::a014", "type": "code", "line": "64-67", "claim": "Run `cp .env.example.k8s.development .env.k8s.development` then add DOCKER_USERNAME/EMAIL/PASSWORD.", "oracle": ["k8s"], "check": "locate template file", "verdict": "misleading", "evidence": "template moved to smartem-devtools/env-examples/.env.example.k8s.development; bare cp fails (needs env-examples/ prefix / other repo)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a016::c1", "atom_id": "docs/operations/environment-variables.md::a016", "type": "code", "line": "70-87", "claim": ".env.k8s.development uses internal service names POSTGRES_HOST=postgres-service:5432, RABBITMQ_HOST=rabbitmq-service:5672, DOCKER_* creds, and simple POSTGRES/RABBITMQ user/pass.", "oracle": ["env-var", "k8s"], "check": "read env-examples/.env.example.k8s.development", "verdict": "true", "evidence": "template lines: POSTGRES_HOST=postgres-service POSTGRES_PORT=5432 RABBITMQ_HOST=rabbitmq-service RABBITMQ_PORT=5672 POSTGRES_USER=username; DOCKER_USERNAME/EMAIL present", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a017::c1", "atom_id": "docs/operations/environment-variables.md::a017", "type": "prose", "line": "89-90", "claim": ".env.k8s.development is used by ./scripts/k8s/dev-k8s.sh to create K8s Secrets and ConfigMaps.", "oracle": ["source-path", "k8s"], "check": "read dev-k8s.sh env-load + secret creation", "verdict": "misleading", "evidence": "function correct (dev-k8s.sh:28 sources .env.k8s.development, :284 create secret, :359 create configmap) but script moved to smartem-devtools", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a018::c1", "atom_id": "docs/operations/environment-variables.md::a018", "type": "prose", "line": "94", "claim": ".env.k8s.staging is used for deploying to a staging Kubernetes cluster.", "oracle": ["k8s"], "check": "grep DEPLOY_ENV staging in dev-k8s.sh", "verdict": "true", "evidence": "dev-k8s.sh:30-31 staging) env_file=.env.k8s.staging; :38 valid values development/staging/production", "confidence": "medium", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a020::c1", "atom_id": "docs/operations/environment-variables.md::a020", "type": "code", "line": "97-100", "claim": "Run `cp .env.example.k8s.staging .env.k8s.staging`.", "oracle": ["k8s"], "check": "locate template file", "verdict": "misleading", "evidence": "template moved to smartem-devtools/env-examples/.env.example.k8s.staging; bare cp does not resolve", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a022::c1", "atom_id": "docs/operations/environment-variables.md::a022", "type": "prose", "line": "108-110", "claim": "Staging is deployed via `DEPLOY_ENV=staging ./scripts/k8s/dev-k8s.sh up`.", "oracle": ["env-var", "source-path", "k8s"], "check": "grep DEPLOY_ENV handling + script location", "verdict": "misleading", "evidence": "DEPLOY_ENV=staging supported (dev-k8s.sh:12,30) but script moved to smartem-devtools/scripts/k8s", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a025::c1", "atom_id": "docs/operations/environment-variables.md::a025", "type": "code", "line": "117-120", "claim": "Run `cp .env.example.k8s.production .env.k8s.production`.", "oracle": ["k8s"], "check": "locate template file", "verdict": "misleading", "evidence": "template moved to smartem-devtools/env-examples/.env.example.k8s.production; bare cp does not resolve", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a026::c1", "atom_id": "docs/operations/environment-variables.md::a026", "type": "prose", "line": "122-126", "claim": "The kubernetes-secrets.md guide referenced for Sealed Secrets best practices exists.", "oracle": ["source-path", "k8s"], "check": "ls target doc", "verdict": "true", "evidence": "smartem-devtools/docs/operations/kubernetes-secrets.md exists", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a029::c1", "atom_id": "docs/operations/environment-variables.md::a029", "type": "code", "line": "136-154", "claim": "First-time setup: cp .env.example .env; cp .env.example.k8s.development .env.k8s.development; edit; ./scripts/k8s/dev-k8s.sh up; run python -m smartem_backend.api_server.", "oracle": ["port", "source-path", "cli-cmd", "k8s"], "check": "cross-check template/script/module locations", "verdict": "misleading", "evidence": "cp .env.example .env & module valid in smartem-decisions, but .env.example.k8s.development template and dev-k8s.sh moved to smartem-devtools -> block spans two repos", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a032::c1", "atom_id": "docs/operations/environment-variables.md::a032", "type": "code", "line": "162-165", "claim": "All Python services call load_dotenv(override=False), which loads .env without overriding existing env vars.", "oracle": ["cli-cmd"], "check": "grep load_dotenv args", "verdict": "true", "evidence": "consumer.py:94, utils.py:104, rmq/config.py:18 all load_dotenv(override=False)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a033::c1", "atom_id": "docs/operations/environment-variables.md::a033", "type": "prose", "line": "167-170", "claim": "Loading priority is: already-exported env vars win over .env values (which win over hardcoded defaults).", "oracle": [], "check": "infer from override=False semantics", "verdict": "true", "evidence": "load_dotenv(override=False) preserves already-set env vars over .env; consistent with stated priority", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a035::c1", "atom_id": "docs/operations/environment-variables.md::a035", "type": "code", "line": "175-182", "claim": "Exporting vars via `set -a && source .env && set +a` then starting python -m smartem_backend.api_server / consumer works.", "oracle": ["cli-cmd"], "check": "confirm .env and modules exist", "verdict": "true", "evidence": ".env present in smartem-decisions root; api_server & consumer both have __main__ guards", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a037::c1", "atom_id": "docs/operations/environment-variables.md::a037", "type": "code", "line": "188-197", "claim": "entrypoint.sh checks `if [ -z \"$KUBERNETES_SERVICE_HOST\" ]` and sources .env when running locally, else uses K8s-injected config.", "oracle": ["source-path", "k8s"], "check": "read entrypoint.sh", "verdict": "true", "evidence": "entrypoint.sh:6 tests $KUBERNETES_SERVICE_HOST, :7-9 sources .env when absent (matches snippet)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a038::c1", "atom_id": "docs/operations/environment-variables.md::a038", "type": "prose", "line": "201-205", "claim": "In Kubernetes, ConfigMaps provide non-sensitive config and Secrets provide sensitive data, .env is not used inside containers, and config is created by dev-k8s.sh.", "oracle": ["source-path", "k8s"], "check": "read entrypoint + dev-k8s.sh secret/configmap creation", "verdict": "true", "evidence": "entrypoint skips .env when KUBERNETES_SERVICE_HOST set; dev-k8s.sh:284 create secret + :359 create configmap --from-literal", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a041::c1", "atom_id": "docs/operations/environment-variables.md::a041", "type": "code", "line": "213-228", "claim": "Scenario 1: ./scripts/k8s/dev-k8s.sh up, then .env (POSTGRES localhost:30432, RABBITMQ localhost:30672) is used by host-run python -m smartem_backend modules.", "oracle": ["env-var", "port", "source-path", "cli-cmd", "k8s"], "check": "verify .env values, modules, script location", "verdict": "misleading", "evidence": ".env NodePorts (localhost:30432/30672) and modules correct, but dev-k8s.sh moved to smartem-devtools/scripts/k8s", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a043::c1", "atom_id": "docs/operations/environment-variables.md::a043", "type": "code", "line": "234-248", "claim": "Scenario 2: full K8s deploy via ./scripts/k8s/dev-k8s.sh up; pods in namespace smartem-decisions; NodePorts API 30080, Adminer 30808, RabbitMQ UI 30673.", "oracle": ["port", "source-path", "url", "cli-cmd", "k8s"], "check": "grep nodePorts + namespace + script/template location", "verdict": "misleading", "evidence": "ports (http-api 30080, adminer 30808, rabbitmq 30673) & namespace smartem-decisions correct, but cp template + dev-k8s.sh moved to smartem-devtools", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a045::c1", "atom_id": "docs/operations/environment-variables.md::a045", "type": "code", "line": "254-269", "claim": "Scenario 3: `./tests/e2e/run-e2e-test.sh` loads .env automatically (the script does `source .env`).", "oracle": ["source-path", "k8s"], "check": "read run-e2e-test.sh", "verdict": "misleading", "evidence": "script moved to smartem-devtools/tests/e2e and sources $WORKSPACE_ROOT/.env.local-test-run (line 59), not .env", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a048::c1", "atom_id": "docs/operations/environment-variables.md::a048", "type": "prose", "line": "277-280", "claim": "Debug with `kubectl get pods -n smartem-decisions` and check NodePort 30432 accessibility.", "oracle": ["port", "url", "cli-cmd", "k8s"], "check": "grep namespace + postgres nodePort", "verdict": "true", "evidence": "NAMESPACE=smartem-decisions (dev-k8s.sh:5); postgres nodePort 30432 (postgres.yaml:80)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a049::c1", "atom_id": "docs/operations/environment-variables.md::a049", "type": "code", "line": "281-284", "claim": "`grep POSTGRES .env` should show localhost:30432 for the K8s NodePort.", "oracle": ["port", "k8s"], "check": "read .env.example POSTGRES values", "verdict": "true", "evidence": ".env.example:16-17 POSTGRES_HOST=localhost POSTGRES_PORT=30432", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a050::c1", "atom_id": "docs/operations/environment-variables.md::a050", "type": "prose", "line": "288", "claim": "dev-k8s.sh emits the symptom `DOCKER_PASSWORD does not appear to be a valid GitHub token`.", "oracle": [], "check": "grep string in dev-k8s.sh", "verdict": "true", "evidence": "dev-k8s.sh:101 log_error \"DOCKER_PASSWORD does not appear to be a valid GitHub token\"", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a052::c1", "atom_id": "docs/operations/environment-variables.md::a052", "type": "code", "line": "291-298", "claim": "A GitHub token from `gh auth token` can be set as DOCKER_PASSWORD=ghp_... in .env.k8s.development.", "oracle": ["env-var", "url", "cli-cmd", "k8s"], "check": "read env-examples note + dev-k8s token validation", "verdict": "true", "evidence": "env-examples/.env.example.k8s.development:8 notes DOCKER_PASSWORD auto-detected from 'gh auth token'; dev-k8s.sh validates ghp_ prefix", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a056::c1", "atom_id": "docs/operations/environment-variables.md::a056", "type": "prose", "line": "318", "claim": "The SmartEM Agent uses a separate dotenv-style Keycloak config file, distinct from backend .env, because the agent runs on EPU workstations rather than inside K8s.", "oracle": ["k8s"], "check": "read agent __main__ keycloak config loading", "verdict": "true", "evidence": "smartem_agent/__main__.py loads separate agent.env via load_keycloak_config; agent runs on EPU workstations (design/CLAUDE.md)", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a057::c1", "atom_id": "docs/operations/environment-variables.md::a057", "type": "table", "line": "320-325", "claim": "Agent Keycloak vars are KEYCLOAK_URL, KEYCLOAK_REALM (e.g. dls), KEYCLOAK_CLIENT_ID (SmartEM_Agent), KEYCLOAK_CLIENT_SECRET.", "oracle": [], "check": "grep vars in agent + keycloak_client + tests", "verdict": "true", "evidence": "keycloak_client.py:60 required = [KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET]; tests use realm=dls, client_id=SmartEM_Agent", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a058::c1", "atom_id": "docs/operations/environment-variables.md::a058", "type": "prose", "line": "327", "claim": "The agent reads config from a file alongside the executable (default) or a --config path; all four values are mandatory and missing/empty ones make it exit at startup.", "oracle": [], "check": "read _default_keycloak_config_path, --config option, load_keycloak_config validation", "verdict": "true", "evidence": "__main__.py:30 default agent.env beside executable; :187 --config; :232-234 ValueError->typer.Exit(1); keycloak_client.py:60-64 raises on missing/empty", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a059::c1", "atom_id": "docs/operations/environment-variables.md::a059", "type": "prose", "line": "329", "claim": "Links to Agent Authentication (../agent/authentication.md) and ADR 0018 (../decision-records/decisions/0018-smartem-agent-keycloak-auth.md) resolve.", "oracle": ["source-path"], "check": "ls target files", "verdict": "true", "evidence": "docs/agent/authentication.md and docs/decision-records/decisions/0018-smartem-agent-keycloak-auth.md both exist in smartem-devtools", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/environment-variables.md::a060::c1", "atom_id": "docs/operations/environment-variables.md::a060", "type": "prose", "line": "333-338", "claim": "Related-docs links (api-server.md, e2e-simulation.md, kubernetes.md, kubernetes-secrets.md, database.md, agent/authentication.md) all resolve.", "oracle": ["source-path", "k8s"], "check": "ls each target doc", "verdict": "true", "evidence": "all six exist under smartem-devtools/docs: backend/api-server.md, development/e2e-simulation.md, operations/kubernetes.md, operations/kubernetes-secrets.md, backend/database.md, agent/authentication.md", "confidence": "high", "file": "docs/operations/environment-variables.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a001::c1", "atom_id": "docs/operations/index.md::a001", "type": "code", "line": "5-17", "claim": "The toctree references nine sibling docs (releasing, publish-smartem-workspace-to-pypi, kubernetes, kubernetes-secrets, containerization, run-container, container-user-configuration, environment-variables, logging), all of which exist.", "oracle": "k8s", "check": "ls of smartem-devtools/docs/operations/", "verdict": "true", "evidence": "All 9 .md files present in docs/operations/ (releasing.md, kubernetes.md, ... logging.md)", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a003::c1", "atom_id": "docs/operations/index.md::a003", "type": "prose", "line": "23", "claim": "releasing.md exists and documents how to release smartem-decisions, smartem-epuplayer, and smartem-workspace.", "oracle": "source-path", "check": "file exists + grep for the three package names", "verdict": "true", "evidence": "releasing.md present; grep hits: smartem-decisions x12, smartem-epuplayer x8, smartem-workspace x15", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a003::c2", "atom_id": "docs/operations/index.md::a003", "type": "prose", "line": "24", "claim": "publish-smartem-workspace-to-pypi.md exists and covers first-time PyPI account and token configuration.", "oracle": "source-path", "check": "file exists + grep for pypi/account/token", "verdict": "true", "evidence": "publish-smartem-workspace-to-pypi.md present; grep hits: PyPI, account, token all abundant", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a004::c1", "atom_id": "docs/operations/index.md::a004", "type": "prose", "line": "28", "claim": "kubernetes.md exists and covers deploying SmartEM to Kubernetes clusters.", "oracle": "source-path,k8s", "check": "file exists + read heading", "verdict": "true", "evidence": "kubernetes.md present; title '# Kubernetes Deployment', body describes K8s deployment configs", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a004::c2", "atom_id": "docs/operations/index.md::a004", "type": "prose", "line": "29", "claim": "kubernetes-secrets.md exists and covers managing secrets and sensitive configuration.", "oracle": "source-path,k8s", "check": "file exists + grep secret", "verdict": "true", "evidence": "kubernetes-secrets.md present; 'secret' appears 116 times", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a005::c1", "atom_id": "docs/operations/index.md::a005", "type": "prose", "line": "33", "claim": "containerization.md exists and covers building container images.", "oracle": "source-path", "check": "file exists + read heading", "verdict": "true", "evidence": "containerization.md present; 'guide covers building and managing SmartEM Decisions container images'", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a005::c2", "atom_id": "docs/operations/index.md::a005", "type": "prose", "line": "34", "claim": "run-container.md exists and covers container execution and debugging.", "oracle": "source-path", "check": "file exists + grep run/exec/debug", "verdict": "true", "evidence": "run-container.md present; 20 hits for debug/execut/run", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a005::c3", "atom_id": "docs/operations/index.md::a005", "type": "prose", "line": "35", "claim": "container-user-configuration.md exists and covers user and permission configuration.", "oracle": "source-path", "check": "file exists + grep user/permission", "verdict": "true", "evidence": "container-user-configuration.md present; 54 hits for permission/user", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a006::c1", "atom_id": "docs/operations/index.md::a006", "type": "prose", "line": "39", "claim": "environment-variables.md exists and is a complete configuration reference.", "oracle": "source-path", "check": "file exists + read heading", "verdict": "true", "evidence": "environment-variables.md present; '# Configure Environment Variables', explains env config files", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/index.md::a006::c2", "atom_id": "docs/operations/index.md::a006", "type": "prose", "line": "40", "claim": "logging.md exists and covers log configuration and structured logging.", "oracle": "source-path", "check": "file exists + grep structured", "verdict": "true", "evidence": "logging.md present; '# Configure Logging and Verbosity', 'structured' appears 3 times", "confidence": "high", "file": "docs/operations/index.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "The project manages k8s secrets using Bitnami Sealed Secrets.", "oracle": "k8s", "check": "found generation script + kubeseal usage + example sealed secret", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/generate-sealed-secrets.sh uses kubeseal; k8s/secret.example.yaml present", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7-9", "claim": "Sealed Secrets secure DB credentials and message-queue passwords.", "oracle": "url,k8s", "check": "inspected secret.example.yaml keys", "verdict": "true", "evidence": "k8s/secret.example.yaml has POSTGRES_USER/PASSWORD and RABBITMQ_USER/PASSWORD", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "34-36", "claim": "Required tools are kubectl, kubeseal and openssl.", "oracle": "cli-cmd,k8s", "check": "script REQUIREMENTS block + openssl rand call", "verdict": "true", "evidence": "generate-sealed-secrets.sh lists kubectl/kubeseal/openssl; uses 'openssl rand' L160", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a007::c1", "atom_id": "a007", "type": "code", "line": "40-48", "claim": "kubeseal v0.18.0 linux-amd64 can be downloaded from bitnami-labs/sealed-secrets releases.", "oracle": "url,k8s", "check": "external download URL, no network", "verdict": "unverifiable", "evidence": "GitHub release download URL not reachable from workspace; version-specific asset", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "52-53", "claim": "The sealed-secrets controller is typically pre-installed on DLS clusters.", "oracle": "k8s", "check": "external cluster/infra assertion", "verdict": "unverifiable", "evidence": "requires access to live DLS Kubernetes cluster", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a009::c1", "atom_id": "a009", "type": "code", "line": "55-57", "claim": "Controller status is checked with kubectl get pods -n kube-system -l name=sealed-secrets-controller.", "oracle": "cli-cmd,k8s", "check": "needs live cluster; label is sealed-secrets convention", "verdict": "unverifiable", "evidence": "command is standard but requires a running cluster to verify", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a010::c1", "atom_id": "a010", "type": "prose", "line": "61", "claim": "The project includes a script that handles sealed secret generation.", "oracle": "k8s", "check": "located script file", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/generate-sealed-secrets.sh exists", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a011::c1", "atom_id": "a011", "type": "code", "line": "63-72", "claim": "generate-sealed-secrets.sh accepts development, production and all arguments.", "oracle": "source-path,k8s", "check": "read show_usage + ENVIRONMENTS array + main arg parse", "verdict": "true", "evidence": "script supports development/staging/production/all (ENVIRONMENTS + generate_all_secrets)", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a014::c1", "atom_id": "a014", "type": "code", "line": "80-82", "claim": "Development secrets are generated via ./scripts/k8s/generate-sealed-secrets.sh development.", "oracle": "source-path,k8s", "check": "script path + development case", "verdict": "true", "evidence": "generate-sealed-secrets.sh has development branch in collect_credentials", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a015::c1", "atom_id": "a015", "type": "prose", "line": "84-88", "claim": "Development mode generates cryptographically secure random passwords.", "oracle": "source-path,k8s", "check": "read collect_credentials development branch", "verdict": "true", "evidence": "uses generate_password via 'openssl rand -base64' for POSTGRES/RABBITMQ passwords", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a015::c2", "atom_id": "a015", "type": "prose", "line": "84-88", "claim": "Development sealed secrets target the smartem-decisions namespace.", "oracle": "source-path,k8s", "check": "read get_namespace()", "verdict": "true", "evidence": "get_namespace development -> 'smartem-decisions' (script L142)", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a015::c3", "atom_id": "a015", "type": "prose", "line": "84-88", "claim": "The script updates k8s/environments/development/secrets.yaml.", "oracle": "source-path,k8s", "check": "read output_file assignment", "verdict": "true", "evidence": "script writes output_file=\"$env_dir/secrets.yaml\" (L293) into k8s/environments/$env", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "92", "claim": "Staging and production require interactive credential input.", "oracle": "k8s", "check": "read collect_credentials staging|production branch", "verdict": "true", "evidence": "staging|production case prompts via read/read_password", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a018::c1", "atom_id": "a018", "type": "prose", "line": "98-100", "claim": "Interactive mode prompts for PostgreSQL and RabbitMQ username and password.", "oracle": "k8s", "check": "read prompt lines", "verdict": "true", "evidence": "prompts 'PostgreSQL username/password' and 'RabbitMQ username/password' in collect_credentials", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a019::c1", "atom_id": "a019", "type": "prose", "line": "102", "claim": "Passwords are entered without echo (masked input).", "oracle": "k8s", "check": "password read via read_password helper", "verdict": "true", "evidence": "passwords collected via read_password() (dedicated masked-read helper), not plain read", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a021::c1", "atom_id": "a021", "type": "code", "line": "110-120", "claim": "A temp secret named smartem-secrets in namespace smartem-decisions-production holds POSTGRES_USER/PASSWORD and RABBITMQ_USER/PASSWORD.", "oracle": "env-var,source-path,cli-cmd,k8s", "check": "compared to script kubectl-create + prod namespace + secret keys", "verdict": "true", "evidence": "SECRET_NAME=smartem-secrets; prod namespace smartem-decisions-production; same 4 from-literal keys", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a022::c1", "atom_id": "a022", "type": "code", "line": "124-130", "claim": "kubeseal --format=yaml --namespace=... seals the temp secret.", "oracle": "source-path,k8s", "check": "matches script kubeseal invocation", "verdict": "true", "evidence": "script runs kubeseal --format=yaml --namespace=\"$namespace\" identically", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a023::c1", "atom_id": "a023", "type": "code", "line": "134-140", "claim": "kubectl apply -f sealed-secret.yaml then get secrets -n smartem-decisions-production.", "oracle": "source-path,cli-cmd,k8s", "check": "namespace exists in manifests", "verdict": "true", "evidence": "smartem-decisions-production namespace used in k8s/environments/production manifests", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a026::c1", "atom_id": "a026", "type": "code", "line": "155-158", "claim": "kubectl apply -k k8s/environments/production/ deploys via kustomize.", "oracle": "cli-cmd,k8s", "check": "path + kustomization.yaml present", "verdict": "true", "evidence": "k8s/environments/production/kustomization.yaml exists (kustomize root)", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a028::c1", "atom_id": "a028", "type": "code", "line": "164-170", "claim": "Deployments smartem-http-api and smartem-worker exist in namespace smartem-decisions-production and can be rollout-restarted.", "oracle": "cli-cmd,k8s", "check": "grep deployment manifests", "verdict": "true", "evidence": "k8s/environments/production/smartem-http-api.yaml and smartem-worker.yaml present", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a029::c1", "atom_id": "a029", "type": "code", "line": "174-180", "claim": "Pod status/logs checked via deployment/smartem-http-api in smartem-decisions-production.", "oracle": "cli-cmd,k8s", "check": "deployment + namespace exist", "verdict": "true", "evidence": "smartem-http-api deployment present in production manifests", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "184", "claim": "Sealed secrets integrate with the project's Kustomize structure.", "oracle": "k8s", "check": "env dirs use kustomization.yaml", "verdict": "true", "evidence": "k8s/environments/{development,staging,production}/kustomization.yaml exist", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a031::c1", "atom_id": "a031", "type": "code", "line": "188-199", "claim": "Each env dir contains secrets.yaml and a kustomization.yaml that references secrets.yaml.", "oracle": "source-path,k8s", "check": "grep 'secrets' in kustomization.yaml + ls env dirs", "verdict": "misleading", "evidence": "kustomization.yaml resources omit secrets.yaml; secrets.yaml not committed (generated by script into the dir)", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a032::c1", "atom_id": "a032", "type": "code", "line": "203-209", "claim": "kubectl apply -k k8s/environments/production/ or -f k8s/environments/production/secrets.yaml.", "oracle": "source-path,cli-cmd,k8s", "check": "path + generation target", "verdict": "true", "evidence": "production kustomization exists; secrets.yaml is generated into that dir by the script", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a035::c1", "atom_id": "a035", "type": "code", "line": "216-220", "claim": "kubeseal can be installed from sealed-secrets latest release download.", "oracle": "url,k8s", "check": "external download URL, no network", "verdict": "unverifiable", "evidence": "GitHub 'latest' release asset not reachable from workspace", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a037::c1", "atom_id": "a037", "type": "code", "line": "223-229", "claim": "Controller health is checked via kubectl get pods/logs -n kube-system -l name=sealed-secrets-controller.", "oracle": "cli-cmd,k8s", "check": "needs live cluster", "verdict": "unverifiable", "evidence": "requires running cluster with sealed-secrets controller", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a039::c1", "atom_id": "a039", "type": "code", "line": "232-241", "claim": "SealedSecret and Secret named smartem-secrets exist in namespace smartem-decisions.", "oracle": "cli-cmd,k8s", "check": "secret name + dev namespace consistency", "verdict": "true", "evidence": "SECRET_NAME=smartem-secrets; smartem-decisions is the development namespace", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a041::c1", "atom_id": "a041", "type": "code", "line": "247-253", "claim": "Validate sealed secret smartem-secrets and dry-run apply k8s/environments/development/secrets.yaml.", "oracle": "source-path,cli-cmd,k8s", "check": "names/paths consistency", "verdict": "true", "evidence": "secret name + dev namespace valid; secrets.yaml is the script's dev output path", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a043::c1", "atom_id": "a043", "type": "code", "line": "259-269", "claim": "Connection test imports smartem_backend.utils.setup_postgres_connection and POSTGRES env vars are injected.", "oracle": "cli-cmd,k8s", "check": "serena/grep for function + env keys", "verdict": "true", "evidence": "utils.py:157 def setup_postgres_connection(echo=False, force_new=False); callable no-arg; POSTGRES_* keys in secret", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a045::c1", "atom_id": "a045", "type": "code", "line": "275-283", "claim": "bash -x ./scripts/k8s/generate-sealed-secrets.sh development runs with verbose output.", "oracle": "source-path,cli-cmd,k8s", "check": "script path exists", "verdict": "true", "evidence": "smartem-devtools/scripts/k8s/generate-sealed-secrets.sh exists and takes 'development'", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a052::c1", "atom_id": "a052", "type": "code", "line": "314-321", "claim": "Custom-named secrets can be created in namespace smartem-decisions and piped to kubeseal.", "oracle": "env-var,source-path,cli-cmd,k8s", "check": "namespace validity + kubeseal usage", "verdict": "true", "evidence": "smartem-decisions is a valid namespace; command structure matches kubeseal usage", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a054::c1", "atom_id": "a054", "type": "code", "line": "327-337", "claim": "Multiple secret sources: run generation script plus a separate api-secrets in smartem-decisions-production.", "oracle": "env-var,source-path,cli-cmd,k8s", "check": "script + prod namespace validity", "verdict": "true", "evidence": "generate-sealed-secrets.sh exists; smartem-decisions-production is a valid namespace", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a056::c1", "atom_id": "a056", "type": "code", "line": "343-353", "claim": "Cross-cluster migration exports secret smartem-secrets from smartem-decisions and re-seals it.", "oracle": "source-path,cli-cmd,k8s", "check": "name/namespace validity", "verdict": "true", "evidence": "secret name smartem-secrets + smartem-decisions namespace are consistent with manifests", "confidence": "medium", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a057::c1", "atom_id": "a057", "type": "code", "line": "357-370", "claim": "Files map: scripts/k8s/generate-sealed-secrets.sh, k8s/environments//{secrets.yaml,kustomization.yaml} where kustomization references secrets.yaml, and k8s/secret.example.yaml.", "oracle": "source-path,k8s", "check": "ls files + grep kustomization", "verdict": "misleading", "evidence": "script + k8s/secret.example.yaml exist, but kustomization.yaml does NOT reference secrets.yaml and secrets.yaml is generated (uncommitted)", "confidence": "high", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes-secrets.md::a058::c1", "atom_id": "a058", "type": "prose", "line": "374-377", "claim": "Further-reading links (Bitnami Sealed Secrets, Kubernetes docs, DLS Confluence, k8s security).", "oracle": "url,k8s", "check": "external URLs, no network", "verdict": "unverifiable", "evidence": "external documentation links not reachable from workspace", "confidence": "low", "file": "docs/operations/kubernetes-secrets.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "Kubernetes deployment configurations for the SmartEM Backend exist across multiple environments.", "oracle": "k8s", "check": "listed k8s/environments dir", "verdict": "true", "evidence": "smartem-devtools/k8s/environments/{development,staging,production} all present", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a002::c1", "atom_id": "a002", "type": "code", "line": "9-27", "claim": "./scripts/k8s/dev-k8s.sh supports default up, status, logs , down, restart and --help.", "oracle": "cli-cmd,k8s,source-path", "check": "read case block in dev-k8s.sh", "verdict": "true", "evidence": "dev-k8s.sh:606-651 case up/down/status/restart/logs/help|-h|--help; default COMMAND=up", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "31", "claim": "Adminer database UI is reachable at http://localhost:30808.", "oracle": "port,url", "check": "grep adminer.yaml nodePort", "verdict": "true", "evidence": "k8s/environments/development/adminer.yaml:49 nodePort: 30808; dev-k8s.sh:533 prints it", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "32", "claim": "RabbitMQ Management is reachable at http://localhost:30673.", "oracle": "port,url", "check": "grep rabbitmq.yaml management port", "verdict": "true", "evidence": "rabbitmq.yaml: management port 15672 -> nodePort 30673 (line 92)", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a003::c3", "atom_id": "a003", "type": "prose", "line": "33", "claim": "The SmartEM Backend HTTP API is reachable at http://localhost:30080 (/health).", "oracle": "port,url", "check": "grep smartem-http-api.yaml nodePort", "verdict": "true", "evidence": "smartem-http-api.yaml:111 nodePort 30080 (port 80 -> targetPort 8000)", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a003::c4", "atom_id": "a003", "type": "prose", "line": "34", "claim": "API documentation is served at http://localhost:30080/docs.", "oracle": "port,url", "check": "port confirmed; /docs is FastAPI default", "verdict": "true", "evidence": "nodePort 30080 confirmed; dev-k8s.sh:537 prints API Documentation http://localhost:30080/docs", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "36", "claim": "The dev-k8s.sh script handles GitHub Container Registry authentication and waits for pods to be ready.", "oracle": "cli-cmd", "check": "grep ghcr secret + wait_for_pods in dev-k8s.sh", "verdict": "true", "evidence": "dev-k8s.sh:230 ensure_ghcr_secret (docker-registry ghcr.io), wait_for_pods gate before print_access_urls", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "40", "claim": "The smartem-frontend Vite dev server proxies /api to http://localhost:8000 by default.", "oracle": "port,url,env-var", "check": "read vite.config.ts", "verdict": "true", "evidence": "smartem-frontend/apps/smartem/vite.config.ts:10 proxyTarget = env.VITE_API_PROXY_TARGET || 'http://localhost:8000'", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a006::c1", "atom_id": "a006", "type": "prose", "line": "42", "claim": "Setting VITE_API_PROXY_TARGET=http://localhost:30080 in apps/smartem/.env.local makes Vite proxy to the NodePort.", "oracle": "env-var,port,url", "check": "vite.config.ts + .env.example", "verdict": "true", "evidence": "vite.config.ts honours VITE_API_PROXY_TARGET; apps/smartem/.env.example:20 shows =http://localhost:30080; nodePort 30080 exists", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a006::c2", "atom_id": "a006", "type": "prose", "line": "43", "claim": "kubectl port-forward -n smartem-decisions svc/smartem-http-api-service 8000:80 forwards to the backend.", "oracle": "cli-cmd,k8s,port", "check": "read smartem-http-api.yaml service", "verdict": "true", "evidence": "service smartem-http-api-service ns smartem-decisions, port 80 -> targetPort 8000 (smartem-http-api.yaml:100-111)", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "45", "claim": "Keycloak is reachable at http://localhost:30090.", "oracle": "port,url", "check": "grep keycloak-mock service nodePort + dev-k8s.sh", "verdict": "true", "evidence": "keycloak-mock/keycloak.yaml:101 nodePort 30090; dev-k8s.sh:535 prints Keycloak http://localhost:30090", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a008::c1", "atom_id": "a008", "type": "code", "line": "49-56", "claim": "The k8s/ directory contains environments/{development,staging,production} and a README.md.", "oracle": "source-path,k8s", "check": "ls k8s/ and k8s/environments/", "verdict": "misleading", "evidence": "environments/ w/ 3 envs present, but NO README.md at k8s/ root (has hpa.yaml + secret.example.yaml instead)", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a009::c1", "atom_id": "a009", "type": "prose", "line": "60-61", "claim": "The project uses Bitnami Sealed Secrets and requires generating sealed secrets before deploying.", "oracle": "url,k8s", "check": "found generate-sealed-secrets.sh + secret.example.yaml", "verdict": "true", "evidence": "scripts/k8s/generate-sealed-secrets.sh present; docs/operations/kubernetes-secrets.md; k8s/secret.example.yaml", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a010::c1", "atom_id": "a010", "type": "code", "line": "65-68", "claim": "./scripts/k8s/generate-sealed-secrets.sh development auto-generates development credentials.", "oracle": "source-path,k8s", "check": "read generate-sealed-secrets.sh", "verdict": "true", "evidence": "generate-sealed-secrets.sh:188-189 development => generate secure random passwords", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a011::c1", "atom_id": "a011", "type": "code", "line": "72-75", "claim": "./scripts/k8s/generate-sealed-secrets.sh production prompts interactively for credentials.", "oracle": "source-path,k8s", "check": "read generate-sealed-secrets.sh usage/case", "verdict": "true", "evidence": "generate-sealed-secrets.sh:60-61 production 'Prompt for credentials interactively'; case staging|production prompts", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a012::c1", "atom_id": "a012", "type": "prose", "line": "77-78", "claim": "Sealed secrets are encrypted with the cluster public key, safe to commit, and decrypted by the sealed-secrets controller into regular k8s secrets.", "oracle": "k8s", "check": "describes standard Bitnami sealed-secrets behaviour; repo uses it", "verdict": "true", "evidence": "Accurate description of Bitnami sealed-secrets model; repo tooling (generate-sealed-secrets.sh) targets it", "confidence": "medium", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a014::c1", "atom_id": "a014", "type": "prose", "line": "82", "claim": "A secret-management doc kubernetes-secrets.md exists and is linked.", "oracle": "source-path,k8s", "check": "ls docs/operations/kubernetes-secrets.md", "verdict": "true", "evidence": "smartem-devtools/docs/operations/kubernetes-secrets.md present (11901 bytes)", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/kubernetes.md::a015::c1", "atom_id": "a015", "type": "prose", "line": "86", "claim": "The 'k8s directory documentation' link (k8s/) resolves to detailed k8s docs.", "oracle": "source-path,k8s", "check": "ls docs/operations/k8s/", "verdict": "misleading", "evidence": "docs/operations/k8s/ does not exist (broken relative link); repo k8s/ dir exists but has no README/docs page", "confidence": "high", "file": "docs/operations/kubernetes.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a000::c1", "atom_id": "docs/operations/logging.md::a000", "type": "prose", "line": "3", "claim": "All SmartEM services support configurable logging levels.", "oracle": "", "check": "consumer argparse -v/-vv, api_server SMARTEM_LOG_LEVEL, agent watch -v/-vv all present", "verdict": "true", "evidence": "consumer.py:1258-1261 -v/-vv; api_server.py:260 SMARTEM_LOG_LEVEL; __main__.py:194 watch verbose", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a001::c1", "atom_id": "docs/operations/logging.md::a001", "type": "prose", "line": "7", "claim": "The -v and -vv flags control verbosity across all SmartEM components.", "oracle": "", "check": "checked every entrypoint for -v/-vv short flags", "verdict": "misleading", "evidence": "consumer & agent watch accept -v/-vv, but api_server has no argparse and agent parse/validate use --verbose int only", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a002::c1", "atom_id": "docs/operations/logging.md::a002", "type": "code", "line": "10-22", "claim": "consumer and api_server both take -v (INFO) and -vv (DEBUG), default ERROR.", "oracle": "cli-cmd", "check": "read consumer main() argparse and api_server __main__ block", "verdict": "misleading", "evidence": "consumer.py:1258-1263 -v/-vv OK; api_server __main__:3000-3006 has no argparse, -v/-vv silently ignored (only SMARTEM_LOG_LEVEL)", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a003::c1", "atom_id": "docs/operations/logging.md::a003", "type": "prose", "line": "25", "claim": "All agent commands support consistent verbosity flags.", "oracle": "", "check": "compared verbose param decls across agent Typer commands", "verdict": "misleading", "evidence": "only watch uses typer.Option('-v',count=True) (__main__.py:194); validate/parse use plain verbose:int=0 -> --verbose only, no -v", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a004::c1", "atom_id": "docs/operations/logging.md::a004", "type": "code", "line": "27-41", "claim": "agent watch/validate/parse commands all accept -v and -vv.", "oracle": "cli-cmd", "check": "inspected Typer param defs for each command", "verdict": "misleading", "evidence": "watch -v/-vv work; validate/parse decl verbose:int=0 -> Typer exposes --verbose not -v, so 'validate ... -v' and 'parse dir ... -vv' error", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a005::c1", "atom_id": "docs/operations/logging.md::a005", "type": "prose", "line": "45", "claim": "The watch command provides additional logging controls beyond -v/-vv.", "oracle": "", "check": "read watch_directory option list", "verdict": "true", "evidence": "__main__.py:176-196 defines log_file, log_interval, agent_id, session_id, heartbeat_interval options on watch", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a006::c1", "atom_id": "docs/operations/logging.md::a006", "type": "code", "line": "47-60", "claim": "watch accepts --log-file, --log-interval, --agent-id, --session-id, --heartbeat-interval and writes to the log file.", "oracle": "cli-cmd", "check": "traced each option; checked log_file usage", "verdict": "misleading", "evidence": "all options exist (__main__.py:177-196) but log_file is never passed to watcher/used anywhere - grep shows only the decl line, so --log-file is a no-op", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a007::c1", "atom_id": "docs/operations/logging.md::a007", "type": "prose", "line": "64", "claim": "HTTP API logging can be controlled via environment variables.", "oracle": "", "check": "grep SMARTEM_LOG_LEVEL in api_server", "verdict": "true", "evidence": "api_server.py:260 os.getenv('SMARTEM_LOG_LEVEL','ERROR') maps to logging level at import", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a008::c1", "atom_id": "docs/operations/logging.md::a008", "type": "code", "line": "66-71", "claim": "SMARTEM_LOG_LEVEL=ERROR/INFO/DEBUG sets api_server log level.", "oracle": "env-var,cli-cmd", "check": "read api_server log-level mapping", "verdict": "true", "evidence": "api_server.py:260-262 log_level_map={'ERROR','INFO','DEBUG'}, default ERROR", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a009::c1", "atom_id": "docs/operations/logging.md::a009", "type": "prose", "line": "75-77", "claim": "ERROR is default; -v gives INFO; -vv gives DEBUG.", "oracle": "", "check": "read verbosity->level mapping in consumer and watch", "verdict": "true", "evidence": "consumer.py:1202-1208 and __main__.py:201-206: >=2 DEBUG, ==1 INFO, else ERROR", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a011::c1", "atom_id": "docs/operations/logging.md::a011", "type": "prose", "line": "82-84", "claim": "At ERROR level backend logs DB connection errors, API startup failures, critical system errors.", "oracle": "", "check": "no per-level content contract in code; requires runtime log inspection", "verdict": "unverifiable", "evidence": "log content-by-level is descriptive; no code contract enumerates these categories, needs runtime observation", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a012::c1", "atom_id": "docs/operations/logging.md::a012", "type": "prose", "line": "87-90", "claim": "At INFO level backend logs startup/shutdown, request summaries, DB status, config info.", "oracle": "", "check": "descriptive log-content claim; no single code contract", "verdict": "unverifiable", "evidence": "requires enumerating/observing all INFO-level log statements at runtime", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a013::c1", "atom_id": "docs/operations/logging.md::a013", "type": "prose", "line": "93-96", "claim": "At DEBUG level backend logs request details, DB queries, MQ ops, stack traces.", "oracle": "", "check": "descriptive log-content claim; no single code contract", "verdict": "unverifiable", "evidence": "api_server.py:303 logs POST/PUT bodies at DEBUG but full mapping needs runtime observation", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a014::c1", "atom_id": "docs/operations/logging.md::a014", "type": "prose", "line": "99-101", "claim": "At ERROR level agent logs file permission errors, API connection failures, critical parsing errors.", "oracle": "", "check": "descriptive log-content claim; no single code contract", "verdict": "unverifiable", "evidence": "requires enumerating/observing agent ERROR-level log statements at runtime", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a015::c1", "atom_id": "docs/operations/logging.md::a015", "type": "prose", "line": "104-108", "claim": "At INFO level agent logs file detection, grid/foilhole creation, API status, SSE events, heartbeat status.", "oracle": "", "check": "descriptive log-content claim; no single code contract", "verdict": "unverifiable", "evidence": "watch does log SSE/API/status but exact INFO-vs-DEBUG mapping needs runtime observation", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a016::c1", "atom_id": "docs/operations/logging.md::a016", "type": "prose", "line": "111-116", "claim": "At DEBUG level agent logs file parsing, fs events, API req/resp, SSE content, heartbeat timing, retries.", "oracle": "", "check": "descriptive log-content claim; no single code contract", "verdict": "unverifiable", "evidence": "requires enumerating/observing agent DEBUG-level log statements at runtime", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a018::c1", "atom_id": "docs/operations/logging.md::a018", "type": "code", "line": "121-125", "claim": "Dev: 'watch /data -vv' and 'api_server -vv' both enable DEBUG logging.", "oracle": "cli-cmd", "check": "verified -vv handling on watch and api_server", "verdict": "misleading", "evidence": "watch -vv OK; api_server has no argparse (__main__:3000-3006) so -vv is ignored, level stays SMARTEM_LOG_LEVEL/ERROR", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a019::c1", "atom_id": "docs/operations/logging.md::a019", "type": "code", "line": "128-132", "claim": "Testing: 'watch /data -v' and 'api_server -v' both enable INFO logging.", "oracle": "cli-cmd", "check": "verified -v handling on watch and api_server", "verdict": "misleading", "evidence": "watch -v OK; api_server -v ignored (no argparse), level unchanged; correct method is SMARTEM_LOG_LEVEL=INFO", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a020::c1", "atom_id": "docs/operations/logging.md::a020", "type": "code", "line": "135-145", "claim": "Production: default commands log at ERROR; watch --log-file with --log-interval and -v rotates operational logs.", "oracle": "cli-cmd", "check": "checked defaults and --log-file wiring", "verdict": "misleading", "evidence": "defaults ERROR OK, --log-interval/-v OK, but --log-file is never used (no file written); __main__.py:177 is its only reference", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a022::c1", "atom_id": "docs/operations/logging.md::a022", "type": "prose", "line": "150", "claim": "The agent's --log-file parameter creates structured JSON logs suitable for analysis.", "oracle": "", "check": "grep log_file across agent module", "verdict": "incorrect", "evidence": "log_file appears only at __main__.py:177 as a CLI default; never passed to watcher or used - no log file is created", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a023::c1", "atom_id": "docs/operations/logging.md::a023", "type": "code", "line": "152-167", "claim": "The JSON log has fields timestamp, event_count and events[] with event_type/source_path/relative_path/size/modified.", "oracle": "", "check": "read _log_status() and grep event_count/source_path/relative_path", "verdict": "incorrect", "evidence": "fs_watcher.py:462-472 status log has queue_size,events_processed,successful,orphaned,failed,orphans_*; no event_count/events/source_path exist", "confidence": "high", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a024::c1", "atom_id": "docs/operations/logging.md::a024", "type": "code", "line": "170-181", "claim": "A logrotate config can manage /var/log/smartem/agent.log.", "oracle": "", "check": "generic ops snippet; logrotate syntax valid", "verdict": "true", "evidence": "generic valid logrotate stanza; not codebase-specific (though agent does not itself write agent.log - see a022)", "confidence": "low", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/logging.md::a025::c1", "atom_id": "docs/operations/logging.md::a025", "type": "prose", "line": "183", "claim": "The verbosity control system reduces log noise in normal operation and gives detailed output when troubleshooting.", "oracle": "", "check": "verified -v/-vv level control exists", "verdict": "true", "evidence": "consumer.py:1202-1208 & __main__.py:201-206 gate verbosity ERROR/INFO/DEBUG as described", "confidence": "medium", "file": "docs/operations/logging.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a002::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a002", "type": "prose", "line": "22", "claim": "The published PyPI package is named 'smartem-workspace' and is built by GitHub Actions in smartem-devtools.", "oracle": "source-path", "check": "read packages/smartem-workspace/pyproject.toml name field; release workflow present", "verdict": "true", "evidence": "pyproject.toml name=\"smartem-workspace\"; .github/workflows/release-smartem-workspace.yml exists", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a002::c2", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a002", "type": "prose", "line": "22", "claim": "GitHub Actions automatically publishes smartem-workspace to both PyPI and TestPyPI.", "oracle": "source-path", "check": "inspect release-smartem-workspace.yml jobs and grep workflows for testpypi", "verdict": "misleading", "evidence": "workflow publishes to PyPI only (publish-pypi, is_stable); no TestPyPI job; grep testpypi in .github/workflows = none", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a002::c3", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a003", "type": "prose", "line": "22-27", "claim": "CI authentication from GitHub Actions requires API tokens stored as GitHub Secrets.", "oracle": "source-path", "check": "inspect publish-pypi job auth mechanism", "verdict": "incorrect", "evidence": "publish uses PyPI Trusted Publishing (pypa/gh-action-pypi-publish@release/v1 + permissions id-token:write); no token secret", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a006::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a006", "type": "prose", "line": "35-37", "claim": "The team email for the package is smartem@diamond.ac.uk.", "oracle": "source-path", "check": "read pyproject.toml authors email", "verdict": "true", "evidence": "pyproject.toml authors email = smartem@diamond.ac.uk", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a033::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a033", "type": "prose", "line": "128-132", "claim": "The publish workflow expects a GitHub secret named PYPI_API_TOKEN.", "oracle": "source-path", "check": "grep .github for PYPI_API_TOKEN / secrets.PYPI", "verdict": "incorrect", "evidence": "grep PYPI_API_TOKEN/secrets.PYPI in .github/workflows = none; publish uses OIDC trusted publishing (release-smartem-workspace.yml:236-237)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a034::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a034", "type": "prose", "line": "134-138", "claim": "The workflow uses a GitHub secret named TEST_PYPI_API_TOKEN to publish to TestPyPI.", "oracle": "source-path", "check": "grep .github for TEST_PYPI_API_TOKEN and testpypi", "verdict": "incorrect", "evidence": "grep TEST_PYPI_API_TOKEN/testpypi in .github/workflows = none; no TestPyPI publish job exists", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a041::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a041", "type": "code", "line": "165-175", "claim": "Building at packages/smartem-workspace with 'uv build' yields smartem_workspace--py3-none-any.whl and smartem_workspace-.tar.gz.", "oracle": "cli-cmd", "check": "read pyproject build-system; ls dist/", "verdict": "true", "evidence": "pyproject build-backend=hatchling; dist/ contains smartem_workspace-*.whl and .tar.gz (example version 0.1.0 stale; current 0.7.0)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a052::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a052", "type": "code", "line": "256-267", "claim": "'smartem-workspace init --preset minimal --no-interactive' is a valid CLI invocation.", "oracle": "cli-cmd", "check": "grep cli.py for init subcommand, --preset, --no-interactive options", "verdict": "true", "evidence": "cli.py: def init (l89); Option --preset help lists 'minimal' (l96); Option --interactive/--no-interactive (l100)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a052::c2", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a052", "type": "code", "line": "256-267", "claim": "'uvx smartem-workspace --version' prints the installed version.", "oracle": "cli-cmd", "check": "grep cli.py for --version option and pyproject scripts entrypoint", "verdict": "true", "evidence": "cli.py has --version option (l67); pyproject [project.scripts] smartem-workspace = smartem_workspace.cli:app", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a076::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a076", "type": "prose", "line": "382-385", "claim": "Package version is set via the 'version' field in packages/smartem-workspace/pyproject.toml.", "oracle": "source-path", "check": "read pyproject.toml version field", "verdict": "true", "evidence": "pyproject.toml has version = \"0.7.0\"; workflow reads BASE_VERSION from this field (release-smartem-workspace.yml:51)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a082::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a082", "type": "prose", "line": "398", "claim": "CI handles version bumping automatically with commitizen.", "oracle": "source-path", "check": "grep repo for commitizen; read version job in workflow", "verdict": "incorrect", "evidence": "no commitizen anywhere (grep=none); version read from pyproject.toml, RC suffix = rc${run_number} via sed (workflow:51,89,197-198)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a094::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a094", "type": "code", "line": "438-440", "claim": "The workflow sets TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}.", "oracle": "source-path", "check": "grep .github/workflows for TWINE / secrets.PYPI", "verdict": "incorrect", "evidence": "no TWINE/secrets.PYPI in workflows; twine only used for 'uvx twine check dist/*' metadata check (workflow:206), upload via pypa action", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a114::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a114", "type": "code", "line": "505-511", "claim": "The GitHub Actions workflow uses env TWINE_USERNAME=__token__ and TWINE_PASSWORD=secrets.PYPI_API_TOKEN/TEST_PYPI_API_TOKEN.", "oracle": "source-path", "check": "grep workflow for TWINE_USERNAME/TWINE_PASSWORD", "verdict": "incorrect", "evidence": "release-smartem-workspace.yml has no TWINE_* env; publish via pypa/gh-action-pypi-publish (OIDC), no token secrets", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a115::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a115", "type": "table", "line": "515-520", "claim": "Triggers: push to main -> TestPyPI (TEST_PYPI_API_TOKEN); tag smartem-workspace-v* -> PyPI (PYPI_API_TOKEN); PR -> no publish; workflow_dispatch -> no publish.", "oracle": "source-path", "check": "read on: triggers and job conditions in workflow", "verdict": "incorrect", "evidence": "push to main -> RC GitHub release only (no TestPyPI); tag correct pattern but publish via OIDC not token; workflow_dispatch DOES release rc/stable (workflow:14-23,78-107,218-222)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a118::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a118", "type": "code", "line": "528-540", "claim": "Every push to main with package changes uploads the package to TestPyPI.", "oracle": "source-path", "check": "read version job main branch path and publish-pypi condition", "verdict": "incorrect", "evidence": "push to main produces an RC GitHub prerelease only (create-release, should_release); PyPI publish gated on is_stable; no TestPyPI (workflow:78-92,218-244)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a120::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a120", "type": "code", "line": "546-559", "claim": "Pushing a release tag smartem-workspace-v* triggers the workflow to publish to PyPI.", "oracle": "source-path", "check": "read on.push.tags and publish-pypi job condition", "verdict": "true", "evidence": "on.push.tags ['smartem-workspace-v*'] (l6); is_stable set on tag (l68-77); publish-pypi if is_stable (l218-237). GitHub Release is automated, not manual", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a099::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a099", "type": "prose", "line": "455-467", "claim": "The CI version bump is driven by conventional commits (feat->minor, fix->patch, BREAKING CHANGE->major) since the last tag.", "oracle": "source-path", "check": "read version job logic in workflow", "verdict": "incorrect", "evidence": "no conventional-commit/semver bumping; version fixed from pyproject.toml, RC=base+rc; stable requires tag==pyproject version (workflow:51,68-107)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a122::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a122", "type": "prose", "line": "565-568", "claim": "A GitHub 'testpypi' environment (URL test.pypi.org/p/smartem-workspace) inherits TEST_PYPI_API_TOKEN.", "oracle": "url", "check": "grep workflow for environment testpypi / TestPyPI", "verdict": "incorrect", "evidence": "no testpypi environment or TestPyPI flow in workflow; only 'pypi' environment defined (workflow:223-225)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a123::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a123", "type": "prose", "line": "570-573", "claim": "A GitHub 'pypi' environment with URL https://pypi.org/p/smartem-workspace is used for the production publish.", "oracle": "url", "check": "read environment block in publish-pypi job", "verdict": "true", "evidence": "publish-pypi environment: name pypi, url https://pypi.org/p/smartem-workspace (workflow:223-225)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a123::c2", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a123", "type": "prose", "line": "570-573", "claim": "The pypi environment inherits/uses the PYPI_API_TOKEN secret.", "oracle": "source-path", "check": "inspect publish step auth", "verdict": "incorrect", "evidence": "publish uses OIDC trusted publishing (id-token:write + pypa action), not a PYPI_API_TOKEN secret (workflow:226-237)", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a151::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a151", "type": "code", "line": "703-716", "claim": "The package CLI source lives at smartem_workspace/cli.py.", "oracle": "source-path", "check": "find cli.py in package", "verdict": "true", "evidence": "packages/smartem-workspace/smartem_workspace/cli.py exists", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a159::c1", "atom_id": "docs/operations/publish-smartem-workspace-to-pypi.md::a159", "type": "prose", "line": "750-752", "claim": "The repository is DiamondLightSource/smartem-devtools (issues at github.com/DiamondLightSource/smartem-devtools/issues).", "oracle": "url", "check": "read pyproject [project.urls]", "verdict": "true", "evidence": "pyproject urls Repository/Issues = github.com/DiamondLightSource/smartem-devtools", "confidence": "high", "file": "docs/operations/publish-smartem-workspace-to-pypi.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "There are three packages in the SmartEM ecosystem to release.", "oracle": "source-path", "check": "counted documented sections and release workflows across repos", "verdict": "misleading", "evidence": "Doc documents FOUR: smartem-decisions, smartem-epuplayer, smartem-workspace, smartem-frontend; 4 release-*.yml workflows exist", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a000::c2", "atom_id": "a000", "type": "prose", "line": "3", "claim": "All releases are tag-driven via GitHub Actions.", "oracle": "source-path", "check": "read workflow `on:` triggers", "verdict": "misleading", "evidence": "RC releases are automatic on push to main (release-*.yml on.push.branches [main]); only stable releases are tag-driven", "confidence": "medium", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a001::c1", "atom_id": "a001", "type": "table", "line": "7-11", "claim": "RC = automatic on push to main w/ path filters; Stable = push a version tag; Manual = workflow_dispatch with rc/stable choice.", "oracle": "source-path", "check": "compared table to all four workflow trigger blocks", "verdict": "true", "evidence": "on.push.branches[main]+paths (RC), tags['*-v*'] (stable), workflow_dispatch inputs release_type choice rc/stable in every workflow", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "15-19", "claim": "CI creates an RC release on merge to main if path filters match and no stable tag exists for the current version.", "oracle": "cli-cmd", "check": "read version job logic in release workflows", "verdict": "true", "evidence": "release-smartem-decisions.yml L75-89: on main, skip RC if `smartem-decisions-v${CLEAN_VERSION}` tag exists else create rc${run_number}", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a002::c2", "atom_id": "a002", "type": "prose", "line": "15-19", "claim": "Stable release is produced by pushing a version tag, after which CI tests, builds, publishes and creates a Release.", "oracle": "cli-cmd", "check": "read tag trigger + downstream jobs", "verdict": "true", "evidence": "tags['smartem-decisions-v*'] sets is_stable; publish-pypi/publish-container/create-release jobs gated on is_stable/should_release", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "23-26", "claim": "smartem-decisions releases via workflow release-smartem-decisions.yml in the smartem-decisions repo.", "oracle": "source-path", "check": "ls .github/workflows", "verdict": "true", "evidence": "repos/DiamondLightSource/smartem-decisions/.github/workflows/release-smartem-decisions.yml present", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "23-26", "claim": "smartem-decisions versioning uses setuptools_scm (version derived from git tags).", "oracle": "source-path", "check": "grep pyproject.toml", "verdict": "true", "evidence": "pyproject.toml L2 setuptools_scm[toml]>=8, L14 dynamic=['version'], L128 [tool.setuptools_scm]; workflow uses setuptools_scm get_version", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a003::c3", "atom_id": "a003", "type": "prose", "line": "23-26", "claim": "smartem-decisions tag prefix is smartem-decisions-v.", "oracle": "source-path", "check": "read workflow tag pattern", "verdict": "true", "evidence": "release-smartem-decisions.yml L6 tags: ['smartem-decisions-v*']; create-release tag_name smartem-decisions-v{version}", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a004::c1", "atom_id": "a004", "type": "prose", "line": "30-33", "claim": "Workflow triggers on changes to src/smartem_agent/**, src/smartem_common/**, src/smartem_backend/api_client.py.", "oracle": "source-path", "check": "read workflow paths filter", "verdict": "true", "evidence": "release-smartem-decisions.yml L7-10 paths list exactly these three (plus the workflow file itself)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a005::c1", "atom_id": "a005", "type": "prose", "line": "37-40", "claim": "Artifacts: Python wheel (PyPI stable only), Windows exe smartem-agent-windows-v{VERSION}.exe, Docker image (GHCR stable only), GitHub Release.", "oracle": "source-path", "check": "read jobs + if-conditions", "verdict": "true", "evidence": "publish-pypi/publish-container if is_stable; build-windows renames to smartem-agent-windows-v${VERSION}.exe; create-release attaches artifacts", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a006::c1", "atom_id": "a006", "type": "code", "line": "44-48", "claim": "A stable release is cut with `git tag smartem-decisions-v0.2.0 && git push origin smartem-decisions-v0.2.0`.", "oracle": "cli-cmd", "check": "matched tag pattern against workflow trigger", "verdict": "true", "evidence": "tag matches on.push.tags['smartem-decisions-v*']; version derived from tag suffix (L71); 0.2.0 is illustrative", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "50-57", "claim": "CI: tests Linux+Windows, ruff+pyright lint, build wheel+exe, smoke-test exe, publish PyPI via OIDC, push Docker to ghcr.io/diamondlightsource/smartem-decisions:{VERSION}, create GitHub Release.", "oracle": "cli-cmd", "check": "read all jobs in release-smartem-decisions.yml", "verdict": "true", "evidence": "jobs test-linux/test-windows, lint (ruff+pyright L177/180), build-wheel/build-windows, smoke-test-windows, publish-pypi (id-token write), publish-container (ghcr.io/${repository}), create-release", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a008::c1", "atom_id": "a008", "type": "prose", "line": "61-65", "claim": "smartem-epuplayer lives in smartem-devtools and releases via release-smartem-epuplayer.yml.", "oracle": "url", "check": "ls workflows + packages", "verdict": "true", "evidence": "smartem-devtools/.github/workflows/release-smartem-epuplayer.yml + packages/smartem-epuplayer/ present", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a008::c2", "atom_id": "a008", "type": "prose", "line": "61-65", "claim": "epuplayer version is set manually in both pyproject.toml and smartem_epuplayer/__init__.py which must match.", "oracle": "source-path", "check": "grep version files + workflow validation", "verdict": "true", "evidence": "pyproject.toml L7 version=1.1.0, __init__.py L1 __version__=1.1.0; workflow L58-61 exits on mismatch", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a008::c3", "atom_id": "a008", "type": "prose", "line": "61-65", "claim": "epuplayer tag prefix is epuplayer-v and package directory is packages/smartem-epuplayer/.", "oracle": "source-path", "check": "read workflow tag + PACKAGE_DIR env", "verdict": "true", "evidence": "release-smartem-epuplayer.yml L6 tags['epuplayer-v*'], L29 PACKAGE_DIR=packages/smartem-epuplayer", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a009::c1", "atom_id": "a009", "type": "prose", "line": "69-70", "claim": "epuplayer workflow triggers on changes to packages/smartem-epuplayer/**.", "oracle": "source-path", "check": "read workflow paths", "verdict": "true", "evidence": "release-smartem-epuplayer.yml L8 paths: packages/smartem-epuplayer/**", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a010::c1", "atom_id": "a010", "type": "prose", "line": "74-76", "claim": "epuplayer artifacts: wheel (PyPI stable only), Windows exe epuplayer-windows-v{VERSION}.exe, GitHub Release.", "oracle": "source-path", "check": "read jobs", "verdict": "true", "evidence": "publish-pypi if is_stable; build-windows renames epuplayer-windows-v${VERSION}.exe; create-release if should_release", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a012::c1", "atom_id": "a012", "type": "code", "line": "82-88", "claim": "Version is bumped in packages/smartem-epuplayer/pyproject.toml (version=) and smartem_epuplayer/__init__.py (__version__=).", "oracle": "source-path", "check": "grep both files", "verdict": "true", "evidence": "pyproject.toml L7 version=, __init__.py L1 __version__=; example 1.2.0 illustrative (actual 1.1.0)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a013::c1", "atom_id": "a013", "type": "prose", "line": "90", "claim": "CI fails if the pyproject and __init__ versions don't match.", "oracle": "cli-cmd", "check": "read version job", "verdict": "true", "evidence": "release-smartem-epuplayer.yml L58-61 emits ::error:: and exit 1 on mismatch", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a014::c1", "atom_id": "a014", "type": "code", "line": "94-97", "claim": "epuplayer stable release: git tag epuplayer-v1.2.0 && git push.", "oracle": "cli-cmd", "check": "matched tag against trigger", "verdict": "true", "evidence": "matches tags['epuplayer-v*']; workflow validates tag suffix == pyproject version (L71-73)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a015::c1", "atom_id": "a015", "type": "prose", "line": "101-105", "claim": "smartem-workspace lives in smartem-devtools and releases via release-smartem-workspace.yml.", "oracle": "url", "check": "ls workflows + packages", "verdict": "true", "evidence": "smartem-devtools/.github/workflows/release-smartem-workspace.yml + packages/smartem-workspace/ present", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a015::c2", "atom_id": "a015", "type": "prose", "line": "101-105", "claim": "workspace version is manual in pyproject.toml and smartem_workspace/__init__.py which must match.", "oracle": "source-path", "check": "grep files + workflow", "verdict": "true", "evidence": "pyproject.toml L7 version=0.7.0, __init__.py L3 __version__=0.7.0; workflow L58-61 exits on mismatch", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a015::c3", "atom_id": "a015", "type": "prose", "line": "101-105", "claim": "workspace tag prefix smartem-workspace-v, package dir packages/smartem-workspace/.", "oracle": "source-path", "check": "read workflow tag + env", "verdict": "true", "evidence": "release-smartem-workspace.yml L6 tags['smartem-workspace-v*'], L29 PACKAGE_DIR=packages/smartem-workspace", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a016::c1", "atom_id": "a016", "type": "prose", "line": "109-110", "claim": "workspace workflow triggers on changes to packages/smartem-workspace/**.", "oracle": "source-path", "check": "read workflow paths", "verdict": "true", "evidence": "release-smartem-workspace.yml L8 paths: packages/smartem-workspace/**", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a017::c1", "atom_id": "a017", "type": "prose", "line": "114-115", "claim": "workspace artifacts: Python wheel (PyPI stable only), GitHub Release (no exe, no Docker).", "oracle": "source-path", "check": "read jobs", "verdict": "true", "evidence": "workflow has publish-pypi (if is_stable) + create-release only; no build-windows or container job", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a019::c1", "atom_id": "a019", "type": "code", "line": "121-127", "claim": "workspace version bumped in packages/smartem-workspace/pyproject.toml and smartem_workspace/__init__.py.", "oracle": "source-path", "check": "grep both files", "verdict": "true", "evidence": "pyproject.toml L7 version=0.7.0, smartem_workspace/__init__.py L3 __version__=0.7.0", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a020::c1", "atom_id": "a020", "type": "code", "line": "131-134", "claim": "workspace stable release: git tag smartem-workspace-v0.7.0 && git push.", "oracle": "cli-cmd", "check": "matched tag against trigger", "verdict": "true", "evidence": "matches tags['smartem-workspace-v*']; workflow validates tag suffix == pyproject version", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a021::c1", "atom_id": "a021", "type": "prose", "line": "138-139", "claim": "smartem-workspace is commonly run via uvx and may serve stale cached versions after publish.", "oracle": "cli-cmd", "check": "read workflow release-notes referencing uvx", "verdict": "true", "evidence": "workspace release notes emit `uvx smartem-workspace --help`; uvx caching is standard behaviour", "confidence": "medium", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a022::c1", "atom_id": "a022", "type": "code", "line": "141-147", "claim": "`uvx --refresh smartem-workspace` and `uvx smartem-workspace@latest` force fresh version.", "oracle": "cli-cmd", "check": "known uvx flags + package name verified", "verdict": "true", "evidence": "--refresh and @latest are valid uvx flags; package name smartem-workspace confirmed on PyPI (publish-pypi url pypi.org/p/smartem-workspace)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a023::c1", "atom_id": "a023", "type": "prose", "line": "151-155", "claim": "smartem-frontend releases via release-smartem-frontend.yml in the smartem-frontend repo.", "oracle": "url", "check": "ls workflow", "verdict": "true", "evidence": "smartem-frontend/.github/workflows/release-smartem-frontend.yml present", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a023::c2", "atom_id": "a023", "type": "prose", "line": "151-155", "claim": "frontend version is manual in both apps/smartem/package.json and apps/smartem/src/version.ts which must match.", "oracle": "source-path", "check": "grep files + workflow", "verdict": "true", "evidence": "package.json L3 version=0.3.0, version.ts FRONTEND_VERSION='0.3.0'; workflow L67-70 exits on mismatch", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a023::c3", "atom_id": "a023", "type": "prose", "line": "151-155", "claim": "frontend tag prefix smartem-frontend-v, app dir apps/smartem/.", "oracle": "source-path", "check": "read workflow tag + APP_DIR", "verdict": "true", "evidence": "release-smartem-frontend.yml L6 tags['smartem-frontend-v*'], L41 APP_DIR=apps/smartem", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a025::c1", "atom_id": "a025", "type": "prose", "line": "161-167", "claim": "frontend workflow path filters: apps/smartem/**, packages/api/**, packages/ui/**, package.json, package-lock.json, Dockerfile, scripts/write-version-json.mjs, .github/workflows/release-smartem-frontend.yml.", "oracle": "source-path", "check": "read workflow paths", "verdict": "true", "evidence": "release-smartem-frontend.yml L8-15 lists all eight paths exactly", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a026::c1", "atom_id": "a026", "type": "prose", "line": "169", "claim": "apps/legacy/** is excluded by design from the frontend release.", "oracle": "source-path", "check": "grep path filters + ls apps/", "verdict": "misleading", "evidence": "apps/legacy no longer exists (removed per #136); it is not in the include filters and there is no explicit exclude directive — stale reference", "confidence": "medium", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a027::c1", "atom_id": "a027", "type": "prose", "line": "173-175", "claim": "frontend artifacts: SPA dist/ (every run), Docker image (GHCR stable and RC), GitHub Release (stable and RC; RC marked prerelease).", "oracle": "source-path", "check": "read build/publish-container/release jobs", "verdict": "true", "evidence": "build uploads smartem-dist unconditionally; publish-container+release gated on should_release (RC+stable); release prerelease=is_rc", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a029::c1", "atom_id": "a029", "type": "code", "line": "181-184", "claim": "frontend version set in apps/smartem/package.json \"version\".", "oracle": "source-path", "check": "grep package.json", "verdict": "true", "evidence": "apps/smartem/package.json L3 \"version\": \"0.3.0\" (0.2.0 in doc is illustrative)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a030::c1", "atom_id": "a030", "type": "code", "line": "186-189", "claim": "frontend version set in apps/smartem/src/version.ts as `export const FRONTEND_VERSION`.", "oracle": "source-path", "check": "read version.ts", "verdict": "true", "evidence": "apps/smartem/src/version.ts: export const FRONTEND_VERSION = '0.3.0' as const", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "191-192", "claim": "CI version job fails if package.json and version.ts differ; RCs are skipped if a stable tag already exists for the current version.", "oracle": "cli-cmd", "check": "read version job", "verdict": "true", "evidence": "release-smartem-frontend.yml L67-70 exit on mismatch; L83-90 skip RC if smartem-frontend-v${CLEAN_VERSION} tag exists", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a032::c1", "atom_id": "a032", "type": "code", "line": "196-199", "claim": "frontend stable release: git tag smartem-frontend-v0.2.0 && git push.", "oracle": "cli-cmd", "check": "matched tag against trigger", "verdict": "true", "evidence": "matches tags['smartem-frontend-v*']; version derived from tag suffix (L78)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "203-207", "claim": "CI determines version from the tag and validates it matches package.json and version.ts.", "oracle": "source-path", "check": "read version job", "verdict": "true", "evidence": "version job reads package.json+version.ts, exits on mismatch, derives VERSION from tag suffix", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a034::c2", "atom_id": "a034", "type": "prose", "line": "203-207", "claim": "CI runs biome check and tsc across the workspace.", "oracle": "cli-cmd", "check": "read lint job + package.json scripts", "verdict": "true", "evidence": "lint job runs `npm run check` (biome check .) and `npm run typecheck` (tsc --workspaces)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a034::c3", "atom_id": "a034", "type": "prose", "line": "203-207", "claim": "CI generates the API client (npm run api:generate), writes version.json, and runs `npm run build:smartem`.", "oracle": "cli-cmd", "check": "grep root package.json scripts + read build job", "verdict": "misleading", "evidence": "No `build:smartem` script exists; root script is `build` (npm run build -w @smartem/app) and workflow runs `npm run build` (L165). api:generate + version.json steps are correct", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a034::c4", "atom_id": "a034", "type": "prose", "line": "203-207", "claim": "CI builds and pushes the Docker image to ghcr.io/diamondlightsource/smartem-frontend:{VERSION} and :latest.", "oracle": "source-path", "check": "read publish-container metadata", "verdict": "true", "evidence": "images ghcr.io/${repository_owner}/smartem-frontend; tags version + latest (latest enabled when is_stable)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a034::c5", "atom_id": "a034", "type": "prose", "line": "203-207", "claim": "CI creates a GitHub Release tagged smartem-frontend-v{VERSION}.", "oracle": "source-path", "check": "read release job", "verdict": "true", "evidence": "release job: tag_name smartem-frontend-v${version}", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a035::c1", "atom_id": "a035", "type": "prose", "line": "211", "claim": "The image emits /version, aliased to version.json by apps/smartem/nginx.conf.", "oracle": "source-path", "check": "grep nginx.conf", "verdict": "true", "evidence": "apps/smartem/nginx.conf L22-24: location = /version { alias /usr/share/nginx/html/version.json; }", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a036::c1", "atom_id": "a036", "type": "code", "line": "213-216", "claim": "/version returns JSON with keys frontend, backendApi, gitSha, buildTime.", "oracle": "url", "check": "read write-version-json.mjs", "verdict": "true", "evidence": "scripts/write-version-json.mjs builds info = {frontend, backendApi, gitSha, buildTime}", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a037::c1", "atom_id": "a037", "type": "prose", "line": "218-219", "claim": "frontend from FRONTEND_VERSION arg, backendApi from bundled OpenAPI spec, gitSha/buildTime from git at build time, per scripts/write-version-json.mjs.", "oracle": "cli-cmd", "check": "read write-version-json.mjs + workflow env", "verdict": "true", "evidence": "script: frontend=process.env.FRONTEND_VERSION, backendApi=openapi.info.version (packages/api/src/openapi.json), gitSha=GIT_SHA(github.sha), buildTime=BUILD_TIME", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a038::c1", "atom_id": "a038", "type": "prose", "line": "223-224", "claim": "All three workflows support workflow_dispatch with an rc/stable choice via the Actions tab.", "oracle": "source-path", "check": "read workflow_dispatch inputs across workflows", "verdict": "misleading", "evidence": "There are FOUR release workflows (decisions, epuplayer, workspace, frontend); all four DO have workflow_dispatch inputs.release_type choice rc/stable — the count 'three' is stale", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "226-229", "claim": "Manual dispatch is useful when an RC wasn't auto-triggered, to re-release after a CI fix, or to get a stable release without pushing a tag.", "oracle": "cli-cmd", "check": "read workflow_dispatch stable branch", "verdict": "true", "evidence": "workflow_dispatch with inputs.release_type=stable sets is_stable=true without a tag (version job elif workflow_dispatch)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a041::c1", "atom_id": "a041", "type": "prose", "line": "235-236", "claim": "Both RC and stable releases appear on the Releases page, with RCs marked as pre-release.", "oracle": "source-path", "check": "read create-release prerelease flag", "verdict": "true", "evidence": "all create-release jobs run when should_release (RC+stable) with prerelease: is_rc == 'true'", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a042::c1", "atom_id": "a042", "type": "code", "line": "240-249", "claim": "PyPI packages are smartem-decisions, smartem-epuplayer, smartem-workspace (pip install ==VERSION).", "oracle": "cli-cmd", "check": "read publish-pypi environment urls", "verdict": "true", "evidence": "publish-pypi env urls pypi.org/p/smartem-decisions, /smartem-epuplayer, /smartem-workspace; versions in doc are illustrative", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a043::c1", "atom_id": "a043", "type": "code", "line": "253-260", "claim": "Docker images: ghcr.io/diamondlightsource/smartem-decisions (stable) and ghcr.io/diamondlightsource/smartem-frontend (stable + rcN).", "oracle": "cli-cmd", "check": "read docker metadata images", "verdict": "true", "evidence": "decisions image ghcr.io/${repository}=diamondlightsource/smartem-decisions; frontend ghcr.io/${owner}/smartem-frontend with rc${run_number} tags", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a045::c1", "atom_id": "a045", "type": "code", "line": "266-269", "claim": "Windows executables are named smartem-agent-windows-v{VERSION}.exe and epuplayer-windows-v{VERSION}.exe and accept --help.", "oracle": "cli-cmd", "check": "read build-windows rename + smoke tests", "verdict": "true", "evidence": "decisions renames smartem-agent-windows-v${VERSION}.exe (smoke --help); epuplayer renames epuplayer-windows-v${VERSION}.exe (smoke --help)", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/releasing.md::a046::c1", "atom_id": "a046", "type": "prose", "line": "273-275", "claim": "First-time PyPI setup is documented in publish-smartem-workspace-to-pypi.md.", "oracle": "source-path", "check": "ls doc file", "verdict": "true", "evidence": "smartem-devtools/docs/operations/publish-smartem-workspace-to-pypi.md present", "confidence": "high", "file": "docs/operations/releasing.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a000::c1", "atom_id": "docs/operations/run-container.md::a000", "type": "prose", "line": "3-4", "claim": "Pre-built smartem-decisions containers are available on GitHub Container Registry at ghcr.io/DiamondLightSource/smartem-decisions.", "oracle": "url", "check": "release workflow docker push target", "verdict": "true", "evidence": "release-smartem-decisions.yml:479 registry ghcr.io; :487 images: ghcr.io/${{ github.repository }} (DiamondLightSource/smartem-decisions)", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a002::c1", "atom_id": "docs/operations/run-container.md::a002", "type": "code", "line": "10-13", "claim": "Running the container with `--version` checks/prints the version.", "oracle": "cli-cmd", "check": "read entrypoint.sh arg handling", "verdict": "incorrect", "evidence": "entrypoint.sh ignores all CLI args ($@ never used); case ${ROLE:-api} -> defaults to api mode, runs alembic+uvicorn, never prints version", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a003::c1", "atom_id": "docs/operations/run-container.md::a003", "type": "prose", "line": "15", "claim": "A numbered release tag can be used instead of `latest` to get a released version.", "oracle": "cli-cmd", "check": "release workflow versioned tagging", "verdict": "true", "evidence": "release-smartem-decisions.yml tags 'smartem-decisions-v*' produce versioned image tags via metadata-action images: ghcr.io/...", "confidence": "medium", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a004::c1", "atom_id": "docs/operations/run-container.md::a004", "type": "prose", "line": "19", "claim": "The container runs in different modes controlled by the ROLE environment variable.", "oracle": "env-var", "check": "read entrypoint.sh", "verdict": "true", "evidence": "entrypoint.sh:15 case \"${ROLE:-api}\" in ... api) ... worker) ... *) exit 1", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a005::c1", "atom_id": "docs/operations/run-container.md::a005", "type": "code", "line": "23-35", "claim": "ROLE=api runs the HTTP API on port 8000, SMARTEM_LOG_LEVEL=INFO/DEBUG controls API logging, and HTTP_API_PORT overrides the port.", "oracle": "env-var,port,cli-cmd", "check": "entrypoint.sh + api_server.py", "verdict": "true", "evidence": "entrypoint.sh:20 uvicorn --port ${HTTP_API_PORT:-8000}; api_server.py:260 getenv SMARTEM_LOG_LEVEL default ERROR; :3003 getenv HTTP_API_PORT 8000", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a006::c1", "atom_id": "docs/operations/run-container.md::a006", "type": "code", "line": "39-48", "claim": "ROLE=worker runs the queue worker, and SMARTEM_LOG_LEVEL=INFO/DEBUG produce INFO/DEBUG worker logging.", "oracle": "env-var,cli-cmd", "check": "entrypoint.sh worker + consumer.py logging", "verdict": "misleading", "evidence": "worker runs (entrypoint.sh:25 python -m consumer) but consumer.main() sets level from argparse -v/-vv (consumer.py:1257-1263, default ERROR); entrypoint passes no -v and SMARTEM_LOG_LEVEL is never read by consumer, so INFO/DEBUG examples have no effect", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a007::c1", "atom_id": "docs/operations/run-container.md::a007", "type": "table", "line": "52-56", "claim": "Env var defaults are ROLE=api, HTTP_API_PORT=8000, SMARTEM_LOG_LEVEL=ERROR.", "oracle": "port", "check": "entrypoint.sh + api_server.py defaults", "verdict": "true", "evidence": "entrypoint.sh ${ROLE:-api} and ${HTTP_API_PORT:-8000}; api_server.py:260 getenv SMARTEM_LOG_LEVEL default \"ERROR\", :3003 getenv HTTP_API_PORT \"8000\"", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a008::c1", "atom_id": "docs/operations/run-container.md::a008", "type": "prose", "line": "60", "claim": "A Kubernetes deployment guide (kubernetes.md) provides a complete dev environment with database and message queue.", "oracle": "source-path,k8s", "check": "ls linked doc", "verdict": "true", "evidence": "docs/operations/kubernetes.md exists (3441 bytes) in smartem-devtools", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a010::c1", "atom_id": "docs/operations/run-container.md::a010", "type": "prose", "line": "66", "claim": "The pre-built containers run as root by default.", "oracle": "", "check": "read Dockerfile", "verdict": "true", "evidence": "Dockerfile:37-39 ARG groupid=0/userid=0/groupname=root; no USER directive, so container runs as root by default", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a010::c2", "atom_id": "docs/operations/run-container.md::a010", "type": "prose", "line": "66", "claim": "Production /dls filesystem access requires building custom images with specific UID/GID.", "oracle": "", "check": "read Dockerfile build args", "verdict": "true", "evidence": "Dockerfile:37-47 groupid/userid/groupname build args create non-root user when != 0; comments cite /dls mount requirement", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/run-container.md::a011::c1", "atom_id": "docs/operations/run-container.md::a011", "type": "prose", "line": "68-72", "claim": "container-user-configuration.md documents custom user/group builds, /dls mounting, security best practices, and permission troubleshooting.", "oracle": "source-path", "check": "grep headings of linked doc", "verdict": "true", "evidence": "container-user-configuration.md exists; headings Build Arguments, Custom User Mode, Volume Mounting/The /dls Directory, Security Considerations (27 /dls refs, permission sections)", "confidence": "high", "file": "docs/operations/run-container.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a000::c1", "atom_id": "a000", "type": "prose", "line": "3", "claim": "A tool called smartem-workspace sets up a complete SmartEM development environment.", "oracle": [], "check": "located package + entry point", "verdict": "true", "evidence": "packages/smartem-workspace/pyproject.toml name=smartem-workspace; [project.scripts] smartem-workspace=smartem_workspace.cli:app", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a001::c1", "atom_id": "a001", "type": "prose", "line": "7", "claim": "smartem-workspace is a command-line tool automating multi-repo workspace setup.", "oracle": [], "check": "read cli.py", "verdict": "true", "evidence": "cli.py: typer.Typer(name='smartem-workspace', help='CLI tool to automate SmartEM multi-repo workspace setup')", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a001::c2", "atom_id": "a001", "type": "prose", "line": "7", "claim": "It handles repository cloning, Claude Code config, Serena MCP setup, and workspace structure creation.", "oracle": [], "check": "read setup/ modules", "verdict": "true", "evidence": "setup/repos.py clone, setup/claude.py, setup/mcp.py (Serena), setup/workspace.py structure all present", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a002::c1", "atom_id": "a002", "type": "prose", "line": "11", "claim": "smartem-workspace init is the command that creates the workspace.", "oracle": [], "check": "read cli.py", "verdict": "true", "evidence": "cli.py @app.command() def init(...) 'Initialize a new SmartEM workspace.'", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a003::c1", "atom_id": "a003", "type": "prose", "line": "13-17", "claim": "init creates repository clones organised by GitHub org (DiamondLightSource, FragmentScreen, GitlabAriaPHP).", "oracle": ["source-path"], "check": "repos.py get_local_dir + repos.json localDir", "verdict": "true", "evidence": "repos.json org aria-php localDir='GitlabAriaPHP'; DLS/FragmentScreen use org name; clone_repo builds repos//", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a003::c2", "atom_id": "a003", "type": "prose", "line": "13-17", "claim": "init sets up Claude Code configuration and Serena MCP server as part of the default run.", "oracle": ["source-path"], "check": "cli.py init defaults", "verdict": "misleading", "evidence": "cli.py init: skip_claude = not with_claude; --with-claude defaults False, and Serena only runs 'if ... claude_config' -> both skipped by default", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a003::c3", "atom_id": "a003", "type": "prose", "line": "13-17", "claim": "init creates workspace structure (CLAUDE.md, tmp/, testdata/ directories).", "oracle": ["source-path"], "check": "setup/workspace.py", "verdict": "true", "evidence": "setup_workspace_structure creates repos/, tmp/, testdata/, testdata/dls-filesystem/; CLAUDE.md symlinked in claude setup", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a007::c1", "atom_id": "a007", "type": "prose", "line": "33-37", "claim": "Prerequisite: Python 3.11 or later.", "oracle": ["source-path", "url", "cli-cmd"], "check": "pyproject requires-python", "verdict": "true", "evidence": "packages/smartem-workspace/pyproject.toml requires-python = '>=3.11'", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a010::c1", "atom_id": "a010", "type": "code", "line": "45-47", "claim": "`uvx smartem-workspace init` runs the tool.", "oracle": [], "check": "entry point name + subcommand", "verdict": "true", "evidence": "pyproject script smartem-workspace=cli:app; init subcommand exists. PyPI publication itself not verifiable here", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a013::c1", "atom_id": "a013", "type": "code", "line": "59-62", "claim": "`uv tool install smartem-workspace` then `smartem-workspace init` works.", "oracle": ["cli-cmd"], "check": "entry point name", "verdict": "true", "evidence": "pyproject [project.scripts] smartem-workspace present; PyPI availability not verifiable here", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a016::c1", "atom_id": "a016", "type": "code", "line": "73-76", "claim": "`pipx install smartem-workspace` installs the tool.", "oracle": [], "check": "package name", "verdict": "true", "evidence": "pyproject name=smartem-workspace; PyPI publication not verifiable here", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a020::c1", "atom_id": "a020", "type": "prose", "line": "91-93", "claim": "Correct invocation is `uvx smartem-workspace init` (not python -m).", "oracle": ["cli-cmd"], "check": "entry point", "verdict": "true", "evidence": "console script smartem-workspace defined; init subcommand valid", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a026::c1", "atom_id": "a026", "type": "code", "line": "121-124", "claim": "`uvx smartem-workspace init --preset smartem-core --no-interactive` uses valid flags.", "oracle": [], "check": "cli.py init options", "verdict": "true", "evidence": "init has --preset and --interactive/--no-interactive options; smartem-core is a defined preset", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a028::c1", "atom_id": "a028", "type": "prose", "line": "132-134", "claim": "minimal preset contains 1 repository.", "oracle": [], "check": "repos.json presets", "verdict": "true", "evidence": "presets.minimal.repos = ['DiamondLightSource/smartem-devtools'] (1)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a030::c1", "atom_id": "a030", "type": "prose", "line": "141-142", "claim": "minimal preset includes DiamondLightSource/smartem-devtools.", "oracle": [], "check": "repos.json presets.minimal", "verdict": "true", "evidence": "presets.minimal.repos=['DiamondLightSource/smartem-devtools']", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a031::c1", "atom_id": "a031", "type": "prose", "line": "146-148", "claim": "smartem-core preset contains 3 repositories.", "oracle": [], "check": "repos.json presets.smartem-core", "verdict": "true", "evidence": "presets.smartem-core.repos = [smartem-decisions, smartem-frontend, smartem-devtools] (3)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a033::c1", "atom_id": "a033", "type": "prose", "line": "155-158", "claim": "smartem-core includes smartem-devtools, smartem-decisions, and smartem-frontend.", "oracle": [], "check": "repos.json presets.smartem-core", "verdict": "true", "evidence": "repos list = smartem-decisions, smartem-frontend, smartem-devtools", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a034::c1", "atom_id": "a034", "type": "prose", "line": "162-164", "claim": "aria-reference preset contains 20+ repositories.", "oracle": [], "check": "resolve aria-reference preset", "verdict": "incorrect", "evidence": "presets.aria-reference.repos=[fandanGO-cryoem-dls, FragmentScreen/* (6), aria-php/data-deposition-api] -> resolves to ~8 repos, not 20+", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a036::c1", "atom_id": "a036", "type": "prose", "line": "171-174", "claim": "aria-reference includes all GitlabAriaPHP/aria-* PHP repositories.", "oracle": [], "check": "repos.json presets.aria-reference", "verdict": "incorrect", "evidence": "Only aria-php/data-deposition-api is listed; the other 18 aria-php repos are NOT in the preset", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a036::c2", "atom_id": "a036", "type": "prose", "line": "171-174", "claim": "aria-reference includes FragmentScreen fandanGO plugins and peer facility plugins (CNB, CERM, GUF).", "oracle": [], "check": "FragmentScreen org repos", "verdict": "true", "evidence": "aria-reference includes FragmentScreen/* -> fandanGO-core/aria/cryoem-cnb/nmr-cerm/nmr-guf all present", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a037::c1", "atom_id": "a037", "type": "prose", "line": "178-180", "claim": "full preset contains 30+ repositories.", "oracle": [], "check": "count all repos in config", "verdict": "true", "evidence": "full preset repos=['*']; organizations total 8+6+19 = 33 repos", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a039::c1", "atom_id": "a039", "type": "prose", "line": "188-191", "claim": "full preset includes all DiamondLightSource, FragmentScreen, and GitlabAriaPHP repositories.", "oracle": [], "check": "presets.full", "verdict": "true", "evidence": "presets.full.repos=['*'] resolves to every org's repos", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a042::c1", "atom_id": "a042", "type": "code", "line": "199-205", "claim": "--path specifies the target workspace directory.", "oracle": [], "check": "cli.py init --path", "verdict": "true", "evidence": "init: path: typer.Option('--path','-p', help='Target directory for workspace')", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a043::c1", "atom_id": "a043", "type": "prose", "line": "207", "claim": "--path defaults to the current directory.", "oracle": [], "check": "cli.py init body", "verdict": "true", "evidence": "init: workspace_path = path or Path.cwd()", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a044::c1", "atom_id": "a044", "type": "prose", "line": "209-212", "claim": "init fails if the target directory exists and is not empty (safety measure).", "oracle": [], "check": "bootstrap.py mkdir + grep for emptiness check", "verdict": "incorrect", "evidence": "bootstrap_workspace: workspace_path.mkdir(parents=True, exist_ok=True); no emptiness/'not empty' check anywhere in package", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a044::c2", "atom_id": "a044", "type": "prose", "line": "209-212", "claim": "init creates repos/, claude-config/, tmp/, testdata/ subdirectories.", "oracle": [], "check": "setup/workspace.py + claude.py", "verdict": "misleading", "evidence": "workspace.py creates repos/,tmp/,testdata/,testdata/dls-filesystem/; claude-config is a symlink only created under --with-claude", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a046::c1", "atom_id": "a046", "type": "code", "line": "218-230", "claim": "--preset accepts smartem-core, full, minimal, aria-reference.", "oracle": [], "check": "repos.json presets keys", "verdict": "true", "evidence": "presets keys = smartem-core, full, aria-reference, minimal (cli help lists same)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a047::c1", "atom_id": "a047", "type": "prose", "line": "232", "claim": "--preset default is None (interactive selection).", "oracle": [], "check": "cli.py init preset default", "verdict": "true", "evidence": "init: preset: str|None = None; when None and interactive -> select_preset", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a048::c1", "atom_id": "a048", "type": "prose", "line": "234-237", "claim": "With --preset the tool still prompts for confirmation unless --no-interactive is used.", "oracle": [], "check": "bootstrap.py flow", "verdict": "true", "evidence": "bootstrap: resolve_preset -> display_selection_summary -> if interactive and not confirm('Proceed with setup?'): cancel", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a051::c1", "atom_id": "a051", "type": "prose", "line": "248-251", "claim": "--no-interactive requires --preset (no default).", "oracle": [], "check": "bootstrap.py else branch", "verdict": "true", "evidence": "bootstrap: else (no preset, not interactive): 'No preset specified and interactive mode disabled' return False", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a053::c1", "atom_id": "a053", "type": "prose", "line": "261", "claim": "A --ssh flag clones repositories using SSH instead of HTTPS.", "oracle": [], "check": "cli.py init options", "verdict": "incorrect", "evidence": "No --ssh option; init defines --git-ssh and --git-https (and sync too). '--ssh' does not exist", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a055::c1", "atom_id": "a055", "type": "prose", "line": "267", "claim": "The default git transport is HTTPS.", "oracle": [], "check": "repos.py get_repo_url + cli default", "verdict": "misleading", "evidence": "use_ssh defaults None = auto-detect: GitHub repos use SSH when SSH auth works, else HTTPS. Not a fixed HTTPS default", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a057::c1", "atom_id": "a057", "type": "prose", "line": "275-278", "claim": "SSH cloning requires network access to github.com on port 22.", "oracle": ["port"], "check": "general SSH/git knowledge + git@github URLs", "verdict": "true", "evidence": "repo.urls.ssh = git@github.com:...; SSH uses TCP 22", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a058::c1", "atom_id": "a058", "type": "prose", "line": "280-282", "claim": "HTTPS URL is https://github.com/DiamondLightSource/smartem-decisions.git and SSH is git@github.com:DiamondLightSource/smartem-decisions.git.", "oracle": ["url", "cli-cmd"], "check": "repos.json urls", "verdict": "true", "evidence": "repos.json smartem-decisions urls exactly match both forms", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a059::c1", "atom_id": "a059", "type": "prose", "line": "286", "claim": "A --skip-claude flag disables Claude Code configuration.", "oracle": [], "check": "cli.py init options", "verdict": "incorrect", "evidence": "No --skip-claude option; Claude is opt-in via --with-claude (skip_claude = not with_claude), skipped by default", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a062::c1", "atom_id": "a062", "type": "prose", "line": "297-301", "claim": "--skip-claude skips .claude/ creation, skills symlinking, settings/permissions config, and CLAUDE.md creation.", "oracle": ["source-path"], "check": "cli.py + setup/claude.py", "verdict": "misleading", "evidence": "Describes the default (Claude off) behaviour under a non-existent flag; also file created is settings.local.json and CLAUDE.md is a symlink", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a063::c1", "atom_id": "a063", "type": "prose", "line": "305", "claim": "A --skip-serena flag skips Serena MCP server configuration.", "oracle": [], "check": "cli.py init options", "verdict": "true", "evidence": "init: skip_serena: typer.Option('--skip-serena', help='Skip Serena MCP setup')", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a068::c1", "atom_id": "a068", "type": "code", "line": "326-333", "claim": "The interactive preset menu offers minimal, smartem-core, aria-reference, full, custom with sizes and repo counts.", "oracle": [], "check": "prompts.py select_preset + preset counts", "verdict": "misleading", "evidence": "select_preset renders a Rich table (Option/Preset/Description/Repos); presets match but no MB sizes shown and aria-reference '20+' is wrong (~8)", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a070::c1", "atom_id": "a070", "type": "code", "line": "340-355", "claim": "Custom selection lists DLS repos (incl cryoem-services) and FragmentScreen fandanGO-core/aria/cryoem-dls.", "oracle": [], "check": "repos.json org repos", "verdict": "true", "evidence": "DLS org includes cryoem-services; FragmentScreen includes fandanGO-core, fandanGO-aria; fandanGO-cryoem-dls is under DiamondLightSource in config", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a071::c1", "atom_id": "a071", "type": "code", "line": "359-371", "claim": "A target-directory confirmation lists dirs to create and asks 'Continue? (Y/n)'.", "oracle": ["source-path"], "check": "bootstrap.py confirm text", "verdict": "misleading", "evidence": "Actual prompt is confirm('Proceed with setup?'); no 'This will create' listing; claude-config only under --with-claude", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a073::c1", "atom_id": "a073", "type": "code", "line": "387-397", "claim": "Configuration output reports 'Linked 7 skills' and 'Indexed repositories'.", "oracle": ["source-path"], "check": "claude.py + mcp.py + config skills", "verdict": "misleading", "evidence": "config has 5 skills not 7; setup_mcp_config only writes .serena/project.yml + .mcp.json, there is no repository indexing step", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a077::c1", "atom_id": "a077", "type": "code", "line": "423-454", "claim": "Workspace tree includes .claude/settings.json, .claude/permissions.json, tmp/logs, tmp/simulations, testdata/epu-output, README.md.", "oracle": ["source-path", "cli-cmd"], "check": "setup/claude.py + workspace.py + mcp.py", "verdict": "misleading", "evidence": "Actual: .claude/settings.local.json (no settings.json/permissions.json), testdata/dls-filesystem (no epu-output), no tmp/logs|simulations, no README.md created; org dirs/GitlabAriaPHP correct", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a079::c1", "atom_id": "a079", "type": "prose", "line": "461-467", "claim": "7 skills are linked: database-admin, devops, technical-writer, git, github, ascii-art, playwright-skill.", "oracle": ["cli-cmd", "k8s"], "check": "claude-code-config.json + claude-code/shared/skills", "verdict": "incorrect", "evidence": "config lists 5 skills: database, devops, tech-writer, git, playwright; skills dir has database/devops/git/tech-writer(+playwright). No github/ascii-art/database-admin", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a080::c1", "atom_id": "a080", "type": "prose", "line": "470-473", "claim": "The tool configures Claude Code settings: type checking, 120-char lines, British English, no emojis.", "oracle": [], "check": "setup/claude.py settings.local.json contents", "verdict": "misleading", "evidence": "settings.local.json only contains {permissions, enabledMcpjsonServers:['serena']}; none of these style settings are written by the tool", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a081::c1", "atom_id": "a081", "type": "prose", "line": "476-478", "claim": "Configured permissions grant read access to all repo files, write to appropriate dirs, execute for dev scripts.", "oracle": [], "check": "claude-code-config.json defaultPermissions", "verdict": "misleading", "evidence": "defaultPermissions.allow = Bash(git:*),Bash(ls:*),Bash(cat:*),WebSearch,mcp__serena__*,mcp__chrome-devtools__* - not the read/write/execute scheme described", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a082::c1", "atom_id": "a082", "type": "prose", "line": "482-486", "claim": "Serena enables symbol search, find references/definitions, jump to implementation, documentation lookup.", "oracle": [], "check": "Serena MCP config + general capability", "verdict": "true", "evidence": "mcpConfig.serena starts serena MCP (find_symbol/find_referencing_symbols etc. are Serena features)", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a089::c1", "atom_id": "a089", "type": "code", "line": "528-531", "claim": "For the smartem-core preset, repos/DiamondLightSource/ contains smartem-devtools, smartem-decisions, smartem-frontend.", "oracle": [], "check": "repos.json presets.smartem-core", "verdict": "true", "evidence": "smartem-core preset resolves to exactly those three DLS repos", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a091::c1", "atom_id": "a091", "type": "prose", "line": "541", "claim": "All repositories are cloned on the main branch by default.", "oracle": [], "check": "repos.py clone_repo git command", "verdict": "true", "evidence": "clone_repo runs `git clone ` with no -b, so checks out each repo's default branch (main)", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a094::c1", "atom_id": "a094", "type": "prose", "line": "557", "claim": "Documentation lives in smartem-devtools/docs/how-to/.", "oracle": [], "check": "ls docs/ in smartem-devtools", "verdict": "incorrect", "evidence": "No docs/how-to directory; docs/ has agent, api, architecture, athena, backend, development, getting-started, operations, decision-records", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a096::c1", "atom_id": "a096", "type": "prose", "line": "564-568", "claim": "docs/how-to/ contains run-backend.md, run-e2e-dev-simulation.md, database-migrations.md, deploy-kubernetes.md.", "oracle": ["source-path", "k8s"], "check": "find docs for those filenames", "verdict": "incorrect", "evidence": "None of run-backend.md/run-e2e-dev-simulation.md/database-migrations.md/deploy-kubernetes.md exist anywhere in smartem-devtools/docs", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a098::c1", "atom_id": "a098", "type": "code", "line": "574-579", "claim": "Backend dev setup runs ./scripts/k8s/dev-k8s.sh up from within smartem-decisions.", "oracle": ["source-path", "cli-cmd", "k8s"], "check": "ls smartem-decisions/scripts vs smartem-devtools", "verdict": "misleading", "evidence": "smartem-decisions has no scripts/ dir; dev-k8s.sh lives at smartem-devtools/scripts/k8s/dev-k8s.sh (moved). uv sync/.venv steps are fine", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a100::c1", "atom_id": "a100", "type": "code", "line": "583-587", "claim": "Frontend runs with npm install then npm run dev.", "oracle": ["cli-cmd"], "check": "smartem-frontend package.json scripts", "verdict": "true", "evidence": "package.json scripts include 'dev'", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a102::c1", "atom_id": "a102", "type": "code", "line": "593-596", "claim": "ls .claude/skills/ should show symlinks to all 7 skills.", "oracle": [], "check": "claude-code-config.json skills count", "verdict": "misleading", "evidence": "Only 5 skills are configured/linked, not 7", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a105::c1", "atom_id": "a105", "type": "code", "line": "604-612", "claim": "Backend tests run with `uv run pytest` in smartem-decisions.", "oracle": ["cli-cmd"], "check": "smartem-decisions pyproject", "verdict": "true", "evidence": "smartem-decisions has [project.optional-dependencies]; pytest is the standard test runner (uv run pytest valid)", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a105::c2", "atom_id": "a105", "type": "code", "line": "604-612", "claim": "Frontend tests run with `npm test` in smartem-frontend.", "oracle": ["cli-cmd"], "check": "smartem-frontend package.json scripts", "verdict": "incorrect", "evidence": "package.json has no 'test' script (scripts: dev, dev:mock, build, typecheck, lint, format, check...); npm test would fail", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a108::c1", "atom_id": "a108", "type": "code", "line": "620-622", "claim": "`smartem-workspace add ` adds a single repository to an existing workspace.", "oracle": [], "check": "cli.py add command", "verdict": "misleading", "evidence": "add command exists but is a stub: prints '[yellow]Not implemented yet: {repo}' and raises typer.Exit(1)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a114::c1", "atom_id": "a114", "type": "code", "line": "640-642", "claim": "`smartem-workspace sync` pulls latest changes for repositories, preserving uncommitted changes.", "oracle": ["cli-cmd"], "check": "cli.py sync + repos.py pull_repo", "verdict": "true", "evidence": "sync command clones missing + pulls existing via `git -C pull --ff-only` (ff-only preserves local uncommitted work)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a117::c1", "atom_id": "a117", "type": "code", "line": "650-652", "claim": "`smartem-workspace status` shows git status for all repositories.", "oracle": [], "check": "cli.py status", "verdict": "true", "evidence": "cli.py @app.command() def status(...) 'Show workspace status (alias for check --scope all)'", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a118::c1", "atom_id": "a118", "type": "prose", "line": "654-657", "claim": "status shows current branch, uncommitted changes, and commits ahead/behind remote.", "oracle": [], "check": "repos.py get_repo_status", "verdict": "misleading", "evidence": "get_repo_status returns {branch, has_changes, path} only; no ahead/behind computation exists", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a119::c1", "atom_id": "a119", "type": "prose", "line": "661", "claim": "Custom presets are defined in the package configuration.", "oracle": [], "check": "config/repos.json", "verdict": "true", "evidence": "presets defined in smartem_workspace/config/repos.json (bundled) and core/repos.json (network source)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a120::c1", "atom_id": "a120", "type": "prose", "line": "663", "claim": "New preset requests go to https://github.com/DiamondLightSource/smartem-devtools/issues.", "oracle": ["url"], "check": "pyproject urls", "verdict": "true", "evidence": "pyproject [project.urls] Issues = https://github.com/DiamondLightSource/smartem-devtools/issues", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a124::c1", "atom_id": "a124", "type": "code", "line": "673-675", "claim": "The workspace can be mounted into a container via docker run -v.", "oracle": ["cli-cmd"], "check": "generic docker usage", "verdict": "true", "evidence": "Standard docker -v volume mount; not tool-specific", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a126::c1", "atom_id": "a126", "type": "code", "line": "681-689", "claim": "A VS Code multi-root workspace can reference the three DLS repo paths.", "oracle": [], "check": "repos.json org dir + repo names", "verdict": "true", "evidence": "repos/DiamondLightSource/{smartem-decisions,smartem-frontend,smartem-devtools} are the actual clone paths", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a128::c1", "atom_id": "a128", "type": "code", "line": "695-698", "claim": "CI usage: uvx smartem-workspace init --preset smartem-core --no-interactive --path /workspace.", "oracle": [], "check": "cli.py init options", "verdict": "true", "evidence": "init supports --preset, --no-interactive, --path together", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a132::c1", "atom_id": "a132", "type": "code", "line": "707-716", "claim": "init errors with 'Target directory ... is not empty' when the directory is not empty.", "oracle": [], "check": "grep emptiness check across package", "verdict": "incorrect", "evidence": "No 'not empty' string or emptiness guard exists; bootstrap uses mkdir(exist_ok=True) and proceeds", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a136::c1", "atom_id": "a136", "type": "code", "line": "721-730", "claim": "init errors 'Repository ... already exists' when a repo directory is present.", "oracle": [], "check": "repos.py clone_repo", "verdict": "incorrect", "evidence": "clone_repo: if repo_path.exists(): prints 'Skipping {repo} (already exists)' and returns True - it does not error", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a147::c1", "atom_id": "a147", "type": "code", "line": "759-762", "claim": "On network failure the tool falls back to bundled configuration.", "oracle": [], "check": "config/loader.py", "verdict": "true", "evidence": "load_config: on network None -> _load_json_from_bundled('repos.json') ('Using bundled fallback config')", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a151::c1", "atom_id": "a151", "type": "code", "line": "772-774", "claim": "Setting GITHUB_TOKEN increases the rate limit for fetching configuration.", "oracle": ["env-var"], "check": "grep GITHUB_TOKEN in package", "verdict": "misleading", "evidence": "No GITHUB_TOKEN/Authorization usage in code; config fetched from raw.githubusercontent.com (not the rate-limited API), so the token has no effect on config fetch", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a154::c1", "atom_id": "a154", "type": "code", "line": "780-782", "claim": "Skill symlink creation can fail on Windows without developer mode.", "oracle": [], "check": "setup/claude.py os.symlink", "verdict": "true", "evidence": "claude.py uses os.symlink for skills; Windows symlink creation requires dev mode/admin", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a174::c1", "atom_id": "a174", "type": "code", "line": "859-862", "claim": "Broken skills can be fixed with `ln -sf ../../claude-config/shared/skills/database-admin database-admin`.", "oracle": [], "check": "claude-code/shared/skills contents", "verdict": "misleading", "evidence": "Skill is named 'database' not 'database-admin'; source path is claude-code/shared/skills/database (claude-config symlinks to claude-code)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a180::c1", "atom_id": "a180", "type": "prose", "line": "877-879", "claim": "How-to guides are at repos/DiamondLightSource/smartem-devtools/docs/how-to/.", "oracle": ["url"], "check": "ls docs/ in smartem-devtools", "verdict": "misleading", "evidence": "docs/how-to/ does not exist; guides are under docs/operations, docs/development etc. Site URL base is plausible", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a181::c1", "atom_id": "a181", "type": "prose", "line": "883-884", "claim": "GitHub Issues and Discussions live under github.com/DiamondLightSource/smartem-devtools.", "oracle": ["url"], "check": "pyproject urls", "verdict": "true", "evidence": "pyproject Repository/Issues point to DiamondLightSource/smartem-devtools", "confidence": "medium", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a182::c1", "atom_id": "a182", "type": "prose", "line": "888-889", "claim": "Contact email is smartem@diamond.ac.uk.", "oracle": ["url"], "check": "pyproject authors", "verdict": "true", "evidence": "pyproject authors email = smartem@diamond.ac.uk", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a184::c1", "atom_id": "a184", "type": "prose", "line": "895-900", "claim": "Run the backend with ./scripts/k8s/dev-k8s.sh up.", "oracle": ["source-path", "cli-cmd", "k8s"], "check": "locate dev-k8s.sh", "verdict": "misleading", "evidence": "dev-k8s.sh is at smartem-devtools/scripts/k8s/dev-k8s.sh, not in the backend (smartem-decisions has no scripts dir)", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a184::c2", "atom_id": "a184", "type": "prose", "line": "895-900", "claim": "Run the frontend with npm run dev.", "oracle": ["source-path", "cli-cmd", "k8s"], "check": "smartem-frontend package.json", "verdict": "true", "evidence": "package.json scripts include 'dev'", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a186::c1", "atom_id": "a186", "type": "prose", "line": "904-907", "claim": "Read .github/CONTRIBUTING.md in smartem-devtools before contributing.", "oracle": ["source-path"], "check": "find CONTRIBUTING.md in smartem-devtools", "verdict": "incorrect", "evidence": "No .github/CONTRIBUTING.md (or any CONTRIBUTING.md) in smartem-devtools; contributing guide is docs/development/contributing.md", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} +{"claim_id": "docs/operations/setup-smartem-workspace.md::a186::c2", "atom_id": "a186", "type": "prose", "line": "904-907", "claim": "Code standards are in claude-config/smartem-decisions/REPO-GUIDELINES.md.", "oracle": ["source-path"], "check": "find REPO-GUIDELINES.md", "verdict": "true", "evidence": "claude-code/smartem-decisions/REPO-GUIDELINES.md exists; claude-config symlinks to claude-code so the path resolves", "confidence": "high", "file": "docs/operations/setup-smartem-workspace.md", "doc_kind": "howto"} diff --git a/docs/frontend/architecture.md b/docs/frontend/architecture.md new file mode 100644 index 0000000..03011d1 --- /dev/null +++ b/docs/frontend/architecture.md @@ -0,0 +1,57 @@ +# Frontend Architecture + +The SmartEM frontend is a single-page application (SPA) that presents acquisition sessions, spatial navigation of grids, and machine-learning quality predictions. It has no server of its own: it is static assets served by nginx that talk to the SmartEM backend HTTP API over `/api`. This page describes how the application is put together; see [Routes and Views](routes.md) for the view-by-view reference and [Development](development.md) for how to run and build it. + +## Monorepo layout + +The frontend lives in the `smartem-frontend` repository, an npm-workspaces monorepo (`"workspaces": ["apps/*", "packages/*"]`, Node >= 22). There is one application and two internal packages: + +| Workspace | Package name | Role | +|---|---|---| +| `apps/smartem` | `@smartem/app` | The SPA - all routes, views, and shell | +| `packages/api` | `@smartem/api` | The generated backend API client (typed hooks, models, MSW handlers) | +| `packages/ui` | `@smartem/ui` | Shared component package - currently an empty stub reserved for future extraction | + +The earlier `apps/legacy` application has been removed; `@smartem/app` is the sole shipped frontend. The monorepo structure is recorded in [ADR 0017](../decision-records/decisions/0017-smartem-frontend-monorepo-restructure.md). + +## Technology stack + +| Concern | Choice | +|---|---| +| UI library | React 19 | +| Routing | TanStack Router (file-based, generated route tree) | +| Server state | TanStack Query (React Query v5) | +| Components and styling | MUI v7 (Material UI) with Emotion; self-hosted Inter font | +| Authentication | `keycloak-js` (Keycloak OIDC, PKCE) | +| API client codegen | Orval (from the backend OpenAPI spec) | +| Mocking | MSW (Mock Service Worker), handlers generated by Orval | +| Build tool | Vite | +| Language and linting | TypeScript, Biome, Lefthook pre-commit hooks | + +Charts (weight-evolution timelines, quality histograms, latent-space scatters, the dashboard timeline) are **bespoke SVG components** under `src/components/` - no charting library is used. The choice of a charting library remains an open decision; any candidate must be weighed against davidia, the DLS visualisation library. + +## Routing + +Routing is **file-based**: each file under `apps/smartem/src/routes/` maps to a URL. The filename encodes the path - dots become path separators, `$param` is a path parameter, a trailing underscore on a segment (`squares_`) is a pathless layout used to nest a detail view outside its collection layout, and `.index` is the index route for a segment. TanStack Router's CLI compiles these into `src/routeTree.gen.ts`, which is committed to version control ([ADR 0009](../decision-records/decisions/0009-commit-generated-route-tree.md)) and regenerated with `npm run router:generate`. + +The URL hierarchy is anchored on the acquisition workflow: acquisition -> grid -> grid square -> foil hole. The full map is in [Routes and Views](routes.md). + +## Data layer + +All backend data is read through `@smartem/api`, a client generated by **Orval** from the backend's OpenAPI specification. For every backend operation Orval emits a typed React Query hook (for example `useGetGridGridsGridUuidGet` for `GET /grids/{grid_uuid}`), the request/response models, and a matching MSW handler. Views call these hooks; the shared `QueryClient` (configured in `__root.tsx` with a 5-minute `staleTime` and a single retry) caches and de-duplicates requests, so, for example, a grid-square list fetched for a table is reused when a row is expanded. + +Requests go through a single Axios instance (`packages/api/src/mutator.ts`) with base URL `/api`. A request interceptor attaches `Authorization: Bearer `; the token is supplied by the auth layer via `setAuthToken`. Regenerating the client is described in [Development](development.md#regenerating-the-api-client); the spec pipeline and version-compatibility policy are covered by [ADR 0020](../decision-records/decisions/0020-openapi-spec-pipeline-and-version-compatibility.md). + +## Application shell and bootstrap + +`src/main.tsx` is the entry point. In live mode it fetches the runtime configuration (`/config.json`) before the first render and reports the backend API version (an observe-only compatibility check, per ADR 0020); in mock mode it starts the MSW worker instead. It mounts the authentication gate (`AuthGate`) and the router. + +`src/routes/__root.tsx` provides the chrome shared by every page: the MUI `ThemeProvider` and `CssBaseline`, the `QueryClientProvider`, the sticky `Header`, and the routed ``. Independent `ErrorBoundary` wrappers around the header, the routed outlet, and other shell regions ensure a failure in one area cannot blank the whole application. TanStack Router and Query devtools are rendered in development only. The header, navigation, command palette, settings, and feature flags are documented in [Shell and Navigation](shell.md). + +## Theming + +The theme is a single light-mode MUI theme defined in `apps/smartem/src/theme.ts` (Inter typography, a neutral grey ramp, and shared status colours), applied once in `__root.tsx`. A `darkMode` feature flag and a corresponding settings entry exist, but a dark theme has not yet been built, so the application is light-mode only as shipped. + +## Runtime configuration + +Keycloak settings and feature flags are **runtime** configuration, not build-time. The SPA fetches `apps/smartem/public/config.json` at boot; in a Kubernetes deployment this file is supplied by a ConfigMap, so the same container image can point at different Keycloak realms and enable different features per environment. The JSON-config-with-typed-defaults approach is recorded in [ADR 0014](../decision-records/decisions/0014-json-config-with-typescript-types.md); the resolved flags are listed in [Shell and Navigation](shell.md#feature-flags). diff --git a/docs/frontend/development.md b/docs/frontend/development.md new file mode 100644 index 0000000..8a57d62 --- /dev/null +++ b/docs/frontend/development.md @@ -0,0 +1,79 @@ +# Frontend Development + +How to run, test, regenerate, and build the SmartEM frontend. For how the application is structured, see [Architecture](architecture.md). + +## Prerequisites + +- **Node.js >= 22** and npm. +- For live mode: a reachable SmartEM backend and a Keycloak realm (a local one is fine - see [Local Keycloak for SmartEM frontend dev](../development/local-keycloak.md)). + +Install dependencies once from the repository root: + +```bash +npm install +``` + +This installs all workspaces and sets up the Lefthook git hooks. + +## Running locally + +Two run modes are available as root scripts: + +| Command | Mode | What it does | +|---|---|---| +| `npm run dev` | Live | Vite dev server against a real backend, proxying `/api`; requires Keycloak and `config.json` | +| `npm run dev:mock` | Mock | Fully offline - MSW serves the API and an auto-signed-in mock user bypasses Keycloak | + +`npm run dev:mock` is the quickest way to see the UI with no backend running. Live mode proxies API calls to the backend given by `VITE_API_PROXY_TARGET` (default `http://localhost:8000`); point it at a running backend - for example a dev cluster's API NodePort - and sign in through Keycloak. + +## Environment variables + +The frontend reads only a small set of Vite variables. Keycloak settings and feature flags are **not** environment variables - they come from the runtime [`config.json`](architecture.md#runtime-configuration). + +| Variable | Read by | Purpose | Default | +|---|---|---|---| +| `VITE_ENABLE_MOCKS` | `main.tsx` | `true` starts MSW, skips `config.json`, and uses the mock auth provider (set by `dev:mock`) | unset (live) | +| `VITE_DEVTOOLS` | dev build | Set to `false` to hide the floating TanStack devtools in development | on | +| `VITE_API_PROXY_TARGET` | `vite.config.ts` (dev server) | Backend target for the `/api` proxy | `http://localhost:8000` | + +An `apps/smartem/.env.example` documents these; copy it to `.env.local` (git-ignored) to override locally. + +## Mock mode + +Mock mode makes the SPA run without any backend. When `VITE_ENABLE_MOCKS=true`, `main.tsx` starts an MSW service worker (`src/mocks/browser.ts`) whose handlers come from `getDefaultMock()` in `@smartem/api` - the handler set that Orval **generates from the OpenAPI spec** alongside the client. Because the handlers are generated, mock coverage tracks the real API automatically; there are no hand-maintained per-endpoint handlers to keep in step. Only two view components are mock-specific (the home `MockDashboard` and the `MockContextStrip`); every other view runs its normal `@smartem/api` hooks against the mocked responses. + +## Regenerating the API client + +`@smartem/api` is generated by Orval from the backend OpenAPI specification. Regenerate it whenever the backend API changes: + +```bash +npm run api:update # fetch the canonical spec, then regenerate the client +``` + +`api:update` is `api:fetch` followed by `api:generate`: + +- `api:fetch` downloads the canonical spec (`docs/api/openapi.json` from `smartem-decisions` `main`) to `packages/api/src/openapi.json`. Use `api:fetch:local` to pull from a locally running backend (`http://localhost:8000/openapi.json`) instead. +- `api:generate` runs Orval (typed hooks, models, and MSW handlers) and stamps the spec's version into `packages/api/src/generated/api-version.ts`. + +At runtime the SPA reports its client's API version against the backend's `/version` as an observe-only compatibility check (a non-blocking banner in development only). The spec pipeline and the semantic-version policy are defined in [ADR 0020](../decision-records/decisions/0020-openapi-spec-pipeline-and-version-compatibility.md). + +## Code quality + +- **Linting and formatting:** Biome - `npm run check` (lint + format check) and `npm run check:fix` to apply fixes; `npm run format` also formats Markdown and YAML with Prettier. +- **Type checking:** `npm run typecheck` (runs `tsc` across the workspaces). +- **Git hooks:** Lefthook runs the checks on commit. +- There is currently **no test runner** configured for the frontend. + +## Building + +```bash +npm run build # -> apps/smartem/dist +``` + +`npm run analyze` builds with a bundle-size visualiser. The build regenerates the route tree and produces static assets only - there is no server-side rendering. + +## Containerisation and release + +The container is a two-stage build (`apps/smartem/Dockerfile`): a Node stage installs dependencies, regenerates the API client, stamps the version, and runs `vite build`; an `nginx` stage serves the static assets. The nginx configuration provides SPA history fallback, serves the runtime `/config.json` and a `/version` endpoint, and reverse-proxies `/api/` to the backend service (configurable via `BACKEND_HOST`). + +Releases are cut by the `release-smartem-frontend` GitHub Actions workflow and published to GHCR (`ghcr.io/diamondlightsource/smartem-frontend`). A push to `main` produces a release-candidate image (`rc`); a `smartem-frontend-v*` tag or a stable `workflow_dispatch` produces a stable release. The frontend is versioned independently of the backend; `src/version.ts` must match `apps/smartem/package.json`, and the workflow fails on a mismatch. The pipeline is recorded in [ADR 0019](../decision-records/decisions/0019-smartem-frontend-release-pipeline.md). diff --git a/docs/frontend/index.md b/docs/frontend/index.md new file mode 100644 index 0000000..ce78fc0 --- /dev/null +++ b/docs/frontend/index.md @@ -0,0 +1,30 @@ +# Frontend + +Documentation for the SmartEM frontend - the web application that presents acquisition sessions, spatial navigation of grids, and machine-learning quality predictions to users. It is a single-page application (SPA) that talks to the SmartEM backend HTTP API. + +```{toctree} +:maxdepth: 1 + +architecture +routes +shell +development +``` + +## Topics + +### Understanding the app + +- [Architecture](architecture.md) - Monorepo layout, technology stack, file-based routing, the generated API client, and theming +- [Routes and Views](routes.md) - The URL structure and a reference for every view: what it shows and which backend data it reads + +### Using and building the app + +- [Shell and Navigation](shell.md) - Header navigation, the command palette, the settings menu, and feature flags +- [Development](development.md) - Running locally, mock mode, environment variables, regenerating the API client, and how the SPA is built and released + +## See also + +- [Keycloak Authentication for SmartEM SPA](../architecture/keycloak-spa-authentication.md) - How the SPA authenticates against Keycloak +- [Local Keycloak for SmartEM frontend dev](../development/local-keycloak.md) - Running a local Keycloak for frontend development +- [SmartEM Frontend: Design Specification](../decision-records/smartem-frontend-design.md) and [UX and Functional Requirements](../decision-records/smartem-frontend-requirements.md) - Original design intent (planning records; the pages above document the app as shipped) diff --git a/docs/frontend/routes.md b/docs/frontend/routes.md new file mode 100644 index 0000000..d75fca3 --- /dev/null +++ b/docs/frontend/routes.md @@ -0,0 +1,145 @@ +# Routes and Views + +This is the reference for every view in the SmartEM frontend: its URL, what it shows, and which backend endpoints it reads. The application uses **acquisition** as the primary user-facing term (the URL space is `/acquisitions/...`); the earlier design records used a `/sessions/...` scheme that was never shipped. + +Routing is file-based (see [Architecture](architecture.md#routing)). Every view reads its data through the generated `@smartem/api` React Query hooks; the backend endpoint each hook wraps is given alongside it. + +## URL map + +``` +/ Home dashboard +/acquisitions Acquisitions list +/acquisitions/$acquisitionId Acquisition overview +/acquisitions/$acquisitionId/grids/$gridId (redirects to /atlas) + .../atlas Atlas spatial view (default) + .../squares Grid squares - Table | Gallery + .../squares/$squareId Square detail - Map | Predictions + .../squares/$squareId/holes/$holeId Foil-hole detail + .../predictions Grid-level predictions + .../workspace Configurable panel workspace +/models Models catalogue +/models/$modelName Model detail +``` + +Within a grid, a **view switcher** offers Atlas / Squares / Predictions / Workspace; the grid squares collection has a **Table | Gallery** sub-switch; and square detail has a **Map | Predictions** sub-switch. The bare grid URL redirects to Atlas. + +## Views at a glance + +| View | URL | Primary backend data | +|---|---|---| +| Home dashboard | `/` | `GET /acquisitions`, `GET /acquisitions/grid-counts` | +| Acquisitions list | `/acquisitions` | `GET /acquisitions` | +| Acquisition overview | `/acquisitions/$acquisitionId` | `GET /acquisitions/{uuid}/grids` | +| Atlas | `.../grids/$gridId/atlas` | grid, gridsquares, atlas image, per-model prediction + latent, suggested squares | +| Grid squares - Table | `.../squares` | gridsquares + per-square foilholes | +| Grid squares - Gallery | `.../squares/gallery` | gridsquares + per-square atlas crops | +| Square detail - Map | `.../squares/$squareId` | gridsquare, foilholes, square image, per-model prediction + latent, suggested holes | +| Square detail - Predictions | `.../squares/$squareId/predictions` | square + foilhole quality time series | +| Foil-hole detail | `.../holes/$holeId` | foilhole, foilhole micrographs | +| Grid predictions | `.../grids/$gridId/predictions` | prediction models + per-model grid prediction | +| Workspace | `.../grids/$gridId/workspace` | grid + gridsquares | +| Models catalogue | `/models` | prediction models, grids, per-grid model weights | +| Model detail | `/models/$modelName` | prediction model, grids, per-grid model weights | + +## Home dashboard - `/` + +The landing page. A three-panel resizable layout: an **Instruments** panel (currently a placeholder, awaiting an instruments endpoint - smartem-devtools#81), an **Acquisitions** panel splitting active from recent sessions with expandable rows and grid counts, and a **Timeline** (a bespoke SVG Gantt grouped by instrument, with Today/Week/Month ranges). Each acquisition links to its overview. + +- **Data:** `GET /acquisitions` (`useGetAcquisitionsAcquisitionsGet`), `GET /acquisitions/grid-counts` (`useGetAcquisitionGridCountsAcquisitionsGridCountsGet`). +- In [mock mode](development.md#mock-mode) a `MockDashboard` is rendered instead of the live dashboard; it is the only mock-only page component besides the context strip. + +## Acquisitions + +### Acquisitions list - `/acquisitions` + +A full-height table of every acquisition (name, status, instrument, start time, duration), newest first; a row opens the acquisition overview. + +- **Data:** `GET /acquisitions`. + +### Acquisition overview - `/acquisitions/$acquisitionId` + +A statistics header (grids completed/total, instrument, duration) above a responsive set of grid cards; each card opens that grid. + +- **Data:** `GET /acquisitions/{uuid}/grids` (`useGetAcquisitionGridsAcquisitionsAcquisitionUuidGridsGet`) for the grids; the acquisition's own metadata is taken from the acquisitions list. +- This route also mounts the [context strip](shell.md#context-strip) for the whole acquisition subtree. + +## Grid views + +The bare grid URL (`/acquisitions/$acquisitionId/grids/$gridId`) immediately **redirects to the atlas view**. All four grid views share the view switcher. + +### Atlas - `.../grids/$gridId/atlas` + +The default grid view and the spatial centrepiece. A split pane: on the left, the **atlas map** - the grid's atlas image with grid-square overlays, per-model prediction overlays, and highlighting of suggested squares; on the right, a panel showing either a latent-space scatter, a preview of the committed square, or an empty state. Hovering a square highlights it; clicking commits it (the heavier square imagery loads only on commit, per #68). + +- **Data:** grid (`GET /grids/{uuid}`), grid squares (`GET /grids/{uuid}/gridsquares`), prediction models (`GET /prediction_models`), the atlas image blob (`GET /grids/{uuid}/atlas_image`), and, fanned out per model, the grid prediction (`GET /prediction_model/{name}/grid/{uuid}/prediction`) and latent representation (`GET /prediction_model/{name}/grid/{uuid}/latent_representation`), plus suggested squares (`GET /grid/{uuid}/prediction_model/{name}/latent_rep/{latent_rep_model_name}/suggested_squares`). +- **Model selection:** the backend does not distinguish model roles, so the view defaults the first model as the prediction model and the second (or first) as the latent-representation model; the latent model is user-selectable. This interim convention is tracked in smartem-frontend#111. + +### Grid predictions - `.../grids/$gridId/predictions` + +A grid-level, per-model prediction dashboard. + +- **Data:** `GET /prediction_models`, then per model `GET /prediction_model/{name}/grid/{uuid}/prediction`. + +### Workspace - `.../grids/$gridId/workspace` + +A configurable multi-panel workspace over the grid's squares, where panels can be collapsed, removed, and added; selecting a square opens its detail. + +- **Data:** grid (`GET /grids/{uuid}`) and grid squares (`GET /grids/{uuid}/gridsquares`). + +## Grid squares (collection) + +The squares collection has a **Table | Gallery** sub-switch. + +### Table - `.../grids/$gridId/squares` + +A sortable, expandable table of grid squares: ID, status, whether selected, defocus, magnification, **hole count**, and **score** (the mean foil-hole quality), plus a button to that square's predictions. Expanding a row reveals a sub-table of the square's foil holes with their suggested acquisition **order** and **quality**. + +- **Data:** grid squares (`GET /grids/{uuid}/gridsquares`); per square, foil holes (`GET /gridsquares/{uuid}/foilholes`) for the hole count and score; in the expanded sub-table, the square's overall prediction (`GET /gridsquare/{uuid}/overall_prediction`) for acquisition order. The React Query cache is shared between the column data and the expanded sub-table, so expanding a row does not refetch. + +### Gallery - `.../grids/$gridId/squares/gallery` + +A paginated thumbnail grid; each tile is an authenticated **crop of the grid atlas** for that square, over a checkered placeholder, with a "collected" indicator. Controls: a collected-only filter, column count (2-5), and page size (9/18/36). + +- **Data:** grid squares (`GET /grids/{uuid}/gridsquares`); per tile, a server-side crop via `GET /grids/{uuid}/atlas_image?x&y&w&h`. +- Column count and page size are persisted to `localStorage` (`smartem.gallery.columns`, `smartem.gallery.pageSize`). Page size is capped because each tile is a heavy crop. + +## Square detail + +Square detail is nested outside the collection layout (via a pathless route), and has a **Map | Predictions** sub-switch. + +### Map - `.../grids/$gridId/squares/$squareId` + +The default square view: the grid-square image with foil-hole overlays, prediction layers, suggested-hole highlighting, and the acquisition-order path. A foil hole opens its detail. An optional right-hand latent-space scatter (toggled by a floating button) cross-highlights the hovered foil hole. + +- **Data (via a single `useSquareMapData` hook):** gridsquare (`GET /gridsquares/{uuid}`), foil holes (`GET /gridsquares/{uuid}/foilholes`), prediction models (`GET /prediction_models`), overall prediction (`GET /gridsquare/{uuid}/overall_prediction`), the square image blob (`GET /gridsquares/{uuid}/gridsquare_image`), per-model square prediction (`GET /prediction_model/{name}/gridsquare/{uuid}/prediction`), and suggested holes (`GET /gridsquares/{uuid}/prediction_model/{name}/latent_rep/{latent_rep_model_name}/suggested_holes`). The route additionally fans out the per-model square latent representation (`GET /prediction_model/{name}/gridsquare/{uuid}/latent_representation`) for the scatter. Prediction layers include each model plus a synthetic "Overall" layer; the same model[0]/model[1] convention as the atlas applies (#111). + +### Predictions - `.../grids/$gridId/squares/$squareId/predictions` + +Per-metric quality analytics for the square: a metric selector, a bespoke SVG "quality over time" line chart, and a foil-hole quality **histogram with an as-of-time slider** - scrub the slider to rebuild the distribution as it stood at a past instant. + +- **Data:** the square's quality time series (`GET /gridsquares/{uuid}/quality_predictions`) and the per-foil-hole time series (`GET /gridsquares/{uuid}/foilhole_quality_predictions`). + +## Foil-hole detail - `.../squares/$squareId/holes/$holeId` + +The deepest view: a foil-hole metadata panel and a grid of micrograph cards showing per-micrograph metrics (motion, CTF fit resolution, particle counts). When no micrographs have been acquired it shows an empty state. + +- **Data:** foil hole (`GET /foilholes/{uuid}`) and its micrographs (`GET /foilholes/{uuid}/micrographs`). +- **Leaf image:** the micrograph cards show metadata only - no per-micrograph image is fetched. The `micrograph_image` serving endpoint is not yet in the generated client; its introduction is tracked by ADR 0021 (proposed) and smartem-decisions#308 / #312. + +## Models + +### Models catalogue - `/models` + +A cross-model **weight matrix** for a selectable grid (with a time slider), above a card grid of the registered models; each card shows the model's name, description, and train/infer/update capability chips, and opens the model detail. + +- **Data:** `GET /prediction_models`, `GET /grids` (the grid picker, defaulting to the first grid), and `GET /grid/{uuid}/model_weights` (every model's weights for that grid, keyed by name). + +### Model detail - `/models/$modelName` + +One model in focus: a header with capability chips and description, a weights timeline, and a single-model weight matrix, for a selectable grid. + +- **Data:** `GET /prediction_models/{name}`, `GET /grids`, and `GET /grid/{uuid}/model_weights` narrowed to this model. + +## Mock mode and views + +Only two view components are mock-only: the `MockDashboard` (home) and the `MockContextStrip`. Every other view uses the live `@smartem/api` hooks; in [mock mode](development.md#mock-mode) those requests are served by MSW rather than a real backend, so the same views run unchanged against mocked responses. diff --git a/docs/frontend/shell.md b/docs/frontend/shell.md new file mode 100644 index 0000000..3b8de67 --- /dev/null +++ b/docs/frontend/shell.md @@ -0,0 +1,68 @@ +# Shell and Navigation + +The shell is the chrome that surrounds every view: the header, the command palette, the settings menu, the authentication controls, and the spatial context strip. It is defined in `apps/smartem/src/routes/__root.tsx` and `apps/smartem/src/components/shell/`. There is deliberately **no footer and no breadcrumb trail** - the context strip provides spatial orientation instead. + +## Header + +The header is a sticky bar (`components/shell/Header.tsx`) containing, left to right: + +- **Logos** - Diamond/eBIC and FragmentScreen marks, each linking out to the respective site in a new tab. +- **Wordmark** - "SmartEM", linking to the home dashboard (`/`). +- **Navigation links** - the primary top-level destinations (see below). +- **Search box** - a read-only field ("Search or jump to...") that opens the command palette; it also shows the `/` keyboard hint. +- **Settings menu** and **authentication controls** - at the right. + +### Navigation links + +| Link | Destination | Notes | +|---|---|---| +| Acquisitions | `/acquisitions` | The acquisition (session) list | +| Models | `/models` | The ML models catalogue | +| Depositions | `/depositions` | Hidden - gated by the `depositions` feature flag (default off); the route does not exist while the flag is off | + +The active link is highlighted by matching the current path prefix. + +## Command palette + +The command palette (`components/widgets/CommandPalette/`) is a fuzzy "jump to" launcher. + +- **Opening it** - press / anywhere (except while typing in an input, textarea, select, or editable field), or click the header search box. Esc closes it. +- **What it searches** - four groups: + - **Navigation** - the visible header links (with extra search keywords, so "sessions" finds Acquisitions). + - **Acquisitions**, **Grids**, and **Models** - live backend data. These queries are lazy: they are fetched only when the palette is first opened, then cached. Each group shows up to 25 results. +- **Matching and keyboard use** - results are scored (exact, prefix, substring, then subsequence) across label, description, and keywords, and re-grouped by category. Arrow keys and Tab move the selection (wrapping), Enter activates it. +- **Deep-linking** - selecting a result navigates straight to it: an acquisition to `/acquisitions/$acquisitionId`, a grid to its atlas view, a model to `/models/$modelName`. + +## Settings menu + +The settings menu exposes six items, of which two are functional and four are placeholders for planned work: + +| Item | State | +|---|---| +| Light / Dark mode | Placeholder ("soon") - the theme is light-only for now | +| Language | Placeholder ("English") | +| Display density | Placeholder - see the `density` feature flag | +| Documentation | Opens this documentation site | +| Keyboard shortcuts | Placeholder | +| Report a bug | Opens the smartem-frontend issue tracker | + +## Authentication controls + +When signed out, the header shows a sign-in button that starts the Keycloak login. When signed in, it shows an account menu with the user's name and email and a sign-out action. Authentication gates the **whole application** - the SPA renders a sign-in screen until Keycloak reports an authenticated session (it is not enforced per route). The mechanism is described in [Keycloak Authentication for SmartEM SPA](../architecture/keycloak-spa-authentication.md); running a local Keycloak for development is covered in [Local Keycloak for SmartEM frontend dev](../development/local-keycloak.md). In [mock mode](development.md#mock-mode) authentication is bypassed with an auto-signed-in mock user. + +## Context strip + +Within an acquisition, a thin **context strip** sits above the view (`components/session/ContextStrip.tsx`). It renders the current position in the acquisition hierarchy - acquisition, then grid, then square, then hole - as a row of linked segments, so a user can jump back up the chain without a breadcrumb bar. It is display-only (it reads route parameters and issues no API calls) and appears only inside acquisition routes; elsewhere it renders nothing. + +## Feature flags + +Incomplete or experimental UI is hidden behind feature flags (`apps/smartem/src/config/feature-flags.ts`). Each flag has a compile-time default of `false`; a deployment can turn any of them on through the `features` map in its runtime [`config.json`](architecture.md#runtime-configuration). + +| Flag | Gates | Default | +|---|---|---| +| `depositions` | The ARIA depositions route and its nav link (not yet built) | off | +| `agentLogs` | A future SSE-backed agent log viewer | off | +| `darkMode` | The dark colour scheme (designed separately, not yet built) | off | +| `density` | The compact/comfortable/spacious display-density control | off | + +Only `depositions` currently gates any visible UI. Because runtime configuration is applied once before the first render and is static thereafter, flags are read synchronously (via `getFeatureFlag` / `useFeatureFlag`) with no reactive re-rendering.