Skip to content

Skip an optional dependency missing from the configuration (0.3.1) - #2

Merged
RegiByte merged 1 commit into
mainfrom
fix/missing-optional-dependency
Oct 24, 2026
Merged

RegiByte merged 1 commit into
mainfrom
fix/missing-optional-dependency

Conversation

@RegiByte

Copy link
Copy Markdown
Owner

Problem

topologicalSort threw Resource "X" depends on "Y" which doesn't exist in the system config for every declared dependency, optional ones included. An optional dependency exists so a resource can run without it, yet a config that leaves it out could not be started, halted, or reconciled to. In practice: resource x:github declares optional: ['x:reviews']; when reviews are turned off the next config has no x:reviews, and reconcileSystem(prev, system, next) threw instead of restarting x:github without reviews.

Once that is fixed, the opposite direction exposes a second bug. Reconciling to a config that adds an optional dependency restarted its unchanged dependents without halting their previous instances, because the halt set only covered changed and removed resources. Before, this could not happen, since a previous config with a missing dependency could never start.

Fix

  • dependencyEdges(deps, resources) in src/dependencies.ts returns the dependencies that are edges in a system: every required one, plus each optional one the system has.
  • topologicalSort, buildTopology (and through it formatTopology, toMermaid, toDot, toJSON) and reconcile's withDependents all walk dependencyEdges. A missing optional dependency adds no edge. A missing required one still reaches the existing throw.
  • startInOrder is unchanged: it already resolves an absent id to undefined, which is how a failed optional dependency is handled.
  • reconcileSystem now halts every restarted resource that was in the previous config, in addition to the changed and removed resources and their dependents.
  • README (Graceful Degradation) and CHANGELOG are updated, and the version is bumped to 0.3.1.

Tests

Before: 86 passing. After: 93 passing (7 new). Six of the new specs fail against the old source. The seventh, a missing required dependency beside a missing optional one at the startSystem level, passes on both versions; it locks the existing throw in place.

  • topology: the sort orders around a missing optional dependency; a missing required one still throws; no edge, dependent or depth is recorded for the missing one, and the Mermaid and DOT output omit it.
  • core: a resource whose optional dependency is absent starts, receives { counter, absent: undefined }, and halts cleanly. A missing required dependency next to a missing optional one still throws.
  • reconcile: removing an optional dependency halts the dependent, then halts the dependency, then restarts the dependent without it, with no errors. Adding the dependency halts the dependent, starts the dependency, then restarts the dependent on it.

npm test, npm run type:check and npm run build all pass.

topologicalSort threw "depends on X which doesn't exist" for every declared
dependency, optional ones included, so startSystem, haltSystem and
reconcileSystem could not run a config that leaves an optional dependency
out, nor reconcile to one. dependencyEdges (dependencies.ts) names the
dependencies that are edges in a system: every required one, and the optional
ones it has. The sort, the topology and reconcile's dependents walk it; a
missing required dependency still throws, and the dependent receives
undefined for the missing optional one, as for a failed one.

Reconciling to a config that adds an optional dependency restarts its
unchanged dependents, which the halt set did not include: the halt set is now
also every restarted resource that was in the previous config.

0.3.1.
@RegiByte
RegiByte merged commit f3a232a into main Oct 24, 2026
1 check passed
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.

1 participant