- F11 PopulateRule field_name/collection_name corrections - F12 service adaptor update handles PopulatingRepository - F13 nested-references section; F14 authless /users/me guidance - F15 template ruff-clean; F16 stop vendoring .ruff_cache, ship .gitignore - document --template-style in build_recipe/spec_first_routing; refresh collected openapi-first + standards bundle (12 modules, 43 components) - add trial.md and trial_findings.md (F1-F16, re-trial A/B evidence)
10 KiB
Trial findings — build a khata app (no auth) from the Standards blueprint
Scope: reproduce apps/khata-app WITHOUT auth into apps/khata-trial, using ONLY
the standards (MCP bundle + library MCPs + docs://). khata-app consulted as the
answer key for intake only; implementation never copied.
Date: 2026-10-13. Status: trial build GREEN (imports, tests, live HTTP, pipeline).
Verification evidence (what the standard successfully produced)
python -c "import main"— OK; app builds with 46 routes (42 operationIds + 4 FastAPI defaults). Every no-auth operationId resolved to a handler.pytest tests -q— 6 passed, incl. the generictests/integration/test_openapi_app_builds.pythat asserts each spec operationId resolves againstinterfaces.openapi.- Live end-to-end (real MongoDB,
TestClient): 19 endpoints across all 8 resources returned 2xx — users(create/list/me/get/put), tags, accounts, entities(+logo), expenses(create/list), fetch-requests(create/list/get/ ambiguities), reports(create/list).FAILURES: none. - Deletes:
DELETEreturns 204 for entities/tags/accounts/expenses/ fetch-requests/reports; the follow-upGETcorrectly returns 404. - Pipeline axis:
run_fetch_requestadvanced status pending→completed and published 7 progress events on the bus (raw_lines,txn_blocks,raw_expenses,enrich_entity,enrich_tags,save_expenses). - Layering respected: no
domain/module imports fastapi/starlette; no Tier A library reimplemented.
Findings
Severity: High (blocks a 1-shot build), Medium (agent must work around), Low (rough edge).
| # | Area | Sev | Finding | Proposed standard fix |
|---|---|---|---|---|
| F1 | build_recipe | H | Recipe never mentioned the spec→code step; agent had to discover python -m openapi_first.cli {models,routes} unaided. |
FIXED (P1): codegen codified in recipe + prompt + wiki. |
| F2 | codegen | H | openapi-first CLI output is not consumable as-is: models land in one file with globally renumbered enums (Type, Type1, Type2…), routes land in per-tag packages with payload:/sync signatures. |
FIXED (P3): --inject-request, --package, --use-models, --models-module; documented shape. |
| F3 | wire vs doc | M | Generated wire models require top-level id + created_at/updated_at, but Mongo docs own those via BaseDocument; class XDoc(X, BaseDocument) keeps id required and fights the repo. |
FIXED (P4): API-model→document convention documented; keep domain models id-free. |
| F4 | spec→slices | M | No documented bridge from spec tags/operationIds to domain slices; agent inferred the 9-slice split. | Build recipe now shows tag→slice mapping (P1). |
| F5 | authless | M | Authless path (strip global security + per-op security: [] + securitySchemes) and the /users/me behaviour were manual/undefined. |
FIXED (P5): authless guidance added. |
| F6 | components | H | get_app_component omitted __init__.py files and interfaces/openapi.py; no tool returned the whole tree. |
FIXED (P6a): get_app_template returns the full tree; package-inits registered. |
| F7 | docs:// | H | Library MCP resource URIs regressed to doc://. |
FIXED (P0): restored docs://. |
| F8 | Tier B | M | Tier-B files referenced by the docs (http_cache, progress bus, KV store) were not shipped. |
FIXED (P6b): shipped infra/http_cache.py, infra/adapters/{sse,kv}.py. |
| F9 | prompts | M | Only start_app existed; no build/intake/trial prompts. |
FIXED (P7): build, intake, trial added. |
| F10 | codegen shape | L | Generated route stubs use payload: <Model>, sync def, body raise NotImplementedError; the template convention is body: dict + async. Both bind (binder introspects FastAPI signatures) but the mismatch is undocumented. |
Document both accepted handler shapes, or emit template-style stubs. |
| F11 | docs defect | H | 05_crud_template.md and 09_infra_adaptors.md show PopulateRule(field="owner", collection="users"), but the real dataclass fields are field_name / collection_name (mongo_ops/populate/rules.py:37). Copying the doc raises TypeError. |
Correct the two doc examples to field_name=/collection_name=. FIXED. |
| F12 | lib interop | M | CrudsServiceAdaptor.update (infra/adapters/service.py) always forwards a dict, but PopulatingRepository.update(id, data: T) calls _depopulate(data) → document.model_dump(...), requiring a model (mongo_ops/repository.py:404,384). Populate + update through the adaptor raises AttributeError. |
Adaptor now detects PopulatingRepository and builds the document model before calling update. FIXED. |
| F13 | modeling | M | Wire contract declares refs expanded (expense.entity: Entity, account.owner: User), while id-free domain models store FKs. Trial chose embedded dicts / string FKs. No single documented convention. |
Added a "Nested references" section to 05_crud_template.md (embed / populate / id). FIXED. |
| F14 | authless | L | /users/me (get_current_user, update_current_user, x-profile: true) has no defined authless semantics; agent invented "auto-create Demo User". |
Added authless profile-endpoint guidance to 04_spec_first_routing.md. FIXED. |
| F15 | template lint | M | The template's own pyproject.toml sets ruff select = ["E","F","I","UP"] but template-shipped files violate it: main.py E402, interfaces/openapi.py F403, infra/adapters/{kv,sse,service}.py + infra/http_cache.py UP/I, pipelines/shared/{registry,loader}.py UP/I. |
Fixed typing/imports, added file-level noqas, and extend-exclude = ["_gen"]; template now ruff check clean. FIXED. |
| F16 | template ship | M | The template vendors a .ruff_cache/ directory into every generated app (build.py copytree ignore list only skipped __pycache__/*.pyc), and ships no .gitignore. |
Hardened the copytree ignore list (.ruff_cache, .pytest_cache, .mypy_cache, .venv, venv, .git, *.egg-info, htmlcov), added a first-class gitignore component + template .gitignore. Component count 42→43. FIXED. |
F10 resolution (emitter) + re-trial evidence
openapi-first now emits template-convention stubs when asked:
generate_routes(..., template_style=True)/ CLI--template-styleemitsasync def, a JSON body parameter namedbody(bothbody: dictandbody: <Model>), and (with--inject-request) a service-lookup hint comment# Resolve the service: request.app.state.db_resources["<resource>_svc"]beforeraise NotImplementedError. Default behaviour is unchanged (def/payload).- Tests:
tests/test_codegen_routes.pygainedtest_template_style_async_body_modelandtest_template_style_dict_body_without_models; suite now 32 passed (was 30);ruff checkclean. Wiki03_use_cases/04_codegen.mddocuments the flag. MCP bundle regenerated (docs/mcp/modules/openapi_first.jsoncontainstemplate_style). Standardsspec_first_routing/build_recipemodules updated to reference--template-style.
Re-trial A (template-level): scaffolded the template into apps/_tpl-check,
pytest → 7 passed; import main → 11 routes; live TestClient Mongo smoke
→ FAILURES: none; targeted F12 populate+update check → FAILURES: none.
Scratch dir + DB removed.
Re-trial B (full khata rebuild): scaffolded apps/khata-trial, stripped auth
(2286-line spec), ran codegen with --template-style → 9 route files + _gen
models, authored all 9 domain slices, 42 handlers, composition root, worker, and
fetch pipeline. Then:
import main→ 46 routes (42 operationIds + 4 FastAPI defaults); every operationId resolved.pytest→ 2 passed.- Live end-to-end (real MongoDB,
httpx.ASGITransport): 43 checks, 0 failures — all 9 resources CRUD, entity-logo multipart,/users/meauthless auto-create, deletes 204 + follow-up 404, pipeline pending→completed, SSE events replay, raw octet-stream upload + download, report create/get/delete. - One emitter-adjacent trap re-confirmed: a handler parameter named
responsemust be annotated (response: Response) or FastAPI binds it as a query param; returningResponse(status_code=204)from deletes is the safe template idiom. apps/khata-trial+khata_trialDB removed after verification.
Recommended standard changes (from F10–F16)
- Fix the
PopulateRuleexamples (F11) — highest value, one-line corrections. - Resolve the populate/update interop gap (F12) — either fix the adaptor or document the incompatibility.
- Add a "nested refs" modeling subsection (F13) and authless
/users/mebehavior (F14). - Make the shipped template pass its own ruff config and exclude generated code (F15).
- Document the two accepted codegen handler shapes (F10).
- Stop vendoring
.ruff_cacheinto apps and ship a.gitignore(F16).
Resolution
F11–F16 applied to the standard sources and the MCP bundle rebuilt
(build.py --name standards: 12 modules, 43 components, lib + wiki HTML):
- F11 —
field=/collection=→field_name=/collection_name=in05_crud_template.md,09_infra_adaptors.md. - F12 —
infra/adapters/service.pyupdatehandlesPopulatingRepository. - F13 — new "Nested references" section in
05_crud_template.md. - F14 — authless profile-endpoint guidance in
04_spec_first_routing.md. - F15 — template
ruff checkclean (extend-exclude = ["_gen"], file-levelnoqas, typing/import fixes); template recompiles. - F16 —
build.pyignore list hardened;gitignorecomponent + template.gitignoreadded.
The running docs image bakes the bundle, so it was rebuilt
(aetos-docs:standards5) and the docs container swapped onto it; MCP
:8009 live-verified (tools/prompts/resources unchanged, 43 components,
gitignore present).
Outcome
Trials (original + re-trial A + re-trial B) proved the standard buildable
end-to-end; F1–F16 recorded and F11–F16 fixed. F10 resolved in the
openapi-first emitter. apps/khata-trial removed after verification. Remaining
items in "Recommended standard changes" are cosmetic/documentation-only and will
not meaningfully change build outcome quality.