Skip to content

feat(contracts): publish inject trigger request and occurrence contracts - #1436

Merged
Brad-Edwards merged 12 commits into
devfrom
1423-inject-trigger-occurrence-contracts
Oct 13, 2026
Merged

Brad-Edwards merged 12 commits into
devfrom
1423-inject-trigger-occurrence-contracts

Conversation

@doublewhy

@doublewhy doublewhy commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Builds on #1428; review that first. The diff includes its commits until it merges.

ADR-112 accepted external-inject-occurrence/rev1 (EI-01–EI-06 in specs/sdl/external-injects.md), and #1353 published it as semantics only. EI-06 states that no published schema changes, and the compatibility contract names closed, versioned invocation carriers as the first adoption step. No portable carrier exists for an external trigger request or a claimed occurrence: the Orchestrator protocol starts whole plans, and ParticipantInjectOccurrenceAnchor and API-424's ControlInjectEffectModel are participant-specific.

This PR is the first contract slice of #1366. It publishes two draft carriers with pure validators. inject-trigger-request-v1 covers target and content binding, and inject-occurrence-v1 covers the actor-bound authorization context and duplicate handling. Nothing executes an inject, and no runtime route, store, scheduler, operation kind or backend call changes. #1449, stacked on this PR, carries the execution and readback contracts, participant correlation and the backend declaration for the rest of #1366.

The package change moves the research-evidence implementation digest, so this PR also republishes both captures on top of #1428's releases. Outcomes and claim limits are unchanged.

Requirement UIDs

Related Issues

Closes #1423

ADR Impact

  • ADR-112: these carriers implement the EI-01–EI-03 request and claim identities; the ADR text is unchanged.
  • ADR-104: the occurrence reuses OperationAdmissionContext and the scoped (actor, operation kind, client key) retry identity.

Changes

  • raes_contracts/contracts/inject_occurrence.py (new):
    • InjectTriggerRequestModel: request key, fresh occurrence ID, target and run scopes, the pinned orchestration-plan-v1, the canonical orchestration.inject.* address, and one to 256 selected bindings. Each binding names a compiled binding address, the compiled provision.node.* node it binds, a realization instance and its revision.
    • A binding address must equal render_compiled_address("orchestration", "inject-binding", <node>, <inject>), the renderer the compiler uses. Module namespaces put dots inside names, so a suffix check cannot tell orchestration.inject-binding.mod.host.mod.release (inject mod.release) from a binding of inject release; the exact rendering can. Each binding/instance pair is selected once. Pair uniqueness deliberately refines feat(contracts): publish inject trigger request and occurrence contracts (part of #1366) #1423's "an instance can be selected once": EI-01 has each binding name a concrete realization instance, and the contract does not make instance identifiers unique across bindings, so the duplicate it refuses is a repeated pair (ambiguous-instance.json repeats one pair with two revisions). Local validation cannot see the plan, so the reference page leaves resolving the node and binding against the pinned plan to adoption.
    • Closed placement (independent, event, or schedule with script, optional story and a slot), an optional content-bound input_ref with no inline values, and the expected order head.
    • InjectOccurrenceModel: the exact request plus the operation ID and an orchestration admission context for the same target and run. It also carries admission evidence, the expected store revision, an order token whose predecessor is the requested head, the scenario, source and realization pins, and required evidence. Request key, occurrence, operation and slot must be distinct values.
    • require_inject_trigger_retry returns the original claim only for the same actor, key and request commitment. validate_inject_occurrence_claims rejects a reused retry key, occurrence, operation, order position, order predecessor or schedule slot within one bounded target/run claim set. Two claims after the same head would fork the run's order. Head tokens are opaque, so the claim set does not check the head chain itself.
  • inject_occurrence_schema.py, _inject_occurrence_exports.py (new), _exports.py, __init__.py, bundle_runtime.py and versions.py: schema bundle and facade exports through their own manifest. Each schema states its own local-consistency invariant (inject-trigger-request-local-consistency, inject-occurrence-local-consistency), plus the exact-retry and unique-claim x-raes-invariants bindings. _exports.py grows by 2 lines to 485.
  • contracts/schemas/control-plane/inject-{trigger-request,occurrence}-v1.json and their publication entries (draft).
  • Fixtures under contracts/fixtures/control-plane/inject-*: 5 valid (including a participant-free environment trigger), 4 schema-invalid and 7 context-invalid, including namespaced-binding.json.
  • docs/explain/reference/inject-occurrence-contracts.md (new), an API reference section at the end of docs/public/api/contracts.rst, and a pointer in contracts/README.md. The API section sits after "Backend operation supervision" because feat(contracts): publish mixed-backend execution and readback contracts #1442 inserts its own section before it.
  • implementations/python/tests/test_issue_1423_inject_occurrence_contracts.py (new): 46 tests.
  • Research evidence: specification-coverage release 72.0.0 and formal-validation release 73.0.0 (baseline 72.0.0 from refactor(contracts): move runtime-fact exports into their own manifest #1428, no deviations), with revision pins advanced as in refactor(contracts): move runtime-fact exports into their own manifest #1428.

Test Plan

  • Unit tests pass
  • Integration tests pass if applicable
  • Full completion suite required in CI before merge
  • No coverage regression: canonical / coverage-reduce passed in CI run 38120712043 (combined shard and integration coverage against the 90% line floor), and SonarCloud reports 99.5% new-code coverage.

This branch is rebased onto #1428's new head cf93eb4, which is rebased onto dev c474c26 after #1485 published new evidence releases. The four code commits were reapplied without conflicts, and git range-diff shows all four unchanged. Both captures were regenerated at the next free releases above #1428's.

pytest implementations/python/tests/test_issue_1423_inject_occurrence_contracts.py passed (46 passed). It drives the fixture corpus through the published schema and the model and asserts the refusal reason of every context-invalid fixture. It compares the published schemas with schema_bundle() and compiles the participant-free environment. It also imports that environment as module namespace mod through parse_sdl_file and compile_runtime_model, and checks that each of the two compiled bindings is accepted for its own inject and refused for the other. The other tests cover exact retry, conflicting retries, fresh repetition, reused claims (including two claims after the same head) and address families.

Mutation check: each of the 23 validator rules in inject_occurrence.py was disabled in turn, and the module failed every time. The rules are the inject, node, event, schedule-event, script and story address families, the plan pin, the binding rendering, binding/instance pairs, the orchestration kind, the admission target and run, the unrebased order, distinct identities, the claim-set bound and single store, the six claim entries, and the retry actor/key and commitment checks. The binding-rendering and retry-commitment mutations each fail 4 tests; the admission target-and-run, unrebased-order and distinct-identity mutations each fail 2; every other mutation fails 1.

With the previous suffix rule restored in place of the exact rendering, exactly the 3 new namespace tests fail: the namespaced-binding fixture, the node-less orchestration.inject-binding.release case and the namespaced compile test. At e098bac, the previous validators accepted orchestration.inject-binding.mod.host.mod.release and orchestration.inject-binding.release for orchestration.inject.release, and a claim set whose two claims both follow order-head-9.

The new module, the facade, schema-size and schema-coverage tests, the #1360 operation tests, the #1353 design test, test_backend_manifest.py and the three evidence modules passed together: 536 passed and 19 deselected. With -m integration, 19 passed and 536 deselected.

tools/check_generated_schemas.py and tools/check_schema_publication.py --base-rev origin/dev pass, and regeneration rewrote only the two inject schemas. nox -s contracts -- --base-rev origin/dev passed all 14 stages, including generated-schema drift, schema publication, JSON artifact validation and both evidence checkers. Both evidence checker CLIs pass, and the helper's --check reports the evidence as current against #1428's branch.

nox -s docs-local passed (Vale and Sphinx HTML). nox -s lint and make policy passed. nox -s verify-fast-feedback -- --base-rev origin/dev passed every stage it ran, including the directly changed test modules (the new module and the three evidence modules: 255 passed, 19 deselected). It skipped YAML syntax because no YAML changed, and requirement governance because the branch carries no requirement UID or scope file.

All 32 PR checks passed at 2bf8c1a (CI run 38120712043 plus the Docs, bootstrap and CodeQL runs; deploy skipped). CI run 38120712043 has 21 jobs, including canonical / checks, canonical / sonar, sonar and verify. The other passing checks are Docs run 38120711523 (build), bootstrap qualification run 38120711509 (4 jobs), CodeQL run 38120709079 (2 jobs) and its summary check, the title and body guards, and GitGuardian. SonarCloud reports no open issues on this PR and a passing quality gate (new-code coverage 99.5%, duplication 0.0%).

Ground Control Checks

  • Repository policy command passes
  • Pre-push code review and test-quality review: not run for this lane; an independent review ran before the PR was marked ready.

make policy passed locally, with requirement governance skipped as described in the Test Plan.

Traceability

  • IMPLEMENTS: implementations/python/packages/raes_contracts/contracts/inject_occurrence.py, implementations/python/packages/raes_contracts/contracts/inject_occurrence_schema.py (requirement-free)
  • TESTS: implementations/python/tests/test_issue_1423_inject_occurrence_contracts.py
  • Design: ADR-112, specs/sdl/external-injects.md EI-01–EI-03, docs/explain/sdl/issue-1353-external-inject-triggering-preflight.md

Checklist

  • Code follows the project coding standards; ruff format and ruff check pass
  • FM3: the coding standards put retries under FM3. This PR publishes ADR-112's exact-retry rule (require_inject_trigger_retry) and claim-uniqueness rule (validate_inject_occurrence_claims), and ADR-112 is classified FM3. The explicit abstract state-machine model is the EI-02 retry and claim identities and the EI-03 ordered, atomic claim. Its executable projection is admit in test_issue_1353_external_inject_design.py with its retry and slot-conflict tests: an identical request under the same actor and key returns retry without a fresh admission, changed content returns key-conflict, a reused occurrence or schedule slot returns occurrence-conflict or slot-conflict, and a stale head returns stale-or-order and is never rebased. The validators also enforce cross-reference and identity constraints. These are compiled address families, binding addresses equal to the compiler's rendering of their node and the requested inject, unique binding/instance pairs, the admission context's target and run, an order predecessor equal to the requested head, distinct claim identities, and unique claims in one store, including order positions and predecessors. The exact rendering keeps module namespaces from changing which inject a binding serves (coding-standards checklist item 7). Local validation does not resolve bindings against the pinned plan or check the head chain, and the reference page states both limits. The validators add no new state, transition or runtime path, so no new model artifact is needed. The FM2 artifacts are here too: invariants in the reference page and in x-raes-invariants, typed contract coverage and table-driven tests. No executable or runtime adoption is claimed.
  • Published contract schemas regenerated; tools/check_generated_schemas.py and tools/check_schema_publication.py --base-rev origin/dev pass
  • PR title is a Conventional Commit; feat because it adds public contract models and schemas
  • Architectural docs updated: new reference page and API reference section; ADR-112 and the EI specification are unchanged

Documentation

Updated: docs/explain/reference/inject-occurrence-contracts.md, docs/public/api/contracts.rst, contracts/README.md, and both research indexes.

@doublewhy
doublewhy force-pushed the 1423-inject-trigger-occurrence-contracts branch from c9cca42 to 0cc65af Compare October 9, 2026 14:31
@doublewhy
doublewhy marked this pull request as ready for review October 9, 2026 15:43
The facade manifest _exports.py is at 499 of 500 lines. Move the contiguous runtime-fact binding-plane family into _runtime_fact_exports.py and spread it in place, so PUBLIC_EXPORTS keeps identical order and membership and the next contract family fits under the cap.
Specification-coverage release 71.0.0 and formal-validation release 72.0.0 bind the current implementation digest; outcomes and claim limits are unchanged.
Publish draft inject-trigger-request-v1 and inject-occurrence-v1 carriers for ADR-112 external injects: compiled inject and binding selection, closed narrative placement, value-free input, actor-bound orchestration admission, unrebased order, and exact-retry and unique-claim helpers. Nothing executes an inject or changes the runtime.
…n supervision

Keep the end-of-file placement agreed with the parallel mixed-backend contracts work, which inserts its section before the backend operation supervision section.
SonarCloud python:S9073 flags the combined participant-free check; assert each compiled participant family separately.
… order head once

Module namespaces put dots inside node and inject names, so an address
suffix cannot show which inject a binding serves. Each selected binding
now carries its compiled provision.node address, and its address must
equal the compiler's rendering of that node and the requested inject.
A claim set now rejects two claims that follow the same order head. The
fixture corpus test asserts each context-invalid fixture's reason, and
each schema states its own local-consistency invariant.
Specification-coverage release 72.0.0 and formal-validation release 73.0.0 bind the current implementation digest; outcomes and claim limits are unchanged.
@doublewhy
doublewhy force-pushed the 1423-inject-trigger-occurrence-contracts branch from 0cc65af to 2bf8c1a Compare October 11, 2026 07:14
…ct-trigger-occurrence-contracts

# Conflicts:
#	docs/research/formal-semantic-validation/analysis-v73.json
#	docs/research/formal-semantic-validation/bundles/retest-v73.json
#	docs/research/formal-semantic-validation/execution-snapshot-v73.json
#	docs/research/formal-semantic-validation/index.md
#	docs/research/specification-coverage/analysis-v72.json
#	docs/research/specification-coverage/execution-snapshot-v72.json
#	docs/research/specification-coverage/index.md
#	implementations/python/packages/raes_contracts/contracts/__init__.py
#	implementations/python/packages/raes_contracts/contracts/bundle_runtime.py
#	implementations/python/tests/test_formal_semantic_validation.py
#	implementations/python/tests/test_issue_989_versioned_evidence.py
#	implementations/python/tests/test_specification_coverage.py
#	tools/check_specification_coverage.py
#	tools/formal_semantic_validation/_baseline.py
#	tools/formal_semantic_validation/_loading.py
#	tools/formal_semantic_validation/_release_revisions.py
#	tools/formal_semantic_validation/_releases.py
#	tools/formal_semantic_validation/_retest.py
…ct-trigger-occurrence-contracts

# Conflicts:
#	contracts/README.md
#	contracts/schema-publication/entries/inject-occurrence-v1.json
#	contracts/schema-publication/entries/inject-trigger-request-v1.json
#	contracts/schemas/control-plane/inject-occurrence-v1.json
#	contracts/schemas/control-plane/inject-trigger-request-v1.json
#	docs/explain/reference/inject-occurrence-contracts.md
#	docs/public/api/contracts.rst
#	docs/research/formal-semantic-validation/analysis-v76.json
#	docs/research/formal-semantic-validation/bundles/retest-v76.json
#	docs/research/formal-semantic-validation/execution-snapshot-v76.json
#	docs/research/formal-semantic-validation/index.md
#	docs/research/specification-coverage/analysis-v75.json
#	docs/research/specification-coverage/execution-snapshot-v75.json
#	docs/research/specification-coverage/index.md
#	implementations/python/packages/raes_contracts/contracts/_inject_occurrence_exports.py
#	implementations/python/packages/raes_contracts/contracts/inject_occurrence.py
#	implementations/python/packages/raes_contracts/contracts/inject_occurrence_schema.py
#	implementations/python/packages/raes_contracts/versions.py
#	implementations/python/tests/test_formal_semantic_validation.py
#	implementations/python/tests/test_issue_989_versioned_evidence.py
#	implementations/python/tests/test_specification_coverage.py
#	tools/check_specification_coverage.py
#	tools/formal_semantic_validation/_baseline.py
#	tools/formal_semantic_validation/_loading.py
#	tools/formal_semantic_validation/_release_revisions.py
#	tools/formal_semantic_validation/_releases.py
#	tools/formal_semantic_validation/_retest.py
@Brad-Edwards
Brad-Edwards merged commit a391904 into dev Oct 13, 2026
29 checks passed
@Brad-Edwards
Brad-Edwards deleted the 1423-inject-trigger-occurrence-contracts branch October 13, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants