Use Gitea Releases for installation and ship VS Code highlighting

This commit is contained in:
Antigravity committed 2026-10-06 14:12:58 +00:00
1 parent a46bd6e132
commit 201bdee372
243 files changed
+3289 -12521

No files matched your search

+2
View File
@@ -2,6 +2,8 @@
/config/setting.json
/.release-key.*/
/.trellis/workspace/
/.trellis/tasks/
/.trellis/private/
/.agents/
/.codex/
**/__pycache__/
+2 -2
View File
@@ -35,12 +35,12 @@
- `check/cache.rs`: per-thread last-result cache with complete source identity; bundled AST caching belongs to `lib.rs`/`source.rs`.
- `native/build/cache.rs`: native artifact identity, integrity validation and atomic publication. `native/build.rs` owns current runtime freshness, snapshots and isolated executable lifetimes.
- `crates/dever-cli/src/main.rs` and `format.rs`: argument parsing, source loading, direct execution of the owned native program, local clean command adaptation and prepared/atomic source formatting writes. `test_runner.rs` is the single owner of application-test ordering, isolated native processes, captured output and temporary project cleanup.
- CLI binary-private `compile.rs` owns LLVM/object/LLD orchestration; `worker.rs` is the service-owned source compiler. Shared `toolchain/runtime_pack.rs` validates packs/snapshots without bridge imports; `compilation.rs` owns canonical input and staging validation, while `service/` owns authenticated transport and child supervision. Reuse core `native::compile_artifact` for private builds and `NativeProgram` for caller-owned received executables. `toolchain/packaging.rs` owns deterministic native release assembly/signing from explicit author inputs; `sdk/native-release.rs` only adapts its private author CLI. Root `runtime-pack` profile and `sdk/native-release.md` own the four-archive build recipe. `test/dever-cli-tests/support/llvm_inputs.rs` holds explicit fixture SDK inputs, and `support/daemon.rs` holds the shared owned-daemon lifecycle; neither is a production input discovery mechanism.
- CLI binary-private `compile.rs` owns LLVM/object/LLD orchestration; `worker.rs` is the service-owned source compiler. Shared `toolchain/runtime_pack.rs` validates packs/snapshots without bridge imports; `compilation.rs` owns canonical input and staging validation, while `service/` owns authenticated transport and child supervision. Reuse core `native::compile_artifact` for private builds and `NativeProgram` for caller-owned received executables. `toolchain/packaging.rs` owns deterministic native release assembly/signing from explicit author inputs; `sdk/native-release.rs` only adapts its private author CLI. Root `runtime-pack` profile owns the four-archive build settings. `test/dever-cli-tests/support/llvm_inputs.rs` holds explicit fixture SDK inputs, and `support/daemon.rs` holds the shared owned-daemon lifecycle; neither is a production input discovery mechanism.
- `library/dever/json/`: ordinary Dever JSON algorithm. `library/dever/http.dever`, `sse.dever`, `websocket.dever` and `tls.dever`: public protocol types/helpers and the native bridge; no duplicate source protocol stack. `task.dever` exposes runtime configuration, sleep and periodic streams.
- `examples/cms/dever/` and `examples/cms/md/`: maintained CMS projects with equal declarations, schema, settings, business behavior and application tests. Generated API/CMD roots replace `module/main.dever`; `api/admin/**` and `api/front/**` are assigned by `sites.<key>.path`. Both projects keep matching `test/<component>/<domain>/<topic>` cases and `config/setting.json`.
- `examples/old/`: archived pre-role-layout language and regression material. It is not a current application root and must not be presented as a directory or visibility template. Formal tests remain under root `test/`.
- `examples/README.md`: current CMS commands and the archive boundary. Historical rejection fixtures keep their original diagnostics only when invoked by dedicated compiler-level tests, not as current application projects.
- `test/dever-tests/tests/`: permanent focused tests. The canonical full-language syntax example is read directly from the PRD so a copied fixture cannot drift.
- `test/dever-tests/tests/`: permanent focused tests. Parser, checker and formatter share `test/dever-tests/fixtures/core/complete_package.dever`; public tests do not depend on private task records.
Core foundations, formatting, optional reference evaluation, official library follow-through and the SQLite/PostgreSQL static ORM have concrete implementations. Compiler distribution and self-hosting remain unshipped; validation evidence belongs to the active task, not this directory map. Compiler phase APIs are bootstrap internals, not an alternative program source format.
+3 -3
View File
@@ -4,9 +4,9 @@ Workspace-owned Rust forbids unsafe code. Decimal128 and insertion-ordered Map r
Root dev/test profiles keep `debug = 0` and `incremental = false` so independent focused test binaries do not recreate multi-gigabyte debuginfo and incremental object trees. Release keeps fat LTO and one codegen unit. Native runtime feature combinations share `target/native-runtime/cargo`; exact per-build inputs live under `target/native-runtime/inputs`. Do not restore one complete Cargo target per feature combination.
Native ABI release archives use the root `runtime-pack` profile and one shared `target/native-runtime-abi` Cargo target for all four driver combinations. Save each completed archive before the next feature build. Preserve link symbols, keep `embedded` disabled, and distinguish deterministic pack assembly from cross-host source reproduction; the author workflow and assertion points live in toolchain-and-library.md and sdk/native-release.md.
Native ABI release archives use the root `runtime-pack` profile and one shared `target/native-runtime-abi` Cargo target for all four driver combinations. Save each completed archive before the next feature build. Preserve link symbols, keep `embedded` disabled, and distinguish deterministic pack assembly from cross-host source reproduction; the contracts and assertion points live in toolchain-and-library.md.
Tests exercise source contracts and observable AST structure, not private helper implementations. Include the PRD's complete package, precedence and associativity, Unicode and line positions, lexical failures, excluded syntax, recovery, structural limits and directory loading. Filesystem tests create and remove only their own temporary directories; source loading never follows symbolic links.
Tests exercise source contracts and observable AST structure, not private helper implementations. Include the shared `test/dever-tests/fixtures/core/complete_package.dever` fixture, precedence and associativity, Unicode and line positions, lexical failures, excluded syntax, recovery, structural limits and directory loading. Filesystem tests create and remove only their own temporary directories; source loading never follows symbolic links.
Explicit LLVM Worker acceptance consumes the prepared immutable `target/native-release-inputs/component-fixture`, with recorded source and binary digests. Cargo's `target/debug/component-fixture` is a mutable build output: feature unification can change its bytes while running the workspace matrix. Ordinary Worker tests use their Cargo-provided executable; never bypass the CI input-change gate to treat a mutable output as a fixed author input.
@@ -37,7 +37,7 @@ cargo clippy --offline -p dever-core -p dever-runtime -p dever-tests --test hell
cargo clippy --offline -p dever-cli --bin dever -- -D warnings
```
The hello target retains stdout, value mutation, package boundaries, missing toolchain and artifact cleanup regressions. Core targets cover the full PRD sample, static rejection, clause permutation, concrete native output, COW ordering, nullable flattening and source-located faults. Foundation-extension cases additionally cover contextual handler syntax, forwarding/specialization recursion, value-only clause domains, List/Bytes/Stream traversal, early stop/resume and one terminal stream failure. Resource tests create only their own temporary files and loopback sockets on port 0 with bounded reads/timeouts. CLI changes also require the actual check/run/build command. These are focused compiler checks, not an integration environment or workspace-wide test run.
The hello target retains stdout, value mutation, package boundaries, missing toolchain and artifact cleanup regressions. Core targets cover the shared complete-package fixture, static rejection, clause permutation, concrete native output, COW ordering, nullable flattening and source-located faults. Foundation-extension cases additionally cover contextual handler syntax, forwarding/specialization recursion, value-only clause domains, List/Bytes/Stream traversal, early stop/resume and one terminal stream failure. Resource tests create only their own temporary files and loopback sockets on port 0 with bounded reads/timeouts. CLI changes also require the actual check/run/build command. These are focused compiler checks, not an integration environment or workspace-wide test run.
Parentheses must not affect context inference: use the shared Group-unwrapping helper for nulls, empty literals and named handlers. Handler signature types must reuse ordinary boundary validation, including generic arity and nested Map-key rules. Stream adapters must preserve `Pull::Last` rather than turning it into an ordinary item. Never retype an existing Decimal/Int local as Float, even after constant propagation. Float zero patterns match both signs, so substituting the pattern constant for the actual input changes division semantics. Preserve source field and Map pair evaluation order in generated code; declaration order controls layout/display only.
@@ -4,7 +4,7 @@
Application deployment targets belong to `toolchain::BuildTarget`, independently of the compiler host. Public `build --target linux-aarch64` may run on Linux x86_64; omitted targets and `run/test` stay native. Propagate the selected target through Lib preparation, CompileRequest, runtime pack selection, signature closure verification and both caches. Verify ELF machine/type for archive members and ordinary objects; a matching manifest label/hash alone does not prove architecture. A warm shared cache must still validate the selected target pack. Do not globally replace `platform_identity` or require an ARM compiler core for cross builds.
Target runtime/interpreter/extension/exec/sandbox assets must match the deployment target. Go build tools explicitly declare `build.host`, while Go output and stdlib headers match `build.goos/goarch` and version. Their sandbox uses host assets; emitted Workers use target assets. Product processes keep cleared environments; author-only cross linker settings or Go overlays must not become application configuration/environment knobs. Python sdist/npm hook execution requires a matching execution environment or a previously prepared complete target receipt; never relabel host native output. The canonical author maps and preparation commands are in `sdk/native-release.md`.
Target runtime/interpreter/extension/exec/sandbox assets must match the deployment target. Go build tools explicitly declare `build.host`, while Go output and stdlib headers match `build.goos/goarch` and version. Their sandbox uses host assets; emitted Workers use target assets. Product processes keep cleared environments; author-only cross linker settings or Go overlays must not become application configuration/environment knobs. Python sdist/npm hook execution requires a matching execution environment or a previously prepared complete target receipt; never relabel host native output.
Executable modes are also part of the cross-target resource contract. Sandbox packaging and application embedding share `asset_executable`; classify both supported target loaders independently of the author's host, while retaining target ELF validation. Regressions must check actual packaged modes from non-executable source inputs and execute the embedded Worker. Host-only loader assertions and successful linking cannot establish target bootstrap behavior. Full-system sandbox fixtures must switch from initramfs rootfs to a normal root mount before bubblewrap uses `pivot_root`; do not loosen the production sandbox to accommodate a fixture mount error.
@@ -22,7 +22,7 @@ Private task constructors without suspension remain ordinary functions. Scope ad
Private `dever run/build/test` uses checked HIR → LLVM → object → in-process LLD, without Rust/Cargo/cc lookup or fallback. Keep LLVM calls binary-private; shared `toolchain/runtime_pack` validates compiler-relative `runtime/<platform>/manifest.json`, normalized paths, version/v1 ABI/target, four profiles and exact CRT/archive digests. Shared code must not reference bridge target methods: actual linkage pulls LLVM into launcher/daemon even when the call appears to be metadata only. Reject symlinks, scripts, thin archives and incomplete references; snapshot/recheck selected inputs before linking. Private compilation reuses `native::compile_artifact`; managed compilation requires authenticated daemon IPC and never falls back. Ephemeral signed Linux fixtures do not establish trimmed or formal six-target release assets.
Native authoring now uses the independent four-profile `runtime-pack` build recipe and `sdk/native-release.md`; the old all-features debug archive remains a test fixture only. Signing prepared inputs and compiling source are separate operations. Do not move Cargo/tool discovery into the maker or application run/build.
Native authoring uses the independent four-profile `runtime-pack` build; the old all-features debug archive remains a test fixture only. Signing prepared inputs and compiling source are separate operations. Do not move Cargo/tool discovery into the maker or application run/build.
Linux first installation reuses the release signature and machine version owner. Author `bootstrap` inputs provide launcher/daemon and their own complete private ELF library closure. The independently authenticated installer reads only its absolute root-owned `config/setting.json`; its public key must be supplied outside the downloaded release, then pinned atomically without key replacement. A bounded journal may name only the seven fixed bootstrap destinations, including the sandbox helper tree and dedicated AppArmor policy. Persist final file modes and child directories before publication; recover uncommitted changes, but never roll back a committed publication because cleanup failed. Stop a newly started service before removing its unit; restore previous service state after restoring files. Offline image installation never invokes systemctl. Incoming packages must match the supported service template; existing signed versions retain their original template and remain usable by pinned projects. Actual global activation and public release require separate operational authorization and evidence.
@@ -30,7 +30,7 @@ Process main is separate from callable roots. Initialize Unix SIGPIPE policy bef
## 2. Signatures
- First installation: root `install` is the sole installer source and supports `bash install` and `curl -fsSL https://git.dever.cc/shemic/dever/install | bash`. Its Bash entry forwards `--version`, `--release`, `--trusted-key`, `--system-root`, and `--no-start` to its embedded Python implementation. Maintain this file directly; skills provide guidance, not another installer, and there is no generation step.
- First installation: root `install` is the sole installer source and supports `bash install` and `curl -fsSL https://git.dever.cc/shemic/dever/raw/branch/main/install | bash`. Its Bash entry forwards `--version`, `--release`, `--trusted-key`, `--system-root`, and `--no-start` to its embedded Python implementation. Maintain this file directly; skills provide guidance, not another installer, and there is no generation step.
- Project creation: `dever new <new-directory> [--markdown]`; `project::create(&Path, bool)` consumes the single template source under the ordinary `skills/assets/template` directory in the same repository revision. The machine launcher selects the active core without reading nonexistent target configuration.
- AI guidance: `dever skill path [project-root]` resolves and verifies the active or project-pinned release's `skills/dever-language`; `dever skill install <new-directory>` atomically publishes a stable loader that invokes the absolute machine launcher. Skill resolution is a public read-only operation and must not open the root-only install lock.
@@ -84,7 +84,7 @@ Process main is separate from callable roots. Initialize Unix SIGPIPE policy bef
Developer onboarding uses one official source repository, `ssh://git@git.dever.cc:2222/shemic/dever.git`. Its ordinary `skills/` directory is committed and versioned with the compiler, without a submodule or separate skill repository; it distinguishes this language from Go Dever. Author `skill` is a required list of `{source,path,sha256}` entries published as signed `skills/dever-language/<path>` artifacts, including SKILL.md; at most 256 files, 1 MiB each and 8 MiB total. New-project source bytes come from the same skill templates; Markdown documentation wraps them without a second semantic copy. Keep the generated configuration CMD-only and free of database/auth prerequisites.
Official `install`, `update` and extension preparation explicitly download from the compiled `https://git.dever.cc/shemic/dever/dist` origin; neither project setting.json nor environment variables choose that origin. Metadata uses `latest/` or `<major.minor.patch>/`; after authentication every payload uses the exact signed version directory, without `v` or `download/`. `dever-release-v2` binds base `artifacts` and optional `extensions` in one signature. Each platform publishes `dever-<platform>.manifest.json`, `.manifest.sig`, `.tar.zst`, plus `.ext-<kind>-<target>.tar.zst`, under immutable version directories. Kind is runtime-pip/npm/go, build-pip/npm, or target; target holds nonhost native runtime/CRT and sandbox. Validate closed IDs, path namespaces, duplicate/overlapping paths, digests and size/count limits in the shared catalog owner. Base retains all four host database profiles, compiler closure, bootstrap, host sandbox and skill. Extensions cannot override base files. Do not fabricate an unsigned merged manifest.
Official `install`, `update` and extension preparation resolve `https://git.dever.cc/api/v1/repos/shemic/dever/releases/latest` or `/tags/v<major.minor.patch>` anonymously. Neither project setting.json nor environment variables choose that origin. Resolve once per operation; the selected stable tag must match the signed manifest even for latest. Metadata is bounded to 2 MiB and 1–512 unique attachments with positive bounded sizes. Download the exact attachment name through its validated `browser_download_url`, enforcing the declared length and HTTPS same-origin redirects. Reject draft/prerelease, malformed tags, duplicate names, missing assets, foreign URLs, truncation and tag/signature mismatch before publication. `dever-release-v2` binds base `artifacts` and optional `extensions` in one signature. Each release publishes `dever-<platform>.manifest.json`, `.manifest.sig`, `.tar.zst`, plus `.ext-<kind>-<target>.tar.zst`. Kind is runtime-pip/npm/go, build-pip/npm, or target; target holds nonhost native runtime/CRT and sandbox. Validate closed IDs, path namespaces, duplicate/overlapping paths, digests and size/count limits in the shared catalog owner. Base retains all four host database profiles, compiler closure, bootstrap, host sandbox and skill. Extensions cannot override base files. Do not fabricate an unsigned merged manifest.
Authenticate the catalog before extraction. Zstandard is a single checksummed frame with window_log_max=27; verify complete compressed EOF/trailer and bounded expansion even after TAR EOF. USTAR contains only its signed ordinary artifact files, with no duplicate, escaping or extra entries; require complete terminator/padding and reject trailing content. Redirects remain on fixed official HTTPS hosts. Publication never replaces another writer's directory. First-install raw `.blob-<sha256>` assets include the launcher and every bootstrap/lib dependency, independently digest-checked before execution. The launcher owns extraction; Python must not implement a second TAR/Zstd parser or require system zstd.
@@ -92,7 +92,7 @@ Shared extensions live in service-writable `cache/extensions/<version>/<catalog-
The root first-install script embeds its independently provisioned public key, verifies the full package before executing bootstrap, and generates only an owned temporary installer config. Its Bash entry passes the embedded Python source through a quoted heredoc to `/usr/bin/python3 -I -`; parse the complete function before invoking it. Non-root installation uses `/usr/bin/sudo`; help must neither elevate nor download. Missing Python 3.9/OpenSSL, unsupported OS/ABI, unavailable package or invalid signature is an explicit failure. Current x86_64 host packages require glibc 2.39; ARM application support does not establish an ARM compiler-host release. The installer never reads business configuration. Real-root installation explicitly starts the machine service unless --no-start is selected; image-root verification must not touch host services. Python/OpenSSL are installer prerequisites, not application runtime dependencies. `sdk/release-assets.py` uses an explicitly supplied author zstd executable and publishes deterministic base/extension/raw assets with Linux NOREPLACE directory rename; it cannot clean another writer's public output.
Web publication must explicitly serve the root script at `/shemic/dever/install` and all assets as anonymous HTTPS, independently of source repository access. Redirects are restricted to HTTPS `git.dever.cc` with no credentials, non-443 port or fragment. Publish all immutable version assets before switching latest metadata. Source pushes and local transport tests do not prove public download readiness. Changing the official origin requires rebuilding the stable launcher/daemon and provisioning them once through the new installer; ordinary update activates version payloads but does not replace that bootstrap pair. Never relabel old binaries as a new release.
The root script is served at `/shemic/dever/raw/branch/main/install`; Release metadata and attachments must also allow anonymous HTTPS reads. Redirects are restricted to HTTPS `git.dever.cc` with no credentials, non-443 port or fragment. Complete all assets before publishing a release. Source pushes and local transport tests do not prove public download readiness. Changing the download protocol requires rebuilding the stable launcher/daemon and provisioning them once through the new installer; ordinary update activates version payloads but does not replace that bootstrap pair. Never relabel old binaries as a new release. Focused Rust/Python tests assert latest/exact selection, signature and length rejection, missing attachments and preservation of active versions. Correct: reuse one resolved release snapshot for the base and installed extensions. Wrong: reopen latest between those downloads or guess an attachment path from its name.
Native author settings use `dever-native-release-input-v1`: version/target/signing_key, core and `core_libraries` inputs, required base/sqlite/postgres/both archive inputs, and ordered start/libraries/end. Input `source` is a normalized author-relative path and `sha256` is the exact lowercase digest; link inputs also specify the destination `path`. Core libraries use plain SONAME filenames and publish below `lib/`; runtime link inputs remain runtime-relative. Reuse the runtime v1 manifest types and validator. Copy into exclusively owned private staging, verify both source and copied digest, sort manifest artifacts, and sign those exact bytes with the supplied owner-private Ed25519 PKCS#8 key. Reject key content as payload, symlinks, path escape and reserved/duplicate destinations. Keep source paths, key material and timestamps out of release metadata. Linux publication uses atomic NOREPLACE; failures clean only owned staging. No tool invocation, environment configuration, downloads, input hard links or implicit trust-key installation belongs to this owner.
@@ -136,7 +136,7 @@ Binary resources cross the compiler bridge separately from bounded textual LLVM
#### Scope / trigger
Native release authoring also packages Python/Node interpreters and Go build tools; Worker preparation consumes the same runtime descriptor validator. The author recipe lives in [sdk/native-release.md](../../../sdk/native-release.md).
Native release authoring also packages Python/Node interpreters and Go build tools; Worker preparation consumes the same runtime descriptor validator.
#### Signatures
@@ -1,793 +0,0 @@
# Dever Minimal Core 0.1 Design
## N4 Concurrent Composition And Reusable Clients (2026-09-12, Complete)
Source `dever.task.start` establishes the one runtime from a synchronous entry with an explicit configuration and static async handler. HTTP system primitives infer an optional final transferable context and pass it to a statically checked handler; applications can wrap this in their own concrete context signature. Ordinary records retain value semantics; Channel/client/resource fields share their existing resource identity.
`timeout(task, millis, fallback)` consumes a Task, waits for cancellation cleanup on expiry, then invokes a zero-input async fallback with the identical output signature. `race(tasks...)` consumes equally typed tasks and drains losers before returning the first result. Neither operation suppresses program faults. `stream(Channel|List|Bytes)` and periodic `dever.task.ticks` are pull-driven; async memory traversal reuses collection handlers and bounded Group dispatch.
Network clients own bounded reusable HTTP/1 connections and stream bodies. TLS uses tokio-rustls/ring, with certificate verification, bundled public roots and explicit custom roots/server PEM material. TCP/TLS share the concrete I/O owner used by HTTP and WebSocket upgrades. Protocol parsing stays in Hyper/tungstenite. Idle connections, pending requests, body chunks and cancellation have explicit owners. No HTTP/2, implicit retries, redirects or detached connection drivers.
The implemented pool fixes one origin per shared HttpClient and uses Hyper's low-level HTTP/1 API. Separate request and physical-connection permits include idle/queued drivers in the same limit. One structured task drives the bounded queue and FuturesUnordered, expiring idle entries without a future request. Its owner guard is constructed before task creation, including the unpolled-child cancellation case. Response EOF returns its lease; early close/fault/cancellation aborts the connection. Listener.close triggers graceful HTTP shutdown; timeout(server, grace_ms, fallback) bounds draining active responses and upgrades.
Locked additions are tokio-rustls 0.26.5, rustls 0.23.44 with ring/std/tls12 and webpki-roots 1.0.8. Shared TLS configuration uses small explicit session-cache capacities. Transport boxes only the large TLS variant; ordinary TCP stays direct. The backend example combines configured source entry, concrete Context with Channel, a pooled request and graceful shutdown on an owned random loopback port.
## N3 Streaming HTTP, SSE And WebSocket (2026-09-12, Approved)
Keep ordinary `serve` direct; `serve_live` binds an async zero-output `(Request, HttpReply)` handler. A Group of one owns each connection's live handler. Shared request decoding, checked response encoding and Hyper configuration avoid a parallel HTTP implementation. A one-slot body channel provides backpressure. Response-body Drop signals cancellation for streamed, buffered and HEAD responses; Session cleanup closes the producer or upgraded socket. Handler faults propagate and cannot be emitted as a successful truncated body.
On upgrade, wait for Hyper's driver, extract its known TcpStream and preread bytes, then keep the connection reservation while awaiting the handler Group. This removes the HTTP write deadline and the need for an extra transport abstraction. WebSocket uses independent reader/writer locks around tokio-tungstenite split halves, the shared Endpoint close signal and bounded message/write buffers with a 4 KiB read buffer. Acquire the writer lock before arming cancellation cleanup. close takes final transport ownership, wakes other operations and completes a bounded handshake.
SSE is a small field encoder plus response-body timer; no routing framework or heartbeat task is needed. Preserve CR/LF/CRLF and trailing data lines, compute final size before allocation and reserve Content-Type for the protocol. Request headers carry Last-Event-ID; replay belongs to application code. WebSocket control frames are processed while receive/messages is polled; no automatic reader or Ping task. Public exact limits and lifecycle are defined once in LANGUAGE.md.
References checked: [tokio-tungstenite 0.30.0](https://docs.rs/tokio-tungstenite/0.30.0/tokio_tungstenite/), [WebSocketConfig](https://docs.rs/tungstenite/0.30.0/tungstenite/protocol/struct.WebSocketConfig.html), [EventSource parsing](https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream). No TLS, HTTP/2, pooling, protocol extensions or compatibility layer in N3.
## N2 HTTP Engine (2026-09-12, Approved)
Hyper HTTP/1 owns parsing/framing/persistence, using the existing Tokio runtime and only hyper-util's Tokio I/O/timer adapter. No Axum dependency is needed for the current single static route contract. This supersedes earlier source-only HTTP and per-request fault-isolation proposals below: handler program faults propagate through the structured server task, while peer protocol/I/O failures end only that connection.
Official source owns Header/Request/Response/Limits, header helpers, send and serve. Header is Text name plus Bytes value; List preserves repeated values. Native conversions resolve fields by name after exact shape checks. Intrinsic carries an optional static callback; callback extraction is shared with Collection for dependency/cycle/specialization traversal, and handler effects/recovery propagate through the existing summaries.
HTTP accepts unique Tokio streams through the existing Listener owner before constructing any aliased public Socket. Existing AsyncStream/Group reserves capacity before accept, observes connection faults while idle, and drains descendants on stop. Hyper never spawns a detached connection driver. Client send polls driver and response in the same bounded future and releases both on completion/cancellation; pooling is deferred.
Bytes uses bytes::Bytes for shared engine storage, slicing and compatible Text ownership transfer. Bodies are buffered within explicit limits, merging fragments incrementally without retaining one metadata entry per tiny frame. Header, body, handler and stalled-write deadlines have separate owners. The public contract and exact protocol exclusions are in LANGUAGE.md; TLS/HTTP2, streaming responses, client pooling and WS/SSE are not N2 deliverables.
## N1 Async Stream And TCP (2026-09-12, Approved)
This new-language phase supersedes the historical compatibility baseline and network pause below. Migrate callers directly; no synchronous TCP backend, aliases or fallback paths.
Use one existing Tokio runtime with its I/O driver enabled. AsyncStream owns one Send stream producer, serializes pulls, and shares a close signal across aliases. Closing or finishing a consumer releases its producer; there is no unbounded buffer or per-item boxed future. Stream remains the distinct synchronous file-sequence type.
Socket and Listener share explicit resource closure. Socket read and write have independent async locks; closure wakes both. A write tracks partial progress and closes the socket if abandoned after writing any bytes, so subsequent operations cannot append to a truncated message. Operation timeout includes waiting for its direction lock. No retry of an entire interrupted write.
AsyncStream traversal may call synchronous effect-checked handlers or async handlers. parallel_each on AsyncStream requires an async zero-output handler and uses the existing structured Group. It checks completed tasks and acquires group capacity before pulling, and observes handler failures even while the producer is idle. No new detached task model or private thread pool.
Compiler types, effects, liveness, native specialization, official network API and existing HTTP transport move together. The synchronous reference evaluator explicitly excludes async execution; no second network implementation is retained for differential tests.
## Structured Concurrency Design And Implementation Contract (2026-09-12)
This preceding phase is implemented through the compiler frontend, checked HIR, native backend and bounded runtime, with evidence in `implement.md`. It superseded the first-stage no-async model. The current N1 decision permits direct source-contract changes without a compatibility baseline.
### One Task Model
Dever has one concurrency model rather than separate async, coroutine and thread abstractions:
- an async function compiles to a stackless coroutine;
- `await` suspends that coroutine without occupying a worker thread;
- `run` schedules the coroutine as a child Task;
- the runtime may move transferable tasks across worker threads;
- `parallel` and `blocking` cross from async scheduling into bounded synchronous execution.
The program creates one Tokio runtime at an async entry point. A program whose reachable entry graph is entirely synchronous retains the current direct native entry and does not initialize Tokio. The runtime configuration owns async worker count, blocking capacity and task limits once; packages do not create private runtimes or pools.
### Source Surface
`async` prefixes a function clause. Every clause in one logical function must agree on this modifier:
```dever
async load_user(id: Id) (user: User) {
user = await(fetch_user(id))
}
```
An async call is context-only, not an ordinary storable future. It may appear directly under `await`, `run` or the matching compiler-owned task operation. This avoids adding a general `Awaitable<T>` value and preserves the current static-call model.
```dever
first = run(load_user(first_id))
second = run(load_user(second_id))
first_user = await(first)
second_user = await(second)
```
`await(async_call(...))` invokes and waits without creating a separately owned Task. `run(async_call(...))` starts first and returns an affine Task whose associated output signature remains in checked HIR. `await(task)` consumes it and yields the same zero, single or named multi-output shape as the function. Task operations consistently use the language's existing call appearance; no second prefix spelling is accepted.
Static handler syntax places `async` before `handler`:
```dever
async serve(
route: async handler(request: Request) (response: Response)
) () {
response = await(route(request))
}
```
`async` has one canonical declaration position immediately before the function name. Existing `pure` and `recover` contracts retain their post-signature positions. Parser, formatter, Markdown signatures and examples must change atomically; no postfix async spelling is accepted.
### Task Ownership
Task is a compiler-owned affine local type, not a new user-defined generic facility. It may be held only in a local slot of the async function that created it. It cannot be copied, compared, stored in an aggregate, passed as an ordinary value, returned, captured by a handler or left live at any clause exit.
Each async entry and spawned task has an internal runtime Scope; the compiler treats every async function as a lexical ownership scope. Directly awaited calls inherit the current Scope because runtime faults cannot be caught, while `run` and Group create a supervised child Scope. Every Task must be consumed exactly once by `await` or `stop`. On abnormal runtime exit, the root or child supervisor requests cancellation and waits for descendant and blocking-work cleanup before returning the failure. There is no detached task in the first release.
`stop` means cancel-and-wait, not fire-and-forget cancellation. To keep failure and output obligations explicit, the first release accepts only zero-output Task values. Result-bearing tasks must be awaited. Cancellation is cooperative at await/yield boundaries; it neither kills a running synchronous operation nor rolls back effects that already occurred.
### Dynamic Task Groups
`group(limit)` creates an affine Group for a dynamic number of zero-output async calls. It is needed for later connection/session ownership without allowing detached children:
```dever
workers = group(128)
run(workers, serve_connection(connection))
await(workers)
```
`run(group, async_call(...))` transfers the child into the Group and returns no Task. It may suspend while the positive capacity limit is full, providing backpressure before starting more work. Ungrouped `run(async_call(...))` is likewise subject to the owning function scope's task limit. `await(group)` closes admission, waits for all children and consumes the Group. `stop(group)` closes admission, requests cancellation, waits for every child and consumes the Group. Group cannot be copied, stored or escape its creating async function.
Only zero-output calls enter a Group, so the runtime cannot accumulate unbounded result values and expected failures cannot be silently discarded. A later result-collecting combinator must define ordering and memory limits separately.
### Thread Transfer And Execution Classes
The type model derives a `transferable` property recursively, alongside the existing comparison/move/transfer properties. Bool, numeric values, Text, Id and Bytes are transferable. Records, choices, nullable values, Lists and Maps are transferable only when every nested value is transferable. Each opaque resource is opted in only when its runtime representation and lifecycle are thread-safe; the current pull-based Stream is initially non-transferable.
Values live across an `await`, captured by `run`, or passed through channel/parallel boundaries must be transferable. Last-use analysis may move uniquely owned values into a task; otherwise ordinary value semantics require a copy. This rule is checked before native emission and is not delegated to a Rust compiler error.
Execution classes remain distinct:
- ordinary synchronous calls execute inline;
- `run` schedules an async call on the shared runtime;
- `parallel(sync_call(...))` accepts only a pure synchronous target with transferable inputs/outputs and runs it through bounded CPU execution;
- `blocking(sync_call(...))` accepts a synchronous target whose transitive effect summary contains a compiler-known blocking system boundary and uses bounded blocking capacity.
`parallel` and `blocking` are async-context operations in the first release: they suspend their caller until the synchronous call completes. Synchronous code continues to use `parallel_each`; it does not initialize Tokio solely to access these operators.
Existing synchronous `parallel_each` keeps its public behavior and joined scoped workers. Async List/Bytes lowering requires a named synchronous function with no transitive concurrency effect, then submits bounded batches directly to the shared Tokio blocking executor, so it neither blocks a Tokio scheduling worker nor creates a nested thread pool. Other collection operations inspect the complete handler effect and cannot hide blocking, concurrency or an unconstrained handler effect on a Tokio scheduling worker. The first release has no handler effect bounds, so an async function cannot invoke a synchronous handler parameter inline. Both parallel paths share worker-limit and batch-size validation. Synchronous Stream remains unavailable in async functions until async Stream is designed.
### Channels
`channel(Type, capacity)` is compiler-contextual syntax: its first operand is a TypeRef, not a runtime type value. It creates a bounded `Channel<Type>` resource. Capacity is required, positive and capped. This deliberately avoids adding generic function invocation syntax solely for one constructor.
Channel aliases share one state. `send(channel, value)` asynchronously waits for capacity; `receive(channel)` asynchronously waits for an item and returns `Item?`, where a value is an item and `null` means the Channel is closed. Because nullable types are idempotent, receiving from `Channel<Item?>` also returns `Item?`; native emission flattens the runtime's nested option. A runtime scheduler or Channel fault remains a located fatal execution failure rather than a domain value. `close(channel)` is idempotent and wakes blocked senders and receivers. No unbounded constructor exists. Values must be transferable. Channel itself cannot be a Map key or comparable value, and its close/alias behavior follows explicit resource semantics rather than ordinary value semantics.
### Effects, Failures And Static Handlers
Async is part of a function and handler signature. Sync callers cannot invoke async functions or async handlers. Async callers may invoke sync code directly and async code only through an explicit async operation. Collection handlers remain synchronous because collection operations do not await handler futures; async handlers are called only through `await` or `run`. Handler specialization keys include the async signature bit, and cycle detection continues across await/run/forwarding edges; async does not legalize recursion.
`await`, `run`, `stop`, group operations, channel waits, timers, `parallel` and `blocking` are effects. The existing transitive effect graph owns these facts, so the first release rejects `async` together with `pure`. Task boundaries do not discharge error-bearing outputs: awaiting restores the original output obligations, while Group and stop accept only zero-output work. Runtime scheduler failure is a located fatal execution failure; expected domain failures remain explicit source choice values.
### HIR And Native Boundaries
The syntax tree records the async modifier and contextual concurrency expressions. Checked HIR owns explicit `AwaitCall`, `RunCall`, `AwaitTask`, `StopTask`, Group and Channel operations. It never represents a generic user-visible Future. Function metadata and HandlerSignature carry the async bit; Task stores its concrete checked output fields.
Native emission generates concrete Rust futures and direct calls. Static handlers remain specialized direct targets; no boxed dynamic callback, generic runtime Value, reflection registry or garbage collector is introduced. The runtime owns scheduler construction, task/group/channel state, limits and cancellation primitives. Compiler checks own source legality, transferability, affine consumption and diagnostic locations.
Runtime cancellation separates a business future from its supervisor. Task/Group stop aborts the business future; the supervisor itself remains alive, drains its child Scope, and only then publishes completion to the parent Scope. Started `spawn_blocking` jobs register a sticky completion signal because Tokio cannot abort them safely. Drop paths request cancellation and detach only supervisor handles whose completion is still owned and awaited by the parent Scope; no Drop implementation calls `block_on`.
### Compatibility And Deferred Work
Existing synchronous source and `parallel_each` behavior stay valid. `LANGUAGE.md`, Markdown contracts and API snapshots are updated with the implementation. The `run` source operator is distinct from the CLI command by grammar context.
The task-foundation phase deferred networking; N1 above resumes AsyncStream and TCP on these primitives. The HTTP engine and WebSocket/SSE remain later work. Detached tasks, arbitrary shared mutation, locks, user thread creation, unbounded channels and forceful cancellation remain out of scope.
## Markdown Source Follow-through (2026-09-09)
`.dever.md` is an additional source container alongside `.dever`. Top-level literal `dever` and `typescript dever` fences contain ordinary Dever declarations; the latter supplies a TypeScript highlighting hint while retaining an explicit Dever marker. A dedicated Markdown contract parser maps one H1 to the package header and one H2 to each type or logical function group before formatting. After shared signature resolution, semantic validation compares package/API/use metadata, type fields/variants and function inputs/outputs with syntax and HIR, retaining both documentation and declaration spans in diagnostics. Shared fence extraction keeps original spans for parsing, diagnostics and per-block formatting. `.dever` and language/runtime semantics are unchanged. The authoritative source-format contract is `LANGUAGE.md` and `.trellis/spec/backend/toolchain-and-library.md`; earlier `.dever`-only and unconstrained-heading limits below are superseded.
## Performance Implementation Boundary (2026-09-08)
- Compiler analysis worker owns types.rs, check.rs integration and contracts dependency scheduling: memoized type properties and monotone reverse-dependency work queues, retaining handler substitution and proof limits. Existing graph traversal is reused.
- Cache worker owns source/parse caching, native/build.rs and its cache helper plus CLI cache integration if necessary. Cache identities include effective source, compiler/toolchain/runtime and options; checked API baselines stay outside cached native execution. No new external dependency or source format.
- Main owns native liveness/emission, runtime collections, JSON/HTTP source algorithms, performance fixtures and integration. Record updates preserve value aliases and evaluation order; consuming iteration retains resource scope; direct split consumption avoids eager character lists; HTTP parsing tracks incremental boundaries and framing in Dever.
- Root tests own semantic regressions and bounded before/after measurements; no services/full suite/commit. Workers are not alone and must preserve unrelated edits. Shared check.rs edits require coordination with the analysis owner. Independent final review checks values, failure order, cache invalidation and protocol bounds.
## Follow-through Implementation Design (2026-09-07)
This section owns the current follow-through; older extension boundaries below describe completed milestones.
- Formatter: add an AST writer using existing spans, preserved comments and precedence. Preserve record initializer expression order. CLI owns filesystem mutation and prepares every result before any rewrite. Expose syntax-only formatting separately from semantic checking.
- Reference evaluator: add an opt-in `reference` compiler feature, not a runtime dependency. Consume checked HIR, normalized domains and static handler bindings; reuse runtime number/resource primitives. Frames hold value locals and preserve copy semantics. Capture stdout and named results for differential tests. Do not add an evaluator production CLI path.
- Standard library: extend the single intrinsic/checker/native catalog for atomic representation/OS operations only. Public result choices live in `library/`; share runtime implementations with the evaluator. Text indexes are Unicode scalar positions; Bytes indexes remain byte offsets. Number parse failures are nullable results, never a silent zero; finite Decimal rules remain unchanged.
- Optimization: analyze actual read order and writes per clause, retaining outputs and assignment hosts. Move dead values, preserve live aliases and short-circuit paths; transfer reduction state between iterations. Verify value semantics before performance comparison.
- Parallel ownership: formatter worker owns formatter modules, formatting CLI and root format tests; reference worker owns evaluator modules and root differential tests. Main agent owns libraries, primitive integration, native optimization, task documents and integration. Coordinate shared module registration edits.
- Verification uses offline focused targets and test-owned files/processes. Performance checks are bounded local computation, exclude compiler startup and use matching numeric/collection wrappers. Existing user sources are never formatter test fixtures.
- Production native compilation and matching performance baselines share one optimization-argument owner (`opt-level=3`, `codegen-units=1`). The single generated source compiles as one unit to keep generic collection inlining independent of source size; compilation time is measured separately from hot paths.
- Status: Core foundations and foundation extension implemented and verified on 2026-09-07
- Date: 2026-09-04
- Product source of truth: `prd.md`
## Completed Foundation Boundary (Historical)
Extend the implemented core with the approved sequence and resource foundation: `reduce`, `reduce_until`, Bytes traversal, `Stream<Item>`, restricted static handler inputs, and file/TCP stream wrappers. Existing clause-only branching, linear bodies and the ban on source loops and user recursion remain permanent. Compiler distribution, cross-platform work and full HTTP/JSON/CRUD packages remain deferred.
Extend the existing checker and HIR in place. Represent user types nominally, resolve every function signature before bodies, prove clause coverage over symbolic input domains, and emit concrete native types/functions. Keep source expression order, including record construction; formatting must not reorder effectful field expressions. Use one runtime implementation for numeric operations, ordered collections and resource primitives. Bundled `.dever` sources define official wrappers and result types through the same frontend as user packages; only fixed primitive signatures have native implementations.
## Foundation Extension Design
This section records the completed foundation-extension design. The Follow-through Implementation Design above owns subsequent additions; later numbered sections retain the original Core roadmap.
### Sequence Contract And Reuse
The checker extends the existing collection-operation owner rather than adding parallel validators. One internal sequence classifier returns the element type and source kind for `List<Item>`, Bytes and `Stream<Item>`; `each`, `reduce` and `reduce_until` reuse it for handler checking and lowering.
- `reduce(handler, sequence, initial_state)` requires a two-input handler `(Item, State) -> State` and returns `State`.
- `reduce_until(handler, sequence, initial_state)` requires `(Item, State) -> (State, stop: Bool)`, processes the current item before stopping, and returns only `State`.
- List and Bytes use finite direct loops. Bytes yields Int values from 0 through 255 without allocating `List<Int>`.
- Stream consumption calls its pull boundary only when the next item is needed. `reduce_until` leaves the next item unread; `reduce` consumes through natural exhaustion.
- `each(Stream)` accepts only a zero-output handler. List/Bytes retain their existing zero-output action and one-output mapping behavior. `filter`, `find` and `sum` remain List-only.
- Core `close(stream)` is a zero-output, idempotent Stream operation. It shares the same sequence operation registry but is checked as resource termination, not traversal.
Handler signature diagnostics, sequence classification and output-shape validation each have one semantic owner. Native lowering receives already classified sequence and handler facts; it does not repeat source-level type decisions.
### Static Handler Representation
`handler(inputs) (named_outputs)` is contextual syntax allowed only after a function input colon. The syntax tree represents value patterns and handler parameter declarations as separate input kinds; it does not disguise a handler as an ordinary `TypeRef`. The semantic model likewise keeps `HandlerSignature` separate from runtime `Type`, because a handler is a compile-time binding rather than a value.
`HandlerSignature` stores positional input types and ordered named output fields. Input names do not participate in compatibility; output name, position and type all do. The checker accepts only a matching named function or a handler parameter already in scope. Handler references cannot enter local slots, aggregates, outputs, comparisons or clause domains.
HIR uses explicit call and argument forms:
```text
HandlerTarget = NamedFunction(FunctionId) | Parameter(HandlerParameterId)
CallTarget = NamedFunction(FunctionId) | Handler(HandlerTarget)
CallArgument = Value(Expression) | Handler(HandlerTarget)
```
Collection/sequence HIR stores the same `HandlerTarget`; it no longer carries a collection-specific optional function ID. This is the reusable representation for direct handler calls, sequence operations and multi-level forwarding.
Native compilation treats a function with handler inputs as a template. A specialization key is the function ID plus its ordered concrete named-function bindings. Forwarding substitutes bindings until every handler call has a concrete target, then emits an ordinary direct call. The specialization graph rejects a repeated active key as direct or indirect recursion before any private Rust source is compiled. Generated handler plumbing contains no function pointers, closures, virtual dispatch or reflective registry; the opaque Stream producer described below is the only dynamic call boundary.
### Stream Representation And Lifecycle
The semantic type model adds `Stream(Box<Type>)`. It is the only new compiler-provided generic type; it is a valid parameter, local, field and choice payload but is not comparable and cannot be a Map key. Copying it preserves opaque resource semantics: every alias shares one cursor, exhausted flag and explicit-close flag.
`dever-runtime` adds one generic pull-stream owner backed by shared synchronized state. Its producer returns either one item or natural end. The only dynamic producer abstraction is inside this opaque I/O boundary, where the PRD explicitly permits it; Dever handlers remain statically specialized. Pulling and closing are serialized, with no prefetch, worker thread or async task.
The terminal rules are:
1. Normal EOF marks the Stream exhausted and releases its producer without yielding an element.
2. A terminal file/socket read error is converted once to `ReadEvent.Failed`, then the Stream becomes exhausted.
3. A terminal accept error is converted once to `ConnectResult.Failed`, then the Stream becomes exhausted.
4. `close(stream)` marks all aliases closed, drops the producer and is a no-op when repeated; later traversal sees ordinary exhaustion.
5. Closing a separately held File, Socket or Listener invalidates the shared underlying resource. The dependent Stream then yields its one typed Failed item and ends.
6. Closing only the Stream drops its source alias but does not close a separately held source handle. Native destruction remains the final cleanup fallback.
`reduce_until` never pre-pulls, so its stop item is consumed and the following item remains available to every Stream alias. Concurrent consumption is outside the language contract; the synchronized state protects resource integrity but does not introduce concurrency semantics.
### Official Package And Runtime Boundary
The public stream API remains ordinary bundled `.dever` source:
```dever
type ReadEvent {
Chunk(bytes: Bytes)
Failed(message: Text)
}
type ReadStreamResult {
Streaming(stream: Stream<ReadEvent>)
Failed(message: Text)
}
```
`dever.io.chunks(file, limit)` and `dever.net.chunks(socket, limit)` return `dever.io.ReadStreamResult`; `dever.net.connections(listener)` returns `Stream<dever.net.ConnectResult>`. Existing one-shot `read` remains unchanged. Invalid limits and already-closed sources fail during stream creation; later source failures become one terminal stream item.
The ownership flow remains:
```text
bundled .dever function and result choices
-> fixed dever.system intrinsic signature
-> generated typed adapter
-> dever-runtime Stream/resource primitive
```
The generated adapter maps runtime pull outcomes (item, natural end or error) into the source-defined concrete choices. Runtime code therefore owns pulling, locking and OS resources, while `.dever` owns public names and recoverable result shapes. No HTTP parser, route table, JSON codec or user model enters the runtime.
### Implementation And Rollback Boundaries
Implementation proceeds through coherent compiler boundaries: syntax/semantic handler declarations; shared handler and sequence HIR; List/Bytes reduce lowering; runtime Stream; file/TCP adapters; then native specialization and end-to-end fixtures. Each boundary must compile and pass its focused tests before the next. Unsupported handler or Stream positions remain compile diagnostics; there is no compatibility fallback to runtime values or eager Stream collection.
The existing one-shot file/TCP APIs, List operations and generated-binary boundary remain compatible. No stored data or public artifact format needs migration. Reverting the extension removes its syntax, standard source declarations, HIR forms and runtime module together while leaving the completed core foundations intact.
## Historical Hello Milestone (2026-09-06)
The completed deliverable is a native `user.dever` whose `main() ()` prints `hello`. A future user API may start with in-memory fixtures; database selection is no longer a prerequisite. Existing parser and surface syntax are preserved.
The implemented Text/action subset covers package and function resolution, Text parameters and local assignment, zero-output calls, checked HIR, native Rust emission and local check/build/run. It checks all source declarations and rejects unsupported constructs, duplicate symbols, invalid exposures, private cross-package calls, source/package mismatches and cycles. This is not full Core checking.
The standard `dever.io.println(Text)` signature resolves to a typed intrinsic. Its implementation belongs to a small dependency-free `dever-runtime`; the native backend embeds that same runtime source in disposable generated Rust. Text uses owned Rust Strings to preserve Dever value semantics. User function and local identifiers lower to numeric IDs, not raw Rust identifiers. Output failures propagate internally to a nonzero process exit with the Dever call location; no new source exception syntax is introduced.
The CLI owns argument parsing and process execution; the core owns checked programs and native compilation. Invoke rustc directly without a shell, using RUSTC or PATH. Compile into an exclusively created temporary directory, retain it through execution and remove owned artifacts afterwards. Explicit build output must not overwrite an existing file. No external dependency, package distribution, interpreter or service is needed.
The following sections describe the wider roadmap. Implemented foundations are recorded in the Active Core Checklist and Core Foundation Evidence in `implement.md`; formatter, reference evaluator, performance gates and application packages are not part of this delivery.
## Deferred Application Architecture
This is a later HTTP/JSON/CRUD direction, not part of the approved foundation extension. It must be reconsidered against the implemented Stream and static-handler contracts before application work begins.
The native path remains `.dever -> parser -> resolver/checker -> typed HIR -> private Rust -> native executable`. HTTP requests must execute the compiled business functions, not an interpreter or a handwritten Rust user service.
| Owner | Required responsibility |
| --- | --- |
| `.dever` application packages | User/input types, validation, CRUD orchestration, parameterized queries, routes and business error mapping |
| `dever-core` | Symbol/type/clause/initialization checks, standard signatures, I/O effects, typed handler checks and native code generation |
| `dever-runtime` | Mature HTTP and JSON implementations, the selected database driver, connection/resource lifecycle and per-request fault boundary |
| CLI | Configuration inputs, `check`, native `build/run` and diagnostics |
Necessary semantic adaptations, with no new surface keywords:
- Allow named function references in fixed HTTP registration positions, extending the existing collection-handler mechanism. They remain statically resolved, not general first-class functions.
- Add opaque standard resource types for database pools and server resources. Ordinary records/collections keep value semantics; resource handles explicitly identify external state and cannot be serialized or mutated as records.
- Give standard I/O operations fixed effect metadata and propagate effects through the call graph. Preserve statement/expression evaluation order; internal async lowering does not introduce `async` or `await` into `.dever`.
- Reuse choice results for recoverable HTTP/JSON/database failures. Program faults are isolated to the failing request and retain Dever source locations.
- Bundle only the required standard HTTP/JSON/database contracts with the toolchain. External package distribution remains deferred.
The current `TypeRef` AST can represent parameterized standard types, but `parser.rs::type_ref_inner` presently recognizes only List/Map/MapEntry generic spellings. Any required qualified standard generic type must be supported through the standard-type contract, without a User-specific parser branch. User-defined generics remain deferred.
Persistence selection is pending. The initial design candidate uses parameterized SQL and static type-driven row decoding, with schema/query definitions owned by one application persistence package. Do not build an ORM or multi-database framework speculatively. JSON and row decoding should share resolved type facts, rather than duplicate user field definitions in Rust.
Verification must show that changing `.dever` handlers, types or routes changes the compiled API. A second small schema/route fixture should prove that compiler/runtime code contains no user-API special case. Report actual CRUD throughput, latency percentiles, errors and memory against equivalent native code under identical database/durability settings; finalize the performance budget after storage and deployment scope are selected.
## 1. Architecture Decision
Core 0.1 is a textual language toolchain, not a semantic-graph workbench. It has one checked frontend and two deliberately different consumers of the same typed HIR:
```text
.dever source root
|
v
source loader -> lexer -> parser -> syntax tree
| |
| +-> formatter
v
package/name resolver
v
type + clause + flow checks
v
typed HIR
/ \
v v
reference evaluator native specialization
v
private Rust emission
v
rustc / LLVM -> binary
dever CLI -> load / check / format / build / run
```
Dever text in `.dever` files or program blocks of `.dever.md` files is the program source. The syntax tree and typed HIR are in-memory compiler representations; they are not editable JSON formats or public compatibility contracts. The evaluator defines a simple executable reference for conformance tests. Production `build` and `run` use ahead-of-time native compilation and never route through the evaluator.
The bootstrap compiler is implemented in Rust because rustc provides a mature LLVM path. The repository starts without an existing workspace or implementation. This is an implementation choice, not a surface-language dependency: diagnostics, type identities and runtime semantics are owned by Dever, and generated Rust is a disposable private build artifact.
## 2. Scope Boundaries
The implementation contains three production crates:
```text
crates/
dever-core/
src/
source.rs
diagnostic.rs
token.rs
lexer.rs
syntax.rs
parser.rs
format.rs
package.rs
resolve.rs
types.rs
clauses.rs
check.rs
hir.rs
value.rs
stdlib.rs
eval.rs
native.rs
lib.rs
dever-runtime/
src/
number.rs
collections.rs
fault.rs
lib.rs
dever-cli/
src/
main.rs
```
Compiler phases remain in one `dever-core` library rather than separate syntax, semantic and IR crates. They share source spans and language contracts and will evolve together during 0.1. `dever-runtime` is the only justified split because generated native programs consume it without depending on parser, checker, evaluator or CLI code. It contains only language-owned numeric, ordered-collection and runtime-fault primitives; it does not contain a dynamic interpreter or plugin layer.
`dever-cli` owns argument parsing, filesystem invocation, exit codes and rendering. It calls `dever-core` APIs and contains no parser, type or evaluation rules.
Permanent integration tests stay under repository-root `test/`, matching project policy. Plain-source examples live under `examples/dever/`; Markdown examples live under `examples/markdown/`. Both may contain small fixture data when needed.
## 3. Reuse And Replacement
### Reuse
- Pinned dependency discipline in the new Rust workspace.
- Small modules and explicit data structures.
- The idea that every diagnostic maps back to a source location.
- Root-level integration tests and deterministic fixtures.
### Replace
- `SemanticGraph`, canonical JSON loading and semantic hashing.
- Refiner candidates, approval, scrap and quarantine workflows.
- Dever IR designed around the old graph.
- Old target-facing Rust and React source generators and their public backend contracts.
- Workbench presentation and temperature-alarm example.
- Old child-task plan and old repository rules that call the semantic graph trusted source.
No adapter translates old JSON into new `.dever`. Such a bridge would preserve the rejected architecture, expand the change surface and create two sources of truth.
The replacement list describes rejected concepts, not files present at kickoff. No old implementation exists in the working tree or HEAD. Initialize `dever-core` and the root test crate first; add the runtime and CLI when their real consumers exist, without empty crates or partially working public commands.
## 4. Source Model
### 4.1 Source Files And Spans
`SourceFile` owns:
- canonical source-root-relative path;
- UTF-8 text;
- precomputed line-start offsets.
`Span` contains a file identifier and half-open byte range. Line and column are calculated only when rendering a diagnostic, keeping compiler nodes small. Every token, syntax node, resolved reference and HIR instruction retains a span.
The loader:
1. walks the supplied source root for `.dever` files;
2. sorts paths lexically before parsing;
3. rejects invalid UTF-8 and duplicate canonical paths;
4. derives the expected package name from the path;
5. returns all diagnostics in deterministic file/span order.
### 4.2 Tokens And Trivia
The lexer emits semantic tokens plus newline and `#` comment trivia. Newlines matter for separating linear statements; whitespace otherwise does not. Comments attach to the following syntax item, or to the preceding item when trailing on the same line, so the formatter can preserve them. `//` is always one integer-division token and is never considered comment syntax.
Numeric text remains unconverted in tokens. Parsing a very large literal directly into i64 or f64 would lose the ability to issue precise range and rounding diagnostics; conversion happens during checking with the expected type. An inexact Decimal literal is rejected rather than silently rounded, while checked Decimal arithmetic may produce the specified rounded result.
### 4.3 Syntax Tree
The parser produces a lossless-enough typed AST:
- `PackageDecl` and exposed names;
- record/choice `TypeDecl`;
- `FunctionClause`;
- input patterns and output declarations;
- assignment/call statements;
- value construction, call, field, literal, unary and binary expressions.
The parser does not resolve names or infer types. It supports bounded recovery at a newline, closing delimiter or next top-level declaration and returns a partial tree plus diagnostics. Later phases run only for packages without syntax errors.
## 5. Formatter
The formatter consumes the syntax tree and attached comments, never performs string replacements over raw source, and owns one layout:
- two-space indentation;
- multiline package exposure and type members, one item per line;
- commas only where grammar requires positional separation;
- no semicolons;
- stable spaces around operators;
- record fields in declaration order after successful resolution;
- parentheses inserted only when required by precedence;
- one blank line between top-level definitions and no trailing whitespace.
`format(format(source)) == format(source)` is an acceptance invariant. Invalid files are not rewritten in Core 0.1; the CLI reports diagnostics and leaves them untouched.
## 6. Resolution And Symbols
Resolution runs in three passes:
1. Register every package, type, function name and function arity.
2. Register record fields, choice variants and full function signatures.
3. Resolve bodies, patterns, exposed names and cross-package references.
Symbols use compiler-assigned integer IDs internally. User-visible identity remains the package-qualified source name, not an opaque semantic graph ID.
The resolver enforces:
- one file per package and exact path/package match;
- local source plus the fixed compiler-owned standard catalog as the only Core 0.1 package origins;
- reserved core single-segment names and the `dever.*` namespace, which local source cannot declare;
- unique names inside a package;
- one function signature per name/arity;
- contiguous clauses for the same signature;
- public access only through `exposes`;
- full qualification across packages;
- acyclic package, type and function-call graphs.
Resolution never performs network access or searches outside the supplied source root in Core 0.1. An unavailable official or third-party package is an ordinary unknown-package diagnostic. Future lockfile-pinned components will be registered as an additional origin before these same resolution passes; this does not require imports, aliases or version text in `.dever` calls.
### 6.1 Distribution Compatibility Contract
External distribution is deferred to 0.2, but Core 0.1 must not close off its confirmed identity model:
- a stable component ID such as `shemic.contact` owns the root of package and type identity;
- registry, Git and local paths are replaceable source transports and never appear in `.dever` references;
- Git additions require an explicit tag or revision, then lock the full commit and normalized SHA-256 content hash;
- CLI-maintained `dever.mod` records direct dependency intent while `dever.lock` records the complete exact graph;
- verified sources live in a global read-only content-addressed store shared across projects;
- only explicit `add`, `update` and `fetch` operations may access the network;
- every package supplied by a component must remain under that component ID, and duplicate providers are errors.
No 0.2 parser, Git client, resolver, store or command is implemented speculatively in Core 0.1. The current resolver needs only reserved namespaces and origin-neutral internal symbol IDs so adding a locked package origin later does not change source names.
The ordinary call graph is computed after all bodies resolve. Sequence handlers and handler-input forwarding are explicit HIR edges; native specialization checks the fully substituted instance graph, so indirect recursion cannot hide behind `each`, `reduce`, `reduce_until` or a handler parameter.
## 7. Type System
`TypeId` points to interned compiler types:
- the six scalar types;
- user record and choice types;
- `Nullable(TypeId)`;
- `List(TypeId)`;
- `Map(KeyTypeId, ValueTypeId)`;
- compiler-owned `MapEntry(KeyTypeId, ValueTypeId)`;
- compiler-owned `Stream(ItemTypeId)` opaque resources;
- ephemeral named multi-output result types.
There are no user-defined generic parameters in 0.1. List, Map, MapEntry, Stream and sequence functions are compiler-provided generic contracts. Static `HandlerSignature` is a separate function-parameter contract and is not a runtime `Type`.
The type interner owns idempotent nullable construction for standard generic returns; explicit doubled `?` syntax remains invalid. Structural equality follows the PRD, including ordered Map pairs and IEEE Float comparisons inside aggregates. Checking, reference evaluation and native lowering must share these contracts.
Checking is expression-directed:
- function inputs and outputs provide declared boundary types;
- assignment provides an expected type once the local is initialized;
- an uninitialized local takes the inferred type of its first complete assignment;
- empty List/Map and context-sensitive numeric literals require an expected type;
- the only implicit numeric conversion is Int to Decimal;
- function output, static handler and sequence-operation contracts are checked before lowering.
Linear bodies make definite assignment a forward pass. The checker tracks each local as uninitialized or initialized with one stable type. Record field writes require an initialized record and preserve its type.
## 8. Clause Analysis
Clause-based conditional dispatch is a permanent source-language constraint, not a Core 0.1 deferral. Native decision trees are its internal implementation and do not introduce another source-level branching form.
Each same-name/same-arity group becomes one `FunctionDef` with multiple clauses. Analysis separates base input types from clause patterns.
Patterns normalize into symbolic sets:
- Bool and no-payload choice types become finite atoms.
- Nullable adds a distinct null atom.
- Payload choices keep the variant atom and bind payload fields after selection.
- Text/numeric constants become singleton sets.
- Int and Decimal comparisons become normalized intervals with open or closed endpoints.
- `other` is computed as the complement of all explicit patterns in that argument position and group.
A multi-input clause is a product of its per-input sets. The analyzer incrementally subtracts each product from the function input universe:
- intersection with prior coverage means overlap;
- an empty clause domain means unreachable;
- remaining universe after all clauses means non-exhaustive.
The algorithm works on finite atoms and interval boundaries, not enumerated i64 or Decimal values. Float range patterns are rejected before this phase. The source order is never part of selection.
At runtime, HIR stores a compiler-chosen decision tree built from the normalized domains. It can optimize tests while preserving order-independent semantics; no runtime “first matching clause” rule exists.
## 9. HIR And Reference Evaluation
The typed HIR contains only resolved IDs and typed operations:
- constants and value construction;
- local/field load and store;
- checked numeric/text/boolean operations;
- direct function and statically bound handler calls;
- standard collection/sequence calls with explicit handler targets;
- clause decision nodes;
- named outputs.
The evaluator uses an explicit call frame with owned locals and a source span for every operation. There is no global mutable program state.
Runtime values are:
```text
Bool
Int(i64)
Decimal(DecimalValue)
Float(f64)
Text
Id
Record
Choice
Nullable
List
Map
Stream
MultiOutput
```
Aggregate values use shared immutable storage with copy-on-write mutation. This implements source-level value semantics without eagerly cloning every nested List, Map or record. The optimization is internal and must pass alias-observation tests.
Map storage uses an insertion-ordered map. In Rust, `indexmap::IndexMap` is the planned implementation; replacement keeps the same wrapper contract. Removal must use order-preserving removal, and overwrite must retain the existing position.
Evaluation returns either named outputs or a `RuntimeFault`. A fault carries category, message and originating `Span` and unwinds only to the current CLI execution boundary. There is no language-level catch path.
The evaluator is intentionally not optimized as the production runtime. Its job is to make language semantics easy to inspect, to evaluate focused tests and to serve as a differential oracle for native output.
## 10. Native AOT Compilation
The native path consumes only fully checked typed HIR. It performs whole-program specialization from the selected zero-input entry function and emits one transient Rust crate:
```text
reachable typed HIR
|
v
specialize concrete List/Map/Stream and handler-binding combinations
|
v
emit static Rust records, enums, functions and loops
|
v
cargo build --release -> native executable
```
There is no generic `Value` in generated function signatures. Dever records lower to concrete Rust structs, choices to concrete enums, nullable values to an internal option representation, and calls to direct functions. Clause domains lower to compiler-selected decision trees. Because sequence handlers are concrete after specialization, `each`, `reduce`, `reduce_until`, `filter`, `find` and `sum` lower to direct calls and loops. List/Bytes loops have no virtual dispatch; only opaque Stream pulling may use the internal producer abstraction at the I/O boundary.
Source-level value semantics do not require eager deep copies. A last-use/liveness pass may move a value when the source is dead; when both aliases remain live, generated aggregates use a language-owned clone or copy-on-write representation. This is invisible to `.dever` and is verified against the evaluator. Locals and non-escaping records remain eligible for normal Rust/LLVM stack and register optimization. The AOT runtime has no garbage collector and no reflection registry.
Checked Int and Decimal behavior is emitted explicitly, so the release profile cannot remove language-required overflow or invalid-operation faults. Runtime helpers carry compact source-location IDs; the executable maps a fault back to the original `.dever` path and span. Internal Rust compiler errors are toolchain failures, not Dever diagnostics, and generated Rust names must never appear as user symbols.
The transient crate depends only on `dever-runtime`. Its generated source and Cargo metadata live in a compiler-managed build directory, are not emitted by a public command and may be deleted at any time. The optimized profile starts with `opt-level = 3`, thin LTO and one codegen unit; benchmark evidence may change those internal settings without changing the language.
The first backend deliberately uses stable Rust as a bootstrap bridge to LLVM. A later direct LLVM or Cranelift backend can consume the same checked HIR and runtime contracts without changing `.dever` source. No `dever-backend-rust` public crate or target-specific surface syntax is introduced.
Self-hosting is a staged replacement, not a Core 0.1 syntax promise:
1. The Rust bootstrap compiler establishes parser, checker, HIR, native semantics and conformance fixtures.
2. Later language phases add the explicit file/process/memory capabilities required to write compilers.
3. The frontend and standard library are reimplemented in `.dever` against the same fixtures and HIR contracts.
4. A stage-1 compiler builds stage 2; stage 2 builds stage 3; stage-2/stage-3 behavior and reproducible artifacts are compared before the Rust frontend becomes only a seed.
## 11. Numeric Implementation
`dever-runtime::number` is the only module allowed to depend on the decimal implementation. It exposes a language-owned `DecimalValue` and operations returning:
```text
Result<DecimalValue, NumericFault>
```
Core 0.1 uses the Rust `dec` crate's `Decimal128` plus a reusable Context configured for 34 digits and round-half-even. Its status flags are cleared and checked around every operation: inexact/rounded is an accepted rounded result, while overflow, underflow, division by zero and invalid operation become Dever runtime faults. Decimal NaN/Infinity never enter a normal Dever `Value`. Compile-time constant evaluation and the reference evaluator call this same runtime wrapper, so native and reference behavior cannot acquire separate Decimal implementations.
This boundary is necessary because `dec` uses libdecnumber internally and exposes more IEEE decimal states than the language permits. Conformance tests, not the dependency's default operators, define Dever behavior.
Int operations use Rust checked arithmetic. Division and remainder explicitly handle zero and `i64::MIN / -1`. Int-to-Decimal conversion is exact.
Float uses Rust f64 operations without fast-math transformations. List summation is left-to-right. NaN/Infinity classification is exposed through `math` functions.
## 12. Standard Library Boundary
Core names such as `each`, `reduce`, `reduce_until`, `close` and `get` are registered from a static table containing:
- accepted arities;
- generic input/output constraints;
- whether an argument is a sequence, value or statically bound handler;
- evaluator function.
Qualified standard packages (`text`, `decimal`, `float`, `math`) use the same table with qualified names. This is a fixed compiler-owned catalog, not a plugin registry.
Every implemented standard operation has one checking contract and one native lowering rule. Sequence source classification and handler validation are shared so List, Bytes and Stream forms cannot drift. The deferred evaluator must later consume these same checked contracts rather than define a second interpretation.
## 13. CLI Contract
`dever-cli` builds one `dever` binary:
```text
dever check <source-root>
dever fmt <source-root>
dever build <source-root> <package.function> --output <path>
dever run <source-root> <package.function>
```
- `check` loads every package and runs all static phases.
- `fmt` prepares every file before writing; if any source file is invalid, no file is modified. Record-field reordering requires successful resolution.
- `build` checks the complete source root, specializes one exposed zero-input function as the entry point and writes an optimized native executable to the requested path.
- `run` uses the same native compilation path in a compiler-managed directory and then invokes the executable; it does not run the reference evaluator.
- Outputs print in declaration order using deterministic Dever value formatting.
- Diagnostics go to stderr; program output goes to stdout.
- Exit 0 means success, exit 1 means source/runtime diagnostics, exit 2 means invalid CLI usage.
The CLI is deliberately not a project/package manager. Source-root selection avoids introducing a manifest before dependency versions or executable targets are designed.
Core 0.1 therefore has no `add`, `update`, registry, signature or component-cache commands. Those are a 0.2 distribution concern; `check`, `build` and `run` remain offline and deterministic.
### 13.1 Command Identity And Coexistence
The Cargo binary and canonical product command remain `dever`. Command examples, help semantics and the public CLI contract use that name.
During Core 0.1 coexistence with the existing Dever framework CLI, a machine may expose this compiler on `PATH` as `deverc`. That entry is a symlink or launcher for the same compiler binary, not a second CLI implementation. It must not replace, shadow or modify the existing `dever` executable.
Repository builds and automated tests invoke the built binary directly and never mutate global executables or shell configuration. Installing, updating or removing the temporary `deverc` entry is an explicitly authorized machine-level operation. Moving the compiler onto the global `dever` name requires a separate migration plan.
## 14. Diagnostics
All compile diagnostics use one structure:
```text
severity
code
message
primary span
zero or more labeled related spans
optional help
```
Examples include both conflicting clause spans, declaration and bad-reference spans, or first initialization and later type-changing assignment. Rendering uses `path:line:column` and a source excerpt. Messages describe the violated Dever rule and never expose Rust type names, crate names or implementation stack traces.
Runtime faults use the same renderer with a runtime category. The CLI may include a Dever call trace, but the first diagnostic always points to the failing `.dever` operation.
## 15. Migration And Repository Cleanup
Implementation starts from the documentation-only repository verified on 2026-09-05:
1. Initialize `dever-core` and `dever-tests`; add `dever-runtime` and `dever-cli` at their implementation stages.
2. Implement `dever-core` around textual language phases.
3. Do not recreate old Refiner, IR, Workbench, generated backend or semantic JSON architecture.
4. Preserve the existing deletion of superseded planning documents; no old child links exist.
5. Update repository `AGENTS.md` and Trellis package specs so future work treats `.dever` as the trusted source.
6. Add the Core 0.1 examples and tests.
No database, persisted user data or old implementation exists, so there is no migration. Git history retains the old planning documents.
## 16. Verification Strategy
Tests are organized by behavior rather than compiler module:
```text
test/dever-tests/tests/
syntax_and_format.rs
package_resolution.rs
type_checking.rs
clause_analysis.rs
value_semantics.rs
collections.rs
numbers.rs
failures.rs
native.rs
differential.rs
performance.rs
cli.rs
```
Fixtures include valid programs plus one focused invalid case per diagnostic. Tests assert diagnostic code and relevant spans, not full prose, except dedicated rendering tests.
The highest-risk property tests are:
- parse/format/parse semantic equivalence and formatter idempotence;
- clause order permutation does not change coverage or result;
- Int division/remainder identity across boundary samples;
- Decimal conformance vectors for precision, rounding and fault flags;
- copy-on-write values do not expose alias mutation;
- Map overwrite/remove/reinsert order;
- Float sum remains left-to-right.
- reference evaluator and native executable produce identical outputs/fault categories for the conformance corpus;
- generated executables do not link the evaluator or perform per-element dynamic dispatch;
- representative native hot paths stay within the PRD performance budget against equivalent safe Rust baselines.
The foundation extension adds focused cases to the existing `syntax_and_format`, `core_semantics`, `core_native`, `runtime_foundations` and `hello_native` targets. They cover handler syntax and forbidden positions, signature matching and forwarding, specialization-cycle rejection, List/Bytes reduce behavior, Stream alias/EOF/stop/close/error behavior, official package compilation, and actual CLI check/run/build of a chunked state parser. File fixtures use test-owned temporary directories; TCP fixtures use only loopback port 0, bounded data and explicit timeouts.
No existing service, browser, npm build, full workspace test or external integration environment is involved in this extension.
## 17. Key Trade-offs
- One compiler crate is less independently reusable than many compiler crates, but avoids unstable public boundaries during the first grammar; the native runtime is separate only because generated programs are a real independent consumer.
- No recursion/effects limits programs to finite pure computation, but makes the first language deterministic and prevents hidden control flow.
- Full clause checking is more compiler work than ordered pattern matching, but it directly enforces the user's requirement that AI-written code cannot hide behavior in ordering or nested branches.
- A decimal dependency is safer than hand-written decimal128 arithmetic; the wrapper and conformance suite prevent it from defining public language behavior.
- A small fixed standard catalog is less extensible than plugins, but keeps the first implementation inspectable and removes speculative architecture.
- Rust emission gets optimized native code much earlier than implementing an LLVM backend, while typed HIR, differential tests and a private artifact boundary prevent Rust from becoming the language definition.
- Keeping a reference evaluator adds implementation work, but it localizes semantic debugging and gives native code generation a deterministic oracle; it is excluded from production binaries.
- Separating component identity from Git/registry location adds two CLI-managed metadata files in 0.2, but prevents repository moves from becoming source-breaking type renames and enables one verified global source store.
# Compiler Contract Extension (2026-09-08)
Extend existing AST fields and clause modifiers, normalize field bounds with the same interval owner as patterns, then analyze checked HIR before publishing Program. Keep private checks in field access/construction owners. Function/package metadata feeds effect/dependency checks and deterministic API snapshots. Failure obligations follow values across aggregates and calls, with explicit source recovery declarations as the only normal-value discharge boundary. Static handlers share existing specialization bindings. Facts must be conservative on mutation and unknown operations; no runtime interpreter, speculative fallback, new dependencies or source keyword for every domain.
Independent syntax/formatter work owns syntax.rs/parser.rs/format.rs; effect/API work owns contracts/effects.rs/api.rs and CLI; main owns semantic types, HIR integration, fact/failure analysis, standard-source migration, tests and docs. All workers preserve shared changes. Core check is the single enforcement entry used by CLI and native execution.
# AI-Oriented Source Architecture Design (2026-09-19)
Source classification is one compiler-owned value derived from the relative source path. It records main or component/domain/role/topic identity and is reused by parsing, Model registration, symbol resolution, API route construction and architecture diagnostics. Those layers must not rediscover roles with independent string searches.
Every physical source still has a unique internal owner name so diagnostics, HIR ownership and native symbol generation remain deterministic. App declarations additionally receive one stable logical alias, `<component>.<domain>.<name>`, regardless of whether they live in `app.dever` or `app/<topic>.dever`. Alias collisions are duplicate declarations. Topic paths remain an implementation detail and never leak into API snapshots or call sites.
Name lookup tries the current physical owner first, then role-relative names in the current domain, same-component App aliases, and finally full App aliases. A resolved reference is accepted only when the role dependency matrix permits it. Main and API may call App; App may call private roles in its own domain and App aliases elsewhere; private roles may collaborate only inside their own domain. API functions are registered for HTTP routing but rejected as source call targets. Model type identity stays path-derived, while generated operations are restricted to their owning domain.
Application `public` modifiers are rejected. App functions and App-declared types form the external contract automatically; Model record/choice/ID types remain type-visible where relations or App contracts require them. After failure inference, every App function is also checked so a private Domain/Adapter error type cannot leak through its effective error set. Bundled standard sources keep compiler-internal visibility metadata because they are not application architecture. The existing API baseline and unused-export checks consume logical App names.
Architecture validation runs before semantic bodies: legal layouts, reserved names, file/directory exclusivity, flat non-API topic directories and the no-single-file-role-directory rule. Declaration-role validation then keeps API thin, Model storage-only and App as the sole cross-domain capability boundary. Port syntax itself is not expanded in this change; `port` is a reserved architectural role until a separately approved replaceable-boundary declaration contract exists.
@@ -1,819 +0,0 @@
# Dever Minimal Core 0.1 Implementation Plan
## HTTP/2 Ceiling And Soak (2026-09-18, Complete)
子任务 `09-13-http2-ceiling-soak` 复用既有负载器,将客户端 worker/CPU 集合参数贯通 HTTP/1、HTTP/2 和长连接入口。34 项运行器定向测试及最小 loopback 回归通过;生产运行时未改。1 CPU、128 MiB 下 h2c/TLS+h2 的 16x16、30k/45k/60k/90k 双次扫描表明本机吞吐约 36k-40k req/s 时服务端接近 1 核,客户端仍占 1.4-2 核;请求槽丢弃使它不是纯服务端上限。28k 的首轮稳态未达 99% 成功率,故改用 20k:128/64 MiB 各 60 秒的四组成功率为 99.77%/99.92%/99.94%/99.80%,无请求错误、OOM 或 FD 残留。h2c/TLS+h2 各 100 轮重连无错误或 FD 残留;短时 RSS 数据不证明小时级无泄漏。原始报告、命令和限制见 `test/performance/README.md`。
HTTP/2 有界验收和 typed ORM 子任务均已完成。根任务保持进行中:历史计划的编译器发行/跨平台与自举仍未交付,小时级稳定性等扩展也没有在本轮获批或验证;不得凭子任务完成数宣称整个语言路线图结束。
## N5 HTTP/2 (2026-09-12, Complete)
子任务 `09-12-http2` 完成:Hyper/h2 buffered/live 服务端、single-use/pooled 客户端、TLS ALPN、固定流控窗口与每连接流配额、逐流 handler/上传/下载/SSE、RST 隔离及 GOAWAY 排空。官方 `Limits.http2: Http2Limits?` 与检查器一起直接演进,静态原生桥接复用既有 Nullable/Record;没有新 intrinsic 或兼容层。
Hyper Executor 的协议任务接入 Dever 子 Scope,关闭 admission 与登记串行,故障上报/注销在 completion 之前;不可取消 supervisor 清理协议任务和已启动 blocking 后代后才释放物理许可。池连接归持久 driver;上传/下载共享流租约。只有 Hyper 结构化交回的未发送请求可在原期限重排,已发请求不重放。
88 个不同定向用例通过:HTTP/2 client/server 各6,HTTP engine12、HTTP library7、live16、pooled network14、pooled library6、async runtime10、structured concurrency11。独立 h2 对端覆盖流控、取消、容量、ALPN、语义与故障;原生程序覆盖单连接 TLS 嵌套代理、流式上传、SSE、run/await 和原有 HTTP/1/WSS。修复传输取消误升为整服故障、响应发布锁重入、GOAWAY 未发请求卡住旧租约和 Response TE 静默删除;测试端句柄和 WSS 独立连接预算也已修正。
最终 CLI release 构建、backend check/fmt-check、network_bench/reference 编译及源码检查通过;独立审查闭环。Rustfmt/Clippy 不可用,官方库 fmt-check 的原有 net.dever 差异未改。临时构建目录和暂存约378 MiB可再生缓存已清理,旧编译器与性能报告保留。不提交、不自动归档;N5 实现没有运行 HTTP/2 性能矩阵。后续仍有 HTTP/2 压测、小时级稳定性、入口调度优化、RFC 8441/协议扩展、数据库、发行和自举。
## P2 Async Comparison And Long Connections (2026-09-12, Complete)
子任务 `09-12-runtime-performance-p2` 继续复用 P1 工具,完成同语义 Rust run/wait 对照、perf 调度定位、HTTP/HTTPS 高负载和 TCP/WS/SSE 分阶段回收测量。12 个运行器检查、4 个独立对端检查、11 个原生入口及新 Rust examples 的离线构建通过;未修改生产编译器/运行时。
1 CPU/128 MiB 下,Dever run/await root约15.50 µs、实验worker约2.003 µs,与Rust15.59/1.984 µs接近;任务入口跨线程切换是明确优化方向,根任务配额、Send和取消语义需保留。54组HTTP高负载发出1500830请求全部成功、299170计划请求未发出;压测槽位已限制结果,不是服务上限。
最终27组长连接覆盖32/128/512条,每组3轮;18144连接、377061消息均成功,81次FD回基线。另各协议512连接十轮,共15360连接、471207消息,无错误/OOM且30次FD回基线;SSE RSS在约25.3 MiB趋稳。修正采样退出与阶段交接竞态、SSE无心跳误通过和客户端清理;完整证据与限制集中在 `test/performance/README.md`。
P2 结束时尚未启用http2/h2依赖;当时规划的TLS ALPN、独立流/连接预算、流控、取消隔离和GOAWAY已由后续N5交付。小时级soak与入口调度优化另行落地,总语言路线图保持未完成。
## P1 Standalone Runtime Performance (2026-09-12, Complete)
用户批准建立常驻内存、异步调度和 HTTP/HTTPS 三组基准,取代旧网络实施阶段的局部“不压测”限制。子任务 `09-12-runtime-performance` 完成:真实原生二进制、Rust runtime/Hyper 对照、有界固定速率负载、Linux RSS/PSS/CPU 和专属 cgroup 预算、原始 JSON 报告。生产语言/运行时未改动。
7 个运行器与 2 个独立 HTTP 对端检查通过;离线原生/release 构建和格式检查通过,Rustfmt/Clippy 不可用。最终 78 组主测、18 组新连接、15 组 64 MiB 空闲基线均完成且无 OOM;主测网络发出 107962 请求全部成功,38 个计划请求未发出并按原因记录。当前只是热缓存、短时、指定负载结果,不是吞吐极限、冷启动整机内存或长期稳定性结论。
运行命令、确切统计口径和首轮数据以 `test/performance/README.md` 为准,原始产物/报告保存在 `target/performance/`。后续候选:同语义 Rust 异步对照与调度剖析、高负载扫描、TCP/WS/SSE 长连接;其余语言/后端路线图仍未完成。
## N4 Concurrent Composition And Reusable Clients (2026-09-12, Complete)
- [x] Implement source runtime configuration, typed handler context, timeout/race and pull-driven stream adapters; extend async memory traversal.
- [x] Add verified TLS transport and bounded reusable HTTP clients with streamed uploads/downloads; integrate HTTPS/WSS and TLS servers.
- [x] Wire compiler contracts, official source, native conversion and examples together.
- [x] Run focused compiler/native and independent loopback protocol/lifecycle tests; review final ownership and limits.
- [x] Update canonical language/spec docs and record evidence without a commit or archive.
### N4 Evidence
83 distinct focused cases passed: async_composition 7, pooled_network 14, pooled_library 5, structured_concurrency 11, async_network 9, http_engine 12, http_library 6 and live_network 16. Reruns are not counted twice. The old structured-concurrency case that rejected async List handlers was migrated to the newly accepted contract and checked for a concurrency effect.
New coverage includes source runtime bounds/start-once, timeout cleanup of started blocking children, race loser cleanup/fault propagation, Channel null/drain/close and periodic stream timing, static handler/output checks, C012 stream failure obligations, async memory traversal/fusion and concrete HTTP Context transfer. Native programs exercise HTTPS, pooled proxy calls through shared Context, streamed uploads/downloads, SSE, WSS and graceful shutdown.
Independent peers verify physical connection reuse, repeated headers, early chunks beyond the response-head deadline, chunked upload, slow/abandoned/truncated bodies, upload failure/cancellation, queue/body/idle deadlines, pool close wakeups, unpolled-task cleanup, certificate CA/name rejection and WSS frames. A reproduced lifetime defect allowed a pool's unpolled driver to escape its creator; constructing the driver owner guard before task creation fixes it. Native checks also exposed missing Channel Debug/Render for shared records and async handlers entering synchronous fusion; both are corrected. Final bounded cleanup removed an infallible Result from the private lease-completion path; all 14 pool cases passed afterward.
Actual CLI backend example check and fmt-check passed without warnings. Offline build produced a standalone binary; running with an empty environment and PATH restricted to /usr/bin:/bin printed exactly `Hello /Dever\nrequests = 1\n` and exited successfully. Official source formatting, optional-reference core/runtime/CLI cargo check, whitespace/conflict-marker and final ownership/contract inspection passed. Rustfmt/Clippy are not installed, so their checks were unavailable; Rust layout was reviewed manually.
Only initial dependency preparation fetched locked packages; validation/builds were offline. All network peers used owned loopback random ports and static test certificates under test/. No full suite, prior performance baseline, existing service, persistent example launch, global install or commit. No throughput/RSS improvement is inferred from functional checks. Temporary build, certificate-generation, formatting and demo directories are removed at handoff; workspace native caches remain. N4 is complete; HTTP2, protocol extensions/close metadata, database, distribution/cross-platform and self-hosting keep the umbrella task active.
## N3 Streaming HTTP, SSE And WebSocket (2026-09-12, Complete)
- [x] Add `serve_live`, HttpReply and LiveLimits while keeping ordinary serve direct. Reuse request/response codecs and connection configuration; one-slot body channel and one-handler Group per live connection.
- [x] Add SSE field encoding, bounded chunks, Last-Event-ID access and body-driven heartbeat timers; no event history or extra heartbeat task.
- [x] Reuse tokio-tungstenite 0.30.0 handshake/protocol for server/client Text/Binary/Ping/Pong, AsyncStream messages and close handshakes. Shared Endpoint lifecycle, 4 KiB read buffer, bounded messages/write buffers and independent directions.
- [x] Integrate resource types, exact nominal records/choices, async effects, static handlers, native conversion, API/Markdown names and Render. No new syntax, compatibility layer, dynamic interpreter or generic transport abstraction.
- [x] Verify owned wire peers and real native programs; update canonical docs/example and manually record the session without a commit or archive.
### N3 Evidence
46 distinct focused cases passed: live_network 16, live_library 3, http_engine 12, http_library 6, async_network 9. Repeated checks after corrections are not counted again. All final checks passed; the earlier slow-peer test assumed normal return and was corrected to observe cleanup on either return or cancellation.
Wire evidence covers early chunk delivery, bounded slow-peer output, SSE exact encoding/heartbeats past the ordinary deadline, idle failure isolation, HEAD cleanup, handler faults, upgrade capacity/preread bytes, independent client masks, fragmentation/UTF-8/control frames, close, partial writes, waiting-writer cancellation and started blocking descendant drain. A reproduced regression showed duplicate response publication closed the existing body; cancellation ownership now starts only after publication is accepted, and the regression passes.
The native source program exercised all four routes (buffered, stream, SSE, WebSocket), including client connect/send/receive/close and static async message-stream handlers. Existing native HTTP clients also matched independent wire peers. Final optional-reference cargo check, actual CLI example check (no warnings), CLI fmt-check, source format roundtrips and explicit whitespace/owner inspection passed. Rustfmt/Clippy are not installed and were not run. No throughput/RSS claims or old baseline.
Only a one-time dependency fetch accessed the registry; checks/builds ran offline. No full suite, existing service, persistent example launch, global install or commit. N3 is complete; TLS/WSS, HTTP2, client pools, protocol extensions, close metadata, database/distribution and other roadmap work keep the umbrella task active.
## N2 HTTP Engine (2026-09-12, Complete)
The user approved continuing after N1. Execute inline; no further planning approval. Reuse Hyper HTTP/1 on the shared Tokio runtime. No legacy compatibility, old baseline, full suite, service checks or global installation.
- [x] Replace the custom HTTP parser/transport with Hyper server/client primitives and migrate all source callers.
- [x] Preserve repeated headers as `List<Header>` with byte values; buffer bodies within explicit limits. Reuse byte storage across the engine boundary.
- [x] Add `serve(async handler, Listener, Limits) -> ()` with bounded structured connections, server keep-alive, chunked input and cancellation cleanup. Configuration/accept/handler faults propagate; peer protocol failures terminate only that connection.
- [x] Add one-shot `send(address, port, Request, Limits) -> ResponseResult`. Poll its driver in the request future; no detached tasks, implicit retries or client pool.
- [x] Check static handler contracts/effects and native record conversion. Replace obsolete parser tests with independent loopback wire cases and focused compiler/native tests.
- [x] Update canonical docs and record evidence. TLS, HTTP/2, client pooling, streaming response bodies, WebSocket and SSE remain later work.
Limits: header buffer at least 8192 bytes (Hyper requirement), nonnegative body bytes, positive timeout, bounded connections. Defaults: 8192 / 1048576 / 5000 ms / 32. Runtime owns HTTP framing; outgoing Content-Length/Transfer-Encoding are rejected. Request header/body/handler and stalled write deadlines prevent one idle peer retaining a connection indefinitely. Static Dever handlers specialize directly; no dynamic interpreter dispatch.
### N2 Evidence
- 44 distinct focused cases passed: http_engine 12, http_library 6, async_network 9, structured_concurrency 11, native_ownership byte cases 3, runtime_foundations bytes case 1, contract_execution selected example checks 2. Repeats after local corrections are not counted again.
- Independent wire cases cover persistence/pipelining, HEAD and 204/205/304, fragmented/chunked/binary bodies, repeated and non-UTF8 headers, conflicting lengths, header/body limits, unsupported trailers/upgrades, truncated responses, all deadlines, connection capacity and descendant cleanup. Header timeout closes directly; body/handler timeouts produce 408/504. Native tests execute real compiled send/serve and compare client bytes to an independent peer.
- Bytes tests retain unique Text storage transfer, shared-alias behavior, shared slices and consuming iteration. Connection setup now configures TCP_NODELAY once through one helper. No old-performance baseline, throughput or RSS claims were derived from correctness tests.
- Core compiled offline with the optional reference feature. Actual CLI HTTP example check/fmt-check and formatting roundtrips passed; the persistent example was not started. Final owning-layer, dependency, obsolete-reference and whitespace review passed. Rustfmt and Clippy were unavailable (components not installed), not reported as passing.
- Hyper 1.11.1, hyper-util 0.1.20, http-body-util 0.1.5 and bytes 1.12.1 are pinned. Missing transitive development dependencies were fetched once; builds/tests remained offline. No Axum, TLS or HTTP2 features were added.
- No full suite, existing service check, load test, global install, commit or task archive. N2 is complete; the umbrella task remains active for N3 and other unshipped roadmap work.
## N1 Async Stream And TCP (2026-09-12, Complete)
- [x] Persist the approved new-language/no-compatibility policy; use existing inline execution routing.
- [x] Add bounded, cancellable async stream consumption and Tokio TCP resources to the shared runtime.
- [x] Integrate AsyncStream types, async handlers, effects/liveness and concrete native code generation.
- [x] Replace synchronous TCP source contracts and migrate official HTTP transport, examples and tests.
- [x] Verify stream backpressure, close/stop, duplex I/O, timeout and compiler/native behavior with focused tests.
- [x] Update public contracts, inspect the final changed flows and record evidence. N2 HTTP engine and N3 WS/SSE remain deferred.
### N1 Evidence
- 62 distinct focused cases passed: async_network 9, async_stream 6, structured_concurrency 11, async_native 6, async_runtime 10, http_library 11, core_native `official_` 5, runtime_foundations `stream` 3, and the reference/native file-stream differential case 1. The HTTP benchmark stayed ignored. Repeated individual checks after local fixes are not counted again.
- Network tests owned only loopback port-0 sockets. A one-worker runtime proved duplex progress, pending close wakeups, read timeout reuse, partial-write cancellation closure, pre-pull backpressure, failure while the producer is idle, child cleanup and listener release. The persistent server example was checked without starting it.
- Native execution exposed missing Render implementations for new resource types; those were fixed at the runtime owner and the affected native checks passed. Migrated TCP differential cases now assert independent native expectations; the synchronous evaluator no longer carries a duplicate TCP implementation.
- Compiler/default and optional reference builds passed offline. Source formatting round trips, explicit whitespace checks and final owning-layer inspection passed. Rustfmt/Clippy were unavailable because those components are not installed; no claim of passing them.
- No workspace-wide tests, service checks, old-performance baseline, network load test, global install or commit. The task remains active for N2 HTTP engine and N3 WebSocket/SSE, not for unfinished N1 work.
## Structured Concurrency Implementation (2026-09-12, Complete)
The user approved this preceding implementation. Networking was paused during it; N1 above resumes AsyncStream/TCP. Each phase uses only focused offline tests under root `test/`; do not run the workspace-wide suite, service checks, old-performance baselines or network load tests.
### Phase A: Freeze Syntax And Semantic Model
- [x] Add the canonical `async name(...)` modifier while retaining post-signature `pure`/`recover`; update lexer/parser/formatter and Markdown signature model together.
- [x] Add async to function identity metadata and HandlerSignature compatibility without changing package/name/arity lookup.
- [x] Reject `async` together with `pure` in the first release and classify every concurrency operator through the existing transitive effect owner.
- [x] Add contextual `await`, `run`, `stop`, `group`, `parallel`, `blocking` and `channel` syntax; reject async calls as ordinary stored values.
- [x] Add HIR task/group/channel operations and compiler-owned affine local metadata; do not add a user-visible Future or generic evaluator value.
- [x] Extend recursive type properties with thread transferability and explicit resource opt-in.
- [x] Add focused syntax/format/check cases for clause agreement, sync-to-async rejection, handler mismatch, forbidden Task escape and transfer failures.
Rollback point: syntax/HIR-only changes must leave all existing synchronous HIR and formatted output unchanged.
### Phase B: Await And One Shared Runtime
- [x] Add Tokio with the smallest required features and one runtime owner in `dever-runtime`.
- [x] Emit async functions as concrete Rust futures while retaining direct sync emission for wholly synchronous entry graphs.
- [x] Implement direct `await(async_call(...))` and async entry execution; preserve zero/single/named multi-output and source-located failures.
- [x] Implement async timer sleep as the first real suspension boundary; do not use TCP/HTTP as the proof fixture.
- [x] Verify multiple sleeping tasks can make progress without one OS thread per task and sync binaries do not initialize the runtime.
Rollback point: runtime dependency and native entry changes are isolated from Task ownership and can be removed while keeping Phase A diagnostics.
### Phase C: Structured Task And Group Ownership
- [x] Implement `run(async_call(...)) -> Task`, `await(task)`, and exactly-once affine consumption across local writes and every clause exit.
- [x] Implement zero-output `stop(task)` as cooperative cancel-and-wait; reject result-bearing stop and repeated consume.
- [x] Implement positive bounded `group(limit)`, backpressured `run(group, call)`, `await(group)` and `stop(group)`.
- [x] Ensure normal and abnormal parent completion cleans all children; no detached path or swallowed expected failure.
- [x] Verify start-before-await concurrency, output equivalence, cancellation cleanup, capacity backpressure and bounded task ownership.
Rollback point: Task and Group runtime modules remain separate from scheduler construction and do not change direct await.
### Phase D: Multi-thread And Blocking Work
- [x] Implement `parallel(sync_call(...))` for pure transferable calls using bounded execution; preserve existing numerical/error semantics.
- [x] Implement the compiler-owned `blocking` boundary only for calls with a transitive compiler-known blocking effect; bound active workers plus queued submissions.
- [x] Route async use of `parallel_each` away from Tokio workers while retaining the current synchronous contract and shared worker validation.
- [x] Configure conservative defaults from available parallelism with explicit hard maxima; do not create per-call runtimes or unconstrained pools.
- [x] Verify multi-core execution, no scheduler starvation under bounded blocking work, exact output/error behavior and small-worker configurations.
Rollback point: retain the existing synchronous `parallel_each` implementation until the common executor passes its focused compatibility cases.
### Phase E: Bounded Channels And Contract Integration
- [x] Add compiler-contextual `channel(Type, capacity)` and the runtime Channel resource.
- [x] Implement async `send`/`receive`, idempotent `close`, wakeup behavior and explicit source-defined result choices.
- [x] Extend effect, failure, dependency, redundancy, API snapshot and Markdown checks for every new signature/operation.
- [x] Add documentation only for implemented behavior and a runnable non-network producer/consumer example.
- [x] Run the available focused syntax/semantic/native/runtime/concurrency, reference-feature and whitespace checks; record unavailable rustfmt/Clippy components explicitly.
Final gate: inspect generated native code for concrete futures/direct static handlers and absence of generic Value/handler dispatch. Record passed, failed and unrun checks without claiming network readiness.
### Final Evidence
- `structured_concurrency`: 11 passed; syntax, formatter shape, affine ownership, transferability, effects/failures, handler boundaries and Channel contracts.
- `async_runtime`: 10 passed; recursive cleanup, cancel-and-wait, Group backpressure, bounded channels, shared blocking limits and async `parallel_each` peak concurrency.
- `async_native`: 6 passed; real native execution of await/Task/Group/Channel/parallel/blocking/timer, sync-entry isolation and nullable Channel flattening.
- Existing synchronous `concurrency`: 5 passed and its explicit benchmark remained ignored; the async example and nullable-result native regressions each passed their exact case.
- `dever-core` with the optional `reference` feature compiled offline. `git diff --check` and explicit trailing-whitespace scans passed. Dependency inspection confirmed Tokio uses only runtime, sync and time features in this phase.
- The installed Rust toolchain has no rustfmt or Clippy component, so those two checks were unavailable rather than passed. No full suite, build, service, network, old-performance baseline, global installation or commit was run.
- Independent final review found no remaining blocking defect after nullable Channel emission, synchronous collection-handler enforcement and transitive async effect boundaries were corrected.
### Deferred Follow-through
- [x] Design and implement AsyncStream/Tokio TCP on the shared runtime in N1. HTTP engine and WebSocket/SSE integration remain later phases.
- [ ] Measure the new packaged binary and live network behavior only after network integration; the user explicitly waived an old-runtime baseline.
## Active Performance Checklist (2026-09-08)
- [x] Capture bounded compiler, record/text/list/map/JSON/HTTP and concurrency baselines.
- [x] Memoize type properties and schedule effect/failure summaries by dependencies.
- [x] Eliminate redundant record field copies and consume unique list elements safely.
- [x] Optimize direct character-list consumption and eligible collection fusion without changing fault order.
- [x] Implement incremental HTTP framing and test in-memory fragments/limits/malformed inputs.
- [x] Cache bundled frontend work and native artifacts with complete invalidation and API checks.
- [x] Measure concurrent task granularity; batch only where justified and preserve bounded Stream consumption.
- [x] Run affected offline tests, native/reference regressions, strict lint, formatting and independent final review; record measurements and update owning docs.
User has authorized the complete implementation. This extends the active task and does not require another planning approval. Two bounded implement workers own analysis and cache respectively; main owns emission/runtime/protocols and benchmark integration. All permanent tests stay in root test/. Never run workspace-wide tests, existing services or global installation; use test-owned temporary inputs/processes only.
## Packaged Runtime Footprint Follow-through (2026-09-09)
- Composite runtime values and generated records/choices now append recursively through `Render::render_to`; named entry outputs reuse that same String instead of building a rendered value and then copying it into a second formatted String. Numeric `to_text` retains its direct `to_string()` path after the bounded hot-path gate exposed a slower generic formatting attempt.
- Native binding temporaries now transfer uniquely owned Text/Bytes storage and List/Map values through consuming operations. Live aliases retain copy-on-write behavior, and resource-containing values remain non-movable under the existing type-property contract.
- Exact-size finite parallel iterators create no more workers than items/batches. Unknown-length Stream dispatch keeps the caller-requested worker limit, bounded queue, cancellation and joined lifecycle.
- Production optimization arguments now use fat LTO and strip symbols; the workspace release runtime uses matching fat LTO/one-unit settings. The same allocation-performance fixture decreased from 4,680,864 to 452,352 bytes (90.3%) and measured 3,468 KiB peak RSS, versus 3,608 KiB without LTO.
- Thin LTO was rejected because ordered Map reached a 1.255 native/baseline ratio. Fat LTO passed all five gates: Int 0.713, Float 1.001, List 0.668, Decimal 0.985 and ordered Map 1.030. It trades substantially longer cold compilation for the user-approved packaged-runtime priority.
- An attempted fixed 64 KiB clamp for one-shot reads was rejected and removed: it changed a 1 MiB regular-file result to 64 KiB and failed the exact performance result. `read(limit)` remains caller-sized; low-memory programs use smaller limits or `chunks`.
- Focused runtime/concurrency/native ownership/core-native checks and both explicit performance scenarios cover the final contracts. No workspace-wide suite, service, external network, global installation or commit was used.
## Performance Evidence (2026-09-08)
- Nominal type properties are memoized after cycle validation. Facts, effects and failures share one dependency graph; summary changes reschedule callers instead of rescanning every function. The same 24-level shared-type debug regression went from 11.56 s to 0.47 s across the combined analysis/frontend changes.
- Native emission reuses checked HIR and last-use analysis. Safe record aliases are coalesced, scalar reads can be snapshotted before consuming large records, and exact field overwrites can move their old value. Unique Lists move elements; shared Lists clone only consumed elements. Resources retain their original lifetime and aliases retain value semantics. Empty Bytes concatenation reuses storage without weakening copy-on-write.
- Text splitting has one iterator implementation. Direct consumers avoid an intermediate List; eligible each/filter stages fuse only when handlers are proven free of observable effects and faults. A pure Int handler alone is insufficient because overflow can change fault order. Generated-code, alias, mutation and native/reference cases cover these boundaries.
- HTTP wire decoding now retains its header boundary and parsed frame between feeds. Transport uses that state; legacy one-shot scan delegates to the same implementation. Three new in-memory cases cover fragmentation, binary bodies, EOF, limits, duplicate headers and private/aliased state; five existing pure protocol tests passed. No live HTTP service was used.
- Bundled AST and the last complete frontend result are cached within a process. Native artifacts persist across processes, keyed by source-generated code, compiler/toolchain/runtime/dependency identities and native options. Atomic publication, fingerprint validation, isolated executable copies and create-new saves preserve correctness. All six cache tests and the explicit actual-CLI API-baseline regression passed. Independent CLI processes still check current sources and API baselines; no persistent HIR cache was introduced.
- Finite parallel inputs use bounded batches through the existing cancellation/join owner; Stream keeps bounded producer-driven pulls. With 32,768 inputs and four workers, median light-handler queue/batch/serial times were 140.945/3.894/0.247 ms; 512-round handlers measured 178.267/20.033/60.185 ms. Batching reduces dispatch cost but does not make tiny tasks preferable to serial execution.
- Main-agent final ownership/JSON/reference verification passed 9/7/10 tests respectively; only three network-dependent differential cases were skipped. Concurrency passed five functional cases, contracts passed 38 affected cases, and strict Clippy/type checks passed for core/runtime/CLI and affected test targets. Actual CLI library formatting and contracts/library checks passed. Independent final review found no blocking issue.
- After the final Bytes optimization, the main agent reran all three incremental HTTP cases and six default native-cache cases successfully. The actual-CLI cache test is ignored by default and had already passed when explicitly selected. Final targeted Rust formatting and new-file whitespace checks passed.
The same source was compiled before and after this change. Linux x86-64, Rust 1.98.0, CPU affinity 4; five alternating processes per side, native internal timing excluding compilation/startup. Text/List/JSON each retain five samples per process after discarding warmup; Map retains one per process. All expected results were asserted. Medians:
| Source workload | Before | After |
| --- | --- | --- |
| 16,384 record Text appends | 14.093 ms | 0.279 ms |
| 16,384 Text List elements | 4.786 ms | 1.342 ms |
| Long JSON string parsing | 28.072 ms | 6.977 ms |
| Record accumulation of 16,384 distinct Map keys | 548.061 ms | 1.999 ms |
| Process peak resident memory | 4,648 KiB | 3,604 KiB |
- Separate HTTP strategy measurement used identical 16,947-byte input in 64-byte fragments, both paths on the new compiler, seven alternating samples after warmup: repeated one-shot scan 4.541 ms, incremental decode 0.366 ms. This is parser work, not server throughput or an old/new compiler comparison.
- Same release CLI hello: cold native miss 0.84 s; subsequent independent-process hits 0.17/0.17/0.16 s, including source/API checks and runtime freshness. In-process frontend cold/hit measured 171.273/3.742 ms.
- The existing five-kernel matching-runtime gate passed again with 45 effective samples per side: native/baseline Int 0.989, Float 0.998, List 0.578, Decimal 0.979, ordered Map 1.006 (limit 1.15). This checks code-generation overhead against the same runtime, not against the old compiler. Build times were measured separately: native 2,137–2,693 ms, baseline 252–680 ms.
- These are bounded local workloads, not whole-language speed guarantees. README, IMPLEMENTATION and owning specs describe the optimization boundaries. No full suite, service integration, global installation or commit; the performance scope is complete while the umbrella language roadmap remains active.
## Active Follow-through Checklist (2026-09-07)
- [x] AST formatter, comments/precedence/idempotence, all-file preparation and CLI fmt/check.
- [x] Text/number library expansion and explicit argument/environment/clock primitives.
- [x] Opt-in reference evaluator, core semantics and native differential cases.
- [x] Last-use and reduction-state ownership optimization with alias/order regressions.
- [x] Bounded reproducible native performance baseline and evidence.
- [x] JSON/HTTP source packages and focused protocol fixtures.
- [x] Controlled concurrent execution with explicit limits/lifecycle and focused verification.
- [ ] Resolve database choice from pending user answer; implement or record explicit deferral.
- [x] Integrate delivered flows, review, update canonical specs and actual validation evidence.
Workers implement formatter and reference evaluator independently; the main agent owns libraries and integration. Checks are targeted, offline and bounded; no full suite, live service, global install, commit or cross-platform distribution. Later checklists are historical.
## Follow-through Evidence (2026-09-07)
- Implemented syntax-only AST formatting and CLI `fmt`/`--check`. Source snapshots are reloaded before staging and before replacement, comparing both the complete path set and file contents. Independent review found the initial missing path-set check; the shared source loader now closes that gap without a second traversal implementation. Per-file replacement remains atomic, not a multi-file OS transaction.
- Added Unicode Text operations, numeric parsing/rendering, process arguments/environment and explicit clocks/sleep through the existing intrinsic catalog and source-defined result choices. JSON and HTTP algorithms are ordinary `.dever` packages; no runtime JSON/HTTP parser or new dependency was added.
- JSON validates grammar, precise numeric spelling, duplicate keys, Unicode, graph references, nesting and expansion limits. HTTP supports a declared HTTP/1.1 single-message subset with binary bodies, Content-Length, strict ASCII headers and typed failures; independent peers check canonical request/response bytes, fragmentation, invalid requests/400, EOF and no-body statuses. Blocking read/write syscalls have explicit timeouts; no strict overall message deadline is claimed.
- `parallel_each` reuses static handlers/sequence classification and adds a bounded producer/worker owner with explicit worker limits, fault cancellation at pull boundaries and joined completion. Stream remains producer-owned; worker inputs/context cannot recursively contain Stream.
- Ordinary last-use moves and transferred reduction state preserve live aliases and output/field order. Resource-containing locals retain lexical lifetime; a differential socket regression verifies no premature peer EOF. Full-domain Int/Decimal guards omit redundant comparisons without altering bounded clauses.
- Passed 25 targeted core/library tests: `standard_library` 5, `native_ownership` 3, `concurrency` 4 and opt-in `differential` 13. `formatter` passed 11 tests including added/renamed source snapshots and all current official packages/examples. JSON passed 7 and HTTP passed 8 in worker validation; the main agent independently reran Unicode/exact-number JSON and the real HTTP server wire/400 case. Production CLI feature resolution excludes `reference`.
- Actual CLI `check`, `fmt`, `run` and `build` succeeded for `examples/library/src`; normal run and a standalone executable under `env -i PATH=/nonexistent` printed `json = {"message":"你好 Dever","total":6}` and `http_roundtrip = true`.
- Independent final review found no remaining blocker in formatter, reference, ownership, numeric domain optimization, concurrency and JSON after the snapshot fix. Main-agent HTTP review checked framing/status/timeout contracts. Database selection remains pending rather than implicitly chosen.
- Passed the explicitly ignored performance target on Linux x86-64, CPU affinity 4, Rust 1.98.0, cached runtime, optimization level 3 and one codegen unit. Each side used five alternating processes and 45 effective samples. Native/baseline median ratios: checked Int 0.952, Float 1.000, List each/filter/sum 0.207, Decimal 1.001, ordered Map 1.145; all met 1.15. Native build times were 1,206–1,793 ms, baseline compile times 255–668 ms, recorded separately from execution. These figures cover the selected kernels only.
- Decimal comparison was corrected to use identical lexical `0.0` representation with baseline parsing outside timing. Map diagnostics identified missed generic hash-table inlining under multiple codegen units; production and baseline now share one optimization-argument constant. This changes compiler optimization scope without changing hashing, panic, arithmetic or value semantics; it trades compilation parallelism for optimization consistency.
- After the final native compiler setting, all 13 differential cases passed again, including exact fault traces, signed zero, Map semantics and resource lifetime. Strict Clippy/type checks passed for core/runtime/CLI and every new test target; targeted rustfmt and explicit whitespace checks passed. The complete follow-through has 51 functional tests plus one five-kernel performance gate, not a full workspace test run.
- No full suite, existing service, external database, global toolchain/command change, Git commit or archive. Distribution/cross-platform remains deferred; self-hosting is still unshipped.
- Status: Approved core foundations and foundation extension implemented and verified on 2026-09-07
- Requirement source: `prd.md`
- Architecture source: `design.md`
- Execution mode: one tightly coupled task; no obsolete child-task dispatch
## Completed Foundation Delivery Rule (Historical)
Implement the foundation extension as one bounded delivery: `reduce`, `reduce_until`, Bytes traversal, `Stream<Item>`, restricted static handler inputs, file/TCP stream wrappers and focused verification. Reuse the existing collection checker, typed HIR, intrinsic catalog, native emitter and resource owner; do not build a parallel execution path. Compiler distribution, cross-platform work, formatter/evaluator work and full HTTP/JSON/CRUD/database packages remain deferred. The completed core and hello milestones are regression baselines; the old API and original phase checklists are historical.
## Foundation Extension Checklist (Authoritative, Complete)
### 1. Syntax And Semantic Contracts
- [x] Parse contextual `handler(inputs) (named_outputs)` only in function input declarations, using a dedicated AST input kind rather than an ordinary value type.
- [x] Parse `Stream<Item>` through the standard generic-type path; reject wrong arity, nullable handler signatures and handler syntax in fields, outputs or local/value positions.
- [x] Add semantic `HandlerSignature` and handler-parameter symbols separately from runtime `Type`; add opaque `Stream(Item)` to the type model with non-comparable, non-Map-key resource semantics.
- [x] Centralize handler compatibility, allowed reference positions and sequence element classification so direct calls, forwarding, `each`, `reduce` and `reduce_until` cannot drift.
### 2. Typed HIR And Static Specialization
- [x] Replace collection-only handler IDs with shared `HandlerTarget`, `CallTarget` and handler/value call arguments.
- [x] Lower direct handler calls and multi-level forwarding without storing handlers in value locals or aggregates.
- [x] Key native specializations by function plus ordered concrete handler bindings, substitute every forwarded target, and emit only direct calls.
- [x] Detect direct and indirect cycles in the substituted specialization graph before private Rust compilation.
### 3. Sequence Operations
- [x] Add `reduce` and `reduce_until` to the single standard-operation registry and HIR, with exact state and `stop: Bool` output contracts.
- [x] Lower List and Bytes traversal as left-to-right direct loops; emit Bytes elements as Int values without an intermediate List.
- [x] Extend `each` to Bytes and zero-output Stream consumption while preserving existing List mapping/action behavior.
- [x] Leave `filter`, `find` and `sum` List-only and reject implicit Stream collection.
- [x] Add idempotent zero-output `close(stream)` and reject it for non-Stream values.
### 4. Stream Runtime And Official Packages
- [x] Add one generic runtime Stream owner with shared cursor/end/close state and serialized pull/close operations; do not add prefetch, threads or async machinery.
- [x] Implement natural EOF, one terminal typed failure item, idempotent close, alias continuation after `reduce_until`, and release of only the Stream-held source alias.
- [x] Add `ReadEvent`, `ReadStreamResult` and `chunks` to bundled `dever.io`; add socket `chunks` and listener `connections` to bundled `dever.net`.
- [x] Extend fixed `dever.system` intrinsic signatures and generated typed adapters only for Stream creation/pulling; keep public result choices in `.dever` source and OS/resource mechanics in runtime.
- [x] Preserve existing one-shot read/write/accept/close contracts without compatibility aliases or behavior changes.
### 5. Focused Verification And Cleanup
- [x] Add parser/semantic negatives for handler placement, type/arity/output mismatch, forbidden storage/return/comparison/clause use and indirect recursion.
- [x] Add native List/Bytes cases for empty input, order, state typing, stop behavior, zero-output each and absence of intermediate Bytes allocation.
- [x] Add runtime/native Stream cases for shared aliases, natural EOF, early stop/resume, repeated close, source close, invalid limit and one terminal failure.
- [x] Compile official wrappers through the normal source loader and test file chunks plus loopback socket chunks/connections with owned resources, bounded data and timeouts.
- [x] Exercise actual CLI `check`, `run`, `build` and the resulting standalone executable with a chunked state-parser fixture whose behavior is defined in `.dever`.
- [x] Run focused formatting/type/lint/test checks only, inspect generated code/artifacts for direct handler calls, validate Trellis context and run whitespace checks.
- [x] Review the final diff for duplicate contracts, runtime handler dispatch, eager Stream collection, HTTP/JSON leakage and unrelated changes; update the canonical specs and implementation evidence.
Planned focused validation targets:
```bash
cargo test --offline -p dever-tests --test syntax_and_format <handler-or-stream-filter>
cargo test --offline -p dever-tests --test core_semantics <reduce-or-handler-filter>
cargo test --offline -p dever-tests --test core_native <sequence-or-specialization-filter>
cargo test --offline -p dever-tests --test runtime_foundations <stream-or-loopback-filter>
cargo test --offline -p dever-tests --test hello_native <regression-filter>
cargo check --offline -p dever-core -p dever-runtime -p dever-cli
python3 ./.trellis/scripts/task.py validate 09-04-dever-language-mvp
git diff --check
```
Filters will be replaced by the concrete test names introduced during implementation. No full suite, workspace-wide test, performance gate, service start, external network call or global command mutation is authorized. File tests use test-owned temporary directories; TCP tests bind only `127.0.0.1:0` and terminate within explicit bounds.
Rollback boundaries are syntax/semantic representation, HIR/specialization, finite sequences, runtime Stream and official wrappers. A boundary is not accepted while it relies on a runtime function value, duplicated checker rule, eager collection fallback or partially exposed public API.
## Foundation Extension Evidence (2026-09-07)
- Handler inputs use dedicated syntax, semantic symbols and HIR arguments. Clause coverage and runtime locals traverse value parameters only. One specialization graph resolves direct and multi-level handler forwarding, records real cross-package handler references and rejects substituted recursion; generated functions contain direct calls and no user-handler function values.
- `reduce`, `reduce_until` and `each` reuse one sequence classifier. List and Bytes lower to direct left-to-right loops, Stream pulls on demand, and `reduce_until` does not pull past the stopping item. Bytes traversal yields Int directly without constructing a List.
- Runtime `Stream<T>` shares cursor/end/close state across aliases and serializes pull/close. File/socket chunks validate creation, release their producer at EOF or after one terminal failure, and support early-stop continuation. Bundled `dever.io` and `dever.net` expose source-defined stream choices through the normal loader.
- Passed `cargo check --offline -p dever-core -p dever-runtime -p dever-cli` and strict Clippy for core/runtime/CLI plus the root test crate. `cargo fmt --all --check` passed.
- Passed 74 focused tests: 22 `syntax_and_format`, 16 `core_semantics`, 16 `core_native`, 8 `runtime_foundations` and 12 `hello_native`. File tests owned their temporary directories; TCP tests used `127.0.0.1:0`, explicit timeouts and bounded transfers.
- Final review made handler signatures reuse ordinary nested Map validation, preserved `Pull::Last` through mapped official streams, and added generated-code plus official creation-failure regressions. An independent read-only review then found no blocking correctness, safety or contract issue.
- Actual CLI `check`, `run` and `build` succeeded for `examples/stream/src`; run and the standalone executable both printed `total = 208`. The executable also ran with an empty environment and no Cargo/Rust path.
- No formatter/evaluator, async/concurrency system, HTTP/JSON/database package, external service, full workspace test, benchmark, distribution change, global command change or commit was included.
## Active Core Checklist
- [x] Record clause-only conditional dispatch as a permanent language rule and remove conditional blocks from the deferral list.
- [x] Retain settled repetition rules and remove the redundant planning blocker.
- [x] Record the user's approval and map the implementation boundary to core language and system interfaces.
- [x] Implement scalar/aggregate types, complete symbols, expressions, outputs and value-semantic assignments.
- [x] Implement clause type inference, normalization, overlap/exhaustiveness analysis and typed native dispatch.
- [x] Implement numeric and collection runtime operations plus shared intrinsic contracts.
- [x] Compile bundled official source and implement Bytes/file/TCP resource foundations.
- [x] Verify focused semantic/native/runtime cases and review the final cross-layer changes.
## Core Foundation Evidence (2026-09-07)
- Implemented six scalars, records/choices/nullable/List/Map/MapEntry, typed expressions, named outputs, stable assignments and value semantics through check/HIR/native execution. Added naming and unused-write diagnostics.
- Function groups infer one signature, use symbolic per-axis domains and rectangle subtraction to prove disjoint coverage, and lower into concrete native dispatch. No conditional blocks, source loops or user recursion were introduced.
- Official source in `library/` shares the same frontend; one intrinsic enum bridges the small system boundary. Decimal128 and ordered maps reuse pinned `dec`/`indexmap` runtime wrappers. Added Bytes, create-new/read-only files, TCP and shared-close opaque handles with explicit source-defined result choices.
- Passed 46 focused tests: 13 `core_semantics`, 9 `core_native`, 5 `runtime_foundations`, 12 existing `hello_native`, and 7 relevant `syntax_and_format` source/diagnostic regressions. Runtime tests used owned temporary files and loopback port 0 only. No full suite or integration environment was run.
- Passed targeted rustfmt and strict Clippy/type checks for compiler/runtime/CLI and affected test targets. Existing tool archives were invoked with command-scoped paths; a local ignored verification wrapper supplies the toolchain library path after Cargo sanitizes its environment. No global installation or environment changes.
- Actual CLI `run examples/core/src app.main` produced all 22 expected named outputs. `check` succeeded with nonexistent RUSTC. `build` produced `target/dever-core-example`, confirmed as a native Linux x86-64 ELF, and it produced the same outputs with `env -i PATH=/nonexistent`.
- Independent read-only review found and closed duplicate choice Map keys, Group-sensitive inference, null-only `first`, and Float signed-zero constant propagation. Added semantic/native regressions; Float literal contextual typing does not retype existing Decimal/Int locals.
- The initial wrapper/fixture errors and one stale numeric rejection fixture were corrected; final affected checks passed. Trellis context validation passed with implementation/check JSONL absent and skipped for inline work. Ordinary `git diff --check` passes but does not inspect the untracked compiler files; targeted formatting and explicit new-file whitespace checks cover those files.
- Formatter, reference evaluator/differential checks, performance gates, full HTTP/JSON/database packages, self-hosting and distribution/cross-compilation were not implemented or claimed by this delivery. No commit/archive; the wider language task remains active.
## Completed Hello Checklist
- [x] Correct the superseded API-first requirements and record the approved Text/action subset.
- [x] Add package/function resolution, semantic diagnostics and checked HIR.
- [x] Add standard output runtime and native compilation.
- [x] Add local check/build/run CLI and `examples/hello/src/app/user.dever`.
- [x] Run focused semantic/native/runtime tests and the actual hello command; announce local process execution first.
- [x] Review the final changes, update implementation evidence and record the session.
## Native Hello Evidence (2026-09-06)
- Implemented the approved Text/action subset, checked HIR, shared stdout runtime, native Rust generation and local `deverc` CLI. Example: `examples/hello/src/app/user.dever`.
- Passed 12 `hello_native` tests and the existing 20 `syntax_and_format` tests. After the final emitter readability change, reran all 12 native tests successfully.
- Passed rustfmt and strict Clippy for core/runtime, both targeted test binaries and the actual CLI binary.
- Actual CLI `check` succeeds even with a nonexistent RUSTC, proving it does not invoke compilation. `run` prints exactly `hello\n`; an unknown entry returns exit 1. `build` produced `target/hello-user`, identified as a native x86-64 ELF, which prints `hello\n` with PATH containing no Rust tools.
- Independent read-only reviewer found no blocking issue; the noted CLI verification gap was closed by the actual commands above. Tests exercise Unicode/escaping, source locations, value semantics and write failures, not a User/hello-specific execution branch.
- Added README run/build instructions and updated owning compiler specs. No global command replacement, external dependency, commit, service, database or load test.
- This bounded milestone is complete. The overall language roadmap remains open; HTTP/JSON/CRUD and persistence are deferred pending the user's next step.
Preserve unrelated changes and do not create a commit without explicit authorization. Verification for the new API must use its own local process and test storage, with functional checks and bounded performance checks described before execution. Do not test against, restart or benchmark existing services. No runtime or load check has been run during this planning turn.
## Historical API Replanning Checklist (Deferred)
- [x] Record the real user API goal and supersede the pure-computation-only boundary.
- [x] Keep `.dever` source and the existing surface syntax; defer the speculative contract-declaration system.
- [ ] Confirm persistence selection.
- [ ] Specify typed HTTP/JSON/storage boundaries and necessary effect/concurrency semantics.
- [ ] Finalize user API behavior and meaningful performance acceptance.
- [ ] Replace the next execution steps with a bounded native application delivery plan.
- [ ] Review the concrete plan with the user before implementation.
Proposed delivery order, to finalize after the open product decisions:
1. Implement name/type/clause/flow checking and HIR for the language features the API actually uses.
2. Establish concrete native business-function execution and source-located failures.
3. Add typed HTTP/JSON boundaries, explicit resource/effect semantics and the selected persistent database adapter.
4. Implement `examples/user-api` entirely in `.dever`, with real CRUD and configuration supplied by the runtime environment.
5. Run announced, isolated functional/persistence/concurrency checks and a bounded native performance comparison.
Complete Decimal/Float/Map libraries, the full reference evaluator, formatter, component distribution and AI indexing remain on the roadmap; they are not all prerequisites for the first user API. Preserve unsupported-source diagnostics rather than silently approximating unimplemented language behavior.
## Initial Frontend Evidence (2026-09-05)
- Implemented: dependency-free Rust workspace, source directory loading, UTF-8 and line/column handling, deterministic diagnostics, lexer, spanned AST and parser.
- Passed: 20 focused `syntax_and_format` tests, including the complete PRD package; Rust formatting check; focused Clippy with `-D warnings`; diff whitespace check.
- Independent review: fixed iterative expression-tree depth bypass and removed inaccurate terminal-width caret rendering. Follow-up review confirmed both fixes.
- Validation tools: rustc/Cargo 1.98.0 are installed but absent from the default PATH. Rustfmt and Clippy were missing, so their exact 1.98.0 official archives were downloaded to ignored `target/verification-tools/`, checked against the installed Rust manifest SHA-256 values and invoked directly with command-scoped library/tool paths. No global toolchain installation or command replacement occurred.
- At that checkpoint, native/differential/performance checks had not run. The Native Hello Evidence above records the subsequently implemented and verified native subset.
- The previous API-first next step was superseded by the approved hello-world milestone; formatter and other language work remain on the roadmap.
## Historical Original Roadmap Notice
The phase checklist below records the original repository bootstrap plan. Its unchecked boxes are not the current implementation status and do not expand the authoritative extension above. The Active Core Checklist and dated evidence describe completed work.
## Phase 1: Initialize The Repository
### 1.1 Preserve and classify current changes
- [x] Record `git status --short`; current changes contain planning and agent metadata, plus two deleted old documents.
- [x] Confirm no old Rust workspace, implementation or test files exist in the working tree or HEAD.
- [x] Preserve existing user changes; no old implementation needs removal.
### 1.2 Establish the new workspace
- [x] Create `crates/dever-core` as the single language library.
- [ ] Add `crates/dever-runtime` in Phase 7 when numeric/fault consumers exist; keep parser, checker, HIR and evaluator code out of it.
- [ ] Add `crates/dever-cli` in Phase 10 for the `dever` binary; do not expose syntax-only success as the public semantic `check` command.
- [x] Create `test/dever-tests` as the only permanent test crate.
- [x] Create root `Cargo.toml`; pin external dependencies when they are first consumed.
- [ ] Add a decimal128 dependency behind `dever-runtime::number` and reuse that wrapper from the evaluator.
- [ ] Add an insertion-ordered map dependency behind `dever-runtime::collections` and reuse that wrapper from the evaluator.
- [x] Do not add parser generators, async runtimes, plugin systems or CLI frameworks.
### 1.3 Establish current rules
- [x] Confirm the rejected prototype and its fixtures/tests are absent; do not add migration scaffolding.
- [x] Preserve existing deletion of superseded plan/spec documents.
- [x] State in `AGENTS.md` that `.dever` source is the trusted source.
- [x] Replace relevant Trellis spec placeholders with the actual frontend ownership and verification rules.
Rollback point: kickoff adds compiler files and updates canonical planning only; existing user deletions and unrelated metadata are preserved.
## Phase 2: Source, Diagnostics And Lexer
### 2.1 Source ownership
- [x] Implement `SourceId`, `SourceFile`, `SourceMap` and half-open `Span`.
- [x] Precompute line starts and render stable one-based line/column positions.
- [x] Load `.dever` paths in lexical order and reject invalid UTF-8.
### 2.2 Diagnostics
- [x] Implement the shared diagnostic model; semantic phases will consume it when implemented.
- [x] Support primary and related spans plus stable diagnostic codes.
- [x] Sort diagnostics deterministically.
- [x] Add renderer tests for the syntax-phase messages, related source files and Unicode locations.
### 2.3 Lexer
- [x] Tokenize identifiers, keywords, delimiters, operators, newlines and comments.
- [x] Preserve numeric spelling and comment trivia.
- [x] Decode Text escapes with precise bad-escape diagnostics.
- [x] Reject non-ASCII identifiers while allowing UTF-8 strings/comments.
Focused checks:
```bash
cargo test --offline -p dever-tests --test syntax_and_format
```
## Phase 3: Parser And Formatter
### 3.1 Syntax tree
- [x] Define focused AST nodes for package, type, function clauses, patterns, statements and expressions.
- [x] Keep every node spanned; do not attach resolved IDs or runtime values.
- [x] Preserve ordered comments with source spans; formatter attachment remains part of Phase 3.3.
### 3.2 Recursive-descent parser
- [x] Parse the package header and exposure list.
- [x] Parse record and choice type declarations.
- [x] Parse function signatures, clause patterns and linear bodies.
- [x] Parse record/List/Map values, calls, field access and operators with fixed precedence.
- [x] Recover only at bounded declaration/statement delimiters.
- [x] Reject excluded constructs rather than accepting compatibility syntax.
### 3.3 Canonical formatter
- [ ] Format every valid syntax node from structure.
- [ ] Preserve comments and normalize whitespace, blank lines and indentation.
- [ ] Add parse-format-parse and twice-format idempotence tests.
- [ ] Refuse to rewrite invalid files.
Focused check:
```bash
cargo test -p dever-tests --test syntax_and_format
```
Milestone boundary: the parser is currently an internal library for focused tests. Do not expose a public checked-source or formatting command until its complete contracts are implemented.
## Phase 4: Package And Name Resolution
- [ ] Register package/type/function headers before resolving bodies.
- [ ] Validate path/package equality and one-file ownership.
- [ ] Load only the supplied source root and fixed standard catalog; do not search external paths or perform network access.
- [ ] Reserve core single-segment package names and `dever.*` against local declarations.
- [ ] Validate `exposes` against locally declared names.
- [ ] Resolve local short names and cross-package fully qualified names.
- [ ] Enforce visibility without import tables.
- [ ] Group same-name/same-arity clauses and enforce contiguity.
- [ ] Reject package, recursive type and direct/indirect function cycles.
- [ ] Include standard core and qualified package names through one fixed catalog.
- [ ] Report unavailable future official/third-party references as unknown packages without an implicit install fallback.
Focused check:
```bash
cargo test -p dever-tests --test package_resolution
```
## Phase 5: Type Checking And Linear Flow
### 5.1 Type interning and declarations
- [ ] Implement scalar, record, choice, nullable, List, Map, MapEntry, Stream and multi-output types, with static handler inputs represented separately from values.
- [ ] Reject record/choice member mixing and recursive user types.
- [ ] Validate choice payload arity and record completeness.
- [ ] Validate Map key eligibility.
### 5.2 Expressions and calls
- [ ] Check literals using expected type without premature numeric conversion.
- [ ] Implement operator tables exactly as specified in the PRD.
- [ ] Permit only lossless Int-to-Decimal implicit promotion.
- [ ] Resolve positional function calls and named multi-output access.
- [ ] Restrict named handler references to sequence operations, direct static invocation and approved handler-input forwarding.
### 5.3 Definite assignment and mutation
- [ ] Track uninitialized/initialized locals in one forward pass.
- [ ] Reject reads and field writes before complete initialization.
- [ ] Require every output on every clause path.
- [ ] Keep a local's inferred type stable across reassignment.
- [ ] Report unused writes without adding defensive runtime behavior.
Focused check:
```bash
cargo test -p dever-tests --test type_checking
```
## Phase 6: Clause Analysis And Typed HIR
### 6.1 Normalize patterns
- [ ] Normalize Bool, null, choice, scalar constant and `other` patterns.
- [ ] Normalize Int/Decimal comparisons into open/closed intervals.
- [ ] Support at most one lower and one upper bound joined by `and`.
- [ ] Reject Float ranges and dynamic/function-based guards.
### 6.2 Prove clause validity
- [ ] Compute overlap, reachability and exhaustiveness over symbolic domains.
- [ ] Support products of multiple input domains without enumerating numeric values.
- [ ] Emit diagnostics that label both conflicting clauses or the uncovered domain.
- [ ] Prove that permuting source clauses leaves the normalized decision model unchanged.
### 6.3 Lower typed HIR
- [ ] Lower only fully checked packages.
- [ ] Replace names with resolved IDs and attach static types/spans.
- [ ] Build decision nodes from normalized clause domains.
- [ ] Keep HIR internal to `dever-core`; do not serialize it as a second source format.
Focused check:
```bash
cargo test -p dever-tests --test clause_analysis
```
Rollback point: do not add an ordered first-match fallback if coverage analysis is incomplete. Unsupported patterns must fail compilation.
## Phase 7: Values, Numbers And Reference Evaluator
### 7.1 Runtime values
- [ ] Implement the language-owned `Value` variants.
- [ ] Use shared immutable aggregate storage with copy-on-write updates.
- [ ] Wrap insertion-ordered Map behavior and use order-preserving removal.
- [ ] Implement deterministic value rendering and named output order.
### 7.2 Numeric layer
- [ ] Implement checked i64 arithmetic, including negative division/remainder edges.
- [ ] Wrap Decimal128 Context with round-half-even and per-operation status clearing/checks; accept inexact/rounded and fault on overflow, underflow, division by zero or invalid operation.
- [ ] Reject Decimal special values at the `DecimalValue` boundary.
- [ ] Implement exact Int-to-Decimal and explicit Int-to-Float conversion.
- [ ] Reject inexact Decimal literals while allowing specified inexact/rounded arithmetic results.
- [ ] Implement f64 arithmetic and IEEE classification functions without fast math.
- [ ] Add Decimal conformance and numeric fault fixtures.
### 7.3 Evaluator
- [ ] Execute HIR with explicit frames and no mutable global state.
- [ ] Select clauses through compiled decision nodes.
- [ ] Preserve source-level value semantics during local and field mutation.
- [ ] Return named outputs or an uncatchable `RuntimeFault` with source span.
- [ ] Add a Dever call trace only as diagnostic context.
- [ ] Keep evaluator entry points internal to compiler tests; production `build` and `run` must not depend on them.
Focused checks:
```bash
cargo test -p dever-tests --test value_semantics
cargo test -p dever-tests --test numbers
cargo test -p dever-tests --test failures runtime
```
## Phase 8: Standard Functions
- [ ] Define one static signature/evaluator catalog for core and qualified standard names.
- [ ] Implement `append`, `first`, `get`, `put`, `remove` and `entries`.
- [ ] Implement `each`, `reduce`, `reduce_until`, `filter`, `find` and `sum` with shared sequence/handler helpers.
- [ ] Enforce one optional context and element-first handler inputs.
- [ ] Preserve order and short-circuit `find`.
- [ ] Keep Float sum left-to-right.
- [ ] Implement minimum `text`, `decimal`, `float` and `math` functions used by the PRD.
- [ ] Do not add unrequested collection aliases, lazy Stream transforms or reduction names besides `reduce` and `reduce_until`.
Focused check:
```bash
cargo test -p dever-tests --test collections
cargo test -p dever-tests --test numbers standard_functions
```
## Phase 9: Native AOT Compilation
### 9.1 Whole-program specialization
- [ ] Start from one checked, exposed, zero-input entry function and collect its reachable HIR.
- [ ] Specialize every concrete record, choice, nullable, List, Map, Stream and static handler-binding combination.
- [ ] Lower clauses to deterministic native decision trees and collection functions to direct loops.
- [ ] Perform last-use/liveness analysis so dead sources move instead of clone while observable value semantics remain unchanged.
- [ ] Reject any native path that would require a generic runtime `Value`, reflective lookup or virtual per-element dispatch.
### 9.2 Private Rust emission
- [ ] Emit concrete Rust types and direct functions from typed HIR through one structured writer with centralized identifier/literal escaping.
- [ ] Emit explicit checked numeric and source-located runtime-fault operations.
- [ ] Make generated programs depend only on `dever-runtime`, never `dever-core` or the evaluator.
- [ ] Keep generated Rust and Cargo metadata in a compiler-owned transient directory with no public emit command.
- [ ] Compile with an optimized release profile starting at opt-level 3, thin LTO and one codegen unit.
- [ ] Translate toolchain failures into stable compiler diagnostics without leaking generated identifiers as Dever names.
### 9.3 Native conformance and performance
- [ ] Differentially compare evaluator and native outputs/fault categories across the language conformance corpus.
- [ ] Inspect native artifacts to prove the evaluator is not linked.
- [ ] Benchmark scalar arithmetic and List `each`/`filter`/`sum` against equivalent safe Rust using the same algorithm, data layout and toolchain.
- [ ] Benchmark Decimal and ordered Map separately against Rust baselines using the same underlying semantic wrappers.
- [ ] Fail the performance gate when representative median native hot-path time exceeds 1.15 times the matching Rust baseline.
Focused checks:
```bash
cargo test -p dever-tests --test native
cargo test -p dever-tests --test differential
cargo test -p dever-tests --test performance -- --ignored
```
Rollback point: the evaluator remains the semantic oracle, but an incomplete native backend cannot silently become the production `run` path. Unsupported lowering is a compile diagnostic.
## Phase 10: CLI And End-To-End Example
### 10.1 CLI
- [ ] Implement `check`, `fmt`, `build` and `run` as thin adapters.
- [ ] Do not add registry, `add`, `update`, signature or component-cache commands in Core 0.1.
- [ ] Validate exact argument counts and stable exit codes.
- [ ] Keep diagnostics on stderr and results on stdout.
- [ ] Make `fmt` all-or-nothing when any source file is invalid.
- [ ] Restrict `build` and `run` to exposed zero-input functions.
- [ ] Make `build` write an optimized native executable to `--output` and make `run` compile/execute the same native path.
- [ ] Keep the Cargo binary and canonical command name as `dever`; do not rename compiler internals or public command semantics to `deverc`.
- [ ] Treat `deverc`, when explicitly installed for coexistence, as a thin link or launcher to the same compiler binary with no divergent behavior.
- [ ] Do not replace the existing framework `dever`, modify system `PATH` or create global executables during repository implementation or automated tests.
- [ ] If global `deverc` setup is later authorized, resolve and record both command targets and verify that the existing global `dever` remains unchanged.
### 10.2 Example and integration
- [ ] Add the canonical account/user package from the PRD.
- [ ] Add a zero-input executable example covering types, clauses, collections and numbers.
- [ ] Verify multiple source files and full qualification.
- [ ] Verify the zero-input example produces identical evaluator and native results.
- [ ] Ensure no example source depends on Rust, JSON graph, React or a hidden host fallback.
Focused check:
```bash
cargo test -p dever-tests --test cli
```
## Phase 11: Cleanup And Final Verification
### 11.1 Structural review
- [ ] Search for duplicate operator, standard-function, diagnostic and type rules.
- [ ] Keep each language rule owned by one table/module.
- [ ] Remove obsolete compatibility code, commented code, placeholders and old architecture references.
- [ ] Confirm the CLI contains no language semantics.
- [ ] Confirm no public Rust API leaks into `.dever` diagnostics or docs.
- [ ] Confirm generated binaries depend on the minimal runtime but not compiler/evaluator modules.
- [ ] Confirm private generated Rust is not documented or tested as a public compatibility format.
### 11.2 Acceptance mapping
- [ ] Map all 20 PRD acceptance criteria to named test cases.
- [ ] Run each relevant focused test target.
- [ ] Run static formatting and diff checks.
- [ ] Record passed, failed, skipped and not-run checks separately.
Permitted final checks, announced before execution:
```bash
cargo fmt --check
cargo check -p dever-core -p dever-runtime -p dever-cli
cargo test -p dever-tests --test syntax_and_format
cargo test -p dever-tests --test package_resolution
cargo test -p dever-tests --test type_checking
cargo test -p dever-tests --test clause_analysis
cargo test -p dever-tests --test value_semantics
cargo test -p dever-tests --test collections
cargo test -p dever-tests --test numbers
cargo test -p dever-tests --test failures
cargo test -p dever-tests --test native
cargo test -p dever-tests --test differential
cargo test -p dever-tests --test cli
python3 ./.trellis/scripts/task.py validate 09-04-dever-language-mvp
git diff --check
```
The performance test is an explicit, separately announced gate and is not part of the default test list. Do not substitute `cargo test --workspace`, `npm run build` or service/integration tests. If a focused check reveals a shared compiler defect, fix the owning implementation once and rerun only affected targets.
## Finalization
- [ ] Run `trellis-check` after implementation.
- [ ] Update canonical Trellis specs for the new crate ownership and language rules.
- [ ] Review the final diff against PRD scope and remove accidental abstractions.
- [ ] Do not commit until the user explicitly authorizes a commit.
- [x] Confirm there are no obsolete planning children to archive.
# Active Compiler Contract Checklist (2026-09-08)
- [x] Contract syntax, formatter and semantic metadata.
- [x] Field privacy, numeric facts, construction/write/output constraints.
- [x] Failure obligations and explicit recovery, including nested values and static handlers.
- [x] Transitive effects, package dependencies and API snapshot/CLI enforcement.
- [x] Evidence-backed redundancy diagnostics and application export analysis.
- [x] Official source/examples migration, targeted tests, native equivalence, docs/specs and final review.
User approved the full contract implementation. Complete the list before claiming delivery. Keep earlier evidence below as historical; no full test suite, external services, distribution work or commit.
## Compiler Contract Evidence (2026-09-08)
- Implemented private record fields, normalized Int/Decimal field/payload/output/handler bounds, explicit error/recover and pure/depends. HIR facts reuse existing clause domains; handler boundaries support safe variance and concrete output inference without forcing wrapper annotations.
- Failure obligations follow copies, nested fields, calls and collections. Official Failed variants are explicitly marked; HTTP transport preserves earlier failure state and returns the inspected result with its stop decision. The Stream example now propagates a CountResult failure instead of silently returning zero. Presentation/test recovery reasons are explicit and consistent across clauses.
- Effects and recovery policies propagate through invoked static handlers. API generation is create-new; check/run/build compare an existing baseline. Adding a private recovering helper changes the effective public snapshot without exposing that helper's name. Depends includes normalized system resource types. W001 is reused; W002–W005 and entry-scoped W007 are advisory, not arbitrary abstraction requirements.
- Final six contract targets passed 47 tests: syntax 4, facts 16, semantics 10, API 9, redundancy 6, execution 2. Existing core semantics passed 16 (15 in the first run, W001 assertion narrowed to its diagnostic then rerun); existing syntax passed 22, formatter passed 11 and the new contracts example roundtrip passed. The independent expected-output core differential case passed. Forty-one migrated source fixtures were checked without executing their network/resource scenarios; the temporary probe was removed.
- Actual CLI api/check/run/build and standalone execution with `env -i PATH=/nonexistent` passed. A temporary source's new exposure was rejected by its unchanged baseline. `examples/contracts/src/dever.api` records the example's contract. Strict installed-driver Clippy/type checks passed for core/CLI and focused contract tests; formatting and explicit whitespace checks cover untracked files as well.
- Independent review found and closed empty-reduce seed consumption, primitive resource dependency omission, and exponential/deep proof graphs. Added positive/negative regressions; immutable shared fact/value graphs preserve aliases, proof work is capped at 16,384 and iterative nominal depth validation caps 128 before recursive analysis. Exhaustion reports C015; no incomplete proof becomes a successful Program.
- Updated LANGUAGE.md, IMPLEMENTATION.md, README and the owning compiler-contract spec. No database/distribution expansion, full suite, integration environment, existing service, global installation or Git commit. The umbrella task stays active for its previously deferred roadmap; this approved contract extension is complete.
## Direct Example Roots And Coverage (2026-09-08)
- Moved all five original source roots from `examples/<name>/src` to `examples/<name>`, including the unchanged contracts API baseline. Removed empty src directories; package-relative nesting, such as hello/app/user.dever, remains. Updated executable test references, current guides and owning specs; earlier dated evidence retains its historical paths.
- Added sequences, text, privacy, parallel and system examples, bringing runnable roots to ten. Added six separately checkable rejected roots for C006/C009/C011/C012/C013/C014. The examples index documents commands, outputs, nondeterministic ordering, runtime input paths and explicit recovery boundaries.
- Reused compiler/check/native APIs in one example runner. Automatic discovery checks every runnable root and any baseline; formatter coverage discovers all examples, including rejection fixtures. No compiler or runtime behavior changed.
- Passed seven example-target tests covering all ten successful roots, nine native executions and six rejection cases; the existing core native test separately verified its 22 expected outputs. The targeted formatter roundtrip passed over every official/example source. Actual CLI contract check and expected C012 rejection passed; example fmt --check passed. Strict Clippy/type checks compiled all affected test targets, including feature-gated differential references.
- No full suite, network/service integration, compiler feature expansion, global installation, commit or archive.
## Isolated PostgreSQL And Dual-source CMS Measurements (2026-09-18)
- In a nonprivileged, owned PostgreSQL 16 instance with a private Unix socket and no TCP listener, the ignored `postgres_orm_uses_the_configured_isolated_database` test passed 1/1. Its shared schema cleanup left no test schemas. Added a focused live named-migration probe for bound before/after SQL, atomic rollback on after failure, successful DDL and once-only re-execution. Test connection came only from a temporary root `config/setting.json`; no database URL environment variable or existing PostgreSQL service was used. After verification, the temporary setting was deleted, the owned instance stopped and its test data removed.
- Focused `cms-profiles` build compiles both current `examples/cms/{dever,md}` source formats using one shared benchmark entry and separately builds base/SQLite/PostgreSQL/both runtime profiles. Artifacts: `target/performance/cms-dual-build-09-18-v2/manifest.json` and `profile-report.json`. Profile bytes: 411,352 / 2,558,200 / 2,708,112 / 4,672,888. CMS Dever/Markdown bytes: 3,151,352 / 3,151,416.
- `target/performance/cms-dual-rss-09-18/report.json` completed all six runs (three per source, READY=3 throughout). Dever/Markdown median readiness 19.87/19.69 ms, idle RSS 5.37/5.37 MiB, PSS 3.28/3.33 MiB, VmHWM 5.37/5.37 MiB. This is warm-cache initialization and bounded idle sampling, not throughput. No hard 64/128 MiB CMS budget or cgroup peak: the local controller is not writable. No broad/full suite, existing service check, or global installation.
- Benchmark runner unit tests pass 40/40; focused test compiled with `--features postgres`; Trellis task context validation passed. Rustfmt and Clippy are unavailable in the installed toolchain. This root roadmap remains in progress; API stays deferred pending its redesign, and hard-budget CMS stress requires a delegated cgroup environment.
# Source Visibility And Module API Implementation (2026-09-18)
- Replaced `package/exposes` syntax with path-derived package identity and explicit `public` for cross-package type/function visibility; fixed private `main.main` as the only run/build entry. Updated bundled sources, CMS Dever/Markdown roots, old runnable examples and Markdown metadata. The hello example now delegates through a public library function to its own `main.dever`.
- Added statically checked module `api/` routes with GET/POST/DELETE naming, recursive paths, private handlers and one `response` output. `dever.api.serve()` opts the application into routing and the existing bounded HTTP engine; typed inputs, JSON serialization and `{code,message,data}` envelopes reuse a single runtime adapter. Listener settings are read from `config/setting.json`, not environment variables; no CMS business API or authentication policy was added.
- Focused checks: core/CLI/runtime/tests cargo check passed; `source_visibility` 10/10, `syntax_and_format` 22/22, `core_semantics` 16/16, `formatter` 11/11 and `markdown_source` 20/20 passed. `api_routes` passed 7/7 before the final test adjustment, then its application-mode native compilation passed 1/1 with config bootstrap and no listener. An isolated temporary-project CLI `check/run/build` and API-baseline rejection test passed. Maintained examples, CMS Dever/Markdown source parity and rejection fixtures passed selected contract tests. Two ORM fixture static-check tests passed without database connections. `deverc check` passed for hello and both CMS formats (CMS retains W001 unused-write warnings).
- Not run: full suite, live HTTP/API service check, real PostgreSQL, CMS runtime/build or benchmarks. Rustfmt and Clippy binaries are unavailable in the installed toolchain. The umbrella task remains in progress for separately deferred roadmap work; historical entries above retain their original date/context.
# AI-Oriented Source Architecture Implementation (2026-09-19, Complete)
- [x] Add one shared source-role classifier and project-layout validation.
- [x] Replace application `public` visibility with automatic App capabilities and logical App aliases.
- [x] Enforce role dependency direction, API non-callability and Model-operation ownership.
- [x] Migrate the Dever and Markdown CMS sources from `service/` to component/domain roles without function-per-file fragmentation.
- [x] Update the language, Markdown and backend compiler/database contracts.
- [x] Add focused layout, visibility, API, Model and CMS parity tests; run only the affected checks.
The implementation does not add environment variables, implicit HTTP startup, authentication policy, a new Port declaration syntax, or backward compatibility for the superseded application layout.
## AI-Oriented Source Architecture Evidence (2026-09-19)
- `SourceLayout` is the single main/component/domain/role/topic classifier. Strict project loading rejects loose application paths, generic buckets, role file/directory conflicts, nested non-API topics and one-file role directories. The check cache includes strict-layout identity; in-memory compiler fragments remain isolated from project-layout enforcement and cannot weaken CLI projects.
- App declarations receive stable `<component>.<domain>.<name>` identities without exposing topic filenames. Role-aware lookup enforces Main/API/App/private-role direction, API non-callability and owning-domain Model operations. App signatures and inferred failures reject private type leakage. Port remains a reserved, rejected declaration role.
- Plain and Markdown CMS now contain only `main`, `user/account/{app,domain,model}` and `news/article/{app,model}`. Both forms have equal normalized declarations, Model/API snapshots and settings. Current examples contain no package/exposes/public syntax, old service/model roots or environment-variable configuration.
- The current performance packager now stages `benchmark/<domain>/app.dever` plus generated `main.dever`; it no longer generates package/exposes headers or passes arbitrary entries to `deverc build`. ORM, profile, Dever CMS and Markdown CMS staged projects all passed actual CLI check. The CLI CMS regression test compiles against the fixed main-only interface and uses a test-owned App probe instead of injecting a loose root source.
- Focused verification passed: core/runtime/CLI Cargo check; `source_architecture` 8/8; `source_visibility` 10/10; `orm_performance_fixture` 3/3; `concurrency` 5/5 with its explicit benchmark ignored; CMS source/config parity 1/1; archived parallel and source-root native regressions passed after the synchronous typed-error fix. Earlier in this same phase `api_routes` passed 7/7. Actual CLI `check` and `fmt --check` passed for both CMS formats, and Python syntax checking passed for the performance builder.
- Not run: full workspace suite, live CMS `run/build`, `cms_project` integration execution, HTTP listener, real PostgreSQL, performance/size/RSS builds or 64/128 MiB stress. Rustfmt and Clippy components remain unavailable. The umbrella language roadmap stays in progress for separately deferred work.
@@ -1,990 +0,0 @@
# Dever Language Definition
## N5:HTTP/2(已完成)
用户在 P2 性能验证结束后批准继续接入 HTTP/2。使用 Hyper 实现 buffered/live 服务端和 single-use/pooled 客户端;复用既有请求/响应、TLS、AsyncStream 与 SSE 接口。Limits.http2 显式配置协议和每连接流数、接收窗口,TLS 必须协商 h2,明文使用 prior knowledge。每流取消隔离,Hyper 内部任务保留 Dever Scope,GOAWAY 排空不自动重试已发请求。详细验收以 `../09-12-http2/prd.md` 为准。
N5 已由 88 项定向测试、原生执行和独立审查验收。后续 HTTP/2 性能矩阵与有界稳态/恢复压测已由子任务完成;小时级稳定性、编译器发行/跨平台、自举及其余路线图仍未完成。
仅定向协议、生命周期与原生桥接验证;不重跑 P1/P2 性能矩阵,不改根任务调度,不接 RFC 8441、h2c Upgrade、推送、trailer、数据库或发行工具。下文各轮排除范围仅约束相应历史阶段。
## 已完成阶段 N4:并发组合与可复用网络客户端(2026-09-12)
用户批准完成上一轮清单的 1 和 2。交付源码运行参数、静态 HTTP handler 的类型化上下文、任务超时/多路竞争等待、周期与 Channel 流、异步内存序列遍历;有界 HTTP 连接池、域名、流式请求/响应、HTTPS/WSS 客户端和 TLS 服务端。保留 N3 SSE/WS 会话、取消和背压契约。HTTP/2、数据库、协议扩展和分发工具不属于本轮。
任务、连接及正文队列有界;取消和关闭回收所属工作,已开始的阻塞工作必须排空。复用 Tokio、Hyper、tungstenite 和 Rustls,不维护兼容接口或自写协议栈。只运行定向编译/测试及自有 loopback 随机端口,不测旧性能基线,不启动现有服务,不提交。
以上范围已接通 checked source、原生编译和运行时。83 项定向用例、实际 CLI check/fmt/build 与无 Rust 工具链环境中的独立后端示例通过;具体证据见 implement.md 的 N4 Evidence,公开用法以 LANGUAGE.md 为准。
## 已完成阶段 N3:流式响应、SSE 与 WebSocket(2026-09-12)
用户已授权“做下一阶段”,沿用新语言直接演进和低内存运行目标。保留普通 HTTP 的直接 handler 路径,增加 `serve_live` 和 HttpReply:同端口处理完整响应、逐块正文、SSE 和 WebSocket。HTTP 请求仍有界缓冲;响应通道只有 1 个槽位,长连接配置与普通请求期限分开。
WebSocket 服务端/客户端复用 tokio-tungstenite 0.30.0(handshake feature),提供 Text/Binary/Ping/Pong、AsyncStream 消息消费和关闭握手。SSE 提供字段编码、Last-Event-ID 访问和空闲注释心跳,历史重放归应用所有。HttpReply/WebSocket 进入编译器资源、类型、effects、API、Markdown 和 native 契约;不引入新语法、旧 API 兼容或自写 WebSocket 协议栈。
连接、handler 及升级后会话共用 Scope/Group 上限与取消排空;等待写锁时取消不能破坏另一任务的帧。仅执行根 test/ 下的定向编译器/运行时验证和自有回环端口,不测试旧性能基线、常驻示例或已有服务。TLS/WSS、HTTP/2、客户端池、子协议/压缩扩展、关闭帧元数据和数据库不属于 N3。
## 前一阶段 N2:HTTP 引擎(2026-09-12,已完成)
用户在 N1 完成后批准继续。采用 Hyper 1.x HTTP/1 引擎、hyper-util TokioIo/Timer 和共享 bytes 缓冲区;不额外引入路由框架。此阶段取代历史“HTTP 全部由 Dever 自写”的实现决定。官方类型与静态业务 handler 仍由 Dever 定义,不保留旧协议 API 或备用解析器。
- `serve(async handler, Listener, Limits) -> ()`:持久连接、流水线、chunked 输入,复用现有有界任务组、背压、超时和取消清理。accept/配置/程序故障沿任务传播,对端协议错误只结束对应连接。
- `send(address, port, Request, Limits) -> ResponseResult`:单次请求,无客户端池和重试,driver 与请求同生命周期。
- Header 列表保留重复头和字节值;消息体在显式上限内缓冲,单帧共享,多帧增量合并。
- 直接迁移示例、检查器、native bridge 和测试。只用离线定向检查、自有 loopback 随机端口,不测旧基线、不跑全量或现有服务。
- TLS、HTTP/2、流式响应、客户端池、非空 trailer、WebSocket/SSE 留待后续;N3 优先处理长连接协议。
## 前一阶段 N1:异步 Stream 与 TCP(2026-09-12,已完成)
用户批准继续,并明确这是全新的语言:无需旧源码兼容,不增加兼容别名、回退实现或推测性的防御分支。仓库调用方直接迁移。此决定取代下文历史阶段中的“网络暂停”和“兼容基线”要求。
- 增加 `AsyncStream<Item>`,支持在 async function 中按需 `each`、`reduce`、`reduce_until`,以及有界 `parallel_each`;handler 可为 async,关闭/提前结束释放生产端。
- `dever.net` 直接采用 Tokio 异步 TCP;connect、listen、accept、read、write 为 async。资源名称仍为 Socket、Listener;无 sync/aio 两套 API。
- 连接流和字节流不预取;并发消费先等待容量再拉取。复用现有结构化 Task/Group,停止后等待子任务清理,连接支持同时读写。
- 保留协议必须的输入约束、失败输出、超时、关闭和取消语义;写入中断不得自动重发已写字节。
- 现有 HTTP 传输调用链随 TCP 改为 async;成熟 HTTP 引擎接入属于 N2,WebSocket/SSE 属于 N3,本轮不实施。
- 只做根 test/ 下定向检查及自己创建的 loopback 随机端口验证;不测旧性能基线,不运行全量测试、业务服务、全量 build 或全局安装。
## 前一阶段:并发基础(2026-09-12,已完成)
前一阶段先完成同步、异步和多线程(含协程/任务)基础,网络当时暂停;当前 N1 恢复异步 Stream/TCP。下述并发契约已经进入编译器前端、HIR、原生后端和 runtime;最终状态以本任务的定向验证和 `LANGUAGE.md` 公开契约为准。不测旧实现性能基线。
### 暂存的网络方案
- 后续需要 TCP、HTTP、WebSocket、SSE 长连接,复用成熟 Rust 库。
- 候选栈:Tokio 统一网络调度,Hyper/hyper-util 处理 HTTP,Axum 复用服务路由、升级和 SSE,WebSocket 复用其 tungstenite 生态,bytes 管理共享缓冲区。实施前核对版本、工具链和 feature 兼容性。
- 共用连接限额、有界队列、背压、超时、取消与关闭。长连接保活独立于普通请求超时;SSE 事件历史保存/重放属于应用责任。
- 不在每次 I/O 上 block_on,也不把同步阻塞 handler 直接放入异步调度线程。HTTPS/WSS、HTTP/2 的首轮范围尚未确定。
- 取消旧性能基线工作,不取消新能力必要的定向正确性验证;不启动业务服务或压测。
### 并发语言契约(已批准实施)
#### Goal
增加可明确推理的同步、异步协程和多线程能力,为 TCP、HTTP、WebSocket 与 SSE 提供统一运行基础。小机器只运行打包后的二进制;空闲任务不占用一个系统线程,任务、线程和队列都有明确上限。契约可以直接演进,维护值/资源语义的清晰性,不承担旧源码兼容义务。
#### In Scope
- 普通 function 保持同步;`async` 声明异步 function,`await` 等待异步调用或已启动任务。
- `run` 启动异步调用并返回受限 Task;`stop` 采用“请求协作取消并等待清理完成”的安全语义,而不是仅发送信号。
- 每个 async function 是默认结构化任务域;动态数量的零输出任务可由有界 `group` 统一拥有、等待和停止。
- `parallel` 将 pure 同步计算交给有界多线程执行;`blocking` 供明确的阻塞边界使用。`parallel_each` 按序列类型复用相应的有界执行器。
- 有界 `channel` 以及 `send`、`receive`、`close` 用于任务通信;禁止无界队列。
- async 静态 handler、跨线程可传递性、任务消费、effects/failures、API 快照、Markdown 契约、格式化和原生代码生成均由编译器检查。
- 运行时复用 Tokio 的协程与多线程调度;整个程序最多建立一个异步运行时,CPU/阻塞任务使用受限执行通道。
#### Out of Scope
- 本阶段不实现 TCP、HTTP、WebSocket、SSE、TLS 或数据库;网络方案按上节暂存。
- 不加入裸线程、共享可变全局对象、用户锁、分离后无人管理的后台任务、无界 channel、强杀线程或副作用回滚。
- 不同时加入 lambda、闭包、一等 function、用户泛型、用户递归、循环块或异常机制。
- 同步 `Stream<Item>` 保持当前阻塞拉取契约;异步流在网络恢复前单独设计,不做隐式转换。
#### Acceptance Criteria
1. 完全同步的入口不启动 Tokio runtime;接口发生变化时直接迁移仓库源码。
2. async function 和 async 静态 handler 的调用都必须直接位于 `await(...)` 或 `run(...)` 上下文;同步 function 中的异步调用在原生构建前拒绝。
3. `await(call(...))` 顺序执行并保持原函数的命名输出;`task = run(call(...))` 先启动,`await(task)` 恰好消费一次任务并取得相同输出。
4. Task 不能复制、存入 record/List/Map/Stream、作为普通参数/输出逃逸、重复等待或在 function 返回时仍未消费;必须 `await` 或 `stop`。
5. `stop(...)` 第一阶段只接受零输出 Task 或 Group,协作取消并等待清理;已发生的副作用不回滚,长时间同步计算不承诺立即停止。
6. `group(limit)` 的 limit 必须为正且有实现上限;只拥有零输出异步任务。容量已满时 `run(group, call(...))` 施加背压,不无限排队;`await(group)` 或 `stop(group)` 消费该 Group。
7. `parallel(call(...))` 只接受 pure 同步 function,参数和输出必须可跨线程传递;`blocking(call(...))` 只接受编译器传递 effect 分析确认包含阻塞系统边界的同步调用。二者第一阶段只能用于 async function,并使用明确的有界容量;同步代码继续使用现有 `parallel_each`。
8. `channel(Type, capacity)` 容量必须为正且有实现上限;send 在满时等待,receive 在空时等待并返回 `Item?`(值表示 Item,`null` 表示 Closed),close 唤醒等待方;调度器或 Channel 内部故障作为带源码位置的 fatal error 传播。
9. async handler 的同步/异步属性是签名的一部分;所有同签名分句必须一致,专门化和递归检查不能因异步边界失效。
10. 任务启动、等待、停止、channel、时间等待和 blocking 均进入传递 effect 分析;pure function 不能隐藏这些效果,失败义务不能因任务边界被吞掉。
11. 异步入口只创建一次运行时;多个挂起任务不按任务创建线程。工作线程、阻塞线程、group、channel 与待处理任务均有保守默认上限及显式配置边界。
12. 仅运行根 `test/` 下针对新增语法、检查、原生执行、取消/关闭和有界调度的离线定向测试;不跑全量测试、服务测试、旧实现基线或网络压测。
#### Key Naming Decisions
- 保留 `async`、`await`、`parallel`、`blocking`、`channel`、`send`、`receive`、`close`。
- 使用 `run` 代替 spawn,使用 `stop` 代替 cancel,使用 `group` 表示动态任务所有者;不再增加 join,统一由 `await` 等待。任务操作沿用函数式调用外观,不提供第二套前缀语法。
- 协程是 async Task 的运行时实现,不增加 coroutine 关键字。
## Markdown Source Follow-through (2026-09-09)
用户批准保留 `.dever`,新增 `.dever.md` 源码容器,并进一步要求编译器强制说明与代码一致。顶层 `dever` 或 `typescript dever` 围栏内写正常 Dever 声明,共用原有语法、契约、API 和原生路径。一个 H1 说明 package;每个 H2 只说明一个 type 或一个 `(函数名, 参数数量)` 及其全部分句。编译器核对 package 名、公开类型/方法/使用方式、类型字段/分支、函数输入输出的名称、类型、数量和顺序,空项显式写 `无`。`typescript dever` 的首个标记供编辑器尝试 TypeScript 语法高亮。跨格式同包重复拒绝;格式化保留说明,诊断定位原文并关联代码。此决定替代下方历史阶段“只加载 `.dever` 文件”及“标题说明无约束”的范围限制,不开放其它语言或 HIR/JSON 作为源码。正式写法见 `LANGUAGE.md` 的 Markdown 小节。
## Approved Performance Follow-through (2026-09-08)
用户批准实施本轮性能方案。保持现有语法、值/资源语义、错误位置、数值顺序和编译器契约。交付类型属性缓存、依赖驱动契约分析、记录更新复制消除、独占列表消费、直接消费字符列表的遍历优化、HTTP 增量扫描、标准库及原生产物缓存。循环融合必须证明副作用和失败顺序等价;并发分批以有限测量为依据,不要求线程池。补编译/运行/内存定向基准并记录优化前后证据。只运行离线定向测试、自有临时文件/进程和内存协议输入,不运行全量或服务压测,不提交或安装全局命令。
## Current Follow-through (2026-09-07)
用户在剩余项盘点后要求“实施剩下的”。继续完成已确定语言的工具、标准库和验证工作,并推进后续运行能力;既有语法、分句、静态 handler、值/资源语义保持兼容。下方历史里程碑的暂缓项不阻止本次明确要求的工作。
- 提供 `.dever` 的规范格式化及 CLI `fmt`/只读检查,保留注释、表达式含义和求值顺序;任一输入语法错误时不改写文件。
- 补常用 Text、数值解析/格式化、进程参数/环境和时钟接口。公开接口与组合逻辑仍由普通 `.dever` package 定义。
- 实现仅用于验证的 HIR 基准解释器,覆盖已实现核心语义并与原生结果、故障位置对照;生产 build/run 始终使用原生编译。
- 实现保持值语义的最后使用优化,提供可复现、有限工作量的同语义性能基线,不对现有服务压测。
- 继续推进 JSON、HTTP 与受控并发等官方包所需能力;协议与应用逻辑使用 Dever 基础语法,系统边界保持最小。
- 数据库引擎此前未定,已向用户异步询问;这一选择不阻塞其余工作。
- 编译器发行、跨平台沿用此前暂缓安排;自举仍是尚未交付的长期项。本次不安装或替换全局命令,不提交,不访问既有服务。
验收按每项真实完成的行为和定向证据记录,不将已完成的基础语法测试当作新增工具和库的验收。
## Goal
当前目标是实现已经确定的基础语言,使官方 package 与应用能够用同一套 `.dever` 语法编写。2026-09-07 用户批准实现基础类型、表达式、函数输出、分句、赋值和值语义、集合与处理函数、统一 package 编译及最小系统接口。原生 Hello World 里程碑已于 2026-09-06 完成,作为回归基线。
长期目标仍是改善 AI 开发中的硬编码、重复实现、无依据兜底和大项目上下文问题。表面语法保留 package、type、function、分句和值语义;用户所说的“用户类”用已有 `type User` 表达,不引入 class 或继承。Rust 等引导技术不进入用户源码或语言兼容契约。
基础阶段曾将 HTTP、JSON 和性能验证后移;当前 Follow-through 已推进这些能力。编译器发行与跨平台仍暂缓,数据库引擎未定;用户 CRUD 示例不是基础语言的额外交付。保留原生执行方向和既有表面语法,不增加契约 DSL。
## Approved Core Implementation
- 沿用既定条件分句、线性 body 和集合函数;不再把循环语法作为待确认项,不增加条件块、循环块或用户递归。
- 每项语言能力贯通源码检查、typed HIR 与原生执行;不能以 AST 已解析代替支持。
- 官方 `.dever` package 与本地 package 共用语言规则。官方命名空间由编译器随附源码提供,本地源码不能冒充官方来源。
- 系统接口提供 Bytes、opaque 文件/TCP 资源、读写和显式关闭;普通值保持值语义,资源别名标识同一外部资源,关闭后操作得到显式失败。
- 可恢复系统错误以官方源码声明的选择型结果返回;程序故障保持源码定位。系统原语不包含 HTTP 路由或应用业务。
- 验收覆盖完整语言样例、负例诊断、分句顺序变化、集合顺序与值语义,以及隔离临时文件和本地回环连接。使用必要的最小定向检查,不运行全量测试、既有服务或压测。
## Approved Foundation Extension
Status: implemented and verified on 2026-09-07.
### Goal And Scope
补齐官方 `.dever` package 编写协议解析、持续 I/O 组合和宿主回调所需的最小表达能力,同时保留分句条件、线性 body、无循环 block 和无用户递归的长期规则。本阶段一次完成 `reduce`、`reduce_until`、Bytes 遍历、`Stream<Item>`、受限静态 handler 参数,以及文件/TCP 的流式包装;不在 runtime 中加入 HTTP 或业务特例。
### Sequence Operations
- 带状态遍历对外名称固定为 `reduce`;此前讨论中的 `fold` 和 `accumulate` 名称作废。
- `reduce(handler, sequence, initial_state)` 固定从左到右执行,始终显式提供初始状态;空序列直接返回初始状态。
- handler 依次接收当前元素和上一步状态,必须输出一个与状态类型相同的下一步状态:
```dever
add(value: Int, total: Int) (next_total: Int) {
next_total = total + value
}
total(values: List<Int>) (result: Int) {
result = reduce(add, values, 0)
}
```
- `reduce_until(handler, sequence, initial_state)` 使用相同顺序,但 handler 必须有两个输出:第一项是下一步状态,第二项必须命名为 `stop` 且类型为 Bool。处理完当前元素后,`stop` 为 true 就立即返回,不再请求下一个元素。
- `reduce` 和 `reduce_until` 不提供额外 context 参数;共享配置应组合进状态,避免同一调用同时维护两套跨元素数据。
- sequence 只接受 `List<Item>`、Bytes 或 `Stream<Item>`。Bytes 的元素静态类型为 Int,运行时值保证在 0 至 255,不先复制为 `List<Int>`。Text 如需按 UTF-8 遍历,必须显式使用 `dever.bytes.from_text`。
- `each(List/Bytes)` 保留零输出动作和单输出 List 映射;`each(Stream)` 只接受零输出 handler,不把未知长度或无限输出隐式收集到内存。Stream 的最终状态由 `reduce`/`reduce_until` 返回。
- `filter`、`find`、`sum` 本阶段仍只处理 List;不增加 Stream 到 Stream 的惰性 map/filter 管道。
### Static Handler Inputs
function 输入可以声明受限 handler 签名:
```dever
serve(
route: handler(request: Request) (response: Response),
config: Config
) (result: ServeResult) {
result = serve_with(route, config)
}
```
- handler 类型只允许出现在 function 输入中;输入按位置匹配类型,命名输出同时匹配名称、顺序和类型。
- 调用方只能传入签名匹配的具名 function,或转发当前 function 的 handler 参数。
- handler 参数可以直接调用,也可以传给 `each`、`reduce`、`reduce_until` 或另一个 handler 输入;不能参与分句匹配,不能赋给局部值、存入 record/List/Map/Stream、比较或作为输出返回。
- handler 不是运行时值。typed HIR 保留静态绑定,AOT 以 function 和实际 handler 绑定组成专门化实例,生成直接调用;不产生函数指针、闭包、虚调用或反射注册表。
- 递归检查覆盖 handler 调用与转发后的专门化调用图,不能通过 handler 绕过无递归规则。
### Stream And Resource Contract
- `Stream<Item>` 是编译器提供的唯一新增标准泛型类型,用户仍不能声明泛型 type 或泛型 function。
- Stream 是拉取式、顺序、一次消费的 opaque 资源;复制 Stream 只产生同一资源的别名,所有别名共享读取位置、结束状态和显式关闭状态。Stream 不可比较,也不能成为 Map key。
- `reduce_until` 提前停止时 Stream 保持可用,调用方可从下一个未消费元素继续处理。正常耗尽后继续消费只得到结束,不会重新执行生产者。
- 正常 EOF 使用 Stream 自然结束表示,不作为普通元素。可恢复 I/O 错误必须作为官方 choice 的 `Failed(message: Text)` 元素交给 handler;终止性错误产生一个 Failed 后结束 Stream,不能转换成默认值或程序故障。
- 流式读取使用 `dever.io.ReadEvent`,其分支固定为 `Chunk(bytes: Bytes)` 和 `Failed(message: Text)`;现有单次 `read` 继续返回 `ReadResult.Read/End/Failed`,两种接口不混用结束语义。
- 流创建结果固定为 `dever.io.ReadStreamResult`,其分支为 `Streaming(stream: Stream<ReadEvent>)` 和 `Failed(message: Text)`。`dever.io.chunks(file, limit)` 与 `dever.net.chunks(socket, limit)` 都返回该类型;非正数、无法表示的 limit 或创建时已经关闭的源立即返回 Failed。
- `dever.net.connections(listener)` 直接产生 `Stream<ConnectResult>`;创建后关闭 listener 或终止性 accept 错误产生一个 `ConnectResult.Failed` 后结束。
- 核心操作 `close(stream)` 没有输出并且幂等。它停止该 Stream 及其别名并释放 Stream 持有的底层资源引用,但不主动关闭调用方仍单独持有的 File、Socket 或 Listener;关闭后的 Stream 与正常耗尽一样不再产生元素。调用方先关闭底层资源时,依赖它的未关闭 Stream 产生一个 Failed 后结束;最后一个资源别名离开作用域时仍由原生资源析构兜底。
- 首阶段只有阻塞、顺序执行;Socket 延续显式 timeout。没有预取、后台线程、async、await、并发消费或共享可变业务全局状态。
### Acceptance Criteria
1. List 和 Bytes 的 `reduce` 按源顺序传递状态,空序列返回初始状态;Bytes 不经过中间 List。
2. `reduce_until` 在 stop 元素之后不调用 handler 或拉取 Stream;同一 Stream 随后从未消费位置继续。
3. `each(Stream)` 接受零输出 handler,拒绝单输出或多输出 handler;List/Bytes 的既有映射和动作语义不回归。
4. handler 输入支持具名 function、跨 package function 和多层静态转发;签名不匹配、存储、返回、比较和 handler 间接递归均在原生构建前拒绝。
5. 生成程序中的 handler 是专门化直接调用;不引入通用 runtime Value、运行时函数指针或逐元素虚拟 handler 分派。
6. Stream 别名共享游标、结束和关闭状态;自然 EOF、提前停止、幂等 `close`、底层关闭与终止性错误均有确定测试。
7. 官方 `dever.io`/`dever.net` 流式接口作为普通 `.dever` 源码经过同一 parser、resolver、checker 和 HIR;runtime 只拥有系统读取和 Stream 拉取原语。
8. 文件验证只使用测试自有临时目录,TCP 验证只使用 `127.0.0.1` 随机端口、有限数据和超时;不连接、启动或修改现有服务。
9. 实际 `check`、`run`、`build` 与独立原生程序覆盖一个分块状态解析样例,证明结果来自 `.dever` handler 和状态,而非编译器特判。
### Out Of Scope
- HTTP、JSON、CRUD、数据库、并发服务器和异步语法;本阶段交付的是这些 package 可复用的底层表达能力。
- 一般化用户泛型、lambda、闭包、一等 function、Stream 惰性转换、循环 block、用户递归和异常机制。
- formatter、语义基准解释器、性能门槛、自举、package 分发、跨平台构建和全局命令迁移。
### Known Risks And Controls
- 静态 handler 转发可能产生重复原生实例;专门化键按 function 与完整 handler 绑定去重,并且只生成入口可达实例,不退化为运行时分派。
- Stream 的内部生产者调用有固定边界成本;文件和 Socket 以 Bytes chunk 为单位拉取,不按单字节跨越动态边界,有限 List/Bytes 热路径仍是直接循环。
- 首阶段的阻塞 Stream 不能承担并发服务器;这是明确范围限制,不能在 HTTP package 中用隐藏线程或全局队列规避。
- 把 I/O 失败作为最后一个元素要求 handler 穷尽处理官方 choice;类型检查和终止状态测试共同防止错误被静默丢弃或无限重复。
## Completed Hello Milestone
- 示例位于 `examples/dever/hello/app/user.dever`,与 `package app.user` 路径一致。
- 支持 Text 字面量、Text 参数、局部赋值、普通零输出函数调用和公开零输入入口。
- 内置标准签名为 `dever.io.println(text: Text) ()`,输出 UTF-8 文本和一个换行;写入失败报告 Dever 调用位置并以非零状态退出。
- 修改文本、调用其他自定义函数、跨包调用公开函数都必须由实际源码驱动;不能特判示例名称或内容。
- 名称、参数、可见性、包路径和重复定义检查在原生编译前完成;递归及包依赖循环按既有契约拒绝。
- 未实现的类型、表达式、分句和输出签名明确报不支持,不静默生成近似代码。
- 提供本地 `check <source-root>`、`run <source-root> <package.function>`、`build <source-root> <package.function> --output <path>`;不替换全局命令。
- 验收真实 stdout、错误定位、原生可执行文件和输出失败;不启动任何服务或执行全量测试。
## Deferred User API Proposal
以下保留之前的 API 讨论作为后续参考;其中持久化、并发与性能要求尚未重新确认,不属于当前验收或实施前置条件。
### Confirmed Requirements
- 应用业务使用 Dever 源码(`.dever` 或 `.dever.md` 的程序块);不能手写 Rust 用户 CRUD 再把 Dever 当作装饰。
- 用户模型只有 ID、姓名、手机号,CRUD 和 HTTP JSON 交互必须实际可用。
- 保留已经讨论的表面语法;只增加实际应用所需的标准 package 能力和必要语义。
- 编译器与通用运行时不能特判 `User`、`name`、`phone` 或 `/users`;这些信息来自应用源码。修改业务类型和路由不能要求修改编译器。
- 数据需要真实持久化;重启后仍可查询,不能用进程内 Map 冒充实际存储。
- 性能验证覆盖真实读写、序列化和持久化,不用空接口成绩替代 CRUD 性能。
业务模型沿用现有语法:
```dever
type User {
id: Id
name: Text
phone: Text
}
```
### Proposed API Acceptance
以下为待最终收敛的最小 API 行为,不增加登录、管理页面或其它业务模块:
| Request | Behavior |
| --- | --- |
| `POST /users` | 提交姓名和手机号;服务端生成 ID,返回创建的用户 |
| `GET /users/{id}` | 查询单个用户;不存在时返回明确的未找到结果 |
| `GET /users` | 按稳定顺序、有上限地分页查询 |
| `PUT /users/{id}` | 完整更新姓名和手机号,ID 不可变 |
| `DELETE /users/{id}` | 删除指定用户,删除后无法查询到 |
- `name`、`phone` 使用 Text;不把手机号当数值,也不擅自添加特定国家的手机号规则或手机号唯一约束。
- 缺少字段、非法 JSON、无效 ID、未知用户和存储失败需要明确响应;不能静默填值或伪装成功。
- 并发访问不能产生重复 ID、半写入或未说明的丢失更新;具体并发写入契约随存储设计确定。
- 吞吐、p50/p95/p99 延迟、错误率和资源使用需要在固定数据量、读写比例、并发和持久化配置下报告,并与同环境、同功能的原生基线比较。数值性能门槛仍待收敛,现有计算内核的 1.15 倍预算不能直接替代 API 指标。
### Open Decisions
- 后续 API 先允许内存测试数据;需要持久化时再讨论数据库、并发更新和性能预算。
### Deferred From This Milestone
- 契约 DSL、AI 生成器、全面重复语义检测和完整的大项目上下文工具。
- 登录/RBAC、前端页面、多租户、分布式部署及其它没有被请求的用户业务。
- 与用户 API 无关的完整数值标准库、全部集合组合、自举和组件分发系统;既有语言契约作为后续路线保留。
## Language Roadmap
后续章节保留原 Minimal Core 的语言契约,作为本轮定义讨论的基础。已确认的长期规则与首版能力限制分别标明;讨论中的功能不代表已经实现,编译器仍须明确拒绝尚未支持的源码。
## Repository Evidence
- 仓库当前没有已发布的 `.dever` parser、formatter 或兼容格式。
- 2026-09-05 开工检查确认:当前仓库只有规划资料,没有 Cargo workspace、Rust 源码或测试。HEAD 也只包含两份旧规划文档。
- 新语言没有兼容旧语义图、旧 CLI、旧生成结果或旧测试的义务。旧规划由 Git 文档历史保留,不在新实现中增加迁移层。
- 从零建立 Rust workspace;复用源码位置诊断思路和仓库根目录 `test/` 约束。旧领域模型和旧后端契约不能决定新语法。
## Core Language Roadmap Scope
1. `.dever` 源文件、UTF-8 文本、注释、literal 和唯一 formatter。
2. package 路径、公开名称、完整限定名和源码根目录内的自动依赖解析。
3. 被动 type、字段型值、选择型值、可空值、List、Map、Bytes 和 opaque Stream 资源。
4. 显式 function 边界、线性 body、局部类型推断和值语义修改。
5. 通过同名 function 分句表达的穷尽、互斥条件规则。
6. 算术、比较、布尔和文本连接表达式。
7. `each`、`reduce`、`reduce_until`、`filter`、`find`、`sum` 以及最小 List/Map 标准函数。
8. Int、Decimal、Float 的确定数值语义和最小 `math`/`decimal` 标准 package。
9. parser、formatter、resolver、type checker、clause analyzer、语义基准解释器、原生 AOT 编译和 CLI。
10. Rust 编写的首代引导编译器,以及不依赖 Rust 表面语法、可被未来 Dever 自举编译器复用的 typed HIR 和运行语义。
## Language Contract
### 1. Source And Package
- 源文件后缀固定为 `.dever`,编码为 UTF-8。
- 代码标识符只使用 ASCII 英文;注释、字符串和文档可以使用任意 UTF-8。
- `#` 开始单行注释;Core 0.1 不提供块注释。`//` 只表示整数除法,不具有第二种词法含义。
- 语句和声明按换行或结构边界分隔,不写分号。
- 一个文件完整拥有一个 package;同一 package 不能拆到多个文件。
- 源文件相对 `src/` 的路径必须与 package 点分名称一致。
```text
src/account/user.dever -> package account.user
```
文件第一项必须是 package 声明:
```dever
package account.user exposes (
User
create
)
```
`exposes` 只能列出当前 package 已声明的 type 或 function。未列出的名称只在当前 package 可见;公开 function 名称会公开该名称的全部签名和分句。
同 package 引用使用短名称,跨 package 引用必须使用完整限定名:
```dever
normalized = normalize_email(email)
user = account.user.create(id, name, email)
```
Core 0.1 不提供 `import`、`use`、通配导入或别名。编译器从完整限定引用建立依赖图,并拒绝 package 循环依赖。`app`、`domain`、`feature`、`flow`、`policy`、`view` 和 `scenario` 都不是关键字,需要时只是普通 package 路径段。
#### Package Origins And Distributed Components
语言层只有 package,不新增 `component` 关键字。package 是源码和 API 命名空间;“组件”只是 registry 中用于安装、签名和版本管理的发布单元,可以包含一个根 package 及若干子 package。组件不能绕过各 package 的 `exposes` 边界。
package 来源固定分为三类:
| 来源 | 名称规则 | 示例 | 获取方式 |
| --- | --- | --- | --- |
| 核心标准 package | 编译器保留的单段名称 | `text`、`math`、`decimal` | 随编译器提供 |
| Dever 官方组件 | `dever.<component>` 根路径 | `dever.http`、`dever.sql` | 显式安装 |
| 公共第三方组件 | `<publisher>.<component>` 根路径 | `stripe.payment`、`shemic.robot` | 显式安装 |
本地业务 package 继续使用项目内路径,例如 `account.user`、`order.checkout`。本地源码不能声明核心标准名称或 `dever.*` 官方命名空间。同一个构建中如果本地源码、标准库或未来已安装组件提供了相同 package 名称,编译器直接报告重复定义,不进行覆盖或静默择优。
调用形式对所有来源一致,版本不会进入业务源码:
```dever
user = account.user.create(input)
name = text.trim(raw_name)
response = dever.http.send(request)
payment = stripe.payment.create(order)
```
从 0.2 起,组件的稳定身份与下载来源相互独立。`shemic.contact` 是进入 package 名称和类型身份的 component id;GitHub、其他 Git 服务或 Dever registry 只是可以替换的 source。仓库迁移不会迫使业务源码改名。
公共组件必须通过命令显式加入,不能因为遇到未知名称就在 `check` 或 `build` 时自动联网:
```text
dever add dever.http@1
dever add stripe.payment@3
dever add shemic.contact --git https://github.com/shemic/contact.git --tag v1.2.0
dever add shemic.contact --git https://github.com/shemic/contact.git --rev 8f34c2a
dever update stripe.payment
dever fetch
```
Git source 必须同时给出 tag 或 revision,不接受未固定的默认分支 HEAD。tag 在加入时解析为完整 commit;`dever.lock` 同时记录 source URL、完整 commit 和规范化源码内容的 SHA-256,后续 tag 被移动也不会改变构建。组件内全部 package 必须位于 component id 根路径下,例如 `shemic.contact` 可以提供 `shemic.contact.phone`,不能同时冒充 `account.user`。
0.2 的项目根目录增加两个由 CLI 维护、需要提交的元数据文件:
```text
dever.mod # component id、直接依赖及其 registry/git/path 来源要求
dever.lock # 完整依赖图、精确版本/commit、内容摘要和实际提供的 package
```
开发者仍只手写 `.dever`;`dever add`、`dever update` 和未来的本地 link 命令负责规范化更新元数据。分开保存意图和精确结果,使直接依赖保持可读,同时保证所有机器解析出同一份源码。
下载源码不复制到每个项目。Dever 使用平台级、可由 `DEVER_HOME` 改址的全局只读内容寻址仓库:
```text
$DEVER_HOME/
git/<source-url-hash>/ # Git bare mirror,只负责取对象
store/sha256/<content-hash>/ # 已校验、不可修改的组件源码
build/<target>/<artifact-hash>/ # 可删除的编译缓存
```
项目目录只保留 `src/`、`dever.mod`、`dever.lock` 和可删除的 `target/dever/` 构建结果。多个项目依赖相同 commit 时共享同一份 store 内容。`dever add`、`dever update`、`dever fetch` 是仅有的默认联网入口;普通 `check`、`build` 和 `run` 只读取本地源码、内置标准 package、lock 与 store,缺失时明确要求执行 `dever fetch`,不隐式下载。
Core 0.1 只实现本地 package 与随编译器提供的核心标准 package,同时锁定上述命名和解析边界。registry、`dever add`、签名验证和外部组件缓存确定延后到 0.2,不影响现有 `.dever` 调用形式。
### 2. Canonical Naming
| 对象 | 唯一格式 | 示例 |
| --- | --- | --- |
| package 路径段 | `lower_snake_case`,优先单数领域名 | `account.user`、`sales_order` |
| 文件名 | `lower_snake_case` | `user.dever` |
| type | `UpperCamelCase` | `User`、`CreateResult` |
| 选择项 | `UpperCamelCase` | `Pending`、`Created` |
| function | `lower_snake_case` | `create`、`can_sign_in` |
| 输入、输出、局部值、字段 | `lower_snake_case` | `user_id`、`created_at` |
| 标准泛型参数 | 有含义的 `UpperCamelCase` | `Item`、`Key`、`Value` |
大小写不控制公开性。缩写按普通单词处理:`Id`、`Url`、`HttpClient`、`user_id`、`callback_url`。编译器强制可机械判断的大小写、ASCII 和路径格式;“是否是好名称”只产生规范诊断,不依赖英语词典阻止编译。
### 3. Types And Literals
Core 0.1 的基础标量只有:
| 类型 | 语义 |
| --- | --- |
| `Bool` | `true` 或 `false` |
| `Int` | 有符号 64 位整数,范围 `-9223372036854775808` 至 `9223372036854775807` |
| `Decimal` | decimal128,最多 34 位有效十进制数字,round half to even |
| `Float` | IEEE 754 binary64 近似数 |
| `Text` | UTF-8 文本;相等按实际字符序列判断,不做隐式 Unicode 规范化 |
| `Id` | 不透明稳定标识,只支持相等比较、传递和序列化 |
Core 0.1 不提供 `UInt`、`BigInt`、`Percent`、字符类型或隐式动态 `Json` 类型。
- 整数 literal 默认是 Int。
- 带小数点的 literal 默认是 Decimal;在明确要求 Float 的静态上下文中直接成为 Float。
- 科学计数 literal,例如 `1.25e6`,是 Float。
- Decimal literal 必须能被 34 位 decimal128 精确表示;源码常量不会被静默舍入,超出精度或范围时由编译器拒绝。运行时算术仍按已确定的银行家舍入产生结果。
- Text 使用双引号,支持 `\\`、`\"`、`\n`、`\r`、`\t` 和 `\u{HEX}` Unicode 转义。
- `true`、`false` 和 `null` 是保留 literal。
- 源码不提供 NaN 或 Infinity literal;Float 运算可以产生这些值。
用户定义 type 只有两种形状,不能混写:
```dever
type User {
id: Id
name: Text
phone: Text?
}
type CreateResult {
Created(user: User)
InvalidPhone(reason: Text)
EmailAlreadyUsed(email: Text)
}
```
字段型 type 是固定字段数据;选择型 type 表示同一时间只能是其中一种选择。type 没有方法、构造器、hook、继承或行为,也不能递归包含自身。
选择项必须通过所属 type 限定:
```dever
result = CreateResult.Created(user)
```
选择项携带的数据在声明中必须有名称和类型,构造时按声明位置传入。字段型值必须一次完整创建:
```dever
user = User {
id = id
name = name
phone = null
}
```
未知、重复或遗漏字段都是编译错误,包括值为 `null` 的可空字段。formatter 按 type 声明顺序输出字段。点号只访问或修改已经完整存在的字段型值,不能逐字段创建半初始化对象。
`Value?` 表示 `Value` 或 `null`。普通类型永远不能为 `null`,`Value??` 非法,也没有 `?.`、`??`、强制解包或隐式默认值。可空值必须交给穷尽的 function 分句处理。
标准泛型函数产生可空返回值时,可空化是幂等的:`first(List<Int?>)`、`find` 的同类调用及 `get(Map<Text, Int?>, key)` 都返回 `Int?`,不产生嵌套可空类型。返回 `null` 不区分“没有元素/键”和“已有值为 null”;需要区分时,业务使用带显式选择项的 Value 类型。源码仍禁止显式书写 `Int??`。
### 4. List And Map Values
List 使用方括号创建:
```dever
users = [first_user, second_user]
empty_users() (users: List<User>) {
users = []
}
```
List 元素必须是同一静态类型。空 List 必须能从 output、参数或其他静态上下文推断元素类型,否则编译错误。
Map 是 `Map<Key, Value>`,使用裸大括号创建:
```dever
default_labels() (labels: Map<Text, Text>) {
labels = {
"env" = "production"
"region" = "china"
}
}
```
同一 Map literal 的 key 类型一致、value 类型一致,重复 key 是编译错误;空 Map 也必须能推断完整类型。`Type { ... }` 是固定字段型值,裸 `{ ... }` 是动态 Map,二者不会合并成万能对象。
Map key 只允许 Text、Int、Bool、Id 和不携带数据的选择型 type。Decimal、Float、可空值、字段型值、List 和 Map 不能成为 key。
Map 保证插入顺序。literal 保留源码顺序;`put` 新 key 时追加,覆盖已有 key 不移动位置;`remove` 后重新写入视为新的末尾项。`entries`、显示和未来的 JSON 边界都必须遵守这一顺序。
### 5. Function Definitions And Calls
所有行为只有一种定义形式:
```dever
line_total(line: Line) (total: Decimal) {
total = line.price * line.quantity
}
```
- 第一组括号是输入,第二组是命名输出。
- 普通输入和输出必须写成 `name: Type`;body 内局部值可以推断。
- 没有输入或输出时仍写空括号。
- 不使用 `function` 或 `return` 关键字。
- 输入名称是当前分句的局部绑定;输出名称和输入输出类型属于稳定签名。
- 调用只按位置传参,不提供命名参数调用。
- 用户 function 以“package、名称、输入数量”确定签名;同名同参数数量的全部分句必须具有相同的基础输入类型和输出契约。不同参数数量可以形成独立签名。
- 同一签名的全部分句必须在同一文件中连续书写。
- Core 0.1 禁止直接或间接递归;重复执行只能通过 `each`、`reduce` 和 `reduce_until` 这些受检查的具名 handler 组合器进入。
选择项会提供所属选择型 type,普通 type 会提供自身类型;`null` 和 `other` 则从同组其他分句推断基础输入类型。只写 `null`/`other` 而没有任何可推断类型的分句组是编译错误。`Text` 与 `null` 两个分句共同形成 `Text?` 输入,而不是两个运行时重载。
单输出调用直接得到该值,零输出调用作为一条动作语句,多输出调用得到只能按输出名称访问的结果:
```dever
allowed = can_sign_in(user.status)
parts = split_name(name)
display = parts.first + parts.last
```
不提供按位置拆包。命名 function 只能在普通调用位置、作为标准序列/集合操作的 handler,或传给受限静态 handler 输入;Core 0.1 不提供 lambda、闭包,也不能把 function 存入局部值、字段、List、Map 或 Stream。
### 6. Function Clauses Replace Conditional Blocks
function 分句是 Dever 唯一的条件分派机制。这是 2026-09-07 用户确认的长期语言规则;条件 block 和条件表达式不属于后续待增加能力。Bool 表达式及其短路规则仍按第 8 节定义,业务分支通过调用具名 function 的分句选择。
示例:
```dever
can_pay(status: OrderStatus.Draft) (allowed: Bool) {
allowed = true
}
can_pay(status: other) (allowed: Bool) {
allowed = false
}
```
冒号左侧始终是当前分句的局部输入名称;不同分句可以使用不同名称。右侧允许:
- 普通类型,表示该类型的任意值;
- `true`、`false`、`null`、Text 或数值常量;
- 完整限定的选择项及其数据绑定,例如 `CreateResult.Created(user)`;
- 选择项中的 `_`,表示忽略该携带值;
- Int 或 Decimal 的静态常量比较;
- 上述数值比较的一个上下界组合;
- 上下文关键词 `other`。
数值范围直接放在输入中:
```dever
absolute(value: Int < 0) (result: Int) {
result = -value
}
absolute(value: Int >= 0) (result: Int) {
result = value
}
grade(score: Int >= 60 and < 90) (name: Text) {
name = "pass"
}
grade(score: Int < 60) (name: Text) {
name = "fail"
}
grade(score: Int >= 90) (name: Text) {
name = "excellent"
}
```
Float 不进入静态范围分句,因为 NaN 不具有普通全序;先通过 `math.is_nan` 等 function 得到 Bool,再对 Bool 写分句。
分句没有源码顺序优先级。编译器把同一签名的全部分句作为一组规则,拒绝类型不一致、重叠、遗漏和不可达情况。多输入 function 中,每个 `other` 是该输入位置全部明确模式的补集,编译器再检查各位置组合是否覆盖完整输入空间。`other` 只在分句输入冒号右侧具有该含义,不能作为普通值存储、传递或组合。
分句输入中不能调用任意 function 或读取运行时状态。更深一层的业务选择必须调用另一个有名称的 function,源码不提供 `if`、`else`、`switch`、`match`、`when` 或 `by` block。
### 7. Linear Body And Value Semantics
body 只包含顺序赋值和 function 调用:
```dever
final_price(price: Decimal, rate: Decimal) (result: Decimal) {
discounted = price * (1 - rate)
result = decimal.round(discounted, 2)
}
```
表达式由 literal、名称、字段访问、值构造、function 调用、一元运算和二元运算组成。没有条件表达式、循环 block、匿名 function、复合赋值或隐藏控制流。
`=` 既用于初始化,也用于修改当前 function 的 input、局部值或 output。编译器强制:
- 读取前已经完整初始化;
- 字段修改的宿主值已经完整初始化;
- function 结束时全部命名输出都已赋值;
- 赋值类型稳定,不通过再次赋值改变局部类型;
- 无意义覆盖和写入后未读取至少产生诊断。
普通值的赋值、传参和输出采用值语义。修改只属于当前局部名称,调用方或其他名称不会观察到共享别名变化;变化只能通过命名 output 或未来的显式 effect function 离开当前调用。实现可以用唯一所有权或写时复制消除真实复制,但源码不暴露借用、生命周期或 `clone`。File、Socket、Listener 和 Stream 是显式 opaque 资源例外:复制只建立同一资源状态的别名,不复制外部资源。
### 8. Expressions And Operators
普通算术直接使用符号:
```dever
subtotal = price * quantity
ratio = 5 / 2
page = 5 // 2
remainder = 5 % 2
eligible = user.active and user.age >= 18
```
| 运算 | 规则 |
| --- | --- |
| `+ - *` | Int、Decimal 或 Float 的同类运算;Int 与 Decimal 混合时 Int 无损提升为 Decimal |
| `/` | Int/Int 返回 Decimal;Decimal 与 Int 返回 Decimal;Float/Float 返回 Float |
| `//` | 只接受 Int,结果向零截断 |
| `%` | 只接受 Int,余数符号跟随被除数,并满足 `a = (a // b) * b + (a % b)` |
| `+` | 也可连接两个 Text |
| `== !=` | 相同可比较类型 |
| `< <= > >=` | Int、Decimal 或 Float 的同类比较;Int 可提升为 Decimal |
| `and or not` | 只接受 Bool,并具有短路语义 |
可比较类型包括上述标量,以及成员递归可比较的字段型、选择型、可空、List、Map 和 MapEntry。字段型按声明字段逐项比较;选择型比较选择项和携带值;List 按位置比较;Map 按插入顺序比较 key/value 对,因此顺序不同的 Map 不相等;可空值中只有两个 null 相等,两个非 null 值继续比较其内容。Float 保持下文的 NaN 规则,包括嵌套在集合或字段中时。命名多输出结果只能访问输出字段,不能直接比较。相等运算不做 Int/Decimal 提升。
Decimal 与 Float 不隐式混合,实际 Int 值转为 Float 也必须显式调用 `float.from_int`。Core 0.1 不提供 Float 到 Decimal 的隐式或有损转换。
运算优先级从高到低固定为:调用和字段访问、一元 `-`/`not`、`* / // %`、`+ -`、比较、`and`、`or`。同级二元运算从左到右;括号可以显式改变顺序。formatter 不依赖空白表达优先级。
### 9. Collection And Sequence Functions
Core 0.1 只保留以下集合行为,不增加循环语法:
```dever
views = each(to_view, users)
views = each(to_view, users, context)
active = filter(is_active, users)
active = filter(is_active, users, context)
user = find(is_target, users)
user = find(is_target, users, context)
total = sum(prices)
total = sum(line_total, lines)
total = sum(line_total, lines, context)
state = reduce(update_state, values, initial_state)
state = reduce_until(update_until_done, values, initial_state)
parallel_each(consume, values, 4)
parallel_each(consume, values, 4, context)
```
- `each`、`filter`、`find` 和投影式 `sum` 最多有一个末尾 context;需要多项共享数据时先组合成字段型值,其 handler 先接收元素、再接收 context。
- `reduce` 和 `reduce_until` 的第三项是必需的初始状态,不是可选 context;handler 先接收元素、再接收当前状态,且不接受第四项参数。
- `each` 的处理 function 有一个输出时按输入顺序产生 `List<Output>`;没有输出时只按顺序执行动作;多个输出不合法。
- `filter` 的处理 function 必须输出 Bool,并保留原列表顺序。
- `find` 找到第一个 Bool 为 true 的元素后停止,返回 `Item?`;没有结果返回 `null`。
- `sum(List<Number>)` 直接求和;投影形式要求处理 function 输出 Int、Decimal 或 Float。
- 空 List 的 `sum` 返回相应数值类型的零。
- 除显式 `parallel_each` 外,List 操作按列表顺序执行,Bytes 按字节位置执行,Stream 按拉取顺序执行;Float 求和不得为了优化而重排。
- `reduce` 和 `reduce_until` 的完整 List/Bytes/Stream、状态和停止契约由 Approved Foundation Extension 定义;它们不开放循环 block 或用户递归。
- `parallel_each` 接受 List、Bytes 或 Stream、1–256 个工作线程和可选 context;handler 必须零输出,输入依次是元素和 context。复用既有静态 handler 与专门化规则,不产生用户可存储的 function 值。
- 生产端顺序拉取且队列有界,工作端的副作用顺序不保证。handler 输入与 context 不能递归包含 Stream,源 Stream 只在生产端拉取;文件与 Socket 等别名仍遵守共享资源规则。
- `parallel_each` 返回前等待全部已启动动作;程序故障在后续拉取边界停止分派,保留第一个已记录故障并传播源码位置。已经阻塞的拉取或系统调用由显式系统超时约束,不提供强制取消或后台遗留任务。非法线程数在拉取前产生程序故障。
其他最小标准函数:
```dever
users = append(users, user)
user = first(users)
value = get(labels, "env")
labels = put(labels, "env", "development")
labels = remove(labels, "env")
pairs = entries(labels)
```
`append` 返回新 List;`first(List<Item>)` 返回 `Item?`。`get` 返回 `Value?`;`put` 和 `remove` 返回新 Map;`entries` 返回按插入顺序排列的 `List<MapEntry<Key, Value>>`。没有 `list.add`、数字点号索引、`map.key` 或 `map[key]`。
`any`、`all`、`min`、`max` 不进入首版;真实重复需求出现后再增加普通标准 function。
### 10. Recoverable Failures And Runtime Faults
可恢复业务失败使用选择型输出:
```dever
create_message(result: CreateResult.Created(user)) (message: Text) {
message = user.name
}
create_message(result: CreateResult.InvalidPhone(reason)) (message: Text) {
message = reason
}
create_message(result: CreateResult.EmailAlreadyUsed(email)) (message: Text) {
message = email + " already exists"
}
```
这同时替代 `throw` 和 `catch`。调用方接收完整选择值,再通过命名分句显式转换。Core 0.1 不提供 exception、自动错误传播、隐藏提前退出或类似 Rust `?` 的语法。
编译器可以证明的不变量错误直接拒绝编译。只能在运行时发现的程序故障终止当前执行边界,并至少报告故障类别、文件、行和列;普通 function 不能捕获、忽略或转换程序故障。执行边界由宿主确定,例如一次 CLI 调用,未来可以是一次请求或任务。
数据库、网络等只要存在恢复可能,就必须在未来以显式选择型结果返回,不能伪装成程序故障。
### 11. Numeric Semantics
- Int 是固定 i64;不环绕、不饱和。静态可证明的溢出是编译错误,运行时溢出是程序故障。
- `Int / Int` 先无损提升为 Decimal 再除,因此 `5 / 2` 是 Decimal `2.5`。
- `Int // Int` 向零截断;`Int % Int` 与商保持代数恒等式。除数为零以及 `-9223372036854775808 // -1` 属于溢出故障。
- Decimal 使用 decimal128 的 34 位有效数字和银行家舍入;算术中的正常 inexact/rounded 状态产生舍入结果,指数上溢、下溢、除零或无效运算产生程序故障,不向源码暴露 Decimal NaN/Infinity。
- 金额位数由 `decimal.round(value, places)` 显式处理;核心算术不根据变量名、字段名或业务场景猜测两位小数。
- Float 遵守 IEEE 754 binary64 非捕获行为:溢出产生正负 Infinity,无效运算产生 NaN,除零产生 Infinity 或 NaN。
- Float 的 NaN 与任何值的 `==` 都为 false,`!=` 为 true,有序比较均为 false;使用 `math.is_finite`、`math.is_nan` 和 `math.is_infinite` 分类。
- 常用科学函数通过 `math.sqrt`、`math.sin`、`math.cos`、`math.log` 和 `math.pow` 等普通标准 package function 提供。
- Decimal 与 Float 不隐式混合。任意精度整数/小数、向量、矩阵、复数、统计与单位系统以后由独立 package 提供。
## Standard Names
无需 import 即可使用的核心名称只有:
- Bool、Int、Decimal、Float、Text、Id、Bytes、List、Map、MapEntry、Stream;
- true、false、null、other;
- each、parallel_each、reduce、reduce_until、filter、find、sum、append、first、get、put、remove、entries、length、用于 Stream 的 close;
- 仅用于 function 输入签名的上下文关键词 handler;
- 算术、比较和布尔运算符。
其他标准行为使用完整 package 名,例如 `text.trim`、`text.lower`、`decimal.round`、`float.from_int` 和 `math.sqrt`。这避免大量隐式全局名称,也让标准库与业务 package 遵守同一调用模型。
## CLI Identity And Coexistence
- 语言、编译器产品和正式命令名保持为 `dever`,文档与命令语义以 `dever` 为准。
- Core 0.1 与现有 Dever 框架 CLI 共存期间,系统 `PATH` 中可以暂时用 `deverc` 暴露语言编译器;不得覆盖、替换或遮蔽现有的 `dever` 命令。
- `deverc` 只是指向同一编译器二进制的安装入口,不形成第二套命令、配置或行为契约。
- 仓库构建与自动测试直接调用构建产物,不修改系统 `PATH` 或全局可执行文件;创建、更新或移除全局 `deverc` 入口必须单独获得用户授权。
- 未来是否让语言编译器接管全局 `dever` 命令属于独立迁移决策,不在 Core 0.1 默认实施范围内。
## Complete Package Example
```dever
public type UserStatus {
Pending
Active
Disabled(reason: Text)
}
public type CreateInput {
id: Id
name: Text
email: Text
phone: Text?
score: Int
labels: Map<Text, Text>
}
public type CreateResult {
Created(user: User)
InvalidName(reason: Text)
InvalidEmail(reason: Text)
}
public type User {
id: Id
name: Text
email: Text
phone: Text?
score: Int
labels: Map<Text, Text>
status: UserStatus
}
public type UserView {
id: Id
name: Text
email: Text
phone: Text
score: Int
can_sign_in: Bool
}
public create(input: CreateInput) (result: CreateResult) {
normalized = normalize(input)
result = create_name(normalized, normalized.name)
}
normalize(input: CreateInput) (result: CreateInput) {
result = input
result.name = text.trim(input.name)
result.email = text.lower(text.trim(input.email))
}
create_name(input: CreateInput, name: "") (result: CreateResult) {
result = CreateResult.InvalidName("name is required")
}
create_name(input: CreateInput, name: other) (result: CreateResult) {
result = create_email(input, name, input.email)
}
create_email(input: CreateInput, name: Text, email: "") (result: CreateResult) {
result = CreateResult.InvalidEmail("email is required")
}
create_email(input: CreateInput, name: Text, email: other) (result: CreateResult) {
user = User {
id = input.id
name = name
email = email
phone = input.phone
score = input.score
labels = input.labels
status = UserStatus.Pending
}
result = CreateResult.Created(user)
}
public activate(user: User) (result: User) {
result = user
result.status = UserStatus.Active
}
public disable(user: User, reason: Text) (result: User) {
result = user
result.status = UserStatus.Disabled(reason)
}
public can_sign_in(status: UserStatus.Active) (allowed: Bool) {
allowed = true
}
public can_sign_in(status: other) (allowed: Bool) {
allowed = false
}
phone_text(phone: Text) (result: Text) {
result = phone
}
phone_text(phone: null) (result: Text) {
result = "Not provided"
}
public to_view(user: User) (view: UserView) {
view = UserView {
id = user.id
name = user.name
email = user.email
phone = phone_text(user.phone)
score = user.score
can_sign_in = can_sign_in(user.status)
}
}
public to_views(users: List<User>) (views: List<UserView>) {
views = each(to_view, users)
}
is_active(user: User) (result: Bool) {
result = can_sign_in(user.status)
}
public active_views(users: List<User>) (views: List<UserView>) {
active = filter(is_active, users)
views = each(to_view, active)
}
has_id(user: User, expected: Id) (matches: Bool) {
matches = user.id == expected
}
public find_user(users: List<User>, id: Id) (user: User?) {
user = find(has_id, users, id)
}
public put_label(user: User, key: Text, value: Text) (result: User) {
result = user
result.labels = put(user.labels, key, value)
}
public label(user: User, key: Text) (value: Text?) {
value = get(user.labels, key)
}
score_bonus(user: User, rate: Decimal) (amount: Decimal) {
amount = user.score * rate
}
public total_score_bonus(users: List<User>, rate: Decimal) (total: Decimal) {
total = sum(score_bonus, users, rate)
}
```
该源码只要求开发者理解 package、type、function、值和普通表达式。它同时覆盖显式业务结果、局部修改、条件分句、可空处理、列表转换、筛选、查找、Map 更新和 Decimal 聚合,所有路径保持平铺且可由编译器完整检查。
## Original Core Deferrals
以下保留原 Core 0.1 的暂缓能力,具体版本安排在后续需求中确定。HTTP、JSON、持久化以及异步/并发 I/O 的应用扩展仍属暂缓 API 提案。
- AI、自然语言转译、画布和业务 DSL;
- Web 页面、React,以及超出首个 API 需要的数据库、网络和部署能力;
- 超出首个 API 需要的 effect/capability、异步、并发和事务能力;不开放任意共享可变业务全局变量;
- package registry、版本解析和项目清单;
- `for/while/break/continue/iterate/recur/repeat`、用户递归和其它通用循环结构;
- class、继承、interface、annotation、macro、unsafe、FFI;
- import、别名、命名参数、lambda、闭包和一般化一等 function;
- 用户泛型 type、递归 type、Set 和动态 Json;首个 API 的 JSON 边界应由静态类型驱动;
- UInt、BigInt、任意精度 Decimal、Percent、日期时间和单位类型;
- 直接 LLVM/Cranelift backend、完成自举、所有权或生命周期表面语法。
这些内容保留为 Core 0.1 暂缓项,不改变 Dever 最终成为通用编程语言的方向。条件 block 已从暂缓清单移除,条件分派始终遵守第 6 节的长期规则。
## Core Roadmap Acceptance Criteria
这些是完整语言路线的验收项。2026-09-07 的基础语法、最小系统接口和后续工具/库分别验收,实际状态与证据见 `implement.md`;发行和自举不因基础里程碑完成而视为已交付。
1. 一个源码根目录中的多个 `.dever` 文件可以被加载;Core 0.1 只读取该根目录和内置标准 package,不隐式联网或搜索外部目录;路径与 package 不一致、保留命名空间、重复 package、未知公开名、未知引用和循环依赖均得到带源码位置的确定诊断。
2. parser 覆盖本 PRD 的 package、两种 type、function、分句、value、statement 和 expression;拒绝已排除的关键字或语法。
3. formatter 对合法程序产生唯一布局,连续执行两次结果不变,并保持注释和程序含义。
4. 编译器强制命名格式、显式 function 边界、位置调用、一文件一 package、连续分句和无递归调用图;同 package 使用短名称,跨 package 必须使用完整限定名,并且只能访问对方 `exposes` 中的名称。
5. type checker 拒绝未知/重复/遗漏字段、半初始化字段写入、未初始化读取、遗漏 output、非法 null、类型漂移、非法 Map key 和未授权隐式转换。
6. clause analyzer 对选择型、可空、Bool、Text 常量、Int/Decimal 范围和多输入组合检查互斥、穷尽与可达性;分句移动顺序不改变执行结果。
7. 本 PRD 的完整 package 示例可以通过解析、格式化、名称解析和类型检查;独立的零输入示例在语义基准解释器和原生程序中产生相同的确定命名输出。
8. 局部赋值和字段修改不会改变调用方或其他名称持有的原值;编译器实现可以优化复制但不能改变可观察语义。
9. `each`、`reduce`、`reduce_until`、`filter`、`find` 和 `sum` 的顺序、handler、context、初始/空结果、停止和类型规则全部有定向测试。
10. List、Map、Bytes 和 Stream,以及 `append`、`first`、`get`、`put`、`remove`、`entries` 遵守可空返回、值/资源语义和稳定顺序。
11. `5 / 2` 得到 Decimal `2.5`,`-5 // 2` 得到 Int `-2`,`-5 % 2` 得到 Int `-1`,并满足商余恒等式。
12. Decimal 能证明 `0.1 + 0.2 == 0.3`,`1 / 3` 使用 34 位有效数字和 round half to even;`decimal.round` 显式控制业务位数。
13. 静态 Int/Decimal 溢出或零除数被拒绝编译;动态故障终止当前执行边界并定位 `.dever` 源码,不环绕、饱和或返回默认值。
14. Float 使用 binary64 和科学计数法,能够产生并通过 `math.is_nan`、`math.is_finite`、`math.is_infinite` 判断 NaN/Infinity;Decimal 与 Float 混用被拒绝。
15. 可恢复失败只能通过选择型输出和穷尽分句传播;源码中不存在 exception、`throw`、`catch` 或自动传播语法。
16. 正式 CLI 名为 `dever`,至少提供 `dever check <source-root>`、`dever fmt <source-root>`、`dever build <source-root> <package.function> --output <path>` 和 `dever run <source-root> <package.function>`;与现有框架 CLI 共存时,临时全局入口 `deverc` 必须调用同一编译器且不得覆盖现有 `dever`;build/run 首版只接受公开的零输入 function,run 执行与 build 相同的原生路径并确定性打印命名输出。
17. 新实现不依赖旧 canonical JSON、SemanticGraph、Refiner、Workbench、温度示例或 Rust/React 生成后端,仓库文档不再把旧路线描述为当前架构。
18. 生产产物不链接通用解释器,不携带运行时 type tag 分派或垃圾回收器;record、choice、List、Bytes 和静态 handler 在 AOT 路径中具有具体原生表示,热循环不发生逐元素动态 handler 分派或无必要堆分配;opaque Stream 只在 I/O 资源拉取边界保留内部生产者抽象。
19. 在同一算法、数据布局、工具链和机器上的 release 基准中,Int/Float 算术及 List `each`/`filter`/`sum` 代表性热路径的中位执行时间不得超过等价 safe Rust 基线的 1.15 倍;Decimal 与有序 Map 分别对使用相同底层语义的 Rust 基线比较,编译时间单独报告。
20. Rust 生成文件只存在于编译器管理的临时构建目录,不是用户源码、公共输出格式或兼容接口;删除这些文件不影响 `.dever` 工程。
## Risks And Deferred Items
- 条件分句的穷尽分析是首版最复杂的静态检查,应先支持有限选择域与一维 Int/Decimal 区间,再组合多个输入;不引入通用定理证明器。
- decimal128 需要成熟实现与一致状态标志。Core 0.1 采用 Rust `dec` crate 的 Decimal128 和可配置 Context,并将其封装在内部数值适配层;语言级测试锁定行为,依赖 API 不得泄漏到 `.dever`。
- 当前仍禁止用户递归,已按批准范围提供显式文件/TCP 系统操作和结构化异步并发;异步网络、一般化 effect 和事务模型尚未实现,因此不是最终完整运行时。
- Float 运算遵守平台 IEEE 754 语义,但任何未来的快速数学优化都必须显式设计,首版不得重排计算。
# Current Source And Module API Contract (2026-09-18)
This user-approved contract supersedes historical `package/exposes`, arbitrary entry arguments and "API deferred" wording above. Source filenames relative to `module/` define package names in both `.dever` and `.dever.md`; top-level type/function declarations default private and may opt into cross-package access with `public`. `deverc run/build <project>` always invokes private `main()` from `module/main.dever` (or `.dever.md`) and accepts no entry argument. Markdown H1 documents path-derived package and public declarations with no executable H1 block.
Each module may own recursive `api/` source containing only private `get_`/`post_`/`delete_` ordinary functions with one `response` output. Routes are statically validated and compiled only when reachable `main()` calls `dever.api.serve()`. GET/DELETE query inputs and POST JSON-object inputs are typed Text/Int/Bool (nullable allowed), and HTTP responses have JSON `code/message/data` envelopes. Listener settings are read only from `config/setting.json`. No application-config environment variable, implicit listener, new auth policy or CMS HTTP business endpoint is part of this milestone. Current canonical contracts are in `LANGUAGE.md`, `MARKDOWN-SYNTAX.md` and `.trellis/spec/backend/`.
# Current AI-Oriented Source Architecture Contract (2026-09-19)
This section supersedes the 2026-09-18 application visibility and package rules above. An application starts only at `module/main.dever` or `module/main.dever.md`; `main()` is implicit and private, and HTTP starts only when it calls `dever.api.serve()`. Application source contains no `package`, `exposes`, `public`, `internal` or import declarations.
Business source uses `module/<component>/<domain>/<role>.dever` or `module/<component>/<domain>/<role>/<topic>.dever`, where role is exactly `app`, `domain`, `model`, `port`, `adapter` or `api`. A role file and its same-name directory are mutually exclusive. Non-API role directories are flat; only `api/` may add nested URL scopes. A role directory containing only one source file is rejected because it creates structure without separation; keep that implementation in the role file instead.
Files group cohesive behavior rather than individual functions. One role file may contain multiple related functions, and one topic file may contain multiple related functions. The topic filename never becomes part of an App call. All App functions are cross-domain capabilities and are addressed as `<component>.<domain>.<function>`; a same-component caller may use `<domain>.<function>`, and source in the same domain may use `app.<function>`. Every other role is domain-private. Cross-domain and cross-component calls may target only App. Model types and ID types may appear in relations and signatures across domains, but generated Model operations remain callable only inside their owning domain. API handlers are HTTP entry adapters and are never source-callable.
App inputs, outputs and inferred failure sets are one public contract. They cannot expose a private Domain/Adapter type; a business error that may leave the domain is declared in App and referenced from same-domain private source as `app.<ErrorType>`.
The dependency direction is `api -> app`, `app -> domain/model/port/adapter` plus another domain's App, while domain/model/port/adapter remain implementation details. Generic dumping grounds such as `common`, `shared`, `utils`, `helper`, `base` and physical `service/` are invalid application architecture. Keep one-use logic inline, same-topic reuse beside its callers, cross-topic domain rules in `domain`, persistence in `model`, and external integrations in `adapter`; introduce `port` only for a real replaceable boundary.
API route prefixes and JSON `code/message/data` envelopes remain as approved on 2026-09-18. Runtime settings still come only from `config/setting.json`; environment-variable configuration remains prohibited. Authentication remains business composition through App/domain capabilities rather than a compiler-owned HTTP policy.
@@ -1,51 +0,0 @@
# Package Distribution Research
## Question
How should a Dever project consume a component downloaded from GitHub or another Git host without leaking transport details into `.dever` source or losing reproducible builds?
## Primary Evidence
- Go defines a module as a versioned collection of packages. The module path is also the prefix of package import paths and usually identifies where the module can be downloaded. Downloaded source is shared through a module cache, and hashes are verified against `go.sum`.
- https://go.dev/ref/mod
- https://go.dev/doc/modules/gomod-ref
- Cargo permits registry, Git and local path sources. Git dependencies may select a tag or revision, and Cargo locks the resolved commit in `Cargo.lock` until an explicit update.
- https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
- Cargo and Go both separate downloaded source caches from project build artifacts. Go also supports optional vendoring for a fully project-local source tree.
- https://go.dev/ref/mod#module-cache
- https://go.dev/ref/mod#vendoring
- https://doc.rust-lang.org/cargo/reference/config.html#cache
## Alternatives
### Git URL as package identity
Example: `github.com.shemic.contact.phone.parse(raw)`.
This is easy to locate but couples every source reference and type identity to the current hosting provider. Repository transfer, self-hosting or registry mirroring then becomes a source-breaking rename. It also creates long names that obscure the business API.
### Project-local copied dependencies
Example: clone every dependency under `vendor/`.
This is transparent and offline-friendly but duplicates source across projects, increases repository size and makes accidental dependency edits likely. It is useful as an explicit export mode, not the default store.
### Stable identity plus independent source
Example identity: `shemic.contact`; example source: `https://github.com/shemic/contact.git` at an exact resolved commit.
This preserves short, stable `.dever` names while allowing registry, Git, mirrors and local development to use the same package identity. It requires dependency metadata and identity verification, but those responsibilities belong to the package manager rather than business source.
## Recommendation
Use the third model:
1. `component id` is the stable namespace and type-identity root.
2. `source` is a replaceable registry, Git or local transport selected outside `.dever`.
3. `dever.mod` records direct dependency intent and source requirements.
4. `dever.lock` records the complete graph, full Git commits and normalized content hashes.
5. A global immutable content-addressed store deduplicates verified source across projects.
6. `check`, `build` and `run` never perform implicit network access; explicit `add`, `update` and `fetch` do.
7. Package declarations must remain beneath the component root, preventing one dependency from supplying another component's namespace.
This deliberately combines Go's small module/package model and shared verified cache with Cargo's source flexibility and exact Git locking, while avoiding Go's coupling between source-level identity and repository location.
@@ -1,55 +0,0 @@
# Core 0.1 Runtime Dependency Research
- Checked: 2026-09-03
- Scope: dependencies required by already-approved Decimal and Map semantics
## Decimal128
Selected for the MVP: `dec = "=0.4.11"`.
Evidence:
- The [dec crate documentation](https://docs.rs/dec/latest/dec/) describes `Decimal128` as a 128-bit decimal representation with 34 digits of precision and exposes a configurable `Context` with rounding and exceptional-condition status.
- The [Decimal128 API](https://docs.rs/dec/latest/dec/struct.Decimal128.html) supports the required arithmetic and classification operations.
Reason:
- It directly implements the approved decimal128 contract instead of approximating it with a lower-precision financial decimal.
- Context status lets Dever convert overflow, division by zero and invalid Decimal states into language-level runtime faults.
- A reusable Context avoids the per-operation default-context overhead documented by the crate.
Constraint:
- `dec` is a safe Rust wrapper over libdecnumber and therefore introduces a native C build dependency. It must remain isolated behind `dever_runtime::number::DecimalValue`.
- Dever conformance tests, not the crate's operator defaults or special-value surface, define public behavior.
- A future pure-Rust replacement is allowed only when it passes the same conformance suite; this does not change `.dever` semantics.
Rejected for Core 0.1:
- `rust_decimal`: pure Rust, but its 96-bit coefficient does not provide the approved 34-digit decimal128 contract.
- Hand-written decimal arithmetic: correct rounding, exponent handling and status behavior are too risky for the language MVP.
- Newly published decimal128 implementations: potentially useful later, but less established than the selected reference implementation.
## Insertion-Ordered Map
Selected for the MVP: `indexmap = "=2.14.0"`.
Evidence:
- The [IndexMap project](https://github.com/indexmap-rs/indexmap) documents hash-based lookup with iteration independent of hash order.
- The [IndexMap API](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html) documents that insertion appends new keys, overwriting preserves the existing position, and order-preserving removal is available.
Reason:
- Those operations match the approved Dever Map contract without maintaining a duplicate vector-plus-index implementation.
- Lookup remains hash based while iteration is deterministic.
Constraint:
- Dever wraps IndexMap and exposes only `get`, `put`, `remove` and `entries`.
- Removal uses the order-preserving operation rather than swap removal.
- Dependency-specific index access never appears in `.dever` or public diagnostics.
## Dependency Discipline
These are planned pins; the initial parser milestone has no external dependencies. Verify availability and pin each version when the runtime first consumes it. No dependency becomes a public language contract. Upgrading either dependency requires running the focused Decimal/Map conformance tests and confirming that serialized/displayed Dever values remain unchanged.
@@ -1,38 +0,0 @@
{
"id": "dever-language-mvp",
"name": "dever-language-mvp",
"title": "Dever Language Compiler Contracts And Library Follow-through",
"description": "N5 HTTP/2、定向性能矩阵与有界稳态/恢复压测、typed ORM 及双源码 CMS 合同修正已完成;业务 API 等用户重新设计,编译器发行/跨平台与自举仍未交付,根语言路线图保持进行中。",
"status": "in_progress",
"dev_type": null,
"scope": ".",
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-04",
"completedAt": null,
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [
"09-12-runtime-performance",
"09-12-runtime-performance-p2",
"09-12-http2",
"09-12-http2-performance",
"09-13-http2-ceiling-soak",
"09-13-typed-orm",
"09-18-cms-dual-source-contracts",
"09-19-application-testing"
],
"parent": null,
"relatedFiles": [],
"notes": "2026-09-18: HTTP/2 有界饱和扫描、128/64 MiB 各 60 秒稳态与各 100 轮重连验收完成;typed ORM 子任务也已完成。原始结果见 test/performance/README.md,历史阶段证据见 implement.md。根任务仍需单独界定发行/跨平台、自举等后续路线;未运行全量测试、操作现有服务、安装全局命令或提交。",
"meta": {
"planning_workflow": "trellis",
"execution_mode": "inline"
}
}
Whitespace-only changes.
Whitespace-only changes.
@@ -1,72 +0,0 @@
# Dever 私有编译器与公开发行边界
## Goal
让用户只接触 Dever 语言、工具链和发行物,不接触或依赖 Rust/Cargo/rustc;Rust 仅作为私有仓库中的首代引导实现,不进入公开源码、安装流程、命令输出或用户运行环境。
## Background
- 当前 `/data/project/dever` 没有配置 Git 远端,Cargo workspace、Rust 源码、测试和 README 尚未提交,因此没有证据表明实现已经对外发布。
- 当前仓库及 Git 历史包含 Rust 实现信息,不能直接转换为公开仓库。
- 当前 `deverc run/build` 调用用户环境中的 `rustc`,并在临时目录生成 Rust 源码;这不符合对外隐藏实现和免 Rust 工具链的要求。
- 二进制实现无法可靠抵抗专业逆向识别。本任务隐藏正常产品表面和源码实现,不承诺不可逆向。
- 当前 Rust 编译器及计划使用的 LLVM 后端都有必须合规审查的第三方许可证;不能通过删除或伪造法定声明隐藏实现。
## Requirements
### Repository Boundary
- 当前编译器仓库保持私有,不向公开仓库推送现有 Git 历史、Cargo 文件、Rust 源码或内部设计资料。
- 对外发行使用一个没有当前仓库历史的新仓库或等价的独立公开渠道。
- 公开内容只包含 Dever 用户需要的文档、`.dever` 示例、安装入口和构建后的发行物,不包含编译器实现源码。
### Product Surface
- 用户只使用正式的 `dever` 命令和 `.dever` 源码,不需要了解 Cargo、Rust、rustc 或内部生成语言。
- 公开文档、安装器、帮助文本、普通诊断、日志和临时文件不得暴露 Rust/Cargo/rustc 实现细节。
- `dever check/run/build` 不要求用户安装 Rust 工具链,也不得调用用户 PATH 中的 `rustc` 或 `cargo`。
- `dever build` 生成的程序可独立运行,不依赖 Cargo、Rust 工具链或私有编译器源码。
- 更换公开发行边界和原生后端不得改变已经确认的 `.dever` 表面语法、诊断位置或当前可执行语义。
### Native Backend
- Rust 可以继续用于私有编译器本身,但用户侧原生编译路径必须由随 `dever` 交付的内置后端完成。
- 不采用“把 rustc 改名或静默捆绑”来满足隐藏要求;底层工具链不能作为可观察的用户侧子进程或安装前置条件。
- 编译必须在用户本机离线完成,不上传 `.dever` 源码或中间表示,也不依赖远程构建节点。
- 不采用云端编译来回避本地后端,因为这会额外引入源码上传、网络依赖和服务可用性边界。
### Initial Platform Boundary
- 首版支持 Linux、Windows 和 macOS;所有编译均在用户本机离线完成。
- Linux、Windows 和 macOS 均提供 x86_64 与 ARM64 版 `dever`。
- 任一受支持宿主上的 `dever build` 都必须能够离线生成 Linux、Windows 和 macOS 的 x86_64/ARM64 程序,形成 6 个宿主发行物到 6 个目标的完全对称矩阵。
- 生成跨平台程序不能依赖远程构建节点或上传源码及中间表示。
- 目标后端、链接能力和 Dever 平台运行时随发行物提供,不能要求用户另行安装目标平台编译器或 SDK。
- `dever build` 为 macOS 目标生成平台运行所需的离线 ad-hoc 链接签名;它不代表开发者身份,也不能通过 Gatekeeper 分发验证。
- Developer ID 身份签名和 Apple 公证是独立的可选发布阶段,不属于 `dever build`;Developer ID 安全时间戳和 Apple 公证均允许并必然需要联网。
## Acceptance Criteria
- [ ] 从一台没有 `cargo` 和 `rustc` 的目标机器安装公开发行物后,可以执行当前里程碑对应的 `dever check/run/build`。
- [ ] 安装、帮助、正常运行、普通错误和构建临时目录中均不出现 Rust、Cargo、rustc 或 `.rs` 源码。
- [ ] `dever build` 的输出是可独立运行的目标平台程序,并保持当前 `.dever` 示例的可观察行为。
- [ ] 公开仓库或公开发行包不包含当前私有仓库的 Git 历史、Cargo metadata、Rust 源码或私有 Trellis 资料。
- [ ] 自动检查能够证明公开构建路径没有启动 `cargo` 或 `rustc`,并覆盖没有 Rust 工具链的干净环境。
- [ ] 断网且未安装 Windows 编译工具链的 Linux 环境可以生成 Windows 程序,并在真实 Windows 环境验证其行为。
- [ ] 断网且未安装 Xcode 或 Apple SDK 的受支持环境可以生成 macOS 程序,并在真实 Intel Mac 和 Apple Silicon Mac 上验证其行为。
- [ ] 六个宿主发行物分别能够生成六个目标程序,36 种宿主到目标组合使用同一命令合同并通过确定性产物检查;每个目标程序均在对应真实系统与架构上执行验证。
- [ ] macOS ARM64 产物包含可离线生成的有效 ad-hoc 链接签名;编译不访问开发者身份或公证服务。
- [ ] 单独的发布流程明确区分 ad-hoc 链接签名、需要联网安全时间戳的 Developer ID 身份签名和需要联网的 Apple 公证。
- [ ] 私有仓库仍可维护和验证 Rust 引导编译器,公开边界不迫使内部源码改名或制造重复实现。
## Out Of Scope
- 阻止专业人员通过反汇编、二进制指纹或运行时特征推断实现语言。
- 为隐藏实现而混淆、加壳或伪造二进制来源。
- 在本任务中实现 HTTP、JSON、数据库、CRUD 或完整 Dever 语言路线。
- 公开编译器源代码或建立外部贡献者的编译器开发环境。
- 在 `dever build` 中管理用户的 Developer ID 凭据、执行身份签名或隐式提交 Apple 公证。
## Open Question
- 是否允许依法必须提供的第三方许可证清单提及 Rust;如果不允许,则 Rust 引导编译器只能作为私有 seed,Dever 自举编译器必须成为首次公开发行的前置条件?
@@ -1,35 +0,0 @@
# Linux Host Cross-Compilation
## Confirmed Toolchain Facts
- LLVM is designed as a cross-compiler, but target code generation alone is insufficient. A complete build also needs target-specific headers, libraries, linker behavior and usually a sysroot: <https://clang.llvm.org/docs/CrossCompilation.html>.
- LLD supports ELF, Windows PE/COFF and macOS Mach-O. Its PE/COFF port is described as complete; Mach-O support exists but is listed after ELF and PE/COFF in completeness: <https://lld.llvm.org/>.
- Microsoft publishes the PE/COFF executable and object format specification, so a Linux-hosted backend can produce Windows artifacts without invoking a Windows compiler: <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format>.
- Apple's documented universal-binary workflow requires Xcode on a Mac and uses Apple target tooling. Distributed macOS software also has Developer ID signing and notarization requirements: <https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary> and <https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution>.
- Apple exposes a Notary REST API, so submission need not use `notarytool`, but this does not remove SDK, linking, signing or real-device validation concerns: <https://developer.apple.com/documentation/NotaryAPI>.
## Product Consequences
### Linux to Windows
This is a practical offline target. A target-neutral Dever frontend can feed an embedded native backend, then a PE/COFF linker can combine generated code with a versioned Dever Windows runtime and required import libraries. The resulting `.exe` still needs validation on real x86_64 and ARM64 Windows systems.
### Linux to macOS
Generating x86_64 or ARM64 instructions and a Mach-O file on Linux is technically possible. Treating that file as a supported macOS product is harder: platform libraries, minimum OS versions, Mach-O signing details, Developer ID distribution, notarization and testing remain Apple-platform concerns. Redistributing an Apple SDK inside the Linux compiler also introduces a licensing boundary and must not be assumed acceptable.
For the current Text/println subset, a self-contained Dever runtime and purpose-built Mach-O path could avoid most SDK use. That shortcut does not establish a maintainable path for the planned HTTP, TLS, files, database and other system integrations.
Local compilation and public distribution are separate contracts. Once the target backend and versioned Dever macOS runtime are installed, `dever build` can remain offline. Apple Silicon requires every executable to be signed, so the linker must create an identity-free ad-hoc signature as part of offline output: <https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-universal-apps-release-notes>. Developer ID distribution signing requires the developer's identity and an online secure timestamp: <https://developer.apple.com/documentation/technotes/tn3161-inside-code-signing-certificates>. Apple's notarization service then requires uploading the signed artifact. Apple exposes a REST API for non-macOS notarization clients, but the service is necessarily online: <https://developer.apple.com/documentation/NotaryAPI>.
## Recommended Boundary
- Keep parsing, checking, HIR and target selection shared.
- Support offline Linux-to-Windows cross-build as a normal target once the embedded backend exists.
- Keep all first-release compilation local and offline; do not transfer source or intermediate representations to a build service.
- Include macOS as an offline compilation target without introducing controlled Mac build infrastructure. Treat Developer ID signing and Apple notarization as separate optional publishing stages, and require real Intel and Apple Silicon validation for supported Mach-O output.
- Implement each target through small ABI/runtime/object-format adapters rather than duplicating compiler flows.
## Open Product Decision
Resolved: the first release uses a fully symmetric Linux, Windows and macOS x86_64/ARM64 host-to-target matrix. Six host distributions must each produce all six targets locally and offline.
@@ -1,23 +0,0 @@
# Distribution Licensing And Implementation Disclosure
## Confirmed Facts
- The Rust project and standard library are distributed under `MIT OR Apache-2.0`: <https://rust-lang.org/policies/licenses/> and <https://github.com/rust-lang/rust/blob/main/COPYRIGHT>.
- A public compiler binary built from the current Rust implementation can contain Rust standard-library code. Its exact notice obligations require a release-license audit; the project must not assume that deleting all Rust attribution is permitted.
- LLVM is `Apache-2.0 WITH LLVM-exception`. LLVM states that binaries including LLVM must reproduce its copyright notice, while permitting closed-source and commercial use: <https://llvm.org/docs/DeveloperPolicy.html#copyright-license-and-patents>.
- Apple licenses prohibit separately using Apple SDKs or running Apple Software on non-Apple-branded hardware, and generally prohibit redistributing Apple Software in whole or part without permission: <https://www.apple.com/legal/sla/docs/xcode.pdf>.
## Consequences
Hiding Cargo files, symbols and implementation-specific diagnostics is a product-surface concern. Required third-party notices are a separate compliance concern and must remain truthful.
There are two viable disclosure boundaries:
1. Ship the Rust bootstrap compiler initially. Normal product surfaces mention only Dever, but a compliant third-party notice may identify Rust and LLVM. Professional binary identification is already outside the task scope.
2. Never ship the Rust bootstrap compiler publicly. Use it only as the private stage-1 seed, complete enough Dever language and runtime capabilities to build a stage-2 compiler, and publicly distribute the verified stage-2/stage-3 Dever compiler. LLVM attribution can remain if LLVM is embedded, but Rust is absent from the public executable and notices.
The second boundary matches a strict “no public Rust trace” requirement but makes self-hosting a release prerequisite rather than a later roadmap item. It materially expands the first public release.
## Rule
Do not use renamed tools, stripped license files, misleading notices, obfuscation or packaging tricks as a substitute for the chosen disclosure boundary.
@@ -1,104 +0,0 @@
# Native Backend Options
## Required Properties
- Consume checked Dever HIR without changing `.dever` syntax or semantics.
- Generate x86_64 and ARM64 ELF, PE/COFF and Mach-O on every supported host.
- Run fully offline without Cargo, rustc, a target SDK or a remote build service.
- Preserve direct native calls, concrete value layouts and the existing native performance direction.
- Keep generated IR, objects and link details private, owned temporary artifacts.
- Avoid six target-specific compiler implementations.
## Options
| Option | Strengths | Blocking weaknesses | Decision |
| --- | --- | --- | --- |
| Pinned LLVM code generation plus LLD | Mature optimization; x86_64/ARM64; ELF, PE/COFF and Mach-O; supports in-process use | Large host binaries, native integration boundary, mandatory LLVM notices, target runtimes still required | Recommended |
| Cranelift plus LLD | Safe Rust-facing library, fast compilation, x86_64/ARM64 | Still needs a cross-format linker and target runtimes; optimization quality creates risk against the native performance contract | Keep as fallback only if measured LLVM integration cost is unacceptable |
| Custom machine-code and executable writers | Full control and small artifacts for the current subset | Reimplements two ISAs, three formats, relocation, unwind and linking; duplicates platform logic and does not scale to the language roadmap | Reject |
| WebAssembly or a bytecode VM inside native launchers | Simple portable execution model | Changes the AOT/runtime architecture, adds a production VM and jeopardizes native performance requirements | Reject for production build/run |
| Generated Rust plus bundled or renamed rustc | Reuses current implementation | Exposes the forbidden toolchain, produces Rust temporary source and makes offline target support depend on Rust sysroots/linkers | Replace completely |
LLVM's cross-compilation documentation still requires target-specific sysroots/libraries beyond machine-code generation: <https://clang.llvm.org/docs/CrossCompilation.html>. LLD supports the three required executable formats and can be embedded for compiler-owned trusted objects: <https://lld.llvm.org/> and <https://lld.llvm.org/NewLLD.html>.
## Proposed Ownership
```text
dever-core
checked, target-neutral HIR and source diagnostics
dever-native
fixed target catalog
HIR -> LLVM lowering
object emission
LLD integration
target-pack selection
owned temporary artifacts
dever-runtime
Dever-owned ABI and runtime behavior
six release-built target packs
dever-cli
--target parsing, host/default selection, output and process adaptation
```
`dever-native` is one justified private workspace boundary: it isolates the large native dependency and target mechanics from parsing/checking. It is not a public backend plugin API. Target variation uses one explicit catalog and small data/config adapters, not inheritance or a registry.
## Build Data Flow
```text
.dever -> check -> typed HIR -> specialize reachable program
-> shared LLVM lowering -> target object in memory
-> fixed target specification + versioned Dever runtime pack
-> embedded LLD driver -> new executable output
```
- LLVM IR and object files are never public output formats.
- `dever build` creates a new output and keeps the current no-overwrite contract.
- `dever run` always builds the host target and runs it; cross-target execution/emulation is not added.
- A cross-target `build` only generates an artifact. Real target runners validate it in release CI.
## Target Catalog
The public catalog has exactly six initial targets:
```text
linux-x86_64
linux-arm64
windows-x86_64
windows-arm64
macos-x86_64
macos-arm64
```
Each target definition owns its LLVM triple, CPU baseline, object format, calling convention, executable suffix, runtime pack and deterministic linker settings. Shared OS and architecture helpers remove repetition; target entries remain explicit and reviewable.
## Runtime Packs
Generated code must use a Dever-owned ABI rather than Rust's unstable ABI or Rust `String` layout. Each target pack contains only Dever-owned startup/runtime objects and permitted platform import metadata. It must not contain Rust standard-library objects, Apple SDK files or target compiler binaries.
The current `dever-runtime` source is a behavior reference, not a stable native ABI. The first backend milestone defines the minimal Text slice, result/fault and `println` ABI once, then supplies six compiled implementations or target-specific adapters behind that contract. Future collections, numbers, HTTP, TLS and storage extend this ABI deliberately rather than reaching into host-language layouts.
## Determinism And Matrix Verification
- Pin one LLVM/LLD source revision and runtime-pack version for all six host builds.
- Remove timestamps, absolute build paths, random UUID inputs and host-dependent ordering.
- Each of six host compilers builds all six targets, producing 36 artifacts.
- Artifacts for the same target and source should be byte-identical across hosts. Hash equality reduces execution validation to one artifact per target; all six target artifacts still run on real matching systems.
- Offline checks deny network access and remove Cargo, rustc, Xcode, Visual Studio, GCC and target SDKs from PATH.
## Release And License Gates
- LLVM permits proprietary distribution but requires its copyright notice in binaries that include it: <https://llvm.org/docs/DeveloperPolicy.html#copyright-license-and-patents>.
- No Apple SDK file may enter a target pack. Apple's agreement prohibits separately using SDKs on non-Apple hardware and redistributing Apple Software in part: <https://www.apple.com/legal/sla/docs/xcode.pdf>.
- Before implementation commits to the macOS target, prove that both Mach-O targets can link the minimal Dever runtime without redistributed Apple SDK material and pass real-machine execution.
- The public-compiler Rust attribution decision in `distribution-licensing.md` remains a product gate.
## Rejected Shortcuts
- Do not rename or conceal rustc, Cargo, LLD or license files.
- Do not special-case the hello example or patch prebuilt executable templates.
- Do not implement one backend per OS.
- Do not upload source or HIR when local target support is missing.
- Do not silently fall back to a VM, host execution or another target.
@@ -1,28 +0,0 @@
{
"id": "private-compiler-distribution",
"name": "private-compiler-distribution",
"title": "Dever 私有编译器与公开发行边界",
"description": "隐藏 Rust 引导实现并建立不依赖 Rust 工具链的公开发行路径",
"status": "planning",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-06",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [
"09-28-shared-toolchain"
],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"检查 HTTP/2 配置与运行时公开合同一致"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查测量公平性、计数完整性、资源预算和验证范围"}
{"file":".trellis/tasks/09-12-http2-performance/research/current-benchmark-and-h2-api.md","reason":"核对实现是否真正拆分物理连接和并发流"}
@@ -1,84 +0,0 @@
# 设计
## 1. 边界与复用
继续使用 `test/performance` 的“构建与测量分离”结构:`build.py` 只生成并指纹化产物,`run.py` 只启动清单内的二进制并采样。进程、cgroup、直方图、逐 case 落盘和失败保留逻辑全部复用。新增的唯一核心抽象是 HTTP 连接拓扑:
```text
ConnectionShape
physical_connections
streams_per_connection
request_slots = physical_connections * streams_per_connection
```
HTTP/1.1 要求 `streams_per_connection = 1`;HTTP/2 要求复用连接。这个模型避免继续把并发请求与 TCP 连接混为一个 `connections` 参数,也让报告能解释吞吐和内存差异来自哪里。
## 2. Rust 基准对端与负载端
`network_bench` 的统一负载入口调整为:
```text
network_bench load h1|h2 URL PATH RATE SECONDS PHYSICAL_CONNECTIONS STREAMS_PER_CONNECTION TIMEOUT_MS REUSE CA
```
所有仓库调用者一次性迁移,不保留第二套旧参数解析。
- HTTP/1.1 保留现有“一名 worker 独占一条连接”的实现,并要求 streams 为 1。
- HTTP/2 在计时前主动建立指定数量的物理连接;每条连接由 Hyper HTTP/2 client driver 持有,`SendRequest` 按该连接的请求槽克隆给 worker。总 worker 数为 shape 的乘积,worker 固定映射到连接,不额外建立连接。
- HTTP/2 连接中断后不重试请求或静默补建连接。失败必须进入错误计数,避免恢复逻辑污染吞吐数据。
- 两种协议共享现有速率调度、队列、响应校验、延迟直方图和结果守恒逻辑,不复制 metrics/validation。
- 结果新增 `http_version`、`physical_connections`、`streams_per_connection`、`request_slots` 和 `successful_handshakes`。H2 的握手数必须等于物理连接数。
H2 请求使用完整 scheme/authority/path 和 `Version::HTTP_2`。明文使用 prior knowledge;TLS client/server 配置按协议派生 ALPN,H2 只允许 `h2`,不改变运行时用于 WSS 的共享 TLS 配置。
服务模式新增 `runtime-http2`、`runtime-https2`、`hyper-http2`、`hyper-https2`。`ServiceKind` 集中派生 runtime/TLS/H2 三个维度,避免模式分支散落。直接 runtime 使用 `Http2Limits`;原生 Hyper 使用 `http2::Builder<TokioExecutor>`,显式配置与 N5 相同的 streams、窗口、header/frame/send-buffer 上限。两者继续复用同一响应路由和连接生命周期。
## 3. Dever 夹具与构建清单
HTTP 夹具暴露 `http`、`https`、`http2`、`https2` 四个入口。`serve` 接收 `dever.http.Limits` 参数,四个 worker 只负责选择 TLS 和协议配置,避免复制路由与关闭流程。
`build.py` 增加 HTTP/2 构建参数并写入 manifest:
- `http2_streams = 16`
- `http2_stream_window_bytes = 65535`
- `http2_connection_window_bytes = 262144`
`config.dever` 分别生成 HTTP/1.1 和 HTTP/2 limits;`task_capacity` 继续按有界并发计算,并校验正式拓扑的总请求槽不超过语言上限。旧产物目录和报告不修改,新构建必须使用新的输出目录。
## 4. Python 运行器
新增两个显式 suite:
- `http2`:吞吐、延迟与活跃资源矩阵。
- `http2-recovery`:同一服务进程上的多轮连接建立、请求和断开。
`--http2-connections` 接受物理连接档位,默认 `1,4`;每连接流数从 manifest 读取。`--implementations` 可将 smoke 或 64 MiB 场景限制为 `dever`,正式矩阵默认为 `dever,runtime,hyper`。已有 `http` suite 的 `--concurrency` 和连接新建/复用语义不变。
吞吐 case 的流程为:启动服务并取得 READY,运行独立 warmup,等待连接释放并采样基线,运行正式负载,同时采样服务端与客户端;负载退出后再次采样以记录 FD/RSS 回落。case 名和 JSON 主键包含实现、传输、路径、速率、物理连接、每连接流和 repeat,避免结果覆盖或混淆。
恢复 case 使用独立参数 `--recovery-connections`、`--recovery-rate`、`--recovery-path`,复用 `--cycles` 与 `--duration`。每轮启动一次负载进程,在同一服务上建立固定 shape,完成请求后关闭连接;运行器等待稳定窗口并保存该轮末样本。FD 不回到 READY 基线、请求错误、握手数不符或服务提前退出均使 case 失败。RSS 只报告趋势,不以“没有立即回到启动值”单独判断泄漏。
报告继续保留 `cases.jsonl` 和 `report.json`,并在完整或部分结果上生成结构化 `summary`。摘要按实现、协议、TLS、路径、速率和 topology 分组,对成功 QPS、p99、RSS/PSS/FD 峰值取各 repeat 中位数;不把不同 offered rate 或饱和/未饱和 case 混合。
## 5. 测量矩阵
正式测量固定服务端 1 worker、1 CPU、128 MiB cgroup、swap=0;客户端绑定另一 CPU且位于服务预算之外。每组 0.5 秒预热、2 秒正式负载、3 个独立服务进程 repeat:
| 正文 | offered rate | topology | 实现 | 传输 |
| --- | --- | --- | --- | --- |
| `/plain` 5 B | 5k / 15k / 30k req/s | 1x16、4x16 | Dever/runtime/Hyper | h2c、TLS+h2 |
| `/bytes` 64 KiB | 100 / 500 / 1k req/s | 1x16、4x16 | Dever/runtime/Hyper | h2c、TLS+h2 |
另运行 10 轮、1x16、`/plain` 的恢复矩阵,以及 64 MiB 下 Dever 的 1x16 明文/TLS 活跃代表场景。若当前机器不能创建指定 cgroup,工具必须明确失败;不得回退为无预算结果后仍标记完成。
## 6. 解释与决策
- 主要比较同一 H2 topology 下 Dever、runtime 和 Hyper 的差异,定位语言桥接、运行时结构化任务和底层协议栈各自成本。
- 1x16 与 4x16 比较多路复用和多物理连接扩展;它们不是“连接数相同”的横向比较。
- `/bytes` 的速率按响应字节量降低,避免只测客户端或 loopback 带宽耗尽;65536 字节正文会越过 65535 字节默认单流窗口,可观察 WINDOW_UPDATE 路径。
- `dropped_capacity` 表示负载端无法在计划时间取得请求槽,不算服务错误;分析时必须同时看 actual QPS、请求延迟和服务 CPU,不能只看 offered rate。
- 不设预定胜负线。若 Dever 相对 direct runtime 出现稳定的大幅差距,或内存随 recovery 周期持续增长,则把证据和复现命令交给后续独立优化任务。
## 7. 回退形态
本阶段不迁移生产 API。若 H2 基准实现无法通过独立 smoke,删除新增 suite、服务模式和夹具入口即可回到原有 H1 基准;历史产物目录不受影响。任何正式测量都写入新的 `target/performance/*` 目录,失败结果保留且不覆盖旧报告。
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"HTTP/2 streams、窗口、ALPN、结构化任务和资源预算合同"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"独立二进制基准、cgroup、报告和定向验证规则"}
{"file":".trellis/tasks/09-12-http2-performance/research/current-benchmark-and-h2-api.md","reason":"现有 H1 基准限制、锁定 Hyper API 和 H2 负载模型结论"}
@@ -1,50 +0,0 @@
# 执行计划
## 1. 任务启动
- [x] 用户审核本 PRD、设计和执行计划后,运行 `task.py start`;启动前不改产品或基准源码。
- [x] 使用 `trellis-before-dev` 重新加载 backend 规范,核对工作区状态和已有性能产物,不清理用户文件。
## 2. 基准协议模型
- [x] 在 `network_bench` 中加入 `h1|h2` 与 `ConnectionShape` 参数校验;H1 限制每连接一槽,H2 限制复用且乘积有界。
- [x] 保留一个速率调度和 metrics 路径;把 H1 独占连接与 H2 共享 sender 封装为两个小的协议执行分支,不引入通用 trait 层级。
- [x] H2 计时前建立固定数量物理连接,按连接分配 sender clone,关闭时排空/终止 driver;不重试、不自动扩容。
- [x] 在结果中输出并校验协议、shape 和握手计数,继续保证全部请求计数守恒和完整正文校验。
## 3. 三类服务实现
- [x] 扩展 `ServiceKind` 和 TLS ALPN 派生,新增 runtime/raw Hyper 的 h2c 与 TLS+h2 模式。
- [x] direct runtime 与 raw Hyper 使用相同的 streams、窗口、header/frame/send-buffer 约束;复用响应路由、accept 限额和关闭流程。
- [x] 重构 Dever HTTP fixture,让四个入口共享 serve/route/lifecycle,只在入口选择协议和 TLS。
- [x] 扩展构建配置与 manifest,生成并指纹化 `http2`、`https2`,保留 H1 产物。
## 4. 运行器与摘要
- [x] 增加 `http2`、`http2-recovery` suite、物理连接档位、实现筛选及恢复参数,并按 manifest 校验 capacity、streams 和窗口。
- [x] HTTP/2 吞吐 case 记录 warmup、正式负载、断开后样本、服务/客户端资源和 cgroup 状态;case 标识包含完整维度。
- [x] 恢复 case 在一个服务进程内运行多轮固定 shape,逐轮校验请求和握手并记录 FD/RSS 回落。
- [x] 从逐 case 结果生成分组中位数 summary;失败时仍保存已完成 case 和可诊断日志。
## 5. 定向验证
- [x] 运行 `python3 -m unittest discover -s test/performance -p 'test_runner.py' -v`,覆盖参数、计数、summary、进程回收和错误路径。
- [x] 离线 release 构建 `network_bench`,运行现有独立 Python H1 对端测试,确认一次性 CLI 迁移没有改变 H1 测量语义。
- [x] 运行 H2 loopback smoke,覆盖 1 条物理连接、至少 16 个请求槽、明文/TLS、`/plain`/`/bytes`、三类服务模式和错误正文拒绝。
- [x] 用新的输出目录构建 Dever 性能夹具,核对 manifest 指纹、HTTP/2 limits 和四个 HTTP 入口。
- [x] 运行最小 `http2`/`http2-recovery` runner smoke,检查失败报告、FD 回落和原始计数完整性。
## 6. 有界正式测量
- [x] 先确认可用 CPU affinity、cgroup v2 cpu/memory 控制器、磁盘和已有服务边界,再说明将执行的有界命令。
- [x] 运行 128 MiB `/plain` 矩阵:5k/15k/30k,1x16/4x16,三实现、h2c/TLS、3 repeats。
- [x] 运行 128 MiB `/bytes` 矩阵:100/500/1000,1x16/4x16,三实现、h2c/TLS、3 repeats。
- [x] 运行 128 MiB 10 轮恢复矩阵,并运行 64 MiB Dever 1x16 明文/TLS 活跃场景。
- [x] 核对全部报告 status、errors、dropped、OOM、FD、RSS/PSS、CPU 和摘要分组;不以单次异常样本下结论。
## 7. 收尾审查
- [x] 更新 `test/performance/README.md` 和 `.trellis/spec/backend/quality-guidelines.md` 的 CLI、矩阵、实测结果和边界说明。
- [x] 检查最终改动是否仍有重复协议分支、混合职责或无必要依赖;确认未修改生产运行时、旧报告和用户文件。
- [x] 运行任务 JSONL 校验、Python 语法检查及受影响的最小回归;不运行全量测试。
- [x] 记录实际通过、失败、跳过和不可用项;完成 Trellis 检查与会话记录,不提交 Git。
@@ -1,40 +0,0 @@
# HTTP/2 性能基准
N5 已完成 HTTP/2 服务端、客户端和连接池的功能接入,但现有独立二进制基准仍只测 HTTP/1.1。当前阶段为 HTTP/2 建立可重复的小机器性能证据,不凭功能测试或理论值宣称性能提升。
## 需求
- 扩展现有显式运行的独立二进制基准,新增 HTTP/2 suite;保留已有 HTTP/1.1、异步和长连接基准的语义及历史报告。
- 覆盖明文 prior knowledge 和 TLS ALPN 两种 HTTP/2 传输,统一比较原生 Dever 程序、直接 `dever-runtime` 和原生 Hyper 三种实现。
- 负载模型必须把物理 TCP 连接数与每连接并发流数分开。每个报告明确记录协议、TLS、物理连接数、每连接请求槽、总请求槽、服务端流/窗口配置和正文大小。
- 至少覆盖单连接多流和多连接多流;请求必须校验状态码、Content-Type 和完整正文,所有计划、发送、完成、失败及丢弃请求均可核对。
- 记录实际成功吞吐、总延迟/排队延迟/请求延迟的 p50/p95/p99,以及服务端和客户端 RSS、PSS、峰值、线程、FD、CPU 和 cgroup 内存/OOM 数据。
- 同一服务进程上重复建立、使用和关闭 HTTP/2 连接,记录每轮断开后的 FD/RSS 差值,验证物理连接能够回收且内存不按轮持续增长。
- 生成按实现、传输、路径、速率和连接拓扑分组的中位数摘要,同时保留逐 case 原始样本和失败时已完成的数据。
- 完成 1 worker、1 服务 CPU、128 MiB、无 swap 的正式矩阵,并完成 64 MiB 下的 Dever 活跃 HTTP/2 代表场景;负载端使用独立 CPU且不计入服务预算。
## 约束
- 本任务只改基准夹具、基准对端、运行器、定向测试和对应性能文档;不修改 `crates/dever-runtime`、编译器或语言公共 API。基准揭示的生产瓶颈另建优化任务,避免边测边改导致基线失效。
- 复用仓库锁定的 Hyper 1.11.1、h2 0.4.19、rustls 和现有进程/cgroup 采样器,不引入新的协议栈或压测依赖。
- HTTP/2 明文只使用 prior knowledge;TLS 只协商 `h2`,不降级 HTTP/1.1。HTTP/1.1 每连接请求槽固定为 1,HTTP/2 只测复用连接,不测每请求新建连接。
- 服务端与客户端固定使用 N5 默认值:每连接 16 流、65535 字节单流接收窗口、262144 字节连接接收窗口,以及运行时已有的 16 KiB frame/send-buffer 上限。
- `/plain` 用于小响应调度和协议开销,`/bytes` 使用 65536 字节正文并跨越默认单流窗口;`/json` 不进入正式矩阵,因为当前夹具只返回预制 JSON,信息与小响应重复。
- 不设与机器无关的绝对 QPS 或内存门槛。错误、OOM、计数不守恒和 FD 未回落属于失败;容量丢弃与相对性能只作为结果,不伪装成通过或失败阈值。
- 仅运行本任务的 Python 单元测试、独立 loopback smoke、必要的离线 release 构建和显式有界性能命令;不运行全量测试,不替换全局二进制,不接触已有服务,不提交 Git。
## 验收标准
- [x] HTTP/1.1 旧入口仍通过现有独立 Python 对端测试;HTTP/2 smoke 证明一条实际物理连接承载至少 16 个并发请求槽,明文和 TLS 均完整成功。
- [x] 构建清单包含 `http2`/`https2` Dever 产物、HTTP/2 配置及所有二进制指纹;运行阶段只使用清单中的已构建产物。
- [x] `http2` suite 覆盖三种实现、两种传输、`/plain` 与 `/bytes`、`1 x 16` 与 `4 x 16` 两种拓扑,并保存可核对的逐 case 数据与聚合摘要。
- [x] `http2-recovery` suite 在同一服务进程上完成至少 10 轮连接周期;每轮请求正确,断开后 FD 回到 READY 基线,且报告保留 RSS 变化序列。
- [x] 128 MiB 正式矩阵中无请求错误、无 OOM;丢弃请求如实报告。64 MiB 代表场景无 OOM,并报告峰值而非只报告进程 RSS。
- [x] 文档写明运行命令、环境、矩阵、限制和实测结论;不得将 HTTP/2 结果外推为所有后端负载或真实网络的性能保证。
- [x] 定向测试覆盖参数校验、计数守恒、连接拓扑字段、摘要分组、H2 明文/TLS 正文校验、进程失败和连接回收;相关检查全部通过。
## 排除项
- RFC 8441 WebSocket-over-HTTP/2、h2c Upgrade、HTTP/3、服务器推送、非空 trailers、HTTP/2 客户端自动重试。
- 小时级 soak、跨主机网络、丢包/限速故障注入、真实公网站点和多平台硬件矩阵。
- 本阶段根据一次测量直接改写运行时热路径或承诺固定的 Fiber/Go 对比数字。
@@ -1,31 +0,0 @@
# 现有基准与 HTTP/2 API 调研
## 现有结构
- `test/performance/build.py` 负责生成 Dever fixtures、复制 Rust peers、记录配置和 SHA-256;`run.py` 只执行已指纹化产物。这一分离可直接复用。
- 当前 HTTP suite 覆盖 Dever、direct runtime、raw Hyper,但三者都只使用 HTTP/1.1。`config.dever` 明确写入 `http2 = null`。
- 当前 loader 的 `connections` 同时决定 worker 数和连接数,每个 worker 串行持有一条 HTTP/1.1 连接;它无法证明 HTTP/2 多路复用。
- 现有 runner 已有固定速率调度、三类延迟直方图、进程 RSS/PSS/FD/CPU 采样、cgroup memory/peak/OOM 采样、逐 case 落盘和异常清理,不应重写。
- `live` suite 已有多轮连接后 FD/RSS 差值的报告方式,可复用其 phase 后采样语义,但 HTTP/2 负载和连接创建应继续由 `network_bench` 负责。
## N5 已有合同
- `Http2Limits` 的公开维度为 streams、stream window 和 connection window;默认分别为 16、65535、262144。
- cleartext HTTP/2 使用 prior knowledge;TLS 配置必须协商 `h2`,不能降级或影响 WSS 的 HTTP/1.1 TLS 配置。
- 客户端按流租约复用物理连接,服务端连接和流均受限;固定 16 KiB frame/send-buffer,关闭自适应窗口。
- `/bytes` fixture 为 65536 字节,恰好跨过默认 65535 字节单流窗口,适合覆盖 flow-control 热路径。
## 锁定依赖可用能力
- Cargo.lock 固定 Hyper 1.11.1、h2 0.4.19、rustls 0.23.44 和 tokio-rustls 0.26.5,不需要新依赖。
- Hyper HTTP/2 `SendRequest` 实现 `Clone`,可让多个请求 worker 共享同一物理连接的 driver。
- Hyper HTTP/2 client/server builder 可显式设置 initial stream/connection window、adaptive window、max concurrent streams、max frame、max send buffer 和 header list size。
- Hyper HTTP/2 request future 的取消会对单流发送 RST_STREAM;基准不需要自己实现帧协议。
## 设计结论
1. 物理连接与请求槽必须拆开;只给现有 loader 换 HTTP/2 handshake 会得到错误的“一 worker 一连接”模型。
2. H2 连接应在计时前主动建立,worker 固定映射到已建 sender;这样报告的物理连接数由 loader 自己控制和证明。
3. H1 和 H2 共享调度、计数和响应校验,但连接执行策略分开。不存在足够稳定的公共行为值得引入 trait 或继承式抽象。
4. raw Hyper 对照必须显式复制 N5 的协议参数;使用 Hyper 默认值会把配置差异误判为 Dever/runtime 开销。
5. 性能任务不同时修改生产 runtime。测量发现的瓶颈需要在固定基线后另立任务验证,否则优化前后结果不可复现。
@@ -1,26 +0,0 @@
{
"id": "http2-performance",
"name": "http2-performance",
"title": "HTTP/2 performance benchmarks",
"description": "扩展现有独立二进制基准,测量小内存下 HTTP/2 多路复用的吞吐、尾延迟、内存和连接回收。",
"status": "completed",
"dev_type": null,
"scope": "test/performance + test/dever-tests/examples/network_bench* + performance docs/spec",
"package": null,
"priority": "P2",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-12",
"completedAt": "2026-09-13",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "HTTP/2 benchmark complete: 224 formal cases plus 60 recovery cycles, fixed 1x16/4x16 topology, h2c/TLS and Dever/runtime/Hyper comparisons. All request errors, OOM and FD deltas were zero; 64 MiB active Dever cases passed. Sixteen runner tests, three loopback tests, release cargo check, Dever fmt-check and independent review passed. No full suite, Rustfmt/Clippy, commit or archive.",
"meta": {}
}
-4
View File
@@ -1,4 +0,0 @@
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"检查 HTTP2 的结构化归属、资源容量和桥接"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"定向测试和无全量测试约束"}
{"file":".trellis/tasks/09-12-http2/prd.md","reason":"完整范围和验收"}
{"file":".trellis/tasks/09-12-http2/design.md","reason":"协议配置与生命周期"}
-21
View File
@@ -1,21 +0,0 @@
# 设计
## API 与复用
在官方 `dever.http.Limits` 增加 `http2: Http2Limits?`;`Http2Limits` 三个 Int 字段为 `streams`、`stream_window_bytes`、`connection_window_bytes`。默认关闭 HTTP/2,`default_http2_limits()` 返回 16 流、65535 字节单流窗口、262144 字节连接窗口。沿用现有 serve/serve_tls/serve_live/client/open/open_stream/send,不增加平行 API。
`Limits.check` 统一校验;HTTP/2 streams 和 connections 的乘积不得超过 65536,窗口遵循 HTTP/2 31 位限制(连接窗口至少 65535)。关闭自适应窗口,保持帧和发送缓存有界。源语言 exact-record 检查与原生结构转换同步修改。
## 运行时边界
- 协议配置、Hyper builder 和 HTTP 专用 TLS ALPN 配置集中在 HTTP 模块,原有 TLS trust/session cache 和 Transport 复用。不能修改共享 TLS 配置使 WSS 错误协商 h2。
- 编解码复用 message;按协议校验头字段,HTTP/2 使用路径查询而不是绝对 URI 作为 source Request.target,authority 与 Host 一致。
- HTTP/1 仍独占连接 lease;HTTP/2 持有共享连接与独立流 lease。EOF 或取消释放流,只有连接 owner 结束才释放物理连接许可。空闲回收不能中止活跃流。
- Hyper Executor 必须接入已有结构化任务 Scope,不能直接用脱离作用域的 TokioExecutor。每流清理独立,程序故障明确传给服务任务,peer reset 只结束该流。
- GOAWAY 排空与业务取消分开;应用已有 timeout(server, grace_ms, fallback) 控制最长排空时间。
## 验证
仅运行本次相关 Rust 定向测试、编译器官方库检查和隔离 loopback 原生程序。独立 HTTP/2 对端验证真正单连接多流、SETTINGS/流控、RST_STREAM、GOAWAY、ALPN 和请求语义。保留已有性能报告;缓存可重建部分暂存到专用 /dev/shm 目录以腾出构建空间。
协议依据:[RFC 9113](https://www.rfc-editor.org/rfc/rfc9113.html) 与当前锁定 Hyper 1.11.1 源码。
@@ -1,5 +0,0 @@
{"file":".trellis/spec/backend/directory-structure.md","reason":"Rust 语言分层与 root test 布局"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"HTTP、TLS、流、结构化任务及原生桥接合同"}
{"file":".trellis/spec/backend/error-handling.md","reason":"程序故障与 peer 失败边界"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"有界定向验证与原生测试"}
{"file":".trellis/tasks/09-12-http2/design.md","reason":"本阶段已批准接口与生命周期设计"}
-49
View File
@@ -1,49 +0,0 @@
# 执行记录
## 实现
- [x] 用户批准方案后实施,复用 Hyper 1.11.1;启用 http2 feature,锁定 h2 0.4.19 和四个传递依赖,不引入第二套协议栈。
- [x] 官方 Limits.http2/Http2Limits、精确记录检查和仓库调用者一起迁移;原生桥接复用 Nullable/Record 转换,无新增 intrinsic。
- [x] protocol/message 集中拥有流控预算、ALPN 派生、authority/Host 和方向相关的头校验。
- [x] buffered/live 服务端和 single-use/pooled 客户端接通;流式上传、下载、SSE、GOAWAY 和取消沿用现有 API。
- [x] ScopedOwner/ScopedSpawner 接入已有 Task Scope、全局容量和 blocking 清理;每流独立 Group,物理许可持有到协议后代清理完成。
- [x] 按工作流完成服务端/客户端实现分工及独立并发、全范围检查;主代理复查关键所有权和运行证据。
- [x] 更新现有 LANGUAGE、IMPLEMENTATION、README、toolchain-and-library 和性能说明;P1/P2 报告仍保留为 HTTP/1 数据。
- [x] 最终定向验证与检查闭环。
- [x] 完成任务状态、会话记录和自有临时目录清理,不提交。
## 复现及修正
- Hyper Executor 不携带 Dever CURRENT_SCOPE:协议 future 改由连接 owner 创建子 Scope;关闭 admission 与登记使用同一把锁。故障报告/注销先于完成信号;不可取消 supervisor 排空后代,等待者被取消也不能遗失 completion。
- 池握手的连接 owner 必须在持久 driver 首次 poll 时创建,不能归临时请求 Scope。
- RST/正文 idle 超时使背压 write 同时失败:先记录传输取消,避免把该流结束当成全局 handler 故障;真正程序错误仍保留原消息。发布失败时先释放 state 锁再析构响应体,避免取消标记重入锁。
- GOAWAY 后 Hyper dispatch ready 不等于能建新流:仅 TrySendError 结构化返回的未发送请求可在原期限内重排;先释放旧上传/流租约,不重放已发送请求。
- HTTP/2 Response TE 必须显式拒绝,不能交给 Hyper 静默删除;请求 TE 只接受 trailers。
- 测试修正:Dever 子任务用 await;静默 h2 peer 可能先收到 SETTINGS,需读到 EOF;独立 h2 的 FlowControl/RecvStream/SendStream 在等 driver 结束前释放。共享原生 fixture 的 HTTP/1 服务端保留三条连接容量,两条供池内嵌套代理,一条供独立 WSS;HTTP/2 客户端仍只允许一条物理连接。
## 验证约束
使用 PATH 指向已有 stable 工具链、CARGO_TARGET_DIR=target/native-runtime、TMPDIR=/dev/shm/dever-http2-native.fW3PNS,Cargo 均 --offline --locked --release。一轮 fetch 后不再联网。仅自有随机 loopback 端口和 test/ 下测试证书,不运行全量测试、既有服务、性能矩阵或全局安装。
首轮 CLI release 构建和 backend 示例 check 通过。官方库 fmt-check 仅报告原有 net.dever 格式差异;修改的 http.dever 无差异。Rustfmt/Clippy 未安装,不作为通过项。
低磁盘处理仅将可再生 target/debug/{incremental,deps,build} 暂存到 /dev/shm/dever-http2-cache.wOoaI9;现有 debug/deverc、源码和 target/performance 报告保留。最后统一记录验证结果与缓存清理。
## 最终测试证据
88 个不同定向用例全部通过(修复后重跑不重复计数):http2_client 6、http2_server 6、http_engine 12、http_library 7、live_network 16、pooled_network 14、pooled_library 6、async_runtime 10、structured_concurrency 11。
- 独立 h2 对端证明一条物理连接的并发请求、流式上传/下载、RST 前后隔离、背压写取消、WINDOW_UPDATE 恢复、流上限与活跃正文不会被 idle 回收、GOAWAY 的物理预算与连接替换、TLS ALPN 成功/拒绝、Host/target、413 与原始 handler 故障。
- 原生 Dever 程序证明 single-use HTTP/2、TLS 同池嵌套代理(客户端只有一个物理名额)、上传、SSE 和 handler 内 run/await。共享 HTTP/1 变体另覆盖 WSS 与优雅停机;测试服务端三连接容量的修正经实际运行确认。
- 最新响应发布取消/锁序改动已重新编译,并由 http2_server 6 项和 live_network 16 项覆盖;已有结构化任务、HTTP/1 连接池与源码语义检查通过。
- 源码空白/冲突标记扫描、性能构建器 Python 语法和任务 context JSONL 验证通过。Rustfmt/Clippy 未安装;未运行性能矩阵、全量测试或既有服务验证。
复现上述范围:
```bash
cargo test --offline --locked --release -p dever-tests --test http2_client --test http2_server --test http_engine --test http_library --test live_network --test pooled_network --test pooled_library --test async_runtime --test structured_concurrency
```
最终 CLI release 构建通过,产物为 `target/native-runtime/release/deverc`;backend 示例的实际 CLI check/fmt-check 均无输出并退出 0。迁移后的 network_bench example 及启用 reference 的 core/runtime 离线 release check 通过。未替换全局命令。
独立审查与主代理最终所有权、调用链、规范和改动范围核对闭环,无剩余生产阻断。清理专属 shm 构建目录,并删除暂存的约 378 MiB 可重建 debug 中间缓存;源码、现有编译器和全部性能报告保留。按用户约束手动更新状态/日志,不提交、不调用自动提交归档流程。N5 完成,HTTP/2 性能测量、RFC 8441、数据库与发行等仍在后续路线图。
-16
View File
@@ -1,16 +0,0 @@
# N5 HTTP/2
用户已批准上一阶段提出的 HTTP/2 接入方案,并要求继续实现。复用 Hyper,面向小机器控制运行内存;不实现旧源码兼容层,不改运行时根任务调度策略。
## 范围与验收
- buffered/live 服务端和 single-use/pooled 客户端支持 HTTP/2;HTTPS 必须通过 ALPN 协商 h2,明文为 prior knowledge。
- `Limits.http2: Http2Limits?` 显式选择协议;null 为 HTTP/1,非空为 HTTP/2。配置包含 streams、stream_window_bytes、connection_window_bytes,连接限额仍由 connections 持有。仓库调用者直接迁移。
- 连接内复用请求,流式上传/下载与 SSE 复用现有正文接口;并发流、窗口、发送缓存有界,单流中止不取消相邻流。
- Hyper 内部任务保留 Dever 的作用域归属;子任务、已启动 blocking、取消及故障完整排空,容量持有到清理完成。
- 监听器关闭发送 GOAWAY 并排空在途流;客户端不向已 GOAWAY 连接派发新请求,不自动重试已发送请求。
- 使用独立线协议对端和原生 Dever 程序验证多路复用、TLS ALPN、流控、取消隔离和生命周期,选取 HTTP/1 与 WS 回归。
## 排除
本阶段不接 RFC 8441 WebSocket-over-HTTP/2、h2c Upgrade、服务器推送、非空 trailers、自动协议降级,不跑全量测试或重新做性能矩阵,不替换全局二进制,不提交 Git。
-26
View File
@@ -1,26 +0,0 @@
{
"id": "http2",
"name": "http2",
"title": "HTTP/2 server and multiplexed client",
"description": "复用 Hyper 接入 HTTP/2 服务端、客户端多路复用、TLS ALPN、流控和结构化取消;验证流式正文与 SSE,保留 HTTP/1 和普通 WebSocket。",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-12",
"completedAt": "2026-09-12",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "N5 complete: 88 focused cases passed, native HTTP/2/TLS proxy/upload/SSE and HTTP/1/WSS verified; CLI build/check and independent review passed. No performance matrix, global install or commit.",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Benchmark verification contracts"}
{"file":".trellis/tasks/09-12-runtime-performance-p2/prd.md","reason":"Scope and acceptance"}
{"file":".trellis/tasks/09-12-runtime-performance-p2/design.md","reason":"Semantic comparison and lifecycle"}
@@ -1,11 +0,0 @@
# Design
现有 test/performance 负责产物和采样,Rust examples 负责压力对端。保留该分工,不增加第二套预算与报告框架。
异步对照使用独立 async_bench example:相同 run/wait、checked Int 加法、输入与批次;runtime-root 与 runtime-worker 仅改变批次所在任务。worker 模式增加一个结构化外层任务,不修改生产调度。对照程序输出 P1 SAMPLE 协议,Python 复用校验和与样本汇总。
长连接使用新增 Dever fixture 与 Rust 对端。Rust 对端有固定连接上限、阶段标记、消息校验和超时;Python 根据阶段采样同一个服务进程,复用 Process/Budget。每轮连接、保持、断开后等待采样,最后重连探测服务可用。TCP/WS 做小正文交互;SSE 校验有限事件,保留流直到断开。
高负载扫描复用现有 HTTP load,不重写压测器。使用独立 CPU 亲和性和 128 MiB 服务预算,合理短窗口与重复次数;保留客户端瓶颈和短窗口限制。
HTTP/2 只做本地依赖/池/协议边界核查:Hyper h2、TLS ALPN、流与连接预算、复用客户端、GOAWAY、取消隔离;HTTP/1 WebSocket 升级保持独立。
@@ -1,4 +0,0 @@
{"file":".trellis/spec/backend/index.md","reason":"Rust language owning layers"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Bounded standalone benchmark contracts"}
{"file":".trellis/tasks/09-12-runtime-performance-p2/prd.md","reason":"Approved scope and acceptance"}
{"file":".trellis/tasks/09-12-runtime-performance-p2/design.md","reason":"Reuse and ownership"}
@@ -1,21 +0,0 @@
# Execution
- [x] 主代理:async_bench、Python 集成、HTTP 扫描、采样、结果分析与 HTTP/2 就绪评估。
- [x] trellis-implement:long connection Rust 对端及 Dever fixture,独立文件所有权,不修改生产代码。
- [x] 定向 offline release 构建、生成原生 fixture、校验工具错误路径、执行有界矩阵。
- [x] trellis-check:复查统计、语义、连接清理和最终结果;主代理复核关键证据。
- [x] 更新现有性能 README、任务与日志,不提交。
磁盘空间有限;复用 target/native-runtime 的 release 依赖。原生构建 TMPDIR 使用任务独有 /dev/shm 临时目录,结束移除自有目录。不并行执行 Cargo 构建。
## 已取得证据
- HTTP/HTTPS 高负载完整 54 组,report: target/performance/http-load-p2-final/report.json。1800000 计划、1500830 实际成功、0 错误、299170 未发出;三实现均有压测槽位丢弃,不能推断服务端吞吐上限。无 OOM。
- 首次 http-load-p2 在子进程退出采样竞态中断:/proc memory teardown 早于 waitpid 状态。已修复采样拥有层,补定向回归,保留中断报告。
- run/wait Rust 对照用生产 task runtime、Bytes 和 checked Int;build 工具使用与 native 相同优化参数和 runtime rlib。async-p2 已完成 18 组:Dever root/worker 15.50/2.003 µs,Rust 15.59/1.984 µs,三轮校验均通过且无 OOM。独立 perf 确认 root/worker 线程切换为 465149/56 次;生产运行时未修改。
- 独立 review 修正了 live 总超时遗漏关闭阶段预算、SSE 保持期无心跳仍通过的问题;静态发现并修正 SSE driver 早期错误所有权和任意截止点半帧误判。所有修改均在 benchmark 工具/fixture 内。
- 首次 live-p2 消息校验通过,但发现阶段读/采样间跨轮竞态:下一轮 FD 混入上轮 disconnected。修复为采样前后 progress 必须一致,丢弃过渡阶段观察,并新增回归。最终 live-p2-final 完整重跑 27 组、18144 连接、377061 消息;错误/OOM 为零,81 次断开 FD 差值均为零。
- live-cycles-p2 对三个协议各做 512 连接十轮重连、每轮 3 秒;15360 连接、471207 消息,错误/OOM 为零,30 次 FD 均回基线。SSE RSS 在前三轮升到约25 MiB,后七轮在25.24–25.36 MiB;TCP约8.8、WS约17 MiB。短时证据不宣称小时级稳定性。
- 12 个 Python 运行器定向检查、4 个独立 TCP/SSE 对端检查通过;11 个 Dever 入口生产优化构建和两个新 Rust example 离线 release/原生优化构建通过。Dever fmt-check、Python解析、源码空白/冲突和范围检查通过;Rustfmt/Clippy 不可用,未运行。
- /dev/shm/dever-perf-p2.E84QV8 已空并移除。保留原始构建/剖析/中断与最终报告以供复核;不提交,不运行自动归档或自动日志提交脚本。
- 最终独立检查无剩余阻断;主代理复核四份最终报告、全部 OOM/FD 计数及无残留预算组。P2 完成,可进入 HTTP/2 阶段,未将实验调度变化当作已实现的生产优化。
@@ -1,12 +0,0 @@
# Runtime performance P2
用户已批准继续上一阶段;本阶段完成同语义异步对照、高负载 HTTP/HTTPS 扫描和 TCP/WebSocket/SSE 有界长连接测试,并回答 HTTP/2 的接入条件。HTTP/2 实现留到下一阶段。
## 验收
- Rust 对照调用现有 Dever task runtime,保留 run/wait 的任务额度、结构化取消和清理;单独标注工作线程调度实验的边界。
- 复用 P1 构建、进程采样、cgroup 和报告逻辑,记录校验和、样本、CPU、上下文切换、RSS/PSS 与环境;不以不同语义的裸 Tokio 数字宣称语言损耗。
- HTTP/HTTPS 固定正文在更高输入速率下记录成功、错误、容量丢弃、调度落后及服务端/客户端 CPU;限速结果不宣称极限吞吐。
- TCP、WebSocket、SSE 支持有限数量的长连接保持、正文验证和多轮断开重连;记录服务端连接前、保持中、断开后的内存和 FD。
- 运行仅限自有 loopback 服务和独立预算组;所有任务、连接和进程有明确停止边界,结束无残留。执行最小定向测试,不运行全量测试、不修改全局安装、不提交。
- 不添加兼容分支、无证据的运行时重构或新依赖;发现实际阻断后再定位归属。
@@ -1,26 +0,0 @@
{
"id": "runtime-performance-p2",
"name": "runtime-performance-p2",
"title": "Runtime performance and long connections",
"description": "Continue approved runtime benchmarks: equivalent async Rust comparison, bounded HTTP load scan and TCP/WebSocket/SSE connection recovery; assess HTTP/2 readiness without implementing it.",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-12",
"completedAt": "2026-09-12",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Benchmark validation and evidence limits"}
@@ -1,9 +0,0 @@
# 测量边界
现有 performance.rs 拥有计算热路径基准,保持原有语义与 1.15 门槛。新增根 test/performance/ 的 Python 标准库驱动,分离构建、进程采样、场景执行和报告。Dever fixtures 经过现有 CLI build 成为独立二进制。Rust 辅助程序作为 dever-tests 的显式 example,复用 workspace 已锁定的 Tokio/Hyper/Rustls。
网络辅助程序同时提供等价 Rust runtime / 原生 Hyper 对照服务和独立负载端;不自行解析 HTTP。固定速率调度以计划发送时间计算延迟,并记录有界并发容量不足或调度逾期导致的丢弃。TLS 校验证书和主机名,复用已有测试 CA;新连接与连接复用分别测量。
进程通过 READY|port(空闲/计算用 READY|0)报告可采样状态。配置统一使用 BENCH_WORKERS、BENCH_CONNECTIONS、BENCH_TIMEOUT_MS、BENCH_LIFETIME_MS、BENCH_CERT、BENCH_KEY 等环境变量。负载 CLI 为 network_bench load URL PATH RATE SECONDS CONNECTIONS TIMEOUT_MS REUSE CA,JSON 标准输出。服务 CLI 为 network_bench runtime-http/runtime-https/hyper-http/hyper-https。各服务 /plain 返回 hello,/json 返回 {"ok":true},/bytes 返回 65536 个 x;带对应 Content-Type,不启用日志。
Linux /proc 采样独立于子程序;cgroup 为可选且显式启用的进程预算,使用新建的专属子组,不调整宿主或已有服务的 cgroup。负载发生器不进入服务预算。默认不声称隔离或峰值吞吐;报告采样间隔、负载端 CPU 和限额状态。汇总保留每轮原始数据,不建立未经校准的绝对性能门槛。
@@ -1,2 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused owned benchmarks and unchanged runtime semantics"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Native optimization and HTTP/TLS/task contracts"}
@@ -1,18 +0,0 @@
# 执行顺序
- [x] 主代理实现 Dever 场景和 Python 构建/采样/报告入口。
- [x] 独立 implement 代理实现 test/dever-tests/examples/network_bench.rs 及其私有模块;复用现有网络依赖。
- [x] 编译定向 fixtures/example,最小验证测量统计和清理,短时 loopback HTTP/HTTPS 对照。
- [x] 检查最终文件、响应校验、延迟分母、丢弃/失败计数、资源预算和退出清理;记录初始结果与限制。
- [x] 更新 test/performance/README.md、现有质量规范及任务证据;不提交。
工具链使用 /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin 的命令级 PATH;所有 Cargo 构建 offline。只运行本任务的最小定向检查,不执行旧 performance target 或全量测试。
## 最终证据
- 七个 Python 运行器定向测试和两个独立 Python HTTP 对端测试通过;负例响应正文必须记 error、成功延迟为空。覆盖热身去除、正确结果/数量、完整窗口、计数守恒、已退出进程和缺控制器时清理。
- Rust example 离线 release check/build 通过;七个 Dever 入口经 CLI 格式化、语义检查和生产优化原生构建。Rustfmt/Clippy 未安装,未报告为通过;显式源码空白/冲突标记检查与 Dever fmt-check 通过。
- 最终基线 78 组:15 内存、9 异步、54 HTTP/HTTPS;另 18 组新连接和 15 组 64 MiB 空闲基线。各组实际调用环境见 target/performance/*/report.json,结果解释以 test/performance/README.md 首轮结果为准。
- 54 组正式网络共计划 108000 请求,发出并成功 107962,错误 0;未发出 38(窗口过期 32、容量不足 6)。预热单独记录。新连接 900 请求全部成功,无丢弃;全部自有预算组无 OOM,结束后没有残留 dever-perf-* cgroup。
- 对照复查修正了大正文逐请求重复分配、短窗口吞吐分母、定时器抖动错误计丢弃、请求 driver 取消所有权、负载线程环境污染、缺控制器清理以及实际请求延迟校验。独立只读 review 最终没有剩余阻断。
- 仅构建/测试工具、fixtures、Cargo 测试依赖和文档发生变化;生产编译器/运行时未修改。保留产物和原始报告,不提交、不运行自动归档脚本。更高负载、长连接/持续稳定性和运行时优化是后续工作。
@@ -1,14 +0,0 @@
# 独立二进制运行性能基准
用户批准实施上一轮提出的第一阶段:常驻内存、异步调度、HTTP/HTTPS。小机器只运行已编译程序;构建资源不是本阶段优化目标。此批准取代历史网络开发阶段不运行压测的局部限制,仅限本任务拥有的有界基准。
验收:
- 根 test/ 内提供显式运行的构建与测量入口,默认测试不启动压测。
- 测量真实 Dever 编译产物,复用现有优化设置;编译不进入运行计时。
- 覆盖同步/异步/HTTP/HTTPS 空闲内存,任务启动/Channel/取消,以及 HTTP/HTTPS 固定速率请求。
- HTTP 对照使用相同 Dever runtime 的 Rust 服务和原生 Tokio/Hyper 服务;校验响应内容,记录成功、失败、未发出请求和尾延迟。
- 输出机器可读结果、环境/配置/二进制身份、CPU/RSS/PSS/线程/FD;可选独立 cgroup 限额,缺少权限时报错,不虚构限额。
- 自有随机 loopback 端口、测试证书、有界时间和负载,退出清理子进程;不访问已有服务,不跑全量测试,不提交。
- 完成短时定向验证和至少一组可解释的初始测量;长时间、多机器/多档压力矩阵明确留待显式运行。
不包含运行时优化、数据库、服务端流式请求、信号 API、TCP/WS/SSE 长连接稳定性和发行工具链。
@@ -1,26 +0,0 @@
{
"id": "runtime-performance",
"name": "runtime-performance",
"title": "Runtime performance baselines",
"description": "Measure standalone Dever memory, async scheduling and HTTP/HTTPS with reproducible bounded workloads and equivalent Rust baselines.",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-12",
"completedAt": "2026-09-12",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,7 +0,0 @@
{"file":".trellis/spec/backend/error-handling.md","reason":"数据库错误、事务失败、取消和资源回收必须显式传播"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查数据库测试边界、内存限制、无重复驱动逻辑和最小验证"}
{"file":".trellis/tasks/09-13-typed-orm/research/architecture-options.md","reason":"复核实现只暴露 Model 包短函数,没有混入实例 method、链式查询器或动态 Value 路径"}
{"file":".trellis/tasks/09-13-typed-orm/prd.md","reason":"逐项核对已确认的 Model、查询、配置、事务、错误、迁移和小内存验收合同"}
{"file":".trellis/tasks/09-13-typed-orm/design.md","reason":"复核编译器/runtime/dialect 边界、driver profile、单连接事务与错误根边界没有重复实现"}
{"file":".trellis/tasks/09-13-typed-orm/research/schema-change-matrix.md","reason":"确认所有自动 schema 变化均无静默数据损失,危险变化必须由命名 migrate 表达"}
{"file":".trellis/tasks/09-13-typed-orm/research/driver-selection.md","reason":"确认 SQLite/PostgreSQL 只复用驱动和连接池,不引入第二套 ORM 或无界池"}
-409
View File
@@ -1,409 +0,0 @@
# 类型安全 ORM 技术设计
## 设计目标
实现一个静态、无反射、默认有界的 ORM,并让数据库能力自然接入 Dever 的顺序调用、结构化并发、错误传播和 AOT 产物裁剪。Model 只描述持久化事实和数据访问;数据库驱动不拥有语言层查询语义。
## 总体边界
```text
Dever source
-> parser / formatter
-> model + function semantic checking
-> effect / error / database binding inference
-> typed QueryPlan + normalized ModelSchema
-> native emitter
-> shared ORM runtime
-> SQLite dialect/executor
-> PostgreSQL dialect/executor
-> packaged binary + external config/setting.json
```
- 编译器拥有 Model 识别、字段类型、查询表达式、静态关系、错误集合和 transaction 合法性。
- ORM runtime 拥有配置、连接生命周期、绑定值、行流、事务、schema 执行和统一错误分类。
- dialect 只拥有 SQL/DDL 差异;driver executor 只拥有具体连接、参数编码和行解码入口。
- 生成代码拥有每个 Model 的具体创建输入、更新输入、查询返回、字段绑定和行 decoder。
- 不建立通用 Repository/BaseModel 层,也不让 SQLite/PostgreSQL 分别复制 CRUD 流程。
## 源码合同
### Model 文件
```dever
package app.model.user exposes (
User
)
type UserStatus {
Active = "启用"
Disabled = "禁用"
}
type User {
uuid: Uuid generated unique
email: Text(254) unique
display_name: Text(1, 64) from nickname
status: UserStatus default UserStatus.Active index
organization_id: app.model.organization.id?
}
index(status, created_at)
seed {
{
email = "admin@example.com"
display_name = "Admin"
status = UserStatus.Active
}
}
migrate remove_legacy_code {
drop legacy_code
}
```
`database <name>`、`index`、`unique`、`relation`、`seed` 和 `migrate` 只在 Model 文件中合法。需要显式使用报告库时,在 package header 后增加 `database report`;未写时按根包/default 规则绑定。普通文件继续只有普通 type/function 语义。
### 查询与事务
```dever
users = user.list({
where = status == UserStatus.Active and organization_id == organization_id
order = [created_at.desc, id.desc]
page = 1
size = 20
with = [organization]
})
transaction disable_organization(
target_organization_id: app.model.organization.id
) () {
user.update({
where = organization_id == target_organization_id
}, {
status = UserStatus.Disabled
})
organization.update(target_organization_id, {
active = false
})
}
```
匿名创建/更新/查询块只在对应参数位置出现,由目标 Model 提供字段命名空间。它们不会成为可传递的动态 Map 或持久查询对象。
### 类型化原生 SQL
复杂查询通过 Model 文件内的专用 `sql` 声明提供,不引入通用泛型或 type-as-value:
```dever
type UserSummary {
status: UserStatus
total: Int
}
sql active(status: UserStatus) (users: List<User>) {
sqlite = "SELECT id, created_at, email, status FROM user WHERE status = ?1"
postgres = "SELECT id, created_at, email, status FROM user WHERE status = $1"
}
sql summary(status: UserStatus) (summary: UserSummary?) {
sqlite = "SELECT status, COUNT(*) FROM user WHERE status = ?1 GROUP BY status"
postgres = "SELECT status, COUNT(*) FROM user WHERE status = $1 GROUP BY status"
}
```
- 两种方言都必须是静态 Text 字面量。SQLite 显式使用 `?N`,PostgreSQL 显式使用 `$N`;编译器不重写 SQL。
- 参数按声明顺序和具体字段类型绑定;禁止插值、动态列名及动态行 `Map`。
- 结果只能是一个具体 `Model`、`Model?`、`List<Model>`,或同文件私有辅助 record 的对应形状。
- Model 文件仍只有文件名对应且 exposed 的 record 注册为 Model。辅助 record 必须被 SQL 结果使用,不生成表,也不能 exposed。
- Model 结果复用生成的 Model decoder;辅助 record 生成具体字段顺序 decoder。缺列、多列、类型不匹配,以及单行结果的零行/多行都作为运行时数据库数据错误传播。
- 调用形式与内置 CRUD 一致,例如 `user.active(status)`。执行复用既有 statement cache、值绑定和隐藏 transaction executor;`List<Record>` 只物化该静态 SQL 实际返回的行,不建立额外动态缓存。
## 编译器设计
### 1. 语法与格式化
- 根据源文件相对路径判定是否属于 `model/`;Model 文件仍解析一个普通 record type,不新增通用 type 种类。
- 在 AST 中增加目的明确的 Model 元数据节点:数据库绑定、字段修饰、复合索引、关系、Seed 和命名迁移。不要把它们编码为字符串或普通函数调用后再反解析。
- 函数声明使用 `FunctionKind::Ordinary | Transaction`,取代继续叠加多个布尔修饰位。
- 删除源码 `async` 和 `await` 节点;`run`、`wait`、`stop` 保留为明确的并发上下文表达式。
- `result(call)` 和 `fail(error_variant)` 作为两个目的单一的核心表达式进入 AST/HIR,不借此加入 try/catch、annotation 或 macro。
- formatter、Markdown 合同解析和 API snapshot 与新语法同步更新;不保留旧语法分支。
### 2. Model 注册与静态元数据
- SourceMap 加载后先建立 `ModelId`,验证文件名、package 路径、唯一公开 record 和 Model 表名。
- 为每个 Model 合成:
- 不透明 `<model-package>.id`;
- 完整 record 中的 `id`/`created_at`;
- 创建和更新输入 shape;
- CRUD/query/relationship 符号;
- choice options 常量;
- 具体 row decoder 与 bind encoder 描述。
- 同一逻辑连接中发生表名、索引名或内部对象名冲突时,在生成 SQL 前报告源码诊断。
- Model CRUD 是编译器合成的包能力,不要求开发者在 `exposes` 中逐个列出,也不扩展成通用 type-as-value。
### 3. 规范化 Schema
新增与方言无关的 `ModelSchema`:
- 稳定 Model/package/table identity;
- 有序字段、逻辑类型、可空性、默认值、generated、旧字段来源;
- 主键、外键、choice CHECK、单列/复合索引;
- relation、Seed 和命名迁移规范化表示。
revision 只对逻辑 schema 和迁移意图计算,不包含 SQLite/PostgreSQL SQL 文本、连接串、文件绝对路径或源码格式。这样两个方言完成后记录相同逻辑身份。
### 4. QueryPlan
所有短函数共用一个静态 IR:
- operation:select/insert/update/delete/upsert/count/exists;
- projection 与具体结果 shape;
- condition tree、稳定 order、page/cursor、limit;
- relation load plan;
- 参数槽位及 Dever 类型;
- transaction requirement 与 Model binding。
查询块在 checker 中解析为 Model 字段 ID 和类型化运算,不把字段名字符串留到 runtime。native emitter 将固定 SQL shape 和参数提取代码写入生成程序。
类型化原生 SQL 作为同一 Model operation HIR 的专用分支:HIR 只保存已校验的 operation 索引和类型化参数,方言文本保留在 `ModelSchema`。native emitter 选择 runtime profile 对应文本,并复用普通 ORM 的 bind encoder、database executor 与 row decoder;不会把 SQL 或结果 shape 推迟到 runtime 解释。`Record`/`Record?` 最多拉取两行以判定基数错误;`List<Record>` 最多保留 `database.max_page_size` 行并额外探测一行,超限失败而不静默截断,因此漏写 SQL `LIMIT` 也不会形成无界进程内结果集。
#### Relation 结果与加载计划
- 编译器公开内置 `Related<T>`,只有 `Unloaded` 和 `Loaded(value)` 两态。ORM 负责构造值;业务代码可以用 `Related.Unloaded<T>()` 和 `Related.Loaded<T>(value)` 分句穷尽匹配。
- `Related` 使用间接 native 布局 `Loaded(Box<T>)`。Model 类型环只有穿过 `Related` 才合法,基础 row decoder 始终把合成关系字段初始化为 `Unloaded`。
- 持久化字段 `<name>_id: target.id` 自动合成 `<name>: Related<Target>`;可空外键合成 `Related<Target?>`。显式 `relation children = target.parent_id` 合成 `children: Related<List<Target>>`。持久化 schema、bind encoder 和基础 decoder 不包含这些合成字段。
- `with = [organization, children]` 在 checker 中解析为 `QueryPlan` 的静态 relation 索引,只允许一层裸 relation 名称;未知、重复和嵌套关系均拒绝。`with` 只允许 `first`、`list`、`cursor`,`stream` 明确拒绝。
- to-one 使用受控 `LEFT JOIN` 或 `INNER JOIN`,支持同一父查询的多个关系;`list` 的 count 查询保持只访问父表,`cursor` 不生成 count。
- to-many 在父结果确定后按父 ID 批量加载并归组,不逐父查询。子记录按 `id ASC` 稳定排序,每个父记录最多加载 `database.max_page_size` 条;父 ID 每 998 个一批,为 SQLite 的第 999 个参数保留 relation limit。空父列表不会生成 `IN ()`。
- 关系查询数量由生成代码 shape 测试验证:每个 relation 只有一个按固定参数批次循环的 `query_owned` 调用点,不在生产热路径加入计数原子。
### 5. 挂起 Effect
- intrinsic 和官方库函数标记基础挂起 effect;调用、静态 handler 和 collection handler 通过现有依赖图传递。
- AST 不再存 `is_async`,HIR 在 effect 收敛后记录内部 `suspends`。native emitter 仅对可达挂起函数生成 Rust `async fn` 和内部 `.await`。
- 普通调用根据目标 effect 降低为同步 call 或内部 await call;源码语义始终是顺序完成。
- 同步入口的完整可达图没有挂起 effect 时沿用直接 `main`;否则生成唯一 Tokio runtime 入口。
### 6. Error Effect
- `error` choice 变体仍是命名、带类型 payload 的领域失败;`fail(...)` 把对应变体加入当前函数错误集合。
- 调用图为每个函数推导封闭错误集合。普通调用在 HIR 中自动传播失败,native 使用具体 Rust `Result<Success, AppError>`;源码不写 `?`。
- 对存在多个正常状态的操作,把正常状态保留为非 error choice,例如 `ReadState.Read/End`,错误集合与成功类型分开。
- `result(call)` 阻止当前调用自动传播,并生成编译器内部 `CapturedResult<SuccessShape, ErrorSet>`。它不引入用户泛型;checker 只允许通过现有穷尽函数分句消费、继续返回或传递,且必须覆盖成功形状和全部 error 变体。
- native 为当前可达程序生成一个具体的错误 enum,并为函数记录精确子集;API/docs/editor 显示每个函数的静态错误集合。没有动态错误对象、字符串类型分派或按变体名猜 HTTP 状态。
- 现有 C012 改为检查 error effect 和 captured result 的消费,继续拒绝丢弃、覆盖、仅打印或仅记录。
### 7. Database Effect 与 Transaction
- 每个 Model operation 在 HIR 标记 `DatabaseEffect(ModelId)`;普通 helper 通过调用图传递 Model 集合。
- 每个 Model 保存声明式 `ConnectionSelector::Explicit(name) | PackageRoot(root)`。source check 可拒绝显然不同的显式绑定;native build 读取 setting 后做最终连接解析和 transaction 单连接验证。
- Transaction 函数必须具有至少一个数据库 effect,且所有 effect 在构建配置中解析为同一连接;并发子调用中的数据库 effect 直接诊断。
- HIR 为 transaction 调用链增加隐藏 `DatabaseContext` 参数。普通路径使用 registry slot,事务路径使用同一 connection 的 transaction handle;不查询线程局部或任务局部状态。
## 配置与构建能力
### 项目根与源码根
- 应用命令接收项目根;项目根内的 `module/` 是唯一 SourceMap 根,package 按文件相对 `module/` 的路径校验和派生。
- `module/user/model/user.dever` 因而对应 `user.model.user`,根包数据库绑定解析为 `database.user`;不存在时才使用 `database.default`。
- `config/`、`data/` 与输出二进制都在项目根下,但不参与 Dever 源码扫描。编译器内部只传递规范化后的项目根和源码根,不在 parser/checker 中重复猜目录。
- 旧的单目录语言 fixture 由测试工具显式传入源码根;应用 CLI 不增加自动回退或第二套模糊目录探测。
### setting.json
配置路径固定为 `<executable-dir>/config/setting.json`。数据库对象直接按连接名组织:
```json
{
"app": {},
"http": {},
"database": {
"default": {
"type": "sqlite",
"path": "data/db/app.db",
"max_connections": 1,
"max_page_size": 100
},
"report": {
"type": "postgres",
"url": "postgres://user:password@db.example.com/report",
"tls": "system",
"min_connections": 0,
"max_connections": 4,
"max_page_size": 100,
"wait_timeout_ms": 5000,
"io_timeout_ms": 30000
}
},
"log": {}
}
```
- 使用严格的结构化 JSON 解析;未知数据库 type、缺失 default、无效数值或被引用连接不存在均失败。
- PostgreSQL 的 `tls` 必须显式为 `system` 或 `disabled`;`system` 使用现有 rustls 系统根并校验证书与主机名,不提供从 TLS 静默回落明文的 `prefer` 模式。
- PostgreSQL 的 `wait_timeout_ms` 限制池等待,`io_timeout_ms` 独立限制 SQL、事务和迁移 I/O;二者范围均为 1–300000 毫秒。
- 相对数据库/日志/upload/cache/tmp 路径都相对可执行文件目录,不相对 cwd。
- 启动时创建 `data/db`、`data/upload`、`data/log`、`data/cache`、`data/tmp`;不创建或修复 setting。
- 连接 selector 按“显式名称 -> 根包同名键 -> default”解析一次,Model 保存连接 slot。
### RuntimeProfile
发布构建读取 setting 中全部数据库 `type`,生成 `RuntimeProfile { sqlite, postgres, async, log }`:
- 只读取能力类型,不把 URL、密码、路径或池参数嵌入二进制或缓存 key。
- runtime 的 SQLite/PostgreSQL 依赖使用 optional Cargo features。
- profile、runtime rlib/dependency 快照和生成源码共同进入 native cache identity。
- 编译器 parse/check 不依赖 setting;生成可部署原生程序时需要 setting。程序启动会再次严格解析实际 setting。
- 运行时配置可以在已打包驱动集合内修改。若改成未打包的数据库类型,启动明确失败并要求重新构建,不做动态下载或回退。
### CMS 纵向样例
```text
examples/dever/cms/
config/setting.json
data/
module/
cms.dever
user/model/user.dever
user/service/register.dever
news/model/news.dever
news/service/publish.dever
```
- `user.model.user` 保存账户及初始化管理员,`news.model.news` 通过 `author_id: user.model.user.id` 建立静态外键。
- `user.service.register` 与 `news.service.publish` 使用 transaction 函数表达真实业务边界,直接复用编译器生成的 Model 操作。
- `cms.main` 只编排可观察的纵向流程,不复制 CRUD、迁移或连接选择逻辑。
- 默认 setting 只启用 bundled SQLite,既是新项目模板也是 SQLite-only 原生产物和小内存基准的真实用例。
## Runtime 设计
### 连接注册表
`DatabaseRegistry` 在应用入口启动 HTTP/任务前完成:
1. 定位并解析 setting;
2. 创建数据目录;
3. 构造所有被 Model 引用的连接池;
4. 解析 Model -> connection slot;
5. 验证 transaction 单连接不变量;
6. 获取每个连接的 schema 锁,执行 migration 和 Seed;
7. 成功后才进入用户入口。
热路径通过整数 slot 取连接,不执行字符串查找。连接实现使用小枚举静态分派,不使用每行/每值 trait object。
### SQLite
- 使用 `deadpool-sqlite` + `rusqlite bundled`。
- pool 上限不超过 Dever 全局 blocking 容量;每次同步交互都通过既有 blocking permit 和 pool object 生命周期。
- 默认一个连接,开启 foreign keys,设置 busy timeout;WAL 是否启用由连接配置明确决定,不在 runtime 猜测。
- transaction 独占同一个 pool object 直到 commit/rollback;RowStream 不能在释放连接后继续读取。
### PostgreSQL
- 使用 `deadpool-postgres` + `tokio-postgres`;TLS 连接通过 `tokio-postgres-rustls` 复用当前 rustls/webpki roots。
- pool 获取、连接建立、查询和 row stream 都使用 Tokio,且有独立的连接上限、获取等待和 I/O 超时。
- driver future 归属连接/pool 的结构化 scope;关闭 registry 时停止接收、等待借出连接归还并排空 driver task。
- 对固定 QueryPlan 使用 statement cache;迁移 DDL 与一次性动态片段不进入无界缓存。
### 统一错误
driver 错误在所属 executor 边界映射为稳定 `DatabaseError`:
- Connection、Timeout、Cancelled;
- Constraint(含 unique/foreign-key/check/not-null 分类);
- NotFound;
- Decode/Encode;
- Migration/SchemaConflict;
- Busy/PoolExhausted;
- Driver(保留可记录的内部 code/cause,但不直接暴露到 HTTP)。
非幂等写入不自动重试。原始 driver cause 进入诊断链,公开 error 只暴露稳定字段。
## Schema 与 Seed 执行
### 内部状态
每个数据库使用一个保留前缀的内部 history 表记录:
- Model identity;
- kind:schema/migration/seed;
- logical name/revision;
- normalized checksum;
- applied_at。
history 不是目标 schema 的替代品。每次启动仍核对数据库 catalog;history 与实际 schema 冲突时拒绝继续,不猜测修复。
### 启动流程
- PostgreSQL 使用数据库级 advisory lock;SQLite 使用写事务锁,防止多个进程同时迁移同一 schema。
- 表不存在:直接创建当前最终表、索引与约束,不回放 `from` 或历史 migrate。
- 表存在:catalog -> normalized actual schema -> diff -> validation plan -> DDL plan。
- 收紧变化先运行只读验证 SQL,任一失败都不执行 DDL。
- 命名 migrate 校验未执行/内容未改,再与 DDL/history 在一个连接事务中提交。
- Seed 在 schema 成功后执行,按唯一键 conflict-do-nothing;只忽略目标唯一冲突。
- 多连接无法拥有全局原子迁移。各连接独立加锁和提交,按连接名稳定顺序执行;后续连接失败时应用不启动,已完成连接依靠 checksum 幂等重跑。
## 查询执行数据流
```text
generated Model function
-> evaluate typed arguments
-> acquire connection or reuse hidden transaction
-> dialect renders fixed QueryPlan
-> prepare/cache statement
-> bind concrete values
-> execute / pull rows
-> generated row decoder
-> Model/List/Page/Cursor result
-> release connection or retain for bounded RowStream
```
- to-one include 与主查询共享受控 join 计划。
- to-many include 在获得父页后用一个有界 `IN (...)` 批次查询并按父 ID 归组;超过参数上限时按固定上限分批,不按父记录逐条查询。
- list 的 count 与 data 查询按同一条件树生成;cursor 只执行 data 查询。
- stream 持有连接和 statement 生命周期,消费者停止、Task 取消或 fault 时通过 Drop/scope 清理归还。
## Transaction 执行
- 最外层 transaction 从已验证 slot 获取一个连接并 begin。
- 生成的 `DatabaseContext` 携带 `ConnectionSlot + TransactionHandle`,只沿隐藏参数传递。
- 普通 Model call 检测到上下文时验证 slot 相同并直接使用 handle;无上下文时从 pool 获取。
- 同连接嵌套 transaction 只增加逻辑作用域,不 begin/commit。
- 函数成功时最外层 commit;error/fault/取消路径统一进入 rollback guard,rollback 完成后再传播原始失败。
- rollback 自身失败追加到诊断 cause,不覆盖原始业务 error/fault。
- transaction context 不能进入 `run`、Group、Channel、Stream producer 或返回值,因此不存在逃逸连接。
## 日志与执行边界
- runtime 维护一个结构化 `DiagnosticContext`,包含 fault/error code、source span、request/task id 和 cause chain。
- lower layer 只附加 cause/context,不记录根日志;HTTP、ownerless Task、CLI 等根边界决定是否记录。
- HTTP 未映射失败在 rollback 后返回通用 500;显式 handler 可把预期 error 映射为 4xx。
- 日志 front-end 先检查 level,再构造字段。队列容量固定;debug/info 满时丢弃并累计,warn/error 等待容量。
- 文件 sink 的编码、轮转和压缩策略不在本任务冻结;先完成有界队列、stderr emergency path 和一次记录合同。
## 性能设计
- 生成代码只保留可达 Model、字段 decoder、QueryPlan 和 driver profile。
- 相同静态 SQL shape 使用每连接 statement cache;值变化不创建新 SQL 字符串或缓存项。PostgreSQL 每连接缓存暂定上限为 64;to-many 参数批次生成的动态 SQL 不进入缓存,最终上限由阶段 10 基准收敛。
- page size、to-many 子项、IN 批次、批量写入、pool、blocking、RowStream、日志全部由一个明确上限约束。
- SQLite 默认一连接,PostgreSQL 默认零预热小池;最终默认值由 64/128 MiB 定向基准确定,并固化为命名常量和文档。
- 性能基准实测 RSS/PSS/VmHWM、cgroup、线程/FD、吞吐、p50/p95/p99 和二进制体积;配置的 pool capacity 单独记录。生产 runtime 尚无低成本观测点的 pool wait、SQL 数量和数据库连接数必须标记为 `unavailable`,不能用 FD 或配置值推算冒充实测。
- 构建产物以 manifest 的 SHA-256 和字节数校验后才运行;每个 ORM case 使用独立 bundle。PostgreSQL URL 的唯一 `{case}` 必须位于数据库名,敏感 URL 只进入权限为 `0600` 的 case setting,不进入 manifest/report。
- transaction cancel 使用单连接 pool:先证明连接可用,再证明长事务占用 pool、客户端在响应前断开、连接在 handler timeout 前归还且事务行数为零,避免把 handler timeout 回滚误报为请求取消。
- 先验证稳定内存和正确回收,再优化 SQL 生成、statement 命中或 decoder;不以缓存未绑定 SQL 或扩大队列换吞吐。
## 风险与处理
- 隐式挂起和错误传播会触及编译器、官方库和全部异步 fixture,应先单独完成语义迁移并保持 Rust runtime 行为,再叠加 ORM。
- SQLite DDL 经常需要重建表;schema planner 必须以真实 catalog fixture 验证,不能只比较生成 SQL 字符串。
- 外部 setting 与驱动裁剪可能不一致;构建记录 capability,启动严格校验,错误要求重建,不加载动态插件。
- 多数据库迁移无法跨连接原子提交;稳定顺序、单连接事务和 checksum 幂等是唯一可维护恢复路径。
- PostgreSQL 测试依赖真实服务;实施阶段只运行隔离的定向 fixture,不连接开发或生产数据库。
## 回滚边界
- 每一实施阶段必须在独立语义门槛下完成;不在半迁移状态同时保留新旧源码语法。
- 自动 schema 操作在提交前不改 history;失败回滚。跨连接已有成功迁移不反向回滚,由幂等重启继续。
- 发布二进制始终保留上一个版本;危险命名 migrate 需要先有数据库备份,自动回滚程序版本不能逆转已提交的数据迁移。
@@ -1,10 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"model、查询计划、资源和公开 API 必须在 checker/HIR/native 中保持静态合同"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"数据库公共包、驱动选择、runtime 构建与缓存必须复用现有工具链边界"}
{"file":".trellis/tasks/09-13-typed-orm/research/architecture-options.md","reason":"依据现有无反射 AOT 和资源语义选择 ORM 表面"}
{"file":".trellis/spec/backend/error-handling.md","reason":"实现默认错误传播、事务回滚、根边界处理和 C012 合同"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查数据库测试边界、内存限制、无重复驱动逻辑和最小验证"}
{"file":".trellis/tasks/09-13-typed-orm/research/implicit-suspension.md","reason":"统一删除 Dever async/await,并保留编译器推导协程、run/wait/stop 和同步入口裁剪合同"}
{"file":".trellis/tasks/09-13-typed-orm/research/field-types.md","reason":"实现隐式字段、常用数据库类型、Uuid、choice、关联 ID、Seed 和索引语义"}
{"file":".trellis/tasks/09-13-typed-orm/research/schema-change-matrix.md","reason":"按变更类别实现自动 diff、数据验证、from、命名 migrate 和两种方言的等价结果"}
{"file":".trellis/tasks/09-13-typed-orm/research/query-api.md","reason":"实现已确认的 Model 短函数、块式查询、分页、关联、事务和连接解析表面"}
{"file":".trellis/tasks/09-13-typed-orm/research/driver-selection.md","reason":"接入 rusqlite/deadpool-sqlite 与 tokio-postgres/deadpool-postgres,并按 runtime profile 裁剪驱动"}
-297
View File
@@ -1,297 +0,0 @@
# 类型安全 ORM 实施清单
## 开始前门槛
- [x] 用户在最终规划摘要之后明确批准进入实现,并要求增加 `examples/dever/cms` 项目。
- [x] 运行 `python3 ./.trellis/scripts/task.py start 09-13-typed-orm`,加载 `trellis-before-dev` 上下文。
- [x] 核对 `Cargo.lock` 中待加入驱动与当前 Rust 1.85/MSRV、license 和 feature;只锁定实际使用的最小 feature。
- [x] 为每个阶段建立对应的根目录 `test/` 定向测试目标;不运行 workspace 全量测试或集成压测。
## 阶段 1:隐式挂起语言表面
目标:删除 Dever 源码 `async`/`await`,保留 Rust runtime 的真实异步执行。
- [x] 调整 lexer/parser/syntax/formatter,删除 function/handler async 与 `await(...)`,保留 `run`/`wait`/`stop`。
- [x] 将 `FunctionClause.is_async` 拆为编译器推导的 HIR `suspends`,复用现有调用依赖图传递基础挂起 effect。
- [x] 更新 checker:普通调用自动按目标 effect 顺序等待;`run` 接受可启动调用,Task/Group 仍保持结构化所有权。
- [x] 更新 native emitter:同步可达图生成直接入口,挂起可达图生成唯一 Tokio 入口和内部 await。
- [x] 更新 API snapshot、Markdown 合同、官方 `dever.task/net/http/websocket/sse` 源码、examples 与相关 fixture,不保留旧语法。
- [x] 清理旧 `AwaitCall`、`TaskStart`、公开 async 专属诊断和死分支,保留 runtime Rust async 节点。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test structured_concurrency
cargo test -p dever-tests --test async_native
cargo test -p dever-tests --test async_network
cargo test -p dever-tests --test http_library
cargo test -p dever-tests --test live_library
```
完成条件:HTTP/TCP/WS/SSE 的 Dever fixture 不含源码 async/await,协议、背压、取消和同步程序无 Tokio 入口的定向测试通过。
## 阶段 2:默认错误传播与执行边界
目标:建立 `fail`、`result(call)`、推导错误集合和运行时统一兜底。
- [x] 在 AST/HIR 增加 `Fail`、`CaptureResult` 和静态 `FailureSet`,通过现有 dependency worklist 传递。
- [x] 将普通调用降低为自动传播,`result(call)` 降低为编译器内部 captured result;不增加用户泛型或每函数 error 声明。
- [x] 拆分官方库中“正常状态 choice”与 error effect,迁移现有 Result choice 的调用方。
- [x] 扩展 C012,覆盖默认传播、captured result 穷尽消费、覆盖、丢弃、仅日志和 `recover`。
- [x] 生成可达应用的具体 Rust error enum、cause chain 和 source span;API snapshot 显示每函数精确错误集合。
- [x] 建立 HTTP、Task、CLI 根边界:事务回滚后分别返回 500/Task failure/非零退出,且只记录一次。
- [x] 增加 `dever.log` level API、共享诊断上下文和有界队列;文件 sink 格式/轮转仍不在本阶段冻结。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test contract_syntax
cargo test -p dever-tests --test contract_semantics
cargo test -p dever-tests --test contract_execution
cargo test -p dever-tests --test hello_native
cargo test -p dever-tests --test http_engine
```
完成条件:默认传播、主动捕获、自定义 fail、C012、HTTP/Task/CLI 边界及一次日志都有正反 fixture。
## 阶段 3:Model 语法与静态 Schema
目标:编译器能从一个 Model 文件得到完整静态持久化合同。
- [x] 增加 Model 路径识别和 Model 专属 declaration/field metadata AST。
- [x] 实现文件名/package/唯一公开 record 校验,合成 `id`、`created_at`、创建输入和更新输入。
- [x] 实现 `Bool/Int/Float/Decimal/Text/Bytes/Uuid/DateTime/Date/Time/Duration/Json/choice/model.id` 及 nullable/static bounds。
- [x] 实现 `generated/default/index/unique/from`、复合 index/unique、relation、seed、migrate 的解析、格式化和源码诊断。
- [x] 实现不透明 Model ID 的解析与类型检查、隐式 FK/index、choice labels 与 `Choice.options`。
- [x] 建立与方言无关的 `ModelSchema` 规范化和稳定 checksum;检查同连接内表/对象名冲突。
- [x] API/Markdown 输出 Model、隐式字段、choice options 和持久化元数据,但不暴露内部输入类型噪音。
建议新增测试目标:`model_syntax`、`model_semantics`、`model_schema`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test model_syntax
cargo test -p dever-tests --test model_semantics
cargo test -p dever-tests --test model_schema
```
完成条件:字段/索引/关联/Seed/migrate 的合法和非法边界在 native build 前有稳定诊断,schema checksum 不受格式或方言影响。
## 阶段 4:配置、目录与 RuntimeProfile
目标:严格加载外部 setting,并按实际能力构建最小 runtime。
- [x] 增加严格 `setting.json` 结构化解析和可执行文件目录定位。
- [x] CLI 应用命令以项目根为输入,统一把 `<project>/module` 交给 SourceMap;fixture 的显式源码根入口保持内部测试用途,不做应用级自动回退。
- [x] 创建固定 `data/db|upload|log|cache|tmp` 目录;setting 缺失/无效直接失败。
- [x] 实现显式连接、根包连接、default 的一次性解析和生成 Model 句柄缓存。
- [x] 将 SQLite/PostgreSQL/config 依赖拆为 runtime optional features。
- [x] native build 从 setting 提取驱动 capability,构建对应 runtime rlib;capability 进入 cache identity,敏感配置不进入生成源码或 identity。
- [x] 启动时复核实际 setting 的驱动集合和 transaction binding;未打包驱动明确失败。
- [x] 增加 base/sqlite/postgres/both 四种产物的依赖和二进制体积报告。
建议新增测试目标:`application_config`、`native_profiles`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test application_config
cargo test -p dever-tests --test native_cache
cargo test -p dever-tests --test native_profiles
```
完成条件:cwd 改变不影响定位,缺失配置无回退;SQLite-only/PostgreSQL-only 产物不含另一驱动。
## 阶段 5:共享 QueryPlan 与 SQLite 纵向闭环
目标:先用一个真实数据库打通静态 CRUD 和有界查询。
- [x] 实现 Model 短函数符号与匿名 create/update/query 块的上下文类型检查。
- [x] 建立共享 QueryPlan、条件树、参数槽、稳定排序、page/cursor/stream 和结果 shape。
- [x] 接入 `deadpool-sqlite` + `rusqlite bundled` 和有界 pool。
- [x] 实现当前 CRUD 所需 SQLite dialect:标识符、占位符、类型/值编码、RETURNING、CHECK/FK/index。
- [x] 生成 Model 专属 bind/row decoder;所有值参数化,不使用反射或动态 Map。
- [x] 实现 create/get/first/list/cursor/count/exists/stream/update/delete/upsert 与有界 create_many。
- [x] 验证 stream/取消/超时/pool exhaustion 后连接归还。
建议新增测试目标:`orm_query`、`sqlite_orm`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test orm_query
cargo test -p dever-tests --test sqlite_orm
```
完成条件:临时 SQLite 数据库完成 CRUD、分页、游标、stream、约束错误和清理;目标机器路径不需要系统 SQLite。
## 阶段 6:Schema Diff、迁移与 Seed
目标:SQLite 先完成空库、升级、危险变化拒绝和幂等初始化。
- [x] 建立 catalog 列 introspection 与 history 中的规范化 actual schema。
- [x] 实现内部 history 表、schema/seed checksum 和并发启动锁。
- [x] 实现空库最终 schema 创建,不回放 `from`/历史 migrate。
- [x] 实现安全字段 diff、约束数据库验证和 SQLite 表重建。
- [x] 实现字段 `from`、稳定命名 migrate、内容不可改和事务性 history。
- [x] 实现 Seed 唯一身份检查、缺失插入、非唯一冲突失败和 checksum 快速跳过。
- [x] 对每一种自动/拒绝/显式变化保留真实 catalog 和数据断言,不只断言 SQL 字符串。
建议新增测试目标:`orm_migration`、`orm_seed`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test orm_migration
cargo test -p dever-tests --test orm_seed
```
完成条件:新库、旧库 rename、长度放宽/收紧、唯一/FK、重建失败回滚、重复启动和 Seed 追加全部通过。
## 阶段 7:PostgreSQL 复用接入
目标:只增加方言和 executor,不复制 Model/QueryPlan/schema 语义。
- [x] 接入 `deadpool-postgres` + `tokio-postgres`;使用 `tokio-postgres-rustls` 支持系统根 TLS 校验和显式禁用。
- [x] 实现当前 CRUD 所需 PostgreSQL dialect、值编码/解码、RETURNING、catalog introspection 和 advisory migration lock。
- [x] 对固定 QueryPlan 使用 statement cache;缓存 key 只来自编译器生成的静态 SQL shape。
- [x] 复用 SQLite 同一 CRUD/schema/Seed fixture 数据集和断言,仅替换隔离数据库适配。
- [x] 验证 pool wait、连接断开、取消、timeout、constraint code 映射、TLS 主机名/证书拒绝和 shutdown drain。
建议新增测试目标:`postgres_orm`,默认标记为 ignored;仅在仓库根 `config/setting.json` 的 `database.postgres_test` 明确配置隔离测试 PostgreSQL 时手动运行,不接触现有服务。
定向验证:
```bash
cargo fmt --all -- --check
cargo test --offline -p dever-tests --features postgres --test postgres_orm -- --ignored --exact postgres_orm_uses_the_configured_isolated_database
```
完成条件:PostgreSQL 与 SQLite 对共享 fixture 返回相同逻辑结果和 schema revision,差异仅存在于 dialect/executor。
## 阶段 8:Transaction 与多数据库
目标:实现无显式句柄、单连接静态约束的 transaction 函数。
- [x] 实现 `FunctionKind::Transaction` 检查和 `DatabaseEffect(ModelId)` 传播。
- [x] build/runtime 分别解析 transaction Model 集合到唯一 connection。
- [x] native 生成隐藏数据库上下文参数和最外层 begin/commit/rollback guard。
- [x] 同连接嵌套 transaction 复用 handle;不创建 savepoint。
- [x] 拒绝无数据库 effect、跨连接、动态切库、transaction context 逃逸和并发数据库子任务。
- [x] 验证 error/fault 时先 rollback,再传播原始失败;rollback failure 只追加 cause。
- [x] 验证 default、根包、显式 report 三种连接绑定和多连接启动顺序/幂等恢复。
建议新增测试目标:`orm_transaction`、`orm_connections`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test orm_transaction
cargo test -p dever-tests --test orm_connections
cargo test --offline -p dever-tests --features postgres --test postgres_orm -- --ignored --exact postgres_orm_uses_the_configured_isolated_database
```
完成条件:两种数据库都验证提交、回滚、嵌套复用、取消和连接归还,跨数据库在执行前被拒绝。
## 阶段 9:Relation 与原生 SQL 逃生口
目标:完成后端常用关联读取和复杂查询能力。
- [x] 实现 to-one relation 推导、显式反向 to-many relation 和 `with` 计划。
- [x] to-one 使用受控 JOIN;to-many 按父页批量加载、稳定排序、数量和参数批次上限。
- [x] 加入生成代码 query-shape 探针,断言父记录数增长不会产生 N+1。
- [x] 实现参数化 typed native SQL,显式 SQLite/PostgreSQL 方言和具体 Model/record decoder。
- [x] 拒绝 SQL 插值、动态列 Map、结果列缺失和类型不匹配。
- [x] 建立 `examples/dever/cms/module/{user,news}/{model,service}`,用用户注册、新闻发布和 author 外键覆盖 Model/Service/transaction 纵向流程。
建议新增测试目标:`orm_relations`、`orm_native_sql`。
定向验证:
```bash
cargo fmt --all -- --check
cargo test -p dever-tests --test orm_relations
cargo test -p dever-tests --test orm_native_sql
```
完成条件:两种数据库返回相同关系结果,to-many SQL 数量有固定上界,原生 SQL 保持参数化和静态解码。
## 阶段 10:小内存与压力验证
目标:在正确性完成后冻结小机器默认值并找出真实瓶颈。
- [x] 扩展 `test/performance` 构建器,产出 sync/base、SQLite-only、PostgreSQL-only 和 HTTP+JSON+DB fixture。
- [x] 构建和测量分离;运行阶段只启动已打包二进制。
- [x] 添加 SQLite/PG idle、CRUD、list/cursor/stream、pool saturation、transaction cancel 场景;PG 仅在根 `config/setting.json` 显式配置 `database.postgres_test` 时运行。
- [x] 报告 binary size、RSS/PSS/VmHWM、cgroup current/peak/OOM、线程/FD、配置 pool capacity、吞吐和 p50/p95/p99;当前无法直接观测的连接数、pool wait、SQL 数明确标记 unavailable。
- [x] 先跑 1 核/128 MiB,再跑 64 MiB;不设置假 QPS 门槛,以无 OOM、无错误、内存有界、连接回收和相对对照为硬条件。
- [x] 根据报告收敛 SQLite pool、PG min/max、page size、row buffer、IN batch 和 statement cache 上限;两档最大 RSS 高水位均约 7 MiB、OOM 为 0,现有有界默认值有充分余量,因此不做无数据依据的调参。
- [x] 最后做 cleanup:删除临时 fixture、重复驱动分支、旧 async/await 代码和未使用 feature。
- [x] 以 `examples/dever/cms` 验证项目级 check/run/build 和 SQLite-only 打包产物。
- [x] 以 `examples/dever/cms` 验证真实业务内存基线。
最小基准检查:
```bash
python3 -m unittest discover -s test/performance -p 'test_runner.py' -v
python3 test/performance/run.py run \
--artifacts target/performance/orm-current \
--output target/performance/orm-128m \
--suite orm --duration 10 --warmup 2 --repeats 3 \
--cgroup-parent /sys/fs/cgroup/system.slice \
--memory-mib 128 --cpu-quota 1
```
该运行会启动隔离的本地测试进程并使用 cgroup;执行前必须说明,确认不会影响现有服务。PostgreSQL 场景只使用专门测试实例。
## 最终自检
- [x] `rg` 确认 Dever 官方库、examples、fixture 不再出现源码 `async`/`await`。
- [x] `rg` 确认没有旧 ORM、链式 Query、显式 Database/Transaction 或双 CRUD 表面。
- [x] 检查生成 SQL 的所有值都通过 bind 参数,只有编译器生成的标识符进入 SQL。
- [x] 检查 SQLite/PostgreSQL 共享 QueryPlan/schema/error 流程,没有复制业务算法。
- [x] 检查所有队列、池、page、batch、stream、日志和 blocking 路径有明确上限。
- [x] 检查最终改动没有调试输出、TODO、临时测试、无用依赖或未使用兼容分支。
- [x] 只报告实际运行的定向验证;未运行 PostgreSQL、压测或受权限限制的 cgroup 检查必须明确标注。
## 高风险文件与回滚点
- 编译器语法/HIR:`crates/dever-core/src/{lexer,parser,syntax,hir,format}.rs`。
- 效果与错误:`crates/dever-core/src/contracts/*`、`check/*`、`native/*`、`api.rs`。
- native profile/cache:`crates/dever-core/src/native/build.rs`、`native/build/cache.rs`、workspace/runtime Cargo 配置。
- Runtime:新增 config/database/orm/dialect 模块与 `crates/dever-runtime/src/task.rs` 的 bounded blocking 接口。
- 官方库和示例:`library/dever/*`、`examples/dever/*`。
- 所有长期测试只能放在仓库根 `test/`。
阶段 1、2 属于语言合同迁移,完成后不回退旧语法;阶段 3-9 每阶段保持编译器与 runtime 同步可用。schema 变更只靠数据库事务/history 回滚,已成功提交的命名迁移不由程序版本回退。
## 2026-09-17 收口验证
- `cargo check --offline -p dever-runtime --features postgres` 通过。
- `application_config` 3/3 通过;PostgreSQL 配置默认值和超时范围已覆盖。
- 原 `postgres_orm` 无环境变量时提前返回却显示 1/1 通过,该证据无效。测试现已改为默认 ignored,并只从根 `config/setting.json` 读取 `database.postgres_test`;真实 PostgreSQL 验收仍未运行。
- PostgreSQL 配置入口回归:performance runner 27/27 定向单测通过,`cargo check --offline -p dever-tests --features postgres --test postgres_orm` 通过;未连接数据库。
- handler effect 替换回归通过;全部维护中示例按 `<project>/module` 统一源码根通过静态检查。
- `cms_project` 已完成 1/1 check/run/build/打包程序验证;最终复验因根文件系统空间不足在链接测试二进制前中止,不是测试逻辑失败。
- rustfmt、clippy 组件未安装;全量测试、真实 PostgreSQL、二进制体积、64/128 MiB 和 cgroup 压测未运行。
## 2026-09-17 最终验收
- 共享 fixture 已落在 `test/dever-tests/tests/support/orm_fixture.rs`;SQLite 与 PostgreSQL 使用同一 schema/Seed、CRUD、create_many、upsert、list/cursor/stream、transaction 和 catalog 断言。SQLite 1/1、隔离 PostgreSQL 1/1 最终复验通过。
- PostgreSQL 真实验收修复并覆盖三处仅靠编译无法发现的问题:cursor 可空参数显式 SQL 类型 cast、UUID 原生二进制参数直接使用 `::uuid`、shutdown SQLSTATE 映射为 connection error。pool wait、断连、timeout、constraint、TLS 拒绝和 drain 均通过。
- PostgreSQL 测试与性能工具只从仓库根 `config/setting.json` 读取 `database.postgres_test`;没有数据库 URL/TLS 环境变量或命令行覆盖入口。测试实例为独立临时 PostgreSQL,未接触现有 5432/5433 服务。
- 四 profile 使用同一最小源码:base 413,880 B、sqlite 2,562,808 B、postgres 2,711,632 B、both 4,676,472 B。依赖报告确认单驱动产物不包含另一驱动,证据见 `target/performance/typed-orm-build-v2/profile-report.json`。
- 1 CPU、无 swap、128 MiB 与 64 MiB 各完成 48/48 ORM case(SQLite/PostgreSQL × 8 场景 × 3 重复),两档 OOM/OOM kill 均为 0,最大 RSS 高水位分别 7,184,384 B 与 7,102,464 B;所有 cancel case 均确认池饱和、零提交和超时前连接回收。报告见 `target/performance/typed-orm-128m-v2/report.json` 与 `target/performance/typed-orm-64m/report.json`。
- 现有 SQLite pool=1、PostgreSQL min/max=0/4、page size=100 以及有界 row/IN/statement 路径均有充分内存余量;本轮数据不支持进一步缩减,保持默认值。
- CMS 128 MiB 与 64 MiB 各 3/3 完成,业务基线每次为 2 条记录,OOM 为 0,RSS 高水位中位数分别 5,513,216 B 与 5,562,368 B。报告见 `target/performance/cms-128m/report.json` 与 `target/performance/cms-64m/report.json`。
- 定向回归:performance runner 32/32、`application_config` 3/3、`orm_query` 8/8、`cms_project` 1/1(实际 check/run/build/打包程序)通过;`cargo check --offline -p dever-runtime --features postgres` 通过。
- 最终独立复核修正了两个收口问题:普通 SQLite `setting.json` 缺少 `database.postgres_test` 时现在按未启用 PostgreSQL 处理,只有显式但非法的测试连接才失败;真实 PostgreSQL fixture 使用 PID+纳秒+计数隔离 schema,并在断言失败/panic 后清理再恢复原始失败。修正后 runner 32/32、PostgreSQL cargo check 与真实验收 1/1 通过,结束后隔离 schema=0、活动连接=0。
- rustfmt、clippy 组件未安装,未运行全量 workspace 测试;以上限制不影响本任务已列出的定向验收。
-211
View File
@@ -1,211 +0,0 @@
# SQLite 与 PostgreSQL 类型安全 ORM
## 目标
为 Dever 增加编译期类型检查、AOT 生成具体代码且无需反射的 ORM。第一轮完整支持 SQLite 和 PostgreSQL,共用 Model、查询计划、事务、迁移和错误合同;小内存机器只运行打包后的二进制,不承担 Rust 编译成本。
## 背景与约束
- Dever 是全新语言,不保留旧 ORM、旧 `async`/`await` 或其它源码兼容层,也不增加防御性回退路径。
- 当前编译器没有用户泛型、反射、动态 `Value`、method 或 type-as-value;公开 ORM 必须保持静态、直接和可 AOT 降低。
- Rust runtime 继续使用 Tokio 和真实异步 I/O。删除 Dever 源码中的 `async`/`await` 只改变语言表面,不把 HTTP、TCP、WebSocket、SSE 或数据库驱动改成阻塞线程模型。
- SQLite 面向单机、小内存程序;PostgreSQL 面向并发后端。二者共享语义,不强行共享执行机制。
- Model、Transaction、RowStream 等数据库资源沿用现有所有权、结构化任务、背压和取消清理原则。
## Model 与字段
- `model/` 下一个源文件定义一个 Model,继续使用普通 record `type`,不增加 `model` 关键字。
- 文件只公开一个与文件名对应的 Model record;表名和列名按文件名、字段名精确派生,不做复数化猜测。
- 同一文件集中字段、索引、关联、choice、Seed、迁移和紧贴持久化的数据访问操作,不增加外部 schema 映射文件。
- 每个 Model 隐式拥有只读 `id` 和 `created_at: DateTime`。二者出现在完整查询结果中,但不出现在创建输入中;`updated_at` 不隐式生成。
- `<model-package>.id` 是数据库生成的有符号 64 位正整数,也是 Model 专属不透明类型。PostgreSQL 使用 64 位 identity,SQLite 使用 `INTEGER PRIMARY KEY`;不同 Model 的 ID 不能混用,也不能按普通 `Int` 运算。
- 关联字段直接引用目标主键,例如 `owner_id: app.model.user.id`、`parent_id: app.model.category.id?`。作为 Model 字段时自动生成外键和普通索引,但不触发隐式加载。
- 第一轮字段类型包括 `Bool`、`Int`、`Float`、`Decimal(P, S)`、`Text`、`Bytes`、Model `.id`、`Uuid`、`DateTime`、`Date`、`Time`、`Duration`、`Json`、无载荷 choice 及其可空形式。
- `List`、`Map` 和普通 record 不隐式序列化为列;结构化持久化必须显式使用 `Json`。
- 字段元数据使用可解析、可格式化、可静态检查的语法,不使用字符串 tag。
### 字符串、字节与数值
- `Text` 表示无界 UTF-8 文本,`Text(max)` 表示最多 max 个 Unicode codepoint,`Text(min, max)` 表示 min 至 max 个 Unicode codepoint;不增加语义重复的 `Char`。
- `Bytes`、`Bytes(max)`、`Bytes(min, max)` 使用相同边界形式,但长度单位是字节。
- PostgreSQL 有界 Text 使用 `VARCHAR` 与必要的 CHECK;SQLite 使用 `TEXT` 与等价 CHECK,不能依赖 SQLite 忽略 VARCHAR 长度。
- `Decimal(P, S)` 必须满足 decimal128 的精度边界;两种数据库往返后保持精确值,不静默舍入。
### UUID 与时间
- `Uuid` 是固定 128 位值,运行时不使用堆分配 Text。PostgreSQL 使用原生 UUID,SQLite 使用 16-byte BLOB;API/JSON/文本统一为小写、带连字符的 36 字符格式。
- 普通 `Uuid` 由调用方提供;`Uuid generated` 由 ORM 创建 UUIDv7 并从创建输入省略;`generated` 与可空不能同时使用。
- `Uuid` 不隐式表示关系,关系仍使用 Model `.id`。
- `DateTime` 表示 UTC 毫秒时间点;`Date` 表示公历日期;`Time` 表示日内毫秒时间;`Duration` 表示固定毫秒时长,不使用包含月份换算的数据库 interval。
### Choice
- Model 文件可声明供字段使用的无载荷 choice;只有文件名对应的 record 生成表。
- 可选项写成 `Pending = "待处理"`。数据库和 API 存储左侧稳定变体名,右侧显示名称不入库。
- 编译器生成保持声明顺序的只读 `Choice.options: Map<Choice, Text>`;一个 choice 的变体必须全部带显示名或全部不带。
- 第一轮不支持 choice payload,也不创建 PostgreSQL 专有 enum。
## 索引、Schema 与迁移
- 单字段索引写为字段后的 `index` 或 `unique`;复合索引写为 `index(a, b)` 或 `unique(a, b)`。
- 索引描述当前目标 schema,不写版本号或手工名称;编译器稳定生成数据库对象名并静态检查字段引用和重复列。
- 当前 Model schema 是唯一目标。ORM 比较数据库系统目录、内部 history 与二进制嵌入的规范化 schema,不生成源码级 `table/` 状态目录。
- 新表直接按当前最终 schema 创建;新增可空字段、有合法常量默认值的非空字段、普通索引等意图唯一的变化自动应用。
- 长度、精度、可空性、默认值和约束直接修改目标字段。放宽变化自动应用;收紧变化先验证现有数据,数据不满足时整次迁移失败并要求命名 migrate,绝不截断、舍入、猜测补值或删除数据。
- 简单字段重命名写为 `display_name: Text(64) from nickname`。已有旧表执行 rename;全新数据库忽略历史来源,只创建最终字段。
- `from` 只供 schema 规划,不进入运行时字段、CRUD 输入、查询 SQL 或最终约束。
- 开发者不维护数字 schema 版本。编译器根据规范化 schema 和变更计划生成 revision 校验值,嵌入二进制并记录到数据库内部 history。
- 复杂数据变化使用 Model 文件内稳定命名的 `migrate <name> { ... }`,包括拆分、合并、跨行转换、删除前归档和显式 drop。
- 一个命名迁移只成功执行一次;已执行迁移的规范化内容改变时拒绝启动。迁移和 history 写入在同一数据库事务内完成。
- SQLite 无法直接 ALTER 的变化由方言层在事务内重建表;PostgreSQL 使用对应 DDL。两者完成后必须得到相同逻辑 schema revision。
- 只有结构化迁移无法表达时,命名 migrate 才可包含明确方言的参数化原生 SQL;不承诺该 SQL 跨数据库复用。
## Seed
- 每个 Model 文件最多一个无版本 `seed { ... }` 块,块内直接写多个匿名创建数据块,不重复 Model 名或 `create`。
- Seed 数据按 Model 创建输入静态检查,不能填写 `id`、`created_at` 或其它 generated 字段。
- 每条 Seed 必须完整提供至少一个单字段或复合唯一键,用于稳定识别初始化记录。
- 编译器为规范化 Seed 生成校验值。内容未变化时启动不执行 Seed SQL;变化时在 schema 迁移成功后,以一个事务重新执行“缺失则插入”。
- 唯一键冲突保留数据库现值;其它非空、CHECK、外键或解码错误必须失败。Seed 不覆盖或删除线上数据,修改/删除内置数据使用命名 migrate。
## 查询与内置操作
- Model 包生成唯一一套短函数:`create`、`create_many`、`get`、`first`、`list`、`cursor`、`count`、`exists`、`stream`、`update`、`delete`、`upsert`。
- 业务代码直接调用 `user.list(...)`、`user.update(...)`,不暴露 `User.Query`、`User.List`、Active Record 实例方法或第二套 CRUD。
- 创建、更新和查询配置使用上下文推导的匿名块;底层生成具体静态输入和结果类型,不引入动态字段 Map。
- 查询只采用一次性配置块:
```dever
users = user.list({
where = status == UserStatus.Active
and age >= 18
and (city == "北京" or city == "上海")
order = [created_at.desc, id.desc]
page = 1
size = 20
with = [owner]
})
```
- 不提供 `user.where(...).order(...).list()` 链式查询器;链式查询不会提升数据库性能,却会要求新增 method、泛型查询对象和所有权语义。
- 多个条件使用现有 `and`、`or`、`not` 和括号;支持比较、`in`、`between`、`contains`、`starts_with`、`ends_with`、null 与关联字段路径。
- 查询表达式在编译期降低为类型检查后的 `QueryPlan`。字段、排序、关系和 SQL 结构不能来自运行时字符串;所有值通过参数绑定。
- `list()` 默认返回第一页,默认 size 为 20,并受 `setting.json` 的统一上限约束;排序不是唯一键时自动追加 `id` 保证稳定分页。
- `list` 返回 `items/page/size/total/pages`;`cursor` 返回 `items/next/has_more` 且不执行 count;`stream` 全量遍历但保持有界内存,不提供无界 `all()`。
- 正向 to-one 由关联 ID 推导;反向 to-many 在 Model 中显式声明 `relation tasks = app.model.task.owner_id`。
- `with` 才加载关系。to-one 使用受控 JOIN 或等价单次查询;to-many 按当前父页 ID 批量加载并归组,必须有排序和数量上限;禁止 lazy loading 和 N+1。
- 多对多使用显式中间 Model,不生成隐藏表。
- 复杂查询保留参数化、类型化的原生 SQL 逃生口。调用必须声明具体 Model/record 结果和方言;不返回动态行 Map,不允许 SQL 字符串插值。
## 数据库配置与绑定
- 应用项目根采用固定布局:`module/` 是唯一 Dever 源码根,`config/` 保存 `setting.json`,`data/` 保存运行数据。编译器从项目根加载时按 `module/` 的相对路径派生逻辑 package,`module` 本身不进入 package 名;`config/` 和 `data/` 不参与源码扫描。
- 可执行文件按自身所在目录解析固定布局,不使用进程当前目录,也不提供环境变量覆盖:
```text
app/
app
config/
setting.json
data/
db/
upload/
log/
cache/
tmp/
```
- `config/setting.json` 是唯一配置文件;缺失、无法解析或数据库配置无效时启动失败。运行时按需创建 `data/` 子目录,不生成默认 setting。
- `setting.json` 顶层使用 `app`、`http`、`database`、`log` 等对象;`database` 直接映射连接名,不增加 `connections` 包装层。
- `database.default` 必须存在;其它名称由应用定义,例如 `database.report`。
- Model 连接按以下顺序在启动时解析并缓存:
1. Model 文件显式 `database report`;
2. 否则按根包名查找,例如 `app.model.user` 查 `database.app`;
3. 根包连接键不存在时使用 `database.default`。
- 只有“根包连接键不存在”允许回落 default。显式连接不存在、default 不存在或被选连接无效都直接启动失败。
- 普通 CRUD 不接收 Database 或 Transaction 参数,也不允许按运行时字符串切换连接。
- SQLite 使用 bundled `rusqlite`,同步调用进入现有有界 blocking 通道,默认小连接数并明确单写者约束。
- PostgreSQL 使用 `tokio-postgres` 与有界异步连接池;池容量、等待和超时全部受配置上限约束。TLS 必须显式选择系统根验证或禁用,不允许验证失败后静默回落明文。
- 编译器按可达数据库能力选择 runtime profile。SQLite-only 二进制不链接 PostgreSQL 驱动,PostgreSQL-only 二进制不链接 SQLite;驱动集合进入原生产物缓存身份。
## CMS 示例项目
- 在 `examples/dever/cms/` 建立可检查、可运行、可构建的完整项目,而不是只展示孤立语法片段。
- 固定目录为 `config/setting.json`、`data/`、`module/cms.dever`、`module/user/model/`、`module/user/service/`、`module/news/model/`、`module/news/service/`。
- `module/user` 和 `module/news` 分别是组件根;逻辑 package 使用 `user.model.*`、`user.service.*`、`news.model.*`、`news.service.*`,入口为 `cms.main`。
- Model 展示隐式 `id`/`created_at`、Uuid、choice、Seed、索引和跨组件关联;Service 只承载注册、发布等事务业务,不包装 Model 已生成的普通 CRUD。
- 示例默认使用 SQLite 和相对路径 `data/db/cms.db`,因此打包二进制可在无外部数据库服务的机器上直接运行;PostgreSQL 通过独立定向 fixture 验证,不让 CMS 示例依赖现有服务。
## 顺序调用、并发与事务
- Dever 源码删除 function/handler 的 `async` 标记和直接调用外层的 `await`。普通调用表示顺序等待完成,编译器根据传递挂起 effect 自动降低为 Rust coroutine。
- 只有 `run(call)` 显式开始并发,`wait(task/group)` 消耗并等待结果,`stop(task/group)` 请求取消并等待结构化清理。
- 同步入口的可达图没有挂起 effect 时不启动异步 runtime;包含网络、数据库、定时器、Channel 或异步 Stream 时只启动一个有界 Tokio runtime。
- 事务使用专用函数声明:
```dever
transaction create_order(...) (...) {
order = order.create({...})
inventory.update({...})
}
```
- 源码不出现 `@transaction`、Database、Transaction、begin、commit、rollback、`async` 或 `await`。
- 编译器分析完整受控调用链中的 Model 数据库 effect,静态推导唯一连接,并通过隐藏参数传递事务上下文;不使用线程局部、任务局部或全局“当前事务”。
- 最外层 transaction 函数开启物理事务;正常完成提交,error、fault 或取消先回滚再传播原始失败。
- 同连接的嵌套 transaction 函数复用外层事务,不开启 savepoint,也不提前提交。
- 无数据库 effect、跨连接、动态无法确定连接,或在 `run`/并发子任务中执行数据库 effect 的 transaction 函数在编译期拒绝。第一轮不支持分布式事务。
## Error、Fault 与日志边界
- choice 变体继续用 `error` 标记可恢复失败;编译器保留并扩展 C012,禁止丢弃、覆盖或仅用日志观察失败。
- 普通可失败调用直接返回成功值,失败自动向当前边界传播,不写 `?`:
```dever
user = user.get(id)
```
- 需要主动处理完整结果时使用一个核心操作 `result(call)`,再由现有穷尽函数分句处理成功和 error 变体。
- 自定义业务失败使用 `fail(OrderError.OutOfStock(id))`。错误集合由编译器沿调用图静态推导并显示在 API、文档和编辑器信息中,不要求每个函数重复声明错误类型。
- `error` 表示可恢复业务/外部失败;`fault` 表示程序不变量、编译器已知资源或 runtime 故障。普通业务函数第一轮不能捕获 fault。
- HTTP 未处理 error/fault 只终止当前请求,回滚事务后返回不泄露内部细节的 500,并在服务端根边界记录一次;预期 4xx 必须由 handler 显式映射。
- Task 失败由 `wait`/`result` 返回;无 owner 的任务边界记录一次。CLI 输出 stderr 并返回非零退出码。
- 日志是观察,不是错误处理;`log.error` 不满足 C012。底层驱动、事务和 Service 不重复记录同一失败。
- 正式日志 API 采用 `dever.log.debug/info/warn/error(message, fields)` 与共享结构化诊断上下文。队列有界:debug/info 满时丢弃当前事件并累计数量,warn/error 施加背压;logger 自身失败或最终 fault 直接写 stderr。
## 性能与资源边界
- ORM 只生成具体 Model 输入、行解码和静态查询计划;共享查询规划、schema diff 和方言实现,不为每个 Model 复制算法。
- 查询使用绑定参数和可复用预编译语句;结果按行拉取,有界缓冲,不默认收集全表。
- 连接池、SQLite blocking、批量写入、分页、关系子列表、stream 和日志队列全部有明确上限。
- 不静默重试非幂等写入;约束冲突、连接失败、超时、取消和解码失败保持可区分。
- 小机器验收以打包二进制的稳定 RSS、空闲连接成本、并发请求内存斜率和取消后的资源回收为准,不以构建机编译耗时或峰值内存为主要目标。
## 验收标准
- 合法 Model 在原生构建前生成确定 schema、创建输入、查询结果和 CRUD;字段、索引、Seed、关系或查询中的非法引用产生源码定位诊断。
- 未声明 `id`/`created_at` 的 Model 仍能读取这两个字段,创建时无需填写;不同 Model ID 无法误传。
- SQLite 与 PostgreSQL 对全部首批字段类型、默认值、长度、精度、choice、外键和索引产生等价逻辑约束并正确往返;PostgreSQL 系统根 TLS 校验和显式明文模式分别通过定向验证。
- 同一个查询配置块在两种数据库生成正确的参数化 SQL,支持分页、游标、stream、复合条件和显式关联加载;to-many 不产生 N+1。
- `list` 默认有界并返回 total/pages,`cursor` 不执行 count,`stream` 在慢消费者下保持有界内存。
- 从空数据库启动时直接创建最终 schema 并执行缺失 Seed;重复启动无重复数据。已存在数据库能安全自动升级,危险变化在没有有效 migrate 时保持原 schema/数据并启动失败。
- 带 `from` 的稳定二进制既能初始化新数据库,也能保留旧列数据完成 rename;自动 revision 和命名迁移 history 可重复验证且不可静默改写。
- 默认、根包和显式连接按既定优先级绑定;显式缺失连接和无效配置启动失败。事务调用链只能使用一个已解析连接。
- create/get/update/delete/list、事务提交、事务回滚和取消清理分别通过 SQLite 与 PostgreSQL 的定向真实数据库 fixture。
- 普通 Dever 网络和数据库调用无需 `async`/`await` 仍是非阻塞 I/O;只有 `run` 建立业务并发,同步程序不启动 Tokio。
- 可恢复 error 不能被丢弃或用日志代替处理;默认传播、`result(call)`、`fail(...)`、HTTP/Task/CLI 边界和事务回滚具有定向编译与运行验证。
- SQLite-only 与 PostgreSQL-only 发布二进制只包含所用驱动;目标机器不需要 Cargo、Rust、SQLite 动态库或 PostgreSQL 客户端 SDK。
- 在 64 MiB 与 128 MiB 约束下分别测量 SQLite、PostgreSQL 的空闲基线、稳定请求负载、流式读取和连接池上限;持续运行时 RSS 不无界增长,取消后连接与事务槽位回到基线。
- `deverc check examples/dever/cms`、`deverc run examples/dever/cms cms.main` 和以同一项目根执行的原生构建均使用 `module/` 作为源码根,并能由 CMS Service 完成初始化数据、注册和发布的纵向流程。
## 暂不纳入第一轮
- 旧源码兼容、第二套 CRUD、链式查询、动态 Model、反射字段访问或运行时动态 SQL 结构。
- MySQL/MariaDB、SQL Server、Oracle 驱动;只保留真实共享边界,不提前增加空适配器。
- 非默认主键、自定义主键类型、以任意 UUID 唯一键建立关系、主键策略迁移。
- choice 稳定变体改名和表/Model 重命名的通用简写;第一轮使用命名 migrate。
- lazy loading、自动 N+1、隐藏多对多表、跨数据库 join、分布式事务、savepoint、读写分离和自动故障转移。
- 数据库专有数组/enum/interval、分区表、表空间、物化视图、触发器、行级安全和在线无锁迁移。
- 通用 annotation、decorator、macro、AST 改写和用户编写的编译期函数处理器。
- 前端、管理页面、package registry、跨平台发行和编译器自举。
- 日志文件 sink 的最终格式、轮转默认值和压缩策略;该项在错误基础设施子阶段收敛,不阻塞 ORM 合同。
@@ -1,76 +0,0 @@
# ORM 架构选项
## 现有语言约束
- 一个源文件完整拥有一个 package,源码相对路径必须与 package 名匹配,因此可用 `model/` 路径承载 Model 身份,不需要再增加 `model` 声明。
- `crates/dever-core/src/native/records.rs` 为用户 record/choice 生成具体类型;生产路径没有通用运行时 Value 或反射分派。
- 系统资源由 `types.rs`、`check/symbols.rs`、`intrinsic.rs`、native emitter 和 runtime 共同定义,错误由官方 `.dever` choice 暴露。
- static handler 是编译期绑定而非运行时函数值,可复用于行映射与查询回调,但不能单独解决表名、主键、索引和关联元数据。
- 当前 native build 复用一个预编译 runtime rlib;数据库驱动选择必须进入可达性、runtime build 和缓存身份,不能只依赖链接器偶然消除代码。
## 表面模型比较
### 显式 Data Mapper(已否决)
概念形态:
```text
find(User, database, id)
insert(database, user)
update(database, user)
```
Model 是数据/schema,Database 和 Transaction 都由调用方显式传递。虽然实现直接,但业务表面重复且容易把同一事务漏传给某次操作;用户已明确否决,不作为公开或兼容 API 保留。
### Active Record(已否决)
概念形态:
```text
User.find(database, id)
user.save(database)
```
该方案会把生成类型和成员暴露给业务代码,用户已明确否决,不作为兼容表面保留。链式查询同样需要新增 method 和中间查询对象语义,且不改善生成 SQL;首版已经确定不采用。
### SQL-first typed mapper
概念形态:参数化 SQL + 静态 row decoder。它不单独承担 ORM,只作为模型包短函数之外的高级逃生口保留:参数必须绑定,调用方必须声明具体 Model/record 结果,数据库专有语句明确选择对应 dialect。
## Model 文件约定
推荐形态:
```dever
package app.model.user exposes (
User
)
type User {
email: Text(254) unique
name: Text(1, 64)
bio: Text?
}
```
- 路径 `app/model/user.dever` 对应 `package app.model.user`,文件名 `user` 精确对应公开 record `User` 和表名 `user`;不做复数化猜测。
- 文件只能公开一个 Model record。私有辅助声明是否允许由实现阶段按真实需要决定,不能形成第二个持久化类型。
- 主键 `id` 和 `created_at` 由 Model 文件约定隐式生成,不进入创建输入;查询结果仍可读取 `user.id` 与 `user.created_at`。其它 Model 通过 `app.model.user.id` 引用该强类型主键。
- 编译器可合成 `User.New` 创建输入和固定 CRUD 成员;这属于 Model 专用静态语义,不意味着加入通用嵌套 type、反射或 type-as-value。
- 简单字段属性紧邻字段;复合唯一约束、复合索引、初始化数据和有序迁移使用同文件的独立声明,避免把复杂结构压进字段行或外部配置。
- 安全且意图唯一的 schema 变化自动生成;索引和 Seed 不使用版本号。简单重命名通过新字段的 `from <old_field>` 明确,编译器自动生成 revision;删除、类型收窄和数据转换等复杂操作才使用 Model 文件底部的命名 migrate。revision/迁移校验值记录在 history 中,SQLite 表重建由 dialect 隐藏。
- Model 文件可以声明自定义持久化操作,但优先组合生成的查询/事务能力;不能覆盖通用 CRUD 的事务、错误和资源合同。
## 驱动边界
- SQLite:成熟 Rust 驱动支持 bundled SQLite,适合产生无需目标系统 SQLite 的单文件程序;同步 API 必须通过 Dever 已有 bounded blocking 资源执行。
- PostgreSQL:成熟 Tokio 驱动原生异步并支持流水线;连接 driver 必须属于 Database/Pool 的结构化作用域。
- 两者共享 QueryPlan、绑定值、行 schema、错误分类和迁移意图;SQL 编码与执行机制保持独立。
## 结论
采用 Model 包短函数作为唯一首版表面,例如 `user.list(...)`、`user.update(...)`。Model 用一文件一普通 `type` 的约定识别,不增加 `model` 关键字;创建、更新和查询块由调用上下文推导,不暴露 `User.Query` 等生成类型。复杂查询使用参数化、类型化的原生 SQL,不引入动态行对象。
跨操作事务采用 `transaction <function>` 专用函数声明。普通调用按顺序等待完成,编译器根据传递挂起 effect 自动降低为协程;Dever 源码不出现 `async`、`await`、Database、Transaction、begin、commit 或 rollback。编译器从 Model 数据库效果推导唯一连接,并把内部事务上下文沿受控调用链传递;正常完成自动提交,error、fault 或取消自动回滚。首版不允许事务中的数据库效果进入并发子任务,也不提供跨库事务、嵌套物理事务或 savepoint。
不引入 `@` 或通用函数装饰器。注解本身并不能减少事务实现,只会额外增加解析、注解解析、顺序、参数、扩展权限和源码映射合同;用户自定义处理还需要通用函数泛型、闭包捕获、async 包装与错误转发。完全自动推导事务和调用点 `transaction(handler)` 也分别存在边界隐蔽与包装遗漏问题,因此都不作为公开表面。
@@ -1,39 +0,0 @@
# 数据库驱动与连接池选择
## 结论
- SQLite 使用 [rusqlite](https://github.com/rusqlite/rusqlite) 并启用 `bundled`,目标机器不依赖系统 SQLite。
- SQLite 池与同步任务适配使用 [deadpool-sqlite](https://docs.rs/crate/deadpool-sqlite/latest),它直接基于 rusqlite,并能把 `bundled` feature 传递给 rusqlite。
- PostgreSQL 使用 [tokio-postgres](https://github.com/sfackler/rust-postgres),保留原生异步连接与流水线能力;TLS 使用 [tokio-postgres-rustls](https://docs.rs/tokio-postgres-rustls/latest/tokio_postgres_rustls/) 复用项目现有 rustls 根证书和主机名校验。
- PostgreSQL 连接池使用 [deadpool-postgres](https://docs.rs/deadpool-postgres/latest/deadpool_postgres/),复用其连接回收和静态语句缓存,不自行实现通用连接池。
## 选择理由
- 四个 crate 属于两组相互配套的驱动/池实现,不需要引入完整 ORM 或第二套查询语义。
- rusqlite 的 bundled 模式静态编译 SQLite,符合打包后二进制直接运行的要求。
- tokio-postgres 与当前 Dever Tokio runtime 一致;它的连接 driver 可以纳入现有结构化任务生命周期。
- deadpool-postgres 已包装 `tokio_postgres::Client`/`Transaction` 并提供 statement cache,适合 Dever 已静态确定 SQL 形状的查询。
- deadpool-sqlite 负责连接池和同步连接的安全交互;Dever 仍在外层施加全局 blocking 上限、取消和超时合同。
## 不采用的方案
- 不采用 SQLx、Diesel 或 SeaORM:Dever 自己拥有 Model、QueryPlan、迁移和类型检查,再引入完整 ORM 会形成重复语义与额外二进制负担。
- 不直接手写 PostgreSQL/SQLite 连接池:池的回收、等待、关闭与失效连接处理不是 Dever 的业务差异点。
- 不让 SQLite 和 PostgreSQL 共用同一个执行 trait object 热路径;共享的是静态 QueryPlan 和错误分类,具体执行通过编译时 profile 与连接枚举分派。
## 二进制裁剪
`dever-runtime` 将数据库依赖设为 optional features:
- `sqlite`:deadpool-sqlite + rusqlite bundled
- `postgres`:deadpool-postgres + tokio-postgres + tokio-postgres-rustls
- `database`:共享 config、ORM 值和错误基础
构建阶段从 `config/setting.json` 读取所有连接的数据库类型,只把驱动能力集合加入 runtime profile 和原生产物缓存身份,不嵌入账号、连接串或其它配置值。运行时设置可以在同一能力集合内改变;改用未打包的数据库类型必须重新构建。
## 验证重点
- 分别比较 base、SQLite-only、PostgreSQL-only 和双驱动二进制大小。
- 64/128 MiB 环境测量空池、最大池、稳定负载和连接归还后的 RSS。
- 验证连接获取超时、取消、driver task 失败、SQLite busy 和 shutdown drain。
- 对静态 SQL 验证 statement cache 命中;迁移 DDL 不进入无界 statement cache。
@@ -1,192 +0,0 @@
# ORM 字段类型与隐式字段
## 默认字段
`model/` 中的每个 Model 即使源码不写,也具有:
```dever
id: <model-package>.id
created_at: DateTime
```
- `id` 是编译器合成、数据库生成的 64 位正整数主键字段;它的公开静态类型就是当前 Model 的 `<model-package>.id`,不能当作普通 Int 运算,不同 Model 之间也不能混用。
- `created_at` 由 ORM 在插入时写入当前 UTC DateTime。
- 两个字段都存在于完整 Model 和查询结果中,不存在于 `Model.New` 创建输入中。
- `updated_at` 不默认生成。Go Dever 现有 Model 中 created_at 明显比 updated_at 普遍;只有确实需要跟踪修改时间的 Model 才显式声明自动更新字段。
- `deleted_at`、version、tenant_id 和操作者字段都具有业务含义,不作为所有 Model 的隐式字段。
因此最小 Model 可以写成:
```dever
type User {
name: Text(1, 64)
email: Text(254) unique
}
```
读取 `user.id`、`user.created_at` 合法,但创建时只提供 name/email。
## 通用类型矩阵
| Dever 类型 | 逻辑语义 | PostgreSQL | SQLite |
| --- | --- | --- | --- |
| Bool | 布尔值 | BOOLEAN | INTEGER + CHECK |
| Int | 有符号 64 位整数 | BIGINT | INTEGER |
| Float | binary64 | DOUBLE PRECISION | REAL |
| Decimal(P, S) | 最多 34 位十进制定点数 | NUMERIC(P,S) | 精确定点编码与约束 |
| Text | 无界 UTF-8 文本 | TEXT | TEXT |
| Text(N) | 0 至 N 个 Unicode codepoint | VARCHAR(N) | TEXT + CHECK |
| Text(M, N) | M 至 N 个 Unicode codepoint | VARCHAR(N) + CHECK | TEXT + CHECK |
| Bytes | 无界字节 | BYTEA | BLOB |
| Bytes(N) / Bytes(M, N) | 有界字节 | BYTEA + CHECK | BLOB + CHECK |
| `<model-package>.id` | 具体 Model 的不透明 64 位主键/外键投影 | BIGINT identity | INTEGER PRIMARY KEY |
| Uuid | 128 位 UUID | UUID | 16-byte BLOB |
| DateTime | UTC 时间点,毫秒精度 | TIMESTAMPTZ(3) | epoch-millis INTEGER |
| Date | 公历日期 | DATE | epoch-day INTEGER |
| Time | 一天内时间,毫秒精度 | TIME(3) | millis-of-day INTEGER |
| Duration | 固定毫秒时长 | BIGINT | INTEGER |
| Json | 已验证 JSON 值 | JSONB | UTF-8 TEXT |
| 无载荷 choice | 封闭枚举 | TEXT + CHECK | TEXT + CHECK |
List、Map、普通 record 不自动变成 JSON 列。调用方必须显式选择 Json,避免字段重构在不知情时改变持久化格式。
## UUID 字段
UUID 不是每个 Model 的隐式字段。只有业务需要公开不可预测标识、跨系统交换标识或在写入数据库前生成标识时才显式声明:
```dever
type User {
uuid: Uuid generated unique
name: Text(1, 64)
}
```
- `uuid: Uuid` 由调用方提供,存在于创建输入中。
- `uuid: Uuid generated` 由 ORM 在创建时生成 UUIDv7,不存在于创建输入中。
- `uuid: Uuid?` 接受空值;`generated` 与可空不能同时使用。
- 运行时使用固定 16 字节值,不用堆分配的 Text 保存 UUID。
- PostgreSQL 使用原生 UUID,SQLite 使用 16-byte BLOB,索引不会保存 36 字符文本。
- API、JSON、日志及显式文本转换统一输出小写、带连字符的 36 字符形式;解析只接受语言标准库定义的 UUID 文本格式。
- Uuid 字段不会自动成为关联。关联仍写成 `app.model.user.id`,以保持外键类型检查和紧凑索引。
## 初始化数据写法
Seed 是当前 Model 的可重复初始化数据。一个块可以包含多条数据,值使用 Model 创建输入进行静态检查:
```dever
type Status {
code: Text(32) unique
name: Text(64)
enabled: Bool default true
}
seed {
{
code = "pending"
name = "待处理"
}
{
code = "done"
name = "已完成"
enabled = true
}
}
```
以后增加初始化记录时直接追加数据块,不维护 Seed 版本号。编译器按 Seed 内容校验值判断是否需要执行;执行时整体使用事务,唯一冲突保留已有记录。源码移除 Seed 也不删除数据,需要修改或删除时使用显式命名迁移。
## 可选项
固定可选项复用语言现有的无载荷 choice `type`,不另造 ORM enum:
```dever
type TaskStatus {
Pending = "待处理"
Running = "进行中"
Done = "已完成"
}
type Task {
title: Text(1, 160)
status: TaskStatus default TaskStatus.Pending
}
seed {
{
title = "初始化任务"
status = TaskStatus.Pending
}
}
```
`Task` 是文件对应的 Model record;`TaskStatus` 是同文件的辅助 choice,不生成表。ORM 第一轮只接受不带 payload 的 choice,并将左侧变体名映射为带 CHECK 约束的 Text。右侧显示名称不存入业务列。
推荐由编译器把显示名称提供为保持声明顺序的只读 Map,不再重复定义映射:
```dever
task_status_options() (options: Map<TaskStatus, Text>) {
options = TaskStatus.options
}
```
需要选项子集或自定义顺序时再返回 `List<TaskStatus>` 或组合 `TaskStatus.options`。choice 负责限制合法值,生成的 Map 负责名称和默认顺序。
## 索引与迁移写法
单字段索引直接跟在字段后,复合索引放在 Model 后;它们都描述当前目标 schema,不使用版本号或手工数据库名称:
```dever
type Task {
tenant_id: app.model.tenant.id
code: Text(32)
status: TaskStatus index
title: Text(1, 160)
}
unique(tenant_id, code)
index(status, created_at)
```
简单字段重命名直接写在新字段旁边:
```dever
type Task {
summary: Text from description
}
```
编译器生成 `RENAME COLUMN`,不会把它误判成删旧列再加新列。revision 由编译器根据规范化 schema 和计划自动生成并写入数据库 history,源码不维护版本号。
拆分、合并、删除前归档等无法附着在单个新字段上的数据变化,才保留一次性命名迁移:
```dever
migrate remove_legacy_code {
drop legacy_code
}
```
迁移名称是复杂操作的稳定身份,内容校验值用于阻止已经执行的迁移被悄悄修改。`from` 需要保留到所有可能升级的数据库都已越过该变更。
## 关联 ID
```dever
type Article {
author_id: app.model.user.id
category_id: app.model.category.id?
title: Text(1, 160)
}
```
- `app.model.user.id` 与 `app.model.category.id` 是不同静态类型,不能误传。
- 点分路径的最后一段 `id` 是目标 Model 的隐式字段投影,不是新增的普通小写 type;它也能用于 function 输入、输出和局部类型位置。
- 非空和可空关联第一轮都使用 `ON DELETE RESTRICT`;SET NULL 或 CASCADE 的通用关系选项不在第一轮,必要时使用明确方言的命名 migrate。
- 每个关联 ID 自动创建普通索引和外键约束。它只表达持久化关系,不自动查询目标对象。
- 关系预加载必须显式请求并保持有界,不能形成隐藏 N+1。
## 时间 API 方向
- DateTime 值始终代表 UTC 时间点,不在值内携带可变时区数据库。
- 时区只参与解析、格式化和日历换算;数据库比较、排序和相减使用统一时间点。
- `created_at` 自动取当前 DateTime。`updated_at` 不隐式生成;第一轮需要该字段时按普通 `DateTime` 明确写入,自动更新修饰词后续单独设计。
- DateTime/Date/Time 的 parse、format、比较和加减由标准 package 提供;数据库函数通过 ORM 查询表达式或明确的原生 SQL 使用。
@@ -1,45 +0,0 @@
# Go Dever ORM 参考结论
## 已核对实现
- `/data/project/shemic/backend/dever/orm/schema_registry.go` 通过 Go 反射读取 `dorm` 字符串 tag;Text 默认 `VARCHAR(255)`,`size:N` 生成 `VARCHAR(N)`,也允许直接写 `type:varchar(N)` 或 `type:text`。
- `ModelConfig.Index`/`Indexes` 使用额外 Go struct 表达单列或复合索引;`ModelConfig.Seeds` 使用 `[]map[string]any` 表达初始化数据。
- `/data/project/shemic/backend/dever/orm/schema_apply.go` 能建表、加字段、修改部分数据库的字段类型、删旧字段,并同步新增、变化和删除的索引。
- 当前 Seeds 只在表首次创建时插入;已有表后来新增 Seed 不会自动补入。Seed 使用动态 Map,字段名和值类型不能在编译期完整检查。
- 当前自动迁移直接对数据库执行,再可选记录 SQL;它会删除不在目标 schema 中的字段和索引。字段重命名只对大小写或下划线差异做有限推断,无法可靠判断任意重命名。
- `database.persist=true` 会把目标 schema 写入 `data/table/*.json`,`dever migrate` 再读取这些文件应用到目标数据库。该目录是 schema 传递/发布产物,不包含某个数据库实例是否成功执行的可靠状态;同一文件可以对应多个状态不同的数据库。
- Go Model wrapper 可以嵌入通用 `orm.Model[T]` 并增加 `AfterSave`、`AfterDelete` 或自定义查询方法。普通 Model 直接复用通用 CRUD,不需要 wrapper。
## 新语言应复用的设计
- 一个 Model 文件集中字段、索引、默认排序、初始化数据和少量紧贴持久化生命周期的扩展。
- 通用 CRUD、过滤、排序、分页、事务和批量操作由 ORM 提供,不为每个 Model 重写。
- 单列与复合索引都从 Model 目标 schema 自动同步。
- Model 声明生成确定的数据库 schema,SQLite/PostgreSQL 只负责方言差异。
## 不应照搬的部分
- 不使用字符串 tag、反射、`any`、动态字段 Map 或运行时猜测字段名。
- 不把无界 Text 默认截成 `VARCHAR(255)`;`Text` 表示无界文本,只有显式长度上限才产生 VARCHAR/CHECK。
- 不只在首次建表时处理初始化数据。每条 Seed 必须有稳定身份并可重复应用。
- 不把“字段从目标结构中消失”直接等同于可以删列,也不把任意新增/缺失列猜成重命名。
- 不把本地 `table/` 文件是否存在当作数据库建表或迁移完成标记。新语言将目标 schema 嵌入二进制,实际状态记录在数据库并通过系统目录核对。
- 不允许自定义方法覆盖并改变通用 CRUD 的基本事务、错误和资源合同;自定义操作组合通用查询或参数化 SQL。
## 推荐字段形态
```dever
type User {
name: Text(1, 64)
email: Text(254) unique
bio: Text?
credit: Decimal(18, 2) default 0
status: Int >= 0 and <= 2 default 1
}
```
- `Text(N)` 表示最多 N 个 Unicode codepoint,`Text(M, N)` 表示 M 至 N 个;无参数 Text 是无界文本。
- `Bytes(N)` 表示最多 N 个字节,`Bytes(M, N)` 表示 M 至 N 个字节。
- `Decimal(P, S)` 描述数据库精度与小数位,必须满足 `1 <= P <= 34`、`0 <= S <= P`,与 Dever decimal128 的 34 位有效数字上限一致。
- `?` 已经表达可空,不再增加 `null/not null` 两套写法。
- `id` 与 `created_at` 由 Model 隐式生成;关联主键用 `app.model.user.id` 这种字段投影引用。默认值和简单 unique 靠字段元数据;复合索引、无版本 Seed 和命名迁移使用同文件的独立声明,避免把复杂结构塞进一行字段。
@@ -1,48 +0,0 @@
# 隐式挂起的顺序调用
## 决定
Dever 源码删除 function/handler 的 `async` 标记和直接异步调用外层的 `await(...)`。普通调用始终表示调用完成后才执行下一条语句;如果调用链包含数据库、网络、定时器、Channel 或异步 Stream 等挂起效果,编译器自动把对应函数降低为协程。
```dever
route(request: dever.http.Request) (response: dever.http.Response) {
user = user.get(request.user_id)
response = render(user)
}
```
等待 I/O 时只挂起当前协程,Tokio 工作线程可以执行其它连接和任务。语法简化不意味着阻塞线程,也不改变 HTTP/TCP/WS/SSE runtime、连接池、背压、超时与取消清理。
## 并发边界
普通调用严格顺序;只有 `run` 显式启动并发:
```dever
first = run(load_user(first_id))
second = run(load_user(second_id))
first_user = wait(first)
second_user = wait(second)
```
- `run(call(...))` 创建受限 Task。
- `wait(task_or_group)` 消耗任务或任务组并取得结果/等待排空。
- `stop(task_or_group)` 请求取消并等待结构化清理。
- CPU 同步计算继续使用 `parallel`,明确阻塞调用继续使用 `blocking`。
- 普通调用不产生 Task,也不允许把可挂起调用结果当 Future 保存。
## 编译器边界
- 系统 intrinsic 和官方库入口提供基础挂起效果;调用图、静态 handler 专门化和 collection handler 传播效果。
- checker 在效果推导后区分顺序调用、`run` 和 Task `wait`;HIR/native 仍可保留内部 coroutine/await 节点,但不把它们暴露到源码。
- 同步入口的完整可达图没有挂起效果时沿用直接原生入口,不初始化 Tokio。
- handler 是否允许挂起由其使用位置和实际效果静态检查,不再由源码 `async handler` 标记。
- API snapshot、Markdown 合同和格式化输出的是新语法;旧 `async`/`await` 不保留兼容分支。
## 迁移范围
- lexer、parser、syntax AST、checker、effect/cycle/failure 分析、HIR 与 native emitter。
- `dever.task`、`dever.net`、`dever.http`、`dever.websocket`、`dever.sse` 的官方 Dever 声明。
- HTTP/1、HTTP/2、TCP、TLS、WebSocket、SSE、AsyncStream、Channel 和结构化并发测试中的嵌入源码。
- backend/http/live/async 示例、性能 fixtures、LANGUAGE 与 Trellis backend 规范。
Rust runtime 内的 Rust `async/.await` 不在迁移范围;删除它们会把高并发 I/O 退化为阻塞或线程堆叠,与目标相反。
@@ -1,20 +0,0 @@
# 字段重命名与迁移版本调研
## 官方实现结论
- [Alembic Autogenerate](https://alembic.sqlalchemy.org/en/latest/autogenerate.html) 明确说明无法检测表名和列名变化;它会生成 add/drop 候选,要求开发者审查并改为 rename。
- [Prisma 自定义迁移](https://docs.prisma.io/docs/orm/prisma-migrate/workflows/customizing-migrations) 的字段重命名示例同样默认得到 drop/add,需要先生成草稿迁移,再人工改成 `RENAME COLUMN`。Prisma 用迁移目录和数据库 `_prisma_migrations` 表维护历史。
- [EF Core 管理迁移](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/managing) 说明框架无法判断“删除并新增”和“重命名”的真实意图;生成代码需要改为 `RenameColumn`,否则会丢失旧列数据。
- [Django 迁移操作](https://docs.djangoproject.com/en/5.2/ref/migration-operations/) 提供显式 `RenameField(old_name, new_name)`;[Django 迁移流程](https://docs.djangoproject.com/en/6.0/topics/migrations/) 把迁移文件视为数据库 schema 的版本控制,并在数据库记录应用历史。
- [Atlas 声明式重命名](https://atlasgo.io/changelog/declarative-schema-renames) 使用紧邻目标列的 `renamed_from` 显式表达意图。Atlas 早期依赖交互式询问,但非交互部署无法回答,因此把 rename 意图写回声明式 schema。
- [Atlas 声明式与版本式对比](https://atlasgo.io/concepts/declarative-vs-versioned) 表明声明式流程可以从目标 schema 规划实际数据库差异;版本式流程则保留完整迁移文件历史。两者都不能依靠一个数字版本推导 rename 意图。
## 对 Dever 的结论
- 只增加 Model 数字版本不能解决字段重命名;版本只标识状态先后,不包含旧列与新列的对应关系。
- 不采用字段名相似度自动猜测。相同类型的删一列、加一列既可能是 rename,也可能是两个独立业务变化。
- 简单 rename 采用声明式字段来源:`new_field: Type from old_field`。它比单独 migrate 块短,并把意图放在所属字段旁。
- 编译器为规范化目标 schema 与迁移计划自动产生 revision id/校验值,嵌入二进制;数据库内部 history 记录实际应用状态。开发者不写数字版本。
- `from` 必须保留到所有需要升级的数据库越过该变化;这与 Atlas 把 `renamed_from` 放进 schema 的原则一致。
- `from` 只描述已有表的升级路径。目标表不存在时直接创建当前最终 schema,只创建新字段,不要求旧字段存在,也不先创建旧结构再回放 rename。
- 拆分、合并、跨行回填、删除前归档等没有单字段对应关系的变化,仍需要命名 migrate。该复杂度来自数据转换本身,不能由版本号或启发式推断消除。
@@ -1,199 +0,0 @@
# ORM 查询与内置操作方案
## 结论
公开表面只使用 Model 包短函数和一次性查询配置块:
```dever
users = user.list({
where = status == UserStatus.Active and age >= 18
order = [created_at.desc, id.desc]
page = 1
size = 20
with = [owner]
})
```
不提供 `User.Query`、`User.List`、Active Record 实例方法或 `user.where(...).order(...).list()` 链式查询器。编译器把匿名块直接静态降低为 `QueryPlan`;源码没有 `async`/`await`,普通调用顺序等待完成。
## 为什么选择配置块
块式和链式可以生成相同 SQL,链式本身没有数据库性能优势。Dever 当前没有通用 method、lambda、用户泛型或 type-as-value,链式方案会额外引入查询对象类型、所有权、复用和执行时机合同。配置块则有以下特点:
- `create`、`update` 和 `list` 保持同一种上下文推导形式。
- 一次调用完整展示条件、排序、分页和关系加载。
- 编译器直接生成一个静态计划,不产生运行时中间查询对象。
- 公开 API 只有一套写法,不需要兼容或维护等价表面。
将来若动态 HTTP 参数确实需要逐步组合条件,应增加受限的类型化 where 值,而不是改造全部 CRUD 为链式查询。
## 基本 CRUD
```dever
users = user.list()
current = user.get(user_id)
created = user.create({
name = "Dever"
status = UserStatus.Active
})
updated = user.update(user_id, {
name = "New name"
})
deleted = user.delete(user_id)
```
`create` 和 `update` 的匿名数据块由调用上下文推导字段。不能写入的 generated 字段、拼错的字段和不兼容值在编译期拒绝;底层仍是具体输入 record,不是动态 Map。
普通可失败调用成功时直接产生成功值,失败自动传播。需要主动处理完整结果时写:
```dever
result = result(user.get(user_id))
```
随后使用现有穷尽函数分句处理成功和 error 变体,不为 ORM 增加 try/catch 或 `?`。
## List、Cursor 与 Stream
`user.list()` 返回有界默认第一页。完整配置为:
```dever
users = user.list({
where = status == UserStatus.Active and created_at >= start
order = [created_at.desc, id.desc]
page = 1
size = 20
with = [owner]
})
```
查询块处于当前 Model 上下文,所以字段直接写 `status`、`created_at`、`owner`,不重复 Model 名。它不是动态 Map,字段、运算符、值、排序和关系都由编译器检查。
- `page` 默认 1,`size` 默认 20,并受统一配置上限约束。
- 默认排序为 `id.desc`;自定义排序不是唯一键时自动追加 `id`,保证页边界稳定。
- 返回 `items`、`page`、`size`、`total`、`pages`。
- 页码查询执行数据查询和 count;不需要 total 时使用 cursor。
深翻页使用同一个配置块:
```dever
users = user.cursor({
after = next
size = 100
where = status == UserStatus.Active
order = [created_at.desc, id.desc]
})
```
`cursor` 返回 `items`、`next`、`has_more`,不执行 count。全表处理使用 `user.stream(query?)`,按行拉取并保持有界缓冲;不提供无界 `all()`。
## Where
多个条件直接组成一个布尔表达式,不增加条件数组:
```dever
users = user.list({
where = status == UserStatus.Active
and age >= 18
and age < 60
and (city == "北京" or city == "上海")
})
```
- 比较支持 `==`、`!=`、`<`、`<=`、`>`、`>=`。
- 逻辑组合使用既有 `and`、`or`、`not` 和括号。
- 集合与区间使用 `in`、`between`。
- Text 匹配使用 `contains`、`starts_with`、`ends_with`。
- 可空字段可直接与 `null` 比较。
- 关系条件使用字段路径,例如 `owner.status == UserStatus.Active`;筛选关系不会自动把关系放进结果。
ORM 将表达式规范化为静态条件树,按源码顺序收集绑定值。相同字段可重复出现,矛盾条件由数据库返回空结果;不引入运行时条件解释器。
## 关联
正向 to-one 从关联 ID 推导:
```dever
type Task {
owner_id: app.model.user.id
}
```
查询时使用 `with = [owner]`。反向 to-many 无法可靠推断名称,因为目标 Model 可能有多个外键,因此在当前 Model 文件显式命名:
```dever
relation tasks = app.model.task.owner_id
```
- to-one 使用受控 JOIN 或等价单次查询。
- to-many 按当前父页 ID 批量加载并归组,不逐父记录查询。
- to-many 子列表必须有稳定排序和数量上限。
- 多对多使用显式中间 Model,不生成隐藏表。
- 不提供 lazy loading,读取普通字段不会在背后访问数据库。
## 内置函数
| 函数 | 用途 |
| --- | --- |
| `user.create(data)` | 创建一条并返回完整 Model |
| `user.create_many(list)` | 有界批量创建 |
| `user.get(id)` | 按主键获取一条 |
| `user.first(query?)` | 按条件获取第一条 |
| `user.list(query?)` | 自动页码分页 |
| `user.cursor(query?)` | 稳定游标分页 |
| `user.count(where?)` | 统计数量 |
| `user.exists(where?)` | 判断是否存在 |
| `user.stream(query?)` | 有界流式遍历 |
| `user.update(id_or_query, changes)` | 单条或条件批量更新 |
| `user.delete(id_or_query)` | 单条或条件批量删除 |
| `user.upsert(key, create, update)` | 按唯一键新增或更新 |
`update`/`delete` 根据首个参数静态区分主键和条件,不增加重复的 `update_many`/`delete_many` 名称。批量写入返回影响行数;业务状态流转、跨表原子编排和外部调用由 transaction 函数组合。
## 数据库绑定
普通调用不传 `Database`:
```dever
user.list({...})
user.update(user_id, {...})
```
Model 的连接在程序启动时解析并缓存:
1. Model 文件显式 `database report`;
2. 否则按根包名查找,例如 `app.model.user` 查 `database.app`;
3. 根包键不存在时使用必需的 `database.default`。
显式连接不存在、default 不存在或选中配置无效时启动失败。生成代码访问固定连接槽位,不按字符串进行每请求查找,也不允许 CRUD 临时切库。
## 自动事务
事务不属于 Model API,源码不取得或传递 Transaction:
```dever
transaction assign_task(
user_id: app.model.user.id,
title: Text
) (result: AssignResult) {
user.update(user_id, {
active = true
})
task.create({
owner_id = user_id
title = title
})
result = AssignResult.Assigned
}
```
编译器分析完整调用链,从 Model 数据库 effect 推导唯一连接。进入最外层函数时自动开启物理事务,直接或经普通顺序 helper 执行的操作都使用隐藏事务上下文;正常完成提交,error、fault 或取消先回滚再离开。
同连接嵌套 transaction 复用外层事务,不使用 savepoint。跨连接事务和事务中的并发数据库子任务在编译期拒绝。该设计不引入 `@`、decorator、显式 begin/commit/rollback 或动态当前事务。
## 实现与性能
- 所有 Model 复用一个编译期 `QueryPlan`、schema 引擎和方言执行层,只生成字段专属的薄代码。
- 参数绑定、预编译语句和具体行解码不经过反射或动态 Map。
- to-many 批量加载避免 N+1,也避免多个 to-many JOIN 产生笛卡尔膨胀。
- 分页、关联子列表、批量写入、行流、连接池和 SQLite blocking 全部有界。
- SQLite 和 PostgreSQL 可以用不同执行器,但必须消费同一逻辑查询计划并返回同一 Dever 类型。
@@ -1,90 +0,0 @@
# Model schema 变更分类
## 设计原则
Model 始终声明当前目标 schema。ORM 只保留三种演进机制:
1. 目标 schema diff:处理意图唯一且不会静默丢失数据的变化。
2. `from`:补充 diff 无法推断的旧身份,例如字段重命名。
3. 命名 `migrate`:处理需要业务决定的数据转换、修复和删除。
不为长度、默认值、可空性、索引等每类变化增加单独版本或迁移关键字。SQLite 和 PostgreSQL 的 DDL 差异由 dialect 处理。
## 变更矩阵
| 变化 | Model 写法 | 默认处理 |
| --- | --- | --- |
| 新建表 | 新增 Model 文件 | 直接创建当前最终 schema |
| 新增可空字段 | 新增 `field: Type?` | 自动添加 |
| 新增必填字段并有常量默认值 | 新增 `field: Type default value` | 自动添加并由数据库填充已有行 |
| 新增必填字段但无默认值 | 新增 `field: Type` | 空表可自动添加;有数据时要求 migrate 回填 |
| 字段重命名 | `new_field: Type from old_field` | 旧表执行 rename;新表只创建新字段 |
| 字段重命名并放宽类型约束 | `new_field: WiderType from old_field` | rename 后自动修改类型/约束 |
| 字段重命名并收紧类型约束 | `new_field: NarrowerType from old_field` | 先验证数据;不满足时要求 migrate |
| Text/Bytes 最大长度增加 | 直接修改类型参数 | 自动修改 |
| Text/Bytes 最大长度减小 | 直接修改类型参数 | 先验证现有值长度;不截断,失败时要求 migrate |
| Text/Bytes 最小长度增加 | 直接修改类型参数 | 先验证现有值;失败时要求 migrate |
| Text/Bytes 最小长度减小 | 直接修改类型参数 | 自动放宽约束 |
| Decimal 精度/小数位变化 | 直接修改类型参数 | 仅可精确往返时自动;可能溢出或舍入时要求 migrate |
| 同族无损类型扩大 | 直接修改字段类型 | 按编译器固定无损转换表自动执行 |
| 跨族或可能有损类型转换 | 直接修改字段类型并提供 migrate | 必须明确转换表达式,禁止依赖数据库隐式强转 |
| 必填改可空 | `Type` 改为 `Type?` | 自动移除 NOT NULL |
| 可空改必填 | `Type?` 改为 `Type` | 验证无 NULL;否则要求 migrate 回填 |
| 增加默认值 | 增加 `default value` | 自动修改,只影响后续写入 |
| 修改默认值 | 修改 `default value` | 自动修改,只影响后续写入 |
| 删除默认值 | 删除 `default value` | 自动修改,不改已有数据 |
| 普通索引增加/删除 | 增删 `index` 或 `index(...)` | 自动同步,不改业务数据 |
| 普通索引列或顺序变化 | 修改 `index(...)` | 删除旧索引并创建新索引 |
| 增加唯一约束 | 增加 `unique` 或 `unique(...)` | 先验证无重复;失败时要求 migrate 去重 |
| 删除唯一约束 | 删除 `unique` | 自动删除约束/索引 |
| 普通索引改唯一 | `index` 改为 `unique` | 先验证无重复 |
| 唯一索引改普通 | `unique` 改为 `index` | 自动放宽 |
| 增加外键/修改关联目标 | 改为 `<model-package>.id` | 验证无孤儿记录;失败时要求 migrate |
| 删除外键 | 改为普通字段类型或删除关联 | 自动删除约束;字段数据保留 |
| 修改外键删除行为 | 第一轮使用命名 migrate | 默认 RESTRICT;通用关系选项语法后续设计 |
| choice 增加变体 | 增加变体 | 自动放宽 CHECK |
| choice 修改显示名称 | 修改右侧文本 | 无 schema 迁移,存储值不变 |
| choice 删除稳定变体 | 删除变体 | 验证没有旧值;否则要求 migrate 映射 |
| choice 稳定变体改名 | 第一轮使用命名 migrate | 显式更新已有存储值并重建 CHECK |
| 删除字段 | 从 record 删除并写命名 migrate | 必须显式确认 drop;关联约束和索引按依赖顺序删除 |
| 删除表 | 删除 Model 并写命名 migrate | 必须显式确认,禁止仅因文件消失自动删表 |
| 表/Model 重命名 | 第一轮使用命名 migrate | 显式执行表 rename 并更新 schema identity |
| 字段拆分 | 当前字段 + 命名 migrate | migrate 负责回填多个目标字段 |
| 字段合并 | 当前字段 + 命名 migrate | migrate 负责合并与冲突规则 |
| 主键策略或主键类型变化 | 命名 migrate | 重建引用关系,首轮不自动处理 |
| generated 策略变化 | 直接修改声明并按需 migrate | 只影响新值时自动;需要改已有值时 migrate |
| Seed 新增记录 | 修改 `seed` | 按唯一键只插入缺失记录 |
| Seed 修改/删除已有记录 | 命名 migrate | Seed 不覆盖或删除线上数据 |
| 字段声明顺序变化 | 调整源码顺序 | 不改变已有数据库列顺序,不产生迁移 |
| 注释、标签、自定义方法变化 | 直接修改源码 | 不产生数据库迁移 |
## 长度修改示例
```dever
type User {
display_name: Text(128) from nickname
}
```
如果旧字段是 `nickname: Text(64)`,规划顺序是先 rename,再把最大长度放宽到 128。全新数据库只创建 `display_name` 的最终定义。
收窄时仍只修改目标类型:
```dever
type User {
display_name: Text(32) from nickname
}
```
ORM 先验证已有值都不超过 32 个 Unicode codepoint。全部满足就应用约束;存在超长值则整个迁移失败,不做自动截断。开发者必须通过命名 migrate 明确选择截断、拒绝、归档或其它业务规则。
## 方言执行边界
- PostgreSQL 原生支持 rename、类型变更、默认值、可空性和约束变更;类型变更可以使用 `USING` 表达式。
- SQLite 直接 ALTER 能力较少。不能原地完成的同一逻辑变化由 dialect 在事务中创建目标临时表、复制转换后的数据、替换旧表并重建索引/约束。
- 两个方言必须得到相同的规范化目标 schema 和 revision;底层是否重建表不能泄漏到 Dever Model 语法。
- Dever 不使用静默截断、自动填充猜测、名称相似度推断或数据库特有隐式类型转换。
## 不属于首轮通用 Model 的数据库管理能力
分区表、表空间、存储参数、物化视图、触发器、行级安全策略、数据库专有索引方法和在线无锁迁移不进入首轮跨数据库 Model 协议。这些能力需要 PostgreSQL 专有扩展或显式原生迁移,不能伪装成 SQLite/PostgreSQL 通用行为。
-26
View File
@@ -1,26 +0,0 @@
{
"id": "typed-orm",
"name": "typed-orm",
"title": "Typed ORM for SQLite and PostgreSQL",
"description": "为 Dever 增加隐式挂起与错误传播基础、一文件 Model、数据库无关查询计划,以及 SQLite/PostgreSQL 的类型安全 ORM、事务和迁移能力。",
"status": "completed",
"dev_type": null,
"scope": "implicit suspension/error contracts + compiler model/query/transaction contracts + runtime config/orm/sqlite/postgres/log foundations + focused database and memory fixtures",
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-13",
"completedAt": "2026-09-17",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,8 +0,0 @@
{"file": ".trellis/tasks/09-19-application-testing/prd.md", "reason": "逐项核对应用测试验收标准"}
{"file": ".trellis/tasks/09-19-application-testing/design.md", "reason": "核对实现未偏离最终架构与隔离边界"}
{"file": ".trellis/tasks/09-19-application-testing/implement.md", "reason": "核对分阶段交付与允许执行的验证"}
{"file": ".trellis/spec/backend/compiler-contracts.md", "reason": "检查语言合同和生产编译路径无回归"}
{"file": ".trellis/spec/backend/error-handling.md", "reason": "检查诊断与测试失败报告"}
{"file": ".trellis/spec/backend/quality-guidelines.md", "reason": "检查重复、职责和定向覆盖"}
{"file": ".trellis/spec/backend/database-guidelines.md", "reason": "检查临时 SQLite 隔离且不触达正式数据库"}
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "检查源码到 CLI/runtime 的跨层闭环"}
@@ -1,143 +0,0 @@
# Dever 应用测试体系技术设计
## Architecture
```text
module/ + test/
-> one SourceMap with compiler-assigned Application/Test origins
-> existing parser and Markdown extraction
-> existing declaration registration and semantic checker
-> Test layout/access rules
-> filename-matched TestCase metadata
-> test-only assert/assert_eq lowering
-> checked Program
-> one native suite containing every checked TestCase specialization
-> case-id dispatch into one isolated native process + temporary project directory per case
-> captured result + deterministic CLI summary
```
There is no test interpreter, dynamic test registry, source macro, runtime reflection or production test symbol. Test behavior is a thin compiler-owned entry mode over the same checked Program and native backend.
## Source Ownership And Identity
`SourceFile` gains a compiler-assigned source origin rather than inferring test authority from a user-controlled path. Production loading remains the current `SourceMap::load(module_root)` behavior. A dedicated project-test loader loads `module/` as application sources and an existing `test/` as test sources; a missing test root is an empty set, while a non-directory or unreadable root is an explicit load error.
Test sources retain two paths:
- display path: `test/user/account/register.dever`, used by diagnostics and CLI output;
- logical path: `user/account/register.dever`, used for package identity and Markdown package documentation.
The new `SourceLayout::Test { component, domain, topic }` is valid only for exactly three logical segments. It applies existing lower-snake-case and generic-bucket rules. `.dever` and `.dever.md` with the same logical stem are one duplicate test identity.
The checker cache identity includes source origin/logical path so a strict application source and a test source with equal bytes cannot share the wrong checked result.
## Test Entry Contract
For `test/user/account/register.dever`, the logical package is `user.account.register`; the checker selects its only ordinary `register() ()` function and records a `TestCase` containing:
- stable public identity `user/account/register`;
- checked private function id;
- source span and whether its reachable graph uses a Model.
Other declarations stay private to the same test file. A same-name overload, transaction/recovery entry, parameters or outputs are rejected before native compilation. Test functions are never marked public and never enter API/model snapshots.
`Program` exposes read-only test metadata to the CLI. Native compilation accepts an already checked test-case id through a dedicated method instead of weakening `Program::entry` or making test functions public.
## Visibility
Test resolution extends the existing role-aware symbol owner rather than adding a parallel resolver:
- same file: private helper functions/types;
- same domain: App and Domain functions/types;
- other domains: App public surface only;
- Model values/types: readable under existing public/type rules;
- Model operations: still rejected because only the owning App role may issue CRUD;
- Adapter, Port, API and other Test owners: not callable.
All production callers reject Test targets before ordinary same-domain checks, preventing reverse dependencies. Test assertions are available only while checking a Test owner.
## Assertions
`assert` and `assert_eq` are compiler-owned test intrinsics, not bundled library functions. They reuse the existing `Intrinsic` expression shape so dependency, effect, liveness and native traversal do not gain a second assertion IR hierarchy.
- `assert(value)` checks one `Bool` expression.
- `assert_eq(actual, expected)` checks the left expression first, uses its exact type as the right-hand expectation, and requires the existing `Type::comparable` contract.
- both return Unit and are legal only as ordinary zero-output actions in Test-owned functions;
- declarations named `assert` or `assert_eq` remain ordinary outside tests and are reserved inside a test file.
Native emission evaluates each operand once and preserves source order. On failure it creates a source-located assertion error. `assert_eq` renders concrete typed values through the existing `Render` implementations; generated records/choices retain their source-shaped renderer. No generic runtime `Value`, serialization round-trip or reflection is introduced.
## Native Execution And Reporting
The CLI checks the combined source set once, sorts `TestCase` metadata, then compiles one native test suite and runs each case serially. The suite unions the existing specialization graphs and dispatches one checked private entry by its stable sorted case index. It does not introduce a dynamic function registry or runtime reflection.
Every case still starts a fresh process from the same immutable suite executable. The runner stages that executable beside a new temporary project for each process, so runtime singletons, generated SQLite settings, Model initialization and data remain isolated without an in-process reset protocol. A suite that contains any database test links the SQLite-capable runtime once; only a selected database branch loads its generated setting and initializes Models. A non-database branch does neither.
Native cache identity continues to cover generated code, compiler identity, runtime bytes, toolchain, platform, environment and compiler options. Tests use a dedicated fast compilation profile because they validate behavior rather than production code generation performance; `run` and `build` retain the production optimization contract.
For each case the runner:
1. creates an owned temporary project directory;
2. reuses the single compiled suite and selects the checked private test entry by internal case index;
3. stages the executable in that directory;
4. writes a generated `config/setting.json` only when the reachable graph uses Model operations;
5. runs the executable with the temporary project as its executable/config root;
6. captures stdout/stderr and records pass/failure;
7. drops all owned native and runtime artifacts before continuing.
Successful process output is captured and omitted from runner status. Failed output is printed in clearly labelled blocks below the case result. A source/check failure prevents all execution; a per-case compile/start/run/assert/business/fault failure is recorded and later cases continue. The final exit status is nonzero when any case failed or was not run.
The first release has no child timeout or signal manager. Explicit cancellation may terminate the command; recoverable success/failure paths own deterministic cleanup.
## SQLite Isolation
The runner never loads the project's deployment database configuration for tests. If no test graph has a database effect, the suite uses the base runtime profile. A mixed suite links the SQLite-capable runtime once, but a selected non-database case writes no setting file and does not call database bootstrap or Model initialization.
If the graph has a database effect, the runner generates a temporary setting with:
- `database.default` as SQLite;
- one SQLite entry for each explicit Model connection name;
- paths below that case's `data/db/`;
- one connection and the existing bounded page defaults.
Package-root selectors retain runtime resolution semantics: an undeclared root falls back to `default`; explicit selectors retain separate connections. The generated settings are parsed by `dever_runtime::config::Settings`, validated by `Program::validate_database_settings`, and consumed by the normal bootstrap, schema migration, Seed, transaction and shutdown code. No test-only database handle reaches Dever source.
The emitter currently initializes the application's complete Model schema once a reachable test uses the database. This preserves foreign keys, migrations and Seed as an application-wide contract. A non-database test uses no database profile even when unrelated Models exist in the module.
## CLI And Formatting
`Action::Test` is added to the existing explicit CLI argument parser; there is no generic subcommand framework. The command performs semantic checking, normal warnings and API baseline validation before execution.
Formatting remains syntax-only. `fmt` discovers both source roots, prepares every replacement before committing any, validates both snapshots again, then performs atomic per-file renames. A missing `test/` contributes no files. This prevents module files from changing when a test Markdown contract or file permission fails.
Human output follows one stable shape:
```text
running 2 tests
test news/article/publish ... ok
test user/account/register ... FAILED
test result: FAILED. 1 passed; 1 failed; 0 not run
```
No JSON/JUnit output or test filter is introduced.
## Markdown And CMS
Test Markdown uses the existing strict document contract. Its package metadata names the logical package (for example `user.account.register`), and its function section documents the filename-matched entry. No test-specific Markdown headings are added.
The maintained CMS Dever and Markdown projects receive equivalent application tests. They call existing App boundaries and assert observable business values and Model counts. They do not add test-only production App functions or duplicate CRUD.
## Compatibility And Trade-offs
- Production `check/api/run/build` continue loading only `module/`; their checked Program and native cache inputs remain unchanged.
- `fmt` intentionally expands to the optional test root because tests are first-class Dever source.
- One suite link removes per-case native compilation. Process-per-case dispatch retains runtime/database isolation, while the suite may be larger than one reachability-pruned test binary.
- Real PostgreSQL application tests are deferred. Adding them later requires an explicit safe test connection and cleanup contract; it must not alter default SQLite behavior.
- There are no fixture hooks or shared test modules. A later addition must prove that it does not introduce hidden order or shared mutable state.
## Cleanup And Rollback
All test execution artifacts are owned by RAII temporary-directory and native-program guards. Cleanup errors do not replace the primary test result. No project source, deployment setting or data path is rewritten by `dever test`.
The change is additive. Rollback removes the Test source origin/layout, assertion lowering, CLI action and example tests; no production source migration or database migration is required.
@@ -1,11 +0,0 @@
{"file": ".trellis/tasks/09-19-application-testing/prd.md", "reason": "当前应用测试需求、边界与验收标准"}
{"file": ".trellis/tasks/09-19-application-testing/design.md", "reason": "已收口的编译器与运行器技术设计"}
{"file": ".trellis/tasks/09-19-application-testing/implement.md", "reason": "分阶段实施与最小验证清单"}
{"file": ".trellis/spec/backend/compiler-contracts.md", "reason": "保持语言、检查器、HIR 与原生后端单一路径合同"}
{"file": ".trellis/spec/backend/directory-structure.md", "reason": "约束源码目录、crate 与测试文件归属"}
{"file": ".trellis/spec/backend/error-handling.md", "reason": "约束源码诊断、运行失败与错误传播"}
{"file": ".trellis/spec/backend/quality-guidelines.md", "reason": "约束复用、复杂度与回归测试质量"}
{"file": ".trellis/spec/backend/toolchain-and-library.md", "reason": "约束 Rust 工具链、依赖与定向命令"}
{"file": ".trellis/spec/backend/database-guidelines.md", "reason": "约束 Model、SQLite 隔离与 PostgreSQL 边界"}
{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "避免测试路径形成平行 parser/checker/runtime"}
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "核对 CLI 到 checker/native/runtime 的完整数据流"}
@@ -1,123 +0,0 @@
# Dever 应用测试体系实施清单
## Start Gate
- [x] 用户在最终规划摘要之后明确批准实施。
- [x] 运行 `python3 ./.trellis/scripts/task.py start 09-19-application-testing`,确认任务进入 `in_progress`。
- [x] 重新加载 `trellis-before-dev`,核对当前 PRD、设计、执行清单和 backend specs。
- [x] 开工前记录实际 git/worktree 状态,只处理本任务拥有的文件,不清理用户已有改动。
## Phase 1: Test Source Layout And Static Contract
- [x] 在 `source.rs` 增加编译器赋予的 Test 来源、显示/逻辑路径和组合项目加载;保持现有生产 `SourceMap::load` 合同。
- [x] 在 layout/parser/checker 中加入严格的 `<component>/<domain>/<topic>` Test 布局,不增加源码关键字或第二套 parser。
- [x] 在检查结果中建立按身份排序的 `TestCase`,强制同名 ordinary 零输入零输出入口和文件内私有辅助声明。
- [x] 扩展 role-aware function/type resolution:同领域 App+Domain、跨领域 App、禁止 Model CRUD/Adapter/Port/API/其他测试。
- [x] 把来源/逻辑路径加入 check cache identity,防止 Test 与普通 strict source 误复用。
- [x] 增加路径、入口、重复身份和可见性的定向正反测试。
## Phase 2: Assertions And Native Entry
- [x] 复用 `Intrinsic` HIR 形状加入仅 Test owner 可构造的 `assert`/`assert_eq`,保持普通同名业务函数语义不变。
- [x] `assert` 强制 Bool;`assert_eq` 强制相同 comparable 类型并保持左右各求值一次、源顺序不变。
- [x] 在 native emitter 生成源码定位的 assertion failure,复用现有 `Render` 输出 actual/expected。
- [x] 增加从 checked `TestCase` 编译私有入口的 native API,不放宽生产 `Program::entry` 的公开性要求。
- [x] 验证标量、record、choice、nullable、List、Map、MapEntry、Model ID,以及不可比较/类型不匹配/生产源码调用断言的拒绝。
## Phase 3: CLI Runner And Isolation
- [x] 在 CLI 增加 `test <project-root>`,复用现有 module 检查、warning 和 API baseline 路径。
- [x] 建立单一 `test_runner` owner,负责排序、单套件 compile、逐用例独立进程、输出捕获、状态汇总和临时目录生命周期;不创建通用 runner/framework 层。
- [x] 无测试时输出 `0 tests` 成功;源码检查失败时零执行;单用例失败后继续后续用例。
- [x] 对无数据库 effect 的用例使用 base profile,不读写 setting。
- [x] 对有数据库 effect 的用例生成临时 SQLite setting,复用正式 Settings 校验、Model schema/Seed/transaction/shutdown。
- [x] 每用例使用独立临时项目和数据库;确认项目 `config/setting.json`、`data/` 与环境变量不参与数据库选择。
- [x] 失败时分区展示捕获的 stdout/stderr,runner 状态行不与测试输出混合。
- [x] 覆盖编译失败、启动失败错误映射、断言失败、未捕获业务失败、runtime fault 和清理。
## Phase 4: Formatter And Maintained CMS
- [x] 调整 CLI formatter,使 module/test 两个可选源码集合先完整诊断和 prepare,再统一 commit;任一失败时零改写。
- [x] 为 CMS Dever 项目增加 `test/<component>/<domain>/<topic>.dever` 业务用例。
- [x] 为 CMS Markdown 项目增加相同路径、入口和合同的 `.dever.md` 用例,遵守现有 Markdown 文档协议。
- [x] 删除 CMS CLI 回归中被正式应用测试替代的临时 probe 源码注入,仅保留仍验证 build/package 的独立职责。
- [x] 更新 `LANGUAGE.md`、`MARKDOWN-SYNTAX.md`、根 README 和 examples README 的正式测试合同与命令。
## Phase 5: Refactor And Verification
- [x] 检查 SourceMap 双根加载、test metadata、assert lowering、runner 报告和 temp ownership 是否存在重复 owner 或平行逻辑。
- [x] 搜索并删除调试输出、过渡 API、无用 helper、死分支、临时测试文件和过期 CMS probe。
- [x] 确认生产 source set、API snapshot、main entry、native cache、runtime profile 与无测试项目行为不变。
- [x] 检查磁盘空间后再运行会链接原生程序的定向测试;不运行 workspace 全量测试、真实 PostgreSQL 或性能压测。
## Verification Record
- `cargo check --offline -p dever-core --features reference -p dever-cli -p dever-tests --test application_testing`:通过。
- `application_testing`:8/8 通过;包含 plain/Markdown 发现、可见性、类型、`result(call)`、复合断言、源码位置及含未引用 Model 的 base profile。
- `formatter`:12/12 通过;module/test 统一 prepare 和只读失败零改写通过;两套 CMS `fmt --check` 通过。
- `source_architecture`:8/8 通过;`markdown_source`:20/20 通过。
- `source_visibility`:10/10 通过;`contract_execution`:9/9 通过;`api_routes`(`api` feature):7/7 通过。
- `cms_project_checks_runs_and_builds_from_an_isolated_copy`:通过;Dever/Markdown 各连续测试两次,部署 setting 被替换为非法 JSON 时测试仍为 2/2,通过后 check/run/build/打包程序仍通过,且无效 test 源码不进入生产命令。
- CLI 零测试、失败继续/输出分区、成功/失败临时目录清理:分别通过;`RUSTC=/bin/false` 定向故障注入确认两个编译失败均汇总且零临时残留。
- 未捕获业务失败和动态数值 fault 的既有原生定向用例:通过。
- `git diff --check` 和变更文件尾随空白扫描:通过;CLI 默认依赖仅启用 `dever-core` default feature,不含 reference。
- `rustfmt`、`clippy` 组件未安装,未运行;未运行 workspace 全量测试、真实 PostgreSQL、性能/内存压测或持久服务。
## Phase 6: Test Suite Compilation Performance
- [x] 在 native owner 中生成一个包含全部 checked TestCase specialization 的测试套件,不增加解释器或动态注册表。
- [x] 复用 native 构建缓存并使用测试专用快速编译参数;生产 `run/build` 优化参数保持不变。
- [x] CLI 只编译一次套件,按稳定用例编号逐个启动独立进程并保留失败继续、输出捕获和临时目录清理。
- [x] 混合数据库/非数据库套件仅在数据库分支加载临时 SQLite 配置并初始化 Model。
- [x] 增加多入口分派、一次编译、缓存命中、配置隔离和失败继续的定向回归。
- [x] 记录 CMS 冷/暖执行耗时和峰值 RSS;不把本机数据外推为生产性能。
### Phase 6 Verification Record
- `cargo check --offline -p dever-core -p dever-cli -p dever-tests --test application_testing --test native_cache`:通过。
- `application_testing`:8/8 通过;一个 native suite artifact 按两个稳定编号执行成功/失败入口,并确认第二次编译命中 native cache。
- `native_cache`:6/6 通过,另有 1 个既有显式 ignored;runtime 内容变化、工具链/参数变化、并发发布、损坏产物拒绝、原始快照与元数据摘要备忘均通过。
- CLI 定向回归:混合数据库/非数据库套件 3/3 通过;失败后继续与 stdout/stderr 捕获通过;零测试不调用编译并成功;套件编译失败时 2/2 用例统一标记失败并清理临时目录。
- 普通单入口 native hello 定向用例通过,确认 `run/build` 共用的生产发射路径未被套件抽取破坏。
- CMS Dever 实际 `deverc test`:2/2 通过。稳定 runtime 下首次单套件链接为 21.37 秒、峰值 346068 KiB;随后内容缓存命中为 1.79 秒、峰值 54132 KiB。优化前同项目为 119.40 秒、594772 KiB。
- 在 Cargo 测试环境的绝对 `RUSTC` 与普通 PATH `rustc` 之间切换会触发 Cargo 重建 native runtime;一次 75.56 秒、594976 KiB 的观测包含该重建,不作为测试套件链接基线。
- runtime 摘要目录只保留当前元数据身份;`/tmp` 无 `dever-application-test-*`、`dever-build-*` 或 `dever-cms-project-*` 残留,CMS 示例无数据库或测试可执行文件残留。
- `git diff --check` 与变更文件尾随空白扫描通过。`rustfmt`、`clippy` 组件仍未安装;未运行 workspace 全量测试、真实 PostgreSQL、64/128 MiB/cgroup 压测或持久服务。
## Focused Validation
允许且计划执行的最小检查:
```bash
cargo check --offline -p dever-core -p dever-cli
cargo test --offline -p dever-tests --test application_testing
cargo test --offline -p dever-tests --test source_architecture
cargo test --offline -p dever-tests --test markdown_source
cargo test --offline -p dever-cli --test cms_project
git diff --check
```
如 `rustfmt`/`clippy` 组件可用,再执行:
```bash
cargo fmt --all -- --check
cargo clippy --offline -p dever-core -p dever-runtime -p dever-cli -p dever-tests --test application_testing -- -D warnings
```
不运行:
- workspace 全量测试;
- `cargo build` 或应用全量发布构建;
- 真实 PostgreSQL;
- 64/128 MiB、cgroup 或吞吐压测;
- 持久 HTTP/网络服务。
## High-risk Files And Review Points
- `crates/dever-core/src/source.rs`、`check.rs`、`check/layout.rs`、`check/symbols.rs`:来源身份和可见性不能泄漏到生产。
- `crates/dever-core/src/check/expressions.rs`、`intrinsic.rs`、`native.rs`、`native/intrinsics.rs`、`native/records.rs`:断言必须静态类型化、单次求值并保留源码位置。
- `crates/dever-core/src/native/build.rs`、`model.rs`:私有测试入口不能放宽生产 entry,SQLite profile 必须来自测试生成配置。
- `crates/dever-cli/src/main.rs`、`format.rs`、新增 runner:临时目录、输出捕获、失败继续和格式化原子性。
- `examples/cms/{dever,md}`:两套测试合同必须同步,不给生产 App 增加测试专用能力。
任何实现如果需要 Test 关键字、共享动态值、生产公开测试函数、环境变量数据库配置或真实 PostgreSQL,均视为超出当前设计,回到 planning 复核。
@@ -1,101 +0,0 @@
# Dever 应用测试体系
## Goal
让 Dever 应用可以用 Dever 源码编写和运行确定性的领域回归测试,使 AI 修改 App、Domain 或 Model 后能够验证业务合同,而不依赖 Rust 宿主测试、HTTP 接口或正式数据库。
测试是可选能力。没有测试的项目保持现有开发和运行方式;测试能力不得引入第二套解释器、生产可见测试入口或环境变量配置。
## Background
- 当前 CLI 只有 `check`、`api`、`fmt`、`run` 和 `build`,应用自身没有测试命令。
- 编译器仓库已有 Rust 回归测试,但它们验证编译器实现,不能表达具体 Dever 应用的业务合同。
- 生产源码从 `module/` 加载,`main.main` 是 `run/build` 的唯一启动入口;App 是跨领域能力边界,Domain、Model、Port、Adapter 和 API 是领域实现边界。
- parser、checker、HIR、原生后端、源码位置、错误传播、Model 迁移和 Seed 已有单一正式路径,应由测试复用。
- ORM 的真实 PostgreSQL 验收已经由独立编译器测试覆盖,不属于首版应用测试命令。
## Requirements
### Source And Discovery
- 应用测试固定放在项目根 `test/`,与 `module/` 生产源码分离。
- 测试路径必须是 `test/<component>/<domain>/<topic>.dever` 或 `test/<component>/<domain>/<topic>.dever.md`,不支持更深层级。
- 每个测试文件由文件名选择同名普通函数作为唯一入口。例如 `test/user/account/register.dever` 执行其中的 `register() ()`。
- 测试入口必须是零输入、零输出的 ordinary 函数。缺少同名函数、同名入口签名错误或重复测试身份必须在执行前产生源码诊断。
- 测试文件中的其他函数和类型只属于该文件,不自动执行,也不能被其他测试文件或生产源码调用。
- 不增加 `test` 关键字、`test_` 前缀、每文件 `main()`、package/expose 声明或顶层可执行语句。
- `dever test <project-root>` 同时加载生产源码和测试源码,并在运行任何用例前完成全部语法、名称、类型、分句、effect、失败义务和架构检查。
- 发现顺序按测试身份稳定排序,不依赖文件系统遍历顺序。
- 项目没有 `test/` 或目录为空时,合法项目执行 `dever test` 输出 `0 tests` 并成功退出。
- `check/api/run/build` 不加载测试源码。测试声明不进入生产二进制、API 快照或 HTTP 路由。
- `fmt` 作为源码工具应同时格式化 `module/` 和存在的 `test/`,保持两者统一的 `.dever`/`.dever.md` 格式合同;任一源码失败时不得部分改写。
### Architecture And Visibility
- 测试文件是所属 `<component>/<domain>` 的测试友元,可以调用同领域 App 和 Domain。
- 跨领域调用只能经过目标 App。
- 测试可以读取 App 返回的 Model 值和同领域类型,但不能直接调用 Model CRUD、Adapter、Port 或 API。
- 生产 App、Domain 和其他测试文件都不能反向调用测试函数或访问测试私有类型。
- 测试不能声明 Model、HTTP 路由或生产 App 能力。
- 测试文件必须表达一个内聚业务场景;不建立 `common`、`shared`、`utils`、`helper`、全局 fixture 等测试垃圾桶。
### Assertions And Failures
- 首版只提供测试源码专用的 `assert(condition)` 和 `assert_eq(actual, expected)`。
- `assert` 只接受 `Bool`。`assert_eq` 要求两侧是相同且可比较的类型,不做数值提升、字符串求值或运行时反射。
- `assert_eq` 复用现有比较与渲染合同,支持标量、record、choice、可空值、List、Map、MapEntry 和 Model ID;失败报告测试身份、源码位置、实际值和期望值。
- 不增加 `assert_true`、`assert_false`、`assert_not_equal`、`assert_null`、异常断言或自定义 matcher。
- 业务失败继续使用现有 `result(call)` 和 choice 分句验证;不增加 `try/catch/throws`。
- 测试成功表示入口正常完成;断言失败、未捕获业务失败或程序 fault 表示该用例失败。
- 一个用例失败后继续运行其余独立用例,最终汇总通过、失败和未运行数量,并以非零状态结束。
### Isolation And Lifecycle
- 首版没有 `before/after`、全局 setup、共享 fixture、用例排序依赖或测试间共享状态。
- 每个测试文件在独立原生进程和独立临时项目目录中运行。
- 不触达 Model 的测试不初始化数据库。
- 触达 Model 的测试由运行器生成每用例独立的临时 SQLite 配置,走正式配置解析、迁移、Seed、事务、连接池和 shutdown 路径。
- 测试运行器不读取、复制或连接项目正式数据库 URL;不使用环境变量或命令行数据库覆盖。
- 临时数据库、数据目录、配置、可执行文件和运行产物在成功或失败后由运行器清理,不写入项目 `data/`。
- 首版不使用外层事务回滚代替隔离,因为这会改变被测 App 的真实提交和回滚语义。
- 测试按稳定顺序串行运行;并行、随机顺序、重试和自动超时不属于首版。
### CLI And Reporting
- 新增正式命令 `dever test <project-root>`;当前开发二进制对应 `deverc test <project-root>`。
- 默认输出简洁的人类可读状态和最终汇总。测试进程 stdout/stderr 被捕获,不得冒充 runner 状态;失败时按独立区块展示。
- 源码诊断、原生编译失败、进程启动失败、断言失败、业务失败和 runtime fault 保留各自可辨认的信息。
- 测试复用与 `run/build` 相同的原生后端、runtime feature 裁剪、源码位置和构建缓存,不链接 reference evaluator。
- 首版不增加测试名过滤、机器报告格式或新的数据库配置结构。
### Maintained Example
- CMS 的 Dever 与 Markdown 项目各自增加等价测试,验证账户规范化、Model 写入/Seed 和文章发布等现有 App 合同。
- 两套测试使用相同路径、入口名称和断言,只因源码容器格式不同而不同。
- 旧 `examples/old/` 不迁移为应用测试模板。
## Acceptance Criteria
- [x] `.dever` 与 `.dever.md` 测试均能按 `<component>/<domain>/<topic>` 稳定发现,文件名选择同名零输入、零输出函数。
- [x] 非法路径、缺失/错误入口、重复身份、测试跨域越权、直接 Model/API 调用和生产源码反向访问测试均在执行前被拒绝并定位源码。
- [x] `assert` 和 `assert_eq` 有正反类型检查;相等失败包含测试身份、原源码位置、实际值和期望值。
- [x] 测试可以通过 `result(call)` 验证业务失败;未捕获业务失败、断言失败和 runtime fault 均使当前用例失败而不阻止后续用例。
- [x] 无 `test/` 的合法项目执行 `dever test` 报告 `0 tests` 并成功;非法生产源码仍被拒绝。
- [x] 纯非数据库套件使用 base runtime;混合套件的非数据库用例不读取配置或初始化数据库;使用 Model 的两个测试各自执行迁移和 Seed,写入互不影响,顺序互换结果不变。
- [x] 测试不读取正式 `setting.json` 的数据库 URL,不连接 PostgreSQL,不在项目 `data/` 或源码目录留下临时文件。
- [x] 测试进程成功和失败后均清理临时配置、SQLite 文件、可执行文件和 native 临时目录。
- [x] `check/api/run/build` 的源码集合、API 快照、CMS 运行结果和生产产物不因存在 `test/` 而变化。
- [x] `fmt` 对 module/test 的变更采用同一次准备后提交;任一格式诊断或准备失败时两处源码均保持原样。
- [x] CMS Dever/Markdown 测试覆盖相同业务合同并由 `dever test` 通过。
- [x] 同一项目连续运行两次时测试顺序和汇总一致,生成测试产物不链接 reference evaluator。
- [x] 一次 `dever test` 只生成和编译一个测试套件程序;每个用例仍在独立子进程和临时项目中运行。
- [x] 测试套件复用 native 内容寻址缓存;未改变有效生成代码时再次运行不调用 rustc 编译测试程序。
- [x] 混合数据库/非数据库用例时,只有数据库用例进程读取运行器生成的 SQLite 配置并初始化 Model;非数据库用例不读取配置或初始化数据库。
## Out Of Scope
- 真实 PostgreSQL 应用测试配置、schema 管理和外部数据库清理;继续由现有 `postgres_orm` 专项验收负责。
- 自动 `before/after`、共享 fixture 文件、mock/stub、Port/Adapter 替身和依赖注入协议。
- 测试筛选、watch、并行、随机顺序、重试、自动超时、覆盖率、快照、属性测试和模糊测试。
- 浏览器/UI、HTTP 录制回放、外部服务沙箱和机器可读报告。
- API 新设计、本体、智能体、package registry、跨平台发行和编译器自举。
@@ -1,28 +0,0 @@
{
"id": "application-testing",
"name": "application-testing",
"title": "Dever 应用测试体系",
"description": "为 Dever 应用建立根 test 目录、dever test 命令、类型安全断言、隔离 fixture 与可重复执行合同",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-19",
"completedAt": null,
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-04-dever-language-mvp",
"relatedFiles": [],
"notes": "",
"meta": {
"execution_mode": "inline"
}
}
@@ -1,6 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused runtime/database/process checks"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Retry, dead-letter and cancellation error behavior"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Job owner separation and no parallel queue implementation"}
{"file": ".trellis/tasks/09-20-durable-jobs/research/implementation-map.md", "reason": "Verify corrected transaction, lease, cron and shutdown contracts"}
{"file": ".trellis/spec/backend/compiler-contracts.md", "reason": "Verify language and native emitter contract parity"}
{"file": ".trellis/spec/backend/database-guidelines.md", "reason": "Verify transaction and lease correctness"}
-100
View File
@@ -1,100 +0,0 @@
# Dever 持久化 Job 设计
## Source Contract
Job role 使用 `job.dever` 或平铺的 `job/*.dever`,继续遵守角色文件与目录互斥、单 topic 必须合并的规则。Job 只是持久任务入口,不是可调用的普通函数:
```dever
database default
job publish_due(input: app.PublishInput) () retry(5) timeout(30000) {
app.publish_due(input.article_id)
}
job cleanup_sessions() () retry(3) timeout(30000) {
app.cleanup_expired()
}
schedule cleanup_sessions = "0 * * * *"
```
Payload record 由同领域 App 声明为公开契约,Job 不新增第二套 DTO 可见性。Job 只能有零个输入,或一个 wire-safe record 输入;输出必须为空;`retry(n)` 表示包含首次执行在内最多执行 `n` 次。`timeout(ms)` 和 `retry` 都是必填、有硬上限的正整数。
共享 wire schema 增加 nominal ModelId 节点。编码仍使用既有 ModelId 数值表示,但 fingerprint 包含所属 Model identity,不能把两个 Model 的 id 降级为同一种 Int/Id。Job payload 仍拒绝 Secret、private Model data、Choice、handler 和不稳定类型。
App 通过 compiler-owned 静态 target 入队:
```dever
id = dever.job.enqueue(job.publish_due, input, key)
id = dever.job.enqueue_at(job.publish_due, input, key, run_at)
id = dever.job.enqueue(job.cleanup_sessions, key)
id = dever.job.enqueue_at(job.cleanup_sessions, key, run_at)
```
Target 不是普通值,不能存储、转发或用于普通 call/run/group。enqueue 使用专用 HIR,不把 Job handler 加入同步调用/effect/递归图;Job handler 作为 worker 独立 root 生成并检查。
## Effect And Transaction Integration
Model 和 Job 都映射到一个统一 database binding。effect 系统记录有类型的 database owner,并通过唯一 accessor 解析为现有 `ConnectionSelector`;Model CRUD 仍保留 Model schema owner,不制造虚假 Model,也不增加第二套 transaction context。
enqueue 只传播队列连接的 database/suspension/failure effect,不同步传播 Job handler 的 Model、Port 或业务 failure。transaction App 的 enqueue 使用现有隐藏 `_database: Option<&Transaction>` 和 `Executor::new`,与同连接 Model 写入共享物理 transaction;无 ambient transaction 时开启并提交短 transaction。显式不同连接在语义检查拒绝,依赖 default/package fallback 的冲突在 settings-aware run/build bootstrap 拒绝。
## Private Schema And States
每个使用 Job 的逻辑 database 初始化 runtime-owned versioned schema,独立于 Model history、snapshot、seed 和 migration。至少包含:
- Job:id、target identity、schema fingerprint、payload、dedupe key、state、run_at、attempt、max attempts、timeout、claim token、lease deadline、安全错误分类/摘要和时间戳。
- Schedule cursor:稳定 schedule identity、cron fingerprint、last processed UTC minute 和更新时间。
状态为 pending、running、succeeded、dead、blocked。业务 key 只在 pending/running 上保证 `(target, key)` 唯一,终结后可重用;schedule occurrence 由持久 cursor 保证永久单窗口物化一次,不能依赖 active-only 唯一索引。
未知 target、schema mismatch 和 corrupt payload 不执行 App,保留原记录并进入 blocked。历史记录的实际 timeout 也必须满足当前 SQLite lease 约束,否则以固定 `invalid_policy` 进入 blocked。attempt 耗尽进入 dead,并由独立有界清理处理,不占用领取可执行任务的槽。运行时不保存 Secret、完整 payload、backtrace 或未经控制的 `AppError::to_string()`;只保存固定类别和有界安全摘要。
## Claim, Lease And Retry
PostgreSQL 使用短 transaction、bounded batch 和 `FOR UPDATE SKIP LOCKED`;SQLite 使用短 `BEGIN IMMEDIATE` 与条件 update。只按当前空闲执行槽 claim,不能预占后在无界内存队列等待。
每次成功 claim 时原子增加 attempt,并生成独立随机 claim token。这样进程在 handler 返回前崩溃也会消耗一次 attempt。过期 running 在 attempt 未达上限时可重领,已达上限时转 dead。
renew、success、retry、blocked/dead 更新均使用 `id + claim token + running state + 未过期 lease` CAS。claim 在获得写锁后读取时钟并建立租约;renew/complete 先锁定 claim 行,再读取同一 Clock,不能用锁等待前的时间续租或确认已过期任务。CAS 失败表示已失去所有权,旧执行者不得覆盖新 owner 状态。worker 在 lease 周期内续租;handler timeout 或安全业务失败按 checked exponential backoff 重新 pending,且不超过配置上限。语义为 at-least-once,外部副作用仍由业务 idempotency 保证。
## UTC Schedule
Cron 固定为五字段 UTC:minute、hour、day-of-month、month、day-of-week。支持 `*`、单值、逗号列表、闭区间和步长;范围分别是 `0..59`、`0..23`、`1..31`、`1..12`、`0..6`,其中 Sunday 为 0。day-of-month 与 day-of-week 同时受限时采用 cron 的 OR 语义,否则匹配受限字段。
首次启用从当前 UTC minute 建 cursor,不回放历史。以后在同一 transaction 锁定 cursor、插入 occurrence、推进 cursor;每轮最多扫描并推进 256 个分钟槽,只对匹配 cron 的槽生成任务,剩余分钟后续继续补。slot key 包含稳定 schedule identity 和 UTC minute,与业务 key 分离。相同 schedule identity 的 cron fingerprint 不一致时启动失败关闭,禁止多版本互相改写 cursor。
## Runtime Topology And Shutdown
严格配置只来自 `config/setting.json`:
```json
{
"runtime": { "mode": "all", "shutdown_ms": 30000 },
"job": {
"workers": 4,
"poll_ms": 250,
"lease_ms": 60000,
"retry_base_ms": 1000,
"retry_max_ms": 300000
}
}
```
`runtime.mode` 只能为 api、worker、all。generated entry 在 listen、schema materialize 或 claim 前统一校验 source capabilities、HTTP/Job 配置、driver 和所有上限。api 模式中的 `dever.job.serve()`、worker 模式中的 `dever.api.serve()`安全 no-op;同一 binary 保留两套静态 dispatch roots,不按运行模式裁剪。
没有 worker capability 的纯 API 应用可省略 runtime,默认 api 和 30000 ms;有 worker capability 时必须显式配置。worker 连接池必须比 worker 槽至少多一个连接,供 claim/heartbeat 使用;不引入独立控制连接池。SQLite 只有一个写者,额外连接无法绕过业务写事务,因此启动前还必须验证 `lease_ms > Job timeout_ms`,让租约覆盖 handler 的最长执行时间;PostgreSQL 可继续使用较短租约并续租。
generated entry 拥有唯一 lifecycle:结构化等待应用 root 与 SIGTERM/Ctrl-C。收到信号后广播 shutdown;API 关闭 listener 并复用 HTTP connection drain,Job 停止 schedule/claim 后等待 active handlers。到 `runtime.shutdown_ms` 才取消剩余作用域;未确认 success 的 Job 保留/恢复 lease。应用 root 汇合后执行 database shutdown,再 flush log。Tokio signal 是既有依赖的 feature 扩展,不引入 signal/queue/cron 框架或 unsafe handler。
Job timeout 上限为 3,600,000 ms,lease 的有界范围为 100..7,200,000 ms。测试使用 lease 上限,保证所有合法 Job timeout 都能在隔离 SQLite 中执行,仍复用生产 claim/dispatch/finish 和策略检查。
## Application Testing
每个 test case 保持独立 native module、进程、临时目录、Port fake graph 和 SQLite connections。闭包同时包含:test 直接 enqueue 的 Job、Job handler 调用的 App/Port,以及 handler 的 Model connections;生产 Adapter 和部署 settings 不得回退进入 case。
测试专用 compiler-owned 操作提供 case-local clock advance 和 bounded due-job drain。它们复用生产 claim/dispatch/finish 状态机,不启动永久 `serve()`,不真实 sleep,也不允许测试直接调用 Job entry。无 database/Job effect 的纯测试仍不加载 settings。
## Explicit Non-Goals
不增加动态 target string、通用 DI/queue framework、外部 broker、非 UTC schedule、exactly-once 外部副作用、payload 自动迁移或 Job 管理 API/front。真实 PostgreSQL 并发、锁与取消仍需隔离 fixture 验收;feature check 不能替代运行证据。
@@ -1,5 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Source role, static target, effect and visibility contracts"}
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Transaction, connection, migration and cancellation invariants"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Structured concurrency, native runtime and shutdown"}
{"file":".trellis/tasks/09-13-typed-orm/design.md","reason":"Existing SQLite/PostgreSQL transaction and pool architecture"}
{"file": ".trellis/tasks/09-20-durable-jobs/research/implementation-map.md", "reason": "Verified source ownership and implementation risks"}
@@ -1,127 +0,0 @@
# Dever 持久化 Job 实施计划
## Start Gate
- [x] time-typed-codec 与 port-adapter 完成。
- [x] 用户批准父任务规划并允许继续实现。
- [x] 读取 structured concurrency、ORM transaction/settings、native profile、application test runner 和 HTTP drain owner。
- [x] 完成源码 implementation map 与独立设计复核。
- [x] 收紧 ModelId、connection effect、claim attempt、cron cursor、lifecycle 和测试 harness 契约。
## 1. Source And Static Contract
- [x] 增加 Job SourceRole、layout、contextual declaration、policy、schedule、formatter 和 Markdown parity。
- [x] 增加 Program Job metadata、静态 target identity、App-owned payload schema 和 nominal ModelId wire codec。
- [x] 增加 enqueue/enqueue_at 专用检查与 HIR;拒绝普通 call、handler/run/group 转发和非法 role/type/signature。
- [x] 将 Job handler 作为独立生成 root,不把 enqueue 视为同步依赖或递归 edge。
Gate:新增 `durable_jobs` 源码/Markdown测试覆盖合法声明、路径、签名、可见性、payload、policy、cron和target正反例。
## 2. Connection Effect And Atomic Enqueue
- [x] 将 Model-only database effect 推广为统一的有类型 connection owner,并集中解析 ConnectionSelector。
- [x] enqueue 只传播队列连接 effect,不传播 handler 业务 effect/failure。
- [x] native emitter 复用 `_database`、Executor 和 wire codec,实现 standalone short transaction 与 ambient transaction enqueue。
- [x] settings-aware bootstrap/test runner 使用统一 connection effect;拒绝解析后的跨连接 transaction。
Gate:enqueue-only transaction 可生成;Model write + enqueue 同时 commit/rollback;跨连接拒绝;同 transaction 不二次 checkout。
## 3. Private Store State Machine
- [x] 实现 SQLite/PostgreSQL runtime private schema、版本校验和初始化锁,不进入 Model history。
- [x] 实现 active-key 原子幂等 enqueue、run-at、bounded row/value limits。
- [x] 实现 bounded claim、claim-time attempt、随机 token、lease renew/expiry 与 owner CAS。
- [x] 实现 success、retry/backoff、dead、blocked 及受控错误分类/摘要。
Gate:SQLite temp DB 覆盖并发去重、终结后 key 重用、到期、双 claimant、崩溃耗尽、过期重领、旧 token 拒绝和 schema/decode/target blocked;PostgreSQL 验证 feature/SQL 生成。
## 4. Schedule, Worker And Test Harness
- [x] 实现共享五字段 UTC cron parser、匹配与 bounded next-slot 搜索(每轮扫描最多 256 个分钟槽,不做无界寻找)。
- [x] 实现持久 cursor、同 transaction occurrence materialize/advance、首次启用和每轮最多 256 个补偿窗口。
- [x] 实现 bounded worker pool、timeout、heartbeat 和静态 payload dispatch。
- [x] 扩展 application test closed graph、case-local clock 和 bounded due-job drain,复用生产状态机。
Gate:多 scheduler 同 slot、completed occurrence 不重建、cursor rollback/restart、cron fingerprint 冲突和补偿上限;不同 case fake/clock/database 不泄漏且不读取部署配置。
## 5. Mode And Lifecycle
- [x] 增加严格 runtime/job settings、source capability 预检和 runtime profile feature/cache identity。
- [x] 实现 api/worker/all gating,保留同 binary 的 API/Job dispatch roots。
- [x] 增加结构化 SIGTERM/Ctrl-C owner;API stop-accept/drain 与 Job stop-claim/drain 共享 deadline。
- [x] generated entry 在应用汇合后统一 database shutdown、log flush,覆盖启动失败和超时取消。
Gate:缺失/非法配置在 listen/claim 前失败;三 mode 只启动应有服务;test-owned 进程验证信号后停止新工作、drain/超时取消和关闭顺序。
## 6. Documentation And Focused Verification
- [x] 更新 LANGUAGE、MARKDOWN-SYNTAX、compiler/database/toolchain 中的 application-testing 契约。
- [x] 检查无环境变量入口、动态 target、第二套 codec/transaction/pool、脱管 task 或生产 Adapter 测试回退。
- [x] 完成独立 Trellis check,修复 in-scope findings。
- [x] 主代理最终复核并更新父任务 checklist。
```bash
cargo check --locked --offline -p dever-core -p dever-runtime -p dever-cli
cargo check --locked --offline -p dever-runtime --features sqlite,postgres
cargo test --locked --offline -p dever-tests --test durable_jobs
cargo test --locked --offline -p dever-tests --test source_architecture
cargo test --locked --offline -p dever-tests --test error_effects
cargo test --locked --offline -p dever-tests --test orm_query
cargo test --locked --offline -p dever-tests --test sqlite_orm
cargo test --locked --offline -p dever-tests --test application_config
cargo test --locked --offline -p dever-tests --test application_testing
cargo test --locked --offline -p dever-tests --test structured_concurrency
cargo test --locked --offline -p dever-tests --test pooled_network graceful_shutdown
git diff --check
```
所有 Cargo 命令使用无增量、debug info 关闭和串行测试。默认不运行 workspace 全量测试、真实性能套件或持久服务。真实 PostgreSQL 仅在仓库隔离 `config/setting.json` fixture 明确存在且用户知情时运行;否则明确记录为未运行。
## 实施记录
- 源码、共享 wire/connection owner、生产 store/worker、native/test roots、mode/lifecycle 已接通;以上勾选表示实现完成,最终 Gate 证据记录在下方,不代替独立 Trellis check。
- 启用既有 Tokio `signal` feature;经主代理明确授权解析依赖,Cargo.lock 仅新增 `errno 0.3.14` 与 `signal-hook-registry 1.4.8` 两个传递项。后续命令均 `--locked --offline`。
- API-only 保留既有省略 runtime 配置的兼容行为(api、30000 ms);有 worker capability 的应用必须显式配置 runtime。worker pool 比执行槽至少多一个连接,以免 handler 事务耗尽 heartbeat/claim 容量。
- enqueue 使用单条 active-key upsert + RETURNING,避免 PostgreSQL 上冲突 insert 后、查询 Id 前任务终结的竞态。
- 新增测试发现 case 裁剪不可达 transaction 后被误报空事务;修复共同 effects validator,真实空事务仍有 clause 并继续被拒绝。
- 数据库服务、真实 PostgreSQL、全量测试、性能/内存压测未运行;rustfmt/clippy 未安装,不安装。
### 最终实施验证
- `cargo check --locked --offline -p dever-core -p dever-runtime -p dever-cli`:通过。
- 同一 check 加 `--features dever-runtime/sqlite,dever-runtime/postgres,dever-runtime/api,dever-core/reference`:通过。PostgreSQL 仅 feature 编译证据,未连接真实数据库,不把它算作锁、并发或 SQL 执行验收。
- `durable_jobs`(sqlite):14/14。覆盖源码/Markdown、跨连接拒绝、nominal ModelId native 往返、同物理事务 commit/rollback、active-key 并发幂等、定时到期、claim-time attempt、旧 token CAS、崩溃耗尽、schema/corrupt/unknown 的 generated dispatch blocked、多 scheduler、完成窗口不重建、cursor 中途失败回滚/重启、256 窗口上限、case-local SQLite/clock/fake 与无数据库 empty drain。
- 同一 `durable_jobs` 目标包含真实 native 二进制的 api/worker/all 三 mode:临时回环监听和临时 SQLite;api 不 claim、worker 不监听、all 同时工作,均 SIGTERM 正常退出。独立 runtime 子进程另覆盖 heartbeat、停止 claim、在途排空与 shutdown deadline 取消后关闭数据库/flush 日志。
- `application_config`:3/3;`application_testing`:8/8;`source_architecture`:8/8。
- `pooled_network graceful_shutdown`:1/1,验证已有 HTTP 停止 accept 并完成在途响应。
- `time_codec wire_schemas_do_not_change_model_time_storage_identity`:1/1。该条旧断言随 nominal ModelId wire 合同更新;无 private/bounded 字段的 Model record 也可通过共享 codec,存储 snapshot 不变。该 fixture 已调整为当前 App 路径,未顺带改造其余旧测试。
- 上述测试均串行;`CARGO_INCREMENTAL=0`,dev/test debug info 为 0,固定 stable toolchain PATH。`git diff --check` 通过;新 Job/lifecycle 源码检查未发现环境变量读取、动态 target、脱管 spawn 或 unsafe。
- 独立 Trellis check 结果见下方;父任务 checklist 由主代理收口,任务仍保持 `in_progress`。
## 独立检查与修复
- 复现 SQLite handler 持写事务时短租约无法续租:业务已提交,但任务仍为 running。当前源码的启动预检要求 SQLite `lease_ms > timeout_ms`;PostgreSQL 继续支持短租约续租。领取在获得锁后建立 lease,续租/完成先锁 claim 行再读注入 Clock,避免使用锁等待前的旧时间。
- worker 在调用业务前重新验证持久化记录的实际 timeout/max attempts;旧策略不满足当前 SQLite lease 或已越界时进入 `blocked/invalid_policy`,不执行 App。公共 timeout 上限仍为 3,600,000 ms,lease 上限为 7,200,000 ms;测试使用最大 lease,合法长任务不会因测试固定短租约被误拒绝。
- 耗尽 attempts 的到期记录改为同一 claim 事务内独立有界清理;eligible 查询跳过耗尽记录,避免它占据执行槽并使 drain 误判队列为空。
- 信号测试原来对同一 run_at 的任务假定按业务 key 执行,但实际稳定排序使用 run_at/UUID;同毫秒 UUID 带随机部分。改用不同到期时间表达本测试的执行次序,同时保留实际 heartbeat、持事务 drain 和超时取消检查。
- 保持既有已审阅的 API-only 默认配置契约;有 worker capability 时 runtime 必填。统一 cron 文档为每轮扫描/推进最多 256 个分钟槽,只有匹配 cron 的槽生成任务。
- 同步 LANGUAGE、design、compiler 目录 owner 图以及 database/toolchain 规范;未增加环境变量、第二套 pool/transaction/codec、动态 target 或脱管任务。
### 独立验证证据
- 最终 `cargo check --locked --offline -p dever-core -p dever-runtime -p dever-cli --features dever-runtime/sqlite,dever-runtime/postgres,dever-runtime/api,dever-core/reference`:通过。
- 最终 `cargo test --locked --offline -p dever-tests --features sqlite --test durable_jobs -- --test-threads=1`:14/14,通过,122.86 秒。包含最大合法 timeout 的 case-local SQLite/fake、claim/renew/complete 锁等待时钟回归、exhausted 后正常任务 drain、旧持久策略 blocked、三 mode 和 SIGTERM 的 grace/timeout/持事务三场景。
- 主代理最终复核再次运行 `sqlite_durable_state_machine`:1/1 通过;core/runtime/CLI 的 SQLite+PostgreSQL+API+reference feature check 再次通过。
- Cargo 命令统一固定 stable PATH、`CARGO_INCREMENTAL=0`、dev/test debug info 为 0。`git diff --check` 和改动 Job 文件尾空白检查通过。
- 一次中间 suite 为 13/14:信号 fixture 的同到期时间随机顺序断言失败;已按上述原因修复,最终完整定向 target 14/14。
- rustfmt/clippy 二进制未安装,未安装或运行;全量测试、真实 PostgreSQL、性能/内存压测未运行。PostgreSQL 证据仅 feature 编译与代码审查,不宣称已完成真实锁/并发/SQL 执行验收。
- 本次独立检查没有剩余已知实现阻塞;任务状态按调度要求保持 `in_progress`,交由主代理最终复核。
## 待复核风险
- 业务 commit 与 Job insert 必须共享同一 physical transaction,不能 after-commit 补写。
- attempt 必须在 claim 时增加;finish/renew/retry 必须比较独立 claim token 和有效 lease。
- cron materialization 必须由 cursor transaction 保证,不能复用 active-only key 或单进程内存 timer。
- enqueue 不得引入 handler 同步 effect/递归;worker/test root 必须包含 handler 的真实 Model/Port/fake 闭包。
- runtime mode 必须在副作用前统一预检;signal drain 不能调用立即 abort 的 Group::stop 伪装优雅退出。
- payload/schema identity 是持久协议;未知版本必须 blocked/fail closed,不能 best-effort decode。
-50
View File
@@ -1,50 +0,0 @@
# Dever 持久化 Job
## Goal
为 Dever 提供数据库持久化、事务一致、可恢复且有界的后台任务和 UTC 定时任务,使 API 与 worker 可使用同一编译产物独立或组合部署,禁止用脱离结构化作用域的异步任务代替可靠 Job。
## Dependencies
- `time-typed-codec`:DateTime、payload codec 和 schema fingerprint。
- `port-adapter`:Job 调用的 App 可以通过 Port 执行外部副作用。
- 复用既有 SQLite/PostgreSQL ORM transaction、pool、settings 和 shutdown。
## Requirements
- 新增 Job source role;允许 `job.dever` 或平铺 `job/*.dever`,角色目录遵守现有 file/directory exclusivity 和 no-single-topic 规则。
- Job entry 使用明确 contextual declaration,零输入或一个 App-owned wire-safe record 输入、零输出,只能调用同领域 App,不能直接访问 Domain/Model/Port/Adapter/API;Job payload 支持保留 Model identity 的 ModelId。
- Job entry 不可被普通 source call;App 只能通过 compiler-owned enqueue/enqueue_at target reference 创建持久化任务。
- 每个 enqueue 必须提供业务 idempotency key;同 Job identity + key 的未终结任务不重复插入。
- Job 声明绑定一个逻辑 database connection,并声明有界 timeout/max attempts;`retry(n)` 表示包含首次执行在内最多执行 n 次,不允许零/负/越界值。
- 立即和 run-at Job 持久化 target identity、payload、schema fingerprint、key、run_at、attempt/state、lease 和错误摘要。
- 周期 Job 使用编译期验证的五字段 UTC cron declaration且只能绑定零输入Job;持久 cursor、occurrence insert 和推进在同一 transaction,每个计划窗口生成确定性key,多进程及已完成任务不能重复创建同一occurrence。带payload的业务计划使用enqueue_at。
- transaction App 内 enqueue复用隐藏 transaction context;Model effect与Job connection不同则编译失败。非 transaction enqueue使用自身短事务。
- SQLite/PostgreSQL使用runtime private versioned schema;初始化、claim、lease renew/expiry、success、retry、dead-letter均有明确事务边界。
- worker采用至少一次执行;attempt 在 claim 时原子增加,每次 claim 使用独立 fencing token;失败按有界指数退避重试,达到上限进入 dead-letter,不无限循环或静默丢弃。
- payload schema不兼容、未知 Job target和解码失败进入dead-letter/blocked状态并记录脱敏原因,不best-effort执行。
- `dever.job.serve()`显式启动worker。combined main使用现有Group并发启动API/Job;不增加隐式后台线程。
- `config/setting.json`提供严格 `runtime.mode=api|worker|all` 和 job worker/lease/poll/shutdown上限;不接受环境变量或CLI覆盖。
- SIGTERM/Ctrl-C停止API accept和Job claim,排空在途工作到deadline,再停止、释放lease/连接并执行数据库/log shutdown。
- 应用测试的Job数据库由runner生成隔离SQLite;不读取部署setting或自动连接PostgreSQL。
## Acceptance Criteria
- [x] Job合法路径/签名可检查和格式化;ModelId payload 保持 nominal identity;普通调用、非法输出/类型、跨域或直接Model/Port访问被拒绝。
- [x] 业务Model写入与Job入队在同一transaction一起commit/rollback;跨连接组合在编译期拒绝。
- [x] 相同identity+key并发入队只保留一个可执行任务,不丢失已提交业务状态。
- [x] 到期前不执行,UTC run-at到期执行;周期任务在多个worker下每个window最多创建一次。
- [x] worker崩溃/取消会消耗 attempt,lease 到期可由另一 worker 恢复;活跃 lease 不被重复 claim,旧 fencing token 不能提交迟到结果。
- [x] handler失败按策略重试,成功终结,达到attempt上限进入dead-letter;错误信息不含Secret/payload全文。
- [x] schema mismatch、unknown target、corrupt payload不会执行App并保留可诊断记录。
- [x] api/worker/all三种mode分别只启动应有服务;缺少相应配置在监听/claim前失败。
- [x] SIGTERM/Ctrl-C定向runtime test验证停止新工作、排空或超时取消以及最终数据库/log关闭顺序。
- [x] SQLite端到端通过;PostgreSQL feature编译通过,真实行为只在隔离 `postgres_test` 明确存在时运行。
## Out Of Scope
- Exactly-once外部副作用、分布式事务和跨数据库原子提交。
- Redis/Kafka/RabbitMQ/SQS和第三方队列。
- 非UTC cron、DST和动态用户时区调度。
- Job管理front;dead-letter运维界面后续由业务API/front实现。
- 无界任务fan-out、优先级抢占和动态代码任务。
@@ -1,109 +0,0 @@
# Research: Durable Job implementation map
- Query: 核实持久化 Job 的源码接入点、最小复用架构、SQL/生命周期风险和定向验收。
- Scope: mixed;以当前本地源码为准,外部资料仅核对 SQL/信号基础能力。
- Date: 2026-09-20
## Findings
### 当前事实和实施前须解决的契约矛盾
1. 当前没有 Job source role、Job runtime、cron parser 或 SIGTERM/Ctrl-C owner。`rg signal|SIGTERM|ctrl_c|cron|Job crates library` 仅发现 `core/wire.rs` 的 `Policy::Job`。不可将设计中的这些能力当作已有实现。
2. `wire::Policy::Job` 已存在,拒绝 Secret/private Model;现有 wire 同样拒绝 ModelId。设计示例 `PublishInput.article_id: content.article.model.id` 因而不成立。需要明确选择:扩展共享 wire owner 支持保持 nominal 身份的 ModelId,或修改示例使用当前 wire-safe DTO;不得只给 Job 私加类型白名单。考虑 Job 最常见用途正是实体异步操作,建议在共享 schema/codegen 增加 ModelId 节点,协议用字符串,fingerprint 保留所属 Model 身份,同时明确适用 policy。当前 `native/wire.rs:32` 的普通 `Id` 已映射 runtime `Id`,不可把 ModelId 在类型系统里降级成普通 Text/Id。
3. `hir.rs:21`、`contracts/effects.rs:18` 的 database effects 是 Model ID 集合;`native.rs:436` 的 transaction 取第一个 Model 找连接。纯 Job enqueue 无 Model 时无法运行。必须推广为连接资源 effect,不能伪造 Model 或复制第二套 transaction context。
4. 现有跨连接检查在 `model.rs:256` 的 `validate_database_settings`,依赖设置解析后连接名,而非仅 AST check。明确显式不同连接可在 check 拒绝;PackageRoot fallback 仍需要 settings-aware CLI 检查/启动校验。不要错误拒绝不同 selector 最终落在同一个 default 的合法情形。
5. `retry(5)` 的含义必须固定为最多 5 次 attempt,还是首次之外 5 次重试。PRD 用 max attempts,建议前者并准确写明;不能只在失败后加 attempt,否则每次执行中崩溃会无限重领。
6. 设计只规定 UTC cron,未规定字段语法、DOM/DOW 组合、停机补跑和首次部署起点。最小实现应固定五字段数字语法、范围/列表/步长和 DOM/DOW 规则;持久 checkpoint、每轮有界补齐。拒绝不能满足的日期组合或规定有界搜索失败,不能无穷找下个时间。
### Owner 文件与复用入口
| 文件/符号 | 当前职责与 Job 接入 |
| --- | --- |
| `crates/dever-core/src/source.rs:25` SourceRole/from_name/name | 当前 App/Domain/Model/Port/Adapter/Api;增加 Job,沿用 path-derived identity。 |
| `check/layout.rs:14,56` RoleFiles | 已有 role file/directory 排他、非 API 平铺、目录至少两文件;增加枚举后复用,不另写 Job scanner。 |
| `syntax.rs:169,184` FunctionClause/FunctionKind | 已有 ordinary/transaction、fails/bodyless;Job kind 和 bounded policy 作为声明元信息。schedule 用独立声明。 |
| `parser.rs:210,707,761` declaration/function/function_contracts | contextual transaction 的 dispatch 可复用;增加 job/retry/timeout/schedule;勿让 retry/timeout 成普通函数调用。 |
| `format.rs:120,258` declaration/function | 共享 AST formatter,输出 job 前缀和稳定 policy 顺序。 |
| `markdown/contract/parse.rs:198,237,261` / `validate.rs:247` | function 元信息、非 function directive 标识已有 owner;schedule 需加入 directive 分支;Markdown policy 元信息应与 source 一致。 |
| `check.rs:321,423,438` Program/test registration | 注册 Job metadata 和 target map;test case closed graph 与 fakes 已完成,必须沿用。 |
| `check/symbols.rs:222` can_call / `:277` can_access_type | API 不可调用的现有模式适用于 Job;普通 function resolution 和 handler resolution 都必须拒绝 Job。Job 仅调用同域 App,DTO 类型访问另定规则。 |
| `check/expressions.rs:521,620,655` static call/handler reference | 可复用 named-path 解析形态,但 enqueue target 不是 Handler 参数,不能转发。新增专用 checked enqueue path,参数先解 target 再按 payload schema 检查。 |
| `hir.rs:164,507,524` ExpressionKind/static_call/CallArgument | 用 JobEnqueue {target,payload,key,run_at} 明确表达入队;遍历/变更遍历要覆盖三个值表达式。enqueue 不等价同步 call。 |
| `contracts/dependencies.rs`, `contracts/effects.rs:61,505`, `contracts/error_effects.rs` | enqueue 引入本 Job 连接、database failures 和 suspension;不得传播 Job handler 的业务失败/网络 effect 到 enqueue 的 App。 |
| `specialize.rs:77,120,178` database_effects/concrete/successors | 独立执行根收集 Job handlers;不要把 enqueue->handler 当普通递归 edge,App 入队自己未来工作的循环是正常队列行为。 |
| `native.rs:292,418,434,569` DB setup/function/call | 统一隐藏 `_database: Option<&Transaction>`;普通 enqueue 继承,Job handler 执行从 None 开始。 |
| `native/orm.rs` | 现有 Executor/参数/错误转换供 Job emitter 复用;不复制数据库错误到字符串再猜类别。 |
| `native/api.rs`, `native/ports.rs` | 静态 dispatcher 和 startup initialization 可参考;Job 生成 concrete decoder + direct function invocation,不造 runtime Value 解释器。 |
| `native/build.rs:120,309,349` | suite profile、runtime features、缓存身份;Job 无 Model 仍需 database+wire+已选驱动。feature/cache 与源码变化同步。 |
| `crates/dever-runtime/src/database.rs:167,317,458` | Database/begin、Executor::new、database_for;所有 Job SQL 通过既有 Sql 双方言和 Value/Row 编解码。 |
| `sqlite.rs:174,223,279` | BEGIN IMMEDIATE、transaction 方法、未确认完成 Drop 丢弃 pool object。可直接用于 claim 短事务。 |
| `postgres.rs:236,302,383` | BEGIN、带 I/O deadline 的事务、Drop 丢弃连接;claim 使用 SKIP LOCKED。 |
| `sqlite/migration.rs`, `postgres/migration.rs:73,109` | Model 自己的 schema/history owner;Job 私有 schema 不进入它们,但复用事务/advisory lock 纪律。 |
| `config.rs:11,70,144,325,356` | 单一 Settings,严格 top-level/duplicate wire 验证,bootstrap/http lazy read。扩展 typed runtime/job config。 |
| `task.rs:78,488`, `task/scoped.rs` | 单 runtime、Scope supervisor、Group;worker/heartbeat/signal 等所有活动必须有结构化 owner。 |
| `api.rs:13`, `http/mod.rs:147,176`, `net.rs:47,232` | API 内创建 listener;HTTP 已响应 listener.close 并 graceful drain。新增 signal owner 触发此路径,不用取消 serve 代替优雅关闭。 |
| `database.rs:496,502`, `postgres.rs:462,473` | prepare/shutdown 当前只负责 PostgreSQL;SQLite 没有显式 pool shutdown 路径。若 PRD 保证最终关闭 SQLite,需补 owner。 |
| `core/wire.rs:10,55,64`, `native/wire.rs:8` | Job policy、稳定版本 schema identity、concrete codec;当前 fingerprint 是确定性描述串,不是固定长度加密 hash。持久字段可存完整 identity,不误称 SHA。 |
| `cli/test_runner.rs:73,126` / `check.rs:466` | 每 case 新进程/临时目录,runner 生成 SQLite setting;无 DB case 无 setting。加入 Job connection、harness,仍不读部署配置。 |
### 建议最小架构和数据流
保留 Job body 为 Function/HIR,额外 Program.jobs 持有 identity/function/connection/payload schema/policy/schedule。compiler 专用 enqueue 表达式只保存静态 target ID 和普通参数;不将 target 暴露成值类型。Job entry 的 ordinary/handler 调用拒绝统一放符号访问 owner。
数据库 effect 推荐抽为统一 `DatabaseBinding`(selector + fallback root 的稳定值或 intern ID),Model 与 Job 都引用。Model 的 CRUD schema/index 仍保持 Model ID,仅 effects/transaction/startup/test 配置改用连接 binding。为最小改动也可用明确的资源枚举 Model/Job 并统一 `binding()` accessor,但不应让消费者每处重复 match。
源码 Job → checked metadata/payload Schema → enqueue HIR → 编码 Encoded JSON → 同一 Executor 写入 → commit → worker claim → 校验 target/schema → concrete decode → direct App 调用 → owner-CAS finish/retry。enqueue 的调用图不包含 handler 业务 effect;worker 启动根和 test harness 根要独立包含 handler/App/Port fake graph,避免漏链接及错误递归判定。
runtime 可由 `job.rs` 拥有 public compiler bridge,`job/store.rs` 拥有 DB 状态机和 SQL,`job/schedule.rs` 拥有 cron/slot,`job/worker.rs` 拥有有界执行;只有职责确实需要时拆这些文件,不添加通用 queue framework。compile-time cron 验证可调用纯 runtime cron parser,避免编译期/运行时双实现。chrono std-only 已存在,复用 UTC calendar;无须引入时区库。
### SQL/租约策略
- runtime private version 表 + job 表 + schedule checkpoint 表。SQLite BEGIN IMMEDIATE,PG 独立 runtime namespace advisory transaction lock;version 不认识时明确失败,不能 CREATE IF NOT EXISTS 后假装兼容。
- enqueue 的 active-only 唯一索引覆盖 `(target,dedupe_key)` 且 predicate 只含待执行/执行中状态;参数化 INSERT ON CONFLICT DO NOTHING,再返回已有 active ID。不能用 SELECT-before-INSERT 作为唯一保障。业务失败 rollback 必须撤销同物理事务的入队。
- PostgreSQL claim:短事务选取到期 pending 或过期 running,`FOR UPDATE SKIP LOCKED LIMIT ...`,写 owner、lease_until、attempt 后提交。SQLite 同过程已有 BEGIN IMMEDIATE 序列化。只 claim 有空闲执行容量的条目,不能先批量占 lease 再在无界内存队列等候。
- attempts 建议在成功 claim 时递增;超时/失败/崩溃恢复都耗费次数。过期且 attempts 达上限的条目需进入 dead-letter,不能永远排除于查询而残留 running。
- owner token 每次 claim 新生成,不能仅以进程 worker ID 作为 fence;旧执行在同 worker 中过期重领后仍可能迟到。finish/renew/retry 带 id + token + state,必要时判断 lease_until;影响行数为 0 即丢失租约,不可继续确认成功。复用现有 OS randomness/Uuid owner,不硬编码 ID。
- renewal 不与 handler 持有同一 transaction;需预算 worker 数与 pool 容量,避免单连接长事务使 heartbeat 饿死。取消后复用当前丢弃未知连接的规则。若 handler 无法强制立即停止,最多只能保证至少一次,外部副作用依赖业务 idempotency。
- PG runtime Int bind 是 i64,SQL 数值列和参数使用 BIGINT 语境;时间全部 epoch ms,勿调用 SQL now 导致测试时钟不一致。错误摘要写固定错误类别/有界安全信息,不保存 AppError Display(业务 message 可含 payload)。
- cron occurrence 幂等不能只用 active-only job 索引:已经成功的 occurrence 可能被另一 scheduler 再建。锁定 checkpoint 行,插入 jobs 与推进 last slot 同事务;或者永久唯一 occurrence ledger。checkpoint 必须按规范化 cron identity,计划变更不能回放全部历史。每轮扫描/补跑有界。
- key/payload/target/schema/error 等都有确定上限;wire 16 MiB 默认上限已经有界,但 worker 并发乘以该值仍需评估,不宣称低内存性能。
### Mode、启动与关闭
当前 RuntimeProfile 只有 sqlite/postgres;Tokio RuntimeConfig 只有线程/任务容量,均没有 api/worker/all。严格 runtime/job 文档必须进入 Settings 的真实解析,不能仅在 serve 里临时读 JSON。主程序调用 serve 的能力图用于校验 mode:worker-only 不要求 HTTP;API-only 不 claim;all 按 main 的 Group 并行拥有两个 server。启动前校验对应模式的缺失配置及所有启用 Job timeout/lease 关系,不能 API 已监听后才发现 Job 不可用。
API runtime::serve 需在创建 listener 后把关闭与 drain 接到同一 shutdown token。signal watcher 是 generated entry/运行时生命周期拥有的 future,不可 detached spawn;SIGINT/SIGTERM 一次广播,停止 accept/claim/schedule 后等待在途工作到统一 deadline。deadline 到达使用现有 Task/Group cancel-and-drain,再执行 database shutdown 和 log flush。`Group::stop` 当前直接 abort body,正常收到 signal 时先合作排空,超时才 stop。
Tokio 1.53.1 当前 features 不含 signal(根 Cargo.toml:20),Cargo.lock 未找到 signal-hook。需启用 Tokio signal feature 并确认 offline 所需依赖已缓存;不要用 unsafe libc handler(workspace 禁 unsafe)或 shell signal 轮询做替代。PG driver shutdown 当前存在;SQLite pool explicit close 和 startup failure 清理须单独覆盖。generated entry 当前仅 profile.postgres 时调用 shutdown,需按完成的生命周期设计调整。
### Application test 隔离
保持每 case 单独 native module、closed fake graph、进程和临时目录。test metadata 收集 enqueue 与 harness 执行的 Job 连接以及 handler 访问的 Model;不能只看 test 主函数的直接 Model effects。每 case SQLite default/explicit connections 由 runner 生成。Job-only case 也生成 setting;非数据库 case 仍无 setting。
设计还没有给 compiler-owned test harness 的源码调用协议,不能只实现 Rust store 单元测试便声称应用测试可用。建议最小 test-only drain-due/advance-clock 操作,返回有界观察结果或保证已处理 due work,测试通过 App 查业务状态;不开放 DB handle 或普通直接 Job entry 调用。假时钟作为 case-owned Clock 注入 store/scheduler,不改全局系统时间、不真实 sleep。
### 定向验收建议
1. 新 `durable_jobs` 源码/formatter/Markdown 定向 target:合法声明、错误 role/直接 call/handler reference、0/1 record 参数、Secret/private/非法 payload、policy 上下界、cron 非法、纯 Job transaction 与跨连接。
2. runtime SQLite temp DB:业务写入+入队 commit/rollback;并发 active key 去重;终结后 key 重用;run_at;多 claimant;租约过期、旧 token finish/renew 拒绝、崩溃耗尽 attempts;有界退避/溢出;损坏/schema/unknown target 不执行。
3. cron 两个独立 scheduler 同 slot、已完成 job 后再次调度、checkpoint rollback/restart、停机补跑上限、闰年/月底/DOM-DOW 规则。
4. `application_testing` 精确 case:同二进制不同 case fake 完全隔离;Job-only SQLite setting;部署 PostgreSQL 配置未读取;受控时钟/harness 触达 App。
5. config 三 mode 缺失/未知/重复字段、timeout/lease/poll/shutdown 约束。测试专有子进程 signal,明确 deadline;仅所属随机回环 listener(若批准)验证停止新请求和 HTTP drain;不向已有服务发送信号。
6. `structured_concurrency`/`pooled_network graceful_shutdown` 受影响 case;SQLite one-connection cancellation 后可恢复;PG 仅 feature cargo check 与 SQL 生成断言,真实 DB 另行授权条件。
## External references
- PostgreSQL SELECT locking 官方说明:`SKIP LOCKED` 适合多消费者 queue 场景,但仅跳过 row lock,仍有表级锁:https://www.postgresql.org/docs/10/sql-select.html 。这是 SQL 策略依据,不是对本仓库 PostgreSQL 实测。
- SQLite 官方 partial index 文档:https://www2.sqlite.org/partialindex.html ,UNIQUE 可只约束满足 predicate 的行。
- Tokio signal 官方 Rustdoc:https://docs.rs/tokio/latest/tokio/signal/unix/fn.signal.html ,Unix signal API 要求 signal feature。仓库实际 pin 1.53.1,应以本地锁定版编译验证依赖。
- 本地版本:Tokio 1.53.1、chrono 0.4.45 std-only;SQLite/Postgres pool 沿现有锁定依赖,不建议增加队列或 cron 框架。
## Related specs
已查 `.trellis/workflow.md`、任务 prd/design/implement/task.json,以及 backend `index.md`、`directory-structure.md`、`error-handling.md`、`quality-guidelines.md`、`compiler-contracts.md`、`database-guidelines.md`、`toolchain-and-library.md`、`logging-guidelines.md`。Job 新约束尤其会改变 compiler-contracts 的 Model-only effect 描述及 test runner 数据库触发条件。
## Caveats / Not Found
- 子 agent `task.py current --source` 返回 none;父 dispatch 明确提供本任务绝对路径,本研究仅写此 research/,不改 active state。
- 未改源码、spec、任务状态;未运行 build、测试、服务、外部数据库或性能测量。
- 当前研究证明复用边界和缺失点,不证明 Job 可执行。policy、cron、payload ModelId 和 test harness 上述未落实契约必须在实施/验收中明确,不能悄悄删需求。
@@ -1,26 +0,0 @@
{
"id": "durable-jobs",
"name": "durable-jobs",
"title": "Dever 持久化 Job",
"description": "实现事务内原子入队、延迟和周期任务、重试租约与 API/worker/all 运行模式",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-20",
"completedAt": "2026-09-20",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-20-production-application-language",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused architecture/effect/config tests"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Single symbol/dispatch owner and legal role layout"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Port failure subset and diagnostics"}
@@ -1,47 +0,0 @@
# Dever Port 与 Adapter 设计
## Source Contract
Port role按路径给出边界身份。根 `port.dever` 的 identity 为 `<component>.<domain>`;topic Port 增加 topic。bodyless contract 示例:
```dever
send(message: Text) () fails app.DeliveryError
```
`fails` 必须引用同领域 App 的公开 error choice(例:App 定义 `DeliveryError`),或公开标准 error choice;至少有一个显式 error variant。Port 自有类型仍私有。这样保留 App 全部函数公开的规则,App 能传播自己的错误,无需增加隐式私有 helper。Port/Adapter 只能引用 Port 签名使用的 App 类型,不能调用 App。普通有 body function 仍沿调用图推断 failure,不新增通用 throws 声明。
Adapter/Test 通过 qualified definition 实现,例如 `port.send(...) () { ... }` 或 `port.mail.send(...) () { ... }`。普通单名 function 是该文件 helper。一个生产 Adapter 文件完整实现一个 Port identity;禁止跨 Port 聚合。一个 TestCase 可以完整 fake 多个 Port。
## Registration And Dispatch
Checker 在所有签名完成后建立 Port contract/implementation table。实现身份来自 adapter role topic;根 `adapter.dever` 使用 `default`。逻辑 Port identity 不包含 Adapter 文件路径。每个可达 Port 必须至少有一个生产实现;不可达声明按既有 unused contract 报 warning。
单实现 native call 直接静态调用。多实现生成固定选择和 concrete match,分支调用具体 specialization。effect analysis 在 HIR 中看到生产实现保守并集;failure 摘要固定为声明全集,实现实际失败必须是子集。运行时选择不改变类型正确性。
## Settings
Adapter 文件可声明一个 contextual `setting` record,并以只读 `setting.<field>` 访问。编译器把它纳入共享 setting wire schema。建议 JSON:
```json
{
"adapter": {
"notification.mail": {
"use": "smtp",
"setting": {
"endpoint": "https://mail.example.test",
"token": "secret-value"
}
}
}
}
```
`use` 在多实现时必需,单实现可省略但若提供必须匹配。端点/证书/凭据属于 Adapter setting;重试业务规则和权限不放配置。错误只报告 Port/Adapter/字段位置,不回显 Secret 值。
## Test Fakes
Test qualified implementation使用相同 signature/failure checker,但 owner 是 TestCase。Suite emission 为每个 case 生成独立 closed binding;case selection 同时选择 test entry 和 fake table。没有 fake 的可达 Port 在测试检查阶段失败,不退回生产 Adapter,确保测试不会意外访问外部系统。
## Role Matrix Migration
`check/symbols.rs` 继续作为唯一 call/type access owner。新增明确矩阵而不是负向条件组合。Model operation owner 检查保持独立但与 role matrix 一致。现有 CMS 尚未使用 Adapter,迁移只需修复拒绝 fixtures 和文档,不保留 App-to-Adapter compatibility。
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Source role, visibility, failure and effect contracts"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Native dispatch, setting and official package boundary"}
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Transaction effect propagation and settings source"}
@@ -1,68 +0,0 @@
# Dever Port 与 Adapter实施计划
## Start Gate
- [x] secure-data-contracts 和 time-typed-codec 已完成。
- [x] 用户批准父任务规划。
- [x] 读取 source layout、parser declarations、symbol access、effects、application testing 和 config owners。
## Implementation
- [x] 增加 Port bodyless contract、fails modifier、AST/formatter/Markdown contract。
- [x] 注册 Port types/contracts 和 Adapter/Test qualified implementations。
- [x] 集中实现 role call/type matrix,删除宽泛 private-role互调分支。
- [x] 检查 implementation signature、outputs、failures、effects 和覆盖完整性。
- [x] 生成单实现直接调用和多实现 closed dispatch。
- [x] 增加 Adapter setting declaration、共享 codec 接入和严格 setting.json binding。
- [x] 把 Port effect 纳入 specialization/transaction/concurrency 检查。
- [x] 扩展 TestCase fake binding 和 suite case隔离。
- [x] 更新 LANGUAGE、Markdown、compiler/toolchain/application-testing specs。
## Implementation evidence (2026-09-20)
- 保留 Port 的 Function ID,附加生产实现 successors;shared dependencies/effects/specialization 纳入这些边,failure 直接 seed 声明全集。新 `check/ports.rs` 拥有声明、完整实现、setting schema 和 fake 绑定验证;`native/ports.rs` 生成直接调用/closed match 与不可变 setting/binding。
- 合同修正:App 只有公开函数且语言用函数分句匹配,故“Port 私有失败再由 App 私有 helper 映射”不可表达。采用主代理确定的 `fails app.ErrorChoice`;App/Port/Adapter 共享已公开错误合同,禁止反向业务调用,不放宽私有类型导出。
- 每个测试进程选定 case fake;check 与套件生成共用逐 case 的闭合图,Port successors 只替换为该 case 的 fake,生产 Adapter/配置不可达。每个 case 独立原生模块与同步/挂起入口,仍只编译一次套件。一个 case 可完整 fake 多个 Port;缺 fake 编译拒绝。
- 新反例发现 Port 无 HIR body 导致原有挂起阻塞检查漏过合成 dispatch;已在同一 shared effects owner 校验 Port successors,拒绝挂起 Port 内联选择阻塞实现。
- 当前已通过:`port_adapter` 10/10(单实现无配置、多实现同二进制切换、Secret setting、错误配置/重复字段、Markdown/fails、角色与 effects、逐 case fake 隔离);`source_architecture` 8/8;`source_visibility` 10/10;`application_config` 3/3;`markdown_source` 20/20。
- 最终 `core/runtime/CLI` feature check(reference + api + postgres)通过;`application_testing` 8/8;`error_effects::result_capture_requires_matching_success_and_error_variants` 1/1;`git diff --check` 通过。
- `structured_concurrency` 3/11 通过、8 个旧 fixture 失败:仍把 time/task.sleep 的 Unit 当 SleepResult、chunks 当 ReadStreamResult、直接构造 error variant,以及 Markdown H1 放代码。失败先发生在这些旧协议校验,未扩到无关 fixture 迁移;新 Port 挂起/阻塞定向反例已通过。
- 未运行全量、外部服务、真实数据库或性能测试;rustfmt/clippy 组件未安装。Port 当前明确拒绝 handler 参数和输入/输出范围 bounds;配置选择/setting 仅原生后端执行,reference 对单个无 setting 实现可执行,其余给显式不支持错误。
- 已完成清理自审:声明注册与实现注册分开;role I/O 验证收敛到 shared effects;配置可执行目录定位复用同一函数;Port/setting 合成函数不参与重复实现建议。任务状态留给主会话复核后推进,未提交或归档。
## Independent review (2026-09-20)
已修复四项实际问题:
- 测试 fake 曾在 native 阶段跨 case 合并;一个阻塞、一个挂起 fake 会触发错误的 Port 阻塞诊断。生产合同推断也曾使用 Test body。现在生产推断排除 Test body,check/native 共用 `ports::test_program` 的 case 闭合图;共享 dependency owner 保留未实例化 helper 的声明检查,逐 case 校验所有 fake operation 的 failure 子集。原生套件按 case 生成独立模块与同步/异步入口,仍一次 rustc、一个二进制。混合 fake 回归先失败、修复后通过;同 case 阻塞违规、未调用 fake 的越界 failure、未实例化 pure helper 的副作用均被拒绝。
- `setting()` 普通函数曾被 parser 误判为 setting record;现在只在 `setting {` 时识别 contextual 声明。
- 显式 `adapter: null` 曾被 serde Option 吞成缺省,单实现可意外启动;保留原始 null 交给 shared wire validator 拒绝,单实现无文件仍可运行。
- Port-local DTO 内嵌 App DTO 曾在合同图展开之前触发 C006。现在在 shape access check 前按既有名字解析规则展开合同类型图;合法嵌套 DTO 通过,无关 App 类型仍被拒绝。正例已先复现失败再通过。
最终验证:`port_adapter` **10/10**、`application_testing` **8/8**、`source_visibility` **10/10**;core/runtime/CLI `cargo check`(reference + api + postgres)通过。命令使用 stable PATH、CARGO_INCREMENTAL=0、locked/offline、dev/test debug=0、serial tests。`git diff --check` 与本轮修改文件逐文件 `git diff --no-index --check /dev/null` 均无空白错误(后者有文件差异时退出 1,不代表空白错误)。LANGUAGE 与 compiler-contracts 已同步。
rustfmt/clippy 未安装,未运行且未安装组件。未运行全量测试、服务、真实数据库或性能测试。此前 structured_concurrency 的旧 fixture 失败记录仍适用,不在本轮扩改。无新增未修复的 in-scope 阻塞;未提交、未归档、未更改任务状态。
## Focused Verification
```bash
cargo check --offline -p dever-core -p dever-runtime -p dever-cli
cargo test --offline -p dever-tests --test source_architecture
cargo test --offline -p dever-tests --test source_visibility
cargo test --offline -p dever-tests --test error_effects
cargo test --offline -p dever-tests --test structured_concurrency
cargo test --offline -p dever-tests --test application_config
cargo test --offline -p dever-tests --test application_testing
cargo test --offline -p dever-tests --test markdown_source
git diff --check
```
不运行外部网络、全量测试、真实数据库或性能基准。Adapter runtime 正例使用 test-owned本地确定性实现,不连接服务。
## Review Risks
- Port implementation不能被普通 name lookup 当成可调用函数。
- 多实现 effect/failure union不能因当前 setting 选择而缩窄静态合同。
- setting schema identity必须包含实现选择,且错误不得回显 Secret。
- 单 suite 的 fake registry不能跨 TestCase共享可变状态或生产绑定。
- role matrix应由一个表/函数拥有,避免 call与type access规则漂移。
-42
View File
@@ -1,42 +0,0 @@
# Dever Port 与 Adapter
## Goal
把外部 HTTP、邮件、对象存储等依赖变成编译期验证、运行时可配置替换的 Port/Adapter 边界,同时收紧应用角色依赖,禁止 App 绕过 Port 和 Domain/Adapter 反向依赖业务层。
## Dependencies
- `secure-data-contracts`:Secret setting 和不可观察边界。
- `time-typed-codec`:类型化 setting 解码和共享 wire schema。
## Requirements
- `port.dever`/`port/*.dever` 只允许类型和 bodyless function contract;每个 contract 显式 `fails` 同领域 App 公开 error choice 或公开标准 error choice,至少一个显式 error variant。Port 自有 DTO 仍私有。
- Port contract 输入、命名输出、failure contract 和逻辑身份稳定,不从任一 Adapter 实现反推。
- Adapter 通过 qualified Port target 显式实现;签名必须相同,实际 failure set 必须是声明集合的子集。
- Adapter 可包含同文件私有类型/helper,但不能调用 App、Domain、Model、其他 Adapter 或 Port;只能调用标准库和自身 helper。
- App 只能调用同领域 Port;Domain 只能调用同领域 Domain;Model 无业务函数;API/Job 只调用同领域 App。
- Port 调用的 suspend/effect 是所有可选实现的保守并集,仍由现有静态 effect 系统决定 worker/await 边界。
- 单实现自动绑定;多个实现必须由 `config/setting.json` 的稳定 Port identity 选择,未配置、未知实现或未知 setting 字段启动失败。
- Adapter 可声明一个 typed setting record;只有被选择实现的 setting 必须存在并解码,Secret 字段不进入日志/错误。
- 编译产物包含闭合实现集合和直接 match,不使用动态库、反射、trait-object business dispatch 或运行时源码扫描。
- Test 可在同一个测试文件完整 fake 多个 Port;fake 只绑定当前 TestCase,测试不读取项目 Adapter setting 或回退到生产实现。
- Port/Adapter 的 `.dever.md` 文档合同与普通源码一致。
## Acceptance Criteria
- [x] 合法 bodyless Port 和单 Adapter 可被 App 调用并生成直接静态实现调用。
- [x] Port body、Adapter 缺失/重复实现、签名/命名输出/failure 不匹配在 check 阶段拒绝。
- [x] App 直调 Adapter、Domain 访问 Model/Port/Adapter、Adapter 反调 App/Domain/Model 和跨领域 Port 调用全部拒绝。
- [x] 多 Adapter 通过 `setting.json` 在不重新编译的情况下选择;未知选择、缺配置、未知字段和错误类型启动失败。
- [x] 未选择 Adapter 的 setting 不要求存在,其代码仍通过静态检查并包含在闭合产物中。
- [x] Secret setting 可以用于批准 sink,但不能出现在日志、错误、API/Job output 或普通字符串中。
- [x] 两个应用测试可以对同一 Port 使用不同 fake,单套件编译后各自运行且互不泄漏。
- [x] Port effect/failure 通过 App、transaction、handler specialization 正确传播,不出现动态 fallback。
## Out Of Scope
- 运行时安装插件、动态库、服务发现和远端代码加载。
- 跨领域直接共享 Port;领域间继续通过 App 合同协作。
- 通用依赖注入容器、Service locator 或应用可变全局 registry。
- Adapter 热切换;setting 在进程启动时验证并固定。
@@ -1,138 +0,0 @@
# Research: Port / Adapter 最小落地路径
- Query: 按现有编译器、静态合同、共享 wire、应用测试机制实现 bodyless Port、闭合 Adapter 选择和 case-local fake。
- Scope: internal
- Date: 2026-09-20
- Task: `/data/project/dever/.trellis/tasks/09-20-port-adapter`,来自父代理显式分派;本代理 `task.py current --source` 返回 none,未修改 task 状态。
## Findings
### 1. 语法与声明注册
现状:`syntax.rs:168` FunctionClause 的 name 是单个 Name,body 为 Vec;`FunctionKind` 只有 Ordinary/Transaction。`parser.rs:685` 强制单名和 `{}`,不存在可复用 bodyless 声明;`function_contracts:718` 已将 pure/recover 作为 contextual Name 处理,可复用 `at_name`,无需新增全局 token 关键字。Parser 已拥有 source layout,但结构只保留 model/application,可增加 role 或必要布尔上下文。
建议最小 AST:保留普通 name,不将所有函数名改 Path;加 `implementation: Option<Path>`、`failure: Option<TypeRef>` 与明确 bodyless 标记(FunctionKind::Port 或独立 declaration kind 均可,不能用空 Vec 判断,因为普通空函数合法)。qualified definition 的末段仍为 name,前缀保存 Port target。仅 Port role 接受 bodyless/fails;仅 Adapter/Test 接受以 port 开始的 qualified definition。普通函数继续当前推断合同,遇到 fails 直接拒绝。Port 仅一个无模式、无 handler 参数的签名起步,拒绝 transaction/recover、重载歧义和多 clause;若要支持 handler 合同,必须额外定义实现方 failure/effect 参数依赖,不能默默沿用未闭合语义。
推荐语法(其中 `setting` 是新 contextual record 声明,不是 `type setting`):
```dever
// module/notification/mail/port.dever
type DeliveryError {
error Unavailable(message: Text)
error Rejected(message: Text)
}
send(message: Text) () fails DeliveryError
// module/notification/mail/adapter/smtp.dever
setting {
endpoint: Text
token: Secret
}
port.send(message: Text) () {
deliver(message, setting.endpoint, setting.token)
}
// deliver 为同文件 helper;真实外部失败需捕获并映射到 port.DeliveryError
// test/notification/mail/delivery.dever
port.send(message: Text) () {
assert_eq(message, "hello")
}
delivery() () {
app.deliver("hello")
}
```
注意:目前 role 目录至少两文件,不能以只含 smtp.dever 的 adapter/ 作为合法单实现例子。单实现使用 adapter.dever(identity default),多个实现再用 adapter/smtp.dever 与 adapter/local.dever。
`check.rs:97-177` 按 qualified name + arity 合并 clauses;qualified 实现必须有自己的物理内部函数 ID,另存 target contract ID,不能注册为原 Port 名或让两个 Adapter 合并成 clauses。`check.rs:236` 先完成全部签名,适合在此后建立 contract/implementation table;bodyless 跳过 clauses coverage/body 输出赋值检查。`check/names.rs:60` 对 qualified 各段独立 snake 检查。`check.rs:485` 现有 Port 一律拒绝应替换成正向声明校验。
### 2. 路径、调用与类型权限
路径已有足够信息:`source.rs:25-105` SourceRole 含 Port/Adapter,Role 保存 component/domain/topics,Test 保存 component/domain/topic。`check/layout.rs` 负责 role 文件/目录互斥、flat topics、至少两文件;不需新目录体系。
`check/symbols.rs:206` can_call 当前:App 能调全部同域 private role;Domain/Model/Port/Adapter 可相互调同域非 App;Test 只能同域 Domain / 任意 App / 自身 Test;API 完全不可作为目标。需要集中 role policy,至少区分 Call 与 Type,不能用同一布尔强行覆盖所有访问。
建议调用矩阵:Main→App;API→本域 App;App→任意 App、本域 Domain/Port;Domain→本域 Domain;Adapter→同文件普通 helper;Test→同文件 helper、本域 Domain、任意 App。Port contract 无 body。生产 qualified implementation 永远不能被 source lookup/handler capture 直接引用。Adapter 不能调其他 Adapter 的 helper,即使同域也不行。Model 操作仍由 `check/orm.rs:247-258` 独立限定本域 App,不能只靠 function call 矩阵,因为 ORM bypass 普通函数 lookup。
`can_access_type:256` 目前先放行 public Model type、自己文件,然后同域 Domain/Port/Adapter 类型互通;因此“Domain 不访问 Model”“Adapter 不反向依赖业务层”必须同时收紧类型 owner,而不是只收紧调用。建议 Adapter 可引用自身类型、本域所实现 Port 的合同类型、public 标准类型;Port 可引用本 Port 类型/public 标准类型,避免通过 App/Domain 造成反向依赖;Domain 可引用本域 Domain 与允许的 App DTO(若保留现有 business error 约定),但不可访问 Model/Port/Adapter。App 可引用本域 Domain/Port、公开 App/Model,不能引用 Adapter setting/helper type。Test 为 fake 新增本域 Port 类型访问,但不新增直接 Port 调用权限。类型矩阵是否允许 Domain 引用 App DTO 与“Domain 只调用 Domain”并不冲突,需明确不要顺手删除现有 App 错误类型引用约定。
重要合同矛盾:`check.rs:559` 拒绝 App 传播 private error 类型。Port 内定义的 DeliveryError 是 private,不能直接从 App 泄漏。最小解法是 App 用既有 result capture + error-only choice wrapper 捕获,再映射 App 自己的公开 error;不要将 Port 类型整体 public 或放开跨域 Port。Port 返回私有 DTO 同理在 App 映射 View。
另一个绕过:can_call 在 `target.bundled` 提前放行所有标准公开函数。单纯角色矩阵不能阻止 App/Domain 直接调用 HTTP/socket/process/file,也不能阻止 Adapter 调用 dever.api.serve。若目标包括“不绕过 Port 外部 I/O”,必须添加 capability 校验并覆盖 direct system intrinsics、别名、handler 与间接调用。现有 effects 是 time/network/blocking 等粗粒度,不能只禁止全部 network(API serve 需要);应按具体允许的边界/effect owner 定义规则,不从模块名 substring 猜。需要父代理固定本期准确范围。
### 3. HIR / failure / effect / specialization
`hir.rs:11` Program 存摘要向量,`Function:71` 存普通函数签名与 clauses,`CallTarget:513` 仅 Function/Handler。最低侵入路径:Port 仍拥有一个 Function ID,新增 Program.port_contracts / adapter_implementations;调用保留 CallTarget::Function(port_function),native 对该函数发出 compiler-owned dispatcher。这样签名解析、result、run/blocking、handler reference 继续用一个 ID,不必对所有 CallTarget match 添加 Port 分支。但必须让 contract Function 明确无 body,并在每个分析 owner 注入 Port 摘要和实现边,不能仅生成 native wrapper。
推荐数据:PortContract { function, identity, allowed_failures: BTreeSet<Failure>, implementations: Vec<ImplementationId> };AdapterImplementation { owner, name, port, operations: BTreeMap<contract_function, implementation_function>, setting_type: Option<TypeId> };TestCase.fake_bindings: BTreeMap<port_id, implementation_id>。identity 根为 component.domain、topic 为 component.domain.topic;实现 name 为 adapter topic 或 default,不使用 package 路径、源码 ID、排序下标作为 JSON 稳定身份。不同 operation 同名/同 arity 用完整 Port identity 区分。
`contracts/dependencies.rs:11` 是共享依赖 owner:contract→所有生产实现;effects/failure worklist reverse callers 必须看到这些边。`specialize.rs:174` Frame::new 当前只遍历 expression static calls,应扩展 synthetic Port successors;`concrete()` 会枚举所有普通函数,也要避免把 fake 当全局生产实现。native `expressions.rs:310` 统一 call_instance/invoke 可复用;dispatcher 每个分支直接调用具体 specialization,并按分支实际 suspension await。所有候选实现必须参与 reachable/codegen,不能按 build-time setting 剪枝,否则无法不重编译切换且 runtime features 错漏。
failure 不是简单 union:`contracts/error_effects.rs:42` 每轮从空摘要推断;Port 函数需每轮 seed **声明允许集合**,而非实现实际 union。否则一个实现暂未抛出的声明错误会消失,`result(call)` 精确错误集合验证随实现变化,违反合同稳定性。允许集从指定 Shape::Choice 中 `variant.error == true` 变体映射 `Failure {ty,variant}`;不得按名字或消息匹配。实现实际 program.failures 是允许集子集,且必须无未约束 handler error 集。实现不写 fails;实现 errors 超集在 contracts 完成后报错。凡 choice 非 error variant 不属于允许集;建议要求至少一个 error variant。是否允许 error-free Port 需明确无失败语法,当前 PRD “每个显式 choice”没有表达空集合的办法,不能假造 error。
`contracts/effects.rs:228` Effects 摘要要 union 所有候选的 known/recoveries/databases/blocking/suspends/handler dependencies;Port 无 body时目前会推成空。`contracts/failures.rs:571` 为值内 failure obligation,不等于 error_effects 的传播错误,不能混淆。`contracts/facts` / clause bounds 如果 Port 带输出 bounds,需要使用已声明输出保证且严格校验实现,不能因空 body绕过保证。最低范围可拒绝 Port bounds,文档明确,而非静默忽略。
### 4. config 与 Secret setting
`runtime/config.rs:72` Document deny_unknown_fields,目前仅 app/http/database/log;app/log 被丢弃,http 延迟解析。加入 adapter 顶层必须修改此唯一 owner。`Settings::load:138` 使用 serde_json::from_str,map 会丢失重复 key;不要先 from_value 再 wire decode,因为重复 key 已不可恢复。建议 adapter 部分保留 raw JSON 子树或用既有 runtime wire Node 从原文本定位,再交共享 concrete decoder;对 Port key、use、setting 重复/未知字段都严格检查。以 bounded wire parse 先验证整个文档也可,但要确认既有配置大小/深度合同变化。
`wire.rs:7` Policy::SettingInput 已允许 Secret;`Schema::build/from_field` 是 eligibility owner,支持 visible record,不允许 bounded/private fields、Choice/Map/ModelId/Related。`native/wire.rs:7` emit 已产生 `<prefix>_decode(text)` 返回具体 T{id},SettingInput 不产生 encoder;Secret 使用 from_input(text bytes),无 need 自制 JSON 转换。register setting 为 owner-private 合成 record(如物理 Adapter 下 Settings),字段本身不可 private,schema fingerprint 包含 policy/nominal identity。setting 字段读取应新 HIR SettingField 或 Setting record expression;禁止赋值、局部 shadow 绕过、从 Adapter 输出 Secret。无需暴露 generic runtime Value 给业务。
JSON 建议仍使用设计稿 `{ "adapter": { "notification.mail": { "use":"smtp", "setting":{...} } } }`:它表达一个 Port identity 选一个实现,该实现完整实现此 Port 的所有 operations。一份 Adapter 实现多个 Port 时,setting 生命周期/多份配置 ownership 尚不一致,MVP 应禁止并以明确诊断说明,或先规定每 Port 独立配置,不能暗中跨 Port 复用某一个 setting。
单实现无 setting 时可完全省略 adapter 条目;单实现有 setting 时条目/setting 必需,use 可省略;多实现必需 use;若配置 use 必须匹配。未选实现 setting 不解码,但所有候选源码/Schema 要检查。未知 Port identity 需按编译闭合表拒绝。选中实现无 setting 时若提供非空 setting 应拒绝(建议完全不允许 setting key,规则更清晰)。错误上下文只报 Port/Adapter/字段路径,不回显原值。
`config::bootstrap:272` 当前将 settings/数据库初始化与 OnceLock 绑定;`native.rs:96` 仅有 profile 才调用 database setup。Port-only 应用也要初始化选择,不能搭便车要求 database 或 HTTP。建议从数据库 setup 抽取共享 settings 初始化/读访问,再生成 Adapter 初始化,保持只读固定绑定;按全部 required bindings 校验完再执行用户 main 或任何外部 I/O。generated immutable OnceLock<closed enum with concrete settings> 符合“一次启动固定”且不是可变 service registry。Secret 不可 Debug/render、不要 derivation泄漏。
`native/build.rs:346` runtime feature selection 与 generated code关联;所有候选 emission保证 HTTP/crypto特征不丢。`native/build/cache.rs:163` cache 包含生成源码和 runtime 输入;选择值不应进入生成源码,metadata/schema/候选集合必须进入。CLI run/build 会预读部署 Settings,须允许新 adapter section且 build不把 use 静态固化;测试运行时不读取部署 Settings。
### 5. case-local fake
`hir.rs:28` TestCase 只有 checked function/span/database metadata;`check.rs` finalize_tests 在 contracts 后;适合加入 fake table 以及每 case Port reachable 验证。相同测试文件就是一个 entry/TestCase,qualified fake 私有实现归属该 owner。fake 与生产实现都走同一个签名/allowed failure checker,fake 不加入生产 adapter table;无 fake的可达 Port check 失败,即使生产恰好单实现也不能退回。
`native.rs:142` suite 单次 emit_definitions 全部 test roots,selected index选择 case;`test_runner.rs:70` 每 case 独立进程/临时项目,仅数据库 case写隔离SQLite setting,现成强隔离可复用。
建议使 specialization 带 immutable binding context(Production / Test(case id)),并在传播 App/helper/handler调用时保持此上下文,Port 在 Test context仅追加对应 fake successor。同一个 App 被两个 case调用将生成两个必要 specialization,不会共享错误 fake。alternative 是每进程选定 immutable fake dispatch enum,但 suite静态 effect/reachability必须仍排除生产实现,并处理同 App不同 fake suspension;该路线较易误把生产代码/features带入 suite。切忌用可变全局 fake registry且每 case reset。
静态生产合同仍是生产实现 effect union,fake不得让业务合同缩窄;测试 codegen可使用各 fake实际 suspension决定 await,但函数签名/specialization应一致,必要时保守 async化。finalize_tests 的 database metadata 要基于 Test context可达集合,防止从生产Adapter推导数据库(理论上Adapter禁止Model操作,但其他effects同理)。测试缺fake检查应检查经App、普通helper、静态handler、run/blocking所有到达路径,不能只扫Test直接calls。
### 6. Markdown / formatter
`format.rs:264-309` 从token重新组装函数名、pure/recover、body;需要输出qualified path、fails类型、bodyless无花括号,保留注释消费顺序。setting 新 Declaration 分支须覆盖 formatter、markdown::declaration_span、source section validation、names及所有 Declaration exhaustive matches。
`markdown/contract/model.rs:36/60` 文档函数仅name/inputs/outputs;`metadata.rs:52` strict ordered元数据,建议Port段新增 `- 允许失败:` 只在Port存在,与 AST failure choice精确比较;Adapter段 name 使用完整 `port.send`,无额外猜测。setting段作为独立record-like Section,字段类型/顺序/数量与源码一致。`validate.rs:232` 用 owner + 最后一段函数名 + arity寻找HIR,会错误定位qualified实现,改为AST declaration span或显式 declaration↔HIR ID映射,不能 `.rsplit('.').next()` 猜。public_functions metadata继续角色导出规则,不能把实现列为公开App能力。
### 7. 建议分阶段实现与最小定向测试
1. AST/parser/formatter/Markdown + contract/implementation registration + role矩阵;只检查,不先运行native。正反例包括Port body、普通fails、qualified非法owner、缺/重复operation、参数类型/输入名/输出名/顺序/arity不匹配、跨域target。
2. 固定failure declaration seed、effect union、synthetic dependencies、handler/run/transaction分析;测试一个blocking实现+一个suspending实现的保守拒绝、错误子集/超集、捕获声明全集,生产配置不能改变合同。
3. shared wire setting + strictJSON选择 +直接native dispatch;确定性本地实现两分支,不联网;同一二进制仅换配置得到不同结果、未知use、缺use、未知字段、重复key、错误类型、未选配置不需存在、选中Secret错误不回显。
4. TestCase绑定与specialization context;两个测试同一个App各自fake结果、同套件单编译、无fake失败、生产配置坏/含真实地址仍不读取、生产实现不作为fallback、case执行失败不影响后续。
5. 同步LANGUAGE/Markdown/compiler/toolchain与root test fixtures,独立复核。
现有入口:`test/dever-tests/tests/{source_architecture,source_visibility,error_effects,structured_concurrency,application_config,application_testing,markdown_source,formatter}.rs`。建议新 `port_adapter.rs` 聚合端到端闭合合同;只运行必要exact测试和cargo check,无真实HTTP/数据库/全量。旧source_architecture中“port reserved”和App→Adapter正例要按新合同更新,不能保留兼容开关。
## Related specs
### 补充:建议本期固定的执行决定
Port failure 合同建议本期采用声明全集、实现子集、App显式映射三条规则。普通App不透传Port私有错误;`fails DeliveryError` 只提取error variants,result仍要求全集匹配。先要求每个Port都有至少一个error variant,error-free语法另行设计,避免本期引入第二种fails语义。
“App不能绕过Port”必须本期实施,否则PRD目标未满足。最小可执行限制是:App/Domain禁止未经Port边界的 `file` 和 `network` intrinsic可达路径;Domain的Model操作另由既有owner检查拒绝。复用 `contracts/effects.rs:547` 的穷尽intrinsic_effect分类,不另写模块名白名单。新增一个内部摘要 `unmediated_io`,沿普通函数和具体handler传递,但穿过Port contract时清空这一项;正常effect/blocking/suspension摘要仍完整union,不能一并清空。这能允许App→Port→Adapter→HTTP,并拒绝App→helper/标准包装→HTTP。检查direct intrinsic同样经过此摘要,handler绑定用既有specialization保守展开。Adapter只允许自己的helpers/标准库,仍需单独禁止 `ApiServe`,并将该入口限制为Main显式启动;不要把整个network类禁在Adapter上。日志、时间、随机、密码散列、process.arguments本期保持已有授权,不把“Port约束”扩大为全新纯函数制度。若希望Domain完全无任何副作用,属于更强约束,不能偷偷从本期文件/网络隔离推出。
- `.trellis/workflow.md`:研究仅写task research;main负责后续 implement/check。
- `.trellis/spec/backend/compiler-contracts.md`:source角色、App公共边界、failure/effects/shared dependencies、Model owner、Test权限、wire sole owner。
- `.trellis/spec/backend/toolchain-and-library.md`:Markdown严格合同、native cache/feature选择、配置只用setting.json、Secret不可观察。
- `.trellis/spec/backend/directory-structure.md`:目录/role归属。
## External references
本研究仅依据当前仓库,不引入新依赖,不需要外部版本或API资料。
## Caveats / Not Found
- 当前没有 bodyless contract、qualified实现、setting record、Port dispatch 或 fake机制;均不能靠修改一条调用矩阵完成。
- PRD未给error-free Port表示法、App绕过Port的标准库I/O精确限制、跨Port Adapter setting owner;这些应在实现前由主代理用既有范围做一致决定并同步设计。
- 明确区分“Port已声明failure全集”和“所有实现实际failure并集”;后者不能替代前者。
- 查不到单独 `.trellis/spec/backend/application-testing.md`,应用测试规则当前在compiler-contracts与toolchain文档;不要引用不存在的规范文件。
- 未运行测试,未修改源码/规范/状态。
@@ -1,26 +0,0 @@
{
"id": "port-adapter",
"name": "port-adapter",
"title": "Dever Port 与 Adapter",
"description": "实现可替换 Port 合同、Adapter 实现和 setting.json 绑定,并收紧角色调用方向",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-20",
"completedAt": "2026-09-20",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-20-production-application-language",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Bounded HTTP/1, HTTP/2 and resource tests"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Input/API/internal error mapping"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Thin API owner and shared HTTP path"}
@@ -1,130 +0,0 @@
# Dever 生产 API 边界设计
## 2026-09-28 自动权限设计
### 静态权限身份
权限是 checked HIR 的入口元数据,不是业务 Text 常量。每个非 public API 入口拥有一个 `Permission`:`key/component/domain/site/action/method`。site 由现有目录映射得到,custom action 来自 API 声明,REST 展开为 read/create/replace/delete。key 不包含 HTTP method;同一 component/domain/site 下重复自定义 action 必须静态拒绝。public 入口没有 Permission。
权限清单由 native application 内嵌。应用数据库初始化后、服务开始前,Runtime 在控制数据库事务中创建并同步 `_dever_permission`:当前清单 upsert 为 active,清单外旧行标记 inactive。build/check 不连接数据库,同步失败时服务不得启动。
### 标准角色存储与请求授权
官方 auth runtime 在授权数据库维护 `_dever_auth_role`、`_dever_auth_role_permission`、`_dever_auth_user_role`,并以 `_dever_auth_version` 管理私有 schema。关系使用稳定 permission key,不使用环境相关自增权限 ID。Role 以 `(site, id)` 为身份,user-role 和 role-permission 同样携带 site 复合边界;不同站点可以复用同一角色 ID,且不能互相覆盖或授权。旧单列角色 ID schema 在一个事务中按 role.site 回填关系并替换三表,未知或不完整 schema 拒绝启动。授权只做 allow 并集,`all_permissions` 受 site 和 enabled 限制。
verify 输出收缩为 `id: Text, user_id: ModelId?, tenant_id: ModelId?`。Runtime 完成身份 scope 后,使用当前 user、site 和 permission key 对角色关系执行单次 EXISTS 查询;没有用户或授权记录返回 Forbidden。权限不进入 token、Session claims、Identity 或源码能力。租户应用在 tenant scope 内查询本租户角色库;平台入口查询控制数据库。
认证、组件可用性、接口授权和业务执行顺序固定为:路由/站点与凭据校验 -> verify -> 建立 tenant scope -> 检查可达租户组件 -> 检查 permission key -> 解码请求与调用 App。public 入口跳过 verify 与角色检查,并且其完整调用图必须静态证明不触达租户组件。
角色写入由 Runtime 的窄接口统一完成:列出 active 权限、创建/更新角色、事务替换角色权限、分配/撤销用户角色。写入时验证 site、active permission、角色状态和当前租户;这些能力只能从受保护的写 API 到达,业务组件不直接写私有表。租户 provision 时创建 site-scoped owner role并分配首个用户,避免授权死锁;保留 Owner 不能通过普通 save/grant/revoke/disable 修改。
### Job 与租户组件
User Job 只保存一个入口 permission key 和既有 provider/site/subject/session/tenant 身份引用。执行顺序固定为重新 verify、确认当前编译权限、检查租户组件、查询当前角色;失败转 blocked 且不进入业务 dispatch。v4 capability 列表迁移到新 schema 时,旧活跃 User Job 统一阻断为 `authorization_contract_changed`,物理删除旧 capability 列,历史终态记录保留。
编译器以 tenant Model 和 tenant Job 的静态归属识别租户组件,并为 API/CMD/Job 入口计算可达租户组件集合。混合组件整体视为租户组件,它拥有的全部 Job 都绑定租户队列,即使单个 Job 只访问 global 数据。控制库 `_dever_tenant_component(tenant_id, component, disabled_at)` 只存禁用例外。禁用优先于角色授权,不删除租户数据或改变 schema。
### 配置收缩
租户配置只有 `database/max_pools/idle_timeout_ms`。`database` 选择控制连接;物理租户数据库仍由现有 database 配置的 tenant target 派生。旧 `mode`、`control_database` 及 table/field 值明确失败,不保留双读或迁移别名。
## 2026-09-26 身份、站点与租户修订
此节是用户批准后的当前合同,替代旧 Auth Boundary 中的无身份运行时限制。实现仍复用既有 AST/HIR、wire、静态路由、隐藏请求上下文、ORM、Job 和 Port,不增加 Go 应用框架层。
### 配置与路由
`sites.<key> = { path, auth, hosts? }`。path 相对每个领域 api 目录,只匹配物理父目录(SourceLayout topics 排除文件名),空 path 用于单站根 API。保留完整 topics 生成 URL,站点不改写路径。每条 HTTP API 必须得到无歧义站点归属,Host 是进一步限制,不是认证结果。CMD 不从 HTTP 目录映射取得可信身份。
API 仅有 rest 和动词到 App 的绑定,增加单个 HTTP 声明的 public 修饰;禁止源码 site 声明、隐式全站共享接口及强制按站点复制 App/Model。注册后的路由元信息是认证 Provider 选择的唯一依据。
### 认证与权限
`auth.providers.<key>` 保存凭据验证配置和业务 App verify 引用。配置引用必须经过静态签名/Effect 校验并进入生产调用图、native emission、数据库初始化和 API 契约。密码/角色/成员业务复用账号领域,认证代码并不要求名为 auth 的目录。
运行时校验凭据后由受控桥接调用 verify;普通源 record 不能写入当前身份。verify 在租户业务自动事务之前完成。只读上下文包含身份来源、站点和可选用户/租户,不携带权限集合。App 可读 auth,Domain 保持纯规则。租户一经建立在当前调用链内不变,切换接口签发新凭据。
操作权限由 API/REST 静态入口自动生成并由 Runtime 查询标准角色关系;源码不登记或检查权限字符串。自动 REST 与 App 共享权限失败及标准 JSON Error 映射。身份对象不包含权限集合或可渲染原始 Token/Cookie。
### 数据边界
Model 的静态结构与物理存储分离:global 使用平台连接,其他持久化 Model 在 tenant 开启时要求可信租户。持久化模式只支持 database。数据库名只由内部稳定租户标识导出,不提供 table/field 分支。
普通 ORM 与自动 REST 使用共同存储/范围合同;不可证明符合隔离的原生 SQL 拒绝。连接由显式可回收拥有者管理,不永久泄漏每租户的 static handle。租户初始化有受控状态与 schema 版本,业务请求不能自行建库。跨平台库引用为逻辑引用,不能产生跨库 FK/Join;写入口只拥有单物理库事务。
### 后台执行与差异实现
Job 的租户身份由入口系统生成并与入队原子持久化,不信任 payload 内自报身份。User Job 只保存 Provider、站点、subject、session、租户声明和一个入口 permission key,不保存 token 或权限集合;执行时重新调用 verify,并查询最新角色关系复核该权限。受控 System 与 User 身份明确区分,缺少可信身份或旧 schema 不能降级。Worker 使用有界调度,租户任务不得回退平台队列。CMD 使用 System 身份,不能读取 HTTP Header/Cookie 或自报用户。
租户功能、额度及配置是业务数据。Port 候选实现静态编译、共享同一签名/失败/Effect 合同,选取依据可信租户配置。log 配置使用现有 logger 并关联调用元数据;runtime 缺省按 API/Job 能力选择模式,显式 CMD 不改变部署配置。
### 验证边界
先静态反例和 runtime in-process 验证,再生成代码/AOT 及双源码 CMS 定向用例。网络/数据库用例只使用 test 自有临时目录、随机 loopback 端口或明确配置的隔离数据库,运行前说明,不启动现有服务,不运行全量测试,不使用环境变量配置。
## Source And Generated CRUD
本节记录目标方向。声明式 API/CMD、编译器生成入口、基础 REST 和部分隐藏请求上下文能力已实现;Model 字段绑定、可信 `owner`、完整 Cookie 策略、标准 API Error 与 Upload 仍是待实施合同,不能把下述示例全部视为现有语法。
```dever
# module/user/profile/model.dever
type Profile {
owner user_id: user.account.model.id = user.auth.id()
name: Text(1, 64) index
bio: Text?
}
# module/user/profile/api.dever
rest
```
路径是 `/user/profile`,不重复目录名。仅当同领域主 Model 唯一时允许无参数 `rest`;多 Model 必须显式指定,歧义时报编译错误。生成 REST 覆盖 GET 列表与 `/{id}` 详情、POST、PUT 全量替换、DELETE,默认只传输本表公开字段和外键 ID,不自动展开或嵌套写关联。分页有界,外部筛选仅允许显式 `search` 或已索引的静态字段,不接受任意 QueryPlan。
Model 字段不重复列读/写名单:普通字段由编译器按字段名推导;`create`、`replace`、`search` 表达式仅描述不同的输入来源或纯转换。`owner field = context_read()` 对生成 REST 的创建赋值及所有读写施加同一行范围;GET/PUT/DELETE 的身份条件必须进入同一数据库查询或写语句,客户端不得设置或修改 owner。字段表达式仅在生成的 REST 入口执行,不改变普通 App `model.create/get/...` 的行为。只读上下文能力和纯函数可作为静态调用目标;数据库、网络、任务和写 Effect 不允许出现在字段绑定中。多个接口需要不同权限策略时使用明确的 App 操作。
```dever
# 自定义动作才需要 App;App 签名就是 API 输入输出合同。
post publish = app.publish
cmd rebuild = app.rebuild
```
`get`、`post`、`put`、`delete` 是 API 声明而非函数名前缀;`cmd` 独立于 HTTP。GET 调用链禁止数据库写 Effect;POST/PUT/DELETE 和 CMD/Job 的数据库写入在入口调用链自动建立事务,失败或取消回滚。`rest` 与同路径同方法的显式声明冲突时报错,不暗中覆盖。无需 App 透传 CRUD、`crud ... on model`、`expose`、`package` 或手写启动 `main.dever`。
应用业务源码由 `<component>/<domain>/<role>` 唯一确定身份;固定 role 为 app/domain/model/port/adapter/api/job。App 函数均为领域公开能力,Domain 保留私有纯业务规则;topic 文件名不进入 App 调用名。非 API 主题目录只允许一层。App/Domain/Port/Adapter/Job 的根文件与主题目录互斥;主 `model.dever` 可以和附属 `model/<topic>.dever` 共存,`api.dever` 可以和 `api/**` 共存。`user/` 仅为 component,`user/profile/` 为 domain。主 Model 的逻辑身份与表名保持 domain 名,附属 Model 的身份与表名取 topic;移动文件不是纯重构。
生成的应用入口收集 API、Job 和 CMD;没有命令参数时按 setting 的 runtime mode 启动服务,CMD 由稳定的 `<component>.<domain>.<name>` 名称和一个 JSON 输入显式选择。CMD 选择不能改写部署配置;没有可运行入口或 CMD 名称歧义时失败。基础 REST 和自定义 HTTP/CMD 共享类型化 wire 与固定 JSON envelope,不能维护第二套动态解码器。
函数约束使用已检查 HIR 和生产入口调用图,不按长度或函数数量猜测质量。未被生产入口或其他真实函数引用的私有实现、完全不增加类型/范围/失败/效果合同的私有转发、只有原样 Model CRUD 的公开 App 包装是硬错误。应用 build/run 对不可达 App 能力报错;库式 check 不推测外部调用方,保留提示。W003 相似实现仍是提示,不自动合并不同业务含义。所有分句保持同文件连续声明,合法单用途复杂规则不受影响。
## Custom Handler Shapes
GET/DELETE 从类型化 query/path 输入读取,POST/PUT 从静态 JSON 请求体读取;multipart 入口接收一个包含受限 scalar/Upload 字段的 record。API 自动继承绑定的 App 签名,不再机械重写参数和唯一 `response` 输出。请求值和业务失败继续使用共享静态 codec 与固定 JSON envelope。
## Context And Response Metadata
Runtime 为每个 request 创建仅在调用链内有效的 opaque Context handle,不作为源函数参数传递。受控 API 能力返回复制的 Text 或 Secret 值,并修改 Context-owned response metadata。Native route 在 handler 成功/标准失败后统一合并 headers/cookies 并构造 envelope;handler 不能直接获得底层 Response。
Cookie使用具体record/choice表示属性。Secret cookie sink接受Secret并直接编码header,不产生Text中间值。普通response header仅接受Text且经过token/CRLF和forbidden-name检查。
## Error Mapping
官方 `dever.api.Error` 是唯一可离开API handler并被协议映射的error choice。API通常通过 `result(app.call(...))` 捕获业务错误,再用小的API-local clause映射。Native route区分:
- success -> 200/0/ok/data;
- api.Error ->对应status/code/message/null;
- InputError -> 400;
- other inferred failure/fault -> log request metadata, return generic500。
错误message有长度上限且不得包含Secret/private value。业务层不依赖HTTP status。
## Upload Lifecycle
HTTP request reader为multipart路径提供bounded streaming/spool模式。Parser逐part验证headers和limits,将文件写入 `data/tmp` 下create-new随机名,业务只看到opaque Upload、原始filename展示值、declared MIME和size。Scalar fields经过shared decoder。
Upload资源owner跟踪临时文件。Storage Adapter通过批准runtime file/stream操作消费并显式commit ownership;未commit drop/close删除。API response完成前检查所有Upload已消费/关闭,防止隐式长期文件。
普通JSON路径保留现有bounded full body,因为全局body limit明确;multipart不复用会全量聚合的`Request.body`。
## Auth Boundary
登录、Session 和资源级授权仍由业务实现。业务 verify 向隐藏调用上下文提供只读身份,供 `owner` 绑定或 App 中显式 `dever.auth` 能力使用;接口角色关系由官方 auth runtime 读取。CMD 使用受控 System 身份;认证请求入队的 User Job 持久化最小身份来源和入口 permission key,并在执行时重跑 verify 与当前授权查询。`owner` 只约束自动 REST,手写 App 的 Model 操作必须明确检查其数据范围。
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"API role, typed inputs, public outputs and failure contracts"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"HTTP/native/runtime resource and codec boundaries"}
{"file":".trellis/spec/backend/logging-guidelines.md","reason":"Request context and secret-free error logging"}
@@ -1,173 +0,0 @@
# Dever 生产 API 边界实施计划
## 2026-09-28 自动权限与租户收口
- [x] 用户确认最终方案:API 自动权限、分层权限目录、标准角色关系、database-only 租户、组件禁用例外和 Job 当前权限复核。
- [x] 修改前完整备份:`/data/project/dever-backups/pre-auto-rbac-20260928-105925/dever.tar.gz`;gzip 校验通过,SHA-256 `700c2756dfcb4c5bceaff9dc1a16b86cfe94599fdeea798740ebc5fe3a461eb0`,约 1.5 MiB。
- [x] HIR/API checker 生成 custom/REST 权限元数据,public 不生成权限,重复 action 静态拒绝。
- [x] Runtime 控制库同步 `_dever_permission`,并提供按 component/domain/site/action 分组的只读目录。
- [x] 官方 auth runtime 建立角色、角色权限和用户角色私有表,提供事务化授权写入与单 key EXISTS 校验;verify Identity 删除 permissions。
- [x] 删除 `dever.auth.require`、`AuthRequire` intrinsic、源码权限字符串和 Job capability 集合;所有非 public API/REST 在 handler 前自动授权。
- [x] Job schema 升级并保存单一 permission key;执行时重新 verify 和查询当前角色,旧活跃 User Job 安全阻断。
- [x] 租户配置改为 `tenant.database`,删除 mode/control_database,不保留兼容分支。
- [x] 自动识别租户组件,生成入口可达组件集合;实现 `_dever_tenant_component` 和 API/CMD/Job 统一禁用检查及 CLI enable/disable。
- [x] CMS Dever/Markdown 改为标准角色分配和自动权限;删除 membership 硬编码权限列表及所有手工 require。
- [x] 增加编译器、配置、Runtime SQLite、Job、组件闭包和双源码 CMS 定向正反测试。
- [x] 运行允许的最小 cargo check/定向测试、diff 检查和独立终审;真实 PostgreSQL 作为需要外部隔离实例的单独验收项记录。
- [x] 抽取只读根 `config/setting.json` 的共享 PostgreSQL fixture,并让 ORM 与授权验收复用同一隔离数据库/随机 schema/失败清理合同;SQLite 与 PostgreSQL 授权目标复用同一数据库无关断言。
- [x] 在真实 PostgreSQL 上运行授权目录、v1→v2 角色迁移、跨站同名角色、多角色、Owner 与失效权限的 ignored 定向验收;2026-09-30 再次通过,使用显式自有实例和临时根配置,结束后恢复配置文件不存在,不探测默认服务。
## 2026-09-26 已授权执行清单
- [x] 用户批准完整修订方案,并要求修改前备份;认证优先复用现有账号领域。
- [x] 备份完整工作目录(包含 untracked 源码与 .git,排除 target)到 `/data/project/dever-backups/pre-auth-tenancy-20260926-rm6kL6/dever.tar.gz`;gzip 检查和 tar 对源逐项比较均通过。SHA-256 `e4f0666d054ca241484e8333c16270f039a26c3c616b7ddd0f0bea50476ca9b7`,约 1.4 MiB。
- [x] 读取现有调用链和规范;确认站点 path 必须匹配 API 父目录,认证 hook 需进入生产根,Model/Job/Adapter 当前启动固定绑定。
- [x] 配置、目录站点归属、public HTTP 声明、静态认证 hook、只读身份和标准错误。
- [x] global/tenant Model、database 模式按租户选择物理库、有界池、显式初始化迁移和单库事务。
- [x] 多用户成员角色示例、能力检查、REST owner 行范围与字段来源;普通 ORM 保持显式业务范围。
- [x] Job/CMD 可信租户、日志配置、runtime 缺省、app 配置消费者迁移;CMD 不接受 JSON 自报租户/用户。
- [x] 租户组件启停由核心私有表和受控 CLI 完成;table/field 隔离及租户级动态 Adapter 已被后续 database-only 决策取消,不保留兼容模式。
- [x] 完成普通/Secret Cookie、标准错误和受限 Upload 合同。
- [x] Dever/Markdown CMS 同步、定向正反验收和现有规范同步。
- [x] 独立最终审查收口;限定复核外部 Origin、密码事务、public POST 错误、契约夹具和 Job 身份撤销,未发现 P0/P1 阻断。
实施与检查使用 Trellis 子代理;最多两个独立代理并行,共享文件必须先分配唯一拥有者。主会话负责合同、整合及最终证据。现有用户改动不回退、不提交 Git。测试仅限当前改动相关定向目标。
## Start Gate
- [x] secure-data-contracts、time-typed-codec、port-adapter完成。
- [x] 用户批准父任务规划,并批准 2026-09-25 的文件/函数/入口/API 改造方案。
- [x] 核对路径冲突、主 Model 推导、CMD 输入合同及现有 API/native/runtime、SourceLayout、ORM、HTTP 边界。
## Implementation
- [x] 收紧 role 布局:Model/API 根文件可与主题目录共存,其余 role 保持互斥;主 Model 身份沿用现有规则。
- [x] 实现 `get/post/put/delete/cmd/rest` 声明、App 绑定、路径生成与 `{id}` 路由;应用严格模式拒绝旧前缀 API 函数体。
- [x] 实现生成的 run/build 入口与 CMD JSON 选择;移除维护中 CMS 的手写 main。
- [x] 实现主 Model 基础 REST:公开字段、GET 列表/详情、POST/PUT/DELETE、静态 SQL 和有界分页。
- [x] 实现 Model 字段 `create/replace/search` 来源、可信 `owner` 身份绑定与所有自动 REST 读写同语句行范围;普通 App ORM 不受隐式修改。
- [x] 实现 GET 写 Effect 拒绝和写入口自动事务;覆盖 API/CMD/Job 生成路径,持续补足提交、回滚与取消的定向运行验证。
- [x] 基于生产调用图拒绝可证明的无用私有函数、无契约纯转发、简单 Model CRUD App 包装及应用构建不可达 App;保留警告和库检查。
- [x] 扩展 API input shape 检查和 shared decoder generation,CMD 使用同一严格 JSON 对象解析。
- [x] 增加 runtime 请求级隐藏作用域,不暴露可保存或伪造的 Context 源参数;HTTP/CMD/Job 调用链静态区分。
- [x] 增加受控 Header 读取/响应写入、Secret Cookie 原始字节的版本化 base64url 读取/写入和 typed CookieOptions;禁止敏感 Header 经 Text 读取、Header 注入及 Secret Cookie 缺少 Secure/HttpOnly。
- [x] `client_address()` 由 HTTP/1、HTTP/2 的 TCP peer 提供,不信任转发 Header;HTTP/1 loopback 定向验证通过。
- [x] 拒绝 CMD/Job 或脱离请求的 Task/parallel/blocking 调用 HTTP Context;HTTP/2 并发流的 request id、path、peer 和响应 Header 隔离定向测试通过。
- [x] 完成普通 Text Cookie 的安全读策略;Secret Cookie 保持独立非 Text 通道。
- [x] 增加官方API Error及native status/envelope映射。
- [x] 重构 request read boundary,认证/路由先于正文,multipart 使用 bounded streaming/spool。
- [x] 增加 affine Upload runtime owner、App→Port→Adapter 单次传递、storage consume/close/cleanup 和 checker 规则。
- [x] JSON request、request id 与现有 HTTP/1、HTTP/2 dispatch 路径共享;后续仍需定向运行验证 HTTP/2/drain。
- [x] 更新 README、LANGUAGE、Markdown 语法与 CMS 两套源码,迁移至编译器生成入口;其余协议文档随 Context/Error/Upload 实施更新。
- [x] Job 私有存储最终升级到 v5:只持久化可复核身份引用和单一入口 permission key,拒绝无身份入队;CMD/cron/Test 显式 System,User 每次执行重新 verify,v4 活跃 User 行阻断并删除旧 capability 列。
- [x] 响应 Header/Cookie 改为 pending/committed 两阶段,序列化和数据库提交成功后才发布;登录类请求使用严格同源校验,Cookie 签发/清理统一读取配置。
- [x] PostgreSQL database 租户由显式 `tenant migrate` 通过控制连接幂等创建后迁移;普通请求/worker 保持无创建、无回退。
## 2026-09-25 定向结果
- `cargo check --offline -p dever-core -p dever-runtime -p dever-cli` 通过。
- API 声明测试 10/10、语法/格式 22/22、基础 REST 4/4、目录规则 12/12、旧 HTTP 路由 7/7 通过。
- CMS Dever/Markdown `check`、`fmt --check` 通过;CMS 同源与生成入口测试 1/1 通过。
- 无 main 的 API/CMD AOT 编译和实际 CMD JSON 调用通过,输出固定 envelope;Job AOT 编译通过。生成入口定向测试 11/11 通过。独立审查指出的必填 Json、事务并发、日志泄漏和 REST Text 边界已修复,并增加定向反例。请求上下文和 TCP peer 测试 5/5、HTTP/2 多路复用定向测试 1/1 通过;CMS 同源测试 1/1 通过。
- 仍需确定 `owner` 的可信业务 actor 来源,以及声明式 API 如何把 App 业务失败映射到官方 API Error;两项不能用客户端 Header/Cookie 自报身份或让 App 隐式依赖 HTTP 状态来代替。Model `create/replace/search` 绑定、普通 Text Cookie 策略及 multipart Upload 还未实现。
- 未运行全量测试、服务级 CMS/HTTP 集成测试、真实数据库或性能/内存压测;rustfmt/clippy 组件未安装。
## 2026-09-26 身份与配置定向结果
- typed `auth/sites/log` 配置、API 物理目录站点归属、单声明 `public`、静态 verify hook、只读 `site/auth` 上下文、HS256 凭据、Cookie Origin 约束和标准 API Error 映射已实现。
- `app` 顶层配置已移除,性能工具迁移到顶层 `performance`;runtime 缺省按 API/Job 能力推导。UUID 官方 parse/to_text 能力已补齐,供业务会话与租户标识使用。
- `/root/.cargo/bin/cargo check --offline -p dever-core -p dever-cli` 通过;`/root/.cargo/bin/cargo check --offline -p dever-runtime --features api` 通过;`api_routes` 7/7 通过(含 native/AOT);此前 `auth_runtime` 2/2、`application_config` 5/5、API 声明 14/14、性能工具 Python 测试 40/40 通过。
- database 模式多租户物理存储、Job/CMD 可信租户、普通 Text Cookie、Upload、REST owner 与字段绑定已经完成;CMS 双源码正在最终复验。
- 此处当时记录的 table/field 隔离与租户级动态 Adapter 后续已由 database-only 决策取消;当前不保留这些模式或兼容分支。
## 2026-09-26 多租户、REST 字段与 Upload 定向结果
- database-only 租户已贯通 global/tenant Model、无回退物理库选择、ready/fingerprint、显式 `tenant migrate`、有界池回收、单库事务和 tenant Job 恢复;当前配置入口是 `tenant.database`。SQLite 无回退/幂等迁移 CMD 1/1、tenant Job 物理库隔离 1/1 通过。
- REST `owner/create/replace/search` 已进入 checked HIR、生产可达图与 native;owner+search 复合索引、双静态 SQL、pure Domain/可信上下文正例和数据库/时间/响应/并发反例通过。SQLite AOT 1/1 通过(74.46 秒)。
- multipart/Upload 边界 5/5 通过,包含流式分块、limits/重复字段/路径穿越、取消清理、affine 静态反例和 loopback POST→App→Port→Adapter→storage 实际 AOT;临时目录在成功后为空。
- 写入口事务允许等待完成且目标无数据库/并发的 `blocking`,仍拒绝 `run` 等逃逸并发;自动事务、拒绝反例和显式 transaction 定向 3/3 通过。
- API AOT 路由复验 1/1 通过;认证运行时此前 2/2、CMD tenant migration 1/1 通过。CMS Plain/Markdown 使用部署 settings 的同源/application reachability 复验 1/1 通过。
- 未运行全量测试、真实 PostgreSQL、HTTP/2 上传 reset、外部对象存储原子发布、服务压测或性能/内存压测;rustfmt/clippy 组件未安装。
## 2026-09-26 可信身份与事务收口结果
- CMS Plain/Markdown 的发布操作统一调用 `dever.auth.owns_user(stored.author_id)`;租户 verify 同时要求 `platform.tenant.active`,避免仅凭有效会话绕过资源所有者或停用租户。
- public API 不再从浏览器 Cookie 建立身份,显式无效 Authorization 仍拒绝;Origin 与 Host 按站点配置的外部 HTTP/HTTPS scheme、主机和有效端口精确匹配,不根据内部监听协议或转发 Header猜测。Cookie 名称、TTL、签发和清理均来自 `config/setting.json`,没有环境变量入口。
- `_dever_jobs` 当前 v5 区分队列物理租户与执行租户,保存 provider/site/subject/session/tenant 引用及单一入口 permission key;不保存 token、Cookie、角色或权限集合。User Job 每次重试均重新 verify 并查询当前角色,legacy v4 活跃 User 行转为 `blocked/authorization_contract_changed`,匿名/无身份路径不能退化为 System。
- SQLite 写事务继续 `BEGIN IMMEDIATE`。含 password verify/hash 的 HTTP 调用链不再生成外层事务,编译器要求全部写入进入显式短 `transaction` 并禁止事务内密码计算;CMS 登录在短事务内重新检查账户、租户和成员关系后创建 session。响应元数据只在响应序列化和数据库提交后转为 committed。
- PostgreSQL 租户迁移会通过配置的控制连接创建派生数据库,SQLSTATE `42P04` 作为并发/重复创建成功处理;尚未连接真实 PostgreSQL 验证权限和服务端行为。
- 定向结果:核心/runtime 的 API、SQLite、PostgreSQL feature `cargo check` 通过;durable Job 静态、生成入口、SQLite 状态机、v5 迁移/持久化、public 边界和 tenant 隔离各 1/1 通过;认证 runtime 3/3、配置 6/6、安全契约 12/12、生成 User Job AOT 1/1 通过。
- 新增撤销回归通过 1/1:测试拥有的临时 SQLite 应用先以有效会话入队 User Job,再撤销 session 并启动 worker;任务转为 `blocked/identity_rejected`,业务副作用表保持为空。该验证使用临时 loopback 与子进程,无外部服务。
- 密码窗口并发回归包含在安全契约中:账号读取与 session 短事务之间模拟密码计算,同时另一连接可完成无关写入;短事务重新读取状态后正常创建 session,不持有跨密码计算的 SQLite 写锁或旧读快照。
- CMS Plain/Markdown `check` 与 `fmt --check` 均通过;两套源码/Model/API/config 同源检查 1/1 通过。两套 `check` 各保留 3 个预期 W001,不影响编译。
- 独立终审只读复核上述五项实现与执行路径,未发现 P0/P1 阻断;真实 PostgreSQL、性能、table/field 和动态 Adapter 不属于本轮收口范围。
- 未运行全量测试、持久服务、真实 PostgreSQL、HTTP 集成、性能/内存压测;rustfmt/clippy 组件未安装。table/field 租户隔离和租户级动态 Adapter 仍按已确认范围延期。
## 2026-09-28 自动权限与租户收口结果
- 权限由 checked HIR 自动生成,custom 使用 action,REST 使用 `read/create/replace/delete`;method 仅作元数据。跨 method/path 重复 key、public 触达租户组件、public/GET 调用角色管理能力均在 native 前拒绝。
- `_dever_permission` 在服务启动前同步;授权库使用版本化 `_dever_auth_role`、`_dever_auth_role_permission`、`_dever_auth_user_role`,三表以 `(site, role_id)` 隔离同名角色,旧单列 ID schema 事务迁移并保留授权数据。精确权限使用索引化 EXISTS,多角色取并集,`all_permissions` 只覆盖同站点。保留 `_dever_owner:<site>` 只能由 `tenant owner` provision,普通 save/grant/revoke/disable 全部拒绝。
- Identity 已固定为 `id/user_id/tenant_id`;源码 `auth.require`、AuthRequire intrinsic、权限列表和 Job capability 集合已删除。受保护 API 顺序固定为 verify、tenant scope、组件检查、精确权限、输入解码和 App。
- Job 已升级到 v5,只保存一个入口 permission key;每次执行按 verify、编译目录、租户组件、当前角色的顺序复核。v4 活跃 User Job 迁移为 `blocked/authorization_contract_changed` 并删除旧 capability 列,System 任务保持可执行。
- 租户只支持 `tenant.database/max_pools/idle_timeout_ms`。租户组件从 tenant Model/Job 自动推导,混合组件拥有的全部 Job 统一使用租户队列,并通过 `deverc tenant component enable|disable` 控制;public 入口不能静态触达租户组件。
- CMS Plain/Markdown 已同步为核心角色管理薄适配,admin/front 各有同站点管理入口,启动说明要求迁移后分别 provision Owner;membership 只表达业务成员关系。两套 `test` 此前各 1/1 通过;本次最终源码改动后,两套 `check`、`fmt --check` 及当前编译器双源码同源/完整应用可达检查通过,check 仅保留 3 个既有 W001。受磁盘空间限制未重复链接两套完整 CMS 测试程序。
- 定向验证通过:core/runtime/CLI cargo check;`application_config` 6/6、`api_declarations` 16/16、`contract_api` 9/9、`auth_runtime` 3/3、`authorization_runtime` 1/1(含旧角色 schema 迁移与跨站同名角色);REST 权限、Identity、Job v4->v5 删列、撤销后 Job 阻断、混合组件 Job 租户 AOT、租户组件 disable/enable、CMS 双源码同源及受保护 auth/Job 顺序 AOT 各 1/1。
- 两轮独立终审指出的站点角色主键、混合组件 Job、CMS 首次 Owner、v5 旧列、Job 复验顺序和旧授权 schema 升级问题均已修复并复验;最终未发现新的 P0/P1。
- 未运行全量 workspace 测试、真实 PostgreSQL、持久服务或性能/内存压测;rustfmt/clippy 组件未安装。本轮实现没有环境变量配置入口。
## 2026-09-28 PostgreSQL 授权验收准备结果
- 新增共享 PostgreSQL 测试 fixture,只接受仓库根 `config/setting.json` 的 `database.postgres_test`;URL 的数据库名必须包含且只包含一个 `{case}`。fixture 为每个目标派生专用数据库名并创建随机 schema,测试成功或 panic 后都从独立 runtime 尽力清理。
- `postgres_orm` 已迁移到共享 fixture;新增 ignored `authorization_postgres` 目标。SQLite 与 PostgreSQL 共用唯一授权合同,覆盖权限目录、旧 v1 schema 数据保留迁移、跨站同 role id、多角色并集、角色禁用/撤销、保留 Owner 限制和失效权限拒绝。
- `/root/.cargo/bin/cargo check --offline -p dever-tests --features api,postgres --test postgres_orm --test authorization_postgres` 通过;SQLite 授权 exact 1/1 通过。无 feature 的 `syntax_and_format` 与仅 SQLite 无 API 的 `sqlite_orm` 编译检查由实施子任务验证通过。
- 独立检查发现 PostgreSQL URL 的 `dbname` 查询参数可覆盖路径数据库,已拒绝明文和百分号编码的覆盖形式;纯 URL 隔离回归 1/1 通过。复验期间磁盘一度归零,两个重复定向链接已中止并清理本轮生成的可重建测试二进制/增量对象,未删除源码、配置或用户数据。
- 真实 PostgreSQL 未运行:仓库根当前没有 `config/setting.json`。测试不会读取环境变量或命令行连接覆盖,也不会自动连接、创建或清理未明确配置的数据库。
- `cargo fmt --all --check` 未运行成功,因为 rustfmt 组件未安装;新增 Rust 已手工按现有样式整理。
## 2026-09-29 真实 PostgreSQL 定向验收
- 使用仓库根 `config/setting.json` 的 `database.postgres_test` 配置(测试凭据仅存在于被 `.gitignore` 忽略的临时文件,验收后已删除),在本机隔离 PostgreSQL 数据库上运行 `authorization_postgres` ignored 定向目标,1/1 通过。
- 本次实际覆盖权限目录同步、v1 角色 schema 迁移、跨站点同名角色、多角色权限并集、角色禁用、用户角色撤销、Owner 保留限制和失效权限拒绝;测试使用随机 schema,临时数据库在验收后清理。
- `postgres_orm` 未通过,失败发生在数据库连接前的共享 fixture 编译阶段:旧 fixture 仍从 `main.dever` 调用 `user.create`,当前 Model 规则要求 Model 操作只在 owning-domain App 中调用。SQLite 共享 fixture 同样复现该编译失败,因此这是独立的 ORM fixture/语言契约迁移项,不是 PostgreSQL 实例或配置问题。
- 没有读取环境变量或命令行连接覆盖;仓库工作区未保留真实 PostgreSQL 凭据或 `config/setting.json`。
## 2026-09-30 当前 PostgreSQL 组合补验
- 共享 ORM fixture 已按 owning-domain App/Model 合同迁移,真实 PostgreSQL ORM/授权 Store ignored 各 1/1 通过,旧 09-29 ORM 失败已关闭。
- `postgres_api` exact ignored 1/1 通过 stock Dever CMS 的真实 HTTP、两个物理租户数据库、同 slug 独立发布、跨站 Cookie/跨租户登录拒绝、普通非 Owner 的多角色/跨站同名角色及同一会话撤销立即失效。测试注册仅在副本追加 `test/dever-tests/fixtures/postgres_api/` 片段,不修改生产 CMS 或 Owner 保留角色规则。
- 新报告 `target/performance/postgres-http-09-30-v1/report-1968019-1790739069361983512.json`;`test-rbac-final.log` 和 `server-rbac.log` 保留真实结果与服务日志。失败轮的站点目录读取和 DELETE body 工具错误已按现行合同修正,未放宽 checker/runtime。
- 临时控制库/租户库全部清理,自有 PG 已停,根 `config/setting.json` 恢复原先不存在;PG 下载/数据目录移入回收站可恢复。Python 工具 57/57、Rust postgres_api 非 ignored 3/3;不宣称 Markdown PostgreSQL/HTTP2 所有组合或全 workspace 复验已运行。
## Focused Verification
```bash
cargo check --offline -p dever-core -p dever-cli
cargo check --offline -p dever-core -p dever-runtime --features dever-runtime/api,dever-runtime/sqlite,dever-runtime/postgres
cargo test --offline -p dever-tests --features api,crypto,sqlite --test secure_contracts --test auth_runtime --test application_config
cargo test --offline -p dever-tests --features api,crypto,sqlite --test durable_jobs revoked_user_job_is_blocked_before_business_dispatch -- --exact
cargo test --offline -p dever-tests --features api,crypto,sqlite --test contract_execution cms_formats_share_source_model_api_and_configuration_contracts -- --exact
cargo test --offline -p dever-tests --features api,postgres --test authorization_postgres postgres_authorization_preserves_site_scoped_roles_and_catalog -- --ignored --exact
cargo run --offline --quiet -p dever-cli -- check examples/cms/dever
cargo run --offline --quiet -p dever-cli -- check examples/cms/md
cargo run --offline --quiet -p dever-cli -- fmt examples/cms/dever --check
cargo run --offline --quiet -p dever-cli -- fmt examples/cms/md --check
cargo test --offline -p dever-tests --features api --test api_routes
cargo test --offline -p dever-tests --test http_engine
cargo test --offline -p dever-tests --test http2_server
cargo test --offline -p dever-tests --test logging
cargo test --offline -p dever-tests --test pooled_network
cargo test --offline -p dever-tests --test source_architecture
cargo test --offline -p dever-tests --test markdown_source
git diff --check
```
上传测试只使用test-owned临时目录和bounded loopback peer,提前说明后运行;不启动持久服务、外部网络、全量测试或性能基准。
## Review Risks
- Context/Upload不能通过record、Task、Channel、App参数或输出逃逸request scope。
- multipart错误/取消所有路径必须删除已创建part,且不能信任filename作为路径。
- Secret cookie不能生成可被Render/log捕获的中间Text。
- API Error必须与未知App failure区分,不能把内部错误message返回客户端。
- HTTP/2并发stream的Context/temporary ownership不能串请求。
- 目录或文件重排不得隐式改变 Model identity、表名、App 调用名和 API 路由;旧 API/Main 方案不能与新方案共存。
@@ -1,86 +0,0 @@
# Dever 生产 API 边界
## 2026-09-28 自动权限与 database-only 租户修订
本节替代下文所有关于手写 `auth.require`、verify 返回权限列表、框架不提供 RBAC、`tenant.mode`、`tenant.control_database`、table/field 租户模式和 Job capability 列表的旧合同。
- `public` 是唯一匿名入口。所有非 public HTTP API 默认同时要求可信身份和当前接口权限;源码不得手写权限字符串或调用 `dever.auth.require`。
- 编译器根据 `<component>/<domain>/api/<site>/**` 和 API 声明自动生成权限目录。权限 key 为 `component.domain.site.action`;HTTP method 只保存为元数据,不进入 key。`rest` 自动生成 `read/create/replace/delete`,自定义 API 使用声明 action。public API 不生成权限项。
- 权限目录由构建产物携带,并在应用初始化时幂等同步到控制数据库 `_dever_permission`;移除的 API 标记 inactive,不直接删除授权历史。源码、组件和业务代码不注册权限。
- 官方 auth runtime 提供租户内 `_dever_auth_role`、`_dever_auth_role_permission`、`_dever_auth_user_role`。用户只通过角色取得权限,多角色取并集;不支持直接用户授权、deny、角色继承或权限优先级。`all_permissions` 仅覆盖同一站点的 active 权限。
- verify 只建立 `id/user_id/tenant_id` 可信身份,不返回权限列表。Runtime 针对当前 permission key 做索引化 EXISTS 查询,不把大量权限写入 JWT、Session 或请求上下文。`dever.auth` 只公开身份读取及签发/清理凭据能力。
- API 权限只决定能否调用动作。资源归属、状态流转和其它数据权限仍由 App 读取可信身份后调用纯 Domain 规则;Domain 不读取隐式身份。未认证返回 401,接口权限或业务授权失败返回 403。
- 多租户只保留 database 隔离。配置改为 `tenant.database`,删除 `tenant.mode` 和 `tenant.control_database`,不保留兼容分支。
- 编译器自动识别组件:拥有 tenant Model 或 tenant Job 的组件属于租户组件,其余为平台组件;混合组件按租户组件处理。租户组件默认启用,控制库 `_dever_tenant_component` 只保存禁用例外。入口携带静态可达组件集合,Runtime 在 API/CMD/Job 入口统一检查。
- User Job 保存可重新验证的身份引用和一个发起操作 permission key,不保存权限集合;每次执行前重新 verify,并按当前角色关系复核该 permission。旧 capability schema 的活跃 User Job 在迁移时阻断,不能降级为 System。
- `_dever_permission` 和 `_dever_tenant_component` 是 Runtime 私有控制表;角色表由官方 auth runtime 统一拥有。项目仍拥有 User、Session、Tenant 业务 Model。
## 2026-09-26 已批准修订
用户已批准在备份后实施完整站点、可信身份、多用户权限和多租户方案。本节替代下文涉及身份、租户、入口配置的旧限制;尚未完成的字段绑定、Cookie、标准错误和上传仍属任务,不因新增范围而消失。
- `sites.<key>.path` 是每个组件/领域 `api/` 下的相对源码目录,递归包含子目录;不是 URL 前缀。`admin` 只匹配 `api/admin/**`,不能匹配同名 `api/admin.dever`。URL 保持现有组件/领域/API topic 生成规则,不增加源码 `site` 声明。站点匹配遗漏、歧义和路由冲突在服务开始前拒绝。
- `public` 只修饰 HTTP API 声明,普通接口默认认证。站点选择配置中的认证 Provider,Host 可进一步约束入口;客户端提供的站点/用户/租户值不能自行成为可信身份。
- Provider 验证签名和站点凭据后,调用静态检查的业务 App verify,检查账号、会话、成员关系并建立只读 `site.key`、`auth.id/user_id/tenant_id`。优先复用已有账号领域,不要求新建 `auth` 领域或 `admin` 组件。
- 身份、接口权限和数据范围分别检查。接口权限由 Runtime 自动校验,数据范围由业务 App/Domain 处理,所有入口不能绕过对应边界。
- 开启多租户后,普通持久化 Model 属于当前租户,`global` Model 使用平台库;只支持 database 分库。不影响普通 DTO,不因缺租户而回退平台库。CRUD、REST、统计、关联和批量路径共用隔离合同。
- 租户连接池有界且可回收;初始化/迁移属于受控租户生命周期。跨库引用保留类型但不能隐式建立物理外键/Join;自动事务只覆盖一个物理数据库。隔离模式切换必须经过数据迁移。
- 租户功能开通和规则配置属于业务数据;实现差异复用已编译 Port/Adapter。禁止租户 ID 分支、复制整套项目或执行数据库内代码。
- Job 原子入队并保存受控租户/执行身份,执行时恢复并复核资格;CMD 不能通过自报用户 ID 伪造身份。日志关联站点/身份/租户而不泄漏凭据。
- 所有部署配置仅来自 `config/setting.json`,无环境变量。保留并应用 log,runtime 可选并按能力选择服务,移除无用途 app 配置及迁移其工具消费者。
- 同步 Dever/Markdown CMS,验证站点目录、跨站凭据、两个租户相同记录 ID、不同角色、匿名边界、Job 上下文、事务回滚和平台共享数据。
本轮已完成并逐项比对的修改前备份:`/data/project/dever-backups/pre-auth-tenancy-20260926-rm6kL6/dever.tar.gz`。SHA-256:`e4f0666d054ca241484e8333c16270f039a26c3c616b7ddd0f0bea50476ca9b7`。
## Goal
在保持固定 JSON envelope 的前提下,以 Model + `rest` 覆盖受约束的普通 CRUD,以显式 API 动词绑定 App 业务动作;同时让 API 安全承载结构化请求、会话 Cookie、显式业务错误、隐藏请求上下文和受限文件上传。本任务正在实施,以下是完整目标合同;已实现的子集以 implement.md 为准。
## Dependencies
- `secure-data-contracts`:Secret Cookie/token 和私密输出拒绝。
- `time-typed-codec`:结构化请求/响应和完整标量 codec。
- `port-adapter`:上传后的对象存储由业务 App 调用 storage Port。
## Requirements
- API 使用 `get`、`post`、`put`、`delete`、`cmd` 和 `rest` 声明;移除 `get_`/`post_`/`delete_` 前缀语法,不加入 `patch`。同领域唯一主 Model 可由无参数 `rest` 推导,自动路径为 `/<component>/<domain>`;自定义动作以 `post publish = app.publish` 等形式绑定 App 签名。
- 纯 CRUD 无需 App;编译器生成的 `rest` 才可直接降低到同领域 Model 操作。手写 API 不直接调用 Model;需要业务处理时调用同领域 App。App 中所有函数都是领域公开能力,私有辅助逻辑归 Domain,不增加 `expose` 或 `crud ... on model` 声明。
- Model 普通字段按名称推导 CRUD 输入输出;只有例外才声明 `create`、`replace`、`search` 或 `owner` 字段绑定。`owner <field> = <context-read expression>` 为自动 REST 创建赋值及 GET/PUT/DELETE 行范围,客户端不能覆盖。字段绑定表达式只能使用静态类型可检查的纯计算或只读上下文能力,禁止数据库、网络和写 Effect;它们不隐式作用于手写 App 的 Model 调用。
- `get` 调用链禁止数据库写 Effect;`post`、`put`、`delete` 写调用链默认同一事务。`cmd`、Job 的数据库写入也按 Effect 建立事务,不依赖 HTTP 动词。无需在 App 源码重复声明 `transaction`。
- 源码身份固定为 `<component>/<domain>/<role>.dever[.md]`,role 只接受 `app/domain/model/port/adapter/api/job`。一个领域优先每个角色一个文件;按主题拆分时非 API 主题只允许一层。App、Domain、Port、Adapter、Job 的根文件与主题目录互斥;主 `model.dever` 可与附属 `model/<topic>.dever` 共存,基础 `api.dever` 可与路径分组 `api/**` 共存。主题必须表达业务含义,不按函数机械拆文件;搬动 Model 文件可能改变持久化身份,不能作为无迁移的整理。
- `deverc run/build` 的目标入口由编译器生成,不要求应用编写 `main.dever`。无命令参数时按 `config/setting.json` 启动 API/worker;指定 CMD 时命令行 JSON 由该 CMD 的静态签名解码。没有可运行入口时报错;部署选择继续只读 `config/setting.json`,不用环境变量覆盖。
- App 函数只表达有生产用途的公开业务能力;私有纯业务规则属于同领域 Domain。静态拒绝可证明无生产调用的私有函数、没有增加契约的纯转发以及只透传 Model CRUD 的 App 包装;应用构建还应拒绝不可达的 App 能力,测试调用不算生产用途。结构相似但业务含义可能不同的实现先保留警告,不用函数数量或行数配额。
- 保留 JSON envelope:成功 HTTP 200、`code=0`、`message="ok"`;错误使用 HTTP status 对应数值 code,`data=null`。
- GET/DELETE 接受 wire-safe scalar 和 nullable query 参数;POST/PUT JSON 使用静态类型请求体,不接受动态字段名或任意查询 Map。
- POST multipart 接受一个包含受限 scalar/Upload 字段的 static record;JSON 和 multipart 模式由签名静态决定且互斥。
- Runtime 隐式传递请求上下文,不在业务函数签名中声明 `dever.api.Context` 参数,也不允许存储或输出。API 的受控能力提供 request id、method/path、受信任边界内的 client address、受限 Header 读取、Text/Secret Cookie 读取,以及受控 response Header/Secure Cookie 写入;身份解析仍由业务实现。
- Header name/value严格校验;禁止应用设置Content-Length、Transfer-Encoding、Connection和原始Set-Cookie。Cookie属性使用typed API并限制Domain/Path/SameSite/Secure/HttpOnly/Max-Age。
- 自定义 API handler 必须把业务失败显式映射为标准 `dever.api.Error`:Invalid/Unauthorized/Forbidden/NotFound/Conflict/TooManyRequests。生成的 REST 使用固定输入、归属和数据库错误映射;未捕获失败返回通用 500 并只写脱敏结构化日志。
- API response继续直接返回静态业务值,不暴露 `dever.http.Response`,不允许自定义绕过envelope的JSON。
- Upload采用流式读取到runtime-owned临时文件或等价有界spool,不将大文件整体复制到内存;限制总字节、单文件、part数、字段数、header和filename长度。
- Upload filename不作为路径,MIME只作声明且需业务/Adapter复核;拒绝路径穿越、重复字段和不完整multipart。
- Upload handle为affine资源:必须由批准storage操作消费或显式close;正常、失败、timeout、取消和进程退出均清理未接管临时文件。
- 登录、Session 和资源级授权由项目 App/Domain 实现;接口 RBAC 由官方 auth runtime 自动执行,不增加业务 middleware。
## Acceptance Criteria
- [x] `rest` 自动生成 GET 列表/详情、POST、PUT、DELETE;显式动词/CMD 和固定 envelope 在 Dever 与 Markdown 源码中一致,DateTime 等标量按 shared codec 格式。
- [x] 路径、角色、Model 自动识别、字段绑定 Effect、`owner` 的所有 REST 读写范围和无歧义规则均有正反例;手写 App Model 调用不受 REST 字段绑定的隐藏影响。
- [x] GET 写入被静态拒绝,POST/PUT/DELETE 及 CMD/Job 写调用链的提交、错误回滚和取消行为有定向验证。
- [x] POST/PUT record 和 GET/DELETE query/path 严格验证 duplicate/unknown/missing/type/size;非法输入返回 400 固定 envelope。
- [x] 请求上下文由 runtime 隐式传递,客户端无法伪造;源码不得声明、存储或返回原始 Context。
- [x] Cookie parse/write覆盖Secure/HttpOnly/SameSite/Path/Max-Age,header injection和受禁header拒绝。
- [x] Secret session token不能日志/JSON/string泄漏,但可通过typed secret cookie sink发送。
- [x] 标准API Error映射到400/401/403/404/409/429;未映射App failure返回脱敏500。
- [x] multipart正常上传不全量驻留内存;越界、畸形、取消和handler失败后无临时文件残留。
- [x] 手写 API 只绑定同领域 App;自动 REST 仅通过编译器生成的 Model 路径。认证示例由业务解析会话并提供只读身份能力。
- [x] HTTP/1和HTTP/2已有deadline、request id、connection drain回归不退化。
- [x] Model/API 根文件和主题目录共存、其他 role 冲突、无 main 的 run/build/CMD JSON、缺少入口以及无意义私有/App 包装都有正反测试;合法单用途业务规则和库检查不被误拒绝。
## Out Of Scope
- GraphQL、gRPC、WebDAV和任意动态路由。
- 外部 IAM、OAuth Provider、直接用户权限、deny、角色继承和复杂策略语言。
- front CORS策略和浏览器页面。
- 断点续传、分片合并、病毒扫描和对象存储协议;由后续业务Port/Adapter扩展。
@@ -1,26 +0,0 @@
{
"id": "production-api",
"name": "production-api",
"title": "Dever 生产 API 边界",
"description": "补齐结构化请求、请求上下文、Cookie、响应 Header 与受限上传能力",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-20",
"completedAt": null,
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-20-production-application-language",
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused verification and no-full-suite policy"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Diagnostics, failure propagation and cleanup review"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Owner boundaries and duplicate implementation review"}
@@ -1,113 +0,0 @@
# Dever 生产级大型应用语言支持设计
## 1. Delivery Shape
父任务只拥有跨阶段合同、子任务顺序和最终集成验收,不直接实施源码。六个子任务按下列依赖执行:
```text
secure-data-contracts ---+
+--> port-adapter --------+
time-typed-codec --------+ +--> large-cms-acceptance
+--> durable-jobs --------+
+--> production-api ------+
```
Port 的类型化配置依赖 Secret 和 typed codec;Job 依赖 DateTime、typed codec 和数据库事务;生产 API 依赖 Secret、typed codec 和既有 HTTP 引擎。CMS 只在所有语言能力分别通过后扩展。
## 2. Source Architecture
最终角色矩阵:
| Caller | Allowed application targets |
| --- | --- |
| Generated entry | configured API/Job startup and shutdown |
| handwritten API | own-domain App |
| generated REST | own-domain Model operations under checked field/access rules |
| Job | own-domain App |
| App | own Domain, own Model operations, own Port, any App |
| Domain | own Domain |
| Model | no business functions; field bindings are checked at generated REST entry |
| Port | declarations only |
| Adapter | implemented Port target, same-file helpers, bundled packages |
| Test | same-domain App/Domain, any App, test-local Port fakes |
Type visibility is narrower than call visibility. App contracts may use public business records and Model IDs, but cannot expose private Model fields, Adapter/Domain implementation types or non-observable runtime resources. Port types remain domain-private. Job/API types must pass the shared wire-schema validator.
## 3. Shared Static Value Schema
The checker derives one internal wire schema from existing concrete `Type` and `Definition` data. It is compiler metadata, not a source-visible reflection API. Three emitters consume it:
- setting decoder: strict JSON object to concrete Adapter/runtime settings;
- Job codec: concrete payload to/from persisted JSON bytes plus schema fingerprint;
- API codec: query/JSON request decode and response encode.
Private fields, Secret and resources are represented in the schema policy, not rediscovered by each emitter. The runtime may reuse bounded JSON token parsing and scalar conversion, but generated programs retain concrete Rust values; no universal application `Value` interpreter crosses business calls.
## 4. Secure Values And Model Privacy
`Secret` is an opaque source scalar with no Render, comparison, ordinary JSON, Map-key or Model storage implementation. Approved constructors are secure random/token generation and typed setting/API decoding. Approved consumers are password/HMAC operations and narrowly typed transport sinks such as secure Cookie or bearer-auth construction. Secret cannot be converted to Text by a generic helper.
Model `private` fields remain stored normally but receive domain-owned access metadata. Generated CRUD blocks in the owning App may assign them; field projection is available only to that App. A type containing a private Model field is rejected at App public signatures, API/Job wire boundaries and test assertions. This keeps the existing Model record internally useful without adding DTO directories; App declares explicit view records beside capabilities.
Password hashing uses a maintained Argon2id implementation with runtime-selected secure salt and encoded hash text. Verification accepts Secret plaintext plus stored private hash and returns Bool/failure without exposing intermediate bytes. Security parameters are named runtime constants with tests, not application configuration knobs.
## 5. Port And Adapter Dispatch
Port role declarations use bodyless function signatures and explicit allowed failure choices. Adapter/Test sources may define a qualified Port implementation; ordinary unqualified functions remain local helpers. The checker builds one Port contract and statically verifies implementation inputs, named outputs and effective failure subsets.
Single implementation binds directly. Multiple implementations receive a compiler-generated closed enum and direct match; `config/setting.json` selects one stable implementation identity. This is runtime selection among compiled functions, not dynamic linking or trait-object business dispatch. Only the selected implementation's setting object is required and decoded.
Test-local fakes are associated with the owning TestCase. The single compiled test suite contains all fakes, while the selected case installs only its own compile-checked binding before entry. Production bindings and project deployment settings are not read by application tests.
## 6. Durable Job Data Flow
Each Job contract has a stable logical identity, concrete input shape, schema fingerprint, logical database binding, timeout and retry policy. Job inputs are zero or one wire-safe record; outputs are empty. Job handlers are registered entries and cannot be called as ordinary functions.
```text
App write call under an automatically owned entry transaction
-> typed enqueue/schedule(job target, payload, dedupe key)
-> hidden transaction context writes _dever_job row
-> business rows and job row commit together
Job worker
-> atomically claims due row with lease
-> decodes exact schema
-> calls own-domain App
-> marks success, schedules retry, or moves to dead-letter
```
SQLite uses a short write transaction and compare-and-update claim. PostgreSQL uses row locking/skip-locked semantics and the existing typed driver boundary. Leases have owner and expiry so a terminated worker does not permanently lose a task. A completed external side effect followed by process failure may execute again; this is documented at-least-once behavior.
Immediate and one-shot scheduled tasks are inserted by App calls. Recurring UTC schedules are compile-time validated declarations and materialize deterministic slot keys so multiple workers do not create duplicate occurrences. Payload signature drift never falls back to best-effort decoding; incompatible work is retained with an explicit failure reason.
## 7. Runtime Modes And Shutdown
`config/setting.json` gains strict runtime/job sections. A single compiled program supports:
- `api`: API server active, Job worker does not claim work;
- `worker`: Job worker active, API listener is not opened;
- `all`: both active for local/small deployments.
The target source contract no longer requires `main.dever` or source-level `serve()` calls. The compiler generates API/Job startup for the configured mode and owns their bounded group, shutdown and resource cleanup. Disabled services return without opening resources. This is a planned migration; the current compiler still uses Main.
The process signal owner is runtime-level and broadcasts to active services. API closes its listener and reuses existing connection drain. Job stops polling/claiming and drains active leases to its deadline. The generated entry then closes databases and flushes logs. Service failure cancels and drains its Group siblings.
## 8. Production API
API route discovery remains compiler-owned. The target syntax uses `get/post/put/delete/cmd/rest`; `rest` lowers plain CRUD to the same-domain Model, while custom actions bind the same-domain App signature. POST/PUT accept typed bodies; query routes accept named wire-safe scalars. A request Context is hidden in the runtime rather than declared as a source parameter. Validated transport observations and bounded Cookie/Header mutation are available through controlled API capabilities; no raw request/response handle reaches App. The detailed contract is owned by the `09-20-production-api` child task.
Handler success is encoded into the existing `{code,message,data}` envelope. Recoverable App failures must be captured and mapped to a standard `dever.api.Error`; uncaptured failures produce a generic internal envelope and structured, secret-free log. This keeps business errors independent of HTTP while allowing explicit 400/401/403/404/409/429 mapping.
Uploads are streamed to an owned temporary file under runtime data/tmp with checked total size, part count, filename and MIME metadata. API receives an opaque upload handle/metadata, and a storage Port consumes it. Drop, cancellation and handler failure remove unclaimed temporary files. The CMS does not expose arbitrary filesystem paths.
## 9. CMS Integration
The maintained CMS is the cross-feature acceptance application, not a second implementation of framework logic. Plain and Markdown forms retain equal normalized declarations. Plain CRUD uses generated REST; domains own explicit App views and commands when business behavior requires them. No dto/service/repository folders are introduced.
Authentication uses Secret password input, private password hash storage, hashed session token storage and secure Cookie output. Media uses a storage Port with a local production Adapter and test fake. Publishing writes the state transition, audit row and scheduled Job atomically. Job execution rechecks state/idempotency before publishing and can retry safely.
## 10. Compatibility And Rollback
This language is pre-release and repository callers migrate atomically; no legacy aliases, environment fallback, old JSON date encoding or dual role matrix remains. Each child task is a rollback boundary. A child is not accepted while canonical docs, Markdown contracts or maintained CMS callers use two protocols.
No database destructive migration is run against user data during compiler tests. Runtime private Job schema uses additive/versioned initialization and test-owned databases. Real PostgreSQL verification requires an explicitly configured isolated database.
@@ -1,4 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Source roles, visibility, effects, API and application contract baseline"}
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Model privacy, transactions, settings and SQLite/PostgreSQL invariants"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Official package, native runtime and lifecycle contracts"}
{"file":".trellis/spec/backend/logging-guidelines.md","reason":"Secret-free logging and HTTP request boundary"}
@@ -1,90 +0,0 @@
# Dever 生产级大型应用语言支持执行计划
## Start Gate
- [x] 用户审阅并明确批准本 PRD、设计和执行顺序。
- [x] 父任务保持 planning;不把父任务作为实现目标。
- [x] 依次完善并启动拥有当前交付物的子任务。
- [x] 开始每个子任务前读取 `trellis-before-dev`、子任务文档和 backend specs。
- [x] 保留现有脏工作区和历史任务,不提交、不归档、不清理用户改动。
## Child Order
### 1. secure-data-contracts
- [x] Model private 字段的领域所有权和公共边界。
- [x] Secret 类型、批准 sink、泄漏拒绝。
- [x] secure random、Argon2id、SHA-256/HMAC、常量时间验证。
- [x] 删除应用 `dever.process.environment`,迁移仓库调用方和文档。
- [x] 定向安全、Model、API/log 边界测试。
### 2. time-typed-codec
- [x] UTC DateTime/Date/Time/Duration API 和无溢出运算。
- [x] 共享静态 wire schema 与具体编码/解码生成。
- [x] 严格 JSON、日期格式、大小/深度和私密/Secret 拒绝。
- [x] 迁移当前 API response codec,保持 envelope。
- [x] 定向 codec、API、Markdown、ORM 表示回归。
### 3. port-adapter
- [x] Port bodyless contract、明确 failure contract 和 formatter/Markdown 文档。
- [x] Adapter/test fake 实现检查与静态 dispatch。
- [x] 收紧角色调用矩阵和类型可见性。
- [x] setting.json 多实现绑定、Adapter-local typed setting 和 Secret 字段。
- [x] 单实现、多实现、错误绑定、fake 隔离和 effect/failure 回归。
### 4. durable-jobs
- [x] Job role、声明、目标引用、payload schema 和 effect 分析。
- [x] SQLite/PostgreSQL 私有 schema、原子入队、claim/lease/retry/dead-letter。
- [x] immediate、run-at、UTC recurring schedule 和 schema drift 行为。
- [x] `dever.job.serve()`、runtime mode、signal/drain 和数据库关闭顺序。
- [x] 应用测试隔离、SQLite 端到端和 PostgreSQL 定向编译/可选隔离验收。
### 5. production-api
- [ ] POST record/query scalar decode 与共享 codec 接入。
- [ ] 注入 Context、Cookie/Header/request metadata。
- [ ] 标准 API Error 映射和未映射失败脱敏。
- [ ] 受限上传、临时文件生命周期和 storage Port 路径。
- [ ] 路由、输入、错误、上传取消和 HTTP drain 回归。
### 6. large-cms-acceptance
- [ ] 扩展 plain/Markdown CMS 领域和等价文档。
- [ ] 实现账户、会话、权限、内容、修订、分类、媒体、发布和审计业务。
- [ ] 接入 secure values、Port/Adapter、Job、API 和 runtime all mode。
- [ ] 增加等价应用测试和无外部服务 fake。
- [ ] 执行最终定向 check/test;任何 run/build 或持久服务验证先向用户说明。
## Cross-child Gates
- [ ] 每个阶段的公共合同只有一个 owner;JSON、配置、Job payload 不复制类型转换。
- [ ] 新增 Intrinsic 时 checker、effects、native emitter、reference policy 和官方 package 同步穷尽。
- [ ] `.dever` 与 `.dever.md` 使用同一 AST/check/HIR,不增加 Markdown 专用语义。
- [ ] 所有长期测试位于仓库根 `test/`,一次性 fixture 在交付前删除。
- [ ] 任何配置只来自 `config/setting.json`;搜索确认无应用环境变量兼容分支。
- [ ] 不添加 front、分发、外部队列、动态插件或内置 RBAC。
## Focused Validation Policy
每个子任务在自己的 `implement.md` 中列出具体命令。共同允许:
```bash
cargo check --offline -p dever-core -p dever-runtime -p dever-cli
cargo test --offline -p dever-tests --test <affected-target>
git diff --check
python3 ./.trellis/scripts/task.py validate <child-task>
```
如 rustfmt/clippy 组件存在,再运行受影响 package/target 的检查;组件缺失时如实记录。默认不运行 workspace 全量测试、性能套件、真实 PostgreSQL、front build、全量应用 build 或持久服务。
## Parent Completion Gate
- [ ] 六个子任务分别完成质量检查、规范同步和归档。
- [ ] 父任务重新核对所有 Requirement 与跨子任务数据流。
- [ ] CMS plain/Markdown 的 schema/API/Job/Port/settings/test 快照一致。
- [ ] 搜索确认不存在旧环境变量入口、App 直调 Adapter、公开私密字段或无界后台任务。
- [ ] 汇总实际通过、跳过和受阻的验证,不把定向测试描述成全量证明。
- [ ] 未经用户明确授权不创建 Git commit。
@@ -1,102 +0,0 @@
# Dever 生产级大型应用语言支持
## Goal
在不引入 front、编译器分发或任意动态插件系统的前提下,补齐 Dever 编写生产级大型后端所需的安全数据、时间与静态编解码、Port/Adapter、持久化 Job、生产 API 和大型 CMS 验收能力。最终应用保持路径驱动、静态类型、显式副作用和 `config/setting.json` 单一部署配置来源,AI 生成代码不能通过新增随意目录、直接调用实现层或脱离作用域的后台任务绕过架构边界。
## Background
- 当前应用架构已经支持 `main/app/domain/model/port/adapter/api` 路径角色,但 `port` 仍拒绝所有声明,尚无 `job` 角色。
- 当前 App 可以直接调用同领域 Adapter,Domain/Model/Port/Adapter 之间的调用范围也过宽,不能形成真实可替换边界。
- 当前 Model 拒绝私密字段;CMS 的密码哈希仍是普通公开 Model 字段。
- 当前 API 只接收 Text/Int/Bool 参数,无法安全承载登录、Cookie、结构化输入和上传。
- `config/setting.json` 的 `app`、`log` 对象当前没有运行时合同,应用仍能调用环境变量读取函数。
- DateTime 等类型已有数据库表示,但缺少 UTC 构造、解析、格式化和运算;API 输出仍使用内部整数表示。
- HTTP 底层已有 listener close 和连接排空;本任务只补进程信号、API/Job 组合生命周期,不重写 HTTP 引擎。
- 应用测试体系已经具备根 `test/`、类型安全断言、独立进程、隔离 SQLite fixture 和单套件编译缓存,可承接新能力的长期回归。
## Requirements
### R1. Stable Application Architecture
- 应用源码角色最终限定为 `app`、`domain`、`model`、`port`、`adapter`、`api`、`job`;不增加 service、repository、controller、dto、middleware、common、shared、internal 或 manager 目录。
- 手写 API/Job 只调用同领域 App;自动 REST 由编译器生成同领域 Model 操作;App 调用同领域 Domain/Model/Port 和其他领域 App;Domain 只调用同领域 Domain;Adapter 只实现 Port 并调用标准库或自身私有辅助函数。`deverc run/build` 的应用入口由编译器生成,不要求源码 `main.dever`。
- Model 不声明业务函数;Port 不含实现;API 和 Job 不承载业务规则;跨领域只能调用 App。
- 单文件与同名角色目录继续互斥;除 API 可按 URL 嵌套外,角色主题目录保持平铺且单主题文件不得机械拆分。
### R2. Secure Data And Configuration
- Model 支持领域私密字段;只有拥有领域的 App 和编译器生成的 Model 代码可以读写,其他领域、API、Job、测试和公开 App 合同不能观察该字段。
- App/API/Job 输出和日志不能包含私密字段或 Secret;静态检查必须在生成本地代码前拒绝。
- 提供不渲染、不可比较、不可序列化到普通 JSON 的 Secret 值,以及安全随机 token、Argon2id 密码 hash/verify、SHA-256/HMAC 和常量时间验证能力。
- Secret 只能进入编译器认可的安全 sink;不得通过字符串拼接、日志、普通 HTTP Header 构造或错误信息隐式解密。
- 部署配置只来自可执行文件相邻的 `config/setting.json`。移除应用层环境变量读取;命令行参数保留为显式程序输入,但不能覆盖应用配置。
- Adapter 和运行时配置具有静态 schema、未知字段拒绝、Secret 字段保护和启动前验证;App/Domain 不直接读取部署配置,业务可变数据继续放 Model。
### R3. Time And Shared Typed Codec
- 后端时间统一使用 UTC;提供 DateTime 当前时间、RFC3339 解析/格式化、Date/Time 解析/格式化和 DateTime/Duration 安全运算。
- 数据库存储继续使用整数毫秒,不改变既有 SQLite/PostgreSQL 列表示。
- 编译器建立一套静态类型值 schema 和具体代码生成路径,供 Adapter setting、Job payload、API 输入/输出共同使用;不得增加运行时反射 Value 解释器。
- JSON 解码拒绝重复键、未知字段、越界数值、错误日期和过深/过大的输入;编码拒绝私密字段、Secret、资源、Task、Stream 和非有限 Float。
- DateTime 的外部 JSON 表示为 UTC RFC3339 字符串,Decimal/Id/Uuid 保持无损文本表示,Duration 明确使用整数毫秒。
### R4. Port And Adapter
- Port 源只声明类型、签名和允许失败,不包含函数体;App 通过 Port 调用外部边界。
- Adapter 显式实现 Port,签名必须完全匹配,实际失败必须落在 Port 声明的集合内;不依赖运行时 trait object 或动态代码加载。
- 单实现自动选择;多实现由 `config/setting.json` 按稳定 Port 身份选择。缺失、未知或不完整绑定在启动前失败。
- 每个 Adapter 可声明自身类型化 setting;未选择 Adapter 的配置不得强制存在。
- 应用测试可为可达 Port 提供用例私有 fake;fake 不进入生产源码、部署配置或其他测试用例。
### R5. Durable Job
- 新增 `job.dever` 或平铺 `job/*.dever`;Job 声明为非源码可调用的后台入口,只能调用同领域 App,输入必须是可持久化静态类型,输出必须为空。
- 支持立即任务、指定 UTC 时间任务和编译期校验的 UTC 周期计划。
- Job 采用数据库持久化、租约领取、并发上限、执行超时、有界退避重试、dead-letter 和崩溃后租约恢复;语义明确为至少一次执行。
- Job payload 保存稳定任务身份和 schema 指纹。部署后遇到不兼容 payload 时进入明确失败状态,不静默误解码。
- App 事务内入队必须复用同一逻辑数据库事务;Job 与业务事务连接不一致时编译失败,避免业务提交成功但任务丢失。
- 任务表、租约和调度状态属于运行时私有 schema,不要求业务创建 Job Model。
- 同一产物支持 `api`、`worker`、`all` 三种运行模式,仅由 `config/setting.json` 选择;编译器生成对应的 API/Job 启动与排空入口,不要求应用手写 `main()` 或 `serve()` 调用。
- SIGTERM/Ctrl-C 停止接收新请求和领取新任务,在配置的期限内排空 HTTP 与在途 Job,再执行数据库和日志关闭。
### R6. Production API Boundary
- 使用 `get/post/put/delete/cmd/rest` 声明,保留固定 `{code,message,data}` JSON envelope,不返回低层 `dever.http.Response`。唯一主 Model 的 `rest` 自动生成受约束的 CRUD,手写 API 绑定同领域 App。
- POST/PUT 支持静态类型请求体;GET/DELETE 支持完整可表示标量的 query/path 输入;复用共享 typed codec 和严格未知字段检查,不暴露动态查询 Map。
- Model 可为自动 REST 声明受 Effect 约束的字段来源和 `owner` 行范围,普通字段由编译器推导;这些绑定不隐式改变手写 App 的 Model 操作。
- 请求 Context 在 runtime 隐式传递;受控 API 能力读取 Header/Cookie/request ID/client address,并设置 Cookie/响应 Header。原始 Context 不出现在源函数签名,也不得存储或输出。
- GET 调用链禁止数据库写入;POST/PUT/DELETE 和 CMD/Job 的写调用链自动建立事务,业务源码无需重复标记 `transaction`。
- API 失败必须显式映射到标准 API Error,未映射业务失败继续视为 500 并脱敏记录。
- 上传使用有总量、单文件、字段数、MIME 和文件名限制的流式/临时文件边界;拒绝路径穿越,临时资源在成功、失败和取消后均清理。
- 鉴权、会话和授权属于业务 App/Domain;不增加 middleware 业务层或框架内置 RBAC。
### R7. Large CMS Acceptance
- 同步维护 `examples/cms/dever` 与 `examples/cms/md`,两者具有等价声明、Model schema、API、Job、Port binding 和测试合同。
- CMS 至少覆盖 user/account、user/session、user/access、content/article、content/revision、content/category、media/asset、publishing/schedule 和 audit/operation。
- 验证密码 hash、Secret token、私密字段、会话 Cookie、权限判断、媒体存储 Port、fake Adapter、事务内发布任务、定时发布、失败重试和审计记录。
- CMS 不引入 front;普通 CRUD 只使用编译器生成的 `rest`,不写透传 App/API 函数。登录、命令、上传等业务入口仍通过手写 API 绑定 App。
## Acceptance Criteria
- [ ] 非法角色路径、App 直调 Adapter、Domain 访问 Model/Port/Adapter、API/Job 跨域调用等在 `deverc check` 阶段稳定拒绝。
- [ ] Model 私密字段可由所属 App 持久化和验证,但无法进入 App/API/Job 输出、日志、其他领域或测试观察面。
- [ ] Secret、密码和安全随机能力有成功、错误和泄漏拒绝回归;应用源码无法读取环境变量配置。
- [ ] UTC 时间和静态 typed codec 对 `.dever`/`.dever.md` 结果一致,API、setting 和 Job 不维护平行编解码逻辑。
- [ ] Port 单实现、多实现配置选择、缺失绑定、签名/失败不匹配和测试 fake 均有正反测试。
- [ ] SQLite Job 可验证原子入队、租约恢复、延迟/周期调度、重试、dead-letter、payload 不兼容和优雅停止;PostgreSQL 代码路径通过定向编译,真实 PostgreSQL 只在隔离配置存在时验收。
- [ ] `api`、`worker`、`all` 三种模式只读取 `config/setting.json`,无环境变量或命令行配置覆盖。
- [ ] 生产 API 验证结构化输入、标准错误、Cookie/Header、请求上下文和受限上传清理,仍保持固定 JSON envelope。
- [ ] Dever 与 Markdown CMS 的 check、test 和行为合同一致;最终定向运行验证不依赖 front 或外部服务。
- [ ] 每个子任务只运行对应最小离线检查;不运行 workspace 全量测试、默认性能压测、真实外部服务或未说明的持久服务。
## Out Of Scope
- front、Page JSON、React 插件和浏览器界面。
- 私有编译器分发、交叉编译、自托管、包注册表和动态插件加载。
- 内置用户/RBAC/租户业务模型;这些由 CMS App/Domain 实现。
- Exactly-once 外部副作用保证;Job 明确采用至少一次语义,业务动作负责幂等。
- 任意 cron 时区和 DST 规则;周期计划首版固定 UTC。
- Redis、Kafka、RabbitMQ 或外部队列依赖;首版复用 SQLite/PostgreSQL。
- 全量测试、默认真实 PostgreSQL、64/128 MiB 压测和 front 构建。
@@ -1,34 +0,0 @@
{
"id": "production-application-language",
"name": "production-application-language",
"title": "Dever 生产级大型应用语言支持",
"description": "补齐 Port/Adapter、安全基础、持久化 Job、生产 API 与大型双源码 CMS 验收,front 和编译器分发延期",
"status": "planning",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-20",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [
"09-20-secure-data-contracts",
"09-20-time-typed-codec",
"09-20-port-adapter",
"09-20-durable-jobs",
"09-20-production-api",
"09-20-large-cms-acceptance",
"09-29-external-libs"
],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused compiler/runtime test selection"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Typed crypto failure and diagnostic rules"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Single owner across checker, emitter and runtime"}
@@ -1,29 +0,0 @@
# Dever 安全数据合同设计
## Model Privacy
复用现有字段 `private` 语法,不增加 Model 专用修饰符。类型检查为字段记录声明 owner 的 domain identity:普通 record private 继续 package-owned,Model private 改为 domain-App-owned。Model 生成 CRUD 的匿名 create/update block允许所属 App 写 private 字段,但投影、记录构造和公开边界仍由统一字段访问检查控制。
App 是跨领域能力边界,因此任何直接或嵌套包含 private Model 字段的 nominal record 都不能出现在 App 参数、输出或可传播错误中。Model ID 和不含 private 字段的显式 View 仍可公开。API/Job/wire 检查复用同一 `observable` 类型属性,不再各自遍历字段猜测。
## Secret Type
Secret 是独立非泛型标量,内部持有受保护字节,不实现普通 Render/Clone-to-Text/JSON。值语义允许受控传参和 record 字段,但以下静态能力为 false:comparable、Map key、persistent、ordinary wire output、fault rendering。
构造来源:
- `dever.crypto.token(bytes)`;
- 后续 typed setting/API Secret 字段解码;
- 必要的测试固定 Secret 构造仅存在于 compiler-owned Test source,不进入生产标准库。
消费 sink 由 Intrinsic 精确枚举,不能通过一个通用 reveal 函数扩散。后续 API/Adapter 新 sink 必须在 checker effect/role policy 中显式加入。
## Crypto Runtime
使用固定版本的成熟 Rust crates 实现 Argon2id、HMAC/SHA-256 与 constant-time 比较;随机源复用 OS getrandom。Argon2id 参数是经过测试的运行时常量,避免应用通过 setting 降低成本。hash 输出为标准 PHC Text,verify 对格式错误返回显式失败而非 panic。
标准 `.dever` package 定义公开 Result choice 和简单函数;runtime 只承载密码学实现。checker 校验官方 choice shape,native emitter 复用现有 resource-result 模式,不创建第二套失败协议。
## Environment Removal
从官方 `dever.process`、Intrinsic、checker effect、native emitter 和 runtime system bridge 同步删除 environment。仓库调用方整体迁移到 setting.json 或测试显式输入,不添加 feature flag 或旧名兼容。
@@ -1,4 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Private fields, public boundaries and type properties"}
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Model field schema and generated CRUD ownership"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Official package and intrinsic implementation rules"}
{"file":".trellis/spec/backend/logging-guidelines.md","reason":"Secret-free render and log requirements"}
@@ -1,71 +0,0 @@
# Dever 安全数据合同实施计划
## Start Gate
- [x] 用户批准父任务最终规划后启动本子任务。
- [x] 读取 compiler-contracts、database、logging、toolchain/library specs。
- [x] 记录当前 private 字段、Model field access、Render/JSON/log 和 process intrinsic 所有调用点,见 research/secure-contracts-source-map.md。
## Implementation
- [x] 扩展类型属性和字段访问 owner,允许 Model private 并限制为所属 App。
- [x] 在现有 App/API/入口/错误/测试边界复用 observable 检查;Job 的消费检查由 durable-jobs 子任务接入相同属性。
- [x] 增加 Secret 类型、语法类型解析、HIR/native/reference 表示和禁止能力。
- [x] 增加 crypto 官方 package、Intrinsic、runtime 实现和依赖;保证失败类型化与错误脱敏。
- [x] 删除 environment 官方函数、Intrinsic/runtime 分支、effect 分类、文档与调用方。
- [x] CMS 账户使用 private password_hash: Text(255),AccountView/ArticleView 隐藏私有值及作者关联;两套源码同步,没有新增登录 API。
- [x] 更新 LANGUAGE/Markdown/compiler/database/logging canonical docs。
## Focused Verification
```bash
cargo check --offline -p dever-core -p dever-runtime -p dever-cli
cargo test --offline -p dever-tests --test model_syntax
cargo test --offline -p dever-tests --test source_visibility
cargo test --offline -p dever-tests --test contract_semantics
cargo test --offline -p dever-tests --test standard_library
cargo test --offline -p dever-tests --test logging
cargo test --offline -p dever-tests --test markdown_source
git diff --check
```
如组件存在,增加受影响 package/target 的 rustfmt 和 `clippy -D warnings`。不运行全量测试、真实数据库、服务或性能测试。
## Review Risks
- private Model 字段不能破坏生成 SQL 列顺序、迁移 fingerprint 或 row decode。
- Secret 不得因派生 Render/Debug、错误路径或 generic collection 自动获得观察能力。
- crypto 失败不得包含输入;随机源和 password verify 不得 panic。
- 删除 environment 后必须穷尽 checker/emitter/reference/effect match,不能留不可达旧分支。
## Implementation Notes — 2026-09-20
- Secret 位于基础 runtime/secret.rs,Clone 保持值语义、Debug 固定脱敏、zeroize 清除已持有字节;argon2/sha2/hmac/getrandom/subtle 由独立 crypto feature 控制。原生构建根据已裁剪代码中的 crypto 调用选特性,也覆盖直接 system intrinsic。
- Model 私有字段访问统一经过 Context::owns_private_fields。类型安全属性缓存沿 Related 关联图做单调反向传播,不把 ORM 关联环当成布局递归。
- Argon2 0.5.3 官方文档核对后采用 Argon2id v19 / Params::default;verify 在计算前限制 PHC 算法、版本、成本及结果长度。执行一次 cargo fetch,锁定 12 个新增依赖包;zeroize 复用已存在的 1.9.0。
- `secure_contracts` 新定向目标 8/8 通过(含 reference、原生与 Test source 执行)。覆盖隐私所有权/Model schema 不变、嵌套非观察边界、Related 自环、环境入口拒绝、密码错误/损坏/资源上限、SHA/HMAC 已知向量、token 独立性及脱敏 Debug。
- 编译检查 core/runtime/cli + crypto/reference 通过。首次验证因磁盘只剩 65 MiB 导致 source_visibility 链接 Bus error,随后 rlib 报 No space left on device;主会话清理 target/debug/incremental 后恢复验证。后续使用 `--config build.incremental=false --config profile.dev.debug=0 --config profile.test.debug=0`,始终 offline + locked,原生测试串行。
- rustfmt/clippy 组件未安装;没有安装全局工具。没有运行全量测试、真实 PostgreSQL、持久服务、CMS 打包运行或性能测试;OS 随机源不可用分支未做系统故障注入。
- contract_semantics 完整目标初次 6/12:6 个旧样例直接构造 error variant,在既有 `construct_variant_with_kind` 禁令处收到 C005;本次没有改该 error 构造语义,也未重写这些旧样例。最终报告保留未通过,不据此宣称全目标成功。
## Verification Results
| 定向验证 | 结果 |
| --- | --- |
| cargo check:core/runtime/cli + crypto/reference | 通过 |
| secure_contracts(crypto,reference,串行) | 8/8 通过 |
| model_syntax | 3/3 通过 |
| source_architecture | 8/8 通过 |
| source_visibility | 10/10 通过 |
| markdown_source | 20/20 通过 |
| standard_library | 5/5 通过 |
| logging | 1/1 通过 |
| differential::time_failures_are_source_defined_results | 1/1 通过;移除 environment 样例后保留显式 result 的时间失败 native/reference 一致性 |
| contract_execution | 首轮 7/9;两个 CMS 失败因关联传播要求 ArticleView,修复后 CMS parity 与 maintained root checker 各 1/1 通过;未重复整个目标 |
| contract_semantics | 完整目标 6/12;6 个既有 error 直接构造样例未重跑;当前 private_fields_protect_access_mutation_and_construction 精确用例 1/1 通过 |
| git diff --check / 本次文件尾随空白扫描 | 通过;项目源码目前未跟踪,因此额外执行显式文件扫描 |
| rustfmt/clippy | 未安装组件;rustfmt 启动失败,未安装或绕过 |
contract_semantics 未通过用例:failure_cannot_be_discarded_overwritten_logged_or_laundered、errors_can_be_returned_translated_or_explicitly_recovered、failure_summaries_follow_long_calls_and_unused_handler_dependency_cycles、nested_failure_survives_value_copies_and_projection、empty_traversal_does_not_handle_context_and_singleton_projection_transfers_failure、possibly_empty_or_null_outputs_cannot_hide_another_failure。首个诊断均为 C005 `error choice variants can only be used with fail`。
当前没有新配置入口、没有登录业务、没有真实数据库/全量/服务/性能验收。Model create/update/读取 private 字段通过静态检查与 schema 不变验证;未据此声称已完成真实数据库运行验收。
@@ -1,37 +0,0 @@
# Dever 安全数据合同
## Goal
让密码、令牌和部署密钥在 Dever 中具有编译器可识别的非观察边界,使大型应用能够存储私密 Model 字段、处理 Secret 和调用成熟密码算法,同时彻底移除环境变量配置入口。
## Dependencies
- 无前置子任务;这是 Port setting、认证 API 和 CMS 会话的基础。
## Requirements
- Model `private` 字段由所属 component/domain 的 App 拥有;生成的 Model CRUD 可以持久化,所属 App 可以访问,其他角色和领域不能访问。
- 含 private Model 字段的完整 Model 不能出现在 App 输入/输出、API/Job wire 输出、日志或跨领域类型观察面;App 使用显式 View record。
- 新增 opaque `Secret` 标量:不可 Render、比较、拼接、普通 JSON 编码、Map key、Model 存储或错误 payload。
- Secret 只允许进入批准 sink:Argon2id hash/verify、HMAC、常量时间验证、后续受控 Cookie/bearer setting sink;不提供通用 Secret-to-Text。
- 提供操作系统 CSPRNG token、Argon2id 密码 hash/verify、SHA-256、HMAC-SHA256 和常量时间字节/摘要验证;使用成熟依赖,不自实现密码算法。
- 密码哈希存储为 private Text,明文密码使用 Secret;错误不得包含明文、密钥或摘要原文。
- 删除 `dever.process.environment` 及对应 intrinsic/runtime bridge、文档和测试;不保留别名、fallback 或 deprecated 路径。
- `dever.process.arguments` 保留,但不得成为 setting override;配置来源规则由后续 Port/Job/API setting 合同继续约束。
- `.dever` 与 `.dever.md` 使用相同语法/语义;所有正式测试放根 `test/`。
## Acceptance Criteria
- [ ] 所属 App 可创建、更新、读取 private Model 字段,并能用其验证密码;其他角色/领域的字段访问在 check 阶段拒绝。
- [ ] App 签名、API response、日志和普通 JSON 对 private/Secret 的直接或嵌套泄漏均被静态拒绝。
- [ ] Secret 不能被渲染、比较、拼接、存入 Model/Map key 或放进普通错误 payload。
- [ ] secure token 两次调用不复用值且使用 OS CSPRNG;无随机源时返回显式安全失败。
- [ ] Argon2id hash/verify 验证成功、错误密码、损坏 hash 和资源上限;SHA/HMAC 与已知向量一致。
- [ ] 搜索和负向编译测试确认应用无法调用环境变量读取,仓库没有兼容入口。
- [ ] 既有普通 record private 字段、Model schema、API envelope、日志和 Markdown 回归保持通过。
## Out Of Scope
- 通用密钥管理服务、密钥轮换协议和硬件安全模块。
- JWT/OAuth、会话业务模型、RBAC 和登录 API;由后续 API/CMS 子任务实现。
- 数据库列加密;private 表示可见性,不声称静态或磁盘加密。
@@ -1,70 +0,0 @@
# Research: 安全数据合同源码边界
- Query: Model private、Secret、密码学及 environment 移除的最小实现路径。
- Scope: internal
- Date: 2026-09-20
## Findings
### 字段与领域身份
- `crates/dever-core/src/syntax.rs:124` 的 AST `Field.private` 已存在;`parser.rs:517`、`:556` 解析普通 record 的 private,选择分支不允许 private。无需新增语法关键字。
- `check/symbols.rs:373` 将 AST private 原样落到 `types.rs:188` 的 checked `Field`。HIR 表达式直接持有 checked Type,因此不需要另造字段隐私 HIR。
- `types.rs:209` 的 `Definition` 已有 owner、kind;`DefinitionKind::Model` 可区分普通 record 与 Model。`source.rs:25` 的 SourceRole、`:84` 的 `SourceLayout::domain()` 是领域身份唯一来源,不应拆字符串判断归属。
- `check/model.rs:539` 当前硬拒绝 Model private;移除此拒绝后已有字段落地流程可保留 metadata。`synthesize_fields`(`:219`)仅插入公开 id/created_at,生成 Page/Cursor 及 Related 字段。持久 schema `ModelField` 不必承载语言隐私,因为数据库列格式不因访问策略改变。
- `check/expressions.rs:167` 的 `Body::field_type` 按 type.owner == context.owner 检查私有字段;写路径复用字段解析。`check/values.rs:41` 独立进行 record 构造授权。两处应改用同一 owner 检查:普通 record 保留 package 私有;Model 只允许同领域 App。可直接复用 `check/orm.rs:247` 对 SourceRole::App 和 layout.domain 的判定。
- `check.rs:506` 的 validate_public_boundaries 只拒绝 private nominal type,并跳过 public record 的 private 字段;它不会拒绝一个 public Model 内含 private 字段。`:557` 的 validate_exported_failures 只检查错误类型自身 public 标志,嵌套 Model 同样需要补覆盖。
- `check/api.rs:149` 的 json_type 已拒绝 record private,但自写递归,与 App 合同尚未共享。建议统一类型可观察性属性,再由 API 保留具体可编码标量白名单。
### Secret 与类型能力唯一所有者
- `types.rs:7` 的 Type 增加独立 Secret;`check/symbols.rs:300` 附近是源码内置标量解析入口;`native.rs:766` 附近是 Rust 表示映射;`reference/value.rs:17` 是 reference Value 枚举。不要用 Named record 或 Bytes 别名模拟 Secret。
- `types.rs:232` 的 Properties + `cache_properties` 已按 nominal DAG 缓存 comparable/transferable/movable。建议在此集中增加可渲染/敏感类型属性;避免在 API、App、fault 三处复制 nominal 图遍历。
- `check/symbols.rs:452` 的 map_key 是独立白名单,Secret 默认不匹配;`check/model.rs:1320` 的 normalize_field_type 是持久化白名单,Secret 默认不可持久化。无需把所有存储参数检查迁入通用 Properties;仅共享安全属性。
- 风险:`Type::Related` 目前直接返回 Properties::ALL(types.rs:92),且 referenced_types(check/symbols.rs:471)特意不展开 Related,避免合法关联 Model 环。敏感信息属性若沿用该路径会漏检,若简单递归则可能死循环。使用有界反向传播/固定点处理关联图,或者在对外可观察类型中拒绝 Related;不能影响现有布局/可比较缓存算法。
- `native/records.rs:40`、`:82` 无条件为 record/choice 生成 Render,逐字段调用 render_to,当前 private 字段同样被渲染。`reference/value.rs:134`、`:177` 同样逐字段展示。Secret 的静态禁令必须覆盖容器和选择 payload;不应靠运行期脱敏代替编译拒绝。
- 原生类型统一 derive Clone/Debug(native/records.rs:25);Secret Rust 表示可有安全 Debug 占位,但不能输出原始字节。不提供普通 Render 实现时,生成器需要避免为包含 Secret 的 nominal 类型生成不可编译 Render,同时错误/入口输出等 render sinks 必须先检查。
- 日志内置签名在 `check/intrinsics.rs:108`,当前接收 Text;应保持此边界,不增加任意值日志转换。JSON 标准库采用显式 JSON 类型,API 泛型序列化是需额外保护的边界。
### environment 完整删除链
1. `library/dever/process.dever:6` EnvironmentResult、`:23` environment 及两个 environment_value 分支。
2. `crates/dever-core/src/intrinsic.rs:26` ProcessEnvironment 枚举、`:140` process_environment 名称解析。
3. `check/intrinsics.rs:85` 签名及官方 EnvironmentResult shape 验证。
4. `contracts/effects.rs:549` process effect 分类,保留 ProcessArguments。
5. `native/intrinsics.rs:72` resource_result → `dever_runtime::process::environment`。
6. `reference/intrinsics.rs:69` recover → 同一个 runtime 函数。
7. `crates/dever-runtime/src/process.rs:16` environment 桥接函数。
8. `test/dever-tests/tests/standard_library.rs:77` process 输入测试及 `:115` 直接 runtime 错误测试;`differential.rs:331` environment 错误一致性样例。
9. `README.md:65` 文档明确宣传该入口,也需要同步删掉。
已有 argv arguments 是独立能力,保留。测试环境配置不要换成另一个应用环境变量入口;删除 environment 专项并增加旧 API/旧 intrinsic 被拒绝的定向断言。
### 依赖与密码学复用
- `Cargo.toml:36` 已精确锁定 getrandom = 0.2.17;runtime `orm.rs:86` 用 getrandom::getrandom。runtime Cargo.toml 当前只在 database feature 下启用,非数据库 crypto 不能依赖 database 才有随机源。
- Cargo.lock 中另有 getrandom 0.4.3(433)、hmac 0.13.0(490)、sha2 0.11.0(1059)、subtle 2.6.1(1143);这些只是已解析的间接依赖,不代表 runtime 可直接 import。
- Cargo.lock 已有 zeroize(1650);没有 argon2。采用 Argon2 crate 时需核对其 digest/password-hash/rand_core 版本,不应假定已锁 sha2/hmac 与任意版本 Argon2 相容。
- 官方包装遵循 process/time 的 Result choice + checker shape 校验 + native resource_result + reference recover;crypto 重用该协议。运行时集中到 crypto 模块;Secret 字节访问保持 crate 内部,禁止公共 reveal。
### 最小定向测试
- `contract_semantics::private_fields_protect_access_mutation_and_construction`(218)保留普通 record package-owner 基线。
- `source_architecture::model_operations_belong_to_the_owning_domain_app`(209)扩展 Model private 字段读写、同领域不同 App topic、Domain/API/Test/跨域拒绝、App 嵌套参数/输出/错误泄漏。
- `model_syntax` 覆盖 private 持久声明及 Secret 存储拒绝;`markdown_source` 验证 private 往返和两种源码一致。
- `contract_api` 覆盖快照不会暴露私有表示,以及公开边界敏感类型拒绝。
- crypto 新的根 test/dever-tests/tests 定向文件可以集中运行时已知答案、native/reference 一致性、Secret 禁止 Render/eq/Map-key/JSON/persistence;`test/dever-tests/Cargo.toml` autotests=false,必须注册新目标。
- `standard_library` 和 reference feature 下 `differential` 修改 environment 案例。优先 cargo test -p dever-tests --test <target> <filter>,无需真实数据库、服务、全量测试。
### Related specs / External references
- `.trellis/spec/backend/compiler-contracts.md`:字段隐私、App 边界、角色及有界类型图。
- `.trellis/spec/backend/database-guidelines.md`:Model 所有权、静态 schema、settings-only 配置。
- 当前任务 `design.md`:Model domain-App owner、opaque Secret、无环境变量兼容路径。
- 外部资料未访问;版本事实来自当前 Cargo.lock,密码算法参数/具体 crate API 需实施者按实际选定版本的官方资料核实。
## Caveats / Not Found
- 子会话 `task.py current --source` 返回 none;输出路径由父代理明确提供,未修改任何任务指针。
- 未执行构建、测试或服务;结论为源码研究,不是运行验收。
- 未发现可复用 Secret 或 argon2 实现。上述 Render 风险是当前实现事实;静态渲染入口完整枚举应在实现阶段继续检查 native entry/failure/assert 渲染,不能只修改 native record emitter。
@@ -1,26 +0,0 @@
{
"id": "secure-data-contracts",
"name": "secure-data-contracts",
"title": "Dever 安全数据合同",
"description": "实现 Model 私密字段、Secret、密码与安全随机能力,并移除应用环境变量配置入口",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "Antigravity",
"assignee": "Antigravity",
"createdAt": "2026-09-20",
"completedAt": "2026-09-20",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "09-20-production-application-language",
"relatedFiles": [],
"notes": "Model private/Secret/crypto/environment removal/CMS View contracts complete. Independent quality gate and final review found no blocker; focused checks and secure_contracts 8/8 passed. Known unrelated contract_semantics legacy failures remain documented.",
"meta": {}
}
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"JSON/time/API focused test matrix"}
{"file":".trellis/spec/backend/error-handling.md","reason":"Parse failure and source diagnostic behavior"}
{"file":".trellis/spec/backend/directory-structure.md","reason":"Single codec owner across consumers"}
@@ -1,28 +0,0 @@
# Dever 时间与类型化编解码设计
## Time Representation
保留现有 i64 毫秒运行时表示和数据库 codec。新增成熟、固定版本的日期时间库只用于严格解析、日历校验、UTC 格式化和 checked arithmetic,不手写日历算法。DateTime format 统一输出 `Z`,parse 可接受 RFC3339 offset 后转换 UTC。Duration 是有符号毫秒。
官方 `dever.time` 继续用普通 `.dever` Result choice 暴露失败,system intrinsic 返回具体 i64/Text。`unix_millis` 可保留为底层数值能力,但业务首选 `now() -> DateTime`。
## Shared Wire Schema
在 checker/HIR 边界新增一个只读的具体 wire schema 描述,节点仅包含已解析类型、record 字段顺序和策略标记。schema 构建复用 nominal cycle/depth 和 private/Secret 属性,不独立遍历 AST。
Schema policy 区分:
- ordinary/API input:可按协议解码,显式 Secret 字段生成 opaque Secret;
- ordinary output:必须可观察且无 Secret/private;
- setting input:允许 Secret,但不允许资源和业务 failure choice;
- persisted Job payload:要求跨进程稳定、无资源和 Secret。
Native emitter 为每个被使用 schema 生成具体转换函数并缓存 specialization。runtime 只提供重复键可见的 bounded JSON parser、标量 parse/format 和错误位置;业务值保持具体 Rust struct/list/scalar。
## API Migration
先将 `native/api.rs` 的 response `json_value` 迁移到共享 encoder,保持 envelope 构造仍归 API owner。输入扩展留给 production-api 子任务。迁移后删除原有平行 Type match,添加生成代码断言防止回退。
## Compatibility
数据库和 Model snapshot 不变。DateTime API JSON 从内部整数改为 RFC3339 是预发布协议的直接替换;仓库内 API fixture 同步迁移,不保留双格式输入输出。
@@ -1,3 +0,0 @@
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Type properties, private boundaries and generated contracts"}
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Official time/json packages and native runtime boundary"}
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Date/time storage representation and ORM compatibility"}
@@ -1,64 +0,0 @@
# Dever 时间与类型化编解码实施计划
## Start Gate
- [x] `secure-data-contracts` 完成并提供稳定 Secret/private 类型属性。
- [x] 用户已批准父任务规划。
- [x] 读取 compiler/toolchain/database/error specs 和当前 API codec、JSON runtime、DateTime ORM codec。
## Implementation
- [x] 增加 checked DateTime/Date/Time/Duration runtime 操作与官方 time API。
- [x] 增加共享 wire schema 类型分类、策略和 schema fingerprint 基础。
- [x] 增加 bounded strict JSON input parser/decoder 和 concrete value encoder。
- [x] 生成具体 scalar/list/record/nullable codec,缓存重复 schema。
- [x] 将 API response 迁移到共享 encoder,删除 `native/api.rs` 平行转换。
- [x] 保持 ORM storage codec/fingerprint 不变并增加回归。
- [x] 更新 LANGUAGE、Markdown、compiler/toolchain/database specs。
### 实施合同(2026-09-20)
- 固定 chrono 0.4.45,std-only;主代理统一 fetch 后全部定向检查使用 locked/offline。Date 是 UTC 午夜 epoch 毫秒,Time 是午夜起毫秒;RFC3339 offset 转 UTC,拒绝闰秒、超过三位小数和四位日历范围以外值。checked 算术仅检查 i64 溢出,超日历范围在格式化边界失败。
- `core/wire.rs` 是唯一类型允许表,policy 进入 identity;`native/wire.rs` 生成具体类型的双向 helper,API 按 identity 缓存。允许 Secret 的输入 policy 只生成 decoder。private/bounds/Choice/Map/ModelId/Related/resource 稳定拒绝;没有放行尚未实施的字段约束验证。
- runtime wire 使用借用 RawValue 保留原始 JSON 数字,逐层 visitor 检查重复键;16 MiB、64 层容器、65536 个值。concrete decoder 拒绝未知和缺失必填字段,缺失 nullable 得到 None。API envelope 只嵌入 Encoded,移除旧 response Value round-trip。
- 官方标准库原来没有 `.dever.md` 副本。经主代理确认,不新增同包重复源码;以 LANGUAGE 合同和 plain/Markdown 调用方同签名、同输出 parity 覆盖 Markdown 验收。
- 没有改 ORM/model fingerprint owner、配置来源、API 输入协议或 CMS 业务;无应用环境变量配置。
### 验证结果(2026-09-20)
- core/runtime/cli,api + reference,以及 postgres + api + reference 两次定向 cargo check:通过。
- 新 `time_codec` 最终 9/9 通过(40.39 秒)。覆盖实际编译运行的 concrete decoder、Decimal/Id/Uuid/负零/原始 Json 精度、缺失/未知/重复字段、无监听器 API dispatch、native/reference 时间成功和失败、plain/Markdown、预算与 policy。随后仅增强 Model 专项为当前 App → Model 调用,独立复验 1/1 通过:四种时间值仍编码为 ORM Int,存储 logical type 和 i64 表示不变。
- 既有 `api_routes` 7/7、`json_library` 7/7、`markdown_source` 20/20、`model_syntax` 3/3、`standard_library` 5/5 均通过。
- `sqlite_orm::shared_orm_fixture_remains_valid_without_opening_a_database`:失败,未打开数据库。`support/orm_fixture.rs` 的旧 fixture 在 `main.dever` 中直接调用 `user.create`,当前 `check/orm.rs::resolve_model` 只允许 Loose 使用 table 短名,当前 Main/App 角色合同不再接受该用法,报 C004。该 fixture 和角色/ORM owner 本次均未修改;按主代理确认记录现存不匹配,不扩大本任务迁移旧 fixture。其余 SQLite 执行用例未运行。
- `git diff --check` 通过;仓库主体为既有未跟踪文件,另对本次新增源码做显式空白和旧转换路径扫描。没有旧 `json_type`、`json_value` 或 API `parse_json` 输出路径残留。
- 未运行全量、真实 PostgreSQL、服务、性能;rustfmt/clippy 组件缺失,未安装。
## Focused Verification
### Trellis 独立检查(2026-09-20)
- 核对共享 schema policy/identity、nominal ID 位移稳定性、根 response bounds、private/Secret/资源拒绝、具体 decoder 与 API envelope;没有发现并行输出类型允许表或 ORM storage/fingerprint 改动。
- 修复 runtime Encoder 的异常状态:对象缺值时 `end()` 保留未完成 frame;写入部分 JSON 后触及字节上限时锁定失败,`finish()` 不再可能发布残缺 Encoded。新增缺值、闭合括号超限和逗号写入后值超限三个回归,同步 toolchain spec。生成代码原本逐步 `?` 传播,修复针对公共 Encoder 自身的 Encoded 不变量。
- 独立 `cargo check --locked --offline -p dever-core -p dever-runtime -p dever-cli --features dever-runtime/postgres,dever-runtime/api,dever-core/reference` 通过(16.79 秒)。命令使用上述 incremental/debug/toolchain 设置。
- 修复后 `time_codec` 9/9 通过(44.22 秒),包括新增 Encoder 回归、native/reference 时间、生成 concrete codec 与无监听器 API dispatch。
- `git diff --check` 及新增/修改源码显式尾随空白扫描通过。rustfmt/clippy 二进制未安装,不能宣称 lint 全通过;未安装组件。
- 未运行全量、真实 PostgreSQL、SQLite 执行、服务或性能测试。已有 SQLite fixture 角色不匹配仍按上述记录保留;无本任务未修复阻塞。
```bash
cargo check --locked --offline -p dever-core -p dever-runtime -p dever-cli --features dever-runtime/postgres,dever-runtime/api,dever-core/reference
cargo test --locked --offline -p dever-tests --features api,reference --test time_codec -- --test-threads=1
cargo test --locked --offline -p dever-tests --features api,reference --test standard_library --test json_library --test api_routes --test model_syntax --test markdown_source -- --test-threads=1
cargo test --locked --offline -p dever-tests --features sqlite --test sqlite_orm shared_orm_fixture_remains_valid_without_opening_a_database -- --exact --test-threads=1
git diff --check
```
不运行全量测试、真实 PostgreSQL、服务或性能基准。PostgreSQL 仅做受影响 feature 的定向 `cargo check`,除非隔离连接被明确提供。
以上命令实际均额外使用 `CARGO_INCREMENTAL=0`、`--config profile.dev.debug=0 --config profile.test.debug=0` 和已安装 stable toolchain 的命令级 PATH;这些是编译工具设置,不是应用配置。
## Review Risks
- JSON parser 必须保留 duplicate key 可见性,不能先解析成会覆盖键的 Map。
- DateTime 外部格式变化不能修改数据库列类型、migration history 或内部排序。
- schema 缓存 identity 必须包含 policy,避免 setting 允许的 Secret 被普通 output 复用。
- 生成 codec 必须保留 record 声明顺序和输入求值顺序,且不泄漏 runtime Value 到业务层。
@@ -1,37 +0,0 @@
# Dever 时间与类型化编解码
## Goal
补齐生产业务所需的 UTC 日期时间操作,并建立唯一的静态类型 wire 编解码合同,为 Adapter setting、Job payload 和 API 输入输出提供同一套安全、无损、可审计的转换。
## Dependencies
- `secure-data-contracts`:提供 Secret 和统一不可观察类型属性。
## Requirements
- DateTime 内部和数据库继续使用 Unix epoch 整数毫秒;Date/Time/Duration 保持既有 i64 存储兼容。
- `dever.time` 提供 UTC now、RFC3339 DateTime parse/format、Date/Time parse/format、Duration 构造,以及 DateTime 加减 Duration、DateTime difference;溢出和非法文本返回显式失败。
- 外部 wire 表示固定:DateTime 为规范 UTC RFC3339 字符串,Date 为 `YYYY-MM-DD`,Time 为带可选毫秒的 `HH:MM:SS[.sss]`,Duration 为整数毫秒。
- 编译器从具体 Type/Definition 派生共享静态 wire schema;setting、Job 和 API 只能通过该 owner 请求 encode/decode 代码。
- 支持 Bool/Int/Float/Decimal/Text/Id/Uuid/DateTime/Date/Time/Duration、nullable、List 和无 private 字段的 record;Json 作为已验证原始 JSON 时保留既有语义。Secret 只允许在 API/setting input policy 中解码,禁止普通 output 和 Job payload。
- Float 编码拒绝 NaN/Infinity;Decimal/Id/Uuid 使用无损字符串;整数严格检查 i64。
- 对象解码拒绝重复键、未知字段和缺失必填字段;嵌套深度、集合长度和总字节受运行时已有/新增明确上限约束。
- 编解码器生成具体值,不把动态 `serde_json::Value` 传入业务函数,不增加反射或 runtime 类型注册表。
- 迁移当前 API response 编码使用共享 schema,保持 `{code,message,data}` envelope 和既有路由行为。
## Acceptance Criteria
- [ ] 时间 parse/format round-trip、UTC 规范化、闰年边界、负 epoch、非法文本和 i64 溢出均有定向测试。
- [ ] SQLite/PostgreSQL Model 的整数毫秒表示和现有 schema fingerprint 不因外部格式变化而改变。
- [ ] 支持类型的 encode/decode round-trip 无损;Decimal/Id/Uuid/DateTime 不经 Float。
- [ ] duplicate/unknown/missing/over-depth/over-size/non-finite/private/resource 稳定拒绝;Secret 仅在批准输入策略成功,普通输出和 Job payload 稳定拒绝。
- [ ] API response 改用共享 codec 后,已有 envelope、状态码和 API route tests 保持一致;DateTime 输出变为 RFC3339。
- [ ] `.dever` 与 `.dever.md` 的时间签名、文档合同和生成行为一致。
- [ ] 后续 setting/Job/API 能消费一个公共 compiler schema API,而无需复制类型匹配分支。
## Out Of Scope
- 本地时区数据库、DST 推断和用户界面格式化。
- 任意 schema registry、Avro/Protobuf 或动态 JSON schema 发布。
- Model 结构自动存储为 JSON;持久化仍需显式 Json 字段。
Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff. Show more