Skip to content

[2/5] Add agent-scoped announcement models and authoring client - #270

Open
rebova-microsoft wants to merge 2 commits into
users/rebova/org-announcements-prereleasefrom
users/rebova/org-announcements-review-contracts
Open

rebova-microsoft wants to merge 2 commits into
users/rebova/org-announcements-prereleasefrom
users/rebova/org-announcements-review-contracts

Conversation

@rebova-microsoft

@rebova-microsoft rebova-microsoft commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review boundary: slice 2/5, staged integration

This PR targets users/rebova/org-announcements-prerelease, not main or a release branch. #269 merged into that staging branch on September 18, 2026, and its shared foundation and review fixes are already included in this branch. The diff here is the announcement contracts/client slice, not a resubmission of the foundation.

#271, #272, and #273 remain stacked above this PR. Reviewed slices can accumulate in prerelease, but promotion waits for the complete feature and required integration validation. Merging this slice does not deploy or approve release of Org Announcements.

Description

Add typed announcement inputs/editor drafts and the tenant-and-agent-scoped authoring client. Keep the API boundary explicit so callers cannot silently turn an update into a create or accept a record owned by another agent.

Current diff

  • Require an explicit deployed titleId and verify ownership on canonical responses; do not synthesize a titleId or fall back to tenant-only routes.
  • Preserve canonical save results and structured validation errors, including errors carried by HTTP 200 responses.
  • Allow read-only editable-copy suggestions to retain primary actions needing repair, including Alert chat actions and missing own targets. Keep discriminators, matching target fields, unknown-field rejection, and forbidden persisted metadata strict.
  • Keep the type-aware omission of Standard-only priority and secondary actions for Alerts.
  • Expose Standard priority labels in the model-facing schema: 0 is Important, 1 is Informational, and omission defaults to Informational.
  • Keep action save validity service-owned: parsing working content does not mean a present action is valid for Draft save or Publish. Preserve lifecycle rules and indeterminate-write handling.
  • Keep client/model tests independent of future MCP runtime modules and add their dedicated CI job.

Feature contract

Creates omit the bulletin identifier; updates supply an existing identifier. Canonical ownership remains on the configuration wrapper. Opening repairable content is read-only, not permission to persist invalid actions. API services remain responsible for authorization and action validation.

Stack and dependency

This is slice 2/5. Head: users/rebova/org-announcements-review-contracts. Base: users/rebova/org-announcements-prerelease.

The current head 0c8a65829a77ab79215506085893ba1e26895884 contains the merged #269 foundation at 2898c62824c9cc2649934aa1aad56940e3ab94ed. Only the two announcement-contract commits are above that staging baseline. #271 continues to target this PR's head branch, with #272 and #273 following it.

Bootstrap prerequisite #262 is already inherited through the shared foundation. The future official release target and final promotion baseline remain separate decisions.

Validation

The offline contracts coverage includes repairable action preservation, priority metadata, structural rejection, authoring responses, and existing client behavior. No Graph or announcement MCP runtime modules are required by this slice.

Python 3.11 CI run 35401118877 completed successfully for the current head. It includes the dedicated Org Announcements configuration job alongside the inherited foundation and landing-page coverage. The PR Checks tab shows the latest outcome, including any additional run triggered by a description update.

Local execution previously used Python 3.13.15. No local Python 3.11 or live-service acceptance is claimed.

Review readiness for this slice

Separate release-promotion gates

  • Remaining stack reviews and full-feature integration.
  • Authorized hosted-widget, real-backend, and end-to-end validation; mock acceptance is not backend validation.
  • Final promotion baseline confirmed and temporary CI branch filters plus their matching assertion cleaned up.

This remains staged integration, not release approval, and performs no deployment.

@rebova-microsoft rebova-microsoft left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File-by-file walkthrough of the announcement API client, draft models, and supporting tests. This is an explanatory review only, not code approval or resolution of existing feedback. This PR adds client-side building blocks, not backend endpoints or the MCP announcement runtime.

Comment thread .github/workflows/ci.yml
node extension.test.js
npm run validate

org-announcements:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Give the new client and models their own automated checks.

This adds an Org Announcements job that sets up Python 3.11, installs the development and feature dependencies, and selects the authoring-client and draft-model tests. Keeping those two files explicit lets this layer be checked before the later directory and MCP runtime pieces are added. It describes what CI will run; it is not a deployment step or a statement that the tests have passed.

