Skip to content
Back to skills

Infocard Publish Sop

BSecurity

Use when creating or publishing an infocard through the mandatory .docs-to-promotion workflow.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
devopspythongobashnodegitbackend

Works with

  • terminal

Security analysis

B88/100
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro scans all 20 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add ccwq/infocard-pub --skill infocard-publish-sop --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Infocard Publish Sop?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Infocard Publish Sop
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/ccwq-infocard-publish-sop/badge)](https://www.skillsdirectory.com/skills/ccwq-infocard-publish-sop)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: infocard-publish-sop
description: Use when creating or publishing an infocard through the mandatory .docs-to-promotion workflow.
version: 4.4.0
---

# Infocard Publish SOP: `.docs` → Promotion → Main Checkout

## Non-negotiable workspace boundary

All information-card work uses the primary `infocard-pub` checkout. The only valid lifecycle is:

```text
Read-only research
→ .docs/<run-id>/<slug>/ authoring candidate, facts, evidence, assets, promotion-manifest.json
→ Publisher validates and promotes declared files into docs/ and assets/
→ local visual gate
→ build / verify / taxonomy / leak checks in primary checkout
→ narrow stage / commit / non-force push from primary checkout
→ cache-busted public HTTP, index, and visual recheck
```

Forbidden for every infocard run:

- `git worktree add/remove/prune`, any worktree reuse, detached HEAD, branch handoff, or temporary clone;
- any temporary information-card repository root;
- force-pushing main;
- Author writes to `docs/`, `assets/`, generated indexes, or Git state;
- ordinary publishing triggering inventory, removal, cleanup, reset, stash, or clean of historical worktrees.

Existing worktrees and `/tmp` artifacts are a separate inventory/cleanup concern and require separate explicit authorization. A dirty primary checkout is ambient state to record and exclude, not a reason to create an alternate checkout.

## Route

Use the light route only when there is one card, one complete primary source/user brief, no sensitive claim/risk trigger, no multi-source reconciliation requirement, and no shared batch. Otherwise use the full route with bounded Research A and, only when needed, Research B.

Creation-and-publication authorization authorizes this whole release chain unless the user explicitly requests draft-only or preview-only work. It does not authorize installing or configuring the subject tool.

### Light-route 20-minute SLA

普通单来源、低风险、单卡整理使用 `scripts/lib/infocard-route.js` 判定。其目标为 15–18 分钟,墙钟硬上限为 20 分钟;预算与 hard-stop 由 `scripts/lib/infocard-light-route.js` 固定。超过范围必须升级 full route,不得通过跳过视觉、manifest、build/verify 或公网复核来满足 SLA。

Light route 的 static gates 必须绑定当前卡片:`node scripts/verify-taxonomy.js docs/<slug>.html.meta.yaml`、`node scripts/check-info-leak.js docs/<slug>.html` 和 `npm run verify:visual-gate -- docs/<slug>.html`。不要在普通单卡路径中调用 `--all`、全量 `fix-taxonomy` 或全仓历史 leak 阻断扫描。

### Full-route 30-minute hard-stop

下列任一情况必须进入 full route:多源核验、敏感声明、复杂视觉、或高密度目录(显式 `highDensity=true`、超过 12 个分类、或超过 60 个条目)。full route 仍然有边界:墙钟硬上限 **30 分钟**,预算为研究 4 分钟、主题 1 分钟、authoring 6 分钟、Publisher 审计 3 分钟、promotion/static 4 分钟、build/verify 5 分钟、release/public 4 分钟,并保留 3 分钟缓冲。

超过研究预算立即停止继续搜索并 author;超过 authoring 预算立即停止子智能体并由 Publisher 做最小缺口接管;超过 30 分钟只能报告明确的 `BLOCKED_AT_*` 终态或执行已有产物的最小恢复,不得重新研究、重复派发同一 slug 或无限修复。

`.stop.jsonl` 诊断记录使用 schema v2:`run_start` 不计阶段时间;research、promotion、visual_capture、visual_review、deployment_wait 和 repair 分开记录;SLA 以 wall clock 为准。

实际 light-route 编排入口为 `npm run run:light-route -- --config <run.json>`。配置必须声明 `runId`、`request`、`preflight.authoringDir`、正式 `htmlPath`,以及需要外部工具完成的 `stageCommands`;截图阶段会收到 `INFOCARD_CAPTURE_PLAN_PATH/JSON`。仅在诊断需要时设置 `diagnosticsPath`,否则不得每次创建 `.stop.jsonl`。编排器使用单调时钟执行阶段预算、定向 static gates 和 20 分钟 hard-stop,并写入四种允许终态之一。

## Roles

### Research

Research produces first-party facts, source boundaries, time-sensitive snapshots, claim verdicts, and evidence gaps. It does not write formal card files, create workspaces, build, commit, push, or publish.

### Content language rules for Author (enforced 2026-09-16)

Dev/tool cards (category: dev-tool, creative-tool, productivity): **English-first**. Section titles, h2/h3 headings, overview labels, and prose explanations must be pure English. Chinese is permitted ONLY for:
1. Naming-reference table source terms (搜索必需 for the Chinese→English lookup value)
2. A concise hook line ≤ 10 characters in the hero subtitle (or omit entirely)

Forbidden in dev/tool cards:
- Chinese prose paragraphs in body text
- Mixed ZH/EN in section titles (e.g. "A抽卡黑盒", "VIDEO AI IS A抽卡黑盒")
- Chinese transliterations in English contexts (e.g. "剪映" → use "JianYing"; "抽卡黑盒" → use "black-box output")
- `lang="zh-CN"` or Chinese font declarations (`PingFang SC`, `Microsoft YaHei`) in dev/tool cards
- Emoji-only section headings without English equivalents

WeChat/public-account cards: Chinese-first (首屏中文, English only for brand names, tech acronyms, protocol names, and necessary native terms).

When editing an existing card to fix language mix: patch the docs/ HTML directly, then `npm run build && npm run verify` before commit. Do NOT re-delegate authoring.

### Author

Author writes only under `.docs/<run-id>/<slug>/`:

```text
card.html
card.html.meta.yaml
facts.json or research.md
theme-decision.json
promotion-manifest.json
visual/
assets/ (only declared assets)
```

The Author must not directly write formal `docs/` or `assets/`, generated indexes, Git state, or `/tmp` files. It does not build, commit, push, or start Wiki sync.

### Bounded Author delegation

同一 bare slug 同时只能有一个 Author 任务锁;第二个任务不得重派完整 authoring,而应读取已有目录并只恢复缺失阶段。协调器已经提供完整来源事实、主题、条目清单、路径和结构时,Author 只允许读取 `theme-decision.json`,然后立即写入五个声明文件;不得重复加载大 Skill、历史模板、历史 manifest 或再次搜索。60 秒没有任何声明文件写入时,协调器必须停止该子任务并直接接管。

子智能体模型必须在派发前通过运行时 provider 配置解析;provider/model 不可用属于立即失败,不得等待模型 400 后再重派。

### Publisher

Publisher owns manifest validation, exact promotion, visual gate, build, static gates, narrow Git staging, commit/push, public verification, and closeout. It operates only in the primary checkout.

## Bundle, sidecar, and manifest

Before authoring, create `.docs/<run-id>/<slug>/promotion-manifest.json`. A `publish-bundle.json` is optional process evidence unless a concrete Publisher/audit command requires it; it is not a second promotion authority.

Manifest requirements:

- every source is relative to the authoring directory;
- every target is repository-relative and only under `docs/` or `assets/`;
- reject absolute paths, `..`, duplicate targets, missing sources, undeclared assets, screenshots, bundles, process files, secrets, and generated indexes;
- never promote `_index.yaml` or `index.html` from `.docs`.

Every formal sidecar is one YAML mapping containing at least:

```text
slug, path, category, title, desc, date, updated, tags,
author, source, source_url, style
```

`path` must exactly equal the manifest HTML target. `date` and `updated` use quoted `YYYY-MM-DD HH:MM:SS`; Publisher sets final promotion time. Theme evidence must be the validated `.docs/<run-id>/<slug>/theme-decision.json` produced by `infocard-theme-assignment` before candidate HTML is written. The selected theme is a complete template from `theme/themes.json`; `theme/*.html` must not be referenced as a stylesheet. Theme selection and visual-evidence details are owned by `infocard-theme-assignment` and `visual-verification-gate`; this SOP only composes those gates. This SOP never generates candidates, ranks themes, or makes a second theme decision. Author delegation is blocked unless that decision exists; delegation context must not hard-code or preselect a concrete theme.

## Promotion

1. Record `git status --short` in the primary checkout and preserve all ambient changes.
2. Validate bundle, manifest, source paths, target paths, sidecars, theme agreement, and artifact allowlist.
3. Copy only manifest-declared HTML, sidecars, and assets into formal `docs/` and `assets/` targets.
4. Inspect the exact promotion diff and record source/target hashes in the bundle.
5. Do not promote a whole authoring directory or undeclared files.

For a batch, validate all manifests first, promote all declared artifacts, then build once in the same primary checkout.

### Timestamp and hash order (updated 2026-09-05)

**Root cause of prior failure**: The SOP previously said "Publisher sets final promotion time" but did not mandate calling the timestamp script before promotion. In update-card scenarios, the old `updated` field survived the build because it was never refreshed before the first promotion.

`npm run build` can refresh an existing card when its formal HTML/sidecar appears in the current diff, but this is implicit detection and cannot replace an update-card contract. The prior incident was more basic: the update authoring/promotion chain never completed, so no formal card change reached the build stage and the old sidecar remained untouched. The Publisher must explicitly call `npm run sync-publish-metadata` **before** promotion whenever updating an existing card, making the edit-date invariant independent of build heuristics.

Required sequence for update cards:

```text
1. node scripts/sync-publish-metadata.js --manifest .docs/<run-id>/<slug>/promotion-manifest.json --timestamp "YYYY-MM-DD HH:MM:SS"  # candidate only
2. node scripts/promote-infocard.js --manifest .docs/<run-id>/<slug>/promotion-manifest.json
3. npm run build
4. (optional) node scripts/verify-meta-timestamps.js
5. visual gate → verify → taxonomy → leak → commit/push
6. independently verify formal sidecar `updated` after build and after push
```

Required sequence for new cards (unchanged):

```text
1. node scripts/promote-infocard.js --manifest .docs/<run-id>/<slug>/promotion-manifest.json
2. npm run build  ← build sets date=updated automatically via sync-build-timestamps
3. copy final formal sidecar back to candidate directory
4. recompute manifest hashes
5. visual gate → verify → taxonomy → leak → commit/push
```

This is one bounded synchronization step. A hash mismatch after it is `BLOCKED_AT_LOCAL_GATE`, not a reason to rerun authoring or repeat build indefinitely.

## Build/verify exception: incremental-vs-full count mismatch (2026-10-08)

**Symptom**: `npm run build` succeeds but `npm run verify` fails with `_index.yaml is out of date`. The disk `_index.yaml` `_count` does not match `buildIndexData()` output (incremental = 1173, full = 1175 in incident).

**Root cause**: Incremental build `buildIndexData({metaPaths:[...]})` can produce a different card count than full rebuild `buildIndexData()`. This is a pre-existing build-system inconsistency. The `index.html` injection path also differs between incremental and full modes.

**Workaround sequence**:

```bash
# Step 1: confirm the mismatch with independent checks
node -e "const {buildIndexData} = require('./scripts/index-build-lib'); const r=buildIndexData(); console.log('full rebuild count:', r._count);"
node -e "const fs=require('fs'); const m=fs.readFileSync('_index.yaml','utf8').match(/_count: (\d+)/); console.log('disk count:', m ? m[1] : 'N/A');"

# Step 2: if counts differ, force full rebuild (may timeout on large repos)
node scripts/build-site.js --full

# Step 3: if --full times out, use git to restore dist/ and commit only source
git checkout -- dist/
git add index.html _index.yaml
git commit -m "build: sync index.html injection and _index.yaml"
npm run verify   # should pass

# Step 4: push
git push origin main
```

**This is a build-system bug, not a publication SOP error.** Work around and continue. Do not let this block card release. After recovery, note as known limitation.

## Required visual gate before build / commit / push

After promotion, before build:

1. render exact formal `docs/<slug>.html` locally;
2. generate the canonical risk-driven capture plan, then capture exactly the desktop/mobile `hero` and selected `complex` regions by delegating to `web-capture` (which uses the runtime-provided `agent-browser` endpoint); package each viewport's two raw images as one labeled contact sheet;
   - Call `web-capture` with the target tab / URL and the `pc` or `mobile` preset
   - `web-capture` returns screenshot paths and geometry checks
   - **Do NOT embed `browser_exec` built-in `cdp()` screenshot logic here** — route all web screenshots through `web-capture`
3. record explicit per-panel `critical / major / minor` findings and bind the four raw images plus two contact sheets to current HTML SHA-256;
4. run `npm run verify:visual-gate -- docs/<slug>.html`;
5. repair and recapture after every HTML/CSS/content/structure change;
6. apply visual-verification-gate budget policy: initial call + two tool retries, or up to three defect repair rounds; validated exhaustion allows direct release with disclosure.

Infrastructure-only capture or visual-review failure must be recorded according to `visual-verification-gate`, with an explicit `evidence_gap`, error category, and outcome. It is never visual pass, and must not fabricate screenshots or dispositions.

### Batch visual preflight

批量开始时先探测一次 CDP / `web-capture`。若不可用,写入批次级 `VISUAL_PENDING` 与错误类别;后续单卡仅引用该状态,不重复调用已确认不可用的 capture backend。基础设施恢复后,必须对仍待发布卡重新采集当前 HTML hash 绑定的桌面和 390px 移动证据。

### Visual exceptions

All `VISUAL_EXCEPTION_AFTER_BUDGET` rules, attempt counting, repair-round schema, and final wording are owned by `visual-verification-gate`. This SOP only requires that the Publisher consume its result; do not duplicate or reinterpret that schema here.

## Main-checkout gates and release

**Execution evidence**: use exit codes plus independent file/index/hash checks; do not treat truncated stdout, a child-agent summary, or a single status code as the release result.

Run, in the primary checkout:

```bash
npm run build
# OUT-OF-BAND: curl -s _index.yaml | python3 -c "import yaml,sys; d=yaml.safe_load(sys.stdin); print('cards:', len(d['cards']))"

npm run verify
# OUT-OF-BAND: echo $? == 0

npm run fix-taxonomy
# OUT-OF-BAND: npm run verify-taxonomy && echo $? == 0

npm run check-leak
# OUT-OF-BAND: echo $? == 0
```

Then inspect every mutation. Stage only promoted artifacts, declared assets, current visual evidence, `_index.yaml`, and `index.html`; never use `git add -A`.

If remote main advances, reconcile once in the same primary checkout, regenerate affected indexes and visual evidence, then retry non-force push. A second integration failure is `BLOCKED_AT_INTEGRATION`.

Public verification uses a blocking detail-page check and a separate post-release evidence task. The blocking check requires cache-busted:

```text
https://ccwq.github.io/infocard-pub/docs/<slug>.html
/_index.yaml
/index.html
```

Verify detail HTTP `200` plus a release-specific fingerprint (slug/title/theme/structural token); this establishes `PUBLISHED_VERIFIED`. Index/home checks and fresh public desktop/mobile first-screen screenshots are recorded as post-release audit evidence. Screenshot failure becomes `PUBLIC_VISUAL_FAILED`/`PUBLIC_VISUAL_PENDING` and does not overwrite the release state; successful screenshots are sent to channel.

## Update, rebuild, and timeout recovery (hardened 2026-09-05)

- New card vs update is decided before authoring through `infocard-update-vs-new-pattern`.
- **Any update to an existing card means the edit date must change.** Before promotion, run `sync-publish-metadata.js`; never rely on `npm run build` or on the site build timestamp to refresh `updated`.
- Theme changes rebuild a `.docs` candidate around the selected theme skeleton, preserve required content, then promote through the same gate.
- Author timeout is a filesystem-first handoff: inspect exactly one declared `.docs/<run-id>/<slug>/` directory. If all three declared artifacts (`card.html`, `card.html.meta.yaml`, `promotion-manifest.json`) exist, continue at Publisher; otherwise Publisher completes the missing artifacts in that same directory after confirming frozen facts and theme decision. Never re-delegate the same slug, repeat research, create a worktree, or claim authoring completion from a child summary alone.
- Record the timeout as an execution event (`timeout/no-authoring` or `timeout/partial-authoring`) and preserve the directory; it is not a release result.
- Build/sidecar failure gets one targeted repair and full gate rerun. Second failure is `BLOCKED_AT_LOCAL_GATE`.
- Before closeout, independently verify `updated` in the formal sidecar is newer than the prior value for an update card and verify its rank under the requested homepage sort mode.

## Closeout

Retain `.docs/<run-id>/<slug>/`. Report route, authoring path, manifest mappings, local build/static state, local visual state, content commit, public HTTP/index state, public visual state, and only terminal exceptions.

Do not start Wiki automatically. Do not list, prune, remove, or otherwise operate historical worktrees in ordinary closeout.

## Acceptance checklist

- [ ] No worktree, clone, detached HEAD, `/tmp/infocard*`, or force push used
- [ ] Author wrote only `.docs/<run-id>/<slug>/`
- [ ] Manifest validates source-to-target allowlist
- [ ] Promotion diff contains only declared formal artifacts
- [ ] Four raw desktop/mobile regional captures and two labeled contact sheets are current and have 0 critical / 0 major per panel
- [ ] Build, verify, taxonomy, and leak gates pass
- [ ] Staged diff excludes ambient state
- [ ] Public detail HTTP/fingerprint release state is separate from public visual evidence state
- [ ] `.docs` evidence retained; no cleanup side effect occurred

## Theme enforcement gate (2026-10-12)

**Rule**: before delegating Author, `infocard-theme-assignment` MUST produce a frozen `theme-decision.json`. The delegation prompt MUST NOT contain `data-theme="<name>"`, `Theme: <name>`, `Create a <name> card`, or any equivalent preselection. It must reference only the decision record.

**Pre-delegation check sequence**:
```bash
cd /home/ccwq/qbox/opendir/project/infocard-pub
# 1. recent themes (last 6 published, grep actual data-theme values)
grep -h 'data-theme=' docs/202610*.html | grep -o 'data-theme="[^"]*"' | tail -6
# 2. check decision record exists
ls .docs/<run-id>/<slug>/theme-decision.json
# 3. verify prompt does not preselect theme
grep -i 'data-theme\|Theme:\|Create a.*card' <delegation-prompt.txt>
# → nonzero = VIOLATION, stop and regenerate prompt
```

**Post-delegation check**:
```bash
# After card.html is written, verify the theme used matches the decision
grep -o 'data-theme="[^"]*"' .docs/<run-id>/<slug>/card.html
# Compare to: jq -r '.selected_theme' .docs/<run-id>/<slug>/theme-decision.json
# → mismatch = BLOCKED_AT_THEME_MISMATCH, return card to Author
```

**Known violations from 2026-10-12 session**: Termany and Strata delegations listed `darkgreen` in the "avoid list" but did not call `infocard-theme-assignment`, did not generate a decision record, and the delegation prompts contained `data-theme` preselection. Both cards were published anyway. This must not recur.

## Promotion gate gotchas (2026-10-14)

`promote-infocard.js` rejects a candidate on three recurring structural errors. Fixing them up front saves a round per card.

1. `theme.html.data-theme` — the `<html>` element must carry `data-theme="<frozen theme>"`. Putting it on `<body>` does not satisfy the gate.
2. `theme.html.implementation: missing required selector .x` — the check is a literal substring test for `class="x`. A node matches only when the selector name comes **first** in the class attribute: `class="main-grid grid gap"` passes, `class="grid main-grid gap"` fails. Read `required_selectors` from `theme/themes.json` and wire each one to a real element.
3. `theme.html.colors` / `theme.html.tokens` — colour literals are forbidden outside `:root` (the checker strips `:root{...}` before scanning) and the page must consume tokens via `var(--token)`. Themes vary: `blue` ships no variables at all, `sage-swiss` requires `.stats-row/.main-grid/.analysis-grid`, `graph-paper` declares none. Declare the extra palette values as tokens inside `:root` and reference them, instead of editing the frozen theme.

Any HTML edit after promotion invalidates the manifest's `sha256`; recompute `files[].sha256` before re-running promotion.

## Push rejection under concurrent sessions (2026-10-14)

Several Hermes sessions share this checkout. A push can fail with `remote ref points at a non-commit object`, or `fatal: bad object refs/heads/main` alongside `did not send all necessary objects`. This is a transient read of a ref another session is rewriting, not divergence.

Recover with: `git fetch origin main`, confirm `git rev-list --left-right --count origin/main...main` shows divergence `0  N`, then re-run the ordinary `git push origin main`. **Never `--force`.**

Because parallel sessions promote cards into the same `docs/`, run `npm run build -- --full` immediately before committing; the regenerated `_index.yaml`/`index.html` will legitimately reference sibling cards produced by other sessions. Commit those two generated files with your own card, but do not commit other sessions' card HTML/sidecar files.

## Canonical references

- `references/publish-protocol-v3.md` — detailed release fields, states, and ownership.
- `../visual-verification-gate/SKILL.md` — screenshot, visual disposition, retry, and exception rules.
- `../infocard/infocard-theme-assignment/SKILL.md` — theme selection and decision-record rules.
- `scripts/sync-publish-metadata.js` — authoritative timestamp refresh for update cards; MUST be called before promotion for any existing (non-new) card to ensure `updated` reflects the current publish time.
- `../infocard-subagent-delegation/SKILL.md` — filesystem-first timeout handoff, no-redelegation rule, and publisher takeover contract. (Now superseded by `../delegated-infocard-publishing/SKILL.md`; that skill is the active reference for the delegation loop.)

Historical worktree incident notes may be retained only for a separately authorized migration/cleanup investigation. They are not active publication instructions and must never be loaded to create, recover, publish, or clean a new information card.

### 修改日期最终验收
每次修改信息卡必须同步正式 sidecar 和生成索引的 updated,保留首次 date。候选同步不算完成;full build 后回读正式 updated,与此次修改时间对比,push 后回读公网 sidecar/索引。构建若恢复旧 updated 必须定向修复后重新构建验证,不能宣称更新完成。

Files in this skill

  • SKILL.md23 KB
  • references/2026-07-25-meta-yaml-format-guide.md1.9 KB
  • references/agent1-delivery-gate-review-loop.md1.9 KB
  • references/agent1-delivery-gate.md1.7 KB
  • references/atomic-meta-write-and-review-boundaries.md2.7 KB
  • references/audit-sidecar-gate.md1.3 KB
  • references/batch-authoring-orchestrator-pattern.md6.6 KB
  • references/batch-content-json-authoring.md6.4 KB
  • references/batch-reliability-gates.md2.6 KB
  • references/batch-topic-card-lessons-20260723.md2.2 KB
  • references/build-index-sync-gotcha.md4.7 KB
  • references/build-verify-commit-order-20260719.md2.1 KB
  • references/bundle-driven-meta-generation.md5 KB
  • references/bundle-to-authorstage-pattern.md6.9 KB
  • references/cdn-propagation-404.md1.7 KB
  • references/cdp-screenshot-delivery.md2 KB
  • references/check-info-leak-phone-false-positive.md1.6 KB
  • references/chrome-headless-screenshot-trap.md3.2 KB
  • references/content-gate-fact-fidelity.md1 KB
  • references/css-table-anti-patterns.md8.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.