From f430bff5c4c3b649b93a75a3b40a2c588a0b2a11 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Thu, 20 Aug 2026 01:23:10 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20iteration=2017=20=E2=80=94=20library=20?= =?UTF-8?q?projects=20+=20dependency=20privacy=20(needs=20refinement)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brainstorm outcome, deliberately NOT implemented (developer decision: keep as an iteration needing further refinement). Records: - the two gaps iterations 15/16 exposed: library-ness is implicit (a no-main project fails woc build mode — the framework is verified via an --emit workaround) and the dep boundary leaks internals (pub has no dep-private tier: parse_request is as importable as Handler). - the conventions corpus: Go (package decides program-ness; cmd/; internal/ = directory-shaped privacy, zero keywords) vs Rust ([lib]/ [[bin]] manifest targets; pub(crate)-family keyword visibility). Doctrine fit points at Go's shape with an explicit manifest key (writeonce HAS a manifest; explicit beats inference in errors). - four open forks for the spec: kind declaration form; internal/ vs pub(lib) vs export-allowlist; dep-boundary-only vs Go's subtree rule; lib+bin duality. Draft acceptance criteria; web-app 14/0 as the regression gate. Roadmap + board rows added. Co-Authored-By: Claude Opus 5 (1M context) --- docs/00-status.md | 2 + .../language-runtime-database/00-story.md | 1 + .../17-library-projects-internal.md | 99 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 docs/stories/language-runtime-database/17-library-projects-internal.md diff --git a/docs/00-status.md b/docs/00-status.md index 57a2f8c..af377ff 100644 --- a/docs/00-status.md +++ b/docs/00-status.md @@ -132,6 +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 | --- @@ -320,6 +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 | | 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 | diff --git a/docs/stories/language-runtime-database/00-story.md b/docs/stories/language-runtime-database/00-story.md index e36d215..361456f 100644 --- a/docs/stories/language-runtime-database/00-story.md +++ b/docs/stories/language-runtime-database/00-story.md @@ -65,6 +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 ` 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 | Review protocol: the developer reads one iteration, approves or amends; the next starts only after approval. Each iteration is an unsplittable diff --git a/docs/stories/language-runtime-database/17-library-projects-internal.md b/docs/stories/language-runtime-database/17-library-projects-internal.md new file mode 100644 index 0000000..4c4a27b --- /dev/null +++ b/docs/stories/language-runtime-database/17-library-projects-internal.md @@ -0,0 +1,99 @@ +# Iteration 17 — library projects and dependency privacy (`kind`, `internal/`) + +> Format: `product/story-iteration-template`. Part of +> [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. + +## Why this iteration exists + +Iterations 15/16 made cross-repo libraries real — and exposed two gaps the +established ecosystems solved long ago: + +1. **Library-ness is implicit and broken.** A project without `fn main` (the + framework) cannot be checked by `woc `: manifest presence forces + build mode, which errors "no `main` entry point found". Iteration 16 + verified the framework through a `woc --emit` workaround — a wart, not a + design. +2. **The dependency boundary leaks internals.** `pub` is module-public with + no dep-private tier: the framework's plumbing (`parse_request`, the hex + decoder, `route_match`) is exactly as importable by the consuming app as + its intended surface (`Handler`, `App`, the builders). Nothing marks "this + module is the library's own business". + +## The conventions, analyzed (the corpus for the spec) + +**Go:** no manifest marker — the *package* decides (`package main` + +`func main` = executable; anything else = library), `cmd//` hosts +multiple binaries, and **`internal/`** carries all encapsulation: the +compiler refuses any import of a path containing `internal/` from outside +the subtree rooted at `internal/`'s parent. Directory-shaped privacy, zero +keywords — public repo, private API. + +**Rust:** manifest-declared targets (`[lib]` / `[[bin]]`, `src/lib.rs` / +`src/main.rs` conventions; a crate can be both) and *keyword-grained* +visibility: `pub`, `pub(crate)`, `pub(super)`, `pub(in path)`. A dependency +sees only what is `pub`-reachable from the crate root. No `internal/` +convention — `pub(crate)` does that job. + +**Fit to writeonce doctrine:** directory-as-module and keyword frugality +point at Go's shape — but writeonce *has* a manifest (Go does not), so the +library marker can be explicit where Go infers, giving clearer errors. + +## Goals (draft — the spec refines) + +- A library project is first-class: declared in `wo.toml`, `woc ` + typechecks it whole (no entry required), `woc build` on it refuses with a + message that says what it is. The framework adopts it and loses the + `--emit` workaround. +- A dependency has a private interior: some modules are importable inside + the dep but not across the `[deps]` boundary; violations are a named + compile diagnostic at the offending `use`. +- 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) + +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. + +## Acceptance Criteria (draft) + +- **Given** the framework marked as a library, **when** `woc ` runs, + **then** it typechecks the whole project with no entry required, and + `woc build` refuses with a message naming the kind. +- **Given** a framework module behind the privacy line, **when** the web-app + `use`s it, **then** a named diagnostic points at the `use` and names the + dependency; inside the framework the same import stays legal. +- **Given** the public surface (`Handler`, `App`, builders), **when** the + web-app builds, **then** nothing changed — `just web-app` stays 14/0. + +## Out Of Scope + +Registries/semver (still future), transitive deps (15's flat-only stands), +`pub(super)`-style fine grains beyond the chosen mechanism, multiple named +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.