Skip to content

fix(compat): hide phantom override commands from --help (tool-existence guard)#515

Open
PeterGuy326 wants to merge 2 commits into
mainfrom
fix/phantom-command-guard
Open

fix(compat): hide phantom override commands from --help (tool-existence guard)#515
PeterGuy326 wants to merge 2 commits into
mainfrom
fix/phantom-command-guard

Conversation

@PeterGuy326

Copy link
Copy Markdown
Collaborator

Problem

Override leaves whose backing MCP tool isn't actually deployed still render in dws <svc> --help, then fail at invocation with tool not found. Measured against the live open-edition tools/list (via cache refresh): 43 phantom commands across 391 overrides. Worst offender is attendance — 38 declared overrides, only 4 deployed tools (group_or_rules / record / shift / summary); the other 34 (class/group/vacation/overtime/schedule/approve/check/report/...) are entire undeployed feature areas.

The binary faithfully renders the metadata — there was no existence guard: the override loop builds a cobra command unconditionally and only consults tool details to enrich descriptions/flags.

Fix — runtime guard (this PR)

BuildDynamicCommands now takes an existingTools oracle (CLI slug → live tool-name set, from the tools/ cache). A leaf whose tool is absent from its resolved server's set is marked Hidden (still invocable — just off the help surface); groups left with no visible leaf are collapsed.

Safety rails — the naive "skip if tool not in detail set" version would blank the entire command tree on a cold cache (the detail/ partition is empty even after a refresh; supplement/overlay servers have no detail at all):

  • acts only when a server's tool set is KNOWN and non-empty; absent/empty = unknown = keep. Cold cache / overlay / plugin paths pass nil → no-op.
  • the oracle reads the tools/ partition (populated by cache refresh), not detail/.
  • serverOverride leaves resolve against the target server's set (contact→hrmregister, doc→doc-comment stay visible).
  • pipeline leaves are never hidden.

Publish-time companion

scripts/dev/check-phantom-overrides.py diffs envelope toolOverride keys against the live tool sets and fails on un-acknowledged phantoms (hidden:true overrides count as acknowledged). This is the discovery-side (Plan A) gate; the matching envelope change (37 phantom overrides → hidden:true, 1 helper-shadowed override removed) ships separately via the discovery-release/Portal flow, not git.

Verification

  • attendance --help: 17 → 4 real commands; warm tools cache.
  • Empty tools cache: all 17 stay visible (guard inert — cold cache never blanks the tree).
  • 22 products and healthy services (calendar, report, …) unaffected.
  • phantom_guard_test.go: hide / cold-cache-keep / empty-or-absent-keep / serverOverride-routing / pipeline-skip. Full internal/compat + internal/app suites green.

Scope note

Independent of #514 (report --contents-file). No overlap in touched code regions; report has zero phantom drift.

…ce guard)

Override leaves whose backing MCP tool is not actually deployed render in
`dws <svc> --help` but fail at invocation ("tool not found"). On the live
open-edition deployment this is 43 commands (attendance alone declares 38
overrides for 4 deployed tools).

BuildDynamicCommands now takes an existingTools oracle (CLI slug -> live tool
name set, from the tools/ cache populated by `cache refresh`). A leaf whose
tool is absent from its resolved server's set is marked Hidden (still
invocable, just off the help surface); groups left with no visible leaf are
collapsed too.

Safety rails (the source-blind version of this fix would blank the whole tree
on a cold cache):
- acts only when a server's tool set is KNOWN and non-empty; absent/empty =
  unknown = keep. Cold cache / overlay / plugin paths pass nil and are a no-op.
- serverOverride leaves resolve against the TARGET server's set
  (contact->hrmregister, doc->doc-comment stay visible).
- pipeline leaves are never hidden (no single backing tool).

The oracle reads the tools/ partition, not detail/ — detail/ is frequently
empty even after a refresh and is unusable as an existence signal.

scripts/dev/check-phantom-overrides.py: discovery-side (publish-time) companion
that diffs envelope toolOverride keys against the live tool sets and fails on
un-acknowledged phantoms (hidden:true overrides are treated as acknowledged).

Verified e2e: attendance --help drops from 17 to its 4 real commands; an empty
tools cache leaves all 17 visible (guard inert); 22 products and healthy
services unaffected. New tests in phantom_guard_test.go cover hide / cold-cache
keep / empty-or-absent keep / serverOverride routing / pipeline skip.
Hiding phantom leaves (via the runtime guard or envelope hidden:true) can leave
a group container with no visible children — it would still render in --help as
an empty heading (e.g. attendance `vacation`/`overtime`). Collapse those.

- hideEmptyGroups now also hides a childless GROUP container. Group containers
  and leaves both set RunE (the group's just prints help), so cobra Runnable()
  can't tell them apart; NewGroupCommand now stamps a dws.kind=group annotation
  (cmdutil.MarkGroup/IsGroup) that can.
- the collapse runs unconditionally (not only when the guard is active) because
  envelope hidden:true empties groups independent of the tools cache. It is
  safe: it only hides a group all of whose children are already hidden, never a
  leaf, and never a product root (so helper/overlay merges that add leaves later
  can't be pre-empted).

Verified e2e both ways with attendance: guard-only (warm cache, clean envelope)
and envelope-only (hidden:true, cold cache) each collapse it to its 4 real
commands; empty cache still shows all. New TestPhantomGuard_EmptyGroupCollapses.
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