Skip to content

fix(history): resolve bounded default context for argument-free dispatch - #16

Merged
Xuepoo merged 1 commit into
mainfrom
ctx-0005/fix-default-scope
Oct 19, 2026
Merged

Xuepoo merged 1 commit into
mainfrom
ctx-0005/fix-default-scope

Conversation

@Xuepoo

@Xuepoo Xuepoo commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Priority: P1 | Area: plugin | Labels: fix,P1,area:plugin | Milestone: v0.1.0 | RFC: RFC-0004 | Task: CTX-0005

Closes #15

Problem

A keybinding invokes bitty-terminal.history:query-transcript (and
query-commands) with an empty args table. The plugin required an explicit
panel/workspace scope and explicit row/byte bounds, so it returned
{ok=false, code=E_HISTORY_SCOPE_MISMATCH} before any host call and no entry
surface appeared. There was no settings fallback.

Issue #12 (compat floor) is closed and unrelated; no newer history issue
covered this defect, so #15 tracks it. (An identical report briefly landed in
bitty-plugins#100 by mistake and is closed as a pointer.)

Resolution rule

  1. Explicit arguments always win unchanged. Any scope.panel /
    scope.workspace present, and any row_count / max_bytes present,
    travel the previous path: the unchanged validate_scope/validate_query
    checks, then the host's grant/scope/bound gate (over-bound still denies,
    never clamps).
  2. Absent scope resolves from the plugin's declared, capability-neutral
    [settings] context: default_panel / default_workspace (empty string
    means unset). Wildcard/blank/control/over-long ids fail closed as
    E_DEF_INVALID. KV never resolves an extent (the caller namespace is the
    scope).
  3. Absent bounds resolve to row_count/max_bytes settings only inside
    the accepted 1..16 / 1..4096 caps; an out-of-window configured value falls
    back to the shipped cap (16 / 4096), so a local default can never become an
    over-bound request.
  4. No valid scope axis keeps the typed E_HISTORY_SCOPE_MISMATCH denial.

Why settings and not a focus getter: the public Lua surface exposes no focus
read, and workspace.list/panel.* would require workspace.read /
panel.focus. The task and this package forbid widening capabilities (and
reading outside the granted history scope), so the sanctioned host context is
the declared settings namespace. The capability set is byte-identical to
main; the host still intersects every query scope with the granted scope.

Change

  • bitty-plugin.toml: [settings] with exactly default_panel,
    default_workspace, row_count (16), max_bytes (4096). No capability
    change.
  • lua/history/init.lua: M.resolve_scope / M.resolve_bounds, wired into
    M.query before validation. validate_scope/validate_query unchanged.
  • tests/history.test.ts: 5 new regressions (3 fail pre-fix):
    • empty-args keybinding dispatch produces entries for the configured panel;
    • explicit scope wins over the default;
    • no bounded context still returns E_HISTORY_SCOPE_MISMATCH;
    • malformed default fails closed as E_DEF_INVALID;
    • defaults stay inside caps while explicit over-bound requests still deny.
  • SDK pin 0a9487f (open PR head) -> 3ea8e58 (merged main; history
    surface from #144 plus declared-settings lint/mock support from #152), so
    the manifest [settings] declarations are enforced by the linter and the
    mock.
  • README/TODO synchronized (including stale compat/pin/verification prose).

Validation

  • just check — exit 0: prettier, markdownlint, metadata, hygiene, portable
    paths, manifest lint (0 errors), luaparse + control, bun 49 pass / 0 fail.
  • bun test tests/history.test.ts -t "bounded default context" — 5 pass;
    against origin/main's init.lua the empty-args, malformed-default, and
    cap regressions fail as expected.
  • bunx --bun luaparse@0.3.1 --file lua/history/init.lua — clean.
  • bun run bitty-plugin-lint bitty-plugin.toml — valid (0 errors, 3 expected
    high-risk consent warnings).
  • gitleaks detect --source . — no leaks.
  • just actionlint — clean.

Risk

Values-plane only: settings supply convenience defaults and grant no
authority; the host intersects every scope with the grant and enforces
capture/rate/bounds. No capability, source, or export change. The SDK pin bump
is required for the declared-settings gate and is a merged main commit.

Requested labels: fix, P1, area:plugin; milestone v0.1.0.

…-free dispatch

A keybinding invokes `query-transcript`/`query-commands` with an empty args
table, so the plugin returned E_HISTORY_SCOPE_MISMATCH and no entry surface
appeared. The public Lua surface exposes no focus getter, and reading
`workspace.list`/`panel.*` would require workspace.read/panel.focus — a
capability widening this package must not take.

Resolution rule: explicit scope/bounds always win unchanged; when they are
absent the plugin reads its declared, capability-neutral settings context
(`default_panel`/`default_workspace`; empty string means unset) and applies
the shipped row/byte caps (16/4096). A configured bound outside 1..cap falls
back to the shipped cap; explicit over-bound requests still deny. With no
valid scope axis the typed E_HISTORY_SCOPE_MISMATCH denial is preserved.

- bitty-plugin.toml: add `[settings]` with the four declared keys; keep the
  capability set byte-identical.
- lua/history/init.lua: add resolve_scope/resolve_bounds and wire them into
  M.query before validation; validate_scope/validate_query stay unchanged.
- tests: empty-args dispatch produces entries for the configured focused
  panel, explicit scope wins, no context still denies, malformed default
  fails closed, defaults stay inside caps (3 of 5 fail pre-fix).
- SDK pin: 0a9487f (open PR head) -> 3ea8e58 (merged main; history surface
  plus declared-settings lint/mock support, PR #152).
- README/TODO synchronized; stale compat/pin/verification prose fixed.

Task: CTX-0005
@Xuepoo Xuepoo added this to the v0.1.0 milestone Oct 19, 2026
@Xuepoo Xuepoo added P1 fix Bug fix area:plugin Plugin manifest and runtime surface labels Oct 19, 2026
@Xuepoo

Xuepoo commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent review by a separate review subagent: APPROVE.

  • Empty-args dispatch resolves the declared default_panel / default_workspace (bounded, capability-neutral) instead of failing; the public Lua surface exposes no focus getter without a capability this plugin must not request.
  • Explicit scope and explicit bounds keep the unchanged validator/host path; over-bound still denies.
  • Unavailable context still returns E_HISTORY_SCOPE_MISMATCH; malformed defaults fail closed as E_DEF_INVALID.
  • Capabilities are byte-identical to main; no new host namespace beyond declared settings.
  • Regression is non-vacuous: 3 of 5 new tests fail against origin/main's init.lua.
  • just check: 49 pass / 0 fail; manifest lint 0 errors (3 expected warnings); gitleaks no leaks. CI green; no CodeRabbit findings.

GitHub rejects a formal approval from the authoring account, so this review is recorded here and in recording/reviews/pr-16-independent-review.md; merging with the owner-authorized admin override.

@Xuepoo
Xuepoo merged commit 43b6dcf into main Oct 19, 2026
7 checks passed
@Xuepoo
Xuepoo deleted the ctx-0005/fix-default-scope branch October 19, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:plugin Plugin manifest and runtime surface fix Bug fix P1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

history: keybinding dispatch denies with E_HISTORY_SCOPE_MISMATCH (empty args)

1 participant