Skip to content

Replace initCalled bool with sync.Once to make init-script guarantee explicit #358

Description

@kindermax

Problem Statement

Executor uses a raw initCalled bool field to ensure the global init script runs exactly once across multiple Execute() calls. This guarantee is invisible from the type: callers reading the struct see a boolean with no indication of why it exists or what it protects. The guard is also not safe for concurrent Execute() calls — two goroutines racing on the first call could both see initCalled == false and run init twice. The current calling pattern is single-threaded, so this hasn't caused a bug, but the guarantee is fragile and silent.

Solution

Replace the initCalled bool field with a sync.Once. The once-only init semantics are encoded in the type, documented by the standard library, and concurrency-safe without any additional locking. No observable behaviour changes; the guarantee becomes explicit at the cost of zero complexity.

User Stories

  1. As a contributor, I want to understand the init-script guarantee by reading the Executor struct definition, so that I don't have to trace through the Execute() body to find the if !e.initCalled guard.
  2. As a contributor, I want the init-script guarantee to be concurrency-safe by construction, so that a future change that calls Execute() from multiple goroutines does not silently break the guarantee.
  3. As a contributor, I want to write a test that calls Execute() twice and verifies the init script ran exactly once, so that I can pin the once-only semantics as a regression anchor.
  4. As a contributor, I want the init logic to not require resetting state between test cases, so that I can construct a fresh Executor per test without worrying about leaked boolean state.
  5. As a maintainer, I want the init-script guarantee to be self-documenting, so that a code reviewer can confirm correctness without knowing the convention around initCalled.
  6. As a maintainer, I want Executor to have no exported or unexported boolean fields that silently alter the behaviour of Execute(), so that the executor's state model is simple and auditable.
  7. As a contributor, I want the removal of the boolean guard to be a no-op refactor with a clear mechanical mapping (if !e.initCalled { e.initCalled = true; ... }e.once.Do(...)), so that I can review the change with confidence that no behaviour was altered.

Implementation Decisions

Testing Decisions

A good test constructs an Executor with a non-empty cfg.Init, calls Execute() twice with valid contexts, and asserts that the init script was invoked exactly once. The test should drive this through Executor.Execute() — the highest available seam — rather than inspecting the once field directly.

Once issue #355 lands, a RecordingRunner can count init invocations precisely. Before #355 lands, a lightweight script-counting stub (a closure over a counter) suffices for the same assertion.

Modules to test:

  • Executor.Execute() — verify init runs once on first call and is skipped on subsequent calls.
  • Executor.Execute() — verify that an Executor with an empty cfg.Init never invokes the init path (the sync.Once consumes silently or the guard inside Do skips it).

Prior art: internal/executor/dependency_error_test.go — package-internal, standard testing package, no third-party assertion library. New tests follow the same style.

Out of Scope

Further Notes

This was surfaced as Candidate 4 ("Make the init-script guarantee explicit") during an architecture review of internal/executor/ on 2026-06-13. It was included as a companion change in issue #355 but is extracted here as a standalone issue so it can be reviewed and merged independently — it is the smallest possible self-contained improvement in the executor package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions