Re-home agents to a new API base URL through the agent/config response
(backend move or per-tenant EU->US region move) - the lesson from the
2026-07-18 WEU->GWC cutover, where the compiled-in API hostname hard-cut
every in-flight agent (no DNS indirection possible: mTLS breaks behind a
proxy, Flex custom-domain TLS is broken).
Backend (ConfigVersion 33):
- AdminConfiguration.AgentMigrateApiBaseUrl (global) +
AgentMigrateTenantOverridesJson (tenantId->URL; empty value pins a
tenant against the global target; override wins)
- GetAgentConfigFunction.ResolveMigrateTarget serves the validated target;
AgentMigrateServed / AgentMigrateRejected warnings as delivery evidence
- TableConfigRepository Store+Map; admin portal Global Settings round-trip
Agent (kill-switch-mirrored hardening):
- Shared AgentEndpointMigrationRules: https-only, *.azurewebsites.net on a
label boundary, no path/port/userinfo - enforced on BOTH sides
- Live fetch only: RemoteConfigService cache strips the field on write AND
read; EndpointMigration.ResolveTarget re-checks the fetch outcome
- AgentRuntimeConfig.Resolve returns before merge/integrity/bootstrap
cleanup on a pending migration (no spurious IntegrityCheckFailed against
a stale migration stub), Program.RunAgent rebuilds Phase 3+4 against the
new URL - single hop, kill signal wins before and after the hop
Tests: AgentEndpointMigrationTests (backend rules + resolution),
EndpointMigrationTests + cache-hardening strip (agent). Full suites green
(Functions 2992, Agent Core 1822, vitest 557, tsc silent).
Doc: docs/agent/endpoint-migration.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gh attestation verify only passes with the repo's current name
(okieselbach/AutopilotMonitor); the signed workflow identity carries it,
and the old dashed name fails identity matching even though git-remote
redirects still work. Verified against the first attested dry-run ZIP.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Build numbers now come from a public counter blob (buildcounter-v2.txt,
reserve-before-build with ETag-CAS) shared by local build.ps1 and CI, so the
two paths can never mint the same number. The CI step that committed
buildcounter.txt bumps to main is gone; the repo file is now a gitignored
offline fallback for -SkipUpload smoke builds only.
CI parity fixes vs the local build:
- ship SummaryDialog.exe.config in the ZIP (WPF AppContextSwitchOverrides;
without it the dialog dies with 0x8007045A when spawned from SYSTEM)
- verify EXE version == manifest == reserved build number before zipping
- update the AdminConfiguration hash-oracle only on publish_as_stable
CI-built release ZIPs now carry a GitHub Artifact Attestation (Sigstore
keyless provenance, actions/attest-build-provenance@v4).
Doc: docs/agent/build-counter-blob.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Probe enrollment e7dbac7a (24H2) showed Windows writes IsAutoPilotDisabled
(capital P) while the troubleshooting FAQ documents IsAutopilotDisabled —
the missing-event flat evidence lookup was case-sensitive and would have
missed it. Registry value names are case-insensitive by contract, so the
dump dictionary now uses OrdinalIgnoreCase; original casing is preserved
in the emitted payload. Doc notes the casing caveat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In the normal IME deployment model the agent installs only after MDM
enrollment, so TPM attestation and ZTD errors from earlier (retried)
attempts sat invisible in the event log. The tracker now replays
Autopilot-channel Level 1-2 records from the last 4h at startup through
the regular emission pipeline.
Spam containment (answering "spam or backfilled event?" — individual
events, but marked and bounded):
- errors only (Warning-level 100 heartbeat excluded by XPath)
- harmless-ID downgrade + burst rollup still applies to backfilled records
- cap of 25 records per scan, newest first (tail logged, not emitted)
- RecordId watermark persisted to autopilot-error-backfill.json, advanced
by both backfill and live watcher -> restarts never re-emit
- events carry backfilled=true + original timeCreated; the event's own
timestamp is emission time, so backend StartedAt alignment is not
dragged backwards by pre-enrollment records
Docs: Observability matrix updated (TPM row now visible-via-backfill) +
new "Startup error backfill" section. Tests: 5 new (XPath, watermark
roundtrip/monotonicity, backfilled/live markers, rollup-on-backfill);
full V2 suite 1804 green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The agent is normally installed by an IME platform script AFTER MDM
enrollment succeeded, so pre-enrollment failures (enrollment blockers,
TPM attestation) are invisible in the normal flow — no enrollment means
no agent and no authenticable session. Await-enrollment mode does not
help either: the binary only polls for the MDM certificate and cannot
authenticate without it. Only bootstrap-token deployments (one pilot
customer) can report pre-MDM.
- New Observability section in docs/agent/autopilot-ztd-diagnostics.md
with a per-rule visibility matrix (normal / bootstrap / await) and the
follow-up option of backfilling Autopilot-channel error records
- ANALYZE-ENRL-003/004 descriptions state the limitation explicitly so
portal users understand why these rules rarely fire
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A flip-flopping user-scoped WHfB CSP read "disabled" once, the arm-C
completion synthesized HelloOutcome="Skipped" and the session completed
5s later while the user sat inside the Hello wizard that had started
230ms earlier (Shell-Core 62404, previously InformationalEvent-only).
Layer 1 — engine (DecisionCore):
- New DecisionSignalKind.HelloWizardStarted + set-once fact
DecisionState.HelloWizardStartedUtc (additive-nullable, census +
snapshot wired).
- Prevention: centralized HelloSatisfiedForCompletion /
HelloPolicyDisabledWithoutWizard predicates veto the policy-disabled
stand-in at all five Skipped-synthesis sites and in the
completion_waiting prerequisites; the AdvisoryCompletion handler gets
a HelloWizardObservedPromote branch (its fallthrough is
re-arm-or-fail).
- Cure: HandleHelloWizardStartedV1 retracts an engine-synthesized
"Skipped" (exact-case discriminator vs the lowercase tracker
vocabulary; first fact-nulling reducer, stage-agnostic because the
RealmJoin gate-defer parks synthetic skips pre-Finalizing), cancels
finalizing_grace, returns to AwaitingHello and arms HelloSafety.
- Agent path: ShellCoreTracker raises HelloWizardStarted before
FinalizingSetupPhaseTriggered; 62404 included in the startup backfill
(fire-once); coordinator timestamp mirror; adapter emit (fire-once).
Layer 2 — HelloTracker confirmation read:
- ApplyPolicyRead state machine: "disabled" commits only after a second
confirming read (~10s poll); "enabled" commits immediately;
disabled→enabled flags flipFlopDetected=true; pending keeps the
policy-unknown wait cadence. Audit fields confirmedReads /
flipFlopDetected on hello_policy_detected.
Fixtures: signal-kinds/hello-wizard-started-v1.json +
enrollment-sessions/userdriven-hello-wizard-unskip-v1.jsonl (772fe502
ordering, ends Completed with HelloOutcome=completed).
Tests: DecisionCore 462/462, V2.Core 1775/1775.
Docs: OKF concept doc docs/agent/hello-wizard-unskip.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GitHub resolves /-prefixed links against the repo root (missing /docs),
so cross-doc navigation broke. Switched all agent docs to standard
relative links and updated the bundle convention in index.md + CLAUDE.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>