docs: iteration 17 forks settled — kind key, internal/ rule (no code)
- fork 1: library-ness manifest-declared, kind = "library", default program - fork 2: privacy = Go internal/ directory rule, named diagnostic at use - fork 3: dep-boundary-only scope; Go subtree rule recorded as later tightening - fork 4: lib+bin dual — library default action is check, explicit build works - Go-inherited rule pinned: internal type in public signature allowed, no check - impact analysis added: framework loses --emit workaround, plumbing under internal/; compiler = two seams (driver kind + dep-use refusal WO-E1xx) - VM zero impact by construction: no .wob change, libs compile whole-program into consumer image, internal modules still emitted (privacy strips nothing) - GC zero mechanism impact; pinned: inference stays whole-program, app usage can promote dep classes, internal/ invisible to gcinfer — intended, not bug - board + roadmap rows: needs-refinement -> forks settled, spec/plan next Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0370727d69
commit
e7f963849e
3 changed files with 96 additions and 27 deletions
|
|
@ -132,7 +132,7 @@ that sequences its tasks. Read one, approve, then the next starts.
|
|||
| 14 | [skillhost host workload](stories/language-runtime-database/14-skillhost-host-workload.md) | ⬜ gaps recorded (branch query-grammar found skillhost needs no new query grammar); each gap a candidate iteration |
|
||||
| 15 | [deps: `wo.toml [deps]`](stories/language-runtime-database/15-deps-package-manager.md) | ✅ **landed 2026-08-18** (branch web-framework): [deps] inline tables, git-binary fetch, wo.lock pinning, offline-when-locked, --update-deps, WO-E106/E107; `just deps-accept` 8/0 |
|
||||
| 16 | [web framework](stories/language-runtime-database/16-web-framework.md) | ✅ **landed 2026-08-19** — writeonce-framework (HTTP/1.1 + router + Handler/Middleware) consumed by web-app through [deps]; `just web-app` 14/0; h2c parked (§C) behind 8/9f/11 |
|
||||
| 17 | [library projects + `internal/`](stories/language-runtime-database/17-library-projects-internal.md) | ⬜ **needs refinement** (2026-08-20): Go internal/ vs Rust pub(crate) analyzed; four forks open — library kind marker, privacy mechanism, rule scope, lib+bin duality |
|
||||
| 17 | [library projects + `internal/`](stories/language-runtime-database/17-library-projects-internal.md) | ⬜ **forks settled 2026-08-20, awaiting spec/plan**: kind = "library" manifest key; Go internal/ rule, dep-boundary-only; lib+bin dual; VM/GC untouched by design (impact analysis in the iteration) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ The C proving-ground work (`exploration/c-runtime/`, phases A–F: 859k reads/s,
|
|||
| 9f | io_uring group-commit write path — batched durability overlapped on shard threads, fsync fallback | **no spec yet** — brainstorm after iterations 8 + 9e |
|
||||
| 9g | Query grammar from real embedded-DB corpora — whole-query count + correlated exists, driven by the skillhost SQL catalogue; add only what a corpus uses | **no spec yet** — three forks; may collapse to "confirm len(query) + add exists" |
|
||||
| 14 | skillhost host workload — port skillhost (MCP host + confined script runner) to writeonce; drives the missing host capabilities into the open (bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process) | **no spec yet** — gaps recorded in the iteration; each gap brainstormed on demand, bounded-subprocess first |
|
||||
| 17 | library projects + dependency privacy — `wo.toml` library kind (checkable, unbuildable) + a dep-internal module tier; framework reorg demonstrates both | **needs refinement** — four forks recorded in the iteration; brainstorm before spec/plan |
|
||||
| 17 | library projects + dependency privacy — `wo.toml` kind = "library" (checkable without entry, dual lib+bin) + Go-style `internal/` at the [deps] boundary; framework reorg demonstrates both | **forks settled 2026-08-20** — decisions + framework/compiler/VM/GC impact in the iteration; spec/plan next |
|
||||
| 10 | HTTP service layer | [plan 6](superpowers/plans/2026-08-01-http-service-layer.md) |
|
||||
| 11 | Fibers | vision §3, [blue-green exploration](plan/exploration/blue-green-vm/00-vision.md) |
|
||||
| 12 | Blue-green deploy | [spec](superpowers/specs/2026-08-03-blue-green-vm-design.md) — plan authored after iterations 9–10 |
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ iterations); no commits by agents — drafts go to `.dev/commit.md`.
|
|||
| 14 | [skillhost host workload](14-skillhost-host-workload.md) | a host-shaped driving workload (writeonce port of skillhost) that names the runtime gaps it exposes: bounded/killable subprocess, stdin/stdout transport, fs metadata, and FFI-vs-out-of-process model driver — each a candidate iteration |
|
||||
| 15 | [deps: `wo.toml [deps]`](15-deps-package-manager.md) | exact-rev git dependencies + `wo.lock` + `.wo-deps` cache; `use <dep>` resolves a fetched project as a module root; flat-only, network-free when locked |
|
||||
| 16 | [web framework](16-web-framework.md) | a `.wo`-library framework (HTTP/1.1 keep-alive behind a TLS-terminating proxy): router, `Handler`/`Middleware` structural interfaces, `@table` data layer; `docs/examples/web-app` consumes it via `[deps]`; h2c parked behind 8/9f/11 |
|
||||
| 17 | [library projects + `internal/`](17-library-projects-internal.md) | first-class library projects (`wo.toml` kind, checkable without an entry) and dependency privacy (a dep-internal module tier, Go's `internal/` shape vs Rust's `pub(crate)` analyzed) — four open forks, needs refinement |
|
||||
| 17 | [library projects + `internal/`](17-library-projects-internal.md) | first-class library projects (`wo.toml` kind = "library", checkable without an entry, dual lib+bin) and dependency privacy (Go's `internal/` rule at the [deps] boundary) — forks settled 2026-08-20, spec/plan next |
|
||||
|
||||
Review protocol: the developer reads one iteration, approves or amends;
|
||||
the next starts only after approval. Each iteration is an unsplittable
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
> [Story — one language, one runtime, one database, one binary](00-story.md).
|
||||
>
|
||||
> **Inserted 2026-08-20, needs further refinement** (developer decision: keep
|
||||
> as an iteration, do not implement yet). The forks below are genuinely open;
|
||||
> a brainstorm/spec settles them before any plan.
|
||||
> as an iteration, do not implement yet).
|
||||
>
|
||||
> **Refined 2026-08-20: all four forks SETTLED** (developer decisions, no code
|
||||
> changed). See "Settled decisions" and "Impact analysis" below. Next step is
|
||||
> the spec/plan; implementation stays parked until asked.
|
||||
|
||||
## Why this iteration exists
|
||||
|
||||
|
|
@ -54,26 +57,90 @@ library marker can be explicit where Go infers, giving clearer errors.
|
|||
- The framework reorganizes to demonstrate both (its parser plumbing moves
|
||||
behind the privacy line; `Handler`/`App`/builders stay public).
|
||||
|
||||
## Open forks (each a real decision for the spec)
|
||||
## Settled decisions (2026-08-20 — the former open forks)
|
||||
|
||||
1. **How library-ness is declared.** `kind = "library"` top-level key vs a
|
||||
`[lib]` section vs pure inference from "no entry-shaped main". Leaning:
|
||||
the explicit key — the manifest exists, and explicit beats inference in
|
||||
error messages — with "program" the default.
|
||||
2. **Privacy mechanism.** Go's `internal/` directory rule (pure
|
||||
use-resolution change, zero new syntax, coarse) vs Rust's `pub(lib)`
|
||||
visibility keyword (fine-grained, touches parser + typechecker + the
|
||||
error catalog) vs a manifest `export = [...]` module allowlist (explicit
|
||||
surface, but a second place to maintain). Leaning: `internal/` — it
|
||||
matches directory-as-module exactly and costs a resolution rule.
|
||||
3. **Scope of the internal rule.** Dep-boundary-only (importable anywhere
|
||||
inside the dep, refused from the consumer) vs Go's full subtree rule
|
||||
(importable only under `internal/`'s parent, even within one project).
|
||||
Dep-boundary-only is the smaller honest cut; Go's rule also disciplines
|
||||
large single projects.
|
||||
4. **Can one project be both** (Rust's lib+bin)? A framework shipping a demo
|
||||
binary wants it; the entry-selection rule (iteration 15's "a dep's main is
|
||||
never the entry") already half-answers it. Decide explicitly.
|
||||
1. **Library-ness is manifest-declared: `kind = "library"`**, a top-level
|
||||
`wo.toml` key, default `"program"`. Explicit beats inference in error
|
||||
messages both ways: a library refuses the default build with a message
|
||||
naming its kind, and a program missing `main` stays a loud error instead
|
||||
of silently becoming a library. (Rejected: a `[lib]` section — ceremony
|
||||
with nothing to hold yet; Go-style inference — makes forgot-`main` and
|
||||
is-a-library indistinguishable.)
|
||||
2. **Privacy mechanism: Go's `internal/` directory rule.** A dependency
|
||||
module whose path contains the segment `internal` is not importable
|
||||
across the `[deps]` boundary; the violation is a named diagnostic at the
|
||||
offending `use`, naming the dependency. Zero new syntax — a pure
|
||||
use-resolution rule, matching the directory-as-module doctrine exactly.
|
||||
(Rejected: `pub(lib)` keyword — parser + typechecker + catalog surface
|
||||
for granularity nothing needs; manifest export allowlist — a second
|
||||
place that drifts from the code.)
|
||||
3. **Scope: dep-boundary only.** `internal/` modules stay importable
|
||||
anywhere INSIDE the dependency; only the consumer is refused. The
|
||||
smaller honest cut; Go's full subtree rule (parent-of-`internal/` scope,
|
||||
enforced even within one project) is recorded as a possible later
|
||||
tightening — adopting it later only ever rejects more, never breaks a
|
||||
consumer.
|
||||
4. **Lib+bin duality: allowed.** A library MAY carry an entry-shaped
|
||||
`main` (demo/self-test binary). `kind = "library"` changes the DEFAULT
|
||||
action of `woc <dir>` to whole-project typecheck; an explicit build
|
||||
invocation still produces the binary when a `main` exists. As a
|
||||
dependency its `main` is never the entry — iteration 15 already ships
|
||||
that rule.
|
||||
|
||||
Follow-on rule inherited from Go, recorded so the spec doesn't relitigate
|
||||
it: an internal type MAY appear in a public signature (Go permits exported
|
||||
functions returning internal types — the consumer can hold and pass the
|
||||
value but cannot `use` the module to name the type). No extra check in v1;
|
||||
it is the library author's own smell to avoid.
|
||||
|
||||
## Impact analysis (what each layer feels)
|
||||
|
||||
**Framework (`docs/examples/writeonce-framework/`).** Gains one manifest
|
||||
line (`kind = "library"`); `woc <dir>` then typechecks the whole project
|
||||
with no entry required — the iteration-16 `--emit` verification workaround
|
||||
dies. Reorg: the plumbing the web-app never imports — the request parser
|
||||
module (parse_request, url_decode, the carry-state record) and the serve
|
||||
loop internals — moves under `internal/`; the public surface (`Handler`,
|
||||
`Middleware`, `App`, `Req`/`Resp`, the response builders) stays where it
|
||||
is. The framework's own `use` of its internal modules stays legal
|
||||
(decision 3). The web-app changes nothing: it already imports only the
|
||||
public modules, so `just web-app` staying 14/0 is the regression gate, not
|
||||
a migration.
|
||||
|
||||
**Compiler (`woc`) — the only place code would change.** Two seams, both in
|
||||
existing passes: the driver reads the `kind` key and picks
|
||||
check-mode-by-default for libraries (build mode already exists; check mode
|
||||
must still run the FULL pipeline — parse, typecheck, borrow check, GC
|
||||
inference — so a green check means what a green build means); and the
|
||||
dep-use resolution (the iteration-15 prefixing step) refuses a consumer
|
||||
`use` whose dep-relative path contains `internal`, with a new WO-E1xx.
|
||||
No lexer, parser, or type-system syntax changes — `internal` is a path
|
||||
shape, not a keyword.
|
||||
|
||||
**VM (`wovm`): zero impact, by construction.** Visibility is name
|
||||
resolution at compile time; the `.wob` format carries no module or
|
||||
visibility metadata to extend — no new opcodes, no version bump, no loader
|
||||
change. A library never yields its own `.wob` at all: iteration 15's model
|
||||
compiles dependencies whole-program into the consumer's single image, and
|
||||
that stands. The VM never learns "library" exists. A dual-built demo
|
||||
binary is an ordinary program image. One honest disclosure: `internal/`
|
||||
modules still compile INTO the consumer's image (there is no dead-code
|
||||
elimination) — privacy restricts naming, it strips nothing.
|
||||
|
||||
**GC: zero mechanism impact, one semantic note worth pinning.** GC-ness is
|
||||
whole-program inferred (iteration 7b): structural SCC plus demand
|
||||
promotion run over the app's AND every dep's classes together, AFTER use
|
||||
resolution — privacy is invisible to inference. So a framework class can
|
||||
still be promoted to traced by how the APP uses it (an escape in app code
|
||||
promotes the escaping projection's class, wherever that class lives), and
|
||||
`internal/` does not fence that off. This is correct and intended:
|
||||
GC-ness stays a per-consumer, whole-program property, not a library
|
||||
promise — a library author cannot pin "my class is untraced" any more
|
||||
than before. Library check mode runs the same inference (over the library
|
||||
alone), so a library-standalone check and an app-embedded compile may
|
||||
legitimately disagree about traced-ness — that is the design, restated
|
||||
here so nobody files it as a bug. Runtime GC (header, barrier, safepoints,
|
||||
budgets) untouched.
|
||||
|
||||
## Acceptance Criteria (draft)
|
||||
|
||||
|
|
@ -94,6 +161,8 @@ binaries per project (`cmd/` convention — record, don't build).
|
|||
|
||||
## Proposed Solution
|
||||
|
||||
Brainstorm → spec settling the four forks, then a small plan: manifest key +
|
||||
driver check-mode for libraries, the use-resolution privacy rule + WO-E1xx
|
||||
diagnostic, the framework reorg, and `just web-app` as the regression gate.
|
||||
Forks settled above (2026-08-20). Remaining path: spec + small plan —
|
||||
manifest `kind` key + driver check-mode for libraries, the use-resolution
|
||||
privacy rule + WO-E1xx diagnostic, the framework reorg (plumbing under
|
||||
`internal/`), and `just web-app` as the regression gate. VM and GC are
|
||||
untouched by design (see Impact analysis).
|
||||
|
|
|
|||
Loading…
Reference in a new issue