Darkwood · PHP 8.5 · Flow · Symfony
Symfony Console demo · flow-pipe
The problem
Verbose, repetitive, ANSI-colored.
Stack traces, debug lines, boilerplate.
Useful signal buried in noise.
Context floods the agent - before any reasoning starts.
Wrong instinct
waterfall → wf
pipeline → pln
stream → strm
Destroys readability. Saves almost nothing.
Root cause
flowchart TB LOG[Engine logs] TOOL[Tool outputs] TRACE[Repeated traces] CHUNK[Noisy chunks] CTX[Agent context window] LOG --> CTX TOOL --> CTX TRACE --> CTX CHUNK --> CTX
Data enters as one giant string - not as a disciplined stream.
Thesis
How data moves - not how words are spelled.
The Flow angle
The demo
flow-pipephp bin/console app:flow-token-demo \
--input=flow-engine-log --show-chunks
Deterministic. Local. No external API.
Architecture
flowchart TB CMD[FlowTokenDemoCommand\napp:flow-token-demo] SVC[TokenPipelineService] RUN[TokenPipelineFlowRunner] PAR[PipelineExpressionParser] REG[PipelineStepRegistry] CTX[PipelineContext] FIX[FlowFixtureProvider] FF[FlowFactory + FiberDriver] CMD --> SVC SVC --> PAR SVC --> RUN PAR --> REG RUN --> FF RUN --> CTX FIX --> CTX
Project structure
FlowTokenDemoCommand
CLI I/O only
TokenPipelineService
Parse + run facade
TokenPipelineFlowRunner
Full pipeline via FlowFactory
Pipeline steps
Pipeline expression
source |> strip_ansi |> remove_noise |> normalize_whitespace
|> chunk:300 |> compress |> budget:1000 |> sink
PipelineExpressionParser splits on |> and resolves names through PipelineStepRegistry.
Three |> semantics
String pipeline language
"source |> sink"
Step → ClosureJob mapping
$step |> (fn ($s) => ...)
Runtime composition
$flow->fn($job)
Visual rhythm of the DSL - at Flow composition time.
Flow runtime
$toJob = fn ($s) => new ClosureJob(fn ($ctx) => $s->apply($ctx));
$flow = (new FlowFactory())->createFlow(
$firstStep |> $toJob,
['driver' => $driver],
);
foreach ($remaining as $step) {
$flow = $flow->fn($step |> $toJob);
}
$flow(new Ip($context));
$flow->await();
TokenPipelineFlowRunner - each step becomes a ClosureJob on PipelineContext.
The result
flowchart LR N[Noisy input\n17k chars] C[Cleaned stream\nstrip + normalize] K[Chunked data\n~300 char segments] Z[Compressed context\ndeduped lines] B[Budgeted output\n≤1000 tokens] N --> C --> K --> Z --> B
Fixture: flow-engine-log · heuristic ~1 token / 4 chars
Before / after
flow-engine-log4,488 est. tokens
17,398 characters
88 est. tokens
334 characters · 98% reduction
Domain vocabulary intact. Noise and repetition removed.
Signal vs noise
flow-lexicon resists compression.371 est. tokens
354 est. tokens · 4.6%
Semantic content carries signal - abbreviating words is not the strategy.
Tomorrow
Read process stdout as it arrives - not after wait().
Takeaway
Token optimization is stream discipline.
Resources
darkwood/content/flow-pipe - Symfony demo repoapp:flow-token-demo - run the pipeline locallydarkwood/flow - orchestration packageInspired by a LinkedIn post on agentic context - cited at the end of the article.