소스 정보
- 저장소
- ccwq/infocard-pub
- 최근 소스 활동
- 2026년 8월 27일 08:36
- 감지된 SKILL.md 언어
- 영어
- 스타
- 1
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/ccwq/infocard-pub --skill infocard-authoring-workflow명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
Use before infocard .docs authoring to select a registered theme from content-aware candidates. Owns the only content-to-theme association, capability filtering, bounded reproducible variation, and theme-decision.json.
Use when one URL or a complete user brief should become one published infocard through the .docs promotion workflow.
Light-batch publish 2–3 cards in parallel without worktree.
| name | infocard-authoring-workflow |
| description | Light-route single-card authoring without subagent. |
| category | infocard |
| tags | ["infocard","authoring","light-route","python","template-clone"] |
Trigger: publishing a single infocard where you have full content and no multi-source cross-validation is needed. Specifically:
Do NOT use when: multi-source cross-validation needed, content is ambiguous, user explicitly requested subagent parallel authoring, or research scope exceeds main-thread context.
When a social post contains a recommendation plus an image/screenshot but omits the canonical project or book name, do not immediately ask the user to choose among every superficially similar search result. First run a bounded first-party matching check using distinctive OCR and structural fingerprints: chapter names, item counts, code-line/read-time figures, author identity, README outline, and public reading links.
If one candidate has a strong multi-signal match, proceed with an explicit attribution boundary in the bundle and card copy, for example: “原帖未点名;根据配图 OCR 与公开 README 交叉匹配,本文聚焦该项目(高置信度)。” Keep the social post's exact wording separate from the matched project's first-party facts, and list rejected candidates only in internal research notes unless the user asked for a comparison.
Ask one clarification question only if the bounded match remains materially uncertain or choosing among candidates would change the requested artifact. Existing explicit “创建/发布” authorization remains valid through a solvable attribution gap; do not reset it merely because the source post is terse.
This pattern is documented for reuse in references/social-source-attribution-matching.md.
When a user asks to find and take over unfinished cards from disk, do not count bundles or HTML files as published-work units. First inventory all run bundles, declared card directories, remote/public evidence. Classify each candidate as 制作中待发布, 已发布待审计, 历史残留/不可直接接管, or 环境/仓库污染; report counts from that classification before claiming takeover.
For a recovered batch, write directly into the primary checkout using write_file. Copy declared artifacts by allowlist, regenerate shared indexes once, and validate every card with the live bundle gate. Recovery bundles must contain canonical fields (slug, html_path, meta_path, asset_dir, manifest_path, source_url, style, keywords, wiki). Normalize slugs to lowercase kebab-case and update bundle paths, HTML filenames, and sidecars together. A local commit is not proof of “待发布” until remote branch and public URL evidence are checked.
When visual_review.required=true, a screenshot result with any critical or major defect blocks release. A successful build, HTTP 200, or DOM check cannot be upgraded to visual pass. Keep the per-card critical / major / minor disposition in run-local evidence.
User expectation for interrupted runs: If the user says “继续 / go / 直到完成”, keep executing to a terminal state rather than returning a plan or pausing after discovery. If a hard visual gate blocks release, report the exact blocker and preserve the recovery evidence; do not call the batch complete.
Visual disagreement rule: A local .table-scroll wrapper and scrollWidth == clientWidth prove only page-level mechanical containment. They do not prove discoverability or visual readability. Dense tables need a visible mobile affordance (for example a short “横向滑动查看完整对比” cue or a partial next-column reveal), and multi-column cards need visible right-side padding/border. Re-screenshot after each repair. If vision still reports clipping while DOM checks pass, keep VISUAL_PENDING and do not push.
See references/recovery-takeover-inventory.md for the evidence matrix and recovery classification recipe.
See references/chatgpt-url-to-infocard.md for the verified workflow to extract infocard content from ChatGPT conversation URLs via abc — navigate, snapshot-parse table/cells/code blocks, and abc screenshot visual verification.
See references/interrupted-batch-recovery.md for the class-level recovery sequence and visual-gate disagreement handling.
See references/github-api-workarounds.md for GitHub API quirks and the reliable git-push pattern for this repo.
See references/wechat-inline-body-compatibility.md for the verified Handline-to-WeChat inline-body conversion and validator gate.
See references/color-material-wechat-inline-recipe.md for the Color Material editorial-to-WeChat natural-flow conversion, allowed-tag contract, leaf-marker audit, and deterministic static checks.
See references/graph-paper-wechat-inline-recipe.md for the graph-paper/manual strict-tag migration, independent allowlist scan, and the <br> to whitespace-leaf correction.
See references/vscode-marketplace-card-build-notes.md for the VS Code Marketplace / Open VSX marketplace-card workflow, provenance split, and build/commit notes from the 2026-08-15 card.
Authoring happens only inside the primary repository checkout's .docs/<run-id>/<slug>/ directory. This is the correct and only authoring location.
The Author must not directly write docs/, assets/, _index.yaml, index.html, Git state, /tmp/infocard*, a clone, or any Git worktree. Create candidate HTML, sidecar, facts, visual evidence, and promotion-manifest.json in .docs; infocard-pub-publisher is the only role that promotes declared files to formal public paths.
Use a rebuild rather than repeated patches when CSS rules duplicate/override each other, grid and flex rules conflict, two repair rounds failed, or the user explicitly asks for a rebuild. Rebuild only the .docs/<run-id>/<slug>/card.html candidate; retain every required content section and let Publisher promote it after visual evidence passes.
For a numbered poster-shell card, keep the number, stripe, and body structurally separate:
.skill-card { display:grid; grid-template-columns:100px 1fr; position:relative; min-height:96px; }
.card-num { grid-column:1; grid-row:1; align-self:center; text-align:right; padding-right:28px; }
.card-stripe { position:absolute; left:96px; top:18px; bottom:18px; width:1px; }
.card-body { grid-column:2; grid-row:1; min-width:0; padding:14px 22px 14px 18px; }
@media (max-width:720px) {
.skill-card { grid-template-columns:68px 1fr; }
.card-num { padding-right:16px; }
.card-stripe { left:64px; top:14px; bottom:14px; }
.card-body { grid-column:2; min-width:0; padding:10px 10px 10px 14px; }
}
Do not let a body fall into the number column. If the mobile variant switches to flex, verify that the body has an explicit usable width; a generic flex:1 patch may lose to a more-specific media rule. For every rebuild, inspect DOM parentage before changing CSS: a prematurely closed tag can turn a description into a grid sibling, which no overflow rule can fix.
At desktop and 390px mobile, inspect scrollWidth, clientWidth, body geometry, number/card center alignment, computed display, and relevant grid-column/flex values. Then capture fresh visual evidence. A structural or CSS change invalidates every prior screenshot and manifest.
Never use a worktree, branch, clone, force-push, or /tmp/infocard* for a poster-shell rebuild.
When the workflow belongs to one repository, prefer placing the skill inside the repo under <repo>/.agents/skills/ so the project owns its own procedures. Hermes also recognizes <repo>/.hermes/skills/, but .agents/skills/ is the clearer cross-tool convention when the repo may be used by more than one agent harness.
Key behavior:
For repo-local skill rollout or skill-heavy repo work, do not jump straight from idea to file edits. First turn the current discussion into a spec with to-spec, then delegate implementation to a subagent that follows the spec and returns concrete changes plus verification evidence.
Use this sequence:
skill:to-spec)This keeps project-specific workflows separate from the global skill library and makes later consolidation easier.
Preflight: record git status --short in the primary checkout before writing. Ambient changes are preserved and excluded from this run; a dirty checkout is not permission to create a worktree, reset, stash, clean, or stage unrelated files.
No worktree for authoring. Use write_file only under .docs/<run-id>/<slug>/. The Publisher later verifies the manifest and promotes the declared public files. Authors never stage.
.docs/<run-id>/<slug>/..docs/<run-id>/<slug>/card.html and .docs/<run-id>/<slug>/card.html.meta.yaml.theme/<style>.html as skeleton input; write the derived candidate only under .docs..docs/<run-id>/<slug>/promotion-manifest.json mapping candidate sources to final docs/<slug>.html, matching sidecar, and declared assets/ targets..docs; do not create formal docs/ files in the Author phase.infocard-theme-assignment is the only owner of content-to-theme association. Before writing HTML, load .docs/<run-id>/<slug>/theme-decision.json and the assignment skill's schema. Authoring must not maintain a lookup table, recommend a concrete theme, or perform a second selection.
The record must already contain content_type, content_shape, candidate_themes, excluded_themes, selection_weights, seed, selected_theme, and user_override. Confirm that the selected theme is registered, the record is internally consistent, and the HTML data-theme plus sidecar style normalize to that selected bare slug. If missing or inconsistent, stop with THEME_BLOCKED and return to theme assignment.
Use the selected registered theme's skill and theme/<selected_theme>.html as skeleton input. User-selected themes remain preferred only when the assignment record marks them accepted after capability filtering; authoring never bypasses that check.
Content depth guide ("内容不要吝啬"): When user asks for practical-guide level:
.step-item + .step-n numbered blocks)#1a1a2e bg + color spans).section-head + .section-no (96px red/blue/black blocks) + .card grid + .risk top-color stripes触发:卡片介绍一个有 Live Demo 的 UI 组件 / React 库,Live Demo 是核心价值。
反模式:Hero 右侧放置占一半高度的浏览器 mockup(Live Demo 截图)。被用户明确否定——截图≠真实动画,卡片变长但信息密度低,且截图与实际效果视觉不一致。
正确结构:
<a class="cta" href="https://...live-demo...">↗ 体验 9 种动画</a>为什么 SVG 示意必须保留:用户原话"所有的 svg 都消失了,也没有动画"——去掉 SVG 后只剩纯文字,信息密度反而比原来更低。正确做法是去掉 mockup 截图,保留紧凑 SVG 网格。
CTA CSS 要点:
.cta {
display: inline-flex; align-items: center; gap: 8px;
background: var(--blue); color: #fff; font-size: 14px; font-weight: 700;
padding: 11px 20px; border-radius: 6px;
width: fit-content; /* ← grid 1fr 下必须!否则占满整行 */
box-shadow: 0 4px 16px rgba(74,120,255,.35);
}
已验证案例:thinking-orbs(1569 ★,MIT,React 18+ / Pure JS 出口)→ darkblue + 去 mockup + 紧凑 SVG 示意
0865ecd · 桌面 0/0/0,移动 0/0/1移动端截屏规范:用 --window-size=390,1500(非 1800),能覆盖一屏信息卡完整内容又不截到页面底部空白。
google-chrome --headless=new --disable-gpu \
--window-size=390,1500 \
--screenshot=/tmp/card-mobile.png \
"http://127.0.0.1:PORT/docs/<slug>.html"
The decision record replaces the former four-line theme_primary/theme_fallback/theme_reject note. Keep rejection reasons and weights in JSON; do not copy them into a competing authoring contract. Any HTML, structure, CSS, content, or theme change invalidates prior visual evidence.
For a multi-source controversy or any card upgraded after feedback such as “内容空洞 / 充分调查 / 作为调查记者”, do not retain a thin fact-check skeleton. Build an investigation dossier with: a conclusion-led overview, a causally connected timeline, at least two attributable primary quotations/notices, a sourced scale-data block, ≥3 stakeholder positions, one legal/industry/comparable-case context, and a clearly separated evidence boundary.
A card fails this gate if it is mainly “confirmed / unverified” labels, if its timeline lacks actor-action-evidence detail, or if deleting most paragraphs leaves the conclusion unchanged. This gate governs content density; wang-reporter-investigation-standard remains the single source of truth for evidence traceability.
Completion criterion: each section adds an independently sourced fact, explanation, or bounded inference; no disputed claim is upgraded beyond its evidence tier.
Method: Use write_file directly — simpler and faster than Python scripts, works reliably for HTML content up to ~20KB. The
Metadata naming rule: Name the sidecar exactly <html-basename>.meta.yaml, and set slug to the full date-prefixed basename used by the repository (for example, 20260729-alacritty), not merely the project name. The build's metadata-shape pass treats a short slug as a warning and may block verification.
Content discipline for tool cards: Prefer claims that are directly supported by the project's README or official documentation. Treat live GitHub counts and benchmark numbers as time-sensitive; either date them, label them approximate, or omit them. Preserve the repository's own feature boundaries, especially when a deliberate omission (such as no built-in tabs) is part of the product's design.
For AI gateway, provider-switcher, or model-router cards, keep these layers separate in prose and comparison tables:
Never infer layer 3 from layer 1 or 2. “Official account remains visible”, “model appears in /model”, and “OpenAI-compatible” are not proof of official subscription billing, endpoint success, or complete tool/streaming/image compatibility. State the route’s required endpoint, credential, protocol, and known capability limit; distinguish native protocol passthrough from conversion adapters.
Completion criterion: every routing advantage names its layer; catalog, OAuth, protocol conversion, local inference source, and billing claims each retain their own first-party evidence.
Python script approach remains as a fallback for very large content.
If write_file is blocked for unsupported scripting reasons:
Template source priority:
theme/<style>.html for the base template; copy to docs/<slug>.htmlPython script fallback (only needed for ~15KB+ content where direct write_file is risky):
import re
path = 'docs/<slug>.html'
with open(path, 'r', encoding='utf-8') as f:
html = f.read()
html = html.replace('<title>infocard-darkblue-style 元素演示</title>',
'<title>Your Title Here</title>')
new_body = r'''...your HTML content...'''
html_new = re.sub(r'<main class="page">.*?</html>', new_body, html, flags=re.DOTALL)
with open(path, 'w', encoding='utf-8') as f:
f.write(html_new)
print("OK:", len(html_new), "bytes")
Execute: python3 /tmp/gen_<slug>.py
⚠️ 子智能体 meta.yaml 三大常见错误(需人工复核):
--- 首尾符:子智能体有时在文件首尾各写一个 ---,导致 YAML 被解析为多文档,报 expected a single document in the stream, but found more 而使 build 失败。修复:删除全部 ---,meta.yaml 必须只有一个文档。style 字段:子智能体有时遗漏 style 字段。修复:补全 style: <theme>。date 填为内容原始日期而非发布时间:用户要求 date 为发布时间,不是内容来源的原始日期。修复:统一填当天发布日。Twitter/X 来源的额外字段:
x_status_id:X 帖子的数字 ID(如 2081932972559855907)author:格式为 "yibie (@yibie)"(显示名 + 括号内用户名)格式规则:
---(js-yaml 会把 --- 当成多文档分隔符,即使只有一处也会引发问题)— in title/desc(导致 YAML 解析歧义)path field:双引号字符串 "docs/<slug>.html"date and updated:"YYYY-MM-DD HH:MM:SS" 格式(空格分隔,无 T,无时区后缀);填实际运行时 UTC 时间:date -u +"%Y-%m-%d %H:%M:%S"category:用户指定值,非固定 "knowledge";常见如 工具推荐、knowledge 等slug:默认无 YYYYMMDD 前缀(如 agent-routing);用户指定加前缀则从用户Correct template:
slug: agent-routing
title: "agent-routing v1.3.0:Subagent 模型智能路由"
desc: "给 subagent 分配「最合适的模型」通常靠经验和运气。agent-routing 把这个决策变成可重复的 calibrate 流程——Haiku/Sonnet/Opus 三级路由 + per-agent effort level。"
date: "2026-07-28 23:10:42"
updated: "2026-07-28 23:10:42"
tags: ["agent-skills", "routing", "model-selection", "Claude-Code"]
category: 工具推荐
source: Twitter
source_url: "https://x.com/i/status/2081932972559855907"
author: "yibie (@yibie)"
x_status_id: "2081932972559855907"
style: infocard-hardblue-style
path: "docs/20260729-agent-routing.html"
sync-build-timestamps 在 build 时用 Shanghai 时区时间覆盖 updated 字段。不要依赖 meta.yaml 中的 updated 作为实际发布时间,以 build 输出日志中的 build_ts 为准。
典型 build 输出:
[build-site] build_ts=2026-07-29 07:11:50 Asia/Shanghai
[sync-build-timestamps] NEW docs/20260729-agent-routing.html.meta.yaml | date=updated=2026-07-29 07:11:50
Commit + push in the same session after verifying the build succeeds.
git add docs/20260729-agent-routing.html docs/20260729-agent-routing.html.meta.yaml
git commit -m "feat: publish agent-routing subagent model routing"
# git push ... # 跳过,不要 push
当用户提供仓库 URL 但标注"需核实"时:
source_url 字段保留原始 URL(需核实) 标注npm run build
Expected: wrote _index.yaml and injected index.html (N cards)
If build fails with "single document in stream, but found more": check meta.yaml for trailing ---, em-dash in fields, or multi-doc YAML.
Skip-build shortcut (new card + index already correct):
When ALL of the following are true, you can skip npm run build entirely:
_index.yaml and index.html were already regenerated by a prior build (e.g. during draft validation)Then go directly to Step 5 commit — the index is already correct.
Build-vs-commit decision tree:
npm run build (regenerates index)npm run build then Step 5git add docs/<slug>.html docs/<slug>.html.meta.yaml _index.yaml index.html
git commit -m "feat: publish <title>"
git push origin main
Wait ~80s for GitHub Pages deploy, then:
curl -sI --max-time 15 "https://ccwq.github.io/infocard-pub/docs/<slug>.html"
# Expect: HTTP/2 200
Symptom (verified 2026-08-18 on deepseek-harness-learning): A resource-collection card displays 4 section cards + 12 sub-resource items, all visually styled as clickable cards, but zero have actual <a href> attributes. Total page links = 1 (Twitter attribution only). All section cards were <a class="mini-card"> without href; all sub-resource items were plain <div>, not <a>.
Root cause: Authoring created dead <a> tags (no href attribute) and non-linked <div> elements that visually resemble links. Visual screenshot review cannot detect this — it requires structural HTML inspection.
Detection (run before build/commit — web_extract strips href attributes, do not use it):
import subprocess, re
r = subprocess.run(['curl','-s','-L','--max-time','30',
'https://ccwq.github.io/infocard-pub/docs/<slug>.html'],
capture_output=True, text=True)
html = r.stdout
hrefs = re.findall(r'<a[^>]+href=["\']([^"\']+)["\']', html)
hrefs_external = [h for h in hrefs if h.startswith('http')]
print(f"Total links: {len(hrefs)}, External links: {len(hrefs_external)}")
print(f"External URLs: {hrefs_external}")
# Also check for dead <a> tags (class contains 'card'/'link' but no href)
dead_a = re.findall(r'<a[^>]+class="([^"]*(?:card|link)[^"]*)"[^>]*(?<!href)>', html)
print(f"Dead <a> (class=card but no href): {len(dead_a)}")
Rule: Every visually clickable resource item must be a real <a href="URL">. If a resource entry has no URL target, it must NOT be visually styled as a card or link — use a plain label instead.
Fix pattern — convert <article> to <a>:
<!-- Before: dead <article> -->
<article class="flow-item cyan">
<div class="t">🏠 GitHub 仓库</div>
<div class="c">DeepSeek Harness 官方源代码仓库</div>
</article>
<!-- After: real <a> -->
<a class="flow-item cyan" href="https://github.com/deepseek-ai/deepseek-harness" target="_blank" rel="noreferrer">
<div class="t">🏠 GitHub 仓库</div>
<div class="c">DeepSeek Harness 官方源代码仓库</div>
</a>
<!-- Before: <article class="mini-card"> in hero-visual -->
<article class="mini-card">
<div class="mini-icon cyan" aria-hidden="true"><svg>...</svg></div>
<div class="label">Official</div>
<div class="value">官方仓库</div>
<div class="desc">源代码、README、示例、架构说明</div>
</article>
<!-- After: <a class="mini-card"> -->
<a class="mini-card" href="https://github.com/deepseek-ai/deepseek-harness" target="_blank" rel="noreferrer">
<div class="mini-icon cyan" aria-hidden="true"><svg>...</svg></div>
<div class="label">Official</div>
<div class="value">官方仓库</div>
<div class="desc">源代码、README、示例、架构说明</div>
</a>
URL research workflow (before authoring the HTML):
curl -sI --max-time 15 "URL" | head -1PASS: links >= expected_resource_count AND dead_a == 0 → proceed to build.
FAIL: any dead <a> tag or link count < expected → repair HTML before push.
⚠️ web_extract strips href attributes — never use it for link verification. Always use curl + regex as shown above. web_extract is safe for extracting readable prose content, not for structural HTML inspection.
Symptom: patch reports "duplicate" or "found 2 matches" but read_file still shows the wrong content. Subsequent patch calls keep returning "duplicate" and making no progress, even with different old_string values.
Root cause: When two elements share the same wrapping structure (e.g. two <div class="feat"> blocks with identical class/label hierarchy), patch finds both and blocks. After the first successful patch on one element, both lines become identical — patch now sees the other element as a new duplicate and blocks again. The tool reports "1 files modified" on the first pass, but the second pass finds two identical instances and stops.
Concrete case from 2026-08-06 (Exa Search API card): Both /search panel and /contents panel had a <div class="feat"> with label "认证". After patching the /search panel correctly, both panels' lines became identical, so the next patch for the other element reported "duplicate" and changed nothing. 15+ patch attempts were made over ~30 minutes.
Recovery patterns (in order of reliability):
replace_all=true on a fully unique substring — only when all instances need identical replacement:
patch(path=html, old_string='<div class="value">WRONG</div>',
new_string='<div class="value">CORRECT</div>', replace_all=True)
Widest unique context — include surrounding lines that only exist around the target element:
# Patch the whole block including unique surrounding lines
patch(path=html,
old_string=''' <section class="shell">
<div class="panel">
<div class="panel-head"><span class="panel-kicker">POST /search</span></div>
<div class="feat"><div class="label">端点</div><div class="value">https://api.exa.ai/search</div></div>
<div class="feat"><div class="label">认证</div><div class="value">WRONG</div></div>''', # ← only /search has this exact URL
new_string=''' <section class="shell">
<div class="panel">
<div class="panel-head"><span class="panel-kicker">POST /search</span></div>
<div class="feat"><div class="label">端点</div><div class="value">https://api.exa.ai/search</div></div>
<div class="feat"><div class="label">认证</div><div class="value">CORRECT</div></div>''')
Line-by-line Python replacement (most reliable for HTML with near-identical div structures):
with open(path, 'r', encoding='utf-8') as f:
lines = f.readlines()
new_lines = []
for line in lines:
if 'WRONG_TEXT' in line:
new_lines.append(line.replace('WRONG_TEXT', 'CORRECT_TEXT'))
else:
new_lines.append(line)
with open(path, 'w', encoding='utf-8') as f:
f.writelines(new_lines)
Byte-level replacement for invisible character differences — when text looks identical but Python can't find it:
with open(path, 'rb') as f:
content = f.read()
# Find by hex/byte pattern
idx = content.find(b'AUTHORIZATION_SUBSTRING')
chunk = content[idx:idx+50]
print(chunk.hex()) # See exact bytes
content = content.replace(b'OLD_BYTES', b'NEW_BYTES')
with open(path, 'wb') as f:
f.write(content)
Use this when text appears in repr() output but in check returns False — indicates invisible Unicode or encoding differences.
Rule: After any patch that reports "duplicate", always read_file the affected lines to verify actual content. A "duplicate" block means nothing was changed. Don't trust "1 files modified" without confirmation.
Prevention: Before patching elements that may be duplicated in an HTML file, scan first:
grep -n 'class="feat"' docs/<slug>.html | grep '认证'
# If output shows 2 lines with same context → use Python line-by-line method
git push origin main
If non-fast-forward:
git fetch origin main && git rebase origin/main
git push origin main
No worktree, no PR, no branch dance.
The theme/hardblue.html template uses .section-no (96×96px grid cell with 3px border) as the numbered block inside .section-head. The CSS defines .section-no with a display:grid; place-items:center layout and color variants .section-no.b (blue) / .section-no.k (black). This is the correct class — use it.
The earlier skill warning about .sec-no was based on a different or outdated source. Always verify against theme/<style>.html directly before writing section numbers.
/* ✅ ACTUAL: sec-no — used in every deployed hardblue card */
.sec-no{
width:34px;height:34px;
display:flex;align-items:center;justify:center;
background:var(--black);color:#fff;
font-size:13px;font-weight:900;
border:1.5px solid var(--black);
flex-shrink:0;box-shadow:var(--shadow-sm)
}
/* ❌ WRONG: section-no — does not exist in deployed HTML */
Always use .sec-no for section numbering. The hardblue SKILL.md's table entry for section-no is outdated.
Symptom: npm run build throws Error: Index build failed: expected a single document in the stream, but found more
Causes (in order of frequency):
--- at end of file → delete it— (U+2014) in title/desc → replace with |\nstyle attribute value in path field → use simpler quoted stringDiagnosis: node -e "const yaml=require('./assets/home/vendor/js-yaml.min.js'); const d=require('fs').readFileSync('docs/<slug>.html.meta.yaml','utf8'); console.log(yaml.loadAll(d).length)" — if count > 1, fix the above.
Symptom: python3 - <<'PY' ... returns exit -1 with no output, or times out.
Cause: Content > ~15KB hits orchestrator command gate.
Fix: Use write_file directly for HTML content up to ~20KB — this is now the preferred method. Only fall back to the Python script approach for very large content.
Symptom: execute_code returns error: "Cron jobs run without a user present to approve it."
Cause: execute_code runs arbitrary Python with tool access. When the calling context is a cron profile or background delegation, it is blocked.
Fix: Use terminal with inline Python via python3 - <<'PY' ... or python3 -c "..." instead. For file operations, use write_file / patch directly.
For 2+ cards simultaneously: write each directly into docs/, then run one shared build.
# All cards written directly to docs/
npm run build # regenerates _index.yaml + index.html for all
git add docs/<slug1>.html docs/<slug2>.html _index.yaml index.html
git commit -m "feat: publish <slug1> + <slug2> cards"
git push origin main
Pages deploys once. Verify all URLs in one loop.
The theme/darkblue.html template contains <title>infocard-darkblue-style 元素演示</title>. Always replace before the re.sub.
When a dispatched subagent times out (e.g. Non-streaming API call timed out after 90s or API call failed after 3 retries), do NOT redispatch. Take the task directly in the main thread. Use write_file for HTML (≤40KB, single write, no Python script needed) and patch for meta.yaml corrections.
Step 1 · Diagnose what the subagent left behind
# Check in the primary checkout docs/ (subagents often write to parent repo)
REPO_ROOT="$(git rev-parse --show-toplevel)" || exit 1
ls "$REPO_ROOT/docs/" | grep <slug>
# If meta.yaml exists but HTML is missing → partial state, fix it
# If both exist → just build + commit + push
Step 2 · Handle partial state (most common: meta.yaml written, HTML missing)
# Read the existing meta.yaml to get slug/date/tags
with open('docs/<slug>.html.meta.yaml') as f:
meta = yaml.safe_load(f)
slug = meta['slug'] # use existing slug, date, tags from subagent's meta.yaml
Step 3 · Write HTML directly with write_file
theme/darkblue.html (lines 500–772 have the full deployed CSS)write_file call (≤40KB, reliable)write_file is faster and less error-pronepatchStep 4 · Commit + push (no subagent re-dispatch)
git add docs/<slug>.html docs/<slug>.html.meta.yaml _index.yaml index.html
git commit -m "feat: add <slug> infocard (YYYY-MM-DD)"
git push origin main
Step 5 · Verify
import urllib.request, time
time.sleep(45) # GitHub Actions + Pages deploy time
url = "https://ccwq.github.io/infocard-pub/docs/<slug>.html"
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
resp = urllib.request.urlopen(req, timeout=15)
content = resp.read().decode('utf-8', errors='replace')
checks = ['Tabbit', '上下文', 'GN06', 'Agent', '91.8']
for c in checks:
print(f" '{c}': {'✓' if c in content else '✗'}")
Verified 2026-08-19: Tabbit card. Subagent API timed out at 90s with Non-streaming API call timed out after 90s. Subagent left only meta.yaml (partial state). Main thread wrote complete 39KB HTML in one write_file call, patched meta.yaml, committed, pushed. HTTP 200 in ~45s.
Rule: Never redispatch a timed-out subagent. The main thread's direct tool access (write_file, patch) succeeds where subagents fail on infrastructure.
Root cause: A grid cell with grid-column: span N inside a parent grid becomes overflow when the parent grid's column count changes via @media query.
Concrete case: agent-row has grid-template-columns: 1fr 1fr 1fr (desktop 3 columns). A model-card has style="grid-column:span 3" (spans all 3). On mobile @media(max-width:720px), agent-row becomes grid-template-columns: 1fr 1fr (2 columns). The span 3 card now overflows because 3 > 2. Vision model reports "text overlap".
Symptom: Desktop looks fine. Mobile has severe element collision in the affected section.
Solution — extract overflowing element as a sibling, not a child:
<!-- BROKEN: model-card is inside agent-row grid -->
<div class="agent-row"> <!-- 3 cols → 2 cols on mobile -->
<div class="agent-card">Research Agent</div>
<div class="agent-card">Biology Agent</div>
<div class="agent-card">Physics Agent</div>
<div class="agent-card" style="grid-column:span 2">ML Agent</div>
<div class="model-card" style="grid-column:span 3">Providers...</div>
<!-- ^^^^^^^^^ overflow on mobile! -->
</div>
<!-- FIXED: model-row is OUTSIDE agent-row, as a sibling -->
<div class="panel-body">
<div class="agent-row"> <!-- 3 cols → 2 cols on mobile, no overflow -->
<div class="agent-card">Research Agent</div>
<div class="agent-card">Biology Agent</div>
<div class="agent-card">Physics Agent</div>
<div class="agent-card">ML Agent</div>
</div>
<div class="model-row">
<!-- flex-wrap, not grid — safe on any width -->
<span>OpenAI</span><span>Anthropic</span><span>Google</span>...
</div>
</div>
CSS rule for extracted row: Use display:flex; flex-wrap:wrap; gap:6px — never another nested grid.
Rule: Never put a span value inside a grid child that is larger than the grid's mobile column count. Always extract spanning elements as siblings.
Mobile screenshot height: Use --window-size=390,2200 (not 1500) for full-length infocards. 2200 captures the complete card without cutting off the bottom.