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
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
python-version: "3.12"
- name: Install private-key scanner
run: python3 -m pip install pre-commit==4.6.1 pyyaml
run: python3 -m pip install pre-commit==4.6.1 pyyaml rdflib==7.6.0
- name: Private-key refusal controls and tracked-file scan
run: python3 scripts/check-private-keys.py

Expand All @@ -31,6 +31,12 @@ jobs:
- name: Crates publication contract
run: python3 scripts/test-crates-workflow.py

- name: Repository pendant source and refusal controls
run: |
python3 scripts/test-repository-share-query.py
python3 scripts/test-repository-knowledge.py
python3 scripts/test-repository-share-publication.py

- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler cmake g++

Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -658,3 +658,23 @@ jobs:
with:
expected-version: ${{ steps.want.outputs.version }}
dry-run: 'false'

repository-pendant:
name: Verify repository pendant
needs: [release-please, release]
if: always() && needs.release.result == 'success'
runs-on: ubuntu-22.04
timeout-minutes: 45
permissions:
contents: read
env:
VERSION: ${{ inputs.version || (startsWith(github.ref, 'refs/tags/') && github.ref_name) || needs.release-please.outputs.tag_name }}
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout exact published source
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: refs/tags/${{ env.VERSION }}
fetch-depth: 0
- name: Require repository-owned trusted-host pendant
run: python3 scripts/verify-repository-share.py "$VERSION" "$(git rev-parse HEAD)"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ book/_build/
*.db
.beads-credential-key
*.gate.lock*

# Quipu local databases and signing state are never repository source.
/.quipu/
35 changes: 35 additions & 0 deletions docs/knowledge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Repository pendant source

The repository-owned release job exports code and document **pointers**, plus
reviewed summaries of ideas, interfaces, decisions, freshness and starting points.
It does not export source chunk text or line spans. Source witnesses bind each
summary to its defining tracked file. The native index must resolve every
curated pointer before the build can proceed. Stored queries and validating
shapes travel with the share.

`alignments.ttl` is currently a valid empty SSSOM/RDF carrier. Its accompanying
status file explicitly records zero approved mappings and seals those bytes.
This is not evidence of global absence. Missing, changed or malformed input
refuses; an explicit empty carrier does not. Dependencies and glossary references
are not identity alignments. Code/commit deduplication and unrelated exactMatch
records are not cross-repository mapping approval.

Candidate mappings must go through Quipu's existing `align propose` and
`align decide` curation path. Keep their source graphs and source witnesses;
do not fill an approval author from a lexical score. Only the reviewed mapping
record may produce derived identity through the engine's existing apply path.
The current source profile deliberately does not import arbitrary nonempty
mapping data until an actual curated artifact and its transport are reviewed.

The copied release workflow verifies the three public assets only after the
ordinary release finishes. The managed trusted host runs this repository's own
publisher, keeps its publication policy private, pins both native engine
executables and the indexer, and refuses retained uncertain upload outcomes.
Default configuration still publishes only the existing Quipu corpus. Enabling
another repository requires its actual trusted host configuration and a clean
release containing this job; committing this source does not publish an asset.

