Three plugins through the gauntlet — Cite Wide, Image Gin, and Perplexed are now installable from inside Obsidian
Two days of marketplace-submission rounds, six dot-releases across three plugins, 86 Dependabot alerts triaged into nothing, one splash page that now visibly differentiates plugins you can install today from ones you can't yet. The Lossless plugin family has its first three public-directory listings, and the surrounding discipline is now codified everywhere it can be re-used.
In this entry
Why Care?
Three of the plugins we've been working on for months are now installable from inside Obsidian. You open the app, go to Settings → Community Plugins → Browse, search for "Cite Wide" or "Image Gin" or "Perplexed," click Install, click Enable, and you're done. No symlink. No git clone. No "you have to be on the dev team."
That sounds simple. The path to get there was not. Obsidian retired their old PR-based submission process two days before we were ready to submit — the queue at obsidianmd/obsidian-releases that had been the entry point for community plugins since 2020 went away in favor of a hosted portal at community.obsidian.md that runs an automated review against every submission. Our first attempt with cite-wide came back with a wall of findings: manifest description tripped two style rules, the lockfile had a four-digit version where Obsidian expects strict semver, the repo was missing a LICENSE at the root, the release tag had a v prefix the portal rejects, and a stale minAppVersion that hadn't been touched since the starter-template days. That was Round 1 of cite-wide, and there were five more rounds — across cite-wide, image-gin, and perplexed — before all three sat clean on the directory.
This entry rolls up that two-day arc. If you're a user, the headline is the table below — three plugins, three install links. If you're a developer who's about to submit your own plugin to the new portal, the next sections are the discipline you'll need.
What's installable today
| Plugin | Version | Install from inside Obsidian | What it does |
| Cite Wide | 0.2.2 | community.obsidian.md/plugins/cite-wide | Converts numeric footnotes into stable hex identifiers, dedupes citations by URL, and parses LLM-pasted research from Perplexity / Google AI / Claude into a canonical Lossless reference format. |
| Image Gin | 0.2.2 | community.obsidian.md/plugins/image-gin | Generates AI images via Recraft and Ideogram, searches stock via Magnific, uploads to ImageKit CDN, intercepts every dragged-or-pasted image with a vault/CDN/Imgur destination gate. |
| Perplexed | 0.1.1 | community.obsidian.md/plugins/perplexed | Streams source-cited research from Perplexity, Anthropic Claude, Perplexica/Vane (self-hosted), or LM Studio (local). Per-directory content-generation templates that fill stub files in bulk. |
In Obsidian: Settings → Community Plugins → Browse → search the plugin name → Install → Enable. From cold-start install to working modal takes about thirty seconds.
The full release narratives live in each plugin's own changelog/releases/ directory and roll up onto the content-farm splash at /releases. Each tells the per-plugin user-facing story; this entry is the family-wide meta-story for the submission process itself.
The submission story
The path went through six dot-releases across the three plugins. Each one was driven by the portal's automated review surfacing a new finding we had to close before approval. The pattern that emerged: the bot scans more than just manifest.json, and the discipline isn't fully discoverable until you walk it.
Round 1 — Cite Wide first goes through the new portal
The old obsidianmd/obsidian-releases PR queue had been our intended submission path. When we went to look at our open PR there, GitHub returned 404. Investigation confirmed Obsidian had retired the PR-based workflow in favor of community.obsidian.md, which processes a hosted form and runs an automated review against every submission. We submitted cite-wide 0.2.0 through the new portal and got back five findings at once:
| Finding | Surface | Fix |
Tag uses v prefix (v0.2.0) | GitHub release tag | Cut a new tag without the prefix — 0.2.0 |
No LICENSE at repo root | Repo file tree | Added MIT LICENSE, "The Lossless Group, 2026" |
versions.json contains "0.0.0.1" — invalid semver | versions.json | Pruned to just {"0.2.0": "1.8.10"}; the four-digit was a Lossless internal convention that does not apply here |
manifest.json author says "Lossless Group", package.json says "The Lossless Group" | Two-file inconsistency | Aligned both to "The Lossless Group" |
minAppVersion: "0.15.0" is a stale starter-template default | manifest.json | Bumped to 1.8.10 to match the sibling plugins and reflect the API floor the codebase actually uses |
After fixing all five, cite-wide 0.2.0 went live. Sibling plugins shipped through the same gauntlet the same day: image-gin 0.2.2, perplexed 0.1.1. Each had the same shape of findings to clear.
Round 2 — The manifest-description self-reference
The portal's bot then surfaced two new findings on cite-wide's manifest description:
Error: Plugin description must not include the word "Obsidian". Warning: Plugin description should not refer to itself (e.g. "this plugin", "a plugin that"). It should describe what it does.
The previous description — "A plugin for Obsidian that enables rigorous citation and reference management." — tripped both rules at once.
The fix landed as cite-wide 0.2.1 with the description rewritten in active voice and naming concrete capabilities instead of restating the medium: "Convert numeric footnotes into stable hex identifiers, dedupe citations by URL, and parse LLM-pasted research into a canonical reference format." No "Obsidian," no "A plugin that...," three things the plugin actually does.
We also fixed the fundingUrl in the same release — it had been pointing at the group homepage (lossless.group) where there's no way to send money. Corrected to buymeacoffee.com/losslessgroup, the actual tip jar.
Round 3 — Wait, the bot still says the same thing
After 0.2.1 shipped, the scorecard kept flagging the same two findings. We checked manifest.json on every surface — local file, the release asset that users download, the manifest at HEAD of the default branch — all clean. The description didn't contain "Obsidian" and didn't self-reference anywhere we could see.
The miss: the GitHub repo's "About" field — the metadata GitHub serves as <meta description> / og:description / twitter:description on the repo page, and which Obsidian's portal pre-renders into its cached card for the plugin. That field still held the original text: "An Obsidian plugin for rigorous, vault-wide citation management..." GitHub's About box is a separate piece of metadata from anything in the repo's files; it has to be set via the GitHub UI's repo-settings sidebar or gh repo edit <owner>/<repo> --description "...". We'd been fixing the manifest and not the About box.
Fixed all three plugins in one batch via gh repo edit. This was a behavioral lesson worth codifying — saved to memory as feedback_plugin_description_three_places.md: manifest.json + package.json + GitHub repo About field, all three need to match, and the bot reads the cached pre-rendered page rather than the live manifest.
Round 4 — The Paste-LLM modal was too small
Not a bot finding — a user-facing UX gap we noticed during the rounds. Cite Wide's Paste LLM Content modal — where you paste a research dump from Perplexity or Google AI to get its citations converted on insert — was using Obsidian's default modal width of about 400px. Pasting a multi-paragraph research response with a reference list meant horizontal scrolling and a textarea you couldn't read what you'd pasted into.
The rest of the family's modals had been migrated to the canonical wide-modal pattern (modalEl.addClass, not contentEl.addClass; the CSS rule sets width: 90vw; max-width: 1100px). The Paste modal was the holdout — using a brittle contentEl.closest('.modal-container') lookup with inline-width hacks. Replaced in cite-wide 0.2.1 with the canonical four-line pattern. The Paste textarea now fits a real research dump.
Round 5 — builtin-modules flagged as a "package with a modern alternative"
The portal's scorecard then surfaced a warning against builtin-modules, an npm package commonly used in esbuild configs to externalize Node.js built-ins. The es-tooling/module-replacements project flags it because Node 14+ ships its own builtinModules export from node:module — a one-line dependency-free replacement:
// before:
import builtins from 'builtin-modules';
// after:
import { builtinModules as builtins } from 'node:module'; Cite Wide 0.2.2 swapped to the built-in, removed builtin-modules from devDependencies, and shrunk the lockfile by one package.
Round 6 — The release attached LICENSE alongside the three assets
The portal also flagged that the 0.2.0 and 0.2.1 releases contained four assets — main.js, manifest.json, styles.css, and LICENSE — while Obsidian's installer only consumes the first three. The extra file triggered a "release contains additional files" warning. Cite Wide 0.2.2 ships exactly the three core assets; the repo-root LICENSE is still there for anyone cloning the source.
Two findings explicitly deferred (neither blocks marketplace approval, both worth a follow-up batch across all three plugins):
GitHub artifact attestations —
actions/attest-build-provenance@v1in a release workflow would cryptographically verify the assets were built from the source repoPinning
@codemirror/state/@codemirror/viewpeer-dep versions to matchobsidian@1.12.3's declared peers —pnpm installsurfaces warnings but the build still passes
What the six rounds taught us
The discipline that emerged across the rounds, codified in context-v/issues/Dependabot-Alerts-Triage-Playbook-For-Lossless-Repos.md and across new sections in the changelog-conventions and pseudomonorepos skills:
Strict three-digit semver across all four version fields.
manifest.json,package.json,versions.jsonentry name, and the GitHub release tag — all four must agree, and none of them can carry the Lossless internal four-segment convention.Plugin description lives in three places, not one.
manifest.json+package.json+ GitHub repo About field. The bot reads the cached pre-rendered card, which inherits from the About field.Release-tag has no
vprefix. The portal expects0.2.1, notv0.2.1.LICENSEbelongs in the repo, not in the release bundle. Attach onlymain.js,manifest.json,styles.css.The portal pre-renders pages with cached metadata. A re-scan needs either a fresh release tag or genuine time for the cache to refresh — there's no "request re-scan" button visible from outside.
fundingUrlis meant to be a tip-jar destination, not the group homepage. Check that the link actually leads somewhere a supporter can send money.
Dependabot: 86 alerts, three buckets, one playbook
Every push to a plugin repo's default branch produced the same banner from GitHub: "Found N high / M moderate / K low vulnerabilities." Across the three plugins we'd never sat down to figure out what those alerts actually were — they'd just been accumulating since the starter-template fork. With the marketplace work surfacing the discipline question anyway, this was the right moment.
The diagnostic walk: pull all open alerts via gh api, grep each flagged package against the actual lockfile, categorize. Three buckets emerged across all three repos:
| Bucket | Packages | Why they're flagged | Dismissal reason |
| A: Already-removed | fastify, @modelcontextprotocol/sdk, body-parser, qs, path-to-regexp | Starter-template residue removed months ago, but Dependabot's alerts persist until GitHub re-scans. None of these are in any current lockfile. | not_used |
| B: Already-fixed | fast-uri | Pinned at 3.1.2 — at or above the alert's first_patched_version. Alert state hadn't caught up to the actual version. | tolerable_risk |
| C: Dev-tool transitive | minimatch, picomatch, brace-expansion, flatted, ajv, js-yaml, @eslint/plugin-kit | Transitive deps of ESLint / TypeScript / esbuild. Vulnerable in the abstract, but not bundled into shipped main.js by esbuild. Only reachable during local pnpm build with attacker-controlled input on a developer machine. Not exploitable at runtime in the installed Obsidian plugin. | tolerable_risk |
86 alerts dismissed total — 30 on cite-wide, 29 on image-gin, 27 on perplexed — via a gh api -X PATCH script that mapped each package name to its bucket and applied the right reason + comment. Hit a GitHub API gotcha along the way: dismissed_comment is capped at 280 characters; my first attempt with longer text returned 422 on every Bucket-C alert. Trimmed comments to 227 chars on retry; all 86 cleared.
The whole playbook lives at context-v/issues/Dependabot-Alerts-Triage-Playbook-For-Lossless-Repos.md for the next time the alerts accumulate. A backstop reference also sits in content-farm/CLAUDE.md so an agent working in the tree finds it without searching.
Splash refresh
With three plugins now installable, the splash needed to reflect that. Five changes shipped:
Dedicated
/releasespage filters the rolled-up changelog feed byfrom_pathstarting withreleases/(orcategory: Releaseas a fallback) — surfaces the version-anchored release narratives separately from the chronological per-day changelog stream. Cards show plugin slug, version pill, title, lede, and a "View on GitHub →" action.Topbar nav gained a "Releases" link between "Philosophy" and "Changelog."
Per-card differentiator on the index — the three published plugins (cite-wide, image-gin, perplexed) now carry:
A purple "Community Plugin" pill with Obsidian's gem icon next to the status pill
An Obsidian-purple icon button in the card footer (sibling of the GitHub icon) that opens the community.obsidian.md page
A thin purple top accent on the card border so published plugins visibly cluster vs. dev-only ones
Legend below the gallery explains the pill's meaning: install from inside Obsidian via Settings → Community Plugins → Browse.
"Want a plugin we haven't built?" CTA below the gallery, pointing at github.com/orgs/lossless-group/projects/2/views/1 (the specific board view for plugin requests) and the GitHub Discussions tab.
The rollup also picked up all four new release narratives from today's pushes — pnpm rollup:sync walked each plugin submodule's changelog/ (now recursively, since the new releases/ subdirectory is a sibling of the per-day files) and wrote them into splash/src/rollup/ for the union-loader to render.
Skills the rounds left behind
Three skills got meaningful updates during this work, each codifying a discipline that's now repeatable across the tree:
changelog-conventions/SKILL.mdgained a new canonical section "These are marketing artifacts, not internal documentation" — names the four-audience cascade (general → nerds passing by → nerds paying close attention → internal team), the "same effort, better impression" pragmatic argument, and the TOC-for-long-files rule. This entry follows that doctrine.git-conventions/SKILL.mdgained a sibling section "Commit messages also serve four audiences" with cross-reference back to the changelog-conventions canon.pseudomonorepos/references/content-rollup.mdgot rewritten from "aspirational, not yet implemented" to a full description of the two working variants — GitHub Content API (content-farm/splash) and local-filesystem (ai-labs/splash) — including the deliberate-sync architecture (pnpm rollup:syncthenpnpm build), the output layout that handleschangelog/releases/subdirectories, and the per-tree composition pattern.
A new issue doc also landed at context-v/issues/Dependabot-Alerts-Triage-Playbook-For-Lossless-Repos.md codifying the three-bucket categorization, the bulk-dismiss script, the GitHub API gotchas, and the 86-alert resolution snapshot.
Files Touched
content-farm/
├── plugin-modules/
│ ├── cite-wide/ (six dot-releases: 0.2.0 → 0.2.1 → 0.2.2)
│ │ ├── manifest.json (description rewrite; fundingUrl; version bump)
│ │ ├── package.json (drop builtin-modules dep; version bump)
│ │ ├── versions.json (add 0.2.1, 0.2.2)
│ │ ├── LICENSE (added — MIT)
│ │ ├── README.md (light wordsmithing pass; typo fixes)
│ │ ├── esbuild.config.mjs (builtin-modules → node:module)
│ │ ├── src/modals/PasteLlmContentModal.ts (wide-modal pattern adoption)
│ │ └── changelog/releases/0.2.2.md (release narrative, git mv'd through 0.2.0 → 0.2.1 → 0.2.2)
│ ├── image-gin/ (0.2.2 marketplace-ready release)
│ │ ├── manifest.json (fundingUrl)
│ │ ├── changelog/releases/0.2.2.md (release narrative — Drop Gate feature)
│ │ └── changelog/releases/0.1.1.md (retrofit to magazine-subtitle frontmatter pattern)
│ └── perplexed/ (0.1.1 marketplace-ready release)
│ ├── manifest.json (fundingUrl)
│ └── changelog/releases/0.1.1.md (release narrative — Directory Templates paradigm)
├── splash/
│ ├── src/
│ │ ├── components/PluginCard.astro (Community-Plugin pill + Obsidian-icon footer link + border accent)
│ │ ├── content/plugin-highlights/ (community_url added on the three published plugins)
│ │ ├── content.config.ts (community_url field on the schema)
│ │ ├── pages/index.astro (legend + participation CTA + nav link)
│ │ ├── pages/releases/index.astro (new dedicated /releases index)
│ │ ├── lib/seo.ts (STATIC_SEO.releasesIndex)
│ │ └── rollup/ (refreshed via pnpm rollup:sync — 33 changelog + 29 context-v files)
└── context-v/
├── issues/
│ └── Dependabot-Alerts-Triage-Playbook-For-Lossless-Repos.md (created — three-bucket triage, bulk-dismiss script)
└── skills/
├── changelog-conventions/SKILL.md (new "marketing artifacts" doctrine section)
├── git-conventions/SKILL.md (new "four audiences" commit-message section)
└── pseudomonorepos/references/content-rollup.md (rewritten: aspirational → two-variant implementation) Reference
Plugin listings on community.obsidian.md: cite-wide, image-gin, perplexed
GitHub releases: cite-wide 0.2.2, image-gin 0.2.2, perplexed 0.1.1
Per-plugin release narratives (each is the magazine-subtitle-style version of "what's new in this release" — these are the canonical per-plugin stories):
plugin-modules/<plugin>/changelog/releases/<version>.mdDependabot triage playbook:
context-v/issues/Dependabot-Alerts-Triage-Playbook-For-Lossless-Repos.mdMarketing-doctrine for changelog/release/README writing:
context-v/skills/changelog-conventions/SKILL.mdsection "These are marketing artifacts, not internal documentation"Roll-up architecture:
context-v/skills/pseudomonorepos/references/content-rollup.mdProject board for plugin requests and contributions: github.com/orgs/lossless-group/projects/2/views/1
GitHub Discussions: github.com/orgs/lossless-group/discussions