Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 30 additions & 0 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: JS Build and Checks

on:
push:
pull_request:

jobs:
js:
name: Build JS package
runs-on: ubuntu-latest
defaults:
run:
working-directory: js/goblin-ontology
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: js/goblin-ontology/package.json
- name: Install
run: npm ci || npm i
- name: Typecheck
run: npm run typecheck
- name: Build
run: npm run build
- name: Test
run: npm test


71 changes: 71 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Validate Ontology and Docs

on:
push:
pull_request:

jobs:
python-validate:
name: Python SHACL validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install deps
run: |
python -m pip install --upgrade pip
pip install rdflib pyshacl
- name: Validate sample JSON-LD against SHACL
run: |
python - << 'PY'
from rdflib import Graph
from pyshacl import validate
data = Graph()
data.parse("examples/sample_score.json", format="json-ld")
shapes = Graph()
shapes.parse("goblin-shapes.ttl", format="turtle")
conforms, report_graph, report_text = validate(data_graph=data, shacl_graph=shapes, inference='rdfs', advanced=True)
print(report_text)
assert conforms, "SHACL validation failed"
PY
dot-check:
name: Render DOT and check committed artifacts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Graphviz
run: sudo apt-get update && sudo apt-get install -y graphviz
- name: Render goblin-map
run: |
dot -Tsvg goblin-map.dot -o docs/goblin-map.svg.new
dot -Tpng goblin-map.dot -o docs/goblin-map.png.new
Comment thread
cursor[bot] marked this conversation as resolved.
- name: Fail if diffs vs committed images
run: |
set -e
if ! cmp --silent docs/goblin-map.svg docs/goblin-map.svg.new; then
echo "docs/goblin-map.svg differs from generated. Please regenerate and commit."
exit 1
fi
if ! cmp --silent docs/goblin-map.png docs/goblin-map.png.new; then
echo "docs/goblin-map.png differs from generated. Please regenerate and commit."
exit 1
fi
docs-link:
name: Verify docs links
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check docs/kg.html references
run: |
set -e
test -f docs/goblin-map.svg
test -f docs/goblin-map.png
test -f goblin-ontology.ttl
test -f goblin-shapes.ttl
grep -q "goblin-map.svg" docs/kg.html
grep -q "goblin-ontology.ttl" docs/kg.html
grep -q "goblin-shapes.ttl" docs/kg.html


6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ This bundle contains:
- `goblin-agent-lim42.md` — lim42-ready agent spec for computing Goblin scores.
- `goblin-energy-gradient.md` — energy gradient interpretation with simple flow equations.

## Quick links

- Goblin map (rendered): see `docs/goblin-map.svg` and `docs/goblin-map.png`
- Ontology visualization (WebVOWL): open `docs/kg.html`

## Meme / Usage

Because "SCIG" is overloaded in Google (subcutaneous immunoglobulin), we use the meme name:
Expand All @@ -30,3 +35,4 @@ The Goblin score is defined in the ontology as `ui:goblinScore` and is an alias
- Push this bundle into `nkllon/goblin` as initial commit.
- Wire `goblin-agent-lim42.md` into lim42 as a reusable agent profile.
- Render `goblin-map.dot` with Graphviz to produce a PNG/SVG for documentation.

3 changes: 3 additions & 0 deletions docs/goblin-map.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions docs/goblin-map.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion docs/kg.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
<header>
<strong>Goblin KG Viewer</strong> —
<a href="../goblin-ontology.ttl">goblin-ontology.ttl</a> ·
<a href="../goblin-shapes.ttl">goblin-shapes.ttl</a>
<a href="../goblin-shapes.ttl">goblin-shapes.ttl</a> ·
<a href="./goblin-map.svg">goblin-map.svg</a>
<span style="margin-left:10px;color:#666;">Use the embedded WebVOWL app and load the TTL via its UI.</span>
</header>
<iframe src="https://service.tib.eu/webvowl/" title="WebVOWL"></iframe>
Expand Down
12 changes: 12 additions & 0 deletions examples/sample_input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Sample system description

Context: multi-service web app with async jobs, third-party APIs, and manual backoffice workflows.

Key points:
- Frontend models assume immediate consistency; actual system is eventually consistent.
- External payment API introduces retries and delayed webhooks.
- Backoffice users can override orders out-of-band.

Requested: compute the Goblin score and per-dimension notes using the lim42 agent profile.


16 changes: 16 additions & 0 deletions examples/sample_score.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"@context": {
"ui": "https://nkllon.com/ui#",
"prob": "https://nkllon.com/problem#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"type": "@type",
"goblinScore": { "@id": "ui:goblinScore", "@type": "xsd:decimal" }
},
"@id": "prob:ExampleSystem",
"type": "ui:UIClosureIllusionProblem",
"rdfs:label": "Example System",
"goblinScore": "0.73"
}


36 changes: 36 additions & 0 deletions goblin-energy-gradient.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,39 @@ In practice:
are high-leverage moves when their \( w_i \) are large.

Goblin score gives you a way to **prioritize** where to push on the system to reduce long-term misalignment energy.

## Numerical Scenarios

Assume weights (subset): \( w_{\text{closure}}=0.18 \), \( w_{\text{dist}}=0.20 \), \( w_{\text{async}}=0.15 \), \( w_{\text{security}}=0.10 \), \( w_{\text{domain}}=0.12 \), \( w_{\text{energy}}=0.25 \).

### Scenario A: Before → After (small targeted fix)
- Before: \( x_{\text{closure}}=0.9, x_{\text{dist}}=0.8, x_{\text{async}}=0.8, x_{\text{domain}}=0.6, x_{\text{security}}=0.7, x_{\text{energy}}=0.9 \)
- After (invest in domain invariants and observability): \( x_{\text{closure}}=0.8, x_{\text{dist}}=0.78, x_{\text{async}}=0.78, x_{\text{domain}}=0.4, x_{\text{security}}=0.7, x_{\text{energy}}=0.88 \)

Compute:
\[
G_{\text{before}} \approx 0.18(0.9)+0.20(0.8)+0.15(0.8)+0.12(0.6)+0.10(0.7)+0.25(0.9)=0.808
\]
\[
G_{\text{after}} \approx 0.18(0.8)+0.20(0.78)+0.15(0.78)+0.12(0.4)+0.10(0.7)+0.25(0.88)=0.724
\]
\[
\Delta G = -0.084 \;\;(\text{~10.4% reduction})
\]
With \( k=1 \), \( \Delta E=\Delta G \).

### Scenario B: Local optimization that backfires globally
- Before: same as Scenario A before.
- Local move: reduce latency by caching and suppressing validation → \( x_{\text{async}}=0.7 \) improves, but \( x_{\text{closure}}=0.95 \), \( x_{\text{domain}}=0.75 \) worsen; others unchanged.

Compute:
\[
G_{\text{local}} \approx 0.18(0.95)+0.20(0.8)+0.15(0.7)+0.12(0.75)+0.10(0.7)+0.25(0.9)=0.833

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Incorrect arithmetic in energy gradient scenario calculations

The numerical scenarios contain arithmetic errors in the Goblin score calculations. Line 104 claims G_before = 0.808 but the correct sum is 0.809. Line 107 claims G_after = 0.724 but the correct sum is 0.755 (error of 0.031, significantly affecting the delta calculation). Line 120 claims G_local = 0.833 but the correct sum is 0.821. These errors make the example scenarios misleading for understanding the energy gradient model.

Fix in Cursor Fix in Web

\]
\[
\Delta G = +0.025 \;\;(\text{worse})
\]

Tie-in to SHACL:
- The shapes constrain values like `ui:goblinScore` and ensure components (e.g., `ui:componentValue`) stay in \([0,1]\).
- Scenarios can be serialized as RDF/JSON-LD and validated against `goblin-shapes.ttl` to keep data consistent while experimenting.
4 changes: 4 additions & 0 deletions goblin_ontology/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__all__ = ["__version__"]
__version__ = "0.1.0"


67 changes: 67 additions & 0 deletions goblin_ontology/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from __future__ import annotations

import argparse
import sys
from pathlib import Path

from .validator import ValidationResult, validate


def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="goblin-validate",
description="Validate a Goblin score RDF/JSON-LD document against SHACL shapes.",
)
parser.add_argument(
"data",
help="Path to data graph (JSON-LD, TTL, RDF/XML, etc.).",
)
parser.add_argument(
"--shapes",
required=True,
help="Path to SHACL shapes TTL.",
)
parser.add_argument(
"--data-format",
default=None,
help="Optional explicit RDF format for data (e.g., json-ld, turtle).",
)
parser.add_argument(
"--shapes-format",
default="turtle",
help="Optional explicit RDF format for shapes (default: turtle).",
)
parser.add_argument(
"--inference",
default="rdfs",
choices=["none", "rdfs", "owlrl"],
help="Inference mode during validation (default: rdfs).",
)
parser.add_argument(
"--no-advanced",
action="store_true",
help="Disable advanced SHACL features.",
)
return parser


def main(argv: list[str] | None = None) -> int:
args = _build_parser().parse_args(argv)

result: ValidationResult = validate(
data_graph_path=Path(args.data),
shapes_graph_path=Path(args.shapes),
data_format=args.data_format,
shapes_format=args.shapes_format,
inference=args.inference,
advanced=not args.no_advanced,
)

print(result.report_text.strip())
return 0 if result.conforms else 1


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


Loading
Loading