refactor(framework)!: one phoxal library with consumer profiles, a Rust-declared api tree, and phoxal::session - #466
Merged
Merged
Conversation
…/src Pure file moves so history follows the files; nothing compiles yet.
Rewrites every path, folds the six manifests into phoxal/Cargo.toml, adds the authoring/router features, and points the protocol tree macro at the new in-crate module root. Workspace check and clippy are green; tests next.
… canon Reduces the policy gate's library graph to phoxal + phoxal-macros, moves the authoring feature check onto the one library, drops the six release-plz packages and the crates.io wait list, and renames the module docs that still named the retired packages.
The library-grammar tests named packages the merge deleted; they are pure string rules, so they now use the surviving package and hypothetical names. The component editor schema's one changed description follows the doc comment that stopped naming the retired phoxal-model package.
…e from record identity The framework is one library, so a baseline is the newest non-yanked `phoxal` and nothing else is read. Deletes the newest-common-version resolution, the partially published train diagnostic, the two-floor refusal, and the predecessor-package fallback. Record identity is now the record kind plus its own key: nothing on the wire carries a Cargo package name, so a record that moved between modules is the same record. A baseline below the `0.66.0` topology floor is still read through the five packages that carried the surface then, and through `phoxal-manifest` for the authored-source leg, at that same exact version. Their records are unioned, which is what lets the merge release prove itself as no wire change. `xtask/src/legacy.rs` holds all of it and is deletable once a `0.66.x` `phoxal` is published. The authored-source probe now depends on `phoxal` with the `authoring` profile and reads the corpus through `phoxal::authoring::probe`; the rehearsal pins the Stable-line tests under their merged module paths in `phoxal`.
`feature gates live only in the framework crate root` keeps a consumer profile a visibility decision: it changes which modules `phoxal/src/lib.rs` declares as public, never what a module compiles. `official participants reach for no host profile` holds this repository's own services, drivers and examples to the participant surface, in source and in the features they enable. `raw transport access stays inside the bus module` and `endpoints and topic keys are never authored by hand` keep the second copies out: of the transport, and of the topic tree. `retired framework libraries stay absent from the dependency graph` refuses a dependency on any of the six merged packages, whose last published versions stay resolvable forever. The authoring-feature check for official participants moves into the new consumer-profile rule, which already covers that package set; the dependency rule keeps it for the framework executable, which is not a participant. The retired-surface self-exemption becomes a list, because the consumer-profile rule spells `phoxal::simulator` in order to hunt for it.
The runbook described a baseline resolved from five carriers with a renamed predecessor, and a source leg read through `phoxal-manifest`. Restates both against the one library, records the one baseline still read through the retired topology and when that section can go, and lists the four new policy rules. Two CI comments counted the packages a train publishes; they now count two.
… one The bus module owns key grammar - liveliness and presence keys, execution roots - so a literal there is the owner stating its own key rather than a second copy of the tree; it is exempt from the topic-literal half exactly as it is from the transport rule. The `impl Endpoint` half still admits only `bus/tree.rs`. A literal beginning `"robot/meshes` is the bundle's asset layout. The asset root and the robot family root collide as text and only their meaning separates them, so the rule reads past that one prefix and says why; a line holding both an asset path and a real key is still a finding. `crates/fixture` joins `xtask` as a tree neither rule reads: it is unpublished dev-only test support that no robot links.
…l_tree! The dynamic endpoint tree stops being a global procedural catalogue and becomes two private declarations that sit in the modules they describe: `crate::nodes!` owns a module's child nodes and their typed dynamic segments, `crate::endpoints!` owns its endpoint leaves beside the payload types they carry. Static segments are spelled by their child module's identifier, dynamic placeholders by their declared variable's, and the leading segment by the family marker, so no complete topic string is authored anywhere. The payload (or request) type is now the endpoint type. Descriptor ZSTs, the generated `endpoint`/`topic` modules, the duplicated per-side builder trees, `ApiFamily`, `ParticipantSpec::ContractApi`, and the marker-trait zoo are gone, replaced by one sealed `Endpoint` with a `Family` and an `EndpointSemantics`, one path tree with `.client()`/`.owner()` chosen at the leaf, and one owned `Topic`. `phoxal::api`, `phoxal::runtime::api` and `phoxal::supervisor::api` are real modules; `phoxal::__compat::contract_surface()` is the crate aggregate the three families and the other owners state their records into.
Two rules the tree cannot enforce inside one crate: an endpoint's family and semantics come from the `endpoints!` declaration beside its payload and nowhere else, and a family-rooted key is rendered by walking the tree rather than spelled by whoever needed it. Committed test sources are outside both, because the bus is the ABI floor and its own tests declare stand-in endpoints and pin rendered keys against the literals they are expected to be. `protocol_tree` joins the retired-surface table: there is no global procedural catalogue to bring back.
… with the api tree Keep one implementation of the authored-endpoint rule (bus_boundary), drop the duplicate api_tree module, exempt test code from the impl half, treat a cfg(test) test_support.rs as test code, allow the frozen presence lease literal beside the bootstrap, repin the six bootstrap rehearsal tests at their merged module path, and stop spelling a runtime key in the world-clock subscriber's error prose.
…e the supervisor, the client and the simulator into it The crate stops being one overwhelming default API and becomes one product with six named consumer roles. `default = ["participant"]` is a robot developer's surface; `session`, `simulator`, `supervisor` and `authoring` are the host roles around it, and `test-harness` is explicit participant test support. A profile decides which modules `lib.rs` declares as `pub` - it never forks a code path, and `cargo xtask policy` keeps it that way by rejecting `cfg(feature` anywhere but the crate root. Three implementations move in behind those profiles: - `phoxal::supervisor::host` is the whole supervisor (`framework/supervisor/src` minus `main.rs`), so `phoxal-supervisor` is now a binary that parses one operand and calls `run()`. With the implementation in-crate, `BusOwner`, `BusConfig`, the embedded router, `TimelineAuthority`, `WorldClockPublisher` and every step minter become `pub(crate)`: no consumer profile receives raw transport, world or fabric ownership. - `phoxal::session` is the CLI's `phoxal-client`, rewritten over the dynamic api tree. `Session` owns the transport and the lifecycle, `SessionHandle` performs typed operations, and the family wrapper modules are gone - consumers name `api`, `runtime::api` and `supervisor::api` directly. - `phoxal::simulator::SimulatorSession` is the external world adapter's whole surface: typed component IO, delegated presence, deterministic close, and a `WorldTime` taken once and moved to the step thread. `phoxal::testing` grows `TestBus` and `step_token`, because opening a bus is now something the framework does on a test's behalf rather than something a test does for itself.
`phoxal::simulator` is now the external simulator host SDK, and it means the opposite of the role attribute that used to be spelled the same way: a world owned from outside the graph rather than one participant inside it. The rule matches the attribute spelling, so the retirement it guards stays guarded and the new module path is ordinary source. The consumer-profile rule's exemption goes with it - it only existed because that module names `phoxal::simulator` - and the bus's two failure-injection hooks stop riding `test-harness`: they are `cfg(test)` and crate-private, which is what their only callers ever needed, and what removes the last Cargo feature named outside the crate root.
… and pin the surfaces a profile hides The crate root carried a second doc fragment for most modules - one on the `mod` line and one in the module's own header - and rustdoc merged the two from different scopes, which quietly broke every intra-doc link in the merged half. Each module's `//!` header is now its documentation, and what `lib.rs` says about a module is a plain comment about the profile decision. That, plus spelling rather than linking anything a profile hides, takes `cargo doc` to zero warnings for `default`, each host profile, and `--all-features` alike. The compile-fail fixtures move with the surfaces they judge. A participant cannot name `BusOwner`, `BusConfig` or the router, and cannot name the runtime or supervisor families at all - the two new `fail/` cases - while the cases that need a host family to exist before they can be refused move to `host_fail/`, run by a second trybuild pass under `session`. `phoxal::participant::launch` gains the encoder half of the launch contract: the CLI writes participant argv locally and into systemd units, so the flag spellings would otherwise live in another repository and drift from the parser that has to accept them. The round-trip test is what keeps them one contract. CI gains the two jobs the profiles need: every canonical profile built and documented explicitly (a workspace build unifies features and can say nothing about any single one), and `cargo semver-checks --all-features`, because release-plz runs that tool with default features and exposes no way to pass a feature selection through.
… that earn them An `impl`-wide allow on `BusOwner`'s liveliness block and on `BusConfig` covered methods that are alive in every profile, which would have hidden a real dead one later. Each allow now names the item whose only caller is a module one profile declares: the supervisor's own presence key, delegated participant presence, and the external session configuration.
…and hide the participant engine from the profile that is one The role attributes already emitted `$crate::__private::…` for every item but one: the const-eval metadata writer reached `concatcp!` through `$crate::participant::metadata`, which forced that module to stay public in every profile. `__private::meta` already carried the same `const_format` re-export for the config-schema path, so the second one beside the record was a duplicate of one macro. Deleting it leaves `__private` as the whole ABI, exactly as the module says it is. With that, `phoxal::participant` takes the visibility the profile table gives it. Its two public children are process contracts a *host* reads - the metadata document a staged binary embeds, and the argv a launcher writes - so it is public for `session`, `simulator`, `supervisor` and `authoring`, and private for the profile that *is* a participant, whose authoring surface is the crate-root facade. Which half of that module is unreachable depends on the profile, so the dead-code allows follow the halves rather than the module: a host profile allows the engine below the two contracts, the two contracts allow their own reader and encoder, and a build that selected no role at all allows both. The participant profile allows neither, which is where the engine is alive and therefore where the lint has something to say. `xtask`'s bus-boundary fixture named `supervisor/src/router.rs`, a path the supervisor move deleted; it now names a file that exists.
…rofile too The table publishes `participant::metadata` for session, supervisor and authoring only; a simulator stands in for drivers but never launches or inspects a participant binary. Also spell the model path by its module name in one authoring comment.
The shared workspace test unifies only the features the members enable, which never includes session or simulator; the profile-gated tests and the required-features integration tests need one all-features run. Rustdoc per profile now fails on warnings, matching the local gate.
A simulator's step loop holds a WorldTime and the only source of one is a session; an adapter proving its own stepping and parking discipline needs a session no supervisor is running. Same constructor the framework's own proofs use; there is no router, so it is not a second way to attach. The four runtime examples state the participant profile they need.
… and state the branch-or-leaf tree rule close() now revokes delegated Ready leases, drops the world's time, closes the transport and returns the BusCloseReport as SimulatorCloseError when any stage left evidence; the struct's field order is the same teardown order, so a session dropped without close tears down safely. The api docs and crate root state that a tree module is a branch (nodes!) or a leaf (endpoints!), never both.
Contributor
Author
|
Review corrections applied (ae69376):
|
… default profile The example is pinned to the published train on purpose and its lockfile is part of the proof; naming the participant feature explicitly only resolves once 0.66 is published, and the default profile is participant anyway.
…rk repository The example robot project is phoxal/robot-rover; an in-repo copy pinned to a published train can only lag the tree or stop being a runnable project. Its CI job goes with it; the authoring-corpus tests and the authored-source compatibility leg read the in-repo fixture robot, which mounts every component type the repository authors.
Contributor
Author
|
Per review: the hello-rover example and its CI job are removed (caa4542); the example robot project is phoxal/robot-rover. The authoring-corpus tests and the authored-source compatibility leg read the in-repo fixture robot ( |
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.
What
The framework's six library packages (
phoxal-protocol,phoxal-bus,phoxal-bundle,phoxal-model,phoxal-manifest,phoxal-runtime-contract) become real modules of the onephoxalcrate.phoxalis now the only external Rust dependency, the framework version owner and the sole compatibility carrier;phoxal-macrosstays as the proc-macro package.participant(default),session,simulator,supervisor,authoring,test-harnessdecide which supported surface is public;cfg(feature)lives only inlib.rsandCargo.toml(policy-checked). Every module compiles in every profile; the compat aggregate is profile-independent.phoxal::api(robot, unchanged for participants),phoxal::runtime::api,phoxal::supervisor::api.protocol_tree!deleted. The api tree is declared in Rust: privatenodes!/endpoints!declarations beside the payloads; the payload type is the endpoint; one path tree with side selection at the leaf (api::topics().drive().state().client()); compatibility records derive from the same declarations; one ownedTopic; sealedEndpoint/EndpointSemantics; the marker-trait zoo, descriptor ZSTs andApiFamilyare gone.phoxal::sessionreplaces the CLI'sphoxal-client(Session,SessionHandle,ConnectOptions,ConnectedExecution, phase-specific errors; frozen bootstrap + compatibility-line refusal unchanged).phoxal::supervisor::hostis the supervisor implementation (featuresupervisor);phoxal-supervisoris amain.rs. Raw bus ownership (BusOwner,Router,TimelineAuthority, world-clock minting) ispub(crate).phoxal::simulator::SimulatorSession/WorldTime: the narrow external-simulator host SDK (featuresimulator).phoxal::authoring(exphoxal-manifest, featureauthoring).cargo xtask compatibilityresolves one publishedphoxalbaseline; record identity is carrier-independent; a below-floor legacy read (xtask/src/legacy.rs, deletable after 0.66.x publishes) unions the five 0.65 packages for the one comparison that crosses the topology floor.Wire-neutral by construction and by proof:
phoxal::__compat::contract_surface()renders the same 64 records as the union of the five published 0.65 surfaces, byte for byte.Evidence
cargo fmt --check,cargo clippy --workspace --all-targets -D warnings,cargo clippy -p phoxal --all-features,cargo hack check -p phoxal --each-feature,cargo test --workspace(861 tests),cargo test -p phoxal --all-features(630 tests),cargo doc --all-featureswithRUSTDOCFLAGS=-D warnings(0 warnings),cargo xtask policy(17 rules, all hold),cargo xtask compatibility rehearse-v1(20 drilled, 20 held).PHOXAL_COMPAT_BASELINE_DIR=<0.65 surfaces> cargo test -p phoxal --all-features --test api -- --ignored->the_aggregate_is_the_0_65_surface_byte_for_byte ... ok(54 endpoint + 7 bus + 1 bundle + 1 identity/version + 1 launch records on both sides).cargo xtask compatibility report->baseline: 0.65.0,contracts: unchanged,source: compatible (3 authored projects),toolchain: 1.88 (unchanged).phoxal::session::Session(session profile) attaches to the releasedphoxal-supervisor 0.65.0process over the frozen bootstrap:CONNECTED: remote framework 0.65.0 robot rgbd-imu-diff-drive; with the workspace labelled 0.66.0 the same attach is refused structurally:REFUSED: remote 0.65.0 local 0.66.0 refusal LocalNewer.session_attaches_to_the_supervisor(host + session on one execution), simulator world-session tests (delegated presence, outputs-before-clock, timeline replacement, deterministic close), 26 trybuild fixtures (family/semantics/side refusals, participant cannot reach host families or open a bus).phoxal::session,phoxal::simulator,phoxal::participant::metadataare unreachable there and reachable under their profiles.Consumers
phoxal-cliandphoxal/simulator-webotscut over in their own PRs against this train (phoxal = "0.66"withsession,authoring/simulator); the org canon follows.Review
Deep-track class (wire contracts, session lifecycle, release plumbing). Reviewed by the integrating agent with the evidence above at the maintainer's direction (no Codex counterpart for this campaign).