Merge Development into staging, and stop navigating by tab id - #1320
Merged
Paul Lizer (paullizer) merged 14 commits intoAug 19, 2026
Conversation
Actions could be configured with a caller-supplied endpoint while authenticating with the application's own workload identity. A caller holding only the User role could save a personal blob_storage action with an arbitrary endpoint and auth.type=identity, causing the application to send a token minted for its own managed identity to that destination. Add a shared Azure endpoint allowlist covering blob, queue, Cosmos, Databricks, Azure Monitor query endpoints and Entra authority hosts for the public, US Government, China and Germany clouds. Validate at save time and again immediately before client construction so actions stored before this change stop working rather than continuing to leak credentials. Enforce each action type's allowedAuthTypes in the shared health-checker choke point, which previously only populated the action modal. Constrain Log Analytics custom clouds so authorityHost and endpointOverride cannot select the token authority or the delegated-token OAuth resource. Validate the endpoint in the Cosmos test-connection route, which builds its own client. Widen blob_storage.definition.json to connection_string, identity and key and extend the blob modal with an auth selector plus endpoint and account key fields. Refactor functions_file_sync.py to source the storage suffix allowlist from the shared module. The upstream azure-storage-blob bearer-challenge defect that motivated this report is not addressed here and still needs a fix in Azure/azure-sdk-for-python. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…-blob-action-hardening # Conflicts: # docs/explanation/release_notes.md Co-authored-by: paullizer <34814295+paullizer@users.noreply.github.com>
…rdening Restrict app-identity action endpoints to Azure origins
A telemetry agent retrieved 900 BatteryVoltage1 samples and was asked for a CSV. The published artifact held 3 rows. A follow-up conversation published 2 rows of server discovery metadata. Three defects combined. 1. build_generated_file_export() let any assistant-rendered table outrank the authorized action rows, so the illustrative excerpt the model pasted replaced the 900-row dataset. CSV now prefers the authorized rows whenever the assistant table is an excerpt of them, matched on shared columns and cell values. A table the model composed itself stays authoritative. 2. CSV never received the publication contract JSON and XML already state, so the model reported that it could not attach files and pasted a sample instead. build_generated_file_output_guidance() now tells the model the server serializes and attaches the artifact, and forbids pasting a row sample in its place. The one-clarification rule is unchanged. 3. A turn whose reply was the schema clarification still published a CSV, assembled from whatever incidental discovery rows the turn produced. That turn now publishes nothing and waits for the user's answer. Version: 0.260.007
…arifications Follow-up to the same customer conversations. Even with the authorized rows winning, two requests still had nothing to export. Discovery calls diluted the dataset. A turn that ran list_instances, list_parameters, then two pages of list_parameter_history merged all four into one table with a Source action column, so the artifact carried lookup rows alongside the samples. Action rows are now grouped by action, so paged calls to one action stay one dataset, and a retrieval result that dwarfs the turn's other calls is used on its own. "create a csv" referring to earlier data had no rows. Action results were read from the current turn only, so a follow-up request exported nothing even though the conversation already held the full result set in its stored citations. When a turn produces no assistant rows and no action rows, the export now reaches back through recent assistant turns, rehydrates their full citation payloads from the stored artifact records, and uses the most recent turn that gathered rows. The reach-back is skipped whenever the turn gathered its own rows, so rows are never double counted, and the conversation is reauthorized before any message read. Answering the schema clarification did nothing. The reply that answers it does not mention CSV, so the request was not recognized and no artifact was published. An unanswered clarification now carries the pending CSV request forward to exactly the next user turn, unless that turn requests a different format. The passthrough contract established by the original request carries with it. Version: 0.260.008
The reach-back read every artifact of a turn just to learn whether it held rows, which capped the window at 5 assistant messages and made a wider one expensive. It also took the first turn with any rows, so a newer memory or discovery call outranked the dataset a request actually meant. Compaction already leaves the answer on the message. A 54KB telemetry result is stored on the assistant message as a 677 byte citation that still carries row_count, the column names, and a remaining_items marker for the dropped tail. That is enough to decide which action holds the data without reading a single payload. Selection is now two phase. select_prior_turn_action_citations() scans the compact citations already returned by the message query, groups them by action, and picks the newest group whose recorded rows clear the dataset threshold, falling back to the most recent rows when no turn has a dataset. Only the winning group's artifacts are then fetched and expanded. The window now follows conversation_history_limit from admin settings rather than a fixed 5, so anything the model can still see in context is reachable by the exporter, capped at 40 messages. Selection is pure and lives with the other row-shape logic; the route keeps authorization and I/O. A guard test pins the compaction assumption so a change to the compaction limits fails loudly instead of silently degrading the scan. Version: 0.260.009
…zed-action-rows fix(csv): build CSV artifacts from authorized action rows
The CSV work left three gaps in the sibling formats and one loose end. JSON and XML were built only by parsing a payload out of the assistant reply, so they could never use authorized action rows and never reached back to rows an earlier turn already gathered. "make that a json" failed exactly the way "create a csv" used to. build_structured_artifact_rows_payload() now serializes authorized rows, with the same earlier-turn reach-back and the same passthrough contract, whenever the reply carries no payload of its own. The schema clarification gate lived inside the CSV branch, so a turn that asked a clarifying question could still publish a DOCX or PDF. It now runs before the format branch and covers every generated file format. DOCX and PDF guidance never forbade claiming that files cannot be created, which is the exact sentence that produced the truncated CSV. It now states the publication contract the other formats already state. The DOCX and PDF passthrough reason code compared a single row-source literal, so it was dropped once rows could come from an earlier turn. It now uses the named row-source set. Version: 0.260.010
…y-across-formats fix(artifacts): give every generated file format the same row resolution
…rry-forward A deployment test surfaced three defects in agent conversations producing CSV artifacts from a truncating telemetry action. The model replied "I cannot create or attach a CSV file in this interface" on the turn that answered a schema clarification, yet the server published the CSV. Version 0.260.008 carried the pending format forward for publication but not for guidance, which both chat paths still resolved from the current user message alone. A clarification answer names no format, so no publication contract ever reached the model. Both paths now resolve guidance through _resolve_generated_file_guidance_format, which falls back to the pending format. A 1,000-row file was produced for a window holding roughly 500 distinct samples. The agent re-requested the same window with an earlier stop time rather than paging forward, and action grouping concatenated both responses. Rows an earlier page of the same action already contributed are now dropped; repeats inside a single response are kept, because the action counted them as distinct records. The source action reported truncated results and the file gave no indication it was partial. Truncation is now detected from the action payload, propagated through the export payload, summary, and artifact metadata for every format, and surfaced as a Partial badge on the artifact card. CSV, DOCX, and PDF guidance now tells the model to page from the last row it holds instead of repeating the range. The telemetry fixture in test_generated_csv_uses_authorized_action_rows derived timestamps from index % 60, so its 900 samples were 60 distinct rows repeated fifteen times, making continuation pages indistinguishable from re-reads. Timestamps now roll over into minutes, matching the fixture's 15-minute window. The per-call row cap belongs to the action's own configuration and is unchanged.
…-and-guidance-carryforward Fix generated artifact paging, truncation disclosure, and guidance carry-forward
Development moved on while the Admin Settings rework was staged, so
the final pull request conflicted. Two files needed resolving.
config.py Both branches independently reached 0.260.011.
Bumped to 0.260.018 so the merged result is clearly
ahead of both.
release_notes Both branches wrote a v0.260.011 section from
parallel work. Their entries are folded into the one
section rather than leaving two headings for the
same version.
Re-verified after the merge
field names 452 on Development, 452 here, none lost, none added
regression set 32 failures, identical to baseline
jinja compile 46/46 admin templates
contract suite 43 tests pass
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Two places still navigated by naming a tab id, and the information architecture rework broke both. The setup walkthrough Each of twelve steps mapped to a tab id. Eleven named tabs that no longer exist, so those steps would have moved nowhere and left the admin looking at whatever was already on screen. Each step already knew which card it was about, so the tab id was a second copy of the same knowledge, free to go stale. Steps now name the card and openAdminCard finds the owning tab from the page. scrollToRelevantSection became redundant and is deleted, along with the old map. Steps 2 and 3 previously pointed at gpt-configuration, which lives inside the legacy model settings dialog and so cannot be scrolled to while that dialog is closed. They now point at multi-endpoint-configuration, the visible card in the same tab and the modern place to configure models. Cosmos throughput validation Clicked scale-tab to reveal an invalid field. scale is a group now, not a tab, so the field needing attention could be left on a hidden tab with no indication of where to look. It now walks up from the invalid field to its own card, so it goes wherever that field actually lives. test_admin_settings_walkthrough_targets.py asserts every step points at an element that exists, that no tab id is hardcoded, and that step numbering has no gaps. A scan confirms no stale '-tab' literal remains in executable admin JavaScript. The rule this rework converged on: never name a tab, name the setting and resolve the tab from the page. Verified regression set 32 failures, identical to baseline admin suite 46 tests pass jinja compile 46/46 admin templates Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
merged commit Aug 19, 2026
3c7f523
into
feature/admin-settings-ia
2 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings
Developmentinto the Admin Settings staging branch so #1319 can merge cleanly, and fixes the last two places that navigated by naming a tab.Part 1 — merge
DevelopmentDevelopmentmoved on while this work was staged (#1317 and the generated file output fixes), which left #1319 conflicting.config.py— both branches independently reached0.260.011. Bumped to 0.260.018 so the merged result is clearly ahead of both.release_notes.md— both branches wrote av0.260.011section from parallel work. Their entries are folded into the one section rather than leaving two headings for the same version. Nothing was dropped from either side.Everything else merged automatically.
Part 2 — the last hardcoded tab ids
Two places still navigated by naming a tab, and the rework broke both. Neither would have thrown an error; they would just have quietly gone nowhere.
The setup walkthrough
Each of twelve steps mapped to a tab id. Eleven named tabs that no longer exist, so those steps would have moved nowhere and left the admin looking at whatever was already on screen.
Each step already knew which card it was about, so the tab id was a second copy of the same knowledge — free to go stale. Steps now name the card, and
openAdminCard()finds the owning tab from the page.scrollToRelevantSection()became redundant and is deleted along with the old map, removing 105 lines.One case needed judgement: steps 2 and 3 pointed at
gpt-configuration, which lives inside the legacy model settings dialog and so cannot be scrolled to while that dialog is closed. They now point atmulti-endpoint-configuration— the visible card in the same tab, and the modern place to configure models.Cosmos throughput validation
Clicked
scale-tabto reveal an invalid field.scaleis a group now, not a tab, so the field needing attention could be left on a hidden tab with no indication of where to look. It now walks up from the invalid field to its own card, so it goes wherever that field actually lives.The rule this converged on
Never name a tab. Name the setting and resolve the tab from the page.
A scan confirms no stale
-tabliteral remains in executable admin JavaScript.test_admin_settings_walkthrough_targets.pyasserts every step points at an element that exists, that no tab id is hardcoded, and that step numbering has no gaps.Verification
The whole point of this rework is that no setting changes, so that was re-checked against
Developmentafter merging rather than assumed:DevelopmentOnce this is merged, #1319 becomes mergeable and runs the full gate set against
Development.