feat(4b): AZURE-profilen leser miljøet sitt, ikke operatørens laptop

Endepunktet løses som første ikke-tomme av vårt eget
PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT og Foundrys injiserte
FOUNDRY_PROJECT_ENDPOINT — vårt vinner, fallbacken lar samme image kjøre
hostet uten ekstra wiring. Presedensen gjelder verdier, ikke deklarasjoner.
Credential velges av samme miljø: AzureCliCredential lokalt,
ManagedIdentityCredential når FOUNDRY_HOSTING_ENVIRONMENT er satt, fordi
containeren ikke har noen Azure CLI. Ikke DefaultAzureCredential — Learns
MAF-veiledning navngir den spesifikke credentialen for å unngå probing.

Load-bearing målt mot hele suiten, fire mutasjoner alle røde + grønn
kontroll: detach credential-valget · presence i stedet for truthiness ·
detach fallbacken · snu presedensen. Fail-fast-testen var vakuøs først —
vårt variabelnavn inneholder det injiserte som delstreng.

De fire åpne azure.yaml-valgene lukket mot de to JSON-skjemaene og ført i
docs/2026-08-13-fase4-azure-yaml-valg.md. Ingen azure.yaml skrevet (4d).

821 passed / 4 skipped. Ruff + format + mypy rene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jk8tauXXAojNKC7Tzq7ziF
This commit is contained in:
Kjell Tore Guttormsen 2026-08-13 22:27:21 +02:00
commit 63eec917d2
7 changed files with 373 additions and 12 deletions

View file

@ -313,6 +313,20 @@ when the seam is detached, so the loop cannot silently degrade into theater.
extension point) via a deterministic, schema-validated manifest that runs *before* the loop is
implemented and exercised against committed fixtures — no bundle has yet been materialized from
a live source.
- **Backends:** a run binds to one of two profiles. `local` (the development default) talks to an
OpenAI-compatible endpoint on loopback and makes no egress. `azure` talks to a Foundry project
and resolves its configuration from the environment *at call time*:
- **Endpoint**`PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` first, then the `FOUNDRY_PROJECT_ENDPOINT`
that Foundry injects into a hosted agent. Ours wins, so exporting it is always decisive; the
injected name is what lets the same image run hosted with no extra wiring. Neither set is a
fail-fast naming both.
- **Credential**`AzureCliCredential` on a developer host (constructing it acquires no token;
`az login` stays your manual step), and `ManagedIdentityCredential` when
`FOUNDRY_HOSTING_ENVIRONMENT` is present, because a hosted container has no Azure CLI and the
platform mints it a dedicated Entra identity instead.
Deployment names are never committed: the role→model map ships `REPLACE-WITH-*` placeholders that
fail fast, and `PORTFOLIO_MODEL_MAP` points at an out-of-tree map that wins over the bundled one.
- **Run:** the `run.py` CLI has **three modes** — a documented partition, since one invocation
cannot exercise every flag:
- **Single-project**`PROJECT_ID --docs-dir <dir>`, plus optional `--bundle-dir`,