Skip to content

id(web): GET /api/tags silently drops the value filter without a key — ?value= returns the whole namespace, ?subject=&value= returns all the subject's tags #1607

Closed

Description

@dezren39

Summary

GET /api/tags silently discards the value query parameter in two of its dispatch combinations, returning unfiltered results with a 200 as if the filter had been applied:

  • GET /api/tags?value=rust (value, no key, no subject) returns every tag in the namespace — the request is indistinguishable from GET /api/tags with no filters at all.
  • GET /api/tags?subject=a.md&value=rust (subject + value, no key) returns all of the subject's tags, including values other than rust.

TagQuery::value is documented as "Filter by tag value (requires key)" (pkgs/id/src/web/tags_ws.rs:125), but nothing enforces the requirement and nothing applies the filter in these arms — the parameter is accepted and dropped.

Evidence (verified this run on main 63ba1705)

  • pkgs/id/src/web/tags_ws.rs:275-307 — get_tags_handler dispatches on (subject, key, value):
    • (Some(subject), None, _) at :294 calls get_tags(subject) — the _ discards value.
    • (None, None, _) at :304 calls list_all(ns) — the _ discards value.
    • The combinations that do honour value are (Some, Some, value) (:280-293, post-filter) and (None, Some(key), Some(value)) (:296-300, find_by_key_value).
  • Probe (verbatim dispatch arms copied from the handler, stub store recording which query ran; run on the laptop, tags_ws.rs sha256 645c8e20 identical both sides):
    • CTRL key=label&value=rust → find_by_key_value, 2 tags (both rust).
    • CTRL subject=a.md&key=label&value=rust → get_by_key + filter, 1 tag.
    • CTRL subject=a.md → 2 tags; CTRL no filters → 4 tags.
    • BUG1 value=rust alone → arm list_all, 4 tags — including label=python and a value-less tag; byte-for-byte the no-filter result.
    • BUG2 subject=a.md&value=rust → arm get_tags(subject), 2 tags — rust and python; the python tag proves the filter never ran.
  • No test pins either combination: the handler has no unit tests for the value-without-key shapes, and the web UI only ever calls ?subject= (pkgs/id/web/src/main.ts:909), so this is an API-surface defect, not a UI one.

Why it matters

A caller that passes value without key — an easy mistake, since the parameter is accepted with a 200 — gets a superset of what it asked for and no signal that the filter was ignored. For a value-only caller that superset is the entire namespace. This is the silent-wrong-answer shape: the response looks like a successful filtered query, so downstream code (scripts, the REPL, future UI) will act on tags it never asked for. Every other loosely-formed input on this surface is refused (request_value rejects value+value_b64 together, resolve_ns 400s an unknown namespace) — value without key is the loose one.

Acceptance criteria

  • GET /api/tags?value=... without key never returns unfiltered results: either a 400 naming the missing key (matching the documented "requires key"), or a real value search — one behaviour, pinned by tests.
  • GET /api/tags?subject=...&value=... without key gets the same treatment (400, or the subject's tags filtered by value).
  • Handler-level tests pin both shapes plus the existing controls: key+value, subject+key+value, subject only, and no filters all keep their current results.
  • No behaviour change for well-formed callers (the web UI's ?subject= calls included).

Dedup

Searched open + closed issues for tags value filter, api/tags value, and related terms: no filing covers the tags REST dispatch. Adjacent but distinct: #1547 (projector read-API limit validation — different repo component and parameter), #1208 (Project pool truncation — names fetchProjectPool, not this handler).

Activity

  1. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 28, 2026
  3. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (new-issue-creator-code-b)
    • Session / ID: new-issue-creator-code-b cron run 2026-10-28 00:50 CDT
    • Found via: filed this run
    • Task: Triage the dropped value filter in GET /api/tags (pkgs/id/src/web/tags_ws.rs get_tags_handler dispatch): pin one behaviour for value-without-key (400 naming the missing key, or a real value search) and add handler tests.
    • Expectation: effort/s — done when both value-without-key shapes stop returning unfiltered results and the controls are pinned by tests.
    • Links: queue packet packet-1607.json (repo-health-code-pilot, triage)
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 28, 2026
  5. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  6. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triage verdict — CONFIRMED, behaviour pinned: 400 (fix = queue task 1848)

    • Agent: queue worker (repo-worker-code), Lane D shift
    • Session / ID: queue task 1706 (triage)
    • Found via: queue claim

    Verified fresh this run: pkgs/id/src/web/tags_ws.rs on current main is byte-identical to the creator probe file (sha256 645c8e2099ce3235366880a7da8a1e9fa7c39178c49a465b9c326eea3d8db6fc), so the probe results hold on current main: value-only returns the whole namespace, subject+value returns all the subject's tags. Code read confirms the mechanism — the (Some(subject), None, _) and (None, None, _) dispatch arms discard value via the wildcard.

    Decision — 400, not a value search. Three grounds: (1) TagQuery::value is already documented "Filter by tag value (requires key)" (:125) — the contract exists, it is just unenforced; (2) this handler's existing refusal style for malformed input is 400 + ApiResponse { ok: false, message } (see bad_ns, :71-80, for bad namespaces) — the fix should reuse that shape with a message naming the missing key; (3) value-only search is not missing functionality: the dedicated surface GET /api/tags/search already supports :value queries (SearchQuery doc, ~:131). Building a second value search into the list endpoint would duplicate it.

    Routing: fix enqueued as queue task 1848 (small_fix, dedupe repo-health:v1:fix:developing-today/code:issue:1607): 400 in both affected arms + handler tests pinning both 400 shapes and all controls (key+value, subject+key+value, subject-only, key-only, no-filter — the web UI's ?subject= calls included). No PR exists for #1607 (checked this run).

  7. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Oct 28, 2026
  8. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Dispatcher binding · openchamber-dispatcher run 2026-10-28 13:45 CDT (burn-by-expiry run: Drew directive 13:44 CDT — fill free session slots, burn the Go window resetting 2:26 PM and the Meta window resetting 4:23 PM)

  9. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    Queue task 1848 checkpoint — fix implemented and PR opened.

    What changed

    • pkgs/id/src/web/tags_ws.rs get_tags_handler: both value-without-key dispatch arms ((Some(subject), None, Some(value)) and (None, None, Some(value))) now return 400 with an ApiResponse{ok:false} message naming the missing key parameter (bad_ns-style), pointing at /api/tags/search?q=:value for value-only search. Previously they silently dropped value and returned unfiltered 200s.
    • Added 5 handler-level (router oneshot) tests: value-only -> 400, subject+value (no key) -> 400, plus controls key+value, subject+key+value, subject-only, key-only, no-filter unchanged.

    Test results

    • cargo test --lib --features web web::tags_ws (in pkgs/id, laptop): 5 passed, 0 failed. Note: the web feature requires web/dist (built via ./scripts/build.sh assets debug); plain --lib without the feature compiles 0 tests for this module.
    • cargo fmt --check src/web/tags_ws.rs: clean.
    • Full suite not run (selective testing per velocity guidance); the change is confined to one match arm plus a test module.

    PR

  10. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    Fix ready for review: #1786 (branch queue/1848-1607-tags-value-without-key, commit 56a94fb — NOT merged).

    What changed (pkgs/id/src/web/tags_ws.rs only, +147):

    • get_tags_handler: new first dispatch arm — when value is present and key is absent, in both the (subject, no key) and (no subject, no key) combinations, return 400 with an ApiResponse{ok:false} message naming the missing key parameter and pointing at /api/tags/search?q=:value (bad_ns-style shape). The wildcard arms that silently discarded value are now unreachable for that case.
    • Untouched per scope: subject+key+value post-filter, key+value find_by_key_value, key-only, subject-only, no-filter, POST/DELETE, search handler.

    Test results (cargo test --lib --features web web::tags_ws in pkgs/id, laptop):

    • 5 passed, 0 failed: value-only -> 400 (message names key), subject+value (no key) -> 400 (message names key), plus controls key+value (2 tags), subject+key+value (1 exact tag), subject-only (2 tags — the web UI ?subject= shape), key-only (3 tags), no-filter (3 tags).
    • cargo fmt --check src/web/tags_ws.rs: clean.
    • Note: plain cargo test --lib web::tags_ws matches 0 tests — the web module needs --features web (plus a prior ./scripts/build.sh assets debug for web/dist); CI's --all-features covers this.
  11. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    Queue task 1848 checkpoint — fix implemented and PR opened.

    What changed

    • pkgs/id/src/web/tags_ws.rs get_tags_handler: both value-without-key dispatch arms ((Some(subject), None, Some(value)) and (None, None, Some(value))) now return 400 with an ApiResponse{ok:false} message naming the missing key parameter (bad_ns-style), pointing at /api/tags/search?q=:value for value-only search. Previously they silently dropped value and returned unfiltered 200s.
    • Added 5 handler-level (router oneshot) tests: value-only -> 400, subject+value (no key) -> 400, plus controls key+value, subject+key+value, subject-only, key-only, no-filter unchanged.

    Test results

    • cargo test --lib --features web web::tags_ws (in pkgs/id, laptop): 5 passed, 0 failed. Note: the web feature requires web/dist (built via ./scripts/build.sh assets debug); plain --lib without the feature compiles 0 tests for this module.
    • cargo fmt --check src/web/tags_ws.rs: clean.
    • Full suite not run (selective testing per velocity guidance); the change is confined to one match arm plus a test module.

    PR

  12. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · bound

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: queue task 1848 · fleet-monitor run 2026-10-29 00:19 CDT
    • Found via: monitor reconciliation (Duty 1B PR↔queue)
    • Task: Binding — open PR fix(id/tags): GET /api/tags returns 400 when value filter lacks key #1786 implements this issue (verified by the monitor this run: PR title/body/files read; scope matches the queued task's packet). Pickup route: dispatcher session (a "Dispatcher binding" comment was posted on this issue 2026-10-28T18:56Z and checkpoint comments name queue task 1848 and the PR; branch queue/1848-1607-tags-value-without-key names the task), but the pickup never landed in the queue — task 1848 still sits queued, attempts 0, never claimed. The queued task is therefore to be treated as PARKED on this PR: resume as the PR's shepherd — review CI, merge when green, do NOT re-implement. Queue-side claim/park by the monitor is pending the by-id claim/bind verb (Dispatcher v1 live: scheduled launches of OpenCode/OpenChamber sessions for queued issues #1337); until it lands, this note is the binding record a lane's revalidation will find.
    • Expectation: PR shepherded to merge; no duplicate implementation.
    • Links: PR fix(id/tags): GET /api/tags returns 400 when value filter lacks key #1786 · queue task 1848
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)queue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobstatus:in-reviewtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions