You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 게이트를 건너뛴
절차 문제로 로그에 남겼다.
Copy file name to clipboardExpand all lines: .llmwiki/wiki/index.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -83,11 +83,11 @@ Operational lore for the plugin system itself — cache, loading, version resolu
83
83
-[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.
84
84
- [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).
85
85
-[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`.
87
87
- [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).
88
88
-[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가 이걸 놓치면 "정리 완료"가 거짓 보고가 된다.
89
89
-[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).
91
91
-[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`.
92
92
- [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.
93
93
- [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.
Copy file name to clipboardExpand all lines: .llmwiki/wiki/log.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,16 @@ Every `/ingest-finding` run and every `/github-dev:post-merge` run that executes
6
6
7
7
---
8
8
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
+
9
19
## 2026-07-30 — post-merge #191: a page whose subject was deleted + a rule the reviewer cannot see (post-merge)
10
20
11
21
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