diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 0000000..7413b8e --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,20 @@ +{ + "name": "hypermnesia-mcp-viz-marketplace", + "interface": { + "displayName": "Hypermnesia MCP Viz" + }, + "plugins": [ + { + "name": "hypermnesia-mcp-viz", + "source": { + "source": "local", + "path": "./" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Developer Tools" + } + ] +} diff --git a/.bestpractices.json b/.bestpractices.json index 0f4f4ec..8600dda 100644 --- a/.bestpractices.json +++ b/.bestpractices.json @@ -50,7 +50,7 @@ "report_responses_status": "Met", "report_responses_justification": "Reports are responded to. Every issue opened in the last six months (#13, #17, #23, #31, #32, #35, #36, #37, #41) received a response and all nine were resolved and closed; none was left unacknowledged.", "enhancement_responses_status": "Met", - "enhancement_responses_justification": "Enhancement requests get an explicit answer rather than silence. #37 (supply chain) and #35 (JS test harness) were accepted and implemented. The distribution plan evolved explicitly: the Claude plugin remains cortex-viz, while PyPI Trusted Publishing and the MCP Registry use the available canonical identity hypermnesia-mcp-viz.", + "enhancement_responses_justification": "Enhancement requests get an explicit answer rather than silence. #37 (supply chain) and #35 (JS test harness) were accepted and implemented. The distribution plan evolved explicitly: the Claude plugin, Codex plugin, PyPI Trusted Publishing project, and MCP Registry now share the canonical identity hypermnesia-mcp-viz; the historical GitHub repository URL remains cdeust/cortex-viz.", "report_archive_status": "Met", "report_archive_justification": "Public, searchable, permanently addressable: https://github.com/cdeust/cortex-viz/issues?q=is%3Aissue", "vulnerability_report_process_status": "Met", diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..c5b437c --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,35 @@ +{ + "name": "hypermnesia-mcp-viz-marketplace", + "owner": { + "name": "Clement Deust", + "email": "admin@ai-architect.tools" + }, + "metadata": { + "description": "Hypermnesia MCP Viz — the read-only visualization companion for Cortex", + "version": "3.0.0" + }, + "plugins": [ + { + "name": "hypermnesia-mcp-viz", + "source": "./", + "description": "Live memory galaxy, methodology map, workflow graph, wiki browser, and execution trace over the shared Cortex store.", + "version": "3.0.0", + "author": { + "name": "Clement Deust", + "email": "admin@ai-architect.tools" + }, + "homepage": "https://github.com/cdeust/cortex-viz", + "repository": "https://github.com/cdeust/cortex-viz", + "license": "MIT", + "keywords": [ + "visualization", + "graph", + "mcp", + "claude-code", + "cortex", + "knowledge-graph" + ], + "category": "productivity" + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 7b6c6dc..926a97e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { - "name": "cortex-viz", + "name": "hypermnesia-mcp-viz", "description": "Standalone visualization MCP for Cortex — a live neural-graph galaxy of every project, file, symbol, memory, discussion and wiki page, plus a per-session execution trace. Read-only bridge over Cortex's shared PostgreSQL and the ~/.claude artifacts.", - "version": "2.8.0", + "version": "3.0.0", "author": { "name": "Clement Deust", "email": "admin@ai-architect.tools" @@ -26,7 +26,7 @@ } }, "mcpServers": { - "cortex-viz": { + "hypermnesia-mcp-viz": { "command": "python3", "args": [ "${CLAUDE_PLUGIN_ROOT}/scripts/launcher.py", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json new file mode 100644 index 0000000..ef2b3f9 --- /dev/null +++ b/.codex-plugin/plugin.json @@ -0,0 +1,47 @@ +{ + "name": "hypermnesia-mcp-viz", + "version": "3.0.0", + "description": "Read-only visualization and graph MCP for Cortex, packaged for Codex.", + "author": { + "name": "Clement Deust", + "email": "admin@ai-architect.tools", + "url": "https://github.com/cdeust" + }, + "homepage": "https://github.com/cdeust/cortex-viz", + "repository": "https://github.com/cdeust/cortex-viz", + "license": "MIT", + "keywords": [ + "visualization", + "graph", + "mcp", + "codex", + "cortex" + ], + "skills": "./skills/", + "mcpServers": { + "hypermnesia-mcp-viz": { + "command": "uvx", + "args": [ + "--from", + "hypermnesia-mcp-viz", + "hypermnesia-mcp-viz" + ] + } + }, + "interface": { + "displayName": "Hypermnesia MCP Viz", + "shortDescription": "Inspect Cortex memory and graph state", + "longDescription": "Open a read-only memory galaxy, methodology map, workflow graph, wiki browser, and execution trace over the shared Cortex store.", + "developerName": "Clement Deust", + "category": "Developer Tools", + "capabilities": [ + "Read", + "Interactive" + ], + "websiteURL": "https://github.com/cdeust/cortex-viz", + "defaultPrompt": [ + "Open the Cortex memory visualization.", + "Show the methodology graph for this project." + ] + } +} diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml index 2e3e80f..6151730 100644 --- a/.github/workflows/Release.yaml +++ b/.github/workflows/Release.yaml @@ -3,24 +3,24 @@ name: Release # Filename casing is part of PyPI's Trusted Publisher identity. Keep this file # as `Release.yaml` to match the verified publisher record exactly. -# Issue #37. Until now cortex-viz had NO release workflow: 2.7.1 was cut by +# Issue #37. Until now Hypermnesia MCP Viz had NO release workflow: 2.7.1 was cut by # hand, so there was no committed, reproducible build path and therefore no # artifact to attest. This workflow is the build that was missing — it is now # the ONLY way a release is produced, and provenance layers on top of it. # -# Delivery reality (do not confuse with "released"): cortex-viz is consumed +# Delivery reality (do not confuse with "released"): hypermnesia-mcp-viz is consumed # through Anthropic's plugin marketplace, PINNED in Cortex's marketplace # manifest. Tagging + this GitHub Release do not reach installs by themselves; # the release checklist still ENDS with bumping that pin. This is the same # pin-gate lesson that let six zetetic-team-subagents releases and two -# cortex-viz releases ship to zero installs (Cortex #179) — a release workflow +# hypermnesia-mcp-viz releases ship to zero installs (Cortex #179) — a release workflow # that runs is necessary but not sufficient. # # The canonical distribution is published to PyPI as `hypermnesia-mcp-viz` # through Trusted Publishing. No long-lived PyPI token exists: the protected # `pypi` environment and id-token permission bind publication to this workflow, -# repository, tag, and environment. The legacy `cortex-viz` console command is -# carried inside the new distribution as a compatibility alias. +# repository, tag, and environment. The canonical console command is +# `hypermnesia-mcp-viz`; no deprecated publication alias is emitted. on: push: @@ -85,7 +85,7 @@ jobs: - name: Build sdist and wheel run: uv build - - name: Verify distribution identity and compatibility entry points + - name: Verify canonical distribution identity and entry point run: python -m scripts.check_distribution_artifact # Fingerprint the browser-executed UI (issue #37 criterion 2). The 91 JS @@ -100,9 +100,9 @@ jobs: set -euxo pipefail find ui -type f -print0 \ | LC_ALL=C sort -z \ - | xargs -0 sha256sum > cortex-viz-ui-manifest.sha256 - echo "UI files fingerprinted: $(wc -l < cortex-viz-ui-manifest.sha256)" - head -5 cortex-viz-ui-manifest.sha256 + | xargs -0 sha256sum > hypermnesia-mcp-viz-ui-manifest.sha256 + echo "UI files fingerprinted: $(wc -l < hypermnesia-mcp-viz-ui-manifest.sha256)" + head -5 hypermnesia-mcp-viz-ui-manifest.sha256 # CycloneDX SBOM from uv.lock (issue #37 criterion 5). This covers the # Python dependency graph, including the viz-tile stack (datashader, @@ -118,8 +118,8 @@ jobs: --format requirements-txt > sbom-requirements.txt wc -l sbom-requirements.txt uv tool run --from cyclonedx-bom cyclonedx-py requirements \ - sbom-requirements.txt --output-format JSON --output-file cortex-viz.cdx.json - ls -la cortex-viz.cdx.json + sbom-requirements.txt --output-format JSON --output-file hypermnesia-mcp-viz.cdx.json + ls -la hypermnesia-mcp-viz.cdx.json # Publish while dist/ contains distributions only. The checksum step # below adds *.sha256 sidecars that Twine rejects as unknown formats. @@ -132,7 +132,7 @@ jobs: - name: Compute SHA-256 checksums for every release asset run: | set -euxo pipefail - for f in dist/* cortex-viz.cdx.json cortex-viz-ui-manifest.sha256; do + for f in dist/* hypermnesia-mcp-viz.cdx.json hypermnesia-mcp-viz-ui-manifest.sha256; do sha256sum "$f" | tee "$f.sha256" done @@ -146,8 +146,8 @@ jobs: subject-path: | dist/*.whl dist/*.tar.gz - cortex-viz.cdx.json - cortex-viz-ui-manifest.sha256 + hypermnesia-mcp-viz.cdx.json + hypermnesia-mcp-viz-ui-manifest.sha256 - name: Create GitHub Release with attested assets uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 @@ -158,8 +158,8 @@ jobs: dist/*.whl dist/*.tar.gz dist/*.sha256 - cortex-viz.cdx.json - cortex-viz.cdx.json.sha256 - cortex-viz-ui-manifest.sha256 - cortex-viz-ui-manifest.sha256.sha256 + hypermnesia-mcp-viz.cdx.json + hypermnesia-mcp-viz.cdx.json.sha256 + hypermnesia-mcp-viz-ui-manifest.sha256 + hypermnesia-mcp-viz-ui-manifest.sha256.sha256 fail_on_unmatched_files: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 367f742..39a06e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Changelog -All notable changes to cortex-viz are documented in this file. +All notable changes to Hypermnesia MCP Viz are documented in this file. Releases before 2.7.0 were recorded as `chore(release)` / `release:` commits in git history. ## [Unreleased] +## [3.0.0] - 2026-08-04 + ### Security - Update the transitive test dependency `brace-expansion` from 5.0.8 to 5.0.9, @@ -14,6 +16,30 @@ Releases before 2.7.0 were recorded as `chore(release)` / `release:` commits in ### Changed +- **Breaking publication rename.** `hypermnesia-mcp-viz` is now the sole + Claude Code plugin, MCP server, Python distribution, and console identity. + Because `2.8.0` was already published, removing the old plugin and console + identities ships as the SemVer-major `3.0.0` release rather than replacing + an immutable artifact in place. + Existing Claude installs must run + `claude plugin uninstall cortex-viz@cortex-plugins`, refresh + `cortex-plugins`, and run + `claude plugin install hypermnesia-mcp-viz@cortex-plugins`. Permission and + tool references must use Claude's composed names: + `mcp__plugin_cortex-viz_cortex-viz__open_visualization` becomes + `mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__open_visualization`, + and `mcp__plugin_cortex-viz_cortex-viz__get_methodology_graph` becomes + `mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__get_methodology_graph`. + Direct-process hosts must replace the removed `cortex-viz` executable with + `hypermnesia-mcp-viz`. This source change and the marketplace rename in + `cdeust/Cortex#351` form one coordinated release and must not be published + independently. +- The PRD bridge now discovers only the canonical `ai-architect-mcp-spec` + Claude plugin and reports that publication identity in its API metadata and + current documentation. A deprecated `prd-spec-generator` install is ignored. +- The artifact guard distinguishes GitHub tag runs from branch and pull-request + refs before enforcing the immutable release version, so normal PR CI is not + rejected for its `/merge` ref name. - Synchronize committed assurance evidence with OpenSSF Best Practices Silver, verified v2.8.0 Sigstore attestations, 81% Python statement coverage, zero open CodeQL alerts, and post-Silver OpenSSF Scorecard 7.4. @@ -24,9 +50,9 @@ Releases before 2.7.0 were recorded as `chore(release)` / `release:` commits in - The canonical Python and MCP Registry distribution is now `hypermnesia-mcp-viz` at version 2.8.0. Releases publish the wheel and source archive to PyPI through Trusted Publishing, and `server.json` describes the - matching stdio package for the official MCP Registry. The Cortex Viz product, - Claude Code plugin, import package, and existing `cortex-viz` command remain - compatible; `hypermnesia-mcp-viz` is an additional canonical command. + matching stdio package for the official MCP Registry. The Python import + package remains `cortex_viz`, but no `cortex-viz` publication or console + alias is emitted. - A versioned, host-neutral live activity contract (`docs/host-event-v1.schema.json`) for Codex, Gemini, and generic MCP-host adapters. `POST /api/activity` normalizes it into the existing activity diff --git a/README.md b/README.md index fe6c615..1302ef9 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,25 @@

- cortex-viz, the read-only visualization layer for Cortex: six live reading angles over your memory, sessions, and code, rendered but never remembered + Hypermnesia MCP Viz, the read-only visualization layer for Cortex: six live reading angles over your memory, sessions, and code, rendered but never remembered

- cortex-viz Trace galaxy on the paper surface: every Claude Code session is a tight phyllotaxis disk of its own prompt → action → file → memory chain, clustered around its domain's olive hub; the selected chain's disks render solid gold in place while the rest of the galaxy stays legible, and the mono status bar streams the exact counts (CHAIN · 108 STEPS · 6 537/6 537 nodes · 8 696/8 696 edges), never rounded. + Hypermnesia MCP Viz Trace galaxy on the paper surface: every Claude Code session is a tight phyllotaxis disk of its own prompt → action → file → memory chain, clustered around its domain's olive hub; the selected chain's disks render solid gold in place while the rest of the galaxy stays legible, and the mono status bar streams the exact counts (CHAIN · 108 STEPS · 6 537/6 537 nodes · 8 696/8 696 edges), never rounded.

Cross-platform MCP for Codex, Gemini CLI, and Claude Code MIT License Python 3.10+ - Version 2.8.0 + Version 3.0.0 OpenSSF Best Practices

-# Cortex Viz +# Hypermnesia MCP Viz -**Cortex Viz is published to Python and MCP registries as `hypermnesia-mcp-viz`.** -The product and Claude Code plugin remain `cortex-viz`, and the existing -`cortex-viz` command remains a supported compatibility alias. +**Hypermnesia MCP Viz is published as `hypermnesia-mcp-viz` everywhere:** +Python, MCP Registry, Claude Code, Codex, Gemini, and the console command. **The cross-platform visualization layer for [Cortex](https://github.com/cdeust/Cortex).** Codex, Gemini CLI, Claude Code, and any stdio MCP host can launch the same read-only server and browser UI. It turns Cortex's memory store, a host-neutral live activity stream, Claude Code's historical session archive, and your codebase graph into six live reading angles: a project galaxy, a 3D anatomical brain, an execution trace, a consolidation kanban, a curated knowledge browser, and a wiki. It **never writes a memory**: it renders, it never remembers. (It does keep its own derived graph/layout caches in the Cortex database, listed under [Boundary](#boundary).) @@ -32,14 +31,45 @@ The whole UI ships on the **AI Architect design system**: a paper-first reading ## Getting Started -Claude Code users can install cortex-viz from the same `cortex-plugins` marketplace as Cortex: +Claude Code users can install Hypermnesia MCP Viz from the same +`cortex-plugins` marketplace as Cortex: ```bash claude plugin marketplace add cdeust/Cortex -claude plugin install cortex-viz +claude plugin install hypermnesia-mcp-viz@cortex-plugins ``` -> **cortex-viz is a companion to [Cortex](https://github.com/cdeust/Cortex) that never writes a memory.** Install Cortex first (`claude plugin install cortex`): cortex-viz reads its shared PostgreSQL store, and writes only its own derived caches there ([Boundary](#boundary)). Point both at the same database: the `database_url` plugin setting defaults to `postgresql://127.0.0.1:5432/cortex`; set it to the same value you gave Cortex. +This install name becomes available in the Cortex marketplace only when this +repository and the matching marketplace change in +[`cdeust/Cortex#351`](https://github.com/cdeust/Cortex/pull/351) are released +together. Maintainers must not publish either half of the rename on its own. + +### Migrating from `cortex-viz` + +Existing Claude Code installs are not aliases for the new plugin identity. +Remove the old install, refresh the marketplace after the coordinated release, +and install the canonical plugin: + +```bash +claude plugin uninstall cortex-viz@cortex-plugins +claude plugin marketplace update cortex-plugins +claude plugin install hypermnesia-mcp-viz@cortex-plugins +``` + +Claude composes plugin tool names from the plugin manifest name, the +`mcpServers` key, and the tool name. Update both old full names wherever they +appear in permission allowlists, hooks, skills, or agents: + +- `mcp__plugin_cortex-viz_cortex-viz__open_visualization` + becomes `mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__open_visualization`. +- `mcp__plugin_cortex-viz_cortex-viz__get_methodology_graph` + becomes `mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__get_methodology_graph`. + +The removed `cortex-viz` console command is not retained as an alias, so +Cursor, Windsurf, VS Code, and other direct-process configurations must invoke +`hypermnesia-mcp-viz` instead. + +> **Hypermnesia MCP Viz is a companion to [Cortex](https://github.com/cdeust/Cortex) that never writes a memory.** Install Cortex first (`claude plugin install hypermnesia-mcp@cortex-plugins`): the visualizer reads its shared PostgreSQL store, and writes only its own derived caches there ([Boundary](#boundary)). Point both at the same database: the `database_url` plugin setting defaults to `postgresql://127.0.0.1:5432/cortex`; set it to the same value you gave Cortex. Restart your Claude Code session, then launch the visualizer: @@ -54,7 +84,7 @@ One launcher opens all six reading angles (Graph · Brain · Trace · Knowledge ### Works without Cortex -No Cortex, no PostgreSQL, no setup: cortex-viz is still useful on its own. The **Trace** view (the default landing view) reads only `~/.claude/projects/*.jsonl` and your local git: every Claude Code session becomes a navigable domain → session → prompt → action → file chain, with per-file diffs and commit history. If you use Claude Code, the data is already on your disk. +No Cortex, no PostgreSQL, no setup: Hypermnesia MCP Viz is still useful on its own. The **Trace** view (the default landing view) reads only `~/.claude/projects/*.jsonl` and your local git: every Claude Code session becomes a navigable domain → session → prompt → action → file chain, with per-file diffs and commit history. If you use Claude Code, the data is already on your disk. - Just install the plugin and run `/cortex-visualize`. When Cortex's database isn't reachable, the server logs one line and starts in **no-DB mode** automatically: Trace is fully live; the five DB-backed views (Graph, Brain, Knowledge, Wiki, Board) appear greyed out with an install pointer instead of erroring. - To skip the database probe entirely, set `CORTEX_VIZ_NO_DB=1` (or pass `--no-db` when running the standalone server directly). @@ -122,7 +152,7 @@ Five columns by consolidation stage (`labile` · `early_ltp` · `late_ltp` · `c - **Trace** *(default)*: the live execution-trace drill: collapsed domain hubs → sessions → the ordered prompt → action → file chain of what actually happened → a file's AST symbols, impact neighbourhood, and git history. Discussions and Cortex `remember`/`recall` ops are woven into the chain. Served live from session JSONL, the code graph, and git on every request (no snapshots, always current). - **Knowledge**: curated memory cards with the feeling (word + signed valence/arousal, never colour alone), the MEANING line and verbatim excerpt, stage/domain/HOT badges, and four measured meters in fixed order (heat · importance · valence · arousal; a zero shows an empty track, never hides); filter by domain, stage, or feeling with exact facet counts. -- **Wiki**: the per-project knowledge base as a browsable Project → Kind → Pages tree with a dossier-style page reader: serif prose with numbered section heads and mono identifier chips, boxed status and kind badges, dated provenance, and an Edit · PDF · TEX · DOCX · HTML export strip. A CodeMirror split-pane editor with live preview sits behind Edit. (The wiki *content* is authored autonomously by [Cortex](https://github.com/cdeust/Cortex#the-autonomous-wiki); cortex-viz is its reading + editing surface.) +- **Wiki**: the per-project knowledge base as a browsable Project → Kind → Pages tree with a dossier-style page reader: serif prose with numbered section heads and mono identifier chips, boxed status and kind badges, dated provenance, and an Edit · PDF · TEX · DOCX · HTML export strip. A CodeMirror split-pane editor with live preview sits behind Edit. (The wiki *content* is authored autonomously by [Cortex](https://github.com/cdeust/Cortex#the-autonomous-wiki); Hypermnesia MCP Viz is its reading + editing surface.)

Knowledge view, curated memory cards on the paper surface: each card carries its tool source, feeling word with signed valence/arousal deltas, MEANING line with verbatim path, stage badge (LABILE · EARLY-LTP), domain chip, HOT flag, four measured meters (heat, importance, valence, arousal), and capture provenance; the filter bar above states exact counts per domain, stage, and feeling @@ -136,28 +166,54 @@ Five columns by consolidation stage (`labile` · `early_ltp` · `late_ltp` · `c ## Install -cortex-viz is a cross-platform MCP server with an optional Claude Code plugin. Point it at the **same database as your Cortex install**: it reads Cortex's memories from that store and never writes them. +Hypermnesia MCP Viz is a cross-platform MCP server with a Claude Code plugin. Point it at the **same database as your Cortex install**: it reads Cortex's memories from that store and never writes them. **As a plugin**: ships the MCP server, the `/cortex-visualize` skill, and the live session-activity hooks. The bundled `scripts/launcher.py` bootstraps its own dependencies on first launch (no manual `pip` needed). Configure the DB via the plugin's `database_url` user-config (defaults to `postgresql://127.0.0.1:5432/cortex`). -**As a raw MCP (v2.8.0 and later):** +**As a raw MCP (v3.0.0 and later):** ```bash pip install "hypermnesia-mcp-viz[data,viz-tile]" # optional PG and large-graph extras -hypermnesia-mcp-viz -# Existing integrations can continue to use: cortex-viz -cortex-viz # or: python -m cortex_viz (stdio MCP transport) +hypermnesia-mcp-viz # or: python -m cortex_viz (stdio MCP transport) +``` + +The published `2.8.0` `hypermnesia-mcp-viz` distribution owned the legacy +`cortex-viz` console shim; there was no separate Python distribution by that +name. Upgrade with the same interpreter/environment that installed it, and the +installer removes the old distribution-owned shim while installing `3.0.0`: + +```bash +python3 -m pip install --upgrade "hypermnesia-mcp-viz>=3.0.0" +``` + +If `cortex-viz` still resolves, first locate every shim and ask Python which +installed distribution owns that console entry point: + +```bash +type -a cortex-viz +python3 - <<'PY' +from importlib import metadata + +owners = [] +for distribution in metadata.distributions(): + if any( + entry.group == "console_scripts" and entry.name == "cortex-viz" + for entry in distribution.entry_points + ): + owners.append((distribution.metadata["Name"], distribution.version)) +print(owners or "no Python distribution owns cortex-viz in this interpreter") +PY ``` -If this repository was previously installed as an editable `cortex-viz` -distribution, run `pip uninstall cortex-viz` before installing the canonical -package so two distributions do not claim the same import package and script. +Uninstall the reported distribution with that same interpreter, or remove a +manually installed shell/file shim through the tool that created it when no +distribution owns it. Do not guess a distribution name from the executable. Set `DATABASE_URL` to the shared Cortex database. `open_visualization` launches the galaxy UI in the browser, bound to `127.0.0.1`. ### Other MCP hosts -Any MCP host can launch the server: it is a plain stdio process. Install it with `pip install hypermnesia-mcp-viz`, then register `hypermnesia-mcp-viz` as the command in your host's MCP config. The compatibility command `cortex-viz` and the module form `python3 -m cortex_viz` are equivalent (Gemini CLI `~/.gemini/settings.json`, Cursor `.cursor/mcp.json`, Windsurf `~/.codeium/windsurf/mcp_config.json`, VS Code `.vscode/mcp.json` under `"servers"`, or Codex CLI: `codex mcp add cortex-viz -- hypermnesia-mcp-viz`). The `open_visualization` tool opens the UI in the browser. The historical **Trace** archive still reads Claude Code JSONLs under `~/.claude/projects/`, but live activity is host-neutral: a Codex, Gemini, or generic MCP adapter can POST the versioned schema below to the local `/api/activity` endpoint. The galaxy/brain/knowledge/wiki/board views still need a [Cortex](https://github.com/cdeust/Cortex) store to read. +Any MCP host can launch the server: it is a plain stdio process. Install it with `pip install hypermnesia-mcp-viz`, then register `hypermnesia-mcp-viz` as both the server name and command in your host's MCP config (Gemini CLI `~/.gemini/settings.json`, Cursor `.cursor/mcp.json`, Windsurf `~/.codeium/windsurf/mcp_config.json`, VS Code `.vscode/mcp.json` under `"servers"`, or Codex CLI: `codex mcp add hypermnesia-mcp-viz -- hypermnesia-mcp-viz`). The equivalent Python module form is `python3 -m cortex_viz`. The `open_visualization` tool opens the UI in the browser. The historical **Trace** archive still reads Claude Code JSONLs under `~/.claude/projects/`, but live activity is host-neutral: a Codex, Gemini, or generic MCP adapter can POST the versioned schema below to the local `/api/activity` endpoint. The galaxy/brain/knowledge/wiki/board views still need a [Cortex](https://github.com/cdeust/Cortex) store to read. ### Host-neutral live activity @@ -188,7 +244,7 @@ database schema. ## Boundary -cortex-viz consumes Cortex's **artifacts on disk + PostgreSQL**, never Cortex's live Python objects: +Hypermnesia MCP Viz consumes Cortex's **artifacts on disk + PostgreSQL**, never Cortex's live Python objects: | Data | Source | |---|---| @@ -197,8 +253,8 @@ cortex-viz consumes Cortex's **artifacts on disk + PostgreSQL**, never Cortex's | Sessions / execution traces | `~/.claude/projects/*.jsonl` | | Cognitive profiles | `~/.claude/methodology/profiles.json` | | Codebase graph (AST symbols, impact) | [`automatised-pipeline`](https://github.com/cdeust/ai-automatised-pipeline) MCP (stdio) | -| PRD document/section nodes | [`prd-spec-generator`](https://github.com/cdeust/ai-prd-generator) MCP + on-disk artifacts | -| **Written** by cortex-viz (its own tables, in the same PG database) | `workflow_graph_snapshot`, `workflow_graph_snapshot_scoped`, `workflow_graph_layout`, `workflow_graph_layout_lod` (derived graph and layout caches) and `session_activity` (live activity stream). Cortex's own memory tables are never written. | +| PRD document/section nodes | [`ai-architect-mcp-spec`](https://github.com/cdeust/ai-architect-mcp-spec) MCP + on-disk artifacts | +| **Written** by Hypermnesia MCP Viz (its own tables, in the same PG database) | `workflow_graph_snapshot`, `workflow_graph_snapshot_scoped`, `workflow_graph_layout`, `workflow_graph_layout_lod` (derived graph and layout caches) and `session_activity` (live activity stream). Cortex's own memory tables are never written. | No `import mcp_server.*` is permitted anywhere in `cortex_viz/`: that invariant is the extraction's correctness check. @@ -213,7 +269,7 @@ No `import mcp_server.*` is permitted anywhere in `cortex_viz/`: that invariant | [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute, the coding style, and the test requirement | | [GOVERNANCE.md](GOVERNANCE.md) | Who decides, who has which role, and how a fork can continue the project without original credentials | | [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) | Expected behaviour and how to report a problem | -| [SECURITY.md](SECURITY.md) | What cortex-viz accesses, the supply-chain assurance, and how to report a vulnerability | +| [SECURITY.md](SECURITY.md) | What Hypermnesia MCP Viz accesses, the supply-chain assurance, and how to report a vulnerability | | [PRIVACY.md](PRIVACY.md) | What is read, what is written, and what leaves your machine | | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | The layers, the read contract, and the trust boundaries | | [docs/ASSURANCE_CASE.md](docs/ASSURANCE_CASE.md) | The threat model and why the security requirements are met, including where the argument is incomplete | diff --git a/SECURITY.md b/SECURITY.md index c399333..f4d756e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,8 +1,8 @@ # Security Policy -## What cortex-viz accesses +## What Hypermnesia MCP Viz accesses -cortex-viz is a visualization bridge over Cortex's data that never writes a +Hypermnesia MCP Viz is a visualization bridge over Cortex's data that never writes a memory, but it does three things that shape its threat model, stated plainly here: @@ -16,7 +16,7 @@ here: build the graphs. - **It writes its own derived caches into that database.** Stated precisely, because an earlier version of this file said "does not write to that store" - and that was wrong: cortex-viz never writes a memory, entity, or + and that was wrong: Hypermnesia MCP Viz never writes a memory, entity, or relationship, but it does create and write five tables of its own, `workflow_graph_snapshot`, `workflow_graph_snapshot_scoped`, `workflow_graph_layout`, `workflow_graph_layout_lod`, and `session_activity`. @@ -29,7 +29,7 @@ below is about. ## Supply-chain assurance -Before issue #37, cortex-viz had **no release workflow at all**: 2.7.1 was +Before issue #37, Hypermnesia MCP Viz had **no release workflow at all**: 2.7.1 was cut by hand, so there was no build to trace an artifact back to. As of #37, every release is produced by `.github/workflows/Release.yaml` and nothing else, and it ships verifiable provenance: @@ -39,10 +39,10 @@ else, and it ships verifiable provenance: downloaded asset binds to this repository and workflow: ```bash - gh attestation verify cortex-viz-ui-manifest.sha256 --repo cdeust/cortex-viz + gh attestation verify hypermnesia-mcp-viz-ui-manifest.sha256 --repo cdeust/cortex-viz ``` -- **UI fingerprint**: `cortex-viz-ui-manifest.sha256` is a `sha256sum` of +- **UI fingerprint**: `hypermnesia-mcp-viz-ui-manifest.sha256` is a `sha256sum` of every file under `ui/`, so the exact first-party JS your browser executes is pinned to the tagged commit and diffable against the previous release. It does **not** cover the three.js and 3d-force-graph bundles that four pages @@ -50,7 +50,7 @@ else, and it ships verifiable provenance: Subresource Integrity hash, so they are outside this guarantee. Tracked in [#50](https://github.com/cdeust/cortex-viz/issues/50). -- **SBOM**: `cortex-viz.cdx.json` (CycloneDX, from `uv.lock`) enumerates the +- **SBOM**: `hypermnesia-mcp-viz.cdx.json` (CycloneDX, from `uv.lock`) enumerates the full Python dependency graph, including the `viz-tile` stack (datashader, pyarrow, numba/llvmlite). @@ -60,10 +60,11 @@ else, and it ships verifiable provenance: (`codeql.yml`) and OpenSSF Scorecard (`scorecard.yml`) run on a schedule. The Scorecard number is a recorded baseline, not a badge. -**Not published to PyPI.** `cortex-viz` is a marketplace plugin, not a -`pip install` package (PyPI returns 404). The install path is the marketplace -pin in Cortex's manifest, so a tag + GitHub Release does not reach installs by -itself: the pin bump is the delivery step. +**Published to PyPI as `hypermnesia-mcp-viz`.** The wheel and source archive +are published through the release workflow's OIDC identity. Claude Code plugin +installs still resolve through the marketplace pin in Cortex's manifest, so a +tag + GitHub Release + PyPI publication does not update those installs by +itself: the coordinated pin bump remains that channel's delivery step. **What this does NOT claim.** Provenance proves *who built the artifact and from which commit*, not that the source is free of defects; and it is worth @@ -79,7 +80,7 @@ with the affected version/commit, reproduction steps, and impact. ### What happens next -cortex-viz has a single maintainer (see `GOVERNANCE.md`), so these are the +Hypermnesia MCP Viz has a single maintainer (see `GOVERNANCE.md`), so these are the commitments one person can actually keep, rather than a service level borrowed from a larger project: @@ -118,7 +119,7 @@ actionable. Each entry states what the checker actually requires — read from | Check | Why it cannot reach maximum here | What would close it | |---|---|---| -| **Code-Review** | Scores approved changesets. GitHub forbids approving your own pull request, and cortex-viz has one maintainer (`GOVERNANCE.md`), so the approved-changeset count is structurally 0/N. | A second maintainer with review rights. | +| **Code-Review** | Scores approved changesets. GitHub forbids approving your own pull request, and Hypermnesia MCP Viz has one maintainer (`GOVERNANCE.md`), so the approved-changeset count is structurally 0/N. | A second maintainer with review rights. | | **Branch-Protection** | The top tiers (`checks/evaluation/branch_protection.go`, Tier 4–5) require a non-zero required-reviewer count and CODEOWNERS review. Setting `required_approving_review_count >= 1` solo would deadlock every merge — no one can approve. Protection IS enabled at the tiers that do not deadlock: no force-push, no deletion, PRs required. | Same as Code-Review: a second reviewer. | | **CII-Best-Practices** | `checks/evaluation/cii_best_practices.go` scores gold=10, silver=7, passing=5, in_progress=2; only **gold** is maximum. Gold requires `contributors_unassociated` — at least two significant contributors not associated with each other. | Two unassociated contributors. | | **Maintained** | Scores 0 while the repository is younger than 90 days. Purely a function of creation date; no change to the repository affects it. | Time. | diff --git a/cortex_viz/__main__.py b/cortex_viz/__main__.py index a92d5fc..cc6b75e 100644 --- a/cortex_viz/__main__.py +++ b/cortex_viz/__main__.py @@ -1,12 +1,12 @@ -"""cortex-viz MCP server entry point. +"""hypermnesia-mcp-viz MCP server entry point. A standalone visualization MCP for Cortex. Reads Cortex's shared PostgreSQL store (read-only, via MemoryReader) and the ~/.claude artifacts; serves the neural-graph galaxy UI and methodology map. Memory/recall/wiki tools remain in the Cortex MCP — this server is the visualization surface only. -Run: ``python -m cortex_viz`` (stdio MCP transport), or either the canonical -``hypermnesia-mcp-viz`` or compatibility ``cortex-viz`` console script. +Run: ``python -m cortex_viz`` (stdio MCP transport), or the canonical +``hypermnesia-mcp-viz`` console script. """ from __future__ import annotations diff --git a/cortex_viz/core/graph_builder_nodes.py b/cortex_viz/core/graph_builder_nodes.py index e5a02b8..a0c5e3b 100644 --- a/cortex_viz/core/graph_builder_nodes.py +++ b/cortex_viz/core/graph_builder_nodes.py @@ -23,7 +23,7 @@ # G7 (design gate): interactive points need DEEP paper values (L<=52%, # >=4.5:1) — the previous set was L64-82%, pale on cream (~1.1-2:1 on the -# ~79k entity nodes these colour, cortex-viz Graph/Trace views). Re-targeted +# ~79k entity nodes these colour, hypermnesia-mcp-viz Graph/Trace views). Re-targeted # to L50% at each entry's original hue (same H, C clamped to 0.10-0.155 — # the DS-deep convention already used by every other constant in this # module's sibling ``workflow_graph_palette.py``), computed via the OKLCH diff --git a/cortex_viz/core/layout_engine.py b/cortex_viz/core/layout_engine.py index b719f2f..0918410 100644 --- a/cortex_viz/core/layout_engine.py +++ b/cortex_viz/core/layout_engine.py @@ -64,7 +64,7 @@ def layout( except ImportError as exc: raise ImportError( "igraph is required for layout — install the 'viz-tile' extra: " - "pip install cortex-viz[viz-tile]" + "pip install hypermnesia-mcp-viz[viz-tile]" ) from exc if not node_ids: diff --git a/cortex_viz/core/tile_renderer.py b/cortex_viz/core/tile_renderer.py index ac107c9..eda48f4 100644 --- a/cortex_viz/core/tile_renderer.py +++ b/cortex_viz/core/tile_renderer.py @@ -75,7 +75,7 @@ def render_tile_png( except ImportError as exc: raise ImportError( "datashader + pandas are required for tile rendering — install " - "the 'viz-tile' extra: pip install cortex-viz[viz-tile]" + "the 'viz-tile' extra: pip install hypermnesia-mcp-viz[viz-tile]" ) from exc min_x, min_y, max_x, max_y = tile_world_bbox(z, x, y) diff --git a/cortex_viz/core/wiki_source_resolve.py b/cortex_viz/core/wiki_source_resolve.py index 6781b97..38e9f20 100644 --- a/cortex_viz/core/wiki_source_resolve.py +++ b/cortex_viz/core/wiki_source_resolve.py @@ -1,7 +1,7 @@ """Resolves a ``wiki.page_sources.source_path`` to the deterministic FILE node id the workflow graph would have minted for the same file (ADR-0051 downstream consumer — see Cortex's ``pg_schema.py`` comment on -``wiki.page_sources``: "Downstream consumer: cortex-viz wiki-page -> +``wiki.page_sources``: "Downstream consumer: hypermnesia-mcp-viz wiki-page -> source-file edges"). Problem this module solves diff --git a/cortex_viz/core/workflow_graph_schema_enums.py b/cortex_viz/core/workflow_graph_schema_enums.py index 301a2e8..a091df3 100644 --- a/cortex_viz/core/workflow_graph_schema_enums.py +++ b/cortex_viz/core/workflow_graph_schema_enums.py @@ -68,7 +68,7 @@ class EdgeKind(str, Enum): # newer memory points at the older fact it replaces). Produced by # ``infrastructure.memory_supersede.load_supersede_edges`` (reads # the recorded ``memories.supersedes_id`` column — Cortex's - # supersede write path owns the lineage, cortex-viz never + # supersede write path owns the lineage, hypermnesia-mcp-viz never # re-derives it) and ingested by # ``core.workflow_graph_supersede.ingest_supersede``. Distinct from # ASSOCIATES_WITH: an association is undirected co-evidence, a diff --git a/cortex_viz/handlers/open_visualization.py b/cortex_viz/handlers/open_visualization.py index 23b6ecb..066aacd 100644 --- a/cortex_viz/handlers/open_visualization.py +++ b/cortex_viz/handlers/open_visualization.py @@ -330,7 +330,7 @@ def _schema_error_result( """ message = ( "Le schéma de la base Cortex est antérieur aux prérequis de " - "cortex-viz. Objets manquants : " + "hypermnesia-mcp-viz. Objets manquants : " + "; ".join(missing) + ". Migration automatique tentée et échouée : " + failure_detail diff --git a/cortex_viz/identity.py b/cortex_viz/identity.py index c0722a4..57477e8 100644 --- a/cortex_viz/identity.py +++ b/cortex_viz/identity.py @@ -1,11 +1,10 @@ -"""Canonical technical identity for the Cortex Viz distribution. +"""Canonical publication identity for Hypermnesia MCP Viz. -The human-facing product and Claude plugin remain ``Cortex Viz`` / -``cortex-viz``. The PyPI and official MCP Registry identity is the secured -Cortex-family name below. Cross-manifest tests bind these constants to -pyproject.toml, server.json, and the plugin version. +PyPI, MCP Registry, Claude Code, Codex, Gemini, release artifacts, and console +entry points all use this single identity. The Python import package remains +``cortex_viz`` and the source repository keeps its historical URL. """ DISTRIBUTION_NAME = "hypermnesia-mcp-viz" MCP_REGISTRY_ID = "io.github.cdeust/hypermnesia-mcp-viz" -VERSION = "2.8.0" +VERSION = "3.0.0" diff --git a/cortex_viz/infrastructure/db_probe.py b/cortex_viz/infrastructure/db_probe.py index 8687a03..ec3c774 100644 --- a/cortex_viz/infrastructure/db_probe.py +++ b/cortex_viz/infrastructure/db_probe.py @@ -1,6 +1,6 @@ -"""Startup decision: does this cortex-viz process have a Cortex database? +"""Startup decision: does this hypermnesia-mcp-viz process have a Cortex database? -cortex-viz delivers standalone value without Cortex: the Trace view reads +hypermnesia-mcp-viz delivers standalone value without Cortex: Trace reads ``~/.claude/projects/*.jsonl`` + git live and never touches PostgreSQL. This module owns the single startup question the composition root (``server.http_standalone``) and the launch handler @@ -85,7 +85,7 @@ def open_store_or_none(): return reader except psycopg.Error as exc: print( - f"[cortex-viz] Cortex PostgreSQL unreachable at " + f"[hypermnesia-mcp-viz] Cortex PostgreSQL unreachable at " f"{_redact_url(reader.url)} ({type(exc).__name__}: {exc}) — " "serving in no-DB mode: the Trace view works fully from " "~/.claude session logs + git; Graph/Brain/Knowledge/Wiki/Board " diff --git a/cortex_viz/infrastructure/memory_associations.py b/cortex_viz/infrastructure/memory_associations.py index 6aa2f07..f97a33f 100644 --- a/cortex_viz/infrastructure/memory_associations.py +++ b/cortex_viz/infrastructure/memory_associations.py @@ -79,7 +79,7 @@ link that happens to share no entity. No I/O beyond the single ``pg_store.query`` SELECT — this module never -INSERTs, UPDATEs, or DELETEs; cortex-viz is a read-only bridge over +INSERTs, UPDATEs, or DELETEs; hypermnesia-mcp-viz is a read-only bridge over Cortex's shared Postgres store. """ diff --git a/cortex_viz/infrastructure/memory_read.py b/cortex_viz/infrastructure/memory_read.py index 0f1b35d..57663ea 100644 --- a/cortex_viz/infrastructure/memory_read.py +++ b/cortex_viz/infrastructure/memory_read.py @@ -1,4 +1,4 @@ -"""Read-only PostgreSQL reader — the cortex-viz ↔ Cortex data contract. +"""Read-only PostgreSQL reader — the hypermnesia-mcp-viz ↔ Cortex contract. This module severs the ONLY hard code-coupling that remained between the visualization stack and the Cortex memory engine: the standalone HTTP server diff --git a/cortex_viz/infrastructure/memory_supersede.py b/cortex_viz/infrastructure/memory_supersede.py index dd16cb4..6052f80 100644 --- a/cortex_viz/infrastructure/memory_supersede.py +++ b/cortex_viz/infrastructure/memory_supersede.py @@ -3,7 +3,7 @@ One SELECT over the recorded ``memories.supersedes_id`` column — the lineage is WRITTEN by Cortex's supersede write path (``remember( supersedes_id)`` / ``supersede_atomic``, merged on Cortex main, PR #82); -cortex-viz only reads what was recorded and never re-derives or +hypermnesia-mcp-viz only reads what was recorded and never re-derives or mutates it. Uses the partial index ``idx_memories_supersedes`` (``WHERE supersedes_id IS NOT NULL``) so the scan touches only the chain rows. @@ -24,7 +24,7 @@ already drops edges whose old endpoint never made it into the graph. No I/O beyond the single ``pg_store.query`` SELECT — this module never -INSERTs, UPDATEs, or DELETEs; cortex-viz is a read-only bridge over +INSERTs, UPDATEs, or DELETEs; hypermnesia-mcp-viz is a read-only bridge over Cortex's shared Postgres store. """ diff --git a/cortex_viz/infrastructure/prd_bridge.py b/cortex_viz/infrastructure/prd_bridge.py index cdb7c6f..7786f16 100644 --- a/cortex_viz/infrastructure/prd_bridge.py +++ b/cortex_viz/infrastructure/prd_bridge.py @@ -1,8 +1,8 @@ -"""Bridge to the ``prd-spec-generator`` sibling MCP (the third live source). +"""Bridge to the ``ai-architect-mcp-spec`` sibling MCP (the third live source). -prd-spec-generator is a STATELESS reducer: it turns a feature description into a -9-file Markdown PRD on demand and keeps no persistent queryable graph. So this -bridge has two halves, both degrading gracefully to empty (mirroring +ai-architect-mcp-spec is a STATELESS reducer: it turns a feature description +into a 9-file Markdown PRD on demand and keeps no persistent queryable graph. +So this bridge has two halves, both degrading gracefully to empty (mirroring ``ap_bridge`` when AP has no graph): 1. A connectable MCPClient (``PRDBridge``) for read-only liveness/config @@ -47,10 +47,10 @@ def _resolve_command() -> dict | None: - """Resolve the MCP-client config for prd-spec-generator. + """Resolve the MCP-client config for ai-architect-mcp-spec. Priority: ``CORTEX_PRD_COMMAND`` env (JSON ``{command,args,env}``) → the - active ``prd-spec-generator@*`` entry in ``installed_plugins.json`` (launch + active ``ai-architect-mcp-spec@*`` entry in ``installed_plugins.json`` (launch via ``bash /bin/ensure-deps.sh `` with the PRD_GEN_* env the plugin's ``.mcp.json`` declares). Returns None when no install is found (graceful degradation). @@ -68,7 +68,7 @@ def _resolve_command() -> dict | None: data = json.loads(installed.read_text(encoding="utf-8")) plugins = data.get("plugins", {}) if isinstance(data, dict) else {} for key, entries in plugins.items(): - if not key.startswith("prd-spec-generator@"): + if not key.startswith("ai-architect-mcp-spec@"): continue if not isinstance(entries, list) or not entries: continue diff --git a/cortex_viz/infrastructure/schema_migrate.py b/cortex_viz/infrastructure/schema_migrate.py index eeb19ce..893eb60 100644 --- a/cortex_viz/infrastructure/schema_migrate.py +++ b/cortex_viz/infrastructure/schema_migrate.py @@ -16,7 +16,7 @@ NOT go through ``scripts/launcher.py``: that script additionally routes through ``launcher_deps.ensure_deps`` (a bootstrap that can pip-install over the network) and commits this call to launcher.py's internal -argv/env protocol, which Cortex has never frozen. cortex-viz's own venv +argv/env protocol, which Cortex has never frozen. hypermnesia-mcp-viz's own venv already satisfies every import ``mcp_server.migrate`` needs (psycopg, psycopg-pool, pgvector, numpy — verified against the installed plugin's dependency set), so ``-m`` with ``cwd=plugin_root`` resolves the module @@ -124,7 +124,7 @@ def run_schema_migration( ) -> MigrationResult: """Invoke ``mcp_server.migrate`` in the installed Cortex plugin. - Precondition: ``database_url`` is the same DSN cortex-viz already + Precondition: ``database_url`` is the same DSN hypermnesia-mcp-viz already reads its store from (schema state must match what the preflight just checked). Postcondition: no in-process import of Cortex code occurs — the migration runs in a fresh subprocess so an old plugin diff --git a/cortex_viz/infrastructure/wiki_graph.py b/cortex_viz/infrastructure/wiki_graph.py index a364c52..883e278 100644 --- a/cortex_viz/infrastructure/wiki_graph.py +++ b/cortex_viz/infrastructure/wiki_graph.py @@ -34,7 +34,7 @@ ``core.wiki_source_resolve.resolve_file_node_id``. No I/O beyond four read-only ``pg_store.query`` SELECTs — this module -never INSERTs, UPDATEs, or DELETEs; cortex-viz is a read-only bridge +never INSERTs, UPDATEs, or DELETEs; hypermnesia-mcp-viz is a read-only bridge over Cortex's shared Postgres store. """ diff --git a/cortex_viz/infrastructure/workflow_graph_source_native_ast.py b/cortex_viz/infrastructure/workflow_graph_source_native_ast.py index 7aa33d8..9779a7d 100644 --- a/cortex_viz/infrastructure/workflow_graph_source_native_ast.py +++ b/cortex_viz/infrastructure/workflow_graph_source_native_ast.py @@ -5,7 +5,7 @@ automatised-pipeline (AP) hasn't indexed. That local parser is a full subsystem (``ast_extractors*`` + ``codebase_*``, 12 modules) whose job AP already does. Per the thin-viz extraction decision (do not duplicate Cortex -subsystems; delegate to the sibling MCP), cortex-viz does NOT bundle the local +subsystems; delegate to the sibling MCP), hypermnesia-mcp-viz does NOT bundle the local parser — codebase symbols come from AP via ``workflow_graph_source_ast``. This stub preserves the interface so ``handlers/workflow_graph`` imports and @@ -28,7 +28,7 @@ class WorkflowGraphNativeASTSource: """Interface-compatible no-op. Local AST enrichment is delegated to AP.""" def load_symbols(self, file_paths: Iterable[str]) -> list[dict[str, Any]]: - # Local tree-sitter parsing is not bundled in cortex-viz; AP supplies + # Local tree-sitter parsing is not bundled in hypermnesia-mcp-viz; AP supplies # codebase symbols via workflow_graph_source_ast. return [] diff --git a/cortex_viz/server/http_standalone.py b/cortex_viz/server/http_standalone.py index a405afc..f8f57c8 100644 --- a/cortex_viz/server/http_standalone.py +++ b/cortex_viz/server/http_standalone.py @@ -99,7 +99,7 @@ def _get_store(no_db: bool): """Return the read-only viz store for this standalone process, or ``None`` when this process runs without a database. - The boundary cut (thin-viz): cortex-viz never instantiates Cortex's + The boundary cut (thin-viz): hypermnesia-mcp-viz never instantiates Cortex's MemoryStore (writes, schema init, embeddings, the full storage layer). It reads Cortex's shared PostgreSQL through MemoryReader, which exposes exactly the 14 read methods + dict-row `_conn` the viz routes consume. @@ -112,7 +112,7 @@ def _get_store(no_db: bool): """ if no_db: print( - "[cortex-viz] no-DB mode (explicit): serving the Trace view " + "[hypermnesia-mcp-viz] no-DB mode (explicit): serving the Trace view " "from ~/.claude session logs + git; DB-backed views disabled.", file=sys.stderr, ) diff --git a/cortex_viz/server/http_standalone_endpoints.py b/cortex_viz/server/http_standalone_endpoints.py index b1ceda3..af86244 100644 --- a/cortex_viz/server/http_standalone_endpoints.py +++ b/cortex_viz/server/http_standalone_endpoints.py @@ -142,7 +142,7 @@ def serve_graph_full(handler, store) -> None: def serve_prd(handler, store=None) -> None: """GET /api/prd — PRD document/section nodes from discovered artifacts. - The third bridge's view (prd-spec-generator). Returns + The third bridge's view (ai-architect-mcp-spec). Returns ``{nodes, edges, available}`` from any ``prd-output//`` PRDs found on disk. ``available`` is False with an empty graph when no PRD has been generated yet (the stateless pipeline keeps no standing store) — the UI @@ -158,7 +158,7 @@ def serve_prd(handler, store=None) -> None: "available": bool(frag["nodes"]), "nodes": frag["nodes"], "edges": frag["edges"], - "meta": {"schema": "prd.v1", "source": "prd-spec-generator"}, + "meta": {"schema": "prd.v1", "source": "ai-architect-mcp-spec"}, }, ) except Exception as e: diff --git a/cortex_viz/server/http_standalone_memories.py b/cortex_viz/server/http_standalone_memories.py index 249f9d6..11ecf65 100644 --- a/cortex_viz/server/http_standalone_memories.py +++ b/cortex_viz/server/http_standalone_memories.py @@ -3,7 +3,7 @@ GET /api/memories — keyset-paged, filterable memory list GET /api/memories/facets — aggregate filter facets (chips) -These read the shared Cortex Postgres directly (cortex-viz is the live bridge, +These read the shared Cortex Postgres directly (hypermnesia-mcp-viz is the live bridge, not a 410 stub). All SQL lives in ``infrastructure.memory_browse``; this module is the thin composition root that parses the query string and serializes. """ diff --git a/cortex_viz/server/http_standalone_nodb.py b/cortex_viz/server/http_standalone_nodb.py index b4f9a70..02c2123 100644 --- a/cortex_viz/server/http_standalone_nodb.py +++ b/cortex_viz/server/http_standalone_nodb.py @@ -77,7 +77,7 @@ def serve_db_unavailable(handler, feature: str) -> None: "error": "db_unavailable", "feature": feature, "detail": ( - "cortex-viz is running in no-DB mode (Trace only). " + "hypermnesia-mcp-viz is running in no-DB mode (Trace only). " f"Install Cortex ({CORTEX_INSTALL_URL}) and point " "DATABASE_URL at its PostgreSQL to enable this view." ), diff --git a/cortex_viz/server/http_standalone_routes.py b/cortex_viz/server/http_standalone_routes.py index 2f442a4..cd5ee24 100644 --- a/cortex_viz/server/http_standalone_routes.py +++ b/cortex_viz/server/http_standalone_routes.py @@ -3,7 +3,7 @@ Split out of ``http_standalone.py`` (was 554 lines) to respect the 500-line file limit. Holds the GET dispatch (``_route_unified_get``), the wiki DB-op path map, and the 410-Gone helper for memory-domain features -that live in the Cortex MCP, not cortex-viz. Composition-root glue — every +that live in the Cortex MCP, not hypermnesia-mcp-viz. Composition-root glue — every endpoint body is in a sibling module; this only routes. """ @@ -34,7 +34,7 @@ def _feature_moved(handler, feature: str, use_instead: str) -> None: """Reply 410 Gone for a tab whose data lives in the Cortex memory MCP. - cortex-viz is the visualization MCP; memory-browser / wiki / causal-chain + hypermnesia-mcp-viz is the visualization MCP; memory-browser / wiki / causal-chain are memory-domain features served by Cortex. This keeps the boundary honest instead of silently 404-ing or importing the subsystem back in. """ @@ -44,7 +44,7 @@ def _feature_moved(handler, feature: str, use_instead: str) -> None: "feature": feature, "detail": ( f"'{feature}' is a Cortex memory feature, not bundled in " - f"cortex-viz. Use {use_instead} via the Cortex MCP." + f"hypermnesia-mcp-viz. Use {use_instead} via the Cortex MCP." ), } ).encode() @@ -190,7 +190,7 @@ def _route_unified_get( return if path_no_qs == "/api/memories": # Keyset-paged memory browser (Knowledge + Board views), read straight - # from the shared Cortex PG — cortex-viz is the live bridge. + # from the shared Cortex PG — hypermnesia-mcp-viz is the live bridge. from cortex_viz.server.http_standalone_memories import serve_memories serve_memories(handler, store) diff --git a/cortex_viz/server/mcp_tools.py b/cortex_viz/server/mcp_tools.py index 388d143..365f6db 100644 --- a/cortex_viz/server/mcp_tools.py +++ b/cortex_viz/server/mcp_tools.py @@ -1,6 +1,6 @@ -"""MCP tool registration for the cortex-viz server. +"""MCP tool registration for the hypermnesia-mcp-viz server. -Registers the visualization tools whose handlers live in cortex-viz: +Registers the visualization tools whose handlers live in hypermnesia-mcp-viz: * open_visualization — launch the bundled neural-graph UI (galaxy view). * get_methodology_graph — return methodology-map graph data. @@ -20,7 +20,7 @@ def register(mcp: FastMCP) -> None: - """Register cortex-viz tools on the FastMCP instance.""" + """Register hypermnesia-mcp-viz tools on the FastMCP instance.""" _register_open_visualization(mcp) _register_get_methodology_graph(mcp) diff --git a/cortex_viz/tool_error_handler.py b/cortex_viz/tool_error_handler.py index 9513cf8..4720899 100644 --- a/cortex_viz/tool_error_handler.py +++ b/cortex_viz/tool_error_handler.py @@ -1,8 +1,8 @@ -"""Friendly error handling for cortex-viz MCP tool calls. +"""Friendly error handling for hypermnesia-mcp-viz MCP tool calls. Minimal counterpart to Cortex's tool_error_handler: wraps a handler so MCP clients never see raw tracebacks, and offloads the (sync-DB-calling) handler -body to a worker thread so it does not block the event loop. cortex-viz does +body to a worker thread so it does not block the event loop. hypermnesia-mcp-viz does NOT bundle Cortex's per-tool admission semaphore or Prometheus metrics — the viz exposes a handful of read/launch tools, not the full memory surface, so that machinery is out of scope here. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index bdfe71c..97f7d79 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -66,7 +66,7 @@ Cortex's live Python objects. | Live host activity | `POST /api/activity`, `docs/host-event-v1.schema.json` | append-only derived activity | | Cognitive profiles | `~/.claude/methodology/profiles.json` | read-only | | Codebase graph (AST symbols, impact) | [`automatised-pipeline`](https://github.com/cdeust/ai-automatised-pipeline) MCP | read-only, stdio | -| PRD document nodes | [`prd-spec-generator`](https://github.com/cdeust/ai-prd-generator) MCP | read-only | +| PRD document nodes | [`ai-architect-mcp-spec`](https://github.com/cdeust/ai-architect-mcp-spec) MCP | read-only | | File diffs and commit history | local `git` | read-only | **Degraded mode is explicit, never silent.** With no database reachable, the diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 2aaf602..1d27f26 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -1,8 +1,9 @@ # Releasing -Releases use the canonical distribution identity `hypermnesia-mcp-viz`. The -GitHub repository, Cortex Viz product name, Claude Code plugin name, Python -import package, and `cortex-viz` compatibility command do not change. +Releases use the canonical product, plugin, MCP server, Python distribution, +and console identity `hypermnesia-mcp-viz`. The Python import package remains +`cortex_viz`, and the historical GitHub repository URL remains +`cdeust/cortex-viz`; neither is a published compatibility identity. ## One-time PyPI setup @@ -19,10 +20,13 @@ identity through the `pypi` GitHub environment. ## Release flow -1. Ensure `pyproject.toml`, `cortex_viz/identity.py`, `server.json`, the Claude - plugin manifest, and the README badge carry the same release version. - `python -m scripts.check_distribution_artifact` proves all five agree after - `uv build`; it is the same gate CI and the release workflow run. +1. Ensure `pyproject.toml`, `cortex_viz/identity.py`, `server.json`, the Claude, + Codex, and Gemini manifests, the Claude marketplace metadata/pin, the lock + file, the README badge, and CHANGELOG carry the same release version. + `python -m scripts.check_distribution_artifact` checks the source surfaces, + requires `GITHUB_REF_NAME` to equal `v` when `GITHUB_REF_TYPE=tag`, + and validates the built wheel after `uv build`; the release workflow runs + that same gate before publication. 2. Promote the `## [Unreleased]` CHANGELOG entries to `## [] - ` and leave `## [Unreleased]` in place, empty, for the next cycle. No gate enforces this, and the section is the human-readable release record. @@ -37,4 +41,4 @@ identity through the `pypi` GitHub environment. resolve through that manifest, so until it is bumped the release reaches zero installs no matter how green the tag build was. That is the failure mode recorded in Cortex #179, which cost six zetetic-team-subagents releases and - two cortex-viz releases. The release is not done until this step lands. + two visualization releases. The release is not done until this step lands. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 0a84e2c..dcbd5ae 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,14 +1,14 @@ # Roadmap -_Last updated: 2026-08-03._ +_Last updated: 2026-08-04._ -cortex-viz is a single-maintainer project (see [GOVERNANCE.md](../GOVERNANCE.md)), +Hypermnesia MCP Viz is a single-maintainer project (see [GOVERNANCE.md](../GOVERNANCE.md)), so this roadmap states direction and known gaps rather than dated commitments. Anything with an issue number is tracked; anything without one is an intention. ## Where the project is -Current version **2.8.0**. All six views (Graph, Brain, Trace, Knowledge, Wiki, +Current version **3.0.0**. All six views (Graph, Brain, Trace, Knowledge, Wiki, Board) are bridged to live data. The galaxy builds end to end at 75k+ nodes and the 3D brain streams the full graph into a cortical mesh. Trace works with no database at all. The supply-chain wave ([#37](https://github.com/cdeust/cortex-viz/issues/37)) @@ -51,7 +51,7 @@ remediation in #49 are complete; they are no longer listed as open work. ## Longer term -- **OpenSSF Best Practices Gold.** cortex-viz [earned Silver on +- **OpenSSF Best Practices Gold.** Hypermnesia MCP Viz [earned Silver on 2026-08-03](https://www.bestpractices.dev/projects/13846). The evidence is in `.bestpractices.json`. Gold additionally needs a second maintainer and two-person review, which depends on @@ -66,10 +66,10 @@ remediation in #49 are complete; they are no longer listed as open work. - **A separate legacy `cortex-viz` PyPI distribution.** The canonical PyPI and MCP Registry name is `hypermnesia-mcp-viz`, matching the published sibling - `hypermnesia-mcp`; that package includes the legacy `cortex-viz` command, so - publishing a second distribution would split the release identity without - adding compatibility. -- **Writing to Cortex's memory tables.** cortex-viz renders, it never + `hypermnesia-mcp`. Version 3.0.0 removes the legacy console shim that 2.8.0 + exposed; publishing a second distribution would split the release identity + instead of completing the migration. +- **Writing to Cortex's memory tables.** Hypermnesia MCP Viz renders, it never remembers. That boundary is the point of the extraction and is not up for negotiation. - **A remote or multi-user deployment.** The server binds 127.0.0.1 and has no diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 0000000..60d978e --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,15 @@ +{ + "name": "hypermnesia-mcp-viz", + "version": "3.0.0", + "description": "Read-only visualization and graph MCP for Cortex, packaged for Gemini CLI.", + "mcpServers": { + "hypermnesia-mcp-viz": { + "command": "uvx", + "args": [ + "--from", + "hypermnesia-mcp-viz", + "hypermnesia-mcp-viz" + ] + } + } +} diff --git a/pyproject.toml b/pyproject.toml index 9b4b5a1..91953e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "hypermnesia-mcp-viz" -version = "2.8.0" +version = "3.0.0" description = "Visualization and graph MCP server for Cortex — neural graph, methodology map, workflow graph, and trace UI extracted from the Cortex memory engine" readme = "README.md" license = "MIT" @@ -45,7 +45,6 @@ Homepage = "https://github.com/cdeust/cortex-viz" Repository = "https://github.com/cdeust/cortex-viz" [project.scripts] -cortex-viz = "cortex_viz.__main__:main" hypermnesia-mcp-viz = "cortex_viz.__main__:main" [project.optional-dependencies] @@ -67,11 +66,11 @@ viz-tile = [ # project already requires. source: osv.dev, resolved 2026-07-28. "Pillow>=12.3.0", ] -# The viz read contract: cortex-viz reads Cortex's PG store + ~/.claude +# The viz read contract: hypermnesia-mcp-viz reads Cortex's PG store + ~/.claude # artifacts directly. These read-path drivers were PROMOTED to core # ``dependencies`` (see the note there) because the server cannot start -# without them. This extra is kept as a back-compat alias so existing -# ``cortex-viz[data]`` install commands / lockfiles still resolve. +# without them. The ``data`` extra remains as an idempotent feature selector +# for host manifests that explicitly request the PostgreSQL read path. data = [ "psycopg[binary]>=3.1", "psycopg-pool>=3.2", diff --git a/scripts/check_distribution_artifact.py b/scripts/check_distribution_artifact.py index 2fb62c9..763d9bd 100644 --- a/scripts/check_distribution_artifact.py +++ b/scripts/check_distribution_artifact.py @@ -5,6 +5,7 @@ import configparser import json +import os import sys import zipfile from email.parser import Parser @@ -37,16 +38,13 @@ def wheel_member(wheel: zipfile.ZipFile, suffix: str) -> str: return wheel.read(matches[0]).decode("utf-8") -def main() -> None: +def require_source_identity() -> None: + """Check every source manifest before opening a built artifact.""" project = tomllib.loads((ROOT / "pyproject.toml").read_text()) require(project["project"]["name"] == DISTRIBUTION_NAME, "project name drifted") require(project["project"]["version"] == VERSION, "project version drifted") require( - project["project"]["scripts"] - == { - "cortex-viz": EXPECTED_ENTRY_POINT, - "hypermnesia-mcp-viz": EXPECTED_ENTRY_POINT, - }, + project["project"]["scripts"] == {"hypermnesia-mcp-viz": EXPECTED_ENTRY_POINT}, "console entry points drifted", ) @@ -67,13 +65,44 @@ def main() -> None: "MCP Registry package declaration drifted", ) - plugin = json.loads((ROOT / ".claude-plugin" / "plugin.json").read_text()) - require(plugin["name"] == "cortex-viz", "display/plugin compatibility changed") - require(plugin["version"] == VERSION, "Claude plugin version drifted") + versioned_manifests = { + "Claude plugin": ROOT / ".claude-plugin" / "plugin.json", + "Codex plugin": ROOT / ".codex-plugin" / "plugin.json", + "Gemini extension": ROOT / "gemini-extension.json", + } + for label, path in versioned_manifests.items(): + manifest = json.loads(path.read_text()) + require(manifest["name"] == DISTRIBUTION_NAME, f"{label} identity drifted") + require(manifest["version"] == VERSION, f"{label} version drifted") + + marketplace = json.loads((ROOT / ".claude-plugin" / "marketplace.json").read_text()) + require(marketplace["metadata"]["version"] == VERSION, "marketplace drifted") + require(marketplace["plugins"][0]["version"] == VERSION, "plugin pin drifted") readme = (ROOT / "README.md").read_text() require(f"version-{VERSION}-brightgreen" in readme, "README badge drifted") require(f'alt="Version {VERSION}"' in readme, "README badge alt drifted") + changelog = (ROOT / "CHANGELOG.md").read_text() + require(f"## [{VERSION}]" in changelog, "CHANGELOG release section drifted") + + +def require_release_tag() -> None: + """Reject a tag whose immutable version disagrees with the artifacts.""" + # GitHub exposes PR refs such as ``107/merge`` through GITHUB_REF_NAME; + # GITHUB_REF_TYPE is the authoritative branch-vs-tag discriminator. + # https://docs.github.com/actions/reference/workflows-and-actions/variables + if os.environ.get("GITHUB_REF_TYPE") != "tag": + return + tag = os.environ.get("GITHUB_REF_NAME") + require(bool(tag), "tag workflow did not provide GITHUB_REF_NAME") + require( + tag == f"v{VERSION}", + f"release tag {tag} does not match project version {VERSION}", + ) + + +def require_wheel_identity() -> None: + """Check the built wheel metadata and its only console entry point.""" with zipfile.ZipFile(require_one_wheel()) as wheel: metadata = Parser().parsestr(wheel_member(wheel, ".dist-info/METADATA")) @@ -84,13 +113,15 @@ def main() -> None: entry_points.read_string(wheel_member(wheel, ".dist-info/entry_points.txt")) require( dict(entry_points["console_scripts"]) - == { - "cortex-viz": EXPECTED_ENTRY_POINT, - "hypermnesia-mcp-viz": EXPECTED_ENTRY_POINT, - }, + == {"hypermnesia-mcp-viz": EXPECTED_ENTRY_POINT}, "wheel console entry points drifted", ) + +def main() -> None: + require_source_identity() + require_release_tag() + require_wheel_identity() print(f"distribution identity OK: {DISTRIBUTION_NAME} {VERSION}") diff --git a/server.json b/server.json index c9013fd..79a4dfe 100644 --- a/server.json +++ b/server.json @@ -6,13 +6,13 @@ "url": "https://github.com/cdeust/cortex-viz", "source": "github" }, - "version": "2.8.0", + "version": "3.0.0", "websiteUrl": "https://ai-architect.tools/cortex", "packages": [ { "registryType": "pypi", "identifier": "hypermnesia-mcp-viz", - "version": "2.8.0", + "version": "3.0.0", "runtimeHint": "python", "transport": { "type": "stdio" diff --git a/skills/cortex-visualize/SKILL.md b/skills/cortex-visualize/SKILL.md index 6aea1e5..c9f2950 100644 --- a/skills/cortex-visualize/SKILL.md +++ b/skills/cortex-visualize/SKILL.md @@ -1,16 +1,16 @@ --- name: cortex-visualize -description: "Launch the interactive Cortex neural-graph visualization (the cortex-viz MCP). Use when the user says 'show visualization', 'show me the graph', 'visualize memories', 'show memory map', 'open neural graph', 'cortex-visualize', or when a visual overview of the memory system, codebase graph, or session trace would help." +description: "Launch the interactive Cortex neural-graph visualization (the hypermnesia-mcp-viz MCP). Use when the user says 'show visualization', 'show me the graph', 'visualize memories', 'show memory map', 'open neural graph', 'cortex-visualize', or when a visual overview of the memory system, codebase graph, or session trace would help." --- -# Visualize — Interactive Neural Graph (cortex-viz) +# Visualize — Interactive Neural Graph (hypermnesia-mcp-viz) ## Keywords visualize, graph, neural graph, memory map, show memories, visual overview, entity graph, methodology graph, codebase graph, session trace, show profile, galaxy ## Overview -Launch the interactive browser-based neural graph served by the **cortex-viz** +Launch the interactive browser-based neural graph served by the **hypermnesia-mcp-viz** MCP. It reads Cortex's shared PostgreSQL store (read-only) plus your `~/.claude` session history and wiki, and opens six reading angles over the same data: **Trace** (default), **Graph** (the galaxy), **Knowledge**, **Wiki**, **Board**, @@ -19,10 +19,11 @@ and **Pipeline**. **Use this skill when:** the user wants a visual overview, is exploring the knowledge/codebase graph, or needs to present or screenshot Cortex's state. -**Requires:** the `cortex-viz` MCP. If its tools are not available, tell the -user to install cortex-viz (https://github.com/cdeust/cortex-viz). +**Requires:** the `hypermnesia-mcp-viz` MCP. If its tools are not available, +tell the user to install `hypermnesia-mcp-viz` +(https://github.com/cdeust/cortex-viz). -**Works without Cortex:** cortex-viz does NOT require the Cortex memory +**Works without Cortex:** hypermnesia-mcp-viz does NOT require the Cortex memory engine to be useful. When Cortex's PostgreSQL is unreachable (or `CORTEX_VIZ_NO_DB=1` is set) the server starts in no-DB mode: the **Trace** view — the per-session execution drill over `~/.claude` session logs + git — @@ -35,13 +36,13 @@ launch it freely even if the user hasn't installed Cortex. ### Launch the neural graph ``` -cortex-viz:open_visualization({}) +mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__open_visualization({}) ``` Or filter to a specific domain: ``` -cortex-viz:open_visualization({ "domain": "cortex" }) +mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__open_visualization({ "domain": "cortex" }) ``` Opens in the browser on a local 127.0.0.1 port. Features: @@ -61,5 +62,5 @@ Opens in the browser on a local 127.0.0.1 port. Features: For custom visualization or analysis without opening the browser: ``` -cortex-viz:get_methodology_graph({ "domain": "" }) +mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__get_methodology_graph({ "domain": "" }) ``` diff --git a/tests/test_bridge_coverage_contracts.py b/tests/test_bridge_coverage_contracts.py index fa332fe..fe63932 100644 --- a/tests/test_bridge_coverage_contracts.py +++ b/tests/test_bridge_coverage_contracts.py @@ -267,14 +267,25 @@ def test_prd_resolve_command_env_and_plugin(tmp_path, monkeypatch): monkeypatch.delenv("CORTEX_PRD_COMMAND") monkeypatch.setattr(Path, "home", lambda: tmp_path) - root = tmp_path / "prd-plugin" + root = tmp_path / "spec-plugin" ensure = root / "bin" / "ensure-deps.sh" ensure.parent.mkdir(parents=True) ensure.write_text("#!/bin/sh") manifest = tmp_path / ".claude" / "plugins" / "installed_plugins.json" manifest.parent.mkdir(parents=True) manifest.write_text( - json.dumps({"plugins": {"prd-spec-generator@x": [{"installPath": str(root)}]}}) + json.dumps( + { + "plugins": { + "prd-spec-generator@legacy": [ + {"installPath": str(tmp_path / "legacy-plugin")} + ], + "ai-architect-mcp-spec@cortex-plugins": [ + {"installPath": str(root)} + ], + } + } + ) ) resolved = prd._resolve_command() assert resolved["command"] == "bash" diff --git a/tests/test_distribution_identity.py b/tests/test_distribution_identity.py index 080cfde..041fd39 100644 --- a/tests/test_distribution_identity.py +++ b/tests/test_distribution_identity.py @@ -4,6 +4,7 @@ import asyncio import json +import os import subprocess import sys from pathlib import Path @@ -20,20 +21,81 @@ def test_source_manifests_share_one_versioned_identity() -> None: project = tomllib.loads((ROOT / "pyproject.toml").read_text()) server = json.loads((ROOT / "server.json").read_text()) plugin = json.loads((ROOT / ".claude-plugin" / "plugin.json").read_text()) + claude_marketplace = json.loads( + (ROOT / ".claude-plugin" / "marketplace.json").read_text() + ) + codex = json.loads((ROOT / ".codex-plugin" / "plugin.json").read_text()) + codex_marketplace = json.loads( + (ROOT / ".agents" / "plugins" / "marketplace.json").read_text() + ) + gemini = json.loads((ROOT / "gemini-extension.json").read_text()) assert project["project"]["name"] == DISTRIBUTION_NAME assert project["project"]["version"] == VERSION assert server["name"] == MCP_REGISTRY_ID assert server["version"] == VERSION + assert server["packages"][0]["version"] == VERSION assert plugin["version"] == VERSION - assert plugin["name"] == "cortex-viz" + assert plugin["name"] == DISTRIBUTION_NAME + assert DISTRIBUTION_NAME in plugin["mcpServers"] + assert claude_marketplace["name"] == f"{DISTRIBUTION_NAME}-marketplace" + assert claude_marketplace["metadata"]["version"] == VERSION + assert claude_marketplace["plugins"][0]["name"] == DISTRIBUTION_NAME + assert claude_marketplace["plugins"][0]["version"] == VERSION + assert codex["name"] == DISTRIBUTION_NAME + assert codex["version"] == VERSION + assert DISTRIBUTION_NAME in codex["mcpServers"] + assert codex_marketplace["name"] == f"{DISTRIBUTION_NAME}-marketplace" + assert codex_marketplace["plugins"][0]["name"] == DISTRIBUTION_NAME + assert gemini["name"] == DISTRIBUTION_NAME + assert gemini["version"] == VERSION + assert DISTRIBUTION_NAME in gemini["mcpServers"] + readme = (ROOT / "README.md").read_text() + changelog = (ROOT / "CHANGELOG.md").read_text() + assert f"version-{VERSION}-brightgreen" in readme + assert f'alt="Version {VERSION}"' in readme + assert f"## [{VERSION}]" in changelog -def test_both_console_names_resolve_to_the_same_entry_point() -> None: + +def test_only_canonical_console_name_is_published() -> None: project = tomllib.loads((ROOT / "pyproject.toml").read_text()) scripts = project["project"]["scripts"] assert scripts["hypermnesia-mcp-viz"] == "cortex_viz.__main__:main" - assert scripts["cortex-viz"] == scripts["hypermnesia-mcp-viz"] + assert "cortex-viz" not in scripts + + +def test_breaking_plugin_migration_is_explicit() -> None: + readme = (ROOT / "README.md").read_text() + changelog = (ROOT / "CHANGELOG.md").read_text() + + for text in (readme, changelog): + assert "claude plugin uninstall cortex-viz@cortex-plugins" in text + assert "hypermnesia-mcp-viz" in text + assert "plugin:cortex-viz:*" not in text + assert "plugin:hypermnesia-mcp-viz:*" not in text + assert "pip uninstall cortex-viz" not in readme + assert "console_scripts" in readme + assert "cdeust/Cortex#351" in changelog + + +def test_claude_tool_names_are_composed_from_plugin_and_server_names() -> None: + plugin = json.loads((ROOT / ".claude-plugin" / "plugin.json").read_text()) + server_names = list(plugin["mcpServers"]) + assert server_names == [DISTRIBUTION_NAME] + + prefix = f"mcp__plugin_{plugin['name']}_{server_names[0]}__" + assert prefix == ("mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__") + + documents = [ + (ROOT / "README.md").read_text(), + (ROOT / "CHANGELOG.md").read_text(), + (ROOT / "skills" / "cortex-visualize" / "SKILL.md").read_text(), + ] + for tool in ("open_visualization", "get_methodology_graph"): + composed_name = f"{prefix}{tool}" + for document in documents: + assert composed_name in document def test_artifact_guard_remains_active_under_python_optimization() -> None: @@ -59,6 +121,40 @@ def test_artifact_guard_remains_active_under_python_optimization() -> None: assert "optimization guard" in result.stderr +def test_release_guard_rejects_tag_version_mismatch() -> None: + env = os.environ.copy() + env["GITHUB_REF_TYPE"] = "tag" + env["GITHUB_REF_NAME"] = "v0.0.0" + result = subprocess.run( + [sys.executable, "-m", "scripts.check_distribution_artifact"], + cwd=ROOT, + env=env, + capture_output=True, + text=True, + check=False, + ) + assert result.returncode != 0 + assert "release tag v0.0.0 does not match project version" in result.stderr + + +def test_release_guard_ignores_branches_and_accepts_the_matching_tag( + monkeypatch, +) -> None: + from scripts.check_distribution_artifact import require_release_tag + + monkeypatch.delenv("GITHUB_REF_TYPE", raising=False) + monkeypatch.delenv("GITHUB_REF_NAME", raising=False) + require_release_tag() + + monkeypatch.setenv("GITHUB_REF_TYPE", "branch") + monkeypatch.setenv("GITHUB_REF_NAME", "107/merge") + require_release_tag() + + monkeypatch.setenv("GITHUB_REF_TYPE", "tag") + monkeypatch.setenv("GITHUB_REF_NAME", f"v{VERSION}") + require_release_tag() + + def test_stdio_handshake_advertises_canonical_identity() -> None: async def exercise() -> None: client = MCPClient( diff --git a/tests/test_http_routes_endpoints_coverage_contracts.py b/tests/test_http_routes_endpoints_coverage_contracts.py index 7e983c8..4ef31d6 100644 --- a/tests/test_http_routes_endpoints_coverage_contracts.py +++ b/tests/test_http_routes_endpoints_coverage_contracts.py @@ -279,6 +279,10 @@ def test_basic_endpoints_report_success_and_errors(monkeypatch): endpoints.serve_prd(_Handler()) response = sent.pop() assert response["available"] is True + assert response["meta"] == { + "schema": "prd.v1", + "source": "ai-architect-mcp-spec", + } monkeypatch.setattr( prd_bridge, "read_prd_graph", diff --git a/tests/test_mcp_app.py b/tests/test_mcp_app.py index 4815b83..b57dec2 100644 --- a/tests/test_mcp_app.py +++ b/tests/test_mcp_app.py @@ -1,8 +1,8 @@ -"""Phase 5 acceptance: the cortex-viz MCP app builds and registers its tools. +"""Phase 5 acceptance: the hypermnesia-mcp-viz app registers its MCP tools. Verifies the FastMCP entry point wires the visualization tools without needing a live database (tool registration is import-time; handler execution is not -exercised here). This is the "cortex-viz MCP loads" half of the extraction's +exercised here). This is the "hypermnesia-mcp-viz MCP loads" half of the extraction's acceptance bar. """ diff --git a/tests/test_mcp_tool_prefixes.py b/tests/test_mcp_tool_prefixes.py index 2ee7dc9..9a97f21 100644 --- a/tests/test_mcp_tool_prefixes.py +++ b/tests/test_mcp_tool_prefixes.py @@ -38,6 +38,8 @@ # Cortex memory. plugin.json: name "hypermnesia-mcp", mcpServers key # "cortex". Renamed from "cortex" in v4.15.0 over a directory collision. "mcp__plugin_hypermnesia-mcp_cortex__", + # This plugin. Both halves come from .claude-plugin/plugin.json. + "mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__", } ) @@ -131,6 +133,7 @@ def test_scan_is_not_vacuous() -> None: ) assert seen, "no MCP prefix found anywhere; the regex or the root is wrong" assert "mcp__plugin_hypermnesia-mcp_cortex__" in seen + assert "mcp__plugin_hypermnesia-mcp-viz_hypermnesia-mcp-viz__" in seen @pytest.mark.parametrize( diff --git a/tests/test_optional_extra_absent.py b/tests/test_optional_extra_absent.py index fd421db..67e90bd 100644 --- a/tests/test_optional_extra_absent.py +++ b/tests/test_optional_extra_absent.py @@ -84,7 +84,7 @@ def test_render_tile_png_names_the_extra_when_datashader_is_absent(monkeypatch): tile_renderer.render_tile_png([("memory:1", 0.0, 0.0, "memory")], z=0, x=0, y=0) assert "viz-tile" in str(excinfo.value) - assert "pip install cortex-viz[viz-tile]" in str(excinfo.value) + assert "pip install hypermnesia-mcp-viz[viz-tile]" in str(excinfo.value) def test_empty_tile_png_reports_the_missing_pillow(monkeypatch): diff --git a/ui/unified/js/activity_stream.js b/ui/unified/js/activity_stream.js index 4d18081..2413dc6 100644 --- a/ui/unified/js/activity_stream.js +++ b/ui/unified/js/activity_stream.js @@ -75,7 +75,7 @@ } // One-shot fetch of PRD document/section nodes (third bridge, - // prd-spec-generator). Empty until a PRD is generated, then the nodes + // ai-architect-mcp-spec). Empty until a PRD is generated, then the nodes // appear in the graph. Best-effort; never blocks the activity stream. function _loadPrd() { try { diff --git a/uv.lock b/uv.lock index 6ad00fe..3eaaeae 100644 --- a/uv.lock +++ b/uv.lock @@ -770,7 +770,7 @@ wheels = [ [[package]] name = "hypermnesia-mcp-viz" -version = "2.8.0" +version = "3.0.0" source = { editable = "." } dependencies = [ { name = "fastmcp" },