Skip to content

Fix #57: read package settings through a typed AetherConfig service - #104

Open
corgab wants to merge 3 commits into
mainfrom
fix/57-aether-config
Open

Fix #57: read package settings through a typed AetherConfig service#104
corgab wants to merge 3 commits into
mainfrom
fix/57-aether-config

Conversation

@corgab

@corgab corgab commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Summary

SubmitQuantumCircuit and PollQuantumTask called the global config('aether.*') helper nine times between them, repeating the same default-resolution expressions, and the literal 'local' default for aether.default lived in four files. This PR centralises the package-level settings in one typed service.

Plan

  1. Add Config\AetherConfig, a thin typed reader over the config repository: every top-level aether.* default becomes a constant, every reader returns the documented type, and reads hit the repository on each call so config()->set() in a test (or a runtime change) is still honoured.
  2. Bind it as a container singleton in the service provider.
  3. Inject it into both jobs' handle() alongside QuantumManager, as the issue proposes. Job constructors and tries() get no method injection from Laravel, so those two spots resolve it from the container; the docblocks say why.
  4. Route every other top-level reader through it as well: QuantumManager (default driver, bridge wiring, per-driver arrays), the php artisan about section, the install command's Python path, and the local simulator's task TTL.

Changes

  • src/Config/AetherConfig.php (new): defaultDriver(), pythonPath(), processTimeout(), queue(), pollInterval(), maxPollAttempts(), persistTasks(), localTaskTtl(), driver(name); constants DEFAULT_DRIVER, DEFAULT_PYTHON_PATH, DEFAULT_PROCESS_TIMEOUT, DEFAULT_POLL_INTERVAL, DEFAULT_MAX_POLL_ATTEMPTS, DEFAULT_LOCAL_TASK_TTL. String settings come back trimmed; blank or non-numeric integers fall back to the default; persistTasks() understands the env spellings ("true", "1", ...) and treats anything unrecognised as disabled.
  • src/AetherServiceProvider.php: singleton binding; About section reads through the service.
  • src/Jobs/SubmitQuantumCircuit.php, src/Jobs/PollQuantumTask.php: handle() takes AetherConfig and uses it for every setting, the attempt budget included; persistSubmission() / persist() receive it explicitly; no config() call left.
  • src/QuantumManager.php: getDefaultDriver(), createLocalDriver(), createAwsDriver(), createBridge() read through a private settings() accessor. It takes the container binding when present and otherwise builds the reader over the repository Manager already holds, so a bare container with only config bound still resolves drivers. The bridge() docblock example reads the driver array through the service.
  • src/Drivers/LocalSimulatorDriver.php: taskTtl() reads localTaskTtl().
  • src/Commands/AetherInstallCommand.php: handle() injects AetherConfig for the Python path.
  • README.md: the Quantum::extend() example reads the driver array through the service. CLAUDE.md: convention documented.
  • Tests: tests/Unit/Config/AetherConfigTest.php (new, dataset-driven: defaults, typed reads, env string casts, trimming, blank handling, live reads); provider tests for the singleton, the default driver resolving through the service, and driver resolution on a bare container; a poll-job test proving handle() budgets attempts from the injected instance; the job tests' handle() calls pass the service.

Overlap

defaultDriver() carries the same blank-string guard that #99 (#50) adds to QuantumManager::getDefaultDriver(); whichever merges second resolves that method in favour of the one-line delegation here.

Tests

  • vendor/bin/pint --test passes.
  • vendor/bin/pest --compact: 819 tests, 1 skipped (791 before: +28).

Closes #57

…service

SubmitQuantumCircuit and PollQuantumTask called the global config() helper
nine times between them, repeating the same default-resolution expressions,
and the literal 'local' default for aether.default lived in four files.

Config\AetherConfig now owns every aether.* default as a constant and
exposes typed readers (defaultDriver(), queue(), pollInterval(),
maxPollAttempts(), persistTasks(), pythonPath(), processTimeout(),
driver()). It is a container singleton, injected into the jobs' handle()
alongside QuantumManager; job constructors and tries(), which Laravel
calls without injection, resolve it from the container. QuantumManager,
the About section and the install command read through it too.

Reads go to the repository on each call, so config()->set() in a test or a
runtime change is still honoured.

Closes #57
…e(), and keep the manager container-independent

- defaultDriver(), pythonPath() and queue() return the trimmed value, so a
  padded env value no longer leaks whitespace into driver names or the
  Process command.
- PollQuantumTask::handle() budgets attempts from the injected AetherConfig
  instead of re-resolving it through tries().
- QuantumManager::settings() falls back to building the reader over its own
  config repository when the container has no binding, so a bare container
  with only 'config' bound still resolves drivers.
- local_task_ttl joins AetherConfig (localTaskTtl()); the extend() examples
  in the bridge() docblock and README read the driver array through it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[maintainability] Job classes read global config() directly in 9 places, duplicating the same default-resolution logic

1 participant