# Iteration 17 — library projects and dependency privacy (`kind`, `internal/`) > Format: fiberloom `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.