connect(servicenow): align /connect ServiceNow with SNOW V2 spec - #217
Draft
mukesh4139 wants to merge 46 commits into
Draft
connect(servicenow): align /connect ServiceNow with SNOW V2 spec#217mukesh4139 wants to merge 46 commits into
mukesh4139 wants to merge 46 commits into
Conversation
…c config schema G1: gate oauth2/basic/graph behind SNOW_ALLOW_LEGACY (out of V2 scope, retained as legacy). G2: authType vocabulary -> entra_user/entra_certificate. G11: config.json reshaped to spec v2 (scope, portalBaseUrl, makerPermissions, entra, connections, stepStatus). There is no spoon - only two auth methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…e (G3, G4) G3: new step0-prerequisites.md gates the flow on ENV-001/002, ENV-CAPACITY-001, ESS-SOLN-001 (reused existing checkpoints, mirrors Workday setup); hands off to /setup on failure, re-runs on resume. Dispatcher now enters ServiceNow via step0. G4: step1 1.3b probes Entra admin (Graph role check) and asks the ServiceNow-admin question, persisting makerPermissions. Also finished G2 rename in the connect dispatcher. Know thyself - and thy permissions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…ic handoff (G7, G14, G8, G12) New step5-portal-and-validate.md implements the missing spec Steps 5c/5b/5e/6: G7 sets ServiceNowPortalBaseURI on both packs (SN-BASEURL-001 or attest, + reset-after-update warning); G14 OBO connection-parameter sharing (attest, no auto-validation); G8 drives the live 'Show my HR cases'/'Show my IT tickets' prompts with 401/403/empty/broken-link routing (the true E2E proof); G12 hands topic creation to the topics/create skill. step4 now chains into step5 instead of stopping. Follow the white rabbit to a working link. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…user attest (G9, G10, G13, G15) G9: admin consent promoted to a discrete step 2.2b (SN-ENTRA-CONSENT-001 + escalation to a consent-capable admin). G10: new 2.5b adds the SNI trustedCertificateSubjects manifest requirement for the cert path (SN-ENTRA-CERT-001). G13: security_admin elevation reminder before ServiceNow OIDC work in both auth paths (spec 1.7). G15: SN-SYSUSER-001 attestation + oid->User ID mapping enforced on the cert system user. G5 (manual/attest default vs MCP accelerator) framed inline per ADR 0001 - full MCP rework flagged for review. Wired SN-* checkpoint references into the steps. What is real? The attestation you can verify. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Author
|
@microsoft-github-policy-service agree company="Microsoft" |
…spine Free your mind: ServiceNow follows Neo out of the connect/stepN Matrix and into the setup-orchestrator form Workday already lives in. - setup/servicenow/tasks.md: canonical 7-group checklist (S1-S7) with hidden Step/checkpoint/gate metadata, reusing ENV-*/ESS-SOLN-001 for groups 1-2 and mutually-exclusive auth-path groups 4 (entra_user) / 5 (entra_certificate). - setup/servicenow/SKILL.md: resume-aware router mirroring setup/SKILL.md, with ServiceNow-specific auth-path-variant rendering and attest-only ServiceNow OIDC. Playbooks, FlightCheck servicenow.py, and the connect redirect land in follow-ups. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…nect redirect Complete the ServiceNow /connect migration to the Workday-style setup orchestrator. There is no spoon-fed step sequence anymore — /connect servicenow now delegates to the resume-aware orchestrator. - Add the 7 ServiceNow setup playbooks (S1-S7): environment, ESS install, connection basics (realizes user-presence spec gap SG-1), the two mutually exclusive Entra sign-in paths (entra_user / entra_certificate), extension pack + connection, and validate/handoff. ServiceNow-internal OIDC rows (S4.3/S4.4/S5.3) stay attest-only: the agent never automates them. - Redirect connect/step1.md + connect/SKILL.md ServiceNow branch to the orchestrator; legacy connect/servicenow/step*.md retained behind ALLOW_LEGACY. - Update test_setup_router.py for the new ServiceNow routing and add test_servicenow_router.py to pin the orchestrator wiring against drift. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
There is no going back to the old flow. Now that /connect servicenow delegates entirely to the setup orchestrator, the connect/servicenow/stepN files were an orphaned island — nothing outside the folder routed into them. Delete all 14, matching how the Workday migration removed connect/workday/. - Drops the legacy oauth2/basic/graph auth paths (out of V2 scope). Only entra_user and entra_certificate are supported. - Remove the now-dead ALLOW_LEGACY gate and legacy-playbook handoff from the orchestrator (capture-servicenow-config, tasks) and the connect docs (step1, SKILL); requests for legacy auth now hard-stop with guidance. - Add test_connect_servicenow_monolith_removed to pin the deletion (parallel to the Workday monolith-removed guard). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Free the ServiceNow setup skills 4/5 from their MANUAL-only limbo:
the Entra side of the flow is Microsoft-Graph-only, so it can be
verified programmatically the same way Workday's WD-ENTRA-* checks are.
Adds checks/servicenow_entra.py emitting three checkpoints, each
runnable in isolation via --checkpoint:
* SN-ENTRA-SCOPE-001 — sign-in app exposes user_impersonation,
pre-authorizes the Power Platform ServiceNow connector
(c26b24aa), and requests openid / profile / User.Read.
* SN-ENTRA-CONSENT-001 — tenant-wide admin consent (AllPrincipals
oauth2PermissionGrant) covering those three delegated scopes.
* SN-ENTRA-CERT-001 — the service-account app (App B) holds a
non-expired AsymmetricX509Cert credential; cross-checks the
recorded thumbprint when one is captured.
Unlike Workday's gallery app, the ServiceNow apps are custom
registrations with no applicationTemplateId, so resolution is driven
entirely by the identifiers the playbooks persist
(entra.* / certificate.*), degrading to SKIPPED — never a silent pass —
when they are absent. Registers the specs (clients=GRAPH, no Dataverse),
adds SN-ENTRA to OWNED_PREFIXES for drift protection, wires the category
into the CLI (new servicenowentra scope; folded into servicenow + full),
and persists stable certificate.appBObjectId / appBClientId so the cert
check can resolve App B.
Tests: 29 new cases mirroring test_entra_app.py (result+remediation
phrase assertions); full flightcheck suite green (889). Verified live —
SCOPE + CONSENT PASS against the provisioned tenant, CERT SKIPs when the
certificate path is not provisioned.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The maker who already created the ServiceNow connection during install was still told to hand-wire it to the extension pack's connection reference in Copilot Studio. Binding an already-created connection is not an OAuth flow - it is one Dataverse write of connectionreferences.connectionid. Automate it. - New scripts/bind_connections.py (ServiceNow-only setup action): finds the ServiceNow connection reference, no-ops if already bound, else binds via sibling-reuse or BAP connection discovery. 0/1/many active connections handled; most-recently-created wins and is reported with its owner so the maker can veto. --dry-run and --json supported; best-effort, never crashes. - Wire P6.3 of install-servicenow-extension-pack.md to run the auto-bind before the manual fallback, keying off exit codes (0 bound, 4 create-first, 3 no-reference, 1 error), then re-verify with SN-CONN-001. - 14 unit tests covering helpers and every run() branch. Verified live: correctly resolved the reference and reported already-bound (no mutation) against the maker's environment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The base ServiceNow setup binds the Dataverse connection reference (SN-CONN-001) but never writes the Copilot Studio per-flow invoker connection, so the agent still surfaces ServiceNow as "Not connected". Close that gap: - pp_env_client.py: shared Power Platform env-API client (pac auth) + pure helpers for reading/writing flow invoker connection bindings. - connect_and_share.py: idempotent ServiceNow connect+share action that binds the flow invoker connection and shares connection parameters. - checks/servicenow_flow_binding.py: SN-FLOWCONN-001 verifies the flow invoker binding shows Connected (distinct from the Dataverse reference). - Register SN-FLOWCONN-001 (registry, cli scope map, runner.tenant_id). - Setup playbook: new P6.3c connect+verify step; write flowBinding state. - Unit tests for all three modules. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add programmatic flow activation and reorganize the ServiceNow /connect extension-pack playbook into the order install -> bind -> turn-on-flows -> connect-and-share -> portal (S6.1 through S6.5). - bind_connections.py: bind Dataverse refs too; add --connector all to bind every unbound reference per connector (reuse-only, never mints connections) - connect_and_share.py: re-POST a flow invoker binding when the connector is not Connected (fixes Stale bindings left after a pack install) - activate_flows.py (new): headless 'turn on flows' via workflows PATCH - install_extension_pack.py / pp_env_client.py: programmatic pack install - playbook/tasks/SKILL docs: renumber S6.x to the new step order - tests: add coverage for Dataverse binding, stale re-POST, activation, install Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add an install-mode fork to the ServiceNow /connect playbook. At S6.1 the maker chooses an automated headless install (via install_extension_pack.py, the recommended default) or a manual Copilot Studio install. The choice is persisted to packs.installMode and reused on resume. All later steps (bind connections, turn on flows, connect and share, portal URL) stay automated regardless of install mode. When an automated install leaves no reusable connection, P6.3 already falls back to guided manual connection creation and then continues automated. - P6.2: new mode-choice step; automated (P6.2-A) and manual (P6.2-M) branches - Record S6.1 gate is now prog when automated, manual (with ACK) when manual - tasks.md / SKILL.md updated to describe the fork Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The P6.2 install-mode question enumerated its options as a bulleted list in the Message body, so the agent rendered it as plain chat text and asked the maker to type a choice instead of invoking the question tool (dropdown). Rewrite the mode question to match the convention used by the other picker steps: keep the Message short with the option descriptions as prose, and place the two selectable labels only in the 'Use the question tool with options ...' directive. Add an explicit note not to bullet the choices in the message body. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Two fixes to the headless extension-pack installer: 1. Fail-closed scope selection. servicenow_packages() previously treated an empty/all-false scope as 'install everything', so an automated run fired while scope was unset (or reset) would silently install BOTH the HRSD and ITSM packs when the maker requested only one. It now installs only the products explicitly selected in config scope; no selection resolves to no_targets (exit 4) with a clear message, installing nothing. 2. Heartbeat during the install poll. The install operation is long-running and was polled silently, making a multi-minute install indistinguishable from a hang (and getting killed prematurely). _poll_install/_install_one now emit progress heartbeats to stderr (stdout stays clean JSON), including a note that the install continues server-side even if interrupted. Playbook P6.2-A gains a scope precondition (confirm scope before installing, verify the dry-run target list) and guidance not to kill a running install but to re-check SN-PKG-001 instead. Adds fail-closed regression and heartbeat tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The connect playbook told the agent to run `--checkpoint SN-CONN-001`, `SN-PKG-001`, `SN-FLOW-*`, and `SN-BASEURL-001`, but none of those are registered as standalone checkpoint IDs in this workspace (only SN-FLOWCONN-001, DV-CONN-001, and SN-ENTRA-* are). The unresolved `--checkpoint SN-CONN-001` call caused the S6.2 bind step to stall with an "unknown checkpoint" error instead of running the bind. - S6.2: drop the phantom SN-CONN-001; verify the ServiceNow bind via `bind_connections.py` exit 0 + DV-CONN-001, with live health confirmed by SN-FLOWCONN-001 at S6.4. - S6.1/S6.3/S6.5: read SN-PKG-001 / SN-FLOW-* / SN-BASEURL-001 from `--scope servicenow --no-open` results (their only emit path). - Add a global rule: an "unknown checkpoint" message means the ID is scope-emitted; switch to the scope run and never treat it as a blocker. - Harden resume: always re-run the idempotent P6.3 bind, since a pack reinstall can silently unbind a reference. Update tasks.md and SKILL.md metadata to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…nect chain Add fire-and-poll modes to install_extension_pack.py (--start returns operation ids without blocking; --status polls one cycle) so the playbook can drive a single self-updating status block across install -> bind -> flows -> connect-and-share -> portal, with up-front expectations. Progress on stderr keeps --json stdout clean. P6.2-A reworked; P6.3-P6.6 advance their own row. 33 installer tests green; live-validated --start. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The ServiceNow S6.2 step reused the Workday-family DV-CONN-001, which keys on the Workday pack's hardcoded `..._92b66` Dataverse reference suffix. In a ServiceNow-only environment that reference is absent, so the check reported NotConfigured (a false block) with a nonsensical "install Workday" message. Add SN-DV-CONN-001 in checks/servicenow.py that matches every Dataverse connection reference by connector (shared_commondataserviceforapps) rather than a pack-specific suffix, so it correctly validates the ServiceNow pack's own reference (new_sharedcommondataserviceforapps_*). States: SKIPPED (no token), NotConfigured (no ref), FAILED (unbound/inactive), PASSED (all bound+active, owner echoed), WARNING (query error). A self-contained run_servicenow_dataverse_checks wrapper makes it runnable via --checkpoint; run_servicenow_checks also calls it for scope runs. Register the checkpoint, repoint the S6.2 playbook/tasks/SKILL references from DV-CONN-001 to SN-DV-CONN-001 (keeping a Workday-contrast note), and add unit tests. Workday's DV-CONN-001 is untouched. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
The extension-pack installer derived install targets from `scope` (hrsd/itsm) read out of the root ESS config `.local/config.json`. But `scope` is ServiceNow-specific and is written by setup skill 3 (S3.1) into the ServiceNow connect config `.local/connect/servicenow/config.json` — nothing ever writes it to the root ESS config. So an automated install (S6.1) following the documented flow found no scope and failed closed (no_targets, exit 4), matching what the P6.2-A playbook already states the installer should read. _load_config now overlays `scope` from the ServiceNow connect config on top of the root ESS config (root endpoint + agent schema/persona are unchanged; a root-level scope is honored only as a back-compat fallback). Correct the no_targets remediation to point at the ServiceNow config file. Add tests for the overlay, override, back-compat fallback, and missing-config cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add a real SN-BASEURL-001 flightcheck that verifies the ServiceNow Portal Base URL is set in the pack template config (Dataverse msdyn_employeeselfservicetemplateconfigs, key ServiceNowPortalBaseURI). Previously the playbook referenced this checkpoint but it did not exist in code. Registers it in the checkpoint registry and adds a self-contained wrapper so it runs both via the servicenow scope and via --checkpoint SN-BASEURL-001. Updates P6.6 playbook to reflect the now-real checkpoint. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add connect_state.py, a shared merge-only helper that durably records connect progress into .local/connect/<connector>/config.json. Wire it into the four ServiceNow action scripts so each records its confirmed factual artifact (packs/connections/flows/status) plus the setupStatus step it owns (S6.1-S6.4) on verified success. This keeps a headless or script-first drive resumable even when the agent's row-recording prose steps are skipped. Persistence is best-effort and wrapped so it never changes a script exit code; explicit config merges remain the source of truth for gate evidence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add an optional one-line `note` to each setupStatus step so the durable record can explain what a stage is about. connect_state.record_setup_step now takes a `note` kwarg (merged in, blank omitted, existing note preserved), and the four ServiceNow action scripts pass a description for S6.1-S6.4. The agent-driven path is documented too: checklist-updater.md and config-schema.md describe the optional `note` key (use the checklist item description). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add SN-CONFIG-001, SN-PERM-001 and SN-USER-001 — the S3.1/S3.2/S3.3 gates that capture-servicenow-config.md and tasks.md already drive via `--checkpoint`, but which were never implemented. A faithful resume into skill 3 therefore hit "unknown checkpoint" and stalled. They are config-only (no Dataverse/Graph), read from .local/connect/servicenow/config.json, and are emitted by a self-contained run_servicenow_capture_checks so each is independently runnable before any pack is installed: - SN-CONFIG-001 (prog): valid instance URL, in-scope product, connector, supported sign-in method. - SN-PERM-001 (prog/manual): ServiceNow admin mandatory (FAIL if absent); Entra admin confirmed programmatically PASSES, otherwise MANUAL. - SN-USER-001 (attest): PASSES once userRecord.activeUserConfirmed is set, else MANUAL with the check to run. Not wired into the flow-gated run_servicenow_checks, so scope-run output is unchanged. 13 new tests; live-verified against the current env. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Copilot Studio's "share connection parameters" reads its own
`{schema}.<guid>.shared_service-now` connection reference, which it
creates on demand when a maker shares. connect_and_share.py was patching
the solution-shipped `.cr.<short>` reference instead, so the portal kept
showing ServiceNow as "not shared" even though the API/Dataverse state
was fully bound and connected. Reverse-engineered the portal's actual
POST /connectionreferences request to confirm.
Step 3 now mirrors the portal: find the `.shared_service-now` reference
and update it, or create it (POST) when absent. Idempotent and dry-run
aware; verified live against the create path, the update path, and re-run.
Also resolve the connection id dynamically (BAP discovery -> most
recently created Connected ServiceNow connection), consistent with
bind_connections, instead of trusting a reference's stored connectionid
which can go stale. Falls back to a bound reference only when discovery
is unavailable. Exit-4 now means "no active connection found".
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…red flows
The connect-and-share step configured `.shared_service-now` references only
for ServiceNow invoker flows registered in the agent's get_user_connections.
When a second extension pack is installed on the same agent (e.g. HRSD
alongside ITSM), its solution-shipped `{schema}.{flowId}.shared_service-now`
reference is bound (connectionid set) but its flow may not yet be registered
on the agent, so the share loop skipped it and connectionparametersetconfig
stayed null — Copilot Studio then showed that pack as "not shared".
Share the union of (a) registered invoker flows and (b) every portal-owned
`.shared_service-now` reference already present in Dataverse. This is
fool-proof against agent-registration staleness and mirrors what the portal
reads. Adds flow_id_from_shared_ref_name helper and covering tests.
Verified live end-to-end on ESS-HR-20260726 (install -> bind -> activate ->
connect&share) for both ITSM and HRSD packs; idempotent on re-run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…eckpoints
The connect-and-share pipeline recorded a single combined S6.4 step only when
BOTH the flow-invoker connect and the parameter share succeeded. If sharing
failed after the connect landed, the run errored (exit 1), nothing was recorded,
and a resume re-ran the whole opaque blob with no visibility into which sub-stage
had failed.
Split them into two independent durable checkpoints so a resume continues from
whichever stage failed:
- S6.4 — Connect the flow invoker connection
- S6.5 — Share the connection parameters (NEW)
- S6.6 — Portal Base URL (renumbered from S6.5)
connect_and_share.py now tolerates a share-write failure without discarding the
confirmed connect: it records S6.4, reports share="error", and exits a new code 6
("connected but sharing failed") so a re-run retries only the share (idempotent
connect is skipped). _persist_connect_state records S6.4 and S6.5 independently
off the result's flow_binding/share fields.
Updates the setup playbook (P6.5 connect+share stages, 6-row live status block,
exit-6 handling), SKILL.md, tasks.md, and the flightcheck S6.x comments to match.
Adds tests for the exit-6 partial-failure path and independent stage recording.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Add a per-product `productStatus` block to the connect config so a single agent can install and track both ServiceNow HR (HRSD) and IT (ITSM) side by side, instead of a single flat `setupStatus` shared across products. - connect_state: add record_product_setup_step() writing productStatus.<product>.<step> via deep-merge (siblings preserved). - install_extension_pack: record S6.1 install per in-scope product into productStatus instead of a single flat setupStatus.S6.1. - checklist template: split Group 6 into variant sub-blocks 6a ServiceNow HR (scope.hrsd), 6b ServiceNow IT (scope.itsm), 6c Shared connection (always), mirroring the mutually-exclusive auth groups; product rows tagged product:. - checklist-updater / capture-config / SKILL.md: route product-tagged rows to productStatus, gate product sub-blocks on scope at render time, restore productStatus on resume. - config-schema: document productStatus and the per-product resume model. - tests: per-product persistence + recorder coverage; update wiring test to the new productStatus.<product>.S6.1 location; validate shared-doc Step IDs against the union of Workday + ServiceNow templates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…t/share)
Extend per-product ServiceNow setup to the connect chain so a single agent with
incrementally installed HRSD + ITSM packs gets independent checklist
visibility/resume for S6.2-S6.5, mirroring the existing per-product S6.1/S6.6.
New scripts/pack_catalog.py is the single source of truth for the persona/scope
catalog (moved from install_extension_pack.py, re-imported there for
back-compat) plus a per-product artifact resolver: it maps each in-scope
product to the connection references and cloud flows its installed child
solution owns (solution -> solutioncomponents -> intersect object ids). It also
hosts bot_schema (moved here to avoid an import cycle) and a shared recorder,
record_product_steps, that writes productStatus.<product>.<step> for every
in-scope product whose owned artifacts satisfy a per-step predicate.
Each action script main() now records per-product state after its existing
shared _persist_* call, gated on non-dry-run + connector success:
- bind_connections.py S6.2 predicate: all owned ServiceNow refs bound
(shared Dataverse ref stays flat setupStatus.S6.2)
- activate_flows.py S6.3 predicate: all owned category-5 flows on
- connect_and_share.py S6.4/S6.5 predicate: owns >=1 cloud flow, gated on
the run global connect/share outcome
Recording swallows exceptions so it never changes a script exit code.
Tests: new test_pack_catalog.py and per-product wiring tests in
test_connect_state_wiring.py. 154 passed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
Delete the 5 ServiceNow-only action scripts (install_extension_pack, bind_connections, activate_flows, connect_and_share, pack_catalog) and their tests. Rewrite the skill-6 orchestrator, SKILL.md, and tasks.md so the maker performs install, bind, flow activation, connect, and share by hand: - Install (S6.1), flow activation (S6.3), and Dataverse bind (S6.2) stay verified by read-only flightcheck (SN-PKG-001, SN-FLOW-*, SN-DV-CONN-001). - Connect (S6.4) and share (S6.5) are now maker-attested (gate=attest); the SN-FLOWCONN-001 programmatic check is no longer gated on since nothing establishes its Power Platform token. Scrub deleted-script references from the retained flightcheck check (servicenow_flow_binding / SN-FLOWCONN-001), servicenow.py remediation, registry, pp_env_client, and connect_state docstrings; update the flow-binding test assertions accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d2f2aaa1-f501-45e5-ad8a-3d7951b8e374
…connection-first ordering - provision-power-platform-environment (SN): use ServiceNow config paths and persist S1.2 on capacity FAILED/WARNING so failures land in .local/connect/servicenow/config.json - permission-gate: add GATE_REASON output so callers can handle a delegated admin instead of only halting - provision-servicenow-entra-user / -certificate: on delegated Entra role, hand the admin the full app-registration runbook and resume via the Application (client) ID(s) - install-servicenow-extension-pack + tasks + SKILL: add S6.0 to create the ServiceNow and Dataverse connections before pack install; binding/flows stay after install Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
Install messages (user + certificate paths) now tell the maker the dialog auto-picks the ServiceNow and Dataverse connections created in S6.0, and to only create/change a connection if the right one isn't offered. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
SN-DV-CONN-001 matched every Dataverse connection reference by connector, so an unrelated unbound reference (the base agent's msdyn_Dataverse) would fail this ServiceNow S6.2 step even when the pack's own Dataverse reference was bound and active. Narrow the match to references whose logical name contains 'sharedcommondataserviceforapps' (the pack family), excluding msdyn_Dataverse and other system references. Verified PASS against a live env where msdyn_Dataverse is unbound but the pack reference is bound. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
Turning on flows (S6.3), connecting the flow invoker (S6.4), and sharing the connection parameters (S6.5) act on artifacts the extension pack installs per product (each pack ships and enables its own flows; SN-FLOW-* labels every flow HRSD/ITSM). They were incorrectly mirrored under the shared setupStatus block. Reclassify S6.3/S6.4/S6.5 as per-product so they mirror under productStatus.<product> alongside the pack install (S6.1) and portal URL (S6.6); only the up-front connection creation (S6.0) and the shared bind (S6.2) stay in setupStatus. Restructure tasks.md section 6 into dependency order: 6a create (shared) -> 6b install (per-product) -> 6c shared bind -> 6d flows/invoker/share/portal (per-product). Update config-schema, SKILL resume rule, checklist-updater example, and the install playbook's checkpoint table and P6.4/P6.5 write routing. Also clarify the P6.3b Dataverse-bind message to name the specific unbound reference instead of blaming the pack. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
.local/ runtime state was already ignored, but .vscode/mcp.json was not, leaving it exposed to accidental commits. Local MCP server config can carry tenant-specific URLs/keys and must stay local (per PR self-review guidance). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
The check now validates only the ServiceNow pack's own Dataverse reference (logical name containing 'sharedcommondataserviceforapps') and excludes the base-agent 'msdyn_Dataverse' and other system references. Update the two contract tests to encode this ownership boundary, making them stronger: - all-bound case now includes an UNBOUND msdyn_Dataverse and asserts PASS with 'All 1' (base-agent reference ignored, not a failure); - not-configured case now includes msdyn_Dataverse yet still expects NotConfigured when the pack's own reference is absent, and matches the updated message marker 'sharedcommondataserviceforapps'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
…roduct The SN-BASEURL-001 checkpoint previously passed any absolute http(s) URL present on a product config, so a stale or wrong portal could pass while the run claimed the confirmed URL was deployed. Compare each in-scope product's stored ServiceNowPortalBaseURI against the confirmed portalBaseUrl from the local ServiceNow connect config (host case-insensitive, path exact), report expected-vs-actual per product, and treat a mismatch as FAILED. Falls back to presence/format validation when no confirmed value is recorded. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
… completion The per-product move (S6.1/S6.3/S6.4/S6.5/S6.6) left three prose spots still routing per-product rows through the flat setupStatus, so resume/render/Done could disagree with the durable source of truth. Point all three at the single routing rule in checklist-updater.md: shared rows from setupStatus, per-product rows from productStatus.<product>. - install-servicenow-extension-pack.md Done: S6.3/S6.4/S6.5 are per-product. - SKILL.md checklist render: use the same routing as the resume-point rule. - SKILL.md persist note: durable mirror is setupStatus or productStatus by row. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
_categorize_servicenow_flows checked the 'HR Service' substring first, so a real ITSM flow named e.g. 'ESS HR ServiceNow ITSM Common Orchestrator' matched HRSD via its 'HR ServiceNow' host-agent prefix. The live run then reported all seven flows as HRSD, corrupting per-product completion evidence. Match the explicit product token (ITSM / HRSD) case-insensitively, checking ITSM first, and never treat the host-agent prefix as a product signal. Add tests asserting each SN-FLOW row's [HRSD]/[ITSM] label, a host-prefix regression case, and direct coverage of the categorizer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
The install playbook required SN-PKG-001 to gate S6.1, but nothing emitted it, so the agent improvised install evidence from unrelated flow/config rows and S6.1 was neither deterministic nor auditable. Add _check_pack_install: per-product install verification keyed on each pack's Dataverse template-config scenario records (EXPECTED_TEMPLATE_CONFIGS, the same artifact SN-CFG reads) — all present -> Passed, some -> Failed (partial / reinstall), none -> NotConfigured. Emits a summary SN-PKG-001 (naming each product's state) plus per-product SN-PKG-010 (HRSD) / SN-PKG-020 (ITSM). Wire into the deep run and register run_servicenow_pack_checks as a self-contained --checkpoint SN-PKG-001 so it runs before any flow exists. Update the playbook to verify via --checkpoint SN-PKG-001 and document the per-product evidence contract. Add tests for HR-only, IT-only, both, partial, not-installed, reinstall recovery, token-skip, query-error, and registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
…rifiable P6.6 treated the Portal Base URL step as an attest gate and accepted a maker attestation (ACK=true -> done) even when SN-BASEURL-001 returned Failed, letting a failed programmatic gate complete setup and hiding broken case/ticket links. Rewrite the P6.6 decision as a status-driven state machine consistent with the shared U.2 prog rule: PASSED -> done; FAILED (Dataverse read an empty/malformed/ mismatched value) -> never attest, keep in-progress, show remediation, re-run --checkpoint SN-BASEURL-001 and loop until it passes; NotConfigured -> back to P6.2; attestation allowed ONLY when verification is genuinely unavailable (Skipped/Warning). Update the checkpoint-table and tasks.md S6.6 gate notes to 'prog; attest only if unverifiable, never on Failed'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
…tructive re-runs Adds a supported post-completion 'Add a product' transition to the ServiceNow setup router (HR->Both / IT->Both): preserves completed state, initializes only the newly scoped product, inserts only its group-6 checklist rows, invalidates only cross-product validation (S7.1), and resumes at the new product's install step. Hardens the skill-3 capture P3.3 merge so a re-run applies null/empty first-run defaults only when a key is absent, never clobbering portalBaseUrl, scope flags, or completed status blocks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
Finding #7: G.1's role-not-held path asked 'Do you have this role?' and let 'Yes' flip a proven-absent programmatic result to an attested pass. It also treated Authorization_RequestDenied (query unavailable) as proven absence. Split the two: a successful query returning roles without the required one is proven absence and can only stop (acquire/delegate/re-run, never attest); a query that could not read the directory falls back to the G.2 attestation gate, where attestation-to-pass is legitimate. Reworded the stale G.2 cross-reference to be self-contained. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
S6.0 previously completed on maker attestation alone, so a missing or unhealthy ServiceNow/Dataverse connection was only caught later at the S6.2 bind or S6.4 flow invoker. Add a programmatic checkpoint that reads the Power Platform connection inventory and asserts both the ServiceNow and Dataverse connection objects exist and are Connected: - checks/servicenow.py: _check_connection_objects + self-contained run_servicenow_connection_object_checks wrapper; run in the deep pass. PASSED completes S6.0; NotConfigured (missing) / Failed (unhealthy) block; MANUAL (inventory unreadable) falls back to maker attestation. - registry.py: register SN-CONN-OBJECTS-001 (PP_ADMIN client, HIGH). - Skill docs (SKILL.md, install-servicenow-extension-pack.md, tasks.md): S6.0 is now prog (maker creates, checkpoint verifies), attest only on MANUAL; P6.0 runs the checkpoint and loops until it passes. - Correct the HRSD template config name ServiceNowHRSDGetCasesList -> ServiceNowHRSDGetUserCases in EXPECTED_TEMPLATE_CONFIGS and the pack/ template-config test fixtures. - Add 4 SN-CONN-OBJECTS-001 tests (both-connected, missing, unhealthy, unreadable-inventory). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
Two UX fixes in the extension-pack playbook so the maker isn't asked to do manual work that may be unnecessary: - P6.4 (turn on flows): verify the SN-FLOW-* rows FIRST and only guide the maker to Power Platform when a flow is actually off. When every in-scope flow already passes, complete S6.3 without prompting. - P6.5 (connect + share): bundle the connect-invoker and OBO-share steps into a single instruction block and one 3-option question (both complete / connected-but-not-shared / not-connected). Derive S6.4 and S6.5 from that single bundled answer instead of two separate prompts, while still recording the two attested stages separately for accurate resume. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
Follow-on to the bundled P6.5 connect-and-share step: the end-to-end validation now reads parameterSharing from saved ServiceNow setup state before prompting. When it is already "shared" (or sharing is not applicable for the chosen connector/auth mode), carry that prior attestation forward as S7.1 evidence and skip the sharing question; only ask when the state is unset. A "Yes" answer now persists parameterSharing = "shared" so later resumes don't re-ask. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c98614a8-d171-40ba-9773-2928d36cffe2
mukesh4139
marked this pull request as ready for review
July 30, 2026 17:13
mukesh4139
marked this pull request as draft
July 31, 2026 16:58
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.
What & why
Migrates
/connect servicenowfrom the legacy monolithicconnect/servicenow/stepNflow to the Workday-style setup orchestrator (pattern from #181). ServiceNow was
the last connect skill on the old monolith. Beyond the port, this PR closes the SNOW
V2 spec gaps, adds programmatic FlightCheck verification for every automatable step,
and makes the flow per-product (HRSD + ITSM) and resume-safe.
setup/servicenow/tasks.md(canonical checklist withper-row metadata) +
setup/servicenow/SKILL.md, a resume-aware Message-block router.mutually exclusive Entra sign-in paths (user + certificate), extension pack +
connection, and validate/handoff.
connect/servicenow/step files deleted;step1.md+connect/SKILL.mddelegate to the orchestrator (drops legacy oauth2/basic/graph paths).Limitations in the legacy setup this PR resolves
The monolith had no durable state model or programmatic verification, so several
failure modes could silently pass or wedge the flow:
was invisible. → deterministic
SN-PKG-001per product.connection surfaced only at bind/flow time. → up-front
SN-CONN-OBJECTS-001gate.product after completion wasn't a supported transition and corrupted state. →
per-product
productStatus+ explicit add-product transition (HR→Both, IT→Both).per-product completion rested on wrong evidence. → classify by explicit product token.
role check could both be completed by user attestation. → failed programmatic gates
now stay blocked; attestation only when evidence is genuinely unavailable.
stored value against the maker-confirmed URL per product.
→ verify first, prompt only on real gaps; bundle connect+share; reuse share state in S7.1.
New programmatic verification introduced by this PR
Every automatable setup step is now backed by a
--checkpoint-runnable FlightCheck(read-only; the agent never mutates Entra/ServiceNow). Net-new checkpoints:
SN-CONN-OBJECTS-001(S6.0) ·SN-PKG-001(S6.1) ·SN-DV-CONN-001(S6.2) ·SN-BASEURL-001(S6.6) ·SN-CONFIG-001/SN-PERM-001/SN-USER-001(S3.x) ·SN-ENTRA-SCOPE-001/SN-ENTRA-CONSENT-001/SN-ENTRA-CERT-001(S4–S5) ·SN-FLOWCONN-001.All registered (drift-protected via
OWNED_PREFIXES) and wired into theservicenow/servicenowentraCLI scopes. Custom ServiceNow app registrationsdegrade to SKIPPED (never a silent pass) when not yet provisioned.
Checkpoints and what each verifies
SN-CONFIG-001SN-PERM-001SN-USER-001SN-ENTRA-SCOPE-001SN-ENTRA-CONSENT-001SN-ENTRA-CERT-001SN-CONN-OBJECTS-001SN-PKG-001SN-DV-CONN-001SN-FLOW-*SN-FLOWCONN-001SN-BASEURL-001Tests
Router wiring + monolith-removed guards; per-checkpoint suites (pack install, portal
confirmed-value, flow classification, connection objects, Entra apps). Full FlightCheck
suite green.
Deferred
ServiceNow-internal OIDC provider, system user, and claim mapping stay attestation
rows (no queryable API) and fall back cleanly, so the flow is spec-correct today.