Skip to content

fix(obs): remove shadowing dispatcher + binary-precedence guard (Phases 1+2)#460

Merged
Data-Wise merged 8 commits into
devfrom
feature/obs-dispatcher-shadowing
Jun 5, 2026
Merged

fix(obs): remove shadowing dispatcher + binary-precedence guard (Phases 1+2)#460
Data-Wise merged 8 commits into
devfrom
feature/obs-dispatcher-shadowing

Conversation

@Data-Wise

Copy link
Copy Markdown
Owner

flow-cli's obs dispatcher shadowed the real Homebrew obs (obsidian-cli-ops) but required a python/obs_cli.py flow-cli never ships, so obs always errored. A shell function beats a PATH binary, so the broken dispatcher won in every interactive shell.

Phase 1 — remove the dead dispatcher (222c43b2)

Delete lib/dispatchers/obs.zsh, zsh/functions/obs.zsh, man/man1/obs.1, tests/test-obs-dispatcher.zsh; scrub obs from inventories (flow.plugin.zsh, help-compliance/browser, commands/{flow,alias,doctor,tutorial}.zsh) and dangling SEE ALSO obs(1) refs. 15 → 14 dispatchers. After: type obs/opt/homebrew/bin/obs.

Phase 2 — general binary-precedence guard (f28b7007)

