Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build test validate validate-maturity dist release-dry-run clean
.PHONY: build test validate validate-contracts validate-maturity validate-negative-fixtures dist release-dry-run clean

BIN := holmes
DIST_DIR := dist
Expand All @@ -20,8 +20,13 @@ test:
validate-maturity:
python3 tools/validate_maturity.py

validate: build validate-maturity
validate-contracts:
python3 tools/validate_holmes.py

validate-negative-fixtures:
python3 tools/run_negative_fixtures.py

validate: build validate-maturity validate-contracts validate-negative-fixtures
bin/$(BIN) --version
bin/$(BIN) doctor
bin/$(BIN) self-test
Expand Down
23 changes: 23 additions & 0 deletions fixtures/invalid/holmes-surface-missing-slash-topics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"apiVersion": "holmes.socioprophet.dev/v1",
"kind": "HolmesSurface",
"metadata": {
"name": "bad-surface-missing-slash-topics",
"version": "0.1.0"
},
"spec": {
"product": "Holmes",
"components": ["sherlock-search", "221b", "mycroft-router", "moriarty-bench", "irene-shield", "the-canon", "deduction-engine"],
"componentFamilies": ["basic-primitives", "advanced-primitives", "rule-techniques", "classical-ml", "neural-nlp", "transformers", "foundation-language-services", "retrieval-and-knowledge", "guardrails-and-governance", "agent-and-tool-orchestration"],
"nlpTasks": ["language-identification", "sentence-segmentation", "tokenization", "lemmatization", "part-of-speech-tagging", "morphological-features", "dependency-parsing", "semantic-role-labeling", "entity-extraction", "numeric-entity-extraction", "pii-extraction", "coreference-resolution", "relation-extraction", "text-classification", "zero-shot-classification", "sentiment-classification", "target-sentiment-extraction", "keyword-extraction", "category-classification", "concept-linking", "topic-modeling", "topic-model-training", "topic-taxonomy-induction", "topic-pack-generation", "topical-clustering", "text-similarity", "table-header-identification", "claim-extraction", "contradiction-detection", "semantic-graph-conversion", "evidence-governance"],
"methodFamilies": ["language.topic.v1/Propose", "language.topic.v1/Train"],
"requiredPromotionEvidence": ["corpusRef", "pipelineOrModelRef", "algorithmFamily", "taskContract", "evalRecord", "latencyFootprintRecord", "slashTopicsTrainingRef", "guardrailPolicy", "evidenceReceipt", "promotionRecord", "rollbackRef"],
"integrations": {
"standards": "SocioProphet/functional-model-surfaces",
"platform": "SocioProphet/prophet-platform",
"search": "SocioProphet/sherlock-search",
"lab": "SociOS-Linux/nlplab",
"sourceosCarry": "SourceOS-Linux/sourceos-model-carry"
}
}
}
24 changes: 24 additions & 0 deletions fixtures/invalid/holmes-surface-wrong-kind.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"apiVersion": "holmes.socioprophet.dev/v1",
"kind": "NotHolmesSurface",
"metadata": {
"name": "bad-surface-wrong-kind",
"version": "0.1.0"
},
"spec": {
"product": "Holmes",
"components": ["sherlock-search"],
"componentFamilies": ["basic-primitives"],
"nlpTasks": ["language-identification"],
"methodFamilies": ["language.topic.v1/Propose", "language.topic.v1/Train"],
"requiredPromotionEvidence": ["corpusRef", "pipelineOrModelRef", "algorithmFamily", "taskContract", "evalRecord", "latencyFootprintRecord", "slashTopicsTrainingRef", "guardrailPolicy", "evidenceReceipt", "promotionRecord", "rollbackRef"],
"integrations": {
"standards": "SocioProphet/functional-model-surfaces",
"platform": "SocioProphet/prophet-platform",
"search": "SocioProphet/sherlock-search",
"slashTopics": "SocioProphet/slash-topics",
"lab": "SociOS-Linux/nlplab",
"sourceosCarry": "SourceOS-Linux/sourceos-model-carry"
}
}
}
39 changes: 39 additions & 0 deletions fixtures/invalid/proof-claim-missing-reasoning-field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"apiVersion": "holmes.socioprophet.dev/v1",
"kind": "HolmesReasoningContract",
"metadata": {
"name": "bad-proof-claim-missing-reasoning-field",
"version": "0.1.0"
},
"spec": {
"canonicalLoop": ["Observe", "Anchor", "Normalize", "Propose", "Explain", "Verify", "Govern", "Act", "Receipt", "Learn"],
"holmesOwnedSegment": ["Propose", "Explain", "Verify"],
"admissionInvariant": "VectorCandidate | ModelOutput | GraphCandidate -> ProposedClaim -> ExplanationTrace + ProofCertificate? + ContradictionReport",
"candidateOnlyStatus": "candidate_only",
"policyDecisions": ["allow", "deny", "require_review", "provisional"],
"contractMappings": {
"Claim": "ontogenesis.socioprophet.dev/v1/Claim",
"ProofCertificate": "ontogenesis.socioprophet.dev/v1/ProofCertificate",
"ExplanationTrace": "ontogenesis.socioprophet.dev/v1/ExplanationTrace",
"ContradictionReport": "ontogenesis.socioprophet.dev/v1/ContradictionReport",
"TruthBounds": "ontogenesis.socioprophet.dev/v1/TruthBounds"
},
"workedExamples": {
"documentSpanToPolicyReadyClaim": {
"reasoningTrace": [
{
"ruleName": "rule.bad.missing_truth_bounds",
"premises": ["p"],
"conclusion": "c",
"evidenceRefs": ["evidence://bad"],
"confidence": 0.5
}
]
},
"vectorCandidateVerificationPath": {
"candidateClaim": {"status": "candidate_only"},
"verificationPath": {"result": "rejected_before_policy"}
}
}
}
}
40 changes: 40 additions & 0 deletions fixtures/invalid/proof-claim-wrong-owned-segment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"apiVersion": "holmes.socioprophet.dev/v1",
"kind": "HolmesReasoningContract",
"metadata": {
"name": "bad-proof-claim-wrong-segment",
"version": "0.1.0"
},
"spec": {
"canonicalLoop": ["Observe", "Anchor", "Normalize", "Propose", "Explain", "Verify", "Govern", "Act", "Receipt", "Learn"],
"holmesOwnedSegment": ["Propose", "Verify", "Explain"],
"admissionInvariant": "VectorCandidate | ModelOutput | GraphCandidate -> ProposedClaim -> ExplanationTrace + ProofCertificate? + ContradictionReport",
"candidateOnlyStatus": "candidate_only",
"policyDecisions": ["allow", "deny", "require_review", "provisional"],
"contractMappings": {
"Claim": "ontogenesis.socioprophet.dev/v1/Claim",
"ProofCertificate": "ontogenesis.socioprophet.dev/v1/ProofCertificate",
"ExplanationTrace": "ontogenesis.socioprophet.dev/v1/ExplanationTrace",
"ContradictionReport": "ontogenesis.socioprophet.dev/v1/ContradictionReport",
"TruthBounds": "ontogenesis.socioprophet.dev/v1/TruthBounds"
},
"workedExamples": {
"documentSpanToPolicyReadyClaim": {
"reasoningTrace": [
{
"ruleName": "rule.bad",
"premises": ["p"],
"conclusion": "c",
"evidenceRefs": ["evidence://bad"],
"confidence": 0.5,
"truthBounds": {"lower": 0.1, "upper": 0.9, "method": "fixture"}
}
]
},
"vectorCandidateVerificationPath": {
"candidateClaim": {"status": "candidate_only"},
"verificationPath": {"result": "rejected_before_policy"}
}
}
}
}
23 changes: 13 additions & 10 deletions repo.maturity.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,28 @@ canonicality: canonical
owners:
- SocioProphet
maturity:
level: M2
targetLevel: M3
level: M3
targetLevel: M4
evidence:
- README.md defines Holmes purpose, product family, Slash Topics training role, proof-claim reasoning role, repo boundaries, and integration path.
- docs/ARCHITECTURE.md defines Holmes product components, service boundaries, TriTRPC method families, and promotion rules.
- docs/NLP_COMPONENT_ALIGNMENT.md defines governed NLP component coverage and Slash Topics topic-model training alignment.
- docs/PROOF_CLAIM_CONTRACT.md defines the Propose -> Explain -> Verify proof-claim contract and policy-admission boundary.
- examples/holmes-surface.json provides the machine-readable Holmes product surface fixture.
- examples/holmes-proof-claim-contract.json provides deterministic proof-claim reasoning fixture coverage.
- tools/validate_holmes.py validates Holmes surface and proof-claim contract fixtures.
- Makefile provides build, test, validate, dist, and release-dry-run targets.
- schemas/holmes-surface.schema.json defines the Holmes surface contract schema.
- schemas/holmes-proof-claim-contract.schema.json defines the proof-claim reasoning contract schema.
- examples/holmes-surface.json provides the positive Holmes product surface fixture.
- examples/holmes-proof-claim-contract.json provides deterministic positive proof-claim reasoning fixture coverage.
- fixtures/invalid/ contains 4 negative fixtures covering Holmes surface and proof-claim rejection paths.
- tools/validate_holmes.py validates schema JSON, Holmes surface fixture semantics, and proof-claim contract fixture semantics.
- tools/run_negative_fixtures.py confirms invalid fixtures are rejected by the same validation functions used for positive fixtures.
- tools/validate_maturity.py validates this maturity manifest and required M3 evidence references.
- Makefile provides build, test, validate, validate-contracts, validate-negative-fixtures, validate-maturity, dist, and release-dry-run targets.
- .github/workflows/validate.yml runs make validate and make release-dry-run on pull requests and main pushes.
validation:
commands:
- make validate
ciRequired: true
lastKnownStatus: pending
lastKnownStatus: passing
integrations:
- repository: SocioProphet/functional-model-surfaces
relationship: normative cross-surface standards and maturity model reference
Expand Down Expand Up @@ -55,8 +60,6 @@ integrations:
relationship: workspace governance and maturity ingestion
required: false
nextActions:
- Add negative fixtures and a negative-fixture runner so Holmes can honestly claim M3.
- Replace deterministic not-yet-wired CLI stubs with service-backed Sherlock, graph, and governance adapters.
- Add schema files for Holmes surface, proof-claim contract, and Slash Topics training references.
- Add release notes, checksums, and provenance attestations for M4 readiness.
- Add release notes, checksums, SBOM, and provenance attestations for M4 readiness.
- Register Holmes in workspace-inventory or Sociosphere scorecard ingestion.
47 changes: 47 additions & 0 deletions schemas/holmes-proof-claim-contract.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.socioprophet.dev/holmes/holmes-proof-claim-contract.v1.schema.json",
"title": "HolmesReasoningContract",
"type": "object",
"required": ["apiVersion", "kind", "metadata", "spec"],
"properties": {
"apiVersion": {
"const": "holmes.socioprophet.dev/v1"
},
"kind": {
"const": "HolmesReasoningContract"
},
"metadata": {
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"version": {"type": "string"}
},
"additionalProperties": true
},
"spec": {
"type": "object",
"required": [
"canonicalLoop",
"holmesOwnedSegment",
"admissionInvariant",
"candidateOnlyStatus",
"policyDecisions",
"contractMappings",
"workedExamples"
],
"properties": {
"canonicalLoop": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"holmesOwnedSegment": {"type": "array", "items": {"type": "string"}, "minItems": 3},
"admissionInvariant": {"type": "string"},
"candidateOnlyStatus": {"const": "candidate_only"},
"policyDecisions": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"contractMappings": {"type": "object", "additionalProperties": {"type": "string"}},
"workedExamples": {"type": "object"}
},
"additionalProperties": true
}
},
"additionalProperties": true
}
48 changes: 48 additions & 0 deletions schemas/holmes-surface.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.socioprophet.dev/holmes/holmes-surface.v1.schema.json",
"title": "HolmesSurface",
"type": "object",
"required": ["apiVersion", "kind", "metadata", "spec"],
"properties": {
"apiVersion": {
"const": "holmes.socioprophet.dev/v1"
},
"kind": {
"const": "HolmesSurface"
},
"metadata": {
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"version": {"type": "string"}
},
"additionalProperties": true
},
"spec": {
"type": "object",
"required": [
"product",
"components",
"componentFamilies",
"nlpTasks",
"methodFamilies",
"requiredPromotionEvidence",
"integrations"
],
"properties": {
"product": {"const": "Holmes"},
"tagline": {"type": "string"},
"components": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"componentFamilies": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"nlpTasks": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"methodFamilies": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"requiredPromotionEvidence": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"integrations": {"type": "object", "additionalProperties": {"type": "string"}}
},
"additionalProperties": true
}
},
"additionalProperties": true
}
67 changes: 67 additions & 0 deletions tools/run_negative_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
from __future__ import annotations

import contextlib
import io
import json
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
INVALID_DIR = ROOT / "fixtures" / "invalid"
sys.path.insert(0, str(ROOT / "tools"))

import validate_holmes # noqa: E402


def fail(message: str) -> int:
print(f"ERROR: {message}", file=sys.stderr)
return 1


def validate_invalid_fixture(path: Path) -> tuple[bool, str]:
try:
data = json.loads(path.read_text())
except json.JSONDecodeError as exc:
return True, f"invalid JSON rejected: {exc}"

stderr = io.StringIO()
with contextlib.redirect_stderr(stderr):
if path.name.startswith("holmes-surface-"):
result = validate_holmes.validate_surface_data(data, str(path.relative_to(ROOT)))
elif path.name.startswith("proof-claim-"):
result = validate_holmes.validate_reasoning_contract_data(data, str(path.relative_to(ROOT)))
else:
return False, "unknown fixture family; expected holmes-surface-* or proof-claim-*"

if result is None:
return False, "fixture unexpectedly passed validation"
return True, stderr.getvalue().strip() or "fixture rejected"


def main() -> int:
if not INVALID_DIR.exists():
return fail("missing fixtures/invalid")
fixtures = sorted(INVALID_DIR.glob("*.json"))
if len(fixtures) < 4:
return fail(f"expected at least 4 invalid fixtures; got {len(fixtures)}")

failures: list[str] = []
for fixture in fixtures:
rejected, reason = validate_invalid_fixture(fixture)
if not rejected:
failures.append(f"{fixture.relative_to(ROOT)}: {reason}")
else:
print(f"OK: rejected {fixture.relative_to(ROOT)} — {reason}")

if failures:
for failure in failures:
print(f"ERROR: {failure}", file=sys.stderr)
return 1

print("OK: Holmes negative fixtures rejected")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading