generated from coulomb/repo-seed
feat(views): wire derived views onto InformationSpace + integration (WP-0010 T5)
Expose backlinks(name), recent_changes(), all_pages(), site_map() on InformationSpace. Integration test exercises all four over two shards (BackLinks aggregate across shards, AllPages/SiteMap span the union, RecentChanges merges an alias decision with shard edits). SCOPE updated; WP-0010 done. 152 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2
SCOPE.md
2
SCOPE.md
@@ -17,7 +17,7 @@ Learnings update both SCOPE and INTENT where necessary.
|
|||||||
|
|
||||||
| Layer | State |
|
| 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 |
|
| 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-*/`) |
|
| 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 |
|
| Demand | NetKingdom integration asks captured, not yet negotiated |
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ from shard_wiki.coordination import (
|
|||||||
from shard_wiki.model import Page
|
from shard_wiki.model import Page
|
||||||
from shard_wiki.policy import DEFAULT_POLICY, Policy
|
from shard_wiki.policy import DEFAULT_POLICY, Policy
|
||||||
from shard_wiki.union import Resolution, UnionGraph
|
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"]
|
__all__ = ["InformationSpace"]
|
||||||
|
|
||||||
@@ -92,3 +102,21 @@ class InformationSpace:
|
|||||||
draft as local truth (I-5: overlay before mutation, always)."""
|
draft as local truth (I-5: overlay before mutation, always)."""
|
||||||
overlay = self.overlay(name, body, actor=actor)
|
overlay = self.overlay(name, body, actor=actor)
|
||||||
return self.apply_overlay(overlay.overlay_id)
|
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)
|
||||||
|
|||||||
52
tests/test_views_integration.py
Normal file
52
tests/test_views_integration.py
Normal file
@@ -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") == ()
|
||||||
@@ -4,7 +4,7 @@ type: workplan
|
|||||||
title: "derived views — wikilinks, BackLinks, RecentChanges, AllPages/SiteMap"
|
title: "derived views — wikilinks, BackLinks, RecentChanges, AllPages/SiteMap"
|
||||||
domain: whynot
|
domain: whynot
|
||||||
repo: shard-wiki
|
repo: shard-wiki
|
||||||
status: active
|
status: done
|
||||||
owner: tegwick
|
owner: tegwick
|
||||||
topic_slug: whynot
|
topic_slug: whynot
|
||||||
created: "2026-06-15"
|
created: "2026-06-15"
|
||||||
@@ -36,7 +36,7 @@ later by SHARD-WP-0011) and carry provenance. Presentation stays out of core (L6
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: SHARD-WP-0010-T1
|
id: SHARD-WP-0010-T1
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "792660c3-9be9-4771-9f51-69d01f0c7f13"
|
state_hub_task_id: "792660c3-9be9-4771-9f51-69d01f0c7f13"
|
||||||
```
|
```
|
||||||
@@ -51,7 +51,7 @@ red-link, CamelCase opt-in.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: SHARD-WP-0010-T2
|
id: SHARD-WP-0010-T2
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "431a54c3-82b5-4b08-b3f0-762624d4c91d"
|
state_hub_task_id: "431a54c3-82b5-4b08-b3f0-762624d4c91d"
|
||||||
```
|
```
|
||||||
@@ -65,7 +65,7 @@ chorus pages aggregate.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: SHARD-WP-0010-T3
|
id: SHARD-WP-0010-T3
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "270c1c31-0445-42b9-9a49-92d32c298eb2"
|
state_hub_task_id: "270c1c31-0445-42b9-9a49-92d32c298eb2"
|
||||||
```
|
```
|
||||||
@@ -79,7 +79,7 @@ alias both appear, newest-first; per-shard attribution present.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: SHARD-WP-0010-T4
|
id: SHARD-WP-0010-T4
|
||||||
status: todo
|
status: done
|
||||||
priority: low
|
priority: low
|
||||||
state_hub_task_id: "898ba43e-cdef-4ce8-9fa3-4ce60ebb4fdd"
|
state_hub_task_id: "898ba43e-cdef-4ce8-9fa3-4ce60ebb4fdd"
|
||||||
```
|
```
|
||||||
@@ -92,7 +92,7 @@ collapses to one entry with divergence noted; sitemap reflects paths.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: SHARD-WP-0010-T5
|
id: SHARD-WP-0010-T5
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "7157544b-5d3b-45a2-ba5a-c32244c59323"
|
state_hub_task_id: "7157544b-5d3b-45a2-ba5a-c32244c59323"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user