diff --git a/SCOPE.md b/SCOPE.md index bb1977d..5dc4d0d 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -17,7 +17,7 @@ Learnings update both SCOPE and INTENT where necessary. | Layer | State | |-------|-------| -| Code | Foundation slice implemented (SHARD-WP-0007): `provenance` + `policy` leaves, `model` (Identity/Placement/Span/Page/CapabilityProfile), `adapters` (contract + FolderAdapter + conformance suite), `coordination` (event-sourced DecisionLog), `union` (resolution + chorus, overlay-aware), `InformationSpace` orchestrator. Write path added (SHARD-WP-0008): writable adapter, overlay engine (draft→patch→apply-under-drift), edit() unifies write-through + overlay-before-mutation. Native engine implemented (SHARD-WP-0014): `engine` (kernel + typed-extension runtime + per-shard activation [ADR-0001] + capability-profile-from-extensions + EngineShardAdapter + the `ext.struct` built-in) — an engine shard attaches to an InformationSpace as a canonical-mode shard. Git-backed coordination log (SHARD-WP-0009): `DecisionLog` storage factored behind an `EventStore`; `GitEventStore` makes the log git-addressable (each space a ref, append = immutable CAS-guarded commit), a per-space `AppendAuthority` (lease) gives a single-writer total order with re-grantable HA hand-off, cross-process read-your-writes verified, and a verbatim one-time importer (`migrate_space`/JSONL) replays in-memory logs into git; `InformationSpace.git_backed(...)` wires it. 128 tests green, ~97% coverage | +| Code | Foundation slice implemented (SHARD-WP-0007): `provenance` + `policy` leaves, `model` (Identity/Placement/Span/Page/CapabilityProfile), `adapters` (contract + FolderAdapter + conformance suite), `coordination` (event-sourced DecisionLog), `union` (resolution + chorus, overlay-aware), `InformationSpace` orchestrator. Write path added (SHARD-WP-0008): writable adapter, overlay engine (draft→patch→apply-under-drift), edit() unifies write-through + overlay-before-mutation. Native engine implemented (SHARD-WP-0014): `engine` (kernel + typed-extension runtime + per-shard activation [ADR-0001] + capability-profile-from-extensions + EngineShardAdapter + the `ext.struct` built-in) — an engine shard attaches to an InformationSpace as a canonical-mode shard. Git-backed coordination log (SHARD-WP-0009): `DecisionLog` storage factored behind an `EventStore`; `GitEventStore` makes the log git-addressable (each space a ref, append = immutable CAS-guarded commit), a per-space `AppendAuthority` (lease) gives a single-writer total order with re-grantable HA hand-off, cross-process read-your-writes verified, and a verbatim one-time importer (`migrate_space`/JSONL) replays in-memory logs into git; `InformationSpace.git_backed(...)` wires it. Derived views (SHARD-WP-0010): `views` (wikilink + red-link model, BackLinks, RecentChanges, AllPages/SiteMap) — recomputable, provenance-carrying, presentation-free, exposed via `InformationSpace.backlinks/recent_changes/all_pages/site_map`. 152 tests green, ~97% coverage | | Intent | `INTENT.md` established; authorization-in-core amendments drafted | | Research | yawex prior art; c2 origins; federation concepts; wikiengines overview (`research/260608-*/`); XWiki/TWiki/Foswiki deep dives (`research/260613-*/`); Xanadu + ZigZag + Roam + Obsidian + Notion + Joplin + Logseq + local-first workspaces (Anytype/AFFiNE/AppFlowy) + Trilium + Wiki.js + Federated Wiki + Wikibase + git-forge wikis + TiddlyWiki + ikiwiki + Quip + MojoMojo + Oddmuse + UseModWiki deep dives & shard-spectrum synthesis (`research/260614-*/`) | | Demand | NetKingdom integration asks captured, not yet negotiated | diff --git a/src/shard_wiki/space.py b/src/shard_wiki/space.py index 9a42b23..32c4532 100644 --- a/src/shard_wiki/space.py +++ b/src/shard_wiki/space.py @@ -23,6 +23,16 @@ from shard_wiki.coordination import ( from shard_wiki.model import Page from shard_wiki.policy import DEFAULT_POLICY, Policy from shard_wiki.union import Resolution, UnionGraph +from shard_wiki.views import ( + AllPagesEntry, + BackLink, + ChangeEntry, + SiteMapNode, + all_pages, + build_backlinks, + recent_changes, + site_map, +) __all__ = ["InformationSpace"] @@ -92,3 +102,21 @@ class InformationSpace: draft as local truth (I-5: overlay before mutation, always).""" overlay = self.overlay(name, body, actor=actor) return self.apply_overlay(overlay.overlay_id) + + # --- derived views (SHARD-WP-0010): recomputable, provenance-carrying, presentation-free --- + + def backlinks(self, name: str, *, camelcase: bool = False) -> tuple[BackLink, ...]: + """Pages across the union that link to ``name`` (UC-18).""" + return build_backlinks(self.union, camelcase=camelcase).to(name) + + def recent_changes(self, *, limit: int | None = None) -> tuple[ChangeEntry, ...]: + """The merged newest-first change feed: coordination journal + shard signals (UC-17).""" + return recent_changes(self.union, self.log, self.space_id, limit=limit) + + def all_pages(self) -> tuple[AllPagesEntry, ...]: + """The union's distinct pages, chorus/equivalence-collapsed with divergence noted.""" + return all_pages(self.union) + + def site_map(self) -> SiteMapNode: + """The union namespace tree built from page placements.""" + return site_map(self.union) diff --git a/tests/test_views_integration.py b/tests/test_views_integration.py new file mode 100644 index 0000000..f0cdfb0 --- /dev/null +++ b/tests/test_views_integration.py @@ -0,0 +1,52 @@ +"""Integration: derived views exposed on InformationSpace over two shards (SHARD-WP-0010 T5).""" + +from shard_wiki.adapters import FolderAdapter +from shard_wiki.model import Identity +from shard_wiki.space import InformationSpace + + +def _shard(tmp_path, name, files): + root = tmp_path / name + for rel, text in files.items(): + p = root / rel + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(text, encoding="utf-8") + return FolderAdapter(name, root) + + +def _space(tmp_path): + space = InformationSpace("space") + space.attach( + _shard(tmp_path, "wiki", {"Home.md": "welcome, see [[Guide]]", "Guide.md": "the guide"}) + ) + space.attach(_shard(tmp_path, "notes", {"Daily.md": "today I read [[Guide]]"})) + return space + + +def test_backlinks_across_two_shards(tmp_path): + space = _space(tmp_path) + sources = {bl.source for bl in space.backlinks("Guide")} + assert sources == {Identity("wiki", "Home"), Identity("notes", "Daily")} + + +def test_all_pages_and_site_map_over_union(tmp_path): + space = _space(tmp_path) + names = {e.name for e in space.all_pages()} + assert names == {"Home", "Guide", "Daily"} + leaves = {p.key for p in space.site_map().pages} + assert {"Home", "Guide", "Daily"} <= leaves + + +def test_recent_changes_includes_alias_and_edits(tmp_path): + space = _space(tmp_path) + space.alias("Start", "wiki:Home", actor="ana") + feed = space.recent_changes() + kinds = {e.kind for e in feed} + assert "alias" in kinds and "edit" in kinds + alias = next(e for e in feed if e.kind == "alias") + assert alias.source == "coordination" and alias.actor == "ana" + + +def test_red_link_creates_no_backlink_via_space(tmp_path): + space = _space(tmp_path) + assert space.backlinks("Nonexistent") == () diff --git a/workplans/SHARD-WP-0010-derived-views.md b/workplans/SHARD-WP-0010-derived-views.md index 6bf35c8..d2f6f20 100644 --- a/workplans/SHARD-WP-0010-derived-views.md +++ b/workplans/SHARD-WP-0010-derived-views.md @@ -4,7 +4,7 @@ type: workplan title: "derived views — wikilinks, BackLinks, RecentChanges, AllPages/SiteMap" domain: whynot repo: shard-wiki -status: active +status: done owner: tegwick topic_slug: whynot created: "2026-06-15" @@ -36,7 +36,7 @@ later by SHARD-WP-0011) and carry provenance. Presentation stays out of core (L6 ```task id: SHARD-WP-0010-T1 -status: todo +status: done priority: high state_hub_task_id: "792660c3-9be9-4771-9f51-69d01f0c7f13" ``` @@ -51,7 +51,7 @@ red-link, CamelCase opt-in. ```task id: SHARD-WP-0010-T2 -status: todo +status: done priority: high state_hub_task_id: "431a54c3-82b5-4b08-b3f0-762624d4c91d" ``` @@ -65,7 +65,7 @@ chorus pages aggregate. ```task id: SHARD-WP-0010-T3 -status: todo +status: done priority: medium state_hub_task_id: "270c1c31-0445-42b9-9a49-92d32c298eb2" ``` @@ -79,7 +79,7 @@ alias both appear, newest-first; per-shard attribution present. ```task id: SHARD-WP-0010-T4 -status: todo +status: done priority: low state_hub_task_id: "898ba43e-cdef-4ce8-9fa3-4ce60ebb4fdd" ``` @@ -92,7 +92,7 @@ collapses to one entry with divergence noted; sitemap reflects paths. ```task id: SHARD-WP-0010-T5 -status: todo +status: done priority: medium state_hub_task_id: "7157544b-5d3b-45a2-ba5a-c32244c59323" ```