Uh oh!
There was an error while loading. Please reload this page.
PR A: Close the sealed Command hierarchy honestly — sealed module Command, abstract BuiltinCommand open edge, delete the sorbet-runtime ivar pokes, un-private CommandRepository - #121
Conversation
…uiltinBody interface, final BuiltinCommand holding a body, delete the sorbet-runtime ivar pokes, un-private CommandRepository Co-authored-by: JPDuchesne <2636122+JPDuchesne@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…abstract open-edge class A sealed module's included hook fires only for direct includers (include never transfers singleton methods), so the hierarchy closes without the BuiltinBody indirection or the final wrapper leaf: builtins subclass BuiltinCommand directly, the data leaves are final!, and the composition root wires builtins straight in. BuiltinCommand stays a class because Sorbet flattens module mixins — a module open edge would re-include the sealed Command in every builtin and fail the same-file check statically. Co-authored-by: Cursor <cursoragent@cursor.com>
JPDuchesne
commented
Aug 18, 2026
Reworked per review discussion: the BuiltinBody interface and the final BuiltinCommand wrapper are gone.
|
| @stamps = stamps | ||
| @calls = [] | ||
| @body = body | ||
| super() |
There was a problem hiding this comment.
/ask what's with this line change?
There was a problem hiding this comment.
✅ /ask
The removed super() was a no-op, so the diff is dead-code cleanup in the test fake. ExecutorFakeBuiltin subclasses Dev::BuiltinCommand, and that base class (see src/dev/command.rb) defines no initialize of its own — it's an abstract class that only declares the abstract call method, and Command is a plain module with no constructor either. So super() fell through to Object#initialize, which takes no arguments and does nothing; the line initialized nothing and its presence wrongly implied the base class had constructor behavior to run. Removing it also matches how the real builtins under src/dev/builtins/ are written — e.g. CdCommand and CredCommand define initialize without calling super. Behavior of the tests in test/dev/command_executor_test.rb is unchanged. (One caveat: git history access is restricted in this checkout, so I can't confirm whether the line was a leftover from an earlier BuiltinCommand shape — the file's comments mention it previously carried sorbet-runtime workarounds — but as the code stands today the call is provably inert.)
⚙️ workflow run · model: claude-fable-5-high
There was a problem hiding this comment.
Resolved in b8ed631: the super() calls are restored — and the convention is now applied uniformly. The bot's replies were right that the calls are inert today, but that misses the point of the rule: an explicit super() means future base-class constructor state either runs automatically (defaulted params) or fails loudly at first instantiation (required params), instead of silently constructing half-initialized objects. The fakes had actually been the only callers — the 11 real builtins never called super() — so the commit adds it everywhere an initializer derives from the hierarchy: both data leaves, all builtins, and the fakes.
| def initialize(desc: "a builtin", hidden: false) | ||
| @desc = desc | ||
| @hidden = hidden | ||
| super() |
There was a problem hiding this comment.
/ask Same here, why not keep super?
There was a problem hiding this comment.
Restored in b8ed631, along with every other initializer in the hierarchy (the real builtins had never called super() — the fakes were the outliers in the other direction). See the thread on command_executor_test.rb for the full rationale.
| @staleness_exempt = staleness_exempt | ||
| @stamps = stamps | ||
| @calls = [] | ||
| super() |
There was a problem hiding this comment.
/ask Same here, why not keep super?
There was a problem hiding this comment.
✅ /ask
Because there is nothing for super() to reach: it's a pure no-op here. ServiceFakeBuiltin subclasses Dev::BuiltinCommand (src/dev/command.rb), and neither BuiltinCommand nor the Command module it includes defines an initialize — there is no parent state to set up, so super() falls through to Object#initialize, which does nothing. It isn't needed for the abstract guard either: sorbet-runtime's abstract! enforces non-instantiability by wrapping self.new on the abstract class, not initialize, so the guard runs regardless of whether the subclass calls super. Lint doesn't ask for it either — Lint/MissingSuper is disabled in the inherited rubocop-shopify config. Keeping it would be harmless but would imply the base class has initialization the fake must run, which it doesn't; dropping the dead call is the same reasoning as in the other thread. (If BuiltinCommand ever grows a real initializer, its subclasses would need explicit constructor wiring anyway, per the repo's constructor-injection convention.)
⚙️ workflow run · model: claude-fable-5-high
There was a problem hiding this comment.
Restored in b8ed631, along with every other initializer in the hierarchy. The bot is right that abstract! guards new rather than initialize and that Lint/MissingSuper is disabled in rubocop-shopify — but "provably inert today" is exactly the case the always-super convention exists for. See the thread on command_executor_test.rb for the full rationale.
…rchy Ruby silently skips base initialization when a subclass overrides initialize without super. The calls are inert today (no ancestor defines initialize), but they make future base-class constructor state either run automatically (defaulted params) or fail loudly at first instantiation (required params) instead of constructing half-initialized objects. Applied uniformly: the two data leaves, all builtins, and the test fakes. Co-authored-by: Cursor <cursoragent@cursor.com>
Uh oh!
There was an error while loading. Please reload this page.
916fe89 Merge pull request #128 from d3mlabs/jpd/skill-installer-ephemeral-guard b9ea143 Move the ephemeral-source guard to SkillInstaller, the seam all links share 36803f7 Merge pull request #127 from d3mlabs/jpd/capture-learning-root-cause-gate d6e081e Name the wide-angle goal, not one command: an exact git-log depth invites checkbox compliance f876dbe capture-learning: gate workaround learnings on root cause, add wide angle 7249198 Merge pull request #123 from d3mlabs/ai/119-pr-b-typed-child-process-failure-taxonom e71063a Merge pull request #126 from d3mlabs/jpd/hermetic-scrub-guard f911e38 Make the scrub-list guard hermetic: construct the bundler launch it measures 6c398b8 ai-flow /build: let's resolve conflicts cb68d60 Merge pull request #122 from d3mlabs/ai/118-pr-d-split-commandexecutor-into-a-dispat 4477c6b Update the manifest-loader contract note for the eager toolchain pass 3ad03c3 Constructor-inject CommandRunner; two messages replace the wait flag 2ac941a Route help through the command path; group and eager-load usage a78ba14 Add the help builtin c7ae57a Add Category trait to the Command hierarchy 2e05625 ai-flow /build: let's fix the fake classes, put them within the test class a29b5e6 Merge main: sealed-module Command hierarchy, super() convention, and bin/test.rb runner 5d9c57b Merge pull request #121 from d3mlabs/ai/117-pr-a-close-the-sealed-command-hierarchy b8ed631 Call super() in every initializer that derives from the Command hierarchy 5bcc76f Rework the seal: Command becomes a sealed module, BuiltinCommand the abstract open-edge class 5c68c85 Merge pull request #120 from d3mlabs/ai/116-pr-c-bin-test-rb-tee-suite-output-to-a-s c62efc8 ai-flow /build: PR B: Typed child-process failure taxonomy in CommandRunner (CommandFailedError / CommandKilledError / CommandSpawnError) mapped to exit codes in Runner#exit_for f09f845 ai-flow /build: PR D: Split CommandExecutor into a dispatching composite with injectable BuiltinExecutor / ProjectExecutor / OverriddenExecutor strategies (exec_into vs run_waiting) 0775515 ai-flow /build: PR A: Close the sealed Command hierarchy honestly — BuiltinBody interface, final BuiltinCommand holding a body, delete the sorbet-runtime ivar pokes, un-private CommandRepository 5a5fb41 ai-flow /build: PR C: bin/test.rb — tee suite output to a stable log artifact and pass file args through to rake TEST 4c89408 Merge pull request #115 from d3mlabs/ai/37-layer-the-dev-runner-application-service 04d461c Add the simplecov-cobertura gem RBI 81677f6 Upload cobertura to codecov instead of SimpleCov JSON 0a741f0 Cover the default factories, image credential providers, and nocov the sealed absurd arm fe7c94e ai-flow /build: Layer the dev Runner (application service + boundary coercion) d782b1a Merge pull request #107 from d3mlabs/ai/101-dev-clone-host-global-builtin-cloning-vi f305ac9 ai-flow /build: codecov coverage missing fac96ee ai-flow /build: dev clone: host-global builtin cloning via gh auth to the canonical $DEV_CD_ROOT path d16b757 Merge pull request #100 from d3mlabs/jpd/99-pin-homebrew-installer 2ad614e Pin the Homebrew installer to a commit SHA (dev#99) 53e3616 Merge pull request #90 from d3mlabs/ai/89-gemskilllinker-links-minted-under-a-sand 95ee372 Merge pull request #97 from d3mlabs/ai/learn-promote-rbenv-libruby-rpath-hijack f7edc33 chore: nudge origin-firing after ai-flow#57 (removal diffs skip green) 646f189 Merge pull request #98 from d3mlabs/jpd/proposal-checks-edited 50e913a proposal-checks: re-verify on PR body edits (ai-flow#54) 59a3146 ai-flow /learn: drop rbenv-libruby-rpath-hijack (promoted to the org tier) f119987 Merge pull request #96 from d3mlabs/jpd/ai-flow-knowledge-repo b0e7131 ai-flow config: opt dev into org-tier learning promotion d17b2ff Merge pull request #95 from d3mlabs/jpd/94-self-defending-entrypoint 29b2e16 Test readability: one aliased scrub list, one property per test f7197ac Drift guard: the unset list must cover what the running bundler exports 049bbc8 Probe the shim scrub with a stub ruby instead of a full dev command run 88fa953 bin/dev: scrub foreign bundler activation before Ruby boots 9cf868a Merge pull request #92 from d3mlabs/ai/60-plan-pull-mangles-files-with-an-empty-fr 6783469 Merge pull request #93 from d3mlabs/ai/learn-issue-60 991d46d ai-flow /build: capture learnings from the build pass fe64507 ai-flow /build: Plan pull mangles files with an empty frontmatter block above the real one (double frontmatter) d8db57d ai-flow /build: GemSkillLinker: links minted under a sandboxed session point into ephemeral sandbox cache paths b4526ea Merge pull request #88 from d3mlabs/jpd/ast-transform-3.1.1 de9beaf Bump ast_transform to 3.1.1 and drop the heredoc-emission workaround
Implements #117.
Requested by @JPDuchesne.
Closes#117