From b47dd43b0491a5f72f75d3edf6bd0349117063b7 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 27 Sep 2026 18:44:21 +0200 Subject: [PATCH] Complete local coordination view switching and record remaining blockers Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e3b8-9815-70f2-9ac5-37bcde2c5090 --- WORK-RECORDS.md | 16 +-- docs/coordination-graph.md | 14 ++- railiance_fabric/graph_explorer_ui.py | 113 ++++++++++++------ tests/test_graph_mode_switch.py | 70 +++++++++++ ...-0028-hosted-financial-fabric-authority.md | 28 ++++- ...-WP-0029-coordination-graph-perspective.md | 24 +++- ...-WP-0031-flex-auth-access-engine-rename.md | 17 ++- 7 files changed, 227 insertions(+), 55 deletions(-) create mode 100644 tests/test_graph_mode_switch.py diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 97c6678..5af362a 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -11,10 +11,10 @@ | workplan | RAIL-FAB-WP-0025 | finished | — | workplans/RAIL-FAB-WP-0025-rail-rapp-reef-fabric-adaptation.md | | workplan | RAIL-FAB-WP-0026 | finished | — | workplans/RAIL-FAB-WP-0026-repo-family-relation-projection.md | | workplan | RAIL-FAB-WP-0027 | finished | — | workplans/RAIL-FAB-WP-0027-versioned-rail-readiness-projection.md | -| workplan | RAIL-FAB-WP-0028 | proposed | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | -| workplan | RAIL-FAB-WP-0029 | proposed | — | workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md | +| workplan | RAIL-FAB-WP-0028 | blocked | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | +| workplan | RAIL-FAB-WP-0029 | blocked | — | workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md | | workplan | RAIL-FAB-WP-0030 | finished | — | workplans/RAIL-FAB-WP-0030-flavor-and-depends-on-graph.md | -| workplan | RAIL-FAB-WP-0031 | active | — | workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md | +| workplan | RAIL-FAB-WP-0031 | blocked | — | workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md | | task | RAIL-FAB-WP-0025-T01 | done | — | workplans/RAIL-FAB-WP-0025-rail-rapp-reef-fabric-adaptation.md | | task | RAIL-FAB-WP-0025-T02 | done | — | workplans/RAIL-FAB-WP-0025-rail-rapp-reef-fabric-adaptation.md | | task | RAIL-FAB-WP-0025-T03 | done | — | workplans/RAIL-FAB-WP-0025-rail-rapp-reef-fabric-adaptation.md | @@ -26,11 +26,11 @@ | task | RAIL-FAB-WP-0027-T02 | done | — | workplans/RAIL-FAB-WP-0027-versioned-rail-readiness-projection.md | | task | RAIL-FAB-WP-0027-T03 | done | — | workplans/RAIL-FAB-WP-0027-versioned-rail-readiness-projection.md | | task | RAIL-FAB-WP-0027-T04 | done | — | workplans/RAIL-FAB-WP-0027-versioned-rail-readiness-projection.md | -| task | RAIL-FAB-WP-0028-T01 | todo | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | -| task | RAIL-FAB-WP-0028-T02 | todo | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | -| task | RAIL-FAB-WP-0028-T03 | todo | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | -| task | RAIL-FAB-WP-0028-T04 | todo | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | -| task | RAIL-FAB-WP-0029-T01 | todo | — | workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md | +| task | RAIL-FAB-WP-0028-T01 | wait | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | +| task | RAIL-FAB-WP-0028-T02 | wait | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | +| task | RAIL-FAB-WP-0028-T03 | wait | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | +| task | RAIL-FAB-WP-0028-T04 | wait | — | workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md | +| task | RAIL-FAB-WP-0029-T01 | wait | — | workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md | | task | RAIL-FAB-WP-0030-T01 | done | — | workplans/RAIL-FAB-WP-0030-flavor-and-depends-on-graph.md | | task | RAIL-FAB-WP-0030-T02 | done | — | workplans/RAIL-FAB-WP-0030-flavor-and-depends-on-graph.md | | task | RAIL-FAB-WP-0030-T03 | done | — | workplans/RAIL-FAB-WP-0030-flavor-and-depends-on-graph.md | diff --git a/docs/coordination-graph.md b/docs/coordination-graph.md index 8ca9c9c..bebe605 100644 --- a/docs/coordination-graph.md +++ b/docs/coordination-graph.md @@ -1,6 +1,6 @@ # Coordination graph (workplans and waits) -State Hub owns workplans. Fabric does not author them. This view *projects* +Repository files own workplans; State Hub indexes them. The Fabric explorer does not edit them. This view *projects* open workplans and open tasks into the existing graph explorer so chokepoints are inspectable. @@ -48,3 +48,15 @@ Chokepoint size is in-degree of **visible** (by default: non-residual) workplan nodes from `depends_on` and remaining `waits_on` edges. Solid blue = indexed `depends_on`. Dashed amber = citation `waits_on`. + +## Switching views + +The mode selector switches between topology and coordination in the same page. +Residual visibility refreshes the coordination payload and is retained in the +shareable URL. A failed refresh leaves the prior graph visible and shows an +error; an empty result can still be switched back to topology. Graph-specific +rules, selection, and zone placement reset when replacing the dataset. + +The registry route is available locally. Hosted acceptance remains blocked by +RAIL-FAB-WP-0028's runtime, persistence, access, and deployment decisions; this +UI change does not establish a production authority. diff --git a/railiance_fabric/graph_explorer_ui.py b/railiance_fabric/graph_explorer_ui.py index 4f23018..6251bcf 100644 --- a/railiance_fabric/graph_explorer_ui.py +++ b/railiance_fabric/graph_explorer_ui.py @@ -568,8 +568,8 @@ def graph_explorer_page() -> str: (() => { const manifestUrl = "/exports/graph-explorer/manifest"; const pageParams = new URLSearchParams(window.location.search); - const graphMode = pageParams.get("mode"); - const includeResidualsParam = ["1", "true", "yes"].includes( + let graphMode = pageParams.get("mode") === "coordination" ? "coordination" : "full"; + let includeResidualsParam = ["1", "true", "yes"].includes( (pageParams.get("include_residuals") || "").toLowerCase() ); const graphUrl = graphMode === "coordination" @@ -2141,6 +2141,7 @@ def graph_explorer_page() -> str: const state = currentViewState(); if (state.search) params.set("search", state.search); if (state.mode && state.mode !== "full") params.set("mode", state.mode); + if (state.mode === "coordination" && includeResidualsParam) params.set("include_residuals", "true"); if (state.layout && state.layout !== "cose") params.set("layout", state.layout); if (state.labelMode && state.labelMode !== "auto") params.set("labelMode", state.labelMode); if (state.nodeTypes.length !== allNodeTypes.length) params.set("nodeTypes", state.nodeTypes.join(",")); @@ -2201,6 +2202,10 @@ def graph_explorer_page() -> str: }; const applyViewState = (state, options = {}) => { + if (cy && "mode" in state && ((state.mode === "coordination") !== (graphMode === "coordination"))) { + switchGraph(state.mode, includeResidualsParam, state); + return; + } if ("search" in state) searchInput.value = state.search || ""; if ("mode" in state) modeSelect.value = optionExists(modeSelect, state.mode) ? state.mode : "full"; if ("layout" in state) layoutSelect.value = optionExists(layoutSelect, state.layout) ? state.layout : "cose"; @@ -2828,11 +2833,7 @@ def graph_explorer_page() -> str: residualsWrap.style.display = coordinationActive ? "" : "none"; residualsToggle.checked = includeResidualsParam; residualsToggle.addEventListener("change", () => { - const url = new URL(window.location.href); - url.searchParams.set("mode", "coordination"); - if (residualsToggle.checked) url.searchParams.set("include_residuals", "true"); - else url.searchParams.delete("include_residuals"); - window.location.search = url.search; + switchGraph("coordination", residualsToggle.checked); }); } profiles = loadProfiles(); @@ -2841,12 +2842,6 @@ def graph_explorer_page() -> str: ...element, data: {...element.data, color: layerColors[element.data.layer] || "#667085"} })); - if (elements.length === 0) { - canvas.innerHTML = "

No graph entities were returned by the registry.

"; - counts.textContent = "0 nodes / 0 edges"; - hiddenSummary.textContent = "Hidden 0"; - return; - } canvas.innerHTML = ""; cy = cytoscape({ container: canvas, @@ -2975,6 +2970,69 @@ def graph_explorer_page() -> str: applyViewState(urlState, {skipUrl: true}); runLayout(); updateUrlState(); + if (!elements.length) profileSummary.textContent = "No graph entities were returned by the registry."; + }; + + let graphRequest = 0; + const switchGraph = async (mode, includeResiduals = includeResidualsParam, viewState = null) => { + const request = ++graphRequest; + const coordination = mode === "coordination"; + const url = coordination + ? `/exports/graph-explorer?mode=coordination${includeResiduals ? "&include_residuals=true" : ""}` + : "/exports/graph-explorer"; + try { + const response = await fetch(url); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const payload = await response.json(); + if (!Array.isArray(payload.elements)) throw new Error("Invalid graph response"); + if (request !== graphRequest) return; + const elements = payload.elements.map((element) => ({ + ...element, + data: {...element.data, color: layerColors[element.data.layer] || "#667085"} + })); + graphMode = coordination ? "coordination" : "full"; + includeResidualsParam = includeResiduals; + activeMode = mode; + modeSelect.value = mode; + document.getElementById("include-residuals-wrap").style.display = coordination ? "" : "none"; + document.getElementById("include-residuals").checked = includeResiduals; + selected = null; + selectedAnchor = null; + selectedZoneId = ""; + focusSet = null; + manualOverrides = {}; + filterRules = []; + editingRuleId = ""; + collapsedZoneSnapshots.clear(); + zoneContainerState.clear(); + zoneSummaries.clear(); + currentProfileId = ""; + profileSelect.value = ""; + allEdgeTypes = [...new Set(elements.map(({data}) => data.edgeType).filter(Boolean))].sort(); + renderChecklist(nodeTypeFilter, allNodeTypes, nodeTypeLabels, "node-type"); + renderChecklist(edgeTypeFilter, allEdgeTypes, {}, "edge-type"); + cy.elements().remove(); + cy.add(elements); + popup.style.display = "none"; + refreshRuleBuilder(); + renderRules(); + syncFilterSummaries(); + if (viewState) applyViewState(viewState); + else { + applyFilters(); + runLayout(); + updateUrlState(); + } + showDetails(null); + updateProfileControls(); + updateProfileSummary(); + if (!elements.length) profileSummary.textContent = "No graph entities were returned by the registry."; + } catch (error) { + if (request !== graphRequest) return; + modeSelect.value = activeMode; + document.getElementById("include-residuals").checked = includeResidualsParam; + profileSummary.textContent = `Could not load graph: ${error.message}`; + } }; const handleFilterChange = () => { @@ -2992,27 +3050,14 @@ def graph_explorer_page() -> str: nodeTypeFilter.addEventListener("change", handleFilterChange); edgeTypeFilter.addEventListener("change", handleFilterChange); modeSelect.addEventListener("input", () => { - if (modeSelect.value === "coordination" && graphMode !== "coordination") { - const url = new URL(window.location.href); - url.searchParams.set("mode", "coordination"); - window.location.search = url.search; + const mode = modeSelect.value || "full"; + if ((mode === "coordination") !== (graphMode === "coordination")) { + switchGraph(mode); return; } - if (graphMode === "coordination" && modeSelect.value !== "coordination") { - const url = new URL(window.location.href); - url.searchParams.delete("mode"); - url.searchParams.delete("include_residuals"); - window.location.search = url.search; - return; - } - activeMode = modeSelect.value || "full"; - focusSet = null; - currentProfileId = ""; - profileSelect.value = ""; - applyFilters(); - updateProfileControls(); - updateProfileSummary(); - updateUrlState(); + graphRequest += 1; + activeMode = mode; + handleFilterChange(); }); layoutSelect.addEventListener("input", () => { currentProfileId = ""; @@ -3179,8 +3224,8 @@ def graph_explorer_page() -> str: document.querySelector("[data-action='focus']").addEventListener("click", applyFocus); document.querySelector("[data-action='clear']").addEventListener("click", () => { searchInput.value = ""; - modeSelect.value = "full"; - activeMode = "full"; + modeSelect.value = graphMode; + activeMode = graphMode; labelSelect.value = "auto"; activeLabelMode = "auto"; zoneBoundaryToggle.checked = true; diff --git a/tests/test_graph_mode_switch.py b/tests/test_graph_mode_switch.py new file mode 100644 index 0000000..46ae1c4 --- /dev/null +++ b/tests/test_graph_mode_switch.py @@ -0,0 +1,70 @@ +"""Execute the browser transition against a small graph/DOM harness.""" +import shutil +import subprocess + +import pytest + +from railiance_fabric.graph_explorer_ui import graph_explorer_page + + +def test_graph_switch_replaces_data_and_retains_previous_graph_on_failure(tmp_path): + if not shutil.which("node"): + pytest.skip("Node.js is required for the UI transition test") + page = graph_explorer_page() + transition = page.split(" let graphRequest = 0;", 1)[1].split( + " const handleFilterChange", 1 + )[0] + script = r''' +const assert = require("node:assert/strict"); +let graphMode = "full", includeResidualsParam = false, activeMode = "full"; +let selected, selectedAnchor, selectedZoneId, focusSet, manualOverrides, filterRules; +let editingRuleId, currentProfileId, allEdgeTypes; +const layerColors = {}, allNodeTypes = [], nodeTypeLabels = {}; +const collapsedZoneSnapshots = new Map(), zoneContainerState = new Map(), zoneSummaries = new Map(); +const modeSelect = {}, profileSelect = {}, nodeTypeFilter = {}, edgeTypeFilter = {}; +const popup = {style: {}}, profileSummary = {}; +const controls = {}; +const document = {getElementById(id) {return controls[id] ||= {style: {}};}}; +let data = [{data: {id: "old"}}], urls = [], updated = 0; +const cy = {elements: () => ({remove() {data = [];}}), add(elements) {data.push(...elements);}}; +const renderChecklist = () => {}, refreshRuleBuilder = () => {}, renderRules = () => {}; +const syncFilterSummaries = () => {}, applyFilters = () => {}, runLayout = () => {}; +const updateUrlState = () => {updated++;}, showDetails = () => {}; +const updateProfileControls = () => {}, updateProfileSummary = () => {}; +const applyViewState = () => {}; +let response = {ok: true, json: async () => ({elements: [{data: {id: "workplan:a"}}]})}; +let fetch = async (url) => {urls.push(url); return response;}; +let graphRequest = 0; +''' + script += transition + r''' +(async () => { + await switchGraph("coordination", true); + assert.equal(data[0].data.id, "workplan:a"); + assert.equal(modeSelect.value, "coordination"); + assert.equal(controls["include-residuals-wrap"].style.display, ""); + assert.match(urls[0], /include_residuals=true/); + response = {ok: false, status: 503}; + await switchGraph("full"); + assert.equal(data[0].data.id, "workplan:a"); + assert.equal(modeSelect.value, "coordination"); + assert.match(profileSummary.textContent, /503/); + response = {ok: true, json: async () => ({elements: []})}; + await switchGraph("by-access-zone"); + assert.deepEqual(data, []); + assert.equal(activeMode, "by-access-zone"); + assert.equal(graphMode, "full"); + assert.equal(controls["include-residuals-wrap"].style.display, "none"); + let resolveOld; + fetch = () => new Promise(resolve => {resolveOld = resolve;}); + const oldRequest = switchGraph("coordination"); + fetch = async () => ({ok: true, json: async () => ({elements: [{data: {id: "latest"}}]})}); + await switchGraph("full"); + resolveOld({ok: true, json: async () => ({elements: [{data: {id: "stale"}}]})}); + await oldRequest; + assert.equal(data[0].data.id, "latest"); + assert.equal(updated, 3); +})().catch(error => {console.error(error); process.exitCode = 1;}); +''' + path = tmp_path / "switch.cjs" + path.write_text(script) + subprocess.run(["node", str(path)], check=True, capture_output=True, text=True) diff --git a/workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md b/workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md index e492182..74d6b49 100644 --- a/workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md +++ b/workplans/RAIL-FAB-WP-0028-hosted-financial-fabric-authority.md @@ -4,14 +4,14 @@ type: workplan title: "Host and operate the financial Fabric authority" domain: financials repo: railiance-fabric -status: proposed +status: blocked flavor: residual origin: residual origin_ref: STATE-WP-0079 owner: codex topic_slug: railiance created: "2026-08-31" -updated: "2026-09-22" +updated: "2026-09-27" state_hub_workstream_id: "58a56363-3bda-50f7-8240-1e45131bc7d9" --- @@ -31,7 +31,7 @@ rollback rehearsal. ```task id: RAIL-FAB-WP-0028-T01 -status: todo +status: wait flavor: residual priority: high state_hub_task_id: "65702f41-b87e-59e5-9396-cd1fda5e8ef4" @@ -45,7 +45,7 @@ snapshot history and deterministic snapshot-set provenance. ```task id: RAIL-FAB-WP-0028-T02 -status: todo +status: wait flavor: residual priority: high state_hub_task_id: "cad1af1d-177b-5a47-a9fa-d99c40bb2649" @@ -59,7 +59,7 @@ Do not expose mutation endpoints without the authorization selected in T01. ```task id: RAIL-FAB-WP-0028-T03 -status: todo +status: wait flavor: residual priority: high state_hub_task_id: "78a73d4e-46aa-53c7-911b-be5f79bc25e8" @@ -73,7 +73,7 @@ accepted snapshot set. ```task id: RAIL-FAB-WP-0028-T04 -status: todo +status: wait flavor: residual priority: high state_hub_task_id: "c66c3fa4-8856-5999-8c47-dbb2f6efc21e" @@ -82,3 +82,19 @@ state_hub_task_id: "c66c3fa4-8856-5999-8c47-dbb2f6efc21e" Starts after T03. Run the fin-hub consumer gate against the hosted owner endpoint, inventory and switch any real callers, exercise rollback, and hand the receipts back to `STATE-WP-0079-T04` before State Hub's Fabric projection routes retire. + +## Loose-end review — 2026-09-27 + +T01–T04 remain blocked. The fin-hub consumer contract selects Fabric as the +specialized authority, but does not select or admit its production runtime, +persistent volume, offsite backup ownership, authentication policy, or deployment +repository. No hosted Fabric onboarding decision was found in this repository +or railiance-platform's workplans. A workstation SQLite service does not satisfy +this plan's durability gate. + +T01 needs a runtime/persistence and access decision with railiance-platform and +railiance-enablement, including named backup/recovery ownership and deployment +admission. T02 waits for that decision; T03 waits for the deployed authority; +T04 waits for freshness/recovery receipts and the fin-hub consumer gate. Keep +State Hub projection routes available until those gates pass. Existing tasks +retain all work; no new task or workplan was opened. diff --git a/workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md b/workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md index 498ce33..2a8d9d6 100644 --- a/workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md +++ b/workplans/RAIL-FAB-WP-0029-coordination-graph-perspective.md @@ -4,17 +4,17 @@ type: workplan title: "Inspectable workplan and wait graph in the Fabric explorer" domain: financials repo: railiance-fabric -status: proposed +status: blocked flavor: planning owner: grok topic_slug: railiance created: "2026-09-14" -updated: "2026-09-14" +updated: "2026-09-27" related: [RAIL-FAB-WP-0028, CUST-WP-0071] state_hub_workstream_id: "1daac341-94dd-5f44-9907-91c611e7e3c1" --- -State Hub remains the workplan authoring surface. Fabric already has a +Repository files remain the workplan source; State Hub indexes them. Fabric already has a graph-explorer shell. This plan is a *perspective*: open workplans, open tasks, and wait/depends_on edges, so the 100+ loose ends are inspectable. @@ -26,7 +26,7 @@ First slice (this file's companion commit): ```task id: RAIL-FAB-WP-0029-T01 -status: todo +status: wait priority: medium state_hub_task_id: "f80a8b15-ea3d-5701-9707-0a6151f50b5c" ``` @@ -35,3 +35,19 @@ Keep the projection a read of State Hub. Add mode switching without a full page reload, chokepoint sizing from in-degree, and a hosted registry path under RAIL-FAB-WP-0028 when that authority exists. Do not let Fabric become the place workplans are edited. + +## Loose-end review — 2026-09-27 + +Completed the repo-local portion of T01: topology/coordination mode changes +and residual visibility now fetch and replace the graph without page reload. +The requested topology mode survives the switch back; empty payloads remain +switchable, failed fetches retain the previous graph, and stale responses cannot +overwrite a newer request. Shared URLs retain residual visibility. Existing +in-degree sizing and indexed/citation edge precedence remain in place. + +T01 stays `wait` and the workplan is `blocked` solely for hosted acceptance +under RAIL-FAB-WP-0028. The read-only coordination export already has a registry +route (`/exports/graph-explorer?mode=coordination`); it must be verified on the +admitted hosted authority once available. No separate task was created for this +remaining gate. Validation: JavaScript syntax check, executable transition +regression, and the repository Python suite (see session progress receipt). diff --git a/workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md b/workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md index 1bf3798..8077c61 100644 --- a/workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md +++ b/workplans/RAIL-FAB-WP-0031-flex-auth-access-engine-rename.md @@ -4,14 +4,14 @@ type: workplan title: "Follow the flex-auth to access-engine repository rename in Fabric" domain: financials repo: railiance-fabric -status: active +status: blocked flavor: residual origin: residual origin_ref: FLEX-WP-0020 owner: codex topic_slug: railiance created: "2026-09-22" -updated: "2026-09-22" +updated: "2026-09-27" state_hub_workstream_id: "631e2974-55ba-58e3-b8ca-dc6c1c294111" --- @@ -68,3 +68,16 @@ Re-run `scan-manifest` for the renamed entry and re-export the graph. Confirm that no `repo:flex-auth` repository node remains, that the `flex-auth.*` runtime nodes are unchanged, and that no edge endpoint is left unresolved. Then reply to flex-auth with the evidence. + +## Loose-end review — 2026-09-27 + +T01–T03 remain `wait`; the workplan is now `blocked`. The local checkout remains +`/home/worsch/flex-auth`; `/home/worsch/access-engine` is absent. The owner's +FLEX-WP-0020 still records T05 (fresh preflight and exact human approval) and T06 +(Forgejo rename) as `wait`. There is no execution receipt satisfying T01. + +Resume only after the owner reports the completed rename with unchanged +repository UUID and Forge ID 42. Then update coordinates and re-ingest under +T02/T03, preserving all `flex-auth.*` runtime identities. Preemptive manifest +changes would point to a missing checkout; no declarations or historical +snapshots were renamed during this review.