This commit is contained in:
@@ -1,130 +0,0 @@
|
||||
# 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 generic
|
||||
`tests/integration/test_openapi_app_builds.py` that asserts each spec
|
||||
operationId resolves against `interfaces.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: `DELETE` returns 204 for entities/tags/accounts/expenses/
|
||||
fetch-requests/reports; the follow-up `GET` correctly returns 404.
|
||||
- Pipeline axis: `run_fetch_request` advanced 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: **H**igh (blocks a 1-shot build), **M**edium (agent must work around),
|
||||
**L**ow (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 `noqa`s, 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-style` emits
|
||||
`async def`, a JSON body parameter named `body` (both `body: dict` and
|
||||
`body: <Model>`), and (with `--inject-request`) a service-lookup hint comment
|
||||
`# Resolve the service: request.app.state.db_resources["<resource>_svc"]`
|
||||
before `raise NotImplementedError`. Default behaviour is unchanged
|
||||
(`def` / `payload`).
|
||||
- Tests: `tests/test_codegen_routes.py` gained
|
||||
`test_template_style_async_body_model` and
|
||||
`test_template_style_dict_body_without_models`; suite now **32 passed** (was
|
||||
30); `ruff check` clean. Wiki `03_use_cases/04_codegen.md` documents the flag.
|
||||
MCP bundle regenerated (`docs/mcp/modules/openapi_first.json` contains
|
||||
`template_style`). Standards `spec_first_routing`/`build_recipe` modules
|
||||
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/me` authless
|
||||
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 `response`
|
||||
must be annotated (`response: Response`) or FastAPI binds it as a query param;
|
||||
returning `Response(status_code=204)` from deletes is the safe template idiom.
|
||||
- `apps/khata-trial` + `khata_trial` DB removed after verification.
|
||||
|
||||
## Recommended standard changes (from F10–F16)
|
||||
|
||||
1. Fix the `PopulateRule` examples (F11) — highest value, one-line corrections.
|
||||
2. Resolve the populate/update interop gap (F12) — either fix the adaptor or
|
||||
document the incompatibility.
|
||||
3. Add a "nested refs" modeling subsection (F13) and authless `/users/me`
|
||||
behavior (F14).
|
||||
4. Make the shipped template pass its own ruff config and exclude generated code
|
||||
(F15).
|
||||
5. Document the two accepted codegen handler shapes (F10).
|
||||
6. Stop vendoring `.ruff_cache` into 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=` in
|
||||
`05_crud_template.md`, `09_infra_adaptors.md`.
|
||||
- F12 — `infra/adapters/service.py` `update` handles `PopulatingRepository`.
|
||||
- F13 — new "Nested references" section in `05_crud_template.md`.
|
||||
- F14 — authless profile-endpoint guidance in `04_spec_first_routing.md`.
|
||||
- F15 — template `ruff check` clean (`extend-exclude = ["_gen"]`, file-level
|
||||
`noqa`s, typing/import fixes); template recompiles.
|
||||
- F16 — `build.py` ignore list hardened; `gitignore` component + template
|
||||
`.gitignore` added.
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user