| D-M2-17-0 |
2026-10-22 |
Built and red-teamed on the old integration line (base int/m1-wave53 with M2-16 merged in) and ported to main after M2-16 landed: only the ticket's own commits were brought over, adapted to main. The commit that copied R-AZ-26 into the old base is dropped, because main already carries it in chapters 04 and 05; the additions to the German chapter files of the old base go to their English files on main; the rulings and the changelog entry are fragments; generated files are regenerated on main. |
The rules of the landing replace the integration branches; main is the reference. |
| D-M2-17-1 |
2026-10-21 |
The relay is the source port runs.AutocompleteRelay (declared by runs, implemented by gateway/adapters/autocompleterelay over Hub.AskAutocomplete), not a kernel port; the op handler of command.autocomplete_result lives in gateway/app and calls runs.CommandExec.AnswerAutocomplete, which authorizes. |
The one-connection rule, the report back and the delivery field belong to this use case only; precedent agents.ConsumerFeed (M2-07), gateway may use runs, not the other way round. |
| D-M2-17-2 |
2026-10-21 |
A command that is unknown, removed, not covered by its scope at the channel or shadowed answers 404 not_found like an unknown ID (through agents.Commands.Resolve); whether the owner may be reached is decided by authorize. App commands answer 404 as well until M2-25, following D-M2-16-20 (Resolve hides them); the use case and the requirement already decide app owners (empty list, required_level). |
Outside its scope a command does not exist at this place (09 10.2); the merged rule of M2-16 wins over the brief's empty list for app owners (D-W-100). |
| D-M2-17-3 |
2026-10-21 |
A request whose encoded command.autocomplete_requested payload reaches sync.event_payload_max_bytes answers 400 validation_failed with errors[].code too_long and an empty pointer. |
Truncation (04 2.2 rule 2) makes no sense for a partial text, and the publisher would only report a programming fault. |
| D-M2-17-4 |
2026-10-21 |
Without a live gateway connection of the owner in this process, and for an app owner until M2-25, the server answers an empty list at once and does not wait. |
R-AG-14 "never hangs"; adapters on REST poll or webhook never receive ephemeral events (04 15.6, 7.4). |
| D-M2-17-5 |
2026-10-21 |
InvokeFacts.MentionsAgent = true for the owner of an autocompleted command, so a guest owner still needs allow_mentioned_agents, write and organization; M2-16 decided the same for command.invoke. |
A slash command addresses its owner explicitly, like a mention (09 10.3, R-AZ-19). |
| D-M2-17-6 |
2026-10-21 |
Exactly one connection gets the request: the live connection of consumer default, else the most recently attached live connection (Hub.Attach numbers the attaches); connections still catching up are skipped. |
default is the adapter's main stream (04 6.3); otherwise the newest wins, as with consumers (R-SY-09). |
| D-M2-17-7 |
2026-10-21 |
Open requests live in an in-process registry (mutex-guarded map, compare-and-swap of domain.AutocompleteRequest); answered and expired entries stay until expires_at plus command.autocomplete_timeout and every call sweeps them, without a goroutine. A restart forgets every request; with cluster.mode = multi only owner connections and REST answers on the same api node are reached until M7-17. |
410 gone needs a memory of the request (06 5); the existing key bounds it without a new number, rl.trigger bounds the entries per person; requests are ephemeral and at most once (04 9 point 4). |
| D-M2-17-8 |
2026-10-21 |
Order of answerAutocomplete and the op: unknown or forgotten request 404 without authorize, then command.autocomplete.respond, then the answer rules (400, the request stays open), then 410 for an expired and 204 without effect for an answered request, then the compare-and-swap. |
The first answer counts (06 7 point 3, agent_runtime); a retry after a lost response must not look like an error, and a late answer learns that it came too late. |
| D-M2-17-9 |
2026-10-21 |
A failed command.autocomplete_result answers error {op, code, detail, ref} and keeps the connection; detail is <pointer> <code> of the first field error, never a value; the dispatcher's proof already applies only to successful ops, so nothing changes there. |
04 6.2 "the connection stays"; 02 9.3 proves only allowed operations. |
| D-M2-17-10 |
2026-10-21 |
The validation codes of a query are unknown_option, autocomplete_not_enabled and duplicate; choices of an answer are display texts (SEC-IN-4: invalid_utf8, control_character, bidi_control), non-empty (too_short) and at most command.autocomplete_max_choices (too_many_items); their length is bounded by the body limit of the chain. |
Suggestion names and values are shown in the composer; values of a query are partial and checked only at invokeCommand. |
| D-M2-17-11 |
2026-10-21 |
command.autocomplete_max_choices gets Max equal to the maxItems of GatewayAutocompleteResult.choices, which AutocompleteAnswer.choices and AutocompleteResult.choices now carry too; the cross-check autocomplete_below_request_timeout requires command.autocomplete_timeout below http.request_timeout. The rule command. of tools/configdoc/planned.yaml is removed, because no command key is planned any more. |
A configured value above the schema bound could never be used over the gateway, and one bound keeps REST and gateway equal (04 6.2); the request context must outlive the empty answer (02 14.2). |
| D-M2-17-12 |
2026-10-21 |
The relay admits the chosen connection by deciding agent.events.consume on the owner (resource agent) in delivery mode, not through sync.CatchUp.Admit, whose event descriptor carries the audience only for the consumer engine; the wait uses a timer the use case receives (AutocompleteDeps.After), because kernel/clock has no timer. |
The receive action of the type is agent.events.consume on the agent itself (05 6, D-M2-08-14); tests drive the wait without real time. |
| D-M2-17-13 |
2026-10-22 |
The integration suite of autocompletion bounds no wait itself: the main world sets command.autocomplete_timeout above testkit.Patience (and http.request_timeout above it), so an answer that must come at once comes within the patience; only TestCommandAutocompleteTimeout waits for a short timeout and checks only the lower bound. "No frame" is proven by order with a marker op on the same connection, "no audit entry" by a later denial; tests that need an owner without a live connection use an agent that never had one. The unit test of AG-23 waits for the start of the manual timer. |
Deterministic tests (PL/21): a test must pass the same way on a loaded machine and on a slow runner. |
| D-M2-17-14 |
2026-10-22 |
Timing comparisons of the AZ-P4 kind go through one helper, testkit.TimingSame: per round warm-up pairs, then counted pairs in alternating order, the shortest counted duration of each side within TimingBound(tolerance), up to TimingRounds rounds, failing only when every round differs. The autocomplete suite uses it; the older median comparisons on main (test/leak, test/integration/runs, identity/adapters/httpapi) are not migrated by this ticket. |
Red team: a single median comparison failed on a machine at load average 60 (43 ms without, 104 ms with the race detector); load only adds delay, a real difference shows in every round. |