fix: repair cp1252-mangled em-dashes and ellipsis in the reference-app wiki
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
All four 06_reference_app pages round-tripped U+2014 (em-dash) and U+2026 (ellipsis) through cp1252 during earlier PowerShell edits; restored the characters and regenerated the wiki HTML + MCP modules.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"module": "wiki_reference_app_client_admin",
|
||||
"content": {
|
||||
"path": "wiki_reference_app_client_admin",
|
||||
"docstring": "# Client Admin — the spec-driven frontend\n\nThe client sample (`standards/src/templates/client/`) renders a full admin —\nauth pages, resource lists, forms, detail views — **from the server's\n`openapi.yaml`**, driven entirely by the x- extension fields. There are no\nhand-written screens per resource.\n\n## What ships\n\nA Vite + React + MUI app wiring the two Aetos packages:\n\n- **`@aetoskia/react-openapi`** — fetches `${VITE_API_BASE_URL}/openapi.json`,\n builds the router and the resource admin from the spec's schemas and x-\n fields (list columns, autocomplete, fk pickers, enum selects, hidden fields…)\n- **`@aetoskia/react-auth`** — login/register/logout against the auth-server\n paths declared in the spec's `securitySchemes.bearerAuth`, and attaches the\n JWT to every API call\n- a shared MUI theme (`src/shared-theme/`) and the shell\n (`Header` / `Footer` / `Home`)\n\n`src/openapi-config.ts` is the only configuration:\n\n```ts\nexport const specConfiguration: SpecConfiguration = {\n specUrl: `${apiBase}/openapi.json`,\n baseApiUrl: apiBase,\n title: \"Vet Admin\",\n getToken: () => tokenStore.get(),\n};\n```\n\n## Pair it with the sample server\n\n```bash\ncd apps/vet-sample-client\nprintf 'VITE_API_BASE_URL=http://localhost:8020\\nVITE_AUTH_BASE_URL=http://localhost:8020\\n' > .env.local\nnpm install\nnpm run dev\n```\n\nOpen the dev-server URL; the admin lists Parents, Pets, Treatments, Vets,\nAppointments and Jobs straight from the vet spec. Edit `openapi.yaml` on the\nserver and the client follows on reload — the spec is the contract on both\nsides.\n\n## Where the x- extensions come from\n\nThe canonical catalog lives in the react-openapi package\n(`spec-extensions.ts`, served to agents as the `specExtensions` module of the\nreact-openapi MCP bundle). The worked examples on the server side are the\n`Job` schema (enum selects via `x-edit-as: select` + `x-ui-type: enum`,\n`x-hidden` field masks, `x-list-columns`) and the FK fields on `Appointment`\n(`x-fk: {resource: pets, prefetch: true}`).\n\n## CI\n\nThe client ships a `drone-npm` shape (Type B in\n[Drone Pipelines](../lib/90_drone_pipelines/)) and follows the\n[Versioning](../lib/91_versioning/) standard (`package.json` +\n`CHANGELOG.md`).\n",
|
||||
"docstring": "# Client Admin — the spec-driven frontend\n\nThe client sample (`standards/src/templates/client/`) renders a full admin —\nauth pages, resource lists, forms, detail views — **from the server's\n`openapi.yaml`**, driven entirely by the x- extension fields. There are no\nhand-written screens per resource.\n\n## What ships\n\nA Vite + React + MUI app wiring the two Aetos packages:\n\n- **`@aetoskia/react-openapi`** — fetches `${VITE_API_BASE_URL}/openapi.json`,\n builds the router and the resource admin from the spec's schemas and x-\n fields (list columns, autocomplete, fk pickers, enum selects, hidden fields…)\n- **`@aetoskia/react-auth`** — login/register/logout against the auth-server\n paths declared in the spec's `securitySchemes.bearerAuth`, and attaches the\n JWT to every API call\n- a shared MUI theme (`src/shared-theme/`) and the shell\n (`Header` / `Footer` / `Home`)\n\n`src/openapi-config.ts` is the only configuration:\n\n```ts\nexport const specConfiguration: SpecConfiguration = {\n specUrl: `${apiBase}/openapi.json`,\n baseApiUrl: apiBase,\n title: \"Vet Admin\",\n getToken: () => tokenStore.get(),\n};\n```\n\n## Pair it with the sample server\n\n```bash\ncd apps/vet-sample-client\nprintf 'VITE_API_BASE_URL=http://localhost:8020\\nVITE_AUTH_BASE_URL=http://localhost:8020\\n' > .env.local\nnpm install\nnpm run dev\n```\n\nOpen the dev-server URL; the admin lists Parents, Pets, Treatments, Vets,\nAppointments and Jobs straight from the vet spec. Edit `openapi.yaml` on the\nserver and the client follows on reload — the spec is the contract on both\nsides.\n\n## Where the x- extensions come from\n\nThe canonical catalog lives in the react-openapi package\n(`spec-extensions.ts`, served to agents as the `specExtensions` module of the\nreact-openapi MCP bundle). The worked examples on the server side are the\n`Job` schema (enum selects via `x-edit-as: select` + `x-ui-type: enum`,\n`x-hidden` field masks, `x-list-columns`) and the FK fields on `Appointment`\n(`x-fk: {resource: pets, prefetch: true}`).\n\n## CI\n\nThe client ships a `drone-npm` shape (Type B in\n[Drone Pipelines](../lib/90_drone_pipelines/)) and follows the\n[Versioning](../lib/91_versioning/) standard (`package.json` +\n`CHANGELOG.md`).\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"module": "wiki_reference_app_domain_and_api",
|
||||
"content": {
|
||||
"path": "wiki_reference_app_domain_and_api",
|
||||
"docstring": "# Domain & API — the resource axis\n\nEvery aggregate follows the same four-file slice\n([CRUD Template](../lib/21_crud_template/)); walk `parents` first, then\n`appointments` when you need foreign keys.\n\n## The slice, end to end (parents)\n\n```\ndomain/parents/\n models.py # framework-free Pydantic model (id optional)\n ports/\n db.py # ParentDocument(Parent, BaseDocument) + create_parent_repo()\n service.py # ParentCrudsService(CrudsServiceAdaptor[...]) + get_parent_service()\ninterfaces/parents/\n routes.py # list/create/get/update/delete — names == operationIds\n```\n\nWiring happens in exactly one place — the composition root\n`init.py`:\n\n```python\nparent_repo = create_parent_repo()\n_resources[\"parent_svc\"] = get_parent_service(parent_repo)\n```\n\nRoutes resolve the service from app state through the shared helper\n(`interfaces/_common.py`):\n\n```python\nasync def list_parents(request: Request, limit: int = 20, offset: int = 0) -> dict:\n docs = await svc(request, \"parent\").search({})\n return paginate([dump(d) for d in docs], limit, offset)\n```\n\n## The spec is the contract\n\n`openapi.yaml` is OpenAPI 3.1 and the\nsingle source of truth: `main.py` builds an `OpenAPIFirstApp` that binds each\n`operationId` to a function of the same name in `interfaces/openapi.py` (a flat\nre-export of every routes module). There are no decorator routers.\n\nThe x- extension fields on schemas and properties are what make the admin\nclient render itself — see [Auto-Rendered Admin](../lib/40_ui_admin/) and\nthe worked `Job` schema (enum selects, hidden fields, filterable columns).\n\n## Foreign keys (appointments)\n\n`Appointment` declares `pet`, `vet`, `treatment` as `dict | int | None`. The\nwrite path normalizes bare ids through `expand_ref`; the read path returns\nfully expanded objects:\n\n```python\npet = await expand_ref(svc(request, \"pet\"), body.get(\"pet\"), \"pet\")\n```\n\nThe list endpoint additionally filters by date prefix and embedded fk ids —\nclient-side over the repository result, the pattern for small reference data.\n\n## Ids on the wire\n\nThe domain models declare `id: int | None = None` and documents use the\n**integer-id** repository variants (`infra/db/repos/mongo_int.py`,\n`create_repo()` in `domain/*/ports/db.py`, `REPO_BACKEND=memory|mongo`). This\nkeeps the OpenAPI `id: integer` contract, the in-memory demo store, and the\nMongo persistence layer consistent.\n\n## Tests\n\n- `tests/integration/test_openapi_app_builds.py` — generic gate: every\n `operationId` resolves and the app builds. Copy it verbatim.\n- `tests/unit/test_jobs_routes.py` — the pattern for route unit tests with a\n service double (no HTTP client needed).\n",
|
||||
"docstring": "# Domain & API — the resource axis\n\nEvery aggregate follows the same four-file slice\n([CRUD Template](../lib/21_crud_template/)); walk `parents` first, then\n`appointments` when you need foreign keys.\n\n## The slice, end to end (parents)\n\n```\ndomain/parents/\n models.py # framework-free Pydantic model (id optional)\n ports/\n db.py # ParentDocument(Parent, BaseDocument) + create_parent_repo()\n service.py # ParentCrudsService(CrudsServiceAdaptor[...]) + get_parent_service()\ninterfaces/parents/\n routes.py # list/create/get/update/delete — names == operationIds\n```\n\nWiring happens in exactly one place — the composition root\n`init.py`:\n\n```python\nparent_repo = create_parent_repo()\n_resources[\"parent_svc\"] = get_parent_service(parent_repo)\n```\n\nRoutes resolve the service from app state through the shared helper\n(`interfaces/_common.py`):\n\n```python\nasync def list_parents(request: Request, limit: int = 20, offset: int = 0) -> dict:\n docs = await svc(request, \"parent\").search({})\n return paginate([dump(d) for d in docs], limit, offset)\n```\n\n## The spec is the contract\n\n`openapi.yaml` is OpenAPI 3.1 and the\nsingle source of truth: `main.py` builds an `OpenAPIFirstApp` that binds each\n`operationId` to a function of the same name in `interfaces/openapi.py` (a flat\nre-export of every routes module). There are no decorator routers.\n\nThe x- extension fields on schemas and properties are what make the admin\nclient render itself — see [Auto-Rendered Admin](../lib/40_ui_admin/) and\nthe worked `Job` schema (enum selects, hidden fields, filterable columns).\n\n## Foreign keys (appointments)\n\n`Appointment` declares `pet`, `vet`, `treatment` as `dict | int | None`. The\nwrite path normalizes bare ids through `expand_ref`; the read path returns\nfully expanded objects:\n\n```python\npet = await expand_ref(svc(request, \"pet\"), body.get(\"pet\"), \"pet\")\n```\n\nThe list endpoint additionally filters by date prefix and embedded fk ids —\nclient-side over the repository result, the pattern for small reference data.\n\n## Ids on the wire\n\nThe domain models declare `id: int | None = None` and documents use the\n**integer-id** repository variants (`infra/db/repos/mongo_int.py`,\n`create_repo()` in `domain/*/ports/db.py`, `REPO_BACKEND=memory|mongo`). This\nkeeps the OpenAPI `id: integer` contract, the in-memory demo store, and the\nMongo persistence layer consistent.\n\n## Tests\n\n- `tests/integration/test_openapi_app_builds.py` — generic gate: every\n `operationId` resolves and the app builds. Copy it verbatim.\n- `tests/unit/test_jobs_routes.py` — the pattern for route unit tests with a\n service double (no HTTP client needed).\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"module": "wiki_reference_app_overview",
|
||||
"content": {
|
||||
"path": "wiki_reference_app_overview",
|
||||
"docstring": "# Reference App — Overview\n\nThe standards ship a **runnable sample** of the whole blueprint: the **vet**\nveterinary-clinic application. It exists to be copied — it is the reference\nfor how a real domain sits on the architecture, and the code an agent pulls\nthrough the MCP tools (`get_app_template`, `get_app_component`).\n\n!!! note \"One sample, two templates\"\n The **server** sample lives at `standards/src/templates/app/`\n (FastAPI + openapi-first + mongo-ops + hexa). The **client** sample lives\n at `standards/src/templates/client/` (Vite + MUI + `@aetoskia/react-openapi`\n + `@aetoskia/react-auth`). Together they are the full-stack reference.\n\n---\n\n## The domain at a glance\n\n| Aggregate | What it demonstrates |\n|---------------|---------------------------------------------------------------|\n| **parents** | The simplest CRUD slice (no foreign keys) — copy this first |\n| **pets** | Embedded list-of-refs (`parents[]`), file upload, enum fields |\n| **treatments**| Plain resource with pricing fields |\n| **vets** | Plain resource; target of prefetch FKs |\n| **appointments** | Single FKs (`pet`, `vet`, `treatment`) expanded on read, filtered list |\n| **jobs** | The worker's queue — durable status machine |\n\nThe pipeline axis ships one worked family, **`appointment_reminder`**, plus the\nreusable `pipelines/shared/` scaffold and a `worker.py` that dispatches jobs by\n`job.kind`.\n\n## Run it\n\n```bash\ncp -r <docs>/standards/src/templates/app apps/vet-sample\ncd apps/vet-sample\npip install -e \".[dev]\"\n\nuvicorn main:app --reload # API on :8020 (memory backend, zero services)\npython worker.py # background worker (polls jobs)\npython -m scripts.seed # optional: seed demo data (mongo backend)\n```\n\nThe default `REPO_BACKEND=memory` means the API boots with **no external\nservices** — repositories are process-local dictionaries. Switch to\n`REPO_BACKEND=mongo` for persistence (see `.env.example`).\n\nThe client pairs with it:\n\n```bash\ncp -r <docs>/standards/src/templates/client apps/vet-sample-client\ncd apps/vet-sample-client\necho \"VITE_API_BASE_URL=http://localhost:8020\" > .env.local\necho \"VITE_AUTH_BASE_URL=http://localhost:8020\" >> .env.local\nnpm install\nnpm run dev # Vite dev server\n```\n\n!!! warning \"Auth\"\n The server ships with the `bearerAuth` security scheme declared\n (`{AUTH_SERVER}` introspection). Without a reachable auth-server the API\n answers `401` on resource routes; `/health` stays public. For a local\n no-auth demo, remove the `security` block from `openapi.yaml` and the\n global security entry — see\n [Auth Integration](../lib/22_auth_integration/).\n\n## Next\n\n- [Domain & API](02_domain_and_api.md) — walking the resource axis\n- [Pipelines & Worker](03_pipelines_and_worker.md) — the pipeline axis\n- [Client Admin](04_client_admin.md) — the spec-driven frontend\n",
|
||||
"docstring": "# Reference App — Overview\n\nThe standards ship a **runnable sample** of the whole blueprint: the **vet**\nveterinary-clinic application. It exists to be copied — it is the reference\nfor how a real domain sits on the architecture, and the code an agent pulls\nthrough the MCP tools (`get_app_template`, `get_app_component`).\n\n!!! note \"One sample, two templates\"\n The **server** sample lives at `standards/src/templates/app/`\n (FastAPI + openapi-first + mongo-ops + hexa). The **client** sample lives\n at `standards/src/templates/client/` (Vite + MUI + `@aetoskia/react-openapi`\n + `@aetoskia/react-auth`). Together they are the full-stack reference.\n\n---\n\n## The domain at a glance\n\n| Aggregate | What it demonstrates |\n|---------------|---------------------------------------------------------------|\n| **parents** | The simplest CRUD slice (no foreign keys) — copy this first |\n| **pets** | Embedded list-of-refs (`parents[]`), file upload, enum fields |\n| **treatments**| Plain resource with pricing fields |\n| **vets** | Plain resource; target of prefetch FKs |\n| **appointments** | Single FKs (`pet`, `vet`, `treatment`) expanded on read, filtered list |\n| **jobs** | The worker's queue — durable status machine |\n\nThe pipeline axis ships one worked family, **`appointment_reminder`**, plus the\nreusable `pipelines/shared/` scaffold and a `worker.py` that dispatches jobs by\n`job.kind`.\n\n## Run it\n\n```bash\ncp -r <docs>/standards/src/templates/app apps/vet-sample\ncd apps/vet-sample\npip install -e \".[dev]\"\n\nuvicorn main:app --reload # API on :8020 (memory backend, zero services)\npython worker.py # background worker (polls jobs)\npython -m scripts.seed # optional: seed demo data (mongo backend)\n```\n\nThe default `REPO_BACKEND=memory` means the API boots with **no external\nservices** — repositories are process-local dictionaries. Switch to\n`REPO_BACKEND=mongo` for persistence (see `.env.example`).\n\nThe client pairs with it:\n\n```bash\ncp -r <docs>/standards/src/templates/client apps/vet-sample-client\ncd apps/vet-sample-client\necho \"VITE_API_BASE_URL=http://localhost:8020\" > .env.local\necho \"VITE_AUTH_BASE_URL=http://localhost:8020\" >> .env.local\nnpm install\nnpm run dev # Vite dev server\n```\n\n!!! warning \"Auth\"\n The server ships with the `bearerAuth` security scheme declared\n (`{AUTH_SERVER}` introspection). Without a reachable auth-server the API\n answers `401` on resource routes; `/health` stays public. For a local\n no-auth demo, remove the `security` block from `openapi.yaml` and the\n global security entry — see\n [Auth Integration](../lib/22_auth_integration/).\n\n## Next\n\n- [Domain & API](02_domain_and_api.md) — walking the resource axis\n- [Pipelines & Worker](03_pipelines_and_worker.md) — the pipeline axis\n- [Client Admin](04_client_admin.md) — the spec-driven frontend\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"module": "wiki_reference_app_pipelines_and_worker",
|
||||
"content": {
|
||||
"path": "wiki_reference_app_pipelines_and_worker",
|
||||
"docstring": "# Pipelines & Worker — the pipeline axis\n\nThe sample proves the full loop: **API creates a job document → worker polls →\npipeline family runs → job reaches a terminal status.**\n\n## The jobs slice\n\n`domain/jobs/` is an ordinary CRUD slice; its model is the durable state\nmachine the worker drives:\n\n```python\nclass Job(BaseModel):\n name: str\n kind: str # dispatch key\n status: Literal[\"pending\", \"processing\", \"done\", \"failed\"] = \"pending\"\n payload: dict | None = None\n retry_count: int = 0\n error_message: str | None = None\n```\n\n`POST /jobs` with `{\"name\": \"...\", \"kind\": \"appointment_reminder\",\n\"payload\": {\"appointment_id\": 1}}` enqueues work; `GET /jobs?status=...`\ninspects the queue.\n\n## The appointment_reminder family\n\n```\npipelines/appointment_reminder/\n abc.py # LoadPort / ComposePort / DeliverPort + root interface\n impl.py # concrete stages, wired root, register(\"appointment_reminder\", ...)\n pipeline.yaml # graph spec (nodes, edges, roots)\n resources/\n pipeline.yaml # runtime spec consumed by the loader\n patterns/reminder.yaml # message template fragments\npipelines/shared/\n ports.py loader.py registry.py # reused by every family\n```\n\nEach stage is a small, independently testable unit. The loader\n(`pipelines/shared/loader.py`) wires the root through `hexa.build` and merges\n`resources/patterns/*.yaml` into the stage-injected registry:\n\n```python\npipeline = get_appointment_reminder_pipeline(appointment_svc)\nreminder = await pipeline.run(appointment_id)\n```\n\nTo add a family: copy `appointment_reminder/`, replace the stages, call\n`register(kind, variant, root_cls)` — see\n[Pipeline Scaffold](../lib/50_pipeline_scaffold/).\n\n## The worker\n\n`worker.py` is the process that turns\ndocuments into execution:\n\n1. poll `job_svc.search({\"status\": \"pending\"})` on `WORKER_POLL_SECONDS`\n2. claim a job: `status → processing`\n3. dispatch through the `HANDLERS` table — `HANDLERS[job.kind](job, resources)`\n4. on success `status → done`; on exception `status → failed`,\n `retry_count += 1`, `error_message` captured; jobs at\n `WORKER_MAX_RETRIES` are skipped\n\n```python\nHANDLERS: dict[str, Callable] = {\n \"appointment_reminder\": run_appointment_reminder,\n # add yours here\n}\n```\n\nThe worker shares the composition root: `from init import init` gives it the\nsame wired services the API uses — one place to add a dependency.\n\n!!! note \"Same process model in production\"\n API and worker are separate processes (see `docker-compose.yaml`: `api`,\n `worker`, `mongo`). Any in-memory progress bus is per-process; to surface\n live progress through the API, swap the bus adaptor for a broker-backed\n one — see [Worker & Progress](../lib/51_worker_and_progress/).\n\n## Tests\n\n- `tests/unit/test_appointment_reminder_pipeline.py` — stage units + the wired\n root against a fake service.\n- `tests/unit/test_worker.py` — dispatch, done/failed transitions, retry\n exhaustion, using an in-memory job service double and a cancelled poll task.\n",
|
||||
"docstring": "# Pipelines & Worker — the pipeline axis\n\nThe sample proves the full loop: **API creates a job document → worker polls →\npipeline family runs → job reaches a terminal status.**\n\n## The jobs slice\n\n`domain/jobs/` is an ordinary CRUD slice; its model is the durable state\nmachine the worker drives:\n\n```python\nclass Job(BaseModel):\n name: str\n kind: str # dispatch key\n status: Literal[\"pending\", \"processing\", \"done\", \"failed\"] = \"pending\"\n payload: dict | None = None\n retry_count: int = 0\n error_message: str | None = None\n```\n\n`POST /jobs` with `{\"name\": \"...\", \"kind\": \"appointment_reminder\",\n\"payload\": {\"appointment_id\": 1}}` enqueues work; `GET /jobs?status=...`\ninspects the queue.\n\n## The appointment_reminder family\n\n```\npipelines/appointment_reminder/\n abc.py # LoadPort / ComposePort / DeliverPort + root interface\n impl.py # concrete stages, wired root, register(\"appointment_reminder\", ...)\n pipeline.yaml # graph spec (nodes, edges, roots)\n resources/\n pipeline.yaml # runtime spec consumed by the loader\n patterns/reminder.yaml # message template fragments\npipelines/shared/\n ports.py loader.py registry.py # reused by every family\n```\n\nEach stage is a small, independently testable unit. The loader\n(`pipelines/shared/loader.py`) wires the root through `hexa.build` and merges\n`resources/patterns/*.yaml` into the stage-injected registry:\n\n```python\npipeline = get_appointment_reminder_pipeline(appointment_svc)\nreminder = await pipeline.run(appointment_id)\n```\n\nTo add a family: copy `appointment_reminder/`, replace the stages, call\n`register(kind, variant, root_cls)` — see\n[Pipeline Scaffold](../lib/50_pipeline_scaffold/).\n\n## The worker\n\n`worker.py` is the process that turns\ndocuments into execution:\n\n1. poll `job_svc.search({\"status\": \"pending\"})` on `WORKER_POLL_SECONDS`\n2. claim a job: `status → processing`\n3. dispatch through the `HANDLERS` table — `HANDLERS[job.kind](job, resources)`\n4. on success `status → done`; on exception `status → failed`,\n `retry_count += 1`, `error_message` captured; jobs at\n `WORKER_MAX_RETRIES` are skipped\n\n```python\nHANDLERS: dict[str, Callable] = {\n \"appointment_reminder\": run_appointment_reminder,\n # add yours here\n}\n```\n\nThe worker shares the composition root: `from init import init` gives it the\nsame wired services the API uses — one place to add a dependency.\n\n!!! note \"Same process model in production\"\n API and worker are separate processes (see `docker-compose.yaml`: `api`,\n `worker`, `mongo`). Any in-memory progress bus is per-process; to surface\n live progress through the API, swap the bus adaptor for a broker-backed\n one — see [Worker & Progress](../lib/51_worker_and_progress/).\n\n## Tests\n\n- `tests/unit/test_appointment_reminder_pipeline.py` — stage units + the wired\n root against a fake service.\n- `tests/unit/test_worker.py` — dispatch, done/failed transitions, retry\n exhaustion, using an in-memory job service double and a cancelled poll task.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user