Fase 3 (AAA+ på publisert flate). Tre av planens premisser falt på måling og er rettet FØR handling, ikke etterpå: * GOVERNANCE-raden hadde feil tiltak. Planen sa «skriv den»; org-ops D11 sier én kanonisk fil som hvert repo LENKER, og filen er nå publisert (målt: HTTP 200 på open/repo-standard). Å skrive vår egen ville gjort oss til kopi nr. 12 av en fil D11-bølgen holder på å rydde vekk. README lenker den, i samme form som repo-mailbox bruker, og bus-faktor 1 står uttalt i den kanoniske teksten. * Release-objektet for v1.0.0 FINNES allerede på open/ (id 155, CHANGELOG-kropp, siden rendrer) — det som mangler er vedlegg, ikke objektet. * WARN RELEASE-STALE fyrer ikke, og kan ikke: regelen sammenligner utgivelse mot tagg og er strukturelt blind for repo med null utgivelser (org-ops hovedbok #18). Gaten var OK/20 sjekker FØR arbeidet startet, så den kan ikke tjene som verifikasjon for denne fasen. Bevisene er Forgejo-APIet, filinnholdet og ren-klon-kjøringen. A5-defekten rettet: env.template:21 sa at credential resolves via DefaultAzureCredential. Den har aldri gjort det — backends.py:149 konstruerer ManagedIdentityCredential eller AzureCliCredential, og Learns MAF-veiledning navngir den spesifikke credentialen NETTOPP for å unngå probing. En operatør som kopierte templaten ble fortalt at feil identitet ville bli brukt. To load-bearing gater (Iron Law: begge røde før fiksen, 2 failed / 7 passed): 1. env.template navngir de credentials backends.py faktisk konstruerer, og ingen linje utgir DefaultAzureCredential for å være mekanismen. LINJEFORANKRET, ikke delstreng: backends.py NAVNGIR klassen fire ganger i kommentarene som begrunner hvorfor den ikke brukes, så en fil-bred substring-gate ville vært rød på nøyaktig den prosaen den beskytter (repoets 08-09-klasse, fjerde gang). 2. README-ens wheel-filnavn bærer versjonen bygget stempler på fila. Uten den ville en versjonsbump stille etterlatt en publisert install-kommando som peker på en fil som ikke finnes. Hver positiv assert er paret med en KONTROLL på at det søkes etter noe som finnes — en ekstraktor som stille finner null lager en gate som bare kan bli grønn. MUTASJONER MÅLT MOT HELE SUITEN, begge røde på riktig test og på INGEN annen: gjeninnfør den usanne credential-påstanden (2 røde, 844 grønne) · la wheel-filnavnet drifte til 1.0.0 (1 rød, 845 grønne). Restaurert fra scratchpad + shasum -c mellom hver. Bumpen selv var den andre mutasjonen: pyproject 1.0.0 → 1.1.0 gjorde README-gaten rød alene, før README ble rettet. SECURITY.md: varslingsfrist (minst én minor-release og aldri under 30 dager mellom kunngjøring og fjerning, med sikkerhetskritisk fjerning som uttalt unntak). Støttetabellen er bevisst VERSJONSFRI — et release-nummer skrevet der ville drevet ved neste tagg, altså samme defektklasse som gate 2 fanger. CLAUDE.md beholdt på flaten med en engelsk innramming øverst (operatørvalg): den sier hva fila er for en fremmed. Innholdet er repoets sterkeste bevis på at hver beslutning er målt; å fjerne det ville fjernet bevis, ikke friksjon. Versjon 1.1.0 — synket i pyproject, __init__, test_smoke og README-kommandoen. 1.0.0-treet kan ikke produsere en kjørbar wheel (force-include kom etter taggen, målt: git show v1.0.0:pyproject.toml har den ikke), så en wheel hengt på den utgivelsen ville vært nøyaktig den usanne påstanden denne fasen finnes for å fjerne. Operatøren valgte bumpen framfor et vedlegg som ikke virker. 846 passed / 4 skipped (fra 837). ruff + format + mypy rene. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ckyg3Pc6k7FRuR6fDGQLJ
105 lines
6.8 KiB
TOML
105 lines
6.8 KiB
TOML
[project]
|
||
name = "portfolio-optimiser"
|
||
version = "1.1.0"
|
||
description = "Generic framework on Microsoft Agent Framework for per-project cost-savings optimization"
|
||
readme = "README.md"
|
||
requires-python = ">=3.10"
|
||
dependencies = [
|
||
# MAF — GA-pakker, IKKE meta-pakken `agent-framework`. Metaen drar `agent-framework-core[all]`,
|
||
# og `[all]` trekker inn de fortsatt-beta integrasjonene (azure-ai-search/cosmos/ollama/…) som
|
||
# tvinger pre-releases og drar med en ALPHA pydantic. Offisiell guide: installer kun det du trenger.
|
||
# Beta-integrasjoner legges til per-fase (med snevert pre-release-scope) når de faktisk trengs.
|
||
"agent-framework-core>=1.9.0,<2", # kjerne (GA) — to-sidig pin (S2.5): major-bump krever re-verifisering av privat-API-premissene (test_maf_version_guard)
|
||
"agent-framework-foundry>=1.8.2", # Azure/Foundry-profil: FoundryChatClient (GA)
|
||
"azure-identity>=1.25", # S4.1: AzureFoundryBackend passes an explicit AzureCliCredential (Foundry requires it); already transitive via foundry — promoted to a declared direct dep (zero new install weight)
|
||
"agent-framework-openai>=1.8.2", # OpenAI + OpenAI-kompatible lokale endpoints (GA) → lokal profil
|
||
# Promotert dev→core i Fase 2 (MVP-runtime, ikke lenger spike-only):
|
||
"agent-framework-orchestrations>=1.0.0", # GA orchestration builders (GroupChat/Concurrent/Magentic); resolves med core 1.9.0
|
||
"pulp>=2.8", # deterministisk validator-solver; PuLP bundler CBC i wheelen (R2). Installert 3.3.2.
|
||
# PuLP 4.0 vil kreve `pip install pulp[cbc]` + COIN_CMD (Fase-migrasjonsnotat).
|
||
"mcp>=1.28.0", # tynn lokal-mappe MCP-server (Step 7) — GA (resolverte 1.28.0) per Step 1-beslutning
|
||
"anyio>=4.14", # kø-(z): ingest_mcp.py bruker anyio.fail_after direkte (MCP-timeout-stien); allerede transitiv via mcp — promotert til deklarert direkte dep (zero new install weight, resolverte 4.14.0)
|
||
"pydantic>=2.11,<3", # IR/validering (B1) — eksplisitt pin til STABIL 2.x, aldri alpha
|
||
# S3.1: brute-force cosine for the hybrid verdict retriever — MAF-free, offline (D-C).
|
||
# Upper bound is <2.3, NOT <3, and it is load-bearing twice over: numpy 2.3+ requires
|
||
# Python >=3.11 (2.4+ requires >=3.12), so a wider range silently contradicts this project's
|
||
# `requires-python = ">=3.10"`; and numpy 2.3+ stubs use PEP 695 `type` statements, which
|
||
# mypy refuses to parse under `python_version = 3.10` (python/mypy#18701) — breaking
|
||
# `uv run mypy src` outright. Raise both floors together or not at all.
|
||
"numpy>=2.0,<2.3",
|
||
"llm-ingestion-okf", # Door A ingest (§4–§6) — the shared implementation of shared/ingest-spec.md; zero runtime deps, MAF-free (D7)
|
||
"llm-ingestion-guard", # Door A content gate — scans materialized concepts before they reach the bundle; zero runtime deps, MAF-free (D7)
|
||
]
|
||
|
||
# Console entry points — the install surface a fresh clone gets from `uv sync` (P4 pkt. 5).
|
||
# Deliberately TWO, not five: `run` is the framework CLI (three documented modes) and `simulation`
|
||
# is the offline end-to-end proof the README points a newcomer at. `costsim` / `hitl` / `preflight`
|
||
# keep the `python -m` form — they are operator utilities, not the product's front door, and every
|
||
# name here is a name the freeze has to carry. Pinned by tests/test_console_entry_points.py.
|
||
[project.scripts]
|
||
portfolio-optimiser = "portfolio_optimiser.run:main"
|
||
portfolio-optimiser-demo = "portfolio_optimiser.simulation:main"
|
||
|
||
# Distribution channel for the shared ingest library (mirrors portfolio-optimiser-claude,
|
||
# verified in consumer CI): git pin against the public Forgejo repo — reproducible for every
|
||
# consumer, uv.lock pins the exact commit behind the tag. Bump the rev on a new library tag.
|
||
[tool.uv.sources]
|
||
llm-ingestion-okf = { git = "https://git.fromaitochitta.com/open/llm-ingestion-okf.git", rev = "v0.3.2" }
|
||
# Pinned to a TAG, never a floating ref — a security component whose version can move under a
|
||
# `uv sync` is a gate that can stop gating without a local diff (the okf `generated`-literal
|
||
# lesson, measured 2026-08-09). Bumping this is a decision, not maintenance.
|
||
llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", rev = "v0.3.4" }
|
||
|
||
# Dev tooling as a PEP 735 dependency-group (uv includes it by default in `uv sync`/`uv run`),
|
||
# so the documented bare `uv sync` + `uv run pytest` workflow installs it without `--extra`.
|
||
#
|
||
# INTENTIONAL, RECORDED DEVIATION (Fase 2 review F4): the original plan Step 1 authorized only
|
||
# moving `agent-framework-orchestrations` + `pulp` from the dev extra into core. Migrating the
|
||
# dev group from `[project.optional-dependencies]` to this PEP 735 `[dependency-groups]` block
|
||
# went beyond that stated scope, but is KEPT (not reverted) because it is what makes the brief's
|
||
# Success Criterion SC1 ("bare `uv sync` + `uv run pytest`") install dev tooling without
|
||
# `--extra` — reverting would break SC1. Logged here so the deviation is no longer silent.
|
||
[dependency-groups]
|
||
dev = [
|
||
"pytest>=8",
|
||
"ruff>=0.6",
|
||
"mypy>=1.11",
|
||
"pytest-asyncio>=0.24", # MAF orchestrations are async (await workflow.run(...)) — test-only
|
||
]
|
||
|
||
[build-system]
|
||
requires = ["hatchling"]
|
||
build-backend = "hatchling.build"
|
||
|
||
[tool.hatch.build.targets.wheel]
|
||
packages = ["src/portfolio_optimiser"]
|
||
|
||
# Fase 4a: the wheel CARRIES shared/ (spec, persona skill, example bundles) as packaged data —
|
||
# a byte-identical mirror under portfolio_optimiser/_shared/, resolved by shared_root() only when
|
||
# no working tree is present (checkout wins; PORTFOLIO_SHARED_ROOT wins over both). This is what
|
||
# makes an installed wheel — and a container — work without a clone, while shared/ itself stays a
|
||
# pull-only subtree at the repo root. Pinned by tests/test_shared_packaged_data_loadbearing.py.
|
||
[tool.hatch.build.targets.wheel.force-include]
|
||
"shared" = "portfolio_optimiser/_shared"
|
||
|
||
[tool.ruff]
|
||
line-length = 100
|
||
src = ["src", "tests", "spikes"]
|
||
|
||
[tool.pytest.ini_options]
|
||
pythonpath = ["src", "."]
|
||
testpaths = ["tests"]
|
||
asyncio_mode = "auto"
|
||
|
||
[tool.mypy]
|
||
python_version = "3.10"
|
||
|
||
# Third-party libs without bundled type stubs (py.typed) — analysed as untyped, not errors.
|
||
[[tool.mypy.overrides]]
|
||
module = ["pulp.*", "agent_framework_foundry.*", "llm_ingestion_guard.*"]
|
||
ignore_missing_imports = true
|
||
# NOTE on `llm_ingestion_guard`: the override alone would make the seam type-BLIND, not
|
||
# type-safe — every symbol arrives as `Any`, so a field rename upstream would type-check
|
||
# happily and fail at runtime. `ingest.materialize_gated` therefore coerces each value it
|
||
# reads off the guard's result objects to a concrete type at the boundary (the okf precedent),
|
||
# and the seam's behaviour is pinned by tests/test_ingest_content_gate_loadbearing.py.
|