Skip to content

Limit content screening intake to uploads and apply metadata edits directly (layer 2 of 4) - #1629

Merged
Paul Lizer (paullizer) merged 3 commits into
microsoft:paullizer-workspace-knowledge-holdsfrom
paullizer:paullizer-screening-upload-only-intake
Oct 3, 2026
Merged

Paul Lizer (paullizer) merged 3 commits into
microsoft:paullizer-workspace-knowledge-holdsfrom
paullizer:paullizer-screening-upload-only-intake

Conversation

@paullizer

@paullizer Paul Lizer (paullizer) commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Screening intake is limited to uploads. Content screening now checks only documents that enter a workspace through a user upload (personal, group or public, including chat uploads saved as chat_upload workspace documents) or File Sync. Generated documents (files the SimpleChat agent action saves, and artifacts published into a workspace) get a server-managed screening_exemption: {"reason": "generated", "version": 1} field when the version is created, before initial_document_marker runs. They get no screening marker, process normally while screening is on, and scan jobs skip them (screening_not_required). Clients can't set the field: reject_screening_fields refuses any screening* key, update_document refuses it, and responses never include it. A document that already has a screening marker stays screened.
  • The publication screening reservation is gone. Removed _consume_publication_reservation, consume_artifact_publication_screening_scan and the reservation, enrollment and consumption code in publication and readiness. Approval now hands a publication straight to processing. A destination without a screening marker counts as screening-complete, and the destination access check stays. A destination held by screening before this version can't be approved; the requester cancels and requests publication again.
  • Metadata edits apply directly. User edits and model-generated metadata on screened documents no longer queue a rescan or hold the document. queue_metadata_rescan is replaced by validate_screened_metadata_update, so a rename still can't change a screened file's extension. The release proof, publication checkpoint and scan-job match no longer compare metadata_fingerprint. The group and public metadata APIs return only updated (200); their routes no longer have a queued (202) branch. The V2 Documents explorer no longer handles a queued receipt. Before, that path removed the document from the list and showed "Screening is queued". Now a save is confirmed only by an updated 200 receipt, and any other receipt keeps the draft. Tag edits on a screened document no longer rewrite the released blob's metadata, which would break the ETag-pinned release.
  • Impact: generated files are available as soon as processing finishes, and editing metadata no longer makes a released document unavailable. In V2, the edited document stays in the list with its edit shown. There is no data migration: existing documents keep their screening state.

Linked issue

Refs #1621

Layer 2 of 4 in the #1621 stack, stacked on #1628 (paullizer-workspace-knowledge-holds).

Release Notes & Latest Features

  • New Feature
  • Bug Fix
  • UI Enhancement
  • Breaking Change
  • Internal only

Is this visible to end users?

  • Yes
  • No

Is this admin-facing (Admin Settings, governance, deployment, config)?

  • Yes
  • No

Should this become a Latest Feature card?

  • Yes
  • No
  • Already added

Screenshot needed for the card?

  • Yes
  • No
  • Attached

Version bump

  • application/single_app/config.py VERSION third segment bumped, or not needed because this is docs-only (0.261.229 → 0.261.230)
  • deployers/version.txt bumped, or not needed because deployers/ was not changed

Testing / validation

The global interpreter can't collect these tests (cryptography 50 with pyOpenSSL 25.3, and Flask 2.2.5 with Werkzeug 3.1.4). Every Python command below ran in a venv overlay: system site packages plus cryptography 46.0.7, Flask 3.1.3, Werkzeug 3.1.6 and Flask-Session 0.8.0.

  • python -m pytest functional_tests/test_content_screening_upload_only_intake.py -q: passed, 127 passed. With the application changes reverted and only the tests applied: 22 failed, 3 errors, 102 passed. The cases that pass either way are the existing client guards, the readiness check and the import check.
  • Base-branch comparison: ran python -m pytest <file> -q -rfE on each of the 259 functional_tests files at base 66a29bd60 and at ed2a15a0d, the branch before the V2 follow-up. No new failures. The functional suites that the follow-up commit 8fc81b523 touches were re-run afterwards; those results are listed below.
    • Base: 5591 passed, 257 failed, 258 errors.
    • Branch: 5648 passed, 256 failed, 258 errors.
    • In the 258 files present on both branches, 502 test IDs fail on base and 501 on this branch. The difference is one flaky scheduler timing test that passed on the branch run.
    • test_tabular_row_orchestration_scale.py times out at the same test on both branches.
  • Updated suites (python -m pytest <file> -q), branch result vs base:
    • test_content_screening_jobs.py: 112 passed (base: 112 passed).
    • test_content_screening_pipeline.py: 26 passed, 6 failed. The base has the same 6 failures, from the _format_vision_analysis_block harness.
    • test_content_screening_history.py: 11 passed (base: 10 passed).
    • test_group_document_management.py: 238 passed (base: 237 passed).
    • test_public_document_publication.py: 59 passed (base: 63 passed). 8 reservation-latch cases were removed and 4 hold and no-reservation cases were added.
    • test_group_document_fixture_parity.py: 103 passed (base: 103 passed).
    • test_public_document_fixture_parity.py: 63 passed (base: 63 passed).
    • test_group_document_publication.py: 127 passed.
    • test_public_document_management.py: 205 passed.
    • test_analysis_artifact_publication.py: 53 passed.
    • test_orchestration_artifact_publication.py: 13 passed.
    • test_group_document_publication_screening_bootstrap.py tested only the removed reservation and was deleted. Its import check moved to the new file.
  • Re-run at 8fc81b523, after the V2 follow-up changed the UI fixtures and the two metadata routes (python -m pytest <file> -q), all passed:
    • test_group_context_fixture_parity.py: 230 passed.
    • test_public_context_fixture_parity.py: 252 passed.
    • test_group_document_fixture_parity.py: 103 passed.
    • test_public_document_fixture_parity.py: 63 passed.
    • test_group_document_management.py: 238 passed.
    • test_public_document_management.py: 205 passed.
    • test_group_screening_fixture_parity.py: 35 passed.
    • test_content_screening_upload_only_intake.py: 127 passed.
  • Route policy scripts, re-run at 8fc81b523, all passed:
    • python functional_tests/route_tests/test_route_blueprint_policy_inventory.py: 11/11.
    • python functional_tests/route_tests/test_route_unauthenticated_policy_contract.py: 6/6.
    • python functional_tests/route_tests/test_route_policy_test_coverage.py: 2/2.
  • V2 SPA, in application/v2_ui after npm ci:
    • npm run typecheck: passed.
    • npm run build: passed. The only warning is the existing chunk-size warning.
  • V2 browser suites (python -m pytest <file> -q), run against the built SPA with local Playwright, PYTHONPATH=ui_tests\fixtures and PLAYWRIGHT_SERVICE_URL unset:
    • ui_tests/test_v2_group_document_management.py: passed, 58 passed. This includes the rewritten test_screened_metadata_edit_applies_directly_and_keeps_the_document_available and the new legacy-queued-202 case in test_malformed_metadata_success_never_discards_the_draft.
      • With the two TS files reverted and the SPA rebuilt, the malformed-receipt test fails on legacy-queued-202, because the old explorer accepted the receipt and closed the dialog.
      • The rewritten test passes either way, because the old explorer already handled updated. The parity tests above pin the server's updated receipt.
    • ui_tests/test_v2_public_documents.py: passed, 67 passed.
    • ui_tests/test_v2_personal_document_scope.py: passed, 12 passed.
    • ui_tests/test_v2_group_document_collaboration.py: passed, 33 passed.
    • ui_tests/test_v2_group_documents.py: passed, 30 passed.
    • ui_tests/test_v2_content_screening.py: 35 passed, 1 failed. The failure is test_admin_policy_is_discoverable_and_editable_before_citations_are_enabled, on the admin screening settings page. It fails the same way against a build of the base branch's V2 source.
  • python -m pytest ui_tests/test_content_screening_classic.py -q, run locally with PLAYWRIGHT_SERVICE_URL unset: 46 passed, 3 failed. The failures are the 3 known base failures: test_classic_policy_prerequisites_fail_closed and test_classic_disable_new_scans_preserves_review_holds[True/False].
  • Docs, re-run at 8fc81b523:
    • python scripts/build_docs_inventory.py: the only change to docs/_data/app_surface.yml was line endings, restored with git checkout -- docs/_data/app_surface.yml.
    • python functional_tests/test_docs_app_surface_coverage.py: passed, 7/7.
    • python functional_tests/test_docs_site_quality.py: passed, 6/6.
  • Guardrails over the added lines (66a29bd60..8fc81b523), all passed:
    • python scripts/check_xss_sinks.py --base-sha 66a29bd60 --head-sha HEAD <38 changed files>: 28 files checked.
    • python scripts/check_broken_access_control.py --base-sha 66a29bd60 --head-sha HEAD <38 changed files>: 26 files checked.
    • python scripts/check_swagger_routes.py application/single_app/route_backend_group_documents.py application/single_app/route_backend_public_document_management.py: 2 changed route files checked.

Known limitation: a metadata edit made through the API while a document is still held is replaced by the inspected copy when the document is released. The workspace interfaces don't offer metadata edits on held documents.

Documentation

  • Release notes updated, or not needed: ### **(v0.261.230)** under Breaking Changes, above v0.261.229.
  • Feature documentation updated, or not needed:
    • CONTENT_SCREENING_FRAMEWORK.md
    • GROUP_DOCUMENT_MANAGEMENT_APIS.md
    • PUBLIC_DOCUMENT_MANAGEMENT_APIS.md
    • GROUP_DOCUMENT_COLLABORATION_APIS.md
    • PUBLIC_DOCUMENT_ARTIFACT_APPROVAL_APIS.md
    • DOCUMENT_PROVENANCE.md
    • V2_GROUP_DOCUMENT_MANAGEMENT.md
    • docs/guides/review-screened-documents.md
    • Layer 3 rewrites the full screening scope.
  • Fix documentation updated, or not needed: docs/explanation/fixes/UPLOAD_ONLY_SCREENING_INTAKE_FIX.md.

