Repository navigation
fix: declare IO.NetListen; adapt to FileError, ambiguous ctors, and stricter default-arg checking - #5
Merged
Merged
Conversation
march main makes capability propagation through imports an ERROR rather
than a warning: a module importing HttpServer (directly or via Conn) must
declare `needs IO.NetListen` itself. Adds it to the 21 modules that do.
Two further adaptations to current main:
- File.* now returns Result(_, FileError), a variant, not Result(_, String).
Six sites concatenated the error straight into a message; they now go
through to_string, which renders the underlying message verbatim
("No such file or directory"). Forge.Routes.generate also propagated a
FileError out of a Result(_, String), now converted at the boundary.
- `Header` is ambiguous between Http.Header and WebSocket.Header. Both
sites destructure headers from HttpServer.req_headers/resp_headers, so
both qualify as Http.Header.
Takes `forge check` against main-a5c3dc70 from 44 errors to 18. The rest
are not bastion's: 17 ambiguous JoinKind/SortDir constructors (Ast vs
DataFrame) inside depot, plus Form.render, whose trailing required
`render_inner` after three defaulted params does not type-check under the
stricter checking — that signature needs a decision, not a mechanical fix.
A required parameter after defaulted ones does not type-check: the reduced-arity desugaring matches the trailing argument against the first default's type, so `render_inner` was checked against `method : String` and `enctype`'s default against `render_inner`. Not a regression — the shape fails on main-8c87ccb2 too; earlier errors simply masked it. No call sites exist in bastion or forgepm, so reordering breaks nothing, and `Form.render(conn, action, fn -> ...)` still reads naturally.
Missed by the earlier sweep: the testing helpers are not in bastion's own `forge check` graph, so the requirement only surfaced when a consumer's test build pulled Bastion.Test.Auth in.
The testing helpers are not in bastion's own `forge check` graph, so
these only surface when a consumer's test build pulls them in — which,
before the resolver kept sibling test modules, never happened.
- `needs IO.NetListen` + `needs IO.Clock` (both propagate in through
HttpServer).
- `Conn`, `Header` and `NoUpgrade` are each declared by two modules in
scope; every use here means the HTTP one, so they qualify to
HttpServer.Conn / Http.Header / HttpServer.NoUpgrade.
`write(path)(content)` is no longer accepted — March functions are not curried, and the parser now rejects `f(...)(...)` outright. The three scaffolding generators build their files through such a writer; they take (path, content) as one call now.
Until march-language/march#106, reachability pruning dropped every test module except the entry, so `forge test` compiled one file and passed. These five files had not been compiled in a long time. - test_form: `Form.Wrapper.render` call sites updated to the parameter order this branch introduced (they exist after all — my earlier claim that nothing calls it came from grepping `Form.render`, and the call is spelled `Form.Wrapper.render`); `import Bastion.Test.Conn` and the capabilities it propagates. - bastion_test: 25 ambiguous `Header` constructors qualified to Http.Header; `needs IO.NetListen`. - test_gate: LenMin/LenMax/NumMin/NumMax qualified to Gate.* — depot's Gate declares the same names. - test_html_css: `import Css`. - test_islands_dataflow: `needs IO.NetListen`. Form's components pointed at the WRONG Gate. `Depot.Gate.error_for` does not exist, so Form.Input/Select/Error failed to link the moment something pulled them into an executable — invisible while the tests were pruned, and invisible to `forge build`, which only ever builds a library. The components' callers build with `Gate.cast` (bastion's own lib/security/gate.march), and that module has exactly the API in use: error_for/2 and get_field/2. Repointed all 12 references. bastion's suite: 81 compile errors -> 0, and 6 runtime failures -> 0.
`forge build` does not fetch deps. On a fresh runner the CAS is empty, so depot was never cloned and every module it provides reported as unknown — `Unknown module \`Pool\`. Did you mean \`Plot\`?` from depot_middleware. Pre-existing; it looked like a depot problem.
Ch4s3
added a commit
that referenced
this pull request
Jul 29, 2026
Ships #5: the IO.NetListen capability declarations every module importing HttpServer now needs, the FileError adaptations, the ambiguous-constructor qualifications, and Form.render's parameter reorder. Needed as a RELEASE, not just a merge: forgepm resolves bastion from the registry (`bastion = "0.2.3"`), so its CI still typechecks the published 0.2.3 sources and fails on exactly the errors #5 fixed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Everything needed to build bastion against current march
main. Found while moving forgepm onto it.Capabilities
march makes capability propagation through imports an error rather than a warning: a module importing
HttpServer(directly, or viaConn) must declareneeds IO.NetListenitself. Added to the 21 modules that do. The count kept growing as it propagated transitively —upload,auth,rate_limit,cors,channel_server,error_overlayonly surfaced after the first round.FileError
File.*now returnsResult(_, FileError), a variant, notResult(_, String). Six sites concatenated the error straight into a message; they go throughto_string, which renders the underlying text verbatim ("No such file or directory"— no constructor noise).Forge.Routes.generatealso leaked aFileErrorout of aResult(_, String); converted at the boundary.Ambiguous constructors
Headeris ambiguous betweenHttp.HeaderandWebSocket.Header;ConnandNoUpgradelikewise. Every site here destructures values fromHttpServer, so they qualify toHttp.Header/HttpServer.Conn/HttpServer.NoUpgrade.Form.renderA required parameter after defaulted ones does not type-check: the reduced-arity desugaring matches the trailing argument against the first default's type, so
render_innerwas checked againstmethod : String. Not a regression — the shape fails on older toolchains too; earlier errors simply masked it. There are no call sites in bastion or forgepm, sorender_innermoves ahead of the defaults;Form.render(conn, action, fn -> ...)still reads naturally. Happy to delete the function instead if you'd rather.Testing helpers
lib/testing/test_auth.marchandlib/testing/test_conn.marchare not in bastion's ownforge checkgraph, so their capability and ambiguity errors only surface when a consumer's test build pulls them in — which, before march-language/march#106 stopped pruning sibling test modules, never happened.Verification
forge checkagainst marchmain+ march-language/march#106: 44 errors → 0. forgepm builds and runs its full 544-test suite green against this.Depends on march-language/depot#1 for the depot-side constructor ambiguities that also show up in bastion's dependency graph.