Skip to content

fix: ArtifactGraph on stdlib TSort; declare every runtime dependency lib/ requires - #74

Open
minerva-sky wants to merge 1 commit into
codenamev:mainfrom
minerva-sky:fix/artifact-graph-tsort
Open

minerva-sky wants to merge 1 commit into
codenamev:mainfrom
minerva-sky:fix/artifact-graph-tsort

Conversation

@minerva-sky

Copy link
Copy Markdown
Collaborator

Closes #63.

Install the gem the way a consumer does (a Gemfile with only gem "agentic") and Agentic::ArtifactGraph raises LoadError: cannot load such file -- rgl/adjacency, Agentic::HumanIntervention::AuthenticationSystem raises LoadError: cannot load such file -- base64 on Ruby >= 3.4. rgl lived in the Gemfile's dev group, base64 rode in on vcr's dependency, and require "concurrent" only worked because dry-core happens to depend on it. Workspace#initialize builds an ArtifactGraph, so the CLI and artifact-generator paths were broken for anyone outside the dev bundle. All three came in with #7; the published 0.2.0 predates them, so no released version is affected yet.

What changes

  • ArtifactGraph keeps a Hash of artifact name => referenced names and lets stdlib TSort sort it. One Tarjan pass (TSort.strongly_connected_components) serves both topological_sort and detect_cycles; the functional form keeps TSort's methods off the class's public surface. Public methods, arities, return shapes, CircularDependencyError, and the dependencies-before-dependents guarantee are unchanged. Declaring rgl instead would have added four runtime gems (rgl, pairing_heap, rexml, stream) for eight graph methods; the CONSTITUTION's "auditable in an afternoon" core says no.
  • detect_cycles now reports the artifacts that actually form each cycle, which is what its docstring always promised, instead of every vertex in the graph. A self-reference is handled explicitly (TSort alone treats a self-loop as a non-cycle). dependencies_of returns references in the order the artifact declared them rather than vertex-insertion order; nothing documented or in lib/ depends on either order.
  • The dead require "base64" is gone; the file never calls Base64.
  • The gemspec declares concurrent-ruby ~> 1.0 (the same bound dry-* already asks for, so no consumer resolution changes) and tsort (a bundled gem from Ruby 4.1, same treatment as logger and cgi). rgl leaves the Gemfile; the lock drops rgl, pairing_heap and stream.
  • spec/lib_requires_spec.rb walks every require under lib/ and fails on anything that is neither Ruby's own library nor a declared runtime dependency. Default gems Ruby has promoted (or announced it will promote) to bundled gems count as needing a declaration, via Gem::BUNDLED_GEMS::SINCE plus a literal copy of 4.0's table for interpreters without it. Run against main it reports exactly rgl ×3, base64 and concurrent, and nothing else. No network, ~50 ms.

Tests: graph + workspace + guard + pack specs 84/84; full suite 1393 examples on Ruby 4.0.2 with only the known event_dispatcher_integration_spec.rb:291 wall-clock flake that #36 de-flakes. A 20,000-deep reference chain sorts in 42 ms (TSort uses an explicit stack).

Follow-ups, not in this PR: #64 refreshes the dev lock (its base64 hunk is identical to this one, so either merge order is clean); authentication_system.rb also requires openssl and json without using them (same orphan pattern, one seat noticed it, trivial to drop with #58's deprecation).

Intended labels: loop:drain, loop:deps.

Panel

  • DHH: cleared (3/3). A library deleted for a Hash plus stdlib; the functional TSort.* form chosen over include TSort so nothing new goes public. Nit taken: none needed.
  • Obie Fernandez: cleared (3/3). Probed ghost references, re-adds, self-loops, nil/String references, concurrent add_node: every rough edge is pre-existing and byte-equivalent to rgl's behaviour, and the LLM entry path already normalises references with Array(). One seat predicted SystemStackError on a ~2000-deep chain; two seats and a direct 20k-node run refute it.
  • Vladimir Dementyev: cleared (3/3). Guard spec replayed on Ruby 3.2.4 (no BUNDLED_GEMS::SINCE there) gives the same verdict on all 35 requires; deterministic, no network. Nits taken: the literal fallback list now carries 4.0's full table (so CI's verdict matches local), and the require list is deduped before find_by_path. dependents_of is a full scan now where rgl was O(out-degree); only caller is a single pass-through in Workspace.
  • The gem consumer: cleared (3/3). ArtifactGraph never shipped (0.2.0 is the last release), nothing serialises the graph, saved agents and plans untouched. Nit taken: concurrent-ruby loosened from ~> 1.3 to ~> 1.0 so no downstream lock pinned at 1.2.x hits a resolver conflict.

🤖 Generated with Claude Code

…lib/ requires

`require "agentic"` could not load ArtifactGraph or AuthenticationSystem
outside the dev bundle: artifact_graph.rb required rgl (Gemfile-only) and
authentication_system.rb required base64 (a bundled gem since Ruby 3.4,
and never used), while event_pipeline.rb required concurrent only through
dry-core's transitive dependency.

- ArtifactGraph keeps a Hash of artifact name => referenced names and lets
  TSort sort it; one Tarjan pass serves topological_sort and detect_cycles.
  detect_cycles now reports the artifacts that actually form each cycle,
  and a self-reference counts as one (TSort alone ignores self-loops).
- The dead `require "base64"` is gone.
- The gemspec declares concurrent-ruby (~> 1.3) and tsort (a bundled gem
  from Ruby 4.1); rgl leaves the Gemfile.
- spec/lib_requires_spec.rb walks every require under lib/ and fails on
  anything that is neither Ruby's own library nor a declared runtime
  dependency, treating promoted-to-bundled gems as needing a declaration.
  Against main it reports exactly rgl, base64 and concurrent.

Closes codenamev#63.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@minerva-sky minerva-sky added loop:drain Implemented from the improvements ledger by the drain loop loop:deps Dependency / language upgrade loop labels Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

loop:deps Dependency / language upgrade loop loop:drain Implemented from the improvements ledger by the drain loop

Projects

None yet

1 participant