_flow_load_dispatcher wraps each lib/dispatchers/*.zsh source, unfunctions any new command that resolves to a PATH binary unless in FLOW_INTENTIONAL_SHADOWS=(r mcp cc) or FLOW_FORCE_DISPATCHER_<NAME>=1. Keys on real defined names (skips _* helpers; no filename convention). New guard test tests/test-dispatcher-binary-precedence.zsh (16/16).

Docs (67b0071d)

15 → 14 dispatcher sweep across ~30 active files + mkdocs nav repair (mkdocs --strict clean). Historical CHANGELOG/RELEASES/specs/.archive left frozen.

Companion work

The obs.1 man page is re-homed in Data-Wise/obsidian-cli-ops#25; Homebrew install in Data-Wise/homebrew-tap#110.

Verification

Full suite: 58 passed, 1 pre-existing unrelated failure (dogfood-scholar-config-sync, fixed separately in #459), 1 expected timeout (e2e-em-dispatcher).

Note: branch is a couple of .STATUS status-log commits behind dev; expect a trivial .STATUS conflict to resolve at merge.

🤖 Generated with Claude Code

Test User and others added 6 commits June 5, 2026 11:00
Phase 1 (Option A): delete dead obs dispatcher + symlink + obs.1 + obs
test + inventory refs (fully fixes the live shadowing bug).
Phase 2: general binary-precedence guard, elevated to a decision -
audit found B1 suffix-strip invariant already false (em<->email-dispatcher,
4 helper files), so B3 post-source self-check is recommended.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flow-cli obs() dispatcher shadowed /opt/homebrew/bin/obs
(obsidian-cli-ops) but required a python/obs_cli.py flow-cli never
ships, so typing `obs` always errored "Python CLI not found". A shell
function beats a PATH binary in lookup, so the broken dispatcher won in
every interactive shell. The zsh/functions/obs.zsh symlink (sourced
after the dispatcher loop) compounded this when obsidian-cli-ops was
present.

Phase 1 (Option A): delete the dispatcher and scrub every obs
reference, dropping flow-cli from 15 to 14 dispatchers.

- Delete lib/dispatchers/obs.zsh, zsh/functions/obs.zsh, man/man1/obs.1,
  and tests/test-obs-dispatcher.zsh.
- Scrub obs from inventories: flow.plugin.zsh, lib/help-compliance.zsh
  (15->14 + map), lib/help-browser.zsh, and
  commands/{flow,alias,tutorial,doctor}.zsh.
- Remove dangling SEE ALSO obs(1) refs and the flow.1 obs subcommand
  block (flow.1, g.1, mcp.1, qu.1, r.1); vendored scribe.1 left as-is.
- Update test suites for 14 dispatchers (help-compliance, dogfood,
  atlas-bridge, browser-preview, cli) and remove obs-specific blocks
  (legacy obs_help alias, _obs_help direct test, obs src-file case).

After: `type obs` -> /opt/homebrew/bin/obs (no obs() function). Affected
guards green; the man-page coverage guard now derives 14 dispatchers and
requires obs.1 gone.

Deferred: headline "15 dispatchers" docs and Phase 2 (general
binary-precedence guard, form TBD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 (Option B3) of the obs shadowing fix: a general binary-precedence
guard in the dispatcher loader so no future dispatcher can silently mask
an installed binary the way the broken obs() did.

After sourcing each lib/dispatchers/*.zsh, the loader diffs the function
table and unfunctions any newly-defined, non-`_` command whose name
resolves to an external PATH binary (whence -p) — UNLESS:
  - it is an intentional shadow: FLOW_INTENTIONAL_SHADOWS=(r mcp cc),
    overridable by pre-setting the array (cc launches Claude Code, not
    the C compiler; r/mcp are flow's dispatchers), or
  - it is forced: FLOW_FORCE_DISPATCHER_<NAME>=1.
The skip is logged under FLOW_DEBUG. Keying on the functions a file
actually defines means no filename convention is needed and `_`-helpers
are ignored automatically — unlike the spec's B1, whose filename->command
guess was already false (email-dispatcher.zsh defines `em`; several
helper files define no command at all).

Adds tests/test-dispatcher-binary-precedence.zsh (16 cases:
drop-on-collision, allowlist/force keep, helper preserved, real r/mcp/cc
survive a live load, obs is not a function) and registers it in
run-all.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the obs dispatcher removal (fix(obs)) and the binary-
precedence guard (feat(dispatchers)). Scrubs the now-removed obs
dispatcher from active documentation and repairs the mkdocs nav left
dangling after tutorials/41-obs-dispatcher.md was deleted.

- Dispatcher count "15" -> "14" across active docs: CLAUDE.md, README,
  MASTER-DISPATCHER-GUIDE/API-REFERENCE/ARCHITECTURE, reference/index,
  DOC-DASHBOARD, commands/at, REFCARD-TEACH/EMAIL, EMAIL-DISPATCHER-GUIDE,
  and 8 tutorial footers (35,37,38,39,40,42,43,44).
- Remove obs from inline dispatcher lists/tables/grammar examples in
  CLAUDE.md, MASTER-* guides, PHILOSOPHY, commands/{flow,alias,config},
  CONTRIBUTING, getting-started/{quick-start,00-welcome}, index,
  QUICK-REFERENCE, ENHANCED-HELP-QUICK-START.
- Remove the deleted obs tutorial's nav entry (mkdocs.yml) and its
  mermaid node + table row (docs/tutorials/index.md).

Historical records left frozen per convention: CHANGELOG(.md),
docs/CHANGELOG.md, RELEASES.md, docs/specs/*, and .archive/*.

Verified: mkdocs build --strict passes (exit 0); no dangling links to
41-obs-dispatcher; "obs" as a naming-convention example (CONVENTIONS,
PHILOSOPHY) and the separate zsh/ obs() smart-function are left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the obs removal, the B3 binary-precedence guard, and the 15->14
docs sweep in the session log + Active Worktrees table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ORCHESTRATE-*.md is a feature-branch working artifact; it should not land
on dev (per CLAUDE.md merge-cleanup rule).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Data-Wise Data-Wise force-pushed the feature/obs-dispatcher-shadowing branch from ab5cd4f to 1e0ec56 Compare June 5, 2026 17:02
Test User and others added 2 commits June 5, 2026 11:24
- Guard: snapshot the function table once around the whole batch instead of
  per-file, and use the fork-free ${commands} hash instead of $(whence -p).
  Eliminates the ~10ms startup overhead (59ms -> ~46ms); test still 16/16.
- Docs: scrub stray 'obs' dispatcher refs missed by the sweep
  (getting-started/00-welcome 8->14 list, CONVENTIONS, PHILOSOPHY).
- CLAUDE.md: document FLOW_INTENTIONAL_SHADOWS + FLOW_FORCE_DISPATCHER_<NAME>
  with the empty-array override caveat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enforces the documented footgun: an explicitly-emptied allowlist (set, not
unset — zsh ${+arr} is 1 for empty arrays) skips the (r mcp cc) default, so a
default-protected shadow like cc is dropped; unset keeps it. Both run in a
fresh `zsh -f` because the guard only acts on functions new to the source
pass — re-sourcing an already-loaded shell would be a no-op. 16 -> 18 cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Data-Wise Data-Wise merged commit fc00837 into dev Jun 5, 2026
1 check passed
@Data-Wise Data-Wise deleted the feature/obs-dispatcher-shadowing branch June 5, 2026 18:17
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