docs: iteration 17 — library projects + dependency privacy (needs refinement)
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 <dir> 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) <noreply@anthropic.com>
This commit is contained in:
parent
d6025e131d
commit
f430bff5c4
3 changed files with 102 additions and 0 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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 <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 |
|
||||
|
||||
Review protocol: the developer reads one iteration, approves or amends;
|
||||
the next starts only after approval. Each iteration is an unsplittable
|
||||
|
|
|
|||
|
|
@ -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 <dir>`: 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/<name>/` 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 <dir>`
|
||||
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 <dir>` 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.
|
||||
Loading…
Reference in a new issue