Mall Self-Curation
The Plugin Mall is self-curating. Day-to-day operations (inventory, scoring, catalog publishing, staleness pruning) run inside this repo's .github/workflows/scan-sources.yml. This skill is the operational playbook for that pipeline.
When to Use
- Authoring or debugging anything under
scripts/scan-sources.cjs/normalize-frontmatter.cjs/compute-trust.cjs/list-refs.cjs/render-catalog.cjs - Weekly catalog-refresh PR (
catalog-refresh/YYYY-MM-DD) is stuck or producing surprising deltas - New source added to
sources/supported-stores.json— verifying it flows through the pipeline - New plugin shape encountered (frontmatter convention we haven't seen before)
- Importing or refreshing a first-party plugin with
npm run vendor - Preparing or reviewing a contributor submission with
submit:prepare/submit:validate - Refreshing first-party catalog state with
npm run maintain -- --curated - Onboarding to Mall internals
Canonical operator commands
| Role | Command | Writes | Approval boundary |
|---|---|---|---|
| Maintainer | npm run vendor -- --source <path-or-repo> --category <category> --repository <url> --ref <tag-or-sha> |
None by default | Dry-run; add --apply, and --replace for existing plugins |
| Maintainer | npm run maintain -- --curated |
First-party catalog, trust, marketplace, README, rendered pages | Review git diff before commit |
| Maintainer | npm run maintain -- --full |
Full registered-store catalog surface | Requires SOURCES_DIR and GitHub auth |
| Contributor | npm run submit:prepare -- ... --apply |
Proposed plugin payload + marketplace entry in contributor branch | Never commits, pushes, merges, or approves |
| Contributor / CI | npm run submit:validate -- --plugin <category>/<name> |
None | Structural eligibility only; CODEOWNER decides acceptance |
All plugin packaging routes through scripts/lib/plugin-package.cjs. Do not
author a second copier or validator for one plugin. The shared library owns
component-path normalization, prompt renaming, metadata generation, secret and
symlink rejection, the 100-file limit, and atomic replacement.
Two PR lanes
| Lane | Trigger | Merge behavior |
|---|---|---|
| Generated catalog refresh | Weekly or manual scan-sources.yml |
Auto-merges after deterministic tests and validation; workflow-owned paths only |
| Curated plugin contribution | Human PR under plugins/ |
validate-plugin-pr.yml reports checks; never auto-merges; @fabioc-aloha CODEOWNER approval required |
Do not apply the catalog-refresh auto-merge rule to plugin contributions. A green contribution is structurally reviewable, not editorially accepted.
The pipeline (per .github/workflows/scan-sources.yml)
bootstrap-sources.cjs ─┐
scan-sources.cjs │
normalize-frontmatter.cjs ├─▶ catalog/index.json
list-refs.cjs │ catalog/stores/*.json
compute-trust.cjs │ scoring/trust-audit.{json,md}
render-catalog.cjs │ catalog/INDEX.md + per-store + per-category
validate-catalog.cjs ─┘ structural gate before merge
README.md (storefront)
Each step is idempotent: re-running with no upstream changes produces byte-identical output.
| Step | Owns | Reads | Writes |
|---|---|---|---|
bootstrap-sources.cjs |
Source-repo clones into $SOURCES_DIR |
sources/supported-stores.json |
$SOURCES_DIR/<name>/ (one shallow clone per third-party store; plugin-mall is excluded — it's this repo) |
scan-sources.cjs |
Per-plugin metadata extraction | sources/supported-stores.json + $SOURCES_DIR/<name>/<pluginDir>/<plugin>/ + this repo's plugins/<category>/<name>/ (for plugin-mall self-scan) |
catalog/stores/<store>.json (without trust scores yet) |
normalize-frontmatter.cjs |
Standard + extended + raw three-layer schema | catalog/stores/<store>.json (plugins[*].frontmatter.raw) |
catalog/stores/<store>.json (plugins[*].frontmatter.standard + extended) |
list-refs.cjs |
Per-plugin version discovery | $SOURCES_DIR/<name>/.git/ + plugin source_path |
catalog/stores/<store>.json (plugins[*].available_refs) |
compute-trust.cjs |
Trust score (provenance + 5 signals) | catalog/stores/*.json + scoring/github-stats.json |
catalog/stores/*.json (store_trust + plugins[*].trust_score) + scoring/trust-audit.{json,md} |
render-catalog.cjs |
JSON → MD rendering | catalog/stores/*.json |
catalog/index.json + catalog/INDEX.md + catalog/stores/<store>.md (one per store) + catalog/categories/<cat>.md (one per category) + README.md (storefront) + sources/SOURCES.md (rendered registry) |
validate-catalog.cjs |
Structural integrity gate | Registry + generated JSON/Markdown | Exit 0 only when registry, stores, counts, trust fields, and rendered surfaces reconcile |
The plugin-mall self-entry — load-bearing rule
sources/supported-stores.json includes one entry with name: "plugin-mall" and provenance: true. This entry refers to this repo itself and is the mechanism that puts Mall-curated plugins in the same catalog as third-party plugins.
Two rules govern it:
- Bootstrap MUST skip
plugin-mall. This repo is already checked out; cloning it from itself is wasteful and could cause race conditions during the workflow.bootstrap-sources.cjsfilters byname !== "plugin-mall". - Scan MUST include
plugin-mall. The scan walks$REPO_ROOT/plugins/(not$SOURCES_DIR/plugin-mall/) and producescatalog/stores/plugin-mall.jsonlike any other store.scan-sources.cjsresolves the path conditionally: third-party stores use$SOURCES_DIR/<name || local_dir_name>/<pluginDir>;plugin-malluses$REPO_ROOT/<pluginDir>.
Crossing this boundary (cloning self in bootstrap, or skipping self in scan) is the most common source of bugs in this pipeline.
Trust scoring formula
Computed by compute-trust.cjs; signals published alongside score so readers can audit why a score is what it is.
| Signal | Range | Source | Mall-as-store | Third-party typical |
|---|---|---|---|---|
| Provenance | 0 or +50 | supported-stores.json provenance field |
+50 | 0 |
| Store maintenance | 0–15 | scoring/github-stats.json last_commit recency |
15 | 0–15 |
| Store adoption | 0–10 | GitHub stars + contributors via scoring/github-stats.json |
10 | 0–10 |
| License clarity | 0–10 | OSI-approved=10, clear non-permissive=7, ambiguous=0 | 7 (PolyForm-NC) | 0–10 |
| Frontmatter completeness | 0–10 | description + version + lastReviewed presence | 10 | 0–10 |
| README presence | 0–5 | README ≥ 200 chars | 5 | 0–5 |
Mall-as-store ceiling: 97/100. Third-party stores cap at 50 by construction (no provenance). Per-plugin score adds up to +20 from plugin-specific signals on top of store score.
If the formula drifts from this table, update this skill in the same commit. Changes to the weights or signals are [behaviour]-class commits; changes that alter what the published-signal contract guarantees are [constitutional].
Workflow + cadence
- Weekly cron: Mondays 11:00 UTC per
.github/workflows/scan-sources.yml - PR-back: only when
catalog/,scoring/,README.md, orsources/SOURCES.mdchange (timestamp-only deltas filtered) - Manual dispatch:
workflow_dispatchavailable for ad-hoc re-scans (e.g., after adding a store tosupported-stores.json) - Deterministic pre-merge gate:
npm test+npm run validatemust pass before the catalog-refresh PR can open or merge - Generated refresh oversight: catalog-refresh PRs auto-merge after gates; the merged diff is the sampling surface for Steward oversight
- Contributor approval: plugin PRs never auto-merge and require CODEOWNER review
What the Mall does vs out-of-scope
| Concern | Owner |
|---|---|
Add / remove a source store from supported-stores.json |
Mall (this skill + source-inventory) |
| Tune trust score weights | Mall (this skill + compute-trust.cjs) |
| Scan / score / render / publish pipeline | Mall |
| Curated plugin payload validation | Mall automation, followed by maintainer approval |
| Decide a plugin earns a slot in any consumer project's baseline | Out of scope (consumer-owned decision) |
| Coherence with a specific consumer project | Out of scope (consumer-owned audit) |
| Reframe what counts as "curated" | Out of scope (editorial decision, not pipeline) |
| Periodic review of how the catalog is consumed downstream | Out of scope (consumer-owned) |
If a question crosses the line into editorial or downstream policy, route it out of the workflow. Don't self-modify cross-repo policy from inside the Mall.
Curation log
Every editorial Mall decision lands in docs/curation-log.md in this repo (Mall's own append-only log). Decisions ship with re-eval dates; routine refreshes do not need log entries unless they triggered a curation action.
Anti-Patterns
| Anti-pattern | Correction |
|---|---|
Editing brain files (skills/instructions/prompts/agents) under plugins/ during the workflow |
The workflow MUST NOT modify plugins/ — editorial changes ship via PR review only. The workflow only writes to catalog/, scoring/, README.md, sources/SOURCES.md. |
| Treating a green contributor check as approval | Automation establishes structural eligibility. The CODEOWNER still judges evidence, overlap, license, safety, category, and maintenance. |
Hand-copying a source plugin into plugins/ |
Use npm run vendor or npm run submit:prepare; both use the shared canonical packager. |
Bootstrap re-cloning plugin-mall from itself |
bootstrap-sources.cjs MUST filter name !== "plugin-mall". |
Scan skipping plugin-mall |
scan-sources.cjs MUST include plugin-mall (walks $REPO_ROOT/plugins/, not bootstrapped sources). |
| Trust score without published signals | Every score MUST come with its signal breakdown in catalog/stores/<store>.json trust_signals field. |
| Hardcoded "curated" tier flag in code | There is NO tier flag in the schema. Curation bias emerges from provenance: true in supported-stores.json. |
| Quietly fudging the license signal so Mall scores 100 | Score honestly. PolyForm-NC = 7/10. The provenance +50 already carries the first-party trust weight. |
Falsifiability
This skill needs revision if any of the following occur by 2026-08-29 (90 days):
- The workflow modifies a file outside
catalog/,scoring/,README.md,sources/SOURCES.md≥1 time - A structurally inconsistent registry/catalog/render state passes
npm run validate≥1 time - Trust scores cluster oddly (e.g., third-party score consistently > 60 — provenance bonus undercalibrated)
- Mall-curated plugins fail to rank #1 in
/mall-searchfor the same name as a third-party entry (provenance signal not flowing) - A new source-shape (frontmatter convention) breaks
normalize-frontmatter.cjs≥2 times without surfacing inscoring/trust-audit.md - The boundary table (what the Mall does vs out-of-scope) produces wrong-routing decisions ≥2 times in a quarter
Track in docs/curation-log.md tagged [MALL-SELF-CURATION].
Related
- source-inventory/SKILL.md — managing
supported-stores.json - store-evaluation/SKILL.md — scoring a candidate store before adding it
- staleness-discipline/SKILL.md — pruning stale entries
- mall-maintenance-rules.instructions.md — always-on routing for this skill