Security checklist

  • New Flask routes include @swagger_route(security=get_auth_security()): no new routes. The two changed metadata routes keep their decorators and only lose the unreachable 202 branch; check_swagger_routes.py passed.
  • Settings sent to non-admin frontends use sanitize_settings_for_user(): no settings responses changed.
  • Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS: the V2 TypeScript changes are bundled locally by Vite into static/v2. No new dependencies or external scripts.
  • No secrets, keys, connection strings, or local-only artifacts are included. The exemption field is server-managed. Clients can't set it, and is_public_document_field keeps it out of responses.

Paul Lizer (paullizer) and others added 3 commits October 3, 2026 00:16
…rectly

Layer 2 of 4 for microsoft#1621, stacked on microsoft#1628.

- Mark documents SimpleChat generates (agent uploads and published artifacts)
  with a server-managed screening_exemption field set in create_document before
  the initial screening marker is chosen. Exempt documents without a marker are
  never enrolled, prepared, scanned or picked up by scan jobs. User uploads,
  chat uploads saved to a workspace and File Sync are still screened.
- Remove the publication screening reservation and its consume latch. Approval
  admits a destination without a screening marker or with an available release.
- Apply metadata edits and model-generated metadata directly instead of queueing
  a metadata rescan, keep the source-format rename rule, stop comparing the
  metadata fingerprint in release proof and recovery, and stop rewriting a
  screened release blob's metadata on tag edits.
- Bump VERSION to 0.261.230 and update release notes, framework, guide and API
  docs; add the upload-only intake fix document and tests.

Refs microsoft#1621

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… edits

The full functional comparison against the base branch found three tests
that still asserted the removed behavior:

- test_content_screening_history: a release proof that doesn't match the
  content still leaves only the screening status; metadata edited after a
  release now stays available.
- test_group_document_fixture_parity / test_public_document_fixture_parity:
  a released screened document's metadata change returns the ordinary
  updated receipt (200) instead of queued (202).

Also records the base-branch comparison in the fix documentation.

Refs microsoft#1621

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Metadata edits now apply directly, so a metadata save never gets a queued
(202) receipt. This finishes that change in the client and the tests:

- documentOperations.ts accepts a metadata save only when the receipt is
  updated with HTTP 200; any other receipt, including a legacy queued 202
  one, is unconfirmed and keeps the draft.
- DocumentExplorer.tsx drops the queued branch that removed the document
  from the list and showed "Screening is queued"; a confirmed save shows
  "Metadata saved." and the document stays listed and available.
- The group and public metadata routes drop their unreachable 202 branch.
- The group and public UI fixtures no longer build a queued receipt.
- test_queued_metadata_acknowledgement_keeps_saved_content_held_for_screening
  becomes test_screened_metadata_edit_applies_directly_and_keeps_the_document_available,
  and the malformed-receipt test gains a legacy-queued-202 case that fails
  against the old explorer.
- Docs: V2 group document management, the fix doc and the release notes.

VERSION stays 0.261.230.

Refs microsoft#1621

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit 4b5346c into microsoft:paullizer-workspace-knowledge-holds Oct 3, 2026
2 checks passed
Paul Lizer (paullizer) added a commit to paullizer/simplechat that referenced this pull request Oct 3, 2026
Brings in paullizer-workspace-knowledge-holds at 4b5346c, which now
includes the upload-only screening intake (0.261.230). Conflicts:
VERSION stays 0.261.231, and the 0.261.231 release notes sit above
0.261.230 and 0.261.229.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer) added a commit that referenced this pull request Oct 3, 2026
Brings in #1630 (Run as approval only when someone else authored the
revision), which the base branch released as 0.261.229.

Layer 1 also used 0.261.229, so its release notes move into the
v0.261.230 section alongside layer 2 (#1629, already merged into this
branch). Merging this pull request takes the base from 0.261.229 to
0.261.230 and delivers both layers. VERSION stays 0.261.230.

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 3, 2026
Bring in the upload-only intake changes that merged into
paullizer-workspace-knowledge-holds. Conflict resolution:
- config.py: keep VERSION 0.261.232.
- release_notes.md: keep the 0.261.232 section above 0.261.230.
- review-screened-documents.md: keep version 0.261.232 and layer 2's two new
  troubleshooting rows, with the workspace-attachment row from 3b.
- test_content_screening_history.py: Version 0.261.232 with both notes.
- CONTENT_SCREENING_FRAMEWORK.md: scope and documentation-version lines now
  credit 0.261.230 for intake and 0.261.232 for container access.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer) added a commit that referenced this pull request Oct 3, 2026
Leave the framework's documentation-version line and existing coverage
paragraph intact for layer 2 (#1629), and add the 0.261.232 notes beside them.
Fold the guide's new scope sentence into its own section.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) deleted the paullizer-screening-upload-only-intake branch October 3, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant