Skip to content

Per-run logging scope: in-memory sink onto BuildContext (FT-6)#453

Draft
ChrisonSimtian wants to merge 4 commits into
Fallout-build:mainfrom
ChrisonSimtian:engine/ft6-logging-scope
Draft

Per-run logging scope: in-memory sink onto BuildContext (FT-6)#453
ChrisonSimtian wants to merge 4 commits into
Fallout-build:mainfrom
ChrisonSimtian:engine/ft6-logging-scope

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Collaborator

Stacked on #452#451#450 (FT-4 → FT-2 → FT-1) — diff includes their commits until they merge; I'll rebase down the chain as they land. Draft until then.

Engine de-statification ([Foundation] epic #315) — FT-6 / #311. Second service to ride the BuildContext rail.

Logging.InMemorySink is now owned per-run by BuildContext.LogSink; the static InMemorySink.Instance is a facade over BuildContext.Current (lazy fallback outside a run). Serilog's ConfigureInMemory (run per build) wires the per-run sink and WriteErrorsAndWarnings reads it — both during the run — so log events no longer carry across invocations. The FT-1 InMemorySink.Clear() on dispose is dropped: the sink is discarded with the context.

Non-breaking — the static Logging API is unchanged. Full build + test suite green.

🤖 Generated with Claude Code

…ion leaks)

BuildManager.Execute leaked process-global state across invocations, so a second build in the same
process (tests, hosted/reentrant scenarios) inherited the first's residue:
- Console.CancelKeyPress / ToolOptions.Created handlers were re-subscribed every call and never
  removed (accumulating);
- the CancellationHandler list kept each run's Finish (capturing a stale build);
- Logging.InMemorySink.Instance carried log events over;
- ValueInjectionUtility's value cache and the NuGet/Npm tool-path resolver config persisted.

Fix (non-breaking): the two global handlers are now held in locals and the finally undoes exactly
what the run set — unsubscribes both, removes Finish from the cancellation list, and resets the
carried-over state (InMemorySink.Clear, ValueInjectionUtility.ClearCache, NuGet/NpmToolPathResolver.Reset).
Build creation moved ahead of the subscriptions so a failed `new T()` leaks nothing. Groundwork for
the BuildContext foundation (FT-2). Full build + test suite green; reentrancy is verified by FT-9.
Adds BuildContext — an internal, per-run, AsyncLocal-ambient scope activated at the top of
BuildManager.Execute (`using var context = BuildContext.Activate()`) and disposed at method exit.
It owns the per-run global-state lifecycle: the Console.CancelKeyPress / ToolOptions.Created
subscriptions, the cancellation-handler list, and the teardown FT-1 centralised (now run on dispose).
BuildManager.CancellationHandler becomes a thin facade over BuildContext.Current — the first
"static surface over the context" seam, and the rail subsequent steps ride (FT-4 ParameterService,
FT-5 tool-path config, FT-6 logging scope move their state onto the context).

Internal-only; not a public contract until the SDK (milestone #7). Disposing on method exit means a
failed `new T()` also leaks nothing. Full build + test suite green.
The active ParameterService is now the per-run instance held on BuildContext.Parameters; the static
ParameterService.Instance becomes a facade over BuildContext.Current. This is the first service to
ride the FT-2 rail: production and tests now exercise the same instance form (killing the test/prod
divergence — tests already `new ParameterService(funcs)`), and the per-run instance + its mutated
fields (ArgumentsFromFilesService / ArgumentsFromCommitMessageService) no longer leak across runs.

A lazy fallback (s_ambient) covers any access outside a build run (no cross-run concern there); it
can retire once that path is confirmed dead. Non-breaking — the static API is unchanged. Full build
+ test suite green.
Per-run logging scope: Logging.InMemorySink is now owned per-run by BuildContext.LogSink, and the
static InMemorySink.Instance becomes a facade over BuildContext.Current (lazy fallback for logging
outside a run). Serilog's ConfigureInMemory (run per build) wires the per-run sink, and
WriteErrorsAndWarnings reads it — both during the run, so log events no longer carry across
invocations. The FT-1 InMemorySink.Clear() in dispose is gone: the sink is discarded with the
context. Second service on the FT-2 rail; same facade-over-Current pattern as FT-4.

Non-breaking (static Logging API unchanged); full build + test suite green.
@ChrisonSimtian ChrisonSimtian added the target/2026 Targets the 2026 calendar-version line (current). See ADR-0004. label Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

target/2026 Targets the 2026 calendar-version line (current). See ADR-0004.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant