Repository navigation
Conversation
- PathMetadata, BuildPostPath, and PostCollector as assembly relays (Component base, processMessage, msg envelope with error accumulation) — the 1.x pending-queue and disconnect-flush machinery is replaced by control ports and an explicit flush bang - PostCollector initializes its config/posts/categories once: the control-port hasData stays true, so an unguarded re-init wiped the collected categories on every activation - node:test suite for the three relays; package scaffold with the 2.x toolchain, CI and publish workflows; the 1.x Gruntfile and index.js entry removed
The setup-node cache: npm step requires a lock file; the library convention is a committed package-lock.json with only node_modules ignored.
- DocumentBuilder: template inheritance resolution, include and posts-availability checks, and pagination expansion — one message in, N page messages out; missing layouts/includes/references fail the message with accumulated errors instead of the 1.x retry queue - The initialized flag was an antipattern: control-port reads are non-consuming, so the work branch reads the buffered config on every activation, and PostCollector detects a new config IP by reference comparison instead of a flag
Reference comparison on the non-consuming control read detects a new config IP; no flag needed.
The copy broke the identity check; the buffered IP data object is not mutated by this component, so keeping the reference itself is safe.
Leftover .coffee files in components/ are fatal to 2.x discovery (uncompiled CoffeeScript crashes registry construction); the BuildPostPath nodeunit spec's replacement is test/Relays.test.js. GetConfig and Jekyll specs stay until their replacements land.
- LoadDocument reads a source file into a document message (path, source, raw body); read failures route to fail with the path message carrying the accumulated error - FrontMatter splits Jekyll YAML front matter into message fields and trims the body; bodies without front matter pass through, invalid YAML fails the message - node:test coverage for both, including the error paths
The 1.x disconnect-flush becomes the 2.x collect-until-bracket-close pattern: the posts leg arrives as a bracketed stream (ReadDir's bracketed directory listing forwarded through the pipeline), hasStream signals the complete stream, and the whole collection is processed in one activation. The component is now stateless — no accumulator, no flush bang, no tearDown state — and a second stream produces a fresh collection.
- graphs/ScanSource: the source tree scan as a subgraph — core/Split fans the source path into three filesystem/ReadDir legs (_posts, _layouts, _includes), each emitting a bracketed directory stream - graphs/Build: the full document pipeline — the scan's posts leg is bracket-stripped (groups/RemoveGroups) for the per-message wrapped components (markdown/ParseMarkdown), runs the relay chain (LoadDocument, FrontMatter, PathMetadata, BuildPostPath), is re-bracketed (groups/Group) to gate the stateless PostCollector, and the flushed config expands (ExpandPosts) into per-post build messages for DocumentBuilder → liquid/Template → filesystem/WriteFile - scripts/generate-graphs.js: the GraphModel-based generator, rerunnable The 0.x-era 8-graph set is replaced by one coherent document flow; the 0.x graphs died with their constraints (groups-as-metadata, WirePattern queues, disconnect flushing).
Same rule as the .coffee removal: the loader scans the package's graph directory, and the 0.x-era graph JSONs (referencing 1.x components and 0.x node names like Filename→document) crash registry construction.
The Pipeline.test.js debug tool's hanging network (noflo/noflo#1048) held the event loop for minutes; the EndToEnd.test.js now has a clean assertion set: the static copy verified, the document pipeline's built output verified, and the after hook force-exits for the streaming network.
…pha.6 The assembly alpha.6 includes the wrapAssembly Assembly-suffix fix (the self-reference recursion guard) — the end-to-end build now completes: the fixture site's post is rendered through the full pipeline (markdown → liquid → write) and the static files are copied.
…ent pipeline is the known open item (WD 35 update #7) The streaming network doesn't self-end (noflo/noflo#1048); the test resolves after a delay and checks the target dir. The static copy leg is verified; the document pipeline requires the assembly bracket correlation and the promise-chain bounding.
No ordering is assumed between the wires: documents, templates, and the site config arrive in any order, only within a stream is there ordering. A document whose referenced parts have not ar 8000 rived stays buffered on its inport (the head message is peeked at non-consumingly and consumed only when the config and template store can resolve it); config and template arrivals re-fire the process and drain the buffer. Missing parts are 'not yet', never 'never', so the fail-on-missing behavior is gone and the fail port is for unexpected errors only. The config port had to become a firing port: a control IIP never fires the process, so a late config could never trigger the retry.
With the CollectFields has/get-order fix and the order-tolerant DocumentBuilder the build network drains and emits end on its own — the noflo/noflo#1048-style 15s forced stop in the networkCallback is obsolete. The e2e test drops its race-with-timeout shape and asserts both the static copy and the rendered document directly
test/Jekyll.coffee and test/GetConfig.coffee were the only consumers of the tango-icon-theme fixture trees (22MB, two full source copies used by the 1.x CopyFile tests). They survived the earlier 1.x cleanup; the 2.x e2e builds its fixture site in a temp directory instead
scripts/debug-e2e.js runs the real asPromise path with a Flowtrace and dumps the activation walk — the tool that pinned the wrapped-subgraph activation leak. Ignore trace dumps and fixture build output
Carries the CollectFields has/get-order fix: the collector has-checks the message and every mapped field's stream completeness before any get, so it no longer opens an activation on partial preconditions — the root cause of the wrapped-subgraph activation leak that kept the build network from shutting down
:title expands to the Jekyll slug (the source name without its extension — a deliberate semantic change from the 1.x name-with- extension quirk; the e2e expectation moves from hello.md to hello.html), :month and :day zero-pad, and a trailing-slash permalink writes index.html into the directory, matching the 2014 Jekyll-generated reference
Converted documents enrich the site collection as they pass: the matching config.posts entry gains the rendered body as content, and pages referencing posts or the paginator resolve once every collected post converted — the 1.x two-pass shape without a second collect and without timers. Layout-chain front matter merges into the page context (a layout's class: index reaches page.class). The drain guard is per-scope and the edge-delivered config port is scoped: false, ready for per-message scope correlation once network liveness accounts for buffered IPs (work documents #45 and #35 update #16)
SplitStatic routes site root entries: front-matter files enter the document pipeline (FrontMatter → builder → shared render tail) so index.html, the category listing pages, 404.html and index.txt render through layouts and liquid instead of being copied verbatim; plain files and directories copy verbatim. CopyStatic walks directory trees recursively, skipping underscore and dot entries at every level and dropping trees that contribute nothing (baz/ with only _posts). FrontMatter's fence matcher accepts empty front matter (--- ---). Pagination pages write directory-style (page2/index.html) with the 2014 reference's paginator links
The fixture reference is regenerated with Jekyll 3.9 (the 2014-era one retires to history): non-root HTML pages write directory-style under the pretty permalink, the root index stays flat, index.txt keeps its name, and the source config gains the jekyll-paginate plugin that modern pagination requires. SplitStatic derives the page output paths from that convention. The parity test walks both trees: text files compare with blank lines collapsed (the documented rdiscount-vs-commonmark formatting difference), binary assets bytewise, and the whole comparison is strict — the known gap (nested _posts discovery, with the five files whose output depends on it) is a visible test todo, not a whitelist. The build runs under a 30s watchdog so a stall fails fast with the diagnostics dumped
The parity test records the whole build into a Flowtrace and exports it as a binary trace (.flowtrace/parity.trace, via @noflo/fbp-protocol) plus the JSON state before asserting, so a stalled or deviating build always leaves the diagnosable artifact behind. CI uploads .flowtrace/ on failure. The stall diagnosis then happens from the trace's activation walk — the last ACTIVATION_BEGIN without a matching ACTIVATION_END names the hung component — not by instrumenting the network
The generator's corpus wiring (FrontMatter between the loads and the builder's template store, the include corpus registering into the liquid engine) was lost in an earlier revert; without it layouts arrived with front matter unstripped and the footer include never registered. The builder's template consumption cannot take forwarded brackets (getData consumes the buffer head, brackets included), so the corpus edges run through groups/RemoveGroups. CopyStatic's tree walk copies binaries as buffers — utf8 round-trips corrupted the PNGs
The wrapped adapters' collector cross-pairing is fixed with unique scopes: ExpandPosts stamps each post (its path), the builder stamps each generated page. On the closedown build (noflo/noflo#1092) the network stays live while a scoped message waits buffered, and the per-scope pairing is immune to async inner completion order. CopyStatic no longer removes directories: the empty-tree rm raced the document pipeline's writes into the same path (baz/ holds both the ignored _posts tree and the rendered baz page), deleting rendered output
The activation-contract fixes from the noflo/noflo#1092 review: the scan, the static split, the copier, and the document loader returned void and ran their asynchronous work fire-and-forget — the network looked quiescent while packets were still parking in edges, and the corpus emerged into the ended network. The async work now returns its promise, holding the activation until the sends complete in-activation. DocumentBuilder: documents consumed on arrival into a per-scope state store — waiting in the port buffer stranded them behind their scope, because the config and template re-fires are unscoped and a scopeless activation cannot see scoped buffers. Every activation drains every scope's queue head-first; the enrichment runs at consumption time
…arker The includes corpus leg delivered its bracket framing into the Template's includes control port, where the framing IPs strand — the component-level closedown's buffered count never drains and the liquid wrapper cascade stops (the network hangs with every output written). The corpus now runs through the same RemoveGroups as the builder leg. The absent-corpus case (a site without _includes) emits a null marker through core/Kick on the ReadDir error, completing the render gate's firing pattern: template + variables + a completed or absent corpus
The released closing machinery and the activation-contract guard
… and the alpha.8 dependency set The includes corpus marker (core/Kick emitting the null corpus on the ReadDir error) reaches the liquid adapter's includes port directly, bypassing the document loader that would reject it; the corpus leg's bracket framing is consumed by the declarable framing termination (upstream eeba008e) instead of the RemoveGroups workaround. The dependency set: the #1093 builds of noflo, graph, assembly, and assembly-utils (the closedown hardening, the activation-contract guard, the leak warning, the stranded-framing diagnostics)
core/Merge is an all-inputs barrier: it waits for every attached input, and the ReadDir error path delivers nothing on the listing edge — the null-corpus marker from core/Kick stranded behind the barrier. The listing and the marker fan into their consumers directly
The monorepo packages at the released alpha.9 (the closing machinery, the activation-contract guard, the leak warning, the stranded-framing diagnostics) and liquid at alpha.3 (the includes scoped:false). The parity test's strict comparison covers everything implemented; the first post in collection order drops under the scoped drain in DocumentBuilder — a visible todo until the state-store drain-ordering edge is resolved, with the flowtrace artifacts on every CI failure
The monorepo packages at alpha.9 (the closing machinery, the activation-contract guard, the leak warning, the stranded-framing diagnostics, the declarable framing termination, the subgraph lifecycle recording) and liquid at alpha.3 (the includes scoped:false, the manifest shipping). The suite: 20 pass, 0 fail, 2 todos — the strict parity comparison against the 3.9-generated Jekyll reference passes, with the nested-posts and scoped-drain gaps as visible todos
…ce drops The .flowtrace/ directory is hidden (dot-prefixed) and upload-artifact@v4 excludes hidden files by default — the artifact upload found no files. include-hidden-files: true fixes the upload. The parity test's gap set: the three 2012 posts drop under CI timing when the corpus leg loses the race to the posts leg — the same scoped-drain interaction as the collection-order post, just resolved differently on the faster runner. The strict comparison covers the pages and static assets; the todos track the platform gaps
The 2.0.0-alpha.10 platform set carries the control-port gate semantics (noflo/noflo#1094) and the one-firing-rule retirement of the triggering property (work document #47). With the gate semantics, the corpus-race strand that dropped the collection-order post — and, under CI timing, the remaining posts — is gone: repeated fixture builds produce one output tree (verified plain and under Flowtrace recording), and the four root posts join the strict Jekyll-reference comparison again. The parity gap set now covers only the unimplemented features: the nested _posts discovery and the nested front-matter pages. The four posts' todo converts into the strict comparison. DocumentBuilder's config port keeps its firing-port declaration, now with the reasoning recorded: the drain's work lives in the per-scope state store, which the gate machinery cannot see; the port reverts to control when the builder returns to buffer-waiting under the one-firing-rule pump (work document #35 update #23).
…tion The nested discovery joins every root entry with /_posts via strings/Append — its standing suffix control keeps the value readable across the whole entry stream, replacing the JoinPath join whose file port consumed the IIP on the first firing and stranded the rest of the listing. The discovered directory's listing fans into the posts leg alongside the root listing. Two source groups on one connection broke the collector's single-flush semantics: one source group completing is not the collection being complete, and flushing on the first group baked an incomplete collection into every listing and pagination page. The new jekyll/StreamEnvelope terminates the per-listing framing and emits one outer bracket frame that closes when the posts leg reaches EOT — the completion moment arrives as a bracket, which fires the collector under the one-firing rule. This is the component-level form of the EOT-to-packet terminator convention (work documents #40 update #10 and #47 §8): EOT itself does not fire a process, so the completion signal rides as a bracket. The activation contract is the holder pattern: one activation consumes and forwards everything until EOT, later arrivals decline at the has-level and leave their packets for the holder, so the frame close can never race a forward. With the complete collection the nested post builds byte-identically with its path-derived category (PathMetadata's source control is now fed as a graph IIP and declared scoped: false), and every listing and pagination page joins the strict comparison. The parity gap set shrinks to the nested front-matter pages (tango/index.md — a front-matter page inside a subdirectory still routes through the static-copy leg verbatim). Two contract fixes the comparison surfaced, both against the Jekyll-generated reference: the layout chain contributes the {{ content }} substitution only (a layout's front matter is the layout's variables, not page variables — the nocontent page renders a bare <body>), and the paginator's previous_page_path for page 2 is the document's own URL (the site root's index.html maps to /), not /index.html.
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.
No description provided.