GitHub-backed graph versioning with Memento history UI (P2.2 MVP) - #350
Merged
Conversation
Mirror named graphs of versioning-enabled dataspaces into GitHub repositories. Each document write (POST/PUT/PATCH/DELETE) schedules an async reconcile task that re-reads the graph from the store and commits it as a sorted N-Triples file, authored with the agent's WebID; a gone graph deletes the file. Commits are chained per file path so they never race the Contents API's SHA-based optimistic locking. Best-effort by design: failures are logged, responses are never delayed or failed. Historical versions are retrievable via GET ?version=<commit-sha>, with Memento-Datetime, a git ETag, and immutable Cache-Control; they inherit the live document's access control. Configured per dataspace via lapp:versioningRepository -> doap:GitRepository in config/system.trig, with the fine-grained access token supplied as a:authToken in secrets/credentials.trig. The GitHub client runs on a new hostname-verified HTTP client factory (the existing clients disable hostname verification against a truststore that includes public CAs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Docker secret mounts as extensionless /run/secrets/credentials, and riot picks the parser by file extension - the merge silently exited 1 and killed the container on startup. Never surfaced before because the credentials secret had not been enabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The document's modified-datetime in the action bar becomes a link when the document is versioned (advertised hypermedia-style via a Link rel=mem:timemap response header, consumed like acl:mode in both pipelines). Clicking it opens a modal listing the versions - datetime and agent per entry, fetched client-side as RDF from GET ?timemap, which serves a Memento (RFC 7089) TimeMap built from the repository's commit history. Version links open in a new tab (the CSR navigation drops query params) and render server-side with a historical-version banner driven by the Memento-Datetime header. Also overrides the Web-Client date templates to drop the language argument: Saxon only ships English date names and prepends a '[Language: en]' fallback marker otherwise (TO-DO: upstream). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The HTML writer perturbs ETags per-agent by parsing them as hex numbers, so the 'git-' prefix 500'd browser requests. The hex-only restriction also stops movable refs (?version=main) from being served with immutable caching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The initial client boot re-fetched the bare document URI, replacing the historical snapshot with the live document and rewriting the URL to mode-only params. Snapshot views skip the boot-time re-fetch - the SSR content is the content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…wide table version/timemap are representation-selecting params (ldh:snapshot-params) that now ride the second-pass RDF fetch and survive both PushState URL rebuilds, so snapshot pages render fully - content blocks included - from the historical graph, and the URL keeps the param. Version links navigate in-app again (no target=_blank). History modal: modal-constructor width, table like the Request access dialog, current version row highlighted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…a row highlight LDH restyles Bootstrap's .info table row to purple, which makes the links in it illegible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors the Request access dialog: legend + text-info description from translations.rdf (en-US/es-ES), Version/Agent table headers likewise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t view Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ResponseHeadersFilter advertises at most acl:Read on ?version=/?timemap responses, so both rendering pipelines disable edit affordances through the existing acl:mode machinery - All six write methods reject snapshot URLs with 405 (a PATCH to ?version= would have written the live document) - The banner says read-only; its strings moved to translations.rdf Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ppend is not) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the audit roadmap's history/undo/audit-trail gap (P2.2) per the wiki implementation plan v1.3.
Capture
Every document write (
POST/PUT/PATCH/DELETE) on a versioning-enabled dataspace schedules an async reconcile task: re-read the graph from the store, commit it as a sorted N-Triples file to a GitHub repository (graph gone → file deleted). Commits are chained per file path so they never race the Contents API's SHA optimistic locking, and are authored with the agent's WebID —git logis the audit trail. Best-effort by design: failures are logged, responses are never delayed or failed.Configured per dataspace via
lapp:versioningRepository→doap:GitRepositoryinconfig/system.trig; the fine-grained token lives insecrets/credentials.trig(a:authToken), merged at entrypoint like SPARQL service credentials. The GitHub client runs on a new hostname-verified HTTP client factory.Retrieval
GET <doc>?version=<sha>serves the historical graph through the normal content-negotiation pipeline withMemento-Datetime, a SHA ETag, and immutableCache-Control; restricted to hex commit SHAs (movable refs must not be cached as immutable). Inherits the live document's ACL.GET <doc>?timemapserves an RFC 7089 TimeMap as RDF (Memento vocabulary), built from the repository's commit history; advertised viaLink rel=memento:timemap, consumed likeacl:modein both pipelines.History UI
The document's modified-datetime in the action bar becomes a link when the document is versioned; clicking it opens a modal (Request-access-style table, translated strings) listing versions newest-first with the current one marked. Version links navigate in-app:
version/timemapare now representation-selecting params (ldh:snapshot-params()) threaded through the CSR fetch and every URL rebuild, so snapshot pages render fully — content blocks included — from the historical graph. A banner marks historical views.Incidental fixes
.trig-suffixed copy (extensionless secret mount maderiotfail silently and killed startup)[Language: en]fallback marker (TO-DO: upstream to Web-Client)Testing
GitHubClientagainst a JDK-built-in fake server, TimeMap model shape, sorted-serialization determinism,VersioningFilterfire/skip conditionshttp-tests/versioning/suite (gated onVERSIONING_TEST_REPO+GITHUB_TOKEN): commit creation with WebID author,?version=round-trip withMemento-Datetime, TimeMap contents, file deletionVersioning is off by default — without
lapp:versioningRepositoryconfig, behavior is unchanged.🤖 Generated with Claude Code