Repository navigation
Per-instance port behavior via node metadata (work document #44) - #1098
Merged
Merged
Conversation
The noflo.* node-metadata keys apply port-behavior overrides to a component instance after instantiation and before wiring, completing the #594 lineage: a graph can now reuse an ecosystem component while tweaking the knobs its graph needs. - noflo.control: string[] — the instance's complete gate-inport set, replacing the declaration: listed names become gates (standing, non-consumingly-read configuration whose arrivals re-fire pending work per scope), unlisted former gates become direct firing edges (replace semantics makes promotion and demotion both expressible) - noflo.scoped: object — port-name to boolean, with the buffer structures re-prepared on flip (the helper runs pre-wiring, so there is no buffered state to migrate) - noflo.forwardBrackets: object — per-inport rule replacement, including the framing-termination empty rule; forwarded brackets attach to the ports the component's actual sends go through, so an override reroutes the framing when the component sends to the overridden target Applied by the Network after the port-name stamping, before subscribeNode. Overrides are per-instance (the loader instantiates one component per node, so other instances keep their declarations), validated loudly (unknown noflo.* keys — the namespace is core-owned — nonexistent ports, non-string names, and mistyped values are build errors named with the node id), and the zero-regression posture holds: a node without noflo.* metadata is untouched. The schema touchpoint: the keys are declared by the noflo/port-behavior metadata schema — the noflo-namespace catalog entry shipped with the publish-time machinery (work documents #37/#25) — and this module is the runtime consumer, validating the subset a running network can check. No catalog artifact ships in this slice; the publish gate (wd #25) is where the namespace reservation and the opt-in composition enforcement land. Spec: the knob matrix from the work document — promote data to gate, demote gate to data, scoped flips in both directions with per-scope pairing, forwarding replaced and terminated per instance, the zero-regression posture pinned by a second instance of the same component, the validation error class, and the network-level wiring with the node-id error surface
- Formatted: the four touched files now pass biome (the CI lint error) - Subgraph nodes are excluded explicitly: a noflo.* override on a subgraph node is a build error naming the limitation — the relay semantics for exported ports were not designed against per-instance port knobs, and the exclusion keeps the deterministic posture. The guard fires only when overrides are present (the first ordering threw for every subgraph node, breaking the whole subgraph suite) - All three knob helpers validate-everything-first, then apply atomically: a later invalid entry cannot leave earlier ports half-flipped (applyPortMetadata is public API) - The pre-wiring contract is in the JSDoc: a noflo.scoped flip re-prepares buffer structures and discards buffered state — direct API callers must honor it - noflo.control tightened: addressable inports are rejected (the gate model does not scan addressable buffers — a promoted addressable port would strand its arrivals); noflo.scoped tightened to inports: the outport-side flip interacts with the ordered-output and forwarding machinery and its semantics need their own design pass (the outport-flip test surfaced load going negative through the ordered machinery under a scope-mismatched send). Both rejections pinned by spec; the outport-side scoped test replaced by its rejection pin - The outport-side scoped decision recorded in work document #44
… rejections documented
- The subgraph-exclusion test uses an empty inner graph: no inner loads
run, so nothing leaks past the assertion (the round-two async-leak
finding — the Subgraph's internal network was mid-build when the
wrapper threw, rejecting unhandled)
- applyScoped validates everything before applying anything: the
boolean and existence checks moved into the validation pass, making
the flips atomic like applyControl (the round-two finding —
{a: false, b: mistyped} left a flipped)
- The three rejections documented in the CHANGELOG and the migration
guide: the subgraph exclusion, the inports-only scoped knob, and the
addressable gate-promotion rejection
- Formatted (the round-two format findings)
bergie
marked this pull request as ready for review
October 18, 2026 00:48
…alf of the knob story The runtime's schema table (work document #37 phase 2) loads installed packages' noflo.json schemas tables at startup and serves the bodies by digest through the Two-Step Cache (0x29, with mandatory client-side digest verification and closure completeness) — so shipping the artifact and pinning it in the manifest is what makes the noflo.* vocabulary visible to protocol clients: fbp-client and noflo-ui fetch the catalog and the per-component composed config schemas, verify digests transitively, and know the vocabulary without assuming. - schemas/port-behavior.json: the noflo-namespace catalog — the three knobs with their schemas, class config, kind node; the outport-side scoped flip documented as not overridable - noflo.json: the package manifest's schemas table, pinning the artifact's digest (computed via the fbp-protocol digestSchema over the canonical form) - The package files field ships noflo.json and schemas/ - PortMetadata's schema touchpoint updated: the artifact ships, the manifest pins it, the runtime's table serves it - The runtime spec pins the client story end to end: loading the @noflo/noflo package manifest into a SchemaTable, the digest pins holding, and the served body byte-identical to the shipped artifact The deployment wiring (which installed packages a given runtime scans) stays the deployer's policy per the work document #24 ladder; the per-component composed config schemas (the allOf composition, work document #37) flow from the publish-time manifests as components opt in
The shipped catalogs' style was enforced only by hand — the format and lint scripts' globs did not include the schemas directories, so inconsistencies between sibling artifacts could not be flagged by CI. The root format script and the packages carrying schemas directories (assembly, fbp-protocol, noflo) now cover them. The reformat of the noflo port-behavior artifact is the kinds arrays collapsing to the siblings' single-line style; the digest is over the canonical form and the manifest pin still matches. Also: the CHANGELOG amendment for the shipped artifact (the round-four finding — the packaging-visible change: the tarball now ships noflo.json and schemas/)
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.
The noflo.* node-metadata keys apply port-behavior overrides to a component instance after instantiation and before wiring, completing the #594 lineage: a graph can now reuse an ecosystem component while tweaking the knobs its graph needs.
Applied by the Network after the port-name stamping, before subscribeNode. Overrides are per-instance (the loader instantiates one component per node, so other instances keep their declarations), validated loudly (unknown noflo.* keys — the namespace is core-owned — nonexistent ports, non-string names, and mistyped values are build errors named with the node id), and the zero-regression posture holds: a node without noflo.* metadata is untouched.
The schema touchpoint: the keys are declared by the noflo/port-behavior metadata schema — the noflo-namespace catalog entry shipped with the publish-time machinery (work documents #37/#25) — and this module is the runtime consumer, validating the subset a running network can check. No catalog artifact ships in this slice; the publish gate (wd #25) is where the namespace reservation and the opt-in composition enforcement land.
Spec: the knob matrix from the work document — promote data to gate, demote gate to data, scoped flips in both directions with per-scope pairing, forwarding replaced and terminated per instance, the zero-regression posture pinned by a second instance of the same component, the validation error class, and the network-level wiring with the node-id error surface.
Fixes #594