diff --git a/docs/explain/reference/README.md b/docs/explain/reference/README.md index d58f0793a..ec834192d 100644 --- a/docs/explain/reference/README.md +++ b/docs/explain/reference/README.md @@ -24,6 +24,9 @@ themselves normative specifications or ADRs. - [published-schema-coverage.md](published-schema-coverage.md) - The `ASR-501` rule requiring every published schema to carry corpus, formal, or declared coverage evidence +- [published-contract-requirement-tracing.md](published-contract-requirement-tracing.md) + - The rule requiring every published schema to trace to a live governing + requirement through an `IMPLEMENTS → SPEC` link - [assessment-semantics.md](assessment-semantics.md) - Architecture guardrails for `SEM-206` assessment semantics work - [objective-semantics.md](objective-semantics.md) diff --git a/docs/explain/reference/published-contract-requirement-tracing.md b/docs/explain/reference/published-contract-requirement-tracing.md new file mode 100644 index 000000000..4cfaa5bbc --- /dev/null +++ b/docs/explain/reference/published-contract-requirement-tracing.md @@ -0,0 +1,107 @@ +# Published-contract requirement tracing + +Every schema under `contracts/schemas/` is a normative published contract +(`ADR-009` §7). `tools/check_schema_publication.py` proves the inventory and +`tools/check_schema_coverage.py` proves that something exercises each contract. +Neither names the requirement that governs a contract, so a schema could be +published with no owner. `experiment-authoring-input-v1` was linked to `EXP-736` +only after review (issue #675, PR #711). + +`tools/check_contract_requirement_tracing.py` closes that gap. It runs as the +`policy / published contract requirement tracing` stage of `nox -s policy` and +reads the whole published inventory on every run, not the changed-file set. + +## The rule + +A published contract is **traced** when at least one requirement record under +`docs/requirements//requirement.md` whose status is `ACTIVE` or `DRAFT` +carries this link to the contract's exact schema path: + +```markdown +- IMPLEMENTS → SPEC `contracts/schemas//.json` ( published schema) +``` + +- **Only `IMPLEMENTS → SPEC` counts.** `CONSTRAINS`, `DOCUMENTS`, `TESTS`, and + `VERIFIES` links record a relation other than ownership. An `IMPLEMENTS` link + to a schema path typed as `CONFIG`, `SCHEMA`, or `DOCUMENTATION` does not + count as ownership either. Retype it to `SPEC` only where it records that the + requirement owns the contract. Some such links record a narrower relation, + such as the vocabulary or temporal rules one requirement adds to a shared SDL + schema; leave those as they are. +- **`CODE_FILE` links never count.** A module link cannot say which contract a + requirement governs. `raes_contracts/contracts/bundle.py` generates every + published contract and several requirements link it as a `CODE_FILE`, so + accepting module links would mark every contract traced. +- **The path must match exactly.** A link to a document beside the schema, + such as the catalog `contracts/concept-authority/behavioral-relations-v1.json`, + does not trace `contracts/schemas/concept-authority/behavioral-relations-v1.json`. +- **`DRAFT` owners qualify.** Requirement governance admits a `DRAFT` + requirement for implementation work, and the owning requirements of several + contract families, such as the time and participant-budget contracts, are + still `DRAFT`. Requiring an `ACTIVE` owner would force a link to a less + precise requirement. +- **`DEPRECATED` and `ARCHIVED` owners never count.** These are the statuses + `tools/policy/requirement_governance.py` rejects for implementation work. A + contract whose only owner is retired is untraced until a live requirement + owns it. +- **One live owner is enough.** A contract may have several. + +The gate reads requirement records offline through +`RepositoryRequirementClient`, the parser behind the repository requirement +source that `tools/policy/requirement_order.yaml` selects. It makes no Ground +Control request, so it runs on every CI policy run. A record that the parser +rejects, including a malformed traceability line, a symlinked record, or a +directory that is not a canonical requirement UID, fails closed as +`[contract-requirement-record-unreadable]` and owns nothing. + +## The inventory + +The denominator is every `contracts/schemas/**/*.json` file. The contracts lane +already fails when that tree disagrees with `schema_bundle()` +(`tools/check_generated_schemas.py`) or with the publication manifest +(`tools/check_schema_publication.py`), so the gate reads the tree rather than +regenerating the bundle. `test_issue_712_contract_requirement_tracing.py` pins +the three inventories to one set. A removed schema leaves the tree, so its +tombstone needs no owner. + +## Repairing a finding + +`[contract-requirement-trace-missing]` names the schema path that has no live +owner. Add the `IMPLEMENTS → SPEC` line to the Traceability section of the +requirement that governs the contract, in the same pull request that publishes +a new schema. Choose the owner by evidence, strongest first: + +1. A live requirement already records the exact schema path under another link + or artifact type, or links the contract's own catalog or publication record. +2. The pull request or commit that published the schema, or its issue, + declared the requirement. +3. The requirement's statement names the contract's subject. Prefer a + requirement that also links the contract's defining module, tests, or + governing ADR. + +Requirement governance admits the evaluated requirement's exact canonical +record, `docs/requirements//requirement.md`, alongside its phase's mapped +ownership roots. A contract owner can therefore add its traceability link in +the publishing change even when its phase, such as `runtime-core`, does not +list that record. Other requirement records and files still need the phase's +existing ownership permissions. Requirement status, prerequisite order, and +code/test traceability checks still apply. + +When an issue-bound scope file assigns the edited record, bind it to its own +requirement UID. Each assigned owner is checked independently. The self-record +permission applies only to that owner's exact record; other assignments must +satisfy the phase's existing mapped ownership roots. + +Do not link a requirement only to turn the gate green. Do not waive a finding +through `tools/policy/exceptions.yaml` because a repair is inconvenient. The +waiver mechanism exists for a finding that is factually wrong. + +## Reporting + +```console +$ uv run --project implementations/python --frozen \ + python tools/check_contract_requirement_tracing.py --report +``` + +The report lists every published contract with its live owners, marks a +contract without one as `UNTRACED`, and never gates. diff --git a/docs/requirements/ACT-605/requirement.md b/docs/requirements/ACT-605/requirement.md index 8b81ee05a..b446cc229 100644 --- a/docs/requirements/ACT-605/requirement.md +++ b/docs/requirements/ACT-605/requirement.md @@ -34,3 +34,5 @@ Requirement inventory expansion. Realistic scenarios require first-class specifi - TESTS → TEST `implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py` (Autonomous participant durability and conformance coverage) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py` (Autonomous participant runtime compilation) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_behavior/__init__.py` (Autonomous participant semantic validation) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-execution-control-v1.json` (participant-execution-control-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-execution-service-state-v1.json` (participant-execution-service-state-v1 published schema) diff --git a/docs/requirements/ACT-610/requirement.md b/docs/requirements/ACT-610/requirement.md index 13ffb51eb..7ee134e03 100644 --- a/docs/requirements/ACT-610/requirement.md +++ b/docs/requirements/ACT-610/requirement.md @@ -23,7 +23,7 @@ Requirement inventory expansion. Defensive behavior needs its own expressible vo - IMPLEMENTS → GITHUB_ISSUE `210` (Defensive Behavior Vocabularies (ACT-610)) - IMPLEMENTS → CONFIG `contracts/concept-authority/controlled-vocabularies-v1.json` (Governed participant defensive behavior vocabulary) -- IMPLEMENTS → CONFIG `contracts/schemas/concept-authority/nist-csf-defensive-categories-source-v1.json` (NIST CSF defensive category source schema) +- IMPLEMENTS → SPEC `contracts/schemas/concept-authority/nist-csf-defensive-categories-source-v1.json` (NIST CSF defensive category source schema) - IMPLEMENTS → CONFIG `contracts/concept-authority/nist-csf-defensive-categories-source-v1.json` (Pinned NIST CSF 2.0 defensive category source) - IMPLEMENTS → SPEC `contracts/schemas/concept-authority/external-concept-bindings-v1.json` (Generic authored defensive behavior classification contract) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/external_concept_bindings.py` (Neutral NIST CSF source snapshot adapter) diff --git a/docs/requirements/ACT-611/requirement.md b/docs/requirements/ACT-611/requirement.md index 355e123f4..241b51bfb 100644 --- a/docs/requirements/ACT-611/requirement.md +++ b/docs/requirements/ACT-611/requirement.md @@ -30,3 +30,5 @@ Requirement inventory expansion. Scenarios need to describe autonomous system be - IMPLEMENTS → GITHUB_ISSUE `211` (Autonomous Service And Agent Behavior Vocabularies (ACT-611)) - TESTS → TEST `implementations/python/tests/test_external_concept_bindings.py` (External concept binding production-path tests) - TESTS → TEST `tools/check_autonomous_behavior_vocabularies.py` (Autonomous behavior vocabulary source integrity checker) +- IMPLEMENTS → SPEC `contracts/schemas/concept-authority/fipa-communicative-acts-source-v1.json` (fipa-communicative-acts-source-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/concept-authority/w3c-activitystreams-activity-types-source-v1.json` (w3c-activitystreams-activity-types-source-v1 published schema) diff --git a/docs/requirements/ACT-614/requirement.md b/docs/requirements/ACT-614/requirement.md index 8efbfade4..7ff66c469 100644 --- a/docs/requirements/ACT-614/requirement.md +++ b/docs/requirements/ACT-614/requirement.md @@ -91,3 +91,4 @@ Requirement inventory expansion. Participant behavior over time is a first-class - TESTS → TEST `implementations/python/tests/test_issue_898_participant_execution_control.py` (Execution-control regression) - TESTS → TEST `implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py` (Control-plane mutation regression) - TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` (Concurrent reservation regression) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-execution-binding-v1.json` (participant-execution-binding-v1 published schema) diff --git a/docs/requirements/API-402/requirement.md b/docs/requirements/API-402/requirement.md index d443056e7..e0d244b82 100644 --- a/docs/requirements/API-402/requirement.md +++ b/docs/requirements/API-402/requirement.md @@ -57,3 +57,11 @@ Current state: implemented. Portable live-execution contracts are required so in - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_backend_operation_exports.py` (Public operation contract facade exports) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_stubs/manifest.py` (Keep operation supervision opt-in; legacy stub does not advertise an unimplemented provider) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_reference_backend/manifest.py` (Keep operation supervision opt-in for reference emulation) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/backend-operation-capabilities-v1.json` (backend-operation-capabilities-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/backend-operation-control-v1.json` (backend-operation-control-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/backend-operation-request-v1.json` (backend-operation-request-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/backend-operation-response-v1.json` (backend-operation-response-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/evaluation-history-event-stream-v1.json` (evaluation-history-event-stream-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/evaluation-result-envelope-v1.json` (evaluation-result-envelope-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/workflow-history-event-stream-v1.json` (workflow-history-event-stream-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/workflow-result-envelope-v1.json` (workflow-result-envelope-v1 published schema) diff --git a/docs/requirements/API-403/requirement.md b/docs/requirements/API-403/requirement.md index 3a588a328..ee387dcfa 100644 --- a/docs/requirements/API-403/requirement.md +++ b/docs/requirements/API-403/requirement.md @@ -29,3 +29,4 @@ Current state: implemented. A per-target control-plane contract is required so r - CONSTRAINS → SPEC `contracts/schemas/control-plane/workflow-cancellation-request-v1.json` (Workflow Cancellation Request Schema) - TESTS → TEST `implementations/python/tests/test_runtime_control_plane.py` (Runtime Control Plane Tests) - TESTS → TEST `implementations/python/tests/test_runtime_control_plane_api.py` (Runtime Control Plane API Tests) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/workflow-cancellation-request-v1.json` (workflow-cancellation-request-v1 published schema) diff --git a/docs/requirements/API-406/requirement.md b/docs/requirements/API-406/requirement.md index 66a4bc99a..a8b2944fb 100644 --- a/docs/requirements/API-406/requirement.md +++ b/docs/requirements/API-406/requirement.md @@ -29,3 +29,5 @@ Requirement inventory expansion. Participant behavior requires portable external - TESTS → TEST `implementations/python/tests/test_participant_backend_contracts.py` (Participant backend-contract carrier fixture and schema tests) - IMPLEMENTS → CONFIG `contracts/profiles/backend/full-remote-control-plane.json` (Full remote control-plane backend profile API-406 carrier requirements) - TESTS → TEST `implementations/python/tests/test_runtime_conformance.py` (API-406 carrier profile and fixture-suite conformance tests) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-lifecycle-event-v1.json` (participant-lifecycle-event-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-observation-envelope-v1.json` (participant-observation-envelope-v1 published schema) diff --git a/docs/requirements/API-407/requirement.md b/docs/requirements/API-407/requirement.md index a3a695f91..309551236 100644 --- a/docs/requirements/API-407/requirement.md +++ b/docs/requirements/API-407/requirement.md @@ -79,3 +79,5 @@ Requirement inventory expansion. Participant-feature boundaries need to be expli - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py` (Effective native transfer and time-service readback enforcement) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_result.py` (Evidence-bounded outcome settlement) - TESTS → TEST `implementations/python/tests/test_issue_1355_mixed_mapping_time.py` (Missing support, stale time, mapped execution, and evidenced stage witnesses) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/mixed-backend-execution-binding-v1.json` (mixed-backend-execution-binding-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/mixed-backend-stage-report-v1.json` (mixed-backend-stage-report-v1 published schema) diff --git a/docs/requirements/API-408/requirement.md b/docs/requirements/API-408/requirement.md index 7263a2358..c11fe3bc6 100644 --- a/docs/requirements/API-408/requirement.md +++ b/docs/requirements/API-408/requirement.md @@ -30,3 +30,5 @@ Requirement inventory expansion. Participant execution must be observable throug - TESTS → TEST `implementations/python/tests/test_runtime_control_plane.py` (Runtime participant retrieval projection tests) - TESTS → TEST `implementations/python/tests/test_runtime_control_plane_api.py` (HTTP participant retrieval route tests) - TESTS → TEST `implementations/python/tests/test_act_614_temporal_durability.py` (Temporal assessments through history retrieval and durable readback) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-history-view-v1.json` (participant-history-view-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-status-view-v1.json` (participant-status-view-v1 published schema) diff --git a/docs/requirements/API-411/requirement.md b/docs/requirements/API-411/requirement.md index 84e5d3f84..f81841e34 100644 --- a/docs/requirements/API-411/requirement.md +++ b/docs/requirements/API-411/requirement.md @@ -25,3 +25,4 @@ Requirement inventory expansion. Participant-local outcomes need portable extern - DOCUMENTS → SPEC `specs/formal/runtime-contracts/participant-backend-contracts.md` (Participant backend-facing contracts formal design (API-411 outcome reports)) - DOCUMENTS → SPEC `contracts/schemas/participant-runtime/participant-outcome-report-v1.json` (Generated participant outcome report schema) - TESTS → TEST `implementations/python/tests/test_participant_backend_contracts.py` (Participant outcome-report fixture and schema tests) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-outcome-report-v1.json` (participant-outcome-report-v1 published schema) diff --git a/docs/requirements/API-412/requirement.md b/docs/requirements/API-412/requirement.md index 4f497e618..966d47117 100644 --- a/docs/requirements/API-412/requirement.md +++ b/docs/requirements/API-412/requirement.md @@ -27,3 +27,4 @@ Current state: implemented. Processor manifests now publish a processor-specific - DOCUMENTS → DOCUMENTATION `contracts/schemas/README.md` (Published Contract Schemas Overview) - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/runtime-architecture.md` (SDL Runtime Architecture) - CONSTRAINS → SPEC `contracts/schemas/processor-manifest/processor-manifest-v2.json` (Processor Manifest Schema v2) +- IMPLEMENTS → SPEC `contracts/schemas/processor-manifest/processor-manifest-v2.json` (processor-manifest-v2 published schema) diff --git a/docs/requirements/API-417/requirement.md b/docs/requirements/API-417/requirement.md index 86f4c2ecc..5d5721547 100644 --- a/docs/requirements/API-417/requirement.md +++ b/docs/requirements/API-417/requirement.md @@ -17,3 +17,8 @@ The ecosystem shall define plain-data contracts for participant episode initiali ## Rationale Primary-source refresh shows that episode lifecycle handling needs portable external contracts rather than backend-local conventions. + +## Traceability + +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-episode-history-event-stream-v1.json` (participant-episode-history-event-stream-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-episode-state-envelope-v1.json` (participant-episode-state-envelope-v1 published schema) diff --git a/docs/requirements/API-418/requirement.md b/docs/requirements/API-418/requirement.md index 6b981d89b..8232e9505 100644 --- a/docs/requirements/API-418/requirement.md +++ b/docs/requirements/API-418/requirement.md @@ -17,3 +17,10 @@ The ecosystem shall define plain-data contracts for participant budgets, quotas, ## Rationale Primary-source refresh shows that participant budgets and quota consumption need portable reporting surfaces if runs and benchmarks are to remain comparable. + +## Traceability + +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-resource-budget-event-v1.json` (participant-resource-budget-event-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-resource-budget-policy-v1.json` (participant-resource-budget-policy-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-resource-budget-state-v1.json` (participant-resource-budget-state-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-resource-pool-capacity-v1.json` (participant-resource-pool-capacity-v1 published schema) diff --git a/docs/requirements/API-421/requirement.md b/docs/requirements/API-421/requirement.md index be8d63092..2a067bc8e 100644 --- a/docs/requirements/API-421/requirement.md +++ b/docs/requirements/API-421/requirement.md @@ -21,3 +21,6 @@ Independent processors and backends need explicit external declarations for how ## Traceability - DOCUMENTS → SPEC `https://fmi-standard.org/docs/3.0.2/` (FMI 3.0.2 Specification) +- IMPLEMENTS → SPEC `contracts/schemas/time/realized-time-model-v1.json` (realized-time-model-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/time/time-model-v1.json` (time-model-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/time/time-runtime-state-v1.json` (time-runtime-state-v1 published schema) diff --git a/docs/requirements/API-423/requirement.md b/docs/requirements/API-423/requirement.md index 5c3cc7a88..306b44989 100644 --- a/docs/requirements/API-423/requirement.md +++ b/docs/requirements/API-423/requirement.md @@ -73,3 +73,4 @@ API-406 and API-409 provide adjacent carriers, but no common contract records th - IMPLEMENTS → DOCUMENTATION `docs/migration/participant-information-flow-control.md` (Participant information-flow control migration guide) - TESTS → TEST `implementations/python/tests/test_public_docs_policy.py` (Executable participant-control public guide claim example) - DOCUMENTS → DOCUMENTATION `docs/public/participant-control.md` (Participant input and output control guide) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/participant-boundary-flow-policy-v1.json` (participant-boundary-flow-policy-v1 published schema) diff --git a/docs/requirements/ASR-501/requirement.md b/docs/requirements/ASR-501/requirement.md index 2c13c2839..f6e533ac4 100644 --- a/docs/requirements/ASR-501/requirement.md +++ b/docs/requirements/ASR-501/requirement.md @@ -51,3 +51,4 @@ Current state: partially implemented. Checked-in normative schemas exist under c - IMPLEMENTS → CODE_FILE `tools/schema_coverage/_classify.py` (Published inventory to covering-leg join) - TESTS → TEST `implementations/python/tests/test_issue_1330_schema_coverage.py` (Published-schema coverage gate and backfilled contract corpora tests) - IMPLEMENTS → GITHUB_ISSUE `1330` (Issue #1330 detect missing fixture or formal coverage for published schemas) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/raes-semantic-invariants-v1.json` (raes-semantic-invariants-v1 published schema) diff --git a/docs/requirements/ASR-511/requirement.md b/docs/requirements/ASR-511/requirement.md index 165614d30..3c6d18a07 100644 --- a/docs/requirements/ASR-511/requirement.md +++ b/docs/requirements/ASR-511/requirement.md @@ -24,7 +24,7 @@ Requirement inventory expansion. Mature ecosystems need explicit validation-stre - IMPLEMENTS → ADR `docs/decisions/adrs/adr-072-validation-and-admission-profiles.md` (ADR-072 validation and admission profiles) - IMPLEMENTS → SPEC `specs/formal/validation-admission-profiles/README.md` (Validation and admission profiles formal specification) - IMPLEMENTS → CONFIG `contracts/profiles/validation/validation-profile-catalog-v1.json` (Validation profile catalog v1) -- IMPLEMENTS → CONFIG `contracts/schemas/profiles/validation-profile-catalog-v1.json` (Published validation profile catalog schema v1) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/validation-profile-catalog-v1.json` (Published validation profile catalog schema v1) - TESTS → TEST `implementations/python/tests/test_validation_profiles.py` (Validation profile catalog tests) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-258-asr-511-layered-validation-profiles-preflight.md` (ASR-511 layered validation profiles design boundary) - DOCUMENTS → DOCUMENTATION `contracts/README.md` (Validation profile catalog contract documentation) diff --git a/docs/requirements/ASR-515/requirement.md b/docs/requirements/ASR-515/requirement.md index 7bd272fdd..d77911d4b 100644 --- a/docs/requirements/ASR-515/requirement.md +++ b/docs/requirements/ASR-515/requirement.md @@ -24,3 +24,4 @@ Requirement inventory expansion. Consumers need explicit disclosure of what vali - IMPLEMENTS → ADR `docs/decisions/adrs/adr-072-validation-and-admission-profiles.md` (ADR-072 validation and admission profiles) - IMPLEMENTS → SPEC `specs/formal/validation-admission-profiles/README.md` (Validation and admission profiles formal specification) - IMPLEMENTS → SPEC `specs/formal/assurance-fulfillment.yaml` (Assurance fulfillment mapping for validation-admission profiles) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/validation-basis-disclosure-v1.json` (validation-basis-disclosure-v1 published schema) diff --git a/docs/requirements/ASR-516/requirement.md b/docs/requirements/ASR-516/requirement.md index 65215fcf4..55a7eee71 100644 --- a/docs/requirements/ASR-516/requirement.md +++ b/docs/requirements/ASR-516/requirement.md @@ -30,8 +30,8 @@ Retains the RAES conformance responsibility while HUB-6 owns cross-product task - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/_authoring_observations.py` (Production output admission and incumbent evidence projection) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/validators.py` (Authoring conformance model registration) - IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (Bounded conformance delivery ownership with retained prerequisites) -- IMPLEMENTS → DOCUMENTATION `contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json` (Portable vector contract) -- IMPLEMENTS → DOCUMENTATION `contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json` (Portable independent-axis comparison contract) +- IMPLEMENTS → SPEC `contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json` (Portable vector contract) +- IMPLEMENTS → SPEC `contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json` (Portable independent-axis comparison contract) - DOCUMENTS → DOCUMENTATION `docs/explain/reference/authoring-adapter-conformance.md` (Consumer API and finite-evidence boundaries) - TESTS → TEST `implementations/python/tests/test_authoring_adapters.py` (Published positive and negative vectors, evidence mutation, bounds and admission regressions) - TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` (Authoring-vector execution from an installed wheel) @@ -39,3 +39,4 @@ Retains the RAES conformance responsibility while HUB-6 owns cross-product task - TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Source-bound evidence replay after authoring conformance changes) - TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current formal replay with immutable historical captures) - TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Evidence release selection, historical isolation and source integrity) +- IMPLEMENTS → SPEC `contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json` (authoring-adapter-profile-v1 published schema) diff --git a/docs/requirements/ASR-530/requirement.md b/docs/requirements/ASR-530/requirement.md index 0def1cca1..ece70bd66 100644 --- a/docs/requirements/ASR-530/requirement.md +++ b/docs/requirements/ASR-530/requirement.md @@ -95,3 +95,4 @@ Agent-assisted development can produce internally coherent code and documentatio - IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1208-v9.json` (Atomic coverage release 9.0.0 for typed selections) - IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v15.json` (Current formal replay with historical revisions retained) - IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1210-v14.json` (Current source-bound coverage replay) +- IMPLEMENTS → SPEC `contracts/schemas/exploit-path-analysis/exploit-path-analysis-evidence-v1.json` (exploit-path-analysis-evidence-v1 published schema) diff --git a/docs/requirements/ASR-535/requirement.md b/docs/requirements/ASR-535/requirement.md index e5148af3c..fb30d01eb 100644 --- a/docs/requirements/ASR-535/requirement.md +++ b/docs/requirements/ASR-535/requirement.md @@ -45,7 +45,7 @@ ADR-081 governs relation discipline and current tests provide bounded evidence, - TESTS → TEST `implementations/python/tests/test_issue_961_participant_opacity.py` (Bounded participant-opacity assurance integration tests) - IMPLEMENTS → GITHUB_ISSUE `961` (Implement bounded participant-opacity profiles and falsification) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/participant_opacity/_model_check.py` (Explicit-state participant-opacity model checker) -- IMPLEMENTS → CONFIG `contracts/schemas/formal-analysis/participant-opacity-model-check-evidence-v1.json` (Participant-opacity model-check evidence contract) +- IMPLEMENTS → SPEC `contracts/schemas/formal-analysis/participant-opacity-model-check-evidence-v1.json` (Participant-opacity model-check evidence contract) - TESTS → TEST `implementations/python/tests/test_issue_962_participant_opacity_model_check.py` (Finite participant-opacity model-check tests) - IMPLEMENTS → GITHUB_ISSUE `962` (Issue #962 finite participant-opacity model checking) - IMPLEMENTS → GITHUB_ISSUE `963` (Prove participant-opacity relation theorems under declared assumptions) @@ -54,3 +54,7 @@ ADR-081 governs relation discipline and current tests provide bounded evidence, - VERIFIES → PROOF `specs/formal/participant-semantics/participant-opacity-proof-evidence.json` (Participant-opacity proof evidence record) - IMPLEMENTS → GITHUB_ISSUE `1109` (Make the offline Isabelle sandbox portable on Ubuntu) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1109-asr-535-isabelle-sandbox-portability.md` (Pinned proof-runtime allowlist decision) +- IMPLEMENTS → SPEC `contracts/schemas/formal-analysis/participant-opacity-analysis-evidence-v1.json` (participant-opacity-analysis-evidence-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/formal-analysis/participant-opacity-analysis-input-v1.json` (participant-opacity-analysis-input-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/formal-analysis/participant-opacity-model-check-input-v1.json` (participant-opacity-model-check-input-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/behavioral-relation-profile-v1.json` (behavioral-relation-profile-v1 published schema) diff --git a/docs/requirements/DSL-111/requirement.md b/docs/requirements/DSL-111/requirement.md index 96ef35f8c..a7dc699f5 100644 --- a/docs/requirements/DSL-111/requirement.md +++ b/docs/requirements/DSL-111/requirement.md @@ -66,3 +66,6 @@ by the [compatibility contract](../../explain/sdl/external-inject-compatibility. - TESTS → TEST `implementations/python/tests/test_runtime_models.py` (Runtime model tests for inject bindings and orchestration window resolution) - TESTS → TEST `implementations/python/tests/test_runtime_planner.py` (Planner tests for inject, event, script, and story scheduling behavior) - TESTS → TEST `implementations/python/tests/test_sdl_realworld.py` (Real-world scenarios exercising nested entities and timeline orchestration together) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/inject-trigger-request-v1.json` (inject-trigger-request-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/inject-occurrence-v1.json` (inject-occurrence-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/inject-occurrence-outcome-v1.json` (inject-occurrence-outcome-v1 published schema) diff --git a/docs/requirements/DSL-114/requirement.md b/docs/requirements/DSL-114/requirement.md index 277a23daf..74d1dc66e 100644 --- a/docs/requirements/DSL-114/requirement.md +++ b/docs/requirements/DSL-114/requirement.md @@ -34,3 +34,6 @@ Requirement inventory expansion. The author-facing language needs an explicit su - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/external_concept_bindings.py` (Portable external concept binding contract) - IMPLEMENTS → SPEC `contracts/schemas/concept-authority/external-concept-bindings-v1.json` (Published external concept bindings schema) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#104` (External knowledge reference surface (DSL-114)) +- IMPLEMENTS → SPEC `contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-input-v1.json` (sdl-candidate-synthesis-input-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-profile-v1.json` (sdl-candidate-synthesis-profile-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json` (sdl-candidate-synthesis-record-v1 published schema) diff --git a/docs/requirements/DSL-142/requirement.md b/docs/requirements/DSL-142/requirement.md index 66fd24c32..e63eb3745 100644 --- a/docs/requirements/DSL-142/requirement.md +++ b/docs/requirements/DSL-142/requirement.md @@ -94,3 +94,4 @@ backend execution, delivery or observation. - TESTS → TEST `implementations/python/tests/test_dsl_142_participant_inject_delivery.py` (DSL-142 participant inject delivery tests) - IMPLEMENTS → SPEC `contracts/schemas/sdl/instantiated-scenario-v1.json` (DSL-142 governed instantiated scenario schema) - TESTS → TEST `implementations/python/tests/test_sdl_catalog_parity.py` (DSL-142 SDL catalog parity tests) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/inject-occurrence-correlation-v1.json` (inject-occurrence-correlation-v1 published schema) diff --git a/docs/requirements/EXP-707/requirement.md b/docs/requirements/EXP-707/requirement.md index 650c00e6b..1edb0c3ef 100644 --- a/docs/requirements/EXP-707/requirement.md +++ b/docs/requirements/EXP-707/requirement.md @@ -29,3 +29,5 @@ Requirement inventory expansion. Experiment data capture requirements are distin - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/inputs.py` (Digest-bound capture specification admission) - TESTS → TEST `implementations/python/tests/test_sce_002_trial_compiler.py` (Capture specification identity and backend admission tests) - TESTS → TEST `implementations/python/tests/test_issue_1401_media_type_coherence.py` (Required media declarations reject unprovable encodings and accept content-provable JSON encodings) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/observation-demand-v1.json` (observation-demand-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/experiment-core/experiment-capture-spec-v1.json` (experiment-capture-spec-v1 published schema) diff --git a/docs/requirements/EXP-709/requirement.md b/docs/requirements/EXP-709/requirement.md index 3de46f74f..da33bcf7b 100644 --- a/docs/requirements/EXP-709/requirement.md +++ b/docs/requirements/EXP-709/requirement.md @@ -25,3 +25,4 @@ Requirement inventory expansion. Derived evaluations must stay distinct from the - DOCUMENTS → SPEC `specs/formal/experiment-core/README.md` (Experiment Core Formal Specification) - TESTS → TEST `implementations/python/tests/test_runtime_contracts.py` (Experiment derived measure conformance and rejection tests) - IMPLEMENTS → GITHUB_ISSUE `88` (Experiment evidence & measures (EXP-707, EXP-708, EXP-709, EXP-715)) +- IMPLEMENTS → SPEC `contracts/schemas/experiment-core/experiment-derived-measure-v1.json` (experiment-derived-measure-v1 published schema) diff --git a/docs/requirements/EXP-719/requirement.md b/docs/requirements/EXP-719/requirement.md index e92838467..c646b1013 100644 --- a/docs/requirements/EXP-719/requirement.md +++ b/docs/requirements/EXP-719/requirement.md @@ -18,3 +18,7 @@ The ecosystem shall support first-class treatment, factor, condition, and contro ## Rationale Requirement inventory expansion. Designed variation is a distinct experiment concern and should not be left implicit inside ad hoc run metadata. + +## Traceability + +- IMPLEMENTS → SPEC `contracts/schemas/experiment-core/experiment-binding-descriptors-v1.json` (experiment-binding-descriptors-v1 published schema) diff --git a/docs/requirements/EXP-733/requirement.md b/docs/requirements/EXP-733/requirement.md index 9c4245e32..919159ee6 100644 --- a/docs/requirements/EXP-733/requirement.md +++ b/docs/requirements/EXP-733/requirement.md @@ -24,3 +24,4 @@ Current state: identified gap. If the same scenario can be driven by different p - TESTS → TEST `implementations/python/tests/test_runtime_contracts.py` (Participant provenance schema bundle tests) - DOCUMENTS → ADR `ADR-041` (Participant Implementation Manifest and Provenance Surface) - IMPLEMENTS → SPEC `contracts/schemas/participant-implementation-provenance/participant-implementation-provenance-v1.json` (Participant implementation provenance v1 JSON Schema) +- IMPLEMENTS → SPEC `contracts/schemas/participant-implementation-configuration/participant-configuration-result-v1.json` (participant-configuration-result-v1 published schema) diff --git a/docs/requirements/GOV-903/requirement.md b/docs/requirements/GOV-903/requirement.md index 3589f7bf9..ac4120f19 100644 --- a/docs/requirements/GOV-903/requirement.md +++ b/docs/requirements/GOV-903/requirement.md @@ -104,3 +104,5 @@ Requirement inventory expansion. Evolution requires explicit migration and upgra - IMPLEMENTS → ADR `docs/decisions/adrs/adr-095-participant-decision-epoch-state-cut-and-delivery-semantics.md` (Amended current lineage authority for issue #1210) - TESTS → TEST `implementations/python/tests/test_issue_1076_compute_realization.py` (Current snapshot identity and legacy VM spelling join integrity) +- IMPLEMENTS → SPEC `contracts/schemas/provenance/sdl-lineage-ledger-v1.json` (sdl-lineage-ledger-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/sdl-semantic-migration-context-v1.json` (sdl-semantic-migration-context-v1 published schema) diff --git a/docs/requirements/GOV-913/requirement.md b/docs/requirements/GOV-913/requirement.md index cd34e104a..1ba175bdd 100644 --- a/docs/requirements/GOV-913/requirement.md +++ b/docs/requirements/GOV-913/requirement.md @@ -189,3 +189,4 @@ Requirement inventory expansion. Reusable ecosystem assets need explicit trust a - TESTS → TEST `implementations/python/tests/test_issue_1205_repository_governance.py` (Retained contract ownership checks without the retired tooling-inventory exception) - IMPLEMENTS → SPEC `specs/formal/participant-semantics/participant-opacity-proof-evidence.json` (Replayed proof evidence bound to the current selected-input and private installer sources) +- IMPLEMENTS → SPEC `contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json` (associated-artifact-manifest-v1 published schema) diff --git a/docs/requirements/GOV-918/requirement.md b/docs/requirements/GOV-918/requirement.md index f07477679..7e362e487 100644 --- a/docs/requirements/GOV-918/requirement.md +++ b/docs/requirements/GOV-918/requirement.md @@ -59,3 +59,4 @@ Portability breaks down when SDL, processor manifests, backend manifests, and re - TESTS → TEST `implementations/python/tests/test_concept_authority_governance.py` (Concept-authority governance gate tests (failure modes, boundaries, catalog error paths)) - DOCUMENTS → ADR `docs/decisions/adrs/adr-062-concept-authority-catalog-governance-gate.md` (ADR-062: Concept-Authority Catalog Governance Gate) - IMPLEMENTS → GITHUB_ISSUE `496` (Issue #496: Concept authority governance gate — new family requires ADR linkage (review CA-6)) +- IMPLEMENTS → SPEC `contracts/schemas/concept-authority/semantic-projection-report-v1.json` (semantic-projection-report-v1 published schema) diff --git a/docs/requirements/GOV-920/requirement.md b/docs/requirements/GOV-920/requirement.md index 63368f8a5..b3e501357 100644 --- a/docs/requirements/GOV-920/requirement.md +++ b/docs/requirements/GOV-920/requirement.md @@ -27,3 +27,8 @@ Interoperability requires more than nominal support for a schema. Shared semanti - TESTS → TEST `implementations/python/tests/test_runtime_contracts.py` (Semantic profile schema publication tests) - DOCUMENTS → SPEC `specs/concept-authority/semantic-profiles.md` (Shared semantic profiles specification) - DOCUMENTS → DOCUMENTATION `docs/explain/reference/shared-concept-model.md` (Shared semantic profile design guidance) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/domain-profile-admission-policy-v1.json` (domain-profile-admission-policy-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/domain-profile-binding-v1.json` (domain-profile-binding-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/domain-profile-definition-v1.json` (domain-profile-definition-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/domain-profile-resolution-context-v1.json` (domain-profile-resolution-context-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/domain-profile-support-declaration-v1.json` (domain-profile-support-declaration-v1 published schema) diff --git a/docs/requirements/GOV-942/requirement.md b/docs/requirements/GOV-942/requirement.md index 32b8c1cdf..b0b13c4f6 100644 --- a/docs/requirements/GOV-942/requirement.md +++ b/docs/requirements/GOV-942/requirement.md @@ -35,3 +35,6 @@ Academic review and range integration require a reproducible, machine-checkable - TESTS → TEST `implementations/python/tests/test_behavioral_relations.py` (Behavioral-relation catalog, binding, and counterexample tests) - TESTS → TEST `implementations/python/tests/test_behavioral_relation_claims.py` (Behavioral-relation claim policy tests) - IMPLEMENTS → GITHUB_ISSUE `747` (Define behavioral-relation taxonomy and prevent conformance/equivalence conflation) +- IMPLEMENTS → SPEC `contracts/schemas/concept-authority/behavioral-relations-v1.json` (behavioral-relations-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/scientific-completeness-assessment-v1.json` (scientific-completeness-assessment-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/profiles/scientific-completeness-taxonomy-v1.json` (scientific-completeness-taxonomy-v1 published schema) diff --git a/docs/requirements/RUN-301/requirement.md b/docs/requirements/RUN-301/requirement.md index 30d24c81e..d76d1669e 100644 --- a/docs/requirements/RUN-301/requirement.md +++ b/docs/requirements/RUN-301/requirement.md @@ -25,3 +25,4 @@ Current state: implemented. A concrete instantiated scenario is needed so later - CONSTRAINS → SPEC `contracts/schemas/sdl/scenario-instantiation-request-v1.json` (Scenario Instantiation Request Schema) - CONSTRAINS → SPEC `contracts/schemas/sdl/instantiated-scenario-v1.json` (Instantiated Scenario Schema) - TESTS → TEST `implementations/python/tests/test_runtime_planner.py` (Runtime Planner Tests) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/scenario-instantiation-request-v1.json` (scenario-instantiation-request-v1 published schema) diff --git a/docs/requirements/RUN-303/requirement.md b/docs/requirements/RUN-303/requirement.md index 9b057f58f..ec6ebe349 100644 --- a/docs/requirements/RUN-303/requirement.md +++ b/docs/requirements/RUN-303/requirement.md @@ -34,3 +34,6 @@ Current state: implemented. Planning needs normative semantics so lifecycle deci - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1103-bounded-dependency-cycle-detection-preflight.md` (Iterative graph-traversal decision and compatibility boundary) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/semantics/planner.py` (Iterative strongly-connected dependency analysis) - TESTS → TEST `implementations/python/tests/test_semantics_planner.py` (Cycle-oracle, long-chain, and deterministic-order regressions) +- IMPLEMENTS → SPEC `contracts/schemas/plans/evaluation-plan-v1.json` (evaluation-plan-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/plans/orchestration-plan-v1.json` (orchestration-plan-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/plans/provisioning-plan-v1.json` (provisioning-plan-v1 published schema) diff --git a/docs/requirements/RUN-311/requirement.md b/docs/requirements/RUN-311/requirement.md index 136cd2be8..ef9de2920 100644 --- a/docs/requirements/RUN-311/requirement.md +++ b/docs/requirements/RUN-311/requirement.md @@ -29,3 +29,5 @@ Primary-source refresh shows that participant-supporting runtimes commonly depen - TESTS → TEST `implementations/python/tests/test_runtime_registry.py` (Registry shape validation for participant_runtime field) - IMPLEMENTS → GITHUB_ISSUE `599` (Issue 599 participant implementation action admission binding) - IMPLEMENTS → PULL_REQUEST `617` (PR 617 participant implementation action admission binding) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-episode-history-event-stream-v1.json` (participant-episode-history-event-stream-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-episode-state-envelope-v1.json` (participant-episode-state-envelope-v1 published schema) diff --git a/docs/requirements/SEM-207/requirement.md b/docs/requirements/SEM-207/requirement.md index b1d7a9d43..f5f05e366 100644 --- a/docs/requirements/SEM-207/requirement.md +++ b/docs/requirements/SEM-207/requirement.md @@ -34,3 +34,4 @@ Requirement inventory phase. Status audit deferred until the full canonical grap - TESTS → TEST `implementations/python/tests/test_semantics_objectives.py` (Objective semantics analyzer + dependency-partition unit tests (TestObjectiveSemantics, TestObjectiveDependencyPartition)) - TESTS → TEST `implementations/python/tests/test_fm2_semantics.py` (Cross-stage objective-semantics agreement (TestObjectiveSemanticAgreement)) - TESTS → TEST `implementations/python/tests/test_sdl_validator.py` (SDL validator objective-binding tests (TestVerifyObjectives)) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/proposition-truth-result-v1.json` (proposition-truth-result-v1 published schema) diff --git a/docs/requirements/SEM-218/requirement.md b/docs/requirements/SEM-218/requirement.md index 8a56f81bf..b51280409 100644 --- a/docs/requirements/SEM-218/requirement.md +++ b/docs/requirements/SEM-218/requirement.md @@ -116,7 +116,7 @@ Current state: identified gap. Honest portability requires normative semantics f - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/realization_structure/_compatibility_downgrade_helpers.py` (Lossless recursive-to-legacy projection helpers) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/bounded_domains.py` (Nullable scalar-domain support for recursive realization constraints) - IMPLEMENTS → SPEC `specs/sdl/recursive-realization-constraints.md` (Normative recursive authority, collection, reference, limit, and provenance semantics) -- IMPLEMENTS → SCHEMA `contracts/schemas/realization-constraints/recursive-realization-constraint-v1.json` (Published recursive realization constraint contract) +- IMPLEMENTS → SPEC `contracts/schemas/realization-constraints/recursive-realization-constraint-v1.json` (Published recursive realization constraint contract) - TESTS → TEST `implementations/python/tests/test_issue_1203_recursive_normal_form.py` (Nested authority, presence, collections, references, limits, refinement, and abstract-model acceptance coverage) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/realization_structure.py` (Authored leaf and scoped collection lowering for mixed runtime constraints) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/realization_concern_explicitness.py` (Concern-owned leaf selection with non-authoritative aggregate support summaries) @@ -330,3 +330,5 @@ Current state: identified gap. Honest portability requires normative semantics f - TESTS → TEST `implementations/python/tests/test_issue_1208_profile_boundaries.py` - TESTS → TEST `implementations/python/tests/test_issue_1208_self_review.py` - TESTS → TEST `implementations/python/tests/test_issue_1208_overcap.py` +- IMPLEMENTS → SPEC `contracts/schemas/plans/backend-realization-preparation-v1.json` (backend-realization-preparation-v1 published schema) +- IMPLEMENTS → SPEC `contracts/schemas/plans/plan-realization-profiles-v1.json` (plan-realization-profiles-v1 published schema) diff --git a/docs/requirements/SEM-220/requirement.md b/docs/requirements/SEM-220/requirement.md index 51fae6a4b..309069ca6 100644 --- a/docs/requirements/SEM-220/requirement.md +++ b/docs/requirements/SEM-220/requirement.md @@ -30,3 +30,4 @@ Primary-source refresh shows that participant decision surfaces vary materially - TESTS → TEST `implementations/python/tests/test_sem_220_participant_decision_surface.py` (SEM-220 participant decision-surface verification suite) - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/lineage.md` (SEM-220 participant decision-surface lineage and nonclaims) - IMPLEMENTS → GITHUB_ISSUE `295` (Participant Decision-Surface Semantics (SEM-220)) +- IMPLEMENTS → SPEC `contracts/schemas/control-plane/participant-decision-surface-v2.json` (participant-decision-surface-v2 published schema) diff --git a/implementations/python/tests/test_issue_712_contract_requirement_tracing.py b/implementations/python/tests/test_issue_712_contract_requirement_tracing.py new file mode 100644 index 000000000..fe75dbe0e --- /dev/null +++ b/implementations/python/tests/test_issue_712_contract_requirement_tracing.py @@ -0,0 +1,418 @@ +"""Published-contract requirement tracing gate (issue #712). + +Every published schema must trace to a live governing requirement: an +``IMPLEMENTS → SPEC`` link to its exact path from a requirement record whose +status is ACTIVE or DRAFT. These tests drive the rule through temporary +repositories, then assert that the live tree satisfies it and that the +inventory the gate reads agrees with ``schema_bundle()`` and the publication +catalog. +""" + +from __future__ import annotations + +import importlib +import json +import shutil +import sys +from pathlib import Path +from types import ModuleType, SimpleNamespace + +import pytest +from raes_contracts.contracts import schema_bundle + +REPO_ROOT = Path(__file__).resolve().parents[3] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.check_contract_requirement_tracing import ( # noqa: E402 + MISSING_RULE_ID, + UNREADABLE_RULE_ID, + evaluate_contract_tracing, + main, + published_schema_paths, +) +from tools.check_schema_publication import load_schema_publication_catalog # noqa: E402 +from tools.policy.common import PolicyFailure # noqa: E402 +from tools.policy.repository_requirements import RepositoryRequirementClient # noqa: E402 +from tools.policy.requirement_governance import evaluate_requirement_governance # noqa: E402 +from tools.policy.requirement_scope import evaluate_requirement_scope, parse_requirement_scope # noqa: E402 + +_STAGE = "policy / published contract requirement tracing" +_SCRIPT = "tools/check_contract_requirement_tracing.py" +_SCHEMA = "contracts/schemas/control-plane/operation-status-v1.json" +_RECORD = "docs/requirements/API-403/requirement.md" + + +def _publish(repo_root: Path, schema_path: str = _SCHEMA) -> str: + target = repo_root / schema_path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text("{}\n", encoding="utf-8") + return schema_path + + +def _link(identifier: str, link_type: str = "IMPLEMENTS", artifact_type: str = "SPEC") -> str: + return f"- {link_type} → {artifact_type} `{identifier}` (fixture link)" + + +def _requirement(repo_root: Path, uid: str, *links: str, status: str = "ACTIVE") -> Path: + record = repo_root / "docs" / "requirements" / uid / "requirement.md" + record.parent.mkdir(parents=True, exist_ok=True) + lines = ["---", f"id: {uid}", f"status: {status}", "---", "", f"# {uid}", "", "## Traceability", "", *links] + record.write_text("\n".join(lines) + "\n", encoding="utf-8") + return record + + +def _paths(failures: list[PolicyFailure], rule_id: str) -> set[str | None]: + return {failure.path for failure in failures if failure.rule_id == rule_id} + + +# --- the rule ---------------------------------------------------------------- + + +@pytest.mark.parametrize("status", ["ACTIVE", "DRAFT"]) +def test_a_live_implements_spec_link_traces_the_contract(tmp_path: Path, status: str) -> None: + _publish(tmp_path) + _requirement(tmp_path, "API-403", _link(_SCHEMA), status=status) + + assert evaluate_contract_tracing(tmp_path) == [] + + +@pytest.mark.parametrize("status", ["DEPRECATED", "ARCHIVED"]) +def test_a_retired_owner_never_traces_the_contract(tmp_path: Path, status: str) -> None: + _publish(tmp_path) + _requirement(tmp_path, "API-403", _link(_SCHEMA), status=status) + + assert _paths(evaluate_contract_tracing(tmp_path), MISSING_RULE_ID) == {_SCHEMA} + + +def test_one_live_owner_is_enough_beside_a_retired_one(tmp_path: Path) -> None: + _publish(tmp_path) + _requirement(tmp_path, "API-402", _link(_SCHEMA), status="DEPRECATED") + _requirement(tmp_path, "API-403", _link(_SCHEMA)) + + assert evaluate_contract_tracing(tmp_path) == [] + + +@pytest.mark.parametrize( + "link", + [ + pytest.param(_link(_SCHEMA, artifact_type="CODE_FILE"), id="implements-typed-as-code-file"), + pytest.param(_link(_SCHEMA, artifact_type="CONFIG"), id="implements-typed-as-config"), + pytest.param(_link(_SCHEMA, artifact_type="SCHEMA"), id="implements-typed-as-schema"), + pytest.param(_link(_SCHEMA, artifact_type="DOCUMENTATION"), id="implements-typed-as-documentation"), + pytest.param(_link(_SCHEMA, link_type="CONSTRAINS"), id="constrains-spec"), + pytest.param(_link(_SCHEMA, link_type="DOCUMENTS"), id="documents-spec"), + pytest.param(_link(_SCHEMA, link_type="TESTS"), id="tests-spec"), + pytest.param(_link(_SCHEMA, link_type="VERIFIES"), id="verifies-spec"), + pytest.param( + _link("contracts/control-plane/operation-status-v1.json"), + id="implements-spec-on-a-sibling-document", + ), + ], +) +def test_only_an_exact_implements_spec_link_traces_the_contract(tmp_path: Path, link: str) -> None: + _publish(tmp_path) + _requirement(tmp_path, "API-403", link) + + failures = evaluate_contract_tracing(tmp_path) + + assert _paths(failures, UNREADABLE_RULE_ID) == set() + assert _paths(failures, MISSING_RULE_ID) == {_SCHEMA} + + +def test_an_untraced_contract_is_reported_by_id_at_its_exact_path(tmp_path: Path) -> None: + _publish(tmp_path) + + [failure] = evaluate_contract_tracing(tmp_path) + + assert (failure.rule_id, failure.path) == (MISSING_RULE_ID, _SCHEMA) + assert "operation-status-v1" in failure.message + + +def _malformed_link(repo_root: Path) -> str: + _requirement(repo_root, "API-403", f"- IMPLEMENTS → SPEC {_SCHEMA}") + return _RECORD + + +def _symlinked_record(repo_root: Path) -> str: + real = _requirement(repo_root / "elsewhere", "API-403", _link(_SCHEMA)) + record = repo_root / _RECORD + record.parent.mkdir(parents=True) + record.symlink_to(real) + return _RECORD + + +def _non_canonical_directory(repo_root: Path) -> str: + _requirement(repo_root, "notes", _link(_SCHEMA)) + return "docs/requirements/notes/requirement.md" + + +@pytest.mark.parametrize("make_record", [_malformed_link, _symlinked_record, _non_canonical_directory]) +def test_an_unreadable_requirement_record_fails_closed_and_owns_nothing(tmp_path: Path, make_record) -> None: + _publish(tmp_path) + record = make_record(tmp_path) + + failures = evaluate_contract_tracing(tmp_path) + + assert _paths(failures, UNREADABLE_RULE_ID) == {record} + assert _paths(failures, MISSING_RULE_ID) == {_SCHEMA} + + +# --- CLI and reporting ------------------------------------------------------- + + +def test_cli_emits_json_naming_each_untraced_contract(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + _publish(tmp_path) + + assert main(["--repo-root", str(tmp_path), "--json"]) == 1 + payload = json.loads(capsys.readouterr().out) + assert [(item["rule_id"], item["path"]) for item in payload] == [(MISSING_RULE_ID, _SCHEMA)] + + +def test_cli_text_mode_reports_on_stderr_and_passes_a_traced_tree( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + _publish(tmp_path) + + assert main(["--repo-root", str(tmp_path)]) == 1 + assert f"[{MISSING_RULE_ID}] {_SCHEMA}:" in capsys.readouterr().err + + _requirement(tmp_path, "API-403", _link(_SCHEMA)) + assert main(["--repo-root", str(tmp_path)]) == 0 + + +def test_a_shared_policy_waiver_suppresses_only_the_named_finding(tmp_path: Path) -> None: + _publish(tmp_path) + other = _publish(tmp_path, "contracts/schemas/control-plane/operation-receipt-v1.json") + waivers = tmp_path / "tools" / "policy" / "exceptions.yaml" + waivers.parent.mkdir(parents=True) + waivers.write_text(f"exceptions:\n - rule_id: {MISSING_RULE_ID}\n paths: [{_SCHEMA}]\n", encoding="utf-8") + + assert main(["--repo-root", str(tmp_path)]) == 1 + _requirement(tmp_path, "API-403", _link(other)) + assert main(["--repo-root", str(tmp_path)]) == 0 + + +def test_report_names_owners_untraced_contracts_and_unreadable_records_without_gating( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + traced = _publish(tmp_path) + untraced = _publish(tmp_path, "contracts/schemas/control-plane/operation-receipt-v1.json") + _requirement(tmp_path, "API-403", _link(traced)) + _requirement(tmp_path, "API-404", _link(traced), status="DRAFT") + _requirement(tmp_path, "API-402", "- not a link record") + + assert main(["--repo-root", str(tmp_path), "--report"]) == 0 + report = capsys.readouterr().out + assert f"{traced}: API-403, API-404" in report + assert f"{untraced}: UNTRACED" in report + assert f"[{UNREADABLE_RULE_ID}] docs/requirements/API-402/requirement.md" in report + + +# --- policy-lane wiring ------------------------------------------------------ + + +class _Reporter: + def __init__(self) -> None: + self.runs: list[str] = [] + self.skips: list[str] = [] + + def run(self, name: str, func: object, *, detail: str = "") -> None: + del detail + self.runs.append(name) + assert callable(func) + func() + + def skip(self, name: str, reason: str) -> None: + del reason + self.skips.append(name) + + +@pytest.fixture +def policy_lanes(monkeypatch: pytest.MonkeyPatch) -> ModuleType: + """Import the lane module against a stand-in ``nox``; the project env has none.""" + + monkeypatch.setitem(sys.modules, "nox", SimpleNamespace(Session=object)) + for name in [name for name in sys.modules if name.startswith("tools.nox_support")]: + monkeypatch.delitem(sys.modules, name) + return importlib.import_module("tools.nox_support.policy_lanes") + + +def test_policy_lane_runs_the_gate_on_the_working_tree_and_skips_it_when_staged( + monkeypatch: pytest.MonkeyPatch, policy_lanes: ModuleType +) -> None: + scripts: list[str] = [] + monkeypatch.setattr(policy_lanes, "_run_project_python", lambda _session, script, *_args: scripts.append(script)) + + working_tree = _Reporter() + policy_lanes._run_working_tree_policies(SimpleNamespace(), working_tree, staged=False, adr_pin_args=[]) + staged = _Reporter() + policy_lanes._run_working_tree_policies(SimpleNamespace(), staged, staged=True, adr_pin_args=[]) + + assert _STAGE in working_tree.runs + assert _SCRIPT in scripts + assert _STAGE in staged.skips + assert _STAGE not in staged.runs + + +# --- owner-record governance ------------------------------------------------- + + +@pytest.fixture +def governed_repo(tmp_path: Path) -> Path: + policy_dir = tmp_path / "tools" / "policy" + policy_dir.mkdir(parents=True) + shutil.copyfile(REPO_ROOT / "tools/policy/requirement_order.yaml", policy_dir / "requirement_order.yaml") + (policy_dir / "exceptions.yaml").write_text("exceptions: []\n", encoding="utf-8") + for uid in ( + "GOV-917", + "GOV-918", + "GOV-919", + "GOV-920", + "GOV-921", + "GOV-922", + "API-412", + "API-413", + "RUN-300", + "RUN-311", + "RUN-313", + "RUN-314", + "RUN-315", + ): + _requirement(tmp_path, uid) + return tmp_path + + +@pytest.mark.parametrize( + ("uid", "schema_path"), + [ + ("RUN-300", _SCHEMA), + ("RUN-311", _SCHEMA), + ("API-412", "contracts/schemas/processor-manifest/processor-manifest-v2.json"), + ("GOV-918", "contracts/schemas/concept-authority/semantic-projection-report-v1.json"), + ("DSL-105", "contracts/schemas/sdl/sdl-authoring-input-v1.json"), + ("SEM-209", "contracts/schemas/sdl/sdl-authoring-input-v1.json"), + ("EXP-701", "contracts/schemas/experiment-core/experiment-run-v1.json"), + ], +) +def test_a_new_contract_and_its_owner_record_pass_both_gates(governed_repo: Path, uid: str, schema_path: str) -> None: + _publish(governed_repo, schema_path) + assert _paths(evaluate_contract_tracing(governed_repo), MISSING_RULE_ID) == {schema_path} + record = _requirement(governed_repo, uid, _link(schema_path)).relative_to(governed_repo).as_posix() + + assert evaluate_contract_tracing(governed_repo) == [] + assert ( + evaluate_requirement_governance( + governed_repo, + [schema_path, record], + client=RepositoryRequirementClient(governed_repo), + requirement_uid=uid, + ) + == [] + ) + + +@pytest.mark.parametrize( + "path", + [ + "docs/requirements/RUN-300/requirement.md", + "docs/requirements/RUN-311/notes.md", + "docs/requirements/RUN-311/requirement.md/extra.md", + "contracts/schemas/experiment-core/experiment-run-v1.json", + ], +) +def test_owner_record_permission_does_not_admit_other_records_or_files(governed_repo: Path, path: str) -> None: + failures = evaluate_requirement_governance( + governed_repo, + [path], + client=RepositoryRequirementClient(governed_repo), + requirement_uid="RUN-311", + ) + + assert [(failure.rule_id, failure.path) for failure in failures] == [("requirement-ownership-mismatch", path)] + + +@pytest.mark.parametrize("assigned_uid", ["RUN-311", "RUN-300"]) +def test_scoped_owner_record_edits_require_the_correct_owner(governed_repo: Path, assigned_uid: str) -> None: + record = "docs/requirements/RUN-311/requirement.md" + scope = parse_requirement_scope( + { + "schema_version": "requirement-scope/v1", + "issue_number": 712, + "primary_requirement_uid": "RUN-311", + "requirement_uids": ["RUN-311", "RUN-300"], + "bindings": {record: [assigned_uid]}, + }, + issue_number=712, + ) + + failures = evaluate_requirement_scope( + governed_repo, + [record], + client=RepositoryRequirementClient(governed_repo), + scope=scope, + ) + + assert [(failure.rule_id, failure.path) for failure in failures] == ( + [] if assigned_uid == "RUN-311" else [("requirement-ownership-mismatch", record)] + ) + + +@pytest.mark.parametrize("status", ["DEPRECATED", "ARCHIVED"]) +def test_owner_record_edits_do_not_admit_retired_requirements(governed_repo: Path, status: str) -> None: + record = _requirement(governed_repo, "RUN-311", status=status).relative_to(governed_repo).as_posix() + + failures = evaluate_requirement_governance( + governed_repo, + [record], + client=RepositoryRequirementClient(governed_repo), + requirement_uid="RUN-311", + ) + + assert [failure.rule_id for failure in failures] == ["requirement-invalid-status"] + + +@pytest.mark.parametrize( + ("path", "rule_id"), + [ + ("implementations/python/packages/raes_contracts/new_contract.py", "traceability-missing-implements"), + ("implementations/python/tests/test_new_contract.py", "traceability-missing-tests"), + ], +) +def test_owner_record_edits_still_require_code_and_test_links(governed_repo: Path, path: str, rule_id: str) -> None: + failures = evaluate_requirement_governance( + governed_repo, + ["docs/requirements/RUN-311/requirement.md", path], + client=RepositoryRequirementClient(governed_repo), + requirement_uid="RUN-311", + ) + + assert [(failure.rule_id, failure.path) for failure in failures] == [(rule_id, path)] + + +def test_owner_record_edits_still_require_completed_prerequisites(governed_repo: Path) -> None: + _requirement(governed_repo, "API-412", status="DRAFT") + + failures = evaluate_requirement_governance( + governed_repo, + ["docs/requirements/RUN-311/requirement.md"], + client=RepositoryRequirementClient(governed_repo), + requirement_uid="RUN-311", + ) + + assert [failure.rule_id for failure in failures] == ["requirement-order-blocked"] + + +# --- the live tree ----------------------------------------------------------- + + +def test_checked_in_repository_traces_every_published_contract() -> None: + assert evaluate_contract_tracing(REPO_ROOT) == [] + + +def test_the_gate_inventory_agrees_with_schema_bundle_and_the_publication_catalog() -> None: + inventory = published_schema_paths(REPO_ROOT) + catalog = sorted(entry["schema_path"] for entry in load_schema_publication_catalog(REPO_ROOT)["schemas"]) + + assert inventory == catalog + assert sorted(Path(schema_path).stem for schema_path in inventory) == sorted(schema_bundle()) diff --git a/tools/check_contract_requirement_tracing.py b/tools/check_contract_requirement_tracing.py new file mode 100644 index 000000000..6a6f978fa --- /dev/null +++ b/tools/check_contract_requirement_tracing.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# ruff: noqa: E402, I001 +"""Structural gate: every published contract traces to a live governing requirement. + +``ADR-009`` §7 makes ``contracts/schemas/`` the normative contract authority, and +``check_schema_publication.py`` and ``check_schema_coverage.py`` prove that each +published schema is inventoried and exercised. Neither asks which requirement +governs it, so a schema could be published, and stay published, with no +requirement owning it (issue #712). This gate asks that question over the whole +published inventory on every run, not over the changed-file set. + +A published contract is **traced** when at least one requirement record under +``docs/requirements//requirement.md`` whose status is ACTIVE or DRAFT +carries an ``IMPLEMENTS → SPEC`` link to the contract's exact schema path. + +- ``CODE_FILE`` links never count. A module such as + ``raes_contracts/contracts/bundle.py`` generates every contract, so a module + link cannot say which contract a requirement governs. +- Other link types never count. ``CONSTRAINS``, ``DOCUMENTS``, ``TESTS``, and + ``VERIFIES`` record a relation other than ownership. An ``IMPLEMENTS`` link + to a schema path typed ``CONFIG``, ``SCHEMA``, or ``DOCUMENTATION`` does not + count either; retype it to ``SPEC`` only where it records ownership. +- DRAFT owners qualify and DEPRECATED or ARCHIVED owners never do. That is the + status rule ``requirement_governance.py`` applies to implementation work. + +The inventory is every ``contracts/schemas/**/*.json`` file. The contracts lane +already fails when that tree disagrees with ``schema_bundle()`` +(``check_generated_schemas.py``) or with the publication manifest +(``check_schema_publication.py``), so this gate reads the tree and does not +regenerate the bundle. Requirement records are read offline through +``RepositoryRequirementClient``, the authority ``requirement_order.yaml`` +selects. An unreadable record fails closed rather than counting as untraced. + +``--report`` prints the owners of each published contract and never gates. +Failures use ``tools.policy.common.PolicyFailure``. The CLI honours ``--json`` +and the shared ``tools/policy/exceptions.yaml`` waiver mechanism, like the other +``policy`` nox-stage entry points. +""" + +from __future__ import annotations + +import argparse +import sys +from collections import defaultdict +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.policy.common import PolicyFailure, apply_exceptions, failures_to_json, load_exceptions +from tools.policy.repository_requirements import RepositoryRequirementClient, RepositoryRequirementError + +__all__ = [ + "GOVERNING_LINK", + "MISSING_RULE_ID", + "RETIRED_STATUSES", + "UNREADABLE_RULE_ID", + "build_tracing_report", + "evaluate_contract_tracing", + "governing_owners", + "main", + "published_schema_paths", +] + +SCHEMAS_ROOT = "contracts/schemas" +REQUIREMENTS_ROOT = "docs/requirements" +GOVERNING_LINK = ("IMPLEMENTS", "SPEC") +RETIRED_STATUSES = frozenset({"ARCHIVED", "DEPRECATED"}) +MISSING_RULE_ID = "contract-requirement-trace-missing" +UNREADABLE_RULE_ID = "contract-requirement-record-unreadable" + + +def published_schema_paths(repo_root: Path = REPO_ROOT) -> list[str]: + """Return every published schema as a sorted repository-relative path.""" + + schemas_root = repo_root / SCHEMAS_ROOT + return sorted(path.relative_to(repo_root).as_posix() for path in schemas_root.rglob("*.json")) + + +def _requirement_uids(repo_root: Path) -> list[str]: + requirements_root = repo_root / REQUIREMENTS_ROOT + if not requirements_root.is_dir(): + return [] + return sorted(path.name for path in requirements_root.iterdir() if path.is_dir()) + + +def _governing_identifiers(client: RepositoryRequirementClient, uid: str) -> list[str]: + """Return the ``IMPLEMENTS → SPEC`` identifiers one requirement can own.""" + + if client.get_requirement("", uid)["status"] in RETIRED_STATUSES: + return [] + return [ + link["artifact_identifier"] + for link in client.get_traceability(uid) + if (link["link_type"], link["artifact_type"]) == GOVERNING_LINK + ] + + +def governing_owners(repo_root: Path = REPO_ROOT) -> tuple[dict[str, set[str]], list[PolicyFailure]]: + """Map each governed artifact to its live owners, failing closed on unreadable records.""" + + client = RepositoryRequirementClient(repo_root) + owners: dict[str, set[str]] = defaultdict(set) + failures: list[PolicyFailure] = [] + for uid in _requirement_uids(repo_root): + try: + identifiers = _governing_identifiers(client, uid) + except RepositoryRequirementError as exc: + failures.append(PolicyFailure(UNREADABLE_RULE_ID, str(exc), f"{REQUIREMENTS_ROOT}/{uid}/requirement.md")) + continue + for identifier in identifiers: + owners[identifier].add(uid) + return dict(owners), failures + + +def evaluate_contract_tracing(repo_root: Path = REPO_ROOT) -> list[PolicyFailure]: + """Return every published contract with no live ``IMPLEMENTS → SPEC`` owner.""" + + owners, failures = governing_owners(repo_root) + failures.extend( + PolicyFailure( + MISSING_RULE_ID, + f"published contract {Path(schema_path).stem} has no IMPLEMENTS → SPEC link " + "from an ACTIVE or DRAFT requirement", + schema_path, + ) + for schema_path in published_schema_paths(repo_root) + if schema_path not in owners + ) + return failures + + +def build_tracing_report(repo_root: Path = REPO_ROOT) -> str: + """Render the owners of every published contract. Never gates.""" + + owners, failures = governing_owners(repo_root) + lines = ["Published-contract requirement tracing report (issue #712)", ""] + for schema_path in published_schema_paths(repo_root): + lines.append(f" - {schema_path}: {', '.join(sorted(owners.get(schema_path, ()))) or 'UNTRACED'}") + lines.extend(f" - {failure.render()}" for failure in failures) + return "\n".join(lines) + "\n" + + +def _active_waivers(repo_root: Path) -> list[dict[str, object]]: + exceptions_file = repo_root / "tools" / "policy" / "exceptions.yaml" + return load_exceptions(repo_root) if exceptions_file.is_file() else [] + + +def _print_failures(failures: list[PolicyFailure], *, as_json: bool) -> None: + if as_json: + print(failures_to_json(failures)) + return + for failure in failures: + print(failure.render(), file=sys.stderr) + + +def parse_args(argv: list[str] | None) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Require every published contract to trace to a live governing requirement (issue #712)." + ) + parser.add_argument("--repo-root", type=Path, default=REPO_ROOT, help="Repository root to check.") + parser.add_argument("--json", action="store_true", help="Emit failures as JSON on stdout.") + parser.add_argument("--report", action="store_true", help="Print each contract's owners and exit 0.") + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv) + if args.report: + print(build_tracing_report(args.repo_root), end="") + return 0 + failures = apply_exceptions(evaluate_contract_tracing(args.repo_root), _active_waivers(args.repo_root)) + if failures: + _print_failures(failures, as_json=args.json) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/nox_support/policy_lanes.py b/tools/nox_support/policy_lanes.py index 19be0307b..58007f200 100644 --- a/tools/nox_support/policy_lanes.py +++ b/tools/nox_support/policy_lanes.py @@ -39,6 +39,7 @@ ("policy / agent guidance profile", "tools/check_agent_guidance.py"), ("policy / example library catalog", "tools/check_example_library.py"), ("policy / published schema coverage", "tools/check_schema_coverage.py"), + ("policy / published contract requirement tracing", "tools/check_contract_requirement_tracing.py"), ("policy / project positioning", "tools/check_project_positioning.py"), ("policy / identity cutover", "tools/check_identity_cutover.py"), ) diff --git a/tools/policy/requirement_governance.py b/tools/policy/requirement_governance.py index 03ea196c4..1d7da7493 100644 --- a/tools/policy/requirement_governance.py +++ b/tools/policy/requirement_governance.py @@ -210,7 +210,7 @@ def evaluate_requirement_governance( ] failures.extend(_check_phase_predecessors(policy, client, phase_match)) - failures.extend(_check_path_ownership(policy, phase_match, changed)) + failures.extend(_check_path_ownership(policy, phase_match, changed, requirement_uid=requirement_uid)) failures.extend(_check_traceability(client, requirement, policy, changed)) return failures @@ -260,14 +260,23 @@ def _check_phase_predecessors( return failures -def _check_path_ownership(policy: dict[str, object], match: PhaseMatch, changed: list[str]) -> list[PolicyFailure]: +def _check_path_ownership( + policy: dict[str, object], + match: PhaseMatch, + changed: list[str], + *, + requirement_uid: str | None = None, +) -> list[PolicyFailure]: allowed = policy.get("ownership", {}).get(match.phase_id, []) if not allowed: return [] + # Contract owners must maintain their traceability in the same change; + # this grants only the evaluated owner's exact record, never a directory. + owner_record = f"docs/requirements/{requirement_uid}/requirement.md" if requirement_uid else None failures: list[PolicyFailure] = [] relevant = [path for path in changed if path.startswith(("implementations/", "contracts/", "specs/", "docs/"))] for path in relevant: - if not path_matches_any(path, allowed): + if path != owner_record and not path_matches_any(path, allowed): failures.append( PolicyFailure( "requirement-ownership-mismatch",