Skip to content

Commit ce3f286

Browse files
committed
docs: 워크트리별 gitignore 상태와 skill_view 한정자 범위를 기록
PR #193 (voice-prompt 플러그인) 머지 후 통합. 리뷰 5회에서 나온 것 중 기존 홈이 있는 것은 그 라인을 고쳤고, 위키에 이미 있던 것은 다시 적지 않았다. code_review.md: - P0 shell injection 을 quote 유무가 아니라 "값이 셸에 코드로 도달하는가" 축으로 다시 썼다. 값이 명령문 텍스트로 치환되면 set -- 도 grep -F 도 방어가 아니다 — 둘 다 셸이 그 줄을 파싱한 뒤에 동작한다. 데이터 채널(변수 바인딩, quoted heredoc + grep -f)만이 기계적 보장이다. - P1 종료 상태 항목에 pipefail 이 만능이 아닌 경우를 붙였다. 생산자가 루프인 for … done | sort 에서 pipefail 은 반대로 틀린다 — 루프 본문의 마지막 명령이 no-match 로 거짓이 되는 정상 케이스를 실패로 보고한다. 정렬을 루프 밖으로 빼는 것이 맞는 수정이다. - Do-not-flag 의 CLAUDE.md 항목에 역방향 오탐을 명시했다. 포인터 규칙은 루트 한정이라 plugins/<name>/CLAUDE.md 가 내용을 갖는 것은 정상이다. AGENTS.md: 같은 Do-not-flag 역방향을 하드 최소본에 한 절로 반영. .llmwiki: - worktree-squash-merge-gotchas 에 gotcha 5 추가. .claude/state/ 와 .llmwiki/.staging/ 는 gitignore 라서 워크트리별로 존재하는데, post-merge Step 1 은 본 저장소만 허용하고 Step 1.5 는 cr-fix 기록을 cwd 기준으로 읽는다. 두 규칙이 서로를 배제하므로 정상 흐름에서 leftover 체크포인트가 실재하는 deferred 를 두고 "none" 을 출력한다. 이번 실행에서 양쪽 디렉터리로 스니펫을 돌려 확인했다. - hermes-plugin-adapter 의 skill_view 한정자 주장을 adapter 범위로 좁혔다. <plugin>: 접두사는 생성된 __init__.py 가 만들므로 HERMES_ELIGIBLE 에만 존재하고, 그 밖의 플러그인은 skill-unit 설치가 유일 경로라 frontmatter name 그대로 등록된다. install-skills.mjs 로 확인. - 이미 위키에 있던 것은 기록하지 않았다: 캐시 후보를 버전 basename 으로 고르는 것과 후보별 산출물 존재 확인은 dual-surface-command-skill-pattern 의 resolver-robustness 규칙과 detector-cannot-look Mode 9 가 이미 담고 있다. 리뷰어가 잡은 것이 위키가 이미 아는 것이었다는 뜻이므로, 새 lore 가 아니라 query-wiki 게이트를 건너뛴 절차 문제로 로그에 남겼다.
1 parent 81e67e8 commit ce3f286

6 files changed

Lines changed: 66 additions & 12 deletions

File tree

.llmwiki/wiki/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ Operational lore for the plugin system itself — cache, loading, version resolu
8383
- [Skill authoring: source-ground then coverage-audit](plugin-ops/skill-authoring-source-grounded-then-audit.md) — build a reference/guidance skill from source-grounded OSS investigation (read repos, not summaries — counts drift), then audit the distillation on the documented-vs-enforced axis (a pattern in the reference taxonomy is not the same as one in the binary ship-gate); or harvest from your own dogfooded build and interview-gate generic vs project-specific before merging into the shared plugin; or, when *adopting* from an external skill library, triage gap-fill / reflect / skip and diff the posture and defaults, not just the rule list (a famous skill can overlap ~90% of your rules yet win on the opposite default posture) — with a fourth lane, diverge-fork, for wanting the capability in a direction upstream would never take, which zeroes update latency rather than speeding it, so attempt upstream first.
8484
- [Skill-on-skill engine layering](plugin-ops/skill-engine-layering.md) — a thin writing/convention layer on another skill's engine references it by bare name (no vendor) + graceful-degrades optional deps, but a HARD engine in a separate marketplace is NOT a degrade target: a fresh install of the layer alone has no engine and blocks the build, so stop-before-build + guide install (prerequisite-stop). Invisible in the author's env where the engine is already installed. "Copy" also covers reproducing the engine's internal API in prose (script names, ports, step ordinals, enum values) — reference lever *concepts* + stable contract keys, engine SKILL.md is SOT. Re-audit periodically, not just at authoring time — the engine evolves independently, so a full re-read of its actual current file tree can surface missing lever coverage or an inaccurate mechanism claim that a re-read of the layer's own prose would miss. Declared mirrors (references detail file / injection payload / README section) fan out in the same edit — a review cycle can be 4/4 mirror-sync gaps, and drift runs in reverse too (mirror-only rules).
8585
- [Skills install wrapper (npx skills)](plugin-ops/skills-install-wrapper.md)`scripts/install-skills.mjs` installs marketplace skills into Hermes/Codex by wrapping `npx skills`: source arg `.` (reads marketplace.json, groups by plugin), selection via *repeated* `-s <name>` (comma fails), codex global lands in `~/.agents/skills/`, Hermes profile targeted by injecting `HERMES_HOME` (one spawn per agent/profile). EXCLUDED is manifest-eligibility-scoped, not install-scoped — the installer filters by skill count, so codex-image stays installable to Codex.
86-
- [Hermes native plugin adapter](plugin-ops/hermes-plugin-adapter.md) — a third runtime (Hermes Agent) reads the same `plugins/<name>/` tree via `plugin.yaml` + `__init__.py` adapters **generated by `scripts/sync-hermes-manifests.mjs`** from marketplace.json (PR #84; github-dev pilot PR #83 absorbed into the generator, coverage now 7 HERMES_ELIGIBLE plugins — the generator allowlist is SoT), installed with `hermes plugins install <owner>/<repo>/plugins/<name> --enable` (bare install warns without the adapter). `plugin.yaml` keeps all 5 marketplace-derived fields (name/version/description/author/kind:standalone) — CodeRabbit's minimal-manifest push was declined (drift moot post-generator, kind load-bearing). Hermes plugin skills are opt-in — load via `skill_view("plugin:skill")`, never bare invocation; restart after `--enable`. Bodies carry a Claude/Codex→Hermes tool-name map (Bash→terminal, Read/Write→read_file/write_file, Edit→patch, Glob/Grep→search_files, AskUserQuestion→clarify, Task→delegate_task, Monitor→process, Skill→skill_view, image gen→image_generate, WebFetch/WebSearch→web_extract/browser_*, NotebookEdit→Jupyter Live Kernel) + a 3-branch `SKILL_DIR` (source → `$HERMES_HOME` → `~/.hermes`; the `${HERMES_SKILL_DIR}` var was tried in PR #84 and reverted — undocumented/unset on install). `--check` guards adapter drift + orphans in CI + pre-commit. Update model: `hermes plugins update` is a plain `git pull` (errors on non-git installs; `plugin.yaml version` unread by Hermes) and the adapters are generated-but-never-executed — only byte-drift is tested. Distinct from skills-install-wrapper (npx skill-level install) and shared-source (Codex manifest gen). Version sync → `.claude/rules/plugin-versioning.md`.
86+
- [Hermes native plugin adapter](plugin-ops/hermes-plugin-adapter.md) — a third runtime (Hermes Agent) reads the same `plugins/<name>/` tree via `plugin.yaml` + `__init__.py` adapters **generated by `scripts/sync-hermes-manifests.mjs`** from marketplace.json (PR #84; github-dev pilot PR #83 absorbed into the generator, coverage now 7 HERMES_ELIGIBLE plugins — the generator allowlist is SoT), installed with `hermes plugins install <owner>/<repo>/plugins/<name> --enable` (bare install warns without the adapter). `plugin.yaml` keeps all 5 marketplace-derived fields (name/version/description/author/kind:standalone) — CodeRabbit's minimal-manifest push was declined (drift moot post-generator, kind load-bearing). Hermes plugin skills are opt-in — load via `skill_view("plugin:skill")`, never bare invocation; restart after `--enable`. That qualification is **adapter-scoped**: the `<plugin>:` prefix is minted by the generated `__init__.py`, so it exists only for HERMES_ELIGIBLE plugins — every other plugin reaches Hermes only through the skill-unit install, which registers the **bare frontmatter `name`** (`install-skills.mjs` `id: fm.name`), so a non-eligible plugin's body advertising the qualified form names an identifier that does not exist. Bodies carry a Claude/Codex→Hermes tool-name map (Bash→terminal, Read/Write→read_file/write_file, Edit→patch, Glob/Grep→search_files, AskUserQuestion→clarify, Task→delegate_task, Monitor→process, Skill→skill_view, image gen→image_generate, WebFetch/WebSearch→web_extract/browser_*, NotebookEdit→Jupyter Live Kernel) + a 3-branch `SKILL_DIR` (source → `$HERMES_HOME` → `~/.hermes`; the `${HERMES_SKILL_DIR}` var was tried in PR #84 and reverted — undocumented/unset on install). `--check` guards adapter drift + orphans in CI + pre-commit. Update model: `hermes plugins update` is a plain `git pull` (errors on non-git installs; `plugin.yaml version` unread by Hermes) and the adapters are generated-but-never-executed — only byte-drift is tested. Distinct from skills-install-wrapper (npx skill-level install) and shared-source (Codex manifest gen). Version sync → `.claude/rules/plugin-versioning.md`.
8787
- [codex-image delegated-CLI bridge design](plugin-ops/codex-image-bridge-design.md) — a Claude skill that shells out to `codex exec` for image gen: inherit the sub-CLI's default model (omit `-m`, auto-tracks latest, no pin maintenance; `--model` opt-in only); least-privilege `-s workspace-write` default over the `--dangerously-bypass-approvals-and-sandbox` bypass (codex 0.142 has no `--yolo` for `codex exec`); validate passthrough at the shell trust boundary (`--model` vs `^[A-Za-z0-9._:-]+$`, enum `--reasoning`/`--sandbox`) — trailing `-` in a char class is literal (a Codex P2 misread, refuted). `disable-model-invocation` retired in 1.2.0: hiding the skill hid the recipe without preventing autonomous cost (140k-token `codex exec` re-derivation dogfood) — the cost gate moved in-body (explicit grounding + ask-when-ambiguous). `-i, --image` is a generic attach flag with no edit-vs-reference semantics of its own (1.3.0 `--ref` addition relies on unverified prompt-text framing); the real injection defense is quoting/array-exec, not a character denylist — a denylist including `\`/`:` broke Windows paths entirely (cr-fix PR #92 dogfood, 5 findings incl. one P0). A second injection surface — the prompt-body heredoc — needs a per-invocation random LITERAL delimiter (a `<<"$DELIM"` variable form is NOT parameter-expanded, so it gives zero randomization; live canary repro, #130).
8888
- [mem0 v2 list API contract quirks](plugin-ops/mem0-rest-list-contract.md) — 라이브 검증된 계약 quirk 3종: entity 와일드카드는 non-null만 매칭(전체 앱 = bare app_id 필터), list는 만료 메모리 기본 은닉(`show_expired: true` 필수), `HTTPResponse.length`는 chunked에서 None(본문 직접 읽기). 삭제·백업 SSOT가 이걸 놓치면 "정리 완료"가 거짓 보고가 된다.
8989
- [mem0 hook latency budget](plugin-ops/mem0-hook-latency-budget.md) — mem0 플러그인 UserPromptSubmit 훅의 8s 예산은 worst-case(resume 2연속 검색 ~10s + rerank 기본 on)를 못 버틴다; 레버는 캐시 파일이 아니라 사용자 소유 settings.json env(`MEM0_RERANK=off`)에 둔다. 플러그인의 rerank-on 기본값은 mem0 공식 Best Practice와 반대. 단 env-lever는 플래그별로 다름 — `auto_save``_identity.sh``~/.mem0/settings.json` 값으로 env를 매 훅 덮어써서 그 파일이 SOT.
90-
- [Worktree lifecycle gotchas around squash-merge](plugin-ops/worktree-squash-merge-gotchas.md)`EnterWorktree`'s default `baseRef: fresh` branches from `origin/<default-branch>`, not local HEAD, so local-only unpushed commits are invisible until rebased in; `gh pr merge --delete-branch` run inside a worktree can switch that worktree's checkout to the base branch and fail a local fast-forward, triggering an `ExitWorktree` ancestry false-positive resolved via bidirectional content-diff + `git reset --hard origin/main`; and squash-merging a base PR with `--delete-branch` auto-CLOSES stacked child PRs instead of retargeting them.
90+
- [Worktree lifecycle gotchas around squash-merge](plugin-ops/worktree-squash-merge-gotchas.md) — `EnterWorktree`'s default `baseRef: fresh` branches from `origin/<default-branch>`, not local HEAD, so local-only unpushed commits are invisible until rebased in; `gh pr merge --delete-branch` run inside a worktree can switch that worktree's checkout to the base branch and fail a local fast-forward, triggering an `ExitWorktree` ancestry false-positive resolved via bidirectional content-diff + `git reset --hard origin/main`; and squash-merging a base PR with `--delete-branch` auto-CLOSES stacked child PRs instead of retargeting them. Fifth: a **gitignored** state directory (`.claude/state/`, `.llmwiki/.staging/`) is per-working-directory, so `post-merge`'s P0 "main repo only" guard and its Step 1.5 cwd-relative read of `.claude/state/cr-fix-<PR>.json` exclude each other — the normal worktree flow makes the leftover-review checkpoint print `leftover-reviews: none` over a live deferred finding. When a guard pins *where* a skill runs, anchor every path it reads to something the guard cannot move (`git rev-parse --git-common-dir`, an explicit argument, or a `git worktree list` scan).
9191
- [AGENTS.md is loaded verbatim](plugin-ops/agents-md-verbatim-no-import.md) — Codex has no `@import` mechanism at all (`agents_md.rs` byte-reads and concatenates), Hermes shows no evidence of one, and Claude never reads `AGENTS.md`; so an `@CLAUDE.md` pointer silently strips all guidance from both, and a prose redirect still misses the Codex cloud reviewer (it loads `## Review guidelines` into its system prompt, it doesn't walk files). The only loader-valid direction is `CLAUDE.md` importing `@AGENTS.md`.
9292
- [A detector must never say "nothing wrong" when it means "could not look"](plugin-ops/detector-cannot-look-vs-nothing-wrong.md) — `set -euo pipefail` turns `find`'s exit-1-on-no-match and `jq`'s exit-on-corrupt-input into a whole-script abort that prints zero bytes; patching it with `|| true` then converts the failure into a confident "no duplicates". Guard on the question you are asking ("can I enumerate `mcpServers` as an object?", not "is this valid JSON?"), contain each failure in its axis, and report `unreadable` as its own state. Same family: a valid TOML `65_536 # bytes` is not valid JSON for `--argjson`, and `${f#$HOME/}` is a pattern expansion. Mode 4 extends it to remote fetchers — a degraded API answer that still parses (GraphQL null envelope, missing pageInfo, probe rc 0) must not read as "clean". Mode 5: the check's own tool is non-portable (`realpath -m` aborts on BSD), so its gate skips every item and the loop converges auto-merge-eligible `clean` — verify tool portability under stock userland. Mode 7 moves the trap to a *removal recommender*: a `/doctor` trim called the `AGENTS.md` tree derivable after cross-checking `.pre-commit-config.yaml` + lint configs, but the repo enforces via `core.hooksPath` → `.githooks/`, so "safe to cut" was an all-clear for an axis never read — enumerate what *enforces* content before calling it redundant, and prove coverage is redundant before deleting the guard that blocked you. Mode 8 explains the recurrence itself: the "wrong surface" repair was applied only to the caller that reported it, so `engagement-gate.sh` still counted CR comments by `created_at` after `sniff-cr-rate-limit.sh` had been widened to `created_at or updated_at` — and since CR edits its walkthrough in place on a clean re-review, a real convergence read as "CR never looked" and `--auto-merge` was unreachable. Widening a signal is not done until every consumer of it is swept — seen twice, the second time as 11 unquoted `${CLAUDE_PLUGIN_ROOT}` hook commands in the Claude manifests against 7 already-quoted Codex descriptors for the same scripts. Mode 2 gained the guard-scope form: `cmd -v X && X … || true` absorbs the whole `&&` chain, so "X absent" and "X ran and failed" both exit 0; bound the absorption with `if …; then …; fi`. Mode 9 turns the lens on the audit itself: a macOS-scoped sweep cannot see a defect that breaks identically on GNU (whole-path sort lets a marketplace name outrank the version — `sort -V` included), nor the reverse regression its own fix introduces (`python`→`python3` breaks a python.org Windows install), nor can a severity justified by one platform's properties be trusted elsewhere. Mode 10 moves from a check that could not look to one that looked and let go: every denylist needs a false-positive exemption, and that exemption is where it leaks — a guard counting a nearby *comment* as evidence of a fallback cleared the exact `md5sum` defect it was built for, and an exclusion phrased "followed by a quote" to admit BSD `sed -i ''` admitted GNU `sed -i "s/…/…/"` as well. Require evidence of the same kind as the property checked, and test the allowlist by reinjecting the defects the guard exists for.
9393
- [Verify shell portability under stock userland](plugin-ops/stock-userland-verification.md) — an interactive shell can shadow `grep` with a `ugrep` function that accepts `-P`, so a `grep -oP` line dead on BSD userland looks healthy when tested by hand; hooks run as child processes that do not inherit the function, and Codex/Hermes have no shim. A portability claim verified in the interactive shell is unsound — re-verify under `env -i PATH=/usr/bin:/bin`, the environment hooks and other runtimes actually get. That rule has a floor: the stripped environment must still contain the tools the script needs, and on macOS `jq` is not in stock userland, so a jq-based suite run that way dies of tool absence having verified nothing — while passing on Linux, which does ship `/usr/bin/jq`. When a needed tool is non-stock, keep `PATH` and instead assert the tools under test are BSD builds (GNU answers `--version`, BSD refuses; a success means Homebrew shadowed them — fail loudly), and invoke `/bin/bash` so bash 3.2 is what runs.

.llmwiki/wiki/log.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ Every `/ingest-finding` run and every `/github-dev:post-merge` run that executes
66

77
---
88

9+
## 2026-07-30 — post-merge #193: gitignored state is per-worktree, and `skill_view` qualification is adapter-scoped (post-merge)
10+
11+
Diff log written before applying the page edits (git-revertible). Merge SHA `81e67e8` — the `voice-prompt` plugin (24th), driven through 5 cr-fix iterations (25 applied, 1 deferred, 1 refuted). Config integration (Step 6) took three learnings to their existing homes, so none is duplicated here (knowledge routing): the shell-injection axis (quote-vs-data-channel) and the `set -o pipefail` non-universality both sharpened existing bullets in `code_review.md` P0/P1, and the plugin-level-`CLAUDE.md` false-positive sharpened the Do-not-flag entry in both `code_review.md` and its `AGENTS.md` hard minimum.
12+
13+
**Deliberately NOT recorded — the wiki already had it.** Two of the PR's late review findings (Codex P1 "pick the cache candidate by version basename, not whole path" and P2 "verify the template exists per candidate rather than pick-then-check") are already covered by `dual-surface-command-skill-pattern`'s resolver-robustness rules ("verify target before committing to a branch", "first-complete cache version") and by `detector-cannot-look-vs-nothing-wrong` Mode 9 ("whole-path sort lets a marketplace name outrank the version"). The reviewer caught what the wiki already knew, which means the query-wiki gate was skipped before the resolver was written — a process miss, not new lore.
14+
15+
- plugin-ops/worktree-squash-merge-gotchas.md: added `## 5. A gitignored state directory is per-worktree, so a guard that forces the main repo cannot read it`. `.claude/state/` and `.llmwiki/.staging/` are gitignored, hence per-working-directory; `github-dev:post-merge` Step 1 P0-aborts outside the main repo while Step 1.5 resolves `.claude/state/cr-fix-<PR>.json` relative to cwd — so in the normal flow (cr-fix in a worktree, post-merge in the main repo) the leftover-review surface reads structurally empty and prints `leftover-reviews: none` over a real deferred finding. sources 3 -> 4; last_verified 2026-07-12 -> 2026-07-30; alias added.
16+
- plugin-ops/hermes-plugin-adapter.md: scoped the `## Skills are opt-in: skill_view, not bare invocation` claim. The `<plugin>:<skill>` namespace is created by the *generated adapter*, so it exists only for HERMES_ELIGIBLE plugins; a skill installed by unit through `scripts/install-skills.mjs` (`npx skills`) is registered under its bare frontmatter `name` (`install-skills.mjs` `id: fm.name` + the globally-unique assert), and a body that advertises the qualified form for a non-eligible plugin names an identifier that does not exist. sources 5 -> 6; last_verified 2026-07-29 -> 2026-07-30; alias added.
17+
- index.md: extended both existing plugin-ops hooks (no new hook — both pages already listed).
18+
919
## 2026-07-30 — post-merge #191: a page whose subject was deleted + a rule the reviewer cannot see (post-merge)
1020

1121
Diff log written before applying the page edits (git-revertible). Merge SHA `4f95949` — two plugins (`anti-slop-design`, `ppt-yeong-style`) removed from the marketplace, plus a new "generalize the rule, not the instance" principle in `github-dev:post-merge`'s `core-principle.md`. Config integration (Step 6) took two learnings into `AGENTS.md` — the `metadata.version`-is-a-release-counter scoping and the "delete an unguarded count restatement instead of adding it to the sync checklist" rule — so neither is duplicated here (knowledge routing). The generalization principle itself is plugin content whose SoT is the skill body; not copied here either.

0 commit comments

Comments
 (0)