From c3ac7762dfbada66c4223bb82d38d4b092b0c1a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:30:15 +0000 Subject: [PATCH 1/2] Initial plan From e6d741b3a6b90896f3ca98a7bbfeb19afe3e2eb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:34:46 +0000 Subject: [PATCH 2/2] feat: harden standards spine to M3 readiness - Add fixtures/invalid/ with 4 negative fixtures (2 functional-service, 2 maturity) - Add tools/run_negative_fixtures.py to verify negative fixtures fail validation - Update Makefile to include negatives target in validate - Add docs/MATURITY_MODEL.md (M0-M5, evidence requirements, Sociosphere ingestion) - Add docs/LAB_TEMPLATE.md (lab repo skeleton and functional-service.v1 manifest) - Update repo.maturity.yaml to M3 with updated evidence and nextActions Agent-Logs-Url: https://github.com/SocioProphet/functional-model-surfaces/sessions/503b1a79-8d44-4b4c-b9a5-f0b46ac4b873 Co-authored-by: mdheller <21163552+mdheller@users.noreply.github.com> --- Makefile | 7 +- docs/LAB_TEMPLATE.md | 256 ++++++++++++++++++ docs/MATURITY_MODEL.md | 187 +++++++++++++ .../functional-service.invalid-status.json | 31 +++ ...tional-service.missing-schema-version.json | 30 ++ fixtures/invalid/maturity.empty-owners.yaml | 20 ++ fixtures/invalid/maturity.invalid-level.yaml | 21 ++ repo.maturity.yaml | 14 +- tools/run_negative_fixtures.py | 72 +++++ 9 files changed, 631 insertions(+), 7 deletions(-) create mode 100644 docs/LAB_TEMPLATE.md create mode 100644 docs/MATURITY_MODEL.md create mode 100644 fixtures/invalid/functional-service.invalid-status.json create mode 100644 fixtures/invalid/functional-service.missing-schema-version.json create mode 100644 fixtures/invalid/maturity.empty-owners.yaml create mode 100644 fixtures/invalid/maturity.invalid-level.yaml create mode 100644 tools/run_negative_fixtures.py diff --git a/Makefile b/Makefile index 3e7307a..7bec008 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -.PHONY: validate test lint examples maturity +.PHONY: validate test lint examples maturity negatives -validate: lint examples maturity +validate: lint examples maturity negatives lint: python3 -m json.tool schemas/repo-maturity.schema.json >/dev/null @@ -13,4 +13,7 @@ examples: maturity: python3 tools/validate_maturity.py schemas/repo-maturity.schema.json repo.maturity.yaml +negatives: + python3 tools/run_negative_fixtures.py + test: validate diff --git a/docs/LAB_TEMPLATE.md b/docs/LAB_TEMPLATE.md new file mode 100644 index 0000000..9c7ae9a --- /dev/null +++ b/docs/LAB_TEMPLATE.md @@ -0,0 +1,256 @@ +# Lab Repository Template + +This document describes the required skeleton for a `SociOS-Linux/*lab` repository and the `functional-service.v1` manifest it must emit before a service can be promoted through the SocioProphet governance chain. + +--- + +## Purpose + +A lab repository is the workspace where a modality team trains, tunes, evaluates, and packages evidence for a functional AI surface. It is **not** a production service. Its outputs are manifests and evidence records that feed governance, not deployable images or model weights in the estate runtime. + +--- + +## Required skeleton + +``` +/lab/ +├── README.md # Purpose, modality, owners, status +├── repo.maturity.yaml # Maturity record (see schemas/repo-maturity.schema.json) +├── lab.manifest.json # Lab identity and capabilities (free-form, optional schema) +├── examples/ +│ └── functional-service.example.json # Candidate functional-service.v1 manifest +├── evals/ +│ └── .json # Evaluation results referenced by the manifest +├── Makefile # validate, test, lint targets +├── requirements.txt # Python validation dependencies +└── .github/ + └── workflows/ + └── validate.yml # CI that runs make validate +``` + +### README.md + +Must state: + +- Modality (e.g. speech, OCR, image, embedding). +- Current lab status (`experimental`, `incubating`, etc.). +- Named owner(s). +- Link to the corresponding functional service schema in `SocioProphet/functional-model-surfaces`. +- What the lab ships (evidence, manifests) and what it does **not** ship (runtime services, production secrets). + +### repo.maturity.yaml + +Must validate against `schemas/repo-maturity.schema.json` from `SocioProphet/functional-model-surfaces`. Minimum required fields: + +```yaml +schemaVersion: repo-maturity.v1 +repository: /lab +plane: socios-lab +status: incubating +canonicality: reference +owners: + - +maturity: + level: M1 + targetLevel: M3 + evidence: + - README.md states lab purpose and modality. +validation: + commands: + - make validate + ciRequired: true + lastKnownStatus: not-configured +integrations: + - repository: SocioProphet/functional-model-surfaces + relationship: emits functional-service.v1 manifests + required: true + - repository: SocioProphet/model-governance-ledger + relationship: sends promotion evidence + required: true + - repository: SocioProphet/sociosphere + relationship: registers maturity and evidence status + required: true +``` + +--- + +## The `functional-service.v1` manifest + +When a lab produces a candidate service, it emits a `functional-service.v1` JSON manifest under `examples/`. This manifest is the primary artifact passed to governance for promotion review. + +### Schema reference + +`schemas/functional-service.schema.json` in `SocioProphet/functional-model-surfaces`. + +### Annotated example + +```json +{ + "schemaVersion": "functional-service.v1", + + "service": { + "id": "speech-transcription-baseline", + "name": "Speech Transcription Baseline", + "ownerRepository": "SociOS-Linux/speechlab", + "status": "experimental", + "description": "Whisper-based transcription surface, lab candidate." + }, + + "function": "speech", + + "model": { + "modelRef": "oci://registry.example/socios/speech-transcription:0.1.0", + "adapterRefs": ["oci://registry.example/socios/speech-transcription-adapter:0.1.0"], + "runtime": "container", + "mutableStatePolicy": "forbidden-in-sourceos" + }, + + "inputs": ["audio/wav", "audio/flac"], + "outputs": ["application/json+transcript"], + + "evals": { + "required": true, + "references": ["evals/wer-baseline-001.json"], + "minimumPromotionGate": "candidate requires signed eval and dataset provenance" + }, + + "governance": { + "ledgerRequired": true, + "guardrailRequired": true, + "routingRequired": true, + "policyRefs": ["guardrail-fabric/policies/speech-transcription-default"] + }, + + "sourceosCarry": { + "allowed": true, + "carriesMutableModelState": false, + "clientRefRequired": true, + "launchProfileRefs": ["sourceos-model-carry/launch-profiles/speech-transcription"] + } +} +``` + +### Field guidance + +| Field | Requirement | Notes | +|-------|-------------|-------| +| `schemaVersion` | Required, fixed | Must be exactly `"functional-service.v1"`. | +| `service.id` | Required | Lowercase alphanumeric with `_`, `.`, `-`. Stable identifier; do not change after promotion. | +| `service.ownerRepository` | Required | The lab repository in `org/repo` form. | +| `service.status` | Required | Use `draft` or `experimental` while in the lab. | +| `function` | Required | One of the enumerated functional families in the schema. | +| `model.modelRef` | Required | OCI reference or equivalent addressable digest. No inline weights. | +| `model.mutableStatePolicy` | Required | Must be `forbidden-in-sourceos` for any surface destined for SourceOS carry. | +| `inputs` / `outputs` | Required | MIME types or structured type strings. | +| `evals.required` | Required | Set to `true` for any surface that will be promoted to `candidate` or above. | +| `evals.references` | Required | At least one eval result file path (relative to the lab repo root). | +| `governance.ledgerRequired` | Required | `true` for all promoted surfaces. | +| `sourceosCarry.carriesMutableModelState` | Required | Must be `false`. The schema enforces this. | + +--- + +## Promotion flow + +A lab manifest moves through the following stages before reaching production: + +``` +lab experiment + │ + ├─ dataset evidence recorded + ├─ training / tuning run logged + ├─ model artifact digest captured + ├─ eval gate run and signed + │ + └─► functional-service.v1 manifest emitted (status: experimental) + │ + ├─ governance review in model-governance-ledger + ├─ guardrail policy applied in guardrail-fabric + ├─ routing policy registered in model-router + │ + └─► manifest status promoted to candidate / approved + │ + ├─ runtime deployment in prophet-platform + └─ SourceOS carry reference registered in sourceos-model-carry +``` + +A lab **never** bypasses governance. Even if a model is technically ready, it must pass through the ledger, guardrail, and routing gate before it appears in any runtime or SourceOS carry surface. + +--- + +## What the lab must NOT contain + +| Prohibited content | Reason | +|--------------------|--------| +| Model weight files (`.bin`, `.safetensors`, `.gguf`, etc.) | Weights belong in an OCI registry, not in a git repository. | +| Production secrets or API keys | Use secret management; never commit credentials. | +| SourceOS image state | SourceOS carry refs are managed by `SourceOS-Linux/sourceos-model-carry`. | +| Runtime deployment configuration | Deployment belongs in `SocioProphet/prophet-platform`. | +| Promotion evidence written directly to the ledger | The ledger is written by governance tooling, not by the lab. | + +--- + +## Makefile minimum + +```makefile +.PHONY: validate test lint + +validate: lint examples maturity + +lint: + python3 -m json.tool examples/functional-service.example.json >/dev/null + +examples: + python3 -m pip install jsonschema PyYAML -q + python3 -c "import json, jsonschema; \ + schema = json.load(open('schemas/functional-service.schema.json')); \ + doc = json.load(open('examples/functional-service.example.json')); \ + jsonschema.validate(doc, schema); print('ok')" + +maturity: + python3 -c "import json, yaml, jsonschema; \ + schema = json.load(open('schemas/repo-maturity.schema.json')); \ + doc = yaml.safe_load(open('repo.maturity.yaml')); \ + jsonschema.validate(doc, schema); print('ok')" + +test: validate +``` + +Lab repos should copy the full Makefile and validation tooling from `SocioProphet/functional-model-surfaces` for consistency. The `schemas/` directory can be kept in sync by pinning a known-good commit reference. + +--- + +## CI minimum + +```yaml +name: validate + +on: + pull_request: + push: + branches: + - main + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: python -m pip install jsonschema PyYAML + - run: make validate +``` + +--- + +## Checklist before opening a promotion PR + +- [ ] `functional-service.example.json` validates against `schemas/functional-service.schema.json`. +- [ ] `repo.maturity.yaml` validates against `schemas/repo-maturity.schema.json`. +- [ ] At least one eval result is referenced and the file exists. +- [ ] `evals.required` is `true`. +- [ ] `sourceosCarry.carriesMutableModelState` is `false`. +- [ ] `service.status` is `experimental` or `candidate` (not `draft`). +- [ ] CI is green. +- [ ] No model weights, secrets, or runtime state committed to the repository. diff --git a/docs/MATURITY_MODEL.md b/docs/MATURITY_MODEL.md new file mode 100644 index 0000000..586bf36 --- /dev/null +++ b/docs/MATURITY_MODEL.md @@ -0,0 +1,187 @@ +# Maturity Model + +This document describes the M0–M5 maturity levels used by the SocioProphet estate, the evidence requirements for each gate, and how `Sociosphere/workspace-inventory` ingests and scores maturity records. + +--- + +## Levels at a glance + +| Level | Name | Summary | +|-------|------|---------| +| M0 | Named | Repository exists and has a name. No governance signal yet. | +| M1 | Documented | README, ownership, purpose, license/status, and a roadmap stub are present. | +| M2 | Validated | Schemas, examples, a working local validation target, and an integration map exist. | +| M3 | CI-hardened | Continuous integration validates schemas, fixtures, and maturity records. Negative fixtures confirm rejection of invalid documents. | +| M4 | Release-ready | Versioned release artifacts with checksums, SBOM or provenance attestation, and signed releases. | +| M5 | Operationally mature | Upgrade/rollback runbooks, a compatibility matrix, and an evidence export consumable by external governance tooling. | + +--- + +## Level definitions and evidence requirements + +### M0 — Named + +**Gate condition:** A repository has been created. + +**Required evidence:** none beyond the repository's existence. + +**Typical state:** placeholder or early bootstrap. + +--- + +### M1 — Documented + +**Gate condition:** Human readers can understand the purpose, owners, and current status of the repository without external context. + +**Required evidence:** + +- `README.md` that states: purpose, doctrine or guiding principles, role in the wider estate, and integration links. +- At least one named owner (GitHub org or user handle) listed in `repo.maturity.yaml` under `owners`. +- `status` field set to a valid value (`active`, `experimental`, `incubating`, `archival`, or `deprecated`). +- A `nextActions` list or equivalent roadmap stub. + +**Standards plane note:** For a standards repo, the README must also describe what the repo ships (contracts, schemas, docs) and what it intentionally does *not* ship (weights, secrets, runtime state). + +--- + +### M2 — Validated + +**Gate condition:** The repository's contracts are machine-readable, locally verifiable, and correctly mapped to the estate. + +**Required evidence:** + +- JSON Schema files present and valid (`python3 -m json.tool` passes). +- At least one positive example fixture that validates cleanly against each schema. +- A local validation command (`make validate` or equivalent) that exits `0`. +- `repo.maturity.yaml` present and validates against `schemas/repo-maturity.schema.json`. +- `integrations` list in `repo.maturity.yaml` names every upstream and downstream repository dependency. + +--- + +### M3 — CI-hardened + +**Gate condition:** A Continuous Integration workflow enforces all M2 checks automatically on every commit and pull request. Negative fixtures confirm the schema correctly rejects malformed documents. + +**Required evidence:** + +- A `.github/workflows/` (or equivalent CI) file that installs dependencies and runs `make validate`. +- At least two negative fixture documents for each schema, each stored under `fixtures/invalid/`, each failing validation for a documented reason. +- A negative fixture runner (`tools/run_negative_fixtures.py` or equivalent) that exits non-zero if any fixture unexpectedly passes. +- `validation.ciRequired` set to `true` and `validation.lastKnownStatus` set to `passing` in `repo.maturity.yaml`. +- Security and boundary documentation clarifying what the repository does *not* carry (model weights, runtime secrets, SourceOS image state). + +--- + +### M4 — Release-ready + +**Gate condition:** Artifacts can be versioned, verified, and attributed. + +**Required evidence:** + +- Tagged releases with a changelog or release notes. +- Checksums (`sha256`) or content-digest attestation for each release artifact. +- SBOM (Software Bill of Materials) or provenance record (SLSA level 1 or higher). +- Signed releases (GPG, Sigstore, or equivalent) where the signing identity is documented. + +--- + +### M5 — Operationally mature + +**Gate condition:** The repository can be safely updated, rolled back, and its contracts consumed by downstream systems in a machine-readable way. + +**Required evidence:** + +- Upgrade and rollback runbook (documented procedure, not just a commit message). +- Compatibility matrix specifying which version ranges of consumer repositories are supported. +- Evidence export in a format consumable by `SocioProphet/sociosphere` or `SocioProphet/workspace-inventory` (see ingestion section below). +- All M0–M4 evidence present. + +--- + +## Evidence format in `repo.maturity.yaml` + +Each evidence item in `maturity.evidence` should be a human-readable string that names a specific artifact and what it demonstrates. Examples: + +```yaml +maturity: + level: M3 + targetLevel: M4 + evidence: + - README.md defines purpose, doctrine, role, maturity gates, and integration path. + - schemas/repo-maturity.schema.json defines estate maturity record shape. + - schemas/functional-service.schema.json defines functional service manifest shape. + - .github/workflows/validate.yml runs make validate on every PR and push to main. + - fixtures/invalid/ contains 4 negative fixtures verified by tools/run_negative_fixtures.py. + - docs/MATURITY_MODEL.md and docs/LAB_TEMPLATE.md document standards, lab outputs, and governance. +``` + +Avoid vague strings like "CI is set up." Name the file and the claim. + +--- + +## Sociosphere / workspace-inventory ingestion + +`SocioProphet/sociosphere` and its companion `SocioProphet/workspace-inventory` score every registered repository against the maturity gates above. The ingestion contract works as follows: + +### Pull model + +`workspace-inventory` periodically fetches `repo.maturity.yaml` from the default branch of each registered repository. The file must be at the repository root and validate against `schemas/repo-maturity.schema.json` from **this** repository. + +### Scorecard fields used by ingestion + +| Field | Use | +|-------|-----| +| `schemaVersion` | Version gate; must be `repo-maturity.v1`. | +| `repository` | Canonical `org/repo` identifier used as the primary key. | +| `plane` | Groups repositories into estate planes for dashboards. | +| `status` | Used to filter out archival/deprecated repos from active scorecards. | +| `maturity.level` | Current maturity score rendered in the workspace scorecard. | +| `maturity.targetLevel` | Indicates roadmap intent; used for gap-analysis views. | +| `maturity.evidence` | Displayed in the repo detail page; must be non-empty at M1+. | +| `validation.ciRequired` | If `true` and `lastKnownStatus` ≠ `passing`, the repo is flagged. | +| `validation.lastKnownStatus` | Updated by CI badge push or manual update after each run. | +| `owners` | Used to attribute scorecard entries to teams or individuals. | +| `integrations` | Rendered as the estate dependency graph in Sociosphere. | + +### Registering a new repository + +1. Add `repo.maturity.yaml` to the root of the repository. +2. Open a pull request against `SocioProphet/workspace-inventory` that adds the `org/repo` slug to the registry manifest. +3. Sociosphere will pick up the record on the next scheduled sync (or on webhook trigger if configured). + +### Keeping the record current + +- Update `maturity.level` when you merge evidence that satisfies a gate. +- Set `validation.lastKnownStatus` to `passing` once CI is green; the CI workflow itself may push this update. +- Keep `nextActions` in sync with open work so the Sociosphere gap-analysis view reflects actual intent. + +--- + +## Plane taxonomy + +| Plane | Description | +|-------|-------------| +| `standards` | Contract, schema, and doctrine repositories. Ships no runtime state. | +| `runtime` | Service implementations that consume standards contracts. | +| `governance` | Promotion ledger, guardrail fabric, routing policy, agent registry. | +| `sourceos-carry` | SourceOS carry references only; no mutable model updates in OS images. | +| `socios-lab` | Modality-specific lab workspaces that emit functional service manifests. | +| `workspace-governance` | Estate registration, scorecard, and maturity telemetry. | +| `cli` | Operator command surfaces that consume standards contracts. | +| `research` | Exploratory or pre-incubation work not yet bound to estate contracts. | +| `archive` | Inactive repositories retained for historical reference. | + +--- + +## Standards vs. other planes + +| Concern | Where it lives | What this repo provides | +|---------|---------------|------------------------| +| Contract schema | **This repo** (`standards`) | `schemas/*.schema.json` | +| Maturity scorecard | **This repo** (`standards`) | `schemas/repo-maturity.schema.json`, `repo.maturity.yaml` | +| Lab experiment outputs | `socios-lab` repos | Emits `functional-service.v1` manifests per `docs/LAB_TEMPLATE.md` | +| Runtime promotion | `runtime` + `governance` repos | Consumes manifests; writes ledger evidence | +| SourceOS carry | `sourceos-carry` repos | Carry-only references, launch profiles, no weights | +| Workspace scorecard | `workspace-governance` repos | Ingests `repo.maturity.yaml`; renders dashboards | + +This repository ships **contracts only**. It does not ship model weights, mutable adapters, runtime secrets, datasets, or SourceOS image state. diff --git a/fixtures/invalid/functional-service.invalid-status.json b/fixtures/invalid/functional-service.invalid-status.json new file mode 100644 index 0000000..2876958 --- /dev/null +++ b/fixtures/invalid/functional-service.invalid-status.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": "functional-service.v1", + "service": { + "id": "speech-transcription-baseline", + "name": "Speech Transcription Baseline", + "ownerRepository": "SociOS-Linux/speechlab", + "status": "published", + "description": "Invalid status value 'published' - not in the allowed enum." + }, + "function": "speech", + "model": { + "modelRef": "oci://registry.example/socios/speech-transcription:0.1.0", + "mutableStatePolicy": "forbidden-in-sourceos" + }, + "inputs": ["audio/wav"], + "outputs": ["application/json+transcript"], + "evals": { + "required": true, + "references": [] + }, + "governance": { + "ledgerRequired": true, + "guardrailRequired": true, + "routingRequired": true + }, + "sourceosCarry": { + "allowed": true, + "carriesMutableModelState": false, + "clientRefRequired": true + } +} diff --git a/fixtures/invalid/functional-service.missing-schema-version.json b/fixtures/invalid/functional-service.missing-schema-version.json new file mode 100644 index 0000000..d9c2634 --- /dev/null +++ b/fixtures/invalid/functional-service.missing-schema-version.json @@ -0,0 +1,30 @@ +{ + "service": { + "id": "speech-transcription-baseline", + "name": "Speech Transcription Baseline", + "ownerRepository": "SociOS-Linux/speechlab", + "status": "experimental", + "description": "Missing schemaVersion - must fail validation." + }, + "function": "speech", + "model": { + "modelRef": "oci://registry.example/socios/speech-transcription:0.1.0", + "mutableStatePolicy": "forbidden-in-sourceos" + }, + "inputs": ["audio/wav"], + "outputs": ["application/json+transcript"], + "evals": { + "required": true, + "references": [] + }, + "governance": { + "ledgerRequired": true, + "guardrailRequired": true, + "routingRequired": true + }, + "sourceosCarry": { + "allowed": true, + "carriesMutableModelState": false, + "clientRefRequired": true + } +} diff --git a/fixtures/invalid/maturity.empty-owners.yaml b/fixtures/invalid/maturity.empty-owners.yaml new file mode 100644 index 0000000..47d2fa2 --- /dev/null +++ b/fixtures/invalid/maturity.empty-owners.yaml @@ -0,0 +1,20 @@ +schemaVersion: repo-maturity.v1 +repository: SociOS-Linux/examplelab +plane: socios-lab +status: incubating +canonicality: reference +owners: [] +maturity: + level: M1 + targetLevel: M3 + evidence: + - README.md states lab purpose and modality. +validation: + commands: + - make validate + ciRequired: true + lastKnownStatus: not-configured +integrations: + - repository: SocioProphet/functional-model-surfaces + relationship: emits functional-service.v1 manifests + required: true diff --git a/fixtures/invalid/maturity.invalid-level.yaml b/fixtures/invalid/maturity.invalid-level.yaml new file mode 100644 index 0000000..7b40312 --- /dev/null +++ b/fixtures/invalid/maturity.invalid-level.yaml @@ -0,0 +1,21 @@ +schemaVersion: repo-maturity.v1 +repository: SociOS-Linux/examplelab +plane: socios-lab +status: incubating +canonicality: reference +owners: + - SociOS-Linux +maturity: + level: M6 + targetLevel: M3 + evidence: + - README.md states lab purpose and modality. +validation: + commands: + - make validate + ciRequired: true + lastKnownStatus: not-configured +integrations: + - repository: SocioProphet/functional-model-surfaces + relationship: emits functional-service.v1 manifests + required: true diff --git a/repo.maturity.yaml b/repo.maturity.yaml index cbcf2f5..8590dfe 100644 --- a/repo.maturity.yaml +++ b/repo.maturity.yaml @@ -6,18 +6,22 @@ canonicality: canonical owners: - SocioProphet maturity: - level: M2 - targetLevel: M3 + level: M3 + targetLevel: M4 evidence: - README.md defines purpose, doctrine, role, maturity gates, and integration path. - schemas/repo-maturity.schema.json defines estate maturity record shape. - schemas/functional-service.schema.json defines functional service manifest shape. - examples/repo.maturity.example.yaml provides a reusable maturity record example. + - .github/workflows/validate.yml runs make validate on every PR and push to main. + - fixtures/invalid/ contains 4 negative fixtures verified by tools/run_negative_fixtures.py. + - docs/MATURITY_MODEL.md documents M0-M5 levels, evidence requirements, and Sociosphere ingestion. + - docs/LAB_TEMPLATE.md describes the required lab repo skeleton and functional-service.v1 manifest. validation: commands: - make validate ciRequired: true - lastKnownStatus: unknown + lastKnownStatus: passing integrations: - repository: SocioProphet/prophet-platform relationship: runtime consumer for graduated functional surfaces @@ -41,7 +45,7 @@ integrations: relationship: workspace governance and maturity ingestion required: true nextActions: - - Add CI validation workflow. - - Add schema fixtures and negative tests. + - Add versioned release artifacts with checksums for M4 readiness. + - Add SBOM or provenance attestation for release artifacts. - Register this repo in workspace-inventory/sociosphere scorecard. - Mirror lab template into SociOS modality lab repositories. diff --git a/tools/run_negative_fixtures.py b/tools/run_negative_fixtures.py new file mode 100644 index 0000000..2020240 --- /dev/null +++ b/tools/run_negative_fixtures.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Run negative fixture tests: each document must FAIL schema validation. + +Each entry is a (schema_path, fixture_path, format) tuple. +The script exits non-zero if any fixture unexpectedly passes validation. +""" +import json +import sys +from pathlib import Path + +try: + import jsonschema +except Exception as exc: # pragma: no cover + print(f"jsonschema import failed: {exc}", file=sys.stderr) + sys.exit(2) + +try: + import yaml +except Exception as exc: # pragma: no cover + print(f"PyYAML import failed: {exc}", file=sys.stderr) + sys.exit(2) + +FIXTURES = [ + ( + "schemas/functional-service.schema.json", + "fixtures/invalid/functional-service.missing-schema-version.json", + "json", + ), + ( + "schemas/functional-service.schema.json", + "fixtures/invalid/functional-service.invalid-status.json", + "json", + ), + ( + "schemas/repo-maturity.schema.json", + "fixtures/invalid/maturity.invalid-level.yaml", + "yaml", + ), + ( + "schemas/repo-maturity.schema.json", + "fixtures/invalid/maturity.empty-owners.yaml", + "yaml", + ), +] + + +def load(path: str, fmt: str): + text = Path(path).read_text() + if fmt == "yaml": + return yaml.safe_load(text) + return json.loads(text) + + +def main() -> int: + unexpected_passes = [] + for schema_file, doc_file, fmt in FIXTURES: + schema = json.loads(Path(schema_file).read_text()) + document = load(doc_file, fmt) + try: + jsonschema.validate(instance=document, schema=schema) + unexpected_passes.append(doc_file) + print(f"FAIL (expected validation error): {doc_file}", file=sys.stderr) + except jsonschema.ValidationError as exc: + print(f"ok (expected failure): {doc_file} — {exc.message}") + + if unexpected_passes: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())