Skip to content

config model cannot express per-index searchControls (or the full searchControls schema) #16

Description

@chuckmeyer

Problem

build_tool() accepts a single searchControls key and copies it to every index:

if "searchControls" in config:
    for idx in tool["indices"]:
        idx["searchControls"] = config["searchControls"]

The API stores searchControls per index, and the dashboard writes them that way. Observed on agent 47d6a15c:

Index searchControls
tcg_cards_msft-build-2026 set
tcg_cards_msft-build-2026_price_asc null
tcg_cards_msft-build-2026_price_desc null

There is no way to express that in agent-config.json. A round-trip of this agent through the CLI necessarily pushes the primary's controls onto both replicas. The dry-run reports this correctly, but the underlying config model cannot represent the desired state.

Note this is separate from #6, which moved searchControls per-index in the payload. The config model is still one shared block.

Suggested fix

Accept searchControls on a replica entry, overriding (or opting out of) the shared block:

{
  "index": "tcg_cards_msft-build-2026",
  "searchControls": { "hitsPerPage": { "exposed": true, "default": 10 } },
  "replicas": [
    { "index": "tcg_cards_msft-build-2026_price_asc", "description": "...", "searchControls": null },
    { "index": "tcg_cards_msft-build-2026_price_desc", "description": "..." }
  ]
}

_index_entry() already handles dict-shaped replica entries, so this extends an existing path. Semantics to settle: does an omitted key inherit the shared block (convenient) or mean "none" (explicit)? Inheriting matches current behavior and is the smaller change; null would mean "explicitly none".

Also: the documented schema is incomplete

searchControls parameters do not share one shape. Observed across 15 agent records:

Parameter Fields
hitsPerPage, page exposed, default, constraint: {min, max}
attributesToRetrieve, responseFields exposed, default: [], constraint, merge
distinct exposed, default (boolean) — no constraint, no merge
facets exposed, default: [] — no constraint, no merge
query, custom never observed non-null

constraint appears only on the numeric parameters, merge only on the two list-valued ones. Any validation or docs work should not assume a uniform shape. The README's searchControls section (#8) predates distinct/facets being observed and is worth extending.

merge semantics are still unknown — null in every record seen.

Activity

  1. chuckmeyer commented on Aug 27, 2026

    @chuckmeyer
    ContributorAuthor

    Splitting this into what shipped and what is being dropped.

    Capability half — delivered by the native config format (#19, #20)

    The original complaint was that agent-config.json could not express per-index searchControls, so a round-trip forced the primary index's controls onto every replica. A native config carries the API's own tools array, so per-index controls are represented exactly as the service stores them. snapshot → update --dry-run is clean for all 12 agents on the account, including ones whose primary index carries controls while their replicas do not — which is precisely the case this issue opened on.

    Probing also confirmed the API accepts per-index searchControls on create, so this is genuine support rather than read-only fidelity.

    Documentation half — #21

    The table here was right that the documented schema was incomplete. #21 corrects it: five of eight parameters were listed, and the single description column implied they share one shape. They do not — constraint appears only on the numeric parameters, merge only on the list-valued ones, and distinct/facets carry neither. query and custom are documented as present-but-never-observed rather than omitted, and the README now states the table was derived by inspecting live agents rather than from a published schema.

    Friendly-format parity — dropping this

    The suggestion in this issue was to accept searchControls on a replica entry, with semantics to settle around whether an omitted key inherits the shared block or means "none".

    I would leave that undone. The capability now exists through the native format, so this would be a convenience for one specific case: provisioning several agents from one template and wanting different controls per index within them. Nobody has asked for that, and it costs a new inheritance rule in the config model — exactly the kind of subtle "which side wins" semantics that produced the diff bugs in #12 and #13. Worth adding when someone actually needs it, as a fresh and smaller issue.

    Closing with #21.

    Still unknown

    merge semantics remain unobserved — null on every index of every agent, so its meaning is a guess. Same for query and custom, which stayed null even after the search-tool panel was filled in during investigation. Each would need a dashboard control that writes them, if one exists.

  2. chuckmeyer commented on Aug 27, 2026

    @chuckmeyer
    ContributorAuthor

    Closing manually — the Closes #16 keyword in #21 did not fire on merge. The capability half shipped in #19/#20 and the documentation half in #21, with two corrections following in the PR above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions