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
17 changes: 17 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: validate

on:
push:
pull_request:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate graphlab
run: make validate
- name: Offline smoke test
run: make smoke
- name: Emit SourceOS carry fixture
run: make carry
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: validate smoke carry

validate:
python3 tools/validate.py

smoke:
python3 tools/smoke.py

carry:
python3 tools/emit_sourceos_carry.py > examples/sourceos-carry.graphlab.json
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
# graphlab
Linux-native lab workspace for graph embeddings, ontology alignment, semantic graph extraction, graph neural experiments, and knowledge-graph model tuning.

Linux-native lab workspace for graph, network, and knowledge-graph feature experiments for SocioProphet services.

## Role in the Holmes lab family

`graphlab` is the first governed pilot lab for the Holmes graph-intelligence stack. It emits a `functional-service.v1` manifest that can be consumed by:

- `SocioProphet/holmes` as the graph-intelligence product surface;
- `SocioProphet/functional-model-surfaces` as the normative service contract spine;
- `SocioProphet/sociosphere` as workspace registry and maturity controller;
- `SourceOS-Linux/sourceos-model-carry` as disabled-by-default SourceOS carry metadata;
- `SociOS-Linux/socios` as opt-in training/tuning orchestration;
- `SocioProphet/model-governance-ledger` and `SocioProphet/model-router` for promotion and routing.

This repository does not store model weights, mutable adapters, personal data, secrets, or promotion authority.

## Local validation

```bash
make validate
make smoke
make carry
```

The smoke path is deterministic and offline-only.
37 changes: 37 additions & 0 deletions evidence/smoke-receipt.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"schemaVersion": "graphlab.smoke-receipt.v0",
"kind": "SmokeReceipt",
"serviceId": "graphlab.holmes.graph-pilot",
"runId": "urn:socioprophet:graphlab:smoke:offline-lexical-v0",
"mode": "offline-deterministic",
"networkRequired": false,
"modelWeightsIncluded": false,
"mutableAdapterIncluded": false,
"inputRef": "examples/smoke-input.json",
"outputRef": "examples/smoke-output.json",
"assertions": [
{
"id": "top-result-holmes",
"description": "Holmes candidate ranks first for governed semantic search query.",
"pass": true
},
{
"id": "no-network",
"description": "Smoke runner uses only Python standard library and local fixtures.",
"pass": true
},
{
"id": "sourceos-no-mutable-state",
"description": "SourceOS carry path carries no model weights or mutable adapters.",
"pass": true
}
],
"governanceRefs": [
"SocioProphet/holmes",
"SocioProphet/functional-model-surfaces",
"SocioProphet/model-governance-ledger",
"SocioProphet/model-router",
"SourceOS-Linux/sourceos-model-carry",
"SocioProphet/sociosphere"
]
}
17 changes: 17 additions & 0 deletions examples/smoke-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"query": "governed semantic search",
"candidates": [
{
"id": "holmes",
"text": "Holmes investigates evidence with semantic search and governed retrieval."
},
{
"id": "weather",
"text": "The weather report predicts rain and wind tomorrow."
},
{
"id": "sourceos",
"text": "SourceOS carries signed service references and local-only model profiles."
}
]
}
22 changes: 22 additions & 0 deletions examples/smoke-output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"serviceId": "graphlab.holmes.graph-pilot",
"query": "governed semantic search",
"ranking": [
{
"id": "holmes",
"score": 3,
"matchedTerms": ["governed", "search", "semantic"]
},
{
"id": "sourceos",
"score": 0,
"matchedTerms": []
},
{
"id": "weather",
"score": 0,
"matchedTerms": []
}
],
"pass": true
}
54 changes: 54 additions & 0 deletions manifests/functional-service.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"schemaVersion": "functional-service.v1",
"service": {
"id": "graphlab.holmes.graph-pilot",
"name": "GraphLab Holmes Graph Pilot",
"ownerRepository": "SociOS-Linux/graphlab",
"status": "experimental",
"description": "Offline deterministic graph-feature and ranking pilot for Holmes graph-intelligence and SourceOS carry integration."
},
"function": "graph",
"model": {
"modelRef": "urn:socioprophet:functional-model:graphlab:deterministic-graph:v0",
"adapterRefs": [],
"runtime": "python-stdlib-offline-smoke",
"mutableStatePolicy": "forbidden-in-sourceos"
},
"inputs": [
"graph/query",
"graph/corpus"
],
"outputs": [
"graph/features",
"ranking/candidates",
"evidence/smoke-receipt"
],
"evals": {
"required": true,
"references": [
"examples/smoke-input.json",
"examples/smoke-output.json",
"evidence/smoke-receipt.example.json"
],
"minimumPromotionGate": "offline-smoke-similarity-ordering"
},
"governance": {
"ledgerRequired": true,
"guardrailRequired": true,
"routingRequired": true,
"policyRefs": [
"SocioProphet/model-governance-ledger",
"SocioProphet/guardrail-fabric",
"SocioProphet/model-router",
"SocioProphet/holmes"
]
},
"sourceosCarry": {
"allowed": true,
"carriesMutableModelState": false,
"clientRefRequired": true,
"launchProfileRefs": [
"urn:srcos:model-carry:functional-service:graphlab-holmes-pilot"
]
}
}
71 changes: 71 additions & 0 deletions tools/emit_sourceos_carry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
MANIFEST = ROOT / "manifests" / "functional-service.json"


def main() -> int:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
service = manifest["service"]
carry = {
"schemaVersion": "functional-service-carry.v0",
"kind": "FunctionalServiceCarry",
"carryId": "urn:srcos:model-carry:functional-service:graphlab-holmes-pilot",
"displayName": "GraphLab Holmes Pilot Carry Profile",
"service": {
"id": service["id"],
"ownerRepository": service["ownerRepository"],
"function": manifest["function"],
"manifestRef": "SociOS-Linux/graphlab:manifests/functional-service.json"
},
"sourceos": {
"includeInImageBuilds": True,
"enabledByDefault": False,
"activationRequiresOptIn": True,
"carriesMutableModelState": False,
"carriesModelWeights": False,
"clientRefRequired": True
},
"runtime": {
"mode": "local-client-reference",
"networkDefault": "denied",
"launchProfileRef": "urn:srcos:launch-profile:graphlab-holmes-pilot:disabled-v0",
"healthCheck": "make smoke"
},
"policy": {
"localOnlyDefault": True,
"sendPromptOffDeviceDefault": False,
"allowToolUse": False,
"allowNetwork": False,
"allowModelDownload": False,
"requiresSignedServiceReference": True,
"requiresLedgerForPromotion": True,
"requiresSignedIntentForPersonalTuning": True
},
"evidence": {
"emitRuntimeHealth": True,
"emitRoutingDecision": True,
"emitSmokeReceipt": True,
"emitPromptHashOnly": True,
"smokeReceiptRef": "SociOS-Linux/graphlab:evidence/smoke-receipt.example.json"
},
"integrations": {
"productSurface": "SocioProphet/holmes",
"workspaceController": "SocioProphet/sociosphere",
"contractSpine": "SocioProphet/functional-model-surfaces",
"carryAuthority": "SourceOS-Linux/sourceos-model-carry",
"optInAutomation": "SociOS-Linux/socios",
"governanceLedger": "SocioProphet/model-governance-ledger",
"router": "SocioProphet/model-router"
}
}
print(json.dumps(carry, indent=2, sort_keys=True))
return 0


if __name__ == "__main__":
raise SystemExit(main())
50 changes: 50 additions & 0 deletions tools/smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
INPUT = ROOT / "examples" / "smoke-input.json"
EXPECTED = ROOT / "examples" / "smoke-output.json"


def terms(text: str) -> set[str]:
return set(re.findall(r"[a-z0-9]+", text.lower()))


def rank(payload: dict) -> dict:
query = payload["query"]
query_terms = terms(query)
rows = []
for candidate in payload.get("candidates", []):
matched = sorted(query_terms & terms(candidate.get("text", "")))
rows.append({
"id": candidate["id"],
"score": len(matched),
"matchedTerms": matched,
})
rows.sort(key=lambda row: (-row["score"], row["id"]))
return {
"serviceId": "graphlab.holmes.graph-pilot",
"query": query,
"ranking": rows,
"pass": bool(rows and rows[0]["id"] == "holmes"),
}


def main() -> int:
payload = json.loads(INPUT.read_text(encoding="utf-8"))
actual = rank(payload)
expected = json.loads(EXPECTED.read_text(encoding="utf-8"))
if actual != expected:
print(json.dumps({"ok": False, "actual": actual, "expected": expected}, indent=2), file=sys.stderr)
return 1
print(json.dumps({"ok": True, "serviceId": actual["serviceId"], "top": actual["ranking"][0]}, indent=2))
return 0


if __name__ == "__main__":
raise SystemExit(main())
80 changes: 80 additions & 0 deletions tools/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]

REQUIRED_FILES = [
"README.md",
"Makefile",
"manifests/functional-service.json",
"examples/smoke-input.json",
"examples/smoke-output.json",
"evidence/smoke-receipt.example.json",
"tools/smoke.py",
"tools/emit_sourceos_carry.py",
]

EXPECTED_SERVICE_ID = "graphlab.holmes.graph-pilot"
EXPECTED_FUNCTION = "graph"


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


def load_json(path: str) -> dict:
return json.loads((ROOT / path).read_text(encoding="utf-8"))


def main() -> int:
for rel in REQUIRED_FILES:
if not (ROOT / rel).exists():
return fail(f"missing required file: {rel}")

manifest = load_json("manifests/functional-service.json")
if manifest.get("schemaVersion") != "functional-service.v1":
return fail("manifest must use functional-service.v1")
if manifest.get("function") != EXPECTED_FUNCTION:
return fail("manifest function must be graph")
service = manifest.get("service", {})
if service.get("id") != EXPECTED_SERVICE_ID:
return fail("manifest service.id mismatch")
if service.get("ownerRepository") != "SociOS-Linux/graphlab":
return fail("manifest ownerRepository mismatch")

sourceos = manifest.get("sourceosCarry", {})
if sourceos.get("allowed") is not True:
return fail("sourceosCarry.allowed must be true")
if sourceos.get("carriesMutableModelState") is not False:
return fail("SourceOS carry must not carry mutable model state")
if sourceos.get("clientRefRequired") is not True:
return fail("SourceOS carry must require a client ref")

output = load_json("examples/smoke-output.json")
if output.get("serviceId") != EXPECTED_SERVICE_ID:
return fail("smoke output serviceId mismatch")
ranking = output.get("ranking", [])
if not ranking or ranking[0].get("id") != "holmes":
return fail("smoke output must rank holmes first")
if output.get("pass") is not True:
return fail("smoke output pass must be true")

receipt = load_json("evidence/smoke-receipt.example.json")
if receipt.get("networkRequired") is not False:
return fail("smoke receipt must be offline")
if receipt.get("modelWeightsIncluded") is not False:
return fail("smoke receipt must not include model weights")
if receipt.get("mutableAdapterIncluded") is not False:
return fail("smoke receipt must not include mutable adapters")

print("OK: graphlab manifest and fixtures validated")
return 0


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