feat(service): protocol ports, validated runtime seeds and typed runtime hook (V8 S1) - #499
Merged
Merged
Conversation
…yped hook t.Port[P] Annotated SkipJsonSchema; ServiceRuntime collaborators as validated ports; runtime seeds t.Port[p.X | None]; settings_type out of the JSON schema; RuntimeBootstrapProvider protocol hook with match-typed resolution (unknown source raises TypeError); dead runtime options and their reads removed after zero-use proof; model_validate replaces model_copy(update=) on runtime assembly; unwrap chains the carried exception. mise.version recorded via make upg; gen fixed point.
Rewrite tests/unit/test_service_bootstrap.py against the public contract: option resolution (none, model passthrough, hook decides settings_type, instance seed wins, context seed), runtime bound to the container context and command bus, ports (real adapter accepted, non-conforming value rejected on construction and assignment, fetch_global of a port service raises, schema lists only data fields, subscripted and concrete port types rejected at class creation). unwrap and .value carry __cause__. Port the flext-edcqq lane's fix for the three unwrap_or(sentinel) test findings byte-identical. Docs: rewrite docs/guides/service-patterns.md for the ADR-019 contract, delete the stale dependency_injector_prompt.md and improvements/dependency-injection-audit.md, align overview.md and dependency-injection-advanced.md, update examples/ex_11. Delete the orphan tests/unit/_golden_public_api.json (no reader since 6fd8045). Projections regenerated by make gen and locks written by make upg with the current flext-infra generator. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ade check Adopt the flext-edcqq lane's introspection repair (uncommitted in ~/fleet-closure-lanes/core-regen/flext-core, branch fix/core-introspection-contract-20260925): PEP 695 alias values resolve through FlextUtilitiesBeartypeTypeAliases with an explicit owner, a deferred alias is a typed m.DeferredAlias instead of a swallowed NameError, and lazy_alias_suffixes lets import failures propagate. This clears the two silent-failure findings in _beartype/_helpers_parts/helpers_part_01.py. The class visitor consults a package's lazy exports only when the package is loaded: the import that created a real class already loaded its package, so a class whose package is not loaded is synthetic and declares no facade. Enforcement no longer imports packages as a side effect. Protocol stubs with a docstring drop the redundant ellipsis (CodeQL py/ineffectual-statement on PR #499). Projections and uv.lock regenerated by make gen with flext-infra b75fd83dd. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
marlon-costa-dc
marked this pull request as ready for review
September 25, 2026 23:47
marlon-costa-dc
added a commit
to flext-sh/flext-infra
that referenced
this pull request
Sep 26, 2026
…on the host make (#867) ## Summary Unblocks every CI that runs `make setup` with the current generator, and makes the superproject CI able to run member gates (V8 S0 / flext-sh/flext#274, beads `flext-x8gn6`, `flext-4jtcb.8`). - **`.WAIT` removed (all repos red at setup).** `_bootstrap_setup_tools` used `.WAIT`, a GNU Make 4.4 token. `make setup` first runs on the runner's GNU Make 4.3, before Mise installs make 4.4.1, and fails with `No rule to make target '.WAIT'`. That failure is in this repo's own tip CI (c298e4e) and in flext-sh/flext-core#499. `.NOTPARALLEL: _bootstrap_setup_tools` keeps the credential check ahead of the Mise pin check on every GNU Make release. - **Members inside a workspace use the workspace runtime (`flext-x8gn6`, operator law 2026-09-24).** Every member renders `MAKE_PROFILE := standalone`, and the `REPOSITORY_ROOT` isolation skipped the physical superproject for that profile. So a submodule member resolved its own `.venv`, and the superproject CI failed all 31 members with 'missing environment interpreter'. `REPOSITORY_ROOT` now always comes from the physical Git superproject; only `GEN_INIT_ONLY` keeps the Makefile's own root. The generated `.envrc` resolves the same root for `VENV_DIR` and the Git and Mise ceilings. A standalone clone or linked worktree has no superproject and keeps its own runtime; measured: `git rev-parse --show-superproject-working-tree` is empty in a linked worktree and returns the workspace for a submodule. - **#866 integration red repaired.** The merge resurrected the rope monkeypatch that 8ece05a retired for the fork `1.14.0+dc.2` (`_rope/pep695_patch.py` plus its protocols and resolvers). It was applied nowhere and failed lint with 14 SLF001 findings; this PR completes the cutover. The fork carries the PEP 695 handlers (measured); the PEP 701 occurrence search is not in the fork and was already inactive. In `nesting_types.py`, a scope without a declaring module now raises (pyrefly bad-argument-type), and the visitor declares `@override` methods (N815, missing-override). ## Validation - New test `tests/unit/codegen/test_codegen_make_member_runtime.py`. A rendered member checked out as a submodule of a real superproject resolves `RUNTIME_ROOT`, `UV_PROJECT` and `RUNTIME_VENV` from the superproject. The same fixture without a superproject keeps its own. - Lane gates: `make gen` exit 0, `make fix` and `make fmt` exit 0, `make check` exit 0 (all gates OK). The base tip failed lint 17 and pyrefly 4. - Runtime proof for `.WAIT` on GNU Make 4.3: this PR's CI setup step on the GitHub runner. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Makes `make setup` run on the host's older GNU Make and makes workspace members share the workspace runtime instead of their own `.venv`, unblocking CI for regenerated repositories and member gates. **Bug Fixes** - Replaced `.WAIT` with `.NOTPARALLEL` so the credential check still precedes the Mise pin check on GNU Make 4.3, which treated `.WAIT` as a missing target. - Submodule members previously resolved their own `.venv`; `REPOSITORY_ROOT` and the generated `.envrc` now resolve the Git superproject runtime, while standalone clones and linked worktrees keep their own. **Refactors** - Completed the cutover away from the retired rope monkeypatch (the fork carries PEP 695 handlers) and removed its protocols and resolvers. - `nesting_types.py` raises when a scope has no declaring module and declares `@override` handlers, fixing lint and pyrefly failures. <sup>Written for commit 0f31573. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/flext-sh/flext-infra/pull/867?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
marlon-costa-dc
added a commit
to flext-sh/flext-infra
that referenced
this pull request
Sep 26, 2026
## Summary `make upg` locks `npm:prettier` platform-independently by clearing `mise_lockfile_platforms`. But the runner still exported `MISE_LOCKFILE_PLATFORMS=` (empty), and mise 2026.9.14 rejects that with `Invalid platform format ''`. So `make upg` failed in every regenerated repository, including the flext-core S1 lane (flext-sh/flext-core#499, which needs `upg` to pick up #867). The variable is now passed only when it names platforms (`${var:+...}`, the idiom the recipe already uses for passthrough variables). The template contract test is updated. ## Validation - Runtime: `make upg` in this repository completes; stage `lock-npm-prettier` exits 0. This PR commits the locks that run refreshed. - `make gen` and `make check` exit 0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes `make upg` failing when locking `npm:prettier` platform-independently. The runner exported an empty `MISE_LOCKFILE_PLATFORMS=`, which mise 2026.9.14 rejects; the variable is now only passed when it names platforms. Bumps mise to 2026.9.14 and refreshes lockfiles. <sup>Written for commit d8c54c5. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/flext-sh/flext-infra/pull/868?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
…NU Make 4.3) make upg pins flext-infra at 8c0c20eca (flext-sh/flext-infra#867 and #868) and the rope fork 1.14.0+dc.2; make gen projects the Makefile without .WAIT, so the CI runner's GNU Make 4.3 can run make setup, and members inside a workspace resolve the workspace runtime. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
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.



Summary
Slice S1 of plan V8 (epic
flext-4jtcb, beadflext-4jtcb.1; ADR-019 proposed in flext-sh/flext#274). It makes the service contract's dependency ports real in the kernel and deletes the dead runtime paths.t.Port[P] = Annotated[P, SkipJsonSchema()]. A service fieldname: t.Port[p.X] = m.Field(exclude=True, ...)is validated byisinstanceon construction and on assignment, and never enters the JSON Schema.FlextService.__pydantic_on_complete__rejects a port type that is not a plain Protocol class (subscripted generic, concrete class) withc.ERR_SERVICE_PORT_TYPE; Pydantic itself rejects a Protocol that is not runtime-checkable.m.ServiceRuntimecollaborators andm.RuntimeBootstrapOptionsare validated ports. Thexseedsruntime_settingsandinitial_contextdropSkipValidationfort.Port[p.X | None];settings_typeandsettings_overridesleave the schema, somodel_json_schema()of any service works and lists only data.u.resolve_runtime_optionsis a typedmatchover an options model or a component: the base'sruntime_bootstrap_optionsis read throughp.RuntimeBootstrapProvider(not declared onx, so no@overridesweep), instance seeds win, any other source raisesTypeError. Nogetattroverlay, no attribute probes, nomodel_copy(update=).u.build_service_runtimeuses the container's context and command bus; a dispatcher that cannot be resolved raises with its cause instead of becomingNone.RuntimeBootstrapOptionsfieldsservices,factories,resources,container_overrides,wire_*,subprojectandregistry;ServiceRuntime.registry(which removes the registry↔runtime cycle);FlextRegistry.configure_runtime,create(runtime=)and the dead_create_initial_runtime;build_registry(runtime=);service_settings_type;_normalize_runtime_override_mapping;resolve_wire_targets;p.CloneableRuntime; the orphantests/unit/_golden_public_api.json.r.unwrap()and.valueraisefromthe carried exception;__exit__is typed.docs/guides/service-patterns.mdis rewritten for the contract. The staledependency_injector_prompt.mdandimprovements/dependency-injection-audit.mdare deleted, andoverview.mdanddependency-injection-advanced.mdare aligned.make genand locks written bymake upgwith the current flext-infra.Validation
unwrap()carries__cause__; the registry builds without recursion.4f367dc0c, run withenv -u VIRTUAL_ENV -u UV_PROJECT_ENVIRONMENT:make genexit 0 twice.make genon the committed candidate leavesgit status --porcelain --untracked-files=allempty (fixed point).make fixandmake fmtexit 0.make check: lint, pyrefly, pyright, mypy (252 s), markdown, security, smells, loc-cap, layout, tier-whitelist, index-declarations, canonical-alias, direnv and deferred-self-reference all at 0. The only red issilent-failure2 (below).make test-fullexit 0: the incremental phase is a testmon cache hit (0 changed files), then 2709 passed with warnings as errors, including the 17 new port and bootstrap tests.silent-failure2 in_utilities/_beartype/_helpers_parts/helpers_part_01.py:33,93(ImportError and NameError branches returning a sentinel). The same findings are in the fleet baseline atc47ea7d79. Their owner is laneflext-edcqq, whose uncommitted refactor (owner-aware alias resolution with new modules) is the fix; it is not portable as a hunk. The threeunwrap_or(sentinel)test findings from the same baseline are fixed here by porting that lane's hunks byte-identically.🤖 Generated with Claude Code
Summary by cubic
Implements slice S1 of the V8 service plan: dependency ports are now real in the kernel, the runtime hook is typed, and dead runtime paths and the registry↔runtime cycle are removed.
New Features
t.Port[P]validates a service dependency withisinstanceon construction and assignment and never leaks it into the JSON Schema; non-Protocol or subscripted port types are rejected at class creation.m.RuntimeBootstrapOptionsandm.ServiceRuntimecollaborators are validated ports;runtime_settingsandinitial_contextseeds dropSkipValidation.u.resolve_runtime_optionsis a typedmatchwhere instance seeds win and unknown sources raiseTypeError.u.build_service_runtimebuilds from the container's context and command bus; unresolvable dispatchers raise with their cause.Result.unwrap()andResult.valueraise with the carried exception as__cause__.Refactors
_beartype.type_aliases+module_source): aTYPE_CHECKING-only alias becomes a typedm.DeferredAlias, enforcementReportexposesreport.completefor deferral-safe inspection,lazy_alias_suffixespropagates import failures, and facade lookup runs only for loaded packages so enforcement never imports packages as a side effect — clearing the twosilent-failurefindings inhelpers_part_01.py.services,factories,resources,container_overrides,wire_*,subproject,registry; plusServiceRuntime.registry,configure_runtime,create(runtime=),build_registry(runtime=),service_settings_type,resolve_wire_targets, andp.CloneableRuntime.service-patterns.mdfor the ADR-019 contract; deleted the stale DI prompt and audit docs and aligned overview/advanced guides.mise.versionandmise.lock;make upgresolves and locks at flext-infra8c0c20ecaand the rope1.14.0+dc.2fork, the projected Makefile drops.WAITso GNU Make 4.3 runsmake setup, workspace members resolve the workspace runtime, and CI passes the token only through BuildKit secrets.Written for commit ebbe6f0. Summary will update on new commits.