writeonce/docs/superpowers/plans/2026-08-20-library-kind-internal.md
shoney.arickathil c0b0dbb846 docs: audit all markdown against the code, fix findings, flatten status folders
- README: shipped concurrency/HTTP/WebSockets sat in the roadmap as "not yet
  available"; "no package manager" contradicted [deps]; the deps example
  would not have compiled (the key IS the module name)
- runtime/README: leads with wovm, wo-rt.c demoted to a historical section;
  dropped 2 nonexistent recipes, crates/rt, @gc refcounting, 13 suites -> 18
- employee + log-watcher READMEs claimed "does not compile"; both are gates
- error catalog: +10 emitted codes incl WO-E250, the only diagnostic the
  shipped query surface raises; recorded why the sweep rotted
- language-surface: group-by parses, then the typechecker refuses it
- 00-code-review + 00-link-audit re-run; history kept, not rewritten
- 48 dead Rust-era exploration links de-linked rather than re-pointed (their
  prose names the retired plan by number); successor map -> discarded.md
- 08-project-structure: compiler/plan/ never existed; corpus has 9 dirs, 5 empty
- releasing.md: dropped a --draft step the workflow never had
- new docs/00-doc-audit.md: findings + disposition, incl one row where the
  audit was wrong and the doc it accused was right
- status folders removed: 34 stories flat, status only in frontmatter; 252
  links recomputed from resolved paths; board/board-views/structure retaught
- story 24 -> in-progress, since frontmatter is now the only truth
- new iteration 38: fs mutation verbs + net.connect, the two capability
  families no iteration owned
- new iteration 39: gofiber/fiber v3.5.0 parity study. The ledger called
  CSRF/sessions unblocked by iteration 34's HMAC, but the runtime has no
  source of randomness at all
- linkcheck skips .dev/.superpowers: 0 broken paths, 0 bad anchors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 19:20:22 +02:00

271 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Iteration 17 — library kind + `internal/`: implementation plan
> **Status: ✅ LANDED 2026-08-20** — executed in full, all six tasks. Was
> parked the same day (developer directive: framework v1 work first), then
> unparked and run. Two disclosed deviations, both because the framework grew
> after this plan was written: `http/parse.wo` was SPLIT rather than moved
> whole (its `media_type`/`form_values` are public surface the web-app calls),
> and the gate reads 26/0 rather than 17/0 (`just web-app` was already at 23
> before this iteration). Board: [docs/00-status.md](../../stories/00-status.md).
> **For agentic workers:** REQUIRED SUB-SKILL: Use
> superpowers:subagent-driven-development (recommended) or
> superpowers:executing-plans to implement this plan task-by-task. Steps use
> checkbox (`- [ ]`) syntax for tracking.
>
> **Style rule (user convention):** this plan carries concept, reason, and
> required behavior in words plus verification commands only — no
> implementation or test code blocks; the executor writes the code.
**Goal:** `kind = "library"` in `wo.toml` makes a project checkable without
an entry, the `internal/` rule keeps a dependency's plumbing private
(WO-E108), and the framework adopts both — `just web-app` proves the public
surface unmoved.
**Architecture:** every change is compile-time and lives in the driver
(`compiler/bin/main.ml`): the manifest reader learns one key, the manifest
build path grows a check branch, and the dep-use resolution walk in
`compile_image` grows the boundary rule. No lexer, parser, typechecker, VM,
`.wob`, or GC change — the spec's impact analysis is normative.
**Tech Stack:** OCaml stdlib only (`woc`), bash acceptance scripts, `just`.
**Spec:** [`../specs/2026-08-20-library-kind-internal-design.md`](../specs/2026-08-20-library-kind-internal-design.md)
(normative). Story:
[`17-library-projects-internal.md`](../../stories/language-runtime-database/17-library-projects-internal.md).
## Global Constraints
- Branch `library-internal`; commits local only, never push.
- OCaml stdlib only; no new executables, no new build steps.
- Absent `kind` means program — every existing project's behavior is
byte-identical; the standing gates prove it each task.
- Exit-code doctrine stays: 0 clean, 1 diagnostics, 2 usage/manifest/IO.
WO-E108 is a diagnostic (exit 1, printed by the normal collector path);
WO-E109 is a manifest error (exit 2, `woc: <file>: error WO-E109: ...`,
the WO-E106 shape).
- `internal` matches a whole path SEGMENT of a dep-relative module path —
never a substring.
- Gates that must stay green after every task: `just woc-test`,
`just oop-e2e`, `just deps-accept`, `just web-app`, `just log-watcher`,
`just employee`.
## Spec deviations, disclosed up front
1. The spec put WO-E108/E109 fixtures in the compile-fail corpus. The
corpus harness (`scripts/oop-e2e.sh`) compiles a fixture DIRECTORY with
no manifest and no network — a `wo.toml` in a fixture dir would flip woc
into manifest-build mode, and WO-E108 additionally needs a fetched dep.
Both diagnostics are therefore gated in `scripts/web-app-accept.sh`
(which already builds a file:// dep chain), not the corpus. Task 5.
2. The spec says check mode "stops before image emission". The cheapest
correct implementation reuses `compile_image` whole — emission happens
in memory and the image is discarded; no artifact is written, no entry
is required (an entry-less image is already legal there, the `--emit`
precedent). Behavior matches the spec; the pipeline boundary is one
step later than the spec's wording.
3. `woc build <dir> -o <out>` never reads the manifest, so it resolves no
`[deps]` (pre-existing, iteration 15). The dual lib+bin case therefore
holds for libraries without `[deps]` — the framework qualifies. Recorded
here, not fixed; a dep-aware explicit build is future work.
---
## Task 1 — the manifest `kind` key + WO-E109
**Files:**
- Modify: `compiler/bin/main.ml` (`manifest_parse` known-key table
~line 808; `manifest_build` ~line 1023).
**Interfaces:**
- Produces: `manifest_parse` accepts top-level `kind`; `manifest_build`
exposes the validated kind ("program" when absent) to Task 2's branch.
- [ ] `manifest_parse`: add `kind` to the known TOP-LEVEL keys (the match
arm that today allows `name`, `version`, `description`). Nothing else in
the parser changes — the value is a quoted string like every other key.
- [ ] `manifest_build`: after the `name` check, read `kind`. Absent or
`"program"` continues to build. `"library"` is Task 2's branch (for this
task, temporarily fall through to build — the framework does not carry
the key until Task 4, so nothing observable changes). Any OTHER value
fails as a manifest error in the WO-E106 print shape with code WO-E109,
naming the given value and the two legal ones, exit 2.
- [ ] Verify by hand: a scratch project under the scratchpad with
`kind = "junk"` refuses with WO-E109 and exit 2; the same project with
`kind = "program"`, and with no `kind` line, builds as before.
- [ ] Verify nothing moved: `just woc-test && just deps-accept && just employee`.
- [ ] Commit.
## Task 2 — library check mode + the build-error hint
**Files:**
- Modify: `compiler/bin/main.ml` (`manifest_build`; `build_mode` no-entry
error ~line 639; `usage_msg` ~lines 42–52 and the long help's build
paragraph ~line 96).
**Interfaces:**
- Consumes: Task 1's validated kind.
- Produces: `woc <dir>` on a `kind = "library"` manifest runs the full
check; the behavior Tasks 4–5 verify against.
- [ ] `manifest_build`, kind `"library"`: resolve `[deps]` and enforce the
`[runtime]` constraint exactly as build does (a library must be
checkable offline once locked), then run `compile_image ~deps dir`,
print diagnostics through the normal `finish` path on error (exit 1),
and exit 0 silently on success. No `target/` directory is created, no
file is written, no entry is required. The full pipeline runs — parse,
typecheck, interface satisfaction, ownership, GC inference — because
`compile_image` already runs it; the in-memory image is discarded
(deviation 2).
- [ ] `build_mode`, the no-entry error: when `<dir>/wo.toml` exists and
names `kind = "library"`, append the library hint to the existing
message — the project is a library; add a `main` for a demo binary or
check it with `woc <dir>`. Read the manifest only if the file exists;
malformed manifests keep failing as they do today.
- [ ] `usage_msg`: the `woc <dir>` line notes "builds a program / checks a
library, per the manifest's `kind`"; the long help gains two sentences
on `kind` and check mode. No new flags.
- [ ] Verify by hand: scratch library project (`kind = "library"`, one
class, no `main`) — `woc <dir>` exits 0 with no output; plant a type
error, `woc <dir>` exits 1 with the normal diagnostic; `woc build <dir>
-o x` fails with the no-entry message plus the library hint; add a
`main`, `woc build` produces a runnable binary (dual case) while
`woc <dir>` still only checks.
- [ ] Verify nothing moved: `just woc-test && just oop-e2e && just deps-accept`.
- [ ] Commit.
## Task 3 — the `internal/` boundary rule (WO-E108)
**Files:**
- Modify: `compiler/bin/main.ml` (`compile_image`'s dep-use walk,
~lines 487–518).
**Interfaces:**
- Consumes: the existing walk that already knows, per file, whether a dep
owns it (`owner`) and prefixes dep-internal use paths.
- Produces: WO-E108 diagnostics in the collector; Task 5 gates on the code
string.
- [ ] In the same `List.map` over parsed files: for a file NOT owned by any
dep (the `owner = None` branch, today untouched), inspect each `Use`
whose FIRST segment names a dep in `deps` — when any LATER segment is
exactly `internal`, add a diagnostic to the collector: code WO-E108, the
use's file and `pos`, message naming the internal module path and the
dependency it belongs to (the `Diag.error` + `Collector.add` pattern at
~line 277). Do not drop the use — the collector's has-error path already
prevents emission, and later resolution errors on the same use are
harmless duplicates suppressed by exit-on-first-report ordering as
today.
- [ ] Dep-owned files stay untouched on this path — the boundary is
consumer-only (spec §3): the dep's own `use internal` (prefixed to
`<dep>/internal` by the existing arm) must keep compiling.
- [ ] The root project's own `internal/` directories are NOT matched: the
rule keys on the first segment being a DEP name, so a root-project
`use internal` never fires it. No code needed — assert it in the Task 5
gate instead.
- [ ] Verify by hand: temp dir pair — a dep with an `internal/` module and
a consumer importing `<dep>/internal` — compile fails exit 1 printing
WO-E108 at the `use`'s line; the dep's own file importing `internal`
compiles.
- [ ] Verify nothing moved: `just woc-test && just deps-accept && just web-app`.
- [ ] Commit.
## Task 4 — framework reorg: adopt `kind` + `internal/`
**Files:**
- Modify: `docs/examples/writeonce-framework/wo.toml` (add
`kind = "library"`), `app.wo` (imports), `README.md` (verification note).
- Move: `http/parse.wo` → `internal/parse.wo`,
`http/serve.wo` → `internal/serve.wo` (git mv; module becomes
`framework/internal` under a consumer, `internal` standalone).
- Not touched: `http/types.wo`, `router/router.wo`, the web-app.
**Interfaces:**
- Consumes: Task 2's check mode (standalone verification), Task 3's rule
(what the move protects).
- Produces: the reorganized layout Task 5's gate checks against.
- [ ] Move the two plumbing files. Same-module access dies with the move:
`internal/serve.wo` and `internal/parse.wo` now need `use http` for
`Req`/`Resp` (they shared the `http` module with `types.wo` before);
`app.wo` adds `use internal` for the serve loop and `Parsed` seam. No
declaration changes — imports only.
- [ ] `wo.toml` gains `kind = "library"` (top-level, beside `name`).
- [ ] README: replace the `--emit` verification workaround sentence with
the check-mode invocation (`woc <dir>` — full pipeline, no entry), and
one sentence on `internal/` being unimportable by consumers.
- [ ] Verify: `compiler/_build/default/bin/woc docs/examples/writeonce-framework`
exits 0 silently — the workaround is dead.
- [ ] Verify the consumer: `just web-app` — all 14 standing checks pass
unchanged (the app imports only `framework`, `framework/http`,
`framework/router`, so NOTHING in it changes).
- [ ] Commit.
## Task 5 — gate: three new checks in web-app-accept
**Files:**
- Modify: `scripts/web-app-accept.sh` (after the build check, before the
serve matrix).
**Interfaces:**
- Consumes: the `$W/fw` framework copy and `$W/app` consumer the script
already builds; Tasks 1–4's behavior.
- Produces: `just web-app` at 17 checks, the iteration's single gate.
- [ ] Check 15 — library check mode: `woc "$W/fw"` (the framework copy,
which now carries `kind = "library"` and no `main`) exits 0 with empty
output.
- [ ] Check 16 — the boundary: copy the app to a second temp dir, append a
`use framework/internal` line to its `main.wo`, compile; assert exit 1
and `WO-E108` in stderr.
- [ ] Check 17 — kind validation: copy the app again, set
`kind = "junk"` in its manifest, compile; assert exit 2 and `WO-E109`
in stderr.
- [ ] Renumber nothing — the script counts dynamically (`pass`/`fail`);
update only the header comment's check inventory.
- [ ] Run the full battery: `just web-app` (17/0) and the standing gates —
`just woc-test`, `just oop-e2e`, `just deps-accept`, `just log-watcher`,
`just employee`.
- [ ] Commit.
## Task 6 — docs closeout
**Files:**
- Modify: `docs/00-status.md` (NEXT PLAN advances; board row 17 → done
with what landed; in-progress row moves to the next order item),
story `17-library-projects-internal.md` (landing banner),
`compiler/src/CODE-LOGIC.md` (driver section: `kind`, check mode, the
boundary rule), `docs/08-project-structure.md` (framework layout gains
`internal/`), framework story `16-web-framework.md` only if it names the
`--emit` workaround.
- [ ] Apply; every claim carries its measured gate result (17/0 etc.); no
forward-looking "will".
- [ ] `just web-app` once more after the doc edits (nothing should move —
honesty check).
- [ ] Commit.
## Success criteria (spec, restated as the gate reads them)
1. Framework checks entry-less (`woc <dir>` exit 0) and `woc build` on it
fails naming the library kind — gate check 15 + Task 2's hand check.
2. A consumer import of `framework/internal` is WO-E108 at the `use`;
the framework's own import stays legal — gate check 16 + Task 3.
3. `just web-app`'s original 14 checks pass unchanged after the reorg —
Task 4/5.
4. A manifest without `kind` behaves byte-identically — every standing
gate, every task.
## Self-review notes
- Spec coverage: §1 kind key → Task 1; §2 driver modes → Task 2; §3 rule →
Task 3; §4 diagnostics → Tasks 1–3; §5 reorg → Task 4; §6 gate → Task 5;
out-of-scope list untouched by any task. Corpus-fixture clause replaced
by deviation 1; emission-boundary wording by deviation 2; dual-with-deps
limit by deviation 3.
- Type consistency: the only cross-task names are the two code strings
(WO-E108, WO-E109), the manifest key `kind`, and the module path
`framework/internal` — spelled identically in every task.
- Risk, disclosed: moving serve/parse breaks same-module visibility they
silently enjoyed beside `types.wo`; Task 4 names the exact import each
file must gain, and the standalone check catches any miss before the
gate runs.