TESTING / DEVELOPMENT · DRAFTdrafts/gate-a-implementation-plan.md
Status dokumen bukan klaim bahwa fiturnya sudah tersedia.

RFC dan proposal mencatat rancangan. Untuk kemampuan yang benar-benar dapat dipakai, lihat matriks kapabilitas runtime dan catatan rilis.

Draft plan — Gate A: executable typed layered CRUD

Status: Proposed draft plan for review by the project owner. This document is written to be folded into the API completion plan or an RFC; it is not an accepted decision. Its implementation notes were refreshed on 2026-10-27 to reflect current code.

Source of truth: The current implementation status remains recorded in the runtime capability matrix and Appendix G status.

Goal

Close API completion Gate A: every generated CRUD operation dispatches through controller → service interface → service implementation → repository interface → repository implementation as typed YAZA code. YAZA repositories own operation selection, validation, and error mapping; a bounded native storage adapter may remain below that boundary until general YAZA-owned storage exists.

Current boundary (as of 2026-10-27)

  • All six Product routes execute explicit YAZA forwarding functions; this is verified by the HTTP smoke.
  • The compiler checks layer/interface metadata names, declaration order, unique method names, and exact one-to-one signature conformance. These declarations are metadata, not typed runtime dispatch contracts.
  • With DATABASE_URL, Product repository functions call the pure-YAZA std/pg/api_records.yz adapter; local PostgreSQL 17.6 CRUD/restart and disposable PostgreSQL 18.6 lifecycle/CRUD evidence pass. Without a database URL, ProductRepository calls std/api/memory_records.yz. It returns typed results through the ordinary handler-response path, while its bounded native adapter still mutates the runtime-owned memory table. Starter CRUD and the complete no-database API HTTP fixture pass, including chunked JSON creation.
  • API startup/schema/readiness hooks and Product PostgreSQL access use the pure-YAZA client and bounded process-local pool; generated API binaries have no libpq dependency. Remote TLS verification, broader server-version/CI evidence, runtime-polymorphic interface dispatch, and generic YAZA-owned storage remain open.
  • Handler outcomes support Text, Option[Text], and Result[Text, E] with fixed mappings for known ApiError variants and safe validation field paths. Typed ApiError mapping exists, but typed model responses and one shared contract remain incomplete.

Staged implementation plan

Stage 1 — Typed interface dispatch in the compiler

  1. Give implements IProductService executable meaning: the compiler lowers the implementing type into a C vtable structure matching the interface signature, or (preferred, simpler first pass) requires a concrete value stored in a project composition root and generates direct calls with compile-time conformance checks.
  2. Extend yaza check so that a service value declared as the interface type can only call methods declared by that interface; a call to a method outside the interface fails compilation with the interface name and the offending method.
  3. Reject implicit conformance: a value used where the interface is required must be checked against the interface method set at compile time (parameters, return types, and the error/optional types of each method).
  4. Negative compiler fixtures: missing method, wrong parameter type, wrong return type, wrong optional/error type, extra methods depending on the conformance strictness decision, duplicate implementation.
  5. Evidence: conformance smoke under normal and ASan/UBSan CLI builds; two consecutive self-host stages, byte-identical C; full YAZA suite remains green.

Exit evidence for this stage: one controller route calling a service through the interface compiles, runs, and rejects a deliberately broken implementation at compile time.

Stage 2 — YAZA-owned in-memory repository

  1. Implemented 2026-10-27, bounded adapter stage: std/api/memory_records.yz exposes list/find/create/replace/patch/delete as Result[Text, Text] repository operations. The native adapter mutates the API's bounded resource table and never writes an HTTP response. ProductRepository maps stable adapter outcomes into ApiError; the normal handler path serializes results.
  2. Starter CRUD and the complete API fixture pass, including request-supplied fields, server-generated IDs, chunked JSON, validation, and DELETE 204. The adapter validates its internal ID against the serialized record before using the schema validator, while ingress validation still rejects client-provided id fields.
  3. Still required for this gate: move from the runtime-owned bounded record table to general YAZA-owned values/storage with an explicit lifetime/concurrency model; provide typed Product/Option results and runtime interface dispatch. Run RSS-growth and sanitizer evidence for the final storage semantics. The current adapter is a real layer improvement, not completion of fully YAZA-owned persistence.

Exit evidence: all six CRUD routes pass the HTTP smoke with zero generic-engine dispatch; record survives response-arena resets; RSS smoke stays within the recorded budget.

Stage 3 — PostgreSQL repository through the pure-YAZA client

  1. Implemented 2026-10-25: Product repository functions now use std/pg/api_records.yz (built over std/pg/transport.yz) for all six PostgreSQL CRUD actions. Live local PostgreSQL 17.6 migration/API CRUD/restart evidence passes.
  2. Implemented 2026-10-26: startup/schema/readiness now use reachable YAZA lifecycle hooks and the pure-YAZA PostgreSQL client; generated API builds no longer link libpq. Migration and the full database-configured HTTP smoke passed on disposable PostgreSQL 18.6.
  3. Decode JSONB rows into Product shapes using the existing pg_query_value typed accessors; use pg_query_with_parameters for every runtime value; keep SQL templates static.
  4. Implemented 2026-10-26: Product CRUD and lifecycle operations now use the bounded process-local pool; the API server and database operations remain single-threaded/synchronous.
  5. Passed locally 2026-10-25: the HTTP smoke's persistence-across-restart check passed with DATABASE_URL and the pure-YAZA driver against PostgreSQL 17.6.

Exit evidence still required: repeat database-configured startup/schema/readiness and CRUD across the supported PostgreSQL matrix and automated CI; verify migration immutability/idempotence, rollback, invalid credentials, timeout, remote certificate validation, and failure paths in normal and ASan/UBSan builds. CI must also assert that generated API binaries remain free of libpq dependencies.

Stage 4 — Typed error contract

  1. Add a shared API error shape/variant (ApiError: InvalidInput, NotFound, Conflict, Storage, with safe field paths) and allow handlers to return Result[T, ApiError] with a stable status mapping: 400/404/409/500 respectively; err payloads stay out of response bodies except for declared safe fields.
  2. Derive the mapping, response schemas, and error codes from the same versioned contract that drives OpenAPI and Postman so generated artifacts cannot drift from runtime behavior.
  3. Update the HTTP smoke to assert the new status/body combinations and confirm no submitted values are reflected in error bodies.

Stage 5 — Retirement of the generic engine for generated APIs

  1. Once Stages 1–4 pass, switch the template to repository implementations only, keep the generic engine compiled out of the generated API unless a fallback mode is explicitly requested.
  2. Update the capability matrix, Appendix G, the roadmap progress ledger, and the template README with dated evidence for each replaced layer.

Acceptance criteria (traceable to Gate A)

  • Each of list, get, create, replace, partial update, and delete executes controller → service interface → service implementation → repository interface → repository implementation in YAZA.
  • In-memory and PostgreSQL repositories satisfy the same interface and pass the same smoke and CRUD assertions.
  • Not-found, conflict, validation, and persistence failures use stable typed codes and safe responses that do not leak secrets or raw SQL errors.
  • The compiler rejects non-conforming implementations at check/build time with source locations.
  • Evidence is recorded under normal and ASan/UBSan builds plus the self-host byte-identical check for every compiler change above.

explicitly out of scope (this plan)

  • General middleware pipeline, role-based authorization, TLS, HTTP client, and pooling (Gate D scope).
  • Model-derived response schema generation beyond what Stage 4 produces; broader validators.
  • Additional database providers (Gate E scope).