Repository navigation
Merge CSV, Excel, PDF, Word and PowerPoint files in V2 chat and workflows - #1641
Merged
Paul Lizer (paullizer) merged 19 commits intoOct 5, 2026
Merged
Conversation
Adds a deterministic tabular_merge Reason capability to V2 chat orchestration so several same-structure CSV or Excel files can be combined into one retained table and rendered to CSV or XLSX through render_file. - functions_tabular_merge.py: pure merge engine (by_name/exact_order policies, Source File provenance column, row limits, exact counts) - functions_orchestration_merge.py: limits, authorized byte loading, records-v1/report persistence and failure-code mapping - Registry, schema, adapter and executor wiring with catalog failure text - Exact tabular export profiles so CSV/XLSX render keeps every column - Admin Merge settings (enabled, chat/workflow document and row limits) - Docs, guides, release notes and functional/UI tests; goldens refreshed Version 0.261.218 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…refs #1619) Lets a V2 chat merge CSV and Excel files whose columns differ, and adds a Gather capability that inspects files before merging. - Engine: union and mapped policies, column aliases, header row, every-sheet mode with a Source Sheet column, exclusion of files or sheets that do not fit, duplicate removal (exact rows or key columns, first or last), bounded multi-column sort, nullable columns, and tabular inspection - tabular_inspect (Gather) retains sheets, headers, row counts, samples and a compatibility summary without a model - tabular_column_mapping_v1 compose profile; tabular_merge binds it as a "mapping" input and validates it again before code applies it - Plan validation shares the engine's option rules; a bound mapping must come from a compose step using the profile - Planner recipe and guidance, new catalog failure messages, plan review wording for merge and inspection settings - Fixes the registry capability list test that Phase 1 left stale - Docs, guide, release notes, functional, .mjs and browser UI tests; goldens regenerated and audited Version 0.261.219 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A workflow task can now merge many CSV and Excel files into one CSV or Excel file with code: Merge files is a new document action. Its files are chosen in order, or found at run time (every matching file, recent files, or the files File Sync changed) and merged in file-name order. Every file is authorized again, read through the access boundary, merged by the Phase 1-2 engine, rendered by the generated-file export framework and attached to the run once. No model runs. - functions_workflow_merge.py executes merges; the runner dispatches merge tasks, discovers run-time files, maps merge failures to WorkflowInputError, marks merge tasks replay-safe, and keys the artifact by run, task and bytes. - The merge contract raises reviewed MergeActionErrors that workflow saves show by task position; editor options say whether Merge is on and its limit. - Chat workflow proposals can carry merge tasks, with new repairable draft codes; the proposal card describes them as code that runs no model. - The V2 editor authors merge tasks; the classic editor routes them to V2. - Version 0.261.220, docs, release notes, and functional and UI tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A workflow Merge files task now has a merge type. Besides combining rows (Phase 3), it can join PDFs into one PDF with a bookmark per file, or put each CSV or Excel file on its own sheet of one Excel workbook. Both use the same source choices as row merges (selected files in order, every matching file, recent files, File Sync changed files), authorize every file again, run with code and no model, and attach one file to the run. - functions_document_merge.py (+ _pdf, _workbook): pure, bounded document merge engine. Only the PDF and workbook assemblers ship in this phase. - PDF: encrypted files are refused; scripts, form actions, XFA and links that open files or programs are removed wherever they appear, keeping only navigation (page links, standard page commands, http/https/mailto). pypdf keeps every source until the file is written, so workflow PDF merges keep the fixed 300 MB input budget. - Workbook: Excel cell types and number formats are kept, CSV is copied as text, formula-like text is never evaluated, and control characters Excel can't store are removed and counted. - The same files always give the same bytes (fixed package dates and ZIP metadata), so a replayed task reuses its idempotent artifact. - Library errors are reported by file name only; a failing cancellation check or storage error passes through unchanged so the run can retry. - functions_workflow_merge.py: shared discovery and authorization, then rows (tabular) or files (documents) with per-kind limits; merge_summary carries the kind. - functions_workflow_drafts.py: blueprint merge kind (tabular, workbook, pdf), format validation (merge_format_invalid) and option validation through the document-action contract; errors never echo input. - Chat proposals and the V2 editor describe each kind; planner guidance covers merge kinds (goldens regenerated, only that bullet changed). - Docs: Phase 4 feature doc, umbrella phase table (chat document merges move to Phase 7), guides, release notes v0.261.221. Version: 0.261.221 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comparing the workflow, orchestration and merge suites with the pre-merge baseline (8a802f1) found three regressions from the merge phases, all in test harnesses that load production code into hand-built namespaces or stub request-boundary modules: - test_support/orchestration_research.py: the offline registry runtime now seeds TABULAR_COLUMN_MAPPING_PROFILE, which the registry imports since Phase 2 (planner failure diagnostics, research selection). - test_workflow_task_result_handoff.py: the inventory runner namespace defines DOCUMENT_ACTION_TYPE_MERGE, which _execute_workflow_dispatch checks for every task since Phase 3 (repeat dispatcher and M365 repeat checkpoint tests). - test_workflow_repeat_editor_options.py: stubs functions_document_actions, which get_workflow_editor_options imports since Phase 3 for the Merge files task options, and checks the merge projection. All 107 compared files now match the baseline. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A workflow Merge files task can now use "Combine Word documents" as its merge type: Word documents are appended, in order, into one .docx with code and no model, choosing files the same four ways as every merge. - functions_document_merge_docx.py: docxcompose-based assembler. The first document is the base (page setup, headers, footers); later bodies keep their styles, numbering, images, tables and footnotes. Formatting keeps each document's look (clashing styles copied under new names) or uses the first document's; page breaks and file-name headings are optional; orphaned comment anchors are removed and reported. - Deterministic output (fixed modified date + normalized package), the result is re-opened before delivery, library errors name only the file, and cancellation-check or storage failures pass through for retry. - docx enabled in MERGE_KINDS_AVAILABLE, DOCUMENT_MERGE_KINDS and the V2 editor; Word merges read at most 300 MB (fixed in-memory budget). - Chat proposals: blueprint kind docx with formatting, page_breaks and source_headings; the merge_options_invalid hint lists each kind's options; planner guidance and goldens updated (merge bullet only); proposal card wording for Word. - requirements.txt: docxcompose==2.2.0 (MIT; pulls in Babel, BSD-3). - Tests: Word assembler (determinism, failures), Word workflow tasks, editor authoring in a browser, proposal parsing and wording; PowerPoint is now the kind that isn't available yet. - Docs: Phase 5 feature doc, umbrella, guides, release notes v0.261.222. Version: 0.261.222 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A workflow Merge files task can now use "Combine PowerPoint decks" as its merge type: the slides of PowerPoint decks are appended, in order, into one .pptx with code and no model. Every merge type is now available. - functions_document_merge_pptx.py: package-level assembler. Slides keep their pictures, media, charts with embedded workbooks, diagrams, tables and notes; keep_source brings layouts, masters and themes (identical ones reused), use_first places slides on the first deck's matching layouts; one section per deck; IDs stay unique and unselected slides leave no orphaned parts; python-pptx re-opens the result. The first deck decides the slide size; differing sizes, embedded fonts, comments and linked content are reported. Encrypted, damaged, oversized and macro-enabled decks are refused. - Deterministic output: fixed ZIP entries (fixed_zip_entry, now shared with normalized_package) and derived section IDs instead of uuid4. - Per-deck and final-write wrappers report library errors by file name only; cancellation-check and storage failures pass through for retry. - pptx enabled in MERGE_KINDS_AVAILABLE (now every kind), DOCUMENT_MERGE_KINDS and the V2 editor; up to 2,000 slides, 300 MB of decks. - Chat proposals: blueprint kind pptx with formatting and sections; planner guidance and goldens updated (merge bullet only); proposal card wording for PowerPoint. - Tests: PowerPoint assembler (determinism, failures), PowerPoint workflow tasks, editor authoring in a browser, proposal parsing and wording, and a withheld kind still refused by the contract, the merge and the editor. - Docs: Phase 6 feature doc, umbrella, guides, release notes v0.261.223. Version: 0.261.223 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 chat orchestration can now join whole documents in a chat turn, without
a workflow. A new Reason capability, document_merge ("Merge documents"),
merges PDFs, Word documents or PowerPoint decks into one file, or puts CSV
and Excel files on separate sheets of one workbook, with the same
deterministic engine workflow merges use. No model reads the files.
The merge step assembles the authorized files once to check them and
retains a document_assembly_v1 description (kind, options, files in order,
the checked file's size and SHA-256), never the bytes. render_file with the
new assembled_document_v1 profile reads only the merge result's own lineage
documents, with access checked again after every read, assembles them
again, and delivers the file only when it is byte-identical; otherwise it
fails as output_source_changed and nothing is uploaded. Plans must name the
kind, use only that kind's settings and render in the merge's own format.
Hardening for every merge:
- Word documents with DDE/DDEAUTO fields are refused, however the field
code is split, nested, cased or encoded; the first document's template
link is removed and linked content is reported.
- PowerPoint ppaction://program and ppaction://macro actions are removed
from every deck, including the first, in every part declared as Office
XML whatever its name or encoding.
- Office packages with an XML part that declares a document type or is
written or declared in another encoding than UTF-8/UTF-16 are refused
before any library parses them (new functions_ooxml_package_guard.py),
in document and spreadsheet merges. Parts are chosen by name and
declared content type; only prologs are read (4 KiB per part, 32 MiB
per package), with cancellation checks.
- Word lists copied from later documents get derived IDs instead of
docxcompose's random ones, so Word merges with lists are byte-identical
(also fixes repeated workflow Word merges attaching a second copy).
- A storage fault while Render re-reads a merged file's originals stays
retryable; _authorize's source-failure translation is shared with the
new reader.
- Tests cover cross-process byte-identical re-assembly, 100 files of 1,000
rows in one workflow run, formula-free CSV/XLSX output, ZIP size lies,
cold imports, and delivery through the real rendering service.
Also: Merge documents in the admin capability list, plan card wording,
planner guidance (workflows only for scheduled, synced or very large
merges), regenerated planner goldens, Phase 7 feature doc, guide, admin
docs and release notes. Version 0.261.224.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…1619) Several CSV or Excel files merged into one Excel file can mean every row on one sheet (tabular_merge) or each file on its own sheet (document_merge kind workbook). The planner used to assume one sheet. It now asks, with exactly two choices, "All rows on one sheet" and "Each file on its own sheet", when the request doesn't decide the layout. - build_deliverable_availability adds MERGE_LAYOUT_FACT only while tabular_merge, document_merge and render_file are all offered, so the planner never asks a question whose answer it can't deliver. - It doesn't ask when CSV output, wording (stack, one table, a sheet per file, separate sheets), a saved preference or an earlier answer decides it; when the user declines, rows go on one sheet and the plan says so. - The general clarification rule now names its exceptions (capability guidance, deliverables facts, workflow instructions) while still preferring stated assumptions otherwise. - Workflow merge proposals ask the same question with the same choices. - New functional test drives the real planner with a scripted model; planner goldens regenerated (only the two intended text changes). - Guide, Phase 7 and umbrella feature docs, admin orchestration page and release notes updated. Version 0.261.225. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui published v0.261.218 through v0.261.232 for nine merged PRs while this branch used v0.261.218 through v0.261.225 for the V2 file merge phases, so config.py and the release notes collided. The integration branch's sections are kept exactly as published. This branch's eight sections shift up fifteen, to v0.261.233 through v0.261.240, and config.py becomes 0.261.240. Version references in this branch's code headers, tests and documentation move with them; only lines this branch added were renumbered, and every other change in the renumbering is checked to be a version number. The integration branch now authorizes a retained result from its conversation and run without rereading its source documents. A merged document is assembled from its original files when it renders, so those files are still read with the user's current access, and only identical bytes are delivered. Two lifecycle tests now pin that: a file the user can no longer open fails the render, and a new version with the same bytes is still delivered. The Phase 7 feature doc and the output lifecycle doc describe the exception. Conflicts: config.py and the release notes (renumbered), the output lifecycle test header, and the workflow proposal card UI test (each side added one test; both are kept). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
While this branch was being synced, paullizer-react-v2-ui merged #1626 and #1633 and published v0.261.233, which this branch had just taken for its first merge phase. The integration branch's sections are kept exactly as published. This branch's eight sections shift up one, to v0.261.234 through v0.261.241, and config.py becomes 0.261.241. Every file only this branch changed is checked to equal its previous content with those versions shifted, and the release notes are the integration branch's file plus this branch's eight sections. functions_structured_file_renderers.py conflicted on imports only: the exact-tabular profiles import from this branch and the scratch directory import from the integration branch are both kept. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui 0.261.233 (#1623) gave short-lived server files a dedicated location: scratch_file_dir() returns /sc-temp-files, which the image creates for its non-root user, when the process can write to it, and None, the platform temp directory, otherwise. File renders and generated-file downloads use it. The merge engines' spools didn't. A merged document over 16 MiB, or a spreadsheet merge's rows over 8 MiB, spilled into the platform temp directory (/tmp in the image). That works, but sits outside the directory the image provides. new_output_spool(), which every document assembler uses, and the tabular _RowSpool now pass dir=scratch_file_dir(). Nothing else changes; openpyxl still keeps its own write-only sheet files in the platform temp directory and removes them once written. test_file_merge_scratch_dir.py makes every spool spill on its first write and records where tempfile creates each file: the scratch directory when it exists, the platform default when it doesn't, for spreadsheet merges and PDF, Word, PowerPoint and workbook merges, with the same result as an in-memory merge. Without this change the five scratch-directory cases fail. Refs #1619 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui merged #1635 and published v0.261.234, which this branch used for Phase 1. The integration branch's sections are kept exactly as published. This branch's nine sections shift up one, to v0.261.235 through v0.261.243, and config.py becomes 0.261.243. Every file only this branch changed is checked to equal its previous content with those versions shifted, every file only the integration branch changed is unchanged, and the release notes are the integration branch's file plus this branch's nine sections. test_workflow_task_result_handoff.py conflicted on its header only. It keeps #1635's "Harness dependencies restored in: 0.261.234" line with this branch's Version, now 0.261.238, and both harness changes: #1635's publication policy and schedule rules, and this branch's merge document action constant. It now passes. functions_workflow_runner.py merged cleanly: #1635 rewords saved-result failures in _execute_workflow_task_sequence, apart from this branch's merge task. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Refs #1619 Split shared document merge primitives into a cycle-free core module and clean up CodeQL findings in merge tests and workbook handling. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
0.261.244 moved the code the PDF, Word, PowerPoint and workbook assemblers share into functions_document_merge_core.py to clear CodeQL's import-cycle alerts. The umbrella, Phase 4 and Phase 7 docs still described functions_document_merge.py as holding all of it. They now say what moved and why, and that callers are unchanged. The v0.261.244 release note now describes the change in the repo's usual terms rather than as a list of alert fixes, and says merges behave exactly as before. Docs only, so the version stays 0.261.244. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CodeQL (alert 4001) found that functions_document_merge_core.py imports typing.Sequence without using it. It came along when the shared code moved out of functions_document_merge.py, but merge_documents, the only function that uses it, stayed in functions_document_merge.py. No other merge module has an unused import. This completes the 0.261.244 CodeQL cleanup, so the version is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui merged #1636 (workflow alert acknowledgment) and published v0.261.235, which this branch used for Phase 1. The integration branch's sections are kept exactly as published. This branch's ten sections shift up one, to v0.261.236 through v0.261.245, and config.py becomes 0.261.245. Every file only this branch changed is checked to equal its previous content with those versions shifted, every file only the integration branch changed is byte-identical to it, and the release notes are the integration branch's file plus this branch's ten sections. Ten files changed on both sides. config.py and the release notes conflicted on versions only. The other eight merged cleanly; in docs/guides/create-a-workflow.md only this branch's six lines about its own versions were shifted, and #1636's line about 0.261.235 is kept. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui merged #1643 (V2 sidebar conversation scroll) and published v0.261.236, which this branch used for Phase 1. The integration branch's sections are kept exactly as published. This branch's ten sections shift up one, to v0.261.237 through v0.261.246, and config.py becomes 0.261.246. Every file only this branch changed is checked to equal its previous content with those versions shifted, every file only the integration branch changed is byte-identical to it, and the release notes are the integration branch's file plus this branch's ten sections. None of the integration branch's own text in this branch's files used the shifted versions. Only config.py and the release notes changed on both sides, and both conflicted on versions only. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
added a commit
that referenced
this pull request
Oct 5, 2026
V2 reached 0.261.238 when #1644 merged, so this branch moves one above it. Sets config.py VERSION to 0.261.239 and moves this branch's own 0.261.238 lines to 0.261.239: the hand-off modules' and tests' Version, Implemented in, MINIMUM_VERSION and assert_app_version_at_least literals, the two route-test coverage notes, the run adapter test's hand-off refusal note, the two hand-off rows in docs/admin/orchestration.md, the feature doc and this branch's release-notes header. #1644's own 0.261.238 lines (its schema docstring, its Microsoft 365 section and troubleshooting row in orchestration.md, its fix docs and its release-notes section) are unchanged. The open V2 PRs claim 0.261.234 (#1639), 0.261.235 (#1637), 0.261.236 (#1638) and 0.261.246 (#1641), none of them 0.261.239. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
paullizer-react-v2-ui merged #1644 (orchestration retained-result and Microsoft 365 action fixes) and published v0.261.237 and v0.261.238, which this branch used for Phases 1 and 2. The integration branch's sections are kept exactly as published. This branch's ten sections shift up two, to v0.261.239 through v0.261.248, and config.py becomes 0.261.248. Every file only this branch changed is checked to equal its previous content with those versions shifted, every file only the integration branch changed is byte-identical to it, and the release notes are the integration branch's file plus this branch's ten sections. Five files changed on both sides. config.py and the release notes conflicted on versions only. functions_orchestration_adapters.py, functions_orchestration_schema.py and docs/admin/orchestration.md merged cleanly; in the admin page only this branch's five lines about its own versions were shifted, and #1644's lines about 0.261.237 and 0.261.238 are kept. #1644's new failure codes and build_failure argument are additive and don't touch the merge capabilities. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
merged commit Oct 5, 2026
a5a5b1c
into
paullizer-react-v2-ui
11 checks passed
Paul Lizer (paullizer)
added a commit
that referenced
this pull request
Oct 6, 2026
Merge only landed V2 with rerere disabled. Preserve both capability-import sets, both draft error families, all incoming release-note sections and the three V2 off-golden fixtures. Keep V2 VERSION 0.261.248 pending the separate hand-off renumber to 0.261.250. Auto-merged shared files preserve both sides; executor registry imports are their exact union. The regenerated docs inventory is unchanged. Full verification remains pending. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
added a commit
that referenced
this pull request
Oct 6, 2026
V2 reached 0.261.248 with #1641; coordinator reserves 0.261.249 for 6b-2 and 0.261.250 for Phase 7a. Change only config.py VERSION and the 44 PR-owned version lines. Preserve every incoming V2 version reference and golden fixture. No behavior changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 6, 2026
Preserve both proposal-test histories and every V2 release-note section. The version bump follows separately. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 6, 2026
Keep all base version history; update only 6b-2's version, tests and documentation after the V2 microsoft#1641 merge. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This was referenced Oct 6, 2026
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 6, 2026
test_the_phase_4_blueprint_and_payloads_are_unchanged failed at V2 15feec6 on the schema digest alone. Its pin (ab12fb1d) predated microsoft#1641, which added the merge task schema and was merged into Phase 7a at b8e4730 without a refresh. The pin now holds 520139cb, the value V2 computes at a5a5b1c, after microsoft#1641 and before hand-off merged, so the test still proves hand-off left the Phase 4 schema unchanged. The payload, validation and dry-run pins are unchanged. The fix doc adds the builder test to Files modified, records the pin under Validation, and gives the 200-document read time as about 1.7 seconds, to match the c07b911 timings. Refs microsoft#1549, microsoft#1543. Follow-up to microsoft#1640. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.
Summary
People often need many files of one kind turned into one: regional CSVs into one spreadsheet, signed forms into one PDF, chapters into one Word document, team decks into one presentation. V2 couldn't do this, and asking a model to rewrite the files risks changing the data. This PR adds deterministic merging to V2 chat orchestration and V2 workflows. Code does the merge; no model reads or rewrites the files. It was built in seven phases under #1619.
tabular_merge,tabular_inspect): CSV and Excel files become one CSV or Excel file. Columns match by name or exact order, or are reconciled by union or by an AI-prepared column mapping that code checks before any row is copied. Header rows, every-sheet mode, duplicate removal, sorting and a Source File column are supported. When a request doesn't say, chat asks whether the result should be one sheet or a sheet per file.document_merge): PDFs into one PDF with a bookmark per file, Word documents into one document, decks into one deck with a section per deck, and spreadsheets into one workbook with a sheet per file. The merge step keeps only a description: the files, options, size and SHA-256. Render (assembled_document_v1) reads the same files again with the user's current access and delivers the file only if it is byte-identical.Worth a careful look:
Hostile files. Word fields that start programs (DDE) are refused. The first document's template link is removed. PowerPoint program and macro actions are removed, as are PDF scripts and actions that open files or programs.
functions_ooxml_package_guard.pyrefuses Office XML parts that declare a DOCTYPE or use an encoding other than UTF-8/16, before any parser reads them. Packages that declare or hide too much uncompressed data are refused. Formula-like cells are neutralized in CSV and written as text in Excel.Access for merged documents.
paullizer-react-v2-uiauthorizes retained results from their conversation and run. A merged document is the deliberate exception: Render reads the originals again throughread_available_document_bytes, which enforces current access and screening. SeeORCHESTRATION_OUTPUT_LIFECYCLE.md.Determinism. The same files and options always give the same bytes: ZIP dates are fixed and Word list IDs are derived. A retried workflow therefore reuses the file it already attached.
Engine layout. CodeQL found import cycles between
functions_document_merge.pyand the assemblers it loads lazily. The code the assemblers share now lives infunctions_document_merge_core.py.functions_document_merge.pykeepsmerge_documentsand still exports the public names, so its callers didn't change.New dependency. Word merges need
docxcompose==2.2.0(MIT), which brings in Babel (BSD-3-Clause). Office and PDF libraries load only when a merge of that kind runs.Phase 3 differences from V2: Merge multiple files (CSV/Excel, PDF, Word, PowerPoint) through chat orchestration and workflows #1619. These were not built:
They are listed on V2: Merge multiple files (CSV/Excel, PDF, Word, PowerPoint) through chat orchestration and workflows #1619 as possible follow-ups.
Linked issue
Refs #1619
Release Notes & Latest Features
Is this visible to end users?
Is this admin-facing (Admin Settings, governance, deployment, config)?
Should this become a Latest Feature card?
Screenshot needed for the card?
Release notes cover v0.261.237 through v0.261.246 in
docs/explanation/release_notes.md. This PR doesn't include a Latest Feature card or screenshot yet.Version bump
application/single_app/config.pyVERSIONthird segment bumped, or not needed because this is docs-onlydeployers/version.txtbumped, or not needed becausedeployers/was not changedEach change bumped the version:
functions_document_merge_core.py, plus small cleanupsdeployers/is unchanged.This branch is synced with
paullizer-react-v2-uiat 25470e7, which published 0.261.236 (#1643), and renumbered above it. If more PRs land there first, it gets synced and renumbered again.Testing / validation
Unless noted, these ran on 29677f0, the merge with
paullizer-react-v2-uiat 25470e7, with Python 3.12 on Windows.application/single_appandfunctional_testswere onPYTHONPATH, plus the repo root andui_testsfor UI tests.test_v2_sidebar_conversation_scroll.pyand the orchestration import allowlist: 979 passed.python -m pytest -q functional_tests/test_m365_runtime_imports.py functional_tests/test_orchestration_dependency_allowlist.py functional_tests/test_document_merge_assembly.py functional_tests/test_workflow_merge_task.py: 141 passed. These cover real runner imports, the orchestration import allowlist, and cold imports of the merge engine without Office or PDF libraries.python functional_tests/test_file_merge_scratch_dir.py: 11 passed. With the twodir=scratch_file_dir()arguments reverted, its 5 scratch-directory cases fail.node --test functional_tests/test_v2_orchestration_merge_arguments.mjs functional_tests/test_v2_workflow_merge_task.mjs functional_tests/test_v2_workflow_proposal_merge.mjs: 25 passed.npm --prefix application/v2_ui run typecheck: passed. Both V2 bundles were rebuilt from this commit:npm --prefix application/v2_ui run buildnpm --prefix application/v2_ui run build -- --outDir ../../ui_tests/artifacts/orchestration-plan-editor, the bundle the UI tests load.python -m pytest <file> -q: 133 passed.test_v2_orchestration_merge_arguments.py,test_v2_workflow_merge_task.py,test_v2_orchestration_workflow_proposal_card.pyandtest_workflow_classic_advanced_guard.py.test_v2_sidebar_conversation_scroll.py(9) andtest_v2_workflow_alert_notices.py(60).test_v2_notifications_bell.py(44),test_v2_workflow_alerts.py(30) andtest_workflow_alert_classic_acknowledgment.py(18) passed.SIMPLECHAT_UI_BASE_URL).paullizer-react-v2-uiwithout this branch:route_tests/test_workflow_alert_acknowledge_policy.py,route_tests/test_workflow_alert_since_hours_policy.pyandtest_group_workflow_file_sync_client_parity.py: setup errors, because this environment'swerkzeughas no__version__.test_workflow_file_sync_prompt_context.pyandtest_workflow_task_document_actions.py: string assertions against runner source that the target no longer contains.python scripts/build_docs_inventory.py: no inventory changes.python -m pytest -q functional_tests/test_docs_app_surface_coverage.py functional_tests/test_docs_site_quality.py: 13 passed.paullizer-react-v2-uiat e1e6949: 465 orchestration, workflow, merge, export, docs and admin test files, run file by file on both branches. There were no new failures:node_modulescache when shards run in parallel, and passes on its own.functional_tests/test_docs_link_integrity.py: 11 broken relative links, none in the merge docs.functional_tests/test_docs_release_notes_integrity.py: the generated release-notes pages are stale.ui_tests/test_workflow_alert_classic_acknowledgment.pyfails when it runs in the same pytest process after any test that uses pytest-playwright'spagefixture. Its harness starts a second sync Playwright, which Playwright refuses ("using Playwright Sync API inside the asyncio loop"). This reproduces onpaullizer-react-v2-uiwith only its own tests, and the file passes on its own.Documentation
New docs:
docs/explanation/features/V2_FILE_MERGE.md: the umbrella feature doc.V2_FILE_MERGE_PHASE_1_SPREADSHEETS.mdthroughV2_FILE_MERGE_PHASE_7_CHAT_DOCUMENTS.md: one doc per phase. The Phase 4 doc explains the shared core module.docs/guides/merge-files.md: the user guide.Updated docs:
docs/admin/agents-actions.mdanddocs/admin/orchestration.md.ORCHESTRATION_OUTPUT_LIFECYCLE.md,CHAT_ORCHESTRATION.mdandGENERATED_FILE_EXPORT_FRAMEWORK.md.docs/guides/create-a-workflow.mdanddocs/guides/create-files-with-orchestration.md.There's no separate fix doc: the bug fixes harden this new feature and are documented in the Phase 7 doc.
Security checklist
@swagger_route(security=get_auth_security()). There are no new routes.sanitize_settings_for_user(). No settings object is sent: the workflow editor receives only Merge's enabled flag and workflow file limit, and Merge settings are edited on the admin Actions pane.