Discovery record — the portfolio manifest and its verifier
Objective. Turn Cosmic Bull's growing software inventory into something that can be discovered, understood, reused, and eventually exposed through cosmicbull.net — and decide what, if anything, should be built, and what belongs on-chain versus in the repository.
Date. 2026-09-22. Chain. pearl-1, height 628232+.
This record follows docs/DISCOVERY_AND_REUSE.md. It is written before the implementation, and the decision it reaches is a refusal to build the obvious thing — so the reasoning matters more than usual, and a reviewer should be able to disagree with it on evidence.
1. The candidate shapes
Three shapes were considered from the start, and named explicitly so that rejecting one is a recorded decision rather than an omission:
| Shape | Verdict | |
|---|---|---|
| (a) | A machine-readable manifest in the repository describing every package with provenance | BUILD |
| (b) | A tool that verifies those manifest claims against the live chain | BUILD — this is the novel part |
| (c) | A new on-chain catalog/registry realm | REFUSED — see §5 |
2. Sources searched
| # | Source | Method | Result |
|---|---|---|---|
| 1 | Cosmic Bull's own catalog | Read catalog/{README,applications,primitives}.md, pearl/DEPLOYMENT.md, pearl/INFRASTRUCTURE.md, all committed DISCOVERY.md/FIXES.md/README.md | 3 registries already live; no machine-readable data anywhere in the repo |
| 2 | Cosmic Bull's own /p/ and /r/ sources | git ls-files; read every deployed package's on-chain outline | See §3 |
| 3 | On-chain pearl-1 | All 647 package paths enumerated via vm/qpaths; keyword sweep then deliberate sampling of unhinted names | See §4 |
| 4 | Public Gno / GitHub ecosystem | gnolang/gno toolchain source (pinned, chain-matched), gnoweb, awesome-gno, gnopm, moul/gno-contracts, tx-indexer, GnoScan, docs.gno.land | See §6 |
| 5 | The chain's own query surface | Read gno.land/pkg/sdk/vm/handler.go in the pinned toolchain; then executed each query against pearl-1 | See §4.1 — decisive |
Coverage — stated honestly
The on-chain sweep enumerated all 647 paths but read 12 packages in depth. The selection rule was: the four mandated targets, then a ~30-term keyword grep over names, then deliberate sampling of unhinted names to test the grep's blind spot (r/moul/present/v2, p/moul/nestedpkg/v1, r/moul/x/daily/linktree/v1, r/g16m0r…/bountyboard — all negative).
That blind-spot test is required here because of a recorded prior miss: our own permbook discovery found that p/samcrew/basedao is a live role-and-member store matching none of access|perm|rbac|role|auth|acl|grant. Four negative samples is mild evidence the grep caught the real candidates. It is not a sample that licenses an ecosystem-wide claim. The 45-package nftmarket cluster, the ~85-package r/moul set and most of r/gnoswap were sampled, not enumerated. A basedao-class miss remains possible in the unread remainder.
Accordingly: no relevant existing implementation was found in the searched sources. Not "none exists".
3. Cosmic Bull's own inventory — the first reuse question
Three of our own live realms are registries. Each was evaluated as the home for this capability before anything new was considered.
| Realm | What it actually stores | Classification | Decision |
|---|---|---|---|
r/…/service_registry | name → {owner, registrant, pkgPath, description, serviceType, metadata}, with Resolve/TryResolve/ListServices/ListByType/Render | COSMIC BULL EXISTING PRIMITIVE — reused | This is the on-chain half. It already is a package directory with descriptions and freeform metadata. Building a second one would duplicate our own deployed work |
r/…/upgrade_registry | address → {owner, name, deprecated, successor} migration chains | RELATED — complementary | Not reused: address-keyed, carries no path or description. Supplies a supersession axis the manifest records as data instead |
r/…/permission_registry | resource → permission → holder ACL | NOT RELEVANT | Access control, not inventory |
p/…/permbook, p/…/duebook | Capability primitives owned by a consumer realm | NOT RELEVANT | Neither indexes packages |
The finding that governs this whole record: a new "Cosmic Bull package registry" realm would be an EXACT DUPLICATE of service_registry, which is live, audited (1 RED + 7 YELLOW, all resolved), and currently holds 1 of 1000 entries against a 1 of 20 per-owner quota.
This is the same call made before, and it is made the same way: a reusable permission capability was refused as an exact duplicate of the deployed permbook. Consistency here is the point.
4. On-chain findings
| Package | What it is | Class | Why |
|---|---|---|---|
r/samcrew/memba_appstore_v2 | Curated app store; Listing keyed by PkgPath with Name/Tagline/Descr/Category/IconCID/AppURL; ListLiveJSON(offset,limit) | RELATED — closest prior art | Right shape, wrong model: a 1 GNOT listing fee into a third party's treasury, a foreign curator's approval, and no version/dependency/provenance/audit fields. Live state queried: ListLiveJSON(0,20) → [] — deployed and empty |
r/g1klfr7…/home | Personal home realm with a project portfolio: Project{Name, Description, PkgPath, Category} | RELATED — exact intent, toy quality | Proves the idea exists on-chain. Not reusable: plain map so ListProjects/Render iterate in non-deterministic order, GetProject panics on unknown names, Render is unbounded and unescaped |
r/g1n4pl5…/gnomi/hubv2 | Module router: SetModule/GetModule/ListModules → "name|path" | RELATED (weak) | Names and paths only — the minimal index skeleton, none of the metadata that makes an inventory understandable |
r/g1mv005…/gnomemepad/meta | Off-chain presentation metadata for token launches, in a realm separate from the thing it describes | RELATED — pattern only | The separation pattern is directly applicable: metadata about frozen packages need not touch them. §6 requires exactly that |
r/samcrew/agent_registry_v2 | AI-agent marketplace: endpoints, prepaid credits, reviews | NOT RELEVANT | Indexes callable off-chain agents with an escrow economy. No PkgPath field at all |
p/gnoland/boards/exts/hub | Immutable DTO snapshot types for the boards forum | NOT RELEVANT | The name is misleading — a defensive-copy layer, not an index. The inverse of the basedao lesson |
r/demo/defi/grc20reg | GRC20 token registry | NOT RELEVANT — one idea taken | Registers tokens, not packages. But its registration is self-proving: the key derives from cur.Previous().PkgPath(), so a realm cannot register under another realm's path. This is the benchmark §5 is judged against |
r/docs/registry | — | NOT DEPLOYED on pearl-1 | Confirmed two ways: absent from the 647-path list, and a live vm/qpaths query on gno.land/r/docs/ returns empty. There is no r/docs namespace on this chain |
r/leon/hor ("Hall of Realms") | — | NOT DEPLOYED on pearl-1 | Named by r/docs/registry's own render text as a canonical registry. Grep confirms only p/leon/{coinsort,ctg,svgbtn} exist here |
4.1 The decisive finding — the chain is already the registry
The chain's own query surface was read from the pinned toolchain (gno.land/pkg/sdk/vm/handler.go) and then executed against pearl-1. It already serves most of what an index would store:
| Query | Returns | Verified on pearl-1 |
|---|---|---|
vm/qpaths | every deployed path under a prefix or @namespace | ✅ 647 paths; 20 under our namespace |
vm/qpkgmeta_json | {path, status, creator, height} — chain-attested, not author-declared | ✅ feeledger → creator g1ut6…agvm3, height 572441, matching our record exactly |
vm/qdoc | package_doc, imports, funcs[] with a crossing flag, types[], values[] | ✅ feeledger → 18 funcs, 1 type, imports [errors, gno.land/p/nt/avl/v0] |
vm/qfile | verbatim source bytes | ✅ used for every byte-match in this portfolio |
/tx?hash= | the deploy transaction itself | ✅ feb4b559… → height 572441, MsgAddPackage, naming the feeledger path |
gnomod.toml cannot carry descriptive metadata: the schema (gnovm/pkg/gnomod/file.go:11-45) is Module, Gno, Ignore, Draft, Private, Replace[], AddPkg — there is no description, author, license, tags or repository field, and AddPkg is chain-injected ("filled by the vmkeeper… not intended to be used offchain").
What the chain does NOT answer, and therefore what is genuinely missing:
- which packages form one portfolio (curation and grouping)
- the link from deployed bytes → git commit → audit record
- verification status — byte match, closure equivalence, audit verdict
- reverse dependencies — who imports X (derivable only by fetching all 647)
- a human description and reuse contract
5. Why an on-chain catalog realm is REFUSED
Shape (c) is the obvious thing to build and it is the wrong thing to build. Four independent reasons, any one of which would be sufficient:
- It duplicates our own deployed work.
service_registryalready storespkgPath + description + serviceType + freeform metadatawith enumeration. §7 of CLAUDE.md classifies that as EXACT DUPLICATE. - It would re-store, in weaker form, facts the chain already attests.
vm/qpkgmeta_jsonreturns creator and height written by the chain at deploy. A realm holding a second copy holds a forgeable copy: the publisher types it in. Replacing a chain-attested fact with a self-attested one is a downgrade presented as a feature. - It cannot be made self-proving.
grc20regis safe because the key derives fromcur.Previous().PkgPath()— a realm cannot register under another realm's path. No equivalent proof exists for a catalog entry describing a frozen/p/package that will never call anything. Every field an index cares about — description, audit status, commit SHA — is inherently an attestation. Our ownservice_registrysays so onResolve: "A resolution is an ATTESTATION, NOT A PROOF." A new realm inherits that and adds nothing. - The ecosystem's prevailing principle says not to.
gnopm's design note states it directly: "No centralized registry exists. Instead, chains serve as the registry."vm/qpathsis that registry.
Cost is a fifth reason and deliberately the weakest: ~5M ugnot to publish claims that are already free to query.
6. Off-chain findings
| Finding | Class | Bearing on the decision |
|---|---|---|
moul/gno-contracts/contracts.json — {pkgpath, kind, name, version, description, deps, published:{network:{uploaded:bool}}} | RELATED — closest prior art, shape adopted | The nearest comparable artifact in the ecosystem. Its evidence model is the gap we fill: "uploaded": true is a bare boolean — no chain-id, no tx hash, no height, no deployer, no content hash. It records whether code is on a network, never how, when, or that the bytes match |
gnopm / gnomod.lock | RELATED | "Chain is the registry" — argues against (c), for (a)+(b) |
gnoweb /search.json, $source overview | RELATED | Solves browsing. Never asserts "these bytes match commit X" |
tx-indexer GraphQL — MsgAddPackage{creator, package}, MemFile.body | REUSABLE EXISTING PRIMITIVE | Deployed bytes are retrievable from the transaction itself — an independent corroborator of a vm/qfile byte-match. Not adopted now (endpoint unverified for pearl-1); recorded as the third evidence source |
awesome-gno | NOT RELEVANT | Human-curated markdown; no machine-readable data, no deployed paths with provenance |
Foundry broadcast/<script>/<chainid>/, OpenZeppelin .openzeppelin/<network>.json | RELATED (non-Gno) | Two ideas adopted: partition by chain-id, and key integrity on a content hash |
| A Gno doc-site generated from chain data | none found | No pkg.go.dev equivalent exists. gno doc -remote and vm/qdoc are the raw material |
No EXACT DUPLICATE was found in the searched sources for a verifier that proves inventory claims against chain bytes.
7. The decision
Build (a) + (b) as repository infrastructure. Deploy nothing new. Reuse service_registry for on-chain discoverability.
| Artifact | Kind | Why here and not elsewhere |
|---|---|---|
catalog/portfolio.json | repo | The chain cannot hold a description (gnomod.toml schema forbids it) and should not hold a second copy of what it already attests |
tools/verify_catalog.py | repo | The novel contribution. Proves every manifest claim against live pearl-1 and the committed source — three-way agreement |
tools/test_verify_catalog.sh | repo | §7a: the checker is code, so it is tested like code. A tool that only ever reports PASS is worse than no tool |
4 × RegisterService on service_registry | on-chain | Reuse. Gives the reusable /p/ primitives on-chain discoverability without deploying a byte |
What makes (b) worth building
Today, catalog/README.md claims "nothing is deployed that this catalog does not record." That claim is true. It was established by hand, in a conversation, and the last repo-wide audit found "stale counts across 13 docs" — prose drift is the failure mode, and it scales with the inventory.
verify_catalog.py turns that sentence into a re-runnable proof, two-sided: every manifest entry must exist on chain with matching bytes, and every on-chain package in the namespace must appear in the manifest. An undocumented deploy and a phantom record both become exit 1.
The on-chain step, and its honest limit
Registering the four /p/ primitives in service_registry is reuse, not construction — no bytes are deployed, and the realm's [addpkg] height is unchanged, which is the proof of that. It addresses a recorded gap: both permbook and duebook carry "known limitation — one consumer; its reusability is a design argument, not a demonstrated fact."
It does not make those registrations proofs. service_registry's INTEGRATOR CONTRACT applies unchanged and in full: attestation-not-proof, name-is-not-authorization, target-can-change.
8. Limits of this record
- 12 of 647 pearl-1 packages were read in depth. §2 states the selection rule and the residual risk. This record supports "not found in the searched sources" and nothing stronger.
- Unverified, and flagged rather than assumed: whether a pearl-1 tx-indexer endpoint is publicly available; GnoScan API stability; the status of gnolang/gno issue #522; whether a package-metadata GNIP/ADR exists in a branch newer than the pinned toolchain.
r/leon/horwas never read — it is not deployed on pearl-1, so the "Hall of Realms" pattern is classified from its description inr/docs/registry's render text, not from its source.- The refusal in §5 is a judgment call. The alternative — deploying a catalog realm — is written down in §1 and §5 precisely so a reviewer can disagree with the reasoning rather than guess at it.