# both of which are ignored by their own directories' rules. This entry is
# belt-and-braces only: if anything ever drops local state here it must not be
# committed.
.local/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Keep generated and local state out of source control.

The Python entries ignore compiled files, while .local/ is a precaution against accidentally adding local state under this feature folder. The accompanying note explains that this feature should reuse the existing shared authentication caches rather than create another private cache here. These rules only control what Git ignores; they do not create, move, or secure a token cache.

return end is not None and end < now


class OrgAnnouncementsClient(AgentDiscoveryClient):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Keep announcement requests tied to the chosen agent.

This is an API client, not an editor UI or backend endpoint implementation.

  • OrgAnnouncementsClient reuses shared authentication and agent discovery. The tenant comes from the authenticated token; the caller supplies the selected deployed ESS agent's opaque titleId (not necessarily a GUID, and not a role). _collection_path puts both into the intended v1.1 EmployeeAgents(...)/essbulletins route, with no tenant-only fallback.
  • list_bulletins and get_bulletin read saved configurations. _require_config requires the response itself to echo the exact top-level titleId, rejecting missing or mismatched scope rather than filling it in.
  • save_bulletin sends native lower-camel JSON and unwraps {id, config, errors}. HTTP 200 with errors is still a validation failure. Returned IDs are checked, not invented; an ID-bearing request updates, while no ID means create. An uncertain network/gateway failure on create is not automatically replayed: the caller must refresh before retrying to avoid duplicates.
  • transition_bulletin sends only {id, status}, leaving preservation of stored content to the service contract. build_manager_state prepares display data: exclude deleted rows, classify by status/schedule, preserve order, and flag a full 50-item archive window without claiming an archive total.

These client-side checks do not establish that the service routes are deployed. MCP announcement runtime wiring is separate.

return self


class SuggestedBulletinDraft(StrictModel):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Separate a proposed draft from saved announcement data.

This file defines data models and conversion helpers, not UI controls or backend storage.

  • SuggestedBulletinDraft is a partial proposal for a new announcement. It cannot supply an announcement ID, saved status, audit fields, or agent identity. Unknown fields are rejected. Suggested actions must have usable targets; ordinary draft actions may remain incomplete so publish-time completeness stays a backend concern.
  • build_create_draft overlays that proposal onto empty-editor defaults without adding an ID. It maps priority and secondaryAction to the editor's Standard-specific field names. Suggested dates become UTC instants: a date-only start begins that UTC day and a date-only end includes its final millisecond. Ambiguous timezone-free date-times are rejected, while the original proposal is separately retained for retries.
  • build_editor_draft_from_config instead starts from saved content, requires its existing ID, and preserves stored schedule strings. Defaults fill missing editor values; they do not rewrite the stored record.
  • OpenAnnouncementsRequest requires explicit create or edit intent for the editor, with an ID required only for edit and suggestions limited to create. There is no republish opener mode. SaveBulletinRequest represents the full authored content and audience, excluding tenant/agent scope from the HTTP body. Blank dates become absent when serialized with exclude_none=True, rather than invalid date strings.

These contracts prepare data for later runtime wiring; constructing a draft does not save or publish it.

httpx>=0.27.0,<1.0
msal>=1.35.0
pydantic>=2.0,<3.0
-r ../agentconfig_core/requirements.txt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Declare feature dependencies and reuse the shared foundation.

This dependency list includes HTTP requests, authentication, typed model validation, and the MCP library for the feature's eventual runtime. The final include pulls in agentconfig_core requirements, keeping shared authentication/cache support defined in one place instead of duplicating it here. Installing these packages supplies libraries; it does not itself register announcement tools, start a server, or make backend routes available.

Comment thread tests/mcp/_mcp_modules.py
)


def load_org_announcements_client_modules() -> dict[str, ModuleType]:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Load just the announcement pieces these tests need.

The new helper asks the existing isolated importer for only client and drafts. Several feature folders use those same short module names, so the importer loads explicit paths and keeps feature-specific aliases to prevent one feature's tests from accidentally using another feature's client. Limiting this helper to two modules also avoids requiring the directory client, telemetry, or MCP server before those later pieces exist.



@pytest.mark.parametrize("launch_folder", ["agentconfig_landing_page"])
@pytest.mark.parametrize("launch_folder", ["agentconfig_landing_page", "agentconfig_org_announcements"])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Check shared configuration discovery from the new folder.

The existing launch-directory test now also changes into the Org Announcements folder and checks that shared configuration discovery still finds the configured environment. Tenant discovery is replaced with a test function, so this case examines path handling rather than contacting a tenant. The second change checks that the feature's requirements include the shared core. Together, they guard against needing a separate configuration or dependency setup just because a feature launches from a different directory.

return envelope


def test_uses_agent_qualified_v11_routes_with_tenant_from_token(monkeypatch) -> None:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Exercise the API contract without a live announcement service.

These tests use fabricated token claims and an HTTP mock to inspect routes, request bodies, and response handling. They cover token-derived tenant plus explicit agent selection, exact titleId echoes, and no tenant-only fallback. Save cases distinguish field errors inside HTTP 200, malformed responses, mismatched IDs, uncertain creates that must not replay, and updates that can retry. Manager-state cases check schedule/status classification, deleted-row exclusion, ordering, and archive-window counts. This is contract coverage, not evidence that backend routes are deployed.

{"standardSecondaryAction": None},
],
)
def test_suggested_drafts_reject_canonical_and_unknown_fields(forbidden) -> None:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walkthrough — Protect the boundary between suggestions, editor state, and saves.

These model tests check that a suggestion cannot smuggle in saved identity, status, audit fields, or agent scope. They also exercise empty-editor defaults, Standard/Alert differences, action validation, original retry values, and suggested-date normalization. Separate edit cases check that existing identity and schedule strings survive conversion. Save cases check that blank dates disappear from outgoing JSON while real instants remain unchanged. The purpose is to keep helpful prefilling from silently changing stored meaning; no UI interaction or backend save is performed here.

@rebova-microsoft

Copy link
Copy Markdown
Contributor Author

Commit ace1f42 preserves repairable read-only copy content while keeping structural and service-owned write validation intact. It also exposes the Standard priority label mapping in the model-facing schema. The updated contracts are merged forward through the existing stack without rewriting history.

Base automatically changed from users/rebova/org-announcements-review-core to users/rebova/org-announcements-prerelease September 18, 2026 22:20
Add typed announcement drafts and the tenant-and-agent-scoped authoring client. Preserve canonical save results, ownership checks, lifecycle semantics, and indeterminate-write recovery. Keep contract tests independent of the future MCP runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eddd3818-bb74-42d3-bcf3-7e0670a57f27
Retain typed primary actions and incomplete targets in editable copies without relaxing structural, scope, or write constraints. Explain Standard priority labels in the model-facing schema and distinguish parsing from backend action validity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 15e9d51c-c328-48e6-9948-8819f9e57f90
@rebova-microsoft
rebova-microsoft force-pushed the users/rebova/org-announcements-review-contracts branch from ace1f42 to 0c8a658 Compare September 18, 2026 22:20
@rebova-microsoft
rebova-microsoft marked this pull request as ready for review September 18, 2026 22:24
@rebova-microsoft
rebova-microsoft requested a balanced review from Copilot September 19, 2026 00:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Bulletin route validation and wrapper-only save identity handling contain correctness issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

Adds typed Org Announcements contracts and a tenant-and-agent-scoped authoring client for the staged feature stack.

Changes:

  • Adds strict draft, action, schedule, and save-request models.
  • Adds scoped bulletin retrieval, mutation, validation, and manager-state handling.
  • Adds comprehensive contract tests and dedicated Python 3.11 CI coverage.
File Description
.github/​workflows/​ci.yml Adds the Org Announcements test job.
solutions/​ess-maker-skills/​src/​mcp/​agentconfig_org_announcements/​.gitignore Excludes generated and local state.
solutions/​ess-maker-skills/​src/​mcp/​agentconfig_org_announcements/​client.py Implements the scoped authoring client.
solutions/​ess-maker-skills/​src/​mcp/​agentconfig_org_announcements/​drafts.py Defines draft and mutation contracts.
solutions/​ess-maker-skills/​src/​mcp/​agentconfig_org_announcements/​requirements.txt Declares feature dependencies.
tests/​mcp/​_mcp_modules.py Adds isolated client/model loading.
tests/​mcp/​agentconfig_core/​test_tenant_context.py Extends shared-foundation assertions.
tests/​mcp/​agentconfig_org_announcements/​test_authoring_client.py Covers client behavior and scoping.
tests/​mcp/​agentconfig_org_announcements/​test_drafts.py Covers draft and save contracts.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +105 to +106
if "/" in bulletin_id or "\\" in bulletin_id or "?" in bulletin_id:
raise ValueError("bulletinId must not contain path or query separators")
"Org Announcements API returned a different announcement "
"than the one that was updated"
)
return config
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.

4 participants