Expand description
Project-wide orchestration: discovery → parse → group → resolve, shared
between bynk-emit’s run_checks (both Mode::Build and Mode::Analyse)
and this crate’s own crate::analysis::analyse_project.
P4.1 (#1115), second scope finding on the tracking issue: this pipeline —
phase_discovery through assemble_unit_info, plus the per-unit symbol
composition (compose_unit_symbols/merge_consumed_exports/
collect_unit_methods) — used to live only in bynk-emit/src/project.rs,
inline in run_checks. A literal no-indirection bynk-check-side analysis
entry point needs the identical sequence, so rather than write a second,
independently-maintained copy (the mistake this whole design track’s
extract, don't duplicate principle exists to prevent — see
lower_field_default_wire, build_capability_op_info for the same move
made earlier in this track), it moved here. bynk-emit’s run_checks
becomes a caller of these functions instead of owning the logic, the same
way P4.0 turned project.rs into a caller of bynk-project.
What stayed in bynk-emit (not shared, because only the Mode::Build path
needs it, or because it’s genuinely emission-shaped): the Mode::Build
bail gate and everything from emission onward (EmitUnitCtx, emit_unit,
collect_history_target_agents). The whole-project messages/locale-
ambiguity/event-subscription checks (P5.0/P5.1), the function-type-
boundary check (P5.2, phase_function_type_boundaries), and
schema-registry reconciliation/platform-lock enforcement (P5.3,
crate::schema_registry::reconcile/phase_platform_lock) have since
moved here too — the P5.2 move closed phase_group’s optional
boundary-check hook, which used to be the only way run_checks and the
new entry point could reach it without duplicating the diagnostic-ordering
logic (see analysis.rs for the residual-gap accounting that remains).
Structs§
- Adapter
Binding - v0.17: a resolved adapter binding — the user-authored
.binding.tsmodule that supplies an adapter’s external provider symbols. Copied verbatim into the output beside the adapter’s emitted interface module so thattscchecks theimplementscontract and compose can import the symbols. - Error
Sink - Collection-point error sink (ADR 0052). Helpers keep their plain
&mut Vec<CompileError>signatures; call sites attribute viaextend_forwith the file in scope at that point. - FnDecl
- P6.49 (design/tracks/the-ir.md §6b), following P6.27’s
ExprIdprecedent (checker.rs:39): re-exported because this module’s own public API —compose_unit_symbols’scombined_types/combined_fns,collect_unit_methods’s return, andUnitInfo::exports’s value type — is already parameterised by these three types.bynk-emitonly ever plumbs the resulting tables through to otherbynk-checkcalls; it never matches a variant of any of the three. - Type
Decl - P6.49 (design/tracks/the-ir.md §6b), following P6.27’s
ExprIdprecedent (checker.rs:39): re-exported because this module’s own public API —compose_unit_symbols’scombined_types/combined_fns,collect_unit_methods’s return, andUnitInfo::exports’s value type — is already parameterised by these three types.bynk-emitonly ever plumbs the resulting tables through to otherbynk-checkcalls; it never matches a variant of any of the three. - Unit
Info - v0.29.4: the per-unit facets that the producer phases build as nine parallel
HashMap<String, _>s, all keyed on unit name. Assembling one record per unit makes the “all these maps share one keyset” invariant structural: a single lookup yields every facet as a field, so the per-column.unwrap()s on the shared keyset disappear. Fields are total —exports/aliases/flatteneddefault to an empty map for a unit with no entry, reproducing the old.unwrap_or(empty)read semantics without the dance.
Enums§
- Build
Target - The build target. Determines how cross-context calls and per-context modules are emitted (v0.8). Bundle mode is the default — all contexts emit into one TypeScript bundle and cross-context calls are direct function invocations. Workers mode produces per-context Cloudflare Worker bundles that communicate via Service Bindings.
- Visibility
- P6.49 (design/tracks/the-ir.md §6b), following P6.27’s
ExprIdprecedent (checker.rs:39): re-exported because this module’s own public API —compose_unit_symbols’scombined_types/combined_fns,collect_unit_methods’s return, andUnitInfo::exports’s value type — is already parameterised by these three types.bynk-emitonly ever plumbs the resulting tables through to otherbynk-checkcalls; it never matches a variant of any of the three. Visibility level for an exports clause (v0.4 §3.3).
Constants§
- FIRSTPARTY_
ID_ BASE - FIRSTPARTY_
ID_ BLOCK - A memoized parse of one first-party synthetic source, keyed by the
call-site’s own
cachestatic — each ofphase_parse’s 7 injection sites below passes a distinct one. Finding #55/#65: the source text is a fixedinclude_str!constant, so its parse is a pure function of that constant and only needs computing once per process, not once per compile/analyse round. The gating below (consumes_bynk,uses_map, etc.) is unaffected — it still runs fresh for every project from that project’s own parseduses/consumes; only the parse result being gated is cached. T3.4 (R2.4): each first-party synthetic unit reserves its own 1M-wideExprIdblock, spaced far above anything a real project’s own file count could ever reach — seefirstparty_parsed’s doc comment for why a fixed reservation, not a threaded counter, is the right shape here.
Functions§
- assemble_
unit_ info - v0.29.4: fold the nine parallel per-unit maps into one
HashMap<String, UnitInfo>. Assembly is driven by thegroupskeyset (the authority), so every group yields exactly one record. Facets that are genuinely optional in the producer maps (exports/aliases/flattened, andfile_indexfor a unit with no declarations) default to empty — reproducing the old.unwrap_or(empty)read semantics as a total field. - check_
discovered_ files - The checks every tree’s file list must pass regardless of where it came
from — a real disk walk (
phase_discovery) or a caller-supplieddiscovered/CompileOptions.sourceslist (#1077/#1081 review). An empty project (bynk.project.no_sources) signals a bail viaErr(()); a file/directory name conflict is a non-fatal diagnostic. - check_
function_ type_ boundary_ items - Item-level body of the boundary confinement, shared with the single-file
(legacy) compile path in
bynk-emit’slib.rs. Relocated alongsidephase_function_type_boundaries(P5.2). - collect_
type_ decls - v0.174 (#592): a
name -> TypeDecltable over a set of items, for the recursive-generic boundary walk. Relocated alongsidephase_function_type_boundaries(P5.2) — public sincebynk-emit’s single-file compile path (lib.rs) also needs it, across the crate boundary this relocation now draws. - collect_
unit_ methods - Phase 8c: collect every method authored anywhere in one unit, keyed by its
attached type’s name — so a type’s methods surface in the file that declares
the type even when the method lives in a sibling file. The collection loop
has no
continues, so it lifts out whole. - compose_
unit_ symbols - Phase 8a: compose one unit’s symbol space — its local table plus a
one-level
usesmixin (commons identity preserved). Returns the combined type/fn/method tables and theimported_fromprovenance maps; the mixin loop has nocontinues, so it lifts out whole. - firstparty_
parsed - handler_
cross_ caps - v0.15: the cross-context capabilities a context’s handlers reference,
as
deps_key → consumed_context. Shared withbynk-emit, not duplicated — seeresolve_consume_prefix’s doc. - inject_
service_ defaults - Inject a single service’s
by/givendefaults into its handlers. A handler that names its ownby(orgiven) overrides the default outright — the default fills only an absent clause, never merges. A service with no default is left untouched (byte-for-byte the pre-v0.155 behaviour). - merge_
consumed_ exports - Phase 8b: merge one context’s
consumesexports into the composed symbol space, recording visibility metadata in the returnedconsumed_types. The per-exportcontinues (missing decl, name conflict) stay internal to the loop, which lifts out whole; name conflicts are pushed intoerrorsand the caller’sgroup_error_baselineguard reacts to them after this returns. - normalize_
service_ defaults - phase_
consumes_ aliases - Phases 5b’/5b’‘: collect each context’s
consumesaliases (alias → consumed-context name), reporting alias-vs-alias conflicts (5b’), then report any alias that clashes with a locally-declared type/fn/capability/service/agent (5b’’). Returns the per-context alias maps; diagnostics go intoerrors. - phase_
detect_ consumes_ cycles - Phase 5c: detect
consumescycles. #696: record eachconsumes-clause site (file + span) keyed by(consumer, target)so a detected cycle anchors on the exact clause that forms the closing edge — a real span in a real file — and renders with source context. Synthetic units are left out so their (snapshot-less) files never claim a diagnostic. - phase_
discovery - Phase 1: discover the
.bynkfiles under the source (and, in split mode, the tests) root by walking the filesystem. Pushes any discovery error intoerrorsand signals a pipeline bail viaErr(())(the caller terminates withfinish); otherwise returns the discovered(src_files, tests_files). - phase_
event_ subscriptions - Events track, slice 0 (spine #936): a
from Events(E)subscription must name a real, declared event — owned either by this context or by a context itconsumes(mirroringdiscover_event_subscribers’s own ownership resolution,project.rs, which silently drops an unresolvable subscription rather than diagnosing it). Runs at the project-wide phase (needsunit_tables+unit_consumestogether, unlike the local, per- contextcheck_service_protocols), alongside the other cross-unit checks that need the same two maps. - phase_
file_ index - Phase 7: build each production unit’s file-declaration index (which file in the unit declares which name), for cross-file lookups in the back half.
- phase_
function_ type_ boundaries - v0.20a: apply the function-type boundary confinement to every serialisable
or boundary-crossing position in a file’s items: record fields and sum
payloads (types can cross contexts and persist), service/agent handler
signatures (the Workers wire), capability operation signatures (kept out
in v0.20a — see ADR 0030), agent state fields, and agent keys. Free
fnsignatures are deliberately NOT walked — they are the non-boundary home of function types. - phase_
group - Phase 3: group the parsed units by qualified name (production units, unit
tests, and integration suites tracked separately), run the per-directory
and path/name consistency checks, enforce the reserved
bynknamespace and the adapterbindingrules, resolve each adapter’s binding module, and fold the adapters’ pinned npm dependencies. Pushes diagnostics intoerrorsand returns the productiongroups/kinds, thetest/integrationgroups, the resolvedadapter_bindings, and the collectednpm_deps. - phase_
locale_ bundle_ ambiguity - Locale capability track, slice 2 (#882): a context whose direct
usesreaches two or more message-bundle commons has no principled single answer for whatLocale.current()should negotiate against — but this is only worth diagnosing when the context actuallyconsumes bynk { Locale }at all; a context with 2+ bundles that never touchesLocalehas nothing ambiguous to resolve. - phase_
messages_ bundles - message-bundles slice 1 (#859): messages-block legality,
@referencecardinality, within-block duplicate codes, and theuses bynk.localedependency. Runs here (not inphase_group) because it needsunit_uses, resolved just above. - phase_
parse - Phase 2: parse every discovered file into a
ParsedFile, recording each file’s source text intosnapshotsand any parse errors intoerrors. Then inject the first-party synthetic units (thebynk/bynk.cloudflareadapters and thebynk.{list,map,string}commons) that the project consumes/uses. Returns the parsed units plus whether thebynkandbynk.cloudflareadapters were injected, and (#1710) each unit’sBrokenDeclNames; signals a pipeline bail viaErr(())when parsing produced errors and yielded no units at all. - phase_
platform_ lock - v0.19 (decisions 0017/0024): enforce the platform lock per deployment
unit — each context under
--target workers, the whole program underbundle(co-location shares the lock). - phase_
resolve_ consumes - Phase 5b: resolve each unit’s
consumesclauses (target exists, is a context or adapter, not self-referential, obeys the adapter selection rules), and for the bracedconsumes U { Cap, … }form validate and record the flattened capabilities. Returns unit → consumed targets and unit → flattened-cap → owning unit; diagnostics go intoerrorsand clause-position references intorefs. - phase_
resolve_ uses - phase_
secrets_ computed_ name - v0.173 (ADR 0196 D1), P5.5 (
design/tracks/semantics-in-the-checker.md§6, §9): warn where abynk.Secretsread names its secret with a computed expression. Non-failing — the program is correct,bynk deploysimply cannot see the name — walked per file rather than per unit, since a mergedUnitTablehas thrown away which file a call site lives in andErrorSink::extend_forattributes a diagnostic to a path. - phase_
symbol_ tables - Phase 4: build each production unit’s combined symbol table from its files,
pushing any table-construction errors into
errors. - phase_
uses_ name_ conflicts - Phase 6: for each unit, detect when two
uses-imported commons declare the same (non-shadowed) type or function name — an unrenamable conflict at the use site. Diagnostics go intoerrors. - phase_
validate_ capability_ exports - Phase 6b’: validate each context/adapter’s
exports capability { … }clauses (v0.15 §4.1) — every name must be a capability the unit declares and provides, with no duplicate exports. Diagnostics go intoerrorsand export references intorefs. - phase_
validate_ providers - Phase 6c: validate that every (non-external) provider matches its capability
exactly — each capability op has a provider op, and every provider op has a
matching capability op with the same parameter and return types. Diagnostics
go into
errors. - phase_
validate_ type_ exports - Phase 6b: validate each context/adapter’s
exports opaque/transparent { … }clauses — every name must be a locally-declared type, with no duplicates within a clause or conflicting visibilities across clauses. Returns unit → (type → visibility); diagnostics go intoerrorsand export references intorefs. - resolve_
consume_ prefix - v0.15’s cross-context capability resolution, relocated alongside
phase_platform_lock(P5.3): resolve agiven/handler capability prefix (ctx.Cap) against a context’s ownconsumes/alias tables. Pure — no codegen, nobynk-emitdependency of its own — so unlikecollect_given_closurethis one is shared rather than duplicated:bynk-emit/src/project.rs’s own copy of this function (and ofhandler_cross_caps) was deleted in review (#1133) and every one of its call sites repointed here —bynk-emitalready depends onbynk-check, so there was no dependency direction to route around, and keeping two copies only bought two things that could drift out of sync for no reason. - tree_
root_ for - The
includetree that discoveredpf, found by matching its absolute path against each tree’s root — nottrees[0]unconditionally, since R3.9 (#1113) lets a file live under anyincludetree, not just the first. Falls back totrees[0]for apfwith noabs_path(unreachable for a real adapter: only synthetic units, which never declarebinding, go without one) or if it somehow matches none. The longest matching root wins, in case oneincludetree is nested inside another. - uses_
first_ order - Phase 5: resolve each unit’s
usesclauses, checking the target exists, is a commons, and is not self-referential. Returns unit → deduplicated list of used commons; diagnostics go intoerrors. #1702 review: the order to check units in — everyusestarget before the units that use it, ties broken by name, so the order stays a function of the source alone (deterministic_diagnostic_order_behaviour). A generic function’s compared type parameters (#1688) are computed while its own unit is checked, in that unit’s environment, so an importer must come after. Units on ausescycle (already an error) are appended in name order.
Type Aliases§
- Broken
Decl Names - #1710: for each unit with a file the strict parse rejected, the names of
the declarations recovery skipped there. References to them, from any file
that can see the unit, are known names, not unknown ones (#1663’s Decision
B); see
crate::check_pipeline::prepare_unit_check_ctx.