archy
PYPI · ARCHY · SCANNED SEP 21
Architectural sensor for Python codebases.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security100
- No malware found by supply-chain analysis.Pass
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
- 1 of 36 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency35
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- License check failed: the license (MIT License) isn't a recognized OSI-approved license. See how to fix → Fail
- Actively maintained (last published 15 days ago).Pass
- Publishes a security disclosure policy (SECURITY.md).Pass
Schema Quality & AI Usability71
- 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
- AI-judged instruction clarity (good).Pass
- Context-footprint check failed: tool/resource definitions use about 3704 tokens (~284/item across 13 items; 13 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management53
- Stability observed for 16 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage71
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 0% of tool parameters carry a description.Fail
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- We read all 13 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
- An AI judge read all 13 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a current MCP spec version (2026-07-28).Pass
How do I install the archy MCP server?
archy runs locally as a PyPI package, launched with uvx archy. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · archy
claude mcp add hslee16-archy -- uvx archy
{
"mcpServers": {
"hslee16-archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
} {
"servers": {
"hslee16-archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
} codex mcp add hslee16-archy -- uvx archy
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"hslee16-archy": {
"type": "local",
"command": [
"uvx",
"archy"
],
"enabled": true
}
}
} openclaw mcp add hslee16-archy --command uvx --arg archy
mcp_servers:
hslee16-archy:
command: "uvx"
args: ["archy"] {
"McpServers": {
"hslee16-archy": {
"Transport": "stdio",
"Command": "uvx",
"Arguments": [
"archy"
]
}
}
} assistant mcp add hslee16-archy -t stdio -c uvx -a archy
{
"mcpServers": {
"hslee16-archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 21 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 50 to 53. That category is still filling its 30-day observation window: 15 days of observed history at the previous scan, 16 at this one. The score rises as the window fills, whether or not the server changes.
- 19 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 43 to 47. That category is still filling its 30-day observation window: 13 days of observed history at the previous scan, 14 at this one. The score rises as the window fills, whether or not the server changes.
- 16 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.
- 14 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.
- 13 Sept 26 +4
- Stability: unverified → 0.27 ▲ functional
- 6 Sept 26 +15
- Malware scan: unverified → pass ▲ security
- 5 Sept 26 +13
- Malware scan: pass → unverified ▼ security
- Injection markers: unverified → pass ▲ security
- First check of Judged manipulation: pass security
- Stability: Stability not yet verified: not enough scan history yet (needs a 30-day window). security
- Tool coverage: unverified → 100 ▲ functional
- MCP protocol: unverified → pass ▲ functional
- Schema quality: unverified → 100 ▲ functional
- First check of Tool coverage: 0 functional
- First check of Schema quality: fail functional
- First check of Destructive annotations: pass functional
- First check of Schema quality: fail functional
- First check of Schema quality: good functional
- First check of Tool coverage: 100 functional
- Package version: 0.13.3 → 0.46.2 functional
- 26 Aug 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 21 Sept 2026 · Analysed pypi/archy@0.46.2
Provenance No attestation
The registry publishes no build provenance for this version, so there is nothing to verify.
| Result | No attestation |
|---|---|
| Ecosystem | pypi |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | hatchling.build |
Background: Why install scripts are a supply-chain risk →
Dependencies 36 packages
| Packages resolved | 36 |
|---|---|
| No linked repository | 1 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
archy_check Check layer boundaries ~450
**Call after any Python edit that adds, removes, or changes an import statement.** Returns forbidden direct edges between layers declared in archy.yaml under `violations`, plus Stable Dependencies Principle violations (when `sdp.enabled: true` in archy.yaml) under `sdp_violations`. Empty lists on both mean no direct boundary crossings. `required_violations` reports the INVERSE constraint: `required:` rules in archy.yaml declare that every module matching `source` must TRANSITIVELY reach `must_reach` (e.g. every standalone entrypoint must reach a model registry that has to be imported before the framework configures itself). Reach counts indirect paths and the implicit package-`__init__` import, so satisfying such a rule once in a package `__init__.py` covers its submodules; each violation carries a `detail` saying which module fails and why, and a rule whose patterns match nothing is reported as a violation rather than passing silently. Fix by adding the missing import, not by deleting the rule. Pass `contracts=True` to ALSO run the transitive (multi-hop) import-linter contracts (Layers, Forbidden, Independence, Protected, AcyclicSiblings) - stricter than the direct archy.yaml edges - nested under the `contracts` field; a failed contract means an indirect import path violates the architecture, so restructure rather than weaken the rule. That path reads .importlinter (or pyproject.toml, override with `contracts_config_path`) and needs `pip install archy[contracts]`; if the extra is absent, `contracts` comes back with `available=false` and an advisory rather than raising (replaces the removed archy_contracts tool). If no archy.yaml is found, returns an in-band CheckErrorPayload (an `error` field, not a raised error) so you can create one or pass `config_path`, and contracts are not run in that case; a malformed archy.yaml instead raises (it cannot be checked against).
| Name | Type | Req | Description |
|---|---|---|---|
| config_path | – | – | – |
| contracts | boolean | – | – |
| contracts_config_path | – | – | – |
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_conventions Report the project's house style ~544
**Call before adding a new class, field, or command to an unfamiliar project.** Derives the repository's own conventions from its source instead of leaving you to infer them by reading. Returns five censuses: `naming` (class-name suffix families GROUPED BY HOME MODULE, so the module you are about to edit shows its own patterns - the answer to 'what do I call a new one and where does it go'), `surfaces` (mirrored helper sets like `_x_to_text`/`_x_to_json` and names defined in several modules - the answer to 'how many places must I wire a new field'), `gates` (sites where a FAILED FINDING exits non-zero, each with its literal exit `code` and the lever that controls it: `flag:--strict`, `config:<attr>`, `param:<name>`, or `hardcoded` - the answer to 'should my new finding fail the build or only warn'), `errors` (the separate, usually much larger set of USER-ERROR exits - bad config, missing file - which say nothing about gating and are kept apart so the gate count stays meaningful), `models` (base-class, frozen, and tuple-vs-list census over the value types), `bases` (kind families grouped by what they derive from TRANSITIVELY, counting only bases this project defines - a family reached through an intermediate class is reported under the base that names it, and suffix agreement is stated so a family whose name is not the rule says so), `registries` (`NAME = Ctor(...)` families with the distribution of every keyword passed - how a project declares codes, messages, or flags when it does not use classes), `export_gaps` (family members absent from an `__all__`, or from a `from .x import Y as Y` block, that already lists their siblings) and `doc_gaps` (members the project's own `.rst`/`.md` surface does not name, where it already documents most of the family). Advisory only; it never fails and mutates nothing. `min_family` raises the floor for reporting a family (default 2, raise it on a large project to cut noise). `include_tests` censuses test modules too (default false:…
| Name | Type | Req | Description |
|---|---|---|---|
| include_tests | boolean | – | – |
| min_family | integer | – | – |
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| bases | array | – | – |
| doc_gaps | array | – | – |
| docs_scanned | integer | – | – |
| errors | array | yes | – |
| export_gaps | array | – | – |
| gates | array | yes | – |
| models | – | yes | – |
| modules_scanned | integer | yes | – |
| modules_unparsed | integer | yes | – |
| naming | array | yes | – |
| partition | – | – | – |
| registries | array | – | – |
| root | string | yes | – |
| surfaces | array | yes | – |
No examples provided.
archy_cycles Find import cycles ~62
Find import cycles (Tarjan SCCs of size >= min_size, plus self-loops) in a Python project. Returns cycles sorted largest-first.
| Name | Type | Req | Description |
|---|---|---|---|
| internal_only | boolean | – | – |
| min_size | integer | – | – |
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | array | yes | – |
No examples provided.
archy_diff Diff against baseline ~81
Compare the current project state to the last snapshot. Returns a risk-weighted `summary` (headline + top regressions / improvements), per-component score deltas, and the cycles and layer violations that have been added or resolved since the baseline. Use after edits to localize regressions; see the `loop` prompt.
| Name | Type | Req | Description |
|---|---|---|---|
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_dsm Design Structure Matrix ~300
Design Structure Matrix view of the import graph. `response_format='summary'` (the DEFAULT) returns a compact overview -- block structure (group labels + sizes), counts, the back-edges (source later than target in the ordering = the cycle signal), and inter-block coupling -- without the full cell list, so a routine call stays small. `response_format='full'` returns the full matrix the agent reads positionally: cell (row=source, col=target) is non-empty when source imports target; it refuses matrices with more than 2000 cells with a DSMTooLargePayload (narrow with `focus`/`package`, or read the summary). Use `group_by='community'` for block-diagonal cohesion, `group_by='layer'` for layer-violation forensics, or `group_by='topological'` to localize back-edges. Narrow large projects with `focus` (qualname + focus_depth-hop neighborhood) or `package` (qualname prefix). When `baseline_path` is provided, returns a DSMDiff regardless of response_format (`new_back_edges` flags cycles the edit just introduced).
| Name | Type | Req | Description |
|---|---|---|---|
| baseline_path | – | – | – |
| focus | – | – | – |
| focus_depth | integer | – | – |
| group_by | string | – | – |
| package | – | – | – |
| path | string | yes | – |
| response_format | string | – | – |
| weight | string | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_duplicates Duplicate-function clusters ~440
Cluster functions with an identical normalized body shape (identifiers/literals folded to placeholders). Returns two tiers: `duplicates` (the primary 'likely duplicate' list to investigate) and `variants` (demoted clusters that are likely intentional: same-class siblings, boilerplate, copies wholly in test suites or vendoring/isolation dirs, and - with `co_change` (the DEFAULT) - `independent` copies whose files are actively maintained yet never co-change in git, i.e. deliberately parallel implementations; each carries a `variant_reason`). Within the primary tier, `exact=true` marks byte-identical (Type-1) clusters, the highest-confidence 'definitely refactor these' subset. `min_nodes` (default 30) is the minimum normalized AST-node count, skipping trivial getters/stubs. This is an advisory SURFACER, never a score axis: refactorability is a semantic call the reader (you) makes, so a cluster means 'investigate,' not 'provably identical' (the co-change demotion lifts primary precision above the ~50% syntactic ceiling by moving benign parallel copies to variants). `near_miss=true` (opt-in, slower) adds a `near_miss` tier: Type-3 (gapped) clones - a copy with statements inserted/removed that the exact shape-hash cannot see - found by token-multiset overlap, each with a `similarity`. LOWER confidence than the shape tiers; use it for recall (the exact hash finds ~0% of Type-3). `response_format='summary'` (the DEFAULT) returns each cluster's ranking fields plus one sample member; `response_format='full'` returns the complete member lists. `co_change=false` skips the git pass. An empty `duplicates` with a `note` is a real answer.
| Name | Type | Req | Description |
|---|---|---|---|
| co_change | boolean | – | – |
| members | integer | – | – |
| min_nodes | integer | – | – |
| near_miss | boolean | – | – |
| path | string | yes | – |
| response_format | string | – | – |
| top_n | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| duplicated_functions | integer | yes | – |
| duplicates | array | yes | – |
| exact_total | integer | yes | – |
| min_nodes | integer | yes | – |
| near_miss | array | yes | – |
| near_total | integer | yes | – |
| note | – | yes | – |
| shown | integer | yes | – |
| total | integer | yes | – |
| variant_total | integer | yes | – |
| variants | array | yes | – |
No examples provided.
archy_graph Inspect dependency graph ~306
Inspect the dependency graph. With no `focus`, `response_format='summary'` (the DEFAULT, replaces the removed archy_graph_summary) returns a compact top-N overview (modules by fan-in, fan-out, PageRank, and edit-risk, plus top external deps; `top_n` controls N) so a routine call doesn't dump the whole graph into context; `response_format='full'` returns the complete node/edge dump matching `archy graph --format json`, but refuses graphs larger than `max_nodes` (default 500, must be >= 1) with a GraphTooLargePayload (bump it explicitly, or narrow with `focus`). Pass `focus=[<qualname or path>]` (replaces the removed archy_graph_focus) for a bounded subgraph centered on those modules: `depth` caps hop distance and `direction` is 'in' (who depends on me), 'out' (my dependencies), or 'both'; each edge carries the source line numbers of the import statements. With `focus` set, `response_format`/`max_nodes`/`top_n` do not apply (the neighborhood is already bounded).
| Name | Type | Req | Description |
|---|---|---|---|
| depth | integer | – | – |
| direction | string | – | – |
| focus | – | – | – |
| internal_only | boolean | – | – |
| max_nodes | integer | – | – |
| path | string | yes | – |
| response_format | string | – | – |
| top_n | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_impact Compute change blast radius ~429
Given a list of changed file paths, return what they affect. `mode='blast'` (the DEFAULT) returns the internal modules that transitively import any of them (the blast radius): use before refactoring or removing a module to see what would break. `propagation_cost` is the MacCormack-style scalar (fraction of internal modules the edit set can reach); `chains` explains *why* each impacted module is reachable (the shortest import path back to a changed module, hop by hop with the line numbers where each import lives) so you can cite the specific edge(s) to preserve. Chains are ranked closest-first and capped at `max_chains` (negative for all); `chains_omitted` reports how many were left out. `mode='affected'` (replaces the removed archy_affected) is the CI-shaped lookup instead: it returns the impact pre-classified into `impacted_tests` and `impacted_modules`, with traversal depth-capped (`depth`, default 5) so a single-line edit doesn't fan out to thousands of nodes. Test detection uses pytest conventions (test_*.py, *_test.py, files under tests/) unless `test_filter` overrides with a recursive glob. Files that resolve to no module are returned in `unresolved`; internal modules only. `co_change=true` (blast mode only, opt-in) adds a `co_changed` list: modules that historically co-change with the edited set in git but have NO import/call edge to it, so the structural blast radius above misses them (the behavioral complement, `archy coupling` scoped to your edit - 'you changed X; historically Y changes with it, check Y'). Source-only and best-effort (empty without git); the only path that makes this tool read git history.
| Name | Type | Req | Description |
|---|---|---|---|
| co_change | boolean | – | – |
| depth | integer | – | – |
| files | array | yes | – |
| max_chains | integer | – | – |
| mode | string | – | – |
| path | string | yes | – |
| test_filter | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_module_view Everything known about ONE module, complete and unranked ~257
**Call when the question is a NEGATIVE.** `archy_conventions` ranks and truncates, so a module's absence from it means nothing; every list here is COMPLETE for the one `module` named, so absence IS the answer. Use it for `does risk import hotspots`, `does anything still reach layers`, `is this module dead`. Returns `imports_internal` (the project modules this one imports, with relative imports resolved), `imported_by` (every project module that imports it, tests included even when they are otherwise set aside, because a module imported only by tests IS imported), `classes`, `functions`, `exports` (null when the module declares no `__all__` and no explicit re-exports, which is different from an empty one), `suffix_families`, `gates` (its exit sites) and `status` - which says whether the module was censused or set aside and why, so an empty result is never mistaken for `nothing to report`. Raises on an unknown module name rather than returning an empty view. Advisory: mutates nothing.
| Name | Type | Req | Description |
|---|---|---|---|
| include_tests | boolean | – | – |
| module | string | yes | – |
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| classes | array | yes | – |
| exports | – | yes | – |
| functions | array | yes | – |
| gates | array | yes | – |
| imported_by | array | yes | – |
| imports_internal | array | yes | – |
| module | string | yes | – |
| status | string | yes | – |
| suffix_families | array | yes | – |
No examples provided.
archy_score Score project structure ~227
Compute the composite quality score (modularity, acyclicity, depth, equality, complexity - geometric mean of five axes) for a Python project. `view='current'` (the DEFAULT) returns the five-axis score payload; optionally append the result to .archy/history.jsonl (record=True) and/or compare against the most recent recorded run as a regression gate (strict=True). Pass record=True to record a baseline at session start (replaces the removed archy_record_baseline). `view='history'` instead reads the recent score history (.archy/history.jsonl), returning up to `last_n` rows oldest-first so you can compare deltas over time (replaces the removed archy_trend); last_n must be >= 1, and record/strict/strict_tolerance do not apply.
| Name | Type | Req | Description |
|---|---|---|---|
| internal_only | boolean | – | – |
| last_n | integer | – | – |
| path | string | yes | – |
| record | boolean | – | – |
| strict | boolean | – | – |
| strict_tolerance | number | – | – |
| view | string | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | – | yes | – |
No examples provided.
archy_simulate Simulate import-edge change ~221
Counterfactual pre-edit check: given a proposed import-edge delta (`add` / `remove` lists of {from, to} module-or-path pairs), return the structural consequence BEFORE any file is written -- new/resolved cycles, new back-edges, new layer/SDP violations, per-axis score delta, and the propagation_cost change -- with a risk-ranked `summary` phrased conditionally ('would form a cycle'). Use it to test a refactoring hypothesis and reshape the plan before editing. Endpoints matching no internal module are returned in `applied.unresolved`. Caveat: it models the graph delta you describe, not arbitrary code edits (it cannot move the complexity axis), and one submodule import (`a.b.c`) also implies edges to its ancestor packages -- include them in `add` to model it exactly (a lone submodule edge is a lower bound; see docs/research/SIMULATE_ORACLE_EMPIRICS.md).
| Name | Type | Req | Description |
|---|---|---|---|
| add | – | – | – |
| path | string | yes | – |
| remove | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| applied | – | yes | – |
| cycles | – | yes | – |
| new_back_edges | array | yes | – |
| propagation_cost | – | yes | – |
| required_violations | – | – | – |
| score_delta | – | yes | – |
| sdp_violations | – | yes | – |
| summary | – | yes | – |
| violations | – | yes | – |
No examples provided.
archy_snapshot Snapshot baseline ~135
Capture score, cycles, and layer violations to .archy/baseline.json as a baseline that archy_diff will compare against. Call at the start of an editing session. Also returns `invariant_brief`: the constraints to know before your first edit (declared layers, forbidden inter-layer edges, whether the graph is acyclic, the baseline score per axis, and the top load-bearing / highest-risk modules). Read it up front to avoid proposing a cross-layer or cycle-introducing edit, rather than being told after the diff. See the `loop` prompt for full usage.
| Name | Type | Req | Description |
|---|---|---|---|
| path | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| baseline_path | string | yes | – |
| cycles | array | yes | – |
| invariant_brief | – | yes | – |
| required_violations | array | – | – |
| score | – | yes | – |
| sdp_violations | array | – | – |
| violations | array | yes | – |
No examples provided.
archy_what_to_refactor_next Rank refactor priorities ~252
Ranked refactor-priority list (replaces the removed archy_hotspots and archy_high_risk_modules via `lens`). `lens='fused'` (the DEFAULT) merges the behavioral lens (cyclomatic complexity x git churn) and the structural lens (the edit-risk composite: central and fragile) into one summed priority, so a module flagged by both generally outranks a single-lens one. `lens='behavioral'` ranks CC x churn hotspots only (needs git; answers 'where is the refactoring leverage?'); `lens='structural'` ranks the edit-risk composite only (git-free; answers 'is this edit dangerous?'). Each entry says which lenses fired and carries a one-line `rationale`. `min_risk` (default 0.15) is the structural floor; pass 0 to surface every module on the structural lens. An empty `priorities` with a `note` is a real answer: there is genuinely nothing to prioritize.
| Name | Type | Req | Description |
|---|---|---|---|
| lens | string | – | – |
| min_risk | number | – | – |
| path | string | yes | – |
| since | – | – | – |
| top_n | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| git_available | boolean | yes | – |
| min_risk | number | yes | – |
| note | – | – | – |
| priorities | array | yes | – |
| shown | integer | yes | – |
| since | – | yes | – |
| total | integer | yes | – |
No examples provided.
What is the archy MCP server?
archy is an MCP server listed in the public MCP registry as io.github.hslee16/archy. Architectural sensor for Python codebases. This page covers its PyPI package (archy).
Is the archy MCP server safe to use?
archy scores 73 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the archy MCP server expose?
archy exposes 13 tools: archy_score, archy_cycles, archy_check, archy_impact, archy_snapshot, and 8 more. Their descriptions and schemas cost roughly 3,704 tokens of context every time the server is loaded.
Is the archy MCP server still maintained?
archy is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.