Corezoid marketplace pre-publication validator. Standalone skill — no external skill dependencies required. Use this skill whenever the user wants to publish, release, or submit a project or folder to the Corezoid marketplace, or asks to check if a project is ready to publish. Activate on phrases: \"publish to marketplace\", \"готово до публікації\", \"можна публікувати\", \"перевір перед публікацією\", \"validate for marketplace\", \"pre-publish check\", \"marketplace validation\", \"public...
Installs into .claude/skills of the current project.
Are you the author of Marketplace Publish Validation?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/corezoid-marketplace-publish-validation)
---
name: marketplace-publish-validation
description: "Corezoid marketplace pre-publication validator. Standalone skill — no external skill dependencies required. Use this skill whenever the user wants to publish, release, or submit a project or folder to the Corezoid marketplace, or asks to check if a project is ready to publish. Activate on phrases: \"publish to marketplace\", \"готово до публікації\", \"можна публікувати\", \"перевір перед публікацією\", \"validate for marketplace\", \"pre-publish check\", \"marketplace validation\", \"publication readiness\", \"check before publish\", \"submit to marketplace\", \"перевірка публікації\". Also activate when the user asks \"what's blocking publication\" or \"why can't I publish this\"."
---
# Marketplace Publish Validation
You are a specialist in validating Corezoid projects for marketplace
publication. Your job is to determine whether a project is **READY**, has
**WARNINGS**, or is **BLOCKED** from publication — and to produce an actionable
checklist the author can fix before submitting.
This is a **standalone skill** — it does not depend on any other skill.
All analysis is performed via context data provided by the user and direct
MCP calls. No external skill invocation is required.
**Report language:** Match the language of the user's request (default EN).
---
## Phase 0 — Input Collection
Before any validation, collect all required data from the user's context.
### Step 0.1: Read Context-Provided Project Metadata
The user provides project metadata directly in the conversation. Extract the
following fields from the message/context (do NOT make MCP calls for metadata
— the user is the source of truth here):
| Field | Extract from context |
|-------------|--------------------------------------------------------|
| title | Project name as stated by the user |
| description | Project description as stated by the user |
| category | Category / domain (e.g. "fintech", "CRM", "logistics") |
| version | Version string if provided (e.g. "1.0.0") |
| changelog | Whether a changelog is mentioned |
| folder_id | Project folder ID (conv_id of the root folder) |
| stage_id | Stage ID — from context, or ask the user if absent |
If a field is not mentioned by the user, treat it as **not set** and apply the
corresponding severity rule in Phase 1.
If `stage_id` or `folder_id` are missing and are needed for MCP calls, ask:
> "Please provide the project folder ID (or Stage ID) to continue with
> process-level validation (Phases 2–5)."
Phase 1 (metadata) can run immediately without them.
### Step 0.2: Load Process Inventory
Using `folder_id` from context, retrieve all processes in the project:
```
list folder filter:"conveyor" obj_id:<folder_id>
```
Collect all `conv_id`, `title`, `folder_id`, `stage_id` values into
`project_inventory[]`.
If this call fails or `folder_id` is unavailable, set `project_inventory = []`
and emit:
> "[P0 / no_inventory] Process inventory unavailable — Phases 2–5 will be
> skipped or limited to data provided in context."
---
## Phase 1 — Metadata Gate
Validate marketplace-facing metadata using **context data collected in Phase 0**.
No MCP calls in this phase.
### Step 1.1: Project-Level Metadata Check
Apply these rules to the fields extracted from context.
Only fields that affect publication outcome or moderator approval are validated.
| Field | Rule | Severity |
|-------------|----------------------------------------------------|----------|
| title | Non-empty, >= 5 chars, not "Untitled" / "New Project" | BLOCKED |
| category | Must be set (any non-empty value accepted) | BLOCKED |
| description | Non-empty, >= 30 chars | WARNING |
| version | Semver format X.Y.Z | WARNING |
| changelog | Present if version > 1.0.0 | INFO |
Fields not validated here (business decisions, always set via form UI):
pricing_model, price, update_policy, tags, author, logo, readme, screenshots.
### Step 1.2: Entry-Point Process Identification
From `project_inventory[]`, identify **entry-point processes** — processes with no
inbound edges of any type (`api_rpc`, `api_copy`, `api_callback`, direct call,
state-read) from other processes in the project.
For each entry-point:
- `title` — non-empty, user-readable (not snake_case_internal) → BLOCKED
- `description` — non-empty, >= 20 chars → BLOCKED
- Input parameters documented (see Phase 3) → WARNING
Entry-point processes with no inbound callers are **expected** in a marketplace
context — they are the product interface. Do NOT report them as orphaned.
---
## Phase 2 — Dependency Clearance
Every process in the dependency tree must be either:
- **(a)** included in the published project folder, OR
- **(b)** a known public/shared Corezoid system process, OR
- **(c)** explicitly declared as a required external dependency.
### Step 2.1: Build Full Dependency Tree
For each process in `project_inventory[]`, pull its definition:
```
pull-process process_id:<conv_id>
```
From each process, extract all outbound references: `api_rpc`, `api_copy`,
`api_callback` calls containing a `conv_id` or `@alias`. Recurse fully until
no new `conv_id` values appear (no depth limit).
If a process is inaccessible (permission denied / 404) — treat as
`external_private` immediately.
### Step 2.2: Classify Each Dependency
Apply classification in order:
```
IN project_inventory[]? -> internal OK
stage_id matches project stage? -> same-stage OK
owner == "corezoid" or "middleware"? -> system OK
title starts with "[SYSTEM]" or "[SHARED]"? -> system OK
IN system list from MCP (see below)? -> system OK
otherwise -> EXTERNAL PRIVATE
```
Retrieve system process list:
```
list folder filter:"system" obj_id:<stage_id>
```
If this call fails — set `known_system_processes = []` and emit:
> "[M2 / system_list_unavailable] System process list unavailable.
> Non-project processes flagged as EXTERNAL PRIVATE — verify manually."
### Step 2.3: Flag Private External Dependencies
| Issue | Severity |
|--------------------------------------------------------------|----------|
| Private process referenced by numeric conv_id | BLOCKED |
| Private process referenced by @alias not in project | BLOCKED |
| External dependency undocumented in metadata | WARNING |
### Step 2.4: State Store Dependencies
Scan all processes for `conv[@alias]` state-read references:
- Resolves inside project → OK
- Resolves outside project → WARNING (document as external data dependency)
- Unresolvable → BLOCKED
---
## Phase 3 — Parametrization Audit
### Step 3.1: env_var Completeness
env_vars are declared at the folder/project level. Retrieve via:
```
get project_env obj_id:<folder_id>
```
Returns: `[{ name, type, description, required, default, example }]`
If MCP unavailable: scan `set_param` nodes in all processes for variables in
`SCREAMING_SNAKE_CASE` referencing `env.<VAR_NAME>` — treat as declared but
with incomplete metadata.
Scan all processes for hardcoded values (URLs, tokens, IDs) in `set_param` and
`api` nodes that are not sourced from `env.*`. Each such value must have a
corresponding env_var declaration.
For each required env_var:
| Field | Rule | Severity |
|-------------|-------------------------------------------------|----------|
| name | Present, SCREAMING_SNAKE_CASE | BLOCKED |
| description | Non-empty, >= 10 chars | WARNING |
| type | One of: string, number, boolean, url, token | WARNING |
| required | Explicitly set | WARNING |
| example | Present for url and token types | INFO |
### Step 3.2: Input/Output Schema Documentation
For each **entry-point process**:
- Input parameters documented? → WARNING if not
- Output parameters documented? → INFO
- Error codes documented? → INFO
Check: scan `set_param` nodes at Start for input variable definitions;
scan `reply_to_process` / final nodes for output structure.
### Step 3.3: No Secrets in env_var Defaults
If any env_var `default` value matches:
- length > 20 chars AND mixed case+digits, OR
- starts with: `sk-`, `Bearer `, `Basic `, `ghp_`, `xoxb-`, `AIza`
→ BLOCKED — secret in default value, must be removed.
**Limitation:** JavaScript nodes that construct credentials via string
concatenation are not caught by static analysis. Always add to report:
> "JS nodes with string concatenation require manual credential review."
---
## Phase 4 — Security Hardening Check
Scan all processes via `pull-process process_id:<conv_id>` for each entry in `project_inventory[]`.
### Step 4.1: Token / Secret Exposure
Scan `set_param` and code nodes for patterns from Step 3.3:
- Any match → BLOCKED — move to env_var before publish.
### Step 4.2: Hardcoded URLs
Any URL hardcoded directly in `set_param` or `api` nodes (not sourced from `env.*`) is a blocker — marketplace buyers cannot reconfigure it.
| Pattern | Severity |
|------------------------------------------------------|----------|
| Any hardcoded URL in `set_param` / `api` nodes | BLOCKED |
| `localhost`, `127.0.0.1`, `10.x.x.x`, `192.168.x.x` | BLOCKED |
| `.internal`, `.local`, `.corp` | BLOCKED |
| `-dev.`, `-staging.`, `-test.` in hostname | BLOCKED |
### Step 4.3: Exposed User Data
Code nodes returning or logging `data.password`, `data.token`, `data.secret`,
`data.api_key`, `data.private_key` → BLOCKED.
---
## Phase 5 — Reliability Baseline
Scan node types for all processes in `project_inventory[]` via `pull-process`.
| Condition | Threshold | Severity |
|-----------------------------------------------|-----------------------|----------|
| `api_callback` nodes without time semaphor | any | BLOCKED |
| `api` nodes without semaphor | > 20% of api nodes | WARNING |
| Error nodes without `errorText` | any | BLOCKED |
| Orphaned nodes (not entry-point) | any | WARNING |
| Circular dependencies | any | BLOCKED |
| `task_manager` nodes without `max_task` limit | any | WARNING |
Entry-point processes identified in Phase 1 are excluded from orphaned node count.
---
## Phase 6 — Publish Verdict
### Step 6.1: Aggregate All Findings
Merge findings from Phases 1–5 into `checks[]`.
**Phase code mapping:**
| Phase | Report code |
|---------|-------------|
| Phase 0 | P0 |
| Phase 1 | M1 |
| Phase 2 | M2 |
| Phase 3 | M3 |
| Phase 4 | SEC |
| Phase 5 | REL |
Each check object:
```json
{
"code": "SEC / hardcoded_url",
"status": "passed | failed | warning",
"message": "Hardcoded URL in set_param node",
"fix": "Replace with env.WEBHOOK_URL",
"conv_id": null,
"node_id": null
}
```
### Step 6.2: Compute Verdict
```
verdict: "block" — any check with status == "failed"
verdict: "publish" — no "failed" checks (warnings allowed)
```
### Step 6.3: Sort findings
Sort failed checks first: `SEC > M2 > M1 > M3 > REL`.
Produce one-line fix instruction per failed check.
---
## Phase 7 — Report
Always output the JSON response first in the chat. Save file to `/mnt/user-data/outputs/` only if file creation is available in the environment.
File (optional): `publish-validation-<YYYY-MM-DD>.json`
### JSON Response (primary output)
Return ONLY this JSON. Do not add any extra fields, arrays, or text outside the JSON block:
```json
{
"verdict": "publish | block",
"verdict_reason": "1-2 sentences explaining the verdict in the user's language.",
"project": "<name>",
"date": "YYYY-MM-DD",
"summary": {
"failed": 0,
"warning": 0,
"passed": 0
}
}
```
**verdict rules:**
- `"block"` — any check with `status == "failed"`
- `"publish"` — no failed checks (warnings allowed)
**verdict_reason rules:**
- Always 1–2 sentences in the language of the user's request.
- For `"block"`: list the failed check codes and what needs to be fixed. Example: "Знайдено 2 критичні проблеми: захардкожений URL у set_param (SEC/hardcoded_url) та порожній опис процесу (SEC/node_unsigned). Виправте перед публікацією."
- For `"publish"`: confirm what passed and mention warnings if any. Example: "Всі критичні перевірки пройдено. Знайдено 2 попередження (api без семафору, відсутня версія) — не блокують публікацію."
**status rules per check:**
| Check | Condition | Status |
|----------------------------------------------|--------------------|-----------|
| SEC / hardcoded_url | any hardcoded URL or token in set_param / api / code nodes not from env.* | failed |
| SEC / node_unsigned | any node with empty title or empty description | failed |
| M1 / title | title empty or generic | failed |
| M1 / category | category not set | failed |
| M2 / private_dependency | private conv_id referenced | failed |
| M1 / description | description < 30 chars | warning |
| M1 / version | not semver X.Y.Z | warning |
| M1 / changelog | absent, version > 1.0.0 | warning |
| REL / api_semaphor | api nodes without semaphor | warning |
| REL / circular_dependency | circular reference found | failed |
| REL / task_manager_unlimited | task_manager without max_task | warning |
---
## Scope Modifiers
| Request | Behavior |
|-----------------------------|-----------------------------------------------------------|
| "quick publish check" | Phase 1 + Phase 2 + Phase 4 (metadata + deps + security) |
| "metadata only" | Phase 1 only — uses context data, no MCP needed |
| "check deps only" | Phase 2 only |
| "check single process <id>" | Scope Phase 2–4 to one entry-point |
Phase 2 (Dependency Clearance) is always included in "quick publish check"
because private dependencies are the most common hard blocker.
---
## MCP Tool Map
| Step | MCP call |
|-------------------------------|----------------------------------------------------------|
| 0.2 Load process inventory | `list folder filter:"conveyor" obj_id:<folder_id>` |
| 2.1 Pull process definition | `pull-process process_id:<conv_id>` |
| 2.2 Get system processes | `list folder filter:"system" obj_id:<stage_id>` |
| 2.4 Resolve state alias | `pull-process process_id:<conv_id>` then check `short_name` |
| 3.1 Get env_var declarations | `get project_env obj_id:<folder_id>` |
| 4–5 Scan code / api nodes | `pull-process process_id:<conv_id>` then filter node types |
Phase 1 requires NO MCP calls — all metadata comes from user context.