From 3d1ff9bfc6f827ab3b70694e665d9a3b82c10fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Kr=C3=A1l?= Date: Tue, 25 Aug 2026 16:32:44 +0200 Subject: [PATCH] docs(memory): record the staging-discipline trap and that @rules references rot unchecked --- docs/memory/PROJECT_MEMORY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/memory/PROJECT_MEMORY.md b/docs/memory/PROJECT_MEMORY.md index f98e778..b56ae8b 100644 --- a/docs/memory/PROJECT_MEMORY.md +++ b/docs/memory/PROJECT_MEMORY.md @@ -205,6 +205,7 @@ - Trigger: a task reads, copies, or extends a lens / skill list inside a CR skill or an agent definition — for example when defining which lenses an agent runs, or mirroring one wrapper's conditional block into another. - Rule: A name in such a list is not proof the target exists. `race-condition-review` sat in the "shared state / concurrency" routing of all four CR wrappers for a long time while shipping in neither `skills/` nor an install, so the routing silently resolved to nothing — and a test pinned the dangling path as *required*, which made the gap look deliberate. Verify every entry against `ls skills/` before copying a list forward, and when a pin asserts a path exists, check the path, not just the pin. A dangling lens is resolved by a human decision (author the skill, or drop the routing) — never by silently keeping the name alive in a new surface. +- Example: PR #785 — `skills/security-review/SKILL.md` had been citing `@rules/security/backend.mdc`, `@rules/code-review/frontend.mdc` and `@rules/code-review/mobile.mdc` in its Constraints. None of the three exists (the security rules are `.md`, and frontend / mobile live under `rules/security/`), so an agent running that skill silently loaded nothing for three of its four declared security rule sets — the frontend and mobile walks it advertises never ran. Generalizes beyond copied lists: **nothing in `composer build` validates that an `@rules/*` or `@skills/*` reference resolves to a file**, so a reference rots in place the moment a rule is renamed or moved, with no failing test. When you open a skill for any reason, cheap insurance is `grep -o '@rules/[a-z-]*/[a-z.-]*md[c]*' | sort -u` piped through a `[ -f ]` check. - Example: issue #753 / PR #754 copied the list into `agents/athena.md` and carried the dangling name with it; the pre-PR CR caught it as Moderate and the agent was pointed at its own verified inventory instead. The lens itself was dropped from all four wrappers afterwards on the user's decision (*„race-condition-review zahodit"*), and the pinning test was inverted to assert no skill reintroduces the path. - Source: https://github.com/pekral/cursor-rules/pull/754 Added: 2026-08-03 - Role: shared @@ -265,3 +266,11 @@ - Example: PR #782 — two new pin tests looped over six wired skills with `expect($skill)->toContain($needle, $slug . ' cites the canonical section')`; both went red against files that did contain the needle. Rewriting them as `substr_count(...)->toBe(1, $slug . ' …')` fixed the diagnostic and upgraded the pins to uniqueness checks in the same edit. - Source: https://github.com/pekral/cursor-rules/pull/782 Added: 2026-08-25 - Role: implementation + +### stage-by-path-when-the-tree-holds-unrelated-work — `git add -A` for a pre-existing-fix commit swallows the feature you are mid-way through + +- Trigger: the *Pre-existing issue handling* workflow (`@skills/resolve-issue/references/pre-existing-issues.md`, `@skills/process-code-review/SKILL.md`) says land each pre-existing fix in its own commit ordered **before** the in-scope commits — but the fix is usually noticed *while* the in-scope change already sits unstaged in the same working tree. +- Rule: `git add -A` at that moment stages both, producing a commit whose subject describes the small fix while its diff carries the whole feature. The result violates the logical-partition rule twice over: one commit bundles two concerns, and the feature ends up split horizontally (implementation in the "fix" commit, tests and docs in the "feat" commit) — the exact shape `@rules/git/general.mdc` *Every commit is live* forbids. Stage the pre-existing fix **by path** (`git add `), and when the fix and the feature touch the *same* file, reconstruct instead: `git reset --soft ; git reset`, restore that file from the base (`git checkout -- `), re-apply only the fix, commit, then restore the full version and commit the rest. Verify the partition with `git show --stat` per commit **before** pushing, not from the subjects — a misdescribed commit reads correct in `git log --oneline`. +- Example: PR #785 (issue #778) — `fix(security-review): pre-existing — repair four rule references` was committed with `git add -A` while the new `rules/security/untrusted-content.md` and nine skill wirings were already in the tree, so that commit carried 174 insertions of feature and the following `feat(...)` commit carried only CHANGELOG, README and tests. Caught by reading `git show --stat` per commit; reshaped by the reset-and-reconstruct above and re-verified with `git rebase --exec 'composer check' master` so all three commits were green standalone. +- Source: https://github.com/pekral/cursor-rules/pull/785 Added: 2026-08-25 +- Role: implementation