The publisher requires the engine's model/digest/dimension manifest recipe.
Loading and vector rebuilding belong to the native engine. No hosted job gets
private publication policy or a new credential route. These source controls do
not establish public asset availability, live loading or service acceptance.
8 changes: 8 additions & 0 deletions docs/knowledge/alignment-status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"status": "no-approved-mappings",
"approved_record_count": 0,
"carrier_sha256": "c345ddc89d879d93150851807656815c8139252ac799c139db8b6d24d3ec3a38",
"scope": "scbrown/bobbin repository pendant source",
"basis": "No verified approved artifact or pairs supplied at source review; empty carrier is explicit. This is not a global absence claim.",
"curation_contract": "https://github.com/scbrown/quipu/blob/main/docs/design/cross-graph-alignment.md"
}
6 changes: 6 additions & 0 deletions docs/knowledge/alignments.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SSSOM/RDF carrier: explicitly no approved Bobbin cross-stack mappings.
# This is an empty mapping graph, not an absent/corrupt input or a claim that
# no mappings exist anywhere. Candidate records require the existing curator
# path before publication as accepted mappings. Dependencies remain dependencies.
@prefix sssom: <https://w3id.org/sssom/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
138 changes: 138 additions & 0 deletions docs/knowledge/repository-knowledge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"source_revision": "b036433f55b75ddb8520d5bf4ad0b681464187eb",
"reviewed_at": "2026-10-20T22:30:00Z",
"max_age_seconds": 2592000,
"records": [
{
"kind": "idea",
"source": "docs/book/src/architecture/overview.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/docs%2Fbook%2Fsrc%2Farchitecture%2Foverview.md#architecture-overview",
"summary": "Bobbin keeps code context local: structural parsing supplies file and symbol pointers, local embeddings supply semantic retrieval, and temporal coupling supplies change context.",
"source_sha256": "e44cb5a7fa67f01057da8566424a168abde6ce642b7c918baa785365d6805eb7",
"type": "Concept"
},
{
"kind": "idea",
"source": "docs/book/src/architecture/overview.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/docs%2Fbook%2Fsrc%2Farchitecture%2Foverview.md#query-pipeline",
"summary": "Search combines semantic and keyword retrieval; context assembly adds related files rather than treating a search hit as the complete task context.",
"source_sha256": "e44cb5a7fa67f01057da8566424a168abde6ce642b7c918baa785365d6805eb7",
"type": "Concept"
},
{
"kind": "interface",
"source": "src/cli/mod.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fcli%2Fmod.rs",
"summary": "The Commands enum defines the public command surface, including search, context and portable index pack operations.",
"source_sha256": "21c85d361c229d3c536c329ee4cb43ff8eb2b0679e4663ea2e5cd32a10fc5577",
"properties": {
"interfaceKind": "CLI",
"publicName": "bobbin Commands enum"
}
},
{
"kind": "interface",
"source": "src/mcp/server.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fmcp%2Fserver.rs",
"summary": "The MCP server advertises the tool interface; defining source is the authority for tool names, inputs and feature conditions.",
"source_sha256": "80696a61fd9220297d45e3ac65f791a18911de0db0add6b0030cec4dd76c443f",
"properties": {
"interfaceKind": "MCP",
"publicName": "Bobbin MCP tools"
}
},
{
"kind": "interface",
"source": "src/http/mod.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fhttp%2Fmod.rs",
"summary": "The HTTP module defines the public REST service and route composition; it is distinct from the stdio MCP transport.",
"source_sha256": "24b6cc34852c375938cc9db7c35a77db3d8655935e80b4ad2946015eec7f4d6b",
"properties": {
"interfaceKind": "REST",
"publicName": "Bobbin HTTP service"
}
},
{
"kind": "interface",
"source": "src/config.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fconfig.rs",
"summary": "Configuration types define keys and defaults, including embedding.backend, embedding.model and embedding.batch_size.",
"source_sha256": "cf69c283d9af17e4ccd1b9181dc6673b48cd4e7c7b7394bf4ba13af7c61958b0",
"properties": {
"interfaceKind": "config",
"publicName": "Bobbin configuration types"
}
},
{
"kind": "interface",
"source": "src/cli/pack.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fcli%2Fpack.rs",
"summary": "Portable Bobbin index packs are a separate file format from Quipu repository pendants; pack operations are defined here.",
"source_sha256": "40afc73061ad6af9564df14d5ebdf2274d71c3e42101dec937c74cd9222f0406",
"properties": {
"interfaceKind": "file-format",
"publicName": "Bobbin portable index pack"
}
},
{
"kind": "interface",
"source": "src/knowledge/mod.rs",
"subject": "http://aegis.gastown.local/ontology/code/bobbin/src%2Fknowledge%2Fmod.rs",
"summary": "The optional knowledge feature integrates with the Quipu crate. This dependency is not an identity assertion between the two repositories.",
"source_sha256": "fc41f9d18e06107ea72c335cc4b9fbd4114a8fc7dcb785f5e4eaa177d76a897c",
"properties": {
"interfaceKind": "library",
"publicName": "Optional Quipu knowledge integration"
},
"depends_on": [
"http://aegis.gastown.local/ontology/code/quipu/src%2Flib.rs"
]
},
{
"kind": "decision",
"source": "CONTRIBUTING.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/CONTRIBUTING.md#feature-integration-checklist",
"summary": "New retrieval signals must be considered for context assembly and its MCP and CLI interfaces. The published contributor checklist is the source of this active guidance.",
"source_sha256": "791ee33f6bc3f9dd9ca7248e2b19b4a154f1853ac1b4e2e14ab1d1b959bd7e5e",
"type": "DecisionRecord",
"properties": {
"status": "active",
"supersessionStatus": "not-recorded-in-source",
"glossaryTerm": "Decision"
},
"see_also": [
"https://scbrown.github.io/quechua/ns#Decision"
]
},
{
"kind": "start-here",
"source": "README.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/README.md",
"summary": "Start with installation and the first successful search, then read architecture and contributor guidance.",
"source_sha256": "8c2e3973751a8dd7e983cd1a35174b098d5c39bcf74146cd07114d718337737c",
"properties": {
"startHereOrder": "1"
}
},
{
"kind": "start-here",
"source": "docs/book/src/architecture/overview.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/docs%2Fbook%2Fsrc%2Farchitecture%2Foverview.md",
"summary": "Read the architecture overview for the indexing, search and optional knowledge graph boundaries.",
"source_sha256": "e44cb5a7fa67f01057da8566424a168abde6ce642b7c918baa785365d6805eb7",
"properties": {
"startHereOrder": "2"
}
},
{
"kind": "start-here",
"source": "CONTRIBUTING.md",
"subject": "http://aegis.gastown.local/ontology/doc/bobbin/CONTRIBUTING.md",
"summary": "Read contributor gates and the feature integration checklist before changing retrieval behavior.",
"source_sha256": "791ee33f6bc3f9dd9ca7248e2b19b4a154f1853ac1b4e2e14ab1d1b959bd7e5e",
"properties": {
"startHereOrder": "3"
}
}
]
}
52 changes: 52 additions & 0 deletions docs/knowledge/repository-knowledge.shapes.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
@prefix a: <http://aegis.gastown.local/ontology/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix sssom: <https://w3id.org/sssom/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix prov: <http://www.w3.org/ns/prov#> .

# This profile constrains only explicitly curated repository knowledge. It
# does not turn every imported Concept or CodeModule into a Bobbin record.
a:BobbinKnowledgeProfile a sh:NodeShape ;
sh:targetSubjectsOf a:knowledgeKind ;
sh:property [ sh:path rdfs:label ; sh:minCount 1 ; ] ;
sh:property [ sh:path rdfs:comment ; sh:minCount 1 ; sh:datatype xsd:string ; ] ;
sh:property [ sh:path a:knowledgeKind ; sh:minCount 1 ;
sh:in ( "idea" "interface" "decision" "start-here" ) ; ] ;
sh:property [ sh:path a:filePath ; sh:minCount 1 ; sh:datatype xsd:string ; ] ;
sh:property [ sh:path a:reviewAfter ; sh:minCount 1 ; sh:maxCount 1 ; sh:datatype xsd:dateTime ; ] ;
sh:property [ sh:path a:sourceHash ; sh:minCount 1 ; sh:maxCount 1 ; sh:pattern "^[0-9a-f]{64}$" ; ] ;
# maxAge is seconds since the explicit source review, not an eviction rule.
sh:property [ sh:path a:maxAge ; sh:minCount 1 ; sh:maxCount 1 ; sh:datatype xsd:integer ; sh:minExclusive 0 ; ] ;
sh:property [ sh:path prov:wasDerivedFrom ; sh:minCount 1 ; sh:nodeKind sh:IRI ; ] .

a:BobbinInterfaceProfile a sh:NodeShape ;
sh:targetSubjectsOf a:interfaceKind ;
sh:property [ sh:path rdfs:label ; sh:minCount 1 ; ] ;
sh:property [ sh:path a:interfaceKind ; sh:minCount 1 ; sh:maxCount 1 ;
sh:in ( "CLI" "MCP" "REST" "config" "file-format" "library" ) ; ] ;
sh:property [ sh:path a:publicName ; sh:minCount 1 ; sh:datatype xsd:string ; ] ;
sh:property [ sh:path a:depends_on ; sh:nodeKind sh:IRI ; ] .

a:BobbinDecisionProfile a sh:NodeShape ;
sh:targetSubjectsOf a:supersessionStatus ;
sh:property [ sh:path rdfs:label ; sh:minCount 1 ; ] ;
sh:property [ sh:path a:status ; sh:minCount 1 ; sh:maxCount 1 ; sh:in ( "active" "superseded" "proposed" ) ; ] ;
sh:property [ sh:path a:supersessionStatus ; sh:minCount 1 ; sh:maxCount 1 ;
sh:in ( "not-recorded-in-source" "recorded" ) ; ] .

# Existing SSSOM/RDF is a record, not a second identity-assertion format. The
# native align decide/apply path remains the only way to derive identity knots.
a:BobbinMappingRecordProfile a sh:NodeShape ;
sh:targetSubjectsOf sssom:subject_id ;
sh:property [ sh:path rdfs:label ; sh:minCount 1 ; ] ;
sh:property [ sh:path sssom:subject_id ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; ] ;
sh:property [ sh:path sssom:object_id ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; ] ;
sh:property [ sh:path sssom:predicate_id ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; ] ;
sh:property [ sh:path sssom:mapping_justification ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; ] .

a:BobbinNoHandAuthoredIdentity a sh:NodeShape ;
sh:targetSubjectsOf owl:sameAs ;
sh:property [ sh:path rdfs:label ; sh:minCount 1 ; ] ;
sh:property [ sh:path owl:sameAs ; sh:maxCount 0 ; ] .
32 changes: 32 additions & 0 deletions docs/knowledge/stored-queries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[
{
"name": "repository_ideas",
"description": "Repository-owned repository ideas",
"template": "PREFIX a: <http://aegis.gastown.local/ontology/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { ?s a a:Concept; rdfs:comment ?summary . FILTER(STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/doc/bobbin/\") || STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/code/bobbin/\")) } ORDER BY ?s LIMIT 100",
"params": []
},
{
"name": "repository_interfaces",
"description": "Repository-owned repository interfaces",
"template": "PREFIX a: <http://aegis.gastown.local/ontology/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { ?s a:interfaceKind ?kind; a:publicName ?name . FILTER(STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/doc/bobbin/\") || STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/code/bobbin/\")) } ORDER BY ?s LIMIT 100",
"params": []
},
{
"name": "repository_decisions",
"description": "Repository-owned repository decisions",
"template": "PREFIX a: <http://aegis.gastown.local/ontology/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { ?s a a:DecisionRecord; a:status ?status . OPTIONAL { ?s <http://purl.org/dc/terms/isReplacedBy> ?successor } FILTER(STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/doc/bobbin/\") || STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/code/bobbin/\")) } ORDER BY ?s LIMIT 100",
"params": []
},
{
"name": "repository_start_here",
"description": "Repository-owned repository start here",
"template": "PREFIX a: <http://aegis.gastown.local/ontology/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { ?s a:startHereOrder ?order . FILTER(STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/doc/bobbin/\") || STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/code/bobbin/\")) } ORDER BY ?s LIMIT 100",
"params": []
},
{
"name": "repository_freshness",
"description": "Repository-owned repository freshness",
"template": "PREFIX a: <http://aegis.gastown.local/ontology/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { ?s a:reviewAfter ?reviewAfter; a:maxAge ?maxAgeSeconds . FILTER(STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/doc/bobbin/\") || STRSTARTS(STR(?s), \"http://aegis.gastown.local/ontology/code/bobbin/\")) } ORDER BY ?s LIMIT 100",
"params": []
}
]
Loading
Loading